Initialize codes based on 11.1 36/168336/1
authorSangchul Lee <sc11.lee@samsung.com>
Fri, 26 Jan 2018 01:17:02 +0000 (10:17 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Fri, 26 Jan 2018 01:17:02 +0000 (10:17 +0900)
Change-Id: I0826e35f3806e89de2b6cc7a1d3060101e1fd8d8
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
797 files changed:
AGPL [new file with mode: 0644]
GPL [new file with mode: 0644]
LGPL [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
PROTOCOL [new file with mode: 0644]
PulseAudioConfig.cmake.in [new file with mode: 0644]
PulseAudioConfigVersion.cmake.in [new file with mode: 0644]
README [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
bootstrap.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
coverity/model.c [new file with mode: 0644]
doc/stream_restore_fallback_table_example.table [new file with mode: 0644]
doxygen/.gitignore [new file with mode: 0644]
doxygen/Makefile.am [new file with mode: 0644]
doxygen/doxygen.conf.in [new file with mode: 0644]
git-version-gen [new file with mode: 0755]
libpulse-mainloop-glib.pc.in [new file with mode: 0644]
libpulse-simple.pc.in [new file with mode: 0644]
libpulse.pc.in [new file with mode: 0644]
m4/.gitignore [new file with mode: 0644]
m4/acx_libwrap.m4 [new file with mode: 0644]
m4/ax_check_compile_flag.m4 [new file with mode: 0644]
m4/ax_check_define.m4 [new file with mode: 0644]
m4/ax_check_flag.m4 [new file with mode: 0644]
m4/ax_cxx_compile_stdcxx.m4 [new file with mode: 0644]
m4/ax_cxx_compile_stdcxx_11.m4 [new file with mode: 0644]
m4/ax_define_dir.m4 [new file with mode: 0644]
m4/ax_pthread.m4 [new file with mode: 0644]
m4/ax_tls.m4 [new file with mode: 0644]
m4/orc.m4 [new file with mode: 0644]
man/.gitignore [new file with mode: 0644]
man/Makefile.am [new file with mode: 0644]
man/default.pa.5.xml.in [new file with mode: 0644]
man/esdcompat.1.xml.in [new file with mode: 0644]
man/pacat.1.xml.in [new file with mode: 0644]
man/pacmd.1.xml.in [new file with mode: 0644]
man/pactl.1.xml.in [new file with mode: 0644]
man/padsp.1.xml.in [new file with mode: 0644]
man/pasuspender.1.xml.in [new file with mode: 0644]
man/pax11publish.1.xml.in [new file with mode: 0644]
man/pulse-cli-syntax.5.xml.in [new file with mode: 0644]
man/pulse-client.conf.5.xml.in [new file with mode: 0644]
man/pulse-daemon.conf.5.xml.in [new file with mode: 0644]
man/pulseaudio.1.xml.in [new file with mode: 0644]
man/start-pulseaudio-x11.1.xml.in [new file with mode: 0644]
man/xmltoman [new file with mode: 0755]
man/xmltoman.css [new file with mode: 0644]
man/xmltoman.dtd [new file with mode: 0644]
man/xmltoman.xsl [new file with mode: 0644]
orc.mak [new file with mode: 0644]
po/.gitignore [new file with mode: 0644]
po/LINGUAS [new file with mode: 0644]
po/POTFILES.in [new file with mode: 0644]
po/POTFILES.skip [new file with mode: 0644]
po/as.po [new file with mode: 0644]
po/be.po [new file with mode: 0644]
po/bn_IN.po [new file with mode: 0644]
po/ca.po [new file with mode: 0644]
po/cs.po [new file with mode: 0644]
po/de.po [new file with mode: 0644]
po/de_CH.po [new file with mode: 0644]
po/el.po [new file with mode: 0644]
po/es.po [new file with mode: 0644]
po/fi.po [new file with mode: 0644]
po/fr.po [new file with mode: 0644]
po/gl.po [new file with mode: 0644]
po/gu.po [new file with mode: 0644]
po/he.po [new file with mode: 0644]
po/hi.po [new file with mode: 0644]
po/hr.po [new file with mode: 0644]
po/hu.po [new file with mode: 0644]
po/id.po [new file with mode: 0644]
po/it.po [new file with mode: 0644]
po/ja.po [new file with mode: 0644]
po/kn.po [new file with mode: 0644]
po/ko.po [new file with mode: 0644]
po/lt.po [new file with mode: 0644]
po/ml.po [new file with mode: 0644]
po/mr.po [new file with mode: 0644]
po/nl.po [new file with mode: 0644]
po/nn.po [new file with mode: 0644]
po/oc.po [new file with mode: 0644]
po/or.po [new file with mode: 0644]
po/pa.po [new file with mode: 0644]
po/pl.po [new file with mode: 0644]
po/pt.po [new file with mode: 0644]
po/pt_BR.po [new file with mode: 0644]
po/ru.po [new file with mode: 0644]
po/sk.po [new file with mode: 0644]
po/sr.po [new file with mode: 0644]
po/sr@latin.po [new file with mode: 0644]
po/sv.po [new file with mode: 0644]
po/ta.po [new file with mode: 0644]
po/te.po [new file with mode: 0644]
po/tr.po [new file with mode: 0644]
po/uk.po [new file with mode: 0644]
po/zh_CN.po [new file with mode: 0644]
po/zh_TW.po [new file with mode: 0644]
pulseaudio-text.svg [new file with mode: 0644]
pulseaudio.supp [new file with mode: 0644]
pulseaudio.svg [new file with mode: 0644]
scripts/benchmark_memory_usage.sh [new file with mode: 0755]
scripts/benchmarks/.gitignore [new file with mode: 0644]
scripts/benchmarks/README [new file with mode: 0644]
scripts/plot_memory_usage.gp [new file with mode: 0644]
shell-completion/bash/pulseaudio [new file with mode: 0644]
shell-completion/zsh/_pulseaudio [new file with mode: 0644]
src/.gitignore [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/daemon/.gitignore [new file with mode: 0644]
src/daemon/Makefile [new symlink]
src/daemon/caps.c [new file with mode: 0644]
src/daemon/caps.h [new file with mode: 0644]
src/daemon/cmdline.c [new file with mode: 0644]
src/daemon/cmdline.h [new file with mode: 0644]
src/daemon/cpulimit.c [new file with mode: 0644]
src/daemon/cpulimit.h [new file with mode: 0644]
src/daemon/daemon-conf.c [new file with mode: 0644]
src/daemon/daemon-conf.h [new file with mode: 0644]
src/daemon/daemon.conf.in [new file with mode: 0644]
src/daemon/default.pa.in [new file with mode: 0755]
src/daemon/dumpmodules.c [new file with mode: 0644]
src/daemon/dumpmodules.h [new file with mode: 0644]
src/daemon/esdcompat.in [new file with mode: 0755]
src/daemon/ltdl-bind-now.c [new file with mode: 0644]
src/daemon/ltdl-bind-now.h [new file with mode: 0644]
src/daemon/main.c [new file with mode: 0644]
src/daemon/pulseaudio-system.conf [new file with mode: 0644]
src/daemon/pulseaudio.desktop.in [new file with mode: 0644]
src/daemon/server-lookup.c [new file with mode: 0644]
src/daemon/server-lookup.h [new file with mode: 0644]
src/daemon/start-pulseaudio-x11.in [new file with mode: 0755]
src/daemon/system.pa.in [new file with mode: 0755]
src/daemon/systemd/user/pulseaudio.service.in [new file with mode: 0644]
src/daemon/systemd/user/pulseaudio.socket [new file with mode: 0644]
src/depmod.py [new file with mode: 0755]
src/map-file [new file with mode: 0644]
src/modules/Makefile [new file with mode: 0644]
src/modules/alsa/alsa-mixer.c [new file with mode: 0644]
src/modules/alsa/alsa-mixer.h [new file with mode: 0644]
src/modules/alsa/alsa-sink.c [new file with mode: 0644]
src/modules/alsa/alsa-sink.h [new file with mode: 0644]
src/modules/alsa/alsa-source.c [new file with mode: 0644]
src/modules/alsa/alsa-source.h [new file with mode: 0644]
src/modules/alsa/alsa-ucm.c [new file with mode: 0644]
src/modules/alsa/alsa-ucm.h [new file with mode: 0644]
src/modules/alsa/alsa-util.c [new file with mode: 0644]
src/modules/alsa/alsa-util.h [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-aux.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-dock-mic.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-fm.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-front-mic.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-headphone-mic.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-headset-mic.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-internal-mic-always.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-internal-mic.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-linein.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-mic-line.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-mic.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-mic.conf.common [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-rear-mic.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-tvtuner.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input-video.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-input.conf.common [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-output-headphones-2.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-output-headphones.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-output-lineout.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-output-mono.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-output-speaker-always.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-output-speaker.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-output.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/analog-output.conf.common [new file with mode: 0644]
src/modules/alsa/mixer/paths/hdmi-output-0.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/hdmi-output-1.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/hdmi-output-2.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/hdmi-output-3.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/hdmi-output-4.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/hdmi-output-5.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/hdmi-output-6.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/hdmi-output-7.conf [new file with mode: 0644]
src/modules/alsa/mixer/paths/iec958-stereo-output.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/default.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/force-speaker-and-int-mic.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/force-speaker.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/kinect-audio.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/maudio-fasttrack-pro.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/native-instruments-korecontroller.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio10.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio2.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio6.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/native-instruments-traktorkontrol-s4.conf [new file with mode: 0644]
src/modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf [new file with mode: 0644]
src/modules/alsa/mixer/samples/ATI IXP--Realtek ALC655 rev 0 [new file with mode: 0644]
src/modules/alsa/mixer/samples/Brooktree Bt878--Bt87x [new file with mode: 0644]
src/modules/alsa/mixer/samples/Ensoniq AudioPCI--Cirrus Logic CS4297A rev 3 [new file with mode: 0644]
src/modules/alsa/mixer/samples/HDA ATI HDMI--ATI R6xx HDMI [new file with mode: 0644]
src/modules/alsa/mixer/samples/HDA Intel--Analog Devices AD1981 [new file with mode: 0644]
src/modules/alsa/mixer/samples/HDA Intel--Realtek ALC889A [new file with mode: 0644]
src/modules/alsa/mixer/samples/Intel 82801CA-ICH3--Analog Devices AD1881A [new file with mode: 0644]
src/modules/alsa/mixer/samples/Logitech USB Speaker--USB Mixer [new file with mode: 0644]
src/modules/alsa/mixer/samples/USB Audio--USB Mixer [new file with mode: 0644]
src/modules/alsa/mixer/samples/USB Device 0x46d:0x9a4--USB Mixer [new file with mode: 0644]
src/modules/alsa/mixer/samples/VIA 8237--Analog Devices AD1888 [new file with mode: 0644]
src/modules/alsa/mixer/samples/VIA 8237--C-Media Electronics CMI9761A+ [new file with mode: 0644]
src/modules/alsa/module-alsa-card.c [new file with mode: 0644]
src/modules/alsa/module-alsa-sink.c [new file with mode: 0644]
src/modules/alsa/module-alsa-source.c [new file with mode: 0644]
src/modules/bluetooth/a2dp-codecs.h [new file with mode: 0644]
src/modules/bluetooth/backend-native.c [new file with mode: 0644]
src/modules/bluetooth/backend-ofono.c [new file with mode: 0644]
src/modules/bluetooth/bluez4-util.c [new file with mode: 0644]
src/modules/bluetooth/bluez4-util.h [new file with mode: 0644]
src/modules/bluetooth/bluez5-util.c [new file with mode: 0644]
src/modules/bluetooth/bluez5-util.h [new file with mode: 0644]
src/modules/bluetooth/module-bluetooth-discover.c [new file with mode: 0644]
src/modules/bluetooth/module-bluetooth-policy.c [new file with mode: 0644]
src/modules/bluetooth/module-bluez4-device.c [new file with mode: 0644]
src/modules/bluetooth/module-bluez4-discover.c [new file with mode: 0644]
src/modules/bluetooth/module-bluez5-device.c [new file with mode: 0644]
src/modules/bluetooth/module-bluez5-discover.c [new file with mode: 0644]
src/modules/bluetooth/rtp.h [new file with mode: 0644]
src/modules/dbus/iface-card-profile.c [new file with mode: 0644]
src/modules/dbus/iface-card-profile.h [new file with mode: 0644]
src/modules/dbus/iface-card.c [new file with mode: 0644]
src/modules/dbus/iface-card.h [new file with mode: 0644]
src/modules/dbus/iface-client.c [new file with mode: 0644]
src/modules/dbus/iface-client.h [new file with mode: 0644]
src/modules/dbus/iface-core.c [new file with mode: 0644]
src/modules/dbus/iface-core.h [new file with mode: 0644]
src/modules/dbus/iface-device-port.c [new file with mode: 0644]
src/modules/dbus/iface-device-port.h [new file with mode: 0644]
src/modules/dbus/iface-device.c [new file with mode: 0644]
src/modules/dbus/iface-device.h [new file with mode: 0644]
src/modules/dbus/iface-memstats.c [new file with mode: 0644]
src/modules/dbus/iface-memstats.h [new file with mode: 0644]
src/modules/dbus/iface-module.c [new file with mode: 0644]
src/modules/dbus/iface-module.h [new file with mode: 0644]
src/modules/dbus/iface-sample.c [new file with mode: 0644]
src/modules/dbus/iface-sample.h [new file with mode: 0644]
src/modules/dbus/iface-stream.c [new file with mode: 0644]
src/modules/dbus/iface-stream.h [new file with mode: 0644]
src/modules/dbus/module-dbus-protocol.c [new file with mode: 0644]
src/modules/echo-cancel/adrian-aec.c [new file with mode: 0644]
src/modules/echo-cancel/adrian-aec.h [new file with mode: 0644]
src/modules/echo-cancel/adrian-aec.orc [new file with mode: 0644]
src/modules/echo-cancel/adrian-license.txt [new file with mode: 0644]
src/modules/echo-cancel/adrian.c [new file with mode: 0644]
src/modules/echo-cancel/adrian.h [new file with mode: 0644]
src/modules/echo-cancel/echo-cancel.h [new file with mode: 0644]
src/modules/echo-cancel/module-echo-cancel.c [new file with mode: 0644]
src/modules/echo-cancel/null.c [new file with mode: 0644]
src/modules/echo-cancel/speex.c [new file with mode: 0644]
src/modules/echo-cancel/webrtc.cc [new file with mode: 0644]
src/modules/gconf/gconf-helper.c [new file with mode: 0644]
src/modules/gconf/module-gconf.c [new file with mode: 0644]
src/modules/jack/module-jack-sink.c [new file with mode: 0644]
src/modules/jack/module-jack-source.c [new file with mode: 0644]
src/modules/jack/module-jackdbus-detect.c [new file with mode: 0644]
src/modules/ladspa.h [new file with mode: 0644]
src/modules/macosx/module-bonjour-publish.c [new file with mode: 0644]
src/modules/macosx/module-coreaudio-detect.c [new file with mode: 0644]
src/modules/macosx/module-coreaudio-device.c [new file with mode: 0644]
src/modules/module-allow-passthrough.c [new file with mode: 0644]
src/modules/module-always-sink.c [new file with mode: 0644]
src/modules/module-augment-properties.c [new file with mode: 0644]
src/modules/module-card-restore.c [new file with mode: 0644]
src/modules/module-cli.c [new file with mode: 0644]
src/modules/module-combine-sink.c [new file with mode: 0644]
src/modules/module-combine.c [new file with mode: 0644]
src/modules/module-console-kit.c [new file with mode: 0644]
src/modules/module-default-device-restore.c [new file with mode: 0644]
src/modules/module-defs.h.m4 [new file with mode: 0644]
src/modules/module-detect.c [new file with mode: 0644]
src/modules/module-device-manager.c [new file with mode: 0644]
src/modules/module-device-restore.c [new file with mode: 0644]
src/modules/module-equalizer-sink.c [new file with mode: 0644]
src/modules/module-esound-compat-spawnfd.c [new file with mode: 0644]
src/modules/module-esound-compat-spawnpid.c [new file with mode: 0644]
src/modules/module-esound-sink.c [new file with mode: 0644]
src/modules/module-filter-apply.c [new file with mode: 0644]
src/modules/module-filter-heuristics.c [new file with mode: 0644]
src/modules/module-hal-detect-compat.c [new file with mode: 0644]
src/modules/module-intended-roles.c [new file with mode: 0644]
src/modules/module-ladspa-sink.c [new file with mode: 0644]
src/modules/module-lirc.c [new file with mode: 0644]
src/modules/module-loopback.c [new file with mode: 0644]
src/modules/module-match.c [new file with mode: 0644]
src/modules/module-mmkbd-evdev.c [new file with mode: 0644]
src/modules/module-native-protocol-fd.c [new file with mode: 0644]
src/modules/module-null-sink.c [new file with mode: 0644]
src/modules/module-null-source.c [new file with mode: 0644]
src/modules/module-pipe-sink.c [new file with mode: 0644]
src/modules/module-pipe-source.c [new file with mode: 0644]
src/modules/module-position-event-sounds.c [new file with mode: 0644]
src/modules/module-protocol-stub.c [new file with mode: 0644]
src/modules/module-remap-sink.c [new file with mode: 0644]
src/modules/module-remap-source.c [new file with mode: 0644]
src/modules/module-rescue-streams.c [new file with mode: 0644]
src/modules/module-role-cork.c [new file with mode: 0644]
src/modules/module-role-ducking.c [new file with mode: 0644]
src/modules/module-rygel-media-server.c [new file with mode: 0644]
src/modules/module-sine-source.c [new file with mode: 0644]
src/modules/module-sine.c [new file with mode: 0644]
src/modules/module-solaris.c [new file with mode: 0644]
src/modules/module-stream-restore.c [new file with mode: 0644]
src/modules/module-suspend-on-idle.c [new file with mode: 0644]
src/modules/module-switch-on-connect.c [new file with mode: 0644]
src/modules/module-switch-on-port-available.c [new file with mode: 0644]
src/modules/module-systemd-login.c [new file with mode: 0644]
src/modules/module-tunnel-sink-new.c [new file with mode: 0644]
src/modules/module-tunnel-source-new.c [new file with mode: 0644]
src/modules/module-tunnel.c [new file with mode: 0644]
src/modules/module-udev-detect.c [new file with mode: 0644]
src/modules/module-virtual-sink.c [new file with mode: 0644]
src/modules/module-virtual-source.c [new file with mode: 0644]
src/modules/module-virtual-surround-sink.c [new file with mode: 0644]
src/modules/module-volume-restore.c [new file with mode: 0644]
src/modules/module-waveout.c [new file with mode: 0644]
src/modules/module-zeroconf-discover.c [new file with mode: 0644]
src/modules/module-zeroconf-publish.c [new file with mode: 0644]
src/modules/oss/module-oss.c [new file with mode: 0644]
src/modules/oss/oss-util.c [new file with mode: 0644]
src/modules/oss/oss-util.h [new file with mode: 0644]
src/modules/raop/module-raop-discover.c [new file with mode: 0644]
src/modules/raop/module-raop-sink.c [new file with mode: 0644]
src/modules/raop/raop-client.c [new file with mode: 0644]
src/modules/raop/raop-client.h [new file with mode: 0644]
src/modules/raop/raop-crypto.c [new file with mode: 0644]
src/modules/raop/raop-crypto.h [new file with mode: 0644]
src/modules/raop/raop-packet-buffer.c [new file with mode: 0644]
src/modules/raop/raop-packet-buffer.h [new file with mode: 0644]
src/modules/raop/raop-sink.c [new file with mode: 0644]
src/modules/raop/raop-sink.h [new file with mode: 0644]
src/modules/raop/raop-util.c [new file with mode: 0644]
src/modules/raop/raop-util.h [new file with mode: 0644]
src/modules/reserve-monitor.c [new file with mode: 0644]
src/modules/reserve-monitor.h [new file with mode: 0644]
src/modules/reserve-wrap.c [new file with mode: 0644]
src/modules/reserve-wrap.h [new file with mode: 0644]
src/modules/reserve.c [new file with mode: 0644]
src/modules/reserve.h [new file with mode: 0644]
src/modules/rtp/headerlist.c [new file with mode: 0644]
src/modules/rtp/headerlist.h [new file with mode: 0644]
src/modules/rtp/module-rtp-recv.c [new file with mode: 0644]
src/modules/rtp/module-rtp-send.c [new file with mode: 0644]
src/modules/rtp/rfc2327.txt [new file with mode: 0644]
src/modules/rtp/rfc2974.txt [new file with mode: 0644]
src/modules/rtp/rfc3550.txt [new file with mode: 0644]
src/modules/rtp/rfc3551.txt [new file with mode: 0644]
src/modules/rtp/rtp.c [new file with mode: 0644]
src/modules/rtp/rtp.h [new file with mode: 0644]
src/modules/rtp/rtsp_client.c [new file with mode: 0644]
src/modules/rtp/rtsp_client.h [new file with mode: 0644]
src/modules/rtp/sap.c [new file with mode: 0644]
src/modules/rtp/sap.h [new file with mode: 0644]
src/modules/rtp/sdp.c [new file with mode: 0644]
src/modules/rtp/sdp.h [new file with mode: 0644]
src/modules/stream-interaction.c [new file with mode: 0644]
src/modules/stream-interaction.h [new file with mode: 0644]
src/modules/udev-util.c [new file with mode: 0644]
src/modules/udev-util.h [new file with mode: 0644]
src/modules/x11/module-x11-bell.c [new file with mode: 0644]
src/modules/x11/module-x11-cork-request.c [new file with mode: 0644]
src/modules/x11/module-x11-publish.c [new file with mode: 0644]
src/modules/x11/module-x11-xsmp.c [new file with mode: 0644]
src/pulse/.gitignore [new file with mode: 0644]
src/pulse/Makefile [new symlink]
src/pulse/cdecl.h [new file with mode: 0644]
src/pulse/channelmap.c [new file with mode: 0644]
src/pulse/channelmap.h [new file with mode: 0644]
src/pulse/client-conf-x11.c [new file with mode: 0644]
src/pulse/client-conf-x11.h [new file with mode: 0644]
src/pulse/client-conf.c [new file with mode: 0644]
src/pulse/client-conf.h [new file with mode: 0644]
src/pulse/client.conf.in [new file with mode: 0644]
src/pulse/context.c [new file with mode: 0644]
src/pulse/context.h [new file with mode: 0644]
src/pulse/def.h [new file with mode: 0644]
src/pulse/direction.c [new file with mode: 0644]
src/pulse/direction.h [new file with mode: 0644]
src/pulse/error.c [new file with mode: 0644]
src/pulse/error.h [new file with mode: 0644]
src/pulse/ext-device-manager.c [new file with mode: 0644]
src/pulse/ext-device-manager.h [new file with mode: 0644]
src/pulse/ext-device-restore.c [new file with mode: 0644]
src/pulse/ext-device-restore.h [new file with mode: 0644]
src/pulse/ext-stream-restore.c [new file with mode: 0644]
src/pulse/ext-stream-restore.h [new file with mode: 0644]
src/pulse/fork-detect.c [new file with mode: 0644]
src/pulse/fork-detect.h [new file with mode: 0644]
src/pulse/format.c [new file with mode: 0644]
src/pulse/format.h [new file with mode: 0644]
src/pulse/gccmacro.h [new file with mode: 0644]
src/pulse/glib-mainloop.c [new file with mode: 0644]
src/pulse/glib-mainloop.h [new file with mode: 0644]
src/pulse/internal.h [new file with mode: 0644]
src/pulse/introspect.c [new file with mode: 0644]
src/pulse/introspect.h [new file with mode: 0644]
src/pulse/json.c [new file with mode: 0644]
src/pulse/json.h [new file with mode: 0644]
src/pulse/mainloop-api.c [new file with mode: 0644]
src/pulse/mainloop-api.h [new file with mode: 0644]
src/pulse/mainloop-signal.c [new file with mode: 0644]
src/pulse/mainloop-signal.h [new file with mode: 0644]
src/pulse/mainloop.c [new file with mode: 0644]
src/pulse/mainloop.h [new file with mode: 0644]
src/pulse/operation.c [new file with mode: 0644]
src/pulse/operation.h [new file with mode: 0644]
src/pulse/proplist.c [new file with mode: 0644]
src/pulse/proplist.h [new file with mode: 0644]
src/pulse/pulseaudio.h [new file with mode: 0644]
src/pulse/rtclock.c [new file with mode: 0644]
src/pulse/rtclock.h [new file with mode: 0644]
src/pulse/sample.c [new file with mode: 0644]
src/pulse/sample.h [new file with mode: 0644]
src/pulse/scache.c [new file with mode: 0644]
src/pulse/scache.h [new file with mode: 0644]
src/pulse/simple.c [new file with mode: 0644]
src/pulse/simple.h [new file with mode: 0644]
src/pulse/stream.c [new file with mode: 0644]
src/pulse/stream.h [new file with mode: 0644]
src/pulse/subscribe.c [new file with mode: 0644]
src/pulse/subscribe.h [new file with mode: 0644]
src/pulse/thread-mainloop.c [new file with mode: 0644]
src/pulse/thread-mainloop.h [new file with mode: 0644]
src/pulse/timeval.c [new file with mode: 0644]
src/pulse/timeval.h [new file with mode: 0644]
src/pulse/utf8.c [new file with mode: 0644]
src/pulse/utf8.h [new file with mode: 0644]
src/pulse/util.c [new file with mode: 0644]
src/pulse/util.h [new file with mode: 0644]
src/pulse/version.h.in [new file with mode: 0644]
src/pulse/volume.c [new file with mode: 0644]
src/pulse/volume.h [new file with mode: 0644]
src/pulse/xmalloc.c [new file with mode: 0644]
src/pulse/xmalloc.h [new file with mode: 0644]
src/pulsecore/Makefile [new symlink]
src/pulsecore/arpa-inet.c [new file with mode: 0644]
src/pulsecore/arpa-inet.h [new file with mode: 0644]
src/pulsecore/asyncmsgq.c [new file with mode: 0644]
src/pulsecore/asyncmsgq.h [new file with mode: 0644]
src/pulsecore/asyncq.c [new file with mode: 0644]
src/pulsecore/asyncq.h [new file with mode: 0644]
src/pulsecore/atomic.h [new file with mode: 0644]
src/pulsecore/aupdate.c [new file with mode: 0644]
src/pulsecore/aupdate.h [new file with mode: 0644]
src/pulsecore/auth-cookie.c [new file with mode: 0644]
src/pulsecore/auth-cookie.h [new file with mode: 0644]
src/pulsecore/authkey.c [new file with mode: 0644]
src/pulsecore/authkey.h [new file with mode: 0644]
src/pulsecore/avahi-wrap.c [new file with mode: 0644]
src/pulsecore/avahi-wrap.h [new file with mode: 0644]
src/pulsecore/bitset.c [new file with mode: 0644]
src/pulsecore/bitset.h [new file with mode: 0644]
src/pulsecore/card.c [new file with mode: 0644]
src/pulsecore/card.h [new file with mode: 0644]
src/pulsecore/cli-command.c [new file with mode: 0644]
src/pulsecore/cli-command.h [new file with mode: 0644]
src/pulsecore/cli-text.c [new file with mode: 0644]
src/pulsecore/cli-text.h [new file with mode: 0644]
src/pulsecore/cli.c [new file with mode: 0644]
src/pulsecore/cli.h [new file with mode: 0644]
src/pulsecore/client.c [new file with mode: 0644]
src/pulsecore/client.h [new file with mode: 0644]
src/pulsecore/conf-parser.c [new file with mode: 0644]
src/pulsecore/conf-parser.h [new file with mode: 0644]
src/pulsecore/core-error.c [new file with mode: 0644]
src/pulsecore/core-error.h [new file with mode: 0644]
src/pulsecore/core-format.c [new file with mode: 0644]
src/pulsecore/core-format.h [new file with mode: 0644]
src/pulsecore/core-rtclock.c [new file with mode: 0644]
src/pulsecore/core-rtclock.h [new file with mode: 0644]
src/pulsecore/core-scache.c [new file with mode: 0644]
src/pulsecore/core-scache.h [new file with mode: 0644]
src/pulsecore/core-subscribe.c [new file with mode: 0644]
src/pulsecore/core-subscribe.h [new file with mode: 0644]
src/pulsecore/core-util.c [new file with mode: 0644]
src/pulsecore/core-util.h [new file with mode: 0644]
src/pulsecore/core.c [new file with mode: 0644]
src/pulsecore/core.h [new file with mode: 0644]
src/pulsecore/cpu-arm.c [new file with mode: 0644]
src/pulsecore/cpu-arm.h [new file with mode: 0644]
src/pulsecore/cpu-orc.c [new file with mode: 0644]
src/pulsecore/cpu-orc.h [new file with mode: 0644]
src/pulsecore/cpu-x86.c [new file with mode: 0644]
src/pulsecore/cpu-x86.h [new file with mode: 0644]
src/pulsecore/cpu.c [new file with mode: 0644]
src/pulsecore/cpu.h [new file with mode: 0644]
src/pulsecore/creds.h [new file with mode: 0644]
src/pulsecore/database-gdbm.c [new file with mode: 0644]
src/pulsecore/database-simple.c [new file with mode: 0644]
src/pulsecore/database-tdb.c [new file with mode: 0644]
src/pulsecore/database.h [new file with mode: 0644]
src/pulsecore/dbus-shared.c [new file with mode: 0644]
src/pulsecore/dbus-shared.h [new file with mode: 0644]
src/pulsecore/dbus-util.c [new file with mode: 0644]
src/pulsecore/dbus-util.h [new file with mode: 0644]
src/pulsecore/device-port.c [new file with mode: 0644]
src/pulsecore/device-port.h [new file with mode: 0644]
src/pulsecore/dllmain.c [new file with mode: 0644]
src/pulsecore/dynarray.c [new file with mode: 0644]
src/pulsecore/dynarray.h [new file with mode: 0644]
src/pulsecore/endianmacros.h [new file with mode: 0644]
src/pulsecore/esound.h [new file with mode: 0644]
src/pulsecore/fdsem.c [new file with mode: 0644]
src/pulsecore/fdsem.h [new file with mode: 0644]
src/pulsecore/ffmpeg/avcodec.h [new file with mode: 0644]
src/pulsecore/ffmpeg/dsputil.h [new file with mode: 0644]
src/pulsecore/ffmpeg/resample2.c [new file with mode: 0644]
src/pulsecore/filter/LICENSE.WEBKIT [new file with mode: 0644]
src/pulsecore/filter/biquad.c [new file with mode: 0644]
src/pulsecore/filter/biquad.h [new file with mode: 0644]
src/pulsecore/filter/crossover.c [new file with mode: 0644]
src/pulsecore/filter/crossover.h [new file with mode: 0644]
src/pulsecore/filter/lfe-filter.c [new file with mode: 0644]
src/pulsecore/filter/lfe-filter.h [new file with mode: 0644]
src/pulsecore/flist.c [new file with mode: 0644]
src/pulsecore/flist.h [new file with mode: 0644]
src/pulsecore/g711.c [new file with mode: 0644]
src/pulsecore/g711.h [new file with mode: 0644]
src/pulsecore/hashmap.c [new file with mode: 0644]
src/pulsecore/hashmap.h [new file with mode: 0644]
src/pulsecore/hook-list.c [new file with mode: 0644]
src/pulsecore/hook-list.h [new file with mode: 0644]
src/pulsecore/i18n.c [new file with mode: 0644]
src/pulsecore/i18n.h [new file with mode: 0644]
src/pulsecore/idxset.c [new file with mode: 0644]
src/pulsecore/idxset.h [new file with mode: 0644]
src/pulsecore/iochannel.c [new file with mode: 0644]
src/pulsecore/iochannel.h [new file with mode: 0644]
src/pulsecore/ioline.c [new file with mode: 0644]
src/pulsecore/ioline.h [new file with mode: 0644]
src/pulsecore/ipacl.c [new file with mode: 0644]
src/pulsecore/ipacl.h [new file with mode: 0644]
src/pulsecore/llist.h [new file with mode: 0644]
src/pulsecore/lock-autospawn.c [new file with mode: 0644]
src/pulsecore/lock-autospawn.h [new file with mode: 0644]
src/pulsecore/log.c [new file with mode: 0644]
src/pulsecore/log.h [new file with mode: 0644]
src/pulsecore/ltdl-helper.c [new file with mode: 0644]
src/pulsecore/ltdl-helper.h [new file with mode: 0644]
src/pulsecore/macro.h [new file with mode: 0644]
src/pulsecore/mcalign.c [new file with mode: 0644]
src/pulsecore/mcalign.h [new file with mode: 0644]
src/pulsecore/mem.h [new file with mode: 0644]
src/pulsecore/memblock.c [new file with mode: 0644]
src/pulsecore/memblock.h [new file with mode: 0644]
src/pulsecore/memblockq.c [new file with mode: 0644]
src/pulsecore/memblockq.h [new file with mode: 0644]
src/pulsecore/memchunk.c [new file with mode: 0644]
src/pulsecore/memchunk.h [new file with mode: 0644]
src/pulsecore/memfd-wrappers.h [new file with mode: 0644]
src/pulsecore/memtrap.c [new file with mode: 0644]
src/pulsecore/memtrap.h [new file with mode: 0644]
src/pulsecore/mime-type.c [new file with mode: 0644]
src/pulsecore/mime-type.h [new file with mode: 0644]
src/pulsecore/mix.c [new file with mode: 0644]
src/pulsecore/mix.h [new file with mode: 0644]
src/pulsecore/mix_neon.c [new file with mode: 0644]
src/pulsecore/modargs.c [new file with mode: 0644]
src/pulsecore/modargs.h [new file with mode: 0644]
src/pulsecore/modinfo.c [new file with mode: 0644]
src/pulsecore/modinfo.h [new file with mode: 0644]
src/pulsecore/module.c [new file with mode: 0644]
src/pulsecore/module.h [new file with mode: 0644]
src/pulsecore/msgobject.c [new file with mode: 0644]
src/pulsecore/msgobject.h [new file with mode: 0644]
src/pulsecore/mutex-posix.c [new file with mode: 0644]
src/pulsecore/mutex-win32.c [new file with mode: 0644]
src/pulsecore/mutex.h [new file with mode: 0644]
src/pulsecore/namereg.c [new file with mode: 0644]
src/pulsecore/namereg.h [new file with mode: 0644]
src/pulsecore/native-common.c [new file with mode: 0644]
src/pulsecore/native-common.h [new file with mode: 0644]
src/pulsecore/object.c [new file with mode: 0644]
src/pulsecore/object.h [new file with mode: 0644]
src/pulsecore/once.c [new file with mode: 0644]
src/pulsecore/once.h [new file with mode: 0644]
src/pulsecore/packet.c [new file with mode: 0644]
src/pulsecore/packet.h [new file with mode: 0644]
src/pulsecore/parseaddr.c [new file with mode: 0644]
src/pulsecore/parseaddr.h [new file with mode: 0644]
src/pulsecore/pdispatch.c [new file with mode: 0644]
src/pulsecore/pdispatch.h [new file with mode: 0644]
src/pulsecore/pid.c [new file with mode: 0644]
src/pulsecore/pid.h [new file with mode: 0644]
src/pulsecore/pipe.c [new file with mode: 0644]
src/pulsecore/pipe.h [new file with mode: 0644]
src/pulsecore/play-memblockq.c [new file with mode: 0644]
src/pulsecore/play-memblockq.h [new file with mode: 0644]
src/pulsecore/play-memchunk.c [new file with mode: 0644]
src/pulsecore/play-memchunk.h [new file with mode: 0644]
src/pulsecore/poll-posix.c [new file with mode: 0644]
src/pulsecore/poll-win32.c [new file with mode: 0644]
src/pulsecore/poll.h [new file with mode: 0644]
src/pulsecore/proplist-util.c [new file with mode: 0644]
src/pulsecore/proplist-util.h [new file with mode: 0644]
src/pulsecore/protocol-cli.c [new file with mode: 0644]
src/pulsecore/protocol-cli.h [new file with mode: 0644]
src/pulsecore/protocol-dbus.c [new file with mode: 0644]
src/pulsecore/protocol-dbus.h [new file with mode: 0644]
src/pulsecore/protocol-esound.c [new file with mode: 0644]
src/pulsecore/protocol-esound.h [new file with mode: 0644]
src/pulsecore/protocol-http.c [new file with mode: 0644]
src/pulsecore/protocol-http.h [new file with mode: 0644]
src/pulsecore/protocol-native.c [new file with mode: 0644]
src/pulsecore/protocol-native.h [new file with mode: 0644]
src/pulsecore/protocol-simple.c [new file with mode: 0644]
src/pulsecore/protocol-simple.h [new file with mode: 0644]
src/pulsecore/pstream-util.c [new file with mode: 0644]
src/pulsecore/pstream-util.h [new file with mode: 0644]
src/pulsecore/pstream.c [new file with mode: 0644]
src/pulsecore/pstream.h [new file with mode: 0644]
src/pulsecore/queue.c [new file with mode: 0644]
src/pulsecore/queue.h [new file with mode: 0644]
src/pulsecore/random.c [new file with mode: 0644]
src/pulsecore/random.h [new file with mode: 0644]
src/pulsecore/ratelimit.c [new file with mode: 0644]
src/pulsecore/ratelimit.h [new file with mode: 0644]
src/pulsecore/refcnt.h [new file with mode: 0644]
src/pulsecore/remap.c [new file with mode: 0644]
src/pulsecore/remap.h [new file with mode: 0644]
src/pulsecore/remap_mmx.c [new file with mode: 0644]
src/pulsecore/remap_neon.c [new file with mode: 0644]
src/pulsecore/remap_sse.c [new file with mode: 0644]
src/pulsecore/resampler.c [new file with mode: 0644]
src/pulsecore/resampler.h [new file with mode: 0644]
src/pulsecore/resampler/ffmpeg.c [new file with mode: 0644]
src/pulsecore/resampler/libsamplerate.c [new file with mode: 0644]
src/pulsecore/resampler/peaks.c [new file with mode: 0644]
src/pulsecore/resampler/soxr.c [new file with mode: 0644]
src/pulsecore/resampler/speex.c [new file with mode: 0644]
src/pulsecore/resampler/trivial.c [new file with mode: 0644]
src/pulsecore/rtkit.c [new file with mode: 0644]
src/pulsecore/rtkit.h [new file with mode: 0644]
src/pulsecore/rtpoll.c [new file with mode: 0644]
src/pulsecore/rtpoll.h [new file with mode: 0644]
src/pulsecore/sample-util.c [new file with mode: 0644]
src/pulsecore/sample-util.h [new file with mode: 0644]
src/pulsecore/sconv-s16be.c [new file with mode: 0644]
src/pulsecore/sconv-s16be.h [new file with mode: 0644]
src/pulsecore/sconv-s16le.c [new file with mode: 0644]
src/pulsecore/sconv-s16le.h [new file with mode: 0644]
src/pulsecore/sconv.c [new file with mode: 0644]
src/pulsecore/sconv.h [new file with mode: 0644]
src/pulsecore/sconv_neon.c [new file with mode: 0644]
src/pulsecore/sconv_sse.c [new file with mode: 0644]
src/pulsecore/semaphore-osx.c [new file with mode: 0644]
src/pulsecore/semaphore-posix.c [new file with mode: 0644]
src/pulsecore/semaphore-win32.c [new file with mode: 0644]
src/pulsecore/semaphore.h [new file with mode: 0644]
src/pulsecore/shared.c [new file with mode: 0644]
src/pulsecore/shared.h [new file with mode: 0644]
src/pulsecore/shm.c [new file with mode: 0644]
src/pulsecore/shm.h [new file with mode: 0644]
src/pulsecore/shmasyncq.c [new file with mode: 0644]
src/pulsecore/shmasyncq.h [new file with mode: 0644]
src/pulsecore/sink-input.c [new file with mode: 0644]
src/pulsecore/sink-input.h [new file with mode: 0644]
src/pulsecore/sink.c [new file with mode: 0644]
src/pulsecore/sink.h [new file with mode: 0644]
src/pulsecore/sioman.c [new file with mode: 0644]
src/pulsecore/sioman.h [new file with mode: 0644]
src/pulsecore/sndfile-util.c [new file with mode: 0644]
src/pulsecore/sndfile-util.h [new file with mode: 0644]
src/pulsecore/socket-client.c [new file with mode: 0644]
src/pulsecore/socket-client.h [new file with mode: 0644]
src/pulsecore/socket-server.c [new file with mode: 0644]
src/pulsecore/socket-server.h [new file with mode: 0644]
src/pulsecore/socket-util.c [new file with mode: 0644]
src/pulsecore/socket-util.h [new file with mode: 0644]
src/pulsecore/socket.h [new file with mode: 0644]
src/pulsecore/sound-file-stream.c [new file with mode: 0644]
src/pulsecore/sound-file-stream.h [new file with mode: 0644]
src/pulsecore/sound-file.c [new file with mode: 0644]
src/pulsecore/sound-file.h [new file with mode: 0644]
src/pulsecore/source-output.c [new file with mode: 0644]
src/pulsecore/source-output.h [new file with mode: 0644]
src/pulsecore/source.c [new file with mode: 0644]
src/pulsecore/source.h [new file with mode: 0644]
src/pulsecore/srbchannel.c [new file with mode: 0644]
src/pulsecore/srbchannel.h [new file with mode: 0644]
src/pulsecore/start-child.c [new file with mode: 0644]
src/pulsecore/start-child.h [new file with mode: 0644]
src/pulsecore/strbuf.c [new file with mode: 0644]
src/pulsecore/strbuf.h [new file with mode: 0644]
src/pulsecore/stream-util.c [new file with mode: 0644]
src/pulsecore/stream-util.h [new file with mode: 0644]
src/pulsecore/strlist.c [new file with mode: 0644]
src/pulsecore/strlist.h [new file with mode: 0644]
src/pulsecore/svolume.orc [new file with mode: 0644]
src/pulsecore/svolume_arm.c [new file with mode: 0644]
src/pulsecore/svolume_c.c [new file with mode: 0644]
src/pulsecore/svolume_mmx.c [new file with mode: 0644]
src/pulsecore/svolume_orc.c [new file with mode: 0644]
src/pulsecore/svolume_sse.c [new file with mode: 0644]
src/pulsecore/tagstruct.c [new file with mode: 0644]
src/pulsecore/tagstruct.h [new file with mode: 0644]
src/pulsecore/thread-mq.c [new file with mode: 0644]
src/pulsecore/thread-mq.h [new file with mode: 0644]
src/pulsecore/thread-posix.c [new file with mode: 0644]
src/pulsecore/thread-win32.c [new file with mode: 0644]
src/pulsecore/thread.h [new file with mode: 0644]
src/pulsecore/time-smoother.c [new file with mode: 0644]
src/pulsecore/time-smoother.h [new file with mode: 0644]
src/pulsecore/tokenizer.c [new file with mode: 0644]
src/pulsecore/tokenizer.h [new file with mode: 0644]
src/pulsecore/typedefs.h [new file with mode: 0644]
src/pulsecore/usergroup.c [new file with mode: 0644]
src/pulsecore/usergroup.h [new file with mode: 0644]
src/pulsecore/winerrno.h [new file with mode: 0644]
src/pulsecore/x11prop.c [new file with mode: 0644]
src/pulsecore/x11prop.h [new file with mode: 0644]
src/pulsecore/x11wrap.c [new file with mode: 0644]
src/pulsecore/x11wrap.h [new file with mode: 0644]
src/tests/Makefile [new symlink]
src/tests/alsa-mixer-path-test.c [new file with mode: 0644]
src/tests/alsa-time-test.c [new file with mode: 0644]
src/tests/asyncmsgq-test.c [new file with mode: 0644]
src/tests/asyncq-test.c [new file with mode: 0644]
src/tests/channelmap-test.c [new file with mode: 0644]
src/tests/close-test.c [new file with mode: 0644]
src/tests/connect-stress.c [new file with mode: 0644]
src/tests/core-util-test.c [new file with mode: 0644]
src/tests/cpu-mix-test.c [new file with mode: 0644]
src/tests/cpu-remap-test.c [new file with mode: 0644]
src/tests/cpu-sconv-test.c [new file with mode: 0644]
src/tests/cpu-volume-test.c [new file with mode: 0644]
src/tests/cpulimit-test.c [new file with mode: 0644]
src/tests/extended-test.c [new file with mode: 0644]
src/tests/flist-test.c [new file with mode: 0644]
src/tests/format-test.c [new file with mode: 0644]
src/tests/get-binary-name-test.c [new file with mode: 0644]
src/tests/gtk-test.c [new file with mode: 0644]
src/tests/hook-list-test.c [new file with mode: 0644]
src/tests/interpol-test.c [new file with mode: 0644]
src/tests/ipacl-test.c [new file with mode: 0644]
src/tests/json-test.c [new file with mode: 0644]
src/tests/ladspa-dbus.py [new file with mode: 0644]
src/tests/lfe-filter-test.c [new file with mode: 0644]
src/tests/lo-latency-test.c [new file with mode: 0644]
src/tests/lo-test-util.c [new file with mode: 0644]
src/tests/lo-test-util.h [new file with mode: 0644]
src/tests/lock-autospawn-test.c [new file with mode: 0644]
src/tests/mainloop-test.c [new file with mode: 0644]
src/tests/mcalign-test.c [new file with mode: 0644]
src/tests/memblock-test.c [new file with mode: 0644]
src/tests/memblockq-test.c [new file with mode: 0644]
src/tests/mix-test.c [new file with mode: 0644]
src/tests/mult-s16-test.c [new file with mode: 0644]
src/tests/once-test.c [new file with mode: 0644]
src/tests/pacat-simple.c [new file with mode: 0644]
src/tests/parec-simple.c [new file with mode: 0644]
src/tests/proplist-test.c [new file with mode: 0644]
src/tests/queue-test.c [new file with mode: 0644]
src/tests/remix-test.c [new file with mode: 0644]
src/tests/resampler-test.c [new file with mode: 0644]
src/tests/rtpoll-test.c [new file with mode: 0644]
src/tests/rtstutter.c [new file with mode: 0644]
src/tests/runtime-test-util.h [new file with mode: 0644]
src/tests/sig2str-test.c [new file with mode: 0644]
src/tests/sigbus-test.c [new file with mode: 0644]
src/tests/smoother-test.c [new file with mode: 0644]
src/tests/srbchannel-test.c [new file with mode: 0644]
src/tests/stripnul.c [new file with mode: 0644]
src/tests/strlist-test.c [new file with mode: 0644]
src/tests/sync-playback.c [new file with mode: 0644]
src/tests/test-daemon.sh [new file with mode: 0755]
src/tests/thread-mainloop-test.c [new file with mode: 0644]
src/tests/thread-test.c [new file with mode: 0644]
src/tests/usergroup-test.c [new file with mode: 0644]
src/tests/utf8-test.c [new file with mode: 0644]
src/tests/volume-test.c [new file with mode: 0644]
src/tests/volume-ui.py [new file with mode: 0644]
src/utils/Makefile [new symlink]
src/utils/pacat.c [new file with mode: 0644]
src/utils/pacmd.c [new file with mode: 0644]
src/utils/pactl.c [new file with mode: 0644]
src/utils/padsp.c [new file with mode: 0644]
src/utils/padsp.in [new file with mode: 0644]
src/utils/pasuspender.c [new file with mode: 0644]
src/utils/pax11publish.c [new file with mode: 0644]
src/utils/qpaeq [new file with mode: 0755]
todo [new file with mode: 0644]
vala/libpulse-mainloop-glib.deps [new file with mode: 0644]
vala/libpulse-mainloop-glib.vapi [new file with mode: 0644]
vala/libpulse-simple.deps [new file with mode: 0644]
vala/libpulse-simple.vapi [new file with mode: 0644]
vala/libpulse.deps [new file with mode: 0644]
vala/libpulse.vapi [new file with mode: 0644]

diff --git a/AGPL b/AGPL
new file mode 100644 (file)
index 0000000..dba13ed
--- /dev/null
+++ b/AGPL
@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/GPL b/GPL
new file mode 100644 (file)
index 0000000..b7b5f53
--- /dev/null
+++ b/GPL
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/LGPL b/LGPL
new file mode 100644 (file)
index 0000000..2d2d780
--- /dev/null
+++ b/LGPL
@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James
+  Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..1459b1d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,44 @@
+All PulseAudio source files, except as noted below, are licensed under the GNU
+Lesser General Public License. (see file LGPL for details)
+
+However, the server side has optional GPL dependencies.  These include the
+libsamplerate and gdbm (core libraries), LIRC (lirc module) and FFTW (equalizer
+module), although others may also be included in the future.  If PulseAudio is
+compiled with these optional components, this effectively downgrades the
+license of the server part to GPL (see the file GPL for details), exercising
+section 3 of the LGPL.  In such circumstances, you should treat the client
+library (libpulse) of PulseAudio as being LGPL licensed and the server part
+(libpulsecore) as being GPL licensed.  Since the PulseAudio daemon, tests,
+various utilities/helpers and the modules link to libpulsecore and/or the afore
+mentioned optional GPL dependencies they are of course also GPL licensed also
+in this scenario.
+
+In addition to this, if D-Bus support is enabled, the PulseAudio client library
+(libpulse) MAY need to be licensed under the GPL, depending on the license
+adopted for libdbus. libdbus is licensed under either of the Academic Free
+License 2.1 or GPL 2.0 or above. Which of these applies is your choice, and the
+result affects the licensing of libpulse and thus, potentially, all programs
+that link to libpulse.
+
+Andre Adrian's echo cancellation implementation is licensed under a less
+restrictive license - see src/modules/echo-cancel/adrian-license.txt for
+details.
+
+Some other files pulled into PA source (i.e. reference implementations that are
+considered too small and stable to be considered as an external library) use the
+more permissive MIT license. This include the device reservation DBus protocol
+and realtime kit implementations.
+
+A more permissive BSD-style license is used for LFE filters, see
+src/pulsecore/filter/LICENSE.WEBKIT for details.
+
+Additionally, a more permissive Sun license is used for code that performs
+u-law, A-law and linear PCM conversions.
+
+The qpaeq program (src/utils/qpaeq) is licensed under the GNU Affero General
+Public License (version 3, or any later version at your discretion). See the
+file AGPL for details.
+
+While we attempt to provide a summary here, it is the ultimate responsibility of
+the packager to ensure the components they use in their build of PulseAudio
+meets their license requirements.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..1f460a9
--- /dev/null
@@ -0,0 +1,122 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+ACLOCAL_AMFLAGS = -I m4
+
+EXTRA_DIST = \
+       AGPL \
+       bootstrap.sh \
+       coverity/model.c \
+       git-version-gen \
+       LICENSE \
+       pulseaudio.supp \
+       GPL \
+       LGPL \
+       doxygen/Makefile.am \
+       doxygen/Makefile.in \
+       doxygen/doxygen.conf.in \
+       PROTOCOL \
+       README \
+       scripts/benchmark_memory_usage.sh \
+       scripts/plot_memory_usage.gp \
+       scripts/benchmarks/README \
+       todo \
+       .gitignore \
+       doxygen/.gitignore \
+       m4/.gitignore \
+       man/.gitignore \
+       po/.gitignore \
+       scripts/benchmarks/.gitignore \
+       src/.gitignore \
+       src/daemon/.gitignore \
+       src/pulse/.gitignore
+
+SUBDIRS = src doxygen man po
+
+MAINTAINERCLEANFILES =
+noinst_DATA =
+
+vapidir = $(datadir)/vala/vapi
+dist_vapi_DATA = \
+               vala/libpulse.deps vala/libpulse.vapi \
+               vala/libpulse-mainloop-glib.deps vala/libpulse-mainloop-glib.vapi \
+               vala/libpulse-simple.deps vala/libpulse-simple.vapi
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libpulse.pc libpulse-simple.pc
+
+if HAVE_GLIB20
+pkgconfig_DATA += \
+        libpulse-mainloop-glib.pc
+endif
+
+cmakedir = $(libdir)/cmake/PulseAudio
+cmake_DATA = PulseAudioConfig.cmake PulseAudioConfigVersion.cmake
+
+bashcompletiondir=@bashcompletiondir@
+dist_bashcompletion_DATA = shell-completion/bash/pulseaudio
+
+install-bashcompletion-aliases:
+       $(LN_S) -f pulseaudio $(DESTDIR)$(bashcompletiondir)/pactl
+       $(LN_S) -f pulseaudio $(DESTDIR)$(bashcompletiondir)/pacmd
+       $(LN_S) -f pulseaudio $(DESTDIR)$(bashcompletiondir)/pasuspender
+       $(LN_S) -f pulseaudio $(DESTDIR)$(bashcompletiondir)/padsp
+       $(LN_S) -f pulseaudio $(DESTDIR)$(bashcompletiondir)/pacat
+       $(LN_S) -f pulseaudio $(DESTDIR)$(bashcompletiondir)/paplay
+       $(LN_S) -f pulseaudio $(DESTDIR)$(bashcompletiondir)/parec
+       $(LN_S) -f pulseaudio $(DESTDIR)$(bashcompletiondir)/parecord
+
+install-data-hook: install-bashcompletion-aliases
+
+zshcompletiondir=@zshcompletiondir@
+dist_zshcompletion_DATA = shell-completion/zsh/_pulseaudio
+
+homepage: all dist doxygen
+       test -d $$HOME/homepage/private
+       mkdir -p $$HOME/homepage/private/projects/pulseaudio $$HOME/homepage/private/projects/pulseaudio/doxygen
+       cp pulseaudio-@PACKAGE_VERSION@.tar.gz $$HOME/homepage/private/projects/pulseaudio
+       cp pulseaudio-@PACKAGE_VERSION@.tar.gz $$HOME/git.fedora/pulseaudio
+       cp -a doxygen/html/* $$HOME/homepage/private/projects/pulseaudio/doxygen
+
+doxygen:
+       $(MAKE) -C doxygen doxygen
+
+eolspace:
+       find \( -name '*.c' -o -name '*.h' -o -name 'Makefile.am' \) -exec perl -i -pe 's/\s+\n$$/\1\n/;' \{\} \;
+
+untabify:
+       find \( -name '*.c' -o -name '*.h' \) -exec perl -i -pe 's/\t/        /g;' \{\} \;
+
+fedora-snapshot: dist
+       cp $(distdir).tar.gz $$HOME/git.fedora/pulseaudio/$(distdir).tar.gz
+
+dist-hook:
+       echo $(VERSION) > $(distdir)/.tarball-version
+       echo $(VERSION) > $(distdir)/.version
+
+check-daemon:
+       $(MAKE) -C src check-daemon
+
+.PHONY: homepage distcleancheck doxygen
+
+# see git-version-gen
+BUILT_SOURCES = $(top_srcdir)/.version
+$(top_srcdir)/.version:
+       echo $(VERSION) > $@-t && mv $@-t $@
+
+DISTCLEANFILES = \
+        po/.intltool-merge-cache
+
+DISTCHECK_CONFIGURE_FLAGS = --with-udev-rules-dir="$$dc_install_base/lib/udev/rules.d" --with-systemduserunitdir="$$dc_install_base/lib/systemd/user" --with-bash-completion-dir="$$dc_install_base/share/bash-completion/completions"
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..3a7c88f
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,489 @@
+PulseAudio 11.1
+
+A bug fix release.
+
+ * Fix a crash in filter modules related to flat volumes and volume sharing
+ * Fix a crash when the bluetooth adapter reports weird MTU size
+ * Disable bluetooth MTU autodetection by default
+ * Add mixer handling back for hardware that doesn't have any alsa-lib
+   configuration
+ * Prioritize USB devices over built-in sound cards (11.0 was supposed to have
+   this feature, but the implementation turned out to be incomplete)
+
+Contributors
+
+  Arun Raghavan
+  Tanu Kaskinen
+
+
+PulseAudio 11.0
+
+Changes at a glance:
+
+ * Support for newer AirPlay hardware
+ * USB and bluetooth devices preferred over internal sound cards
+ * Bluetooth HSP headset role implemented
+ * Bluetooth HFP audio gateway role implemented (requires oFono)
+ * Bluetooth HSP audio gateway and HFP hands-free unit roles can be enabled
+   simultaneously
+ * Upmixing can now be disabled without bad side effects
+ * Avoid having unavailable sinks or sources as the default
+ * Option to avoid resampling more often
+ * Option to automatically switch bluetooth profile to HSP more often
+ * Better latency control in module-loopback
+ * Changed module argument names in module-ladspa-sink and
+   module-virtual-surround-sink
+ * Fixed input device handling in module-waveout
+ * Improved bluetooth MTU configuration
+ * GNU Hurd support
+ * Applications can request LADSPA or virtual surround filtering for their
+   streams
+ * Support for 32-bit applications on 64-bit systems in padsp
+
+Detailed change log:
+
+  https://www.freedesktop.org/wiki/Software/PulseAudio/Notes/11.0/
+
+Contributors
+
+  Arun Raghavan
+  ced2c
+  Christian Kellner
+  Colin Leroy
+  Corentin Noël
+  David Mandelberg
+  David Michael
+  Denis Shulyaka
+  Felipe Sateler
+  Georg Chini
+  Grzegorz Kolodziejczyk
+  Hajime Fujita
+  Hui Wang
+  Imre Vadász
+  Jungsup Lee
+  Karl Ove Hufthammer
+  KimJeongYeon
+  Luiz Augusto von Dentz
+  Martin Blanchard
+  Matthias Wabersich
+  Mihai Moldovan
+  Milo Casagrande
+  Moo
+  Moritz Bruder
+  Muhammet Kara
+  muzena
+  Pali Rohár
+  Paul Seyfert
+  Peter Meerwald-Stadler
+  Philip Chimento
+  Piotr Drąg
+  Rafael Fontenelle
+  Renjith Thomas
+  Stephen Paul Weber
+  Takashi Sakamoto
+  Tanu Kaskinen
+  Ted Ying
+  Vadim Troshchinskiy
+  Wim Taymans
+  Yuri Chornoivan
+
+
+PulseAudio 10.0
+
+Changes at a glance:
+
+ * Automatically switch Bluetooth profile when using VoIP applications
+ * New module for prioritizing passthrough streams (module-allow-passthrough)
+ * Fix hotplugging support for USB surround sound cards
+ * Separate volumes for Bluetooth A2DP and HSP profiles
+ * memfd-based shared memory mechanism enabled by default
+ * Removed module-xenpv-sink
+ * Dropped dependency to json-c
+ * When using systemd to start PulseAudio, pulseaudio.socket is always started
+   first
+ * Compatibility with OpenSSL 1.1.0
+ * Clarified qpaeq license
+
+Detailed change log:
+
+  https://www.freedesktop.org/wiki/Software/PulseAudio/Notes/10.0/
+
+Contributors
+
+  Ahmed S. Darwish
+  Aidan Gauland
+  Alexander E. Patrakov
+  Anton Lundin
+  Arun Raghavan
+  Balázs Úr
+  Chris Billington
+  Cédric Valmary
+  Deepak Srivastava
+  Dušan Kazik
+  Felipe Sateler
+  Fran Dieguez
+  Georg Chini
+  Guillaume Desmottes
+  Jan Alexander Steffens
+  John Paul Adrian Glaubitz
+  Juha Kuikka
+  Juho Hämäläinen
+  KimJeongYeon
+  Marcin Lewandowski
+  Mario Blättermann
+  Moo
+  Nils Schneider
+  Pali Rohár
+  Peter Meerwald-Stadler
+  Philip Withnall
+  Pierre Ossman
+  Piotr Drąg
+  Rikard Söderström
+  Romain Naour
+  Sylvain Baubeau
+  Tanu Kaskinen
+  Ulrich Eckhardt
+  Viktar Vaŭčkievič
+
+
+PulseAudio 9.0
+
+Changes at a glance:
+
+ * Automatic routing improvements
+ * Beamforming and various other new features in the WebRTC echo canceller
+ * Various improvements in module-role-cork and module-role-ducking
+ * LFE remixing disabled by default
+ * memfd-backed shared memory transport
+ * Support for sample rates up to 384 kHz
+ * webrtc-audio-processing dependency minimum version bumped to 0.2
+ * Changed the C standard from C99 to C11.
+
+Detailed change log:
+
+  http://www.freedesktop.org/wiki/Software/PulseAudio/Notes/9.0
+
+Contributors
+
+  Ahmed S. Darwish
+  Alexander E. Patrakov
+  Arun Raghavan
+  Barun Kumar Singh
+  David Henningsson
+  Deepak Srivastava
+  Gabor Kelemen
+  Georg Chini
+  Jeremy Huddleston Sequoia
+  Jonathan Perkin
+  Juho Hämäläinen
+  Jungsup Lee
+  Kamil Rytarowski
+  Marcin Lewandowski
+  Milo Casagrande
+  Muhammet Kara
+  Nazar Mokrynskyi
+  Peter Meerwald
+  Piotr Drąg
+  Sachin Kumar Chauhan
+  Sangchul Lee
+  Tanu Kaskinen
+  YunQiang Su
+
+
+PulseAudio 8.0
+
+Changes at a glance:
+
+ * Automatic routing more likely to change profile
+ * OS X and NetBSD support improvements
+ * Systemd journal logging for clients
+ * New LFE balance programming interface
+ * Module-dbus-protocol improvements
+ * More flexible configuration file handling
+ * pulsecore-8.0.so moved to a private directory
+ * New script for measuring memory consumption
+ * Various bug fixes and small improvements
+
+Detailed change log:
+
+  http://www.freedesktop.org/wiki/Software/PulseAudio/Notes/8.0
+
+Contributors
+
+  Ahmed S. Darwish
+  Alexander E. Patrakov
+  Andrey Semashev
+  Arun Raghavan
+  David Henningsson
+  Deepak Srivastava
+  Felipe Sateler
+  Georg Chini
+  Jason Gerecke
+  John Horan
+  Juho Hämäläinen
+  Julien Isorce
+  Kamil Rytarowski
+  KimJeongYeon
+  Kiran Krishnappa
+  Lev Melnikovsky
+  Manish Sogi
+  Mihai Moldovan
+  Mingye Wang
+  Nazar Mokrynskyi
+  Peter Mattern
+  Peter Meerwald
+  Pierre Ossman
+  Ross Burton
+  Shawn Walker
+  Takashi Iwai
+  Tanu Kaskinen
+  Thomas Petazzoni
+  Yuri Chornoivan
+  Zbigniew Kempczyński
+  jungsup lee
+
+
+PulseAudio 7.0
+
+Changes at a glance:
+
+  * LFE channel synthesis with low-pass filtering
+  * New libsoxr based resamplers
+  * Socket activation support for TCP
+  * The "srbchannel" IPC mechanism enabled by default
+  * More flexible jack detection support when using UCM
+  * Exiting due to SIGTERM isn't considered a failure
+  * Better support for Creative SoundBlaster Omni Surround 5.1
+
+Detailed change log:
+
+  http://www.freedesktop.org/wiki/Software/PulseAudio/Notes/7.0
+
+Contributors:
+
+  Ahmed S. Darwish
+  Alexander E. Patrakov
+  Andrey Semashev
+  Andrey Zholos
+  Arun Raghavan
+  Cédric Valmary
+  David Henningsson
+  David Herrmann
+  Deepak Srivastava
+  Dušan Kazik
+  Felipe Sateler
+  Hui Wang
+  Jaska Uimonen
+  Juho Hämäläinen
+  Manish Sogi
+  Michael Cree
+  Milo Casagrande
+  Muhammet Kara
+  Nazar Mokrynskyi
+  Paul Menzel
+  Peter Meerwald
+  Pierre Ossman
+  Piotr Drąg
+  René J. V. Bertin
+  Sagar Nageshmurthy
+  Shawn Walker
+  Sukesh Adiga
+  Takashi Sakamoto
+  Tanu Kaskinen
+  Ville Skyttä
+  Wim Taymans
+  Yuri Chornoivan
+  Zavadovsky Yan
+
+
+PulseAudio 6.0
+
+Changes at a glance:
+
+  * Bluez 5 HSP (headset profile) support, both with and without oFono
+  * SystemD socket activation support
+  * Better support for multichannel and 2.1 profiles
+  * Protocol optimisations
+  * Remap optimisations
+  * Lots of other enhancements, bug fixes, and documenation and i18n updates
+
+Detailed change log:
+
+  http://www.freedesktop.org/wiki/Software/PulseAudio/Notes/6.0
+
+Contributors:
+
+  Alexander Couzens
+  Alexander E. Patrakov
+  Alexander Kurtz
+  Alexander Potashev
+  Andrey Semashev
+  Arun Raghavan
+  Bradley Broom
+  Cheng-Chia Tseng
+  Colin Guthrie
+  David Henningsson
+  Dimitris Spingos
+  Dušan Kazik
+  Evan Nemerson
+  Felipe Sateler
+  Georg Chini
+  Hemanth Meenakshisundaram
+  Hui Wang
+  James Bunton
+  Jason Newton
+  Javier Jardón
+  Joe Marcus Clarke
+  Josef Andersson
+  João Paulo Rechi Vita
+  Juho Hämäläinen
+  kozdincer
+  Luiz Augusto von Dentz
+  Lukas Peleska
+  Lukasz Marek
+  Mauro Guerrera
+  Michał Górny
+  Niels Ole Salscheider
+  Pali Rohár
+  Parin Porecha
+  Pete Beardmore
+  Peter Meerwald
+  Peter Ujfalusi
+  Pierre Ossman
+  Piotr Drąg
+  poljar (Damir Jelić)
+  Rafael Ferreira
+  Rex Dieter
+  Ricardo Salveti de Araujo
+  Sajeesh Sidharthan
+  Sjoerd Simons
+  Tanu Kaskinen
+  Wim Taymans
+  Yuri Chornoivan
+
+
+PulseAudio 5.0
+
+Changes at a glance:
+
+  * BlueZ 5 support (A2DP only)
+  * Reimplementation of the tunnel modules
+  * Native log target support for systemd-journal
+  * Resampler refactoring
+  * --monitor-stream option for parecord and parec
+  * "latency_msec" argument for module-rtp-recv
+  * "inhibit_auto_suspend" argument for module-rtp-send
+  * "auto" argument for module-tunnel-sink and module-tunnel-source
+  * Removed module-bluetooth-proximity
+  * Jack detection for line out
+  * Laptop internal surround speaker volume support
+  * Improved float->s16 and s16->float sample conversion for ARM NEON
+  * "Available" flag for card profiles
+  * Removed module-dbus-protocol from the default configuration
+  * Lots of other enhancements, bug fixes, and documenation and i18n updates
+
+Detailed change log:
+
+  http://www.freedesktop.org/wiki/Software/PulseAudio/Notes/5.0
+
+Contributors:
+
+  Alexander Couzens
+  Alexander E. Patrakov
+  Alexander Potashev
+  Anton Lundin
+  Arun Raghavan
+  Ben Brewer
+  Cheng-Chia Tseng
+  Colin Guthrie
+  Damir Jelić
+  Daniel Albers
+  David Henningsson
+  Dominique Leuenberger
+  Héctor Martín
+  Ismo Puustinen
+  Jan Alexander Steffens
+  Javier Jardón
+  João Paulo Rechi Vita
+  Juho Hämäläinen
+  Kenneth Perry
+  Kerrick Staley
+  Kiran Krishnappa
+  Koop Mast
+  Lars-Peter Clausen
+  Laurentiu Nicola
+  Luiz Augusto von Dentz
+  Magnus Ekhall
+  Mikel Astiz
+  Mitchell Fang
+  Nikolay Amiantov
+  Parin Porecha
+  Peter Meerwald
+  Pierre Ossman
+  Piotr Drąg
+  Rafael Ferreira
+  Ryan Lortie
+  Scott Reeves
+  Shuai Fan
+  Stefan Sperling
+  Tanu Kaskinen
+  Wim Taymans
+  Yuri Chornoivan
+  victory
+
+
+PulseAudio 4.0
+
+Changes at a glance:
+
+  * Better handling of low latency requests
+  * Optimisations while mixing (generic, ARM NEON)
+  * Default resampler is now speex-float-1 (lower CPU usage)
+  * Major Bluetooth refactoring for better reliability and easier maintenance
+  * Fixes for graceful hand-off to/from JACK
+  * New module to apply ducking based on stream roles
+  * Echo canceller infrastructure fixes
+  * Bash and zsh completion for command line tools
+  * Solaris and OS X fixes
+  * Lots of other enhancements, bug fixes, and documenation and i18n updates
+
+Detailed change log:
+
+  http://www.freedesktop.org/wiki/Software/PulseAudio/Notes/4.0
+
+Contributors:
+
+  Akihiro Tsukada
+  Albert Zeyer
+  Arun Raghavan
+  Christoph Gysin
+  David Henningsson
+  Flavio Ceolin
+  Jarkko Suontausta
+  Javier Jardón
+  João Paulo Rechi Vita
+  Jyri Sarha
+  Luiz Augusto von Dentz
+  Maarten Lankhorst
+  Marc-Antoine Perennou
+  Martin Pitt
+  Matěj Laitl
+  Mikel Astiz
+  Paul Meng
+  Peter Meerwald
+  Peter Nelson
+  Piotr Drąg
+  poljar (Damir Jelić)
+  Robin H. Johnson
+  Stefan Huber
+  Ștefan Săftescu
+  Tanu Kaskinen
+  Vadim Troshchinskiy
+  Wang Xingchao
+  Yuri Chornoivan
+
+----
+
+Change logs for previous versions can be seen at:
+
+  http://www.freedesktop.org/wiki/Software/PulseAudio/Notes
diff --git a/PROTOCOL b/PROTOCOL
new file mode 100644 (file)
index 0000000..546998b
--- /dev/null
+++ b/PROTOCOL
@@ -0,0 +1,427 @@
+### v8, implemented by >= 0.8
+
+First version supported.
+
+### v9, implemented by >= 0.9.0
+
+Reply for PA_COMMAND_CREATE_PLAYBACK_STREAM,
+PA_COMMAND_CREATE_RECORD_STREAM now returns buffer_attrs that are used:
+
+Four new fields in reply of PA_COMMAND_CREATE_PLAYBACK_STREAM:
+
+   maxlength
+   tlength
+   prebuf
+   minreq
+
+Two new fields in reply of PA_COMMAND_CREATE_RECORD_STREAM:
+
+   maxlength
+   fragsize
+
+### v10, implemented by >= 0.9.5
+
+New opcodes:
+
+ PA_COMMAND_MOVE_SINK_INPUT
+ PA_COMMAND_MOVE_SOURCE_OUTPUT
+
+SHM data transfer support
+
+### v11, implemented by >= 0.9.7
+
+Reply to PA_COMMAND_GET_SINK_INPUT_INFO, PA_COMMAND_GET_SINK_INPUT_INFO_LIST gets new field at the end:
+
+ mute
+
+New opcodes:
+
+ PA_COMMAND_SET_SINK_INPUT_MUTE
+ PA_COMMAND_SUSPEND_SINK
+ PA_COMMAND_SUSPEND_SOURCE
+
+### v12, implemented by >= 0.9.8
+
+S32LE, S32BE is now known as sample spec.
+
+Gained six new bool fields for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM request at the end:
+
+ no_remap_channels
+ no_remix_channels
+ fix_format
+ fix_rate
+ fix_channels
+ no_move
+ variable_rate
+
+Reply to these opcodes now includes:
+
+ sample_spec
+ channel_map
+ device_index
+ device_name
+ suspended
+
+New opcodes for changing buffer attrs:
+
+ PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR
+ PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR
+
+New opcodes for changing sampling rate:
+
+ PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE
+ PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE
+
+New opcodes for notifications:
+
+ PA_COMMAND_PLAYBACK_STREAM_SUSPENDED
+ PA_COMMAND_CAPTURE_STREAM_SUSPENDED
+ PA_COMMAND_PLAYBACK_STREAM_MOVED
+ PA_COMMAND_CAPTURE_STREAM_MOVED
+
+### v13, implemented by >= 0.9.11
+
+New fields for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM request at the end:
+
+ peak_detect (bool)
+ adjust_latency  (bool)
+
+Replace field "name" for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM at the end:
+
+ proplist
+
+Replace field "name" for PA_COMMAND_SET_CLIENT_NAME request at the end:
+
+ proplist
+
+On response of PA_COMMAND_SET_CLIENT_NAME:
+
+ client_index
+
+New proplist field for sink, source, sink input, source output introspection opcodes and at the end:
+
+ proplist
+
+New opcodes for proplist modifications
+
+  PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST
+  PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST
+  PA_COMMAND_UPDATE_CLIENT_PROPLIST
+  PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST
+  PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST
+  PA_COMMAND_REMOVE_CLIENT_PROPLIST
+
+New field for PA_COMMAND_PLAY_SAMPLE:
+
+  proplist
+
+New field for PA_COMMAND_PLAY_SAMPLE response:
+
+  idx
+
+New field for PA_COMMAND_CREATE_PLAYBACK_STREAM at the end:
+
+  start_muted
+
+Buffer attributes for PA_COMMAND_CREATE_PLAYBACK_STREAM and
+PA_COMMAND_CREATE_RECORD_STREAM may now be 0 for default values.
+
+New field for PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
+PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR at the end:
+
+  adjust_latency (bool)
+
+new message:
+
+  PA_COMMAND_STARTED
+
+### v14, implemented by >= 0.9.12
+
+new message:
+
+  PA_COMMAND_EXTENSION
+
+PA_COMMAND_CREATE_PLAYBACK_STREAM:
+
+  bool volume_set at the end
+
+PA_COMMAND_CREATE_RECORD_STREAM, PA_COMMAND_CREATE_PLAYBACK_STREAM:
+
+  bool early_requests at the end
+
+New field for PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
+PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR at the end:
+
+  early_requests (bool)
+
+### v15, implemented by >= 0.9.15
+
+PA_COMMAND_CREATE_PLAYBACK_STREAM
+
+  bool muted at the end
+
+PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM:
+
+  bool dont_inhibit_auto_suspend at the end
+
+PA_COMMAND_GET_MODULE_INFO_LIST
+
+  remove bool auto_unload
+  add proplist at the end
+
+new messages:
+
+  PA_COMMAND_GET_CARD_INFO
+  PA_COMMAND_GET_CARD_INFO_LIST
+  PA_COMMAND_SET_CARD_PROFILE
+
+  PA_COMMAND_CLIENT_EVENT
+  PA_COMMAND_PLAYBACK_STREAM_EVENT
+  PA_COMMAND_RECORD_STREAM_EVENT
+
+  PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED
+  PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED
+
+### v16, implemented by >= 0.9.15
+
+new messages:
+
+  PA_COMMAND_SET_SINK_PORT
+  PA_COMMAND_SET_SOURCE_PORT
+
+## v17, implemented by >= 0.9.20
+
+new flag at end of CREATE_PLAYBACK_STREAM:
+
+    bool relative_volume
+
+## v18, implemented by >= 0.9.22
+
+new flag at end of CREATE_PLAYBACK_STREAM:
+
+    bool passthrough
+
+## v19, implemented by >= 0.9.22
+
+New flag at the end of sink input and source output introspection data:
+
+    bool corked
+
+## v20, implemented by >= 1.0
+
+Two new flags at the end of sink input introspection data:
+
+    bool has_volume
+    bool volume_writable
+
+## v21, implemented by >= 1.0
+
+Changes for format negotiation in the extended API.
+
+New fields PA_COMMAND_CREATE_PLAYBACK_STREAM:
+
+    uint8_t n_formats
+    format_info format1
+    ...
+    format_info formatn
+
+One new field in reply from PA_COMMAND_CREATE_PLAYBACK_STREAM:
+
+    format_info format
+
+New fields in reply from PA_COMMAND_GET_SINK_INFO (and thus
+PA_COMMAND_GET_SINK_INFO_LIST)
+
+    uint8_t n_formats
+    format_info format1
+    ...
+    format_info formatn
+
+One new field in reply from PA_COMMAND_GET_SINK_INPUT_INFO (and thus
+PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
+
+    format_info format
+
+## v22, implemented by >= 1.0
+
+New fields PA_COMMAND_CREATE_RECORD_STREAM:
+
+    uint8_t n_formats
+    format_info format1
+    ...
+    format_info formatn
+    volume
+    bool muted
+    bool volume_set
+    bool muted_set
+    bool relative_volume
+    bool passthrough
+
+One new field in reply from PA_COMMAND_CREATE_RECORD_STREAM:
+
+    format_info format
+
+New fields in reply from PA_COMMAND_GET_SOURCE_INFO (and thus
+PA_COMMAND_GET_SOURCE_INFO_LIST)
+
+    uint8_t n_formats
+    format_info format1
+    ...
+    format_info formatn
+
+Five new fields in reply from PA_COMMAND_GET_SOURCE_OUTPUT_INFO (and thus
+PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
+
+    volume
+    bool mute
+    bool has_volume
+    bool volume_writable
+    format_info format
+
+## v23, implemented by >= 1.0
+
+New field in PA_COMMAND_UNDERFLOW:
+
+    int64_t index
+
+## v24, implemented by >= 2.0
+
+New field in all commands that send/receive port introspection data
+(PA_COMMAND_GET_(SOURCE|SINK)_INFO,
+PA_COMMAND_GET_(SOURCE|SINK)_INFO_LIST):
+
+    uint32_t available
+
+The field is added once for every port.
+
+## v25, implemented by >= 2.0
+
+When port availability changes, send a subscription event for the
+owning card.
+
+## v26, implemented by >= 2.0
+
+In reply from PA_COMMAND_GET_CARD_INFO (and thus
+PA_COMMAND_GET_CARD_INFO_LIST), the following is added:
+
+    uint32_t n_ports
+
+...followed by n_ports extended port entries, which look like this:
+
+    string name
+    string description
+    uint32_t priority
+    uint32_t available
+    uint8_t direction
+    proplist
+    uint32_t n_profiles
+    string profile_name_1
+    ...
+    string profile_name_n
+
+Profile names must match earlier sent profile names for the same card.
+
+## v27, implemented by >= 3.0
+
+New opcodes:
+    PA_COMMAND_SET_PORT_LATENCY_OFFSET
+
+New field in the card commands that send/receive port introspection data
+PA_COMMAND_GET_CARD_INFO(_LIST)):
+
+    int64_t latency_offset
+
+The field is added once for every port.
+
+## v28, implemented by >= 4.0
+
+New value for encoding format type in format_info
+PA_COMMAND_CREATE_(PLAYBACK|RECORDING)_STREAM and its reply,
+In reply from PA_COMMAND_GET_(SOURCE|SOURCE_OUTPUT|SINK|SINK_INPUT)_INFO[_LIST],
+SUBCOMMAND_SAVE_FORMATS, in reply from SUBCOMMAND_READ_FORMATS[_ALL]
+
+    (uint8_t ) PA_ENCODING_MPEG2_AAC_IEC61937 := 6
+
+## v29, implemented by >= 5.0
+#
+New field in all commands that send/receive profile introspection data
+(PA_COMMAND_GET_CARD_INFO)
+
+    uint32 available
+
+The field is added once for every profile.
+
+## v30, implemented by >= 6.0
+#
+A new protocol mechanism supported: Two ringbuffers in shared memory.
+Pulseaudio fdsem (wrappers around event file descriptors) are used for
+signalling new data.
+The protocol has a new SHM flag telling whether a SHM memblock is writable
+by both sides.
+
+PA_COMMAND_ENABLE_SRBCHANNEL
+First sent from server to client, tells the client to start listening on
+the additional SHM ringbuffer channel.
+This command also has ancillary data (two eventfds attached to it).
+Must be directly followed by a memblock which is the ringbuffer memory.
+When memblock is received by the client, it acks by sending
+PA_COMMAND_ENABLE_SRBCHANNEL back (without ancillary or memblock data).
+
+PA_COMMAND_DISABLE_SRBCHANNEL
+Tells the client to stop listening on the additional SHM ringbuffer channel.
+Acked by client by sending PA_COMMAND_DISABLE_SRBCHANNEL back.
+
+## v31, implemented by >= 9.0
+
+Memfd shared-memory support is now added to PulseAudio as an opt-in feature.
+Add 'enable-memfd=yes' to daemon's configuration to use memfds, instead of
+POSIX shm, by default.
+
+Memfd is a simple memory sharing mechanism, added by the systemd/kdbus
+developers, to share pages between processes in an anonymous, no global
+registry needed, no mount-point required, relatively secure, manner.
+
+PulseAudio memfd support builds the necessary (but not yet sufficient)
+groundwork for a better integration with per-app containers (e.g. xdg-app)
+
+For further details on memfds in general, please check:
+
+  https://dvdhrm.wordpress.com/2014/06/10/memfd_create2/
+  Archived at: http://www.webcitation.org/6gnHTy9Kr
+
+Moreover, for both client and server, the second most-significant bit of
+the version tag is now used to flag memfd SHM support. On the way forward,
+the two most-significant _bytes_ of the version tag are now also reserved
+for flags.
+
+PA_COMMAND_REGISTER_MEMFD_SHMID
+New command that can be sent both ways, from client to server and vice versa.
+This is needed to transfer a memfd pool's blocks without passing its fd every
+time, thus minimizing overhead and avoiding fd leaks.
+
+The registration command above sends a packet with the pool's memfd fd as
+ancillary data. Such packet has an ID that uniquely identifies the pool's
+memfd memory area. Upon arrival, the other end (client or server) creates a
+permanent ID<->memfd mapping.
+
+By doing so, there's need to reference the pool's memfd file descriptor any
+further -- just its ID. Thus both endpoints can then quickly and safely
+close their memfd file descriptors.
+
+## v32, implemented by >= 10.0
+
+Enable memfd transport by default.
+
+This protocol bump was only created to mark 9.0 clients. Although they
+support memfd transport, such older clients has an iochannel bug that would
+break memfd audio if they're run in 32-bit mode over a 64-bit kernel. Thus
+influence these buggy libraries to use POSIX shared memory, by signalling
+memfd support only to 10.0+ clients.
+
+Check commit 451d1d676237c81 for further details.
+
+#### If you just changed the protocol, read this
+## module-tunnel depends on the sink/source/sink-input/source-input protocol
+## internals, so if you changed these, you might have broken module-tunnel.
+## Don't forget to test module-tunnel-{source,sink} when pushing protocol
+## changes.
diff --git a/PulseAudioConfig.cmake.in b/PulseAudioConfig.cmake.in
new file mode 100644 (file)
index 0000000..191eb67
--- /dev/null
@@ -0,0 +1,12 @@
+set(PULSEAUDIO_FOUND TRUE)
+
+set(PULSEAUDIO_VERSION_MAJOR @PA_MAJOR@)
+set(PULSEAUDIO_VERSION_MINOR @PA_MINOR@)
+set(PULSEAUDIO_VERSION @PA_MAJOR@.@PA_MINOR@)
+set(PULSEAUDIO_VERSION_STRING "@PA_MAJOR@.@PA_MINOR@")
+
+find_path(PULSEAUDIO_INCLUDE_DIR pulse/pulseaudio.h HINTS "@PA_INCDIR@")
+find_library(PULSEAUDIO_LIBRARY NAMES pulse libpulse HINTS "@PA_LIBDIR@")
+ifelse(@HAVE_GLIB20@, 1, dnl
+find_library(PULSEAUDIO_MAINLOOP_LIBRARY NAMES pulse-mainloop-glib libpulse-mainloop-glib HINTS "@PA_LIBDIR@")
+)dnl
diff --git a/PulseAudioConfigVersion.cmake.in b/PulseAudioConfigVersion.cmake.in
new file mode 100644 (file)
index 0000000..e2947de
--- /dev/null
@@ -0,0 +1,11 @@
+set(PACKAGE_VERSION @PA_MAJOR@.@PA_MINOR@)
+
+if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" )
+  set(PACKAGE_VERSION_COMPATIBLE FALSE)
+else("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" )
+  set(PACKAGE_VERSION_COMPATIBLE TRUE)
+  if( "${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}")
+    set(PACKAGE_VERSION_EXACT TRUE)
+  endif( "${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}")
+endif("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" )
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..470de40
--- /dev/null
+++ b/README
@@ -0,0 +1,43 @@
+PULSEAUDIO SOUND SERVER
+
+WEB SITE:
+       http://pulseaudio.org/
+
+GIT:
+       git://anongit.freedesktop.org/pulseaudio/pulseaudio
+
+GITWEB/CGIT:
+       http://cgit.freedesktop.org/pulseaudio/pulseaudio/
+
+MAILING LIST:
+       http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
+
+GIT COMMITS MAILING LIST:
+       http://lists.freedesktop.org/mailman/listinfo/pulseaudio-commits
+
+TRAC/BUGZILLA TICKET CHANGES MAILING LIST:
+       http://lists.freedesktop.org/mailman/listinfo/pulseaudio-bugs
+
+IRC:
+       #pulseaudio on irc.freenode.org
+
+FRESHMEAT:
+       http://freshmeat.net/projects/pulseaudio/
+
+OHLOH:
+       http://www.ohloh.net/projects/4038
+
+AUTHORS:
+       Several
+
+HACKING:
+       In order to run pulseaudio from the build dir __OPTIMIZE__ should be
+       disabled (look at src/pulsecore/core-util.h::pa_run_from_build_tree()),
+       this can be done by passing "CFLAGS=-O0" to the configure script:
+         ./autogen.sh
+         CFLAGS="-ggdb3 -O0" LDFLAGS="-ggdb3" ./configure
+         make
+         ./src/pulseaudio -n -F src/default.pa -p $(pwd)/src/
+
+SPELLING:
+        PulseAudio
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..f89eace
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+# Only there to make jhbuild happy
+
+NOCONFIGURE=1 ./bootstrap.sh
+
+exec ./configure  "$@"
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755 (executable)
index 0000000..57494fc
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then
+    cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \
+    chmod +x .git/hooks/pre-commit && \
+    echo "Activated pre-commit hook."
+fi
+
+if [ -f .tarball-version ]; then
+    echo "Marking tarball version as modified."
+    echo -n `cat .tarball-version | sed 's/-rebootstrapped$//'`-rebootstrapped >.tarball-version
+fi
+
+# We check for this here, because if pkg-config is not found in the
+# system, it's likely that the pkg.m4 macro file is also not present,
+# which will make PKG_PROG_PKG_CONFIG be undefined and the generated
+# configure file faulty.
+if ! pkg-config --version &>/dev/null; then
+    echo "pkg-config is required to bootstrap this program"
+    DIE=1
+fi
+
+# Other necessary programs
+intltoolize --version >/dev/null || DIE=1
+test "$DIE" = 1 && exit 1
+
+autopoint --force
+AUTOPOINT='intltoolize --automake --copy' autoreconf --force --install --verbose
+
+if test "x$NOCONFIGURE" = "x"; then
+    CFLAGS="$CFLAGS -g -O0" ./configure --sysconfdir=/etc --localstatedir=/var --enable-force-preopen "$@" && \
+        make clean
+fi
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..77b5ff5
--- /dev/null
@@ -0,0 +1,1700 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+# This file is part of PulseAudio.
+#
+# Copyright 2004-2008 Lennart Poettering
+# Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+#
+# PulseAudio is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+AC_PREREQ(2.63)
+
+AC_INIT([pulseaudio],[m4_esyscmd(./git-version-gen .tarball-version)],[pulseaudio-discuss (at) lists (dot) freedesktop (dot) org],[pulseaudio],[http://pulseaudio.org/])
+AC_CONFIG_SRCDIR([src/daemon/main.c])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_AUX_DIR([build-aux])
+AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability subdir-objects silent-rules color-tests dist-xz tar-ustar])
+
+AS_IF([! test -n "$VERSION"], [
+   AC_MSG_ERROR([git-version-gen failed])
+])
+
+m4_define(pa_major, `echo $VERSION | cut -d. -f1 | cut -d- -f1`)
+m4_define(pa_minor, `echo $VERSION | cut -d. -f2 | cut -d- -f1`)
+
+AC_SUBST(PA_MAJOR, pa_major)
+AC_SUBST(PA_MINOR, pa_minor)
+AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
+
+AC_SUBST(PA_API_VERSION, 12)
+AC_SUBST(PA_PROTOCOL_VERSION, 32)
+
+# The stable ABI for client applications, for the version info x:y:z
+# always will hold y=z
+AC_SUBST(LIBPULSE_VERSION_INFO, [20:2:20])
+
+# A simplified, synchronous, ABI-stable interface for client
+# applications, for the version info x:y:z always will hold y=z
+AC_SUBST(LIBPULSE_SIMPLE_VERSION_INFO, [1:1:1])
+
+# The ABI-stable GLib adapter for client applications, for the version
+# info x:y:z always will hold y=z
+AC_SUBST(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO, [0:5:0])
+
+AC_CANONICAL_HOST
+AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.])
+
+AC_CHECK_PROG([STOW], [stow], [yes], [no])
+
+AS_IF([test "x$STOW" = "xyes" && test -d /usr/local/stow], [
+    AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***])
+    ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}"
+])
+
+AM_SILENT_RULES([yes])
+
+
+#### Checks for programs. ####
+
+# mkdir -p
+
+AC_PROG_MKDIR_P
+
+# ln -s
+
+AC_PROG_LN_S
+
+# CC
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+# Only required if you want the WebRTC canceller -- no runtime dep on
+# libstdc++ otherwise
+AC_PROG_CXX
+AX_CXX_COMPILE_STDCXX_11([ext],[optional])
+AC_PROG_GCC_TRADITIONAL
+AC_USE_SYSTEM_EXTENSIONS
+
+# M4
+
+AC_CHECK_PROGS([M4], gm4 m4, no)
+AS_IF([test "x$M4" = "xno"], AC_MSG_ERROR([m4 missing]))
+
+# pkg-config
+
+PKG_PROG_PKG_CONFIG
+
+# gettext
+
+if test "x$enable_nls" != "xno"; then
+IT_PROG_INTLTOOL([0.35.0])
+
+AM_GNU_GETTEXT_VERSION([0.18.1])
+AM_GNU_GETTEXT([external])
+
+GETTEXT_PACKAGE=pulseaudio
+AC_SUBST([GETTEXT_PACKAGE])
+AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE],["$GETTEXT_PACKAGE"],[Gettext package])
+else
+# workaround till an intltool m4 bug is fixed upstream
+# (https://bugs.launchpad.net/intltool/+bug/904647)
+USE_NLS=no
+AC_SUBST(USE_NLS)
+fi
+
+
+#### Determine host OS ####
+
+# if the host has the possibility of sys/capability.h for dropping privileges
+# used to determine if we should error out if it is not found
+host_has_caps=0
+
+os_is_linux=0
+os_is_win32=0
+os_is_darwin=0
+
+AC_MSG_CHECKING([host operating system])
+case "$host_os" in
+    linux*)
+        AC_MSG_RESULT([linux])
+        host_has_caps=1
+        os_is_linux=1
+    ;;
+    freebsd*)
+        AC_MSG_RESULT([freebsd])
+        host_has_caps=1
+    ;;
+    netbsd*)
+        AC_MSG_RESULT([netbsd])
+    ;;
+    darwin*)
+        AC_MSG_RESULT([darwin])
+        os_is_darwin=1
+        AC_DEFINE([OS_IS_DARWIN], 1, [Build target is Darwin.])
+    ;;
+    mingw*)
+        AC_MSG_RESULT([win32])
+        os_is_win32=1
+        AC_DEFINE([OS_IS_WIN32], 1, [Build target is Windows.])
+    ;;
+    *)
+        AC_MSG_RESULT([unknown])
+    ;;
+esac
+
+AM_CONDITIONAL(OS_IS_DARWIN, test "x$os_is_darwin" = "x1")
+AM_CONDITIONAL(OS_IS_WIN32, test "x$os_is_win32" = "x1")
+AC_SUBST([OS_IS_WIN32], [$os_is_win32])
+
+# Platform specific hacks
+case "$host_os" in
+    darwin* )
+        AC_DEFINE([_DARWIN_C_SOURCE], [200112L], [Needed to get NSIG on Mac OS X])
+    ;;
+    mingw* )
+        AC_DEFINE([WIN32_LEAN_AND_MEAN], 1, [Needed to avoid including unnecessary headers on Windows])
+    ;;
+    solaris* )
+        AC_DEFINE(_XOPEN_SOURCE,       600, [Needed to get declarations for msg_control and msg_controllen on Solaris])
+        AC_DEFINE(__EXTENSIONS__,        1, [Needed to get declarations for msg_control and msg_controllen on Solaris])
+    ;;
+esac
+
+
+#### Compiler flags ####
+
+AX_CHECK_COMPILE_FLAG([-std=gnu11],
+   [],
+   [AC_MSG_ERROR([*** Compiler does not support -std=gnu11])],
+   [-pedantic -Werror])
+
+AX_APPEND_COMPILE_FLAGS(
+    [-Wall -W -Wextra -pipe -Wno-long-long -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing -Wwrite-strings -Wno-unused-parameter -ffast-math -fno-common -fdiagnostics-show-option -fdiagnostics-color=auto],
+    [], [-pedantic -Werror])
+
+AS_CASE([" $CFLAGS "], [*" -O0 "*], [], [
+    # Don't append the flag if it already exists.
+    # Only enable fastpath asserts when doing a debug build, e.g. from bootstrap.sh.
+    AX_APPEND_FLAG([-DFASTPATH], [CPPFLAGS])
+
+    # Cannot use AX_APPEND_FLAG here, as it assumes no space inside the added flags.
+    # Cannot append flags with AX_APPEND_FLAG one by one, as this would destroy all fortifications
+    # if CPPFLAGS already contain -D_FORTIFY_SOURCE=2.
+
+    # Warnings to be aware of that appear with -D_FORTIFY_SOURCE=2 but without -U_FORTIFY_SOURCE:
+    # On Fedora 20 with -O0: #warning _FORTIFY_SOURCE requires compiling with optimization (-O) [-Wcpp]
+    # On Gentoo with -O2:    "_FORTIFY_SOURCE" redefined [enabled by default]
+    AS_VAR_APPEND([CPPFLAGS],[" -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2"])
+])
+
+#### Linker flags ####
+
+# Check whether the linker supports the -version-script option.
+# The Make variable $(srcdir) needs to be in the LDFLAGS in that form,
+# so that it is expanded the right way in every subdir.
+AX_CHECK_LINK_FLAG(["-Wl,-version-script=${srcdir}/src/map-file"],
+    [VERSIONING_LDFLAGS='-Wl,-version-script=$(abs_top_srcdir)/src/map-file'])
+AC_SUBST([VERSIONING_LDFLAGS])
+
+# Use immediate (now) bindings; avoids the funky re-call in itself.
+# The -z now syntax is lifted from Sun's linker and works with GNU's too, other linkers might be added later.
+AX_APPEND_LINK_FLAGS([-Wl,-z,now], [IMMEDIATE_LDFLAGS])
+AC_SUBST([IMMEDIATE_LDFLAGS])
+
+# On ELF systems we don't want the libraries to be unloaded since we don't clean them up properly,
+# so we request the nodelete flag to be enabled.
+# On other systems, we don't really know how to do that, but it's welcome if somebody can tell.
+AX_APPEND_LINK_FLAGS([-Wl,-z,nodelete], [NODELETE_LDFLAGS], [-shared])
+AC_SUBST([NODELETE_LDFLAGS])
+
+# Check for the proper way to build libraries that have no undefined symbols
+case $host in
+    # FreeBSD (et al.) does not complete linking for shared objects when pthreads
+    # are requested, as different implementations are present.
+    *-freebsd* | *-openbsd*) ;;
+    *)
+        for possible_flag in "-Wl,--no-undefined" "-Wl,-z,defs"; do
+            AX_CHECK_LINK_FLAG([$possible_flag], [NOUNDEFINED_LDFLAGS="$possible_flag"; break])
+        done
+    ;;
+esac
+AC_SUBST([NOUNDEFINED_LDFLAGS])
+
+
+#### Atomic operations ####
+
+# Native atomic operation support
+AC_ARG_ENABLE([atomic-arm-linux-helpers],
+    AS_HELP_STRING([--disable-atomic-arm-linux-helpers],[use inline asm or libatomic_ops instead]))
+
+AC_ARG_ENABLE([atomic-arm-memory-barrier],
+    AS_HELP_STRING([--enable-atomic-arm-memory-barrier],[only really needed in SMP arm systems]))
+
+if test "x$enable_atomic_arm_memory_barrier" = "xyes"; then
+    AC_DEFINE_UNQUOTED(ATOMIC_ARM_MEMORY_BARRIER_ENABLED, 1, [Enable memory barriers])
+fi
+
+# If everything else fails use libatomic_ops
+need_libatomic_ops=yes
+
+AC_CACHE_CHECK([whether $CC knows __sync_bool_compare_and_swap()],
+    pulseaudio_cv_sync_bool_compare_and_swap, [
+    AC_LINK_IFELSE(
+        [AC_LANG_PROGRAM([], [[int a = 4; __sync_bool_compare_and_swap(&a, 4, 5);]])],
+        [pulseaudio_cv_sync_bool_compare_and_swap=yes],
+        [pulseaudio_cv_sync_bool_compare_and_swap=no])
+    ])
+
+if test "$pulseaudio_cv_sync_bool_compare_and_swap" = "yes" ; then
+    AC_DEFINE([HAVE_ATOMIC_BUILTINS], 1, [Have __sync_bool_compare_and_swap() and friends.])
+    need_libatomic_ops=no
+else
+    # HW specific atomic ops stuff
+    AC_MSG_CHECKING([architecture for native atomic operations])
+    case $host in
+        *-netbsd*)
+            AC_MSG_RESULT([yes])
+            need_libatomic_ops=no
+        ;;
+        arm*)
+            AC_MSG_RESULT([arm])
+            AC_MSG_CHECKING([whether we can use Linux kernel helpers])
+            # The Linux kernel helper functions have been there since 2.6.16. However
+            # compile time checking for kernel version in cross compile environment
+            # (which is usually the case for arm cpu) is tricky (or impossible).
+            if test "x$os_is_linux" = "x1" && test "x$enable_atomic_arm_linux_helpers" != "xno"; then
+                AC_MSG_RESULT([yes])
+                AC_DEFINE_UNQUOTED(ATOMIC_ARM_LINUX_HELPERS, 1, [special arm linux implementation])
+                need_libatomic_ops=no
+            else
+                AC_MSG_RESULT([no])
+                AC_CACHE_CHECK([compiler support for arm inline asm atomic operations],
+                    pulseaudio_cv_support_arm_atomic_ops, [
+                    AC_COMPILE_IFELSE(
+                        [AC_LANG_PROGRAM([], [[
+                            volatile int a=0;
+                            int o=0, n=1, r;
+                            asm volatile ("ldrex    %0, [%1]\n"
+                                          "subs  %0, %0, %2\n"
+                                          "strexeq %0, %3, [%1]\n"
+                                          : "=&r" (r)
+                                          : "r" (&a), "Ir" (o), "r" (n)
+                                          : "cc");
+                            return (a==1 ? 0 : -1);
+                        ]])],
+                        [pulseaudio_cv_support_arm_atomic_ops=yes],
+                        [pulseaudio_cv_support_arm_atomic_ops=no])
+                ])
+                AS_IF([test "$pulseaudio_cv_support_arm_atomic_ops" = "yes"], [
+                    AC_DEFINE([ATOMIC_ARM_INLINE_ASM], 1, [Have ARM atomic instructions.])
+                    need_libatomic_ops=no
+                ])
+            fi
+        ;;
+        *-freebsd*)
+            AC_MSG_RESULT([yes])
+            need_libatomic_ops=no
+        ;;
+        *)
+            AC_MSG_RESULT([unknown])
+        ;;
+    esac
+fi
+
+# If we're on ARM, check for the ARMV6 instructions we need */
+case $host in
+  arm*)
+    AC_CACHE_CHECK([support for required armv6 instructions],
+      pulseaudio_cv_support_armv6,
+      [AC_COMPILE_IFELSE(
+         [AC_LANG_PROGRAM([],
+           [[volatile int a = -60000, b = 0xaaaabbbb, c = 0xccccdddd;
+             asm volatile ("ldr r0, %2 \n"
+                           "ldr r2, %3 \n"
+                           "ldr r3, %4 \n"
+                           "ssat r1, #8, r0 \n"
+                           "str r1, %0 \n"
+                           "pkhbt r1, r3, r2, LSL #8 \n"
+                           "str r1, %1 \n"
+                           : "=m" (a), "=m" (b)
+                           : "m" (a), "m" (b), "m" (c)
+                           : "r0", "r1", "r2", "r3", "cc");
+             return (a == -128 && b == 0xaabbdddd) ? 0 : -1;
+           ]])],
+         [pulseaudio_cv_support_armv6=yes],
+         [pulseaudio_cv_support_armv6=no])
+      ])
+    AS_IF([test "$pulseaudio_cv_support_armv6" = "yes"], [
+        AC_DEFINE([HAVE_ARMV6], 1, [Have ARMv6 instructions.])
+      ])
+  ;;
+  *)
+  ;;
+esac
+
+#### NEON optimisations ####
+AC_ARG_ENABLE([neon-opt],
+    AS_HELP_STRING([--enable-neon-opt], [Enable NEON optimisations on ARM CPUs that support it]))
+
+AS_IF([test "x$enable_neon_opt" != "xno"],
+    [save_CFLAGS="$CFLAGS"; CFLAGS="-mfpu=neon $CFLAGS"
+     AC_COMPILE_IFELSE(
+        [AC_LANG_PROGRAM([[#include <arm_neon.h>]], [])],
+        [
+         HAVE_NEON=1
+         NEON_CFLAGS="-mfpu=neon"
+        ],
+        [
+         HAVE_NEON=0
+         NEON_CFLAGS=
+        ])
+     CFLAGS="$save_CFLAGS"
+    ],
+    [HAVE_NEON=0])
+
+AS_IF([test "x$enable_neon_opt" = "xyes" && test "x$HAVE_NEON" = "x0"],
+      [AC_MSG_ERROR([*** Compiler does not support -mfpu=neon or CFLAGS override -mfpu])])
+
+AC_SUBST(HAVE_NEON)
+AC_SUBST(NEON_CFLAGS)
+AM_CONDITIONAL([HAVE_NEON], [test "x$HAVE_NEON" = x1])
+AS_IF([test "x$HAVE_NEON" = "x1"], AC_DEFINE([HAVE_NEON], 1, [Have NEON support?]))
+
+
+#### libtool stuff ####
+
+LT_PREREQ(2.4)
+LT_INIT([dlopen win32-dll disable-static])
+
+dnl As an extra safety device, check for lt_dladvise_init() which is
+dnl only implemented in libtool 2.x, and refine as we go if we have
+dnl refined requirements.
+dnl
+dnl Check the header files first since the system may have a
+dnl libltdl.so for runtime, but no headers, and we want to bail out as
+dnl soon as possible.
+dnl
+dnl We don't need any special variable for this though, since the user
+dnl can give the proper place to find libltdl through the standard
+dnl variables like LDFLAGS and CPPFLAGS.
+
+AC_CHECK_HEADER([ltdl.h],
+    [AC_CHECK_LIB([ltdl], [lt_dladvise_init], [LIBLTDL=-lltdl], [LIBLTDL=])],
+    [LIBLTDL=])
+
+AS_IF([test "x$LIBLTDL" = "x"],
+    [AC_MSG_ERROR([Unable to find libltdl version 2. Makes sure you have libtool 2.4 or later installed.])])
+AC_SUBST([LIBLTDL])
+
+
+###################################
+#   Basic environment checks      #
+###################################
+
+#### Checks for header files. ####
+
+# ISO
+AC_HEADER_STDC
+
+# POSIX
+AC_CHECK_HEADERS_ONCE([arpa/inet.h glob.h grp.h netdb.h netinet/in.h \
+    netinet/in_systm.h netinet/tcp.h poll.h pwd.h sched.h \
+    sys/mman.h sys/select.h sys/socket.h sys/wait.h \
+    sys/uio.h syslog.h sys/dl.h dlfcn.h linux/sockios.h])
+AC_CHECK_HEADERS([netinet/ip.h], [], [],
+                 [#include <sys/types.h>
+                  #if HAVE_NETINET_IN_H
+                  # include <netinet/in.h>
+                  #endif
+                  #if HAVE_NETINET_IN_SYSTM_H
+                  # include <netinet/in_systm.h>
+                  #endif
+                 ])
+AC_CHECK_HEADERS([sys/resource.h], [HAVE_SYS_RESOURCE_H=1], [HAVE_SYS_RESOURCE_H=0])
+AC_SUBST(HAVE_SYS_RESOURCE_H)
+AC_CHECK_HEADERS([sys/un.h], [HAVE_AF_UNIX=1], [HAVE_AF_UNIX=0])
+AM_CONDITIONAL(HAVE_AF_UNIX, test "x$HAVE_AF_UNIX" = "x1")
+AC_SUBST(HAVE_AF_UNIX)
+
+# Linux
+AC_CHECK_HEADERS([linux/input.h], [HAVE_EVDEV=1], [HAVE_EVDEV=0])
+AM_CONDITIONAL([HAVE_EVDEV], [test "x$HAVE_EVDEV" = "x1"])
+
+AC_CHECK_HEADERS_ONCE([sys/prctl.h])
+
+# Solaris
+AC_CHECK_HEADERS_ONCE([sys/conf.h sys/filio.h])
+
+# Windows
+AC_CHECK_HEADERS_ONCE([windows.h winsock2.h ws2tcpip.h])
+
+# NetBSD
+AC_CHECK_HEADERS_ONCE([sys/atomic.h])
+
+# Other
+AC_CHECK_HEADERS_ONCE([sys/ioctl.h])
+AC_CHECK_HEADERS_ONCE([byteswap.h])
+AC_CHECK_HEADERS_ONCE([sys/syscall.h])
+AC_CHECK_HEADERS_ONCE([sys/eventfd.h])
+AC_CHECK_HEADERS_ONCE([execinfo.h])
+AC_CHECK_HEADERS_ONCE([langinfo.h])
+AC_CHECK_HEADERS_ONCE([regex.h pcreposix.h])
+AC_CHECK_HEADERS_ONCE([locale.h xlocale.h])
+
+AM_CONDITIONAL(HAVE_SYS_EVENTFD_H, test "x$ac_cv_header_sys_eventfd_h" = "xyes")
+
+#### Typdefs, structures, etc. ####
+
+AC_C_CONST
+AC_C_BIGENDIAN
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_CHECK_TYPES(ssize_t, , AC_DEFINE([ssize_t], [signed long], [Define ssize_t if it is not done by the standard libs.]))
+AC_TYPE_OFF_T
+
+AC_TYPE_UID_T
+AC_CHECK_DECLS(environ)
+
+AC_CHECK_SIZEOF(void*)
+
+fast_64bit_operations="no"
+# This check covers x32-ABI
+AC_CHECK_DECL([__x86_64__], [fast_64bit_operations="yes"], [], [])
+if test "x$fast_64bit_operations" = "xno"; then
+    AS_IF([test $ac_cv_sizeof_voidp -ge 8], [fast_64bit_operations="yes"])
+fi
+
+AS_IF([test "x$fast_64bit_operations" = "xyes"], AC_DEFINE([HAVE_FAST_64BIT_OPERATIONS], 1, [Have CPU with fast 64-bit operations?]))
+
+# SIGXCPU
+AX_CHECK_DEFINE([signal.h], [SIGXCPU], [HAVE_SIGXCPU=1], [HAVE_SIGXCPU=0])
+AS_IF([test "x$HAVE_SIGXCPU" = "x1"], AC_DEFINE([HAVE_SIGXCPU], 1, [Have SIGXCPU?]))
+AM_CONDITIONAL(HAVE_SIGXCPU, test "x$HAVE_SIGXCPU" = "x1")
+
+# INADDR_NONE, Solaris lacks this
+AX_CHECK_DEFINE([netinet/in.h], [INADDR_NONE], [],
+    [AX_CHECK_DEFINE([winsock2.h], [INADDR_NONE], [],
+        [AC_DEFINE([INADDR_NONE],  [0xffffffff], [Define INADDR_NONE if not found in <netinet/in.h>])])])
+
+
+# _Bool
+AC_CACHE_CHECK([whether $CC knows _Bool],
+    pulseaudio_cv__Bool,
+    [AC_COMPILE_IFELSE(
+        [AC_LANG_PROGRAM([], [[_Bool b;]])],
+        [pulseaudio_cv__Bool=yes],
+        [pulseaudio_cv__Bool=no])
+    ])
+
+AS_IF([test "$pulseaudio_cv__Bool" = "yes"], AC_DEFINE([HAVE_STD_BOOL], 1, [Have _Bool.]))
+
+
+#### Check for libs ####
+
+# ISO
+AC_SEARCH_LIBS([pow], [m])
+
+# POSIX
+AC_SEARCH_LIBS([sched_setscheduler], [rt])
+AC_SEARCH_LIBS([dlopen], [dl])
+AC_SEARCH_LIBS([shm_open], [rt])
+AC_SEARCH_LIBS([inet_ntop], [nsl])
+AC_SEARCH_LIBS([timer_create], [rt])
+AC_SEARCH_LIBS([pthread_setaffinity_np], [pthread])
+AC_SEARCH_LIBS([pthread_getname_np], [pthread])
+AC_SEARCH_LIBS([pthread_setname_np], [pthread])
+
+# BSD
+AC_SEARCH_LIBS([connect], [socket])
+AC_SEARCH_LIBS([backtrace], [execinfo ubacktrace])
+
+# Darwin/OS X
+if test "x$os_is_darwin" = "x1" ; then
+    AC_MSG_CHECKING([looking for Apple CoreService Framework])
+    AC_CHECK_HEADER([CoreServices/CoreServices.h],
+        [LIBS="$LIBS -framework CoreServices"],
+        [AC_MSG_ERROR([CoreServices.h header file not found])]
+    )
+
+    AC_MSG_RESULT([ok])
+    AC_DEFINE([HAVE_CLOCK_GETTIME], 1, [Using clock_gettime() replacement])
+    HAVE_BONJOUR=1
+fi
+
+AM_CONDITIONAL([HAVE_BONJOUR], [test "x$HAVE_BONJOUR" = x1])
+
+# Windows
+AC_SEARCH_LIBS([regexec], [pcreposix])
+# This magic is needed so we do not needlessly add static libs to the win32  build, disabling its ability to make dlls.
+AC_CHECK_FUNCS([getopt_long], [], [AC_CHECK_LIB([iberty], [getopt_long])])
+
+
+#### Check for functions ####
+
+# ISO
+AC_CHECK_FUNCS_ONCE([lrintf strtof])
+
+# POSIX
+AC_FUNC_FORK
+AC_FUNC_GETGROUPS
+AC_CHECK_FUNCS_ONCE([chmod chown fstat fchown fchmod clock_gettime getaddrinfo getgrgid_r getgrnam_r \
+    getpwnam_r getpwuid_r gettimeofday getuid mlock nanosleep \
+    pipe posix_fadvise posix_madvise posix_memalign setpgid setsid shm_open \
+    sigaction sleep symlink sysconf uname pthread_setaffinity_np pthread_getname_np pthread_setname_np])
+AC_CHECK_FUNCS([mkfifo], [HAVE_MKFIFO=1], [HAVE_MKFIFO=0])
+AC_SUBST(HAVE_MKFIFO)
+AM_CONDITIONAL(HAVE_MKFIFO, test "x$HAVE_MKFIFO" = "x1")
+
+# X/OPEN
+AC_CHECK_FUNCS_ONCE([readlink])
+
+# SUSv2
+AC_CHECK_FUNCS_ONCE([ctime_r usleep])
+
+# SUSv3
+AC_CHECK_FUNCS_ONCE([strerror_r])
+
+# BSD
+AC_CHECK_FUNCS_ONCE([lstat paccept])
+
+# Non-standard
+AC_CHECK_FUNCS_ONCE([setresuid setresgid setreuid setregid seteuid setegid ppoll strsignal sig2str strtod_l pipe2 accept4])
+
+AC_FUNC_ALLOCA
+
+AC_CHECK_FUNCS([regexec], [HAVE_REGEX=1], [HAVE_REGEX=0])
+AM_CONDITIONAL(HAVE_REGEX, [test "x$HAVE_REGEX" = "x1"])
+
+# Large File-Support (LFS)
+AC_SYS_LARGEFILE
+# Check for open64 to know if the current system does have open64() and similar functions
+AC_CHECK_FUNCS_ONCE([open64])
+
+AC_SEARCH_LIBS([dladdr], [dl], [HAVE_DLADDR=1], [HAVE_DLADDR=0])
+AC_DEFINE(HAVE_DLADDR, [1], [Have dladdr?])
+
+###################################
+#      External libraries         #
+###################################
+
+#### [lib]iconv ####
+
+AM_ICONV
+
+#### Linux memfd_create(2) SHM support ####
+
+AC_ARG_ENABLE([memfd],
+    AS_HELP_STRING([--disable-memfd], [Disable Linux memfd shared memory]))
+
+AS_IF([test "x$enable_memfd" != "xno"],
+    AC_CHECK_DECL(SYS_memfd_create, [HAVE_MEMFD=1], [HAVE_MEMFD=0], [#include <sys/syscall.h>]),
+    [HAVE_MEMFD=0])
+
+AS_IF([test "x$enable_memfd" = "xyes" && test "x$HAVE_MEMFD" = "x0"],
+    [AC_MSG_ERROR([*** Your Linux kernel does not support memfd shared memory.
+                  *** Use linux v3.17 or higher for such a feature.])])
+
+AC_SUBST(HAVE_MEMFD)
+AM_CONDITIONAL([HAVE_MEMFD], [test "x$HAVE_MEMFD" = x1])
+AS_IF([test "x$HAVE_MEMFD" = "x1"], AC_DEFINE([HAVE_MEMFD], 1, [Have memfd shared memory.]))
+
+#### X11 (optional) ####
+
+AC_ARG_ENABLE([x11],
+    AS_HELP_STRING([--disable-x11],[Disable optional X11 support]))
+
+AS_IF([test "x$enable_x11" != "xno"],
+    [PKG_CHECK_MODULES(X11, [ x11-xcb xcb >= 1.6 ice sm xtst ], HAVE_X11=1, HAVE_X11=0)],
+    HAVE_X11=0)
+
+AS_IF([test "x$enable_x11" = "xyes" && test "x$HAVE_X11" = "x0"],
+    [AC_MSG_ERROR([*** X11 not found])])
+
+AC_SUBST(HAVE_X11)
+AM_CONDITIONAL([HAVE_X11], [test "x$HAVE_X11" = x1])
+AS_IF([test "x$HAVE_X11" = "x1"], AC_DEFINE([HAVE_X11], 1, [Have X11?]))
+
+#### Capabilities (optional) ####
+
+CAP_LIBS=''
+
+AC_ARG_WITH([caps],
+    AS_HELP_STRING([--without-caps],[Omit support for dropping capabilities.]))
+
+if test "x${with_caps}" != "xno"; then
+    AC_SEARCH_LIBS([cap_init], [cap], [], [])
+
+    # Only give an error on hosts that we know could support capabilities
+    AC_CHECK_HEADERS([sys/capability.h], [], [
+      if test "${host_has_caps}" = "1"; then
+        AC_MSG_ERROR([*** sys/capability.h not found.  Use --without-caps to disable capabilities support.])
+      fi])
+fi
+
+#### Valgrind (optional) ####
+
+AC_CHECK_HEADERS_ONCE([valgrind/memcheck.h])
+
+#### check unit tests ####
+
+AC_ARG_ENABLE([tests],
+    AS_HELP_STRING([--disable-tests],[Disable unit tests]))
+
+AS_IF([test "x$enable_tests" != "xno"],
+    [PKG_CHECK_MODULES(LIBCHECK, [ check >= 0.9.10 ], HAVE_LIBCHECK=1, HAVE_LIBCHECK=0)],
+    HAVE_LIBCHECK=0)
+
+AS_IF([test "x$enable_tests" = "xyes" && test "x$HAVE_LIBCHECK" = "x0"],
+    [AC_MSG_ERROR([*** check library not found or too old])])
+
+AM_CONDITIONAL([HAVE_TESTS], [test "x$HAVE_LIBCHECK" = x1])
+
+#### Sound file ####
+
+PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ])
+
+#### atomic-ops ####
+
+AC_MSG_CHECKING([whether we need libatomic_ops])
+if test "x$need_libatomic_ops" = "xyes"; then
+    AC_MSG_RESULT([yes])
+    AC_CHECK_HEADERS([atomic_ops.h],
+        [CFLAGS="$CFLAGS -DAO_REQUIRE_CAS"],
+        [AC_MSG_ERROR([*** libatomic-ops headers not found])])
+
+    # Win32 does not need the lib and breaks horribly if we try to include it
+    AS_IF([test "x$os_is_win32" != "x1"], [LIBS="$LIBS -latomic_ops"])
+else
+    AC_MSG_RESULT([no])
+fi
+
+#### Libsamplerate support (optional) ####
+
+AC_ARG_ENABLE([samplerate],
+    AS_HELP_STRING([--enable-samplerate],[Enable optional libsamplerate support (DEPRECATED)]))
+
+AS_IF([test "x$enable_samplerate" = "xyes"],
+    [PKG_CHECK_MODULES(LIBSAMPLERATE, [ samplerate >= 0.1.0 ], HAVE_LIBSAMPLERATE=1, HAVE_LIBSAMPLERATE=0)],
+    HAVE_LIBSAMPLERATE=0)
+
+AS_IF([test "x$enable_samplerate" = "xyes" && test "x$HAVE_LIBSAMPLERATE" = "x0"],
+    [AC_MSG_ERROR([*** Libsamplerate not found])])
+
+AM_CONDITIONAL([HAVE_LIBSAMPLERATE], [test "x$HAVE_LIBSAMPLERATE" = x1])
+AS_IF([test "x$HAVE_LIBSAMPLERATE" = "x1"], AC_DEFINE([HAVE_LIBSAMPLERATE], 1, [Have libsamplerate?]))
+
+AS_IF([test "x$HAVE_LIBSAMPLERATE" = "x1"], AC_MSG_WARN([Support for libsamplerate is DEPRECATED]))
+
+#### Database support ####
+
+AC_ARG_WITH([database],
+    AS_HELP_STRING([--with-database=auto|tdb|gdbm|simple],[Choose database backend.]),[],[with_database=auto])
+
+
+AS_IF([test "x$with_database" = "xauto" -o "x$with_database" = "xtdb"],
+    [PKG_CHECK_MODULES(TDB, [ tdb ], HAVE_TDB=1, HAVE_TDB=0)],
+    HAVE_TDB=0)
+AS_IF([test "x$HAVE_TDB" = "x1"], with_database=tdb)
+
+AS_IF([test "x$with_database" = "xtdb" && test "x$HAVE_TDB" = "x0"],
+    [AC_MSG_ERROR([*** tdb not found])])
+
+
+AS_IF([test "x$with_database" = "xauto" -o "x$with_database" = "xgdbm"],
+    [
+        HAVE_GDBM=1
+        AC_CHECK_LIB(gdbm, gdbm_open, [], HAVE_GDBM=0)
+        AC_CHECK_HEADERS(gdbm.h, [], HAVE_GDBM=0)
+    ],
+    HAVE_GDBM=0)
+AS_IF([test "x$HAVE_GDBM" = "x1"],
+    [
+        with_database=gdbm
+        GDBM_CFLAGS=
+        GDBM_LIBS=-lgdbm
+    ])
+
+AS_IF([test "x$with_database" = "xgdbm" && test "x$HAVE_GDBM" = "x0"],
+    [AC_MSG_ERROR([*** gdbm not found])])
+
+
+AS_IF([test "x$with_database" = "xauto" -o "x$with_database" = "xsimple"],
+    HAVE_SIMPLEDB=1,
+    HAVE_SIMPLEDB=0)
+AS_IF([test "x$HAVE_SIMPLEDB" = "x1"], with_database=simple)
+
+AS_IF([test "x$HAVE_TDB" != x1 -a "x$HAVE_GDBM" != x1 -a "x$HAVE_SIMPLEDB" != x1],
+    AC_MSG_ERROR([*** missing database backend]))
+
+
+AM_CONDITIONAL([HAVE_TDB], [test "x$HAVE_TDB" = x1])
+AS_IF([test "x$HAVE_TDB" = "x1"], AC_DEFINE([HAVE_TDB], 1, [Have tdb?]))
+
+AC_SUBST(GDBM_CFLAGS)
+AC_SUBST(GDBM_LIBS)
+AM_CONDITIONAL([HAVE_GDBM], [test "x$HAVE_GDBM" = x1])
+AS_IF([test "x$HAVE_GDBM" = "x1"], AC_DEFINE([HAVE_GDBM], 1, [Have gdbm?]))
+
+AM_CONDITIONAL([HAVE_SIMPLEDB], [test "x$HAVE_SIMPLEDB" = x1])
+AS_IF([test "x$HAVE_SIMPLEDB" = "x1"], AC_DEFINE([HAVE_SIMPLEDB], 1, [Have simple?]))
+
+#### OSS support (optional) ####
+
+AC_ARG_ENABLE([oss-output],
+    AS_HELP_STRING([--disable-oss-output],[Disable optional OSS output support]))
+
+AC_ARG_ENABLE([oss-wrapper],
+    AS_HELP_STRING([--disable-oss-wrapper],[Disable optional OSS wrapper support]))
+
+AC_ARG_WITH([pulsedsp-location],
+    AS_HELP_STRING([--with-pulsedsp-location],[Specify location where OSS wrapper will be installed]))
+
+AS_IF([test "x$enable_oss_output" != "xno" -o "x$enable_oss_wrapper" != "xno"],
+    [AC_CHECK_HEADERS([sys/soundcard.h], HAVE_OSS=1, HAVE_OSS=0)],
+    HAVE_OSS=0)
+
+AS_IF([test "x$enable_oss_output" = "xyes" -o "x$enable_oss_wrapper" = "xyes" && test "x$HAVE_OSS" = "x0"],
+    [AC_MSG_ERROR([*** OSS support not found])])
+
+AS_IF([test "x$enable_oss_output" != "xno"],
+    [AS_IF([test "x$HAVE_OSS" = "x1"], HAVE_OSS_OUTPUT=1, HAVE_OSS_OUTPUT=0)],
+    HAVE_OSS_OUTPUT=0)
+
+AS_IF([test "x$enable_oss_wrapper" != "xno"],
+    [AS_IF([test "x$HAVE_OSS" = "x1"], HAVE_OSS_WRAPPER=1, HAVE_OSS_WRAPPER=0)],
+    HAVE_OSS_WRAPPER=0)
+
+AS_IF([test "x$with_pulsedsp_location" != "x"],
+    [PULSEDSP_LOCATION="$with_pulsedsp_location"],
+    [PULSEDSP_LOCATION="\$(pkglibdir)"])
+
+AC_SUBST(HAVE_OSS_OUTPUT)
+AC_SUBST(PULSEDSP_LOCATION)
+AM_CONDITIONAL([HAVE_OSS_OUTPUT], [test "x$HAVE_OSS_OUTPUT" = "x1"])
+AM_CONDITIONAL([HAVE_OSS_WRAPPER], [test "x$HAVE_OSS_WRAPPER" = "x1"])
+AS_IF([test "x$HAVE_OSS_OUTPUT" = "x1"], AC_DEFINE([HAVE_OSS_OUTPUT], 1, [Have OSS output?]))
+AS_IF([test "x$HAVE_OSS_WRAPPER" = "x1"], AC_DEFINE([HAVE_OSS_WRAPPER], 1, [Have OSS wrapper (padsp)?]))
+
+# Some platforms like SunOS (Illumos) may ship without SOUND_PCM_* functionality
+if test "x$HAVE_OSS" = "x1"; then
+    AC_CHECK_DECLS([SOUND_PCM_READ_RATE], [], [], [[#include <sys/soundcard.h>]])
+    AC_CHECK_DECLS([SOUND_PCM_READ_CHANNELS], [], [], [[#include <sys/soundcard.h>]])
+    AC_CHECK_DECLS([SOUND_PCM_READ_BITS], [], [], [[#include <sys/soundcard.h>]])
+fi
+
+#### CoreAudio support (optional) ####
+
+AC_ARG_ENABLE([coreaudio-output],
+    AS_HELP_STRING([--disable-coreaudio-output],[Disable optional CoreAudio output support]))
+
+AS_IF([test "x$enable_coreaudio_output" != "xno"],
+    [AC_CHECK_HEADERS([CoreAudio/CoreAudio.h], HAVE_COREAUDIO=1, HAVE_COREAUDIO=0)],
+    HAVE_COREAUDIO=0)
+
+AS_IF([test "x$enable_coreaudio_output" = "xyes" && test "x$HAVE_COREAUDIO" = "x0"],
+    [AC_MSG_ERROR([*** CoreAudio output support not found])])
+
+AC_SUBST(HAVE_COREAUDIO)
+AM_CONDITIONAL([HAVE_COREAUDIO], [test "x$HAVE_COREAUDIO" = "x1" && test "x$enable_coreaudio_output" != "xno"])
+
+AS_IF([test "x$HAVE_COREAUDIO" = "x1"], AC_DEFINE([HAVE_COREAUDIO], 1, [Have CoreAudio?]))
+
+#### ALSA support (optional) ####
+
+AC_ARG_ENABLE([alsa],
+    AS_HELP_STRING([--disable-alsa],[Disable optional ALSA support]))
+
+AS_IF([test "x$enable_alsa" != "xno"],
+    [PKG_CHECK_MODULES(ASOUNDLIB, [ alsa >= 1.0.19 ], HAVE_ALSA=1, HAVE_ALSA=0)],
+    HAVE_ALSA=0)
+
+AS_IF([test "x$enable_alsa" = "xyes" && test "x$HAVE_ALSA" = "x0"],
+    [AC_MSG_ERROR([*** Needed alsa >= 1.0.19 support not found])])
+
+AS_IF([test "x$HAVE_ALSA" = "x1"],
+    [
+        save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $ASOUNDLIB_CFLAGS"
+        AC_CHECK_HEADERS([use-case.h], HAVE_ALSA_UCM=1, HAVE_ALSA_UCM=0)
+        CPPFLAGS="$save_CPPFLAGS"
+    ],
+    HAVE_ALSA_UCM=0)
+
+AC_SUBST(HAVE_ALSA)
+AM_CONDITIONAL([HAVE_ALSA], [test "x$HAVE_ALSA" = x1])
+AS_IF([test "x$HAVE_ALSA" = "x1"], AC_DEFINE([HAVE_ALSA], 1, [Have ALSA?]))
+AS_IF([test "x$HAVE_ALSA_UCM" = "x1"], AC_DEFINE([HAVE_ALSA_UCM], 1, [Have ALSA UCM?]))
+
+#### EsounD support (optional) ####
+
+AC_ARG_ENABLE([esound],
+    AS_HELP_STRING([--disable-esound],[Disable optional EsounD support]))
+AM_CONDITIONAL([HAVE_ESOUND], [test "x$enable_esound" != "xno"])
+AS_IF([test "x$enable_esound" != "xno"], [HAVE_ESOUND=1])
+
+#### Solaris audio support (optional) ####
+
+AC_ARG_ENABLE([solaris],
+    AS_HELP_STRING([--disable-solaris],[Disable optional Solaris audio support]))
+
+AS_IF([test "x$enable_solaris" != "xno"],
+    [AC_CHECK_HEADERS([sys/audio.h], HAVE_SOLARIS=1, HAVE_SOLARIS=0)],
+    HAVE_SOLARIS=0)
+
+AS_IF([test "x$enable_solaris" = "xyes" && test "x$HAVE_SOLARIS" = "x0"],
+    [AC_MSG_ERROR([*** Solaris audio support not found])])
+
+AM_CONDITIONAL([HAVE_SOLARIS], [test "x$HAVE_SOLARIS" = x1])
+AS_IF([test "x$HAVE_SOLARIS" = "x1"], AC_DEFINE([HAVE_SOLARIS], 1, [Have Solaris audio?]))
+
+#### WaveOut audio support (optional) ####
+
+AC_ARG_ENABLE([waveout],
+    AS_HELP_STRING([--disable-waveout],[Disable optional WaveOut audio support]))
+
+AS_IF([test "x$enable_waveout" != "xno"],
+    [AC_CHECK_HEADERS([mmsystem.h], HAVE_WAVEOUT=1, HAVE_WAVEOUT=0, [#include <windows.h>])],
+    HAVE_WAVEOUT=0)
+
+AS_IF([test "x$enable_waveout" = "xyes" && test "x$HAVE_WAVEOUT" = "x0"],
+    [AC_MSG_ERROR([*** WaveOut audio support not found])])
+
+AC_SUBST(HAVE_WAVEOUT)
+AM_CONDITIONAL([HAVE_WAVEOUT], [test "x$HAVE_WAVEOUT" = x1])
+AS_IF([test "x$HAVE_WAVEOUT" = "x1"], AC_DEFINE([HAVE_WAVEOUT], 1, [Have WaveOut audio?]))
+
+#### GLib 2 support (optional) ####
+
+AC_ARG_ENABLE([glib2],
+    AS_HELP_STRING([--disable-glib2],[Disable optional GLib 2 support]))
+
+AS_IF([test "x$enable_glib2" != "xno"],
+    [PKG_CHECK_MODULES(GLIB20, [ glib-2.0 >= 2.4.0 ], HAVE_GLIB20=1, HAVE_GLIB20=0)],
+    HAVE_GLIB20=0)
+
+AS_IF([test "x$enable_glib2" = "xyes" && test "x$HAVE_GLIB20" = "x0"],
+    [AC_MSG_ERROR([*** GLib 2 support not found])])
+
+AC_SUBST(HAVE_GLIB20)
+AM_CONDITIONAL([HAVE_GLIB20], [test "x$HAVE_GLIB20" = x1])
+AS_IF([test "x$HAVE_GLIB20" = "x1"], AC_DEFINE([HAVE_GLIB], 1, [Have GLIB?]))
+
+#### GTK3 support (optional) ####
+
+AC_ARG_ENABLE([gtk3],
+    AS_HELP_STRING([--disable-gtk3],[Disable optional Gtk+ 3 support]))
+
+AS_IF([test "x$enable_gtk3" != "xno"],
+    [PKG_CHECK_MODULES(GTK30, [ gtk+-3.0 ], HAVE_GTK30=1, HAVE_GTK30=0)],
+    HAVE_GTK30=0)
+
+AS_IF([test "x$enable_gtk3" = "xyes" && test "x$HAVE_GTK30" = "x0"],
+    [AC_MSG_ERROR([*** Gtk+ 3 support not found])])
+
+AM_CONDITIONAL([HAVE_GTK30], [test "x$HAVE_GTK30" = x1])
+AS_IF([test "x$HAVE_GTK30" = "x1"], AC_DEFINE([HAVE_GTK], 1, [Have GTK?]))
+
+#### GConf support (optional) ####
+
+AC_ARG_ENABLE([gconf],
+    AS_HELP_STRING([--disable-gconf],[Disable optional GConf support]))
+
+AS_IF([test "x$enable_gconf" != "xno"],
+    [PKG_CHECK_MODULES(GCONF, [ gconf-2.0 >= 2.4.0 gobject-2.0 ], HAVE_GCONF=1, HAVE_GCONF=0)],
+    HAVE_GCONF=0)
+
+AS_IF([test "x$enable_gconf" = "xyes" && test "x$HAVE_GCONF" = "x0"],
+    [AC_MSG_ERROR([*** GConf support not found])])
+
+AM_CONDITIONAL([HAVE_GCONF], [test "x$HAVE_GCONF" = x1])
+
+#### Avahi support (optional) ####
+
+AC_ARG_ENABLE([avahi],
+    AS_HELP_STRING([--disable-avahi],[Disable optional Avahi support]))
+
+AS_IF([test "x$enable_avahi" != "xno"],
+    [PKG_CHECK_MODULES(AVAHI, [ avahi-client >= 0.6.0 ], HAVE_AVAHI=1, HAVE_AVAHI=0)],
+    HAVE_AVAHI=0)
+
+AS_IF([test "x$enable_avahi" = "xyes" && test "x$HAVE_AVAHI" = "x0"],
+    [AC_MSG_ERROR([*** Avahi support not found])])
+
+AC_SUBST(HAVE_AVAHI)
+AM_CONDITIONAL([HAVE_AVAHI], [test "x$HAVE_AVAHI" = x1])
+
+#### JACK (optional) ####
+
+AC_ARG_ENABLE([jack],
+    AS_HELP_STRING([--disable-jack],[Disable optional JACK support]))
+
+AS_IF([test "x$enable_jack" != "xno"],
+    [PKG_CHECK_MODULES(JACK, [ jack >= 0.117.0 ], HAVE_JACK=1, HAVE_JACK=0)],
+    HAVE_JACK=0)
+
+AS_IF([test "x$enable_jack" = "xyes" && test "x$HAVE_JACK" = "x0"],
+    [AC_MSG_ERROR([*** JACK support not found])])
+
+AM_CONDITIONAL([HAVE_JACK], [test "x$HAVE_JACK" = x1])
+
+#### Async DNS support (optional) ####
+
+AC_ARG_ENABLE([asyncns],
+    AS_HELP_STRING([--disable-asyncns],[Disable optional Async DNS support]))
+
+AS_IF([test "x$enable_asyncns" != "xno"],
+    [PKG_CHECK_MODULES(LIBASYNCNS, [ libasyncns >= 0.1 ], HAVE_LIBASYNCNS=1, HAVE_LIBASYNCNS=0)],
+    HAVE_LIBASYNCNS=0)
+
+AS_IF([test "x$enable_asyncns" = "xyes" && test "x$HAVE_LIBASYNCNS" = "x0"],
+    [AC_MSG_ERROR([*** Async DNS support not found])])
+
+AM_CONDITIONAL([HAVE_LIBASYNCNS], [test "x$HAVE_LIBASYNCNS" = x1])
+AS_IF([test "x$HAVE_LIBASYNCNS" = "x1"], AC_DEFINE([HAVE_LIBASYNCNS], 1, [Have libasyncns?]))
+
+#### TCP wrappers (optional) ####
+
+AC_ARG_ENABLE([tcpwrap],
+    AS_HELP_STRING([--disable-tcpwrap],[Disable optional TCP wrappers support]))
+
+AS_IF([test "x$enable_tcpwrap" != "xno"],
+    [
+        ACX_LIBWRAP
+        AS_IF([test "x$LIBWRAP_LIBS" != "x"], HAVE_TCPWRAP=1, HAVE_TCPWRAP=0)
+    ],
+    HAVE_TCPWRAP=0)
+
+AS_IF([test "x$enable_tcpwrap" = "xyes" && test "x$HAVE_TCPWRAP" = "x0"],
+    [AC_MSG_ERROR([*** TCP wrappers support not found])])
+
+AC_SUBST(LIBWRAP_LIBS)
+
+#### LIRC support (optional) ####
+
+AC_ARG_ENABLE([lirc],
+    AS_HELP_STRING([--disable-lirc],[Disable optional LIRC support]))
+
+LIRC_CFLAGS=
+LIRC_LIBS=
+
+AS_IF([test "x$enable_lirc" != "xno"],
+    [
+        HAVE_LIRC=1
+        AC_CHECK_HEADER(lirc/lirc_client.h, [], [HAVE_LIRC=0])
+        AC_CHECK_LIB(lirc_client, lirc_init, [LIRC_LIBS=-llirc_client], [HAVE_LIRC=0])
+    ],
+    HAVE_LIRC=0)
+
+AS_IF([test "x$enable_lirc" = "xyes" && test "x$HAVE_LIRC" = "x0"],
+    [AC_MSG_ERROR([*** LIRC support not found])])
+
+AC_SUBST(LIRC_CFLAGS)
+AC_SUBST(LIRC_LIBS)
+AM_CONDITIONAL([HAVE_LIRC], [test "x$HAVE_LIRC" = x1])
+
+#### D-Bus support (optional) ####
+
+AC_ARG_ENABLE([dbus],
+    AS_HELP_STRING([--disable-dbus],[Disable optional D-Bus support]))
+
+AS_IF([test "x$enable_dbus" != "xno"],
+    [PKG_CHECK_MODULES(DBUS, [ dbus-1 >= 1.4.12 ], HAVE_DBUS=1, HAVE_DBUS=0)],
+    HAVE_DBUS=0)
+
+AS_IF([test "x$enable_dbus" = "xyes" && test "x$HAVE_DBUS" = "x0"],
+    [AC_MSG_ERROR([*** D-Bus not available or too old version])])
+
+AS_IF([test "x$HAVE_DBUS" = "x1"],
+    [
+        save_CFLAGS="$CFLAGS"; CFLAGS="$CFLAGS $DBUS_CFLAGS"
+        save_LIBS="$LIBS"; LIBS="$LIBS $DBUS_LIBS"
+        AC_CHECK_FUNCS(dbus_watch_get_unix_fd)
+        CFLAGS="$save_CFLAGS"
+        LIBS="$save_LIBS"
+    ])
+
+AC_SUBST(HAVE_DBUS)
+AM_CONDITIONAL([HAVE_DBUS], [test "x$HAVE_DBUS" = x1])
+AS_IF([test "x$HAVE_DBUS" = "x1"], AC_DEFINE([HAVE_DBUS], 1, [Have D-Bus.]))
+
+PA_MACHINE_ID="${sysconfdir}/machine-id"
+AX_DEFINE_DIR(PA_MACHINE_ID, PA_MACHINE_ID, [D-Bus machine-id file])
+PA_MACHINE_ID_FALLBACK="${localstatedir}/lib/dbus/machine-id"
+AX_DEFINE_DIR(PA_MACHINE_ID_FALLBACK, PA_MACHINE_ID_FALLBACK,
+             [Fallback machine-id file])
+
+#### BlueZ support (optional, dependent on D-Bus and SBC) ####
+
+AC_ARG_ENABLE([bluez4],
+    AS_HELP_STRING([--disable-bluez4],[Disable optional BlueZ 4 support]))
+AC_ARG_ENABLE([bluez5],
+    AS_HELP_STRING([--disable-bluez5],[Disable optional BlueZ 5 support]))
+
+## SBC ##
+AS_IF([test "x$enable_bluez4" != "xno" || test "x$enable_bluez5" != "xno"],
+    [PKG_CHECK_MODULES(SBC, [ sbc >= 1.0 ], HAVE_SBC=1, HAVE_SBC=0)],
+    HAVE_SBC=0)
+
+## BlueZ 4 ##
+AS_IF([test "x$enable_bluez4" != "xno" && test "x$HAVE_DBUS" = "x1" && test "x$HAVE_SBC" = "x1"], HAVE_BLUEZ_4=1,
+      HAVE_BLUEZ_4=0)
+AS_IF([test "x$enable_bluez4" = "xyes" && test "x$HAVE_BLUEZ_4" != "x1"],
+    [AC_MSG_ERROR([*** BLUEZ 4 support not found (requires sbc and D-Bus)])])
+AC_SUBST(HAVE_BLUEZ_4)
+AM_CONDITIONAL([HAVE_BLUEZ_4], [test "x$HAVE_BLUEZ_4" = x1])
+
+## BlueZ 5 ##
+AS_IF([test "x$enable_bluez5" != "xno" && test "x$HAVE_DBUS" = "x1" && test "x$HAVE_SBC" = "x1"], HAVE_BLUEZ_5=1,
+      HAVE_BLUEZ_5=0)
+AS_IF([test "x$enable_bluez5" = "xyes" && test "x$HAVE_BLUEZ_5" != "x1"],
+    [AC_MSG_ERROR([*** BLUEZ 5 support not found (requires sbc and D-Bus)])])
+AC_SUBST(HAVE_BLUEZ_5)
+AM_CONDITIONAL([HAVE_BLUEZ_5], [test "x$HAVE_BLUEZ_5" = x1])
+
+AS_IF([test "x$HAVE_BLUEZ_4" = "x1" || test "x$HAVE_BLUEZ_5" = "x1"], HAVE_BLUEZ=1, HAVE_BLUEZ=0)
+AC_SUBST(HAVE_BLUEZ)
+AM_CONDITIONAL([HAVE_BLUEZ], [test "x$HAVE_BLUEZ" = x1])
+
+## Bluetooth Headset profiles backend ##
+
+AC_ARG_ENABLE([bluez5-ofono-headset],
+    AS_HELP_STRING([--disable-bluez5-ofono-headset],[Disable optional ofono headset backend support (Bluez 5)]))
+AS_IF([test "x$HAVE_BLUEZ_5" = "x1" && test "x$enable_bluez5_ofono_headset" != "xno"], HAVE_BLUEZ_5_OFONO_HEADSET=1,
+      HAVE_BLUEZ_5_OFONO_HEADSET=0)
+AC_SUBST(HAVE_BLUEZ_5_OFONO_HEADSET)
+AM_CONDITIONAL([HAVE_BLUEZ_5_OFONO_HEADSET], [test "x$HAVE_BLUEZ_5_OFONO_HEADSET" = x1])
+AS_IF([test "x$HAVE_BLUEZ_5_OFONO_HEADSET" = "x1"], AC_DEFINE([HAVE_BLUEZ_5_OFONO_HEADSET], 1, [Bluez 5 ofono headset backend enabled]))
+
+AC_ARG_ENABLE([bluez5-native-headset],
+    AS_HELP_STRING([--disable-bluez5-native-headset],[Disable optional native headset backend support (Bluez 5)]))
+AS_IF([test "x$HAVE_BLUEZ_5" = "x1" && test "x$enable_bluez5_native_headset" != "xno"],
+      [PKG_CHECK_MODULES(BLUEZ, [ bluez >= 4.101 ], HAVE_BLUEZ_5_NATIVE_HEADSET=1, HAVE_BLUEZ_5_NATIVE_HEADSET=0)],
+      HAVE_BLUEZ_5_NATIVE_HEADSET=0)
+AS_IF([test "x$enable_bluez5_native_headset" = "xyes" && test "x$HAVE_BLUEZ_5_NATIVE_HEADSET" = "x0"],
+      [AC_MSG_ERROR([*** BlueZ 5 native headset backend support not available (requires the libbluetooth headers)])])
+AC_SUBST(HAVE_BLUEZ_5_NATIVE_HEADSET)
+AM_CONDITIONAL([HAVE_BLUEZ_5_NATIVE_HEADSET], [test "x$HAVE_BLUEZ_5_NATIVE_HEADSET" = x1])
+AS_IF([test "x$HAVE_BLUEZ_5_NATIVE_HEADSET" = "x1"], AC_DEFINE([HAVE_BLUEZ_5_NATIVE_HEADSET], 1, [Bluez 5 native headset backend enabled]))
+
+#### UDEV support (optional) ####
+
+AC_ARG_ENABLE([udev],
+    AS_HELP_STRING([--disable-udev],[Disable optional UDEV support]))
+
+AS_IF([test "x$enable_udev" != "xno" -a \( "x$HAVE_OSS" = "x1" -o "x$HAVE_ALSA" = "x1" \)],
+    [PKG_CHECK_MODULES(UDEV, [ libudev >= 143 ], HAVE_UDEV=1, HAVE_UDEV=0)],
+    HAVE_UDEV=0)
+
+AS_IF([test "x$enable_udev" = "xyes" && test "x$HAVE_UDEV" = "x0"],
+    [AC_MSG_ERROR([*** UDEV support not found])])
+
+AC_SUBST(HAVE_UDEV)
+AM_CONDITIONAL([HAVE_UDEV], [test "x$HAVE_UDEV" = x1])
+AS_IF([test "x$HAVE_UDEV" = "x1"], AC_DEFINE([HAVE_UDEV], 1, [Have UDEV.]))
+
+#### HAL compat support (optional, dependent on UDEV) ####
+
+AC_ARG_ENABLE([hal-compat],
+    AS_HELP_STRING([--disable-hal-compat],[Disable optional HAL->udev transition compatibility support]))
+
+AS_IF([test "x$enable_hal_compat" != "xno"],
+    [AS_IF([test "x$HAVE_UDEV" = "x1"], HAVE_HAL_COMPAT=1, HAVE_HAL_COMPAT=0)],
+    HAVE_HAL_COMPAT=0)
+
+AM_CONDITIONAL([HAVE_HAL_COMPAT], [test "x$HAVE_HAL_COMPAT" = x1])
+AS_IF([test "x$HAVE_HAL_COMPAT" = "x1"], AC_DEFINE([HAVE_HAL_COMPAT], 1, [Have HAL compatibility.]))
+
+#### IPv6 connection support (optional) ####
+
+AC_ARG_ENABLE([ipv6],
+    AS_HELP_STRING([--disable-ipv6],[Disable optional IPv6 support]))
+
+AS_IF([test "x$enable_ipv6" != "xno"], [HAVE_IPV6=1], [HAVE_IPV6=0])
+
+AS_IF([test "x$HAVE_IPV6" = "x1"], AC_DEFINE([HAVE_IPV6], 1, [Define this to enable IPv6 connection support]))
+
+#### OpenSSL support (optional) ####
+
+AC_ARG_ENABLE([openssl],
+    AS_HELP_STRING([--disable-openssl],[Disable OpenSSL support (used for Airtunes/RAOP)]))
+
+AS_IF([test "x$enable_openssl" != "xno"],
+    [PKG_CHECK_MODULES(OPENSSL, [ openssl > 0.9 ], HAVE_OPENSSL=1, HAVE_OPENSSL=0)],
+    HAVE_OPENSSL=0)
+
+AS_IF([test "x$enable_openssl" = "xyes" && test "x$HAVE_OPENSSL" = "x0"],
+    [AC_MSG_ERROR([*** OpenSSL support not found])])
+
+AM_CONDITIONAL([HAVE_OPENSSL], [test "x$HAVE_OPENSSL" = x1])
+AS_IF([test "x$HAVE_OPENSSL" = "x1"], AC_DEFINE([HAVE_OPENSSL], 1, [Have OpenSSL]))
+
+#### FFTW (optional) ####
+
+AC_ARG_WITH([fftw],
+    AS_HELP_STRING([--without-fftw],[Omit FFTW-using modules (equalizer)]))
+
+AS_IF([test "x$with_fftw" != "xno"],
+    [PKG_CHECK_MODULES(FFTW, [ fftw3f ], HAVE_FFTW=1, HAVE_FFTW=0)],
+    HAVE_FFTW=0)
+
+AS_IF([test "x$with_fftw" = "xyes" && test "x$HAVE_FFTW" = "x0"],
+    [AC_MSG_ERROR([*** FFTW support not found])])
+
+AM_CONDITIONAL([HAVE_FFTW], [test "x$HAVE_FFTW" = "x1"])
+
+#### speex (optional) ####
+
+AC_ARG_WITH([speex],
+    AS_HELP_STRING([--without-speex],[Omit speex (resampling, AEC)]))
+
+AS_IF([test "x$with_speex" != "xno"],
+    [PKG_CHECK_MODULES(LIBSPEEX, [ speexdsp >= 1.2 ], HAVE_SPEEX=1, HAVE_SPEEX=0)],
+    HAVE_SPEEX=0)
+
+AS_IF([test "x$with_speex" = "xyes" && test "x$HAVE_SPEEX" = "x0"],
+    [AC_MSG_ERROR([*** speex support not found])])
+
+AM_CONDITIONAL([HAVE_SPEEX], [test "x$HAVE_SPEEX" = "x1"])
+AS_IF([test "x$HAVE_SPEEX" = "x1"], AC_DEFINE([HAVE_SPEEX], 1, [Have speex]))
+
+#### soxr (optional) ####
+
+AC_ARG_WITH([soxr],
+    AS_HELP_STRING([--without-soxr],[Omit soxr (resampling)]))
+
+AS_IF([test "x$with_soxr" != "xno"],
+    [PKG_CHECK_MODULES(LIBSOXR, [ soxr >= 0.1.1 ], HAVE_SOXR=1, HAVE_SOXR=0)],
+    HAVE_SOXR=0)
+
+AS_IF([test "x$with_soxr" = "xyes" && test "x$HAVE_SOXR" = "x0"],
+    [AC_MSG_ERROR([*** soxr support not found])])
+
+AM_CONDITIONAL([HAVE_SOXR], [test "x$HAVE_SOXR" = "x1"])
+AS_IF([test "x$HAVE_SOXR" = "x1"], AC_DEFINE([HAVE_SOXR], 1, [Have soxr]))
+
+
+#### gcov support (optional) #####
+
+AC_ARG_ENABLE([gcov],
+    AS_HELP_STRING([--enable-gcov],[Enable optional gcov coverage analysis]))
+
+GCOV_CFLAGS=
+GCOV_LIBS=" -lgcov"
+
+AS_IF([test "x$enable_gcov" = "xyes"],
+    [
+        HAVE_GCOV=1
+        GCOV_CFLAGS="$GCOV_CFLAGS -fprofile-arcs -ftest-coverage"
+        GCOV_LIBS="$GCOV_LIBS -fprofile-arcs"
+    ],
+    HAVE_GCOV=0)
+
+AC_SUBST(GCOV_CFLAGS)
+AC_SUBST(GCOV_LIBS)
+AM_CONDITIONAL([HAVE_GCOV], [test "x$HAVE_GCOV" = x1])
+
+#### ORC (optional) ####
+
+ORC_CHECK([0.4.11])
+
+#### systemd support (optional) ####
+
+AC_ARG_ENABLE([systemd-daemon],
+    AS_HELP_STRING([--disable-systemd-daemon],[Disable optional systemd daemon (socket activation) support]))
+
+AC_ARG_ENABLE([systemd-login],
+    AS_HELP_STRING([--disable-systemd-login],[Disable optional systemd login support]))
+
+AC_ARG_ENABLE([systemd-journal],
+    AS_HELP_STRING([--disable-systemd-journal],[Disable optional systemd journal support]))
+
+# Newer systemd's combine their subcomponent libraries into one
+# If it exists, we should use it for the further checks
+
+AS_IF([test "x$enable_systemd_daemon" != "xno" || test "x$enable_systemd_login" != "xno" || test "x$enable_systemd_journal" != "xno"],
+    [PKG_CHECK_MODULES(SYSTEMD, [ libsystemd ], HAVE_SYSTEMD=1, HAVE_SYSTEMD=0)],
+    HAVE_SYSTEMD=0)
+
+AS_IF([test "x$HAVE_SYSTEMD" = "x1"],
+    [
+        HAVE_SYSTEMD_DAEMON=1
+        HAVE_SYSTEMD_LOGIN=1
+        HAVE_SYSTEMD_JOURNAL=1
+    ])
+
+AC_ARG_WITH([systemduserunitdir],
+        AS_HELP_STRING([--with-systemduserunitdir=DIR], [Directory for systemd user service files]),
+        [], [with_systemduserunitdir=$($PKG_CONFIG --variable=systemduserunitdir systemd)])
+if test "x$with_systemduserunitdir" != xno; then
+        AC_SUBST([systemduserunitdir], [$with_systemduserunitdir])
+fi
+
+#### systemd daemon support (optional) ####
+
+AS_IF([test "x$enable_systemd_daemon" != "xno"],
+    [AS_IF([test "x$HAVE_SYSTEMD_DAEMON" != "x1"], [PKG_CHECK_MODULES(SYSTEMDDAEMON, [ libsystemd-daemon ], HAVE_SYSTEMD_DAEMON=1, HAVE_SYSTEMD_DAEMON=0)])],
+    HAVE_SYSTEMD_DAEMON=0)
+
+AS_IF([test "x$enable_systemd_daemon" = "xyes" && test "x$HAVE_SYSTEMD_DAEMON" = "x0"],
+    [AC_MSG_ERROR([*** Needed systemd daemon support not found])])
+
+AC_SUBST(HAVE_SYSTEMD_DAEMON)
+AM_CONDITIONAL([HAVE_SYSTEMD_DAEMON], [test "x$HAVE_SYSTEMD_DAEMON" = x1])
+AS_IF([test "x$HAVE_SYSTEMD_DAEMON" = "x1"], AC_DEFINE([HAVE_SYSTEMD_DAEMON], 1, [Have SYSTEMDDAEMON?]))
+
+#### systemd login support (optional) ####
+
+AS_IF([test "x$enable_systemd_login" != "xno"],
+    [AS_IF([test "x$HAVE_SYSTEMD_LOGIN" != "x1"], [PKG_CHECK_MODULES(SYSTEMDLOGIN, [ libsystemd-login ], HAVE_SYSTEMD_LOGIN=1, HAVE_SYSTEMD_LOGIN=0)])],
+    HAVE_SYSTEMD_LOGIN=0)
+
+AS_IF([test "x$enable_systemd_login" = "xyes" && test "x$HAVE_SYSTEMD_LOGIN" = "x0"],
+    [AC_MSG_ERROR([*** Needed systemd login support not found])])
+
+AC_SUBST(HAVE_SYSTEMD_LOGIN)
+AM_CONDITIONAL([HAVE_SYSTEMD_LOGIN], [test "x$HAVE_SYSTEMD_LOGIN" = x1])
+AS_IF([test "x$HAVE_SYSTEMD_LOGIN" = "x1"], AC_DEFINE([HAVE_SYSTEMD_LOGIN], 1, [Have SYSTEMDLOGIN?]))
+
+#### systemd journal support (optional) ####
+
+AS_IF([test "x$enable_systemd_journal" != "xno"],
+    [AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" != "x1"], [PKG_CHECK_MODULES(SYSTEMDJOURNAL, [ libsystemd-journal ], HAVE_SYSTEMD_JOURNAL=1, HAVE_SYSTEMD_JOURNAL=0)])],
+    HAVE_SYSTEMD_JOURNAL=0)
+
+AS_IF([test "x$enable_systemd_journal" = "xyes" && test "x$HAVE_SYSTEMD_JOURNAL" = "x0"],
+    [AC_MSG_ERROR([*** Needed systemd journal support not found])])
+
+AC_SUBST(HAVE_SYSTEMD_JOURNAL)
+AM_CONDITIONAL([HAVE_SYSTEMD_JOURNAL], [test "x$HAVE_SYSTEMD_JOURNAL" = x1])
+AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], AC_DEFINE([HAVE_SYSTEMD_JOURNAL], 1, [Have SYSTEMDJOURNAL?]))
+
+#### Build and Install man pages ####
+
+AC_ARG_ENABLE([manpages],
+    AS_HELP_STRING([--disable-manpages],[Disable building and installation of man pages]))
+
+AM_CONDITIONAL([BUILD_MANPAGES], [test "x$enable_manpages" != "xno"])
+
+#### PulseAudio system group & user  #####
+
+AC_ARG_WITH(system_user, AS_HELP_STRING([--with-system-user=<user>],[User for running the PulseAudio daemon as a system-wide instance (pulse)]))
+if test -z "$with_system_user" ; then
+    PA_SYSTEM_USER=pulse
+else
+    PA_SYSTEM_USER=$with_system_user
+fi
+AC_SUBST(PA_SYSTEM_USER)
+AC_DEFINE_UNQUOTED(PA_SYSTEM_USER,"$PA_SYSTEM_USER", [User for running the PulseAudio system daemon])
+
+AC_ARG_WITH(system_group,AS_HELP_STRING([--with-system-group=<group>],[Group for running the PulseAudio daemon as a system-wide instance (pulse)]))
+if test -z "$with_system_group" ; then
+    PA_SYSTEM_GROUP=pulse
+else
+    PA_SYSTEM_GROUP=$with_system_group
+fi
+AC_SUBST(PA_SYSTEM_GROUP)
+AC_DEFINE_UNQUOTED(PA_SYSTEM_GROUP,"$PA_SYSTEM_GROUP", [Group for the PulseAudio system daemon])
+
+AC_ARG_WITH(access_group,AS_HELP_STRING([--with-access-group=<group>],[Group which is allowed access to a system-wide PulseAudio daemon (pulse-access)]))
+if test -z "$with_access_group" ; then
+    PA_ACCESS_GROUP=pulse-access
+else
+    PA_ACCESS_GROUP=$with_access_group
+fi
+AC_SUBST(PA_ACCESS_GROUP)
+AC_DEFINE_UNQUOTED(PA_ACCESS_GROUP,"$PA_ACCESS_GROUP", [Access group])
+
+AC_ARG_ENABLE([per-user-esound-socket],
+    AS_HELP_STRING([--disable-per-user-esound-socket],[Use global esound socket directory /tmp/.esd/socket.]))
+
+if test "x$enable_per_user_esound_socket" != "xno"; then
+    USE_PER_USER_ESOUND_SOCKET=1
+    AC_DEFINE([USE_PER_USER_ESOUND_SOCKET], [1], [Define this if you want per-user esound socket directories])
+else
+    USE_PER_USER_ESOUND_SOCKET=0
+fi
+
+#### PulseAudio system runtime dir ####
+
+PA_SYSTEM_RUNTIME_PATH="${localstatedir}/run/pulse"
+AX_DEFINE_DIR(PA_SYSTEM_RUNTIME_PATH, PA_SYSTEM_RUNTIME_PATH, [System runtime dir])
+PA_SYSTEM_CONFIG_PATH="${localstatedir}/lib/pulse"
+AX_DEFINE_DIR(PA_SYSTEM_CONFIG_PATH, PA_SYSTEM_CONFIG_PATH, [System config dir])
+PA_SYSTEM_STATE_PATH="${localstatedir}/lib/pulse"
+AX_DEFINE_DIR(PA_SYSTEM_STATE_PATH, PA_SYSTEM_STATE_PATH, [System state dir])
+
+PA_BINARY=${bindir}/pulseaudio${EXEEXT}
+AX_DEFINE_DIR(PA_BINARY, PA_BINARY, [Location of pulseaudio binary])
+
+PACTL_BINARY=${bindir}/pactl${EXEEXT}
+AX_DEFINE_DIR(PACTL_BINARY, PACTL_BINARY, [Location of pactl binary])
+
+AC_SUBST(PA_SOEXT, [.so])
+AC_DEFINE(PA_SOEXT, [".so"], [Shared object extension])
+
+AC_SUBST(pulseconfdir, ["${sysconfdir}/pulse"])
+AX_DEFINE_DIR(PA_DEFAULT_CONFIG_DIR, pulseconfdir, [Location of configuration files])
+
+#### Mac OSX specific stuff #####
+
+AC_ARG_ENABLE(mac-universal,
+    AS_HELP_STRING([--enable-mac-universal], [Build Mac universal binaries]),
+    enable_mac_universal=$enableval, enable_mac_universal="no")
+
+AC_ARG_WITH(mac-version-min,
+    AS_HELP_STRING([--with-mac-version-min=<version>], [Defines the earliest version of MacOS X that the executables will run on.]),
+    mac_version_min=$withval, mac_version_min="10.5")
+
+AC_ARG_WITH(mac-sysroot,
+    AS_HELP_STRING([--with-mac-sysroot=<path>], [SDK basedir to use as the logical root directory for headers and libraries.]),
+    mac_sysroot=$withval)
+
+if test "x$os_is_darwin" = "x1" ; then
+    LDFLAGS="$LDFLAGS -mmacosx-version-min=$mac_version_min"
+    CFLAGS="$CFLAGS -mmacosx-version-min=$mac_version_min"
+
+    if test "x$mac_sysroot" != "x" ; then
+        LDFLAGS="$LDFLAGS -isysroot $mac_sysroot"
+        CFLAGS="$CFLAGS -isysroot $mac_sysroot"
+    fi
+
+    if test "x$enable_mac_universal" = "xyes" ; then
+        mac_arches="-arch i386 -arch x86_64"
+        LDFLAGS="$LDFLAGS $mac_arches"
+        CFLAGS="$CFLAGS $mac_arches"
+    fi
+fi
+
+AC_ARG_ENABLE([webrtc-aec],
+    AS_HELP_STRING([--enable-webrtc-aec], [Enable the optional WebRTC-based echo canceller]))
+
+AS_IF([test "x$enable_webrtc_aec" = "xyes" && test "$HAVE_CXX11" = "0"],
+    [AC_MSG_ERROR([*** webrtc-audio-processing needs C++11 support])])
+
+AS_IF([test "x$enable_webrtc_aec" != "xno"],
+    [PKG_CHECK_MODULES(WEBRTC, [ webrtc-audio-processing >= 0.2 ], [HAVE_WEBRTC=1], [HAVE_WEBRTC=0])],
+    [HAVE_WEBRTC=0])
+
+AS_IF([test "x$enable_webrtc_aec" = "xyes" && test "x$HAVE_WEBRTC" = "x0"],
+    [AC_MSG_ERROR([*** webrtc-audio-processing library not found])])
+
+AM_CONDITIONAL([HAVE_WEBRTC], [test "x$HAVE_WEBRTC" = "x1"])
+
+AC_ARG_ENABLE([adrian-aec],
+    AS_HELP_STRING([--enable-adrian-aec], [Enable Adrian's optional echo canceller]))
+AS_IF([test "x$enable_adrian_aec" != "xno"],
+    [HAVE_ADRIAN_EC=1])
+AM_CONDITIONAL([HAVE_ADRIAN_EC], [test "x$HAVE_ADRIAN_EC" = "x1"])
+
+
+#### Thread support ####
+
+AX_TLS
+AS_IF([test "$ac_cv_tls" = "__thread"],
+    AC_DEFINE([SUPPORT_TLS___THREAD], 1, [Define this if the compiler supports __thread for Thread-Local Storage]))
+
+# Win32 build breaks with win32 pthread installed
+AS_IF([test "x$os_is_win32" != "x1"],
+  [AX_PTHREAD])
+
+AS_IF([test "x$ax_pthread_ok" = "xyes"],
+    AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris]))
+
+
+
+###################################
+#            Output               #
+###################################
+
+AC_DEFINE_UNQUOTED(PA_CFLAGS, "$CFLAGS", [The CFLAGS used during compilation])
+
+# Check whether to build tests by default (as compile-test) or not
+AC_ARG_ENABLE([default-build-tests],
+    AS_HELP_STRING([--disable-default-build-tests], [Build test programs only during make check]))
+AM_CONDITIONAL([BUILD_TESTS_DEFAULT], [test "x$enable_default_build_tests" != "xno"])
+
+AC_ARG_ENABLE([legacy-database-entry-format],
+        AS_HELP_STRING([--disable-legacy-database-entry-format], [Try to load legacy (< 1.0) database files (card, device and volume restore).]))
+if test "x$enable_legacy_database_entry_format" != "xno" ; then
+        AC_DEFINE(ENABLE_LEGACY_DATABASE_ENTRY_FORMAT, [1], [Legacy database entry format])
+fi
+
+AC_ARG_ENABLE([static-bins],
+    AS_HELP_STRING([--enable-static-bins],[Statically link executables.]))
+AM_CONDITIONAL([STATIC_BINS], [test "x$enable_static_bins" = "xyes"])
+
+AC_ARG_WITH(
+        [preopen-mods],
+        AS_HELP_STRING([--with-preopen-mods],[Modules to preopen in daemon (default: all).]),
+        [PREOPEN_MODS=$withval], [PREOPEN_MODS="all"])
+AM_CONDITIONAL([PREOPEN_MODS], [test "x$PREOPEN_MODS" != "xall"])
+if test "x$PREOPEN_MODS" != "xall" ; then
+    tmpLIBS=""
+    for mod in $PREOPEN_MODS; do
+        tmpLIBS="$tmpLIBS module-$mod.la"
+    done
+    PREOPEN_MODS="$tmpLIBS"
+    AC_SUBST(PREOPEN_MODS)
+fi
+
+AC_ARG_WITH(
+        [module-dir],
+        AS_HELP_STRING([--with-module-dir],[Directory where to install the modules to (defaults to ${libdir}/pulse-${PA_MAJORMINOR}/modules]),
+        [modlibexecdir=$withval], [modlibexecdir="${libdir}/pulse-${PA_MAJORMINOR}/modules"])
+
+AC_SUBST(modlibexecdir)
+AX_DEFINE_DIR(PA_DLSEARCHPATH, modlibexecdir, [Modules dir])
+
+AC_ARG_WITH(
+        [udev-rules-dir],
+        AS_HELP_STRING([--with-udev-rules-dir],[Directory where to install udev rules to (defaults to /lib/udev/rules.d)]),
+        [udevrulesdir=$withval], [udevrulesdir="/lib/udev/rules.d"])
+
+AC_SUBST(udevrulesdir)
+
+AC_ARG_WITH([bash-completion-dir],
+        AS_HELP_STRING([--with-bash-completion-dir=DIR], [Directory for bash completion files]),
+        [bashcompletiondir=$withval], [bashcompletiondir=$($PKG_CONFIG --variable=completionsdir bash-completion)])
+if test "x$bashcompletionsdir" = "x"; then
+       bashcompletiondir="${datadir}/bash-completion/completions"
+fi
+
+AC_SUBST(bashcompletiondir)
+
+AC_ARG_WITH(
+        [zsh-completion-dir],
+        AS_HELP_STRING([--with-zsh-completion-dir], [Zsh completions directory (defaults to ${datadir}/zsh/site-functions)]),
+        [zshcompletiondir=$withval], [zshcompletiondir="${datadir}/zsh/site-functions"])
+
+AC_SUBST(zshcompletiondir)
+
+AC_ARG_ENABLE([force-preopen],
+    AS_HELP_STRING([--enable-force-preopen],[Preopen modules, even when dlopen() is supported.]))
+
+if test "x$enable_force_preopen" = "xyes"; then
+    FORCE_PREOPEN=yes
+else
+    FORCE_PREOPEN=no
+fi
+
+AM_CONDITIONAL([FORCE_PREOPEN], [test "x$FORCE_PREOPEN" = "xyes"])
+
+AC_CONFIG_FILES([
+Makefile
+src/Makefile
+man/Makefile
+libpulse.pc
+libpulse-simple.pc
+libpulse-mainloop-glib.pc
+doxygen/Makefile
+doxygen/doxygen.conf
+src/pulse/version.h
+po/Makefile.in
+man/pulseaudio.1.xml
+man/esdcompat.1.xml
+man/pax11publish.1.xml
+man/pacat.1.xml
+man/pacmd.1.xml
+man/pactl.1.xml
+man/pasuspender.1.xml
+man/padsp.1.xml
+man/pulse-daemon.conf.5.xml
+man/pulse-client.conf.5.xml
+man/default.pa.5.xml
+man/pulse-cli-syntax.5.xml
+man/start-pulseaudio-x11.1.xml
+])
+
+AC_CONFIG_FILES([src/esdcompat:src/daemon/esdcompat.in], [chmod +x src/esdcompat])
+AC_CONFIG_FILES([src/start-pulseaudio-x11:src/daemon/start-pulseaudio-x11.in], [chmod +x src/start-pulseaudio-x11])
+AC_CONFIG_FILES([src/client.conf:src/pulse/client.conf.in])
+AC_CONFIG_FILES([src/daemon.conf:src/daemon/daemon.conf.in],
+    [m4 src/daemon.conf > src/daemon.conf.gen && mv src/daemon.conf.gen src/daemon.conf])
+AC_CONFIG_FILES([src/default.pa:src/daemon/default.pa.in],
+    [m4 src/default.pa > src/default.pa.gen && mv src/default.pa.gen src/default.pa])
+AC_CONFIG_FILES([src/system.pa:src/daemon/system.pa.in],
+    [m4 src/system.pa > src/system.pa.gen && mv src/system.pa.gen src/system.pa])
+AS_IF([test "x$HAVE_SYSTEMD_DAEMON" = "x1"],
+    [
+        AC_CONFIG_FILES([src/pulseaudio.service:src/daemon/systemd/user/pulseaudio.service.in])
+    ])
+
+# CMake related ProjectConfig files
+PA_LIBDIR="$libdir"
+AX_DEFINE_DIR(PA_LIBDIR, PA_LIBDIR, [PulseAudio library dir])
+PA_INCDIR="$includedir"
+AX_DEFINE_DIR(PA_INCDIR, PA_INCDIR, [PulseAudio include dir])
+
+AC_CONFIG_FILES([PulseAudioConfig.cmake:PulseAudioConfig.cmake.in],
+    [m4 PulseAudioConfig.cmake > PulseAudioConfig.cmake.gen && mv PulseAudioConfig.cmake.gen PulseAudioConfig.cmake])
+AC_CONFIG_FILES([PulseAudioConfigVersion.cmake])
+
+AC_OUTPUT
+
+# ==========================================================================
+
+AS_IF([test "x$HAVE_MEMFD" = "x1"], ENABLE_MEMFD=yes, ENABLE_MEMFD=no)
+AS_IF([test "x$HAVE_X11" = "x1"], ENABLE_X11=yes, ENABLE_X11=no)
+AS_IF([test "x$HAVE_OSS_OUTPUT" = "x1"], ENABLE_OSS_OUTPUT=yes, ENABLE_OSS_OUTPUT=no)
+AS_IF([test "x$HAVE_OSS_WRAPPER" = "x1"], ENABLE_OSS_WRAPPER=yes, ENABLE_OSS_WRAPPER=no)
+AS_IF([test "x$HAVE_ALSA" = "x1"], ENABLE_ALSA=yes, ENABLE_ALSA=no)
+AS_IF([test "x$HAVE_COREAUDIO" = "x1"], ENABLE_COREAUDIO=yes, ENABLE_COREAUDIO=no)
+AS_IF([test "x$HAVE_SOLARIS" = "x1"], ENABLE_SOLARIS=yes, ENABLE_SOLARIS=no)
+AS_IF([test "x$HAVE_WAVEOUT" = "x1"], ENABLE_WAVEOUT=yes, ENABLE_WAVEOUT=no)
+AS_IF([test "x$HAVE_GLIB20" = "x1"], ENABLE_GLIB20=yes, ENABLE_GLIB20=no)
+AS_IF([test "x$HAVE_GTK30" = "x1"], ENABLE_GTK30=yes, ENABLE_GTK30=no)
+AS_IF([test "x$HAVE_GCONF" = "x1"], ENABLE_GCONF=yes, ENABLE_GCONF=no)
+AS_IF([test "x$HAVE_AVAHI" = "x1"], ENABLE_AVAHI=yes, ENABLE_AVAHI=no)
+AS_IF([test "x$HAVE_JACK" = "x1"], ENABLE_JACK=yes, ENABLE_JACK=no)
+AS_IF([test "x$HAVE_LIBASYNCNS" = "x1"], ENABLE_LIBASYNCNS=yes, ENABLE_LIBASYNCNS=no)
+AS_IF([test "x$HAVE_LIRC" = "x1"], ENABLE_LIRC=yes, ENABLE_LIRC=no)
+AS_IF([test "x$HAVE_DBUS" = "x1"], ENABLE_DBUS=yes, ENABLE_DBUS=no)
+AS_IF([test "x$HAVE_UDEV" = "x1"], ENABLE_UDEV=yes, ENABLE_UDEV=no)
+AS_IF([test "x$HAVE_SYSTEMD_DAEMON" = "x1"], ENABLE_SYSTEMD_DAEMON=yes, ENABLE_SYSTEMD_DAEMON=no)
+AS_IF([test "x$HAVE_SYSTEMD_LOGIN" = "x1"], ENABLE_SYSTEMD_LOGIN=yes, ENABLE_SYSTEMD_LOGIN=no)
+AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], ENABLE_SYSTEMD_JOURNAL=yes, ENABLE_SYSTEMD_JOURNAL=no)
+AS_IF([test "x$HAVE_BLUEZ_4" = "x1"], ENABLE_BLUEZ_4=yes, ENABLE_BLUEZ_4=no)
+AS_IF([test "x$HAVE_BLUEZ_5" = "x1"], ENABLE_BLUEZ_5=yes, ENABLE_BLUEZ_5=no)
+AS_IF([test "x$HAVE_BLUEZ_5_OFONO_HEADSET" = "x1"], ENABLE_BLUEZ_5_OFONO_HEADSET=yes, ENABLE_BLUEZ_5_OFONO_HEADSET=no)
+AS_IF([test "x$HAVE_BLUEZ_5_NATIVE_HEADSET" = "x1"], ENABLE_BLUEZ_5_NATIVE_HEADSET=yes, ENABLE_BLUEZ_5_NATIVE_HEADSET=no)
+AS_IF([test "x$HAVE_HAL_COMPAT" = "x1"], ENABLE_HAL_COMPAT=yes, ENABLE_HAL_COMPAT=no)
+AS_IF([test "x$HAVE_TCPWRAP" = "x1"], ENABLE_TCPWRAP=yes, ENABLE_TCPWRAP=no)
+AS_IF([test "x$HAVE_LIBSAMPLERATE" = "x1"], ENABLE_LIBSAMPLERATE="yes (DEPRECATED)", ENABLE_LIBSAMPLERATE=no)
+AS_IF([test "x$HAVE_IPV6" = "x1"], ENABLE_IPV6=yes, ENABLE_IPV6=no)
+AS_IF([test "x$HAVE_OPENSSL" = "x1"], ENABLE_OPENSSL=yes, ENABLE_OPENSSL=no)
+AS_IF([test "x$HAVE_FFTW" = "x1"], ENABLE_FFTW=yes, ENABLE_FFTW=no)
+AS_IF([test "x$HAVE_ORC" = "xyes"], ENABLE_ORC=yes, ENABLE_ORC=no)
+AS_IF([test "x$HAVE_ADRIAN_EC" = "x1"], ENABLE_ADRIAN_EC=yes, ENABLE_ADRIAN_EC=no)
+AS_IF([test "x$HAVE_SPEEX" = "x1"], ENABLE_SPEEX=yes, ENABLE_SPEEX=no)
+AS_IF([test "x$HAVE_SOXR" = "x1"], ENABLE_SOXR=yes, ENABLE_SOXR=no)
+AS_IF([test "x$HAVE_WEBRTC" = "x1"], ENABLE_WEBRTC=yes, ENABLE_WEBRTC=no)
+AS_IF([test "x$HAVE_TDB" = "x1"], ENABLE_TDB=yes, ENABLE_TDB=no)
+AS_IF([test "x$HAVE_GDBM" = "x1"], ENABLE_GDBM=yes, ENABLE_GDBM=no)
+AS_IF([test "x$HAVE_SIMPLEDB" = "x1"], ENABLE_SIMPLEDB=yes, ENABLE_SIMPLEDB=no)
+AS_IF([test "x$HAVE_ESOUND" = "x1"], ENABLE_ESOUND=yes, ENABLE_ESOUND=no)
+AS_IF([test "x$HAVE_ESOUND" = "x1" -a "x$USE_PER_USER_ESOUND_SOCKET" = "x1"], ENABLE_PER_USER_ESOUND_SOCKET=yes, ENABLE_PER_USER_ESOUND_SOCKET=no)
+AS_IF([test "x$HAVE_GCOV" = "x1"], ENABLE_GCOV=yes, ENABLE_GCOV=no)
+AS_IF([test "x$HAVE_LIBCHECK" = "x1"], ENABLE_TESTS=yes, ENABLE_TESTS=no)
+AS_IF([test "x$enable_legacy_database_entry_format" != "xno"], ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=yes, ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=no)
+
+echo "
+ ---{ $PACKAGE_NAME $VERSION }---
+
+    prefix:                        ${prefix}
+    sysconfdir:                    ${sysconfdir}
+    localstatedir:                 ${localstatedir}
+    modlibexecdir:                 ${modlibexecdir}
+    System Runtime Path:           ${PA_SYSTEM_RUNTIME_PATH}
+    System State Path:             ${PA_SYSTEM_STATE_PATH}
+    System Config Path:            ${PA_SYSTEM_CONFIG_PATH}
+    Zsh completions directory:     ${zshcompletiondir}
+    Bash completions directory:    ${bashcompletiondir}
+    Compiler:                      ${CC}
+    CFLAGS:                        ${CFLAGS}
+    CPPFLAGS:                      ${CPPFLAGS}
+    LIBS:                          ${LIBS}
+
+    Enable memfd shared memory:    ${ENABLE_MEMFD}
+    Enable X11:                    ${ENABLE_X11}
+    Enable OSS Output:             ${ENABLE_OSS_OUTPUT}
+    Enable OSS Wrapper:            ${ENABLE_OSS_WRAPPER}
+    Enable EsounD:                 ${ENABLE_ESOUND}
+    Enable Alsa:                   ${ENABLE_ALSA}
+    Enable CoreAudio:              ${ENABLE_COREAUDIO}
+    Enable Solaris:                ${ENABLE_SOLARIS}
+    Enable WaveOut:                ${ENABLE_WAVEOUT}
+    Enable GLib 2.0:               ${ENABLE_GLIB20}
+    Enable Gtk+ 3.0:               ${ENABLE_GTK30}
+    Enable GConf:                  ${ENABLE_GCONF}
+    Enable Avahi:                  ${ENABLE_AVAHI}
+    Enable Jack:                   ${ENABLE_JACK}
+    Enable Async DNS:              ${ENABLE_LIBASYNCNS}
+    Enable LIRC:                   ${ENABLE_LIRC}
+    Enable D-Bus:                  ${ENABLE_DBUS}
+      Enable BlueZ 4:              ${ENABLE_BLUEZ_4}
+      Enable BlueZ 5:              ${ENABLE_BLUEZ_5}
+        Enable ofono headsets:     ${ENABLE_BLUEZ_5_OFONO_HEADSET}
+        Enable native headsets:    ${ENABLE_BLUEZ_5_NATIVE_HEADSET}
+    Enable udev:                   ${ENABLE_UDEV}
+      Enable HAL->udev compat:     ${ENABLE_HAL_COMPAT}
+    Enable systemd
+      Daemon (Socket Activation):  ${ENABLE_SYSTEMD_DAEMON}
+      Login (Session Tracking):    ${ENABLE_SYSTEMD_LOGIN}
+      Journal (Logging):           ${ENABLE_SYSTEMD_JOURNAL}
+    Enable TCP Wrappers:           ${ENABLE_TCPWRAP}
+    Enable libsamplerate:          ${ENABLE_LIBSAMPLERATE}
+    Enable IPv6:                   ${ENABLE_IPV6}
+    Enable OpenSSL (for Airtunes): ${ENABLE_OPENSSL}
+    Enable fftw:                   ${ENABLE_FFTW}
+    Enable orc:                    ${ENABLE_ORC}
+    Enable Adrian echo canceller:  ${ENABLE_ADRIAN_EC}
+    Enable speex (resampler, AEC): ${ENABLE_SPEEX}
+    Enable soxr (resampler):       ${ENABLE_SOXR}
+    Enable WebRTC echo canceller:  ${ENABLE_WEBRTC}
+    Enable gcov coverage:          ${ENABLE_GCOV}
+    Enable unit tests:             ${ENABLE_TESTS}
+    Database
+      tdb:                         ${ENABLE_TDB}
+      gdbm:                        ${ENABLE_GDBM}
+      simple database:             ${ENABLE_SIMPLEDB}
+
+    System User:                   ${PA_SYSTEM_USER}
+    System Group:                  ${PA_SYSTEM_GROUP}
+    Access Group:                  ${PA_ACCESS_GROUP}
+    Enable per-user EsounD socket: ${ENABLE_PER_USER_ESOUND_SOCKET}
+    Force preopen:                 ${FORCE_PREOPEN}
+    Preopened modules:             ${PREOPEN_MODS}
+
+    Legacy Database Entry Support: ${ENABLE_LEGACY_DATABASE_ENTRY_FORMAT}
+"
+
+if test "${ENABLE_SPEEX}" = "no" && test "${ENABLE_WEBRTC}" = "no" && test "${ENABLE_ADRIAN_EC}" = "no" ; then
+AC_MSG_ERROR([At least one echo canceller implementation must be available.])
+fi
+
+if test "${ENABLE_DBUS}" = "no" && test "x$os_is_win32" != "x1" ; then
+   echo "
+===== WARNING WARNING WARNING WARNING WARNING WARNING WARNING =====
+You do not have D-Bus support enabled. It is strongly recommended
+that you enable D-Bus support if your platform supports it.
+Many parts of PulseAudio use D-Bus, from ConsoleKit interaction
+to the Device Reservation Protocol to speak to JACK, Bluetooth
+support and even a native control protocol for communicating and
+controling the PulseAudio daemon itself.
+===== WARNING WARNING WARNING WARNING WARNING WARNING WARNING =====
+"
+fi
+
+if test "${ENABLE_UDEV}" = "no" && test "x$os_is_win32" != "x1" ; then
+   echo "
+===== WARNING WARNING WARNING WARNING WARNING WARNING WARNING =====
+You do not have udev support enabled. It is strongly recommended
+that you enable udev support if your platform supports it as it is
+the primary method used to detect hardware audio devices (on Linux)
+and is thus a critical part of PulseAudio on that platform.
+===== WARNING WARNING WARNING WARNING WARNING WARNING WARNING =====
+"
+fi
+
+if test "${ENABLE_SPEEX}" = "no" && test "x$os_is_win32" != "x1" ; then
+   echo "
+===== WARNING WARNING WARNING WARNING WARNING WARNING WARNING =====
+You do not have speex support enabled. It is strongly recommended
+that you enable speex support if your platform supports it as it is
+the primary method used for audio resampling and is thus a critical
+part of PulseAudio on that platform.
+===== WARNING WARNING WARNING WARNING WARNING WARNING WARNING =====
+"
+fi
diff --git a/coverity/model.c b/coverity/model.c
new file mode 100644 (file)
index 0000000..afe7ca5
--- /dev/null
@@ -0,0 +1,18 @@
+/* Coverity Scan model
+ * Copyright (C) 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
+ *
+ * This is a modeling file for Coverity Scan which helps to avoid false
+ * positives and increase scanning accuracy by explaining code Coverity
+ * can't see (out of tree libraries); the model file must be uploaded by
+ * an admin to:
+ * https://scan.coverity.com/projects/pulseaudio?tab=analysis_settings
+ */
+
+void fail(void) {
+    __coverity_panic__();
+}
+
+void fail_unless(int x) {
+    if (!x)
+        __coverity_panic__();
+}
diff --git a/doc/stream_restore_fallback_table_example.table b/doc/stream_restore_fallback_table_example.table
new file mode 100644 (file)
index 0000000..0a4ade0
--- /dev/null
@@ -0,0 +1,13 @@
+# This is an example fallback table file for module-stream-restore.
+# Lines starting with '#' or ';' are regarded as comments and ignored.
+# Empty lines are allowed.
+#
+# Each non-comment and non-empty line defines the default volume
+# for one stream-restore database key. For example:
+
+sink-input-by-media-role:phone -4.2
+
+# That line sets the volume of sink inputs with media role "phone"
+# to -4.2 dB (relative to the sink volume), if the
+# "sink-input-by-media-role:phone" key doesn't already exist in the
+# stream-restore database. Positive dB values are not allowed.
diff --git a/doxygen/.gitignore b/doxygen/.gitignore
new file mode 100644 (file)
index 0000000..189d6c7
--- /dev/null
@@ -0,0 +1,4 @@
+/doxygen.conf
+/html/
+/Makefile
+/Makefile.in
diff --git a/doxygen/Makefile.am b/doxygen/Makefile.am
new file mode 100644 (file)
index 0000000..4696fb2
--- /dev/null
@@ -0,0 +1,22 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+doxygen: doxygen.conf
+       doxygen $<
+
+clean-local:
+       -rm -rf html
+
+.PHONY: all doxygen
diff --git a/doxygen/doxygen.conf.in b/doxygen/doxygen.conf.in
new file mode 100644 (file)
index 0000000..82ccad6
--- /dev/null
@@ -0,0 +1,1902 @@
+# Doxyfile 1.8.3.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME           = PulseAudio
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = @PACKAGE_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
+# started.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
+# files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# Disable only in case of backward compatibilities issues.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented classes,
+# or namespaces to their corresponding documentation. Such a link can be
+# prevented in individual cases by by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES (the
+# default) will make doxygen replace the get and set methods by a property in
+# the documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE      = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = NO
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = NO
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = NO
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if section-label ... \endif
+# and \cond section-label ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path. Do not use
+# file names with spaces, bibtex cannot handle them.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = @srcdir@/../src/pulse/channelmap.h \
+                         @srcdir@/../src/pulse/context.h \
+                         @srcdir@/../src/pulse/def.h \
+                         @srcdir@/../src/pulse/direction.h \
+                         @srcdir@/../src/pulse/error.h \
+                         @srcdir@/../src/pulse/ext-stream-restore.h \
+                         @srcdir@/../src/pulse/ext-device-manager.h \
+                         @srcdir@/../src/pulse/ext-device-restore.h \
+                         @srcdir@/../src/pulse/format.h \
+                         @srcdir@/../src/pulse/gccmacro.h \
+                         @srcdir@/../src/pulse/glib-mainloop.h \
+                         @srcdir@/../src/pulse/introspect.h \
+                         @srcdir@/../src/pulse/mainloop-api.h \
+                         @srcdir@/../src/pulse/mainloop-signal.h \
+                         @srcdir@/../src/pulse/mainloop.h \
+                         @srcdir@/../src/pulse/operation.h \
+                         @srcdir@/../src/pulse/proplist.h \
+                         @srcdir@/../src/pulse/pulseaudio.h \
+                         @srcdir@/../src/pulse/rtclock.h \
+                         @srcdir@/../src/pulse/sample.h \
+                         @srcdir@/../src/pulse/scache.h \
+                         @srcdir@/../src/pulse/simple.h \
+                         @srcdir@/../src/pulse/stream.h \
+                         @srcdir@/../src/pulse/subscribe.h \
+                         @srcdir@/../src/pulse/thread-mainloop.h \
+                         @srcdir@/../src/pulse/timeval.h \
+                         @srcdir@/../src/pulse/utf8.h \
+                         @srcdir@/../src/pulse/util.h \
+                         @srcdir@/../src/pulse/version.h \
+                         @srcdir@/../src/pulse/volume.h \
+                         @srcdir@/../src/pulse/xmalloc.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           = @srcdir@/../src/utils \
+                         @srcdir@/../src/tests
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page (index.html).
+# This can be useful if you have a project on for instance GitHub and want reuse
+# the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = pa_ \
+                         PA_
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+#  for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
+# tag will in the future become obsolete.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
+# the output directory.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
+# style string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW      = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE   = 1
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
+# SVG. The default value is HTML-CSS, which is slower, but has the best
+# compatibility.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
+# copy of MathJax from http://www.mathjax.org before deployment.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS     =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript.
+# There are two flavours of web server based search depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools.
+# See the manual for details.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain
+# the search results. Doxygen ships with an example indexer (doxyindexer) and
+# search engine (doxysearch.cgi) which are based on the open source search engine
+# library Xapian. See the manual for configuration details.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will returned the search results when EXTERNAL_SEARCH is enabled.
+# Doxygen ships with an example search engine (doxysearch) which is based on
+# the open source search engine library Xapian. See the manual for configuration
+# details.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
+# of to a relative location where the documentation can be found.
+# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             = PA_C_DECL_BEGIN= \
+                         PA_C_DECL_END=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
+# doxygen is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
+# exceeded by 50% before the limit is enforced.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG        = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/git-version-gen b/git-version-gen
new file mode 100755 (executable)
index 0000000..079b93e
--- /dev/null
@@ -0,0 +1,173 @@
+#!/bin/sh
+# Print a version string.
+scriptversion=2012-09-25.20
+
+# Copyright (C) 2007-2008 Free Software Foundation
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+#   produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+#   presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+#   a checked-out repository.  Created with contents that were learned at
+#   the last time autoconf was run, and used by git-version-gen.  Must not
+#   be present in either $(srcdir) or $(builddir) for git-version-gen to
+#   give accurate answers during normal development with a checked out tree,
+#   but must be present in a tarball when there is no version control system.
+#   Therefore, it cannot be used in any dependencies.  GNUmakefile has
+#   hooks to force a reconfigure at distribution time to get the value
+#   correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+#   tarball.  Usable in dependencies, particularly for files that don't
+#   want to depend on config.h but do want to track version changes.
+#   Delete this file prior to any autoconf run where you want to rebuild
+#   files to pick up a version string change; and leave it stale to
+#   minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+#         m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+#         [bug-project@example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+#      echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+#      echo $(VERSION) > $(distdir)/.tarball-version
+#      echo $(VERSION) > $(distdir)/.version
+
+case $# in
+    1) ;;
+    *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
+esac
+
+tarball_version_file=$1
+nl='
+'
+v=
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+    v=`cat $tarball_version_file` || exit 1
+    case $v in
+       *$nl*) v= ;; # reject multi-line output
+       [0-9]*)
+               echo "$v" | tr -d '\012'
+               exit 0
+               ;;
+       *) v= ;;
+    esac
+    test -z "$v" \
+       && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
+fi
+
+# This is presently used by the GNOME-OSTree build system; it
+# helps support the case where the meta-build system has already
+# determined the git revision, but we may not be able to run "git describe"
+# because we're inside a chroot.
+if test -n "$GIT_DESCRIBE_FOR_BUILD";
+then
+    v=$GIT_DESCRIBE_FOR_BUILD
+fi
+
+if test -n "$v"
+then
+    : # use $v
+elif test -e .git \
+    && v=`git describe --abbrev=4 --match='v[0-9]*' HEAD 2>/dev/null` \
+    && [ -n "$v" ]
+then
+    # If we are on a "dev" tag, we need to check that it is not the same
+    # reference as the a previous version tag (this only happens when we are
+    # working with a release tag).
+    # NB The below trick relies on the $v being an exact tag to work which
+    # will only work when HEAD == tag. When further commits have been made on top
+    # of the tag, the $v will be supplimented with the number of commits since
+    # that tag and the commit ref of the most recent commit and thus will
+    # fail the test below (as intended)
+    v2=`git describe --abbrev=4 --match='v[0-9]\.[0-9]' --contains $v 2>/dev/null | cut -d'^' -f1`
+    [ -n "$v2" ] && v=$v2
+
+    # Is this a new git that lists number of commits since the last
+    # tag or the previous older version that did not?
+    #   Newer: v6.10-77-g0f8faeb
+    #   Older: v6.10-g0f8faeb
+#    case $v in
+#      *-*-*) : git describe is okay three part flavor ;;
+#      *-*)
+#          : git describe is older two part flavor
+#          # Recreate the number of commits and rewrite such that the
+#          # result is the same as if we were using the newer version
+#          # of git describe.
+#          vtag=`echo "$v" | sed 's/-.*//'`
+#          numcommits=`git rev-list "$vtag"..HEAD | wc -l`
+#          v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+#          ;;
+#    esac
+
+    # Change the first '-' to a '.', so version-comparing tools work properly.
+    # Remove the "g" in git describe's output string, to save a byte.
+#    v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+    :
+else
+    echo 1>&2 "$0: Failed to determine git revision"
+    exit 1
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Don't declare a version "dirty" merely because a time stamp has changed.
+git status > /dev/null 2>&1
+
+dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
+case "$dirty" in
+    '') ;;
+    *) # Append the suffix only if there isn't one already.
+       case $v in
+         *-dirty) ;;
+         *) v="$v-dirty" ;;
+       esac ;;
+esac
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d '\012'
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/libpulse-mainloop-glib.pc.in b/libpulse-mainloop-glib.pc.in
new file mode 100644 (file)
index 0000000..23d8421
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libpulse-mainloop-glib
+Description: PulseAudio GLib 2.0 Main Loop Wrapper
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lpulse-mainloop-glib @PTHREAD_LIBS@
+Libs.private: -L${libdir}/pulseaudio -lpulsecommon-@PA_MAJORMINOR@
+Cflags: -I${includedir} -D_REENTRANT
+Requires: libpulse glib-2.0
diff --git a/libpulse-simple.pc.in b/libpulse-simple.pc.in
new file mode 100644 (file)
index 0000000..dc855fd
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libpulse-simple
+Description: PulseAudio Simplified Synchronous Client Interface
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lpulse-simple @PTHREAD_LIBS@
+Libs.private: -L${libdir}/pulseaudio -lpulsecommon-@PA_MAJORMINOR@
+Cflags: -I${includedir} -D_REENTRANT
+Requires: libpulse
diff --git a/libpulse.pc.in b/libpulse.pc.in
new file mode 100644 (file)
index 0000000..23d888b
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+modlibexecdir=@modlibexecdir@
+
+Name: libpulse
+Description: PulseAudio Client Interface
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lpulse @PTHREAD_LIBS@
+Libs.private: -L${libdir}/pulseaudio -lpulsecommon-@PA_MAJORMINOR@
+Cflags: -I${includedir} -D_REENTRANT
diff --git a/m4/.gitignore b/m4/.gitignore
new file mode 100644 (file)
index 0000000..db94175
--- /dev/null
@@ -0,0 +1,37 @@
+codeset.m4
+fcntl-o.m4
+gettext.m4
+glibc2.m4
+glibc21.m4
+iconv.m4
+intdiv0.m4
+intl.m4
+intldir.m4
+intlmacosx.m4
+intltool.m4
+intmax.m4
+inttypes-pri.m4
+inttypes_h.m4
+lcmessage.m4
+lib-ld.m4
+lib-link.m4
+lib-prefix.m4
+libtool.m4
+lock.m4
+longlong.m4
+ltoptions.m4
+ltsugar.m4
+ltversion.m4
+lt~obsolete.m4
+nls.m4
+po.m4
+printf-posix.m4
+progtest.m4
+size_max.m4
+stdint_h.m4
+threadlib.m4
+uintmax_t.m4
+visibility.m4
+wchar_t.m4
+wint_t.m4
+xsize.m4
diff --git a/m4/acx_libwrap.m4 b/m4/acx_libwrap.m4
new file mode 100644 (file)
index 0000000..ccf8afc
--- /dev/null
@@ -0,0 +1,19 @@
+AC_DEFUN([ACX_LIBWRAP], [
+LIBWRAP_LIBS=
+saved_LIBS="$LIBS"
+LIBS="$LIBS -lwrap"
+AC_MSG_CHECKING([for tcpwrap library and headers])
+AC_LINK_IFELSE(
+[AC_LANG_PROGRAM(
+[#include <tcpd.h>
+#include <syslog.h>
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;],
+[struct request_info *req;
+return hosts_access (req);])],
+[AC_DEFINE(HAVE_LIBWRAP, [], [Have tcpwrap?])
+LIBWRAP_LIBS="-lwrap"
+AC_MSG_RESULT(yes)],
+[AC_MSG_RESULT(no)])
+LIBS="$saved_LIBS"
+])
diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4
new file mode 100644 (file)
index 0000000..ca36397
--- /dev/null
@@ -0,0 +1,74 @@
+# ===========================================================================
+#   http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+#   Check whether the given FLAG works with the current language's compiler
+#   or gives an error.  (Warnings, however, are ignored)
+#
+#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+#   success/failure.
+#
+#   If EXTRA-FLAGS is defined, it is added to the current language's default
+#   flags (e.g. CFLAGS) when the check is done.  The check is thus made with
+#   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to
+#   force the compiler to issue an error when a bad flag is given.
+#
+#   INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+#   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 4
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+  ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+  _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+  AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+    [AS_VAR_SET(CACHEVAR,[yes])],
+    [AS_VAR_SET(CACHEVAR,[no])])
+  _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_VAR_IF(CACHEVAR,yes,
+  [m4_default([$2], :)],
+  [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/m4/ax_check_define.m4 b/m4/ax_check_define.m4
new file mode 100644 (file)
index 0000000..4bc6948
--- /dev/null
@@ -0,0 +1,92 @@
+# ===========================================================================
+#      http://www.gnu.org/software/autoconf-archive/ax_check_define.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AC_CHECK_DEFINE([symbol], [ACTION-IF-FOUND], [ACTION-IF-NOT])
+#   AX_CHECK_DEFINE([includes],[symbol], [ACTION-IF-FOUND], [ACTION-IF-NOT])
+#
+# DESCRIPTION
+#
+#   Complements AC_CHECK_FUNC but it does not check for a function but for a
+#   define to exist. Consider a usage like:
+#
+#    AC_CHECK_DEFINE(__STRICT_ANSI__, CFLAGS="$CFLAGS -D_XOPEN_SOURCE=500")
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#
+#   This program is free software; you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation; either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 8
+
+AU_ALIAS([AC_CHECK_DEFINED], [AC_CHECK_DEFINE])
+AC_DEFUN([AC_CHECK_DEFINE],[
+AS_VAR_PUSHDEF([ac_var],[ac_cv_defined_$1])dnl
+AC_CACHE_CHECK([for $1 defined], ac_var,
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
+  #ifdef $1
+  int ok;
+  #else
+  choke me
+  #endif
+]])],[AS_VAR_SET(ac_var, yes)],[AS_VAR_SET(ac_var, no)]))
+AS_IF([test AS_VAR_GET(ac_var) != "no"], [$2], [$3])dnl
+AS_VAR_POPDEF([ac_var])dnl
+])
+
+AU_ALIAS([AX_CHECK_DEFINED], [AX_CHECK_DEFINE])
+AC_DEFUN([AX_CHECK_DEFINE],[
+AS_VAR_PUSHDEF([ac_var],[ac_cv_defined_$2_$1])dnl
+AC_CACHE_CHECK([for $2 defined in $1], ac_var,
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <$1>]], [[
+  #ifdef $2
+  int ok;
+  #else
+  choke me
+  #endif
+]])],[AS_VAR_SET(ac_var, yes)],[AS_VAR_SET(ac_var, no)]))
+AS_IF([test AS_VAR_GET(ac_var) != "no"], [$3], [$4])dnl
+AS_VAR_POPDEF([ac_var])dnl
+])
+
+AC_DEFUN([AX_CHECK_FUNC],
+[AS_VAR_PUSHDEF([ac_var], [ac_cv_func_$2])dnl
+AC_CACHE_CHECK([for $2], ac_var,
+dnl AC_LANG_FUNC_LINK_TRY
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([$1
+                #undef $2
+                char $2 ();],[
+                char (*f) () = $2;
+                return f != $2; ])],
+                [AS_VAR_SET(ac_var, yes)],
+                [AS_VAR_SET(ac_var, no)])])
+AS_IF([test AS_VAR_GET(ac_var) = yes], [$3], [$4])dnl
+AS_VAR_POPDEF([ac_var])dnl
+])# AC_CHECK_FUNC
diff --git a/m4/ax_check_flag.m4 b/m4/ax_check_flag.m4
new file mode 100644 (file)
index 0000000..52405fd
--- /dev/null
@@ -0,0 +1,147 @@
+# ===========================================================================
+#       http://www.gnu.org/software/autoconf-archive/ax_check_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CHECK_PREPROC_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
+#   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
+#   AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
+#   AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
+#   AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS])
+#   AX_APPEND_LINK_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS])
+#
+# DESCRIPTION
+#
+#   Check whether the given FLAG works with the current language's
+#   preprocessor/compiler/linker, or whether they give an error. (Warnings,
+#   however, are ignored.)
+#
+#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+#   success/failure.
+#
+#   If EXTRA-FLAGS is defined, it is added to the current language's default
+#   flags (e.g.  CFLAGS) when the check is done.  The check us thus made
+#   with the following flags: "CFLAGS EXTRA-FLAGS FLAG".  EXTRA-FLAGS can
+#   for example be used to force the compiler to issue an error when a bad
+#   flag is given.
+#
+#   AX_APPEND_FLAG appends the FLAG to the FLAG-VARIABLE shell variable or
+#   the current language's flags if not specified.  FLAG is not added to
+#   FLAG-VARIABLE if it is already in the shell variable.
+#
+#   AX_APPEND_COMPILE_FLAGS checks for each FLAG1, FLAG2, etc. using
+#   AX_CHECK_COMPILE_FLAG and if the check is successful the flag is added
+#   to the appropriate FLAGS variable with AX_APPEND_FLAG.  The
+#   FLAGS-VARIABLE and EXTRA-FLAGS arguments are the same as in the other
+#   macros. AX_APPEND_LINK_FLAGS does the same for linker flags.
+#
+#   NOTE: Based on AX_CHECK_COMPILER_FLAGS and AX_CFLAGS_GCC_OPTION.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2009 Steven G. Johnson <stevenj@alum.mit.edu>
+#   Copyright (c) 2009 Matteo Frigo
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 2
+
+AC_DEFUN([AX_CHECK_PREPROC_FLAG],
+[AC_PREREQ(2.59) dnl for _AC_LANG_PREFIX
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]cppflags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG preprocessor accepts $1], CACHEVAR, [
+  ax_check_save_flags=$CPPFLAGS
+  CPPFLAGS="$CPPFLAGS $4 $1"
+  AC_PREPROC_IFELSE([AC_LANG_PROGRAM()],
+    [AS_VAR_SET([CACHEVAR],[yes])],
+    [AS_VAR_SET([CACHEVAR],[no])])
+  CPPFLAGS=$ax_check_save_flags])
+AS_VAR_IF([CACHEVAR], "yes",
+  [m4_default([$2], :)],
+  [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_PREPROC_FLAGS
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.59) dnl for _AC_LANG_PREFIX
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+  ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+  _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+  AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
+    [AS_VAR_SET([CACHEVAR],[yes])],
+    [AS_VAR_SET([CACHEVAR],[no])])
+  _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_VAR_IF([CACHEVAR], "yes",
+  [m4_default([$2], :)],
+  [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
+
+AC_DEFUN([AX_CHECK_LINK_FLAG],
+[AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
+AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
+  ax_check_save_flags=$LDFLAGS
+  LDFLAGS="$LDFLAGS $4 $1"
+  AC_LINK_IFELSE([AC_LANG_PROGRAM()],
+    [AS_VAR_SET([CACHEVAR],[yes])],
+    [AS_VAR_SET([CACHEVAR],[no])])
+  LDFLAGS=$ax_check_save_flags])
+AS_VAR_IF([CACHEVAR], "yes",
+  [m4_default([$2], :)],
+  [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_LINK_FLAGS
+
+
+AC_DEFUN([AX_APPEND_FLAG],
+[AC_PREREQ(2.59) dnl for _AC_LANG_PREFIX
+AC_REQUIRE([AC_PROG_GREP])
+AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[]FLAGS)])dnl
+AS_VAR_SET_IF([FLAGS],
+  [AS_IF([AS_ECHO(" $[]FLAGS ") | $GREP " $1 " 2>&1 >/dev/null],
+    [AC_RUN_LOG([: FLAGS already contains $1])],
+    [AC_RUN_LOG([: FLAGS="$FLAGS $1"])
+    AS_VAR_APPEND([FLAGS], [" $1"])])],
+  [AS_VAR_SET([FLAGS],[$1])])
+AS_VAR_POPDEF([FLAGS])dnl
+])dnl AX_APPEND_FLAG
+
+AC_DEFUN([AX_APPEND_COMPILE_FLAGS],
+[for flag in $1; do
+  AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3])
+done
+])dnl AX_APPEND_COMPILE_FLAGS
+
+AC_DEFUN([AX_APPEND_LINK_FLAGS],
+[for flag in $1; do
+  AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3])
+done
+])dnl AX_APPEND_LINK_FLAGS
diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4
new file mode 100644 (file)
index 0000000..079e17d
--- /dev/null
@@ -0,0 +1,558 @@
+# ===========================================================================
+#   http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+#   Check for baseline language coverage in the compiler for the specified
+#   version of the C++ standard.  If necessary, add switches to CXXFLAGS to
+#   enable support.  VERSION may be '11' (for the C++11 standard) or '14'
+#   (for the C++14 standard).
+#
+#   The second argument, if specified, indicates whether you insist on an
+#   extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+#   -std=c++11).  If neither is specified, you get whatever works, with
+#   preference for an extended mode.
+#
+#   The third argument, if specified 'mandatory' or if left unspecified,
+#   indicates that baseline support for the specified C++ standard is
+#   required and that the macro should error out if no mode with that
+#   support is found.  If specified 'optional', then configuration proceeds
+#   regardless, after defining HAVE_CXX${VERSION} if and only if a
+#   supporting mode is found.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+#   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+#   Copyright (c) 2015 Paul Norman <penorman@mac.com>
+#   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 1
+
+dnl  This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
+dnl  (serial version number 13).
+
+AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
+  m4_if([$1], [11], [],
+        [$1], [14], [],
+        [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],
+        [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
+  m4_if([$2], [], [],
+        [$2], [ext], [],
+        [$2], [noext], [],
+        [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
+  m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
+        [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
+        [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
+        [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
+  AC_LANG_PUSH([C++])dnl
+  ac_success=no
+  AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
+  ax_cv_cxx_compile_cxx$1,
+  [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+    [ax_cv_cxx_compile_cxx$1=yes],
+    [ax_cv_cxx_compile_cxx$1=no])])
+  if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
+    ac_success=yes
+  fi
+
+  m4_if([$2], [noext], [], [dnl
+  if test x$ac_success = xno; then
+    for switch in -std=gnu++$1 -std=gnu++0x; do
+      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+      AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+                     $cachevar,
+        [ac_save_CXXFLAGS="$CXXFLAGS"
+         CXXFLAGS="$CXXFLAGS $switch"
+         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+          [eval $cachevar=yes],
+          [eval $cachevar=no])
+         CXXFLAGS="$ac_save_CXXFLAGS"])
+      if eval test x\$$cachevar = xyes; then
+        CXXFLAGS="$CXXFLAGS $switch"
+        ac_success=yes
+        break
+      fi
+    done
+  fi])
+
+  m4_if([$2], [ext], [], [dnl
+  if test x$ac_success = xno; then
+    dnl HP's aCC needs +std=c++11 according to:
+    dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
+    dnl Cray's crayCC needs "-h std=c++11"
+    for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do
+      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+      AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+                     $cachevar,
+        [ac_save_CXXFLAGS="$CXXFLAGS"
+         CXXFLAGS="$CXXFLAGS $switch"
+         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+          [eval $cachevar=yes],
+          [eval $cachevar=no])
+         CXXFLAGS="$ac_save_CXXFLAGS"])
+      if eval test x\$$cachevar = xyes; then
+        CXXFLAGS="$CXXFLAGS $switch"
+        ac_success=yes
+        break
+      fi
+    done
+  fi])
+  AC_LANG_POP([C++])
+  if test x$ax_cxx_compile_cxx$1_required = xtrue; then
+    if test x$ac_success = xno; then
+      AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
+    fi
+  else
+    if test x$ac_success = xno; then
+      HAVE_CXX$1=0
+      AC_MSG_NOTICE([No compiler with C++$1 support was found])
+    else
+      HAVE_CXX$1=1
+      AC_DEFINE(HAVE_CXX$1,1,
+                [define if the compiler supports basic C++$1 syntax])
+    fi
+
+    AC_SUBST(HAVE_CXX$1)
+  fi
+])
+
+
+dnl  Test body for checking C++11 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+)
+
+
+dnl  Test body for checking C++14 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+)
+
+
+dnl  Tests for new features in C++11
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201103L
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+  namespace test_static_assert
+  {
+
+    template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+  }
+
+  namespace test_final_override
+  {
+
+    struct Base
+    {
+      virtual void f() {}
+    };
+
+    struct Derived : public Base
+    {
+      virtual void f() override {}
+    };
+
+  }
+
+  namespace test_double_right_angle_brackets
+  {
+
+    template < typename T >
+    struct check {};
+
+    typedef check<void> single_type;
+    typedef check<check<void>> double_type;
+    typedef check<check<check<void>>> triple_type;
+    typedef check<check<check<check<void>>>> quadruple_type;
+
+  }
+
+  namespace test_decltype
+  {
+
+    int
+    f()
+    {
+      int a = 1;
+      decltype(a) b = 2;
+      return a + b;
+    }
+
+  }
+
+  namespace test_type_deduction
+  {
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static const bool value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static const bool value = true;
+    };
+
+    template < typename T1, typename T2 >
+    auto
+    add(T1 a1, T2 a2) -> decltype(a1 + a2)
+    {
+      return a1 + a2;
+    }
+
+    int
+    test(const int c, volatile int v)
+    {
+      static_assert(is_same<int, decltype(0)>::value == true, "");
+      static_assert(is_same<int, decltype(c)>::value == false, "");
+      static_assert(is_same<int, decltype(v)>::value == false, "");
+      auto ac = c;
+      auto av = v;
+      auto sumi = ac + av + 'x';
+      auto sumf = ac + av + 1.0;
+      static_assert(is_same<int, decltype(ac)>::value == true, "");
+      static_assert(is_same<int, decltype(av)>::value == true, "");
+      static_assert(is_same<int, decltype(sumi)>::value == true, "");
+      static_assert(is_same<int, decltype(sumf)>::value == false, "");
+      static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+      return (sumf > 0.0) ? sumi : add(c, v);
+    }
+
+  }
+
+  namespace test_noexcept
+  {
+
+    int f() { return 0; }
+    int g() noexcept { return 0; }
+
+    static_assert(noexcept(f()) == false, "");
+    static_assert(noexcept(g()) == true, "");
+
+  }
+
+  namespace test_constexpr
+  {
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+    {
+      return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+    }
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c(const CharT *const s) noexcept
+    {
+      return strlen_c_r(s, 0UL);
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("1") == 1UL, "");
+    static_assert(strlen_c("example") == 7UL, "");
+    static_assert(strlen_c("another\0example") == 7UL, "");
+
+  }
+
+  namespace test_rvalue_references
+  {
+
+    template < int N >
+    struct answer
+    {
+      static constexpr int value = N;
+    };
+
+    answer<1> f(int&)       { return answer<1>(); }
+    answer<2> f(const int&) { return answer<2>(); }
+    answer<3> f(int&&)      { return answer<3>(); }
+
+    void
+    test()
+    {
+      int i = 0;
+      const int c = 0;
+      static_assert(decltype(f(i))::value == 1, "");
+      static_assert(decltype(f(c))::value == 2, "");
+      static_assert(decltype(f(0))::value == 3, "");
+    }
+
+  }
+
+  namespace test_uniform_initialization
+  {
+
+    struct test
+    {
+      static const int zero {};
+      static const int one {1};
+    };
+
+    static_assert(test::zero == 0, "");
+    static_assert(test::one == 1, "");
+
+  }
+
+  namespace test_lambdas
+  {
+
+    void
+    test1()
+    {
+      auto lambda1 = [](){};
+      auto lambda2 = lambda1;
+      lambda1();
+      lambda2();
+    }
+
+    int
+    test2()
+    {
+      auto a = [](int i, int j){ return i + j; }(1, 2);
+      auto b = []() -> int { return '0'; }();
+      auto c = [=](){ return a + b; }();
+      auto d = [&](){ return c; }();
+      auto e = [a, &b](int x) mutable {
+        const auto identity = [](int y){ return y; };
+        for (auto i = 0; i < a; ++i)
+          a += b--;
+        return x + identity(a + b);
+      }(0);
+      return a + b + c + d + e;
+    }
+
+    int
+    test3()
+    {
+      const auto nullary = [](){ return 0; };
+      const auto unary = [](int x){ return x; };
+      using nullary_t = decltype(nullary);
+      using unary_t = decltype(unary);
+      const auto higher1st = [](nullary_t f){ return f(); };
+      const auto higher2nd = [unary](nullary_t f1){
+        return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+      };
+      return higher1st(nullary) + higher2nd(nullary)(unary);
+    }
+
+  }
+
+  namespace test_variadic_templates
+  {
+
+    template <int...>
+    struct sum;
+
+    template <int N0, int... N1toN>
+    struct sum<N0, N1toN...>
+    {
+      static constexpr auto value = N0 + sum<N1toN...>::value;
+    };
+
+    template <>
+    struct sum<>
+    {
+      static constexpr auto value = 0;
+    };
+
+    static_assert(sum<>::value == 0, "");
+    static_assert(sum<1>::value == 1, "");
+    static_assert(sum<23>::value == 23, "");
+    static_assert(sum<1, 2>::value == 3, "");
+    static_assert(sum<5, 5, 11>::value == 21, "");
+    static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+  }
+
+  // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+  // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+  // because of this.
+  namespace test_template_alias_sfinae
+  {
+
+    struct foo {};
+
+    template<typename T>
+    using member = typename T::member_type;
+
+    template<typename T>
+    void func(...) {}
+
+    template<typename T>
+    void func(member<T>*) {}
+
+    void test();
+
+    void test() { func<foo>(0); }
+
+  }
+
+}  // namespace cxx11
+
+#endif  // __cplusplus >= 201103L
+
+]])
+
+
+dnl  Tests for new features in C++14
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
+
+// If the compiler admits that it is not ready for C++14, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201402L
+
+#error "This is not a C++14 compiler"
+
+#else
+
+namespace cxx14
+{
+
+  namespace test_polymorphic_lambdas
+  {
+
+    int
+    test()
+    {
+      const auto lambda = [](auto&&... args){
+        const auto istiny = [](auto x){
+          return (sizeof(x) == 1UL) ? 1 : 0;
+        };
+        const int aretiny[] = { istiny(args)... };
+        return aretiny[0];
+      };
+      return lambda(1, 1L, 1.0f, '1');
+    }
+
+  }
+
+  namespace test_binary_literals
+  {
+
+    constexpr auto ivii = 0b0000000000101010;
+    static_assert(ivii == 42, "wrong value");
+
+  }
+
+  namespace test_generalized_constexpr
+  {
+
+    template < typename CharT >
+    constexpr unsigned long
+    strlen_c(const CharT *const s) noexcept
+    {
+      auto length = 0UL;
+      for (auto p = s; *p; ++p)
+        ++length;
+      return length;
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("x") == 1UL, "");
+    static_assert(strlen_c("test") == 4UL, "");
+    static_assert(strlen_c("another\0test") == 7UL, "");
+
+  }
+
+  namespace test_lambda_init_capture
+  {
+
+    int
+    test()
+    {
+      auto x = 0;
+      const auto lambda1 = [a = x](int b){ return a + b; };
+      const auto lambda2 = [a = lambda1(x)](){ return a; };
+      return lambda2();
+    }
+
+  }
+
+  namespace test_digit_seperators
+  {
+
+    constexpr auto ten_million = 100'000'000;
+    static_assert(ten_million == 100000000, "");
+
+  }
+
+  namespace test_return_type_deduction
+  {
+
+    auto f(int& x) { return x; }
+    decltype(auto) g(int& x) { return x; }
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static constexpr auto value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static constexpr auto value = true;
+    };
+
+    int
+    test()
+    {
+      auto x = 0;
+      static_assert(is_same<int, decltype(f(x))>::value, "");
+      static_assert(is_same<int&, decltype(g(x))>::value, "");
+      return x;
+    }
+
+  }
+
+}  // namespace cxx14
+
+#endif  // __cplusplus >= 201402L
+
+]])
diff --git a/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4
new file mode 100644 (file)
index 0000000..09db383
--- /dev/null
@@ -0,0 +1,39 @@
+# ============================================================================
+#  http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
+# ============================================================================
+#
+# SYNOPSIS
+#
+#   AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+#   Check for baseline language coverage in the compiler for the C++11
+#   standard; if necessary, add switches to CXXFLAGS to enable support.
+#
+#   This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
+#   macro with the version set to C++11.  The two optional arguments are
+#   forwarded literally as the second and third argument respectively.
+#   Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
+#   more information.  If you want to use this macro, you also need to
+#   download the ax_cxx_compile_stdcxx.m4 file.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+#   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+#   Copyright (c) 2015 Paul Norman <penorman@mac.com>
+#   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 14
+
+include([ax_cxx_compile_stdcxx.m4])
+
+AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])])
diff --git a/m4/ax_define_dir.m4 b/m4/ax_define_dir.m4
new file mode 100644 (file)
index 0000000..b74d155
--- /dev/null
@@ -0,0 +1,49 @@
+# ===========================================================================
+#       http://www.gnu.org/software/autoconf-archive/ax_define_dir.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION])
+#
+# DESCRIPTION
+#
+#   This macro sets VARNAME to the expansion of the DIR variable, taking
+#   care of fixing up ${prefix} and such.
+#
+#   VARNAME is then offered as both an output variable and a C preprocessor
+#   symbol.
+#
+#   Example:
+#
+#     AX_DEFINE_DIR([DATADIR], [datadir], [Where data are placed to.])
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Stepan Kasal <kasal@ucw.cz>
+#   Copyright (c) 2008 Andreas Schwab <schwab@suse.de>
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2008 Alexandre Oliva
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 6
+
+AU_ALIAS([AC_DEFINE_DIR], [AX_DEFINE_DIR])
+AC_DEFUN([AX_DEFINE_DIR], [
+  prefix_NONE=
+  exec_prefix_NONE=
+  test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix
+  test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix
+dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn
+dnl refers to ${prefix}.  Thus we have to use `eval' twice.
+  eval ax_define_dir="\"[$]$2\""
+  eval ax_define_dir="\"$ax_define_dir\""
+  AC_SUBST($1, "$ax_define_dir")
+  AC_DEFINE_UNQUOTED($1, "$ax_define_dir", [$3])
+  test "$prefix_NONE" && prefix=NONE
+  test "$exec_prefix_NONE" && exec_prefix=NONE
+])
diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4
new file mode 100644 (file)
index 0000000..6d400ed
--- /dev/null
@@ -0,0 +1,317 @@
+# ===========================================================================
+#        http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+#
+# DESCRIPTION
+#
+#   This macro figures out how to build C programs using POSIX threads. It
+#   sets the PTHREAD_LIBS output variable to the threads library and linker
+#   flags, and the PTHREAD_CFLAGS output variable to any special C compiler
+#   flags that are needed. (The user can also force certain compiler
+#   flags/libs to be tested by setting these environment variables.)
+#
+#   Also sets PTHREAD_CC to any special C compiler that is needed for
+#   multi-threaded programs (defaults to the value of CC otherwise). (This
+#   is necessary on AIX to use the special cc_r compiler alias.)
+#
+#   NOTE: You are assumed to not only compile your program with these flags,
+#   but also link it with them as well. e.g. you should link with
+#   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#
+#   If you are only building threads programs, you may wish to use these
+#   variables in your default LIBS, CFLAGS, and CC:
+#
+#     LIBS="$PTHREAD_LIBS $LIBS"
+#     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+#     CC="$PTHREAD_CC"
+#
+#   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+#   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
+#   (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#
+#   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+#   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+#   PTHREAD_CFLAGS.
+#
+#   ACTION-IF-FOUND is a list of shell commands to run if a threads library
+#   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
+#   is not found. If ACTION-IF-FOUND is not specified, the default action
+#   will define HAVE_PTHREAD.
+#
+#   Please let the authors know if this macro fails on any platform, or if
+#   you have any other suggestions or comments. This macro was based on work
+#   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
+#   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
+#   Alejandro Forero Cuervo to the autoconf macro repository. We are also
+#   grateful for the helpful feedback of numerous users.
+#
+#   Updated for Autoconf 2.68 by Daniel Richard G.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+#   Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 20
+
+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+AC_DEFUN([AX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_PUSH([C])
+ax_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test x"$ax_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+#      ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case ${host_os} in
+        solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+
+        ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
+        ;;
+
+        darwin*)
+        ax_pthread_flags="-pthread $ax_pthread_flags"
+        ;;
+esac
+
+if test x"$ax_pthread_ok" = xno; then
+for flag in $ax_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+                pthread-config)
+                AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
+                if test x"$ax_pthread_config" = xno; then continue; fi
+                PTHREAD_CFLAGS="`pthread-config --cflags`"
+                PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+                ;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+                        static void routine(void *a) { a = 0; }
+                        static void *start_routine(void *a) { return a; }],
+                       [pthread_t th; pthread_attr_t attr;
+                        pthread_create(&th, 0, start_routine, 0);
+                        pthread_join(th, 0);
+                        pthread_attr_init(&attr);
+                        pthread_cleanup_push(routine, 0);
+                        pthread_cleanup_pop(0) /* ; */])],
+                [ax_pthread_ok=yes],
+                [])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test "x$ax_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$ax_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+        AC_MSG_CHECKING([for joinable pthread attribute])
+        attr_name=unknown
+        for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+            AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+                           [int attr = $attr; return attr /* ; */])],
+                [attr_name=$attr; break],
+                [])
+        done
+        AC_MSG_RESULT($attr_name)
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        flag=no
+        case ${host_os} in
+            aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
+            osf* | hpux*) flag="-D_REENTRANT";;
+            solaris*)
+            if test "$GCC" = "yes"; then
+                flag="-D_REENTRANT"
+            else
+                flag="-mt -D_REENTRANT"
+            fi
+            ;;
+        esac
+        AC_MSG_RESULT(${flag})
+        if test "x$flag" != xno; then
+            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+        fi
+
+        AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+            ax_cv_PTHREAD_PRIO_INHERIT, [
+                AC_LINK_IFELSE([
+                    AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=no])
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
+            AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: compile with *_r variant
+        if test "x$GCC" != xyes; then
+            case $host_os in
+                aix*)
+                AS_CASE(["x/$CC"],
+                  [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
+                  [#handle absolute path differently from PATH based program lookup
+                   AS_CASE(["x$CC"],
+                     [x/*],
+                     [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
+                     [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
+                ;;
+            esac
+        fi
+fi
+
+test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$ax_pthread_ok" = xyes; then
+        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+        :
+else
+        ax_pthread_ok=no
+        $2
+fi
+AC_LANG_POP
+])dnl AX_PTHREAD
diff --git a/m4/ax_tls.m4 b/m4/ax_tls.m4
new file mode 100644 (file)
index 0000000..033e3b1
--- /dev/null
@@ -0,0 +1,76 @@
+# ===========================================================================
+#          http://www.gnu.org/software/autoconf-archive/ax_tls.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_TLS([action-if-found], [action-if-not-found])
+#
+# DESCRIPTION
+#
+#   Provides a test for the compiler support of thread local storage (TLS)
+#   extensions. Defines TLS if it is found. Currently knows about GCC/ICC
+#   and MSVC. I think SunPro uses the same as GCC, and Borland apparently
+#   supports either.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Alan Woodland <ajw05@aber.ac.uk>
+#   Copyright (c) 2010 Diego Elio Petteno` <flameeyes@gmail.com>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 10
+
+AC_DEFUN([AX_TLS], [
+  AC_MSG_CHECKING(for thread local storage (TLS) class)
+  AC_CACHE_VAL(ac_cv_tls, [
+    ax_tls_keywords="__thread __declspec(thread) none"
+    for ax_tls_keyword in $ax_tls_keywords; do
+       AS_CASE([$ax_tls_keyword],
+          [none], [ac_cv_tls=none ; break],
+          [AC_TRY_COMPILE(
+              [#include <stdlib.h>
+               static void
+               foo(void) {
+               static ] $ax_tls_keyword [ int bar;
+               exit(1);
+               }],
+               [],
+               [ac_cv_tls=$ax_tls_keyword ; break],
+               ac_cv_tls=none
+           )])
+    done
+  ])
+  AC_MSG_RESULT($ac_cv_tls)
+
+  AS_IF([test "$ac_cv_tls" != "none"],
+    AC_DEFINE_UNQUOTED([TLS], $ac_cv_tls, [If the compiler supports a TLS storage class define it to that here])
+      m4_ifnblank([$1], [$1]),
+    m4_ifnblank([$2], [$2])
+  )
+])
diff --git a/m4/orc.m4 b/m4/orc.m4
new file mode 100644 (file)
index 0000000..983d462
--- /dev/null
+++ b/m4/orc.m4
@@ -0,0 +1,69 @@
+dnl pkg-config-based checks for Orc
+
+dnl specific:
+dnl ORC_CHECK([REQUIRED_VERSION])
+
+AC_DEFUN([ORC_CHECK],
+[
+  ORC_REQ=ifelse([$1], , "0.4.6", [$1])
+
+  AC_ARG_ENABLE(orc,
+  AC_HELP_STRING([--enable-orc],[use Orc if installed]),
+  [case "${enableval}" in
+    auto) enable_orc=auto ;;
+    yes) enable_orc=yes ;;
+    no)  enable_orc=no ;;
+    *) AC_MSG_ERROR(bad value ${enableval} for --enable-orc) ;;
+  esac
+  ],
+  [enable_orc=auto]) dnl Default value
+
+  if test "x$enable_orc" != "xno" ; then
+    PKG_CHECK_MODULES(ORC, orc-0.4 >= $ORC_REQ, [
+      AC_DEFINE(HAVE_ORC, 1, [Use Orc])
+      HAVE_ORC=yes
+      if test "x$ORCC" = "x" ; then
+        AC_MSG_CHECKING(for usable orcc)
+        ORCC=`$PKG_CONFIG --variable=orcc orc-0.4`
+        dnl check whether the orcc found by pkg-config can be run from the build environment
+        dnl if this is not the case (e.g. when cross-compiling) fall back to orcc from PATH
+        AS_IF([$ORCC --version 1> /dev/null 2> /dev/null], [], [ORCC=`which orcc`])
+        AC_MSG_RESULT($ORCC)
+      fi
+      AC_SUBST(ORCC)
+      ORCC_FLAGS="--compat $ORC_REQ"
+      AC_SUBST(ORCC_FLAGS)
+      AS_IF([test "x$ORCC" = "x"], [HAVE_ORCC=no], [HAVE_ORCC=yes])
+    ], [
+      if test "x$enable_orc" = "xyes" ; then
+        AC_MSG_ERROR([--enable-orc specified, but Orc >= $ORC_REQ not found])
+      fi
+      AC_DEFINE(DISABLE_ORC, 1, [Disable Orc])
+      HAVE_ORC=no
+      HAVE_ORCC=no
+    ])
+  else
+    AC_DEFINE(DISABLE_ORC, 1, [Disable Orc])
+    HAVE_ORC=no
+    HAVE_ORCC=no
+  fi
+  AM_CONDITIONAL(HAVE_ORC, [test "x$HAVE_ORC" = "xyes"])
+  AM_CONDITIONAL(HAVE_ORCC, [test "x$HAVE_ORCC" = "xyes"])
+
+]))
+
+AC_DEFUN([ORC_OUTPUT],
+[
+  if test "$HAVE_ORC" = yes ; then
+    printf "configure: *** Orc acceleration enabled.\n"
+  else
+    if test "x$enable_orc" = "xno" ; then
+      printf "configure: *** Orc acceleration disabled by --disable-orc.  Slower code paths\n"
+      printf "               will be used.\n"
+    else
+      printf "configure: *** Orc acceleration disabled.  Requires Orc >= $ORC_REQ, which was\n"
+      printf "               not found.  Slower code paths will be used.\n"
+    fi
+  fi
+  printf "\n"
+])
diff --git a/man/.gitignore b/man/.gitignore
new file mode 100644 (file)
index 0000000..abf96d9
--- /dev/null
@@ -0,0 +1,6 @@
+/*.1
+/*.1.xml
+/*.5
+/*.5.xml
+/Makefile
+/Makefile.in
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644 (file)
index 0000000..d4f4edb
--- /dev/null
@@ -0,0 +1,97 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+DISTCLEANFILES = \
+       $(noinst_DATA)
+
+noinst_DATA = \
+       pulseaudio.1.xml \
+       esdcompat.1.xml \
+       pax11publish.1.xml \
+       pacat.1.xml \
+       pacmd.1.xml \
+       pactl.1.xml \
+       pasuspender.1.xml \
+       padsp.1.xml \
+       pulse-daemon.conf.5.xml \
+       pulse-client.conf.5.xml \
+       default.pa.5.xml \
+       pulse-cli-syntax.5.xml \
+       start-pulseaudio-x11.1.xml
+
+xmllint: $(noinst_DATA)
+       for f in $(noinst_DATA) ; do \
+                       xmllint --noout --valid "$$f" || exit 1 ; \
+       done
+
+if BUILD_MANPAGES
+
+dist_man_MANS = \
+       pulseaudio.1 \
+       esdcompat.1 \
+       pax11publish.1 \
+       pacat.1 \
+       pacmd.1 \
+       pactl.1 \
+       pasuspender.1 \
+       padsp.1 \
+       pulse-daemon.conf.5 \
+       pulse-client.conf.5 \
+       default.pa.5 \
+       pulse-cli-syntax.5 \
+       start-pulseaudio-x11.1
+
+CLEANFILES = \
+       $(dist_man_MANS)
+
+%: %.xml Makefile
+       $(AM_V_GEN) perl $(srcdir)/xmltoman $< > $@ || rm -f $@
+
+if OS_IS_WIN32
+SYMLINK_PROGRAM=cd $(DESTDIR)$(man1dir) && cp
+else
+SYMLINK_PROGRAM=ln -sf
+endif
+install-data-hook:
+       $(SYMLINK_PROGRAM) pacat.1 $(DESTDIR)$(man1dir)/paplay.1
+       $(SYMLINK_PROGRAM) pacat.1 $(DESTDIR)$(man1dir)/parec.1
+       $(SYMLINK_PROGRAM) pacat.1 $(DESTDIR)$(man1dir)/parecord.1
+       $(SYMLINK_PROGRAM) pacat.1 $(DESTDIR)$(man1dir)/pamon.1
+
+uninstall-hook:
+       rm -f $(DESTDIR)$(man1dir)/paplay.1
+       rm -f $(DESTDIR)$(man1dir)/parec.1
+       rm -f $(DESTDIR)$(man1dir)/parecord.1
+       rm -f $(DESTDIR)$(man1dir)/pamon.1
+endif
+
+EXTRA_DIST = \
+       pulseaudio.1.xml.in \
+       esdcompat.1.xml.in \
+       pax11publish.1.xml.in \
+       pacat.1.xml.in \
+       pacmd.1.xml.in \
+       pactl.1.xml.in \
+       pasuspender.1.xml.in \
+       padsp.1.xml.in \
+       pulse-daemon.conf.5.xml.in \
+       pulse-client.conf.5.xml.in \
+       default.pa.5.xml.in \
+       pulse-cli-syntax.5.xml.in \
+       start-pulseaudio-x11.1.xml.in \
+       xmltoman \
+       xmltoman.css \
+       xmltoman.xsl \
+       xmltoman.dtd
diff --git a/man/default.pa.5.xml.in b/man/default.pa.5.xml.in
new file mode 100644 (file)
index 0000000..38bb725
--- /dev/null
@@ -0,0 +1,58 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="default.pa" section="5" desc="PulseAudio Sound Server Startup Script">
+
+  <synopsis>
+    <p><file>~/.config/pulse/default.pa</file></p>
+    <p><file>@PA_DEFAULT_CONFIG_DIR@/default.pa</file></p>
+    <p><file>@PA_DEFAULT_CONFIG_DIR@/system.pa</file></p>
+  </synopsis>
+
+  <description>
+    <p>The PulseAudio sound server interprets a configuration script on
+    startup, which is mainly used to define the set of modules to load. When
+    PulseAudio runs in the per-user mode and
+    <file>~/.config/pulse/default.pa</file> exists, that file is used. When
+    PulseAudio runs in the per-user mode and that file doesn't exist,
+    <file>@PA_DEFAULT_CONFIG_DIR@/default.pa</file> is used. When PulseAudio
+    runs as a system service, <file>@PA_DEFAULT_CONFIG_DIR@/system.pa</file> is
+    used.</p>
+
+    <p>The script should contain directives in the PulseAudio CLI language, as
+    documented in <manref name="pulse-cli-syntax" section="5"/>.</p>
+  </description>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;;
+    PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulse-cli-syntax" section="5"/>,
+      <manref name="pulse-daemon.conf" section="5"/>,
+      <manref name="pulseaudio" section="1"/>,
+      <manref name="pacmd" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/esdcompat.1.xml.in b/man/esdcompat.1.xml.in
new file mode 100644 (file)
index 0000000..36f6667
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="esdcompat" section="1" desc="PulseAudio ESD wrapper script">
+
+  <synopsis>
+    <cmd>esdcompat [<arg>options</arg>]</cmd>
+    <cmd>esdcompat <opt>--help</opt></cmd>
+    <cmd>esdcompat <opt>--version</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p><file>esdcompat</file> is a compatibility script that takes the
+    same arguments as the ESD sound daemon <manref name="esd"
+    section="1"/>, but uses them to start a the PulseAudio sound server with the appropriate parameters. It is
+    required to make PulseAudio a drop-in replacement for esd, i.e. it
+    can be used to make <manref name="gnome-session" section="1"/>
+    start up PulseAudio instead of esd.</p>
+
+    <p>It is recommended to make <file>esd</file> a symbolic link to this script.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-tcp | -promiscuous | -d | -b | -r | -as | -unix | -public | -terminate | -nobeeps | -trust | -port | -bind</opt></p>
+
+      <optdesc><p>These options understood by the original
+      <file>esd</file> are ignored by
+      <file>esdcompat</file>.</p></optdesc>
+
+    </option>
+
+
+    <option>
+      <p><opt>-spawnpid | -spawnfd</opt></p>
+
+      <optdesc><p>These internally used options understood by the
+      original <file>esd</file> are properly handled by
+      <file>esdcompat</file>, however are not to be used
+      manually.</p></optdesc>
+
+    </option>
+
+  </options>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="esd" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pacat.1.xml.in b/man/pacat.1.xml.in
new file mode 100644 (file)
index 0000000..fb63e93
--- /dev/null
@@ -0,0 +1,263 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="pacat" section="1" desc="Play back or record raw or encoded audio streams on a PulseAudio sound server">
+
+  <synopsis>
+    <cmd>paplay [<arg>options</arg>] [<arg>FILE</arg>]</cmd>
+    <cmd>parecord [<arg>options</arg>] [<arg>FILE</arg>]</cmd>
+    <cmd>pacat [<arg>options</arg>] [<arg>FILE</arg>]</cmd>
+    <cmd>parec [<arg>options</arg>] [<arg>FILE</arg>]</cmd>
+    <cmd>pamon [<arg>options</arg>] [<arg>FILE</arg>]</cmd>
+    <cmd>pacat <opt>--help</opt></cmd>
+    <cmd>pacat <opt>--version</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p><file>pacat</file> is a simple tool for playing back or
+    capturing raw or encoded audio files on a PulseAudio sound
+    server. It understands all audio file formats supported by
+    <file>libsndfile</file>.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-r | --record</opt></p>
+
+      <optdesc><p>Capture audio data and write it to the specified file or to STDOUT if none is specified. If the tool is called under the name <file>parec</file> this is the default.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-p | --playback</opt></p>
+
+      <optdesc><p>Read audio data from the specified file or STDIN if none is specified, and play it back. If the tool is called under the name <file>pacat</file> this is the default.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-v | --verbose</opt></p>
+
+      <optdesc><p>Enable verbose operation. Dumps the current playback time to STDERR during playback/capturing.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-s | --server</opt><arg>=SERVER</arg></p>
+
+      <optdesc><p>Choose the server to connect to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-d | --device</opt><arg>=SINKORSOURCE</arg></p>
+
+      <optdesc><p>Specify the symbolic name of the sink/source to play/record this stream on/from.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--monitor-stream</opt><arg>=INDEX</arg></p>
+
+      <optdesc><p>Record from the sink input with index INDEX.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-n | --client-name</opt><arg>=NAME</arg></p>
+
+      <optdesc><p>Specify the client name <file>paplay</file> shall pass to the server when connecting.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--stream-name</opt><arg>=NAME</arg></p>
+
+      <optdesc><p>Specify the stream name <file>paplay</file> shall pass to the server when creating the stream.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--volume</opt><arg>=VOLUME</arg></p>
+
+      <optdesc><p>Specify the initial playback volume to use. Choose a value between 0 (silent) and 65536 (100% volume).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--rate</opt><arg>=SAMPLERATE</arg></p>
+
+      <optdesc><p>Capture or play back audio with the specified sample rate. Defaults to 44100 Hz.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--format</opt><arg>=FORMAT</arg></p>
+
+      <optdesc><p>Capture or play back audio with the specified sample
+      format. Specify one of <opt>u8</opt>, <opt>s16le</opt>,
+      <opt>s16be</opt>, <opt>s32le</opt>, <opt>s32be</opt>,
+      <opt>float32le</opt>, <opt>float32be</opt>, <opt>ulaw</opt>,
+      <opt>alaw</opt>, <opt>s32le</opt>, <opt>s32be</opt>,
+      <opt>s24le</opt>, <opt>s24be</opt>, <opt>s24-32le</opt>,
+      <opt>s24-32be</opt>. Depending on the endianness of the CPU the
+      formats <opt>s16ne</opt>, <opt>s16re</opt>, <opt>s32ne</opt>,
+      <opt>s32re</opt>, <opt>float32ne</opt>, <opt>float32re</opt>,
+      <opt>s32ne</opt>, <opt>s32re</opt>, <opt>s24ne</opt>,
+      <opt>s24re</opt>, <opt>s24-32ne</opt>, <opt>s24-32re</opt> (for
+      native, resp. reverse endian) are available as aliases. Defaults
+      to s16ne.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--channels</opt><arg>=CHANNELS</arg></p>
+
+      <optdesc><p>Capture or play back audio with the specified number
+      of channels. If more than two channels are used it is
+      recommended to use the <opt>--channel-map</opt> option
+      below. Defaults to 2.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--channel-map</opt><arg>=CHANNELMAP</arg></p>
+
+      <optdesc><p>Explicitly choose a channel map when playing back
+      this stream. The argument should be a comma separated list of
+      channel names: <opt>front-left</opt>, <opt>front-right</opt>,
+      <opt>mono</opt>, <opt>front-center</opt>, <opt>rear-left</opt>,
+      <opt>rear-right</opt>, <opt>rear-center</opt>, <opt>lfe</opt>,
+      <opt>front-left-of-center</opt>,
+      <opt>front-right-of-center</opt>, <opt>side-left</opt>,
+      <opt>side-right</opt>, <opt>top-center</opt>,
+      <opt>top-front-center</opt>, <opt>top-front-left</opt>,
+      <opt>top-front-right</opt>, <opt>top-rear-left</opt>,
+      <opt>top-rear-right</opt>, <opt>top-rear-center</opt>, or any of
+      the 32 auxiliary channel names <opt>aux0</opt> to
+      <opt>aux31</opt>.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--fix-format</opt></p>
+      <optdesc><p>If passed, the sample format of the stream is changed to the native format of the sink the stream is connected to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--fix-rate</opt></p>
+      <optdesc><p>If passed, the sampling rate of the stream is changed to the native rate of the sink the stream is connected to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--fix-channels</opt></p>
+      <optdesc><p>If passed, the number of channels and the channel map of the stream is changed to the native number of channels and the native channel map of the sink the stream is connected to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--no-remix</opt></p>
+      <optdesc><p>Never upmix or downmix channels.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--no-remap</opt></p>
+      <optdesc><p>Never remap channels. Instead of mapping channels by their name this will match them solely by their index/order.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--latency</opt><arg>=BYTES</arg></p>
+      <optdesc><p>Explicitly configure the latency, with a time
+      specified in bytes in the selected sample format. If left out
+      the server will pick the latency, usually relatively high for
+      power saving reasons. Use either this option or
+      <opt>--latency-msec</opt>, but not both.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--latency-msec</opt><arg>=MSEC</arg></p>
+      <optdesc><p>Explicitly configure the latency, with a time
+      specified in milliseconds. If left out the server will pick the
+      latency, usually relatively high for power saving reasons. Use
+      either this option or <opt>--latency</opt>, but not
+      both.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--process-time</opt><arg>=BYTES</arg></p>
+      <optdesc><p>Explicitly configure the process time, with a time
+      specified in bytes in the selected sample format. If left out
+      the server will pick the process time. Use either this option or
+      <opt>--process-time-msec</opt>, but not both.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--process-time-msec</opt><arg>=MSEC</arg></p>
+      <optdesc><p>Explicitly configure the process time, with a time
+      specified in miliseconds. If left out the server will pick the
+      process time. Use either this option or <opt>--process-time</opt>,
+      but not both.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--property</opt><arg>=PROPERTY=VALUE</arg></p>
+      <optdesc><p>Attach a property to the client and stream. May be
+      used multiple times</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--raw</opt></p>
+      <optdesc><p>Play/record raw audio data. This is the default if
+      this program is invoked as <cmd>pacat</cmd>, <cmd>parec</cmd> or
+      <cmd>pamon</cmd>.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--file-format</opt><arg>[=FFORMAT]</arg></p>
+      <optdesc><p>Play/record encoded audio data in the file format
+      specified. This is the default if this program is invoked as
+      <cmd>paplay</cmd> and <cmd>parecord</cmd>.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--list-file-formats</opt></p>
+      <optdesc><p>List supported file formats.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Limitations">
+    <p>Due to a limitation in <file>libsndfile</file>
+    <file>paplay</file> currently does not always set the correct channel
+    mapping for playback of multichannel (i.e. surround) audio files, even
+    if the channel mapping information is available in the audio file.</p>
+  </section>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="pactl" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pacmd.1.xml.in b/man/pacmd.1.xml.in
new file mode 100644 (file)
index 0000000..b11eb24
--- /dev/null
@@ -0,0 +1,73 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="pacmd" section="1" desc="Reconfigure a PulseAudio sound server during runtime">
+
+  <synopsis>
+    <cmd>pacmd</cmd>
+    <cmd>pacmd <opt>--help</opt></cmd>
+    <cmd>pacmd <opt>--version</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p>This tool can be used to introspect or reconfigure a running
+    PulseAudio sound server during runtime. It connects to the sound
+    server and offers a simple live shell that can be used to enter
+    the commands also understood in the <file>default.pa</file>
+    configuration scripts.</p>
+
+    <p>To exit the live shell, use ctrl+d. Note that the 'exit' command
+    inside the shell will tell the PulseAudio daemon itself to shutdown!</p>
+
+    <p>If any arguments are passed on the command line, they will be
+    passed into the live shell which will process the command and exit.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulse-cli-syntax" section="5"/>,
+      <manref name="pulseaudio" section="1"/>,
+      <manref name="pactl" section="1"/>,
+      <manref name="default.pa" section="5"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in
new file mode 100644 (file)
index 0000000..39569b6
--- /dev/null
@@ -0,0 +1,266 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="pactl" section="1" desc="Control a running PulseAudio sound server">
+
+  <synopsis>
+    <cmd>pactl [<arg>options</arg>] <arg>COMMAND</arg> [<arg>ARGS ...</arg>]</cmd>
+    <cmd>pactl <opt>--help</opt></cmd>
+    <cmd>pactl <opt>--version</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p><file>pactl</file> can be used to issue control commands to the PulseAudio sound server.</p>
+
+    <p><file>pactl</file> only exposes a subset of the available operations. For the full set use the <manref name="pacmd" section="1"/>.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-s | --server</opt><arg>=SERVER</arg></p>
+
+      <optdesc><p>Choose the server to connect to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-n | --client-name</opt><arg>=NAME</arg></p>
+
+      <optdesc><p>Specify the client name <file>pactl</file> shall pass to the server when connecting.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Commands">
+
+    <option>
+      <p><opt>stat</opt></p>
+      <optdesc><p>Dump a few statistics about the memory usage of the PulseAudio daemon.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>info</opt></p>
+      <optdesc><p>Dump some info about the PulseAudio daemon.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>list</opt> [<arg>short</arg>] [<arg>TYPE</arg>]</p>
+      <optdesc><p>Dump all currently loaded modules, available sinks, sources, streams, etc.  <arg>TYPE</arg> must be one of:
+      modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards.  If not specified, all info is listed.  If
+      short is given, output is in a tabular format, for easy parsing by scripts.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>exit</opt></p>
+      <optdesc><p>Asks the PulseAudio server to terminate.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>upload-sample</opt> <arg>FILENAME</arg> [<arg>NAME</arg>]</p>
+      <optdesc><p>Upload a sound from the specified audio file into
+      the sample cache. The file types supported are those understood
+      by <file>libsndfile</file>. The sample in the cache is named
+      after the audio file, unless the name is explicitly
+      specified.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>play-sample</opt> <arg>NAME</arg> [<arg>SINK</arg>]</p>
+      <optdesc><p>Play the specified sample from the sample cache. It
+      is played on the default sink, unless the symbolic name or the
+      numerical index of the sink to play it on is
+      specified.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>remove-sample</opt> <arg>NAME</arg></p>
+      <optdesc><p>Remove the specified sample from the sample cache.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>load-module</opt> <arg>NAME</arg> [<arg>ARGUMENTS ...</arg>]</p>
+      <optdesc><p>Load the specified module with the specified arguments into the running sound server.
+      Prints the numeric index of the module just loaded to STDOUT. You can use it to unload the module later.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>unload-module</opt> <arg>ID|NAME</arg></p>
+      <optdesc><p>Unload the module instance identified by the specified numeric index or unload all modules by the specified name.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>move-sink-input</opt> <arg>ID</arg> <arg>SINK</arg></p>
+      <optdesc><p>Move the specified playback stream (identified by its numerical index) to the specified sink (identified by its symbolic name or numerical index).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>move-source-output</opt> <arg>ID</arg> <arg>SOURCE</arg></p>
+      <optdesc><p>Move the specified recording stream (identified by its numerical index) to the specified source (identified by its symbolic name or numerical index).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>suspend-sink</opt> <arg>SINK</arg> <arg>1|0</arg></p>
+      <optdesc><p>Suspend or resume the specified sink (which may be
+      specified either by its symbolic name, or by its numeric index),
+      depending whether 1 (suspend) or 0 (resume) is passed as last
+      argument. Suspending a sink will pause all playback. Depending
+      on the module implementing the sink this might have the effect
+      that the underlying device is closed, making it available for
+      other applications to use. The exact behaviour depends on the
+      module.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>suspend-source</opt> <arg>SOURCE</arg> <arg>1|0</arg></p>
+      <optdesc><p>Suspend or resume the specified source (which may be
+      specified either by its symbolic name, or by its numeric index),
+      depending whether 1 (suspend) or 0 (resume) is passed as last
+      argument. Suspending a source will pause all
+      capturing. Depending on the module implementing the source this
+      might have the effect that the underlying device is closed,
+      making it available for other applications to use. The exact
+      behaviour depends on the module.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-card-profile</opt> <arg>CARD</arg> <arg>PROFILE</arg></p>
+      <optdesc><p>Set the specified card (identified by its symbolic name or numerical index) to the specified profile (identified by its symbolic name).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-default-sink</opt> <arg>SINK</arg></p>
+      <optdesc><p>Make the specified sink (identified by its symbolic name) the default sink.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-sink-port</opt> <arg>SINK</arg> <arg>PORT</arg></p>
+      <optdesc><p>Set the specified sink (identified by its symbolic name or numerical index) to the specified port (identified by its symbolic name).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-default-source</opt> <arg>SOURCE</arg></p>
+      <optdesc><p>Make the specified source (identified by its symbolic name) the default source.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-source-port</opt> <arg>SOURCE</arg> <arg>PORT</arg></p>
+      <optdesc><p>Set the specified source (identified by its symbolic name or numerical index) to the specified port (identified by its symbolic name).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-port-latency-offset</opt> <arg>CARD</arg> <arg>PORT</arg> <arg>OFFSET</arg></p>
+      <optdesc><p>Set a latency offset to a specified port (identified by its symbolic name) that belongs to a card (identified by its symbolic name or numerical index).
+      <arg>OFFSET</arg> is a number which represents the latency offset in microseconds</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-sink-volume</opt> <arg>SINK</arg> <arg>VOLUME [VOLUME ...]</arg></p>
+      <optdesc><p>Set the volume of the specified sink (identified by its symbolic name or numerical index).
+      <arg>VOLUME</arg> can be specified as an integer (e.g. 2000, 16384), a linear factor (e.g. 0.4, 1.100), a percentage
+      (e.g.  10%, 100%) or a decibel value (e.g. 0dB, 20dB).  If the volume specification start with a + or - the volume
+      adjustment will be relative to the current sink volume.  A single volume value affects all channels; if multiple
+      volume values are given their number has to match the sink's number of channels.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-source-volume</opt> <arg>SOURCE</arg> <arg>VOLUME [VOLUME ...]</arg></p>
+      <optdesc><p>Set the volume of the specified source (identified by its symbolic name or numerical index).
+      <arg>VOLUME</arg> can be specified as an integer (e.g. 2000, 16384), a linear factor (e.g. 0.4, 1.100), a percentage
+      (e.g.  10%, 100%) or a decibel value (e.g. 0dB, 20dB).  If the volume specification start with a + or - the volume
+      adjustment will be relative to the current source volume.  A single volume value affects all channels; if multiple
+      volume values are given their number has to match the source's number of channels.</p></optdesc> </option>
+
+    <option>
+      <p><opt>set-sink-input-volume</opt> <arg>INPUT</arg> <arg>VOLUME [VOLUME ...]</arg></p>
+      <optdesc><p>Set the volume of the specified sink input (identified by its numerical index).
+      <arg>VOLUME</arg> can be specified as an integer (e.g. 2000, 16384), a linear factor (e.g. 0.4, 1.100), a percentage
+      (e.g.  10%, 100%) or a decibel value (e.g. 0dB, 20dB).  If the volume specification start with a + or - the volume
+      adjustment will be relative to the current sink input volume.  A single volume value affects all channels; if multiple
+      volume values are given their number has to match the sink input's number of channels.</p></optdesc> </option>
+
+    <option>
+      <p><opt>set-source-output-volume</opt> <arg>OUTPUT</arg> <arg>VOLUME [VOLUME ...]</arg></p>
+      <optdesc><p>Set the volume of the specified source output (identified by its numerical index).
+      <arg>VOLUME</arg> can be specified as an integer (e.g. 2000, 16384), a linear factor (e.g. 0.4, 1.100), a percentage
+      (e.g.  10%, 100%) or a decibel value (e.g. 0dB, 20dB).  If the volume specification start with a + or - the volume
+      adjustment will be relative to the current source output volume.  A single volume value affects all channels; if multiple
+      volume values are given their number has to match the source output's number of channels.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-sink-mute</opt> <arg>SINK</arg> <arg>1|0|toggle</arg></p>
+      <optdesc><p>Set the mute status of the specified sink (identified by its symbolic name or numerical index).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-source-mute</opt> <arg>SOURCE</arg> <arg>1|0|toggle</arg></p>
+      <optdesc><p>Set the mute status of the specified source (identified by its symbolic name or numerical index).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-sink-input-mute</opt> <arg>INPUT</arg> <arg>1|0|toggle</arg></p>
+      <optdesc><p>Set the mute status of the specified sink input (identified by its numerical index).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-source-output-mute</opt> <arg>OUTPUT</arg> <arg>1|0|toggle</arg></p>
+      <optdesc><p>Set the mute status of the specified source output (identified by its numerical index).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-sink-formats</opt> <arg>SINK</arg> <arg>FORMATS</arg></p>
+      <optdesc><p>Set the supported formats of the specified sink (identified by its numerical index) if supported by the sink.
+      <arg>FORMATS</arg> is specified as a semi-colon (;) separated list of formats in the form
+      'encoding[, key1=value1, key2=value2, ...]' (for example, AC3 at 32000, 44100 and 48000 Hz would be specified as
+      'ac3-iec61937, format.rate = "[ 32000, 44100, 48000 ]"').
+      </p></optdesc> </option>
+
+    <option>
+      <p><opt>subscribe</opt></p>
+      <optdesc><p>Subscribe to events, pactl does not exit by itself, but keeps waiting for new events.</p></optdesc>
+    </option>
+
+  </section>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <seealso>
+    <p>
+      <manref name="pulseaudio" section="1"/>,
+      <manref name="pacmd" section="1"/>
+    </p>
+  </seealso>
+
+</manpage>
diff --git a/man/padsp.1.xml.in b/man/padsp.1.xml.in
new file mode 100644 (file)
index 0000000..2ee1e11
--- /dev/null
@@ -0,0 +1,108 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="padsp" section="1" desc="PulseAudio OSS Wrapper">
+
+  <synopsis>
+    <cmd>padsp [<arg>options</arg>] <arg>PROGRAM</arg> [<arg>ARGUMENTS ...</arg>]</cmd>
+    <cmd>padsp <opt>-h</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p><file>padsp</file> starts the specified program and
+    redirects its access to OSS compatible audio devices
+    (<file>/dev/dsp</file> and auxiliary devices) to a PulseAudio
+    sound server.</p>
+
+    <p><file>padsp</file> uses the $LD_PRELOAD environment variable
+    that is interpreted by <manref name="ld.so" section="8"/> and thus
+    does not work for SUID binaries and statically built
+    executables.</p>
+
+    <p>Equivalent to using <file>padsp</file> is starting an
+    application with $LD_PRELOAD set to
+    <file>libpulsedsp.so</file></p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+
+    <option>
+      <p><opt>-s</opt> <arg>SERVER</arg></p>
+
+      <optdesc><p>Set the PulseAudio server to connect to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-n</opt> <arg>NAME</arg></p>
+
+      <optdesc><p>The client application name that shall be passed to the server when connecting.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-m</opt> <arg>NAME</arg></p>
+
+      <optdesc><p>The stream name that shall be passed to the server when creating a stream.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-M</opt></p>
+
+      <optdesc><p>Disable <file>/dev/mixer</file> emulation.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-S</opt></p>
+
+      <optdesc><p>Disable <file>/dev/sndstat</file> emulation.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-D</opt></p>
+
+      <optdesc><p>Disable <file>/dev/dsp</file> emulation.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-d</opt></p>
+
+      <optdesc><p>Enable debug output.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="pasuspender" section="1"/>, <manref name="ld.so" section="8"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pasuspender.1.xml.in b/man/pasuspender.1.xml.in
new file mode 100644 (file)
index 0000000..086e175
--- /dev/null
@@ -0,0 +1,78 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="pasuspender" section="1" desc="Temporarily suspend PulseAudio">
+
+  <synopsis>
+    <cmd>pasuspender [<arg>options</arg>] -- <arg>PROGRAM</arg> [<arg>ARGUMENTS ...</arg>]</cmd>
+    <cmd>pasuspender <opt>--help</opt></cmd>
+    <cmd>pasuspender <opt>--version</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p><file>pasuspender</file> is a tool that can be used to tell a
+    local PulseAudio sound server to temporarily suspend access to the
+    audio devices, to allow other
+    applications access them directly. <file>pasuspender</file> will
+    suspend access to the audio devices, fork a child process, and
+    when the child process terminates, resume access again.</p>
+
+    <p>Make sure to include <opt>--</opt> in
+    your <file>pasuspender</file> command line before passing the
+    subprocess command line (as shown
+    above). Otherwise <file>pasuspender</file> itself might end up
+    interpreting the command line switches and options you intended to
+    pass to the subprocess.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-s | --server=</opt><arg>SERVER</arg></p>
+
+      <optdesc><p>Specify the sound server to connect to.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="padsp" section="1"/>, <manref name="pacmd" section="1"/>, <manref name="pactl" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pax11publish.1.xml.in b/man/pax11publish.1.xml.in
new file mode 100644 (file)
index 0000000..6f6039a
--- /dev/null
@@ -0,0 +1,149 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="pax11publish" section="1" desc="PulseAudio X11 Credential Utility">
+
+  <synopsis>
+    <cmd>pax11publish <opt>-h</opt></cmd>
+    <cmd>pax11publish [<arg>options</arg>] [<opt>-d</opt>]</cmd>
+    <cmd>pax11publish [<arg>options</arg>] <opt>-e</opt></cmd>
+    <cmd>pax11publish [<arg>options</arg>] <opt>-i</opt></cmd>
+    <cmd>pax11publish [<arg>options</arg>] <opt>-r</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p>The <file>pax11publish</file> utility can be used to dump or
+    manipulate the PulseAudio server credentials that can be stored as
+    properties on the X11 root window.</p>
+
+    <p>Please note that the loadable module
+    <file>module-x11-publish</file> exports the same information
+    directly from the PulseAudio sound server, and should in most
+    cases be used in preference over this tool.</p>
+
+    <p>Use the following command to dump the raw
+    PulseAudio-specific data that is stored in your X11 root
+    window:</p>
+
+    <p>xprop -root | grep ^PULSE_</p>
+
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-d</opt></p>
+
+      <optdesc><p>Read the PulseAudio server credentials currently set
+      on the X11 root window and dump them in a human readable form. This reads the
+      PULSE_SERVER, PULSE_SINK, PULSE_SOURCE and PULSE_COOKIE
+      properties.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-i</opt></p>
+      <optdesc><p>Similar to <opt>-d</opt>, however dumps them in a
+      Bourne shell compatible format so they may be used together with
+      the <file>eval</file> shell command to set the $PULSE_SERVER,
+      $PULSE_SINK, $PULSE_SOURCE environment variables. Also reads the
+      authentication cookie from the root window and stores it in
+      <file>~/.config/pulse/cookie</file>. </p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-e</opt></p>
+
+      <optdesc><p>Export the currently locally used sound server,
+      sink, source configuration to the X11 root window. This takes
+      the data from the $PULSE_SERVER, $PULSE_SINK, $PULSE_SOURCE
+      environment variables and combines them with the data from
+      <file>~/.config/pulse/client.conf</file> (or
+      <file>@PA_DEFAULT_CONFIG_DIR@/client.conf</file> if that file does not
+      exist). If specific options are passed on the command line
+      (<opt>-S</opt>, <opt>-O</opt>, <opt>-I</opt>, <opt>-c</opt>, see
+      below), they take precedence. Also uploads the local
+      authentication cookie <file>~/.config/pulse/cookie</file> to the X11
+      server.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-r</opt></p>
+
+      <optdesc><p>Removes the configured PulseAudio configuration from the X11 root window.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-D</opt> <arg>DISPLAY</arg></p>
+
+      <optdesc><p>Connect to the specified X11 display, instead of the default one configured in $DISPLAY.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-S</opt> <arg>SERVER</arg></p>
+
+      <optdesc><p>Only valid for <opt>-e</opt>: export the specified
+      PulseAudio server as default to the X11 display instead of the
+      one configured via local configuration.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-O</opt> <arg>SINK</arg></p>
+
+      <optdesc><p>Only valid for <opt>-e</opt>: export the specified
+      sink as default sink to the X11 display instead of the one
+      configured via local configuration.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-I</opt> <arg>SOURCE</arg></p>
+
+      <optdesc><p>Only valid for <opt>-e</opt>: export the specified
+      source as default to the X11 display instead of the one
+      configured via local configuration.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-c</opt> <arg>FILE</arg></p>
+
+      <optdesc><p>Only valid for <opt>-e</opt>: export the PulseAudio
+      authentication cookie stored in the specified file to the X11
+      display instead of the one stored in <file>~/.config/pulse/cookie</file>.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="xprop" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pulse-cli-syntax.5.xml.in b/man/pulse-cli-syntax.5.xml.in
new file mode 100644 (file)
index 0000000..0a0faba
--- /dev/null
@@ -0,0 +1,351 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="pulse-cli-syntax" section="5" desc="PulseAudio Command Line Interface Syntax">
+
+  <synopsis>
+    <p><file>~/.config/pulse/default.pa</file></p>
+    <p><file>@PA_DEFAULT_CONFIG_DIR@/default.pa</file></p>
+    <p><file>@PA_DEFAULT_CONFIG_DIR@/system.pa</file></p>
+  </synopsis>
+
+  <description>
+    <p>
+      PulseAudio provides a simple command line language used by configuration
+      scripts, the pacmd interactive shell, and the modules module-cli and
+      module-cli-protocol-{unix,tcp}. Empty lines and lines beginning with a
+      hashmark (#) are silently ignored. Several commands are supported.
+    </p>
+
+    <p>
+      Note that any boolean arguments can be given positively as '1', 't', 'y',
+      'true', 'yes' or 'on'. Likewise, negative values can be given as '0',
+      'f', 'n', 'false', 'no' or 'off'. Case is ignored.
+    </p>
+  </description>
+
+  <section name="General Commands">
+
+    <option>
+      <p><opt>help</opt></p>
+      <optdesc><p>Show a quick help on the commands available.</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Status Commands">
+    <option>
+      <p><opt>list-modules</opt></p>
+      <optdesc><p>Show all currently loaded modules with their arguments.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>list-cards</opt></p>
+      <optdesc><p>Show all currently registered cards</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>list-sinks</opt> or <opt>list-sources</opt></p>
+      <optdesc><p>Show all currently registered sinks (resp. sources).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>list-clients</opt></p>
+      <optdesc><p>Show all currently active clients.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>list-sink-inputs</opt> or <opt>list-source-outputs</opt></p>
+      <optdesc><p>Show all currently active inputs to sinks a.k.a. playback
+      streams (resp. outputs of sources a.k.a. recording streams).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>stat</opt></p>
+      <optdesc><p>Show some simple statistics about the allocated memory blocks and the space used by them.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>info</opt> or <opt>ls</opt> or <opt>list</opt></p>
+      <optdesc><p>A combination of all status commands described above (all
+      three commands are synonyms).</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Module Management">
+    <option>
+      <p><opt>load-module</opt> <arg>name</arg> [<arg>arguments...</arg>]</p>
+      <optdesc><p>Load a module specified by its name and arguments. For most
+      modules it is OK to be loaded more than once.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>unload-module</opt> <arg>index|name</arg></p>
+      <optdesc><p>Unload a module, specified either by its index in the module
+      list or its name.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>describe-module</opt> <arg>name</arg></p>
+      <optdesc><p>Give information about a module specified by its name.</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Volume Commands">
+    <option>
+      <p><opt>set-sink-volume|set-source-volume</opt> <arg>index|name</arg> <arg>volume</arg></p>
+      <optdesc><p>Set the volume of the specified sink (resp. source). You may
+      specify the sink (resp. source) either by its index in the sink/source list
+      or by its name. The volume should be an integer value greater or equal than
+      0 (muted). Volume 65536 (0x10000) is 'normal' volume a.k.a. 100%. Values
+      greater than this amplify the audio signal (with clipping).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-sink-mute|set-source-mute</opt> <arg>index|name</arg> <arg>boolean</arg></p>
+      <optdesc><p>Mute or unmute the specified sink (resp. source). You may
+      specify the sink (resp. source) either by its index or by its name.
+      The mute value is either 0 (not muted) or 1 (muted).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-sink-input-volume|set-source-output-volume</opt> <arg>index</arg> <arg>volume</arg></p>
+      <optdesc><p>Set the volume of a sink input (resp. source output) specified
+      by its index. The same volume rules apply as with set-sink-volume.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-sink-input-mute|set-source-output-mute</opt> <arg>index</arg> <arg>boolean</arg></p>
+      <optdesc><p>Mute or unmute a sink input (resp. source output) specified
+      by its index. The same mute rules apply as with set-sink-mute.</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Configuration Commands">
+    <option>
+      <p><opt>set-default-sink|set-default-source</opt> <arg>index|name</arg></p>
+      <optdesc><p>Make a sink (resp. source) the default. You may specify the
+      sink (resp. source) by its index in the sink (resp. source) list or by its
+      name.</p><p>Note that defaults may be overridden by various policy modules
+      or by specific stream configurations.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-card-profile</opt> <arg>index|name</arg> <arg>profile-name</arg></p>
+      <optdesc><p>Change the profile of a card.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-sink-port|set-source-port</opt> <arg>index|name</arg> <arg>port-name</arg></p>
+      <optdesc><p>Change the profile of a sink (resp. source).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-port-latency-offset</opt> <arg>card-index|card-name</arg> <arg>port-name</arg> <arg>offset</arg> </p>
+      <optdesc><p>Change the latency offset of a port belonging to the specified card</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>suspend-sink|suspend-source</opt> <arg>index|name</arg> <arg>boolean</arg></p>
+      <optdesc><p>Suspend (i.e. disconnect from the underlying hardware) a sink
+      (resp. source).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>suspend</opt> <arg>boolean</arg></p>
+      <optdesc><p>Suspend all sinks and sources.</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Moving streams">
+    <option>
+      <p><opt>move-sink-input|move-source-output</opt> <arg>index</arg> <arg>sink-index|sink-name</arg></p>
+      <optdesc><p>Move sink input (resp. source output) to another sink
+      (resp. source).</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Property lists">
+    <option>
+      <p><opt>update-sink-proplist|update-source-proplist</opt> <arg>index|name</arg> <arg>properties</arg></p>
+      <optdesc><p>Update the properties of a sink (resp. source) specified by
+      name or index. The property is specified as e.g. device.description="My
+      Preferred Name"</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>update-sink-input-proplist|update-source-output-proplist</opt> <arg>index</arg> <arg>properties</arg></p>
+      <optdesc><p>Update the properties of a sink input (resp. source output)
+      specified by index. The properties are specified as above.</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Sample Cache">
+    <option>
+      <p><opt>list-samples</opt></p>
+      <optdesc><p>Lists the contents of the sample cache.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>play-sample</opt> <arg>name</arg> <arg>sink-index|sink-name</arg></p>
+      <optdesc><p>Play a sample cache entry to a sink.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>remove-sample</opt> <arg>name</arg></p>
+      <optdesc><p>Remove an entry from the sample cache.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>load-sample</opt> <arg>name</arg> <arg>filename</arg></p>
+      <optdesc><p>Load an audio file to the sample cache.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>load-sample-lazy</opt> <arg>name</arg> <arg>filename</arg></p>
+      <optdesc><p>Create a new entry in the sample cache, but don't load the
+      sample immediately. The sample is loaded only when it is first used.
+      After a certain idle time it is freed again.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>load-sample-dir-lazy</opt> <arg>path</arg></p>
+      <optdesc><p>Load all entries in the specified directory into the sample
+      cache as lazy entries. A shell globbing expression (e.g. *.wav) may be
+      appended to the path of the directory to add.</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Killing Clients/Streams">
+    <option>
+      <p><opt>kill-client</opt> <arg>index</arg></p>
+      <optdesc><p>Remove a client forcibly from the server. There is no protection
+      against the client reconnecting immediately.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>kill-sink-input|kill-source-output</opt> <arg>index</arg></p>
+      <optdesc><p>Remove a sink input (resp. source output) forcibly from the
+      server. This will not remove the owning client or any other streams opened
+      by the same client from the server.</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Log Commands">
+    <option>
+      <p><opt>set-log-level</opt> <arg>numeric-level</arg></p>
+      <optdesc><p>Change the log level.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-log-meta</opt> <arg>boolean</arg></p>
+      <optdesc><p>Show source code location in log messages.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-log-target</opt> <arg>target</arg></p>
+      <optdesc><p>Change the log target (null, auto, journal, syslog, stderr,
+      file:PATH, newfile:PATH).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-log-time</opt> <arg>boolean</arg></p>
+      <optdesc><p>Show timestamps in log messages.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>set-log-backtrace</opt> <arg>num-frames</arg></p>
+      <optdesc><p>Show backtrace in log messages.</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Miscellaneous Commands">
+    <option>
+      <p><opt>play-file</opt> <arg>filename</arg> <arg>sink-index|sink-name</arg></p>
+      <optdesc><p>Play an audio file to a sink.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>dump</opt></p>
+      <optdesc><p>Dump the daemon's current configuration in CLI commands.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>dump-volumes</opt></p>
+      <optdesc><p>Debug: Shows the current state of all volumes.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>shared</opt></p>
+      <optdesc><p>Debug: Show shared properties.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>exit</opt></p>
+      <optdesc><p>Terminate the daemon. If you want to terminate a CLI
+      connection ("log out") you might want to use ctrl+d</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Meta Commands">
+    <p>
+      In addition to the commands described above there are a few meta directives
+      supported by the command line interpreter.
+    </p>
+    <option>
+      <p><opt>.include</opt> <arg>filename|folder</arg></p>
+      <optdesc><p>Executes the commands from the specified script file or in all
+      of the *.pa files within the folder.</p></optdesc>
+    </option>
+    <option>
+      <p><opt>.fail</opt> and <opt>.nofail</opt></p>
+      <optdesc><p>Enable (resp. disable) that following failing commands will
+      cancel the execution of the current script file. This is ignored when
+      used on the interactive command line.</p></optdesc>
+    </option>
+    <option>
+      <p><opt>.ifexists</opt> <arg>filename</arg></p>
+      <optdesc><p>Execute the subsequent block of commands only if the specified
+      file exists. Typically <arg>filename</arg> indicates a module. Relative
+      paths are resolved using the module directory as the base. By using an
+      absolute path, the existance of other files can be checked as well.</p></optdesc>
+    </option>
+    <option>
+      <p><opt>.else</opt> and <opt>.endif</opt></p>
+      <optdesc><p>A block of commands is delimited by an <opt>.else</opt> or
+      <opt>.endif</opt> meta command. Nesting conditional commands is not
+      supported.</p></optdesc>
+    </option>
+  </section>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;;
+    PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="default.pa" section="5"/>,
+      <manref name="pacmd" section="1"/>,
+      <manref name="pulseaudio" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pulse-client.conf.5.xml.in b/man/pulse-client.conf.5.xml.in
new file mode 100644 (file)
index 0000000..b88898c
--- /dev/null
@@ -0,0 +1,156 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="pulse-client.conf" section="5" desc="PulseAudio client configuration file">
+
+  <synopsis>
+    <p><file>~/.config/pulse/client.conf</file></p>
+    <p><file>~/.config/pulse/client.conf.d/*.conf</file></p>
+    <p><file>@PA_DEFAULT_CONFIG_DIR@/client.conf</file></p>
+    <p><file>@PA_DEFAULT_CONFIG_DIR@/client.conf.d/*.conf</file></p>
+  </synopsis>
+
+  <description>
+    <p>The PulseAudio client library reads configuration directives from
+    a configuration file on startup. If the per-user file
+    <file>~/.config/pulse/client.conf</file> exists, it is used, otherwise the
+    system configuration file <file>@PA_DEFAULT_CONFIG_DIR@/client.conf</file>
+    is used. In addition to those main files, configuration directives can also
+    be put in files under directories
+    <file>~/.config/pulse/client.conf.d/</file> and
+    <file>@PA_DEFAULT_CONFIG_DIR@/client.conf.d/</file>. Those files have to
+    have the .conf file name extension, but otherwise the file names can be
+    chosen freely. The files under client.conf.d are processed in alphabetical
+    order. In case the same option is set in multiple files, the last file to
+    set an option overrides earlier files. The main client.conf file is
+    processed first, so options set in files under client.conf.d override the
+    main file.</p>
+
+    <p>The configuration file is a simple collection of variable
+    declarations. If the configuration file parser encounters either ;
+    or # it ignores the rest of the line until its end.</p>
+
+    <p>For the settings that take a boolean argument the values
+    <opt>true</opt>, <opt>yes</opt>, <opt>on</opt> and <opt>1</opt>
+    are equivalent, resp. <opt>false</opt>, <opt>no</opt>,
+    <opt>off</opt>, <opt>0</opt>.</p>
+
+  </description>
+
+  <section name="Directives">
+
+    <option>
+      <p><opt>default-sink=</opt> The default sink to connect to. If
+      specified overwrites the setting in the daemon. The environment
+      variable <opt>$PULSE_SINK</opt> however takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>default-source=</opt> The default source to connect
+      to. If specified overwrites the setting in the daemon. The
+      environment variable <opt>$PULSE_SOURCE</opt> however takes
+      precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>default-server=</opt> The default sever to connect
+      to. The environment variable <opt>$PULSE_SERVER</opt> takes
+      precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>autospawn=</opt> Autospawn a PulseAudio daemon when
+      needed. Takes a boolean value, defaults to <opt>yes</opt>.</p>
+    </option>
+
+    <option>
+      <p><opt>daemon-binary=</opt> Path to the PulseAudio daemon to
+      run when autospawning. Defaults to a path configured at compile
+      time.</p>
+    </option>
+
+    <option>
+      <p><opt>extra-arguments=</opt> Extra arguments to pass to the
+      PulseAudio daemon when autospawning. Defaults to
+      <opt>--log-target=syslog</opt>
+      </p>
+    </option>
+
+    <option>
+      <p><opt>cookie-file=</opt> Specify the path to the PulseAudio
+      authentication cookie. Defaults to
+      <file>~/.config/pulse/cookie</file>.</p>
+    </option>
+
+    <option>
+      <p><opt>enable-shm=</opt> Enable data transfer via POSIX
+      or memfd shared memory. Takes a boolean argument, defaults to
+      <opt>yes</opt>. If set to <opt>no</opt>, communication with
+      the server will be exclusively done through data-copy over
+      sockets.</p>
+    </option>
+
+    <option>
+      <p><opt>enable-memfd=</opt>. Enable data transfer via memfd
+      shared memory. Takes a boolean argument, defaults to
+      <opt>yes</opt>.</p>
+    </option>
+
+    <option>
+      <p><opt>shm-size-bytes=</opt> Sets the shared memory segment
+      size for clients, in bytes. If left unspecified or is set to 0
+      it will default to some system-specific default, usually 64
+      MiB. Please note that usually there is no need to change this
+      value, unless you are running an OS kernel that does not do
+      memory overcommit.</p>
+    </option>
+
+    <option>
+      <p><opt>auto-connect-localhost=</opt> Automatically try to
+      connect to localhost via IP. Enabling this is a potential
+      security hole since connections are only authenticated one-way
+      and a rogue server might hence fool a client into sending it its
+      private (e.g. VoIP call) data. This was enabled by default on
+      PulseAudio version 0.9.21 and older. Defaults to
+      <opt>no</opt>.</p>
+    </option>
+
+    <option>
+      <p><opt>auto-connect-display=</opt> Automatically try to connect
+      to the host X11's $DISPLAY variable is set to. The same security
+      issues apply as to <opt>auto-connect-localhost=</opt>. Defaults
+      to <opt>no</opt>.</p>
+    </option>
+
+  </section>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;;
+    PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulse-daemon.conf" section="5"/>, <manref name="pulseaudio" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pulse-daemon.conf.5.xml.in b/man/pulse-daemon.conf.5.xml.in
new file mode 100644 (file)
index 0000000..f0550f3
--- /dev/null
@@ -0,0 +1,556 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="pulse-daemon.conf" section="5" desc="PulseAudio daemon configuration file">
+
+  <synopsis>
+    <p><file>~/.config/pulse/daemon.conf</file></p>
+    <p><file>~/.config/pulse/daemon.conf.d/*.conf</file></p>
+    <p><file>@PA_DEFAULT_CONFIG_DIR@/daemon.conf</file></p>
+    <p><file>@PA_DEFAULT_CONFIG_DIR@/daemon.conf.d/*.conf</file></p>
+  </synopsis>
+
+  <description>
+    <p>The PulseAudio sound server reads configuration directives from
+    a configuration file on startup. If the per-user file
+    <file>~/.config/pulse/daemon.conf</file> exists, it is used, otherwise the
+    system configuration file <file>@PA_DEFAULT_CONFIG_DIR@/daemon.conf</file>
+    is used. In addition to those main files, configuration directives can also
+    be put in files under directories
+    <file>~/.config/pulse/daemon.conf.d/</file> and
+    <file>@PA_DEFAULT_CONFIG_DIR@/daemon.conf.d/</file>. Those files have to
+    have the .conf file name extension, but otherwise the file names can be
+    chosen freely. The files under daemon.conf.d are processed in alphabetical
+    order. In case the same option is set in multiple files, the last file to
+    set an option overrides earlier files. The main daemon.conf file is
+    processed first, so options set in files under daemon.conf.d override the
+    main file.</p>
+
+    <p>Please note that the server also reads a configuration script on
+    startup. See <manref name="default.pa" section="5"/>.</p>
+
+    <p>The configuration file is a simple collection of variable
+    declarations. If the configuration file parser encounters either ;
+    or # it ignores the rest of the line until its end.</p>
+
+    <p>For the settings that take a boolean argument the values
+    <opt>true</opt>, <opt>yes</opt>, <opt>on</opt> and <opt>1</opt>
+    are equivalent, resp. <opt>false</opt>, <opt>no</opt>,
+    <opt>off</opt>, <opt>0</opt>.</p>
+
+  </description>
+
+  <section name="General Directives">
+
+    <option>
+      <p><opt>daemonize= </opt> Daemonize after startup. Takes a
+      boolean value, defaults to <opt>no</opt>. The <opt>--daemonize</opt>
+      command line option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>fail=</opt> Fail to start up if any of the directives
+      in the configuration script <file>default.pa</file>
+      fail. Takes a boolean argument, defaults to <opt>yes</opt>. The <opt>--fail</opt> command line
+      option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>allow-module-loading=</opt> Allow/disallow module
+      loading after startup. This is a security feature that if
+      disabled makes sure that no further modules may be loaded into
+      the PulseAudio server after startup completed. It is recommended
+      to disable this when <opt>system-instance</opt> is
+      enabled. Please note that certain features like automatic
+      hot-plug support will not work if this option is enabled. Takes
+      a boolean argument, defaults to <opt>yes</opt>. The
+      <opt>--disallow-module-loading</opt> command line option takes
+      precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>allow-exit=</opt> Allow/disallow exit on user
+      request. Defaults to <opt>yes</opt>.</p>
+    </option>
+
+    <option>
+      <p><opt>resample-method=</opt> The resampling algorithm to
+      use. Use one of <opt>src-sinc-best-quality</opt>,
+      <opt>src-sinc-medium-quality</opt>, <opt>src-sinc-fastest</opt>,
+      <opt>src-zero-order-hold</opt>, <opt>src-linear</opt>,
+      <opt>trivial</opt>, <opt>speex-float-N</opt>,
+      <opt>speex-fixed-N</opt>, <opt>ffmpeg</opt>, <opt>soxr-mq</opt>,
+      <opt>soxr-hq</opt>, <opt>soxr-vhq</opt>. See the
+      documentation of libsamplerate and speex for explanations of the
+      different src- and speex- methods, respectively. The method
+      <opt>trivial</opt> is the most basic algorithm implemented. If
+      you're tight on CPU consider using this. On the other hand it has
+      the worst quality of them all. The Speex resamplers take an
+      integer quality setting in the range 0..10 (bad...good). They
+      exist in two flavours: <opt>fixed</opt> and <opt>float</opt>. The former uses fixed point
+      numbers, the latter relies on floating point numbers. On most
+      desktop CPUs the float point resampler is a lot faster, and it
+      also offers slightly better quality. The soxr-family methods
+      are based on libsoxr, a resampler library from the SoX sound processing utility.
+      The mq variant has the best performance of the three. The hq is more expensive
+      and, according to SoX developers, is considered the best choice for audio of up to 16 bits per sample.
+      The vhq variant has more precision than hq and is more suitable for larger samples. The Soxr resamplers
+      generally offer better quality at less CPU compared to other resamplers, such as speex.
+      The downside is that they can add a significant delay to the output
+      (usually up to around 20 ms, in rare cases more).
+      See the output of <opt>dump-resample-methods</opt> for a complete list of all
+      available resamplers. Defaults to <opt>speex-float-1</opt>. The
+      <opt>--resample-method</opt> command line option takes precedence.
+      Note that some modules overwrite or allow overwriting of the
+      resampler to use.</p>
+    </option>
+
+    <option>
+      <p><opt>avoid-resampling=</opt> If set, try to configure the
+      device to avoid resampling. This only works on devices which
+      support reconfiguring their rate, and when no other streams are
+      already playing or capturing audio. The device will also not be
+      configured to a rate less than the default and alternate sample
+      rates.</p>
+    </option>
+
+    <option>
+      <p><opt>enable-remixing=</opt> If disabled never upmix or
+      downmix channels to different channel maps. Instead, do a simple
+      name-based matching only. Defaults to <opt>yes.</opt></p>
+    </option>
+
+    <option>
+      <p><opt>remixing-use-all-sink-channels=</opt> If enabled, use
+      all sink channels when remixing. Otherwise, remix to the minimal
+      set of sink channels needed to reproduce all of the source
+      channels. (This has no effect on LFE remixing.) Defaults to
+      <opt>yes</opt>.</p>
+    </option>
+
+    <option>
+      <p><opt>enable-lfe-remixing=</opt> If disabled when upmixing or
+      downmixing ignore LFE channels. When this option is disabled the
+      output LFE channel will only get a signal when an input LFE
+      channel is available as well. If no input LFE channel is
+      available the output LFE channel will always be 0. If no output
+      LFE channel is available the signal on the input LFE channel
+      will be ignored. Defaults to <opt>no</opt>.</p>
+    </option>
+
+    <option>
+      <p><opt>lfe-crossover-freq=</opt> The crossover frequency (in Hz) for the
+      LFE filter. Set it to 0 to disable the LFE filter. Defaults to 0.</p>
+    </option>
+
+    <option>
+      <p><opt>use-pid-file=</opt> Create a PID file in the runtime directory
+      (<file>$XDG_RUNTIME_DIR/pulse/pid</file>). If this is enabled you may
+      use commands like <opt>--kill</opt> or <opt>--check</opt>. If
+      you are planning to start more than one PulseAudio process per
+      user, you better disable this option since it effectively
+      disables multiple instances. Takes a boolean argument, defaults
+      to <opt>yes</opt>. The <opt>--use-pid-file</opt> command line
+      option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>cpu-limit=</opt> If disabled do not install the CPU load
+      limiter, even on platforms where it is supported. This option is
+      useful when debugging/profiling PulseAudio to disable disturbing
+      SIGXCPU signals. Takes a boolean argument, defaults to
+      <opt>no</opt>. The <opt>--no-cpu-limit</opt> command line
+      argument takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>system-instance=</opt> Run the daemon as system-wide
+      instance, requires root privileges. Takes a boolean argument,
+      defaults to <opt>no</opt>. The <opt>--system</opt> command line
+      argument takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>local-server-type=</opt> Please don't use this option if
+      you don't have to! This option is currently only useful when you
+      want D-Bus clients to use a remote server. This option may be
+      removed in future versions. If you only want to run PulseAudio
+      in the system mode, use the <opt>system-instance</opt> option.
+      This option takes one of <opt>user</opt>, <opt>system</opt> or
+      <opt>none</opt> as the argument. This is essentially a duplicate
+      for the <opt>system-instance</opt> option. The difference is the
+      <opt>none</opt> option, which is useful when you want to use a
+      remote server with D-Bus clients. If both this and
+      <opt>system-instance</opt> are defined, this option takes
+      precedence. Defaults to whatever the <opt>system-instance</opt>
+      is set.</p>
+    </option>
+
+    <option>
+      <p><opt>enable-shm=</opt> Enable data transfer via POSIX
+      or memfd shared memory. Takes a boolean argument, defaults to
+      <opt>yes</opt>. The <opt>--disable-shm</opt> command line
+      argument takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>enable-memfd=</opt>. Enable memfd shared memory. Takes
+      a boolean argument, defaults to <opt>yes</opt>.</p>
+    </option>
+
+    <option>
+      <p><opt>shm-size-bytes=</opt> Sets the shared memory segment
+      size for the daemon, in bytes. If left unspecified or is set to 0
+      it will default to some system-specific default, usually 64
+      MiB. Please note that usually there is no need to change this
+      value, unless you are running an OS kernel that does not do
+      memory overcommit.</p>
+    </option>
+
+    <option>
+      <p><opt>lock-memory=</opt> Locks the entire PulseAudio process
+      into memory. While this might increase drop-out safety when used
+      in conjunction with real-time scheduling this takes away a lot
+      of memory from other processes and might hence considerably slow
+      down your system. Defaults to <opt>no</opt>.</p>
+    </option>
+
+    <option>
+      <p><opt>flat-volumes=</opt> Enable 'flat' volumes, i.e. where
+      possible let the sink volume equal the maximum of the volumes of
+      the inputs connected to it. Takes a boolean argument, defaults
+      to <opt>yes</opt>.</p>
+    </option>
+
+  </section>
+
+  <section name="Scheduling">
+
+    <option>
+      <p><opt>high-priority=</opt> Renice the daemon after startup to
+      become a high-priority process. This a good idea if you
+      experience drop-outs during playback. However, this is a certain
+      security issue, since it works when called SUID root only, or
+      RLIMIT_NICE is used. root is dropped immediately after gaining
+      the nice level on startup, thus it is presumably safe. See
+      <manref section="1" name="pulseaudio"/> for more
+      information. Takes a boolean argument, defaults to <opt>yes</opt>. The <opt>--high-priority</opt>
+      command line option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>realtime-scheduling=</opt> Try to acquire SCHED_FIFO
+      scheduling for the IO threads. The same security concerns as
+      mentioned above apply. However, if PA enters an endless loop,
+      realtime scheduling causes a system lockup. Thus, realtime
+      scheduling should only be enabled on trusted machines for
+      now. Please note that only the IO threads of PulseAudio are made
+      real-time. The controlling thread is left a normally scheduled
+      thread. Thus enabling the high-priority option is orthogonal.
+      See <manref section="1" name="pulseaudio"/> for more
+      information. Takes a boolean argument, defaults to <opt>yes</opt>. The
+      <opt>--realtime</opt> command line option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>realtime-priority=</opt> The realtime priority to
+      acquire, if <opt>realtime-scheduling</opt> is enabled. Note: JACK uses 10
+      by default, 9 for clients. Thus it is recommended to choose the
+      PulseAudio real-time priorities lower. Some PulseAudio threads
+      might choose a priority a little lower or higher than the
+      specified value. Defaults to <opt>5</opt>.</p>
+    </option>
+
+    <option>
+      <p><opt>nice-level=</opt> The nice level to acquire for the
+      daemon, if <opt>high-priority</opt> is enabled. Note: on some
+      distributions X11 uses -10 by default. Defaults to -11.</p>
+    </option>
+
+  </section>
+
+  <section name="Idle Times">
+
+    <option>
+      <p><opt>exit-idle-time=</opt> Terminate the daemon after the
+      last client quit and this time in seconds passed. Use a negative value to
+      disable this feature. Defaults to 20. The
+      <opt>--exit-idle-time</opt> command line option takes
+      precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>scache-idle-time=</opt> Unload autoloaded sample cache
+      entries after being idle for this time in seconds. Defaults to
+      20. The <opt>--scache-idle-time</opt> command line option takes
+      precedence.</p>
+    </option>
+
+  </section>
+
+  <section name="Paths">
+
+    <option>
+      <p><opt>dl-search-path=</opt> The path where to look for dynamic
+      shared objects (DSOs/plugins). You may specify more than one
+      path separated by colons. The default path depends on compile
+      time settings. The <opt>--dl-search-path</opt> command line
+      option takes precedence. </p>
+    </option>
+
+    <option>
+      <p><opt>default-script-file=</opt> The default configuration
+      script file to load. Specify an empty string for not loading a
+      default script file. The default behaviour is to load
+      <file>~/.config/pulse/default.pa</file>, and if that file does not
+      exist fall back to the system wide installed version
+      <file>@PA_DEFAULT_CONFIG_DIR@/default.pa</file>. If run in system-wide
+      mode the file <file>@PA_DEFAULT_CONFIG_DIR@/system.pa</file> is used
+      instead. If <opt>-n</opt> is passed on the command line
+      or <opt>default-script-file=</opt> is disabled the default
+      configuration script is ignored.</p>
+    </option>
+
+    <option>
+      <p><opt>load-default-script-file=</opt> Load the default
+      configuration script file as specified
+      in <opt>default-script-file=</opt>. Defaults to <opt>yes</opt>.</p>
+    </option>
+
+  </section>
+
+  <section name="Logging">
+
+    <option>
+      <p><opt>log-target=</opt> The default log target. Use either
+      <opt>stderr</opt>, <opt>syslog</opt>, <opt>journal</opt> (optional),
+      <opt>auto</opt>, <opt>file:PATH</opt> or <opt>newfile:PATH</opt>. On traditional
+      systems <opt>auto</opt> is equivalent to <opt>syslog</opt>. On systemd-enabled
+      systems, auto is equivalent to <opt>journal</opt>, in case <opt>daemonize</opt>
+      is enabled, and to <opt>stderr</opt> otherwise. If set to <opt>file:PATH</opt>,
+      logging is directed to the file indicated by PATH. <opt>newfile:PATH</opt> is
+      otherwise the same as <opt>file:PATH</opt>, but existing files are never
+      overwritten. If the specified file already exists, a suffix is added to
+      the file name to avoid overwriting. Defaults to <opt>auto</opt>. The
+      <opt>--log-target</opt> command line option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>log-level=</opt> Log level, one of <opt>debug</opt>,
+      <opt>info</opt>, <opt>notice</opt>, <opt>warning</opt>,
+      <opt>error</opt>. Log messages with a lower log level than
+      specified here are not logged. Defaults to
+      <opt>notice</opt>. The <opt>--log-level</opt> command line
+      option takes precedence. The <opt>-v</opt> command line option
+      might alter this setting.</p>
+    </option>
+
+    <option>
+      <p><opt>log-meta=</opt> With each logged message log the code
+      location the message was generated from. Defaults to
+      <opt>no</opt>.</p>
+    </option>
+
+    <option>
+      <p><opt>log-time=</opt> With each logged message log the
+      relative time since startup. Defaults to <opt>no</opt>.</p>
+    </option>
+
+    <option>
+      <p><opt>log-backtrace=</opt> When greater than 0, with each
+      logged message log a code stack trace up the specified
+      number of stack frames. Defaults to <opt>0</opt>.</p>
+    </option>
+
+  </section>
+
+  <section name="Resource Limits">
+
+    <p>See <manref name="getrlimit" section="2"/> for
+    more information. Set to -1 if PulseAudio shall not touch the resource
+    limit. Not all resource limits are available on all operating
+    systems.</p>
+
+    <option>
+      <p><opt>rlimit-as</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-rss</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-core</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-data</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-fsize</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-nofile</opt> Defaults to 256.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-stack</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-nproc</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-locks</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-sigpending</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-msgqueue</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-memlock</opt> Defaults to 16 KiB. Please note
+      that the JACK client libraries may require more locked
+      memory.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-nice</opt> Defaults to 31. Please make sure that
+      the default nice level as configured with <opt>nice-level</opt>
+      fits in this resource limit, if <opt>high-priority</opt> is
+      enabled.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-rtprio</opt> Defaults to 9. Please make sure that
+      the default real-time priority level as configured with
+      <opt>realtime-priority=</opt> fits in this resource limit, if
+      <opt>realtime-scheduling</opt> is enabled. The JACK client
+      libraries require a real-time priority of 9 by default.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-rttime</opt> Defaults to 1000000.</p>
+    </option>
+
+  </section>
+
+  <section name="Default Device Settings">
+
+    <p>Most drivers try to open the audio device with these settings
+    and then fall back to lower settings. The default settings are CD
+    quality: 16bit native endian, 2 channels, 44100 Hz sampling.</p>
+
+    <option>
+      <p><opt>default-sample-format=</opt> The default sampling
+      format. Specify one of <opt>u8</opt>, <opt>s16le</opt>,
+      <opt>s16be</opt>, <opt>s24le</opt>, <opt>s24be</opt>,
+      <opt>s24-32le</opt>, <opt>s24-32be</opt>, <opt>s32le</opt>,
+      <opt>s32be</opt> <opt>float32le</opt>, <opt>float32be</opt>,
+      <opt>ulaw</opt>, <opt>alaw</opt>. Depending on the endianness of
+      the CPU the formats <opt>s16ne</opt>, <opt>s16re</opt>,
+      <opt>s24ne</opt>, <opt>s24re</opt>, <opt>s24-32ne</opt>,
+      <opt>s24-32re</opt>, <opt>s32ne</opt>, <opt>s32re</opt>,
+      <opt>float32ne</opt>, <opt>float32re</opt> (for native,
+      resp. reverse endian) are available as aliases.</p>
+    </option>
+
+    <option>
+      <p><opt>default-sample-rate=</opt> The default sample frequency.</p>
+    </option>
+
+    <option>
+      <p><opt>default-sample-channels</opt> The default number of channels.</p>
+    </option>
+
+    <option>
+      <p><opt>default-channel-map</opt> The default channel map.</p>
+    </option>
+
+    <option>
+      <p><opt>alternate-sample-rate</opt> The alternate sample
+      frequency. Sinks and sources will use either the
+      default-sample-rate value or this alternate value, typically 44.1
+      or 48kHz. Switching between default and alternate values is
+      enabled only when the sinks/sources are suspended. This option
+      is ignored in passthrough mode where the stream rate will be used.
+      If set to the same value as the default sample rate, this feature is
+      disabled.</p>
+    </option>
+
+  </section>
+
+  <section name="Default Fragment Settings">
+
+    <p>Some hardware drivers require the hardware playback buffer to
+    be subdivided into several fragments. It is possible to change
+    these buffer metrics for machines with high scheduling
+    latencies. Not all possible values that may be configured here are
+    available in all hardware. The driver will find the nearest
+    setting supported. Modern drivers that support timer-based
+    scheduling ignore these options.</p>
+
+    <option>
+      <p><opt>default-fragments=</opt> The default number of
+      fragments. Defaults to 4.</p>
+    </option>
+    <option>
+      <p><opt>default-fragment-size-msec=</opt>The duration of a
+      single fragment. Defaults to 25ms (i.e. the total buffer is thus
+      100ms long).</p>
+    </option>
+
+  </section>
+
+  <section name="Default Deferred Volume Settings">
+
+    <p>With the flat volume feature enabled, the sink HW volume is set
+    to the same level as the highest volume input stream. Any other streams
+    (with lower volumes) have the appropriate adjustment applied in SW to
+    bring them to the correct overall level. Sadly hardware mixer changes
+    cannot be timed accurately and thus this change of volumes can sometimes
+    cause the resulting output sound to be momentarily too loud or too soft.
+    So to ensure SW and HW volumes are applied concurrently without any
+    glitches, their application needs to be synchronized. The sink
+    implementation needs to support deferred volumes. The following
+    parameters can be used to refine the process.</p>
+
+    <option>
+      <p><opt>enable-deferred-volume=</opt> Enable deferred volume for the sinks that
+      support it. This feature is enabled by default.</p>
+    </option>
+    <option>
+      <p><opt>deferred-volume-safety-margin-usec=</opt> The amount of time (in
+      usec) by which the HW volume increases are delayed and HW volume
+      decreases are advanced. Defaults to 8000 usec.</p>
+    </option>
+    <option>
+      <p><opt>deferred-volume-extra-delay-usec=</opt> The amount of time (in usec)
+      by which HW volume changes are delayed. Negative values are also allowed.
+      Defaults to 0.</p>
+    </option>
+
+  </section>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulse-client.conf" section="5"/>, <manref name="default.pa" section="5"/>, <manref name="pulseaudio" section="1"/>, <manref name="pacmd" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pulseaudio.1.xml.in b/man/pulseaudio.1.xml.in
new file mode 100644 (file)
index 0000000..f732b8a
--- /dev/null
@@ -0,0 +1,500 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="pulseaudio" section="1" desc="The PulseAudio Sound System">
+
+  <synopsis>
+    <cmd>pulseaudio [<arg>options</arg>]</cmd>
+    <cmd>pulseaudio <opt>--help</opt></cmd>
+    <cmd>pulseaudio <opt>--version</opt></cmd>
+    <cmd>pulseaudio <opt>--dump-conf</opt></cmd>
+    <cmd>pulseaudio <opt>--dump-modules</opt></cmd>
+    <cmd>pulseaudio <opt>--dump-resample-methods</opt></cmd>
+    <cmd>pulseaudio <opt>--cleanup-shm</opt></cmd>
+    <cmd>pulseaudio <opt>--start</opt></cmd>
+    <cmd>pulseaudio <opt>--kill</opt></cmd>
+    <cmd>pulseaudio <opt>--check</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p>PulseAudio is a networked low-latency sound server for Linux, POSIX and Windows systems.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--dump-conf</opt></p>
+
+      <optdesc><p>Load the daemon configuration file
+      <file>daemon.conf</file> (see below), parse remaining
+      configuration options on the command line and dump the resulting
+      daemon configuration, in a format that is compatible with
+      <file>daemon.conf</file>.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--dump-modules</opt></p>
+
+      <optdesc><p>List available loadable modules. Combine with
+      <opt>-v</opt> for a more elaborate listing.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--dump-resample-methods</opt></p>
+      <optdesc><p>List available audio resamplers.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--cleanup-shm</opt></p>
+
+      <optdesc><p>Identify stale PulseAudio POSIX shared memory
+      segments in <file>/dev/shm</file> and remove them if
+      possible. This is done implicitly whenever a new daemon starts
+      up or a client tries to connect to a daemon. It should normally
+      not be necessary to issue this command by hand. Only available
+      on systems with POSIX shared memory segments implemented via a
+      virtual file system mounted to <file>/dev/shm</file>
+      (e.g. Linux).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--start</opt></p>
+
+      <optdesc><p>Start PulseAudio if it is not running yet. This is
+      different from starting PulseAudio without <opt>--start</opt>
+      which would fail if PA is already running. PulseAudio is
+      guaranteed to be fully initialized when this call
+      returns. Implies <opt>--daemonize</opt>.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-k | --kill</opt></p>
+
+      <optdesc><p>Kill an already running PulseAudio daemon of the
+      calling user (Equivalent to sending a SIGTERM).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--check</opt></p>
+
+      <optdesc><p>Return 0 as return code when the PulseAudio daemon
+      is already running for the calling user, or non-zero
+      otherwise. Produces no output on the console except for errors
+      to stderr.</p></optdesc>
+    </option>
+
+
+    <option>
+      <p><opt>--system</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Run as system-wide instance instead of
+      per-user. Please note that this disables certain features of
+      PulseAudio and is generally not recommended unless the system
+      knows no local users (e.g. is a thin client). This feature needs
+      special configuration and a dedicated UNIX user set up. It is
+      highly recommended to combine this with
+      <opt>--disallow-module-loading</opt> (see below).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-D | --daemonize</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Daemonize after startup, i.e. detach from the
+      terminal. Note that when running as a systemd service you should
+      use <opt>--daemonize=no</opt> for systemd notification to work.
+      </p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--fail</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Fail startup when any of the commands specified in
+      the startup script <file>default.pa</file> (see below)
+      fails.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--high-priority</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Try to acquire a high Unix nice level. This will
+      only succeed if the calling user has a non-zero RLIMIT_NICE
+      resource limit set (on systems that support this), or we're
+      called SUID root (see below), or we are configure to be run as
+      system daemon (see <arg>--system</arg> above). It is recommended
+      to enable this, since it is only a negligible security risk (see
+      below).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--realtime</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Try to acquire a real-time scheduling for
+      PulseAudio's I/O threads. This will only succeed if the calling
+      user has a non-zero RLIMIT_RTPRIO resource limit set (on systems
+      that support this), or we're called SUID root (see below), or we
+      are configure to be run as system daemon (see
+      <arg>--system</arg> above). It is recommended to enable this
+      only for trusted users, since it is a major security risk (see
+      below).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--disallow-module-loading</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Disallow module loading after startup. This is a
+      security feature since it disallows additional module loading
+      during runtime and on user request. It is highly recommended
+      when <arg>--system</arg> is used (see above). Note however, that
+      this breaks certain features like automatic module loading on hot
+      plug.</p></optdesc>
+
+    </option>
+
+    <option>
+      <p><opt>--disallow-exit</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Disallow user requested exit</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--exit-idle-time</opt><arg>=SECS</arg></p>
+
+      <optdesc><p>Terminate the daemon when idle and the specified
+      number of seconds passed.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--scache-idle-time</opt><arg>=SECS</arg></p>
+
+      <optdesc><p>Unload autoloaded samples from the cache when they
+      haven't been used for the specified number of
+      seconds.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--log-level</opt><arg>[=LEVEL]</arg></p>
+
+      <optdesc><p>If an argument is passed, set the log level to the
+      specified value, otherwise increase the configured verbosity
+      level by one. The log levels are numerical from 0 to 4,
+      corresponding to <arg>error</arg>, <arg>warn</arg>,
+      <arg>notice</arg>, <arg>info</arg>, <arg>debug</arg>. Default
+      log level is <arg>notice</arg>, i.e. all log messages with lower
+      log levels are printed: <arg>error</arg>, <arg>warn</arg>,
+      <arg>notice</arg>.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-v | --verbose</opt></p>
+
+      <optdesc><p>Increase the configured verbosity level by one (see
+      <opt>--log-level</opt> above). Specify multiple times to
+      increase log level multiple times.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--log-target</opt><arg>={auto,syslog,journal,stderr,file:PATH,newfile:PATH}</arg></p>
+
+      <optdesc><p>Specify the log target. If set to <arg>auto</arg>
+      (which is the default), then logging is directed to syslog when
+      <opt>--daemonize</opt> is passed, otherwise to
+      STDERR. If set to <arg>journal</arg> logging is directed to the systemd
+      journal. If set to <arg>file:PATH</arg>, logging is directed to
+      the file indicated by PATH. <arg>newfile:PATH</arg> is otherwise
+      the same as file:PATH, but existing files are never overwritten.
+      If the specified file already exists, a suffix is added to the
+      file name to avoid overwriting.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--log-meta</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Show source code location in log messages.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--log-time</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Show timestamps in log messages.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--log-backtrace</opt><arg>=FRAMES</arg></p>
+
+      <optdesc><p>When FRAMES is greater than 0, log for each message a
+      stack trace up to the number of specified stack frames.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-p | --dl-search-path</opt><arg>=PATH</arg></p>
+
+      <optdesc><p>Set the search path for dynamic shared objects
+      (plugins).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--resample-method</opt><arg>=METHOD</arg></p>
+
+      <optdesc><p>Use the specified resampler by default (See
+      <opt>--dump-resample-methods</opt> above for possible
+      values).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--use-pid-file</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Create a PID file. If this options is disabled it is possible to run multiple sound servers per user.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--no-cpu-limit</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Do not install CPU load limiter on platforms that
+      support it. By default, PulseAudio will terminate itself when it
+      notices that it takes up too much CPU time. This is useful as a
+      protection against system lockups when real-time scheduling is
+      used (see below). Disabling this mechanism is useful when
+      debugging PulseAudio with tools like <manref name="valgrind"
+      section="1"/> which slow down execution.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--disable-shm</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>PulseAudio clients and the server can exchange audio
+      data via POSIX or memfd shared memory segments (on systems that
+      support this). If disabled PulseAudio will communicate exclusively
+      over sockets. Please note that data transfer via shared memory
+      segments is always disabled when PulseAudio is running with
+      <opt>--system</opt> enabled (see above).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--enable-memfd</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>PulseAudio clients and the server can exchange audio
+      data via memfds - the anonymous Linux Kernel shared memory mechanism
+      (on kernels that support this). If disabled PulseAudio will
+      communicate via POSIX shared memory.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-L | --load</opt><arg>="MODULE ARGUMENTS"</arg></p>
+
+      <optdesc><p>Load the specified plugin module with the specified
+      arguments.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-F | --file</opt><arg>=FILENAME</arg></p>
+
+      <optdesc><p>Run the specified script on startup. May be
+      specified multiple times to specify multiple scripts to be run
+      in order. Combine with <opt>-n</opt> to disable loading of the
+      default script <file>default.pa</file> (see below).</p></optdesc>
+    </option>
+    <option>
+      <p><opt>-C</opt></p>
+
+      <optdesc><p>Open a command interpreter on STDIN/STDOUT after
+      startup. This may be used to configure PulseAudio dynamically
+      during runtime. Equivalent to
+      <opt>--load</opt><arg>=module-cli</arg>.</p></optdesc>
+    </option>
+    <option>
+      <p><opt>-n</opt></p>
+
+      <optdesc><p>Don't load default script file
+      <file>default.pa</file> (see below) on startup. Useful in
+      conjunction with <opt>-C</opt> or
+      <opt>--file</opt>.</p></optdesc>
+    </option>
+
+
+  </options>
+
+  <section name="Files">
+
+    <p><file>~/.config/pulse/daemon.conf</file>,
+    <file>@PA_DEFAULT_CONFIG_DIR@/daemon.conf</file>: configuration settings
+    for the PulseAudio daemon. If the version in the user's home
+    directory does not exist the global configuration file is
+    loaded. See <manref name="pulse-daemon.conf" section="5"/> for
+    more information.</p>
+
+    <p><file>~/.config/pulse/default.pa</file>,
+    <file>@PA_DEFAULT_CONFIG_DIR@/default.pa</file>: the default configuration
+    script to execute when the PulseAudio daemon is started. If the
+    version in the user's home directory does not exist the global
+    configuration script is loaded. See <manref name="default.pa"
+    section="5"/> for more information.</p>
+
+    <p><file>~/.config/pulse/client.conf</file>,
+    <file>@PA_DEFAULT_CONFIG_DIR@/client.conf</file>: configuration settings
+    for PulseAudio client applications. If the version in the user's
+    home directory does not exist the global configuration file is
+    loaded.  See <manref name="pulse-client.conf" section="5"/> for
+    more information.</p>
+
+  </section>
+
+  <section name="Signals">
+
+    <p><arg>SIGINT, SIGTERM</arg>: the PulseAudio daemon will shut
+    down (Same as <opt>--kill</opt>).</p>
+
+    <p><arg>SIGHUP</arg>: dump a long status report to STDOUT or
+    syslog, depending on the configuration.</p>
+
+    <p><arg>SIGUSR1</arg>: load module-cli, allowing runtime
+    reconfiguration via STDIN/STDOUT.</p>
+
+    <p><arg>SIGUSR2</arg>: load module-cli-protocol-unix, allowing
+    runtime reconfiguration via a AF_UNIX socket. See <manref
+    name="pacmd" section="1"/> for more information.</p>
+
+  </section>
+
+  <section name="UNIX Groups and users">
+
+    <p>Group <arg>pulse-rt</arg>: if the PulseAudio binary is marked
+    SUID root, then membership of the calling user in this group
+    decides whether real-time and/or high-priority scheduling is
+    enabled. Please note that enabling real-time scheduling is a
+    security risk (see below).</p>
+
+    <p>Group <arg>pulse-access</arg>: if PulseAudio is running as a system
+    daemon (see <opt>--system</opt> above) access is granted to
+    members of this group when they connect via AF_UNIX sockets. If
+    PulseAudio is running as a user daemon this group has no
+    meaning.</p>
+
+    <p>User <arg>pulse</arg>, group <arg>pulse</arg>: if PulseAudio is running as a system
+    daemon (see <opt>--system</opt> above) and is started as root the
+    daemon will drop privileges and become a normal user process using
+    this user and group. If PulseAudio is running as a user daemon
+    this user and group has no meaning.</p>
+  </section>
+
+  <section name="Real-time and high-priority scheduling">
+    <p>To minimize the risk of drop-outs during playback it is
+    recommended to run PulseAudio with real-time scheduling if the
+    underlying platform supports it. This decouples the scheduling
+    latency of the PulseAudio daemon from the system load and is thus
+    the best way to make sure that PulseAudio always gets CPU time
+    when it needs it to refill the hardware playback
+    buffers. Unfortunately this is a security risk on most systems,
+    since PulseAudio runs as user process, and giving realtime
+    scheduling privileges to a user process always comes with the risk
+    that the user misuses it to lock up the system -- which is
+    possible since making a process real-time effectively disables
+    preemption.</p>
+
+    <p>To minimize the risk PulseAudio by default does not enable
+    real-time scheduling. It is however recommended to enable it
+    on trusted systems. To do that start PulseAudio with
+    <opt>--realtime</opt> (see above) or enabled the appropriate option in
+    <file>daemon.conf</file>. Since acquiring realtime scheduling is a
+    privileged operation on most systems, some special changes to the
+    system configuration need to be made to allow them to the calling
+    user. Two options are available:</p>
+
+    <p>On newer Linux systems the system resource limit RLIMIT_RTPRIO
+    (see <manref name="setrlimit" section="2"/> for more information)
+    can be used to allow specific users to acquire real-time
+    scheduling. This can be configured in
+    <file>/etc/security/limits.conf</file>, a resource limit of 9 is recommended.</p>
+
+    <p>Alternatively, the SUID root bit can be set for the PulseAudio
+    binary. Then, the daemon will drop root privileges immediately on
+    startup, however retain the CAP_NICE capability (on systems that
+    support it), but only if the calling user is a member of the
+    <arg>pulse-rt</arg> group (see above). For all other users all
+    capabilities are dropped immediately. The advantage of this
+    solution is that the real-time privileges are only granted to the
+    PulseAudio daemon -- not to all the user's processes.</p>
+
+    <p>Alternatively, if the risk of locking up the machine is
+    considered too big to enable real-time scheduling, high-priority
+    scheduling can be enabled instead (i.e. negative nice level). This
+    can be enabled by passing <opt>--high-priority</opt> (see above)
+    when starting PulseAudio and may also be enabled with the
+    appropriate option in <file>daemon.conf</file>. Negative nice
+    levels can only be enabled when the appropriate resource limit
+    RLIMIT_NICE is set (see <manref name="setrlimit" section="2"/> for
+    more information), possibly configured in
+    <file>/etc/security/limits.conf</file>. A resource limit of 31
+    (corresponding with nice level -11) is recommended.</p>
+  </section>
+
+  <section name="Environment variables">
+
+    <p>The PulseAudio client libraries check for the existence of the
+    following environment variables and change their local configuration accordingly:</p>
+
+    <p><arg>$PULSE_SERVER</arg>: the server string specifying the server
+    to connect to when a client asks for a sound server connection and doesn't
+    explicitly ask for a specific server. The server string is a list of
+    server addresses separated by whitespace which are tried in turn. A server
+    address consists of an optional address type specifier (unix:, tcp:, tcp4:,
+    tcp6:), followed by a path or host address. A host address may include an
+    optional port number. A server address may be prefixed by a string enclosed
+    in {}. In this case the following server address is ignored unless the prefix
+    string equals the local hostname or the machine id (/etc/machine-id).</p>
+
+    <p><arg>$PULSE_SINK</arg>: the symbolic name of the sink to connect to when a client creates a playback stream and doesn't explicitly ask for a specific sink.</p>
+
+    <p><arg>$PULSE_SOURCE</arg>: the symbolic name of the source to connect to when a client creates a record stream and doesn't explicitly ask for a specific source.</p>
+
+    <p><arg>$PULSE_BINARY</arg>: path of PulseAudio executable to run when server auto-spawning is used.</p>
+
+    <p><arg>$PULSE_CLIENTCONFIG</arg>: path of file that shall be read instead of <file>client.conf</file> (see above) for client configuration.</p>
+
+    <p><arg>$PULSE_COOKIE</arg>: path of file that contains the PulseAudio
+    authentication cookie. Defaults to <file>~/.config/pulse/cookie</file>.</p>
+
+    <p>These environment settings take precedence -- if set -- over the configuration settings from <file>client.conf</file> (see above).</p>
+
+  </section>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulse-daemon.conf" section="5"/>, <manref name="default.pa" section="5"/>, <manref name="pulse-client.conf" section="5"/>, <manref name="pacmd" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/start-pulseaudio-x11.1.xml.in b/man/start-pulseaudio-x11.1.xml.in
new file mode 100644 (file)
index 0000000..66fe476
--- /dev/null
@@ -0,0 +1,48 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<manpage name="start-pulseaudio-x11" section="1" desc="PulseAudio Sound Server X11 Startup Script">
+
+  <synopsis>
+    <cmd>start-pulseaudio-x11 [<arg>pulseaudio options</arg>]</cmd>
+  </synopsis>
+
+  <description>
+    <p>This script starts pulseaudio (if not already running) and loads modules to
+    publish access credentials to the PulseAudio server in the X11 root window and to synthesize
+    X11 media key events on cork/uncork requests. Additionally it registers
+    PulseAudio to the X11 Session Manager.</p>
+
+    <p>All arguments are directly passed to pulseaudio.</p>
+  </description>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;;
+    PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/xmltoman b/man/xmltoman
new file mode 100755 (executable)
index 0000000..92422f0
--- /dev/null
@@ -0,0 +1,216 @@
+#!/usr/bin/perl -w
+
+#    xmltoman - simple xml to man converter
+#    Copyright (C) 2000-2002 Oliver Kurth <oku@masqmail.cx>
+#                       2003 Lennart Poettering <mzkzygbzna@0pointer.de>
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+use XML::Parser;
+
+my $buffer = "";
+my $break_req = 0;
+
+my @stack;
+my $stack_n = 0;
+
+my $para = 0; 
+
+sub out {
+    my $t = shift;
+
+    if ($t ne "") {
+        print $t;
+        $break_req=1;
+    }
+}
+
+sub out_buf {
+    local $_;
+
+    my $space = shift;
+
+    $_ = $buffer;
+    $buffer = "";
+
+    s/\n/\ /gm;
+    s/\s+/\ /gm;
+    s/^\s*//gm if (!$break_req);
+    s/^\s$//gm if (!$space);
+    
+    out($_);
+}
+
+sub stack_push {
+    my $a = shift;
+
+    if ($stack_n == 0 or $a ne $stack[$stack_n-1]) {
+        out("\\fB") if $a =~ /^bold$/;
+        out("\\fI") if $a =~ /^italic$/;
+    }
+
+    $stack[$stack_n++] = $a;
+}
+
+sub stack_pop {
+    local $_;
+    
+    if ($stack_n > 0) {
+        $stack_n--;
+
+        if ($stack_n > 0) {
+            $a = $stack[$stack_n-1];
+            out("\\fB") if $a =~ /^bold$/;
+            out("\\fI") if $a =~ /^italic$/;
+        } else {
+            out("\\f1");
+        }
+    }
+}
+
+sub handle_start {
+    local $_;
+    my $expat = shift;
+    my $element = shift;
+    my %attr = @_;
+    
+    $_ = $element;
+
+    if (/^manpage$/) {
+        out_buf(0);
+        print "\n" if ($break_req);
+        print ".TH " . $attr{name} . " " . $attr{section} . " User Manuals\n";
+        print ".SH NAME\n";
+        print $attr{name} . " \\- " . $attr{desc} . "\n";
+        $break_req = 0;
+    } elsif (/^synopsis$/) {
+        out_buf(0);
+        print "\n" if ($break_req);
+        print ".SH SYNOPSIS\n";
+        $section = $element;
+        $break_req = 0;
+        stack_push("bold");
+    } elsif (/^description$/) {
+        out_buf(0);
+        print "\n" if ($break_req);
+        print ".SH DESCRIPTION\n";
+        $section = $element;
+        $break_req = 0;
+    } elsif (/^options$/) {
+        out_buf(0);
+        print "\n" if ($break_req);
+        print ".SH OPTIONS\n";
+        $section = $element;
+        $break_req = 0;
+    } elsif (/^seealso$/) {
+        out_buf(0);
+        print "\n" if ($break_req);
+        print ".SH SEE ALSO\n";
+        $section = $element;
+        $break_req = 0;
+    } elsif (/^section$/) {
+        out_buf(0);
+        print "\n" if ($break_req);
+        print ".SH ".uc($attr{name})."\n";
+        $section = $attr{name};
+        $break_req = 0;
+    } elsif (/^option$/) {
+        out_buf(0);
+        print "\n" if ($break_req);
+        print ".TP\n";
+        $break_req = 0;
+    } elsif (/^p$/ or /^cmd$/) {
+        out_buf(0);
+        print "\n" if ($para);
+        $break_req = 0;
+    } elsif (/^optdesc$/) {
+        out_buf(0);
+        $break_req = 0;
+    } elsif (/^arg$/ or /^file$/) {
+        out_buf(1);
+        stack_push("italic");
+    } elsif (/^opt$/) {
+        out_buf(1);
+        stack_push("bold");
+    } elsif (/^manref$/) {
+        out_buf(1);
+        stack_push("bold");
+        out($attr{name} ."(" . $attr{section} . ")");
+        stack_pop();
+    } elsif (/^url$/) {
+        out_buf(1);
+        stack_push("bold");
+        out($attr{href});
+        stack_pop();
+    };
+
+    $para = 0;
+}
+
+sub handle_end {
+    local $_;
+    my $expat = shift;
+    my $element = shift;
+    
+    $_ = $element;
+
+    $para = 0;
+
+    if (/^description$/ or /^options$/ or /^section$/ or /^seealso$/) {
+        out_buf(0);
+    } elsif (/^p$/ or /^cmd$/) {
+        out_buf(0);
+        print "\n" if ($break_req);
+        $para = 1;
+        $break_req = 0;
+    } elsif (/^synopsis$/) {
+        out_buf(0);
+        stack_pop();
+    } elsif (/^opt$/ or /^arg$/ or /^file$/) {
+        out_buf(1);
+        stack_pop();
+    } elsif (/^manpage$/) {
+        out_buf(0);
+        print "\n" if $break_req;
+        $break_req = 0;
+    } elsif (/^optdesc$/ or /^cmd$/ or /^option$/) {
+        # Simply ignore
+    } else {
+        out_buf(1);
+    }
+};
+
+sub handle_char {
+    local $_;
+    my $expat = shift;
+    my $string = shift;
+    
+    $buffer .= $string;
+}
+
+MAIN:{
+    my $file = shift;
+
+    if (!$file) {
+        print STDERR "You need to specify a file to parse\n";
+        exit(1);
+    }
+    
+    my $parser = new XML::Parser(Handlers => {
+        Start => \&handle_start, 
+        End => \&handle_end,
+        Char => \&handle_char});
+
+    $parser->parsefile($file, ProtocolEncoding => 'ISO-8859-1');
+}
diff --git a/man/xmltoman.css b/man/xmltoman.css
new file mode 100644 (file)
index 0000000..2ddb867
--- /dev/null
@@ -0,0 +1,27 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify it under
+  the terms of the GNU General Public License as published by the Free
+  Software Foundation; either version 2 of the License, or (at your
+  option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+  for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+body { color: black; background-color: white; } 
+a:link, a:visited { color: #900000; }       
+h1 { text-transform:uppercase; font-size: 18pt; } 
+p { margin-left:1cm; margin-right:1cm; } 
+.cmd { font-family:monospace; }
+.file { font-family:monospace; }
+.arg { text-transform:uppercase; font-family:monospace; font-style: italic; }
+.opt { font-family:monospace; font-weight: bold;  }
+.manref { font-family:monospace; }
+.option .optdesc { margin-left:2cm; }
diff --git a/man/xmltoman.dtd b/man/xmltoman.dtd
new file mode 100644 (file)
index 0000000..e0e211c
--- /dev/null
@@ -0,0 +1,36 @@
+<!--
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify it under
+  the terms of the GNU General Public License as published by the Free
+  Software Foundation; either version 2 of the License, or (at your
+  option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+  for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<!ELEMENT manpage (synopsis | description | section | options | seealso)*>
+<!ATTLIST manpage name CDATA #REQUIRED section CDATA #REQUIRED desc CDATA #IMPLIED>
+<!ELEMENT arg (#PCDATA)>
+<!ELEMENT p (#PCDATA | arg | url | manref | opt | file )*>
+<!ELEMENT synopsis (cmd | p)+>
+<!ELEMENT description (p)+>
+<!ELEMENT section (p | option)*>
+<!ATTLIST section name CDATA #REQUIRED>
+<!ELEMENT option (#PCDATA | p | optdesc)*>
+<!ELEMENT optdesc (#PCDATA | p )*>
+<!ELEMENT cmd (#PCDATA | arg | opt)*>
+<!ELEMENT options (p | option)*>
+<!ELEMENT seealso (p)*>
+<!ELEMENT opt (#PCDATA)>
+<!ELEMENT file (#PCDATA)>
+<!ELEMENT manref EMPTY>
+<!ATTLIST manref name CDATA #REQUIRED section CDATA #REQUIRED href CDATA #IMPLIED>
+<!ELEMENT url EMPTY>
+<!ATTLIST url href CDATA #REQUIRED>
diff --git a/man/xmltoman.xsl b/man/xmltoman.xsl
new file mode 100644 (file)
index 0000000..1271064
--- /dev/null
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="iso-8859-15"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
+
+<!--
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify it under
+  the terms of the GNU General Public License as published by the Free
+  Software Foundation; either version 2 of the License, or (at your
+  option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+  for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<xsl:output method="xml" version="1.0" encoding="iso-8859-15" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes"/>
+
+<xsl:template match="/manpage">
+
+    <html>
+
+    <head>
+      <title><xsl:value-of select="@name"/>(<xsl:value-of select="@section"/>)</title>
+      <style type="text/css">
+        body { color: black; background-color: white; }
+        a:link, a:visited { color: #900000; }
+        h1 { text-transform:uppercase; font-size: 18pt; }
+        p { margin-left:1cm; margin-right:1cm; }
+        .cmd { font-family:monospace; }
+        .file { font-family:monospace; }
+        .arg { text-transform:uppercase; font-family:monospace; font-style: italic; }
+        .opt { font-family:monospace; font-weight: bold;  }
+        .manref { font-family:monospace; }
+        .option .optdesc { margin-left:2cm; }
+      </style>
+    </head>
+    <body>
+      <h1>Name</h1>
+      <p><xsl:value-of select="@name"/>
+        <xsl:if test="string-length(@desc) &gt; 0"> - <xsl:value-of select="@desc"/></xsl:if>
+      </p>
+      <xsl:apply-templates />
+    </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="p">
+ <p>
+  <xsl:apply-templates/>
+ </p>
+</xsl:template>
+
+<xsl:template match="cmd">
+ <p class="cmd">
+  <xsl:apply-templates/>
+ </p>
+</xsl:template>
+
+<xsl:template match="arg">
+  <span class="arg"><xsl:apply-templates/></span>
+</xsl:template>
+
+<xsl:template match="opt">
+  <span class="opt"><xsl:apply-templates/></span>
+</xsl:template>
+
+<xsl:template match="file">
+  <span class="file"><xsl:apply-templates/></span>
+</xsl:template>
+
+<xsl:template match="optdesc">
+  <div class="optdesc">
+    <xsl:apply-templates/>
+  </div>
+</xsl:template>
+
+<xsl:template match="synopsis">
+  <h1>Synopsis</h1>
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="seealso">
+  <h1>Synopsis</h1>
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="description">
+  <h1>Description</h1>
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="options">
+  <h1>Options</h1>
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="section">
+  <h1><xsl:value-of select="@name"/></h1>
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="option">
+  <div class="option"><xsl:apply-templates/></div>
+</xsl:template>
+
+<xsl:template match="manref">
+  <xsl:choose>
+    <xsl:when test="string-length(@href) &gt; 0">
+    <a class="manref"><xsl:attribute name="href"><xsl:value-of select="@href"/></xsl:attribute><xsl:value-of select="@name"/>(<xsl:value-of select="@section"/>)</a>
+    </xsl:when>
+    <xsl:otherwise>
+    <span class="manref"><xsl:value-of select="@name"/>(<xsl:value-of select="@section"/>)</span>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="url">
+  <a class="url"><xsl:attribute name="href"><xsl:value-of select="@href"/></xsl:attribute><xsl:value-of select="@href"/></a>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/orc.mak b/orc.mak
new file mode 100644 (file)
index 0000000..a1069a7
--- /dev/null
+++ b/orc.mak
@@ -0,0 +1,48 @@
+#
+# This is a Makefile.am fragment to build Orc code. It is based
+# on the orc.mak file distributed in the GStreamer common
+# repository.
+#
+# Include this file like this:
+#
+#  include $(top_srcdir)/orc.mak
+#
+# For each Orc source file, append its name (without the extension)
+# to ORC_SOURCE:
+#
+#  ORC_SOURCE += gstadderorc
+#
+# This will create gstadder-orc-gen.c and gstadder-orc-gen.h, which
+# you need to add to your nodist_module_SOURCES.
+#
+# Note that this file appends to BUILT_SOURCES and CLEANFILES, so
+# define them before including this file.
+#
+
+
+EXTRA_DIST += $(addsuffix .orc,$(ORC_SOURCE))
+
+if HAVE_ORC
+ORC_BUILT_SOURCE = $(addsuffix -orc-gen.c,$(ORC_SOURCE))
+ORC_BUILT_HEADER = $(addsuffix -orc-gen.h,$(ORC_SOURCE))
+
+BUILT_SOURCES += $(ORC_BUILT_SOURCE) $(ORC_BUILT_HEADER)
+CLEANFILES += $(BUILT_SOURCES)
+
+
+orcc_v_gen = $(orcc_v_gen_$(V))
+orcc_v_gen_ = $(orcc_v_gen_$(AM_DEFAULT_VERBOSITY))
+orcc_v_gen_0 = @echo "  ORCC   $@";
+
+cp_v_gen = $(cp_v_gen_$(V))
+cp_v_gen_ = $(cp_v_gen_$(AM_DEFAULT_VERBOSITY))
+cp_v_gen_0 = @echo "  CP     $@";
+
+%-orc-gen.c: %.orc
+       @mkdir -p $(@D)
+       $(orcc_v_gen)$(ORCC) --implementation -o $@ $<
+
+%-orc-gen.h: %.orc
+       @mkdir -p $(@D)
+       $(orcc_v_gen)$(ORCC) --header -o $@ $<
+endif
diff --git a/po/.gitignore b/po/.gitignore
new file mode 100644 (file)
index 0000000..1ebb427
--- /dev/null
@@ -0,0 +1,16 @@
+/.intltool-merge-cache
+/Makefile.in.in
+/Makevars.template
+/POTFILES
+/Rules-quot
+/boldquot.sed
+/en@boldquot.header
+/en@quot.header
+/insert-header.sin
+/pulseaudio.pot
+/quot.sed
+/remove-potcdate.sin
+/*.mo
+/*.gmo
+/Makefile
+/Makefile.in
diff --git a/po/LINGUAS b/po/LINGUAS
new file mode 100644 (file)
index 0000000..005500a
--- /dev/null
@@ -0,0 +1,44 @@
+as
+be
+bn_IN
+ca
+cs
+de_CH
+de
+el
+es
+fi
+fr
+gl
+gu
+he
+hi
+hr
+hu
+id
+it
+ja
+kn
+ko
+lt
+ml
+mr
+nl
+nn
+oc
+or
+pa
+pl
+pt_BR
+pt
+ru
+sk
+sr@latin
+sr
+sv
+ta
+te
+tr
+uk
+zh_CN
+zh_TW
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644 (file)
index 0000000..e2d6d11
--- /dev/null
@@ -0,0 +1,201 @@
+src/daemon/caps.c
+src/daemon/cmdline.c
+src/daemon/cpulimit.c
+src/daemon/daemon-conf.c
+src/daemon/dumpmodules.c
+src/daemon/ltdl-bind-now.c
+src/daemon/main.c
+src/daemon/pulseaudio.desktop.in
+src/daemon/systemd/user/pulseaudio.service.in
+src/modules/alsa/alsa-mixer.c
+src/modules/alsa/alsa-sink.c
+src/modules/alsa/alsa-source.c
+src/modules/alsa/alsa-util.c
+src/modules/alsa/module-alsa-card.c
+src/modules/alsa/module-alsa-sink.c
+src/modules/alsa/module-alsa-source.c
+src/modules/bluetooth/module-bluez4-device.c
+src/modules/bluetooth/module-bluez5-device.c
+src/modules/echo-cancel/module-echo-cancel.c
+src/modules/gconf/gconf-helper.c
+src/modules/gconf/module-gconf.c
+src/modules/jack/module-jack-sink.c
+src/modules/jack/module-jack-source.c
+src/modules/macosx/module-coreaudio-device.c
+src/modules/module-allow-passthrough.c
+src/modules/module-always-sink.c
+src/modules/module-cli.c
+src/modules/module-combine.c
+src/modules/module-console-kit.c
+src/modules/module-default-device-restore.c
+src/modules/module-detect.c
+src/modules/module-device-restore.c
+src/modules/module-equalizer-sink.c
+src/modules/module-esound-compat-spawnfd.c
+src/modules/module-esound-compat-spawnpid.c
+src/modules/module-esound-sink.c
+src/modules/module-filter-apply.c
+src/modules/module-ladspa-sink.c
+src/modules/module-lirc.c
+src/modules/module-match.c
+src/modules/module-mmkbd-evdev.c
+src/modules/module-native-protocol-fd.c
+src/modules/module-null-sink.c
+src/modules/module-pipe-sink.c
+src/modules/module-pipe-source.c
+src/modules/module-position-event-sounds.c
+src/modules/module-protocol-stub.c
+src/modules/module-remap-sink.c
+src/modules/module-rescue-streams.c
+src/modules/module-rygel-media-server.c
+src/modules/module-sine.c
+src/modules/module-solaris.c
+src/modules/module-stream-restore.c
+src/modules/module-suspend-on-idle.c
+src/modules/module-tunnel.c
+src/modules/module-tunnel-sink-new.c
+src/modules/module-tunnel-source-new.c
+src/modules/module-virtual-surround-sink.c
+src/modules/module-volume-restore.c
+src/modules/module-zeroconf-discover.c
+src/modules/module-zeroconf-publish.c
+src/modules/oss/module-oss.c
+src/modules/oss/oss-util.c
+src/modules/reserve-wrap.c
+src/modules/rtp/module-rtp-recv.c
+src/modules/rtp/module-rtp-send.c
+src/modules/rtp/rtp.c
+src/modules/rtp/sap.c
+src/modules/rtp/sdp.c
+src/modules/x11/module-x11-bell.c
+src/modules/x11/module-x11-publish.c
+src/modules/x11/module-x11-xsmp.c
+src/pulse/channelmap.c
+src/pulse/client-conf.c
+src/pulse/client-conf-x11.c
+src/pulse/context.c
+src/pulse/direction.c
+src/pulsecore/asyncmsgq.c
+src/pulsecore/asyncq.c
+src/pulsecore/auth-cookie.c
+src/pulsecore/authkey.c
+src/pulsecore/avahi-wrap.c
+src/pulsecore/cli.c
+src/pulsecore/cli-command.c
+src/pulsecore/client.c
+src/pulsecore/cli-text.c
+src/pulsecore/conf-parser.c
+src/pulsecore/core.c
+src/pulsecore/core-error.c
+src/pulsecore/core-scache.c
+src/pulsecore/core-subscribe.c
+src/pulsecore/core-util.c
+src/pulsecore/core-util.h
+src/pulsecore/dbus-util.c
+src/pulsecore/dllmain.c
+src/pulsecore/dynarray.c
+src/pulsecore/fdsem.c
+src/pulsecore/ffmpeg/resample2.c
+src/pulsecore/flist.c
+src/pulsecore/g711.c
+src/pulsecore/hashmap.c
+src/pulsecore/hook-list.c
+src/pulsecore/i18n.c
+src/pulsecore/idxset.c
+src/pulsecore/iochannel.c
+src/pulsecore/ioline.c
+src/pulsecore/ipacl.c
+src/pulsecore/lock-autospawn.c
+src/pulsecore/log.c
+src/pulsecore/ltdl-helper.c
+src/pulsecore/mcalign.c
+src/pulsecore/memblock.c
+src/pulsecore/memblockq.c
+src/pulsecore/memchunk.c
+src/pulsecore/modargs.c
+src/pulsecore/modinfo.c
+src/pulsecore/module.c
+src/pulsecore/msgobject.c
+src/pulsecore/mutex-posix.c
+src/pulsecore/mutex-win32.c
+src/pulsecore/namereg.c
+src/pulsecore/object.c
+src/pulsecore/once.c
+src/pulsecore/packet.c
+src/pulsecore/parseaddr.c
+src/pulsecore/pdispatch.c
+src/pulsecore/pid.c
+src/pulsecore/pipe.c
+src/pulsecore/play-memblockq.c
+src/pulsecore/play-memchunk.c
+src/pulsecore/poll-posix.c
+src/pulsecore/poll-win32.c
+src/pulsecore/proplist-util.c
+src/pulsecore/protocol-cli.c
+src/pulsecore/protocol-esound.c
+src/pulsecore/protocol-http.c
+src/pulsecore/protocol-native.c
+src/pulsecore/protocol-simple.c
+src/pulsecore/pstream.c
+src/pulsecore/pstream-util.c
+src/pulsecore/queue.c
+src/pulsecore/random.c
+src/pulsecore/resampler.c
+src/pulsecore/rtpoll.c
+src/pulsecore/sample-util.c
+src/pulsecore/sconv.c
+src/pulsecore/sconv-s16be.c
+src/pulsecore/sconv-s16le.c
+src/pulsecore/semaphore-posix.c
+src/pulsecore/semaphore-win32.c
+src/pulsecore/shared.c
+src/pulsecore/shm.c
+src/pulsecore/sink.c
+src/pulsecore/sink-input.c
+src/pulsecore/sioman.c
+src/pulsecore/socket-client.c
+src/pulsecore/socket-server.c
+src/pulsecore/socket-util.c
+src/pulsecore/sound-file.c
+src/pulsecore/sound-file-stream.c
+src/pulsecore/source.c
+src/pulsecore/source-output.c
+src/pulsecore/start-child.c
+src/pulsecore/strbuf.c
+src/pulsecore/strlist.c
+src/pulsecore/tagstruct.c
+src/pulsecore/thread-mq.c
+src/pulsecore/thread-posix.c
+src/pulsecore/thread-win32.c
+src/pulsecore/time-smoother.c
+src/pulsecore/tokenizer.c
+src/pulsecore/x11prop.c
+src/pulsecore/x11wrap.c
+src/pulse/error.c
+src/pulse/ext-stream-restore.c
+src/pulse/format.c
+src/pulse/glib-mainloop.c
+src/pulse/introspect.c
+src/pulse/mainloop-api.c
+src/pulse/mainloop.c
+src/pulse/mainloop-signal.c
+src/pulse/operation.c
+src/pulse/proplist.c
+src/pulse/sample.c
+src/pulse/scache.c
+src/pulse/simple.c
+src/pulse/stream.c
+src/pulse/subscribe.c
+src/pulse/thread-mainloop.c
+src/pulse/timeval.c
+src/pulse/utf8.c
+src/pulse/util.c
+src/pulse/volume.c
+src/pulse/xmalloc.c
+src/tests/resampler-test.c
+src/utils/pacat.c
+src/utils/pacmd.c
+src/utils/pactl.c
+src/utils/padsp.c
+src/utils/pasuspender.c
+src/utils/pax11publish.c
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
new file mode 100644 (file)
index 0000000..8afd0d6
--- /dev/null
@@ -0,0 +1,3 @@
+src/pulsecore/atomic.h
+src/modules/module-virtual-sink.c
+src/modules/module-virtual-source.c
diff --git a/po/as.po b/po/as.po
new file mode 100644 (file)
index 0000000..45d1e40
--- /dev/null
+++ b/po/as.po
@@ -0,0 +1,3025 @@
+# translation of pulseaudio.master-tx.as.po to Assamese
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Amitakhya Phukan <aphukan@fedoraproject.org>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx.as\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:52+0000\n"
+"Last-Translator: Amitakhya Phukan <aphukan@fedoraproject.org>\n"
+"Language-Team: Assamese <>\n"
+"Language: as\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Lokalize 0.2\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() এ এটা বৰ ডাঙৰ মান ঘূৰালে: %lu bytes (%lu ms) ।\n"
+"অতি সম্ভৱ এইটো ALSA চালক '%s' ৰ এটা বাগ । অনুগ্ৰহ কৰি এই সমস্যা ALSA বিকাশকক "
+"জনাওক ।"
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() এ এটা বৰ ডাঙৰ মান ঘূৰালে: %li bytes (%s%lu ms) ।\n"
+"অতি সম্ভৱ এইটো ALSA চালক '%s' ৰ এটা বাগ । অনুগ্ৰহ কৰি এই সমস্যা ALSA বিকাশকক "
+"জনাওক ।"
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() এ এটা বৰ ডাঙৰ মান ঘূৰালে: %lu bytes (%lu ms) ।\n"
+"অতি সম্ভৱ এইটো ALSA চালক '%s' ৰ এটা বাগ । অনুগ্ৰহ কৰি এই সমস্যা ALSA বিকাশকক "
+"জনাওক ।"
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() এ এটা বৰ ডাঙৰ মান ঘূৰালে: %lu bytes (%lu ms) ।\n"
+"অতি সম্ভৱ এইটো ALSA চালক '%s' ৰ এটা বাগ । অনুগ্ৰহ কৰি এই সমস্যা ALSA বিকাশকক "
+"জনাওক ।"
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "সদায়ে অন্তত এটা sink লোড কৰি ৰখা হ'ব, প্ৰয়োজনত null sink ব্যৱহাৰ কৰা হ'ব"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "ডামি নিৰ্গম"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "ভাৰ্চুয়াল LADSPA sink"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "NULL sink ৰ সময় নিৰ্ধাৰণ"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Null ফলাফল"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "আভ্যন্তৰীণ অ'ডিঅ'"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "মোডেম"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "প্ৰাথমিক lt_dlopen loader পোৱা ন'গ'ল ।"
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "নতুন dl loader বিতৰণ কৰিবলৈ বিফল ।"
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind now loader যোগ কৰিবলৈ বিফল ।"
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "চিগ্নেল %s পোৱা গ'ল ।"
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "প্ৰস্থান কৰা হৈছে ।"
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "ব্যৱহাৰকৰ্তা '%s' পোৱা ন'গ'ল ।"
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "'%s' সমষ্টি পোৱা ন'গ'ল ।"
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "ব্যৱহাৰকৰ্তা '%s' (UID %lu) আৰু সমষ্টি '%s' (GID %lu) পোৱা গ'ল ।"
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "ব্যৱহাৰকৰ্তা '%s' আৰু সমষ্টি '%s' ৰ GID অমিল ।"
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "ব্যৱহাৰকৰ্তা '%s' ৰ ঘৰৰ পঞ্জিকা '%s' নহয়, আওকাণ কৰা হৈছে ।"
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' সৃষ্টি কৰিবলৈ বিফল: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "সমষ্টিৰ তালিকা সলনি কৰিবলৈ ব্যৰ্থ: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID সলনি কৰিবলৈ ব্যৰ্থ: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID সলনি কৰিবলৈ ব্যৰ্থ: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "ৰূটৰ অধিকাৰ সফলভাবে এৰোৱা গ'ল ।"
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "এই স্থাপত্যত প্ৰণালী ব্যাপক মোড অসমৰ্থিত ।"
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) বিফল: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "আদেশ শাৰী বিশ্লেষণ কৰিবলৈ বিফল ।"
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "ডেমন নাই চলা"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "PID %u ৰূপে ডেমন চলিছে"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "ডেমন kill কৰিবলৈ ব্যৰ্থ: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"root পৰিচয়ে এই প্ৰোগ্ৰাম সঞ্চালিত হোৱা উচিত নহয় (ন'হ'লে   system উল্লিখিত হয়) ।"
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Root ৰ অধিকাৰ আৱশ্যক ।"
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "প্ৰণালী চানেকিৰ ক্ষেত্ৰত   start সমৰ্থিত নহয় ।"
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "প্ৰণালী মোডত চলিছে, কিন্তু   disallow exit নিৰ্ধাৰিত নহয়!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "প্ৰণালী মোডত চলিছে, কিন্তু   disallow module loading নিৰ্ধাৰিত নহয়!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "প্ৰণালী মোডত চলিছে, SHM মোড বলপূৰ্বক নিষ্ক্ৰিয় কৰা হৈছে!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"প্ৰণালী মোডত চলিছে, কাম নকৰা সময়ৰ পৰা প্ৰস্থান কৰা বলপূৰ্বক নিষ্ক্ৰিয় কৰা হৈছে!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdio প্ৰাপ্ত কৰিবলৈ ব্যৰ্থ ।"
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe বিফল: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() বিফল: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() বিফল: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "ডেমন আৰম্ভ কৰিবলৈ বিফল ।"
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "সফলতাৰে ডেমন আৰম্ভ কৰা হৈছে ।"
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() বিফল: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "এইটো PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "সঙ্কলনৰ গৃহস্থ: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "সঙ্কলনৰ CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "গৃহস্থত চলোৱা হৈছে: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u CPU পোৱা গৈছে ।"
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "পেজৰ মাপ %lu bytes"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Valgrind সমৰ্থনৰ সৈতে সঙ্কলন কৰা হৈছে: হয়"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Valgrind সমৰ্থনৰ সৈতে সঙ্কলন কৰা হৈছে: নহয়"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "valgrind মোডত চলিছে: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "গৃহস্থত চলোৱা হৈছে: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Optimized build: হয়"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "Optimized build: নহয়"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG ব্যাখ্যা কৰা হৈছে, সকলো asserts নিষ্ক্ৰিয় কৰা হৈছে ।"
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH ব্যাখ্যা কৰা হৈছে, অকল fast path asserts নিষ্ক্ৰিয় কৰা হৈছে ।"
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "সকলো asserts সক্ৰিয় কৰা হৈছে ।"
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "যন্ত্ৰ ID প্ৰাপ্ত কৰিবলৈ ব্যৰ্থ"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "যন্ত্ৰ ID হ'ল %s ।"
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "সেশান ID হল %s।"
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "ৰান টাইম পঞ্জিকা %s ব্যৱহাৰ কৰা হৈছে ।"
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "অৱস্থাসূচক পঞ্জিকা %s ব্যৱহাৰ কৰা হৈছে ।"
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "মডিউল ডিৰেক্টৰি %s ব্যৱহাৰ কৰা হচ্ছে।"
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "প্ৰণালী মোডত চলিছে: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"আপনি সিস্টেম মোডে PA সঞ্চালিত কৰছেন এবং এটি না কৰাই বাঞ্ছনীয়।\n"
+"এৰ ফলে প্ৰত্যাশামত ফলাফল না পাওয়াৰ সম্ভাবনা ৰয়েছে।\n"
+"সিস্টেম মোডে ব্যৱহাৰেৰ সমস্যা সম্পৰ্কে জানতে হলে http://www.freedesktop.org/wiki/"
+"Software/PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ দেখুন।"
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() ব্যৰ্থ ।"
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "নতুন high resolution timers পোৱা হয়! অভিনন্দন!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"শ্ৰীমান, আপোনাৰ কাৰ্ণেল পূৰণি! high resolution timer সক্ৰিয় থকা Linux ক আজি "
+"উপদেশ দিয়া হয়!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() ব্যৰ্থ ।"
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "ডেমন আৰম্ভ কৰিবলৈ ব্যৰ্থ ।"
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"তুলি লোৱা মডিউল নোহোৱাকে ডেমন আৰম্ভ কৰা হৈছে, কোনো কাম সঞ্চালন কৰা সম্ভৱ নহয় ।"
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "ডেমন আৰম্ভ কৰা সম্পূৰ্ণ ।"
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "ডেমন বন্ধ কৰাৰ প্ৰক্ৰিয়া আৰম্ভ কৰা হৈছে ।"
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "ডেমন বন্ধ কৰা হৈছে ।"
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"   h,   help                            Show this help\n"
+"        version                         Show version\n"
+"        dump conf                       Dump default configuration\n"
+"        dump modules                    Dump list of available modules\n"
+"        dump resample methods           Dump available resample methods\n"
+"        cleanup shm                     Cleanup stale shared memory "
+"segments\n"
+"        start                           Start the daemon if it is not "
+"running\n"
+"   k    kill                            Kill a running daemon\n"
+"        check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"        system[=BOOL]                   Run as system wide instance\n"
+"   D,   daemonize[=BOOL]                Daemonize after startup\n"
+"        fail[=BOOL]                     Quit when startup fails\n"
+"        high priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"        realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"        disallow module loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"        disallow exit[=BOOL]            Disallow user requested exit\n"
+"        exit idle time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"        module idle time=SECS           Unload autoloaded modules when idle "
+"and\n"
+"                                        this time passed\n"
+"        scache idle time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"        log level[=LEVEL]               Increase or set verbosity level\n"
+"   v                                    Increase the verbosity level\n"
+"        log target={auto,syslog,stderr} Specify the log target\n"
+"        log meta[=BOOL]                 Include code location in log "
+"messages\n"
+"        log time[=BOOL]                 Include timestamps in log messages\n"
+"        log backtrace=FRAMES            Include a backtrace in log messages\n"
+"   p,   dl search path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"        resample method=METHOD          Use the specified resampling method\n"
+"                                        (See   dump resample methods for\n"
+"                                        possible values)\n"
+"        use pid file[=BOOL]             Create a PID file\n"
+"        no cpu limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"        disable shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"   L,   load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"   F,   file=FILENAME                   Run the specified script\n"
+"   C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"   n                                    Don't load default script file\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "  daemonize দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "  fail দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"  log level ৰ কাৰণে লগ স্তৰৰ তৰ্ক প্ৰত্যাশিত (হয় সংখ্যা ০..৪ ৰ সীমাত বা debug, "
+"info, notice, warn, error ৰ যিকোনো এটা) ।"
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "  high priority দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "  realtime দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "  disallow module loading দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "  disallow exit দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "  use pid file দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "অবৈধ লগ লক্ষ্য: 'syslog', 'stderr' বা 'auto' ৰ এটা ব্যৱহাৰ কৰক"
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "  log time দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "  log meta দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "resample পদ্ধতি '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "  system দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "  no cpu limit দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "  disable shm দ্বাৰা বুলিয়েন তৰ্ক প্ৰত্যাশিত"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "নাম: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "মডিউল সংক্ৰান্ত কোনো তথ্য উপলব্ধ নাই\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "সংস্কৰণ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "বিৱৰণ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "নিৰ্মাতা: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "ব্যৱহাৰ পদ্ধতি: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "এবাৰ তুলি লোৱা হ'ব: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "অবচিত কৰাৰ সতৰ্কবাৰ্তা: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "পাথ: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] লগ লক্ষ্য '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] লগৰ স্তৰ '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] resample পদ্ধতি '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] rlimit '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] চানেকিৰ বিন্যাস '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] চানেকিৰ মাত্ৰা '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] চানেকিৰ চেনেল '%s' বৈধ নহয়"
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] চেনেল মেপ '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] অংশৰ সংখ্যা '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] অংশৰ মাপ '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] nice স্তৰ '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] চানেকিৰ মাত্ৰা '%s' বৈধ নহয় ।"
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "বিন্যাস নথিপত্ৰ খুলিবলৈ ব্যৰ্থ: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"নিৰ্ধাৰিত অবিকল্পিত চেনেল মেপত নিৰ্ধাৰিত অবিকল্পিত চেনেলৰ সংখ্যাতকে বেলেগ সংখ্যক "
+"চেনেল আছে ।"
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### চিহ্নিত বিন্যাস নথিপত্ৰৰ পৰা পঢ়া হ'ব: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "অধিকাৰ বৰ্জন কৰা হচ্ছে।"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio শব্দ ব্যৱস্থা"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "PulseAudio শব্দ ব্যৱস্থা আৰম্ভ কৰা হ'ব"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio শব্দ ব্যৱস্থা"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "PulseAudio শব্দ ব্যৱস্থা আৰম্ভ কৰা হ'ব"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "মোনো"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "সন্মুখত কেন্দ্ৰস্থিত"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "সন্মুখত কেন্দ্ৰস্থিত"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "সন্মুখত বাওঁফালে"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "পিছত কেন্দ্ৰস্থিত"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "পিছত বাওঁফালে"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "পিছত সোঁফালে"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "সন্মুখত কেন্দ্ৰৰ বাওঁফালে"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "সন্মুখত কেন্দ্ৰৰ সোঁফালে"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "কাষত বাওঁফালে"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "কাষত সোঁফালে"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "অ'ক্সিলেৰি ০"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "অ'ক্সিলেৰি ০"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "অ'ক্সিলেৰি ১"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "অ'ক্সিলেৰি ৩"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "অ'ক্সিলেৰি ৪"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "অ'ক্সিলেৰি ৪"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "অ'ক্সিলেৰি ৬"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "অ'ক্সিলেৰি ৭"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "অ'ক্সিলেৰি ৮"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "অ'ক্সিলেৰি ৯"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "অ'ক্সিলেৰি ১০"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "অ'ক্সিলেৰি ১১"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "অ'ক্সিলেৰি ১২"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "অ'ক্সিলেৰি ১৩"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "অ'ক্সিলেৰি ১৪"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "অ'ক্সিলেৰি ১৫"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "অ'ক্সিলেৰি ১৬"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "অ'ক্সিলেৰি ১৭"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "অ'ক্সিলেৰি ১৮"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "অ'ক্সিলেৰি ১৯"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "অ'ক্সিলেৰি ২০"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "অ'ক্সিলেৰি ২১"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "অ'ক্সিলেৰি ২২"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "অ'ক্সিলেৰি ২৩"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "অ'ক্সিলেৰি ২৪"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "অ'ক্সিলেৰি ২৫"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "অ'ক্সিলেৰি ২৬"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "অ'ক্সিলেৰি ২৭"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "অ'ক্সিলেৰি ২৮"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "অ'ক্সিলেৰি ২৯"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "অ'ক্সিলেৰি ৩০"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "অ'ক্সিলেৰি ৩১"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "ওপৰত কেন্দ্ৰস্থিত"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "ওপৰত সন্মুখত কেন্দ্ৰস্থিত"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "ওপৰত সন্মুখত বাওঁফালে"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "ওপৰত সন্মুখত বাওঁফালে"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "ওপৰত পিছত কেন্দ্ৰস্থিত"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "ওপৰত পিছত বাওঁফালে"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "ওপৰত পিছত সোঁফালে"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(অবৈধ)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "স্টিৰিঅ'"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "ছাৰাউণ্ড ৪.০"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "ছাৰাউণ্ড ৪.১"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "ছাৰাউণ্ড ৫.০"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "ছাৰাউণ্ড ৫.১"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "ছাৰাউণ্ড ৭.১"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "ঠিক আছে"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "ব্যৱহাৰৰ অধিকাৰ প্ৰত্যাখ্যাত"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "অজ্ঞাত নিৰ্দেশ"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "অবৈধ তৰ্ক"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "পদাৰ্থ উপস্থিত"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "এই ধৰনৰ কোনো পদাৰ্থ উপস্থিত নাই"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "সংযোগ নাকচ কৰা হৈছে"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "প্ৰোটোকল সংক্ৰান্ত ত্ৰুটি"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "সময়সীমা"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "কোনো অনুমোদনৰ কি নাই"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "অভ্যন্তৰীণ ত্ৰুটি"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "সংযোগ বন্ধ কৰা হৈছে"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "পদাৰ্থ kill কৰা হৈছে"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "সেৱক বৈধ নহয়"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "মডিউল আৰম্ভ কৰিবলৈ ব্যৰ্থ"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "অৱস্থা সঠিক নহয়"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "তথ্য অনুপস্থিত "
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "বিসঙ্গতিপূৰ্ণ প্ৰটকল সংস্কৰণ"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "অত্যাধিক বড়"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "সমৰ্থন কৰা নহয়"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "অজানা ত্ৰুটিৰ কোড"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "এই ধৰনৰ কোনো এক্সটেনশন নাই"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "অবচিত বৈশিষ্ট্য"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "অনুপস্থিত বাস্তবায়ন"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "গ্ৰাহক ফৰ্ক কৰা হৈছে"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "নিবেশ/নিৰ্গম ত্ৰুটি"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "যন্ত্ৰ বা সম্পদ ব্যস্ত"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() ব্যৰ্থ: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "কুকিৰ তথ্য বিশ্লেষণ কৰিবলৈ ব্যৰ্থ"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "বিন্যাস নথিপত্ৰ '%s' খুলিবলৈ ব্যৰ্থ: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "কোনো কুকি তুলি লোৱা নহয় । কুকি নোহোৱাকে সংযোগৰ প্ৰচেষ্টা কৰা হৈছে ।"
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "অজানা এক্সটেনশন '%s' ৰ বাবে বাৰ্তা প্ৰাপ্ত হৈছে"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "স্ট্ৰিম ড্ৰেইন (অৰ্থাৎ ফাঁকা) কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "প্লে ব্যাক স্ট্ৰিম ফাঁকা কৰা হয়েছে।"
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "সাৰ্ভাৰেৰ সাথে স্থাপিত সংযোগ ফাঁকা কৰা হচ্ছে।"
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_write() ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "সাফল্যেৰ সাথে স্ট্ৰিম নিৰ্মিত হয়েছে।"
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "বাফাৰেৰ মাপ: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "বাফাৰেৰ মাপ: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "স্যাম্পেলেৰ spec '%s', ও চ্যানেল ম্যাপ '%s' ব্যৱহাৰ কৰা হচ্ছে।"
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "যন্ত্ৰ %s ৰ সাথে সংযোগ কৰা হয়েছে (%u, %ssuspended)।"
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "ষ্ট্ৰিম সংক্ৰান্ত ত্ৰুটি: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "স্ট্ৰিম যন্ত্ৰ স্থগিত কৰা হয়েছে। %s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "স্ট্ৰিম যন্ত্ৰ পুনৰাৰম্ভ কৰা হয়েছে। %s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "ধীৰ গতিৰ স্ট্ৰিম.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "স্ট্ৰিম মাত্ৰা অতিক্ৰম কৰিছে।%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "স্ট্ৰিম আৰম্ভ কৰা হয়েছে। %s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "%s যন্ত্ৰে স্ট্ৰিম স্থানান্তৰ কৰা হয়েছে (%u, %ssuspended)।%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "not "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "স্ট্ৰিম বাফাৰেৰ এট্ৰিবিউট পৰিবৰ্তিত হয়েছে। %s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "সংযোগ স্থাপিত হয়েছে।%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "সংযোগ বিফল: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "ফাইলেৰ সমাপ্তি সনাক্ত হয়েছে।"
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "সিগন্যাল প্ৰাপ্ত হয়েছে, প্ৰস্থান কৰা হ'ব।"
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "লেটেন্সিৰ পৰিমাণ প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "সময়: %0.3f ছেকেণ্ড; লেটেন্সি: %0.0f usec ।"
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"   h,   help                            Show this help\n"
+"        version                         Show version\n"
+"\n"
+"   r,   record                          Create a connection for recording\n"
+"   p,   playback                        Create a connection for playback\n"
+"\n"
+"   v,   verbose                         Enable verbose operations\n"
+"\n"
+"   s,   server=SERVER                   The name of the server to connect "
+"to\n"
+"   d,   device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"   n,   client name=NAME                How to call this client on the "
+"server\n"
+"        stream name=NAME                How to call this stream on the "
+"server\n"
+"        volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"        rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"        format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24 32le, s24 32be (defaults to "
+"s16ne)\n"
+"        channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"        channel map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"        fix format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"        fix rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"        fix channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"        no remix                        Don't upmix or downmix channels.\n"
+"        no remap                        Map channels by index instead of "
+"name.\n"
+"        latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"        process time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"        property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"        raw                             Record/play raw PCM data.\n"
+"        file format=FFORMAT             Record/play formatted PCM data.\n"
+"        list file formats               List available file formats.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"libpulse ৰ সৈতে সঙ্কলন কৰা হৈছে %s\n"
+"libpulse ৰ সৈতে যুক্ত %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "ক্লায়েন্টেৰ নাম '%s' বৈধ নয়"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "স্ট্ৰিমেৰ নাম '%s' বৈধ নয়।"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "চ্যানেল ম্যাপ '%s' বৈধ নয়"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "লেটেন্সিৰ জন্য নিৰ্ধাৰিত বৈশিষ্ট্য '%s' বৈধ নয়"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "প্ৰসেসেৰ সময়েৰ বৈশিষ্ট্য '%s' বৈধ নয়"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "বৈশিষ্ট্য '%s' বৈধ নয়।"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "ফাইলেৰ অজানা বিন্যাস %s।"
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "অবৈধ স্যাম্পেল নিৰ্ধাৰিত"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "অত্যাধিক আৰ্গুমেন্ট।"
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "স্যাম্পেলেৰ মান নিৰ্ধাৰণেৰ ফাইল নিৰ্মাণ কৰতে ব্যৰ্থ"
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "শব্দেৰ ফাইল খুলতে ব্যৰ্থ।"
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"সতৰ্কবাৰ্তা: চিহ্নিত স্যাম্পেল নিৰ্ধাৰণেৰ ফাইলটিৰ তথ্য, এই ফাইলেৰৰ পৰা উপলব্ধ তথ্য "
+"দ্বাৰা প্ৰতিস্থাপিত হ'ব।"
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "ফাইলৰ পৰা স্যাম্পেল সংক্ৰান্ত তথ্য প্ৰাপ্ত কৰতে ব্যৰ্থ।"
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "সতৰ্কবাৰ্তা: ফাইলৰ পৰা চ্যানেলেৰ ম্যাপ নিৰ্ধাৰণ কৰতে ব্যৰ্থ।"
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "চ্যানেলেৰ ম্যাপ ও স্যাম্পেলেৰ নিৰ্ধাৰিত মানে গৰমিল"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "সতৰ্কবাৰ্তা: ফাইলেত চ্যানেলেৰ ম্যাপ লিখতে ব্যৰ্থ।"
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"এটা %s স্ট্ৰিম খোলা হচ্ছে। এটিৰ জন্য '%s' ৰ স্যাম্পেলেৰ নিৰ্ধাৰিত মান ও '%s' "
+"চ্যানেলেৰ ম্যাপ প্ৰয়োগ কৰা হ'ব।"
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "ৰেকৰ্ড কৰা হৈছে"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "প্লে বেক"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "আদেশ শাৰী বিশ্লেষণ কৰিবলৈ বিফল ।"
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() ব্যৰ্থ।"
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() ব্যৰ্থ।"
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() ব্যৰ্থ।"
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() ব্যৰ্থ: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() ব্যৰ্থ।"
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() ব্যৰ্থ।"
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "স্থগিত কৰিবলৈ ব্যৰ্থ: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "পুনৰাৰম্ভ কৰিবলৈ ব্যৰ্থ: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "সতৰ্কবাৰ্তা: ধ্বনি সেৱক স্থানীয় নহয়, স্থগিত কৰা নহয় ।\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "<b>সংযোগৰ মোড</b>: %s<br>\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT প্ৰাপ্ত হৈছে, প্ৰস্থান কৰা হৈছে ।\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "সতৰ্কবাৰ্তা: চিগ্নেল %u দ্বাৰা চাইল্ড প্ৰক্ৰিয়া বন্ধ কৰা হৈছে\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"   h,   help                            Show this help\n"
+"        version                         Show version\n"
+"   s,   server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"libpulse ৰ সৈতে সঙ্কলন কৰা হৈছে %s\n"
+"libpulse ৰ সৈতে যুক্ত %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() ব্যৰ্থ ।\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() ব্যৰ্থ ।\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() ব্যৰ্থ ।\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "পৰিসংখ্যান প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "বৰ্ত্তমানে ব্যৱহৃত: %u blocks containing %s bytes total.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "সম্পূৰ্ণ জীৱনকালত বিতৰণ কৰা: %u blocks containing %s bytes total.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "চানেকি কেশ্বৰ মাপ: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "সাৰ্ভাৰ সংক্ৰান্ত তথ্য প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"ব্যৱহাৰকৰ্তাৰ নাম: %s\n"
+"গৃহস্থৰ নাম: %s\n"
+"সেৱকৰ নাম: %s\n"
+"সেৱকৰ সংস্কৰণ: %s\n"
+"চানেকিৰ অবিকল্পিত নিৰ্ধাৰিত মান: %s\n"
+"অবিকল্পিত চেনেল মেপ: %s\n"
+"অবিকল্পিত চিঙ্ক: %s\n"
+"অবিকল্পিত উৎস: %s\n"
+"কুকি: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "sink সংক্ৰান্ত তথ্য প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"চিঙ্ক #%u\n"
+"\tঅৱস্থা: %s\n"
+"\tনাম: %s\n"
+"\tবিৱৰণ: %s\n"
+"\tচালক: %s\n"
+"\tচানেকি নিৰ্ধাৰণ: %s\n"
+"\tচেনেল মাপ: %s\n"
+"\tগৰাকীৰ অংশ: %u\n"
+"\tমিউট: %s\n"
+"\tধ্বনি মাত্ৰা: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase ধ্বনি: %s%s%s\n"
+"\tমণিটৰ উৎস: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tফ্লেগ: %s%s%s%s%s%s\n"
+"\tগুণ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tপোৰ্ট:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tসক্ৰিয় পোৰ্ট: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tপোৰ্ট:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "উৎস সংক্ৰান্ত তথ্য প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"উৎস #%u\n"
+"\tঅৱস্থা: %s\n"
+"\tনাম: %s\n"
+"\tবিৱৰণ: %s\n"
+"\tচালক: %s\n"
+"\tচানেকি নিৰ্ধাৰণ: %s\n"
+"\tচেনেল মেপ: %s\n"
+"\tগৰাকীৰ অংশ: %u\n"
+"\tমিউট: %s\n"
+"\tধ্বনিৰ মাত্ৰা: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tচিঙ্কৰ মণিটৰ: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tফ্লেগ: %s%s%s%s%s%s\n"
+"\tগুণ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "মডিউল সংক্ৰান্ত তথ্য প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"অংশ #%u\n"
+"\tনাম: %s\n"
+"\tতৰ্ক: %s\n"
+"\tব্যৱহাৰৰ কাউন্টাৰ: %s\n"
+"\tগুণ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "ক্লায়েন্ট সংক্ৰান্ত তথ্য প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"গ্ৰাহক #%u\n"
+"\tচালক: %s\n"
+"\tগৰাকীৰ অংশ: %s\n"
+"\tগুণ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "কাৰ্ড সংক্ৰান্ত তথ্য প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"কাৰ্ড #%u\n"
+"\tনাম: %s\n"
+"\tচালক: %s\n"
+"\tগৰাকীৰ অংশ: %s\n"
+"\tগুণ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tপাৰ্শ্বৰূপ:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tসক্ৰিয় পাৰ্শ্বৰূপ: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "sink নিবেশ সংক্ৰান্ত তথ্য প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"চিঙ্ক নিবেশ #%u\n"
+"\tচালক: %s\n"
+"\tগৰাকীৰ অংশ: %s\n"
+"\tগ্ৰাহক: %s\n"
+"\tচিঙ্ক: %u\n"
+"\tচানেকি নিৰ্ধাৰণ: %s\n"
+"\tচেনেল মেপ: %s\n"
+"\tমিউট: %s\n"
+"\tধ্বনি মাত্ৰা: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample ধৰণ: %s\n"
+"\tগুণ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "উৎস নিৰ্গম সংক্ৰান্ত তথ্য প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"চিঙ্ক নিবেশ #%u\n"
+"\tচালক: %s\n"
+"\tগৰাকীৰ অংশ: %s\n"
+"\tগ্ৰাহক: %s\n"
+"\tচিঙ্ক: %u\n"
+"\tচানেকি নিৰ্ধাৰণ: %s\n"
+"\tচেনেল মেপ: %s\n"
+"\tমিউট: %s\n"
+"\tধ্বনি মাত্ৰা: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample ধৰণ: %s\n"
+"\tগুণ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "স্যাম্পেল সংক্ৰান্ত তথ্য প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"চানেকি #%u\n"
+"\tনাম: %s\n"
+"\tচানেকি নিৰ্ধাৰণ: %s\n"
+"\tচেনেল মেপ: %s\n"
+"\tধ্বনি মাত্ৰা: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tসময়: %0.1fs\n"
+"\tআকাৰ: %s\n"
+"\tএলেহুৱা: %s\n"
+"\tনথিপত্ৰৰ নাম: %s\n"
+"\tগুণ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "ব্যৰ্থতা: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "উৎস সংক্ৰান্ত তথ্য প্ৰাপ্ত কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "স্যাম্পেল আপলোড কৰতে ব্যৰ্থ: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "সম্পূৰ্ণ হওয়াৰ পূৰ্বে ফাইল সমাপ্ত হয়েছে"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "সেৱক বৈধ নহয়"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT প্ৰাপ্ত হয়েছে, প্ৰস্থান কৰা হয়েছে।"
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "অবৈধ শব্দেৰ মাত্ৰা নিৰ্ধাৰিত"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"   h,   help                            Show this help\n"
+"        version                         Show version\n"
+"   s,   server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"libpulseৰ সৈতে সঙ্কলন কৰা %s\n"
+"libpulse ৰ সৈতে যুক্ত %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "লোড কৰাৰ উদ্দেশ্যে অনুগ্ৰহ কৰি এটা স্যাম্পেল ফাইল উল্লেখ কৰুন"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "শব্দেৰ ফাইল খুলতে ব্যৰ্থ।"
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "সতৰ্কবাৰ্তা: ফাইলৰ পৰা স্যাম্পেলেৰ নিৰ্ধাৰিত মাপ নিৰ্মাণ কৰতে ব্যৰ্থ।"
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "বাজানোৰ উদ্দেশ্যে এটা স্যাম্পেল ফাইল উল্লেখ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "অপসাৰণেৰ উদ্দেশ্যে এটা স্যাম্পেল ফাইল উল্লেখ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "সিংক নিবেশ ইন্ডেক্স ও এটা সিংক নিৰ্ধাৰণ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "সোৰ্স নিৰ্গম ইন্ডেক্স ও এটা সোৰ্স নিৰ্ধাৰণ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "মডিউলেৰ নাম ও আৰ্গুমেন্ট নিৰ্ধাৰণ কৰা আবশ্যক।"
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "মডিউল ইন্ডেক্স নিৰ্ধাৰণ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr "একাধিক সিংক নিৰ্ধাৰণ কৰা যাবে না। বুলিয়েন মান নিৰ্ধাৰণ কৰা আবশ্যক।"
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr "একাধিক সোৰ্স নিৰ্ধাৰণ কৰা যাবে না। বুলিয়েন মান নিৰ্ধাৰণ কৰা আবশ্যক।"
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "কাৰ্ডেৰ নাম/ইন্ডেক্স ও এটা প্ৰোফাইলেৰ নাম উল্লেখ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "sink ৰ নাম/ইন্ডেক্স ও এটা পোৰ্টেৰ নাম উল্লেখ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "উৎসেৰ নাম/ইন্ডেক্স ও এটা পোৰ্টে নাম উল্লেখ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "sink ৰ নাম/ইন্ডেক্স ও এটা পোৰ্টেৰ নাম উল্লেখ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "উৎসেৰ নাম/ইন্ডেক্স ও এটা শব্দেৰ মাত্ৰা উল্লেখ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "সিংক নিবেশ ইন্ডেক্স ও শব্দেৰ মাত্ৰা নিৰ্ধাৰণ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "সিংক নিবেশ ইন্ডেক্স বৈধ নয়"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "সোৰ্স নিৰ্গম ইন্ডেক্স ও এটা সোৰ্স নিৰ্ধাৰণ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "সিংক নিবেশ ইন্ডেক্স বৈধ নয়"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "sink ৰ নাম/ইন্ডেক্স ও এটা নিঃশব্দতাৰ বুলিয়ান উল্লেখ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "অবৈধ স্যাম্পেল নিৰ্ধাৰিত"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "উৎসেৰ নাম/ইন্ডেক্স ও নিঃশব্দতাৰ বুলিয়ান উল্লেখ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "সিংক নিবেশ ইন্ডেক্স ও নিঃশব্দতাৰ বুলিয়ান নিৰ্ধাৰণ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "অবৈধ সিংক নিবেশ ইন্ডেক্স নিৰ্ধাৰিত"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "উৎসেৰ নাম/ইন্ডেক্স ও নিঃশব্দতাৰ বুলিয়ান উল্লেখ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "অবৈধ সিংক নিবেশ ইন্ডেক্স নিৰ্ধাৰিত"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "sink ৰ নাম/ইন্ডেক্স ও এটা নিঃশব্দতাৰ বুলিয়ান উল্লেখ কৰা আবশ্যক"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "কোনো বৈধ কমান্ড নিৰ্ধাৰিত হয়নি।"
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [ D display] [ S server] [ O sink] [ I source] [ c file]  [ d| e| i| r]\n"
+"\n"
+"  d    Show current PulseAudio data attached to X11 display (default)\n"
+"  e    Export local PulseAudio data to X11 display\n"
+"  i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+"  r    Remove PulseAudio data from X11 display\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "আদেশ শাৰী বিশ্লেষণ কৰিবলৈ ব্যৰ্থ ।\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "সেৱক: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "উৎস: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "চিঙ্ক: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "কুকি: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "কুকি সংক্ৰান্ত তথ্য বিশ্লেষণ কৰিবলৈ ব্যৰ্থ\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "কুকি সংক্ৰান্ত তথ্য সংৰক্ষণ কৰিবলৈ ব্যৰ্থ\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "গ্ৰাহক বিন্যাস নথিপত্ৰ তুলিবলৈ ব্যৰ্থ ।\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "পৰিবেশ বিন্যাস সংক্ৰান্ত তথ্য পঢ়িবলৈ ব্যৰ্থ ।\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN প্ৰাপ্ত কৰিবলৈ ব্যৰ্থ ।\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "কুকি সংক্ৰান্ত তথ্য তুলিবলৈ ব্যৰ্থ\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "এতিয়াও বাস্তবায়িত নহয় ।\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "PulseAudio ডেমন চলছে না বা সেশানৰ ডেমন ৰূপে চলছে না।"
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio ডেমন kill কৰিবলৈ ব্যৰ্থ ।"
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "ডেমনৰ পৰা কোনো প্ৰতিক্ৰিয়া পোৱা নাযায় ।"
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "autospawn লক প্ৰয়োগ কৰিবলৈ ব্যৰ্থ ।"
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA ই আমাক যন্ত্ৰৰ পৰা নতুন তথ্য লিখিবলৈ উথালে, কিন্তু একো লিখিবলৈ নাছিল!\n"
+"অতি সম্ভৱ এইটো ALSA চালক '%s' ৰ এটা বাগ । অনুগ্ৰহ কৰি এই সমস্যা ALSA বিকাশকক "
+"জনাওক ।\n"
+"POLLOUT নিৰ্ধাৰিত হোৱাৰি পিছতো আমি উথিলো    কিন্তু তাৰ পিছৰ snd_pcm_avail() এ ০ "
+"দিলে বা অন্য এটা মান < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA ই আমাক যন্ত্ৰৰ পৰা নতুন তথ্য পঢ়িবলৈ উথালে, কিন্তু একো পঢ়িবলৈ নাছিল!\n"
+"অতি সম্ভৱ এইটো ALSA চালক '%s' ৰ এটা বাগ । অনুগ্ৰহ কৰি এই সমস্যা ALSA বিকাশকক "
+"জনাওক ।\n"
+"POLLIN নিৰ্ধাৰিত হোৱাৰি পিছতো আমি উথিলো    কিন্তু তাৰ পিছৰ snd_pcm_avail() এ ০ "
+"দিলে বা অন্য এটা মান < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "বন্ধ"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "High Fidelity Playback (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "হাই ফিডেলিটি ক্যাপচাৰ (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telephony Duplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio ধ্বনি সেৱক"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "নিৰ্গম যন্ত্ৰ"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "নিবেশ যন্ত্ৰ"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@ ত অ'ডিঅ'"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "নিবেশ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "ডকিং স্টেছনৰ পৰা নিবেশ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "ডকিং স্টেছনৰ মাইক্ৰোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "ডকিং স্টেছনৰ পৰা নিবেশ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "লাইন ইন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "মাইক্ৰোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "ডকিং স্টেছনৰ মাইক্ৰোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "মাইক্ৰোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "বহিস্থিত মাইক্ৰোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "অভ্যন্তৰীণ মাইক্ৰোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "ৰেডিঅ'"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "ভিডিঅ'"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "স্বয়ংক্ৰিয় গেইন নিয়ন্ত্ৰণ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "স্বয়ংক্ৰিয় গেইন নিয়ন্ত্ৰণ প্ৰয়োগ কৰা ন'হ'ব"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "বুস্ট"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "বুস্ট প্ৰয়োগ কৰা ন'হ'ব"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "বিবৰ্ধক"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "বিবৰ্ধন প্ৰয়োগ কৰা ন'হ'ব"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "বুস্ট"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "বুস্ট প্ৰয়োগ কৰা ন'হ'ব"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+#, fuzzy
+msgid "Headphones"
+msgstr "এনালগ হেড ফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "এনালগ নিবেশ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "ডকিং স্টেছনৰ মাইক্ৰোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "এনালগ নিৰ্গম"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "এনালগ নিৰ্গম (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "লাইন ইন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "এনালগ মোনো নিৰ্গম"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "এনালগ স্টিৰিঅ'"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "ডিজিটেল স্টিৰিঅ' (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "ডিজিটেল স্টিৰিঅ' (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "এনালগ মোনো"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "এনালগ স্টিৰিঅ'"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "এনালগ ছাৰাউন্ড ২.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "এনালগ ছাৰাউন্ড ৩.০"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "এনালগ ছাৰাউন্ড ৩.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "এনালগ ছাৰাউন্ড ৪.০"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "এনালগ ছাৰাউন্ড ৪.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "এনালগ ছাৰাউন্ড ৫.০"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "এনালগ ছাৰাউন্ড ৫.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "এনালগ ছাৰাউন্ড ৬.০"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "এনালগ ছাৰাউন্ড ৬.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "এনালগ ছাৰাউন্ড ৭.০"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "এনালগ ছাৰাউন্ড ৭.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "ডিজিটেল স্টিৰিঅ' (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "ডিজিটেল স্টিৰিঅ' (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "ডিজিটেল ছাৰাউন্ড ৪.০ (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "ডিজিটেল ছাৰাউন্ড ৫.১ (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "ডিজিটেল স্টিৰিঅ' (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "ডিজিটেল ছাৰাউন্ড ৫.১ (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "এনালগ মোনো ডুপ্লেক্স"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "এনালগ স্টিৰিঅ' ডুপ্লেক্স"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "ডিজিটেল স্টিৰিঅ' ডুপ্লেক্স (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Null ফলাফল"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "নিবেশ"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] এই স্থাপত্যত rlimit সমৰ্থিত নহয় ।"
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() ব্যৰ্থ"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "উৎসৰ নিৰ্গম #%u\n"
+#~ "\tচালক: %s\n"
+#~ "\tগৰাকীৰ অংশ: %s\n"
+#~ "\tগ্ৰাহক: %s\n"
+#~ "\tউৎস: %u\n"
+#~ "\tচানেকি নিৰ্ধাৰণ: %s\n"
+#~ "\tচেনেল মেপ: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample ধৰণ: %s\n"
+#~ "\tগুণ:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload sample FILENAME [NAME]\n"
+#~ "%s [options] play sample NAME [SINK]\n"
+#~ "%s [options] remove sample NAME\n"
+#~ "%s [options] move sink input SINKINPUT SINK\n"
+#~ "%s [options] move source output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load module NAME [ARGS ...]\n"
+#~ "%s [options] unload module MODULE\n"
+#~ "%s [options] suspend sink SINK 1|0\n"
+#~ "%s [options] suspend source SOURCE 1|0\n"
+#~ "%s [options] set card profile CARD PROFILE\n"
+#~ "%s [options] set sink port SINK PORT\n"
+#~ "%s [options] set source port SOURCE PORT\n"
+#~ "%s [options] set sink volume SINK VOLUME\n"
+#~ "%s [options] set source volume SOURCE VOLUME\n"
+#~ "%s [options] set sink input volume SINKINPUT VOLUME\n"
+#~ "%s [options] set sink mute SINK 1|0\n"
+#~ "%s [options] set source mute SOURCE 1|0\n"
+#~ "%s [options] set sink input mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "   h,   help                            Show this help\n"
+#~ "        version                         Show version\n"
+#~ "\n"
+#~ "   s,   server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "   n,   client name=NAME                How to call this client on the "
+#~ "server\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s %s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "ডিজিটেল ছাৰাউন্ড ৪.০ (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Low Frequency Emmiter"
diff --git a/po/be.po b/po/be.po
new file mode 100644 (file)
index 0000000..162c555
--- /dev/null
+++ b/po/be.po
@@ -0,0 +1,3175 @@
+# Belarusian tanslation of PulseAudio
+# Copyright (C) 2016 PulseAudio's COPYRIGHT HOLDER
+# This file is distributed under the same license as the PulseAudio package.
+#
+# Viktar Vaŭčkievič <victorenator@gmail.com>, 2016.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PulseAudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-07-16 14:23+0300\n"
+"PO-Revision-Date: 2016-07-19 11:06+0300\n"
+"Last-Translator: Viktar Vaŭčkievič <victorenator@gmail.com>\n"
+"Language-Team: Belarusian <>\n"
+"Language: be\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<"
+"=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Lokalize 2.0\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [параметры]\n"
+"\n"
+"Каманды:\n"
+"  -h, --help                            Паказаць гэту даведку.\n"
+"      --version                         Паказаць версію.\n"
+"      --dump-conf                       Вывесці агаданую канфігурацыю.\n"
+"      --dump-modules                    Вывесці спіс даступных модуляў.\n"
+"      --dump-resample-methods           Вывесці спіс даступных метадаў\n"
+"                                        перадыскрэтызацыі.\n"
+"      --cleanup-shm                     Ачысціць выкарыстаныя сегменты\n"
+"                                        сумеснай памяці.\n"
+"      --start                           Запусціць фонавы сэрвіс, калі ён\n"
+"                                        яшчэ не запушчаны.\n"
+"  -k  --kill                            Знішчыць запушчаны фонавы сэрвіс.\n"
+"      --check                           Праверыць ці запушчаны фонавы сэрвіс\n"
+"                                        (толькі код завяршэння).\n"
+"\n"
+"Параметры:\n"
+"      --system[=BOOL]                   Агульнасістэмны рэжым.\n"
+"  -D, --daemonize[=BOOL]                Запусціць як фонавы сэрвіс.\n"
+"      --fail[=BOOL]                     Выйсці пры памылцы запуску.\n"
+"      --high-priority[=BOOL]            Паспрабаваць павысіць прыярытэт\n"
+"                                        (nice)\n"
+"                                        (даступна толькі карыстальніку root,\n"
+"                                        з уключаным SUID ці з павышаным\n"
+"                                        RLIMIT_NICE).\n"
+"      --realtime[=BOOL]                 Паспрабаваць уключыць планіроўшчык\n"
+"                                        рэальнага часу\n"
+"                                        (даступна толькі root,\n"
+"                                        з уключаным SUID ці з павышаным\n"
+"                                        RLIMIT_RTPRIO).\n"
+"      --disallow-module-loading[=BOOL]  Забараніць загрузку/выгрузку модуляў\n"
+"                                        па запыту карыстальніка пасля\n"
+"                                        запуску.\n"
+"      --disallow-exit[=BOOL]            Забараніць выхад па запыту\n"
+"                                        карыстальніка.\n"
+"      --exit-idle-time=СЕКУНДЫ          Завяршыць работу пасля азначанага\n"
+"                                        часу бяздзеяння.\n"
+"      --scache-idle-time=СЕКУНДЫ        Выгрузіць аўтаматычна загружаныя\n"
+"                                        сэмплы пасля азначанага часу\n"
+"                                        бяздзеяння.\n"
+"      --log-level[=УЗРОВЕНЬ]            Павялічыць або ўставіць узровень\n"
+"                                        інфарматыўнасці.\n"
+"  -v  --verbose                         Павялічыць узровень інфарматыўнасці.\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Вызначыць журнал.\n"
+"      --log-meta[=BOOL]                 Дадаць месца ў коду да паведамленняў"
+" журнала.\n"
+"      --log-time[=BOOL]                 Дадаць час да паведамленняў журнала.\n"
+"      --log-backtrace=FRAMES            Дадаць стэк выклікаў да паведамленняў"
+" журнала.\n"
+"  -p, --dl-search-path=ШЛЯХ             Задаць шлях для пошуку дынамічных\n"
+"                                        раздзяляльных абʼектаў (дадаткаў).\n"
+"      --resample-method=МЕТАД           Выкарыстоўваць азначаны метад\n"
+"                                        перадыскрэтызацыі\n"
+"                                        (Глядзіце --dump-resample-methods\n"
+"                                        для магчымых значэнняў).\n"
+"      --use-pid-file[=BOOL]             Стварыць PID-файл.\n"
+"      --no-cpu-limit[=BOOL]             Не ўсталёўваць абмежаванні на"
+" выкарыстанне\n"
+"                                        працэсара на платформах, якія\n"
+"                                        падтрымліваюць гэта.\n"
+"      --disable-shm[=BOOL]              Адключыць падтрымку сумеснай памяці.\n"
+"      --enable-memfd[=BOOL]             Уключыць падтрымку memfd-сумеснай\n"
+"                                        памяці.\n"
+"\n"
+"Сцэнарыі запуску:\n"
+"  -L, --load=\"МОДУЛЬ АРГУМЕНТЫ\"         Загрузіць азначаны модуль з\n"
+"                                        азначанымі аргументамі.\n"
+"  -F, --file=НАЗВА_ФАЙЛА                Выканаць азначаны сцэнарый.\n"
+"  -C                                    Адкрыць камандны радок у\n"
+"                                        бягучым тэрмінале пасля запуску.\n"
+"  -n                                    Не загружаць агаданы файл сцэнарыю.\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "--fail чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level чакае значэнне ўзроўню інфарматыўнасці (лікавае ў дыяпазоне 0–4"
+" ці адно з debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "--realtime чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Некарэктны журнал: патрабуецца або адно з «syslog», «journal»,"
+" «stderr», ці «auto», ці існуючы файл «file:<шлях>», «newfile:<шлях>»."
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Некарэктны журнал: патрабуецца або адно з «syslog», «stderr», ці"
+" «auto», ці існуючы файл «file:<шлях>», «newfile:<шлях>»."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "--log-time чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Некарэктны метад перадыскрэтызацыі «%s»."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "--system чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm чакае булевы аргумент"
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr "--enable-memfd чакае булевы аргумент"
+
+#: ../src/daemon/daemon-conf.c:260
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Некарэктны журнал «%s»."
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Некарэктны ўзровень інфармавання «%s»."
+
+#: ../src/daemon/daemon-conf.c:290
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Некарэктны метад перадыскрэтызацыі «%s»."
+
+#: ../src/daemon/daemon-conf.c:312
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Некарэктны rlimit «%s»."
+
+#: ../src/daemon/daemon-conf.c:332
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Некарэктны фармат сэмлаў «%s»."
+
+#: ../src/daemon/daemon-conf.c:349 ../src/daemon/daemon-conf.c:366
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Некарэктная частата дыскрэтызацыі «%s»."
+
+#: ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Некарэктная колькасць каналаў «%s»."
+
+#: ../src/daemon/daemon-conf.c:406
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Некарэктная схема каналаў «%s»."
+
+#: ../src/daemon/daemon-conf.c:423
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Некарэктная колькасць фрагментаў «%s»."
+
+#: ../src/daemon/daemon-conf.c:440
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Некарэктная даўжыня фрагмента «%s»."
+
+#: ../src/daemon/daemon-conf.c:457
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Некарэктны ўзровень прыярытэту (nice) «%s»."
+
+#: ../src/daemon/daemon-conf.c:500
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Некарэктны тып сервера «%s»."
+
+#: ../src/daemon/daemon-conf.c:615
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Не атрымалася адкрыць файл канфігурацыі: %s"
+
+#: ../src/daemon/daemon-conf.c:631
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"У агаданай схеме каналаў колькасць каналаў не супадае з агаданай колькасцю"
+" каналаў."
+
+#: ../src/daemon/daemon-conf.c:718
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Прачытана з файла канфігурацыі: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Назва: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Інфармацыя па модулю не даступна\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Версія: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Апісанне: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Аўтар: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Выкарыстанне: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Загружаць адзін раз: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "Папярэджанне аб састарэласці: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Шлях: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Не атрымалася адкрыць модуль %s: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Не атрымалася знайсці арыгінальны lt_dlopen-загрузчык."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Не атрымалася вылучыць новы dl-загрузчык."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Не атрымалася дадаць bind-now-loader."
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Не атрымалася знайсці карыстальніка «%s»."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Не атрымалася знайсці групу «%s»."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID карыстальніка «%s» і групы «%s» не супадаюць."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Хатні каталог карыстальніка «%s» не «%s» — ігнаруецца."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Не атрымалася стварыць «%s»: %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Не атрымалася змяніць спіс груп: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Не атрымалася змяніць GID: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Не атрымалася змяніць UID: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "Агульнасістэмны рэжым не падтрымліваецца на гэтай платформе."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Не атрымалася разабраць камандны радок."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Агульнасістэмны рэжым немагчымы для звычайнага карыстальніка. Будзе запушчаны"
+" толькі сэрвіс пошуку D-Bus-сервера."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Не атрымалася знішчыць сэрвіс: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Гэтая праграма не прызначана для запуску пад карыстальнікам root (акрамя"
+" выпадку, калі зададзены параметр --system)."
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Патрабуюцца прывілеі карыстальніка root."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "--start не падтрымліваецца для агульнасістэмнага экзэмпляра."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr "Выяўлены сервер карыстальніка ў %s — адмова ад запуску."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Выяўлены сервер карыстальніка ў %s, які ўяўляецца як лакальны — прадаўжаем"
+" запуск."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr ""
+"Выконваецца ў агульнасістэмным рэжыме, але --disallow-exit не зададзены."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr ""
+"Выконваецца ў агульнасістэмным рэжыме, але --disallow-module-loading не"
+" зададзены."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr "Выконваецца ў агульнасістэмным рэжыме — прымусова адключаны SHM-рэжым."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr ""
+"Выконваецца ў агульнасістэмным рэжыме — прымусова адключана завяршэнне работы"
+" па бяздзеянню."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "Не атрымалася атрымаць stdio."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() пацярпела няўдачу: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() пацярпела няўдачу: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:568
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() пацярпела няўдачу: %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Не атрымалася запусціць фонавы сэрвіс."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() пацярпела няўдачу: %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Не атрымалася атрымаць ідэнтыфікатар машыны"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually "
+"do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Добра, вы запусцілі PA у агульнасістэмным рэжыме. Калі ласка, пераканайцеся,"
+" што вы на самай справе"
+"хочаце гэтага.\n"
+"Калі ласка, прачытайце http://www.freedesktop.org/wiki/Software/PulseAudio/Doc"
+"umentation/User/WhatIsWrongWithSystemWide/ для тлумачэння, што"
+" агульнасістэмны рэжым — звычайна дрэнная ідэя."
+
+#: ../src/daemon/main.c:990
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() пацярпела няўдачу."
+
+#: ../src/daemon/main.c:1022
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() пацярпела няўдачу."
+
+#: ../src/daemon/main.c:1090
+msgid "Failed to initialize daemon."
+msgstr "Не атрымалася ініцыялізаваць фонавы сэрвіс."
+
+#: ../src/daemon/main.c:1095
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"Запуск фонавага сэрвісу без якіх-небудзь загружаных модуляў — адмова ад працы."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Гукавая сістэма PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Запуск гукавой сістэмы PulseAudio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Уваход"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Уваход док-станцыі"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Мікрафон док-станцыі"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Лінейны ўваход док-станцыі"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Лінейны ўваход"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1710
+msgid "Microphone"
+msgstr "Мікрафон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Пярэдні мікрафон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Задні мікрафон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Знешні мікрафон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Убудаваны мікрафон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Радыё"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Відэа"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Аўтаматычнае рэгуляванне ўзмацнення"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Без аўтаматычнага рэгулявання ўзмацнення"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Узмацненне"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Без ўзмацнення"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Узмацняльнік"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Без ўзмацняльніка"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Узмацненне басоў"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Без ўзмацнення басоў"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1717
+msgid "Speaker"
+msgstr "Дынамік"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Навушнікі"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Аналагавы ўваход"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Мікрафон док-станцыі"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Мікрафон гарнітуры"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Аналагавы выхад"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "Аналагавы выхад нізкіх частот"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Лінейны выхад"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Аналагавы монавыхад"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Дынамікі"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Лічбавы выхад (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Лічбавы ўваход (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Лічбавы скразны (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Шматканальны ўваход"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Шматканальны выхад"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Аналагавы мона"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Аналагавы стэрэа"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Шматканальны"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Аналагавы абʼёмны 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Аналагавы абʼёмны 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Аналагавы абʼёмны 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Аналагавы абʼёмны 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Аналагавы абʼёмны 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Аналагавы абʼёмны 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Аналагавы абʼёмны 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Аналагавы абʼёмны 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Аналагавы абʼёмны 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Аналагавы абʼёмны 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Аналагавы абʼёмны 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Лічбавы стэрэа (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Лічбавы скразны (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Лічбавы абʼёмны 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Лічбавы абʼёмны 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Лічбавы абʼёмны 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Лічбавы стэрэа (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Лічбавы абʼёмны 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4151
+msgid "Analog Mono Duplex"
+msgstr "Аналагавы мона дуплекс"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Stereo Duplex"
+msgstr "Аналагавы стэрэа дуплекс"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Лічбавы стэрэа дуплекс (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Multichannel Duplex"
+msgstr "Шматканальны дуплекс"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2295
+#: ../src/modules/bluetooth/module-bluez5-device.c:1941
+msgid "Off"
+msgstr "Адключаны"
+
+#: ../src/modules/alsa/alsa-mixer.c:4254
+#, c-format
+msgid "%s Output"
+msgstr "%s выхад"
+
+#: ../src/modules/alsa/alsa-mixer.c:4262
+#, c-format
+msgid "%s Input"
+msgstr "%s уваход"
+
+#: ../src/modules/alsa/alsa-sink.c:570
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA разбудзіла нас, каб запісаць новыя дадзеныя ў прыладу, але, на самай"
+" справе, пісаць няма чаго.\n"
+"Хутчэй за ўсё, гэта памылка ў ALSA-драйверы «%s». Калі ласка, паведаміце аб"
+" гэтым распрацоўнікам ALSA.\n"
+"Мы прачнуліся з устаноўленым POLLOUT, аднак наступны выклік snd_pcm_avail()"
+" вярнуў 0 або іншае значэнне, меншае за min_avail."
+
+#: ../src/modules/alsa/alsa-sink.c:747
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA разбудзіла нас, каб запісаць новыя дадзеныя ў прыладу, але, на самай"
+" справе, пісаць няма чаго!\n"
+"Хутчэй за ўсё, гэта памылка ў ALSA-драйверы «%s». Калі ласка, паведаміце аб"
+" гэтым распрацоўнікам ALSA.\n"
+"Мы прачнуліся з устаноўленым POLLOUT, аднак наступны выклік snd_pcm_avail()"
+" вярнуў 0 або іншае значэнне, меншае за min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA разбудзіла нас, каб прачытаць новыя дадзеныя з прылады, але, на самай"
+" справе, чытаць няма чаго.\n"
+"Хутчэй за ўсё, гэта памылка ў ALSA-драйверы «%s». Калі ласка, паведаміце аб"
+" гэтым распрацоўнікам ALSA.\n"
+"Мы прачнуліся з устаноўленым POLLIN, аднак наступны выклік snd_pcm_avail()"
+" вярнуў 0 або іншае значэнне, меншае за min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA разбудзіла нас, каб прачытаць новыя дадзеныя з прылады, але, на самай"
+" справе, чытаць няма чаго!\n"
+"Хутчэй за ўсё, гэта памылка ў ALSA-драйверы «%s». Калі ласка, паведаміце аб"
+" гэтым распрацоўнікам ALSA.\n"
+"Мы прачнуліся з устаноўленым POLLIN, аднак наступны выклік snd_pcm_avail()"
+" вярнуў 0 або іншае значэнне, меншае за min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1166 ../src/modules/alsa/alsa-util.c:1241
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"Выклік snd_pcm_avail() вярнуў выключна вялікае значэнне: %lu байтаў (%lu"
+" мс).\n"
+"Хутчэй за ўсё, гэта памылка ў ALSA-драйверы «%s». Калі ласка, паведаміце аб"
+" гэтым распрацоўнікам ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1216
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"Выклік snd_pcm_delay() вярнуў выключна вялікае значэнне: %li байтаў (%s%lu"
+" мс).\n"
+"Хутчэй за ўсё, гэта памылка ў ALSA-драйверы «%s». Калі ласка, паведаміце аб"
+" гэтым распрацоўнікам ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1257
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"Выклік snd_pcm_avail_delay() вярнуў дзіўнае значэнне: затрымка %lu меншая за"
+" даступную %lu.\n"
+"Хутчэй за ўсё, гэта памылка ў ALSA-драйверы «%s». Калі ласка, паведаміце аб"
+" гэтым распрацоўнікам ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1300
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"Выклік snd_pcm_mmap_begin() вярнуў выключна вялікае значэнне: %lu байтаў (%lu"
+" мс).\n"
+"Хутчэй за ўсё, гэта памылка ў ALSA-драйверы «%s». Калі ласка, паведаміце аб"
+" гэтым распрацоўнікам ALSA."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2089
+#: ../src/modules/bluetooth/module-bluez5-device.c:1700
+msgid "Headset"
+msgstr "Гарнітура"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1705
+msgid "Handsfree"
+msgstr "Хэндс-фры"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1723
+msgid "Headphone"
+msgstr "Навушнік"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1728
+msgid "Portable"
+msgstr "Партатыўная сістэма"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1733
+msgid "Car"
+msgstr "Аўтамабільная сістэма"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1738
+msgid "HiFi"
+msgstr "Hi-Fi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1743
+msgid "Phone"
+msgstr "Тэлефон"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2137
+#: ../src/modules/bluetooth/module-bluez5-device.c:1695
+#: ../src/modules/bluetooth/module-bluez5-device.c:1711
+#: ../src/modules/bluetooth/module-bluez5-device.c:1749
+msgid "Bluetooth Output"
+msgstr "Bluetooth-выхад"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2140
+#: ../src/modules/bluetooth/module-bluez5-device.c:1694
+#: ../src/modules/bluetooth/module-bluez5-device.c:1716
+#: ../src/modules/bluetooth/module-bluez5-device.c:1722
+#: ../src/modules/bluetooth/module-bluez5-device.c:1748
+msgid "Bluetooth Input"
+msgstr "Bluetooth-уваход"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2176
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Прайграванне высокай якасці (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2187
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Захоп высокай якасці (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2198
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Тэлефонны дуплекс (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2210
+msgid "Handsfree Gateway"
+msgstr "Шлюз хэндс-фры"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1786
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Прайграванне высокай якасці (A2DP-прыёмнік)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1797
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Захоп высокай якасці (A2DP-крыніца)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1808
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Навушнікі гарнітуры (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1820
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Аўдыяшлюз гарнітуры (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<назва крыніцы> source_properties=<уласьцівасьці крыніцы>"
+" source_master=<назва крыніцы для фільтрацыі> sink_name=<назва прыёмніка>"
+" sink_properties=<уласьцівасьці прыёмніка> sink_master=<назва прыёмніка для"
+" фільтрацыі> adjust_time=<частата карэтыроўкі ў секундах> adjust_threshold=<"
+"велічыня дрэйфу ў мілісекундах, пасля якой патрабуецца карэтыроўка> format=<"
+"фармат сэмлаў> rate=<частата сэмлаў> channels=<колькасць каналаў>"
+" channel_map=<схема каналаў> aec_method=<скарыстаная рэалізацыя> aec_args=<"
+"параметры сістэмы рэхападаўлення>"
+"save_aec=<захоўваць дадзеныя рэхападаўлення ў /tmp> autoloaded=<зададзены,"
+" калі гэты модуль загружаны аўтаматычна> use_volume_sharing=<yes ці no>"
+" use_master_format=<yes ці no> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "Уключаны"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Фіктыўны выхад"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+"Заўсёды пакідаць хоць бы адзін прыёмнік загружаным, нават калі ён пусты"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Эквалайзер агульнага прызначэння"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<назва прыёмніка> sink_properties=<уласцівасці прыёмніка>"
+" sink_master=<прыёмнік, з якім злучыцца> format=<фармат сэмлаў> rate=<"
+"частата сэмлаў> channels=<колькасць каналаў> channel_map=<схема каналаў>"
+" autoloaded=<зададзены, калі гэты модуль загружаны аўтаматычна>"
+" use_volume_sharing=<yes ці no> "
+
+#: ../src/modules/module-filter-apply.c:47
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<аўтаматычна выгрузіць нявыкарыстаныя фільтры?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Віртуальны LADSPA-прыёмнік"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<назва прыёмніка> sink_properties=<уласцівасці прыёмніка> master=<"
+"назва прыёмніка для фільтрацыі> format=<фармат сэмлаў> rate=<частата сэмлаў>"
+" channels=<колькасць каналаў> channel_map=<схема каналаў> plugin=<назва"
+" LADSPA-дадатку> label=<этыкетка LADSPA-дадатку> control=<спіс кантрольных"
+" значэнняў, падзеленых коскамі> input_ladspaport_map=<спіс назваў уваходных"
+" LADSPA-партоў, падзеленых коскамі> output_ladspaport_map=<спіс назваў"
+" выхадных LADSPA-партоў, падзеленых коскамі> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Сінхронны пусты прыёмнік"
+
+#: ../src/modules/module-null-sink.c:280
+msgid "Null Output"
+msgstr "Пусты выхад"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Прылады вываду"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Прылады ўводу"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Аўдыя на @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Тунэль для %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:517
+#: ../src/modules/module-tunnel-source-new.c:516
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Тунэль да %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Віртуальны абʼёмны прыёмнік"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/"
+"left_hrir.wav "
+msgstr ""
+"sink_name=<назва прыёмніка> sink_properties=<уласцівасці прыёмніка> master=<"
+"назва прыёмніка для фільтрацыі> format=<фармат сэмлаў> rate=<частата сэмлаў>"
+" channels=<number of channels> channel_map=<колькасць каналаў>"
+" use_volume_sharing=<yes ці no> force_flat_volume=<yes ці no> hrir=<шлях да"
+" left_hrir.wav> "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "Гукавы сервер PulseAudio"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Мона"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Пярэдні цэнтральны"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Пярэдні левы"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Пярэдні правы"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Задні цэнтральны"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Задні левы"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Задні правы"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Сабвуфер"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Пярэдні левацэнтральны"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Пярэдні правацэнтральны"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Бакавы левы"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Бакавы правы"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Дапаможны 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Дапаможны 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Дапаможны 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Дапаможны 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Дапаможны 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Дапаможны 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Дапаможны 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Дапаможны 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Дапаможны 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Дапаможны 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Дапаможны 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Дапаможны 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Дапаможны 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Дапаможны 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Дапаможны 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Дапаможны 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Дапаможны 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Дапаможны 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Дапаможны 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Дапаможны 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Дапаможны 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Дапаможны 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Дапаможны 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Дапаможны 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Дапаможны 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Дапаможны 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Дапаможны 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Дапаможны 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Дапаможны 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Дапаможны 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Дапаможны 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Дапаможны 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Верхні цэнтральны"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Верхні пярэдні цэнтральны"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Верхні пярэдні левы"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Верхні пярэдні правы"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Верхні задні цэнтральны"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Верхні задні левы"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Верхні задні правы"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:175 ../src/pulse/volume.c:294
+#: ../src/pulse/volume.c:320 ../src/pulse/volume.c:340
+#: ../src/pulse/volume.c:372 ../src/pulse/volume.c:412
+#: ../src/pulse/volume.c:431
+msgid "(invalid)"
+msgstr "(некарэктнае)"
+
+#: ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Стэрэа"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Абʼёмны 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Абʼёмны 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Абʼёмны 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Абʼёмны 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Абʼёмны 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() пацярпела няўдачу"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "Выклік xcb_connection_has_error() вярнуў true"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Не атрымалася разабраць дадзеныя cookie"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Атрымана паведамленне для невядомага пашырэння «%s»"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "увод"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "вывад"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "двунакіраваны"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "некарэктны"
+
+#: ../src/pulsecore/core-util.c:1836
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"XDG_RUNTIME_DIR (%s) не належыць нам (uid %d), а — uid %d! (Гэта можа"
+" адбыцца, напрыклад, калі вы спрабуеце злучыцца з PulseAudio звычайнага"
+" карыстальніка як карыстальнік root па ўбудаваным пратаколе. Не рабіце так.)"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "так"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "не"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Не атрымалася даступіцца да блакіроўкі для аўтазапуску."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Не атрымалася адкрыць файл журнала «%s»."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr "Не атрымалася адкрыць файлы журнала «%s», «%s.1», «%s.2» … «%s.%d»."
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Некарэктны журнал."
+
+#: ../src/pulsecore/sink.c:3459
+msgid "Built-in Audio"
+msgstr "Убудаванае аўдыя"
+
+#: ../src/pulsecore/sink.c:3464
+msgid "Modem"
+msgstr "Мадэм"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "Добра"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Доступ забаронены"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Невядомая каманда"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Некарэктны аргумент"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "Абʼект існуе"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Няма такога абʼекта"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Злучэнне адкінута"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Памылка пратакола"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Таймаут"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Няма ключа аўтэнтыфікацыі"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Унутраная памылка"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Злучэнне завяршылася"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Абʼект знішчаны"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Некарэктны сервер"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Не атрымалася ініцыялізаваць модуль"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Дрэнны стан"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Няма дадзеных"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Несумяшчальныя версіі пратаколу"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Занадта вялікі"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Не падтрымліваецца"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Невядомы код памылкі"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Няма такога пашырэння"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Састарэлая функцыянальнасць"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Адсутнічае рэалізацыя"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Кліент падвоіўся"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Памылка ўводу/вываду"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Прылада ці рэсурс заняты"
+
+#: ../src/pulse/sample.c:177
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %u-канальны %u Гц"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f ГіБ"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f МіБ"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f КіБ"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%u B"
+msgstr "%u байт"
+
+#: ../src/utils/pacat.c:117
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Не атрымалася зліць паток: %s"
+
+#: ../src/utils/pacat.c:122
+msgid "Playback stream drained."
+msgstr "Паток прайграванне зліты."
+
+#: ../src/utils/pacat.c:133
+msgid "Draining connection to server."
+msgstr "Зліццё злучэння з серверам."
+
+#: ../src/utils/pacat.c:146
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:169
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() пацярпела няўдачу: %s"
+
+#: ../src/utils/pacat.c:210
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() пацярпела няўдачу: %s"
+
+#: ../src/utils/pacat.c:260 ../src/utils/pacat.c:290
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() пацярпела няўдачу: %s"
+
+#: ../src/utils/pacat.c:340
+msgid "Stream successfully created."
+msgstr "Паток паспяхова створаны."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() пацярпела няўдачу: %s"
+
+#: ../src/utils/pacat.c:347
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Метрыкі буфера: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:350
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Метрыкі буфера: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:354
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Выкарыстоўваецца спецыфікацыя сэмплаў «%s» і схема каналаў «%s»."
+
+#: ../src/utils/pacat.c:358
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Злучаны з прыладай %s (нумар: %u, прыпынены: %s)."
+
+#: ../src/utils/pacat.c:368
+#, c-format
+msgid "Stream error: %s"
+msgstr "Памылка патоку: %s"
+
+#: ../src/utils/pacat.c:378
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Паток прыпынены.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Паток узноўлены.%s"
+
+#: ../src/utils/pacat.c:388
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Паток спустошаны.%s"
+
+#: ../src/utils/pacat.c:395
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Паток перапоўнены.%s"
+
+#: ../src/utils/pacat.c:402
+#, c-format
+msgid "Stream started.%s"
+msgstr "Паток запушчаны.%s"
+
+#: ../src/utils/pacat.c:409
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Паток перасунуты ў прыладу %s (%u, %sпрыпынены).%s"
+
+#: ../src/utils/pacat.c:409
+msgid "not "
+msgstr "не"
+
+#: ../src/utils/pacat.c:416
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Змяніліся атрыбуты буфера патоку.%s"
+
+#: ../src/utils/pacat.c:431
+msgid "Cork request stack is empty: corking stream"
+msgstr "Стэк запытаў закаркавання пусты — паток закаркоўваецца"
+
+#: ../src/utils/pacat.c:437
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Стэк запытаў закаркавання пусты — паток адкаркоўваецца"
+
+#: ../src/utils/pacat.c:441
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr ""
+"Папярэджанне: Атрымана запытаў адкаркавання больш, чым запытаў закаркавання."
+
+#: ../src/utils/pacat.c:466
+#, c-format
+msgid "Connection established.%s"
+msgstr "Злучэнне ўстаноўлена.%s"
+
+#: ../src/utils/pacat.c:469
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() пацярпела няўдачу: %s"
+
+#: ../src/utils/pacat.c:507
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() пацярпела няўдачу: %s"
+
+#: ../src/utils/pacat.c:513
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Не атрымалася ўсталяваць маніторны паток: %s"
+
+#: ../src/utils/pacat.c:517
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() пацярпела няўдачу: %s"
+
+#: ../src/utils/pacat.c:530 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Злучэнне не ўдалося: %s"
+
+#: ../src/utils/pacat.c:563
+msgid "Got EOF."
+msgstr "Дасягнуты канец файла."
+
+#: ../src/utils/pacat.c:600
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() пацярпела няўдачу: %s"
+
+#: ../src/utils/pacat.c:621
+msgid "Got signal, exiting."
+msgstr "Атрыманы сігнал — выхад."
+
+#: ../src/utils/pacat.c:635
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Не атрымалася атрымаць затрымку: %s"
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Час: %0.3f с; Затрымка: %0.0f мкс."
+
+#: ../src/utils/pacat.c:661
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() пацярпела няўдачу: %s"
+
+#: ../src/utils/pacat.c:671
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [параметры]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Паказаць гэту даведку.\n"
+"      --version                         Паказаць версію.\n"
+"\n"
+"  -r, --record                          Стварыць злучэнне для запісу.\n"
+"  -p, --playback                        Стварыць злучэнне для прайгравання.\n"
+"\n"
+"  -v, --verbose                         Павялічыць узровень інфарматыўнасці.\n"
+"\n"
+"  -s, --server=СЕРВЕР                   Назва сервера для злучэння.\n"
+"  -d, --device=ПРЫЛАДА                  Назва прыёмніка/крыніцы для\n"
+"                                        злучэння.\n"
+"  -n, --client-name=НАЗВА               Назва гэтага кліента на серверы.\n"
+"      --stream-name=НАЗВА               Назва гэтага патоку на серверы.\n"
+"      --volume=ГУЧНАСЦЬ                 Пачатковая (лінейная) гучнасць у\n"
+"                                        дыяпазоне 0–65536.\n"
+"      --rate=ЧАСТАТА                    Частата сэмплаў у Гц (агаданая\n"
+"                                        44100).\n"
+"      --format=ФАРМАТ_СЭМПЛАЎ           Тып сэмплаў, адзін з s16le, s16be,\n"
+"                                        u8, float32le, float32be, ulaw,\n"
+"                                        alaw, s32le, s32be, s24le, s24be,\n"
+"                                        s24-32le, s24-32be (агаданы s16ne)\n"
+"      --channels=КАНАЛАЎ                Колькасць каналаў: 1 для мона,\n"
+"                                        2 для стэрэа.\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=СХЕМА_КАНАЛАЎ       Схема каналаў замест агаданай.\n"
+"      --fix-format                      Узяць фармат сэмплаў з прыёмніка\n"
+"                                        або крыніцы, з якім злучаны паток.\n"
+"      --fix-rate                        Узяць частату сэмплаў з прыёмніка\n"
+"                                        або крыніцы, з якім злучаны паток.\n"
+"      --fix-channels                    Узяць колькасць і схему каналаў з\n"
+"                                        прыёмніка або крыніцы, з якім"
+" злучаны\n"
+"                                        паток.\n"
+"      --no-remix                        Не змешваць каналы.\n"
+"      --no-remap                        Сумяшчаць каналы па нумару,\n"
+"                                        а не па назве.\n"
+"      --latency=БАЙТЫ                   Запытаць затрымку ў байтах.\n"
+"      --process-time=БАЙТЫ              Запытаць час апрацоўкі аднаго запыту\n"
+"                                        ў байтах.\n"
+"      --latency-msec=МКС                Запытаць затрымку ў мікрасекундах.\n"
+"      --process-time-msec=МКС           Запытаць час апрацоўкі аднаго запыту\n"
+"                                        ў мікрасекундах.\n"
+"      --property=УЛАСЦІВАСЦЬ=ЗНАЧЭННЕ   Задаць значэнне уласцівасці.\n"
+"      --raw                             Запісваць або прайграваць сырыя\n"
+"                                        PCM-дадзеныя.\n"
+"      --passthrough                     Перадаваць дадзеныя наскрозь.\n"
+"      --file-format[=ФАРМАТ_ФАЙЛА]      Запісваць або прайграваць\n"
+"                                        фарматаваныя PCM-дадзеныя.\n"
+"      --list-file-formats               Паказаць спіс даступных фарматаў\n"
+"                                        файлаў.\n"
+"      --monitor-stream=НУМАР            Запісваць з увахода прыёмніка з\n"
+"                                        зададзеным нумарам.\n"
+
+#: ../src/utils/pacat.c:788
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr "Прайграванне кадаваных аўдыёфайлаў на гукавым серверы PulseAudio."
+
+#: ../src/utils/pacat.c:792
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr "Захоп аўдыёдадзеных з гукавога сервера PulseAudio і запіс іх у файл."
+
+#: ../src/utils/pacat.c:796
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+"Захоп аўдыёдадзеных з гукавога сервера PulseAudio і запіс іх у STDOUT ці"
+" зададзены файл."
+
+#: ../src/utils/pacat.c:800
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+"Прайграванне аўдыёдадзеных з STDIN ці зададзенага аўдыёфайла на гукавым"
+" серверы PulseAudio."
+
+#: ../src/utils/pacat.c:814
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Скампілявана з libpulse %s\n"
+"Звязана з libpulse %s\n"
+
+#: ../src/utils/pacat.c:847 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Некарэктная назва кліента «%s»"
+
+#: ../src/utils/pacat.c:862
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Некарэктная назва патоку «%s»"
+
+#: ../src/utils/pacat.c:899
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Некарэктная схема каналаў «%s»"
+
+#: ../src/utils/pacat.c:928 ../src/utils/pacat.c:942
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Некарэктная затрымка «%s»"
+
+#: ../src/utils/pacat.c:935 ../src/utils/pacat.c:949
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Некарэктная спецыфікацыя часу працэсу «%s»"
+
+#: ../src/utils/pacat.c:961
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Некарэктная ўласцівасць «%s»"
+
+#: ../src/utils/pacat.c:980
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Невядомая фармат файла %s."
+
+#: ../src/utils/pacat.c:995
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Не атрымалася разабраць значэнне для --monitor-stream"
+
+#: ../src/utils/pacat.c:1006
+msgid "Invalid sample specification"
+msgstr "Некарэктная спецыфікацыя сэмплаў"
+
+#: ../src/utils/pacat.c:1016
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1028
+msgid "Too many arguments."
+msgstr "Занадта шмат аргументаў."
+
+#: ../src/utils/pacat.c:1039
+msgid "Failed to generate sample specification for file."
+msgstr "Не атрымалася згенераваць спецыфікацыю сэмплаў для файла."
+
+#: ../src/utils/pacat.c:1065
+msgid "Failed to open audio file."
+msgstr "Не атрымалася адкрыць аўдыяфайл."
+
+#: ../src/utils/pacat.c:1071
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Папярэджанне: зададзеная спецыфікацыя сэмплаў будзе перапісана спецыфікацыяй"
+" сэмплаў"
+" з файла."
+
+#: ../src/utils/pacat.c:1074 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Не атрымалася вызначыць спецыфікацыю сэмплаў з файла."
+
+#: ../src/utils/pacat.c:1083
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Папярэджанне: Не атрымалася вызначыць схему каналаў з файла."
+
+#: ../src/utils/pacat.c:1094
+msgid "Channel map doesn't match sample specification"
+msgstr "Схема каналаў не адпавядае спецыфікацыі сэмплаў"
+
+#: ../src/utils/pacat.c:1105
+msgid "Warning: failed to write channel map to file."
+msgstr "Папярэджанне: не атрымалася запісаць схему каналаў у файл."
+
+#: ../src/utils/pacat.c:1120
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "Адкрыццё патоку %s з спецыфікацыяй сэмплаў «%s» і схемай каналаў «%s»."
+
+#: ../src/utils/pacat.c:1121
+msgid "recording"
+msgstr "запіс"
+
+#: ../src/utils/pacat.c:1121
+msgid "playback"
+msgstr "прайграванне"
+
+#: ../src/utils/pacat.c:1145
+msgid "Failed to set media name."
+msgstr "Не атрымалася ўсталяваць назву патоку."
+
+#: ../src/utils/pacat.c:1152 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() пацярпела няўдачу."
+
+#: ../src/utils/pacat.c:1175
+msgid "io_new() failed."
+msgstr "io_new() пацярпела няўдачу."
+
+#: ../src/utils/pacat.c:1182 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() пацярпела няўдачу."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() пацярпела няўдачу: %s"
+
+#: ../src/utils/pacat.c:1196
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() пацярпела няўдачу."
+
+#: ../src/utils/pacat.c:1203 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() пацярпела няўдачу."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "НАЗВА [АРГУМЕНТЫ …]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "НАЗВА|НУМАР"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "НАЗВА"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "НАЗВА|НУМАР ГУЧНАСЦЬ"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "НУМАР ГУЧНАСЦЬ"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "НАЗВА|НУМАР 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "НУМАР 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "НАЗВА|НУМАР КЛЮЧ=ЗНАЧЭННЕ"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "НУМАР КЛЮЧ=ЗНАЧЭННЕ"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "НУМАР"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "НАЗВА ПРЫЁМНІК|НУМАР"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "НАЗВА НАЗВА_ФАЙЛА"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "ШЛЯХ"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "НАЗВА_ФАЙЛА ПРЫЁМНІК|НУМАР"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "НУМАР ПРЫЁМНІК|КРЫНІЦА"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "КАРТКА ПРОФІЛЬ"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "НАЗВА|НУМАР ПОРТ"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "НАЗВА_КАРТКІ|№_КАРТЫ ПОРТ ЗРУХ"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "МЭТА"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "ЛІКАВЫ ЎЗРОВЕНЬ"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "КАДРАЎ"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Паказаць гэтую даведку\n"
+"      --version                         Паказаць версію\n"
+"Калі каманда не азначана, pacmd запусціцца ў інтэрактыўным рэжыме.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Скампілявана з libpulse %s\n"
+"Звязана з libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "Няма запушчанага сэрвісу PulseAudio ці ён не працуе як сесійны сэрвіс."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Не атрымалася знішчыць фонавы сэрвіс PulseAudio."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Фонавы сэрвіс не адказвае."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Не атрымалася атрымаць статыстыку: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Выкарыстоўваецца ў дадзены момант: %u блокаў агульным памерам %s байтаў.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Выдзелена на працягу ўсяго тэрміну службы: %u блокаў агульным памерам %s"
+" байтаў.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Памер кэшу сэмплаў: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Не атрымалася атрымаць інфармацыю аб серверы: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Серверны радок: %s\n"
+"Версія пратакола бібліятэкі: %u\n"
+"Версія пратакола сервера: %u\n"
+"Ці лакальны: %s\n"
+"Нумар кліента: %u\n"
+"Памер блока: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Імя карыстальніка: %s\n"
+"Назва вузла: %s\n"
+"Назва сервера: %s\n"
+"Версія сервера: %s\n"
+"Агаданая спецыфікацыя сэмлаў: %s\n"
+"Агаданая схема каналаў: %s\n"
+"Агаданы прыёмнік: %s\n"
+"Агаданая крыніца: %s\n"
+"Cookie: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Не атрымалася атрымаць інфармацыю аб прыёмніку: %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Прыёмнік № %u\n"
+"\tСтан: %s\n"
+"\tНазва: %s\n"
+"\tАпісанне: %s\n"
+"\tДрайвер: %s\n"
+"\tСпецыфікацыя сэмлаў: %s\n"
+"\tСхема каналаў: %s\n"
+"\tМодуль-уладальнік: %u\n"
+"\tАбязгучаны: %s\n"
+"\tГучнасць: %s\n"
+"\t        баланс %0.2f\n"
+"\tБазавая гучнасць: %s\n"
+"\tМаніторная крыніца: %s\n"
+"\tЗатрымка: %0.0f мкс, сканфігуравана %0.0f мкс\n"
+"\tФлагі: %s%s%s%s%s%s%s\n"
+"\tУласцівасці:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tПарты:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tАктыўны порт: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tФарматы:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Не атрымалася атрымаць інфармацыю аб крыніцы: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Крыніца № %u\n"
+"\tСтан: %s\n"
+"\tНазва: %s\n"
+"\tАпісанне: %s\n"
+"\tДрайвер: %s\n"
+"\tСпецыфікацыя сэмплаў: %s\n"
+"\tСхема каналаў: %s\n"
+"\tМодуль-уладальнік: %u\n"
+"\tАбязгучана: %s\n"
+"\tГучнасць: %s\n"
+"\t        баланс %0.2f\n"
+"\tБазавая гучнасць: %s\n"
+"\tЦі манітор прыёмніка: %s\n"
+"\tЗатрымка: %0.0f мкс, сканфігуравана %0.0f мкс\n"
+"\tФлагі: %s%s%s%s%s%s\n"
+"\tУласцівасці:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "н/д"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Не атрымалася атрымаць інфармацыю аб модулі: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Модуль № %u\n"
+"\tНазва: %s\n"
+"\tАргументы: %s\n"
+"\tЛічыльнік выкарыстання: %s\n"
+"\tУласцівасці:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Не атрымалася атрымаць інфармацыю аб кліенце: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Кліент № %u\n"
+"\tДрайвер: %s\n"
+"\tМодуль-уладальнік: %s\n"
+"\tУласцівасці:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Не атрымалася атрымаць інфармацыю аб картцы: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Картка № %u\n"
+"\tНазва: %s\n"
+"\tДрайвер: %s\n"
+"\tМодуль-уладальнік: %s\n"
+"\tУласцівасці:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tПрофілі:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr "\t\t%s: %s (прыёмнікаў: %u, крыніц: %u, прыярытэт: %u, даступны: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tАктыўны профіль: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tУласцівасці:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tЧастка профілю(яў): %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Не атрымалася атрымаць інфармацыю аб уваходзе прыёмніка: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Уваход прыёмніка № %u\n"
+"\tДрайвер: %s\n"
+"\tМодуль-уладальнік: %s\n"
+"\tКліент: %s\n"
+"\tПрыёмнік: %u\n"
+"\tСпецыфікацыя сэмплаў: %s\n"
+"\tСхема каналаў: %s\n"
+"\tФармат: %s\n"
+"\tЗакаркаваны: %s\n"
+"\tАбязгучаны: %s\n"
+"\tГучнасць: %s\n"
+"\t        баланс %0.2f\n"
+"\tЗатрымка буфера: %0.0f мкс\n"
+"\tЗатрымка прыёмніка: %0.0f мкс\n"
+"\tМетад перадыскрэтызацыі: %s\n"
+"\tУласцівасці:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Не атрымалася атрымаць інфармацыю аб выхадзе крыніцы: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Выхад крыніцы № %u\n"
+"\tДрайвер: %s\n"
+"\tМодуль-уладальнік: %s\n"
+"\tКліент: %s\n"
+"\tКрыніца: %u\n"
+"\tСпецыфікацыя сэмплаў: %s\n"
+"\tСхема каналаў: %s\n"
+"\tФармат: %s\n"
+"\tЗакаркаваны: %s\n"
+"\tАбязгучаны: %s\n"
+"\tГучнасць: %s\n"
+"\t        баланс %0.2f\n"
+"\tЗатрымка буфера: %0.0f мкс\n"
+"\tЗатрымка крыніцы: %0.0f мкс\n"
+"\tМетад перадыскрэтызацыі: %s\n"
+"\tУласцівасці:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Не атрымалася атрымаць інфармацыю аб сэмпле: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Сэмпл № %u\n"
+"\tНазва: %s\n"
+"\tСпецыфікацыя: %s\n"
+"\tСхема каналаў: %s\n"
+"\tГучнасць: %s\n"
+"\t        баланс %0.2f\n"
+"\tПрацягласць: %0.1fs\n"
+"\tПамер: %s\n"
+"\tЛянівы: %s\n"
+"\tНазва файла: %s\n"
+"\tУласцівасці:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Няўдача: %s"
+
+#: ../src/utils/pactl.c:866
+#, fuzzy, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Не атрымалася выгрузіць модуль: модуль не загружаны"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Не атрымалася задаць гучнасць: вы спрабуеце задаць гучнасць для %d каналаў,"
+" але агульная колькасць каналаў %d\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Не атрымалася задаць фармат: некарэктны фармат «%s»"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Не атрымалася выгрузіць сэмпл: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Заўчасны канец файла"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "зʼяўленне"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "змяненне"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "выдаленне"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "невядомая"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "прыёмніка"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "крыніцы"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "уваходу прыёмніка"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "выхаду крыніцы"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "модуля"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "кліента"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "кэша сэмплаў"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "сервера"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "карткі"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Падзея %s %s № %u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "Атрыманы сігнал SIGINT — выхад."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Некарэктная гучнасць"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Гучнасць па-за дапушчальны дыяпазоне.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "Некарэктная колькасць значэнняў гучнасці.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "Супярэчлівае значэнне гучнасці.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[параметры]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[ТЫП]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "НАЗВА_ФАЙЛА [НАЗВА]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "НАЗВА [ПРЫЁМНІК]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "НАЗВА|НУМАР ГУЧНАСЦЬ [ГУЧНАСЦЬ …]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "НУМАР ГУЧНАСЦЬ [ГУЧНАСЦЬ …]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "НАЗВА|НУМАР 1|0|toggle"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "НУМАР 1|0|toggle"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "НУМАР ФАРМАТЫ"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Адмысловыя назвы: @DEFAULT_SINK@, @DEFAULT_SOURCE@ і @DEFAULT_MONITOR@ —\n"
+"могуць быць скарыстаны для ўказання агаданага прыёмніка, крыніцы ці"
+" манітора.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Паказаць гэтую даведку\n"
+"      --version                         Паказаць версію\n"
+"\n"
+"  -s, --server=СЕРВЕР                   Назва сервера для злучэння\n"
+"  -n, --client-name=НАЗВА               Назва гэтага кліента на серверы\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Скампілявана з libpulse %s\n"
+"Звязана з libpulse %s\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Нічога не задавайце ці адно з: %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Калі ласка, задайце файл сэмплаў для загрузкі"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Не атрымалася адкрыць аўдыяфайл."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Папярэджанне: Не атрымалася вызначыць спецыфікацыю сэмплаў з файла."
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Неабходна задаць назву сэмпла для прайгравання"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Неабходна задаць назву сэмпла для выдалення"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "Неабходна задаць нумар уваходу прыёмніка і прыёмнік"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "Неабходна задаць нумар выхаду крыніцы і крыніцу"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Неабходна задаць назву модуля і аргументы."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "Неабходна задаць нумар модуля ці яго назву"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Вы не можаце задаць больш аднаго прыёмніка. Неабходна задаць булева значэнне."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Некарэктная спецыфікацыя прыпынення."
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Вы не можаце задаць больш адной крыніцы. Неабходна задаць булева значэнне."
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Неабходна задаць нумар ці назву карткі і назву профілю"
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Неабходна задаць нумар ці назву прыёмніка і назву порта"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "Неабходна задаць назву прыёмніка"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "Неабходна задаць нумар ці назву крыніцы і назву порта"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Неабходна задаць назву крыніцы"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Неабходна задаць нумар ці назву прыёмніка і гучнасць"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Неабходна задаць нумар ці назву крыніцы і гучнасць"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "Неабходна задаць нумар ўваходу прыёмніка і гучнасць"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "Некарэктны нумар уваходу прыёмніка"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "Неабходна задаць нумар выхаду крыніцы і гучнасць"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Некарэктны нумар выхаду крыніцы"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Неабходна задаць нумар ці назву прыёмніка і дзеянне абязгучвання (0 —"
+" адключыць, 1 — уключыць, toggle — пераключыць)"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Некарэктная спецыфікацыя абязгучвання"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Неабходна задаць нумар ці назву крыніцы і дзеянне абязгучвання (0 —"
+" адключыць, 1 — уключыць, toggle — пераключыць)"
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Неабходна задаць нумар уваходу прыёмніка і дзеянне абязгучвання (0 —"
+" адключыць, 1 — уключыць, toggle — пераключыць)"
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "Некарэктны нумар уваходу прыёмніка"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+"Неабходна задаць нумар выхаду крыніцы і дзеянне абязгучвання (0 — адключыць,"
+" 1 — уключыць, toggle — пераключыць)"
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Некарэктны нумар выхаду крыніцы"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Неабходна задаць нумар прыёмніка і спіс фарматаў, падзеленых кропкамі з"
+" коскамі"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr "Неабходна задаць нумар ці назву карткі, назву порту і зрух затрымкі."
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Немагчыма разабраць зрух затрымкі"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Зададзена некарэктная каманда."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Не ўдалося ўзнавіць: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Не ўдалося прыпыніць: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "Папярэджанне: Нелакальны гукавы сервер не прыпыняецца.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Злучэнне не ўдалося: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Атрыманы сігнал SIGINT — выхад.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "Папярэджанне: даччын працэс завяршыўся па сігналу %u\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [параметры] … \n"
+"\n"
+"  -h, --help                            Паказаць гэту даведку.\n"
+"      --version                         Паказаць версію.\n"
+"  -s, --server=СЕРВЕР                   Назва сервера для злучэння.\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Скампілявана з libpulse %s\n"
+"Звязана з libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() пацярпела няўдачу.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() пацярпела няўдачу.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() пацярпела няўдачу.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D дысплей] [-S сервер] [-O прыёмнік] [-I крыніца] [-c файл]"
+" [-d|-e|-i|-r]\n"
+"\n"
+" -d    Паказаць бягучыя дадзеныя PulseAudio, прымацаваныя да X11-дысплея"
+" (агаданая)\n"
+" -e    Экспартаваць дадзеныя PulseAudio у X11-дысплей\n"
+" -i    Імпартаваць дадзеныя PulseAudio з X11-дысплея ў лакальныя зменныя"
+" асяроддзя.\n"
+" -r    Выдаліць дадзеныя PulseAudio з X11-дысплея\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Не атрымалася разабраць камандны радок.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Сервер: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Крыніца: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Прыёмнік: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Не атрымалася разабраць дадзеныя cookie\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Не атрымалася захаваць дадзеныя cookie\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Не атрымалася атрымаць поўнае даменнае імя (FQDN).\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Не атрымалася загрузіць дадзеныя cookie\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Не рэалізованы.\n"
diff --git a/po/bn_IN.po b/po/bn_IN.po
new file mode 100644 (file)
index 0000000..83e6137
--- /dev/null
@@ -0,0 +1,3045 @@
+# translation of pulseaudio.master-tx.bn_IN.po to Bengali INDIA
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Runa Bhattacharjee <runab@redhat.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx.bn_IN\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:52+0000\n"
+"Last-Translator: Runa Bhattacharjee <runab@redhat.com>\n"
+"Language-Team: Bengali INDIA <anubad@lists.ankur.org.in>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() থেকে প্রাপ্ত মান অত্যাধিক বড়: %lu বাইট (%lu ms)।\n"
+"সম্ভবত এটি ALSA ড্রাইভার '%s'-র একটি বাগ। অনুগ্রহ করে এই সমস্যা সম্বন্ধে ALSA "
+"ডিভেলপরদের সূচিত করুন।"
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() থেকে প্রাপ্ত মান অত্যাধিক বড়: %li বাইট (%s%lu ms)।\n"
+"সম্ভবত এটি ALSA ড্রাইভার '%s'-র একটি বাগ। অনুগ্রহ করে এই সমস্যা সম্বন্ধে ALSA "
+"ডিভেলপরদের সূচিত করুন।"
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() থেকে প্রাপ্ত মান অত্যাধিক বড়: %lu বাইট (%lu ms)।\n"
+"সম্ভবত এটি ALSA ড্রাইভার '%s'-র একটি বাগ। অনুগ্রহ করে এই সমস্যা সম্বন্ধে ALSA "
+"ডিভেলপরদের সূচিত করুন।"
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() থেকে প্রাপ্ত মান অত্যাধিক বড়: %lu বাইট (%lu ms)।\n"
+"সম্ভবত এটি ALSA ড্রাইভার '%s'-র একটি বাগ। অনুগ্রহ করে এই সমস্যা সম্বন্ধে ALSA "
+"ডিভেলপরদের সূচিত করুন।"
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+"সর্বদা অন্তত একটি sink লোড করে রাখা হবে, প্রয়োজনে null sink ব্যবহার করা হবে"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "ডামি আউটপুট"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "ভার্চুয়াল LADSPA sink"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "NULL sink-র সময় নির্ধারণ"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Null ফলাফল"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "অভ্যন্তরীণ অডিও"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "মোডেম"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "মূল lt_dlopen লোডার সনাক্ত করতে ব্যর্থ।"
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "নতুন dl লোডার বরাদ্দ করতে ব্যর্থ।"
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind-now-loader যোগ করতে ব্যর্থ।"
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "%s সিগন্যাল প্রাপ্ত হয়েছে।"
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "প্রস্থান করা হচ্ছে।"
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "'%s' ব্যবহারকারী সন্ধান করতে ব্যর্থ।"
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "দল '%s' সন্ধান করতে ব্যর্থ।"
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "ব্যবহারকারী '%s' (UID %lu) ও দল '%s' (GID %lu) প্রাপ্ত হয়েছে।"
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "'%s' ব্যবহারকারীর ও '%s' দলের GID-র মধ্যে গরমিল।"
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr ""
+"'%s' ব্যবহারকারী ব্যক্তিগত ডিরেক্টরি রূপে '%s' ধার্য করা হয়নি, অগ্রাহ্য করা হবে।"
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' নির্মাণ করতে ব্যর্থ: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "দলের তালিকা পরিবর্তন করতে ব্যর্থ: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID পরিবর্তন করতে ব্যর্থ: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID পরিবর্তন করতে ব্যর্থ: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "root-র অধিকার সাফল্যের সাথে বর্জন করা হয়েছে।"
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "এই প্ল্যাটফর্মে, সিস্টেমব্যাপী মোড সমর্থিত নয়।"
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) বিফল: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "কমান্ড-লাইন পার্স করতে ব্যর্থ।"
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "ডেমন চলছে না"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "PID %u রূপে ডেমন চলছে"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "ডেমন kill করতে ব্যর্থ: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"root পরিচয়ে এই প্রোগ্রামটি সঞ্চালিত হওয়া উচিত নয় (যদি না --system উল্লিখিত হয়)।"
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Root-র অধিকার আবশ্যক।"
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "সিস্টেম ইনস্ট্যান্সের ক্ষেত্রে --start সমর্থিত নয়।"
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "সিস্টেম মোডে চলছে, কিন্তু --disallow-exit নির্ধারিত হয়নি!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "সিস্টেম মোডে চলছে, কিন্তু --disallow-module-loading নির্ধারিত হয়নি!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "সিস্টেম মোডে চলছে, SHM মোড বলপূর্বক নিষ্ক্রিয় করা হচ্ছে!"
+
+# http://linux.die.net/man/1/pulseaudio এখানে রেফারেন্স পাওয়া যাবে
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"সিস্টেম মোডে চলছে, কর্মহীন অবস্থার জন্য ধার্য সময়সীমা পূর্তী পরে প্রস্থানের ব্যবস্থা "
+"বলপূর্বক নিষ্ক্রিয় করা হচ্ছে!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdio প্রাপ্ত করতে ব্যর্থ।"
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "পাইপ বিফল: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() বিফল: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() বিফল: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "ডেমন আরম্ভ করতে বিফল।"
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "সাফল্যের সাথে ডেমন আরম্ভ করা হয়েছে।"
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() বিফল: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "এটি PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "কম্পাইলেশনের হোস্ট: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "কম্পাইলশনের CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "চিহ্নিত হোস্টে চলছে: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u CPU পাওয়া গিয়েছে।"
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "পেজের মাপ %lu বাইট"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Valgrind সমর্থন সহ কম্পাইল করা হয়েছে: হ্যাঁ"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Valgrind সমর্থন সহ কম্পাইল করা হয়েছে: না"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "valgrind মোডে চলছে: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "চিহ্নিত হোস্টে চলছে: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "সর্বাপেক্ষ উত্তম বিল্ড: হ্যাঁ"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "সর্বাপেক্ষ উত্তম বিল্ড: না"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG ব্যাখ্যা করা হয়েছে, সকল অ্যাসার্ট নিষ্ক্রিয় করা হয়েছে।"
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH ব্যাখ্যা করা হয়েছে, শুধুমাত্র ফাস্ট পাথ অ্যাসার্ট নিষ্ক্রিয় করা হয়েছে।"
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "সকল অ্যাসার্ট সক্রিয় করা হয়েছে।"
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "মেশিন ID প্রাপ্ত করতে ব্যর্থ"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "মেশিন ID হল %s।"
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "সেশান ID হল %s।"
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "রান-টাইম ডিরেক্টরি %s ব্যবহার করা হচ্ছে।"
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "অবস্থাসূচক ডিরেক্টরি %s ব্যবহার করা হচ্ছে।"
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "মডিউল ডিরেক্টরি %s ব্যবহার করা হচ্ছে।"
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "সিস্টেম মোডে চলছে: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"আপনি সিস্টেম মোডে PA সঞ্চালিত করছেন এবং এটি না করাই বাঞ্ছনীয়।\n"
+"এর ফলে প্রত্যাশামত ফলাফল না পাওয়ার সম্ভাবনা রয়েছে।\n"
+"সিস্টেম মোডে ব্যবহারের সমস্যা সম্পর্কে জানতে হলে http://www.freedesktop.org/wiki/"
+"Software/PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ দেখুন।"
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() ব্যর্থ।"
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "উচ্চ-রেসোলিউশনের নতুন টাইমার উপলব্ধ রয়েছে! পরীক্ষা করে দেখুন!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr "উচ্চ-রেসোলিউশনের নতুন টাইমার সহ Linux সক্রিয় করা বাঞ্ছনীয়!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() ব্যর্থ।"
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "ডেমন আরম্ভ করতে ব্যর্থ।"
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"লোড করা মডিউল বিনা ডেমন আরম্ভ করা হয়েছে এবং কোনো কর্ম সঞ্চালন করা সম্ভব নয়।"
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "ডেমন আরম্ভ করা হয়েছে।"
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "ডেমন বন্ধ করার প্রক্রিয়া আরম্ভ করা হয়েছে।"
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "ডেমন বন্ধ করা হয়েছে।"
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            সাহায্যের এই বার্তা প্রদর্শন করা হবে\n"
+"      --version                         সংস্করণ প্রদর্শন করা হবে\n"
+"      --dump-conf                       ডিফল্ট কনফিগারেশন ডাম্প করা হবে\n"
+"      --dump-modules                    উপলব্ধ মডিউলের তালিকা ডাম্প করা হবে\n"
+"      --dump-resample-methods           উপলব্ধ রি-স্যাম্পেলের পদ্ধতি ডাম্প করা "
+"হবে\n"
+"      --cleanup-shm                     যৌথরূপে ব্যবহৃত মেমরির পুরোনো অংশগুলি "
+"পরিশ্রুত করা হবে\n"
+"      --start                           ডেমন সক্রিয় না হলে তা আরম্ভ করুন\n"
+"  -k  --kill                            চলমান ডেমন kill করুন\n"
+"      --check                           চলমান ডেমন পরীক্ষা করুন (শুধুমাত্র এক্সিট "
+"কোড উৎপন্ন হবে)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   সিস্টেমব্যাপী ইন্সট্যান্স রূপে সঞ্চালিত "
+"হবে\n"
+"  -D, --daemonize[=BOOL]                আরম্ভের পরে ডেমন নির্মাণ করা হবে\n"
+"      --fail[=BOOL]                     আরম্ভ করতে ব্যর্থ হলে প্রস্থান করা হবে\n"
+"      --high-priority[=BOOL]            nice-র উচ্চ মাত্রা ধার্যের প্রচেষ্টা করা "
+"হবে\n"
+"                                        (root, SUID অথবা\n"
+"                                        উচ্চ মাত্রার RLIMIT_NICE-র ক্ষেত্রে "
+"উপলব্ধ করা হবে)\n"
+"      --realtime[=BOOL]                 রিয়েল-টাইম শিডিউলিং সক্রিয় করার "
+"প্রচেষ্টা করুন\n"
+"                                        (root, SUID অথবা\n"
+"                                        উচ্চ মাত্রার RLIMIT_RTPRIO-র ক্ষেত্রে "
+"উপলব্ধ করা হবে)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            ব্যবহারকারী দ্বারা প্রস্থানের অনুরোধ "
+"গ্রাহ্য করা হবে না\n"
+"      --exit-idle-time=SECS             কর্মহীন অবস্থায় চিহ্নিত সময় অতিবাহিত "
+"হলে, ডেমনটি\n"
+"                                        বন্ধ করুন\n"
+"      --module-idle-time=SECS           কর্মহীন অবস্থায় চিহ্নিত সময় অতিবাহিত "
+"হলে, অটো-লোড করা\n"
+"                                        মডিউলগুলি আন-লোড করুন\n"
+"      --scache-idle-time=SECS           কর্মহীন অবস্থায় চিহ্নিত সময় অতিবাহিত "
+"হলে, অটো-লোড করা\n"
+"                                        স্যাম্পেলগুলি আন-লোড করুন\n"
+"      --log-level[=LEVEL]               ভার্বোসিটির মাত্রা বৃদ্ধি অথবা নির্ধারণ "
+"করুন\n"
+"  -v                                    ভার্বোসিটির মাত্রা বৃদ্ধি করুন\n"
+"      --log-target={auto,syslog,stderr} লগের উদ্দিষ্ট স্থান উল্লেখ করা হবে\n"
+"      --log-meta[=BOOL]                 লগ-বার্তার মধ্যে কোডের অবস্থান অন্তর্ভুক্ত "
+"করা হবে\n"
+"      --log-time[=BOOL]                 লগ-বার্তার মধ্যে সময় অন্তর্ভুক্ত করা হবে\n"
+"      --log-backtrace=FRAMES            লগ-বার্তার মধ্যে ব্যাক-ট্রেস অন্তর্ভুক্ত করা "
+"হবে\n"
+"  -p, --dl-search-path=PATH             পরিবর্তনশীল যৌথব্যবহারের অবজেক্ট (প্লাগ-"
+"ইন) অনুসন্ধানের\n"
+"                                        পাথ নির্ধারণ করুন\n"
+"      --resample-method=METHOD          উল্লিখিত রি-স্যাম্পলিং পদ্ধতি প্রয়োগ করা "
+"হবে\n"
+"                                        (সম্ভাব্য মান জানার জন্য --dump-"
+"resample-methods\n"
+"                                        দেখুন)\n"
+"      --use-pid-file[=BOOL]             একটি PID ফাইল নির্মাণ করুন\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              যৌথ মেমরি ব্যবহারের সমর্থন নিষ্ক্রিয় করা "
+"হবে।\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         চিহ্নিত আর্গুমেন্ট সহ, উল্লিখিত প্লাগ-"
+"ইন\n"
+"                                        লোড করা হবে\n"
+"  -F, --file=FILENAME                   চিহ্নিত স্ক্রিপ্ট সঞ্চালন করুন\n"
+"  -C                                    প্রারম্ভের পরে চলমান TTY-র মধ্যে একটি "
+"কমান্ড-লাইন\n"
+"                                         আরম্ভ করুন\n"
+"\n"
+"  -n                                    ডিফল্ট স্ক্রিপ্ট ফাইল লোড করা হবে না\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level-র ক্ষেত্রে লগ স্তরের আর্গুমেন্ট প্রত্যাশিত (0..4 সীমার মধ্যে একটি সংখ্যা "
+"অথবা debug, info, notice, warn, ও error-র মধ্যে একটি মান)।"
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "লগের উদ্দিষ্ট স্থন বৈধ নয়: 'syslog', 'stderr' অথবা 'auto' প্রয়োগ করুন।"
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "রি-স্যাম্পেল পদ্ধতি '%s' বৈধ নয়।"
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm দ্বারা বুলিয়ান আর্গুমেন্ট প্রত্যাশিত"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "নাম: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "মডিউল সংক্রান্ত কোনো তথ্য উপলব্ধ নেই\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "সংস্করণ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "বিবরণ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "নির্মাতা: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "ব্যবহার পদ্ধতি: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "একবার লোড করা হবে: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "অবচিত করার সতর্কবার্তা: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "পাথ: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] লগ টার্গেট '%s' বৈধ নয়।"
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] লগের স্তর '%s' বৈধ নয়।"
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] রি-স্যাম্পেল পদ্ধতি '%s' বৈধ নয়।"
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] rlimit '%s' বৈধ নয়।"
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] স্যাম্পেলের বিন্যাস '%s' বৈধ নয়।"
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] স্যাম্পেলের মাত্রা '%s' বৈধ নয়।"
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] স্যাম্পেলের চ্যানেল '%s' বৈধ নয়"
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] চ্যানেল ম্যাপ '%s' বৈধ নয়।"
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] অংশ সংখ্যা '%s' বৈধ নয়।"
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] অংশের মাপ '%s' বৈধ নয়।"
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] nice স্তর '%s' বৈধ নয়।"
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] স্যাম্পেলের মাত্রা '%s' বৈধ নয়।"
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "কনফিগারেশন ফাইল খুলতে ব্যর্থ: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"ডিফল্ট চ্যানেল ম্যাপের মধ্যে অন্তর্ভুক্ত চ্যানেলের সংখ্যা ও চ্যানেলের ডিফল্ট সংখ্যার মধ্যে "
+"গরমিল।"
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### চিহ্নিত কনফিগারেশন ফাইল থেকে পড়া হবে: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "অধিকার বর্জন করা হচ্ছে।"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio শব্দ ব্যবস্থা"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "PulseAudio শব্দ ব্যবস্থা আরম্ভ করা হবে"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio শব্দ ব্যবস্থা"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "PulseAudio শব্দ ব্যবস্থা আরম্ভ করা হবে"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "মোনো"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "সামনে কেন্দ্রস্থিত"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "সামনে বাঁদিকে"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "সামনে ডানদিকে"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "পিছনে কেন্দ্রস্থিত"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "পিছনে বাঁদিকে"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "পিছনে ডানদিকে"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "সামনে কেন্দ্রের-বাঁদিকে"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "সামনে কেন্দ্রের-ডানদিকে"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "পাশে বাঁদিকে"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "পাশে ডানদিকে"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "অক্সিলারি ০"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "অক্সিলারি ১"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "অক্সিলারি ২"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "অক্সিলারি ৩"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "অক্সিলারি ৪"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "অক্সিলারি ৫"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "অক্সিলারি ৬"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "অক্সিলারি ৭"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "অক্সিলারি ৮"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "অক্সিলারি ৯"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "অক্সিলারি ১০"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "অক্সিলারি ১১"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "অক্সিলারি ১২"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "অক্সিলারি ১৩"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "অক্সিলারি ১৪"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "অক্সিলারি ১৫"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "অক্সিলারি ১৬"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "অক্সিলারি ১৭"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "অক্সিলারি ১৮"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "অক্সিলারি ১৯"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "অক্সিলারি ২০"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "অক্সিলারি ২১"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "অক্সিলারি ২২"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "অক্সিলারি ২৩"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "অক্সিলারি ২৪"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "অক্সিলারি ২৫"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "অক্সিলারি ২৬"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "অক্সিলারি ২৭"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "অক্সিলারি ২৮"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "অক্সিলারি ২৯"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "অক্সিলারি ৩০"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "অক্সিলারি ৩১"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "উপরে কেন্দ্রস্থিত"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "উপরে সামনে কেন্দ্রস্থিত"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "উপরে সামনে বাঁদিকে"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "উপরে সামনে ডানদিকে"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "উপরে পিছনে কেন্দ্রস্থিত"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "উপরে পিছনে বাঁদিকে"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "উপরে পিছনে ডানদিকে"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(অবৈধ)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "স্টিরিও"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "সারাউন্ড ৪.০"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "সারাউন্ড ৪.১"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "সারাউন্ড ৫.০"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "সারাউন্ড ৫.১"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "সারাউন্ড ৭.১"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "ঠিক আছে"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "ব্যবহারাধিকার প্রত্যাখ্যাত"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "অজানা কমান্ড"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "অবৈধ আর্গুমেন্ট"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "এনটিটি উপস্থিত রয়েছে"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "এই ধরনের কোনো এনটিটি উপস্থিত নেই"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "সংযোগ প্রত্যাখ্যান করা হয়েছে"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "প্রোটোকল সংক্রান্ত ত্রুটি"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "সময়সীমা"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "কোনো অনুমোদনের-কি নেই"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "অভ্যন্তরীণ ত্রুটি"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "সংযোগ বন্ধ করা হয়েছে"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "এনটিটি kill করা হয়েছে"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "সার্ভার বৈধ নয়"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "মডিউল আরম্ভ করতে ব্যর্থ"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "অবস্থা সঠিক নয়"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "তথ্য অনুপস্থিত "
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "প্রোটোকলের সংস্করণে গরমিল"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "অত্যাধিক বড়"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "সমর্থিত নয়"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "অজানা ত্রুটির কোড"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "এই ধরনের কোনো এক্সটেনশন নেই"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "অবচিত বৈশিষ্ট্য"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "অনুপস্থিত বাস্তবায়ন"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "ক্লায়েন্ট ফর্ক করা হয়েছে"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "ইনপুট/আউটপুট ত্রুটি"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "ডিভাইস অথবা রিসোর্সটি ব্যস্ত"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f গিবিবাইট"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f মিবিবাইট"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f কিবিবাইট"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u বাইট"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() ব্যর্থ: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "কুকির তথ্য পার্স করতে ব্যর্থ"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "কনফিগারেশন ফাইল '%s' খুলতে ব্যর্থ: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "কোনো কুকি লোড করা হয়নি। কুকি বিনা সংযোগের প্রচেষ্টা করা হচ্ছে।"
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "অজানা এক্সটেনশন '%s'-র জন্য বার্তা প্রাপ্ত হয়েছে"
+
+# drain a stream = যখন স্ট্রিমের মধ্যে উপস্থিত সকল তথ্য আহরণ করা হয় ও স্ট্রিমটি সম্পূর্ণরূপে ফাঁকা হয়ে যায়।
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "স্ট্রিম ড্রেইন (অর্থাৎ ফাঁকা) করতে ব্যর্থ: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "প্লে-ব্যাক স্ট্রিম ফাঁকা করা হয়েছে।"
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "সার্ভারের সাথে স্থাপিত সংযোগ ফাঁকা করা হচ্ছে।"
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() ব্যর্থ: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_write() ব্যর্থ: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() ব্যর্থ: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "সাফল্যের সাথে স্ট্রিম নির্মিত হয়েছে।"
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() ব্যর্থ: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "বাফারের মাপ: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "বাফারের মাপ: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "স্যাম্পেলের spec '%s', ও চ্যানেল ম্যাপ '%s' ব্যবহার করা হচ্ছে।"
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "ডিভাইস %s-র সাথে সংযোগ করা হয়েছে (%u, %ssuspended)।"
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "ষ্ট্রিম সংক্রান্ত ত্রুটি: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "স্ট্রিম ডিভাইস স্থগিত করা হয়েছে। %s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "স্ট্রিম ডিভাইস পুনরারম্ভ করা হয়েছে। %s"
+
+# underrun = ধীর গতির স্ট্রিম
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "ধীর গতির স্ট্রিম.%s"
+
+# overrun=the stream fills up the allocated buffer space and there is no more space for it
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "স্ট্রিম মাত্রা অতিক্রম করেছে।%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "স্ট্রিম আরম্ভ করা হয়েছে। %s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "%s ডিভাইসে স্ট্রিম স্থানান্তর করা হয়েছে (%u, %ssuspended)।%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "না "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "স্ট্রিম বাফারের অ্যাট্রিবিউট পরিবর্তিত হয়েছে। %s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "সংযোগ স্থাপিত হয়েছে।%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() ব্যর্থ: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() ব্যর্থ: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() ব্যর্থ: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "সংযোগ বিফল: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "ফাইলের সমাপ্তি সনাক্ত হয়েছে।"
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() ব্যর্থ: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "সিগন্যাল প্রাপ্ত হয়েছে, প্রস্থান করা হবে।"
+
+# latency here = delay (technical term
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "লেটেন্সির পরিমাণ প্রাপ্ত করতে ব্যর্থ: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "সময়: %0.3f সেকেন্ড; লেটেন্সি: %0.0f usec।"
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() ব্যর্থ: %s"
+
+# reverting this to english because the command line text gets messed up
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --file-format=FFORMAT             Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"libpulse সহযোগে কম্পাইল করা হয়েছে %s\n"
+"libpulse-র সাথে যুক্ত %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "ক্লায়েন্টের নাম '%s' বৈধ নয়"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "স্ট্রিমের নাম '%s' বৈধ নয়।"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "চ্যানেল ম্যাপ '%s' বৈধ নয়"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "লেটেন্সির জন্য নির্ধারিত বৈশিষ্ট্য '%s' বৈধ নয়"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "প্রসেসের সময়ের বৈশিষ্ট্য '%s' বৈধ নয়"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "বৈশিষ্ট্য '%s' বৈধ নয়।"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "ফাইলের অজানা বিন্যাস %s।"
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "অবৈধ স্যাম্পেল নির্ধারিত"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "অত্যাধিক আর্গুমেন্ট।"
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "স্যাম্পেলের মান নির্ধারণের ফাইল নির্মাণ করতে ব্যর্থ"
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "শব্দের ফাইল খুলতে ব্যর্থ।"
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"সতর্কবার্তা: চিহ্নিত স্যাম্পেল নির্ধারণের ফাইলটির তথ্য, এই ফাইলের থেকে উপলব্ধ তথ্য "
+"দ্বারা প্রতিস্থাপিত হবে।"
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "ফাইল থেকে স্যাম্পেল সংক্রান্ত তথ্য প্রাপ্ত করতে ব্যর্থ।"
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "সতর্কবার্তা: ফাইল থেকে চ্যানেলের ম্যাপ নির্ধারণ করতে ব্যর্থ।"
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "চ্যানেলের ম্যাপ ও স্যাম্পেলের নির্ধারিত মানে গরমিল"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "সতর্কবার্তা: ফাইলের মধ্যে চ্যানেলের ম্যাপ লিখতে ব্যর্থ।"
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"একটি %s স্ট্রিম খোলা হচ্ছে। এটির জন্য '%s'-র স্যাম্পেলের নির্ধারিত মান ও '%s' "
+"চ্যানেলের ম্যাপ প্রয়োগ করা হবে।"
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "রেকর্ড করা হচ্ছে"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "প্লে-ব্যাক"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "কমান্ড-লাইন পার্স করতে ব্যর্থ।"
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() ব্যর্থ।"
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() ব্যর্থ।"
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() ব্যর্থ।"
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() ব্যর্থ: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() ব্যর্থ।"
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() ব্যর্থ।"
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "স্থগিত করতে ব্যর্থ: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "পুনরারম্ভ করতে ব্যর্থ: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "সতর্কবার্তা: শব্দের সার্ভারটি স্থানীয় নয় ও স্থগিত করা হচ্ছে না।\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "সংযোগ বিফল: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT প্রাপ্ত হয়েছে, প্রস্থান করা হয়েছে।\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "সতর্কবার্তা: সিগন্যাল %u দ্বারা চাইল্ড প্রসেস বন্ধ করা হয়েছে\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            এই সাহায্য বার্তা প্রদর্শন করা হবে\n"
+"      --version                         সংস্করণ প্রদর্শন করা হবে\n"
+"  -s, --server=SERVER                   সংযোগ করার উদ্দেশ্যে চিহ্নিত সার্ভারের "
+"নাম\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"libpulse সহযোগে কম্পাইল করা হয়েছে %s\n"
+"libpulse-র সাথে যুক্ত %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() ব্যর্থ।\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() ব্যর্থ।\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() ব্যর্থ।\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "পরিসংখ্যান প্রাপ্ত করতে ব্যর্থ: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "বর্তমানে ব্যবহৃত: %u ব্লকের মধ্যে উপস্থিত সর্বমোট %s বাইট।\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"সম্পূর্ণ কর্মকালের জন্য বরাদ্দ করা হয়েছে: %u ব্লকের মধ্যে উপস্থিত সর্বমোট %s বাইট।\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "স্যাম্পেল ক্যাশের মাপ: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "সার্ভার সংক্রান্ত তথ্য প্রাপ্ত করতে ব্যর্থ: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"ব্যবহারকারীর নাম: %s\n"
+"হোস্ট-নেম: %s\n"
+"সার্ভারের নাম: %s\n"
+"সার্ভারের সংস্করণ: %s\n"
+"স্যাম্পেলের ডিফল্ট নির্ধারিত মান: %s\n"
+"ডিফল্ট চ্যানেল ম্যাপ: %s\n"
+"ডিফল্ট সিংক: %s\n"
+"ডিফল্ট সোর্স: %s\n"
+"কুকি: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "sink সংক্রান্ত তথ্য প্রাপ্ত করতে ব্যর্থ: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"সিংক #%u\n"
+"\tঅবস্থা: %s\n"
+"\tনাম: %s\n"
+"\tবিবরণ: %s\n"
+"\tড্রাইভার: %s\n"
+"\tস্যাম্পেলের বৈশিষ্ট্য: %s\n"
+"\tচ্যানেল ম্যাপ: %s\n"
+"\tচিহ্নিত মডিউলের মালিকানাধীন: %u\n"
+"\tনিঃশব্দ: %s\n"
+"\tআওয়াজ: %s%s%s\n"
+"\t        ভারসাম্য %0.2f\n"
+"\tআওয়াজের মূল মাত্রা: %s%s%s\n"
+"\tসোর্স নিরীক্ষণ: %s\n"
+"\tলেটেন্সি: %0.0f usec, কনফিগার করা হয়েছে %0.0f usec\n"
+"\tফ্ল্যাগ: %s%s%s%s%s%s\n"
+"\tবিবিধ বৈশিষ্ট্য:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tপোর্ট:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tসক্রিয় পোর্ট: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tপোর্ট:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "উৎস সংক্রান্ত তথ্য প্রাপ্ত করতে ব্যর্থ: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"সোর্স #%u\n"
+"\tঅবস্থা: %s\n"
+"\tনাম: %s\n"
+"\tবিবরণ: %s\n"
+"\tড্রাইভার: %s\n"
+"\tস্যাম্পেলের বৈশিষ্ট্য: %s\n"
+"\tচ্যানেল ম্যাপ: %s\n"
+"\tচিহ্নিত মডিউলের মালিকানাধীন: %u\n"
+"\tনিঃশব্দ: %s\n"
+"\tআওয়াজ: %s%s%s\n"
+"\t        ভারসাম্য %0.2f\n"
+"\tআওয়াজের মূল মাত্রা: %s%s%s\n"
+"\tসিংক নিরীক্ষণ: %s\n"
+"\tলেটেন্সি: %0.0f usec, কনফিগার করা হয়েছে %0.0f usec\n"
+"\tফ্ল্যাগ: %s%s%s%s%s%s\n"
+"\tবিবিধ বৈশিষ্ট্য:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "মডিউল সংক্রান্ত তথ্য প্রাপ্ত করতে ব্যর্থ: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"মডিউল #%u\n"
+"\tনাম: %s\n"
+"\tআর্গুমেন্ট: %s\n"
+"\tব্যবহারের সংখ্যা: %s\n"
+"\tবিবিধ বৈশিষ্ট্য:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "ক্লায়েন্ট সংক্রান্ত তথ্য প্রাপ্ত করতে ব্যর্থ: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ক্লায়েন্ট #%u\n"
+"\tড্রাইভার: %s\n"
+"\tচিহ্নিত মডিউলের মালিকানাধীন: %s\n"
+"\tবিবিধ বৈশিষ্ট্য:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "কার্ড সংক্রান্ত তথ্য প্রাপ্ত করতে ব্যর্থ: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"কার্ড #%u\n"
+"\tনাম: %s\n"
+"\tড্রাইভার: %s\n"
+"\tচিহ্নিত মডিউলের মালিকানাধীন: %s\n"
+"\tবিবিধ বৈশিষ্ট্য:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tপ্রোফাইল:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tসক্রিয় প্রোফাইল: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "sink ইনপুট সংক্রান্ত তথ্য প্রাপ্ত করতে ব্যর্থ: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"সিংক ইনপুট #%u\n"
+"\tড্রাইভার: %s\n"
+"\tচিহ্নিত মডিউলের মালিকানাধীন: %s\n"
+"\tক্লায়েন্ট: %s\n"
+"\tসিংক: %u\n"
+"\tস্যাম্পেলের বৈশিষ্ট্য: %s\n"
+"\tচ্যানেল ম্যাপ: %s\n"
+"\tনিঃশব্দ: %s\n"
+"\tআওয়াজ: %s\n"
+"\t        %s\n"
+"\t        ভারসাম্য %0.2f\n"
+"\tবাফারের লেটেন্সি: %0.0f usec\n"
+"\tসিংকের লেটেন্সি: %0.0f usec\n"
+"\tরি-স্যাম্পেলের পদ্ধতি %s\n"
+"\tবিবিধ বৈশিষ্ট্য:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "উৎস আউটপুট সংক্রান্ত তথ্য প্রাপ্ত করতে ব্যর্থ: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"সিংক ইনপুট #%u\n"
+"\tড্রাইভার: %s\n"
+"\tচিহ্নিত মডিউলের মালিকানাধীন: %s\n"
+"\tক্লায়েন্ট: %s\n"
+"\tসিংক: %u\n"
+"\tস্যাম্পেলের বৈশিষ্ট্য: %s\n"
+"\tচ্যানেল ম্যাপ: %s\n"
+"\tনিঃশব্দ: %s\n"
+"\tআওয়াজ: %s\n"
+"\t        %s\n"
+"\t        ভারসাম্য %0.2f\n"
+"\tবাফারের লেটেন্সি: %0.0f usec\n"
+"\tসিংকের লেটেন্সি: %0.0f usec\n"
+"\tরি-স্যাম্পেলের পদ্ধতি %s\n"
+"\tবিবিধ বৈশিষ্ট্য:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "স্যাম্পেল সংক্রান্ত তথ্য প্রাপ্ত করতে ব্যর্থ: %s"
+
+# Lazy = low quality sample
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"স্যাম্পেল #%u\n"
+"\tনাম: %s\n"
+"\tস্যাম্পেলের বৈশিষ্ট্য: %s\n"
+"\tচ্যানেলের ম্যাপ: %s\n"
+"\tআওয়াজ: %s\n"
+"\t        %s\n"
+"\t        ভারসাম্য %0.2f\n"
+"\tঅবকাল: %0.1fs\n"
+"\tমাপ: %s\n"
+"\tলেজি (নিম্নমান): %s\n"
+"\tফাইলের নাম: %s\n"
+"\tবিবিধ বৈশিষ্ট্য:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "ব্যর্থতা: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "উৎস সংক্রান্ত তথ্য প্রাপ্ত করতে ব্যর্থ: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "স্যাম্পেল আপলোড করতে ব্যর্থ: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "সম্পূর্ণ হওয়ার পূর্বে ফাইল সমাপ্ত হয়েছে"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "সার্ভার বৈধ নয়"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT প্রাপ্ত হয়েছে, প্রস্থান করা হয়েছে।"
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "অবৈধ শব্দের মাত্রা নির্ধারিত"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            এই সাহায্য বার্তা প্রদর্শন করা হবে\n"
+"      --version                         সংস্করণ প্রদর্শন করা হবে\n"
+"  -s, --server=SERVER                   সংযোগ করার উদ্দেশ্যে চিহ্নিত সার্ভারের "
+"নাম\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"libpulse সহযোগে কম্পাইল করা %s\n"
+"libpulse-র সাথে যুক্ত %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "লোড করার উদ্দেশ্যে অনুগ্রহ করে একটি স্যাম্পেল ফাইল উল্লেখ করুন"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "শব্দের ফাইল খুলতে ব্যর্থ।"
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "সতর্কবার্তা: ফাইল থেকে স্যাম্পেলের নির্ধারিত মাপ নির্মাণ করতে ব্যর্থ।"
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "বাজানোর উদ্দেশ্যে একটি স্যাম্পেল ফাইল উল্লেখ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "অপসারণের উদ্দেশ্যে একটি স্যাম্পেল ফাইল উল্লেখ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "সিংক ইনপুট ইন্ডেক্স ও একটি সিংক নির্ধারণ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "সোর্স আউটপুট ইন্ডেক্স ও একটি সোর্স নির্ধারণ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "মডিউলের নাম ও আর্গুমেন্ট নির্ধারণ করা আবশ্যক।"
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "মডিউল ইন্ডেক্স নির্ধারণ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr "একাধিক সিংক নির্ধারণ করা যাবে না। বুলিয়েন মান নির্ধারণ করা আবশ্যক।"
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr "একাধিক সোর্স নির্ধারণ করা যাবে না। বুলিয়েন মান নির্ধারণ করা আবশ্যক।"
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "কার্ডের নাম/ইন্ডেক্স ও একটি প্রোফাইলের নাম উল্লেখ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "sink-র নাম/ইন্ডেক্স ও একটি পোর্টের নাম উল্লেখ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "উৎসের নাম/ইন্ডেক্স ও একটি পোর্টে নাম উল্লেখ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "sink-র নাম/ইন্ডেক্স ও একটি পোর্টের নাম উল্লেখ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "উৎসের নাম/ইন্ডেক্স ও একটি শব্দের মাত্রা উল্লেখ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "সিংক ইনপুট ইন্ডেক্স ও শব্দের মাত্রা নির্ধারণ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "সিংক ইনপুট ইন্ডেক্স বৈধ নয়"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "সোর্স আউটপুট ইন্ডেক্স ও একটি সোর্স নির্ধারণ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "সিংক ইনপুট ইন্ডেক্স বৈধ নয়"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "sink-র নাম/ইন্ডেক্স ও একটি নিঃশব্দতার বুলিয়ান উল্লেখ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "অবৈধ স্যাম্পেল নির্ধারিত"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "উৎসের নাম/ইন্ডেক্স ও নিঃশব্দতার বুলিয়ান উল্লেখ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "সিংক ইনপুট ইন্ডেক্স ও নিঃশব্দতার বুলিয়ান নির্ধারণ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "অবৈধ সিংক ইনপুট ইন্ডেক্স নির্ধারিত"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "উৎসের নাম/ইন্ডেক্স ও নিঃশব্দতার বুলিয়ান উল্লেখ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "অবৈধ সিংক ইনপুট ইন্ডেক্স নির্ধারিত"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "sink-র নাম/ইন্ডেক্স ও একটি নিঃশব্দতার বুলিয়ান উল্লেখ করা আবশ্যক"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "কোনো বৈধ কমান্ড নির্ধারিত হয়নি।"
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    X11 প্রদর্শনের সাথে যুক্ত PulseAudio-র বর্তমান তথ্য প্রদর্শন করা হবে (ডিফল্ট)\n"
+" -e    স্থানীয় PulseAudio-র তথ্য X11 প্রদর্শনে এক্সপোর্ট করা হবে\n"
+" -i    X11 প্রদর্শন থেকে PulseAudio-র তথ্য স্থানীয় এনভায়রনমেন্ট ভেরিয়েবল ও কুকি "
+"ফাইলের মধ্যে ইম্পোর্ট করা হবে\n"
+" -r    X11 প্রদর্শন থেকে PulseAudio-র তথ্য মুছে ফেলা হবে\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "কমান্ড-লাইন পার্স করতে ব্যর্থ।\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "সার্ভার: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "সোর্স: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "সিংক: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "কুকি: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "কুকি সংক্রান্ত তথ্য পার্স করতে ব্যর্থ\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "কুকি সংক্রান্ত তথ্য সংরক্ষণ করতে ব্যর্থ\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "ক্লায়েন্ট কনফিগারেশন ফাইল লোড করতে ব্যর্থ।\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "পরিবেশ কনফিগারেশন সংক্রান্ত তথ্য পড়তে ব্যর্থ।\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN প্রাপ্ত করতে ব্যর্থ।\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "কুকি সংক্রান্ত তথ্য লোড করতে ব্যর্থ\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "এখনো বাস্তবায়িত হয়নি।\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "PulseAudio ডেমন চলছে না অথবা সেশানের ডেমন রূপে চলছে না।"
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio ডেমন kill করতে ব্যর্থ।"
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "ডেমন থেকে কোনো প্রতিক্রিয়া পাওয়া যাচ্ছে না।"
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "autospawn লক প্রয়োগ করতে ব্যর্থ।"
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"লেখার যোগ্য কোনো তথ্য উপস্থিত না থাকলেও, ডিভাইসের মধ্যে নতুন তথ্য লেখার উদ্দেশ্যে "
+"ALSA থেকে চেতাবনী প্রাপ্ত হয়েছে!\n"
+"সম্ভবত এটি ALSA ড্রাইভার '%s'-র একটি বাগ। অনুগ্রহ করে এই সমস্যা সম্বন্ধে ALSA "
+"ডিভেলপরদের সূচিত করুন।\n"
+"POLLOUT set দ্বারা চেতাবনী সৃষ্টি হয়েছে -- পরবর্তী snd_pcm_avail() থেকে 0 অথবা < "
+"min_avail-র থেকে কম অন্য একটি মান প্রাপ্ত হয়েছে।"
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"পড়ার যোগ্য কোনো তথ্য উপস্থিত না থাকলেও, ডিভাইস থেকে নতুন তথ্য পড়ার উদ্দেশ্যে ALSA "
+"থেকে চেতাবনী প্রাপ্ত হয়েছে!\n"
+"সম্ভবত এটি ALSA ড্রাইভার '%s'-র একটি বাগ। অনুগ্রহ করে এই সমস্যা সম্বন্ধে ALSA "
+"ডিভেলপরদের সূচিত করুন।\n"
+"POLLIN set দ্বারা চেতাবনী সৃষ্টি হয়েছে -- পরবর্তী snd_pcm_avail() থেকে 0 অথবা < "
+"min_avail-র থেকে কম অন্য একটি মান প্রাপ্ত হয়েছে।"
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "বন্ধ"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "হাই-ফিডেলিটি প্লে-ব্যাক (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "হাই-ফিডেলিটি ক্যাপচার (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "টেলিফোনি ডুপ্লে (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio শব্দের সার্ভার"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "আউটপুট ডিভাইস"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "ইনপুট ডিভাইস"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@-র মধ্যে অডিও"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "ইনপুট"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "ডকিং স্টেশন থেকে ইনপুট"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "ডকিং স্টেশনের মাইক্রোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "ডকিং স্টেশন থেকে ইনপুট"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "লাইন-ইন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "মাইক্রোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "ডকিং স্টেশনের মাইক্রোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "মাইক্রোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "বহিস্থিত মাইক্রোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "অভ্যন্তরীণ মাইক্রোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "রেডিও"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "ভিডিও"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "স্বয়ংক্রিয় গেইন নিয়ন্ত্রণ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "স্বয়ংক্রিয় গেইন নিয়ন্ত্রণ প্রয়োগ করা হবে না"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "বুস্ট"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "বুস্ট প্রয়োগ করা হবে না"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "বিবর্ধক"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "বিবর্ধন প্রয়োগ করা হবে না"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "বুস্ট"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "বুস্ট প্রয়োগ করা হবে না"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "অ্যানালগ হেড-ফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "অ্যানালগ ইনপুট"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "ডকিং স্টেশনের মাইক্রোফোন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "অ্যানালগ আউটপুট"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "অ্যানালগ আউটপুট (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "লাইন-ইন"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "অ্যানালগ মোনো আউটপুট"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "অ্যানালগ স্টিরিও"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "ডিজিট্যাল স্টিরিও (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "ডিজিট্যাল স্টিরিও (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "অ্যানালগ মোনো"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "অ্যানালগ স্টিরিও"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "অ্যানালগ সারাউন্ড ২.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "অ্যানালগ সারাউন্ড ৩.০"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "অ্যানালগ সারাউন্ড ৩.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "অ্যানালগ সারাউন্ড ৪.০"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "অ্যানালগ সারাউন্ড ৪.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "অ্যানালগ সারাউন্ড ৫.০"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "অ্যানালগ সারাউন্ড ৫.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "অ্যানালগ সারাউন্ড ৬.০"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "অ্যানালগ সারাউন্ড ৬.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "অ্যানালগ সারাউন্ড ৭.০"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "অ্যানালগ সারাউন্ড ৭.১"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "ডিজিট্যাল স্টিরিও (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "ডিজিট্যাল স্টিরিও (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "ডিজিট্যাল সারাউন্ড ৪.০ (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "ডিজিট্যাল সারাউন্ড ৫.১ (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "ডিজিট্যাল স্টিরিও (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "ডিজিট্যাল সারাউন্ড ৫.১ (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "অ্যানালগ মোনো ডুপ্লে"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "অ্যানালগ স্টিরিও ডুপ্লে"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "ডিজিট্যাল স্টিরিও ডুপ্লে (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Null ফলাফল"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "ইনপুট"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] এই প্ল্যাটফর্মে rlimit সমর্থিত নয়।"
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() ব্যর্থ"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "সোর্সের আউটপুট #%u\n"
+#~ "\tড্রাইভার: %s\n"
+#~ "\tচিহ্নিত মডিউলের মালিকানাধীন: %s\n"
+#~ "\tক্লায়েন্ট: %s\n"
+#~ "\tসোর্স: %u\n"
+#~ "\tস্যাম্পেলের বৈশিষ্ট্য: %s\n"
+#~ "\tচ্যানেলের ম্যাপ: %s\n"
+#~ "\tবাফারের লেটেন্সি: %0.0f usec\n"
+#~ "\tসোর্সের লেটেন্সি: %0.0f usec\n"
+#~ "\tরি-স্যাম্পেলের পদ্ধতি: %s\n"
+#~ "\tবিবিধ বৈশিষ্ট্য:\n"
+#~ "\t\t%s\n"
+
+# reverting to english because the command line output gets messed up
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "ডিজিট্যাল সারাউন্ড ৪.০ (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "কম ফ্রিকোয়েন্সির নিঃসরণকারী"
diff --git a/po/ca.po b/po/ca.po
new file mode 100644 (file)
index 0000000..3e1648e
--- /dev/null
+++ b/po/ca.po
@@ -0,0 +1,3397 @@
+# Catalan translation of pulseaudio by Softcatalà
+# Copyright (C) 2008 Free Software Foundation
+# This file is distributed under the same license as the pulseaudio package.
+#
+# Xavier Conde Rueda <xavi.conde@gmail.com>, 2008.
+# Agustí Grau <fletxa@gmail.com>, 2009.
+# Judith Pintó Subirada <judithp@gmail.com>
+#
+# This file is translated according to the glossary and style guide of
+# Softcatalà. If you plan to modify this file, please read first the page
+# of the Catalan translation team for the Fedora project at:
+# http://www.softcatala.org/projectes/fedora/
+# and contact the previous translator.
+#
+# Aquest fitxer s'ha de traduir d'acord amb el recull de termes i la guia
+# d'estil de Softcatalà. Si voleu modificar aquest fitxer, llegiu si
+# us plau la pàgina de catalanització del projecte Fedora a:
+# http://www.softcatala.org/projectes/fedora/
+# i contacteu l'anterior traductor/a.
+# Josep Torné Llavall <josep.torne@gmail.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:52+0000\n"
+"Last-Translator: Josep Torné Llavall <josep.torne@gmail.com>\n"
+"Language-Team: Catalan <fedora@softcatala.net>\n"
+"Language: ca\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ha retornat un valor excepcionalment gran: %lu bytes (%lu "
+"ms).\n"
+"Probablement es tracta d'un error del controlador de l'ALSA '%s'. Informeu "
+"d'aquest incident als desenvolupadors de l'ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() ha retornat un valor excepcionalment gran: %li bytes (%s%lu "
+"ms).\n"
+"Probablement es tracta d'un error del controlador de l'ALSA '%s'. Informeu "
+"d'aquest incident als desenvolupadors de l'ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ha retornat un valor excepcionalment gran: %lu bytes (%lu "
+"ms).\n"
+"Probablement es tracta d'un error del controlador de l'ALSA '%s'. Informeu "
+"d'aquest incident als desenvolupadors de l'ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() ha retornat un valor excepcionalment gran: %lu bytes "
+"(%lu ms).\n"
+"Probablement es tracta d'un error del controlador de l'ALSA '%s'. Informeu "
+"d'aquest incident als desenvolupadors de l'ALSA."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Conserva sempre almenys un conducte carregat fins i tot si és el nul"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Sortida fingida"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "Conducte virtual LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<nom per al conducte> sink_properties=<propietats per al conducte> "
+"master=<nom del conducte del filtre> format=<format de mostra> rate=<ràtio "
+"de mostra> channels=<nombre de canals> channel_map=<mapa de canals> "
+"pulgin=<nom del connector ladspa> label=<etiqueta del connector ladspa> "
+"control=<llista separada per comes dels valors de control d'entrada>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "Conducte NULL"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Sortida nul·la"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "Audio intern"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "Mòdem"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "No s'ha trobat el carregador lt_dlopen original."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "No s'ha pogut allotjar el nou carregador dl."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "No s'ha pogut afegir bind-now-loader."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "S'ha obtingut la senyal %s."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "S'està sortint."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "No s'ha trobat l'usuari '%s'."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "No s'ha trobat el grup '%s'."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "S'han trobat l'usuari '%s' (UID %lu) i el grup '%s' (GID %lu)."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "El GID de l'usuari '%s' i del grup '%s' no coincideixen."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "El directori arrel de l'usuari '%s' no és '%s', s'ignorarà."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "No s'ha pogut crear '%s': %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "No s'ha pogut canviar la llista del grup: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "No s'ha pogut canviar el GID: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "No s'ha pogut canviar l'UID: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "S'han alliberat els permisos de root."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "El mode de sistema global no és compatible amb aquesta plataforma."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "S'ha produït un error en setrlimit(%s, (%u, %u)): %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "No s'ha pogut interpretar la línia d'ordres."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "El dimoni no s'està executant"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "El dimoni s'està executant amb PID %u"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "S'ha produït un error en matar el dimoni: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"No és necessari executar aquesta aplicació com a root (excepte si "
+"s'especifica --system)"
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Es requereixen privilegis de root."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "La opció --start no està suportada per a instàncies de sistema."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr ""
+"S'està executant en mode sistema, però no s'ha especificat l'opció --"
+"disallow-exit."
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"S'està executant en mode sistema, però no s'ha especificat l'opció --"
+"disallow-module-loading."
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr ""
+"S'està executant en mode sistema, es deshabilitarà el mode SHM forçosament."
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"S'està executant en mode sistema, la sortida per temps d'inactivitat es "
+"deshabilita."
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "S'ha produït un error en adquirir stdio."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "Ha fallat la canonada: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "Ha fallat fork(): %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "Ha fallat read(): %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "S'ha produït un error en iniciar el dimoni."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "S'ha iniciat el dimoni."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "Ha fallat read(): %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Aquest és el PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Host de compilació: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "CFLAGS de compilació: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "S'està executant en el host: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "S'han trobat %u CPU's"
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "La mida de pàgina és de %lu bytes."
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Compilat amb suport per a Valgrind: sí"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Compilat amb suport per a Valgrind: no"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "S'està executant amb el mode valgrind: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "S'està executant en el host: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Construcció optimitzada: sí"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "Construcció optmitzada: no"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG està definit, s'han desactivat totes les assercions."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr ""
+"FASTPATH està definit, només s'ha deshabilitat les assercions de camí ràpid."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "S'han habilitat totes les assercions."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "No s'ha pogut obtenir l'ID de la màquina"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "L'ID de la màquina és %s."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "L'ID de la sessió és %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "S'està utilitzant el directori d'execució %s."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "S'està utilitzant el directori d'estat %s."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "S'està utilitzant el directori dels móduls %s."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "S'està executant en mode sistema: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Esteu utilitzant el PA en mode sistema. Tingueu en compte que molt "
+"probablement no hauríeu de fer-ho.\n"
+"No obstant això, si ho feu és la vostra responsabilitat si no funciona com "
+"s'esperava.\n"
+"Si us plau, llegiu http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ per a una explicació de per "
+"què el mode sistema sol ser una mala idea."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "S'ha produït un error en pa_pid_file_create()."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "Estan disponibles els temporitzadors frescos d'alta resolució."
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Es recomana la utilització d'un nucli amb els temporitzadors d'alta "
+"resolució habilitats."
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "S'ha produït un error en pa_core_new()."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "S'ha produït un error en inicialitzar el dimoni."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "El dimoni s'ha iniciat sense cap mòdul carregat, no funcionarà."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "S'ha completat la inicialització del dimoni."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "S'ha iniciat l'aturada del dimoni."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "S'ha aturat el dimoni."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [opcions]\n"
+"\n"
+"COMANDES:\n"
+"  -h, --help                            Mostra aquesta ajuda\n"
+"      --version                         Mostra la versió\n"
+"      --dump-conf                       Volca la configuració per omissió\n"
+"      --dump-modules                    Volca la llista de mòduls\n"
+"      --dump-resample-methods           Volca els mètodes disponibles de "
+"remostratge\n"
+"      --cleanup-shm                     Neteja els segments de memòria "
+"compartida sense emprar\n"
+"      --start                           Inicia el dimoni si aquest no s'està "
+"executant\n"
+"  -k  --kill                            Mata el dimoni en execució\n"
+"      --check                           Comprova l'execució del dimoni\n"
+"\n"
+"OPCIONS:\n"
+"      --system[=BOOL]                   Executa com una instància de "
+"sistema\n"
+"  -D, --daemonize[=BOOL]                Converteix en dimoni després de la "
+"inicialització\n"
+"      --fail[=BOOL]                     Surt quan falli la inicialització\n"
+"      --high-priority[=BOOL]            Prova d'establir un nivell de \n"
+"                                        Prioritat alta (només disponible com "
+"a root,\n"
+"                                        amb SUID o amb un RLIMIT_NICE "
+"elevat)\n"
+"      --realtime[=BOOL]                 Intenta habilitar la programació en\n"
+"                                        en temps real (només disponible com "
+"a root,\n"
+"                                        amb SUID o amb un RLIMIT_RTPRIO "
+"elevat)\n"
+"      --disallow-module-loading[=BOOL]  Inhabilita el mòdul de carrega/"
+"descarrega \n"
+"                                        de mòduls demanats per l'usuari "
+"després de l'inici\n"
+"      --disallow-exit[=BOOL]            Inhabilita la petició de l'usuari de "
+"sortida\n"
+"      --exit-idle-time=SEGS             Mata el dimoni quan estigui inactiu "
+"i hagi passat\n"
+"                                        aquest temps\n"
+"      --module-idle-time=SEGS           Descarrega els mòduls carregats "
+"automàticament\n"
+"                                        quan estigui inactiu o hagi passat "
+"aquest temps\n"
+"      --scache-idle-time=SEGS           Descarrega les mostres carregades "
+"automàticament\n"
+"                                        després que hagi passat aquest "
+"temps\n"
+"      --log-level[=NIVELL]              Incrementa o especifica el nivell de "
+"detall\n"
+"  -v                                    Incrementa el nivell de detall\n"
+"      --log-target={auto,syslog,stderr} Especifica el destí del registre\n"
+"      --log-meta[=BOOL]                 Inclou codi de localització en els "
+"missatges de registre\n"
+"      --log-time[=BOOL]                 Inclou marques de temps en els "
+"missatges de registre\n"
+"      --log-backtrace=MARCS             Inclou una traça en els missatges de "
+"registre\n"
+"  -p, --dl-search-path=CAMÍ             Estableix el camí de cerca "
+"d'objectes dinàmics\n"
+"                                        compartits (plugins)\n"
+"      --resample-method=MÈTODE          Utilitza el mètode de remostreig\n"
+"                                        (Per veure els valors possibles "
+"utilitza                                        --dump-resample-methods)\n"
+"      --use-pid-file[=BOOL]             Crea un fitxer PID\n"
+"      --no-cpu-limit[=BOOL]             No instal·lis un limitador de "
+"càrrega de CPU\n"
+"                                        en plataformes que ho suportin.\n"
+"      --disable-shm[=BOOL]              Inhabilita el suport de memòria "
+"compartida.\n"
+"\n"
+"SCRIPT D'INICI:\n"
+"  -L, --load=\"ARGUMENTS MÒDUL\"        Carrega el mòdul especificat amb\n"
+"                                        els arguments especificats\n"
+"  -F, --file=NOMFITXER                  Executa l'script especificat\n"
+"  -C                                    Obre una línia d'ordres en la TTY "
+"actual després\n"
+"                                        de l'inici\n"
+"\n"
+"  -n                                    No carreguis el fitxer de "
+"configuració per omissió\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize necessita un argument booleà"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail necessita un argument booleà"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level necessita un argument de nivell de log (valor númeric 0..4 o "
+"debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority necessita un argument booleà"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime necessita un argument booleà"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading necessita un argument booleà"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit necessita un argument booleà"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file necessita un argument booleà"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "Objectiu de log invàlid: utilitzeu 'syslog', 'stderr' o 'auto'."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--logtime necessita un argument booleà"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta necessita un argument booleà"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Mètode de remostratge invàlid '%s'."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system necessita un argument booleà"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit necessita un argument booleà"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm necessita un argument booleà"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Nom: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "No hi ha informació del módul disponible\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Versió: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Descripció: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Utilització: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Càrrega: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "Advertència d'obsolescència: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Ruta: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Destí de registre incorrecte '%s'"
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Nivell de registre incorrecte '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Mètode de remostreig incorrecte '%s'."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] rlimit incorrecte '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Format de mostra incorrecte '%s'."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Velocitat de mostreig '%s'."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Canals de mostreig incorrectes '%s'."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Mapa de canals incorrecte '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Nombre de fragments incorrecte '%s'."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Mida de fragment incorrecta '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Nivell de prioritat incorrecte '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Velocitat de mostreig '%s'."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Error en obrir el fitxer de configuració: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"El mapa de canals especificat per omissió té un número de canals diferent "
+"del número de canals especificat per omissió."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Lectura del fitxer de configuració: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "Alliberant els privilegis."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Sistema de so PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Inicialitza el sistema de so PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "Sistema de so PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "Inicialitza el sistema de so PulseAudio"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Frontal central"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Frontal esquerra"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Frontal dreta"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Posterior central"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Posterior esquerra"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Posterior dreta"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Frontal central part esquerra"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Frontal central part dreta"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Lateral esquerra"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Lateral dreta"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Auxiliar 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Auxiliar 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Auxiliar 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Auxiliar 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Auxiliar 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Auxiliar 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Auxiliar 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Auxiliar 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Auxiliar 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Auxiliar 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Auxiliar 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Auxiliar 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Auxiliar 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Auxiliar 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Auxiliar 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Auxiliar 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Auxiliar 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Auxiliar 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Auxiliar 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Auxiliar 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Auxiliar 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Auxiliar 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Auxiliar 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Auxiliar 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Auxiliar 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Auxiliar 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Auxiliar 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Auxiliar 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Auxiliar 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Auxiliar 31"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Auxiliar 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Auxiliar 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Superior central"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Superior frontal central"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Superior frontal esquerra"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Superior frontal dreta"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Superior posterior central"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Superior posterior esquerra"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Superior posterior dreta"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(incorrecte)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "Estèreo"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "Envolvent 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "Envolvent 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "Envolvent 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "Envolvent 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "Envolvent 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "D'acord"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "S'ha denegat l'accès"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Ordre desconeguda"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Argument incorrecte"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "L'entitat existeix"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "No existeix l'entitat"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "S'ha refusat la connexió"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "S'ha produït un error de protocol"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "S'ha esgotat el temps"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "No s'ha trobat la clau d'autorització"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "S'ha produït un error intern"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "S'ha finalitzat la connexió"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "S'ha matat l'entitat"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Servidor incorrecte"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Ha fallat la inicialització del mòdul"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Estat incorrecte"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Sense dades"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "Versió de protocol incorrecta"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Massa gran"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "No suportat"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Codi d'error desconegut"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "No existeix l'extensió"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Funcionalitat obsoleta"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Manca la implementació"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Client bifurcat"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Error d'entrada/sortida"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Dispositiu o recurs ocupat"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "Ha fallat pa_context_connect(): %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "Ha fallat el parseig de les dades de la cookie"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "S'ha produït un error en obrir el fitxer de configuració '%s': %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "No s'ha carregat cap cookie. S'està intentant connectar sense aquesta."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "S'ha rebut un missatge per a una extensió desconeguda '%s'"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "S'ha produït un error en drenar el fluxe: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "Flux de reproducció drenat."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "S'està drenant la connexió amb el servidor."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "Ha fallat pa_stream_write(): %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "Ha fallat pa_stream_begin_write(): %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "Ha fallat pa_stream_peek(): %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "Flux creat correctament."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "Ha fallat pa_stream_get_buffer_attr(): %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Mètriques del búffer: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Mètriques del búffer: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr ""
+"S'estan utilitzant les especificacions de mostreig '%s', mapejat del canal "
+"'%s'."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "S'ha connectat al dispositiu %s (%u, %ssuspès)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "S'ha produït un error en l'stream: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Flux del dispositiu suspès.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Flux del dispositiu reprès.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Dades insuficients al flux.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Desbordament de flux.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "S'ha iniciat el flux.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "S'ha mogut el flux al dispositiu %s (%u, %ssuspès).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "no "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Els atributs del flux de memòria intermèdia han canviat.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "S'ha establert la connexió.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "Ha fallat pa_stream_new(): %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "Ha fallat pa_stream_connect_playback(): %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "Ha fallat pa_stream_connect_record(): %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Ha fallat la connexió: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "S'ha llegit el fi del fitxer."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "Ha fallat write(): %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "S'ha rebut un senyal, s'està sortint."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "No s'ha pogut obtenir la latència: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Temps: %0.3f segs; Latència: %0.0f microsegs."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "Ha fallat pa_stream_update_timing_info(): %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [opcions]\n"
+"\n"
+"  -h, --help                            Mostra aquesta ajuda\n"
+"      --version                         Mostra la versió\n"
+"\n"
+"  -r, --record                          Crea una connexió per a "
+"l'enregistrament\n"
+"  -p, --playback                        Crea una connexió per a la "
+"reproducció\n"
+"\n"
+"  -v, --verbose                         Habilita les operacions detallades\n"
+"\n"
+"  -s, --server=SERVIDOR                 Nom del servidor al qual connectar-"
+"se\n"
+"  -d, --device=DISPOSITIU               Nom del conducte/font al qual "
+"connectar-se\n"
+"  -n, --client-name=NOM                 Com cridar aquest client al "
+"servidor\n"
+"      --stream-name=NOM                 Com cridar aquest flux de dades al "
+"servidor\n"
+"      --volume=VOLUM                    Especifica el volum inicial lineal "
+"dins el rang 0...65536\n"
+"      --rate=VELOCITATMOSTREIG          La velocitat de mostreig en Hz (per "
+"omissió, 44100)\n"
+"      --format=FORMATMOSTRA             El tipus de mostra, una de s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be "
+"(per omissió, s16ne)\n"
+"      --channels=CANALS                 Número de canals, 1 per a mono, 2 "
+"per a estèreo\n"
+"                                        (per omissió, 2)\n"
+"      --channel-map=MAPACANAL           Mapa de canals a utilitzar\n"
+"      --format-fix                      Pren el format de mostra del "
+"conducte al qual s'està connectant\n"
+"                                        el flux.\n"
+"      --fix-rate                        Pren la velocitat de mostreig del "
+"conducte al qual\n"
+"                                        s'està connectant el flux.\n"
+"      --fix-channels                    Pren el número de canals i el mapa "
+"de canals del\n"
+"                                        conducte al qual s'està connectant "
+"el flux de dades.\n"
+"      --no-remix                        No barregis els canals.\n"
+"      --no-remap                        Mapeja els canals per índex en "
+"comptes de per nom .\n"
+"      --latencia=BYTES                   Sol·licita la latència en bytes.\n"
+"      --process-time=BYTES              Sol·licita el temps de procés per "
+"petició en bytes.\n"
+"      --property=PROPIETAT=VALOR         Establir la propietat especificada "
+"per al valor especificat.\n"
+"      --raw                             Gravació/reproducció de dades crues "
+"PCM.\n"
+"      --format-fitxer=FFORMAT             Gravació/reproducció de dades amb "
+"format PCM.\n"
+"      --list-file-formats               Llista disponible de formats de "
+"fitxer.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Compilat amb libpulse %s\n"
+"Enllaçat amb libpulse %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Nom del client invàlid '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Nom de flux de dades invàlid '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Mapa de canals invàlid '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Especificació de latència invàlida '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Especificació de temps de procés invàlida '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Propietat invàlida '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Format desconegut de fitxer %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "Especificació de mostra invàlida"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "Massa arguments."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "No s'ha pogut generar l'especificació de mostra del fitxer."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "No s'ha pogut obrir el fitxer d'àudio."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Advertència: l'especificació de mostra especificada se sobreescriurà amb "
+"l'especificació del fitxer."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "No s'ha pogut determinar l'especificació de mostra del fitxer."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr ""
+"Advertència: no s'ha pogut determinar el mapeig de canals des del fitxer."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "El mapa de canals no coincideix amb l'especificació de mostra"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "Advertència: no s'ha pogut escriure el mapa de canals en un fitxer."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"S'està obrint un flux de dades %s amb especificació de mostra '%s' i mapa de "
+"canals '%s'."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "enregistrant"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "reproducció"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "No s'ha pogut interpretar la línia d'ordres."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "Ha fallat el pa_mainloop_new()."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "Ha fallat el io_new()."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "Ha fallat el pa_context_new()."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "Ha fallat pa_context_connect(): %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "Ha fallat el pa_context_new()."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "Ha fallat el pa_mainloop_run()."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "No s'ha pogut suspendre: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "No s'ha pogut en rependre: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "ADVERTÈNCIA: el sevidor de so no és local, no s'està suspenent.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Ha fallat la connexió: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "S'ha rebut SIGINT, s'està sortint.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "ADVERTÈNCIA: procés fill acabat pel senyal %u\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [opcions] ... \n"
+"\n"
+"  -h, --help                            Mostra aquesta ajuda\n"
+"      --version                         Mostra la versió\n"
+"  -s, --server=SERVIDOR                 Nom del servidor al qual connectar-"
+"se\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Compilat amb libpulse %s\n"
+"Enllaçat amb libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "Ha fallat el pa_mainloop_new().\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "Ha fallat el pa_context_new().\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "Ha fallat el pa_mainloop_run().\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "No s'han pogut obtenir les estadístiques: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Actualment s'estan utilitzant: %u blocs que contenen %s bytes en total.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Allotjats durant el temps de vida: %u blocs que contenen %s bytes en total.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Mida de la memòria cau de mostres: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "No s'ha pogut obtenir la informació del servidor: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Nom d'usuari: %s\n"
+"Nom del host: %s\n"
+"Nom del servidor: %s\n"
+"Versió del servidor: %s\n"
+"Especificació per omissió de la mostra: %s\n"
+"Mapa de canals per omissió: %s\n"
+"Conducte per omissió: %s\n"
+"Font per omissió: %s\n"
+"Galeta: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "No s'ha pogut obtenir la informació del conducte: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Conducte #%u\n"
+"\tEstat: %s\n"
+"\tNom: %s\n"
+"\tDescripció: %s\n"
+"\tControlador: %s\n"
+"\tEspecificació de mostra: %s\n"
+"\tMapa de canals: %s\n"
+"\tPropietari del mòdul: %u\n"
+"\tSilenciat: %s\n"
+"\tVolum: %s%s%s\n"
+"\t        balanceig %0.2f\n"
+"\tVolum bàsic: %s%s%s\n"
+"\tFont del monitor: %s\n"
+"\tLatència: %0.0f microsegs., configurat %0.0f microsegs\n"
+"\tModificadors: %s%s%s%s%s%s\n"
+"\tPropietats:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPorts:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tPort actiu: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tPorts:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "No s'ha pogut obtenir la informació de la font: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Font #%u\n"
+"\tEstat: %s\n"
+"\tNom: %s\n"
+"\tDescripció: %s\n"
+"\tControlador: %s\n"
+"\tEspecificació de mostra: %s\n"
+"\tMapa de canals: %s\n"
+"\tPropietari del mòdul: %u\n"
+"\tSilenciat: %s\n"
+"\tVolum: %s%s%s\n"
+"\t        balanceig %0.2f\n"
+"\tVolum bàsic: %s%s%s\n"
+"\tMonitor del conducte: %s\n"
+"\tLatència: %0.0f microsegs., configurat %0.0f microsegs.\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tPropietats:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "No s'ha pogut obtenir informació del mòdul: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Mòdul #%u\n"
+"\tNom: %s\n"
+"\tArguments: %s\n"
+"\tContador d'utilització: %s\n"
+"\tPropietats:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "No s'ha pogut obtenir informació del client: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Client #%u\n"
+"\tControlador: %s\n"
+"\tPropietari del mòdul: %s\n"
+"\tPropietats:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "No s'ha pogut obtenir la informació de la targeta: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Targeta #%u\n"
+"\tNom: %s\n"
+"\tControlador: %s\n"
+"\tPropietari del mòdul: %s\n"
+"\tPropietats:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tPerfils:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tPerfil actiu: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "No s'ha pogut obtenir informació del conducte d'entrada: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Conducte d'entrada #%u\n"
+"\tControlador: %s\n"
+"\tPropietari del mòdul: %s\n"
+"\tClient: %s\n"
+"\tConducte: %u\n"
+"\tEspecificació de mostra: %s\n"
+"\tMapa de canals: %s\n"
+"\tSilenciat: %s\n"
+"\tVolum: %s\n"
+"\t        %s\n"
+"\t        balanç %0.2f\n"
+"\tLatència de búffer: %0.0f microsegs.\n"
+"\tLatència del conducte: %0.0f microsegs.\n"
+"\tMètode de remostreig: %s\n"
+"\tPropietats:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "No s'ha pogut obtenir la informació del conducte de sortida: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Conducte d'entrada #%u\n"
+"\tControlador: %s\n"
+"\tPropietari del mòdul: %s\n"
+"\tClient: %s\n"
+"\tConducte: %u\n"
+"\tEspecificació de mostra: %s\n"
+"\tMapa de canals: %s\n"
+"\tSilenciat: %s\n"
+"\tVolum: %s\n"
+"\t        %s\n"
+"\t        balanç %0.2f\n"
+"\tLatència de búffer: %0.0f microsegs.\n"
+"\tLatència del conducte: %0.0f microsegs.\n"
+"\tMètode de remostreig: %s\n"
+"\tPropietats:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "No s'ha pogut obtenir informació de la mostra: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Mostra #%u\n"
+"\tNom: %s\n"
+"\ttEspecificació de mostra: %s\n"
+"\tMapa de canals: %s\n"
+"\tVolum: %s\n"
+"\t         %s\n"
+"\t         balanceig %0.2f\n"
+"\tDuració: %0.1fs\n"
+"\tMida: %s\n"
+"\tLazy: %s\n"
+"\tNom de fitxer: %s\n"
+"\tPropietats:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "Ha fallat: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "No s'ha pogut obtenir la informació de la font: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "No s'ha pogut pujar la mostra: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "S'ha trobat un fi de fitxer prematurament"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr "conducte"
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr "font"
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+#, fuzzy
+msgid "source-output"
+msgstr "font"
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "Servidor incorrecte"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "S'ha rebut SIGINT, s'està sortint."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "Especificació de volum invàlida"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [opcions] ... \n"
+"\n"
+"  -h, --help                            Mostra aquesta ajuda\n"
+"      --version                         Mostra la versió\n"
+"  -s, --server=SERVIDOR                 Nom del servidor al qual connectar-"
+"se\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Compilat amb libpulse %s\n"
+"Enllaçat amb libpulse %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "Si us plau, especifiqueu un fitxer de mostra per a carregar"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "No s'ha pogut obrir el fitxer de so."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+"Advertiment: No s'ha pogut determinar l'especificació de mostra a partir del "
+"fitxer."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "Heu d'especificar un nom de mostra a reproduir"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "Heu d'especificar un nom de mostra a suprimir"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "Heu d'especificar una entrada del conducte i un conducte"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "Heu d'especificar un índex de font de sortida i una font"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "Heu d'especificar un nom de mòdul i els seus arguments."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "Heu d'especificar un índex de mòdul"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"No haríeu d'especificar més d'un conducte. Heu d'especificar un valor booleà."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"No haríeu d'especificar més d'una font. Heu d'especificar un valor booleà."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Heu d'especificar un nom o un índex de targeta i un nom de perfil"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Heu d'especificar un nom o un índex de conducte i un nom de port"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "Heu d'especificar un nom o un índex de font i un nom de port"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Heu d'especificar un nom o un índex de conducte i un volum"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "Heu d'especificar un nom o un índex de font i un volum"
+
+#
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "Heu d'especificar un índex entrada del conducte i un volum"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "Índex d'entrada del conducte invàlid"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "Heu d'especificar un índex de font de sortida i una font"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "Índex d'entrada del conducte invàlid"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr ""
+"Heu d'especificar un nom o un índex de conducte i un booleà de silenciat"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "Especificació de mostra invàlida"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Heu d'especificar un nom o un índex de font i un booleà de silenciat"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr ""
+"Heu d'especificar un índex d'entrada del conducte i un booleà de silenciat"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "Especificació d'índex d'entrada del conducte invàlida"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "Heu d'especificar un nom o un índex de font i un booleà de silenciat"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "Especificació d'índex d'entrada del conducte invàlida"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Heu d'especificar un nom o un índex de conducte i un booleà de silenciat"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "Ordre especificada no vàlida."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D pantalla] [-S servidor] [-O conducte] [-I font] [-c fitxer]  [-d|-e|-"
+"i|-r]\n"
+"\n"
+" -d    Mostra les dades actuals de PulseAudio vinculat a una pantalla X11 "
+"(per omissió)\n"
+" -e    Exporta les dades locals de PulseAudio a una pantalla X11\n"
+" -i    Importa les dades de PulseAudio d'una pantalla X11 a les variables "
+"d'entorn locals i a un fitxer cookie\n"
+" -r    Esborra les dades de PulseAudio d'una pantalla X11\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "No s'ha pogut analitzar la línia d'ordres.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "Servidor: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "Font: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Conducte: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Galeta: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "No s'han pogut parsejar les dades de la galeta\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "No s'han pogut desar les dades de la galeta\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "No s'ha pogut carregar el fitxer de configuració del client.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "No s'han pogut llegir les dades de configuració de l'entorn.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "No s'ha pogut obtenir el nom de domini qualificat complet.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "No s'han pogut carregar les dades de la galeta\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Encara no s'ha implementat.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"El dimoni PulseAudio no s'està executant, o no s'està executant com a dimoni "
+"de la sessió."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "No s'ha pogut matar el dimoni PulseAudio."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "El dimoni no respon."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "No s'ha pogut accedir al bloqueig d'autospawn."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA ens ha despertat per a escriure dades noves al dispositiu però no hi "
+"havia res a escriure!\n"
+"Probablement es tracta d'un error del controlador de l'ALSA '%s'. Informeu "
+"d'aquest problema als desenvolupadors de l'ALSA.\n"
+"Ens han aixecat amb POLLOUT activat -- tanmateix una crida posterior a "
+"snd_pcm_avail() ha retornat 0 o un altre valor < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Alsa ens ha cridat per a llegir dades noves del dispositiu, però no hi ha "
+"res a llegir!\n"
+"Probablement es tracta d'un error de la controladora '%s' de l'ALSA. "
+"Reporteu aquest problema als desenvolupadors de l'ALSA.\n"
+"Ens han aixecat amb POLLIN activat -- tanmateix una crida posterior a "
+"snd_pcm_avail() ha retornat 0 o un altre valor < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "Inactiu"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Reproducció d'alta fidelitat (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Captura d'alta fidelitat (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Dúplex de telefonia (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "Servidor de so PulseAudio"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr ""
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+#, fuzzy
+msgid "Input Devices"
+msgstr "Entrada %s"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+#, fuzzy
+msgid "Input"
+msgstr "Entrada %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "Audio intern"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+msgid "Docking Station Line In"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+#, fuzzy
+msgid "Microphone"
+msgstr "Mono analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "Mono analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "Mono analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+#, fuzzy
+msgid "Internal Microphone"
+msgstr "Audio intern"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+#, fuzzy
+msgid "Radio"
+msgstr "Mono analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+#, fuzzy
+msgid "Video"
+msgstr "Estèreo analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+msgid "Bass Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+msgid "No Bass Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+#, fuzzy
+msgid "Headphones"
+msgstr "Mono analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+#, fuzzy
+msgid "Analog Input"
+msgstr "Mono analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+#, fuzzy
+msgid "Analog Output"
+msgstr "Sortida nul·la"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+msgid "Line Out"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+#, fuzzy
+msgid "Analog Mono Output"
+msgstr "Mono analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "Estèreo analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "Estèreo digital (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Estèreo digital (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "Mono analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "Estèreo analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+#, fuzzy
+msgid "Analog Surround 2.1"
+msgstr "Envolvent analògic 4.1 "
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+#, fuzzy
+msgid "Analog Surround 3.0"
+msgstr "Envolvent analògic 4.0 "
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+#, fuzzy
+msgid "Analog Surround 3.1"
+msgstr "Envolvent analògic 4.1 "
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "Envolvent analògic 4.0 "
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "Envolvent analògic 4.1 "
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "Envolvent analògic 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "Envolvent analògic 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+#, fuzzy
+msgid "Analog Surround 6.0"
+msgstr "Envolvent analògic 4.0 "
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+#, fuzzy
+msgid "Analog Surround 6.1"
+msgstr "Envolvent analògic 4.1 "
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+#, fuzzy
+msgid "Analog Surround 7.0"
+msgstr "Envolvent analògic 4.0 "
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "Envolvent analògic 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "Estèreo digital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Estèreo digital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Envolvent digital 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Envolvent digital 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "Estèreo digital (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Envolvent digital 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+#, fuzzy
+msgid "Analog Mono Duplex"
+msgstr "Mono analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+#, fuzzy
+msgid "Analog Stereo Duplex"
+msgstr "Estèreo analògic"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+#, fuzzy
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Estèreo digital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Sortida nul·la"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "Entrada %s"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<nom per al conducte> sink_properties=<propietats per al conducte> "
+"master=<nom del conducte del filtre> format=<format de mostra> rate=<ràtio "
+"de mostra> channels=<nombre de canals> channel_map=<mapa de canals> "
+"pulgin=<nom del connector ladspa> label=<etiqueta del connector ladspa> "
+"control=<llista separada per comes dels valors de control d'entrada>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit no disponible en aquesta plataforma."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "Ha fallat XOpenDisplay()"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Sortida de la font #%u\n"
+#~ "\tControladr: %s\n"
+#~ "\tPropietari del mòdul: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tFont: %u\n"
+#~ "\tEspecificació de mostra: %s\n"
+#~ "\tMapa de canals: %s\n"
+#~ "\tLatència de búffer: %0.0f microsegs.\n"
+#~ "\tLatència de la font: %0.0f microsegs.\n"
+#~ "\tMètode de remostreig: %s\n"
+#~ "\tPropietats:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [opcions] stat\n"
+#~ "%s [opcions] list\n"
+#~ "%s [opcions] exit\n"
+#~ "%s [opcions] upload-sample NOMFITXER [NOM]\n"
+#~ "%s [opcions] play-sample NOM [CONDUCTE]\n"
+#~ "%s [opcions] remove-sample NOM\n"
+#~ "%s [opcions] move-sink-input CONDUCTEENTRADA CONDUCTE\n"
+#~ "%s [opcions] move-source-output FONTSORTIDA FONT\n"
+#~ "%s [opcions] load-module NOM [ARGUMENTS ...]\n"
+#~ "%s [opcions] unload-module MÒDUL\n"
+#~ "%s [opcions] suspend-sink CONDUCTE 1|0\n"
+#~ "%s [opcions] suspend-source FONT 1|0\n"
+#~ "%s [opcions] set-card-profile TARGETA PERFIL \n"
+#~ "%s [opcions] set-sink-port CONDUCTE PORT \n"
+#~ "%s [opcions] set-source-port FONT PORT \n"
+#~ "%s [options] set-sink-volume CONDUCTE VOLUM\n"
+#~ "%s [options] set-source-volume FONT VOLUM\n"
+#~ "%s [options] set-sink-input-volume CONDUCTEENTRADA VOLUM\n"
+#~ "%s [options] set-sink-mute CONDUCTE 1|0\n"
+#~ "%s [options] set-source-mute FONT 1|0\n"
+#~ "%s [options] set-sink-input-mute CONDUCTEENTRADA 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Mostra aquesta ajuda\n"
+#~ "      --version                         Mostra la versió\n"
+#~ "\n"
+#~ "  -s, --server=SERVIDOR                 Nom del servidor on connectar-"
+#~ "s'hi\n"
+#~ "  -n, --client-name=NOM                 Com cridar aquest client en el "
+#~ "servidor\n"
+
+#, fuzzy
+#~ msgid "%s+%s"
+#~ msgstr "%s %s"
+
+#, fuzzy
+#~ msgid "%s / %s"
+#~ msgstr "%s %s"
+
+#, fuzzy
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Envolvent digital 4.0 (IEC958/AC3)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Emissor de baixa freqüència"
+
+#~ msgid "Invalid client name '%s'\n"
+#~ msgstr "Nom del client invàlid '%s'\n"
+
+#~ msgid "Failed to determine sample specification from file.\n"
+#~ msgstr "No s'ha pogut determinar l'especificació de mostra del fitxer.\n"
+
+#~ msgid "select(): %s"
+#~ msgstr "select(): %s"
+
+#~ msgid "Cannot connect to system bus: %s"
+#~ msgstr "No s'ha pogut connectar al bus del sistema: %s"
+
+#~ msgid "Cannot get caller from PID: %s"
+#~ msgstr "No s'ha pogut obtenir una crida del PID: %s"
+
+#~ msgid "Cannot set UID on caller object."
+#~ msgstr "No s'ha pogut especificar l'UID en l'objecte crida."
+
+#~ msgid "Failed to get CK session."
+#~ msgstr "No s'ha pogut obtenir la sessió CK."
+
+#~ msgid "Cannot set UID on session object."
+#~ msgstr "No s'ha pogut definir l'UID en l'objecte sessió."
+
+#~ msgid "Cannot allocate PolKitAction."
+#~ msgstr "No s'ha pogut assignar PolKitAction."
+
+#~ msgid "Cannot set action_id"
+#~ msgstr "No s'ha pogut definir action_id"
+
+#~ msgid "Cannot allocate PolKitContext."
+#~ msgstr "No s'ha pogut assignar PolKitContext."
+
+#~ msgid "Cannot initialize PolKitContext: %s"
+#~ msgstr "No s'ha pogut inicialitzar PolKitContext: %s"
+
+#~ msgid "Could not determine whether caller is authorized: %s"
+#~ msgstr "No s'ha pogut determinar si la crida està autoritzada: %s"
+
+#~ msgid "Cannot obtain auth: %s"
+#~ msgstr "No s'ha pogut obtenir l'autorització: %s"
+
+#~ msgid "PolicyKit responded with '%s'"
+#~ msgstr "PolicyKit ha respós '%s'"
+
+#~ msgid ""
+#~ "High-priority scheduling (negative Unix nice level) for the PulseAudio "
+#~ "daemon"
+#~ msgstr ""
+#~ "Programació d'alta prioritat (nivell Unix nice negatiu) per al dimoni "
+#~ "PulseAudio"
+
+#~ msgid "Real-time scheduling for the PulseAudio daemon"
+#~ msgstr "Programació en temps real per al dimoni PulseAudio"
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring high-priority scheduling."
+#~ msgstr ""
+#~ "Les normes d'ús del sistema no permeten PulseAudio adquirir programació "
+#~ "d'alta prioritat."
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring real-time scheduling."
+#~ msgstr ""
+#~ "Les normes d'ús del sistema no permeten la programació en temps real de "
+#~ "PulseAudio."
+
+#~ msgid "read() failed: %s\n"
+#~ msgstr "Ha fallat read(): %s\n"
+
+#~ msgid "pa_context_connect() failed: %s\n"
+#~ msgstr "Ha fallat el pa_context_connect(): %s\n"
+
+#~ msgid "We're in the group '%s', allowing high-priority scheduling."
+#~ msgstr ""
+#~ "Aquesta aplicació està en el grup '%s', s'està establint la prioritat "
+#~ "alta."
+
+#~ msgid "We're in the group '%s', allowing real-time scheduling."
+#~ msgstr ""
+#~ "Aquesta aplicació està en el grup '%s', s'està establint la prioritat en "
+#~ "temps real."
+
+#~ msgid "PolicyKit grants us acquire-high-priority privilege."
+#~ msgstr "PolicyKit ha permés el privilegi acquire-high-priority."
+
+#~ msgid "PolicyKit refuses acquire-high-priority privilege."
+#~ msgstr "PolicyKit ha rebutjat el privilegi acquire-high-priority."
+
+#~ msgid "PolicyKit grants us acquire-real-time privilege."
+#~ msgstr "PolicyKit ha permés el privilegi acquire-real-time."
+
+#~ msgid "PolicyKit refuses acquire-real-time privilege."
+#~ msgstr "PolicyKit ha rebutjat el privilegi acquire-real-time."
+
+#~ msgid ""
+#~ "Called SUID root and real-time and/or high-priority scheduling was "
+#~ "requested in the configuration. However, we lack the necessary "
+#~ "privileges:\n"
+#~ "We are not in group '%s', PolicyKit refuse to grant us the requested "
+#~ "privileges and we have no increase RLIMIT_NICE/RLIMIT_RTPRIO resource "
+#~ "limits.\n"
+#~ "For enabling real-time/high-priority scheduling please acquire the "
+#~ "appropriate PolicyKit privileges, or become a member of '%s', or increase "
+#~ "the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."
+#~ msgstr ""
+#~ "S'ha demanat SUID de root i una planificació de temps real i/o d'alta "
+#~ "prioritat. Tanmateix, falten els privilegis necessaris:\n"
+#~ "No es pertany al grup '%s'. PolicyKit no atorga els privilegis demanats i "
+#~ "no s'ha incrementat els límits de recursos RLIMIT_NICE/RLIMIT_RTPRIO.\n"
+#~ "Per habilitar la planificació en temps real o d'alta prioritat, heu "
+#~ "d'obtenir els privilegis de PolicyKit adequats, o pertànyer al grup '%s', "
+#~ "o incrementar els límits de recursos de RLIMIT_NICE/RLIMIT_RTPRIO per a "
+#~ "aquest usuari."
+
+#~ msgid ""
+#~ "High-priority scheduling enabled in configuration but not allowed by "
+#~ "policy."
+#~ msgstr ""
+#~ "La prioritat alta està habilitada en la configuració però no està permesa "
+#~ "per la política."
+
+#~ msgid "Successfully increased RLIMIT_RTPRIO"
+#~ msgstr "S'ha incrementat el valor de RLIMIT_RTPRIO amb éxit."
+
+#~ msgid "RLIMIT_RTPRIO failed: %s"
+#~ msgstr "S'ha produït un error amb RLIMIT_RTPRIO: %s"
+
+#~ msgid "Giving up CAP_NICE"
+#~ msgstr "S'abandona CAP_NICE"
+
+#~ msgid ""
+#~ "Real-time scheduling enabled in configuration but not allowed by policy."
+#~ msgstr ""
+#~ "La prioritat de temps real està habilitada en la configuració però no "
+#~ "està permesa per la política."
+
+#~ msgid "Limited capabilities successfully to CAP_SYS_NICE."
+#~ msgstr "S'han limitat les capacitats cap a CAP_SYS_NICE."
+
+#~ msgid "time_new() failed.\n"
+#~ msgstr "Ha fallat el time_new().\n"
+
+#~ msgid "Stream successfully created\n"
+#~ msgstr "Flux creat amb èxit\n"
+
+#~ msgid "Stream errror: %s\n"
+#~ msgstr "S'ha produït un error en el flux: %s\n"
+
+#~ msgid "Connection established.\n"
+#~ msgstr "S'ha establert la connexió.\n"
+
+#~ msgid ""
+#~ "%s [options] [FILE]\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -v, --verbose                         Enable verbose operation\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -d, --device=DEVICE                   The name of the sink to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ "      --stream-name=NAME                How to call this stream on the "
+#~ "server\n"
+#~ "      --volume=VOLUME                   Specify the initial (linear) "
+#~ "volume in range 0...65536\n"
+#~ "      --channel-map=CHANNELMAP          Set the channel map to the use\n"
+#~ msgstr ""
+#~ "%s [opcions] [FITXER]\n"
+#~ "\n"
+#~ "  -h, --help                            Mostra aquesta ajuda\n"
+#~ "      --version                         Mostra la versió\n"
+#~ "\n"
+#~ "  -v, --verbose                         Habilita les operacions "
+#~ "detallades\n"
+#~ "\n"
+#~ "  -s, --server=SERVIDOR                 Nom del servidor al qual "
+#~ "connectar-se\n"
+#~ "  -d, --device=DISPOSITIU               Nom del conducte al qual "
+#~ "connectar-se\n"
+#~ "  -n, --client-name=NOM                 Com cridar aquest client al "
+#~ "servidor\n"
+#~ "      --stream-name=NOM                 Com cridar aquest flux al "
+#~ "servidor\n"
+#~ "      --volume=VOLUM                    Especifica el volum inicial "
+#~ "(lineal) dins el rang 0...65536\n"
+#~ "      --rate=VELOCITATMOSTREIG          La velocitat de mostreig en Hz "
+#~ "(per omissió, 44100)\n"
+#~ "      --format=FORMATMOSTRA             El tipus de mostra, una de s16le, "
+#~ "s16be, u8, float32le,\n"
+#~ "                                        float32be, ulaw, alaw, s32le, "
+#~ "s32be (per omissió, s16ne)\n"
+#~ "      --channels=CANALS                 Número de canals, 1 per a mono, 2 "
+#~ "per a estèreo\n"
+#~ "                                        (per omissió, 2)\n"
+#~ "      --channel-map=MAPACANAL           Mapa de canals a utilitzar\n"
+#~ "      --fix-format                      Pren el format de mostra del "
+#~ "conducte al qual s'està connectant\n"
+#~ "                                        el flux.\n"
+#~ "      --fix-rate                        Pren la velocitat de mostreig del "
+#~ "conducte al qual\n"
+#~ "                                        s'està connectant el flux.\n"
+#~ "      --fix-channels                    Pren el número de canals i el "
+#~ "mapa de canals del\n"
+#~ "                                        conducte al qual s'està "
+#~ "connectant el flux.\n"
+#~ "      --no-remix                        No mesclar els canals.\n"
+#~ "      --no-remap                        Mapeja els canals per índex "
+#~ "enlloc de per nom .\n"
+#~ "      --latency=BYTES                   Especifica la latència en bytes.\n"
+#~ "      --process-time=BYTES              Especifica el temps de procés per "
+#~ "petició en bytes.\n"
+
+#~ msgid ""
+#~ "paplay %s\n"
+#~ "Compiled with libpulse %s\n"
+#~ "Linked with libpulse %s\n"
+#~ msgstr ""
+#~ "paplay %s\n"
+#~ "Compilat amb libpulse %s\n"
+#~ "Enllaçat amb libpulse %s\n"
+
+#~ msgid "Invalid channel map\n"
+#~ msgstr "Mapa de canals invàlid\n"
+
+#~ msgid "Failed to open file '%s'\n"
+#~ msgstr "No s'ha pogut obrir el fitxer '%s'\n"
+
+#~ msgid "Channel map doesn't match file.\n"
+#~ msgstr "El mapa de canals no coincideix amb el fitxer.\n"
+
+#~ msgid "Using sample spec '%s'\n"
+#~ msgstr "S'estan utilitzant les especificacions de mostra '%s'\n"
+
+#~ msgid "Output %s + Input %s"
+#~ msgstr "Sortida %s + Entrada %s"
+
+#~ msgid ""
+#~ "Called SUID root and real-time/high-priority scheduling was requested in "
+#~ "the configuration. However, we lack the necessary priviliges:\n"
+#~ "We are not in group '"
+#~ msgstr ""
+#~ "La crida de la SUID de root i la prioritat alta/temps real estàn "
+#~ "especificades en la configuració, però no té els permissos necessaris:\n"
+#~ "No es pertany al grup '"
+
+#~ msgid ""
+#~ "' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
+#~ "For enabling real-time scheduling please acquire the appropriate "
+#~ "PolicyKit priviliges, or become a member of '"
+#~ msgstr ""
+#~ "' i PolicyKit ha denegat els permísos. S'està lliberant SUID. \n"
+#~ "Per habilitar la prioritat en temps real, s'ha de adquirir els permissos "
+#~ "de PolicyKit, o pertanyer a '"
+
+#~ msgid ""
+#~ "', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this "
+#~ "user."
+#~ msgstr ""
+#~ "', o incremetar els límits de recursos RLIMIT_NICE/RLIMIT_RTPRIO per "
+#~ "aquest usuari."
+
+#~ msgid "Default sink name (%s) does not exist in name register."
+#~ msgstr "El nom sink per omissió  (%s) no existeix en el registre de noms."
+
+#~ msgid "Buffer overrun, dropping incoming data\n"
+#~ msgstr ""
+#~ "S'ha sobrepassat la memòria intermitja, s'estan descartant les dades "
+#~ "entrants\n"
+
+#~ msgid "pa_stream_drop() failed: %s\n"
+#~ msgstr "Ha fallat pa_stream_drop(): %s\n"
+
+#~ msgid "muted"
+#~ msgstr "silenciat"
+
+#~ msgid ""
+#~ "*** Autoload Entry #%u ***\n"
+#~ "Name: %s\n"
+#~ "Type: %s\n"
+#~ "Module: %s\n"
+#~ "Argument: %s\n"
+#~ msgstr ""
+#~ "*** Entrada de càrrega automàtica #%u ***\n"
+#~ "Nom: %s\n"
+#~ "Tipus: %s\n"
+#~ "Mòdul: %s\n"
+#~ "Arguments: %s\n"
diff --git a/po/cs.po b/po/cs.po
new file mode 100644 (file)
index 0000000..454fc33
--- /dev/null
+++ b/po/cs.po
@@ -0,0 +1,3239 @@
+# Czech translation of pulseaudio.
+# Copyright (C) 2008, 2009 the author(s) of pulseaudio.
+# This file is distributed under the same license as the pulseaudio package.
+# Petr Kovar <pknbe@volny.cz>, 2008, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:52+0000\n"
+"Last-Translator: Petr Kovar <pknbe@volny.cz>\n"
+"Language-Team: Czech <translation-team-cs@lists.sourceforge.net>\n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Generator: Lokalize 1.0\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() navrátil hodnotu, která je nezvykle vysoká: %lu bajtů (%lu "
+"ms).\n"
+"S největší pravděpodobností se jedná o chybu v ovladači ALSA \"%s\". "
+"Nahlaste prosím tento problém vývojářům ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() navrátil hodnotu, která je nezvykle vysoká: %li bajtů (%s%lu "
+"ms).\n"
+"S největší pravděpodobností se jedná o chybu v ovladači ALSA \"%s\". "
+"Nahlaste prosím tento problém vývojářům ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() navrátil hodnotu, která je nezvykle vysoká: %lu bajtů (%lu "
+"ms).\n"
+"S největší pravděpodobností se jedná o chybu v ovladači ALSA \"%s\". "
+"Nahlaste prosím tento problém vývojářům ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() navrátil hodnotu, která je nezvykle vysoká: %lu bajtů "
+"(%lu ms).\n"
+"S největší pravděpodobností se jedná o chybu v ovladači ALSA \"%s\". "
+"Nahlaste prosím tento problém vývojářům ALSA."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Udržuje nahraný vždy alespoň jeden cíl, i když se jedná o \"null\""
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Prázdný výstup"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "Virtuální cíl LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<název cíle> sink_properties=<vlastnosti cíle> master=<název "
+"filtrovaného cíle> format=<vzorkovací formát> rate=<vzorkovací frekvence> "
+"channels=<počet kanálů> channel_map=<mapa kanálů> plugin=<název zásuvného "
+"modulu ladspa> label=<popisek zásuvného modulu ladspa> control=<čárkou "
+"oddělený seznam hodnot ovládání vstupu>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "Taktovaný cíl NULL"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Výstup \"null\""
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "Vnitřní zvukový systém"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Nezdařilo se nalézt původní nahrávací program lt_dlopen."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "Nezdařilo se přidělení nového nahrávacího programu dl."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "Nezdařilo se přidat bind-now-loader."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "Získán signál %s."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "Ukončování."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Nezdařilo se nalézt uživatele \"%s\"."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Nezdařilo se nalézt skupinu \"%s\"."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Nalezen uživatel \"%s\" (UID %lu) a skupina \"%s\" (GID %lu)."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID uživatele \"%s\" a skupiny \"%s\" nesouhlasí."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Domovský adresář uživatele \"%s\" není \"%s\", bude ignorováno."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Nezdařilo se vytvořit \"%s\": %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Nezdařilo se změnit seznam skupin: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Nezdařilo se změnit GID: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Nezdařilo se změnit UID: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "Oprávnění superuživatele úspěšně zrušena."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "Režim celého systému není na této platformě podporován."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) selhalo: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "Nezdařila se analýza příkazového řádku."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "Démon neběží"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "Démon běží jako PID %u"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Zabití démona se nezdařilo: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Tento program není určen ke spuštění pod superuživatelem (není-li zadáno --"
+"system)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Jsou vyžadována oprávnění superuživatele."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start nepodporováno u systémových instancí."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "Běží v systémovém režimu, ale nenastaveno --disallow-exit!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "Běží v systémovém režimu, ale nenastaveno --disallow-module-loading!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "Běží v systémovém režimu, vynuceně se vypíná režim SHM!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "Běží v systémovém režimu, vynuceně se vypíná čas nečinnosti ukončení!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "Nezdařilo se získání stdio."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe selhalo: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() selhalo: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() selhalo: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "Spuštění démona selhalo."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "Spuštění démona bylo úspěšné."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() selhalo: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Toto je PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Překladový počítač: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "Překladové CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "Běží na počítači: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "Nalezen následující počet CPU: %u."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "Velikost stránky je %lu bajtů"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Přeloženo s podporou Valgrind: ano"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Přeloženo s podporou Valgrind: ne"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Běží v režimu valgrind: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "Běží na počítači: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Optimalizované sestavení: ano"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "Optimalizované sestavení: ne"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG definováno, všechny výrazy zakázány."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH definováno, zakázány pouze výrazy rychlých cest."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "Všechny výrazy povoleny."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "Nezdařilo se získání ID počítače"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "ID počítače je %s."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "ID sezení je %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Používán běhový adresář %s."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "Používán stavový adresář %s."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "Používán adresář modulů %s."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Běží v systémovém režimu: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Dobrá, máte tedy PA spuštěn v systémovém režimu. Vemte prosím na vědomí, že "
+"k tomuto by až na výjimečné situace němelo docházet.\n"
+"Pokud v této činnosti přesto budete pokračovat, nesete riziko za možné "
+"špatné a nepředvídatelné chování systému.\n"
+"Vysvětlení, proč je systémový režim obvykle velmi špatný nápad, si můžete "
+"přečíst na http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/"
+"User/WhatIsWrongWithSystemWide/ ."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() selhalo."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr ""
+"Jsou dostupné výtečné časovače o vysokém rozlišení. Tak s chutí do toho!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Sorry, vole, kernel error! Tip šéfkuchaře na dnešní den zní: Linux se "
+"zapnutými časovači o vysokém rozlišení."
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() selhalo."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "Selhalo spuštění démona."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Spuštění démona bez jakýchkoliv nahraných modulů, běh bude odmítnut."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "Spuštění démona dokončeno."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "Vypínání démona spuštěno."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "Démon ukončen."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [přepínače]\n"
+"\n"
+"PŘÍKAZY:\n"
+"  -h, --help                            Zobrazí tuto nápovědu\n"
+"      --version                         Zobrazí verzi\n"
+"      --dump-conf                       Vypíše výchozí nastavení\n"
+"      --dump-modules                    Vypíše seznam dostupných modulů\n"
+"      --dump-resample-methods           Vypíše dostupné metody "
+"převzorkování\n"
+"      --cleanup-shm                     Vyprázdní zastaralé části sdílené "
+"paměti\n"
+"      --start                           Spustí démona, pokud neběží\n"
+"  -k  --kill                            Zabije běžícího démona\n"
+"      --check                           Zjistí, zda démon běží (vrací pouze "
+"ukončovací kód)\n"
+"\n"
+"PŘEPÍNAČE:\n"
+"      --system[=BOOLEOVSKÁ]             Poběží jako celosystémová instance\n"
+"  -D, --daemonize[=BOOLEOVSKÁ]          Stane se démonem po spuštění\n"
+"      --fail[=BOOLEOVSKÁ]               Ukončí se v případě selhání "
+"spuštění\n"
+"      --high-priority[=BOOLEOVSKÁ]      Pokusí se nastavit vysokou úroveň "
+"nice\n"
+"                                        (dostupné pouze u superuživatele, v "
+"případě SUID nebo\n"
+"                                        se zvýšeným RLIMIT_NICE)\n"
+"      --realtime[=BOOLEOVSKÁ]           Pokusí se zapnout plánování v "
+"reálném čase\n"
+"                                        (dostupné pouze u superuživatele, v "
+"případě SUID nebo\n"
+"                                        se zvýšeným RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOLEOVSKÁ]  Nepovolí nahrání či zrušení "
+"nahrání modulu po spuštění\n"
+"                                        vyžádané uživatelem modulu\n"
+"      --disallow-exit[=BOOLEOVSKÁ]      Nepovolí ukončení vyžádané "
+"uživatelem\n"
+"      --exit-idle-time=SEKUNDY          Ukončí démona v případě nečinnosti a "
+"po\n"
+"                                        této době\n"
+"      --module-idle-time=SEKUNDY        Zruší nahrání automaticky nahraných "
+"modulů v případě nečinnosti\n"
+"                                        a po této době\n"
+"      --scache-idle-time=SEKUNDY        Zruší nahrání automaticky nahraných "
+"vzorků v případě nečinnosti a\n"
+"                                        po této době\n"
+"      --log-level[=ÚROVEŇ]              Zvýší nebo nastaví úroveň "
+"podrobností\n"
+"  -v                                    Zvýší úroveň podrobností\n"
+"      --log-target={auto,syslog,stderr} Určí cíl záznamů\n"
+"      --log-meta[=BOOLEOVSKÁ]           Do záznamů zahrne umístění kódu\n"
+"      --log-time[=BOOLEOVSKÁ]           Do záznamů zahrne určení času\n"
+"      --log-backtrace=RÁMCE             Do záznamů zahrne backtrace\n"
+"  -p, --dl-search-path=CESTA            Nastaví cestu hledání z důvodu "
+"dynamického sdílení\n"
+"                                        objektů (zásuvných modulů)\n"
+"      --resample-method=METODA          Použije zadanou metodu "
+"převzorkování\n"
+"                                        (Možné hodnoty viz\n"
+"                                        --dump-resample-methods)\n"
+"      --use-pid-file[=BOOLEOVSKÁ]       Vytvoří soubor PID\n"
+"      --no-cpu-limit[=BOOLEOVSKÁ]       Nenainstaluje omezovač zátěže CPU\n"
+"                                        na platformách, které ho podporují.\n"
+"      --disable-shm[=BOOLEOVSKÁ]        Vypne podporu sdílené paměti.\n"
+"\n"
+"SPOUŠTĚCÍ SKRIPT:\n"
+"  -L, --load=\"ARGUMENTY MODULU\"       Nahraje zadaný zásuvný modul\n"
+"                                        s určeným argumentem\n"
+"  -F, --file=NÁZEVSOUBORU               Spustí zadaný skript\n"
+"  -C                                    Po spuštění otevře příkazový řádek\n"
+"                                        na běžícím TTY\n"
+"\n"
+"  -n                                    Nenahraje výchozí soubor skriptu\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize předpokládá booleovský argument"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail předpokládá booleovský argument"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level předpokládá argument protokolovací úrovně (buď číselný v rozmezí "
+"0..4, nebo jeden z debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority předpokládá booleovský argument"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime předpokládá booleovský argument"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading předpokládá booleovský argument"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit předpokládá booleovský argument"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file předpokládá booleovský argument"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr ""
+"Neplatný cíl protokolu: použijte buďto \"syslog\", \"stderr\" nebo \"auto\"."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time předpokládá booleovský argument"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta předpokládá booleovský argument"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Neplatná metoda převzorkování \"%s\"."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system předpokládá booleovský argument"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit předpokládá booleovský argument"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm předpokládá booleovský argument"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Název: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "O modulu nejsou dostupné žádné informace\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Verze: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Popis: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Použití: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Načíst jednou: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "VAROVÁNÍ ZASTARALOSTI: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Cesta: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Neplatný protokolovací cíl \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Neplatná protokolovací úroveň \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Neplatná metoda převzorkování \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Neplatné rlimit \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Neplatný vzorkovací formát \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Neplatná vzorkovací frekvence \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Neplatné vzorkovací kanály \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Neplatná mapa kanálů \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Neplatný počet fragmentů \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Neplatná velikost fragmentu \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Neplatná úroveň nice \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Neplatná vzorkovací frekvence \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Nezdařilo se otevřít konfigurační soubor: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Zadaná výchozí mapa kanálů obsahuje odlišný počet kanálů než je zadaný "
+"výchozí počet kanálů."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Čtení z konfiguračního souboru: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "Rušení oprávnění."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Zvukový systém PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Spustit zvukový systém PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "Zvukový systém PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "Spustit zvukový systém PulseAudio"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Přední středový"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Přední levý"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Přední pravý"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Zadní středový"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Zadní levý"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Zadní pravý"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Přední levý středový"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Přední pravý středový"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Boční levý"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Boční pravý"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Aux 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Aux 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Aux 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Aux 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Aux 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Aux 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Aux 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Aux 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Aux 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Aux 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Aux 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Aux 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Aux 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Aux 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Aux 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Aux 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Aux 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Aux 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Aux 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Aux 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Aux 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Aux 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Aux 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Aux 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Aux 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Aux 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Aux 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Aux 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Aux 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Aux 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Aux 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Aux 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Horní středový"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Horní přední středový"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Horní přední levý"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Horní přední pravý"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Horní zadní středový"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Horní zadní levý"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Horní zadní pravý"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(neplatné)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "Budiž"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "Přístup odepřen"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Neznámý příkaz"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Neplatný argument"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "Entita existuje"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "Taková entita neexistuje"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "Spojení odmítnuto"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "Chyba protokolu"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "Časový limit"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "Bez autorizačního klíče"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "Vnitřní chyba"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "Spojení přerušeno"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "Entita zabita"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Neplatný server"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Spuštění modulu selhalo"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Chybný stav"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Žádná data"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "Nekompatibilní verze protokolu"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Příliš velké"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "Nepodporováno"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Neznámý chybový kód"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "Takové rozšíření neexistuje"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Zastaralá vlastnost"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Scházející implementace"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Klient rozvětven"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Chyba vstupu/výstupu"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Zařízení nebo zdroj se používá"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() selhalo: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "Selhala analýza dat cookie"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "Selhalo otevření konfiguračního souboru \"%s\": %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "Žádný soubor cookie nenahrán. Pokus o spojení bez tohoto kroku."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Přijata zpráva pro neznámé rozšíření \"%s\""
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Nezdařilo se vyprázdnit proud: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "Proud přehrávání vyprázdněn."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "Vyprazdňování spojení se serverem."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() selhalo: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() selhalo: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() selhalo: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "Proud úspěšně vytvořen."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() selhalo: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr ""
+"Metrika vyrovnávací paměti: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Metrika vyrovnávací paměti: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Používáno určení vzorku \"%s\", mapa kanálů \"%s\"."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "Připojeno k zařízení %s (%u, %ssuspended)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "Chyba proudu: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Proudové zařízení pozastaveno.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Proudové zařízení obnoveno.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Podběhnutí proudu.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Přeběhnutí proudu.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "Proud spuštěn.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Proud přesunut na zařízení %s (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "nikoliv "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Změněny atributy vyrovnávací paměti proudu.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "Spojení navázáno.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() selhalo: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() selhalo: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() selhalo: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Spojení selhalo: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "Získáno EOF."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() selhalo: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "Získán signál, ukončování."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Nezdařilo se získat latenci: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Čas: %0.3f sekund; latence: %0.0f μs"
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() selhalo: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [přepínače]\n"
+"\n"
+"  -h, --help                            Zobrazí tuto nápovědu\n"
+"      --version                         Zobrazí verzi\n"
+"\n"
+"  -r, --record                          Vytvoří spojení z důvodu nahrávání\n"
+"  -p, --playback                        Vytvoří spojení z důvodu přehrávání\n"
+"\n"
+"  -v, --verbose                         Zapne nakládání s úrovní "
+"podrobností\n"
+"\n"
+"  -s, --server=SERVER                   Název připojovaného serveru\n"
+"  -d, --device=ZAŘÍZENÍ                 Název připojovaného cíle či zdroje\n"
+"  -n, --client-name=NÁZEV               Způsob volání tohoto klienta na "
+"serveru\n"
+"      --stream-name=NÁZEV               Způsob volání tohoto proudu na "
+"serveru\n"
+"      --volume=HLASITOST                Určí počáteční (lineární) hlasitost "
+"v rozmezí 0...65536\n"
+"      --rate=VZORKOVACÍFREKVENCE        Vzorkovací frekvence v Hz (výchozí "
+"je 44100)\n"
+"      --format=FORMÁTVZORKU             Typ vzorku, jeden z s16le, s16be, "
+"u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be "
+"(výchozí je s16ne)\n"
+"      --channels=KANÁLY                 Počet kanálů, u mono 1, u sterea 2\n"
+"                                        (výchozí je 2)\n"
+"      --channel-map=MAPAKANÁLŮ          Mapa kanálů určená k použití namísto "
+"výchozí\n"
+"      --fix-format                      Získá formát vzorku z cíle, ke "
+"kterému se\n"
+"                                        připojuje proud.\n"
+"      --fix-rate                        Získá vzorkovací frekvenci z cíle, "
+"ke kterému se\n"
+"                                        připojuje proud.\n"
+"      --fix-channels                    Získá počet kanálů a mapu kanálů z "
+"cíle, ke kterému se\n"
+"                                        připojuje proud.\n"
+"      --no-remix                        Nesměšuje kanály.\n"
+"      --no-remap                        Mapuje kanály dle indexu namísto "
+"názvu.\n"
+"      --latency=BAJTY                   Vyžádá určenou latenci v bajtech.\n"
+"      --process-time=BAJTY              Vyžádá určený čas zpracování na "
+"požadavek v bajtech.\n"
+"      --property=VLASTNOST=HODNOTA      Nastaví určenou vlastnost na určenou "
+"hodnotu.\n"
+"      --raw                             Nahrává/přehrává surová data PCM.\n"
+"      --file-format=FORMÁT              Nahrává/přehrává formátovaná data "
+"PCM.\n"
+"      --list-file-formats               Zobrazí seznam dostupných formátů "
+"souborů.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Přeloženo s libpulse %s\n"
+"Propojeno s libpulse %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Neplatný název klienta \"%s\""
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Neplatný název proudu \"%s\""
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Neplatná mapa kanálů \"%s\""
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Neplatné upřesnění latence \"%s\""
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Neplatné upřesnění času zpracování \"%s\""
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Neplatná vlastnost \"%s\""
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Neznámý formát souboru %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "Neplatné určení vzorku"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "Příliš mnoho argumentů."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "Selhalo vytvoření určení vzorku souboru."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "Selhalo otevření zvukového souboru."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Varování: zadané určení vzorku bude přepsáno určením získaným ze souboru."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "Selhalo zjištění určení vzorku ze souboru."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Varování: Selhalo zjištění mapy kanálů ze souboru."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "Mapa kanálů se neshoduje s určením vzorku"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "Varování: selhal zápis mapy kanálů do souboru."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "Otevírání proudu %s s určením vzorku \"%s\" a mapou kanálů \"%s\"."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "nahrávání"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "přehrávání"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "Nezdařila se analýza příkazového řádku."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() selhalo."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() selhalo."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() selhalo."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() selhalo: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() selhalo."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() selhalo."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Nezdařilo se pozastavení: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Nezdařilo se obnovení: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "VAROVÁNÍ: Zvukový server není místní, nedojde k pozastavení.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Spojení selhalo: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Získáno SIGINT, ukončování.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "VAROVÁNÍ: Proces potomka ukončen signálem %u\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [přepínače] ... \n"
+"\n"
+"  -h, --help                            Zobrazí tuto nápovědu\n"
+"      --version                         Zobrazí verzi\n"
+"  -s, --server=SERVER                   Název připojovaného serveru\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Přeloženo s libpulse %s\n"
+"Propojeno s libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() selhalo.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() selhalo.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() selhalo.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Selhalo získání statistik: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Právě používáno: %u bloků obsahujících celkem %s bajtů.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Alokováno během celého běhu: %u bloků obsahujících celkem %s bajtů.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Velikost vzorkovací vyrovnávací paměti: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Nezdařilo se získání informací o serveru: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Uživatelské jméno: %s\n"
+"Název počítače: %s\n"
+"Název serveru: %s\n"
+"Verze serveru: %s\n"
+"Výchozí určení vzorku: %s\n"
+"Výchozí mapa kanálů: %s\n"
+"Výchozí cíl: %s\n"
+"Výchozí zdroj: %s\n"
+"Cookie: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Nezdařilo se získání informací o cíli: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Cíl č. %u\n"
+"\tStav: %s\n"
+"\tNázev: %s\n"
+"\tPopis: %s\n"
+"\tOvladač: %s\n"
+"\tUrčení vzorku: %s\n"
+"\tMapa kanálů: %s\n"
+"\tModul vlastníka: %u\n"
+"\tZtlumení: %s\n"
+"\tHlasitost: %s%s%s\n"
+"\t        vyvážení %0.2f\n"
+"\tZákladní hlasitost %s%s%s\n"
+"\tZdroj sledování: %s\n"
+"\tLatence: %0.0f μs, konfigurováno %0.0f μs\n"
+"\tPříznaky: %s%s%s%s%s%s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPorty:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tAktivní port: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tPorty:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Nezdařilo se získání informací o zdroji: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Zdroj č. %u\n"
+"\tStav: %s\n"
+"\tNázev: %s\n"
+"\tPopis: %s\n"
+"\tOvladač: %s\n"
+"\tUrčení vzorku: %s\n"
+"\tMapa kanálů: %s\n"
+"\tModul vlastníka: %u\n"
+"\tZtlumení: %s\n"
+"\tHlasitost: %s%s%s\n"
+"\t        vyvážení %0.2f\n"
+"\tZákladní hlasitost %s%s%s\n"
+"\tSledování zdroje: %s\n"
+"\tLatence: %0.0f μs, konfigurováno %0.0f μs\n"
+"\tPříznaky: %s%s%s%s%s%s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "nic"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Selhalo získání informací o modulu: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modul č. %u\n"
+"\tNázev: %s\n"
+"\tArgument: %s\n"
+"\tPočet použití %s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Selhalo získání informací o klientu: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Klient č. %u\n"
+"\tOvladač: %s\n"
+"\tModul vlastníka: %s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Selhalo získání informací o kartě: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Karta č. %u\n"
+"\tNázev: %s\n"
+"\tOvladač: %s\n"
+"\tModul vlastníka: %s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfily:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tAktivní profil: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Nezdařilo se získání informací o vstupu cíle: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Vstup cíle č. %u\n"
+"\tOvladač: %s\n"
+"\tModul vlastníka: %s\n"
+"\tKlient: %s\n"
+"\tCíl: %u\n"
+"\tUrčení vzorku: %s\n"
+"\tMapa kanálů: %s\n"
+"\tZtlumení: %s\n"
+"\tHlasitost: %s\n"
+"\t        %s\n"
+"\t        vyvážení %0.2f\n"
+"\tLatence vyrovnávací paměti: %0.0f μs\n"
+"\tLatence cíle: %0.0f μs\n"
+"\tMetoda převzorkování: %s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Selhalo získání informace o výstupu zdroje: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Vstup cíle č. %u\n"
+"\tOvladač: %s\n"
+"\tModul vlastníka: %s\n"
+"\tKlient: %s\n"
+"\tCíl: %u\n"
+"\tUrčení vzorku: %s\n"
+"\tMapa kanálů: %s\n"
+"\tZtlumení: %s\n"
+"\tHlasitost: %s\n"
+"\t        %s\n"
+"\t        vyvážení %0.2f\n"
+"\tLatence vyrovnávací paměti: %0.0f μs\n"
+"\tLatence cíle: %0.0f μs\n"
+"\tMetoda převzorkování: %s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Selhalo získání informace o vzorku: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Vzorek č. %u\n"
+"\tNázev: %s\n"
+"\tUrčení vzorku: %s\n"
+"\tMapa kanálů: %s\n"
+"\tHlasitost: %s\n"
+"\t        %s\n"
+"\t        vyvážení %0.2f\n"
+"\tDélka: %0.1fs\n"
+"\tVelikost: %s\n"
+"\tOpoždění: %s\n"
+"\tNázev souboru: %s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "Selhání: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Nezdařilo se získání informací o zdroji: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Selhalo nahrání vzorku: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "Předčasný konec souboru"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "Neplatný server"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "Získáno SIGINT, ukončování."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "Neplatné určení hlasitosti"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [přepínače] ... \n"
+"\n"
+"  -h, --help                            Zobrazí tuto nápovědu\n"
+"      --version                         Zobrazí verzi\n"
+"  -s, --server=SERVER                   Název připojovaného serveru\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Přeloženo s libpulse %s\n"
+"Propojeno s libpulse %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "Zadejte prosím soubor se vzorkem určeným k nahrání"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "Selhalo otevření zvukového souboru."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Varování: Selhalo zjištění určení vzorku ze souboru."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "Je nutné zadat název vzorku určeného k přehrání"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "Je nutné zadat název vzorku určeného k odstranění"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "Je nutné zadat vstup cíle a cíl"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "Je nutné zadat index výstupu zdroje a zdroj"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "Je nutné zadat název modulu a argumenty."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "Je nutné zadat index modulu"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr "Nelze zadat více než jeden cíl. Je nutné zadat booleovskou hodnotu."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr "Nelze zadat více než jeden zdroj. Je nutné zadat booleovskou hodnotu."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Je nutné upřesnit název karty/indexu a název profilu"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Je nutné upřesnit název cíle/indexu a název portu"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "Je nutné upřesnit název zdroje/indexu a název portu"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Je nutné upřesnit název cíle/indexu a hlasitost"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "Je nutné upřesnit název zdroje/indexu a hlasitost"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "Je nutné zadat index vstupu cíle a hlasitost"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "Neplatný index vstupu cíle"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "Je nutné zadat index výstupu zdroje a zdroj"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "Neplatný index vstupu cíle"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "Je nutné upřesnit název cíle/indexu a booleovskou hodnotu ztlumení"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "Neplatné určení vzorku"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Je nutné upřesnit název zdroje/indexu a booleovskou hodnotu ztlumení"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "Je nutné zadat index vstupu cíle a booleovskou hodnotu ztlumení"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "Neplatné určení indexu vstupu cíle"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "Je nutné upřesnit název zdroje/indexu a booleovskou hodnotu ztlumení"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "Neplatné určení indexu vstupu cíle"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "Je nutné upřesnit název cíle/indexu a booleovskou hodnotu ztlumení"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "Nezadán žádný platný příkaz."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D displej] [-S server] [-O cíl] [-I zdroj] [-c soubor]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Zobrazí aktuální data PulseAudio přiřazená k displeji X11 (výchozí)\n"
+" -e    Exportuje místní data PulseAudio na displej X11\n"
+" -i    Importuje data PulseAudio z displeje X11 mezi místní proměnné "
+"prostředí a soubor cookie.\n"
+" -r    Odstraní data PulseAudio z displeje X11\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Selhala analýza příkazového řádku.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "Server: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "Zdroj: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Cíl: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Nezdařilo se analyzovat data cookie\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Nezdařilo se uložit data cookie\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "Nezdařilo se nahrát konfigurační soubor klienta.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "Nezdařilo se přečtení konfiguračních dat prostředí.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Nezdařilo se získání FQDN.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Nezdařilo se nahrát data cookie\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Doposud neimplementováno.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "Neběží žádný démon PulseAudio, nebo neběží jako démon sezení."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Nezdařilo se zabít démona PulseAudio."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "Démon neodpovídá."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "Nelze přistoupit k zámku automatického spouštění."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nás probudila z důvodu zápisu nových dat na zařízení, ale ve "
+"skutečnosti nebylo co zapisovat.\n"
+"S největší pravděpodobností se jedná o chybu v ovladači ALSA \"%s\". "
+"Nahlaste prosím tento problém vývojářům ALSA.\n"
+"Probudilo nás nastavení POLLOUT - nicméně následné snd_pcm_avail() vrátilo 0 "
+"či jinou hodnotu < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nás probudila z důvodu čtení nových dat ze zařízení, ale ve skutečnosti "
+"nebylo co číst.\n"
+"S největší pravděpodobností se jedná o chybu v ovladači ALSA \"%s\". "
+"Nahlaste prosím tento problém vývojářům ALSA.\n"
+"Probudilo nás nastavení POLLIN - nicméně následné snd_pcm_avail() vrátilo 0 "
+"či jinou hodnotu < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "Vypnuto"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Přehrávání s velmi věrnou reprodukcí (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Záznam s velmi věrnou reprodukcí (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Duplexní telefonie (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "Zvukový server PulseAudio"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "Výstupní zařízení"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "Vstupní zařízení"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "Zvuk na @HOSTNAME@"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "Vstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "Vstup dokovací stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "Mikrofon dokovací stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "Vstup dokovací stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "Linkový vstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "Mikrofon dokovací stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "Externí mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "Interní mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "Rádio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "Obraz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "Automatické ovládání zesílení"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "Bez automatického ovládání zesílení"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "Zesílení"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "Bez zesílení"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "Zesilovač"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "Bez zesilovače"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "Zesílení"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "Bez zesílení"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "Analogová sluchátka"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "Analogový vstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "Mikrofon dokovací stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "Analogový výstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "Analogový výstup (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "Linkový vstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "Analogový výstup mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "Analogové stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "Digitální stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digitální stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "Analogové mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "Analogové stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "Analogový Surround 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "Analogový Surround 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "Analogový Surround 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "Analogový Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "Analogový Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "Analogový Surround 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "Analogový Surround 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "Analogový Surround 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "Analogový Surround 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "Analogový Surround 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "Analogový Surround 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "Digitální stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digitální stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digitální Surround 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digitální Surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "Digitální stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digitální Surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "Analogové duplexní mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "Analogové duplexní stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Digitální duplexní stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Výstup \"null\""
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "Vstup"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<název cíle> sink_properties=<vlastnosti cíle> master=<název "
+"filtrovaného cíle> format=<vzorkovací formát> rate=<vzorkovací frekvence> "
+"channels=<počet kanálů> channel_map=<mapa kanálů> plugin=<název zásuvného "
+"modulu ladspa> label=<popisek zásuvného modulu ladspa> control=<čárkou "
+"oddělený seznam hodnot ovládání vstupu>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit na této platformě není podporováno."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() selhalo"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Výstup zdroje č. %u\n"
+#~ "\tOvladač: %s\n"
+#~ "\tModul vlastníka: %s\n"
+#~ "\tKlient: %s\n"
+#~ "\tZdroj: %u\n"
+#~ "\tUrčení vzorku: %s\n"
+#~ "\tMapa kanálů: %s\n"
+#~ "\tLatence vyrovnávací paměti: %0.0f μs\n"
+#~ "\tLatence zdroje: %0.0f μs\n"
+#~ "\tMetoda převzorkování: %s\n"
+#~ "\tVlastnosti:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [přepínače] stat\n"
+#~ "%s [přepínače] list\n"
+#~ "%s [přepínače] exit\n"
+#~ "%s [přepínače] upload-sample NÁZEVSOUBORU [NÁZEV]\n"
+#~ "%s [přepínače] play-sample NÁZEV [CÍL]\n"
+#~ "%s [přepínače] remove-sample NÁZEV\n"
+#~ "%s [přepínače] move-sink-input VSTUPCÍLE CÍL\n"
+#~ "%s [přepínače] move-source-output VÝSTUPZDROJE ZDROJ\n"
+#~ "%s [přepínače] load-module NÁZEV [ARG ...]\n"
+#~ "%s [přepínače] unload-module MODUL\n"
+#~ "%s [přepínače] suspend-sink CÍL 1|0\n"
+#~ "%s [přepínače] suspend-source ZDROJ 1|0\n"
+#~ "%s [přepínače] set-card-profile KARTA PROFIL\n"
+#~ "%s [přepínače] set-sink-port CÍL PORT\n"
+#~ "%s [přepínače] set-source-port ZDROJ PORT\n"
+#~ "%s [přepínače] set-sink-volume CÍL HLASITOST\n"
+#~ "%s [přepínače] set-source-volume ZDROJ HLASITOST\n"
+#~ "%s [přepínače] set-sink-input-volume VSTUPCÍLE HLASITOST\n"
+#~ "%s [přepínače] set-sink-mute CÍL 1|0\n"
+#~ "%s [přepínače] set-source-mute ZDROJ 1|0\n"
+#~ "%s [přepínače] set-sink-input-mute VSTUPCÍLE 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Zobrazí tuto nápovědu\n"
+#~ "      --version                         Zobrazí verzi\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   Název připojovaného serveru\n"
+#~ "  -n, --client-name=NÁZEV               Způsob volání tohoto klienta na "
+#~ "serveru\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Digitální Surround 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Nízkofrekvenční zářič"
+
+#~ msgid "Invalid client name '%s'\n"
+#~ msgstr "Neplatný název klienta \"%s\"\n"
+
+#~ msgid "Failed to determine sample specification from file.\n"
+#~ msgstr "Selhalo zjištění určení vzorku ze souboru.\n"
+
+#~ msgid "select(): %s"
+#~ msgstr "select(): %s"
+
+#~ msgid "Cannot connect to system bus: %s"
+#~ msgstr "Nelze se spojit se systémovou sběrnicí: %s"
+
+#~ msgid "Cannot get caller from PID: %s"
+#~ msgstr "Nelze získat volajícího z PID: %s"
+
+#~ msgid "Cannot set UID on caller object."
+#~ msgstr "Nelze nastavit UID na objekt volajícího."
+
+#~ msgid "Failed to get CK session."
+#~ msgstr "Nezdařilo se získání sezení CK."
+
+#~ msgid "Cannot set UID on session object."
+#~ msgstr "Nelze nastavit UID na objekt sezení."
+
+#~ msgid "Cannot allocate PolKitAction."
+#~ msgstr "Nelze alokovat PolKitAction."
+
+#~ msgid "Cannot set action_id"
+#~ msgstr "Nelze nastavit action_id"
+
+#~ msgid "Cannot allocate PolKitContext."
+#~ msgstr "Nelze alokovat PolKitContext."
+
+#~ msgid "Cannot initialize PolKitContext: %s"
+#~ msgstr "Nelze spustit PolKitContext: %s"
+
+#~ msgid "Could not determine whether caller is authorized: %s"
+#~ msgstr "Nezdařilo se určit, zda je volající oprávněn: %s"
+
+#~ msgid "Cannot obtain auth: %s"
+#~ msgstr "Nezdařilo se získat oprávnění: %s"
+
+#~ msgid "PolicyKit responded with '%s'"
+#~ msgstr "PolicyKit odpověděl s \"%s\""
+
+#~ msgid ""
+#~ "High-priority scheduling (negative Unix nice level) for the PulseAudio "
+#~ "daemon"
+#~ msgstr ""
+#~ "Plánování o vysoké prioritě (záporná úroveň nice v Unixu) démona "
+#~ "PulseAudio"
+
+#~ msgid "Real-time scheduling for the PulseAudio daemon"
+#~ msgstr "Plánování v reálném čase démona PulseAudio"
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring high-priority scheduling."
+#~ msgstr ""
+#~ "Systémová pravidla znemožňují technologii PulseAudio získat přístup k "
+#~ "plánování o vysoké prioritě."
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring real-time scheduling."
+#~ msgstr ""
+#~ "Systémová pravidla znemožňují technologii PulseAudio získat přístup k "
+#~ "plánování v reálném čase."
+
+#~ msgid "read() failed: %s\n"
+#~ msgstr "read() selhalo: %s\n"
+
+#~ msgid "pa_context_connect() failed: %s\n"
+#~ msgstr "pa_context_connect() selhalo: %s\n"
+
+#~ msgid "We're in the group '%s', allowing high-priority scheduling."
+#~ msgstr ""
+#~ "Nacházíme se ve skupině \"%s\", což umožňuje plánování o vysoké prioritě."
+
+#~ msgid "We're in the group '%s', allowing real-time scheduling."
+#~ msgstr ""
+#~ "Nacházíme se ve skupině \"%s\", což umožňuje plánování v reálném čase."
+
+#~ msgid "PolicyKit grants us acquire-high-priority privilege."
+#~ msgstr "PolicyKit nám udělil oprávnění acquire-high-priority."
+
+#~ msgid "PolicyKit refuses acquire-high-priority privilege."
+#~ msgstr "PolicyKit nám neudělil oprávnění acquire-high-priority."
+
+#~ msgid "PolicyKit grants us acquire-real-time privilege."
+#~ msgstr "PolicyKit nám udělil oprávnění acquire-real-time."
+
+#~ msgid "PolicyKit refuses acquire-real-time privilege."
+#~ msgstr "PolicyKit nám neudělil oprávnění acquire-real-time."
+
+#~ msgid ""
+#~ "Called SUID root and real-time and/or high-priority scheduling was "
+#~ "requested in the configuration. However, we lack the necessary "
+#~ "privileges:\n"
+#~ "We are not in group '%s', PolicyKit refuse to grant us the requested "
+#~ "privileges and we have no increase RLIMIT_NICE/RLIMIT_RTPRIO resource "
+#~ "limits.\n"
+#~ "For enabling real-time/high-priority scheduling please acquire the "
+#~ "appropriate PolicyKit privileges, or become a member of '%s', or increase "
+#~ "the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."
+#~ msgstr ""
+#~ "Voláno SUID root a v nastavení bylo požádáno o plánování v reálném čase "
+#~ "či o vysoké prioritě. Schází nám ovšem potřebná oprávnění.\n"
+#~ "Nejsme ve skupině \"%s\", PolicyKit nám odmítá přidělit požadovaná "
+#~ "oprávnění a je nutné zvýšit omezení zdroje RLIMIT_NICE/RLIMIT_RTPRIO.\n"
+#~ "Plánování v reálném čase či o vysoké prioritě zapnete získáním "
+#~ "příslušných oprávnění PolicyKit, nebo tím, že se stanete členy \"%s\", "
+#~ "nebo uživateli zvýšíte omezení zdroje RLIMIT_NICE/RLIMIT_RTPRIO."
+
+#~ msgid ""
+#~ "High-priority scheduling enabled in configuration but not allowed by "
+#~ "policy."
+#~ msgstr ""
+#~ "Plánování o vysoké prioritě v konfiguraci zapnuto, ale nepovoleno "
+#~ "pravidly."
+
+#~ msgid "Successfully increased RLIMIT_RTPRIO"
+#~ msgstr "Úspěšně zvýšeno RLIMIT_RTPRIO"
+
+#~ msgid "RLIMIT_RTPRIO failed: %s"
+#~ msgstr "RLIMIT_RTPRIO selhalo: %s"
+
+#~ msgid "Giving up CAP_NICE"
+#~ msgstr "Vzdávání se CAP_NICE"
+
+#~ msgid ""
+#~ "Real-time scheduling enabled in configuration but not allowed by policy."
+#~ msgstr ""
+#~ "Plánování v reálném čase v konfiguraci zapnuto, ale nepovoleno pravidly."
+
+#~ msgid "Limited capabilities successfully to CAP_SYS_NICE."
+#~ msgstr "Schopnosti úspěšně omezeny na CAP_SYS_NICE."
+
+#~ msgid "time_new() failed.\n"
+#~ msgstr "time_new() selhalo.\n"
+
+#~ msgid "Output %s + Input %s"
+#~ msgstr "Výstup %s + vstup %s"
+
+#~ msgid "Stream successfully created\n"
+#~ msgstr "Proud úspěšně vytvořen\n"
+
+#~ msgid "Stream errror: %s\n"
+#~ msgstr "Chyba proudu: %s\n"
+
+#~ msgid "Connection established.\n"
+#~ msgstr "Spojení navázáno.\n"
+
+#~ msgid ""
+#~ "%s [options] [FILE]\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -v, --verbose                         Enable verbose operation\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -d, --device=DEVICE                   The name of the sink to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ "      --stream-name=NAME                How to call this stream on the "
+#~ "server\n"
+#~ "      --volume=VOLUME                   Specify the initial (linear) "
+#~ "volume in range 0...65536\n"
+#~ "      --channel-map=CHANNELMAP          Set the channel map to the use\n"
+#~ msgstr ""
+#~ "%s [přepínače] [SOUBOR]\n"
+#~ "\n"
+#~ "  -h, --help                            Zobrazí tuto nápovědu\n"
+#~ "      --version                         Zobrazí verzi\n"
+#~ "\n"
+#~ "  -v, --verbose                         Zapne nakládání s úrovní "
+#~ "podrobností\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   Název připojovaného serveru\n"
+#~ "  -d, --device=ZAŘÍZENÍ                 Název připojovaného cíle\n"
+#~ "  -n, --client-name=NÁZEV               Způsob volání tohoto klienta na "
+#~ "serveru\n"
+#~ "      --stream-name=NÁZEV               Způsob volání tohoto proudu na "
+#~ "serveru\n"
+#~ "      --volume=HLASITOST                Určí počáteční (lineární) "
+#~ "hlasitost v rozmezí 0...65536\n"
+#~ "      --channel-map=MAPAKANÁLŮ          Nastaví mapu kanálů určenou k "
+#~ "použití\n"
+
+#~ msgid ""
+#~ "paplay %s\n"
+#~ "Compiled with libpulse %s\n"
+#~ "Linked with libpulse %s\n"
+#~ msgstr ""
+#~ "paplay %s\n"
+#~ "Přeloženo s libpulse %s\n"
+#~ "Propojeno s libpulse %s\n"
+
+#~ msgid "Invalid channel map\n"
+#~ msgstr "Neplatná mapa kanálů\n"
+
+#~ msgid "Failed to open file '%s'\n"
+#~ msgstr "Selhalo otevření souboru \"%s\"\n"
+
+#~ msgid "Channel map doesn't match file.\n"
+#~ msgstr "Mapa kanálů neodpovídá souboru.\n"
+
+#~ msgid "Using sample spec '%s'\n"
+#~ msgstr "Používá se vzorkovací specifikace \"%s\"\n"
diff --git a/po/de.po b/po/de.po
new file mode 100644 (file)
index 0000000..e73b652
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,3210 @@
+# translation of pulseaudio.master-tx.de.po to
+# German translation of pulseaudio
+# Copyright (C) 2008 pulseaudio
+# This file is distributed under the same license as the pulseaudio package.
+#
+#
+# Fabian Affolter <fab@fedoraproject.org>, 2008-2009.
+# Micha Pietsch <barney@fedoraproject.org>, 2008, 2009.
+# Hedda Peters <hpeters@redhat.com>, 2009, 2012.
+# Mario Blättermann <mario.blaettermann@gmail.com>, 2016.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx.de\n"
+"Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
+"product=PulseAudio&keywords=I18N+L10N&component=misc\n"
+"POT-Creation-Date: 2016-09-24 21:48+0200\n"
+"PO-Revision-Date: 2016-09-29 12:52+0200\n"
+"Last-Translator: Mario Blättermann <mario.blaettermann@gmail.com>\n"
+"Language-Team: \n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 1.8.9\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [Optionen]\n"
+"\n"
+"BEFEHLE:\n"
+"  -h, --help                            zeigt diese Hilfe an\n"
+"      --version                         zeigt die Version an\n"
+"      --dump-conf                       zeigt die Standardkonfiguration an\n"
+"      --dump-modules                    zeigt die Liste verfügbarer Module "
+"an\n"
+"      --dump-resample-methods           zeigt die verfügbaren Resample-"
+"Methoden an\n"
+"      --cleanup-shm                     bereinigt veraltete Bereiche des "
+"gemeinsamen Speichers\n"
+"      --start                           startet den Hintergrunddienst, falls "
+"noch nicht geschehen\n"
+"  -k  --kill                            beendet den laufenden "
+"Hintergrunddienst\n"
+"      --check                           prüft auf laufende "
+"Hintergrunddienste\n"
+"                                          (gibt nur einen Exit-Code zurück)\n"
+"\n"
+"OPTIONEN:\n"
+"      --system[=BOOL]                   führt eine systemweite Instanz aus\n"
+"  -D, --daemonize[=BOOL]                wird nach Start zum "
+"Hintergrunddienst\n"
+"      --fail[=BOOL]                     beendet, wenn Start fehlschlägt\n"
+"      --high-priority[=BOOL]            versuch, die höchste Priorität zu "
+"setzen\n"
+"                                          (Nur verfügbar als root, wenn SUID "
+"oder\n"
+"                                          mit erhöhtem RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 versucht, Echtzeit-Scheduling zu "
+"aktivieren\n"
+"                                          (Nur verfügbar als root, wenn SUID "
+"oder\n"
+"                                          mit erhöhtem RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  verbietet (Ent-)laden durch Nutzer "
+"angeforderte\n"
+"                                          Module nach dem Start\n"
+"      --disallow-exit[=BOOL]            verbietet Beenden auf Anfrage des "
+"Nutzers\n"
+"      --exit-idle-time=SEK              beendet den Hintergrunddienst, wenn "
+"für\n"
+"                                          diese Zeit untätig\n"
+"      --scache-idle-time=SEK            entlädt untätige automatisch "
+"geladene\n"
+"                                          Samples nach dieser Zeit\n"
+"      --log-level[=GRAD]                erhöht oder setzt den Grad der "
+"Ausführlichkeit\n"
+"  -v  --verbose                         erhöht den Grad der Ausführlichkeit\n"
+"      --log-target={auto,syslog,stderr,file:PFAD,newfile:PFAD}\n"
+"                                          gibt das Protokoll-Ziel an\n"
+"      --log-meta[=BOOL]                 gibt den Speicherort des Codes in "
+"Protokollnachrichten an\n"
+"      --log-time[=BOOL]                 gibt den Zeitstempel in "
+"Protokollnachrichten an\n"
+"      --log-backtrace=FRAMES            Backtrace in Protokollnachrichten "
+"angeben\n"
+"  -p, --dl-search-path=PFAD             gibt den Suchpfad für dynamisch "
+"freigegebene\n"
+"                                          Objekte (Plugins) an\n"
+"      --resample-method=METHOD          nutzt diese Resampling-Methode "
+"(siehe --dump-resample-methods\n"
+"                                          für mögliche Werte)\n"
+"      --use-pid-file[=BOOL]             erstellt eine PID-Datei\n"
+"      --no-cpu-limit[=BOOL]             installiert keine CPU-Lastbegrenzung "
+"auf\n"
+"                                          unterstützten Systemen\n"
+"      --disable-shm[=BOOL]              deaktiviert die Unterstützung für "
+"Shared Memory\n"
+"      --enable-memfd[=BOOL]             aktiviert die Unterstützung für "
+"Shared Memory durch memfd\n"
+"\n"
+"STARTSKRIPT:\n"
+"  -L, --load=\"MODUL ARGUMENTE\"          lädt das Plugin-Modul mit diesen "
+"Parametern\n"
+"  -F, --file=DATEINAME                  führt dieses Skript aus\n"
+"  -C                                    öffnet nach dem Start auf laufendem "
+"TTY\n"
+"                                          eine Befehlszeile\n"
+"\n"
+"  -n                                    lädt kein Standardskript\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "--fail erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level erfordert Parameter für Grad der Protokollierung (entweder "
+"numerisch im Bereich 0..4 oder einen dieser: debug, info, notice, warn, "
+"error)."
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "--realtime erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Ungültiges Protokollziel: Benutzen Sie entweder »syslog«, »journal«, "
+"»stderr« oder »auto« oder einen gültigen Dateinamen »file:<Pfad>«, »newfile:"
+"<Pfad>«."
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Ungültiges Protokollziel: Benutzen Sie entweder »syslog«, »stderr« oder "
+"»auto« oder einen gültigen Dateinamen »file:<Pfad>«, »newfile:<Pfad>«."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "--realtime erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Ungültige Resample-Methode »%s«."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "--system erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm erfordert boolesche Variable"
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr "--enable-memfd erfordert boolesche Variable"
+
+#: ../src/daemon/daemon-conf.c:260
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Ungültiges Protokollziel »%s«."
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Ungültige Protokollierstufe »%s«."
+
+#: ../src/daemon/daemon-conf.c:290
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Ungültige Resample-Methode »%s«."
+
+#: ../src/daemon/daemon-conf.c:312
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Ungültiges rlimit »%s«."
+
+#: ../src/daemon/daemon-conf.c:332
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Ungültiges Sample-Format »%s«."
+
+#: ../src/daemon/daemon-conf.c:349 ../src/daemon/daemon-conf.c:366
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Ungültige Sample-Rate »%s«."
+
+#: ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Ungültige Sample-Kanäle »%s«."
+
+#: ../src/daemon/daemon-conf.c:406
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Ungültige Kanal-Zuordnung »%s«."
+
+#: ../src/daemon/daemon-conf.c:423
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Ungültige Anzahl von Fragmenten »%s«."
+
+#: ../src/daemon/daemon-conf.c:440
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Ungültige Fragmentgröße »%s«."
+
+#: ../src/daemon/daemon-conf.c:457
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Ungültige Prioritätsstufe »%s«."
+
+#: ../src/daemon/daemon-conf.c:500
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Ungültiger Servertyp »%s«."
+
+#: ../src/daemon/daemon-conf.c:615
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Öffnen der Konfigurationsdatei fehlgeschlagen : %s"
+
+#: ../src/daemon/daemon-conf.c:631
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Die angegebene Standard-Kanalzuordnung hat eine andere Anzahl von Kanälen "
+"als die angegebene Standard-Kanal-Anzahl."
+
+#: ../src/daemon/daemon-conf.c:718
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Aus Konfigurationsdatei wird gelesen: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Name: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Keine Modul-Informationen verfügbar\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Version: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Beschreibung: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Aufruf: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Einmalig laden: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "WARNUNG (DEPRECATION): %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Pfad: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Modul »%s« konnte nicht geladen werden: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Ursprünglicher lt_dlopen-Lader konnte nicht gefunden werden."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Neuer dlopen-Loader konnte nicht zugewiesen werden."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Hinzufügen von bind-now-loader fehlgeschlagen."
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Benutzer »%s« wurde nicht gefunden."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Gruppe »%s« wurde nicht gefunden."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr ""
+"Gruppenkennung von Benutzer »%s« und Gruppe »%s« stimmen nicht überein."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Benutzerverzeichnis von Benutzer »%s« ist nicht »%s«, wird ignoriert."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "»%s« konnte nicht erzeugt werden: %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Wechseln der Gruppen-Liste fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Wechseln der Gruppenkennung fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Wechseln der UID fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "System-Modus auf dieser Plattform nicht unterstützt."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Befehlszeile konnte nicht ausgewertet werden."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Der Systemmodus wurde für einen Nicht-Root-Benutzer verweigert. Es wird nur "
+"der Suchdienst für D-Bus-Server gestartet."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Hintergrundprozess konnte nicht abgebrochen werden: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Dieses Programm sollte ohne die Option --system nicht als Administrator "
+"ausgeführt werden."
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Root-Berechtigungen benötigt."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "--start wird für System-Instanzen nicht unterstützt."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+"Durch Benutzer eingerichteter Server auf %s, Start oder Autospawn wird "
+"verweigert."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Durch Benutzer eingerichteter Server auf %s, welcher lokal zu sein scheint. "
+"Weitere Prüfungen werden ausgeführt."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr "System-Modus aktiv, jedoch --disallow-exit nicht gesetzt."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr "System-Modus aktiv, jedoch --disallow-module-loading nicht gesetzt."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr "System-Modus aktiv, SHM-Modus gezwungenermaßen deaktiviert."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr "System-Modus aktiv, Exit-Idle-Time gezwungenermaßen deaktiviert."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "Reservieren von STDIO fehlgeschlagen."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:568
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Start des Hintergrunddienstes fehlgeschlagen."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Beziehen der Maschinen-ID fehlgeschlagen"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually "
+"do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"OK, nun wird PulseAudio im Systemmodus betrieben. Bitte überlegen Sie, ob "
+"Sie dies wirklich tun wollen.\n"
+"Für eine Erklärung, warum der Systemmodus eine schlechte Idee ist, lesen Sie "
+"bitte http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/"
+"WhatIsWrongWithSystemWide/."
+
+#: ../src/daemon/main.c:990
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() fehlgeschlagen."
+
+#: ../src/daemon/main.c:1022
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() fehlgeschlagen."
+
+#: ../src/daemon/main.c:1090
+msgid "Failed to initialize daemon."
+msgstr "Hintergrunddienst konnte nicht initialisiert werden."
+
+#: ../src/daemon/main.c:1095
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Hintergrunddienst verweigert Ausführung, da keine Module geladen."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio Soundsystem"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Das PulseAudio Soundsystem starten"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Eingabe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Eingabe über Docking-Station"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Mikrofon der Docking-Station"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Line-Eingang der Docking-Station"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Line-Eingang"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1710
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Vorderes Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Rückwärtiges Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Externes Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Internes Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Video"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Automatische Verstärkungsregelung"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Keine automatische Verstärkungsregelung"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Boost"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Kein Boost"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Verstärker"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Kein Verstärker"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Bassverstärkung"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Keine Bassverstärkung"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1717
+msgid "Speaker"
+msgstr "Lautsprecher"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Kopfhörer"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Analoger Eingang"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Mikrofon der Docking-Station"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Mikrofon am Kopfhörer"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Analoge Ausgabe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "LFE mit separater Mono-Ausgabe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Line-Ausgang"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Analoge Mono-Ausgabe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Lautsprecher"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Digitalausgang (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Digitaleingang (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digitale Durchschleifung (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Mehrkanaleingang"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Mehrkanalausgang"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Analog Mono"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Analog Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Mehrkanal"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Analog Surround 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Analog Surround 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Analog Surround 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Analog Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Analog Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Analog Surround 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Analog Surround 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Analog Surround 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Analog Surround 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Analog Surround 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Analog Surround 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Digital Stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digitale Durchschleifung (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digital Surround 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digital Surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Digital Surround 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Digital Stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digital Surround 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4151
+msgid "Analog Mono Duplex"
+msgstr "Analog Mono Duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Stereo Duplex"
+msgstr "Analog Stereo Duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Digital Stereo Duplex (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Multichannel Duplex"
+msgstr "Mehrkanal-Duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2319
+#: ../src/modules/bluetooth/module-bluez5-device.c:1965
+msgid "Off"
+msgstr "Aus"
+
+#: ../src/modules/alsa/alsa-mixer.c:4254
+#, c-format
+msgid "%s Output"
+msgstr "%s-Ausgabe"
+
+#: ../src/modules/alsa/alsa-mixer.c:4262
+#, c-format
+msgid "%s Input"
+msgstr "%s-Eingabe"
+
+#: ../src/modules/alsa/alsa-sink.c:570
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA weckte uns auf, um neue Daten auf das Gerät zu schreiben, doch es gab "
+"nichts zum Schreiben!\n"
+"Dies ist höchstwahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden "
+"Sie diesen Fehler den ALSA-Entwicklern.\n"
+"Wir wurden durch das POLLOUT-Set geweckt, allerdings lieferte ein "
+"anschließender snd_pcm_avail() den Wert 0 oder einen anderen Wert < "
+"min_avail."
+
+#: ../src/modules/alsa/alsa-sink.c:747
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA weckte uns auf, um neue Daten auf das Gerät zu schreiben, doch es gab "
+"nichts zum Schreiben!\n"
+"Dies ist höchstwahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden "
+"Sie diesen Fehler den ALSA-Entwicklern.\n"
+"Wir wurden durch das POLLOUT-Set geweckt, allerdings lieferte ein "
+"anschließender snd_pcm_avail() den Wert 0 oder einen anderen Wert < "
+"min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA weckte uns auf, um neue Daten vom Gerät zu lesen, doch es gab nichts "
+"zum Lesen!\n"
+"Dies ist höchstwahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden "
+"Sie diesen Fehler den ALSA-Entwicklern.\n"
+"Wir wurden durch das POLLIN-Set geweckt, allerdings lieferte ein "
+"anschließender snd_pcm_avail() den Wert 0 oder einen anderen Wert < "
+"min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA weckte uns auf, um neue Daten vom Gerät zu lesen, doch es gab nichts "
+"zum Lesen!\n"
+"Dies ist höchstwahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden "
+"Sie diesen Fehler den ALSA-Entwicklern.\n"
+"Wir wurden durch das POLLIN-Set geweckt, allerdings lieferte ein "
+"anschließender snd_pcm_avail() den Wert 0 oder einen anderen Wert < "
+"min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1166 ../src/modules/alsa/alsa-util.c:1241
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() gibt einen Wert zurück, der außerordentlich groß ist: %lu "
+"bytes (%lu ms).\n"
+"Dies ist wahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden Sie "
+"dieses Problem den ALSA-Entwicklern."
+
+#: ../src/modules/alsa/alsa-util.c:1216
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() gibt einen Wert zurück, der außerordentlich groß ist: %li "
+"Bytes (%s%lu ms).\n"
+"Dies ist wahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden Sie "
+"dieses Problem den ALSA-Entwicklern."
+
+#: ../src/modules/alsa/alsa-util.c:1257
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() gibt einen ungewöhnlichen Wert zurück: Verzögerung %lu "
+"ist kleiner als das verfügbare %lu.\n"
+"Dies ist wahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden Sie "
+"dieses Problem den ALSA-Entwicklern."
+
+#: ../src/modules/alsa/alsa-util.c:1300
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() gibt einen Wert zurück, der außerordentlich groß ist: "
+"%lu Bytes (%lu ms).\n"
+"Dies ist wahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden Sie "
+"dieses Problem den ALSA-Entwicklern."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2089
+#: ../src/modules/bluetooth/module-bluez5-device.c:1700
+msgid "Headset"
+msgstr "Headset"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1705
+msgid "Handsfree"
+msgstr "Freisprecheinrichtung"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1723
+msgid "Headphone"
+msgstr "Kopfhörer"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1728
+msgid "Portable"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1733
+msgid "Car"
+msgstr "Auto"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1738
+msgid "HiFi"
+msgstr "HiFi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1743
+msgid "Phone"
+msgstr "Telefon"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2137
+#: ../src/modules/bluetooth/module-bluez5-device.c:1695
+#: ../src/modules/bluetooth/module-bluez5-device.c:1711
+#: ../src/modules/bluetooth/module-bluez5-device.c:1749
+msgid "Bluetooth Output"
+msgstr "Bluetooth-Ausgabe"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2140
+#: ../src/modules/bluetooth/module-bluez5-device.c:1694
+#: ../src/modules/bluetooth/module-bluez5-device.c:1716
+#: ../src/modules/bluetooth/module-bluez5-device.c:1722
+#: ../src/modules/bluetooth/module-bluez5-device.c:1748
+msgid "Bluetooth Input"
+msgstr "Bluetooth-Eingabe"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2181
+msgid "High Fidelity Playback (A2DP)"
+msgstr "High Fidelity Playback (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2193
+msgid "High Fidelity Capture (A2DP)"
+msgstr "High Fidelity Capture (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2205
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telephony Duplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2218
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1790
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "High Fidelity Playback (A2DP-Ziel)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1802
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "High Fidelity Capture (A2DP-Quelle)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1814
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1827
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr ""
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<Name der Quelle> source_properties=<Eigenschaften der Quelle> "
+"source_master=<Name der zu filternden Quelle> sink_name=<Name des Ziels> "
+"sink_properties=<Eigenschaften des Ziels> sink_master=<Name des zu "
+"filternden Ziels> adjust_time=<Intervall zur Neujustierung der Rate in "
+"Sekunden> adjust_threshold=<maximale Abweichung zur Neujustierung in "
+"Millisekunden> format=<Sample-Format> rate=<Samplerate> channels=<Anzahl der "
+"Kanäle> channel_map=<Kanalzuordnung> aec_method=<zu verwendende "
+"Implementation> aec_args=<Parameter für die AEC-Engine> "
+"save_aec=<Speicherung der AEC-Daten in /tmp> autoloaded=<setzen, wenn dieses "
+"Modul automatisch geladen wird> use_volume_sharing=<yes oder no> "
+"use_master_format=<yes oder no> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "Ein"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Dummy-Ausgabe"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Hält stets ein Ziel geladen, selbst wenn dies ein Null-Ziel ist"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Allzweck-Equalizer"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<Name des Ziels> sink_properties=<Eigenschaften des Ziels> "
+"sink_master=<Ziel, zu dem verbunden werden soll> format=<Sample-Format> "
+"rate=<Samplerate> channels=<Anzahl der Kanäle> channel_map=<Kanalzuordnung> "
+"autoloaded=<setzen, wenn dieses Modul automatisch geladen wird> "
+"use_volume_sharing=<yes oder no> "
+
+#: ../src/modules/module-filter-apply.c:47
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<ungenutzte Filter automatisch entladen?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Virtuelles LADSPA-Ziel"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<Name des Ziels> sink_properties=<Eigenschaften des Ziels> "
+"master=<Name des zu filternden Ziels> format=<Sample-Format> "
+"rate=<Samplerate> channels=<Anzahl der Kanäle> "
+"channel_map=<Eingabekanalzuordnung> plugin=<Name des LADSPA-Plugins> "
+"label=<Bezeichnung des LADSPA-Plugins> control=<durch Kommata getrennte "
+"Liste von Eingabekontrollwerten> input_ladspaport_map=<durch Kommata "
+"getrennte Liste von Namen der LADSPA-Eingabeports> "
+"output_ladspaport_map=<durch Kommata getrennte Liste von Namen der LADSPA-"
+"Ausgabeports> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Getaktetes NULL-Ziel"
+
+#: ../src/modules/module-null-sink.c:280
+msgid "Null Output"
+msgstr "Null-Ausgabe"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Ausgabegeräte"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Eingabegeräte"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Audio auf @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Tunnel für %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:517
+#: ../src/modules/module-tunnel-source-new.c:516
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Tunnel zu %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Virtuelles Surround-Ziel"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/"
+"left_hrir.wav "
+msgstr ""
+"sink_name=<Name des Ziels> sink_properties=<Eigenschaften des Ziels> "
+"master=<Name des zu filternden Ziels> format=<Abtastformat> "
+"rate=<Abtastrate> channels=<Anzahl der Kanäle> channel_map=<Kanalzuordnung> "
+"use_volume_sharing=<yes oder no> force_flat_volume=<yes oder no> hrir=/Pfad/"
+"zu/left_hrir.wav "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio Soundserver"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Vorne Mitte"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Vorne links"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Vorne rechts"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Hinten Mitte"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Hinten links"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Hinten rechts"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Subwoofer"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Vorne links der Mitte"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Vorne rechts der Mitte"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Seite links"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Seite rechts"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Zusatz 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Zusatz 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Zusatz 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Zusatz 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Zusatz 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Zusatz 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Zusatz 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Zusatz 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Zusatz 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Zusatz 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Zusatz 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Zusatz 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Zusatz 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Zusatz 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Zusatz 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Zusatz 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Zusatz 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Zusatz 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Zusatz 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Zusatz 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Zusatz 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Zusatz 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Zusatz 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Zusatz 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Zusatz 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Zusatz 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Zusatz 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Zusatz 26"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Zusatz 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Zusatz 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Zusatz 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Zusatz 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Oben Mitte"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Oben vorne Mitte"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Oben vorne Links"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Oben vorne Rechts"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Oben hinten Mitte"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Oben hinten links"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Oben hinten rechts"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:174 ../src/pulse/volume.c:294
+#: ../src/pulse/volume.c:320 ../src/pulse/volume.c:340
+#: ../src/pulse/volume.c:372 ../src/pulse/volume.c:412
+#: ../src/pulse/volume.c:431
+msgid "(invalid)"
+msgstr "(ungültig)"
+
+#: ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() fehlgeschlagen"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() hat »wahr« zurückgegeben"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Auswerten der Cookie-Daten fehlgeschlagen"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Nachricht für unbekannte Erweiterung »%s« erhalten"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "Eingabe"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "Ausgabe"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "Bidirektional"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "ungültig"
+
+#: ../src/pulsecore/core-util.c:1837
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"XDG_RUNTIME_DIR (%s) gehört uns (Benutzerkennung %d) nicht, sondern der "
+"Benutzerkennung %d! Dies kann beispielsweise passieren, wenn Sie sich über "
+"das native Protokoll als Root-Benutzer mit einem ohne Root-Rechte "
+"betriebenen PulseAudio-Server verbinden wollen. Sie sollten dies nicht tun."
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "ja"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "nein"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Fehler beim Zugriff auf Autostart-Sperre."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Zieldatei »%s« konnte nicht geöffnet werden."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Es wurde versucht, die Zieldateien »%s«, »%s.1«, »%s.2« … »%s.%d« zu öffnen, "
+"was jedes Mal fehlschlug."
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Ungültiges Protokollziel."
+
+#: ../src/pulsecore/sink.c:3459
+msgid "Built-in Audio"
+msgstr "Internes Audio"
+
+#: ../src/pulsecore/sink.c:3464
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Zugriff verweigert"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Unbekannter Befehl"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Ungültiger Parameter"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "Entität existiert bereits"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Entität nicht vorhanden"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Verbindung verweigert"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Protokollfehler"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Zeitüberschreitung"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Kein Legitimierungsschlüssel"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Interner Fehler"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Verbindung beendet"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Entität terminiert"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Ungültiger Server"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Modulinitialisierung fehlgeschlagen"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Ungültiger Zustand"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Keine Daten"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Inkompatible Protokollversion"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Zu groß"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Nicht unterstützt"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Unbekannter Fehlercode"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Erweiterung nicht vorhanden"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Veraltete Funktion"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Fehlende Implementation"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Client geteilt"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Eingabe/Ausgabe-Fehler"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Gerät oder Ressource ist belegt"
+
+#: ../src/pulse/sample.c:176
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:188
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:190
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:192
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:194
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:117
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Entleeren des Streams fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:122
+msgid "Playback stream drained."
+msgstr "Wiedergabe-Stream entleert."
+
+#: ../src/utils/pacat.c:133
+msgid "Draining connection to server."
+msgstr "Verbindung zu Server entleert."
+
+#: ../src/utils/pacat.c:146
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:169
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:210
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_write() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:260 ../src/utils/pacat.c:290
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:340
+msgid "Stream successfully created."
+msgstr "Stream wurde erfolgreich erstellt."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:347
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Pufferdaten: maxlenght=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:350
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Pufferdaten: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:354
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Abtastwert-Angabe »%s« wird benutzt, Kanalzuordnung »%s«."
+
+#: ../src/utils/pacat.c:358
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Verbunden mit Gerät %s (Index: %u, ausgesetzt: %s)."
+
+#: ../src/utils/pacat.c:368
+#, c-format
+msgid "Stream error: %s"
+msgstr "Stream-Fehler: %s"
+
+#: ../src/utils/pacat.c:378
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Stream-Gerät ausgesetzt.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Stream-Gerät reaktiviert.%s"
+
+#: ../src/utils/pacat.c:388
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Stream leergelaufen.%s"
+
+#: ../src/utils/pacat.c:395
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Stream überlaufen.%s "
+
+#: ../src/utils/pacat.c:402
+#, c-format
+msgid "Stream started.%s"
+msgstr "Stream gestartet: %s"
+
+#: ../src/utils/pacat.c:409
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Stream an Gerät %s übergeben (%u, %sausgesetzt).%s"
+
+#: ../src/utils/pacat.c:409
+msgid "not "
+msgstr "nicht "
+
+#: ../src/utils/pacat.c:416
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Stream-Zwischenspeicher-Attribute geändert.%s"
+
+# Was ist Corking?
+# https://lists.freedesktop.org/archives/pulseaudio-discuss/2009-December/005767.html
+#: ../src/utils/pacat.c:431
+msgid "Cork request stack is empty: corking stream"
+msgstr "Stapel der Unterbrechungsanfragen ist leer: Stream wird unterbrochen"
+
+#: ../src/utils/pacat.c:437
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+"Stapel der Unterbrechungsanfragen ist leer: Stream wird wiederaufgenommen"
+
+#: ../src/utils/pacat.c:441
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr ""
+"Warnung: Es wurden mehr Anfragen zur Wiederaufnahme des Streams als zu "
+"dessen Unterbrechung empfangen."
+
+#: ../src/utils/pacat.c:466
+#, c-format
+msgid "Connection established.%s"
+msgstr "Verbindung hergestellt.%s"
+
+#: ../src/utils/pacat.c:469
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:507
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:513
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Einrichten der Überwachung des Streams fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:517
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:530 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Verbindungsfehler: %s"
+
+#: ../src/utils/pacat.c:563
+msgid "Got EOF."
+msgstr "EOF empfangen."
+
+#: ../src/utils/pacat.c:600
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:621
+msgid "Got signal, exiting."
+msgstr "Signal empfangen, wird beendet."
+
+#: ../src/utils/pacat.c:635
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Erhalten der Latenz fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Zeit: %0.3f sec; Latenz: %0.0f usec."
+
+#: ../src/utils/pacat.c:661
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:671
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [Optionen]\n"
+"%s\n"
+"\n"
+"  -h, --help                            zeigt diese Hilfe an\n"
+"      --version                         zeigt die Version an\n"
+"\n"
+"  -r, --record                          baut eine Aufnahme-Verbindung auf\n"
+"  -p, --playback                        baut eine Wiedergabe-Verbindung auf\n"
+"\n"
+"  -v, --verbose                         zeigt ausführliche Meldungen an\n"
+"\n"
+"  -s, --server=SERVER                   Name des zu verbindenden Servers\n"
+"  -d, --device=GERÄT                    Name des/der zu verbindenden Ziels/"
+"Quelle\n"
+"  -n, --client-name=NAME                Name des Clients auf dem Server\n"
+"      --stream-name=NAME                Name des Streams auf dem Server\n"
+"      --volume=VOLUME                   gibt die initiale (lineare) "
+"Lautstärke zwischen 0...65536 an\n"
+"      --rate=ABTASTRATE                 Abtastate in Hz (Standard 44100)\n"
+"      --format=ABTASTFORMAT             gibt ein Abtastformat von s16le, "
+"s16be, u8, float32le,\n"
+"                                          float32be, ulaw, alaw, s32le, "
+"s32be, s24le, s24be,\n"
+"                                          s24-32le, s24-32be an (Standard "
+"ist s16ne)\n"
+"      --channels=CHANNELS               Anzahl Kanäle, 1 für Mono, 2 für "
+"Stereo\n"
+"                                          (Standard ist 2)\n"
+"      --channel-map=KANALZUORDNUNG      nutzt diese geänderte "
+"Kanalzuordnung\n"
+"      --fix-format                      nutzt das Abtastformat des mit dem "
+"Ziel\n"
+"                                          verbundenen Streams.\n"
+"      --fix-rate                        nutzt die Abtastrate des mit dem "
+"Ziel\n"
+"                                          verbundenen Streams.\n"
+"      --fix-channels                    nutzt die Anzahl und Zuordnung der "
+"Kanäle\n"
+"                                          des mit dem Ziel verbundenen "
+"Streams.\n"
+"      --no-remix                        mischt Kanäle nicht up-/down.\n"
+"      --no-remap                        ordnet Kanäle nach Index statt Name "
+"zu.\n"
+"      --latency=BYTES                   fordert diese Latenz in Bytes an.\n"
+"      --process-time=BYTES              fordert diese Prozesszeit pro "
+"Anfrage in Bytes an.\n"
+"      --latency-msec=MSEC               fordert diese Latenz in "
+"Millisekunden an.\n"
+"      --process-time-msec=MSEC          fordert diese Prozesszeit pro "
+"Anfrage in Millisekunden an.\n"
+"      --property=EIGENSCHAFT=WERT       setzt die angegebene Eigenschaft auf "
+"den angegebenen Wert.\n"
+"      --raw                             nimmt PCM-Rohdaten auf oder spielt "
+"diese ab.\n"
+"      --passthrough                     gibt die Daten unverändert weiter.\n"
+"      --file-format[=FFORMAT]           nimmt formatierte PCM-Daten auf oder "
+"spielt diese ab.\n"
+"      --list-file-formats               listet verfügbare Dateiformate auf.\n"
+"      --monitor-stream=INDEX            zeichnet von der Ziel-Eingabe mit "
+"dem Index INDEX auf.\n"
+"\n"
+
+#: ../src/utils/pacat.c:788
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr "Kodierte Audio-Dateien auf einem PulseAudio-Soundserver wiedergeben."
+
+#: ../src/utils/pacat.c:792
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr ""
+"Audio-Daten von einem PulseAudio-Soundserver aufnehmen und in eine Datei "
+"schreiben."
+
+#: ../src/utils/pacat.c:796
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+"Audio-Daten von einem PulseAudio-Soundserver aufnehmen und in die "
+"Standardausgabe oder eine angegebene Datei schreiben."
+
+#: ../src/utils/pacat.c:800
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+"Audio-Daten aus der Standardeingabe oder der angegebenen Datei auf einem "
+"PulseAudio-Soundserver wiedergeben."
+
+#: ../src/utils/pacat.c:814
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Kompiliert mit libpulse %s\n"
+"Gelinkt mit libpulse %s\n"
+
+#: ../src/utils/pacat.c:847 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Ungültiger Client-Name »%s«"
+
+#: ../src/utils/pacat.c:862
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Ungültiger Stream-Name »%s«"
+
+#: ../src/utils/pacat.c:899
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Ungültige Kanal-Zuweisung »%s«"
+
+#: ../src/utils/pacat.c:928 ../src/utils/pacat.c:942
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Ungültige Latenz-Angaben »%s«"
+
+#: ../src/utils/pacat.c:935 ../src/utils/pacat.c:949
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Ungültige Prozesszeit-Angaben »%s«"
+
+#: ../src/utils/pacat.c:961
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Ungültige Eigenschaft »%s«"
+
+#: ../src/utils/pacat.c:980
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Unbekanntes Dateiformat %s."
+
+#: ../src/utils/pacat.c:995
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Der Parameter für --monitor-stream konnte nicht ausgewertet werden."
+
+#: ../src/utils/pacat.c:1006
+msgid "Invalid sample specification"
+msgstr "Ungültige Abtastwert-Angaben"
+
+#: ../src/utils/pacat.c:1016
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1028
+msgid "Too many arguments."
+msgstr "Zu viele Argumente."
+
+#: ../src/utils/pacat.c:1039
+msgid "Failed to generate sample specification for file."
+msgstr "Beziehen der Abtastwert-Informationen für die Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1065
+msgid "Failed to open audio file."
+msgstr "Öffnen der Audio-Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1071
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr "Warnung: Beziehen der Abtastwert-Angabe aus Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1074 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Beziehen der Abtastwert-Informationen der Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1083
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Warnung: Bestimmung der Kanalzuordnung aus Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1094
+msgid "Channel map doesn't match sample specification"
+msgstr "Kanalzuordnung entspricht nicht Abtastwert-Angaben"
+
+#: ../src/utils/pacat.c:1105
+msgid "Warning: failed to write channel map to file."
+msgstr "Warnung: Schreiben der Kanalzuordnung in Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1120
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"%s-Stream mit Abtastwert-Angabe »%s« und Kanalzuordnung »%s« wird geöffnet."
+
+#: ../src/utils/pacat.c:1121
+msgid "recording"
+msgstr "aufnehmen"
+
+#: ../src/utils/pacat.c:1121
+msgid "playback"
+msgstr "abspielen"
+
+#: ../src/utils/pacat.c:1145
+msgid "Failed to set media name."
+msgstr "Medienname konnte nicht gesetzt werden."
+
+#: ../src/utils/pacat.c:1152 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() fehlgeschlagen"
+
+#: ../src/utils/pacat.c:1175
+msgid "io_new() failed."
+msgstr "io_new() fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1182 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_new() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:1196
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1203 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() fehlgeschlagen."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "NAME [ARGUMENTE …]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "NAME|#N"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "NAME"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "NAME|#N LAUTSTÄRKE"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N LAUTSTÄRKE"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "NAME|#N 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "NAME|#N SCHLÜSSEL=WERT"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#N SCHLÜSSEL=WERT"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "NAME ZIEL|#N"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "NAME DATEINAME"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "PFADNAME"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "DATEINAME ZIEL|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#N ZIEL|QUELLE"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "KARTENPROFIL"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "NAME|#N PORT"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "KARTENNAME|KARTEN-#N PORT POSITION"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "ZIEL"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "NUMERISCHE STUFE"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "FRAMES"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            zeigt diese Hilfe an\n"
+"      --version                         zeigt die Version an\n"
+"Wenn kein Befehl angegeben ist, startet pacmd im interaktiven Modus.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Kompiliert mit libpulse %s\n"
+"Gelinkt mit libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "Es läuft kein PulseAudio-Dienst oder nicht als Sitzungsdienst."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Beenden des PulseAudio-Dienstes fehlgeschlagen."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Dienst antwortet nicht."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Beziehen der Statistik fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Momentane Nutzung: %u Blöcke mit insgesamt %s Bytes.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Während gesamter Laufzeit: %u Blöcke mit insgesamt %s Bytes.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Abtast-Pufferspeichergröße: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Beziehen der Server-Information fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Server-Zeichenkette: %s\n"
+"Bibliotheks-Protokollversion: %u\n"
+"Server-Protokollversion: %u\n"
+"ist lokal: %s\n"
+"Client-Index: %u\n"
+"Tile-Größe: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Name des Benutzers: %s\n"
+"Rechnername: %s\n"
+"Name des Servers: %s\n"
+"Version des Servers: %s\n"
+"Standard-Abtastwert-Angabe: %s\n"
+"Standard-Kanal-Zuordnung: %s\n"
+"Standard-Ziel: %s\n"
+"Standard-Quelle: %s\n"
+"Cookie: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Erhalten der Ziel-Informationen fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Ziel #%u\n"
+"\tStatus: %s\n"
+"\tName: %s\n"
+"\tBeschreibung: %s\n"
+"\tTreiber: %s\n"
+"\tAbtastwert-Angabe: %s\n"
+"\tKanalzuordnung: %s\n"
+"\tBesitzer-Modul: %u\n"
+"\tStumm: %s\n"
+"\tLautstärke: %s\n"
+"\t        Verteilung %0.2f\n"
+"\tBasis-Lautstärke: %s\n"
+"\tQuellen-Monitor: %s\n"
+"\tLatenz: %0.0f usec, eingestellt %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPorts:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tAktiver Port: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tFormate:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Beziehen der Quellen-Informationen fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Quelle #%u\n"
+"\tStatus: %s\n"
+"\tName: %s\n"
+"\tBeschreibung: %s\n"
+"\tTreiber: %s\n"
+"\tAbtastwert-Angabe: %s\n"
+"\tKanalzuordnung: %s\n"
+"\tBesitzer-Modul: %u\n"
+"\tStumm: %s\n"
+"\tLautstärke: %s\n"
+"\t        Verteilung %0.2f\n"
+"\tBasis-Lautstärke: %s\n"
+"\tZiel-Monitor: %s\n"
+"\tLatenz: %0.0f usec, eingestellt %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "k. A."
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Beziehen der Modul-Information fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modul #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tNutzungszähler: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Beziehen der Client-Information fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Client #%u\n"
+"\tTreiber: %s\n"
+"\tOwner-Modul: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Beziehen der Karten-Information fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Karte #%u\n"
+"\tName: %s\n"
+"\tTreiber: %s\n"
+"\tOwner-Modul: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfile:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr "\t\t%s: %s (Ziele: %u, Quellen: %u, Priorität: %u, verfügbar: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tAktives Profil: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tEigenschaften:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tTeil der/des Profil(s): %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Erhalten der Ziel-Eingabe-Informationen fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Ziel-Eingabe #%u\n"
+"\tTreiber: %s\n"
+"\tBesitzer-Modul: %s\n"
+"\tClient: %s\n"
+"\tZiel: %u\n"
+"\tAbtastwert-Angabe: %s\n"
+"\tKanalzuordnung: %s\n"
+"\tFormat: %s\n"
+"\tUnterbrochen: %s\n"
+"\tStumm: %s\n"
+"\tLautstärke: %s\n"
+"\t        Verteilung %0.2f\n"
+"\tPufferlatenz: %0.0f usec\n"
+"\tZiel-Latenz: %0.0f usec\n"
+"\tResample-Methode: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Informationen über Quell-Ausgabe konnten nicht erhalten werden: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Quellen-Ausgabe #%u\n"
+"\tTreiber: %s\n"
+"\tBesitzer-Modul: %s\n"
+"\tClient: %s\n"
+"\tQuelle: %u\n"
+"\tAbtastwert-Angabe: %s\n"
+"\tKanalzuordnung: %s\n"
+"\tFormat: %s\n"
+"\tUnterbrochen: %s\n"
+"\tStumm: %s\n"
+"\tLautstärke: %s\n"
+"\t        Verteilung %0.2f\n"
+"\tPufferlatenz: %0.0f usec\n"
+"\tQuellen-Latenz: %0.0f usec\n"
+"\tResampling-Methode: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Beziehen der Abtastwert-Informationen fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tAbtastwert-Angabe: %s\n"
+"\tKanalzuordnung: %s\n"
+"\tLautstärke: %s\n"
+"\t        Verteilung %0.2f\n"
+"\tDauer: %0.1fs\n"
+"\tGröße: %s\n"
+"\tLazy: %s\n"
+"\tDateiname: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Modul konnte nicht entladen werden: Modul %s ist nicht geladen"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Lautstärke konnte nicht gesetzt werden: Sie haben versucht, die Lautstärke "
+"für %d Kanäle einzustellen, aber es werden nur %d Kanäle unterstützt.\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Format konnte nicht gesetzt werden: Formatzeichenkette %s ist ungültig"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Hochladen des Sample fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Dateiende ist zu früh aufgetreten"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "neu"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "ändern"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "entfernen"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "unbekannt"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "Ziel"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "Quelle"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "Ziel-Eingabe"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "Quellen-Eingabe"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "Modul"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "Client"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "Sample-Pufferspeicher"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "Server"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "Karte"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Ereignis »%s« auf %s #%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT empfangen, wird beendet."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Ungültige Angabe der Lautstärke"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Lautstärke ist außerhalb des einstellbaren Bereichs.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "Ungültige Anzahl in der Lautstärkeangabe.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "Inkonsistente Angabe der Lautstärke.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[Optionen]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[TYP]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "DATEINAME [NAME]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "NAME [ZIEL]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "NAME|#N LAUTSTÄRKE [LAUTSTÄRKE …]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#N LAUTSTÄRKE [LAUTSTÄRKE …]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "NAME|#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "#N FORMATE"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Die speziellen Bezeichnungen @DEFAULT_SINK@, @DEFAULT_SOURCE@ und "
+"@DEFAULT_MONITOR@\n"
+"können zur Angabe des Standard-Ziels, der Standard-Quelle und der Standard-"
+"Überwachung verwendet werden.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            zeigt diese Hilfe an\n"
+"      --version                         zeigt die Version an\n"
+"\n"
+"  -s, --server=SERVER                   gibt den Namen des Servers an, mit "
+"dem\n"
+"                                          verbunden werden soll\n"
+"  -n, --client-name=NAME                gibt den Namen dieses Clients auf "
+"dem Server an\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Kompiliert mit libpulse %s\n"
+"Gelinkt mit libpulse %s\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Geben Sie nichts oder eines der Folgenden an: %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Geben Sie eine zu öffnende Sample-Datei an"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Öffnen der Audio-Datei fehlgeschlagen."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Warnung: Beziehen der Abtastwert-Angabe aus Datei fehlgeschlagen."
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Sie müssen eine abzuspielende Sample-Datei angeben"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Sie müssen eine zu löschende Sample-Datei angeben"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "Sie müssen einen Ziel-Eingabe-Indexwert und ein Ziel angeben"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr ""
+"Sie müssen eine Indexwert für die Quell-Ausgabe und eine Quelle angeben"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Sie müssen einen Modulnamen angeben und Argumente übergeben."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "Sie müssen einen Indexwert für ein Modul oder einen Namen angeben"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Sie dürfen nicht mehrere Ziele angeben. Sie müssen zumindest eine boolesche "
+"Variable übergeben."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Ungültige Aussetzungs-Angaben."
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Sie dürfen nicht mehrere Quellen angeben. Sie müssen zumindest eine "
+"boolesche Variable übergeben."
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Sie müssen einen Karten-Namen/Indexwert und einen Profilnamen angeben."
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Sie müssen einen Ziel-Namen/-Indexwert und einen Portnamen angeben."
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "Sie müssen einen Ziel-Namen angeben."
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "Sie müssen einen Quellennamen/-Indexwert und einen Portnamen angeben."
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Sie müssen einen Quellennamen angeben."
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Sie müssen einen Ziel-Namen/-Indexwert und eine Lautstärke angeben."
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Sie müssen einen Quellennamen/-Indexwert und einen Portnamen angeben"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "Sie müssen einen Ziel-Eingabe-Indexwert und eine Lautstärke angeben."
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "Ungültiger Ziel-Eingabe-Index"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr ""
+"Sie müssen eine Indexwert für die Quell-Ausgabe und eine Lautstärke angeben."
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Ungültiger Quellen-Ausgabe-Index"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Sie müssen einen Ziel-Namen/-Indexwert und eine Aktion für die "
+"Stummschaltung angeben (0, 1, oder »toggle«)."
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Ungültige Angaben zur Stummschaltung"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Sie müssen einen Quellennamen/-Indexwert und eine Aktion für die "
+"Stummschaltung angeben (0, 1, oder »toggle«)."
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Sie müssen einen Ziel-Eingabe-Index und eine Aktion für die Stummschaltung "
+"angeben (0, 1, oder »toggle«)."
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "Ungültige Ziel-Eingabe-Index-Angaben"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+"Sie müssen einen Quellen-Ausgabe-Index und eine Aktion für die "
+"Stummschaltung angeben (0, 1, oder »toggle«)."
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Ungültige Quellen-Ausgabe-Index-Angaben"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Sie müssen einen Ziel-Indexwert und eine durch Kommata getrennte Liste der "
+"unterstützten Formate angeben"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr ""
+"Sie müssen einen Karten-Namen/Indexwert, einen Portnamen und einen "
+"Latenzversatz angeben"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Latenzversatz konnte nicht ausgewertet werden"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Kein gültiger Befehl angegeben."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Wiederaufnahme fehlgeschlagen: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Aussetzen fehlgeschlagen: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "WARNUNG: Sound-Server läuft nicht lokal, nicht ausgesetzt.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Verbindungsfehler: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT empfangen, wird beendet.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "WARNUNG: Kind-Prozess durch Signal %u beendet\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [Optionen] … \n"
+"\n"
+"  -h, --help                            zeigt diese Hilfe an\n"
+"      --version                         zeigt die Version an\n"
+"  -s, --server=SERVER                   gibt den Namen des Zielservers an\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"kompiliert mit libpulse %s\n"
+"Gelinkt mit libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() fehlgeschlagen.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() fehlgeschlagen.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() fehlgeschlagen.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D Anzeige] [-S Server] [-O Ziel] [-I Quelle] [-c Datei]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    zeigt aktuell mit X11-Anzeige verbundene PulseAudio-Daten an "
+"(Standard)\n"
+" -e    exportiert lokale PulseAudio-Daten an X11-Anzeige\n"
+" -i    importiert PulseAudio-Daten von X11-Anzeige in lokale "
+"Umgebungsvariablen und Cookie-Datei.\n"
+" -r    löscht PulseAudio-Daten von X11-Anzeige\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Auswerten der Befehlszeile fehlgeschlagen.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Server: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Quelle: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Ziel: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Auswerten der Cookie-Daten fehlgeschlagen.\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Speichern der Cookie-Daten fehlgeschlagen\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Beziehen des FQDN fehlgeschlagen.\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Laden der Cookie-Daten fehlgeschlagen\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Noch nicht implementiert.\n"
diff --git a/po/de_CH.po b/po/de_CH.po
new file mode 100644 (file)
index 0000000..6424bd5
--- /dev/null
@@ -0,0 +1,3254 @@
+# German translation of pulseaudio
+# Copyright (C) 2008 pulseaudio
+# This file is distributed under the same license as the pulseaudio package.
+#
+# Micha Pietsch <barney@fedoraproject.org>, 2008, 2009.
+# Fabian Affolter <fab@fedoraproject.org>, 2008-2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:53+0000\n"
+"Last-Translator: Fabian Affolter <fab@fedoraproject.org>\n"
+"Language-Team: German <fedora-trans-de@redhat.com>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Poedit-Language: German\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() gibt einen Wert zurück, welche außerordentlich groß ist: %lu "
+"bytes (%lu ms).\n"
+"Dies ist wahrscheinlich ein Fehler im ALSA-Treiber '%s'. Bitte melden Sie "
+"diesen Punkt den ALSA-Entwicklern."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() gibt einen Wert zurück, welche außerordentlich groß ist: %li "
+"bytes (%s%lu ms).\n"
+"Dies ist wahrscheinlich ein Fehler im ALSA-Treiber '%s'. Bitte melden Sie "
+"diesen Punkt den ALSA-Entwicklern."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() gibt einen Wert zurück, welche außerordentlich groß ist: %lu "
+"bytes (%lu ms).\n"
+"Dies ist wahrscheinlich ein Fehler im ALSA-Treiber '%s'. Bitte melden Sie "
+"diesen Punkt den ALSA-Entwicklern."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() gibt einen Wert zurück, welche außerordentlich groß "
+"ist: %lu bytes (%lu ms).\n"
+"Dies ist wahrscheinlich ein Fehler im ALSA-Treiber '%s'. Bitte melden Sie "
+"diesen Punkt den ALSA-Entwicklern."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Dummy-Ausgabe"
+
+#: ../src/modules/module-ladspa-sink.c:48
+#, fuzzy
+msgid "Virtual LADSPA sink"
+msgstr "Virtueller LADSPA-Sink"
+
+#: ../src/modules/module-ladspa-sink.c:52
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr ""
+
+#: ../src/modules/module-null-sink.c:284
+#, fuzzy
+msgid "Null Output"
+msgstr "Ausgang %s"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "Internes Audio"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Ursprünglicher dlopen-Loader konnte nicht gefunden werden."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "Neuer dlopen-Loader konnte nicht gefunden werden."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "Hinzufügen von Bind-Now-Loader fehlgeschlagen."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "Signal %s empfangen."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "Wird beendet."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Benutzer '%s' nicht gefunden."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Gruppe '%s' nicht gefunden."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Benutzer '%s' (UID %lu) und Gruppe '%s' (GID %lu) gefunden."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID von Benutzer '%s' und Gruppe '%s' stimmen nicht überein."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Benutzerverzeichnis von Benutzer '%s' ist nicht '%s', ignoriere."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Konnte '%s' nciht erzeugen: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Wechseln der Gruppen-Liste fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Wechseln der GID fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Wechseln der UID fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "Root-Berechtigungen erfolgreich zurückgesetzt."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "System-Modus auf dieser Plattform nicht unterstützt."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "Parsen der Kommandzeile fehlgeschlagen."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "Daemon läuft nicht"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "Daemon läuft als PID %u"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Konnte Prozess nicht abbrechen: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Dieses Programm sollte ohne die Option --system nicht als Administrator "
+"ausgeführt werden."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Root-Berechtigungen benötigt."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start nicht unterstützt für System-Instanzen."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "System-Modus aktiv, jeodch --disallow-exit nicht gesetzt!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "System-Modus aktiv, jedoch --disallow-module-loading nicht gesetzt!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "System-Modus aktiv, SHM-Modus gezwungenermaßen deaktiviert!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "System-Modus aktiv, Exit-Idle-Time gezwungenermaßen deaktiviert!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "Reservieren von STDIO fehlgeschlagen."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "Start des Daemons fehlgeschlagen."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "Start des Daemons erfolgreich."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() fehlgeschlagen: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Dies ist PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Kompilier-Host: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "Kompilier-CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "Laufe auf Host: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u CPUs gefunden."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "Seitengröße ist %lu Bytes."
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Kompiliere mit Valgrind-Unterstützung: ja"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Kompiliere mit Valgrind-Unterstützung: nein"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Läuft im Valgrind-Modus: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "Laufe auf Host: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Optimiertes Build: ja"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "Optimiertes Build: nein"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG definiert, alle Ansprüche deaktiviert."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH definiert, nur fast-path-Ansprüche deaktiviert."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "Alle Ansprüche aktiviert."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "Beziehen der Maschinen-ID fehlgeschlagen"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "System- ID ist %s."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "System- ID ist %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Nutze Laufzeit-Verzeichnis %s."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "Nutze Zustands-Verzeichnis %s."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "Modul-Verzeichnis %s benutzen."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Laufe im System-Modus: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() fehlgeschlagen."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "Neue hochauslösende Timer verfügbar! Guten Appetit!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr "Der Chefkoch empfiehlt: Linux mit aktivierten hochauslösenden Timern!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() fehlgeschlagen."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "Konnte Daemon nicht initialisieren."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Daemon verweigert Ausführung, da keine Module geladen."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "Start des Daemons abgeschlossen."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "Herunterfahren des Daemon gestartet."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "Daemon beendet."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"BEFEHLE:\n"
+"  -h, --help                            Zeige diese Hilfe\n"
+"      --version                         Zeige Version\n"
+"      --dump-conf                       Zeige Standardkonfiguration\n"
+"      --dump-modules                    Zeige Liste verfügbarer Module\n"
+"      --dump-resample-methods           Zeige verfügbare Resample-Methoden\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Starte Daemon, falls noch nicht "
+"geschehen\n"
+"  -k  --kill                            Laufenden Daemon beenden\n"
+"      --check                           Prüfe laufende Daemone (gibt nur "
+"einen Exit-Code zurück)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Als systemweite Instanz ausführen\n"
+"  -D, --daemonize[=BOOL]                Nach Start zum Daemon machen\n"
+"      --fail[=BOOL]                     Beenden, wenn Start fehlschlägt\n"
+"      --high-priority[=BOOL]            Nutze höchste Priorität\n"
+"                                        (Nur verfügbar als root, wenn SUID "
+"oder\n"
+"                                        mit erhöhtem RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Versuche, Echtzeit-Scheduling zu "
+"aktivieren\n"
+"                                        (Nur verfügbar als root, wenn SUID "
+"oder\n"
+"                                        mit erhöhtem RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Verbiete (Ent-)laden durch Nutzer "
+"angeforderter\n"
+"                                        Module nach dem Start\n"
+"      --disallow-exit[=BOOL]            Verbiete Beenden auf Anfrage des "
+"Nutzers\n"
+"      --exit-idle-time=SECS             Beende Daemon, wenn für diese Zeit \n"
+"                                        untätig\n"
+"      --module-idle-time=SECS           Entlade untätige Module nach dieser "
+"Zeit\n"
+"      --scache-idle-time=SECS           Entlade untätige automatisch "
+"geladene \n"
+"                                        Samples nach dieser Zeit\n"
+"      --log-level[=STUFE]               Grad der Ausführlichkeit angeben\n"
+"  -v                                    Ausführliche Meldungen\n"
+"      --log-target={auto,syslog,stderr} Protokoll-Ziel angeben\n"
+"  -p, --dl-search-path=PFAD             Suchpfad für dynamisch "
+"freigegebene \n"
+"                                        Objekte (Plugins)\n"
+"      --resample-method=METHODE          Nutze diese Resampling-Methode\n"
+"                                        (Siehe --dump-resample-methods für\n"
+"                                        mögliche Werte)\n"
+"      --use-pid-file[=BOOL]             Eine PID-Datei erstellen\n"
+"      --no-cpu-limit[=BOOL]             CPU-Lastbegrenzung auf "
+"unterstützten\n"
+"                                        Systemen nicht installieren.\n"
+"      --disable-shm[=BOOL]              Keine Unterstützung für Shared "
+"Memory.\n"
+"\n"
+"STARTUP-SCRIPT:\n"
+"  -L, --load=\"MODUL-ARGUMENTE\"       Plugin-Modul mit diesen Parametern \n"
+"                                        laden.\n"
+"  -F, --file=DATEINAMEN                   Dieses Skript ausführen\n"
+"  -C                                    Nach Start auf laufendem TTY \n"
+"                                        eine Kommandozeile öffnen\n"
+"\n"
+"  -n                                    Standardskript nicht laden\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "Option --daemonize erfordert bool'schen Wert"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "Option --fail erfordert bool'schen Wert"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level erfordert Wert für Grad der Protokollierung (entweder numerisch "
+"im Bereich 0..4 or einen dieser: debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "Option --high-priority erfordert bool'schen Wert"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "Option --realtime erfordert bool'schen Wert"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "Option --disallow-module-loading erfordert bool'schen Wert"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit erfordert boolsches Argument"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "Option --use-pid-file erfordert bool'schen Wert"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr ""
+"Ungültiges Log-Ziel: Benutzen Sie entweder 'syslog', 'stderr' oder 'auto'."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--realtime erfordert boolsches Argument"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta erfordert boolschen Wert"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Ungültige Resample-Methode '%s'."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--System erwartet Boolean-Argument"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "Option --no-cpu-limit erfordert bool'schen Wert"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "Option --disable-shm erfordert bool'schen Wert"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Name: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "Keine Modul-Informationen verfügbar\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Version: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Beschreibung: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Verwendung: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Lade einmalig: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, fuzzy, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "DEPRECATION WARNING: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Pfad: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Ungültiges Log-Ziel '%s'."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Ungültige Log-Stufe '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Ungültige Resample-Methode '%s'."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Ungültiges rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Ungültiges Sample-Format '%s'."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Ungültige Sample-Rate '%s'."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Ungültige Sample-Kanäle '%s'."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Ungültige Kanal-Zuordnung '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Ungültige Anzahl von Fragmenten '%s'."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Ungültige Fragmentgröße '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Ungültige Nice-Stufe '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Ungültige Sample-Rate '%s'."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Öffnen der Konfigurationsdatei fehlgeschlagen : %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Die angegebene Standard-Kanalzuordnung hat eine andere Anzahl von Kanälen "
+"als die angegebene Standard-Kanal-Anzahl."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Lese von Konfigurationsdatei: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "Root-Privilegien aufräumen."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio Sound System"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Das PulseAudio Sound System starten"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio Sound System"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "Das PulseAudio Sound System starten"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Vorne Mitte"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Vorne Links"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Vorne Rechts"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Hinten Mitte"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Hinten Links"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Hinten Rechts"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Vorne Links der Mitte"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Vorne Rechts der Mitte"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Seite Links"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Seite Rechts"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Zusatz 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Zusatz 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Zusatz 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Zusatz 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Zusatz 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Zusatz 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Zusatz 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Zusatz 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Zusatz 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Zusatz 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Zusatz 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Zusatz 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Zusatz 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Zusatz 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Zusatz 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Zusatz 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Zusatz 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Zusatz 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Zusatz 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Zusatz 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Zusatz 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Zusatz 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Zusatz 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Zusatz 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Zusatz 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Zusatz 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Zusatz 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Zusatz 26"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Zusatz 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Zusatz 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Zusatz 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Zusatz 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Oben Mitte"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Oben Vorne Mitte"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Oben Vorne Links"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Oben Vorne Rechts"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Oben Hinten Mitte"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Oben Hinten Links"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Oben Hinten Rechts"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(ungültig)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "Zugriff abgelehnt"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Unbekannter Befehl"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Ungültiges Argument"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "Entität existiert bereits"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "Entität nicht vorhanden"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "Verbindung verweigert"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "Protokollfehler"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "Zeitüberschreitung"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "Kein Authorisierungsschlüssel"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "Interner Fehler"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "Verbindung beendet"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "Entität terminiert"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Ungültiger Server"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Modulinitialisierung fehlgeschlagen"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Ungültiger Zustand"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Keine Daten"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "Inkompatible Protokollversion"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Zu groß"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "Nicht unterstützt"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Unbekannter Fehlercode"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "Erweiterung nicht vorhanden"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Veraltete Funktion"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Fehlende Implementation"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Client geteilt"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Eingabe/Ausgabe-Fehler"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Gerät oder Ressource beschäftigt"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_new() fehlgeschlagen: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "Parsen der Cookie-Daten fehlgeschlagen"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "Konfigurationsdatei '%s' konnte nicht geöffnet werden: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "Verbindungsversuch ohne Cookie, da keines geladen."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Nachricht für unbekannte Erweiterung '%s' erhalten"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Entleeren des Streams fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "Wiedergabe-Stream entleert."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "Verbindung zu Server entleert."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_write() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "Stream wurde erfolgreich erstellt."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Pufferdaten: maxlenght=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Pufferdaten: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Benutze Sample-Angabe '%s', Kanalzuordnung '%s'."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "Verbunden mit Gerät %s (%u, %sausgesetzt)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "Stream-Fehler: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Stream-Gerät ausgesetzt.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Stream-Gerät reaktiviert.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Stream leergelaufen.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Stream überlaufen.%s "
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "Stream gestartet: %s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Stream an Gerät %s übergeben (%u, %sausgesetzt).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "nicht "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Stream-Zwischenspeicher-Attribute geändert.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "Verbindung hergestellt.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Verbindungsfehler: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF empfangen."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "Signal empfangen, beenden."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Erhalten der Latenz fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Zeit: %0.3f sec; Latenz: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Diese Hilfe anzeigen\n"
+"      --version                         Version anzeigen\n"
+"\n"
+"  -r, --record                          Aufnahme-Verbindung aufbauen\n"
+"  -p, --playback                        Wiedergabe-Verbindung aufbauen\n"
+"\n"
+"  -v, --verbose                         Ausführliche Meldungen\n"
+"\n"
+"  -s, --server=SERVER                   Name des zu verbindenden Servers\n"
+"  -d, --device=DEVICE                   Name zu verbindender Sink/Quelle\n"
+"  -n, --client-name=NAME                Rufname des Clients auf dem Server\n"
+"      --stream-name=NAME                Rufname des Streams auf dem Server\n"
+"      --volume=VOLUME                   Initiale (lineare) Lautstärke "
+"zwischen 0...65536 angeben\n"
+"      --rate=SAMPLERATE                 Sample-Rate in Hz (Standard 44100)\n"
+"      --format=SAMPLEFORMAT             Ein Sample-Format von s16le, s16be, "
+"u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be "
+"(defaults to s16ne)\n"
+"      --channels=CHANNELS               Anzahl Kanäle, 1 für mono, 2 für "
+"stereo\n"
+"                                        (Standard ist 2)\n"
+"      --channel-map=CHANNELMAP          Diese geänderte Kanalzuordnung "
+"nutzen\n"
+"      --fix-format                      Sample-Format des mit Sink\n"
+"                                        verbundenen Streams nutzen.\n"
+"      --fix-rate                        Sample-Rate des mit Sink\n"
+"                                        verbundenen Streams nutzen.\n"
+"      --fix-channels                    Anzahl und Zuordnung der Kanäle\n"
+"                                        des mit Sink verbundenen\n"
+"                                        Streams nutzen.\n"
+"      --no-remix                        Kanäle nicht up-/down-mischen.\n"
+"      --no-remap                        Kanäle nach Index statt Name "
+"zuordnen.\n"
+"      --latency=BYTES                   Diese Latenz verwenden.\n"
+"      --process-time=BYTES              Diese Prozesszeit pro Anfrage "
+"verwenden.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Kompiliert mit libpulse %s\n"
+"Gelinkt mit libpulse %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Ungültiger Client-Name '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Ungültiger Stream-Name '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Ungültige Kanal-Zuweisung '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Ungültige Latenz-Angaben '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Ungültige Prozesszeit-Angaben '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Ungültige Eigenschaft '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Unbekanntes Dateiformat %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "Ungültige Sample-Angaben"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "Zu viele Argumente."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "Beziehen der Sample-Informationen für die Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "Öffnen der Audio-Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr "Warnung: Beziehen der Sample-Angabe aus Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "Beziehen der Sample-Informationen der Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Warnung: Bestimmung der Kanalzuordnung aus Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "Kanalzuordnung entspricht nicht Einstellungen des Samples"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "Warnung: Schreiben der Kanalzuordnung in Datei fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Öffnen eines %s-Streams mit Sample-Angabe '%s' und Kanalzuordnung '%s'."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "aufnehmen"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "abspielen"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "Parsen der Kommandzeile fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() fehlgeschlagen"
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_new() fehlgeschlagen: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_new() fehlgeschlagen."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() fehlgeschlagen."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Aussetzen fehlgeschlagen: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Resume fehlgeschlagen: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "WARNUNG: Sound-Server läuft nicht lokal, nicht ausgesetzt.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Verbindungsfehler: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT empfangen, beende.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "WARNUNG: Kind-Prozess durch Signal %u beendet\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Diese Hilfe zeigen\n"
+"      --version                         Zeige Version\n"
+"  -s, --server=SERVER                   Name des Zielservers\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"kompiliert mit libpulse %s\n"
+"Gelinkt mit libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() fehlgeschlagen.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() fehlgeschlagen.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() fehlgeschlagen.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Beziehen der Statistik fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Momentane Nutzung: %u Blöcke mit insgesamt %s Bytes.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Während gesamter Laufzeit: %u Blöcke mit insgesamt %s Bytes.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Sample-Pufferspeichergrösse: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Beziehen der Server-Information fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Name des Nutzers: %s\n"
+"Rechnername: %s\n"
+"Name des Servers: %s\n"
+"Version des Server: %s\n"
+"Standard-Sample-Angabe: %s\n"
+"Standard-Kanal-Zuordnung: %s\n"
+"Standard-Ausgabe: %s\n"
+"-Standard-Quelle: %s\n"
+"Cookie: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Erhalten der Sink-Informationen fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Senke #%u\n"
+"\tStatus: %s\n"
+"\tName: %s\n"
+"\tBeschreibung: %s\n"
+"\tTreiber: %s\n"
+"\tSample-Angabe: %s\n"
+"\tKanalzuordnung: %s\n"
+"\tOwner-Modul: %u\n"
+"\tStumm: %s\n"
+"\tLautstärke: %s%s%s\n"
+"\t        Verteilung %0.2f\n"
+"\tBasis-Lautstärke: %s%s%s\n"
+"\tQuelle Monitor: %s\n"
+"\tLatenz: %0.0f usec, eingestellt %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tProfile:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tAktive Profile: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tProfile:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Beziehen der Quellen-Informationen fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Quelle #%u\n"
+"\tStatus: %s\n"
+"\tName: %s\n"
+"\tBeschreibung: %s\n"
+"\tTreiber: %s\n"
+"\tSample-Angabe: %s\n"
+"\tKanalzuordnung: %s\n"
+"\tBesitzer-Modul: %u\n"
+"\tStumm: %s\n"
+"\tLautstärke: %s%s%s\n"
+"\t        Verteilung %0.2f\n"
+"\tBasis-Lautstärke: %s%s%s\n"
+"\tSenke-Monitor: %s\n"
+"\tLatenz: %0.0f usec, eingestellt %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "k. A."
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Beziehen der Modul-Information fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modul #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tNutzungszähler: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Beziehen der Client-Information fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Client #%u\n"
+"\tTreiber: %s\n"
+"\tOwner-Modul: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Beziehen der Karten-Information fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Karte #%u\n"
+"\tName: %s\n"
+"\tTreiber: %s\n"
+"\tOwner-Modul: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfile:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tAktive Profile: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Konnte Sink-Eingabe-Informationen nicht holen: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Eingabe-Senke #%u\n"
+"\tTreiber: %s\n"
+"\tOwner-Modul: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample-Angabe: %s\n"
+"\tKanalzuordnung: %s\n"
+"\tStumm: %s\n"
+"\tLautstärke: %s\n"
+"\t        %s\n"
+"\t        Verteilung %0.2f\n"
+"\tPufferlatenz: %0.0f usec\n"
+"\tSink-Latenz: %0.0f usec\n"
+"\tResample-Methode: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Konnte Informationen über Quell-Ausgabe nicht holen: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Eingabe-Senke #%u\n"
+"\tTreiber: %s\n"
+"\tOwner-Modul: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample-Angabe: %s\n"
+"\tKanalzuordnung: %s\n"
+"\tStumm: %s\n"
+"\tLautstärke: %s\n"
+"\t        %s\n"
+"\t        Verteilung %0.2f\n"
+"\tPufferlatenz: %0.0f usec\n"
+"\tSink-Latenz: %0.0f usec\n"
+"\tResample-Methode: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Beziehen der Sample-Informationen fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tLautstärke: %s\n"
+"\t        %s\n"
+"\t        Verteilung %0.2f\n"
+"\tDauer: %0.1fs\n"
+"\tGrösse: %s\n"
+"\tLazy: %s\n"
+"\tDateinamen: %s\n"
+"\tEigenschaften:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "Fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Beziehen der Quellen-Informationen fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Hochladen des Sample fehlgeschlagen: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "Dateiende ist zu früh aufgetreten"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr "Sink"
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr "Quelle"
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+#, fuzzy
+msgid "source-output"
+msgstr "Quelle"
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "Ungültiger Server"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT empfangen, beenden."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "Ungültige Sample-Angaben"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Diese Hilfe zeigen\n"
+"      --version                         Zeige Version\n"
+"  -s, --server=SERVER                   Name des Zielservers\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Kompiliert mit libpulse %s\n"
+"Gelinkt mit libpulse %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "Geben Sie eine zu öffnende Sample-Datei an"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "Öffnen der Audio-Datei fehlgeschlagen."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Warnung: Beziehen der Sample-Angabe aus Datei fehlgeschlagen."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "Sie müssen eine abzuspielende Sample-Datei angeben"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "Sie müssen eine zu löschende Sample-Datei angeben"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "Sie müssen einen Sink-Eingabe-Indexwert und einen Sink angeben"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr ""
+"Sie müssen eine Indexwert für die Quell-Ausgabe und eine Quelle angeben"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "Sie müssen einen Modulnamen angeben und Argumente übergeben."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "Sie müssen einen Indexwert für ein Modul angeben"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Sie sollten nur eine Senke angeben. Sie müssen zumindest einen bool'schen "
+"Wert übergeben."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Sie sollten nur eine Quelle angeben. Sie müssen zumindest einen bool'schen "
+"Wert übergeben."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Sie müssen einen Karten-Name/Indexwert und einen Profilnamen angeben"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Sie müssen einen Senkennamen/-Indexwert und einen Portnamen angeben"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "Sie müssen einen Quellennamen/-Indexwert und einen Portnamen angeben"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Sie müssen einen Senkennamen/-Indexwert und einen Portnamen angeben"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "Sie müssen einen Quellennamen/-Indexwert und einen Portnamen angeben"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "Sie müssen einen Sink-Eingabe-Indexwert und einen Sink angeben"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "Ungültiger Sink-Eingabe-Index"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr ""
+"Sie müssen eine Indexwert für die Quell-Ausgabe und eine Quelle angeben"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "Ungültiger Sink-Eingabe-Index"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "Sie müssen einen Senkennamen/-Indexwert und einen Portnamen angeben"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "Ungültige Sample-Angaben"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Sie müssen einen Quellennamen/-Indexwert und einen Portnamen angeben"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "Sie müssen einen Sink-Eingabe-Indexwert und einen Sink angeben"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "Ungültige Sink-Eingabe-Index-Angaben"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "Sie müssen einen Quellennamen/-Indexwert und einen Portnamen angeben"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "Ungültige Sink-Eingabe-Index-Angaben"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "Sie müssen einen Senkennamen/-Indexwert und einen Portnamen angeben"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "Kein gültiger Befehl angegeben."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Zeige aktuell mit X11-Anzeige verbundene PulseAudio-Daten (Standard)\n"
+" -e    Lokale PulseAudio-Daten an X11-Anzeige exportieren\n"
+" -i    PulseAudio-Daten von X11-Anzeige in lokale Umgebungsvariablen und "
+"Cookie importieren.\n"
+" -r    PulseAudio-Daten von X11-Anzeige löschen\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Parsen der Kommandozeile fehlgeschlagen.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "Server: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "Quelle: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Sink: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Paresen der Cookie-Daten fehlgeschlagen.\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Speichern der Cookie-Daten fehlgeschlagen\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "Laden der Client-Konfigurationsdatei fehlgeschlagen.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "Lesen the Umgebungsdaten fehlgeschlagen.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Beziehen des FQDN fehlgeschlagen.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Laden der Cookie-Daten fehlgeschlagen\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Noch nicht implementiert.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "Es läuft kein PulseAudio-Dienst oder nicht als Sessiondienst."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Terminieren des PulseAudio-Daemon fehlgeschlagen."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "Daemon antwortet nicht."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "Fehler beim Zugriff auf Autostart -Sperre."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "Aus"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "High Fidelity-Wiedergabe (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "High Fidelity-Aufnahme (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telephony Duplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio Sound Server"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr ""
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+#, fuzzy
+msgid "Input Devices"
+msgstr "Eingang %s"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+#, fuzzy
+msgid "Input"
+msgstr "Eingang %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "Internes Audio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+msgid "Docking Station Line In"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "Internes Audio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "Internes Audio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+#, fuzzy
+msgid "Internal Microphone"
+msgstr "Internes Audio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+msgid "Bass Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+msgid "No Bass Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+#, fuzzy
+msgid "Headphones"
+msgstr "Analog Mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+#, fuzzy
+msgid "Analog Input"
+msgstr "Analog Mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+#, fuzzy
+msgid "Analog Output"
+msgstr "Ausgang %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+msgid "Line Out"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+#, fuzzy
+msgid "Analog Mono Output"
+msgstr "Analog Mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "Analog Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "Digital Stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digital Stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "Analog Mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "Analog Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+#, fuzzy
+msgid "Analog Surround 2.1"
+msgstr "Analog Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+#, fuzzy
+msgid "Analog Surround 3.0"
+msgstr "Analog Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+#, fuzzy
+msgid "Analog Surround 3.1"
+msgstr "Analog Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "Analog Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "Analog Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "Analog Surround 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "Analog Surround 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+#, fuzzy
+msgid "Analog Surround 6.0"
+msgstr "Analog Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+#, fuzzy
+msgid "Analog Surround 6.1"
+msgstr "Analog Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+#, fuzzy
+msgid "Analog Surround 7.0"
+msgstr "Analog Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "Analog Surround 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "Digital Stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digital Stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digital Surround 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digital Surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "Digital Stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digital Surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+#, fuzzy
+msgid "Analog Mono Duplex"
+msgstr "Analog Mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+#, fuzzy
+msgid "Analog Stereo Duplex"
+msgstr "Analog Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+#, fuzzy
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Digital Stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Ausgang %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "Eingang %s"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit auf dieser Plattform nicht unterstützt."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() fehlgeschlagen"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Quell-Ausgabe #%u\n"
+#~ "\tTreiber: %s\n"
+#~ "\tOwner-Modul: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tQuelle: %u\n"
+#~ "\tSample-Spezifizierung: %s\n"
+#~ "\tKanalzuordnung: %s\n"
+#~ "\tPufferlatenz: %0.0f usec\n"
+#~ "\tQuelllatenz: %0.0f usec\n"
+#~ "\tResample-Methode: %s\n"
+#~ "\tEigenschaften:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Diese Hilfe anzeigen\n"
+#~ "      --version                         Version anzeigen\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   Name des Zielservers\n"
+#~ "  -n, --client-name=NAME                Rufname des Clients auf dem "
+#~ "Server\n"
+
+#, fuzzy
+#~ msgid "%s+%s"
+#~ msgstr "%s %s"
+
+#, fuzzy
+#~ msgid "%s / %s"
+#~ msgstr "%s %s"
+
+#, fuzzy
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Digital Surround 4.0 (IEC958/AC3)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Niedrigfrequenzemitter"
+
+#~ msgid "Invalid client name '%s'\n"
+#~ msgstr "Ungültiger Client-Name '%s'\n"
+
+#~ msgid "Failed to determine sample specification from file.\n"
+#~ msgstr "Beziehen der Sample-Informationen der Datei fehlgeschlagen.\n"
+
+#~ msgid "select(): %s"
+#~ msgstr "select(): %s"
+
+#~ msgid "Cannot connect to system bus: %s"
+#~ msgstr "Kann nicht mit dem System-Bus verbinden: %s"
+
+#~ msgid "Cannot get caller from PID: %s"
+#~ msgstr "Kann Caller von PID nicht beziehen: %s"
+
+#~ msgid "Cannot set UID on caller object."
+#~ msgstr "Kann UID für Caller-Objekt nicht setzen."
+
+#~ msgid "Failed to get CK session."
+#~ msgstr "Kann CK-Session nicht beziehen."
+
+#~ msgid "Cannot set UID on session object."
+#~ msgstr "Kann UID für Session-Objekt nicht setzen."
+
+#~ msgid "Cannot allocate PolKitAction."
+#~ msgstr "Konnte PolKitAction nicht zuordnen."
+
+#~ msgid "Cannot set action_id"
+#~ msgstr "Kann action_id nicht setzen"
+
+#~ msgid "Cannot allocate PolKitContext."
+#~ msgstr "Konnte PolKitContext nicht zuordnen."
+
+#~ msgid "Cannot initialize PolKitContext: %s"
+#~ msgstr "Konnte PolKitContext nicht initialisieren: %s"
+
+#~ msgid "Could not determine whether caller is authorized: %s"
+#~ msgstr "Autorisierung des Callers konnte nicht sichergestellt werden: %s"
+
+#~ msgid "Cannot obtain auth: %s"
+#~ msgstr "Keine Authorisierung erhalten: %s"
+
+#~ msgid "PolicyKit responded with '%s'"
+#~ msgstr "PolicyKit antwortete mit '%s'"
+
+#, fuzzy
+#~ msgid ""
+#~ "High-priority scheduling (negative Unix nice level) for the PulseAudio "
+#~ "daemon"
+#~ msgstr ""
+#~ "Hochprioritäts-Terminierung () (negative Unix nice level) für den "
+#~ "PulseAudio-Dienst"
+
+#~ msgid "Real-time scheduling for the PulseAudio daemon"
+#~ msgstr "Echtzeit-Terminierung des PulseAudio-Daemon"
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring high-priority scheduling."
+#~ msgstr ""
+#~ "System-Richtlinien verhindert PulseAudio beim Erlangen des high-priority "
+#~ "scheduling."
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring real-time scheduling."
+#~ msgstr ""
+#~ "System-Richtlinien verhindert PulseAudio beim Erlangen der Echtzeit-"
+#~ "Terminierung."
+
+#~ msgid "read() failed: %s\n"
+#~ msgstr "read() fehlgeschlagen: %s\n"
+
+#~ msgid "pa_context_connect() failed: %s\n"
+#~ msgstr "pa_context_connect() fehlgeschlagen: %s\n"
+
+#~ msgid "We're in the group '%s', allowing high-priority scheduling."
+#~ msgstr ""
+#~ "Wir befinden uns in der Gruppe '%s', was Scheduling höchster Priorität "
+#~ "ermöglicht."
+
+#~ msgid "We're in the group '%s', allowing real-time scheduling."
+#~ msgstr ""
+#~ "Wir befinden uns in der Gruppe '%s', was Echtzeit-Scheduling ermöglicht."
+
+#~ msgid "PolicyKit grants us acquire-high-priority privilege."
+#~ msgstr "Richtlinien gewähren das Recht aquire-high-priority."
+
+#~ msgid "PolicyKit refuses acquire-high-priority privilege."
+#~ msgstr "Richtlinien verweigern das Recht acquire-high-priority."
+
+#~ msgid "PolicyKit grants us acquire-real-time privilege."
+#~ msgstr "Richtlinien gewähren das Recht aquire-real-time."
+
+#~ msgid "PolicyKit refuses acquire-real-time privilege."
+#~ msgstr "Richtlinien verweigern das Recht acquire-real-time."
+
+#, fuzzy
+#~ msgid ""
+#~ "Called SUID root and real-time and/or high-priority scheduling was "
+#~ "requested in the configuration. However, we lack the necessary "
+#~ "privileges:\n"
+#~ "We are not in group '%s', PolicyKit refuse to grant us the requested "
+#~ "privileges and we have no increase RLIMIT_NICE/RLIMIT_RTPRIO resource "
+#~ "limits.\n"
+#~ "For enabling real-time/high-priority scheduling please acquire the "
+#~ "appropriate PolicyKit privileges, or become a member of '%s', or increase "
+#~ "the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."
+#~ msgstr ""
+#~ "' und PolicyKit verweigern diese Rechte. Verwerfe SUID wieder.\n"
+#~ "Erlangen Sie die den Richtlinien entsprechenden Rechte, um Echtzeit-"
+#~ "Scheduling zu aktivieren oder werden Sie Mitglied der Gruppe '"
+
+#~ msgid ""
+#~ "High-priority scheduling enabled in configuration but not allowed by "
+#~ "policy."
+#~ msgstr "Scheduling höchster Priorität konfiguriert, jedoch nicht erlaubt."
+
+#~ msgid "Successfully increased RLIMIT_RTPRIO"
+#~ msgstr "RLIMIT_RTPRIO erfolgreich erhöht"
+
+#~ msgid "RLIMIT_RTPRIO failed: %s"
+#~ msgstr "RLIMIT_RTPRIO fehlgeschlagen: %s"
+
+#~ msgid "Giving up CAP_NICE"
+#~ msgstr "Verwerfe CAP_NICE"
+
+#~ msgid ""
+#~ "Real-time scheduling enabled in configuration but not allowed by policy."
+#~ msgstr "Echtzeit-Scheduling konfiguriert, jedoch nicht erlaubt."
+
+#~ msgid "Limited capabilities successfully to CAP_SYS_NICE."
+#~ msgstr "Fähigkeiten erfolgreich auf CAP_SYS_NICE reduziert."
+
+#~ msgid "time_new() failed.\n"
+#~ msgstr "time_new() fehlgeschlagen.\n"
+
+#~ msgid "Output %s + Input %s"
+#~ msgstr "Ausgabe %s + Eingabe %s"
+
+#~ msgid "Stream successfully created\n"
+#~ msgstr "Stream erfolgreich erzeugt\n"
+
+#~ msgid "Stream errror: %s\n"
+#~ msgstr "Stream-Fehler: %s\n"
+
+#~ msgid "Connection established.\n"
+#~ msgstr "Verbindung hergestellt.\n"
+
+#~ msgid ""
+#~ "%s [options] [FILE]\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -v, --verbose                         Enable verbose operation\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -d, --device=DEVICE                   The name of the sink to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ "      --stream-name=NAME                How to call this stream on the "
+#~ "server\n"
+#~ "      --volume=VOLUME                   Specify the initial (linear) "
+#~ "volume in range 0...65536\n"
+#~ "      --channel-map=CHANNELMAP          Set the channel map to the use\n"
+#~ msgstr ""
+#~ "%s [options] [FILE]\n"
+#~ "\n"
+#~ "  -h, --help                            Zeige diese Hilfe\n"
+#~ "      --version                         Zeige Version\n"
+#~ "\n"
+#~ "  -v, --verbose                         Ausführliche Meldungen\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   Name des Zielservers\n"
+#~ "  -d, --device=DEVICE                   Name des Ziel-Sink\n"
+#~ "  -n, --client-name=NAME                Rufname des Clients auf dem "
+#~ "Server\n"
+#~ "      --stream-name=NAME                Rufname des Streams auf dem "
+#~ "Server\n"
+#~ "      --volume=VOLUME                   Initiale (lineare) Lautstärke "
+#~ "zwischen 0...65536\n"
+#~ "      --channel-map=CHANNELMAP          Diese Kanalzuordnung nutzen\n"
+
+#~ msgid ""
+#~ "paplay %s\n"
+#~ "Compiled with libpulse %s\n"
+#~ "Linked with libpulse %s\n"
+#~ msgstr ""
+#~ "paplay %s\n"
+#~ "Kompliert mit libpulse %s\n"
+#~ "Gelinkt mit libpulse %s\n"
+
+#~ msgid "Invalid channel map\n"
+#~ msgstr "Ungültige Kanal-Zuweisung\n"
+
+#~ msgid "Failed to open file '%s'\n"
+#~ msgstr "Öffnen der Datei '%s' fehlgeschlagen\n"
+
+#~ msgid "Channel map doesn't match file.\n"
+#~ msgstr "Kanal-Zuweisung stimmt mit Datei nicht überein.\n"
+
+#~ msgid "Using sample spec '%s'\n"
+#~ msgstr "Sampling-Angabe '%s' wird benutzt\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "Called SUID root and real-time and/or high-priority scheduling was "
+#~ "requested in the configuration. However, we lack the necessary "
+#~ "privileges:\n"
+#~ "We are not in group '"
+#~ msgstr ""
+#~ "Konfiguration fordert Aufruf der SUID root und Echtzeit-Scheduling "
+#~ "höchster Priorität. Allerdings fehlen die nötigen Rechte:\n"
+#~ "Wir befinden uns nicht in der Gruppe '"
+
+#~ msgid "--log-time boolean argument"
+#~ msgstr "--log-time erfordert bool'schen Wert"
+
+#~ msgid ""
+#~ "', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this "
+#~ "user."
+#~ msgstr ""
+#~ "' oder erhöhen sie die RLIMIT_NICE/RLIMIT_RTPRIO-Ressourcenbegrenzungen "
+#~ "für diesen Nutzer."
+
+#~ msgid "Default sink name (%s) does not exist in name register."
+#~ msgstr "Vorgabename für Sink (%s) existiert nicht im Namensregister."
+
+#~ msgid "Buffer overrun, dropping incoming data\n"
+#~ msgstr "Pufferüberlauf, verwerfe eingehende Daten\n"
+
+#~ msgid "pa_stream_drop() failed: %s\n"
+#~ msgstr "pa_stream_drop() fehlgeschlagen: %s\n"
+
+#~ msgid "muted"
+#~ msgstr "stumm"
+
+#~ msgid ""
+#~ "*** Autoload Entry #%u ***\n"
+#~ "Name: %s\n"
+#~ "Type: %s\n"
+#~ "Module: %s\n"
+#~ "Argument: %s\n"
+#~ msgstr ""
+#~ "*** Autoload-Eintrag #%u ***\n"
+#~ "Name: %s\n"
+#~ "Typ: %s\n"
+#~ "Modul: %s\n"
+#~ "Argument: %s\n"
+
+#~ msgid "socketpair(): %s"
+#~ msgstr "socketpair(): %s"
diff --git a/po/el.po b/po/el.po
new file mode 100644 (file)
index 0000000..cfa0e46
--- /dev/null
+++ b/po/el.po
@@ -0,0 +1,3470 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Dimitris Glezos <dimitris@glezos.com>, 2008.
+# Thalia Papoutsaki <saliyath@gmail.com>, 2009, 2012.
+# Dimitris Spingos (Δημήτρης Σπίγγος) <dmtrs32@gmail.com>, 2013, 2014.
+msgid ""
+msgstr ""
+"Project-Id-Version: el\n"
+"Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?product=Pul"
+"seAudio&keywords=I18N+L10N&component=misc\n"
+"POT-Creation-Date: 2014-05-02 22:03+0000\n"
+"PO-Revision-Date: 2014-05-08 09:53+0300\n"
+"Last-Translator: Dimitris Spingos (Δημήτρης Σπίγγος) <dmtrs32@gmail.com>\n"
+"Language-Team: team@lists.gnome.gr\n"
+"Language: el\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Virtaal 0.7.0\n"
+
+#: ../src/daemon/caps.c:54
+msgid "Cleaning up privileges."
+msgstr "Εκκαθάριση δικαιωμάτων."
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+#| msgid ""
+#| "%s [options]\n"
+#| "\n"
+#| "COMMANDS:\n"
+#| "  -h, --help                            Show this help\n"
+#| "      --version                         Show version\n"
+#| "      --dump-conf                       Dump default configuration\n"
+#| "      --dump-modules                    Dump list of available modules\n"
+#| "      --dump-resample-methods           Dump available resample methods\n"
+#| "      --cleanup-shm                     Cleanup stale shared memory "
+#| "segments\n"
+#| "      --start                           Start the daemon if it is not "
+#| "running\n"
+#| "  -k  --kill                            Kill a running daemon\n"
+#| "      --check                           Check for a running daemon (only "
+#| "returns exit code)\n"
+#| "\n"
+#| "OPTIONS:\n"
+#| "      --system[=BOOL]                   Run as system-wide instance\n"
+#| "  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+#| "      --fail[=BOOL]                     Quit when startup fails\n"
+#| "      --high-priority[=BOOL]            Try to set high nice level\n"
+#| "                                        (only available as root, when "
+#| "SUID or\n"
+#| "                                        with elevated RLIMIT_NICE)\n"
+#| "      --realtime[=BOOL]                 Try to enable realtime "
+#| "scheduling\n"
+#| "                                        (only available as root, when "
+#| "SUID or\n"
+#| "                                        with elevated RLIMIT_RTPRIO)\n"
+#| "      --disallow-module-loading[=BOOL]  Disallow module user requested "
+#| "module\n"
+#| "                                        loading/unloading after startup\n"
+#| "      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+#| "      --exit-idle-time=SECS             Terminate the daemon when idle "
+#| "and this\n"
+#| "                                        time passed\n"
+#| "      --scache-idle-time=SECS           Unload autoloaded samples when "
+#| "idle and\n"
+#| "                                        this time passed\n"
+#| "      --log-level[=LEVEL]               Increase or set verbosity level\n"
+#| "  -v                                    Increase the verbosity level\n"
+#| "      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+#| "                                        Specify the log target\n"
+#| "      --log-meta[=BOOL]                 Include code location in log "
+#| "messages\n"
+#| "      --log-time[=BOOL]                 Include timestamps in log "
+#| "messages\n"
+#| "      --log-backtrace=FRAMES            Include a backtrace in log "
+#| "messages\n"
+#| "  -p, --dl-search-path=PATH             Set the search path for dynamic "
+#| "shared\n"
+#| "                                        objects (plugins)\n"
+#| "      --resample-method=METHOD          Use the specified resampling "
+#| "method\n"
+#| "                                        (See --dump-resample-methods for\n"
+#| "                                        possible values)\n"
+#| "      --use-pid-file[=BOOL]             Create a PID file\n"
+#| "      --no-cpu-limit[=BOOL]             Do not install CPU load limiter "
+#| "on\n"
+#| "                                        platforms that support it.\n"
+#| "      --disable-shm[=BOOL]              Disable shared memory support.\n"
+#| "\n"
+#| "STARTUP SCRIPT:\n"
+#| "  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin "
+#| "module with\n"
+#| "                                        the specified argument\n"
+#| "  -F, --file=FILENAME                   Run the specified script\n"
+#| "  -C                                    Open a command line on the "
+#| "running TTY\n"
+#| "                                        after startup\n"
+#| "\n"
+#| "  -n                                    Don't load default script file\n"
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"ΕΝΤΟΛΕΣ:\n"
+"  -h, --help                            Εμφάνιση της βοήθειας\n"
+"      --version                         Εμφάνιση της έκδοσης\n"
+"      --dump-conf                       Ρύθμιση προεπιλεγμένης αποτύπωσης\n"
+"      --dump-modules                    Λίστα αποτύπωσης διαθέσιμων ενοτήτων\n"
+"      --dump-resample-methods           Διαθέσιμη αποτύπωση μεθόδων "
+"επαναδειγματοληψίας\n"
+"      --cleanup-shm                     Καθαρισμός παλιών κοινόχρηστων "
+"τμημάτων μνήμης\n"
+"      --start                           Έναρξη του δαίμονα αν δεν εκτελείται\n"
+"  -k  --kill                            Τερματισμός εκτελούμενου δαίμονα\n"
+"      --check                           Έλεγχος για εκτελούμενο δαίμονα "
+"(επιστρέφει μόνο κώδικα εξόδου)\n"
+"\n"
+"ΕΠΙΛΟΓΕΣ:\n"
+"      --system[=BOOL]                   Εκτέλεση ως στιγμιοτύπου ολόκληρου "
+"του συστήματος\n"
+"  -D, --daemonize[=BOOL]                Ο δαίμονας μετά την εκκίνηση\n"
+"      --fail[=BOOL]                     Έξοδος όταν η εκκίνηση αποτυγχάνει\n"
+"      --high-priority[=BOOL]            Προσπάθεια ορισμού υψηλού επιπέδου "
+"nice\n"
+"                                        (διαθέσιμο μόνο ως υπερχρήστης, με "
+"SUID ή\n"
+"                                        με ανεβασμένο RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Προσπάθεια ενεργοποίησης σχεδιασμού "
+"πραγματικού χρόνου\n"
+"                                        (διαθέσιμο μόνο ως υπερχρήστης, με "
+"SUID ή\n"
+"                                        με ανεβασμένο RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Απαγόρευση ενότητας που ζητήθηκε από "
+"τον χρήστη\n"
+"                                        για φόρτωση/εκφόρτωση μετά την "
+"εκκίνηση\n"
+"      --disallow-exit[=BOOL]            Απαγόρευση εξόδου που ζητήθηκε από "
+"τον χρήστη\n"
+"      --exit-idle-time=SECS             Τερματισμός του δαίμονα όταν είναι "
+"αδρανής και πέρασε\n"
+"                                        αυτός ο χρόνος\n"
+"      --scache-idle-time=SECS           Εκφόρτωση αυτόματα φορτωμένων "
+"δειγμάτων όταν είναι αδρανή και\n"
+"                                        πέρασε αυτός ο χρόνος\n"
+"      --log-level[=LEVEL]               Αύξηση ή ορισμός  επιπέδου "
+"λεπτομέρειας\n"
+"  -v                                    Αύξηση του επιπέδου λεπτομέρειας\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Ορισμός του προορισμού καταγραφής\n"
+"      --log-meta[=BOOL]                 Συμπερίληψη θέσης κώδικα σε μηνύματα "
+"καταγραφής\n"
+"      --log-time[=BOOL]                 Συμπερίληψη χρονικών σημάνσεων σε "
+"μηνύματα καταγραφής\n"
+"      --log-backtrace=FRAMES            Συμπερίληψη οπισθοανίχνευσης σε "
+"μηνύματα καταγραφής\n"
+"  -p, --dl-search-path=PATH             Ορισμός της διαδρομής ανίχνευσης για "
+"δυναμικά κοινόχρηστα\n"
+"                                        αντικείμενα (πρόσθετα)\n"
+"      --resample-method=METHOD          Χρήση της ειδικής μεθόδου "
+"επαναδειγματοληψίας\n"
+"                                        (Δείτε --dump-resample-methods για\n"
+"                                        πιθανές τιμές)\n"
+"      --use-pid-file[=BOOL]             Δημιουργία αρχείου PID\n"
+"      --no-cpu-limit[=BOOL]             Να μην εγκαθίστανται οριοθέτες "
+"φόρτωσης CPU σε\n"
+"                                        λειτουργικά που το υποστηρίζουν.\n"
+"      --disable-shm[=BOOL]              Απενεργοποίηση κοινόχρηστης "
+"υποστήριξης μνήμης.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Φόρτωση της συγκεκριμένης ενότητας "
+"προσθέτου\n"
+"                                        με το συγκεκριμένο όρισμα\n"
+"  -F, --file=FILENAME                   Εκτέλεση του συγκεκριμένου σεναρίου\n"
+"  -C                                    Άνοιγμα μιας γραμμής εντολών στο "
+"εκτελούμενο TTY\n"
+"                                        μετά την εκκίνηση\n"
+"\n"
+"  -n                                    Να μην φορτώνεται το προεπιλεγμένο "
+"αρχείο σεναρίου\n"
+
+#: ../src/daemon/cmdline.c:245
+msgid "--daemonize expects boolean argument"
+msgstr "Το --daemonize περιμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/cmdline.c:253
+msgid "--fail expects boolean argument"
+msgstr "Το --fail περιμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/cmdline.c:264
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"Το --log-level περιμένει όρισμα επιπέδου καταγραφής (είτε αριθμητικό σε "
+"διάστημα 0..4 ή ένα όρισμα αποσφαλμάτωσης, πληροφορίας, ειδοποίησης, "
+"προειδοποίησης, σφάλματος)."
+
+#: ../src/daemon/cmdline.c:276
+msgid "--high-priority expects boolean argument"
+msgstr "Το --high-priority περιμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/cmdline.c:284
+msgid "--realtime expects boolean argument"
+msgstr "Το --realtime αναμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/cmdline.c:292
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "Το --disallow-module-loading περιμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/cmdline.c:300
+msgid "--disallow-exit expects boolean argument"
+msgstr "Το --disallow-exit περιμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/cmdline.c:308
+msgid "--use-pid-file expects boolean argument"
+msgstr "Το --use-pid-file περιμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/cmdline.c:327
+#| msgid ""
+#| "Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid "
+#| "file name 'file:<path>', 'newfile:<path>'."
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Άκυρος προορισμός καταγραφής: χρησιμοποιήστε είτε 'syslog', 'journal' "
+"'stderr' είτε 'auto' ή ένα έγκυρο όνομα αρχείου 'file:<path>', "
+"'newfile:<path>'."
+
+#: ../src/daemon/cmdline.c:329
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Άκυρος προορισμός καταγραφής: χρησιμοποιήστε είτε 'syslog', 'stderr' είτε "
+"'auto' ή ένα έγκυρο όνομα αρχείου 'file:<path>', 'newfile:<path>'."
+
+#: ../src/daemon/cmdline.c:337
+msgid "--log-time expects boolean argument"
+msgstr "Το --log-time περιμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/cmdline.c:345
+msgid "--log-meta expects boolean argument"
+msgstr "Το --log-meta περιμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/cmdline.c:365
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Άκυρη μέθοδος επαναδειγματοληψίας '%s'."
+
+#: ../src/daemon/cmdline.c:372
+msgid "--system expects boolean argument"
+msgstr "Το --system περιμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/cmdline.c:380
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "Το --no-cpu-limit περιμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/cmdline.c:388
+msgid "--disable-shm expects boolean argument"
+msgstr "Το --disable-shm περιμένει όρισμα τιμής Μπουλ"
+
+#: ../src/daemon/daemon-conf.c:260
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Άκυρος προορισμός καταγραφής '%s'."
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Άκυρο επίπεδο καταγραφής '%s'."
+
+#: ../src/daemon/daemon-conf.c:290
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Άκυρη μέθοδος επαναδειγματοληψίας '%s'."
+
+#: ../src/daemon/daemon-conf.c:312
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Άκυρο rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:332
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Άκυρη μορφή δείγματος '%s'."
+
+#: ../src/daemon/daemon-conf.c:350 ../src/daemon/daemon-conf.c:368
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Άκυρος ρυθμός δειγμάτων '%s'."
+
+#: ../src/daemon/daemon-conf.c:391
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Άκυρα κανάλια δείγματος '%s'."
+
+#: ../src/daemon/daemon-conf.c:408
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Άκυρη απεικόνιση καναλιού '%s'."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Άκυρος αριθμός τμημάτων '%s'."
+
+#: ../src/daemon/daemon-conf.c:442
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Άκυρο μέγεθος τμήματος '%s'."
+
+#: ../src/daemon/daemon-conf.c:459
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Άκυρο επίπεδο nice '%s'."
+
+#: ../src/daemon/daemon-conf.c:502
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Άκυρος τύπος διακομιστή '%s'."
+
+#: ../src/daemon/daemon-conf.c:615
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Αποτυχία ανοίγματος αρχείου ρυθμίσεων: %s"
+
+#: ../src/daemon/daemon-conf.c:631
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Η συγκεκριμένη προεπιλεγμένη απεικόνιση καναλιού έχει έναν διαφορετικό "
+"αριθμό καναλιών από τον συγκεκριμένο προεπιλεγμένο αριθμό καναλιών."
+
+#: ../src/daemon/daemon-conf.c:718
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Ανάγνωση από το αρχείο ρυθμίσεων: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Όνομα: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "Δεν υπάρχουν διαθέσιμες πληροφορίες για την ενότητα\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Έκδοση: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Περιγραφή: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Συγγραφέας: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Χρήση: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Φόρτωση μια φορά: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "ΠΡΟΕΙΔΟΠΟΙΗΣΗ ΠΑΡΩΧΗΜΕΝΟΥ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Διαδρομή: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:77
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Αποτυχία ανοίγματος ενότητας %s: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:128
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Αποτυχία εύρεσης αρχικού φορτωτή lt_dlopen."
+
+#: ../src/daemon/ltdl-bind-now.c:133
+msgid "Failed to allocate new dl loader."
+msgstr "Αποτυχία κατανομής νέου φορτωτή dl."
+
+#: ../src/daemon/ltdl-bind-now.c:146
+msgid "Failed to add bind-now-loader."
+msgstr "Αποτυχία προσθήκης φορτωτή άμεσης σύνδεσης."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "Ελήφθη σήμα %s."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "Έξοδος."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Αποτυχία εύρεσης χρήστη '%s'."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Αποτυχία εύρεσης ομάδας '%s'."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Βρέθηκε χρήστης '%s' (UID %lu) και ομάδα '%s' (GID %lu)."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "Το GID του χρήστη '%s' και της ομάδας '%s' δεν ταιριάζουν."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Ο προσωπικός κατάλογος του χρήστη '%s' δεν είναι '%s', παράβλεψη."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Αποτυχία δημιουργίας '%s': %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Αποτυχία αλλαγής του καταλόγου ομάδας: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Αποτυχία αλλαγής GID: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Αποτυχία αλλαγής UID: %s"
+
+#: ../src/daemon/main.c:271
+msgid "Successfully changed user to \""
+msgstr "Πετυχημένη αλλαγή χρήστη σε \""
+
+#: ../src/daemon/main.c:279
+msgid "System wide mode unsupported on this platform."
+msgstr "Η κατάσταση συστήματος δεν υποστηρίζεται σε αυτό το λειτουργικό."
+
+#: ../src/daemon/main.c:297
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "Αποτυχία setrlimit(%s, (%u, %u)): %s"
+
+#: ../src/daemon/main.c:498
+msgid "Failed to parse command line."
+msgstr "Αποτυχία ανάλυσης γραμμής εντολών."
+
+#: ../src/daemon/main.c:537
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Άρνηση κατάστασης συστήματος για μη υπερχρήστη. Εκκίνηση μόνο της υπηρεσίας "
+"αναζήτησης διακομιστή διαύλου δεδομένων."
+
+#: ../src/daemon/main.c:619
+msgid "Daemon not running"
+msgstr "Ο δαίμονας δεν εκτελείται"
+
+#: ../src/daemon/main.c:621
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "Ο δαίμονας εκτελείται ως PID %u"
+
+#: ../src/daemon/main.c:636
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Αποτυχία τερματισμού δαίμονα: %s"
+
+#: ../src/daemon/main.c:665
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Αυτό το πρόγραμμα δεν προορίζεται να εκτελεστεί από υπερχρήστη (εκτός και "
+"οριστεί --system)."
+
+#: ../src/daemon/main.c:668
+msgid "Root privileges required."
+msgstr "Απαιτούνται δικαιώματα υπερχρήστη."
+
+#: ../src/daemon/main.c:675
+msgid "--start not supported for system instances."
+msgstr "Δεν υποστηρίζεται το --start για στιγμιότυπα συστήματος."
+
+#: ../src/daemon/main.c:715
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+"Ο ρυθμισμένος από τον χρήστη διακομιστής στο %s, αρνιέται να ξεκινήσει/να "
+"παράξει αυτόματα."
+
+#: ../src/daemon/main.c:721
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Ο ρυθμισμένος από τον χρήστη διακομιστής στο %s, φαίνεται να είναι τοπικός. "
+"Βαθύτερη διερεύνηση."
+
+#: ../src/daemon/main.c:726
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr ""
+"Εκτελείται σε λειτουργία συστήματος, αλλά δεν ορίστηκε η --disallow-exit!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"Εκτελείται σε λειτουργία συστήματος, αλλά δεν ορίστηκε η --disallow-module-"
+"loading!"
+
+#: ../src/daemon/main.c:732
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr ""
+"Εκτελείται σε λειτουργία συστήματος, απενεργοποιώντας αναγκαστικά τη "
+"λειτουργία SHM!"
+
+#: ../src/daemon/main.c:737
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"Εκτελείται σε λειτουργία συστήματος, απενεργοποιώντας αναγκαστικά τον αδρανή "
+"χρόνο εξόδου!"
+
+#: ../src/daemon/main.c:765
+msgid "Failed to acquire stdio."
+msgstr "Αποτυχία λήψης τυπικής εισόδου/εξόδου."
+
+#: ../src/daemon/main.c:771 ../src/daemon/main.c:842
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "Αποτυχία pipe(): %s"
+
+#: ../src/daemon/main.c:776 ../src/daemon/main.c:847
+#, c-format
+msgid "fork() failed: %s"
+msgstr "αποτυχία fork(): %s"
+
+#: ../src/daemon/main.c:791 ../src/daemon/main.c:862 ../src/utils/pacat.c:569
+#, c-format
+msgid "read() failed: %s"
+msgstr "Αποτυχία read(): %s"
+
+#: ../src/daemon/main.c:797
+msgid "Daemon startup failed."
+msgstr "Αποτυχία έναρξης δαίμονα."
+
+#: ../src/daemon/main.c:799
+msgid "Daemon startup successful."
+msgstr "Πετυχημένη έναρξη δαίμονα."
+
+#: ../src/daemon/main.c:830
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "Αποτυχία setsid(): %s"
+
+#: ../src/daemon/main.c:916
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Αυτό είναι το PulseAudio %s"
+
+#: ../src/daemon/main.c:917
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Οικοδεσπότης μεταγλώττισης: %s"
+
+#: ../src/daemon/main.c:918 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "CFLAGS μεταγλώττισης: %s"
+
+#: ../src/daemon/main.c:921
+#, c-format
+msgid "Running on host: %s"
+msgstr "Εκτελείται στον οικοδεσπότη: %s"
+
+#: ../src/daemon/main.c:924
+#, c-format
+msgid "Found %u CPUs."
+msgstr "Βρέθηκαν %u CPUs."
+
+#: ../src/daemon/main.c:926
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "Το μέγεθος της σελίδας είναι %lu ψηφιολέξεις"
+
+#: ../src/daemon/main.c:929
+msgid "Compiled with Valgrind support: yes"
+msgstr "Μεταγλωττισμένο με υποστήριξη Valgrind: ναι"
+
+#: ../src/daemon/main.c:931
+msgid "Compiled with Valgrind support: no"
+msgstr "Μεταγλωττισμένο με υποστήριξη Valgrind: όχι"
+
+#: ../src/daemon/main.c:934
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Εκτελείται σε κατάσταση valgrind: %s"
+
+#: ../src/daemon/main.c:936
+#, c-format
+msgid "Running in VM: %s"
+msgstr "Εκτελείται σε VM: %s"
+
+#: ../src/daemon/main.c:939
+msgid "Optimized build: yes"
+msgstr "Βελτιστοποιημένη δόμηση: ναι"
+
+#: ../src/daemon/main.c:941
+msgid "Optimized build: no"
+msgstr "Βελτιστοποιημένη δόμηση: όχι"
+
+#: ../src/daemon/main.c:945
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "Ορίστηκε NDEBUG, όλες οι διεκδικήσεις απενεργοποιήθηκαν."
+
+#: ../src/daemon/main.c:947
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr ""
+"Ορίστηκε FASTPATH, μόνο οι γρήγορες διεκδικήσεις διαδρομής απενεργοποιήθηκαν."
+
+#: ../src/daemon/main.c:949
+msgid "All asserts enabled."
+msgstr "Όλες οι διεκδικήσεις ενεργοποιήθηκαν."
+
+#: ../src/daemon/main.c:953
+msgid "Failed to get machine ID"
+msgstr "Αποτυχία λήψης αναγνωριστικού μηχανής"
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Machine ID is %s."
+msgstr "Το αναγνωριστικό μηχανής είναι %s."
+
+#: ../src/daemon/main.c:960
+#, c-format
+msgid "Session ID is %s."
+msgstr "Το αναγνωριστικό συνεδρίας είναι %s."
+
+#: ../src/daemon/main.c:966
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Χρήση καταλόγου χρόνου εκτέλεσης %s."
+
+#: ../src/daemon/main.c:971
+#, c-format
+msgid "Using state directory %s."
+msgstr "Χρήση καταλόγου κατάστασης %s."
+
+#: ../src/daemon/main.c:974
+#, c-format
+msgid "Using modules directory %s."
+msgstr "Χρήση καταλόγου ενοτήτων %s."
+
+#: ../src/daemon/main.c:976
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Εκτελείται σε κατάσταση συστήματος: %s"
+
+#: ../src/daemon/main.c:979
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Εντάξει, εκτελείτε PA σε λειτουργία συστήματος. Παρακαλούμε, σημειώστε ότι "
+"κατά πάσα πιθανότητα δεν θα έπρεπε να το κάνετε.\n"
+"Αν παρόλα αυτά το κάνετε, τότε είναι δικό σας σφάλμα, αν τα πράγματα δεν "
+"δουλέψουν όπως αναμενόταν.\n"
+"Παρακαλούμε, διαβάστε http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ για μια εξήγηση γιατί η "
+"λειτουργία συστήματος είναι συνήθως μια άσχημη ιδέα."
+
+#: ../src/daemon/main.c:996
+msgid "pa_pid_file_create() failed."
+msgstr "Αποτυχία pa_pid_file_create()."
+
+#: ../src/daemon/main.c:1006
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "Καινούργιοι χρονιστές υψηλής ανάλυσης είναι διαθέσιμοι! Καλή όρεξη!"
+
+#: ../src/daemon/main.c:1008
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Δικέ μου, ο πυρήνας σου είναι για τα μπάζα! Η πρόταση του σεφ σήμερα είναι "
+"Λίνουξ με ενεργοποιημένους τους χρονιστές υψηλής ανάλυσης!"
+
+#: ../src/daemon/main.c:1026
+msgid "pa_core_new() failed."
+msgstr "Αποτυχία pa_core_new()."
+
+#: ../src/daemon/main.c:1104
+msgid "Failed to initialize daemon."
+msgstr "Αποτυχία αρχικοποίησης του δαίμονα."
+
+#: ../src/daemon/main.c:1109
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"Ο δαίμονας ξεκίνησε χωρίς οποιεσδήποτε φορτωμένες ενότητες, αρνούμενος να "
+"δουλέψει."
+
+#: ../src/daemon/main.c:1147
+msgid "Daemon startup complete."
+msgstr "Ολοκληρώθηκε η έναρξη του δαίμονα."
+
+#: ../src/daemon/main.c:1153
+msgid "Daemon shutdown initiated."
+msgstr "Άρχισε ο τερματισμός του δαίμονα."
+
+#: ../src/daemon/main.c:1184
+msgid "Daemon terminated."
+msgstr "Ο δαίμονας τελείωσε."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Σύστημα ήχου PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Έναρξη του συστήματος ήχου PulseAudio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2251
+msgid "Input"
+msgstr "Εισαγωγή"
+
+#: ../src/modules/alsa/alsa-mixer.c:2252
+msgid "Docking Station Input"
+msgstr "Είσοδος σταθμού αγκύρωσης"
+
+#: ../src/modules/alsa/alsa-mixer.c:2253
+msgid "Docking Station Microphone"
+msgstr "Μικρόφωνο σταθμού αγκύρωσης"
+
+#: ../src/modules/alsa/alsa-mixer.c:2254
+msgid "Docking Station Line In"
+msgstr "Γραμμή εισόδου σταθμού αγκύρωσης"
+
+#: ../src/modules/alsa/alsa-mixer.c:2255 ../src/modules/alsa/alsa-mixer.c:2340
+msgid "Line In"
+msgstr "Γραμμή εισόδου"
+
+#: ../src/modules/alsa/alsa-mixer.c:2256 ../src/modules/alsa/alsa-mixer.c:2334
+#: ../src/modules/bluetooth/module-bluez4-device.c:2101
+#: ../src/modules/bluetooth/module-bluez5-device.c:1451
+msgid "Microphone"
+msgstr "Μικρόφωνο"
+
+#: ../src/modules/alsa/alsa-mixer.c:2257 ../src/modules/alsa/alsa-mixer.c:2335
+msgid "Front Microphone"
+msgstr "Μπροστινό μικρόφωνο"
+
+#: ../src/modules/alsa/alsa-mixer.c:2258 ../src/modules/alsa/alsa-mixer.c:2336
+msgid "Rear Microphone"
+msgstr "Πίσω μικρόφωνο"
+
+#: ../src/modules/alsa/alsa-mixer.c:2259
+msgid "External Microphone"
+msgstr "Εξωτερικό μικρόφωνο"
+
+#: ../src/modules/alsa/alsa-mixer.c:2260 ../src/modules/alsa/alsa-mixer.c:2338
+msgid "Internal Microphone"
+msgstr "Εσωτερικό μικρόφωνο"
+
+#: ../src/modules/alsa/alsa-mixer.c:2261 ../src/modules/alsa/alsa-mixer.c:2341
+msgid "Radio"
+msgstr "Ραδιόφωνο"
+
+#: ../src/modules/alsa/alsa-mixer.c:2262 ../src/modules/alsa/alsa-mixer.c:2342
+msgid "Video"
+msgstr "Βίντεο"
+
+#: ../src/modules/alsa/alsa-mixer.c:2263
+msgid "Automatic Gain Control"
+msgstr "Αυτόματος έλεγχος απολαβής"
+
+#: ../src/modules/alsa/alsa-mixer.c:2264
+msgid "No Automatic Gain Control"
+msgstr "Χωρίς αυτόματο έλεγχο απολαβής"
+
+#: ../src/modules/alsa/alsa-mixer.c:2265
+msgid "Boost"
+msgstr "Ενίσχυση"
+
+#: ../src/modules/alsa/alsa-mixer.c:2266
+msgid "No Boost"
+msgstr "Χωρίς ενίσχυση"
+
+#: ../src/modules/alsa/alsa-mixer.c:2267
+msgid "Amplifier"
+msgstr "Ενισχυτή"
+
+#: ../src/modules/alsa/alsa-mixer.c:2268
+msgid "No Amplifier"
+msgstr "Χωρίς ενισχυτή"
+
+#: ../src/modules/alsa/alsa-mixer.c:2269
+msgid "Bass Boost"
+msgstr "Ενίσχυση μπάσων"
+
+#: ../src/modules/alsa/alsa-mixer.c:2270
+msgid "No Bass Boost"
+msgstr "Χωρίς ενίσχυση μπάσων"
+
+#: ../src/modules/alsa/alsa-mixer.c:2271
+#: ../src/modules/bluetooth/module-bluez4-device.c:2106
+#: ../src/modules/bluetooth/module-bluez5-device.c:1458
+msgid "Speaker"
+msgstr "Ηχείο"
+
+#: ../src/modules/alsa/alsa-mixer.c:2272 ../src/modules/alsa/alsa-mixer.c:2344
+msgid "Headphones"
+msgstr "Ακουστικά"
+
+#: ../src/modules/alsa/alsa-mixer.c:2333
+msgid "Analog Input"
+msgstr "Αναλογική είσοδος"
+
+#: ../src/modules/alsa/alsa-mixer.c:2337
+msgid "Dock Microphone"
+msgstr "Μικρόφωνο σταθμού αγκύρωσης"
+
+#: ../src/modules/alsa/alsa-mixer.c:2339
+msgid "Headset Microphone"
+msgstr "Μικρόφωνο ακουστικού"
+
+#: ../src/modules/alsa/alsa-mixer.c:2343
+msgid "Analog Output"
+msgstr "Αναλογική έξοδος"
+
+#: ../src/modules/alsa/alsa-mixer.c:2345
+msgid "LFE on Separate Mono Output"
+msgstr "LFE σε ξεχωριστή μονοφωνική έξοδο"
+
+#: ../src/modules/alsa/alsa-mixer.c:2346
+msgid "Line Out"
+msgstr "Γραμμή εξόδου"
+
+#: ../src/modules/alsa/alsa-mixer.c:2347
+msgid "Analog Mono Output"
+msgstr "Αναλογική μονοφωνική έξοδος"
+
+#: ../src/modules/alsa/alsa-mixer.c:2348
+msgid "Speakers"
+msgstr "Ηχεία"
+
+#: ../src/modules/alsa/alsa-mixer.c:2349
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / Θύρα εμφάνισης"
+
+#: ../src/modules/alsa/alsa-mixer.c:2350
+msgid "Digital Output (S/PDIF)"
+msgstr "Ψηφιακή έξοδος (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2351
+msgid "Digital Input (S/PDIF)"
+msgstr "Ψηφιακή είσοδος (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2352
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Ψηφιακή διέλευση (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3807
+msgid "Analog Mono"
+msgstr "Αναλογικό μονοφωνικό"
+
+#: ../src/modules/alsa/alsa-mixer.c:3808
+msgid "Analog Stereo"
+msgstr "Αναλογικό στερεοφωνικό"
+
+#: ../src/modules/alsa/alsa-mixer.c:3809
+msgid "Analog Surround 2.1"
+msgstr "Αναλογικός περιφερειακός ήχος 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3810
+msgid "Analog Surround 3.0"
+msgstr "Αναλογικός περιφερειακός ήχος 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3811
+msgid "Analog Surround 3.1"
+msgstr "Αναλογικός περιφερειακός ήχος 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3812
+msgid "Analog Surround 4.0"
+msgstr "Αναλογικός περιφερειακός ήχος 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3813
+msgid "Analog Surround 4.1"
+msgstr "Αναλογικός περιφερειακός ήχος 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3814
+msgid "Analog Surround 5.0"
+msgstr "Αναλογικός περιφερειακός ήχος 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3815
+msgid "Analog Surround 5.1"
+msgstr "Αναλογικός περιφερειακός ήχος 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3816
+msgid "Analog Surround 6.0"
+msgstr "Αναλογικός περιφερειακός ήχος 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3817
+msgid "Analog Surround 6.1"
+msgstr "Αναλογικός περιφερειακός ήχος 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3818
+msgid "Analog Surround 7.0"
+msgstr "Αναλογικός περιφερειακός ήχος 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3819
+msgid "Analog Surround 7.1"
+msgstr "Αναλογικός περιφερειακός ήχος 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3820
+msgid "Analog 4-channel Input"
+msgstr "Αναλογική 4κάναλη είσοδος"
+
+#: ../src/modules/alsa/alsa-mixer.c:3821
+msgid "Digital Stereo (IEC958)"
+msgstr "Ψηφιακό στερεοφωνικό (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3822
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Ψηφιακή διέλευση (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3823
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Ψηφιακός περιφερειακός ήχος 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3824
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Ψηφιακός περιφερειακός ήχος 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3825
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Ψηφιακός περιφερειακός ήχος 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3826
+msgid "Digital Stereo (HDMI)"
+msgstr "Ψηφιακός στερεοφωνικός (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3827
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Ψηφιακός περιφερειακός ήχος 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3956
+msgid "Analog Mono Duplex"
+msgstr "Αναλογικός μονοφωνικός αμφίδρομος"
+
+#: ../src/modules/alsa/alsa-mixer.c:3957
+msgid "Analog Stereo Duplex"
+msgstr "Αναλογικός στερεοφωνικός αμφίδρομος"
+
+#: ../src/modules/alsa/alsa-mixer.c:3958
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Ψηφιακός στερεοφωνικός αμφίδρομος (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3959
+#: ../src/modules/alsa/module-alsa-card.c:193
+#: ../src/modules/bluetooth/module-bluez4-device.c:2297
+#: ../src/modules/bluetooth/module-bluez5-device.c:1656
+msgid "Off"
+msgstr "Ανενεργό"
+
+#: ../src/modules/alsa/alsa-mixer.c:4058
+#, c-format
+msgid "%s Output"
+msgstr "Έξοδος %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:4066
+#, c-format
+msgid "%s Input"
+msgstr "Είσοδος %s"
+
+#: ../src/modules/alsa/alsa-sink.c:570 ../src/modules/alsa/alsa-sink.c:748
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Η ALSA ενεργοποιήθηκε για εγγραφή νέων δεδομένων στη συσκευή, αλλά δεν "
+"υπήρχε στην πραγματικότητα τίποτα για να γραφτεί!\n"
+"Κατά πάσα πιθανότητα αυτό είναι ένα σφάλμα του οδηγού ALSA '%s'. Παρακαλούμε "
+"αναφέρτε αυτό το θέμα στους προγραμματιστές ALSA.\n"
+"Ενεργοποίηση με ορισμό POLLOUT -- όμως η επόμενη snd_pcm_avail() επέστρεψε 0 "
+"ή άλλη τιμή < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529 ../src/modules/alsa/alsa-source.c:681
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Η ALSA ενεργοποιήθηκε για ανάγνωση νέων δεδομένων από τη συσκευή, αλλά δεν "
+"υπήρχε στην πραγματικότητα τίποτα για να διαβαστεί!\n"
+"Κατά πάσα πιθανότητα αυτό είναι ένα σφάλμα του οδηγού ALSA '%s'. Παρακαλούμε "
+"αναφέρτε αυτό το θέμα στους προγραμματιστές ALSA.\n"
+"Ενεργοποίηση με ορισμό POLLIN -- όμως η επόμενη snd_pcm_avail() επέστρεψε 0 "
+"ή άλλη τιμή < min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1135 ../src/modules/alsa/alsa-util.c:1210
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"Το snd_pcm_avail() επέστρεψε μια τιμή που είναι πολύ μεγάλη: %lu ψηφιολέξεις "
+"(%lu ms).\n"
+"Το πιθανότερο αυτό είναι ένα σφάλμα στον οδηγό ALSA '%s'. Παρακαλούμε, "
+"αναφέρτε αυτό το θέμα στους προγραμματιστές ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1185
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"Το snd_pcm_delay() επέστρεψε μια τιμή που είναι πολύ μεγάλη: %li ψηφιολέξεις "
+"(%s%lu ms).\n"
+"Το πιθανότερο αυτό είναι ένα σφάλμα στον οδηγό ALSA '%s'. Παρακαλούμε, "
+"αναφέρτε αυτό το θέμα στους προγραμματιστές ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1226
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"Το snd_pcm_avail_delay() επέστρεψε περίεργες τιμές: η καθυστέρηση %lu είναι "
+"μικρότερη από το avail %lu.\n"
+"Το πιθανότερο αυτό είναι ένα σφάλμα στον οδηγό ALSA '%s'. Παρακαλούμε, "
+"αναφέρτε αυτό το θέμα στους προγραμματιστές ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1269
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"Το snd_pcm_mmap_begin() επέστρεψε μια τιμή που είναι πολύ μεγάλη: %lu "
+"ψηφιολέξεις (%lu ms).\n"
+"Το πιθανότερο αυτό είναι ένα σφάλμα στον οδηγό ALSA '%s'. Παρακαλούμε, "
+"αναφέρτε αυτό το θέμα στους προγραμματιστές ALSA."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2091
+#: ../src/modules/bluetooth/module-bluez5-device.c:1441
+msgid "Headset"
+msgstr "Ακουστικά"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2096
+#: ../src/modules/bluetooth/module-bluez5-device.c:1446
+msgid "Handsfree"
+msgstr "Ανοιχτής ακρόασης"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2111
+#: ../src/modules/bluetooth/module-bluez5-device.c:1464
+msgid "Headphone"
+msgstr "Ακουστικό"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2116
+#: ../src/modules/bluetooth/module-bluez5-device.c:1469
+msgid "Portable"
+msgstr "Φορητό"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2121
+#: ../src/modules/bluetooth/module-bluez5-device.c:1474
+msgid "Car"
+msgstr "Αυτοκίνητο"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2126
+#: ../src/modules/bluetooth/module-bluez5-device.c:1479
+msgid "HiFi"
+msgstr "Υψηλή πιστότητα"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2131
+#: ../src/modules/bluetooth/module-bluez5-device.c:1484
+msgid "Phone"
+msgstr "Τηλέφωνο"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2139
+#: ../src/modules/bluetooth/module-bluez5-device.c:1436
+#: ../src/modules/bluetooth/module-bluez5-device.c:1452
+#: ../src/modules/bluetooth/module-bluez5-device.c:1490
+msgid "Bluetooth Output"
+msgstr "Έξοδος μπλουτούθ"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2142
+#: ../src/modules/bluetooth/module-bluez5-device.c:1435
+#: ../src/modules/bluetooth/module-bluez5-device.c:1457
+#: ../src/modules/bluetooth/module-bluez5-device.c:1463
+#: ../src/modules/bluetooth/module-bluez5-device.c:1489
+msgid "Bluetooth Input"
+msgstr "Είσοδος μπλουτούθ"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2178
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Αναπαραγωγή υψηλής ποιότητα (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2189
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Λήψη υψηλής ποιότητας (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2200
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Αμφίδρομη τηλεφωνία (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2212
+msgid "Handsfree Gateway"
+msgstr "Πύλη ανοιχτής ακρόασης"
+
+#. TODO: Change this profile's name to a2dp_sink, to reflect the remote
+#. * device's role and be consistent with the a2dp source profile
+#: ../src/modules/bluetooth/module-bluez5-device.c:1529
+#| msgid "High Fidelity Playback (A2DP)"
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Αναπαραγωγή υψηλής ποιότητας (δέκτης A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1540
+#| msgid "High Fidelity Capture (A2DP)"
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Λήψη υψηλής ποιότητας (πηγή A2DP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"source_name=<όνομα πηγής> source_properties=<ιδιότητες πηγής> "
+"source_master=<όνομα πηγής για φιλτράρισμα> sink_name=<όνομα δέκτη> "
+"sink_properties=<ιδιότητες δέκτη> sink_master=<όνομα δέκτη για φιλτράρισμα> "
+"adjust_time=<συχνότητα επαναρύθμισης ρυθμών σε s> adjust_threshold=<πόση "
+"μετατόπιση να επαναρυθμίσετε κατόπιν σε ms> format=<μορφή δείγματος> "
+"rate=<ρυθμός δειγμάτων> channels=<αριθμός καναλιών> channel_map=<απεικόνιση "
+"καναλιού> aec_method=<υλοποίηση για χρήση> aec_args=<παράμετροι για την "
+"μηχανή AEC> save_aec=<αποθήκευση δεδομένων AEC σε /tmp> autoloaded=<ορίστε "
+"αν αυτή η ενότητα είναι αυτόματα φορτωμένη> use_volume_sharing=<ναι ή όχι> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:756
+msgid "On"
+msgstr "Ενεργό"
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+"Κρατά πάντα τουλάχιστον έναν φορτωμένο δέκτη ακόμα κι αν είναι ένας μηδενικός"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Εικονική έξοδος"
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr "Ισοσταθμιστής γενικής χρήσης"
+
+#: ../src/modules/module-equalizer-sink.c:76
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<όνομα δέκτη> sink_properties=<ιδιότητες δέκτη> "
+"sink_master=<δέκτης για σύνδεση> format=<μορφή δείγματος> rate=<ρυθμός "
+"δειγμάτων> channels=<αριθμός καναλιών> channel_map=<απεικόνιση καναλιού> "
+"autoloaded=<πρίστε αν αυτή η ενότητα θα φορτώνεται αυτόματα> "
+"use_volume_sharing=<ναι ή όχι> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<αυτόματη εκφόρτωση αχρησιμοποίητων φίλτρων;>"
+
+#: ../src/modules/module-ladspa-sink.c:53
+msgid "Virtual LADSPA sink"
+msgstr "Εικονικός δέκτης LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:57
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<όνομα του δέκτη> sink_properties=<ιδιότητες δέκτη> master=<όνομα "
+"δέκτη για φιλτράρισμα> format=<μορφή δείγματος> rate=<ρυθμός δείγματος> "
+"channels=<αριθμός καναλιών> channel_map=<απεικόνιση καναλιού εισόδου> "
+"plugin=<όνομα προσθέτου ladspa> label=<ετικέτα προσθέτου ladspa> "
+"control=<κατάλογος που χωρίζεται με κόμματα των τιμών ελέγχου εισόδου> "
+"input_ladspaport_map=<κατάλογος που χωρίζεται με κόμματα των ονομάτων θυρών "
+"LADSPA> output_ladspaport_map=<κατάλογος που χωρίζεται με κόμματα των "
+"ονομάτων θυρών εξόδου LADSPA> "
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "Δέκτης NULL με χρονιστή"
+
+#: ../src/modules/module-null-sink.c:280
+msgid "Null Output"
+msgstr "Μηδενική έξοδος"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:907
+msgid "Output Devices"
+msgstr "Συσκευές εξόδου"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:908
+msgid "Input Devices"
+msgstr "Συσκευές εισόδου"
+
+#: ../src/modules/module-rygel-media-server.c:1065
+msgid "Audio on @HOSTNAME@"
+msgstr "Ήχος στο @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:293
+#: ../src/modules/module-tunnel-source-new.c:294
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Διόδευση για %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:483
+#: ../src/modules/module-tunnel-source-new.c:485
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Διόδευση για %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:49
+msgid "Virtual surround sink"
+msgstr "Εικονικός περιφερειακός ήχος δέκτη"
+
+#: ../src/modules/module-virtual-surround-sink.c:53
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/"
+"left_hrir.wav "
+msgstr ""
+"sink_name=<όνομα δέκτη> sink_properties=<ιδιότητες δέκτη> master=<όνομα "
+"δέκτη για φιλτράρισμα> format=<μορφή δείγματος> rate=<ρυθμός δειγμάτων> "
+"channels=<αριθμός καναλιών> channel_map=<απεικόνιση καναλιού> "
+"use_volume_sharing=<ναι ή όχι> force_flat_volume=<ναι ή όχι> hrir=/path/to/"
+"left_hrir.wav "
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "Διακομιστής ήχου PulseAudio"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:754
+msgid "Mono"
+msgstr "Μονοφωνικό"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Μπροστά κέντρο"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Μπροστά αριστερά"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Μπροστά δεξιά"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Πίσω κέντρο"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Πίσω αριστερά"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Πίσω δεξιά"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr "Υποβαθύφωνο"
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Μπροστά αριστερά από το κέντρο"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Μπροστά δεξιά από το κέντρο"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Πλάγια αριστερά"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Πλάγια δεξιά"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Βοηθητικό 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Βοηθητικό 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Βοηθητικό 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Βοηθητικό 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Βοηθητικό 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Βοηθητικό 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Βοηθητικό 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Βοηθητικό 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Βοηθητικό 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Βοηθητικό 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Βοηθητικό 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Βοηθητικό 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Βοηθητικό 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Βοηθητικό 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Βοηθητικό 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Βοηθητικό 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Βοηθητικό 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Βοηθητικό 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Βοηθητικό 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Βοηθητικό 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Βοηθητικό 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Βοηθητικό 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Βοηθητικό 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Βοηθητικό 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Βοηθητικό 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Βοηθητικό 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Βοηθητικό 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Βοηθητικό 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Βοηθητικό 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Βοηθητικό 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Βοηθητικό 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Βοηθητικό 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Επάνω στο κέντρο"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Πάνω εμπρός στο κέντρο"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Πάνω εμπρός αριστερά"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Πάνω εμπρός δεξιά"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Πάνω πίσω στο κέντρο"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Πάνω πίσω αριστερά"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Πάνω πίσω δεξιά"
+
+#: ../src/pulse/channelmap.c:481 ../src/pulse/format.c:123
+#: ../src/pulse/sample.c:177 ../src/pulse/volume.c:296
+#: ../src/pulse/volume.c:322 ../src/pulse/volume.c:342
+#: ../src/pulse/volume.c:374 ../src/pulse/volume.c:414
+#: ../src/pulse/volume.c:433
+msgid "(invalid)"
+msgstr "(άκυρο)"
+
+#: ../src/pulse/channelmap.c:758
+msgid "Stereo"
+msgstr "Στερεοφωνικό"
+
+#: ../src/pulse/channelmap.c:763
+msgid "Surround 4.0"
+msgstr "Περιφερειακός ήχος 4.0"
+
+#: ../src/pulse/channelmap.c:769
+msgid "Surround 4.1"
+msgstr "Περιφερειακός ήχος 4.1"
+
+#: ../src/pulse/channelmap.c:775
+msgid "Surround 5.0"
+msgstr "Περιφερειακός ήχος 5.0"
+
+#: ../src/pulse/channelmap.c:781
+msgid "Surround 5.1"
+msgstr "Περιφερειακός ήχος 5.1"
+
+#: ../src/pulse/channelmap.c:788
+msgid "Surround 7.1"
+msgstr "Περιφερειακός ήχος 7.1"
+
+#: ../src/pulse/client-conf-x11.c:55 ../src/utils/pax11publish.c:99
+msgid "xcb_connect() failed"
+msgstr "Αποτυχία xcb_connect()"
+
+#: ../src/pulse/client-conf-x11.c:60 ../src/utils/pax11publish.c:104
+msgid "xcb_connection_has_error() returned true"
+msgstr "Επιστροφή αληθούς της xcb_connection_has_error()"
+
+#: ../src/pulse/client-conf-x11.c:96
+msgid "Failed to parse cookie data"
+msgstr "Αποτυχία ανάλυσης δεδομένων μπισκότου"
+
+#: ../src/pulse/context.c:529
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "Δεν φορτώθηκαν μπισκότα. Προσπάθεια για σύνδεση χωρίς μπισκότα."
+
+#: ../src/pulse/context.c:610
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:665
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1366
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Ελήφθη μήνυμα για άγνωστη επέκταση '%s'"
+
+#: ../src/pulsecore/lock-autospawn.c:143 ../src/pulsecore/lock-autospawn.c:229
+msgid "Cannot access autospawn lock."
+msgstr "Αδύνατη η πρόσβαση κλειδώματος αυτόματης δημιουργίας."
+
+#: ../src/pulsecore/log.c:155
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Αποτυχία ανοίγματος αρχείου προορισμού '%s'."
+
+#: ../src/pulsecore/log.c:178
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Δοκιμάστηκε το άνοιγμα αρχείου προορισμού '%s', '%s.1', '%s.2' ... '%s.%d', "
+"αλλά όλα απέτυχαν."
+
+#: ../src/pulsecore/log.c:633
+msgid "Invalid log target."
+msgstr "Άκυρος προορισμός καταγραφής."
+
+#: ../src/pulsecore/sink.c:3428
+msgid "Built-in Audio"
+msgstr "Εσωτερικός ήχος"
+
+#: ../src/pulsecore/sink.c:3433
+msgid "Modem"
+msgstr "Μόντεμ"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "Εντάξει"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "Άρνηση πρόσβασης"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Άγνωστη εντολή"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Άκυρο όρισμα"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "Ύπαρξη οντότητας"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "Ανυπαρξία τέτοιας οντότητας"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "Άρνηση σύνδεσης"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "Σφάλμα πρωτοκόλου"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "Λήξη χρόνου"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "Χωρίς κλειδί εξουσιοδότησης"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "Εσωτερικό σφάλμα"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "Τερματισμός σύνδεσης"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "Τερματισμός οντότητας"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Άκυρος διακομιστής"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Αποτυχία αρχικοποίησης ενότητας"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Κακή κατάσταση"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Χωρίς δεδομένα"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "Ασύμβατη έκδοση πρωτοκόλλου"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Υπερβολικά μεγάλο"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "Δεν υποστηρίζεται"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Άγνωστος κωδικός σφάλματος"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "Ανυπαρξία τέτοιας επέκτασης"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Παλιά λειτουργία"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Έλλειψη υλοποίησης"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Διακλαδωμένος πελάτης"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Σφάλμα εισόδου/εξόδου"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Συσκευή ή πόροι είναι απασχολημένοι"
+
+#: ../src/pulse/sample.c:179
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:197
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Εμφάνιση της βοήθειας\n"
+"-v, --verbose                         Εκτύπωση μηνυμάτων αποσφαλμάτωσης\n"
+"      --from-rate=SAMPLERATE          Από ρυθμό δειγμάτων σε Hz (προεπιλογή "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      Από τύπο δείγματος (προεπιλογή s16le)\n"
+"      --from-channels=CHANNELS        Από αριθμό καναλιών (προεπιλογή 1)\n"
+"      --to-rate=SAMPLERATE            Σε ρυθμό δειγμάτων σε Hz (προεπιλογή "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        Σε τύπο δείγματος (προεπιλογή s16le)\n"
+"      --to-channels=CHANNELS          Σε αριθμό καναλιών (προεπιλογή 1)\n"
+"      --resample-method=METHOD        Μέθοδος επαναδειγματοληψίας "
+"(προεπιλογή αυτόματο)\n"
+"      --seconds=SECONDS               Από διάρκεια ροής (προεπιλογή 60)\n"
+"\n"
+"Αν οι μορφές δεν ορίζονται, η δοκιμή εκτελεί όλους τους συνδυασμούς "
+"μορφών, \n"
+"πίσω και μπρος.\n"
+"\n"
+"Ο τύπος δείγματος πρέπει να είναι ένας από s16le, s16be, u8, float32le, "
+"float32be, ulaw, alaw,\n"
+"s24le, s24be, s24-32le, s24-32be, s32le, s32be (προεπιλογή s16ne)\n"
+"\n"
+"Δείτε --dump-resample-methods για πιθανές τιμές των μεθόδων "
+"επαναδειγματοληψίας.\n"
+
+#: ../src/tests/resampler-test.c:356
+#, c-format
+msgid "%s %s\n"
+msgstr "%s %s\n"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+
+#: ../src/utils/pacat.c:118
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Αποτυχία εκκένωσης ροής: %s"
+
+#: ../src/utils/pacat.c:123
+msgid "Playback stream drained."
+msgstr "Εκκενώθηκε η ροή αναπαραγωγής."
+
+#: ../src/utils/pacat.c:134
+msgid "Draining connection to server."
+msgstr "Εκκενώνεται η σύνδεση στον διακομιστή."
+
+#: ../src/utils/pacat.c:147
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:170
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "Αποτυχία της pa_stream_write(): %s"
+
+#: ../src/utils/pacat.c:211
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "Αποτυχία της pa_stream_begin_write(): %s"
+
+#: ../src/utils/pacat.c:261 ../src/utils/pacat.c:291
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "Αποτυχία της pa_stream_peek(): %s"
+
+#: ../src/utils/pacat.c:341
+msgid "Stream successfully created."
+msgstr "Η ροή δημιουργήθηκε με επιτυχία."
+
+#: ../src/utils/pacat.c:344
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "Αποτυχία της pa_stream_get_buffer_attr(): %s"
+
+#: ../src/utils/pacat.c:348
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr ""
+"Μετρικά ενδιάμεσης μνήμης: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:351
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Μετρικά ενδιάμεσης μνήμης: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:355
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Χρήση της προδιαγραφής δείγματος '%s', απεικόνιση καναλιού '%s'."
+
+#: ../src/utils/pacat.c:359
+#, c-format
+#| msgid "Connected to device %s (%u, %ssuspended)."
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Σύνδεση με τη συσκευή %s (δείκτης: %u, σε αναστολή: %s)."
+
+#: ../src/utils/pacat.c:369
+#, c-format
+msgid "Stream error: %s"
+msgstr "Σφάλμα ροής: %s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Αναστολή συσκευή ροής.%s"
+
+#: ../src/utils/pacat.c:381
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Συνέχιση συσκευής ροής.%s"
+
+#: ../src/utils/pacat.c:389
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Ανεπάρκεια ροής.%s"
+
+#: ../src/utils/pacat.c:396
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Υπέρβαση ροής.%s"
+
+#: ../src/utils/pacat.c:403
+#, c-format
+msgid "Stream started.%s"
+msgstr "Εκκίνηση ροής.%s"
+
+#: ../src/utils/pacat.c:410
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Η ροή μετακινήθηκε στη συσκευή %s (%u, %s αναστολή).%s"
+
+#: ../src/utils/pacat.c:410
+msgid "not "
+msgstr "όχι "
+
+#: ../src/utils/pacat.c:417
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Αλλαγή γνωρισμάτων ενδιάμεσης μνήμης ροής.%s"
+
+#: ../src/utils/pacat.c:432
+msgid "Cork request stack is empty: corking stream"
+msgstr "Η στοίβα αίτησης κλεισίματος είναι κενή: ροή κλεισίματος"
+
+#: ../src/utils/pacat.c:438
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Η στοίβα αίτησης κλεισίματος είναι κενή: ροή ανοίγματος"
+
+#: ../src/utils/pacat.c:442
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+"Προειδοποίηση: Ελήφθησαν περισσότερες αιτήσεις ανοίγματος από κλεισίματα!"
+
+#: ../src/utils/pacat.c:467
+#, c-format
+msgid "Connection established.%s"
+msgstr "Επίτευξη σύνδεσης.%s"
+
+#: ../src/utils/pacat.c:470
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "Αποτυχία pa_stream_new(): %s"
+
+#: ../src/utils/pacat.c:508
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "Αποτυχία pa_stream_connect_playback(): %s"
+
+#: ../src/utils/pacat.c:514
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Αποτυχία ορισμού ροής οθόνης: %s"
+
+#: ../src/utils/pacat.c:518
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "Αποτυχία pa_stream_connect_record(): %s"
+
+#: ../src/utils/pacat.c:531 ../src/utils/pactl.c:1455
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Αποτυχία σύνδεσης: %s"
+
+#: ../src/utils/pacat.c:564
+msgid "Got EOF."
+msgstr "Ελήφθη EOF."
+
+#: ../src/utils/pacat.c:601
+#, c-format
+msgid "write() failed: %s"
+msgstr "Αποτυχία write(): %s"
+
+#: ../src/utils/pacat.c:622
+msgid "Got signal, exiting."
+msgstr "Ελήφθη σήμα, έξοδος."
+
+#: ../src/utils/pacat.c:636
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Αποτυχία λήψης λανθάνοντος χρόνου: %s"
+
+#: ../src/utils/pacat.c:641
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Χρόνος: %0.3f sec; λανθάνων χρόνος %0.0f usec."
+
+#: ../src/utils/pacat.c:662
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "Αποτυχία pa_stream_update_timing_info(): %s"
+
+#: ../src/utils/pacat.c:672
+#, c-format
+#| msgid ""
+#| "%s [options]\n"
+#| "\n"
+#| "  -h, --help                            Show this help\n"
+#| "      --version                         Show version\n"
+#| "\n"
+#| "  -r, --record                          Create a connection for "
+#| "recording\n"
+#| "  -p, --playback                        Create a connection for playback\n"
+#| "\n"
+#| "  -v, --verbose                         Enable verbose operations\n"
+#| "\n"
+#| "  -s, --server=SERVER                   The name of the server to connect "
+#| "to\n"
+#| "  -d, --device=DEVICE                   The name of the sink/source to "
+#| "connect to\n"
+#| "  -n, --client-name=NAME                How to call this client on the "
+#| "server\n"
+#| "      --stream-name=NAME                How to call this stream on the "
+#| "server\n"
+#| "      --volume=VOLUME                   Specify the initial (linear) "
+#| "volume in range 0...65536\n"
+#| "      --rate=SAMPLERATE                 The sample rate in Hz (defaults "
+#| "to 44100)\n"
+#| "      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+#| "s16be, u8, float32le,\n"
+#| "                                        float32be, ulaw, alaw, s32le, "
+#| "s32be, s24le, s24be,\n"
+#| "                                        s24-32le, s24-32be (defaults to "
+#| "s16ne)\n"
+#| "      --channels=CHANNELS               The number of channels, 1 for "
+#| "mono, 2 for stereo\n"
+#| "                                        (defaults to 2)\n"
+#| "      --channel-map=CHANNELMAP          Channel map to use instead of the "
+#| "default\n"
+#| "      --fix-format                      Take the sample format from the "
+#| "sink the stream is\n"
+#| "                                        being connected to.\n"
+#| "      --fix-rate                        Take the sampling rate from the "
+#| "sink the stream is\n"
+#| "                                        being connected to.\n"
+#| "      --fix-channels                    Take the number of channels and "
+#| "the channel map\n"
+#| "                                        from the sink the stream is being "
+#| "connected to.\n"
+#| "      --no-remix                        Don't upmix or downmix channels.\n"
+#| "      --no-remap                        Map channels by index instead of "
+#| "name.\n"
+#| "      --latency=BYTES                   Request the specified latency in "
+#| "bytes.\n"
+#| "      --process-time=BYTES              Request the specified process "
+#| "time per request in bytes.\n"
+#| "      --latency-msec=MSEC               Request the specified latency in "
+#| "msec.\n"
+#| "      --process-time-msec=MSEC          Request the specified process "
+#| "time per request in msec.\n"
+#| "      --property=PROPERTY=VALUE         Set the specified property to the "
+#| "specified value.\n"
+#| "      --raw                             Record/play raw PCM data.\n"
+#| "      --passthrough                     passthrough data \n"
+#| "      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+#| "      --list-file-formats               List available file formats.\n"
+#| "      --monitor-stream=INDEX            Record from the sink input with "
+#| "index INDEX.\n"
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Εμφάνιση της βοήθειας\n"
+"      --version                         Εμφάνιση έκδοσης\n"
+"\n"
+"  -r, --record                          Δημιουργία σύνδεσης για εγγραφή\n"
+"  -p, --playback                        Δημιουργία σύνδεσης για αναπαραγωγή\n"
+"\n"
+"  -v, --verbose                         Ενεργοποίηση λεπτομερών λειτουργιών\n"
+"\n"
+"  -s, --server=SERVER                   Το όνομα του διακομιστή για σύνδεση\n"
+"  -d, --device=DEVICE                   Το όνομα του δέκτη/προέλευσης για "
+"σύνδεση\n"
+"  -n, --client-name=NAME                Πώς να κληθεί ο πελάτης στον "
+"διακομιστή\n"
+"      --stream-name=NAME                Πώς να κληθεί η ροή στον διακομιστή\n"
+"      --volume=VOLUME                   Ορισμός της αρχικής (γραμμικής) "
+"έντασης στην περιοχή 0...65536\n"
+"      --rate=SAMPLERATE                 Ο ρυθμός δειγμάτων σε Hz (προεπιλογή "
+"το 44100)\n"
+"      --format=SAMPLEFORMAT             Ο τύπος δείγματος, ένας από s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (προεπιλογή το "
+"s16ne)\n"
+"      --channels=CHANNELS               Ο αριθμός των καναλιών, 1 για "
+"μονοφωνικό, 2 για στερεοφωνικό\n"
+"                                        (προεπιλογή το 2)\n"
+"      --channel-map=CHANNELMAP          Απεικόνιση καναλιού για χρήση αντί "
+"για την προεπιλογή\n"
+"      --fix-format                      Λήψη της μορφής δείγματος από τον "
+"δέκτη στον οποίον \n"
+"                                        συνδέεται η ροή.\n"
+"      --fix-rate                        Λήψη του ρυθμού δειγμάτων από τον "
+"δέκτη στον οποίο\n"
+"                                        συνδέεται η ροή.\n"
+"      --fix-channels                    Λήψη του αριθμού των καναλιών και "
+"της απεικόνισης καναλιού\n"
+"                                        από τον δέκτη στον οποίο συνδέεται η "
+"ροή.\n"
+"      --no-remix                        Να μην αναβαθμίζονται ή "
+"υποβαθμίζονται κανάλια.\n"
+"      --no-remap                        Απεικόνιση καναλιών με δείκτη αντί "
+"για όνομα.\n"
+"      --latency=BYTES                   Αίτηση του συγκεκριμένου λανθάνοντος "
+"χρόνου σε ψηφιολέξεις.\n"
+"      --process-time=BYTES              Αίτηση του συγκεκριμένου χρόνου "
+"διαδικασίας ανά αίτηση σε ψηφιολέξεις. \n"
+"      --latency-msec=MSEC               Αίτηση του συγκεκριμένου λανθάνοντος "
+"χρόνου σε msec.\n"
+"      --process-time-msec=MSEC          Αίτηση του συγκεκριμένου χρόνου "
+"διεργασίας ανά αίτηση σε msec.\n"
+"      --property=PROPERTY=VALUE         Ορισμός της συγκεκριμένης ιδιότητας "
+"στη συγκεκριμένη τιμή.\n"
+"      --raw                             Εγγραφή/αναπαραγωγή  ακατέργαστων "
+"δεδομένων PCM.\n"
+"      --passthrough                     διέλευση δεδομένων \n"
+"      --file-format[=FFORMAT]           Εγγραφή/αναπαραγωγή μορφοποιημένων "
+"δεδομένων PCM.\n"
+"      --list-file-formats               Κατάλογος διαθέσιμος μορφών αρχείων.\n"
+"      --monitor-stream=INDEX            Εγγραφή από την είσοδο του δέκτη με "
+"δείκτη INDEX.\n"
+
+#: ../src/utils/pacat.c:810
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Μεταγλωττισμένο με libpulse %s\n"
+"Συνδεμένο με libpulse %s\n"
+
+#: ../src/utils/pacat.c:843 ../src/utils/pactl.c:1651
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Άκυρο όνομα πελάτη '%s'"
+
+#: ../src/utils/pacat.c:858
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Άκυρο όνομα ροής '%s'"
+
+#: ../src/utils/pacat.c:895
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Άκυρη απεικόνιση καναλιού '%s'"
+
+#: ../src/utils/pacat.c:924 ../src/utils/pacat.c:938
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Άκυρη προδιαγραφή λανθάνοντος χρόνου '%s'"
+
+#: ../src/utils/pacat.c:931 ../src/utils/pacat.c:945
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Άκυρη προδιαγραφή χρόνου επεξεργασίας '%s'"
+
+#: ../src/utils/pacat.c:957
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Άκυρη ιδιότητα '%s'"
+
+#: ../src/utils/pacat.c:976
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Άγνωστη μορφή αρχείου %s."
+
+#: ../src/utils/pacat.c:991
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Αποτυχία ανάλυσης του ορίσματος για --monitor-stream"
+
+#: ../src/utils/pacat.c:1002
+msgid "Invalid sample specification"
+msgstr "Άκυρη προδιαγραφή δείγματος"
+
+#: ../src/utils/pacat.c:1012
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1017
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1024
+msgid "Too many arguments."
+msgstr "Υπερβολικά ορίσματα."
+
+#: ../src/utils/pacat.c:1035
+msgid "Failed to generate sample specification for file."
+msgstr "Αποτυχία δημιουργίας προδιαγραφής δείγματος για αρχείο."
+
+#: ../src/utils/pacat.c:1061
+msgid "Failed to open audio file."
+msgstr "Αποτυχία ανοίγματος αρχείου ήχου."
+
+#: ../src/utils/pacat.c:1067
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Προειδοποίηση: η συγκεκριμένη προδιαγραφή δείγματος θα αντικατασταθεί με την "
+"προδιαγραφή από το αρχείο."
+
+#: ../src/utils/pacat.c:1070 ../src/utils/pactl.c:1718
+msgid "Failed to determine sample specification from file."
+msgstr "Αποτυχία προσδιορισμού προδιαγραφής δείγματος από το αρχείο."
+
+#: ../src/utils/pacat.c:1079
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Προειδοποίηση: Αποτυχία προσδιορισμού απεικόνισης καναλιού από αρχείο."
+
+#: ../src/utils/pacat.c:1090
+msgid "Channel map doesn't match sample specification"
+msgstr "Η απεικόνιση καναλιού δεν ταιριάζει στην προδιαγραφή του δείγματος"
+
+#: ../src/utils/pacat.c:1101
+msgid "Warning: failed to write channel map to file."
+msgstr "Προειδοποίηση: αποτυχία εγγραφής απεικόνισης καναλιού σε αρχείο."
+
+#: ../src/utils/pacat.c:1116
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Άνοιγμα ροής %s με προδιαγραφή δείγματος '%s' και απεικόνιση καναλιού '%s'."
+
+#: ../src/utils/pacat.c:1117
+msgid "recording"
+msgstr "Ηχογράφηση"
+
+#: ../src/utils/pacat.c:1117
+msgid "playback"
+msgstr "Αναπαραγωγή"
+
+#: ../src/utils/pacat.c:1141
+msgid "Failed to set media name."
+msgstr "Αποτυχία ορισμού ονόματος μέσων."
+
+#: ../src/utils/pacat.c:1148 ../src/utils/pactl.c:2068
+msgid "pa_mainloop_new() failed."
+msgstr "Αποτυχία pa_mainloop_new()."
+
+#: ../src/utils/pacat.c:1171
+msgid "io_new() failed."
+msgstr "Αποτυχία io_new()."
+
+#: ../src/utils/pacat.c:1178 ../src/utils/pactl.c:2080
+msgid "pa_context_new() failed."
+msgstr "Αποτυχία pa_context_new()."
+
+#: ../src/utils/pacat.c:1186 ../src/utils/pactl.c:2086
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "Αποτυχία pa_context_connect(): %s"
+
+#: ../src/utils/pacat.c:1192
+msgid "pa_context_rttime_new() failed."
+msgstr "Αποτυχία pa_context_rttime_new()."
+
+#: ../src/utils/pacat.c:1199 ../src/utils/pactl.c:2091
+msgid "pa_mainloop_run() failed."
+msgstr "Αποτυχία pa_mainloop_run()."
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pactl.c:1573
+msgid "NAME [ARGS ...]"
+msgstr "ΟΝΟΜΑ [ΟΡΙΣΜΑΤΑ ...]"
+
+#: ../src/utils/pacmd.c:54 ../src/utils/pacmd.c:62 ../src/utils/pactl.c:1574
+msgid "NAME|#N"
+msgstr "ΟΝΟΜΑ|#Ν"
+
+#: ../src/utils/pacmd.c:55 ../src/utils/pacmd.c:65 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1578
+msgid "NAME"
+msgstr "ΟΝΟΜΑ"
+
+#: ../src/utils/pacmd.c:56
+msgid "NAME|#N VOLUME"
+msgstr "ΟΝΟΜΑ| ΕΝΤΑΣΗ #Ν"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N VOLUME"
+msgstr "#Ν ΕΝΤΑΣΗ"
+
+#: ../src/utils/pacmd.c:58 ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1576
+msgid "NAME|#N 1|0"
+msgstr "ΟΝΟΜΑ|#Ν 1|0"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N 1|0"
+msgstr "#Ν 1|0"
+
+#: ../src/utils/pacmd.c:60
+msgid "NAME|#N KEY=VALUE"
+msgstr "ΟΝΟΜΑ|#Ν KEY=VALUE"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N KEY=VALUE"
+msgstr "#N KEY=VALUE"
+
+#: ../src/utils/pacmd.c:63
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:64
+msgid "NAME SINK|#N"
+msgstr "ΟΝΟΜΑ ΔΕΚΤΗ|#Ν"
+
+#: ../src/utils/pacmd.c:66 ../src/utils/pacmd.c:67
+msgid "NAME FILENAME"
+msgstr "ΟΝΟΜΑΣΙΑ ΟΝΟΜΑΤΟΣ ΑΡΧΕΙΟΥ"
+
+#: ../src/utils/pacmd.c:68
+msgid "PATHNAME"
+msgstr "ΟΝΟΜΑ ΔΙΑΔΡΟΜΗΣ"
+
+#: ../src/utils/pacmd.c:69
+msgid "FILENAME SINK|#N"
+msgstr "ΟΝΟΜΑ ΑΡΧΕΙΟΥ ΔΕΚΤΗ|#Ν"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pactl.c:1575
+msgid "#N SINK|SOURCE"
+msgstr "#Ν ΔΕΚΤΗΣ|ΠΗΓΗ"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pacmd.c:79 ../src/utils/pacmd.c:80
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1577
+msgid "CARD PROFILE"
+msgstr "ΚΑΤΑΤΟΜΗ ΚΑΡΤΑΣ"
+
+#: ../src/utils/pacmd.c:75 ../src/utils/pactl.c:1579
+msgid "NAME|#N PORT"
+msgstr "ΟΝΟΜΑ|ΘΥΡΑ #Ν"
+
+#: ../src/utils/pacmd.c:76 ../src/utils/pactl.c:1585
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "ΟΝΟΜΑ ΚΑΡΤΑΣ|ΚΑΡΤΑ-#Ν ΑΝΤΙΣΤΑΘΜΙΣΗ ΘΥΡΑΣ"
+
+#: ../src/utils/pacmd.c:77
+msgid "TARGET"
+msgstr "ΠΡΟΟΡΙΣΝΟΣ"
+
+#: ../src/utils/pacmd.c:78
+msgid "NUMERIC LEVEL"
+msgstr "ΑΡΙΘΜΗΤΙΚΟ ΕΠΙΠΕΔΟ"
+
+#: ../src/utils/pacmd.c:81
+msgid "FRAMES"
+msgstr "ΠΛΑΙΣΙΑ"
+
+#: ../src/utils/pacmd.c:83
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Εμφάνιση της βοήθειας\n"
+"      --version                         Εμφάνιση έκδοσης\n"
+"Όταν δεν δίνεται καμιά εντολή pacmd ξεκινά στην διαδραστική λειτουργία.\n"
+
+#: ../src/utils/pacmd.c:130
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Μεταγλωττισμένο με libpulse %s\n"
+"Συνδεμένο με libpulse %s\n"
+
+#: ../src/utils/pacmd.c:144
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Δεν εκτελείται ο δαίμονας PulseAudio, ή δεν εκτελείται ως δαίμονας συνεδρίας."
+
+#: ../src/utils/pacmd.c:149
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:166
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:174
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Αποτυχία τερματισμού δαίμονα PulseAudio."
+
+#: ../src/utils/pacmd.c:182
+msgid "Daemon not responding."
+msgstr "Ο δαίμονας δεν απαντά."
+
+#: ../src/utils/pacmd.c:214 ../src/utils/pacmd.c:323 ../src/utils/pacmd.c:341
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:270
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:281 ../src/utils/pacmd.c:301
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:166
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Αποτυχία λήψης στατιστικών: %s"
+
+#: ../src/utils/pactl.c:172
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Χρησιμοποιούνται τώρα: %u ομάδες που περιέχουν συνολικά %s ψηφιολέξεις.\n"
+
+#: ../src/utils/pactl.c:175
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Κατανεμημένα κατά τη διάρκεια του συνολικού χρόνου ζωής: %u ομάδες που "
+"περιέχουν συνολικά %s ψηφιολέξεις.\n"
+
+#: ../src/utils/pactl.c:178
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "μέγεθος κρυφής μνήμης δείγματος: %s\n"
+
+#: ../src/utils/pactl.c:187
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Αποτυχία λήψης πληροφοριών διακομιστή: %s"
+
+#: ../src/utils/pactl.c:192
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Συμβολοσειρά διακομιστή: %s\n"
+"Έκδοση πρωτοκόλλου βιβλιοθήκης: %u\n"
+"Έκδοση πρωτοκόλλου διακομιστή: %u\n"
+"Είναι τοπικό: %s\n"
+"Δείκτης πελάτη: %u\n"
+"Μέγεθος παράθεσης: %zu\n"
+
+#: ../src/utils/pactl.c:208
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Όνομα χρήστη: %s\n"
+"Όνομα οικοδεσπότη: %s\n"
+"Όνομα διακομιστή: %s\n"
+"Έκδοση διακομιστή: %s\n"
+"Προεπιλεγμένη προδιαγραφή δείγματος: %s\n"
+"Προεπιλεγμένη απεικόνιση καναλιού: %s\n"
+"Προεπιλεγμένος δέκτης: %s\n"
+"Προεπιλεγμένη πηγή: %s\n"
+"Μπισκότο: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:257 ../src/utils/pactl.c:902 ../src/utils/pactl.c:980
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Αποτυχία λήψης πληροφοριών δέκτη: %s"
+
+#: ../src/utils/pactl.c:283
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Δέκτης #%u\n"
+"\tΚατάσταση: %s\n"
+"\tΌνομα: %s\n"
+"\tΠεριγραφή: %s\n"
+"\tΟδηγός: %s\n"
+"\tΠροδιαγραφή δείγματος: %s\n"
+"\tΑπεικόνιση καναλιού: %s\n"
+"\tΕνότητα κατόχου: %u\n"
+"\tΣίγαση: %s\n"
+"\tΈνταση: %s\n"
+"\t        ισορροπία %0.2f\n"
+"\tΒασική ένταση: %s\n"
+"\tΠηγή οθόνης: %s\n"
+"\tΛανθάνων χρόνος: %0.0f usec, ρυθμισμένο %0.0f usec\n"
+"\tΣημαίες: %s%s%s%s%s%s%s\n"
+"\tΙδιότητες:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:327 ../src/utils/pactl.c:433 ../src/utils/pactl.c:594
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tΘύρες:\n"
+
+#: ../src/utils/pactl.c:334 ../src/utils/pactl.c:440
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tΕνεργή θύρα: %s\n"
+
+#: ../src/utils/pactl.c:340 ../src/utils/pactl.c:446
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tΜορφές:\n"
+
+#: ../src/utils/pactl.c:364 ../src/utils/pactl.c:922 ../src/utils/pactl.c:995
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Αποτυχία λήψης πληροφοριών πηγής: %s"
+
+#: ../src/utils/pactl.c:390
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Πηγή #%u\n"
+"\tΚατάσταση: %s\n"
+"\tΌνομα: %s\n"
+"\tΠεριγραφή: %s\n"
+"\tΟδηγός: %s\n"
+"\tΠροδιαγραφή δείγματος: %s\n"
+"\tΑπεικόνιση καναλιού: %s\n"
+"\tΕνότητα κατόχου: %u\n"
+"\tΣίγαση: %s\n"
+"\tΈνταση: %s\n"
+"\t        ισορροπία %0.2f\n"
+"\tΒασική ένταση: %s\n"
+"\tΟθόνη δέκτη: %s\n"
+"\tΛανθάνων χρόνος: %0.0f usec, ρυθμισμένο %0.0f usec\n"
+"\tΣημαίες: %s%s%s%s%s%s\n"
+"\tΙδιότητες:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:418 ../src/utils/pactl.c:488 ../src/utils/pactl.c:531
+#: ../src/utils/pactl.c:573 ../src/utils/pactl.c:671 ../src/utils/pactl.c:672
+#: ../src/utils/pactl.c:683 ../src/utils/pactl.c:741 ../src/utils/pactl.c:742
+#: ../src/utils/pactl.c:753 ../src/utils/pactl.c:804 ../src/utils/pactl.c:805
+#: ../src/utils/pactl.c:811
+msgid "n/a"
+msgstr "μη διαθέσιμο"
+
+#: ../src/utils/pactl.c:457 ../src/utils/pactl.c:861
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Αποτυχία λήψης πληροφοριών ενότητας: %s"
+
+#: ../src/utils/pactl.c:480
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Ενότητα #%u\n"
+"\tΌνομα: %s\n"
+"\tΌρισμα: %s\n"
+"\tΜετρητής χρήσης: %s\n"
+"\tΙδιότητες:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:499
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Αποτυχία λήψης πληροφοριών πελάτη: %s"
+
+#: ../src/utils/pactl.c:525
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Πελάτης #%u\n"
+"\tΟδηγός: %s\n"
+"\tΕνότητα κατόχου: %s\n"
+"\tΙδιότητες:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:542
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Αποτυχία λήψης πληροφοριών κάρτας: %s"
+
+#: ../src/utils/pactl.c:565
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Κάρτα #%u\n"
+"\tΌνομα: %s\n"
+"\tΟδηγός: %s\n"
+"\tΕνότητα κατόχου: %s\n"
+"\tΙδιότητες:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tΚατατομές:\n"
+
+#: ../src/utils/pactl.c:588
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tΕνεργή κατατομή: %s\n"
+
+#: ../src/utils/pactl.c:602
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tΙδιότητες:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:607
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tΤμήμα των κατατομών: %s"
+
+#: ../src/utils/pactl.c:624 ../src/utils/pactl.c:942 ../src/utils/pactl.c:1010
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Αποτυχία λήψης πληροφοριών εισόδου δέκτη: %s"
+
+#: ../src/utils/pactl.c:653
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Είσοδος δέκτη #%u\n"
+"\tΟδηγός: %s\n"
+"\tΕνότητα κατόχου: %s\n"
+"\tΠελάτης: %s\n"
+"\tΔέκτης: %u\n"
+"\tΠροδιαγραφή δείγματος: %s\n"
+"\tΑπεικόνιση καναλιού: %s\n"
+"\tΜορφή: %s\n"
+"\tCorked: %s\n"
+"\tΣίγαση: %s\n"
+"\tΈνταση: %s\n"
+"\t        ισορροπία %0.2f\n"
+"\tΛανθάνων χρόνος ενδιάμεσης μνήμης: %0.0f usec\n"
+"\tΛανθάνων χρόνος δέκτη: %0.0f usec\n"
+"\tΜέθοδος επαναδειγματοληψίας: %s\n"
+"\tΙδιότητες:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:694 ../src/utils/pactl.c:962 ../src/utils/pactl.c:1025
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Αποτυχία λήψης πληροφοριών εξόδου πηγής: %s"
+
+#: ../src/utils/pactl.c:723
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Έξοδος πηγής #%u\n"
+"\tΟδηγός: %s\n"
+"\tΕνότητα κατόχου: %s\n"
+"\tΠελάτης: %s\n"
+"\tΔέκτης: %u\n"
+"\tΠροδιαγραφή δείγματος: %s\n"
+"\tΑπεικόνιση καναλιού: %s\n"
+"\tΜορφή: %s\n"
+"\tCorked: %s\n"
+"\tΣίγαση: %s\n"
+"\tΈνταση: %s\n"
+"\t        ισορροπία %0.2f\n"
+"\tΛανθάνων χρόνος ενδιάμεσης μνήμης: %0.0f usec\n"
+"\tΛανθάνων χρόνος δέκτη: %0.0f usec\n"
+"\tΜέθοδος επαναδειγματοληψίας: %s\n"
+"\tΙδιότητες:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:764
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Αποτυχία λήψης πληροφοριών δείγματος: %s"
+
+#: ../src/utils/pactl.c:791
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Δείγμα #%u\n"
+"\tΌνομα: %s\n"
+"\tΠροδιαγραφή δείγματος: %s\n"
+"\tΑπεικόνιση καναλιού: %s\n"
+"\tΈνταση: %s\n"
+"\t        ισορροπία %0.2f\n"
+"\tΔιάρκεια: %0.1fs\n"
+"\tΜέγεθος: %s\n"
+"\tΟκνηρότητα: %s\n"
+"\tΌνομα αρχείου: %s\n"
+"\tΙδιότητες:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:819 ../src/utils/pactl.c:829
+#, c-format
+msgid "Failure: %s"
+msgstr "Αποτυχία: %s"
+
+#: ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Αποτυχία εκφόρτωσης ενότητας: η ενότητα %s δεν φορτώθηκε"
+
+#: ../src/utils/pactl.c:886
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr "Αποτυχία ορισμού έντασης: Προσπαθήσατε να ορίσετε εντάσεις για %d κανάλια, "
+"ενώ τα υποστηριζόμενα κανάλια είναι %d\n"
+
+#: ../src/utils/pactl.c:1052
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Αποτυχία ορισμού μορφής: άκυρη συμβολοσειρά μορφής %s"
+
+#: ../src/utils/pactl.c:1095
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Αποτυχία αποστολής δείγματος: %s"
+
+#: ../src/utils/pactl.c:1112
+msgid "Premature end of file"
+msgstr "Πρόωρο τέλος του αρχείου"
+
+#: ../src/utils/pactl.c:1132
+msgid "new"
+msgstr "νέο"
+
+#: ../src/utils/pactl.c:1135
+msgid "change"
+msgstr "αλλαγή"
+
+#: ../src/utils/pactl.c:1138
+msgid "remove"
+msgstr "αφαίρεση"
+
+#: ../src/utils/pactl.c:1141 ../src/utils/pactl.c:1176
+msgid "unknown"
+msgstr "άγνωστο"
+
+#: ../src/utils/pactl.c:1149
+msgid "sink"
+msgstr "δέκτης"
+
+#: ../src/utils/pactl.c:1152
+msgid "source"
+msgstr "πηγή"
+
+#: ../src/utils/pactl.c:1155
+msgid "sink-input"
+msgstr "είσοδος δέκτη"
+
+#: ../src/utils/pactl.c:1158
+msgid "source-output"
+msgstr "έξοδος πηγής"
+
+#: ../src/utils/pactl.c:1161
+msgid "module"
+msgstr "ενότητα"
+
+#: ../src/utils/pactl.c:1164
+msgid "client"
+msgstr "πελάτης"
+
+#: ../src/utils/pactl.c:1167
+msgid "sample-cache"
+msgstr "κρυφή μνήμη δείγματος"
+
+#: ../src/utils/pactl.c:1170
+msgid "server"
+msgstr "διακομιστής"
+
+#: ../src/utils/pactl.c:1173
+msgid "card"
+msgstr "κάρτα"
+
+#: ../src/utils/pactl.c:1182
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Συμβάν '%s' στο %s #%u\n"
+
+#: ../src/utils/pactl.c:1461
+msgid "Got SIGINT, exiting."
+msgstr "Ελήφθη SIGINT, έξοδος."
+
+#: ../src/utils/pactl.c:1488
+msgid "Invalid volume specification"
+msgstr "Άκυρη προδιαγραφή έντασης"
+
+#: ../src/utils/pactl.c:1511
+msgid "Volume outside permissible range.\n"
+msgstr "Η ένταση εκτός επιτρεπτής περιοχής.\n"
+
+#: ../src/utils/pactl.c:1524
+#| msgid "Invalid volume specification"
+msgid "Invalid number of volume specifications.\n"
+msgstr "Άκυρος αριθμός προδιαγραφών έντασης.\n"
+
+#: ../src/utils/pactl.c:1536
+#| msgid "Invalid volume specification"
+msgid "Inconsistent volume specification.\n"
+msgstr "Ασύμβατη προδιαγραφή έντασης.\n"
+
+#: ../src/utils/pactl.c:1566 ../src/utils/pactl.c:1567
+#: ../src/utils/pactl.c:1568 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1570 ../src/utils/pactl.c:1571
+#: ../src/utils/pactl.c:1572 ../src/utils/pactl.c:1573
+#: ../src/utils/pactl.c:1574 ../src/utils/pactl.c:1575
+#: ../src/utils/pactl.c:1576 ../src/utils/pactl.c:1577
+#: ../src/utils/pactl.c:1578 ../src/utils/pactl.c:1579
+#: ../src/utils/pactl.c:1580 ../src/utils/pactl.c:1581
+#: ../src/utils/pactl.c:1582 ../src/utils/pactl.c:1583
+#: ../src/utils/pactl.c:1584 ../src/utils/pactl.c:1585
+#: ../src/utils/pactl.c:1586
+msgid "[options]"
+msgstr "[επιλογές]"
+
+#: ../src/utils/pactl.c:1568
+msgid "[TYPE]"
+msgstr "[ΤΥΠΟΣ]"
+
+#: ../src/utils/pactl.c:1570
+msgid "FILENAME [NAME]"
+msgstr "ΟΝΟΜΑΑΡΧΕΙΟΥ [ΟΝΟΜΑ]"
+
+#: ../src/utils/pactl.c:1571
+msgid "NAME [SINK]"
+msgstr "ΟΝΟΜΑ [ΔΕΚΤΗΣ]"
+
+#: ../src/utils/pactl.c:1580
+#| msgid "NAME|#N VOLUME"
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "ΟΝΟΜΑ| ΕΝΤΑΣΗ #Ν [ΕΝΤΑΣΗ ...]"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#N ΕΝΤΑΣΗ [ΕΝΤΑΣΗ ...]"
+
+#: ../src/utils/pactl.c:1582
+msgid "NAME|#N 1|0|toggle"
+msgstr "ΟΝΟΜΑ|#Ν 1|0|εναλλαγή"
+
+#: ../src/utils/pactl.c:1583
+msgid "#N 1|0|toggle"
+msgstr "#Ν 1|0|εναλλαγή"
+
+#: ../src/utils/pactl.c:1584
+msgid "#N FORMATS"
+msgstr "ΜΟΡΦΕΣ #Ν"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Τα ειδικά ονόματα @DEFAULT_SINK@, @DEFAULT_SOURCE@ και @DEFAULT_MONITOR@\n"
+"μπορούν να χρησιμοποιηθούν ορίζοντας τον προεπιλεγμένο δέκτη, πηγή και "
+"οθόνη.\n"
+
+#: ../src/utils/pactl.c:1590
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Εμφάνιση αυτής της βοήθειας\n"
+"      --version                         Εμφάνιση έκδοσης\n"
+"\n"
+"  -s, --server=SERVER                   Το όνομα του διακομιστή για σύνδεση\n"
+"  -n, --client-name=NAME                Πώς να κληθεί αυτός ο πελάτης στον "
+"διακομιστή\n"
+
+#: ../src/utils/pactl.c:1631
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Μεταγλωττισμένο με libpulse %s\n"
+"Συνδεμένο με libpulse %s\n"
+
+#: ../src/utils/pactl.c:1690
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Ορίστε τίποτα, ή ένα από τα: %s"
+
+#: ../src/utils/pactl.c:1700
+msgid "Please specify a sample file to load"
+msgstr "Παρακαλούμε ορίστε ένα αρχείο δείγματος για φόρτωση"
+
+#: ../src/utils/pactl.c:1713
+msgid "Failed to open sound file."
+msgstr "Αποτυχία ανοίγματος αρχείου ήχου."
+
+#: ../src/utils/pactl.c:1725
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+"Προειδοποίηση: Αποτυχία προσδιορισμού προδιαγραφής δείγματος από αρχείο."
+
+#: ../src/utils/pactl.c:1735
+msgid "You have to specify a sample name to play"
+msgstr "Πρέπει να ορίσετε ένα όνομα δείγματος για αναπαραγωγή"
+
+#: ../src/utils/pactl.c:1747
+msgid "You have to specify a sample name to remove"
+msgstr "Πρέπει να ορίσετε ένα όνομα δείγματος για αφαίρεση"
+
+#: ../src/utils/pactl.c:1756
+msgid "You have to specify a sink input index and a sink"
+msgstr "Πρέπει να ορίσετε έναν δείκτη εισόδου δέκτη και έναν δέκτη"
+
+#: ../src/utils/pactl.c:1766
+msgid "You have to specify a source output index and a source"
+msgstr "Πρέπει να ορίσετε έναν δείκτη εξόδου πηγής και μια πηγή"
+
+#: ../src/utils/pactl.c:1781
+msgid "You have to specify a module name and arguments."
+msgstr "Πρέπει να ορίσετε ένα όνομα ενότητας και ορίσματα."
+
+#: ../src/utils/pactl.c:1801
+msgid "You have to specify a module index or name"
+msgstr "Πρέπει να ορίσετε έναν δείκτη ενότητας ή όνομα"
+
+#: ../src/utils/pactl.c:1814
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Δεν μπορείτε να ορίσετε περισσότερους από έναν δέκτη. Πρέπει να ορίσετε μια "
+"τιμή Μπουλ."
+
+#: ../src/utils/pactl.c:1819 ../src/utils/pactl.c:1839
+msgid "Invalid suspend specification."
+msgstr "Άκυρη προδιαγραφή αναστολής."
+
+#: ../src/utils/pactl.c:1834
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Δεν μπορείτε να ορίσετε περισσότερες από μία πηγές. Πρέπει να ορίσετε μια "
+"τιμή Μπουλ."
+
+#: ../src/utils/pactl.c:1851
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Πρέπει να ορίσετε ένα όνομα/δείκτη κάρτας και ένα όνομα κατατομής"
+
+#: ../src/utils/pactl.c:1862
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Πρέπει να ορίσετε ένα όνομα/δείκτη δέκτη και ένα όνομα θύρας"
+
+#: ../src/utils/pactl.c:1873
+msgid "You have to specify a sink name"
+msgstr "Πρέπει να ορίσετε ένα όνομα δέκτη"
+
+#: ../src/utils/pactl.c:1883
+msgid "You have to specify a source name/index and a port name"
+msgstr "Πρέπει να ορίσετε ένα όνομα/δείκτη πηγής και ένα όνομα θύρας"
+
+#: ../src/utils/pactl.c:1894
+msgid "You have to specify a source name"
+msgstr "Πρέπει να ορίσετε ένα όνομα πηγής"
+
+#: ../src/utils/pactl.c:1904
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Πρέπει να ορίσετε ένα όνομα/δείκτη δέκτη και μια ένταση"
+
+#: ../src/utils/pactl.c:1917
+msgid "You have to specify a source name/index and a volume"
+msgstr "Πρέπει να ορίσετε ένα όνομα/δείκτη πηγής και μια ένταση"
+
+#: ../src/utils/pactl.c:1930
+msgid "You have to specify a sink input index and a volume"
+msgstr "Πρέπει να ορίσετε έναν δείκτη εισόδου δέκτη και μια ένταση"
+
+#: ../src/utils/pactl.c:1935
+msgid "Invalid sink input index"
+msgstr "Άκυρος δείκτης εισόδου δέκτη"
+
+#: ../src/utils/pactl.c:1946
+msgid "You have to specify a source output index and a volume"
+msgstr "Πρέπει να ορίσετε έναν δείκτη εξόδου πηγής και μια ένταση"
+
+#: ../src/utils/pactl.c:1951
+msgid "Invalid source output index"
+msgstr "Άκυρος δείκτης εξόδου πηγής"
+
+#: ../src/utils/pactl.c:1962
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "Πρέπει να ορίσετε έναν δείκτη/όνομα δέκτη και μια τιμή Μπουλ σίγασης"
+
+#: ../src/utils/pactl.c:1967 ../src/utils/pactl.c:1982
+#: ../src/utils/pactl.c:2002 ../src/utils/pactl.c:2020
+msgid "Invalid mute specification"
+msgstr "Άκυρη προδιαγραφή σίγασης"
+
+#: ../src/utils/pactl.c:1977
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Πρέπει να ορίσετε ένα όνομα/δείκτη πηγής και μια τιμή Μπουλ σίγασης"
+
+#: ../src/utils/pactl.c:1992
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "Πρέπει να ορίσετε έναν δείκτη εισόδου δέκτη και μια τιμή Μπουλ σίγασης"
+
+#: ../src/utils/pactl.c:1997
+msgid "Invalid sink input index specification"
+msgstr "Άκυρη προδιαγραφή δείκτη εισόδου δέκτη"
+
+#: ../src/utils/pactl.c:2010
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "Πρέπει να ορίσετε έναν δείκτη εξόδου πηγής και μια τιμή Μπουλ σίγασης"
+
+#: ../src/utils/pactl.c:2015
+msgid "Invalid source output index specification"
+msgstr "Άκυρη προδιαγραφή δείκτη εξόδου πηγής"
+
+#: ../src/utils/pactl.c:2032
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Πρέπει να ορίσετε έναν δείκτη δέκτη και έναν κατάλογο χωριζόμενο με ; των "
+"υποστηριζόμενων μορφών"
+
+#: ../src/utils/pactl.c:2044
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr ""
+"Πρέπει να ορίσετε ένα όνομα/δείκτη κάρτας, ένα όνομα θύρας και μια "
+"μετατόπιση λανθάνοντος χρόνου"
+
+#: ../src/utils/pactl.c:2051
+msgid "Could not parse latency offset"
+msgstr "Αδύνατη η ανάλυση μετατόπισης λανθάνοντος χρόνου"
+
+#: ../src/utils/pactl.c:2063
+msgid "No valid command specified."
+msgstr "Δεν ορίστηκε έγκυρη εντολή."
+
+#: ../src/utils/pasuspender.c:81
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:94
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:113
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Αποτυχία συνέχισης: %s\n"
+
+#: ../src/utils/pasuspender.c:147
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Αποτυχία αναστολής: %s\n"
+
+#: ../src/utils/pasuspender.c:172
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr ""
+"ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Ο διακομιστής ήχου δεν είναι τοπικός, δεν αναστέλλεται.\n"
+
+#: ../src/utils/pasuspender.c:185
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Αποτυχία σύνδεσης: %s\n"
+
+#: ../src/utils/pasuspender.c:203
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Ελήφθη SIGINT, έξοδος.\n"
+
+#: ../src/utils/pasuspender.c:221
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Η θυγατρική διεργασία τελείωσε με σήμα %u\n"
+
+#: ../src/utils/pasuspender.c:230
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Εμφάνιση της βοήθειας\n"
+"      --version                         Εμφάνιση έκδοσης\n"
+"  -s, --server=SERVER                   Το όνομα του διακομιστή για σύνδεση\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:268
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Μεταγλωττισμένο με libpulse %s\n"
+"Συνδεμένο με libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:297
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "Αποτυχία pa_mainloop_new().\n"
+
+#: ../src/utils/pasuspender.c:310
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "Αποτυχία pa_context_new().\n"
+
+#: ../src/utils/pasuspender.c:322
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "Αποτυχία pa_mainloop_run().\n"
+
+#: ../src/utils/pax11publish.c:60
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Εμφάνιση τρεχόντων δεδομένων PulseAudio που είναι προσαρτημένα σε "
+"προβολή X11 (προεπιλογή)\n"
+" -e    Εξαγωγή τοπικών δεδομένων PulseAudio σε προβολή X11\n"
+" -i    Εισαγωγή δεδομένων PulseAudio από προβολή X11 σε τοπικές μεταβλητές "
+"περιβάλλοντος και αρχείου μπισκότων.\n"
+" -r    Αφαίρεση δεδομένων PulseAudio από προβολή X11\n"
+
+#: ../src/utils/pax11publish.c:93
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Αποτυχία ανάλυσης γραμμής εντολών.\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Server: %s\n"
+msgstr "Διακομιστής: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Source: %s\n"
+msgstr "Πηγή: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Δέκτης: %s\n"
+
+#: ../src/utils/pax11publish.c:118
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Μπισκότο: %s\n"
+
+#: ../src/utils/pax11publish.c:136
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Αποτυχία ανάλυσης δεδομένων μπισκότου\n"
+
+#: ../src/utils/pax11publish.c:141
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Αποτυχία αποθήκευσης δεδομένων μπισκότου\n"
+
+#: ../src/utils/pax11publish.c:156
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "Αποτυχία φόρτωσης αρχείου ρυθμίσεων πελάτη.\n"
+
+#: ../src/utils/pax11publish.c:161
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "Αποτυχία ανάγνωσης δεδομένων διαμόρφωσης περιβάλλοντος.\n"
+
+#: ../src/utils/pax11publish.c:178
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Αποτυχία λήψης FQDN.\n"
+
+#: ../src/utils/pax11publish.c:198
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Αποτυχία φόρτωσης δεδομένων μπισκότου\n"
+
+#: ../src/utils/pax11publish.c:216
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Δεν έχει υλοποιηθεί ακόμα.\n"
+
+#~ msgid "PulseAudio Sound System KDE Routing Policy"
+#~ msgstr "Η πολιτική δρομολόγησης KDE συστήματος ήχου PulseAudio"
+
+#~ msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+#~ msgstr ""
+#~ "Εκκίνηση του συστήματος ήχου PulseAudio με πολιτική δρομολόγησης KDE"
+
+#~ msgid "Failed to open configuration file '%s': %s"
+#~ msgstr "Αποτυχία ανοίγματος αρχείου ρυθμίσεων '%s': %s"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Digital Surround 4.0 (IEC958)"
diff --git a/po/es.po b/po/es.po
new file mode 100644 (file)
index 0000000..d75c7ee
--- /dev/null
+++ b/po/es.po
@@ -0,0 +1,3346 @@
+# Fedora Spanish translation of PulseAudio.
+# This file is distributed under the same license as the PulseAudio Package.
+#
+# Domingo Becker <domingobecker@gmail.com>, 2009.
+# Héctor Daniel Cabrera <h.daniel.cabrera@gmail.com>, 2009.
+# Fernando Gonzalez Blanco <fgonz@fedoraproject.org>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PulseAudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:53+0000\n"
+"Last-Translator: Fernando Gonzalez Blanco <fgonz@fedoraproject.org>\n"
+"Language-Team: Spanish <fedora-trans-es@redhat.com>\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Spanish\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() devolvió un valor que es excepcionalmente grande: %lu bytes "
+"(%lu ms).\n"
+"Lo más probable es que sea un error del controlador ALSA '%s'. Por favor, "
+"informe ésto a los desarrolladores de ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() devolvió un valor que es excepcionalmente grande: %li bytes "
+"(%s%lu ms).\n"
+"Lo más probable es que sea un error del controlador ALSA '%s'. Por favor, "
+"informe ésto a los desarrolladores de ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() devolvió un valor que es excepcionalmente grande: %lu bytes "
+"(%lu ms).\n"
+"Lo más probable es que sea un error del controlador ALSA '%s'. Por favor, "
+"informe ésto a los desarrolladores de ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() devolvió un valor que es excepcionalmente grande: %lu "
+"bytes (%lu ms).\n"
+"Lo más probable es que sea un error del controlador ALSA '%s'. Por favor, "
+"informe ésto a los desarrolladores de ALSA."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Siempre tenga al menos un sumidero cargado aunque sea uno nulo"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Salida Boba"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "Sumidero virtual LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<nombre para el sumidero> sink_properties=<propiedades para el "
+"sumidero> master=<nombre del sumidero a filtrar> format=<formato de ejemplo> "
+"rate=<tasa de ejemplo> channels=<cantidad de canaless> channel_map=<mapeo de "
+"canales> plugin=<nombre del complemento ladspa> label=<etiqueta del "
+"complemento ladspa> control=<lista separada por comas de valores de control "
+"de entrada>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "Sumidero nulo sincronizado"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Salida Nula"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "Audio Interno"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "Módem"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Falló al buscar cargador el cargador llt_dlopen original."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "Falló al asignar el cargador dl nuevo."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "Falló al agregar bind-now-loader."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "Se obtuvo la señal %s."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "Saliendo."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Falló al buscar usuario '%s'."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Falló al buscar grupo '%s'."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Se encontró el usuario '%s' (UID %lu) y el grupo '%s' (GID %lu)."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID del usuario '%s' y del grupo '%s' no son similares."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "El directorio de inicio del usuario '%s' no es '%s', ignorando."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Falló al crear '%s': %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Falló al cambiar la lista de grupo: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Falló al cambiar GID: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Falló al cambiar UID: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "Se han liberado con éxitos los privilegios de root."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "El modo a nivel de sistema no es soportado en esta plataforma."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) falló: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "Falló al analizar la línea de comando."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "El demonio no está funcionando"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "El demonio está funcionando como PID %u"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "No se ha podido detener el demonio: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Este programa no tiene por qué ser ejecutado como root (a menos que --system "
+"sea especificado)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Se necesitan privilegios de root."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start no está soportado para las instancias del sistema."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr ""
+"Ejecutándose en modo de sistema, ¡pero no se ha configurado --disallow-exit! "
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"Ejecutándose en modo de sistema, ¡pero no se ha configurado --disallow-"
+"module-loading!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr ""
+"Ejecutándose en modo de sistema, ¡desactivando forzadamente el modo SHM!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"Ejecutándose en modo de sistema, ¡desactivando forzadamente exit idle time!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "Fallo al intentar adquirir stdio."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "Falló el pipe: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "Falló el fork(): %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "Falló la operación read(): %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "Falló el inicio del demonio. "
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "El demonio se inició exitosamente."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "Falló la operación read(): %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Esto es PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Host de compilación: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "Compilación CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "Ejecutándose en el host: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "Se encontraron %u CPUs."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "El tamaño de la página es de %lu bytes"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Soporte para compilar con Valgrind: si"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Soporte para compilar con Valgrind: no"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Ejecutándose en modo valgrind: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "Ejecutándose en el host: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Build optimizado: si"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "Build optimizado: no"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG definido, todos los chequeos deshabilitados."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH definido, sólo se deshabilitan los chequeos fast path."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "Todos los chequeos habilitados."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "Fallo al intentar obtener el ID de la máquina"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "El ID de la máquina es %s"
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "El ID de la sesión es %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Utilizando directorio de tiempo de ejecución %s."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "Utilizando directorio de estado %s."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "Utilizando directorio de módulos %s."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Ejecutándose en modo de sistema: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Bien, o sea que está ejecutando PA en modo de sistema. Por favor entienda "
+"que, en general, no debería estar haciéndolo.\n"
+"Si insiste en seguir utilizando este modo, será debido a su propio accionar "
+"que las cosas no funcionen como se esperaba.\n"
+"Por favor lea http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ para obtener una explicación "
+"acerca de por qué es una mala idea utilizar el  modo sistema."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "Ha fallado pa_pid_file_create()."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr ""
+"¡Existen cronómetros de alta resolución fresquitos y disponibles! ¡Bon "
+"appetit!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"¡Amigo, su kernel deja mucho que desear! ¡El plato que hoy recomienda el "
+"chef es Linux con cronómetros de alta resolución activados!  "
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "Falló pa_core_new()."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "Fallo al intentar iniciar el demonio."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"El demonio se ha iniciado sin ningún módulo cargado, y por ello se niega a "
+"funcionar."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "El demonio se inició completamente."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "Comienza a apagarse el demonio."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "El demonio se ha apagado."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [opciones]\n"
+"\n"
+"COMANDOS:\n"
+"  -h, --help                            Muestra esta ayuda\n"
+"      --version                         Muestra la versión\n"
+"      --dump-conf                       Vuelca la configuración por defecto\n"
+"      --dump-modules                    Vuelca una lista de múdulos "
+"disponibles\n"
+"      --dump-resample-methods           Vuelca los métodos disponibles de "
+"remuestreo\n"
+"      --cleanup-shm                     Limpia los segmentos de memoria "
+"compartidos\n"
+"      --start                           Inicia el demonio, si es que aún no "
+"está funcionando\n"
+"  -k  --kill                            Detiene a un demonio funcionando\n"
+"      --check                           Verifica qué demonios están "
+"funcionando\n"
+"\n"
+"OPCIONES:\n"
+"      --system[=BOOL]                   Se ejecuta como unica instancia a "
+"nivel del sistema\n"
+"  -D, --daemonize[=BOOL]                Se convierte en demonio luego de "
+"iniciarse\n"
+"      --fail[=BOOL]                     Se cierra cuando falla el inicio\n"
+"      --high-priority[=BOOL]            Trata de establecer un nivel de nice "
+"alto\n"
+"                                        (sólo disponible como root, cuando "
+"el SUID o\n"
+"                                        con RLIMIT_NICE) elevado\n"
+"      --realtime[=BOOL]                 Trata de activar planificación en "
+"tiempo real\n"
+"                                        (sólo disponible como root, cuando "
+"el SUID o\n"
+"                                        con RLIMIT_RTPRIO) elevado\n"
+"      --disallow-module-loading[=BOOL]  No permite la carga/descarga del "
+"módulo por el usuario\n"
+"                                        después que se haya iniciado\n"
+"      --disallow-exit[=BOOL]            No permite la petición del usuario "
+"de abandonar el programa\n"
+"      --exit-idle-time=SECS             Desactiva un demonio cuando está "
+"ocioso y\n"
+"                                        ha transcurrido esta cantidad de "
+"tiempo\n"
+"      --module-idle-time=SECS           Descarga modulos que se han cargado "
+"automáticamente cuando están ociosos y\n"
+"                                        ha transcurrido esta cantidad de "
+"tiempo\n"
+"      --scache-idle-time=SECS           Descarga muestras cargadas "
+"automáticamente cuando están\n"
+"                                        ociosos y ha transcurrido esta "
+"cantidad de tiempo\n"
+"      --log-level[=LEVEL]               Aumenta o define el grado de salida "
+"a utilizar\n"
+"  -v                                    Aumenta el grado de salida\n"
+"      --log-target={auto,syslog,stderr} Especifica el destino del log\n"
+"  -p, --dl-search-path=PATH             Establece la ruta de búsqueda "
+"(search path) para complementos\n"
+"                                        (plugins) compartidos\n"
+"      --resample-method=METHOD          Utiliza un método de remuestreo "
+"específico\n"
+"                                        (Ver en --dump-resample-methods los "
+"valores posibles)\n"
+"      --use-pid-file[=BOOL]             Crea el archivo PID\n"
+"      --no-cpu-limit[=BOOL]             No instala un limitador de carga de "
+"CPU en\n"
+"                                        plataformas que lo soporten.\n"
+"      --disable-shm[=BOOL]              Deshabilita el soporte para memoria "
+"compartida.\n"
+"\n"
+"SCRIPT DE INICIO:\n"
+"  -L, --load=\"ARGUMENTOS DEL MODULO\"  Carga el módulo complemento con los "
+"parámetros dados\n"
+"  -F, --file=FILENAME                   Ejecuta el script especificado\n"
+"  -C                                    Abre una línea de comando en el TTY "
+"actual después de iniciar\n"
+"\n"
+"  -n                                    No carga el archivo script "
+"predeterminado\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize espera un argumento booleano"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail espera un argumento booleano"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level espera un argumento en el nivel del log (ya sea numérico, que "
+"caiga en el rango de 0..4; ya sea uno de debug, info, notice, warn, o "
+"error). "
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority espera un argumento booleano"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime espera un argumento booleano"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading espera un argumento booleano"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit espera un argumento booleano"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use pid-file espera un argumento booleano"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "Log target inválido: use o \"syslog\", o \"stderr\", o \"auto\"."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time espera un argumento booleano"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta espera un argumento booleano"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Método de remuestreo inválido '%s'"
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system espera un argumento booleano"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit espera un argumento booleano"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm espera un argumento booleano"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Nombre: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "No existe información disponible acerca del módulo\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Versión: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Descripción: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Uso: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Carga una vez: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "ADVERTENCIA DE COMPATIBILIDAD: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Ruta: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Destino de log inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Nivel de log inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Método de remuestreo inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Rlimit inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Formato de muestra inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Tasa de muestra inválida '%s'."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Canales de muestra inválidos '%s'."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Mapa de canal inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Cantidad de fragmentoa inválidos '%s'."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Tamaño inválido de fragmento '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Nivel de nice inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Tasa de muestra inválida '%s'."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "No se pudo abrir el archivo de configuración: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"El mapa de canal predeterminado especificado tiene un número de canales "
+"distinto al especificado como predeterminado."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Leyendo desde el archivo de confioguración: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "Abandonando privilegios."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Sistema de Sonido PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Iniciar el Sistema de Sonido PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "Sistema de Sonido PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "Iniciar el Sistema de Sonido PulseAudio"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Frente central"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Frente izquierdo"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Frente derecho"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Posterior central"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "POsterior izquierdo"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "POsterior derecho"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Frente izquierdo del centro"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Frente derecho del centro"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Lateral izquierdo"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Lateral derecho"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Auxiliar 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Auxiliar 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Auxiliar 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Auxiliar 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Auxiliar 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Auxiliar 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Auxiliar 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Auxiliar 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Auxiliar 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Auxiliar 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Auxiliar 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Auxiliar 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Auxiliar 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Auxiliar 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Auxiliar 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Auxiliar 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Auxiliar 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Auxiliar 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Auxiliar 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Auxiliar 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Auxiliar 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Auxiliar 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Auxiliar 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Auxiliar 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Auxiliar 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Auxiliar 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Auxiliar 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Auxiliar 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Auxiliar 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Auxiliar 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Auxiliar 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Auxiliar 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Central superior"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Central frontal superior"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Frontal superior izquierdo"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Frontal superior derecho"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Posterior central superior "
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Posterior izquierdo superior"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Posterior derecho superior"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(inválido)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "Estéreo"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "Envolvente 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "Envolvente 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "Envolvente 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "Envolvente 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "Envolvente 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "Acceso negado"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Comando desconocido"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Argumento inválido"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "Entidad existente"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "No existe tal entidad"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "Conexión negada"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "Error de protocolo"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "Timeout"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "Sin hay llave de autorización"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "Error interno"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "Conexión finalizada"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "Entidad terminada"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Servidor inválido"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Falló la inicialización del módulo"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Mal estado"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Sin datos"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "Versión de protocolo incompatible"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Demasiado largo"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "No soportado"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Código de error desconocido"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "No existe tal extensión"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Funcionalidad Obsoleta"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Falta implementación"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Cliente iniciado"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Error de Entrada/Salida"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Dispositivo o recurso ocupado"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() falló: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "Fallo al analizar los datos de la cookie"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "Fallo al abrir el archivo de configuración '%s': %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "No se ha cargado ninguna cookie. Intentando conectar de todos modos."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(:) %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Se ha recibido un mensaje para una extensión desconocida '%s'"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Falló al drenar el flujo: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "El flujo de reproducción ha sido drenado."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "Drenando conexión con el servidor."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() falló: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_write() falló: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() falló: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "Se ha creado exitosamente el flujo (stream)."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() falló: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Métrica del búfer: maxlenght=%u, tlenghth=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Métrica del búfer: maxlenght=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Utilizando especificaciones de muestra '%s', mapa del canal '%s'."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "Conectado al dispositivo %s (%u, %ssuspended)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "Error de flujo: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Dispositivo de flujo suspendido.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Dispositivo de flujo reestablecido.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Flujo agotado.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Flujo saturado.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "Flujo iniciado.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Fujo trasladado al dispositivo %s (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "no"
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Los atributos del búfer de flujo han cambiado.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "Conexión establecida.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() falló: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() falló: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() falló: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Error en la conexión: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "Se tiene EOF."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() falló: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "Hay señal, saliendo (exiting)."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "No se pudo obtener latencia: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Tiempo: %0.3f seg; Latencia: %0.0f useg."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() falló: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [opciones]\n"
+"\n"
+"  -h, --help                            Muestra esta ayuda\n"
+"      --version                         Muestra la versión\n"
+"\n"
+"  -r, --record                          Crea una conexión para grabar\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Habilita operaciones con vocabulario "
+"más detallado\n"
+"\n"
+"  -s, --server=SERVER                   El nombre del servidor con el que "
+"conectarse\n"
+"  -d, --device=DEVICE                   El nombre del sumidero/fuente a la "
+"que conectarse\n"
+"  -n, --client-name=NAME                Cómo llamar a este cliente en el "
+"servidor\n"
+"      --stream-name=NAME                Cómo llamar a este flujo en el "
+"servidor\n"
+"      --volume=VOLUME                   Especifica el salida inicial "
+"(linear) de volumen dentro del rango 0...65536\n"
+"      --rate=SAMPLERATE                 Tasa de muestra en Hz (establecida "
+"en 44100 por defecto)\n"
+"      --format=SAMPLEFORMAT             El tipo de ejemplo, alguno entre "
+"s16le, s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be "
+"(establecido en s16ne por defecto)\n"
+"      --channels=CHANNELS               La cantidad de canales, 1 para mono, "
+"2 para stereo\n"
+"                                        (establecido en 2 por defecto)\n"
+"      --channel-map=CHANNELMAP          Mapeo de canales a ser usado en "
+"lugar del establecido por defecto\n"
+"      --fix-format                      Obtener el formato de ejemplo desde "
+"el sumidero al que el flujo\n"
+"                                        se ha conectado.\n"
+"      --fix-rate                        Obtiene la tasa de ejemplo desde el "
+"destino al que el flujo\n"
+"                                        se ha conectado.\n"
+"      --fix-channels                    Obtener el mapa y la cantidad de "
+"canales\n"
+"                                        desde el sumidero al que el flujo se "
+"ha conectado.\n"
+"      --no-remix                        No realiza un upmix o un downmix de "
+"los canales.\n"
+"      --no-remap                        Mapea canales por índices en lugar "
+"de por nombres.\n"
+"      --latency=BYTES                   Solicita la latencia especificada en "
+"bytes.\n"
+"      --process-time=BYTES              Solicita los procesos de tiempo por "
+"pedido especificados en bytes.\n"
+"      --property=PROPERTY=VALUE         Estabelce la propiedad especificada "
+"al valor especificado.\n"
+"      --raw                             Graba/reproduce datos PCM con "
+"formato raw.\n"
+"      --file-format=FFORMAT             Graba/reproduce datos PCM "
+"formateados.\n"
+"      --list-file-formats               Muestra una lista con los formatos "
+"de archivo disponibles.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Compilado con libpulse %s\n"
+"Linkeado con libpulse %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Nombre de cliente '%s' inválido"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Nombre de flujo '%s' inválido"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Mapa de canales '%s' inválido"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Especificación de latencia '%s' inválida"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Especificación de tiempo de proceso '%s' inválida"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Propiedad '%s' inválida"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Formato de archivo desconocido %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "Especificación de muestra inválida"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open() %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "Demasiados argumentos."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "Falló al generar especificación de ejemplo para el archivo."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "Falló al abrir el archivo de sonido."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Aviso: el ejemplo de especificación indicado será sobreescrito con las "
+"especificaciones del archivo."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "Falló al determinar especificación de ejemplo del archivo."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Aviso: Falló al determinar el mapeo del canal desde el archivo."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "El mapa del canal no se corresponde con la especificación de muestra"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "Aviso: Faló al escribir el mapeo del canal en el archivo."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Abriendo un flujo %s con especificación de muestra '%s' y mapeo de canal "
+"'%s'."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "grabando"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "playback"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "Falló al analizar la línea de comando."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() falló."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() falló."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() falló."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() falló: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() falló."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() falló."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Error al suspender: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Error al continuar: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "AVISO: El servidor de sonido no es local, no se suspende.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Error en la conexión: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Hay SIGINT, saliendo.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "AVISO: El proceso niño terminado por la señal %u\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [opciones] ... \n"
+"\n"
+"  -h, --help                            Muestra esta ayuda\n"
+"      --version                         Muestra la versión\n"
+"  -s, --server=SERVER                   El nombre del servidor con el que "
+"conectarse\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Compilado con libpulse %s\n"
+"Linkeado con libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() falló.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() falló.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() falló.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Error al intentar obtener estadísticas: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Actualmente en uso: %u bloques conteniendo %s bytes en total.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Ubicados durante a lo largo del tiempo: %u bloques conteniendo %s bytes en "
+"total.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Tamaño del cache de muestra: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Error al intentar obtener información del servidor: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Nombre de usuario: %s\n"
+"Nombre del equipo: %s\n"
+"Nombre del servidor: %s\n"
+"Versión del servidor: %s\n"
+"Especificación de muestra por defecto: %s\n"
+"Mapa de canal por defecto: %s\n"
+"Sumidero por defecto: %s\n"
+"Fuente por defecto: %s\n"
+"Cookie: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Error al intentar obtener información del sumidero: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sumidero #%u\n"
+"\tEstado: %s\n"
+"\tNombre: %s\n"
+"\tDescripción: %s\n"
+"\tControlador: %s\n"
+"\tEspecificación de la Muestra: %s\n"
+"\tMapa del Canal: %s\n"
+"\tMódulo Dueño: %u\n"
+"\tMudo: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tVolume Base: %s%s%s\n"
+"\tMonitorear Fuente: %s\n"
+"\tLatencia: %0.0f usec, configurado %0.0f useg\n"
+"\tBanderas: %s%s%s%s%s%s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPuertos:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tPuerto Activo: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tPuertos:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Error al intentar obtener información de la fuente: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Fuente #%u\n"
+"\tEstado: %s\n"
+"\tNombre: %s\n"
+"\tDescripción: %s\n"
+"\tControlador: %s\n"
+"\tEspecificación de la Muestra: %s\n"
+"\tMapa del Canal: %s\n"
+"\tMódulo Dueño: %u\n"
+"\tMudo: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tVolume Base: %s%s%s\n"
+"\tMonitoreo del Sumidero: %s\n"
+"\tLatencia: %0.0f usec, configurado %0.0f useg\n"
+"\tBanderas: %s%s%s%s%s%s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Error al intentar obtener información del módulo: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Módulo #%u\n"
+"\tNombre: %s\n"
+"\tArgumento: %s\n"
+"\tContador de uso: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Error al intentar obtener información del cliente: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Cliente #%u\n"
+"\tControlador: %s\n"
+"\tMódulo dueño: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Fallo al obtener la información de la placa: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Placa #%u\n"
+"\tNombre: %s\n"
+"\tControlador: %s\n"
+"\tMódulo dueño: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tPerfiles:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tPerfil Activo: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Error al intentar obtener información de entrada del sumidero: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Entrada del sumidero #%u\n"
+"\tControlador: %s\n"
+"\tMódulo dueño: %s\n"
+"\tCliente: %s\n"
+"\tSumidero: %u\n"
+"\tEspecificación de muestra: %s\n"
+"\tMapa de canales: %s\n"
+"\tMudo: %s\n"
+"\tVolumen: %s\n"
+"\t         %s\n"
+"\t         balance %0.2f\n"
+"\tLatencia del búfer: %0.0f useg\n"
+"\tLatencia del sumidero: %0.0f useg\n"
+"\tMétodo de remuestreo: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Falló al obtener información de salida de la fuente: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Entrada del sumidero #%u\n"
+"\tControlador: %s\n"
+"\tMódulo dueño: %s\n"
+"\tCliente: %s\n"
+"\tSumidero: %u\n"
+"\tEspecificación de muestra: %s\n"
+"\tMapa de canales: %s\n"
+"\tMudo: %s\n"
+"\tVolumen: %s\n"
+"\t         %s\n"
+"\t         balance %0.2f\n"
+"\tLatencia del búfer: %0.0f useg\n"
+"\tLatencia del sumidero: %0.0f useg\n"
+"\tMétodo de remuestreo: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Falló al obtener información de la muestra: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Muestra #%u\n"
+"\tNombre: %s\n"
+"\tEspecificaciones de la muestra: %s\n"
+"\tMapa del canal: %s\n"
+"\tVolumen: %s\n"
+"\t         %s\n"
+"\t         balance %0.2f\n"
+"\tDuración: %0.1fs\n"
+"\tTamaño: %s\n"
+"\tLazy: %s\n"
+"\tNombre del archivo: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "Falla: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Error al intentar obtener información de la fuente: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Falló al subir muestra: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "Fin de archivo prematuro"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr "destino"
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr "fuente"
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+#, fuzzy
+msgid "source-output"
+msgstr "fuente"
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "Servidor inválido"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "Hay un SIGINT, saliendo."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "Especificación de volumen inválida"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [opciones] ... \n"
+"\n"
+"  -h, --help                            Muestra esta ayuda\n"
+"      --version                         Muestra la versión\n"
+"  -s, --server=SERVER                   El nombre del servidor con el que "
+"conectarse\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Compilado con libpulse %s\n"
+"Linked con libpulse %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "Por favor, especifique un archivo de muestra a cargar"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "Error al intentar abrir el archivo de sonido."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+"Aviso: Falló al intentar determinar especificación de la muestra desde el "
+"archivo."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "Debe especificar un nombre de muestra para reproducir"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "Debe especificar un nombre de muestra a eliminar"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "Debe especificar un índice para la entrada al sumidero y un sumidero"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "Debe especificar un índice para las salida de la fuente y una fuente"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "Debe especificar un nombre de módulo y los argumentos."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "Debe especificar un índice de módulo"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"No puede especificar más de un sumidero. Tiene que especificar un valor "
+"booleano."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"No puede especificar más de una fuente. Tiene que especificar un valor "
+"booleano."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Debe especificar un nombre/índice de placa y un nombre de perfil"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Debe especificar un nombre/índice de sumidero y un nombre de puerto"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "Debe especificar un nombre/índice de fuente y un nombre de puerto"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Debe especificar un nombre/índice de sumidero y el volumen"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "Debe especificar un nombre/índice de fuente y un volumen"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "Debe especificar un índice de sumidero y un volumen"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "Indice de entrada a sumidero inválido"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "Debe especificar un índice para las salida de la fuente y una fuente"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "Indice de entrada a sumidero inválido"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "Debe especificar un nombre/índice de sumidero y un booleano para mudo"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "Especificación de muestra inválida"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Debe especificar un nombre/índice de fuente y un booleano para mudo"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr ""
+"Debe especificar un índice de entrada a sumidero y un booleano para mudo"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "Especificación de índice de entrada a sumidero inválida"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "Debe especificar un nombre/índice de fuente y un booleano para mudo"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "Especificación de índice de entrada a sumidero inválida"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "Debe especificar un nombre/índice de sumidero y un booleano para mudo"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "No se ha especificado ningún comando válido."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Muestra los datos actuales de PulseAudio asociados en un display X11 "
+"(por defecto)\n"
+" -e    Exporta los datos locales de PulseAudio a un display X11\n"
+" -i    Importa los datos de PulseAudio de un display X11 hacia las variables "
+"del entorno local y el archivo de cookies.\n"
+" -r    Elimina todo dato de PulseAudio de un display X11\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Error al interpretar una línea de comando.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "Servidor: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "Fuente: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Destino: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Error al intepretar datos de cookie\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Error al intentar guardar datos de cookie\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "Error al intentar cargar el archivo de configuración del cliente.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "Error al intentar leer datos de configuración de entorno.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Error al obtener FQDN.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Error al cargar datos de cookie\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Aún no se ha implementado.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"El demonio PulseAudio no está ejecutándose, o no se está ejecutando como un "
+"demonio de sesión."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Error al intentar detener el demonio de PulseAudio."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "El demonio no responde."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "No se puede acceder al candado de autogeneración."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nos despertó para escribir nuevos datos al dispositivo, ¡pero en "
+"realidad no hay nada para escribir!\n"
+"Probablemente sea un error en el controlador ALSA '%s'. Por favor, informe "
+"esto a los desarrolladores de ALSA.\n"
+"Nos despertaron con POLLOUT puesto -- sin embargo, una llamada a "
+"snd_pcm_avail() devolvió 0 u otro valor < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nos despertó para leer nuevos datos desde el dispositivo, ¡pero en "
+"realidad no hay nada para leer!\n"
+"Lo más probable es que sea un error del controlador ALSA '%s'. Por favor, "
+"informe esto a los desarrolladores de ALSA.\n"
+"Nos despertaron con POLLIN puesto -- sin embargo, una llamada a snd_pcm_avail"
+"() devolvió 0 u otro valor < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "Apagado"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Reproducción de Alta Fidelidad (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Captura de Alta Fidelidad (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telefonía Duplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "Servidor de Sonido PulseAudio"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "Dispositivos de salida"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "Dispositivos de entrada"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "Audio en @HOSTNAME@"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "Entrada"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "Estación dock de entrada"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "Micrófono de la estación Dock"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "Estación dock de entrada"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "En línea"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "Micrófono"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "Micrófono de la estación Dock"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "Micrófono"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "Micrófono externo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "Micrófono interno"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "Vídeo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "Control automático de ganancia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "Sin control automático de ganancia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "Incremento de ganancia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "Sin incremento de ganancia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "Amplificador"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "Sin amplificador"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "Incremento de ganancia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "Sin incremento de ganancia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "Auriculares analógicos"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "Entrada analógica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "Micrófono de la estación Dock"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "Salida analógica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "Salida analógica (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "En línea"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "Salida Mono analógica "
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "Estéreo Analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "Estéreo Digital (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Estéreo Digital (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "Mono Analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "Estéreo Analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "Análogico Envolvente 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "Análogico Envolvente 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "Análogico Envolvente 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "Análogo Envolvente 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "Análogo Envolvente 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "Análogo Envolvente 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "Análogo Envolvente 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "Análogico Envolvente 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "Análogico Envolvente 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "Análogico Envolvente 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "Análogo Envolvénte 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "Estéreo Digital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Estéreo Digital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digital Envolvente 4.0 (IEC9588/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digital Envolvente 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "Estéreo Digital (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digital Envolvente 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "Mono Analógico.Duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "Estéreo Analógico.Duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Estéreo Digital Duplex(IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Salida Nula"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "Entrada"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<nombre para el sumidero> sink_properties=<propiedades para el "
+"sumidero> master=<nombre del sumidero a filtrar> format=<formato de ejemplo> "
+"rate=<tasa de ejemplo> channels=<cantidad de canaless> channel_map=<mapeo de "
+"canales> plugin=<nombre del complemento ladspa> label=<etiqueta del "
+"complemento ladspa> control=<lista separada por comas de valores de control "
+"de entrada>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] Rlimit no soportado en esta plataforma."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() falló"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Salida de la fuente #%u\n"
+#~ "\tControlador: %s\n"
+#~ "\tMódulo dueño: %s\n"
+#~ "\tCliente: %s\n"
+#~ "\tFuente: %u\n"
+#~ "\tEspecificación de muestra: %s\n"
+#~ "\tMapa del canal: %s\n"
+#~ "\tLatencia del búfer: %0.0f useg\n"
+#~ "\tLatencia de la fuente: %0.0f useg\n"
+#~ "\tMétodo de remuestreo: %s\n"
+#~ "\tPropiedades:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [opciones] stat\n"
+#~ "%s [opciones] list\n"
+#~ "%s [opciones] exit\n"
+#~ "%s [opciones] upload-sample FILENAME [NAME]\n"
+#~ "%s [opciones] play-sample NAME [SINK]\n"
+#~ "%s [opciones] remove-sample NAME\n"
+#~ "%s [opciones] move-sink-input ID SINK\n"
+#~ "%s [opciones] move-source-output ID SOURCE\n"
+#~ "%s [opciones] load-module NAME [ARGS ...]\n"
+#~ "%s [opciones] unload-module ID\n"
+#~ "%s [opciones] suspend-sink [SINK] 1|0\n"
+#~ "%s [opciones] suspend-source [SOURCE] 1|0\n"
+#~ "%s [opciones] set-card-profile [CARD] [PROFILE] \n"
+#~ "%s [opciones] set-sink-port [SINK] [PORT] \n"
+#~ "%s [opciones] set-source-port [SOURCE] [PORT] \n"
+#~ "%s [opciones] set-sink-volume SINK VOLUME\n"
+#~ "%s [opciones] set-source-volume SOURCE VOLUME\n"
+#~ "%s [opciones] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [opciones] set-sink-mute SINK 1|0\n"
+#~ "%s [opciones] set-source-mute SOURCE 1|0\n"
+#~ "%s [opciones] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Muestra esta ayuda\n"
+#~ "      --version                         Muestra la versión\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   El nombre del servidor al que "
+#~ "conectarse\n"
+#~ "  -n, --client-name=NAME                El nombre de este cliente en el "
+#~ "servidor\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Digital Envolvente 4.0 (IEC9588)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Emisor de baja frecuencia"
+
+#~ msgid "Invalid client name '%s'\n"
+#~ msgstr "Nombre de cliente inválido '%s'\n"
+
+#~ msgid "Failed to determine sample specification from file.\n"
+#~ msgstr ""
+#~ "Error al intentar determinar especificación de ejemplo del archivo.\n"
+
+#~ msgid "select(): %s"
+#~ msgstr "select(): %s"
+
+#~ msgid "Cannot connect to system bus: %s"
+#~ msgstr "No se puede conectar al bus del sistema: %s"
+
+#~ msgid "Cannot get caller from PID: %s"
+#~ msgstr "No se puede obtener el llamador desde el PID: %s"
+
+#~ msgid "Cannot set UID on caller object."
+#~ msgstr "No se puede poner UID en el objeto llamador."
+
+#~ msgid "Failed to get CK session."
+#~ msgstr "Falló al obtener sesión CK."
+
+#~ msgid "Cannot set UID on session object."
+#~ msgstr "No se puede poner UID en el objeto de sesión."
+
+#~ msgid "Cannot allocate PolKitAction."
+#~ msgstr "No se puede asignar PolKitAction."
+
+#~ msgid "Cannot set action_id"
+#~ msgstr "No se pudo poner action_id"
+
+#~ msgid "Cannot allocate PolKitContext."
+#~ msgstr "No se pudo asignar PolKitContext."
+
+#~ msgid "Cannot initialize PolKitContext: %s"
+#~ msgstr "No se pudo inicializar PolKitContext: %s"
+
+#~ msgid "Could not determine whether caller is authorized: %s"
+#~ msgstr "No se pudo determinar si el llamador está autorizado: %s"
+
+#~ msgid "Cannot obtain auth: %s"
+#~ msgstr "No se pudo obtener auth: %s"
+
+#~ msgid "PolicyKit responded with '%s'"
+#~ msgstr "PolicyKit respondió con '%s'"
+
+#~ msgid ""
+#~ "High-priority scheduling (negative Unix nice level) for the PulseAudio "
+#~ "daemon"
+#~ msgstr ""
+#~ "Planificación de alta prioridad (nivel Unix negativo) para el demonio "
+#~ "PulseAudio"
+
+#~ msgid "Real-time scheduling for the PulseAudio daemon"
+#~ msgstr "Planificación de tiempo real para el demonio de PulseAudio."
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring high-priority scheduling."
+#~ msgstr ""
+#~ "Las políticas del sistema impidieron a PulseAudio adquirir la "
+#~ "planificación de alta prioridad."
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring real-time scheduling."
+#~ msgstr ""
+#~ "Las políticas del sistema impidieron a PulseAudio adquirir la "
+#~ "planificación de tiempo real."
+
+#~ msgid "read() failed: %s\n"
+#~ msgstr "read() falló: %s\n"
+
+#~ msgid "pa_context_connect() failed: %s\n"
+#~ msgstr "pa_context_connect() falló: %s\n"
+
+#~ msgid "We're in the group '%s', allowing high-priority scheduling."
+#~ msgstr ""
+#~ "Estamos en el grupo '%s', permitiendo planificación de prioridad alta."
+
+#~ msgid "We're in the group '%s', allowing real-time scheduling."
+#~ msgstr "Estamos en el grupo '%s', permitiendo planificación en tiempo real."
+
+#~ msgid "PolicyKit grants us acquire-high-priority privilege."
+#~ msgstr "PolicyKit garantiza que se obtenga el privilegio de alta prioridad."
+
+#~ msgid "PolicyKit refuses acquire-high-priority privilege."
+#~ msgstr "PolicyKit se niega a dar acceso al privilegio de alta prioridad."
+
+#~ msgid "PolicyKit grants us acquire-real-time privilege."
+#~ msgstr "PolicyKit garantiza el acceso al privilegio de tiempo real."
+
+#~ msgid "PolicyKit refuses acquire-real-time privilege."
+#~ msgstr "PolicyKit se niega a dar acceso al privilegio de tiempo real."
+
+#~ msgid ""
+#~ "Called SUID root and real-time and/or high-priority scheduling was "
+#~ "requested in the configuration. However, we lack the necessary "
+#~ "privileges:\n"
+#~ "We are not in group '%s', PolicyKit refuse to grant us the requested "
+#~ "privileges and we have no increase RLIMIT_NICE/RLIMIT_RTPRIO resource "
+#~ "limits.\n"
+#~ "For enabling real-time/high-priority scheduling please acquire the "
+#~ "appropriate PolicyKit privileges, or become a member of '%s', or increase "
+#~ "the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."
+#~ msgstr ""
+#~ "Se llamó con SUID root y se pidió planificación en tiempo real y/o de "
+#~ "alta prioridad en la configuración. Sin embargo, no se tiene los "
+#~ "privilegios necesarios:\n"
+#~ "No se está en el grupo '%s'. PolicyKit rechaza darnos el permiso "
+#~ "necesario y no se puede aumentar los límites del recurso RLIMIT_NICE/"
+#~ "RLIMIT_RTPRIO.\n"
+#~ "Para habilitar la planifiación de tiempo real/alta prioridad por favor "
+#~ "adquiera los privilegios apropiados en PolicyKit, o hágase miembro de "
+#~ "'%s', o aumente los límites del recurso RLIMIT_NICE/RLIMIT_RTPRIO para "
+#~ "este usuario."
+
+#~ msgid ""
+#~ "High-priority scheduling enabled in configuration but not allowed by "
+#~ "policy."
+#~ msgstr ""
+#~ "Está habilitadada la planificación de prioridad alta, pero no están "
+#~ "permitidas por la política."
+
+#~ msgid "Successfully increased RLIMIT_RTPRIO"
+#~ msgstr "RLIMIT_RTPRIO incrementado en forma exitosa"
+
+#~ msgid "RLIMIT_RTPRIO failed: %s"
+#~ msgstr "Fallo en RLIMIT_RTPRIO: %s"
+
+#~ msgid "Giving up CAP_NICE"
+#~ msgstr "Abandonando CAP_NICE"
+
+#~ msgid ""
+#~ "Real-time scheduling enabled in configuration but not allowed by policy."
+#~ msgstr ""
+#~ "Está habilitada la planificación en tiempo real, pero no está permitido "
+#~ "por la política."
+
+#~ msgid "Limited capabilities successfully to CAP_SYS_NICE."
+#~ msgstr "Capacidades limitadas con éxito para CAP_SYS_NICE-"
+
+#~ msgid "time_new() failed.\n"
+#~ msgstr "time_new() falló.\n"
+
+#~ msgid "Output %s + Input %s"
+#~ msgstr "Salida %s + Entrada %s"
+
+#~ msgid "Stream successfully created\n"
+#~ msgstr "Se ha creado el flujo exitosamente\n"
+
+#~ msgid "Stream errror: %s\n"
+#~ msgstr "Error de flujo: %s\n"
+
+#~ msgid "Connection established.\n"
+#~ msgstr "Conección establecida.\n"
+
+#~ msgid ""
+#~ "%s [options] [FILE]\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -v, --verbose                         Enable verbose operation\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -d, --device=DEVICE                   The name of the sink to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ "      --stream-name=NAME                How to call this stream on the "
+#~ "server\n"
+#~ "      --volume=VOLUME                   Specify the initial (linear) "
+#~ "volume in range 0...65536\n"
+#~ "      --channel-map=CHANNELMAP          Set the channel map to the use\n"
+#~ msgstr ""
+#~ "%s [options] [FILE]\n"
+#~ "\n"
+#~ "  -h, --help                            Muestra esta ayuda\n"
+#~ "      --version                         Muestra la versión\n"
+#~ "\n"
+#~ "  -v, --verbose                         Habilita operación con "
+#~ "vocabulario más detallado\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   El nombre del servidor al que "
+#~ "conectarse\n"
+#~ "  -d, --device=DEVICE                   El nombre del destino al que "
+#~ "conectarse\n"
+#~ "  -n, --client-name=NAME                Cómo llamar a este cliente en el "
+#~ "servidor\n"
+#~ "      --stream-name=NAME                Cómo llamar a este flujo en el "
+#~ "servidor\n"
+#~ "      --volume=VOLUME                   Especifica el volumen inicial "
+#~ "(linear)en el rango de 0...65536\n"
+#~ "      --channel-map=CHANNELMAP          Establece el mapa del canal para "
+#~ "el uso\n"
+
+#~ msgid ""
+#~ "paplay %s\n"
+#~ "Compiled with libpulse %s\n"
+#~ "Linked with libpulse %s\n"
+#~ msgstr ""
+#~ "paplay %s\n"
+#~ "Compilado con libpulse %s\n"
+#~ "Linked con libpulse %s\n"
+
+#~ msgid "Invalid channel map\n"
+#~ msgstr "Mapa de canal inválido\n"
+
+#~ msgid "Failed to open file '%s'\n"
+#~ msgstr "Error al intentar abrir el archivo '%s'\n"
+
+#~ msgid "Channel map doesn't match file.\n"
+#~ msgstr "El mapa del canal no se corresponde con el archivo.\n"
+
+#~ msgid "Using sample spec '%s'\n"
+#~ msgstr "Utilizando especificaciones de muestra '%s'\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "Called SUID root and real-time and/or high-priority scheduling was "
+#~ "requested in the configuration. However, we lack the necessary "
+#~ "privileges:\n"
+#~ "We are not in group '"
+#~ msgstr ""
+#~ "En la configuración se ha pedido una llamada de SUID root y planificación "
+#~ "en tiempo real/de prioridad alta. Sin embargo, carecemos de los "
+#~ "provilegios necesarios:\n"
+#~ "No estamos en el grupo '"
+
+#, fuzzy
+#~ msgid "--log-time boolean argument"
+#~ msgstr "--disallow-exit argumento booleano"
+
+#~ msgid ""
+#~ "' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
+#~ "For enabling real-time scheduling please acquire the appropriate "
+#~ "PolicyKit priviliges, or become a member of '"
+#~ msgstr ""
+#~ "' y PolicyKit se niega a darnos privilegios. Abandonando SUID de nuevo.\n"
+#~ "Para permitir planificación en tiempo real, por favor adquiera los "
+#~ "privilegios de PolicyKit adecuados, o forme parte de '"
+
+#~ msgid ""
+#~ "', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this "
+#~ "user."
+#~ msgstr ""
+#~ "', o incremente los límites del recurso RLIMIT_NICE/RLIMIT_RTPRIO para "
+#~ "ese usuario. "
+
+#~ msgid "Default sink name (%s) does not exist in name register."
+#~ msgstr ""
+#~ "El nombre de destino por defecto (%s) no existe en el registro de nombres."
+
+#~ msgid "Buffer overrun, dropping incoming data\n"
+#~ msgstr "Búfer desbordado, abandonando datos entrantes\n"
+
+#~ msgid "pa_stream_drop() failed: %s\n"
+#~ msgstr "pa_stream_drop() falló: %s\n"
+
+#~ msgid "muted"
+#~ msgstr "mudo"
+
+#~ msgid ""
+#~ "*** Autoload Entry #%u ***\n"
+#~ "Name: %s\n"
+#~ "Type: %s\n"
+#~ "Module: %s\n"
+#~ "Argument: %s\n"
+#~ msgstr ""
+#~ "*** Entrada de Autoload #%u ***\n"
+#~ "Nombre: %s\n"
+#~ "Tipo: %s\n"
+#~ "Módulo: %s\n"
+#~ "Argumento: %s\n"
diff --git a/po/fi.po b/po/fi.po
new file mode 100644 (file)
index 0000000..01e4139
--- /dev/null
+++ b/po/fi.po
@@ -0,0 +1,3279 @@
+# pulseaudio translation to Finnish (fi).
+# Copyright (C) 2008 Timo Jyrinki
+# This file is distributed under the same license as the pulseaudio package.
+# Timo Jyrinki <timo.jyrinki@iki.fi>, 2008.
+# Ville-Pekka Vainio <vpivaini@cs.helsinki.fi>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: git trunk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:53+0000\n"
+"Last-Translator: Ville-Pekka Vainio <vpivaini@cs.helsinki.fi>\n"
+"Language-Team: Finnish <laatu@lokalisointi.org>\n"
+"Language: fi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() palautti poikkeuksellisen suuren arvon: %lu tavua (%lu ms).\n"
+"Tämä on todennäköisesti ohjelmavirhe ALSA-ajurissa ”%s”. Ilmoita tästä "
+"ongelmasta ALSA-kehittäjille."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() palautti poikkeuksellisen suuren arvon: %li tavua (%s%lu "
+"ms).\n"
+"Tämä on todennäköisesti ohjelmavirhe ALSA-ajurissa ”%s”. Ilmoita tästä "
+"ongelmasta ALSA-kehittäjille."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() palautti poikkeuksellisen suuren arvon: %lu tavua (%lu ms).\n"
+"Tämä on todennäköisesti ohjelmavirhe ALSA-ajurissa ”%s”. Ilmoita tästä "
+"ongelmasta ALSA-kehittäjille."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() palautti poikkeuksellisen suuren arvon: %lu tavua (%lu "
+"ms).\n"
+"Tämä on todennäköisesti ohjelmavirhe ALSA-ajurissa ”%s”. Ilmoita tästä "
+"ongelmasta ALSA-kehittäjille."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Pidä aina vähintään yksi nielu ladattuna, vaikka se olisi tyhjä nielu"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Valeulostulo"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "Virtuaalinen LADSPA-nielu"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<nielun nimi> sink_properties=<nielun asetukset> "
+"master=<suodatettavan nielun nimi> format=<näytemuoto> "
+"rate=<näytteenottotaajuus> channels=<kanavien määrä> "
+"channel_map=<kanavakartta> plugin=<ladspa-liitännäisen nimi> label=<ladspa-"
+"liitännäisen nimiö (label)> control=<pilkulla erotettu luettelo "
+"syötteenhallinta-arvoja>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "Kellotettu tyhjä nielu"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Tyhjä ulostulo"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "Sisäinen äänentoisto"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "Modeemi"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Alkuperäisen lt_dlopen-lataimen löytäminen epäonnistui."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "Uuden dl-lataaminen varaaminen epäonnistui."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind-now-loaderin lisääminen epäonnistui."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "Saatiin signaali %s."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "Poistutaan."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Käyttäjää ”%s” ei löydetty."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Ryhmää ”%s” ei löydetty."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Löydettiin käyttäjä ”%s” (UID %lu) ja ryhmä ”%s” (GID %lu)."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "Käyttäjän ”%s” ja ryhmän ”%s” GID:t eivät vastaa toisiaan."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Käyttäjän ”%s” kotihakemisto ei ole ”%s”, ohitetaan."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Hakemiston ”%s” luominen epäonnistui: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Ryhmäluettelon vaihtaminen epäonnistui: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID:n vaihtaminen epäonnistui: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID:n vaihtaminen epäonnistui: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "Root-oikeuksista luopuminen onnistui."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "Järjestelmänlaajuista tilaa ei tueta tällä alustalla."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) epäonnistui: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "Komentorivin jäsentäminen epäonnistui."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "Taustaprosessi ei ole käynnissä"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "Taustaprosessi käynnissä prosessitunnisteella %u"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Taustaprosessin lopettaminen epäonnistui: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Tätä ohjelmaa ei ole tarkoitettu suoritettavaksi pääkäyttäjänä (ellei --"
+"system ole määritelty)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Pääkäyttäjän (root) oikeudet vaaditaan."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start-valitsinta ei tueta järjestelmätilassa."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr ""
+"Suoritetaan järjestelmätilassa, mutta --disallow-exit ei ole asetettuna!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"Suoritetaan järjestelmätilassa, mutta --disallow-module-loading ei ole "
+"asetettuna!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr ""
+"Suoritetaan järjestelmätilassa, otetaan SHM-tila pakotetusti pois käytöstä."
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"Suoritetaan järjestelmätilassa, otetaan poistumisen joutenoloaika "
+"pakotetusti pois käytöstä."
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdio:n saaminen epäonnistui."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "putki epäonnistui: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() epäonnistui: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() epäonnistui: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "Taustaprosessin käynnistys epäonnistui."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "Taustaprosessin käynnistys onnistui."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() epäonnistui: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Tämä on PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Käännöksen isäntäkone: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "Käännösaikaiset C-liput (CFLAGS): %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "Käynnissä isäntäkoneella: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "Löydettiin %u CPU:ta."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "Sivun koko on %lu tavua"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Käännetty Valgrind-tuella: kyllä"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Käännetty Valgrind-tuella: ei"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Käynnissä valgrind-tilassa: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "Käynnissä isäntäkoneella: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Optimoitu rakentaminen: kyllä"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "Optimoitu rakentaminen: ei"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG on määritelty, kaikki assertit ovat poissa käytöstä."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH on määritelty, vain fast path -assertit ovat poissa käytöstä."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "Kaikki assertit ovat käytössä."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "Konetunnisteen nouto epäonnistui"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "Konetunniste on %s."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "Istunnon tunniste on %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Käytetään ajonaikaista hakemistoa %s."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "Käytetään tilahakemistoa %s."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "Käytetään moduulihakemistoa %s."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Suoritetaan järjestelmätilassa: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"PA:ta suoritetaan järjestelmätilassa. Näin ei luultavasti pitäisi tehdä.\n"
+"Jos silti teet näin, on sinun vikasi jos kaikki ei toimikaan odotetusti.\n"
+"Lisätietoja siitä, miksi järjestelmätilan käyttäminen on yleensä huono "
+"ajatus on osoitteessa http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/"
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() epäonnistui."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "Korkean tarkkuuden ajastimet käytettävissä."
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Hei, ytimesi on kehno! Linux korkean tarkkuuden ajastimien tuella on hyvin "
+"suositeltava!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() epäonnistui."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "Taustaprosessin alustus epäonnistui."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"Taustaprosessin käynnistys ilman ladattavia moduuleita, kieltäydytään "
+"toiminnasta."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "Taustaprosessin käynnistys valmis."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "Taustaprosessin sulkeminen käynnistetty."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "Taustaprosessi lopetettu."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [valitsimet]\n"
+"\n"
+"KOMENNOT:\n"
+"  -h, --help                            Näytä tämä ohje\n"
+"      --version                         Näytä versio\n"
+"      --dump-conf                       Tulosta oletusasetukset\n"
+"      --dump-modules                    Tulosta saatavilla olevien\n"
+"                                        moduulien luettelo\n"
+"      --dump-resample-methods           Tulosta saatavilla\n"
+"                                        olevat\n"
+"                                        uudelleennäytteistystavat\n"
+"      --cleanup-shm                     Puhdista vanhentuneet jaetun\n"
+"                                        muistin segmentit\n"
+"      --start                           Käynnistä taustaprosessi, jos se\n"
+"                                        ei ole käynnissä\n"
+"  -k  --kill                            Tapa suoritettava taustaprosessi\n"
+"      --check                           Tarkista onko\n"
+"                                        taustaprosessi suoritettavana\n"
+"                                        (palauttaa vain lopetuskoodin)\n"
+"\n"
+"VALITSIMET:\n"
+"      --system[=BOOL]                   Suorita järjestelmänlaajuisena\n"
+"  -D, --daemonize[=BOOL]                Vaihda\n"
+"                                        taustaprosessiksi käynnistyksen\n"
+"                                        jälkeen\n"
+"      --fail[=BOOL]                     Lopeta kun käynnistys epäonnistuu\n"
+"      --high-priority[=BOOL]            Yritä asettaa korkea nice-taso\n"
+"                                        (käytettävissä vain root-\n"
+"                                        käyttäjänä, SUIDilla tai\n"
+"                                        kohotetulla RLIMIT_NICE-arvolla)\n"
+"      --realtime[=BOOL]                 Yritä asettaa reaaliaikainen\n"
+"                                        ajoitus (käytettävissä vain\n"
+"                                        root-käyttäjänä, SUIDilla tai\n"
+"                                        kohotetulla RLIMIT_RTPRIO-arvolla)\n"
+"      --disallow-module-loading[=BOOL]  Kiellä käyttäjän pyytämä moduulin\n"
+"                                        lataus tai poisto käynnistyksen\n"
+"                                        jälkeen\n"
+"      --disallow-exit[=BOOL]            Kiellä käyttäjän pyytämä\n"
+"                                        lopettaminen\n"
+"      --exit-idle-time=SEK              Lopeta taustaprosessi, kun se on\n"
+"                                        toimettomana ja tämä aika on\n"
+"                                        kulunut\n"
+"      --module-idle-time=SEK            Poista automaattisesti ladatut\n"
+"                                        moduulit, kun taustaprosessi on\n"
+"                                        toimettomana ja tämä aika on\n"
+"                                        kulunut\n"
+"      --scache-idle-time=SEK            Poista automaattisesti ladatut\n"
+"                                        näytteet, kun taustaprosessi on\n"
+"                                        toimettomana ja tämä aika on\n"
+"                                        kulunut\n"
+"      --log-level[=TASO]                Aseta tai kasvata\n"
+"                                        lokikirjoituksen tasoa\n"
+"  -v                                    Kasvata tulosteiden\n"
+"                                        yksityiskohtaisuutta\n"
+"      --log-target={auto,syslog,stderr} Aseta lokikirjoituksen kohde\n"
+"      --log-meta[=BOOL]                 Lisää koodikohta\n"
+"                                        lokiviesteihin\n"
+"      --log-time[=BOOL]                 Lisää aikaleima lokiviesteihin\n"
+"      --log-backtrace=KEHYKSET          Lisää pinolistaus lokiviesteihin\n"
+"  -p, --dl-search-path=POLKU            Aseta dynaamisten jaettujen\n"
+"                                        objektien (liitännäisten)\n"
+"                                        hakupolku\n"
+"      --resample-method=TAPA            Käytä määritettyä\n"
+"                                        uudelleennäytteistyksen tapaa\n"
+"                                        (--dump-resample-methods\n"
+"                                        luetteloi mahdolliset arvot)\n"
+"      --use-pid-file[=BOOL]             Luo PID-tiedosto\n"
+"      --no-cpu-limit[=BOOL]             Älä asenna suoritinkuorman\n"
+"                                        rajoitinta alustoilla, jotka\n"
+"                                        tukevat sitä\n"
+"      --disable-shm[=BOOL]              Poista jaetun muistin tuki käytöstä\n"
+"\n"
+"ALOITUSKOMENTOSARJA:\n"
+"  -L, --load=\"MODUULIN ARGUMENTIT\"    Lataa liitännäismoduuli annetulla\n"
+"                                        argumentilla\n"
+"  -F, --file=TIEDOSTO                   Suorita annettu komentosarja\n"
+"  -C                                    Avaa komentokehote nykyiseen\n"
+"                                        päätteeseen käynnistyksen jälkeen\n"
+"\n"
+"  -n                                    Älä lataa oletuskomentosarja-\n"
+"                                        tiedostoa\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level vaatii lokikirjoituksen tason argumentiksi (joko numero väliltä "
+"0..4 tai yksi seuraavista: debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr ""
+"Virheellinen lokikirjoituksen kohde: käytä jotain seuraavista: ”syslog”, "
+"”stderr” tai ”auto”."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Virheellinen uudelleennäytteistyksen tapa ”%s”."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm vaatii totuusarvoisen argumentin"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Nimi: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "Moduulitietoja ei saatavilla\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Versio: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Kuvaus: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Tekijä: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Käyttö: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Lataa kerran: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "VAROITUS VANHENTUNEISUUDESTA: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Polku: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Virheellinen lokikirjoituksen kohde ”%s”."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Virheellinen lokikirjoituksen taso ”%s”."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Virheellinen uudelleennäytteistyksen tapa ”%s”."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Virheellinen rlimit ”%s”."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Virheellinen näytemuoto ”%s”."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Virheellinen näytteenottotaajuus ”%s”."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Virheelliset näytekanavat ”%s”."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Virheellinen kanavakartta ”%s”."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Virheellinen fragmenttimäärä ”%s”."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Virheellinen fragmenttikoko ”%s”."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Virheellinen nice-taso ”%s”."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Virheellinen näytteenottotaajuus ”%s”."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Asetustiedoston avaaminen epäonnistui: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Kanavien oletusmäärä ja oletuskanavakartan kanavien määrä poikkeavat "
+"toisistaan."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Luettu asetustiedostosta: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "Luovutaan oikeuksista."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio-äänijärjestelmä"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Käynnistä PulseAudio-äänijärjestelmä"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio-äänijärjestelmä"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "Käynnistä PulseAudio-äänijärjestelmä"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Keski"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Vasen etu"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Oikea etu"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Takakeski"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Vasen taka"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Oikea taka"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Vasemmalle keskeltä etu"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Oikealle keskeltä etu"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Vasen sivu"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Oikea sivu"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Avustava 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Avustava 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Avustava 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Avustava 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Avustava 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Avustava 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Avustava 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Avustava 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Avustava 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Avustava 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Avustava 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Avustava 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Avustava 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Avustava 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Avustava 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Avustava 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Avustava 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Avustava 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Avustava 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Avustava 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Avustava 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Avustava 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Avustava 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Avustava 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Avustava 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Avustava 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Avustava 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Avustava 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Avustava 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Avustava 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Avustava 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Avustava 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Keski ylä"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Keski ylä etu"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Vasen ylä etu"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Oikea ylä etu"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Keski ylä taka"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Vasen ylä taka"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Oikea ylä taka"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(virheellinen)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "4.0-tilaääni"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "4.1-tilaääni"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "5.0-tilaääni"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "5.1-tilaääni"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "7.1-tilaääni"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "Pääsy evätty"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Tuntematon komento"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Virheellinen argumentti"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "Entiteetti on jo olemassa"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "Ei kyseisenlaista entiteettiä"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "Yhteys hylätty"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "Yhteyskäytäntövirhe"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "Aikakatkaisu"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "Ei todentamisavainta"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "Sisäinen virhe"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "Yhteys katkennut"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "Entiteetti lopetettu"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Virheellinen palvelin"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Moduulin alustus epäonnistui"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Virheellinen tila"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Ei dataa"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "Epäyhteensopiva yhteyskäytännön versio"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Liian suuri"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "Ei tuettu"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Tuntematon virhekoodi"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "Ei kyseisenlaista laajennusta"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Puuttuva toiminnallisuus"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Puuttuva toteutus"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Asiakasohjelma haarautui"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Siirräntävirhe"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Laite tai resurssi on varattu"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %u kan. %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() epäonnistui: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "Evästetietojen jäsennys epäonnistui"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "Asetustiedoston avaaminen epäonnistui: ”%s”: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "Ei ladattua evästettä. Yritetään yhdistämistä ilman."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Saatiin viesti tuntemattomalle laajennokselle ”%s”"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Virran tyhjentäminen epäonnistui: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "Toistovirta on tyhjennetty."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "Tyhjennetään yhteyttä palvelimelle."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() epäonnistui: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_write() epäonnistui: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() epäonnistui: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "Virran luonti onnistui."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() epäonnistui: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Puskuritiedot: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Puskuritiedot: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Käytetään näytemäärittelyä ”%s”, kanavakarttaa ”%s”."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "Yhdistetty laitteeseen %s (%u, %ssuspended)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "Virtavirhe: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Virtalaite keskeytetty.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Virtalaite palautettu.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Virran alivuoto.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Virran ylivuoto.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "Virta käynnistetty.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Virta siirretty laitteelle %s (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "ei "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Virran puskuriattribuutteja muutettu.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "Yhteys muodostettu.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() epäonnistui: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() epäonnistui: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() epäonnistui: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Yhteysvirhe: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "Saatiin EOF."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() epäonnistui: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "Saatiin signaali, lopetetaan."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Latenssin selvittäminen epäonnistui: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Aika: %0.3f s; Latenssi: %0.0f μs."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() epäonnistui: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [valitsimet]\n"
+"\n"
+"  -h, --help                            Näytä tämä ohje\n"
+"      --version                         Näytä versio\n"
+"\n"
+"  -r, --record                          Luo yhteys nauhoitukselle\n"
+"  -p, --playback                        Luo yhteys toistolle\n"
+"\n"
+"  -v, --verbose                         Yksityiskohtaiset tulosteet\n"
+"\n"
+"  -s, --server=PALVELIN                 Sen palvelimen nimi, johon "
+"yhdistetään\n"
+"  -d, --device=LAITE                    Sen nielun/lähteen nimi, johon "
+"yhdistetään\n"
+"  -n, --client-name=NIMI                Kuinka tätä asiakasohjelmaa "
+"kutsutaan palvelimella\n"
+"      --stream-name=NIMI                Kuinka tätä virtaa kutsutaan "
+"palvelimella\n"
+"      --volume=ÄÄNENVOIMAKKUUS          Määritä (lineaarinen) "
+"aloitusäänenvoimakkuus väliltä 0...65536\n"
+"      --rate=NÄYTTEENOTTOTAAJUUS        Näytteenottotaajuus hertseinä"
+"(oletus: 44100)\n"
+"      --format=NÄYTEMUOTO               Näytteen tyyppi, yksi seuraavista:"
+"s16le, s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be\n"
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (oletus s16ne)\n"
+"      --channels=KANAVIA                Kanavien määrä, 1=mono, 2=stereo\n"
+"                                        (oletus: 2)\n"
+"      --channel-map=KANAVAKARTTA        Oletuksen sijasta käytettävä "
+"kanavakartta\n"
+"      --fix-format                      Valitse näytemuoto nielusta, jossa "
+"virta on\n"
+"      --fix-rate                        Valitse näytteenottotaajuus "
+"nielusta, jossa virta on\n"
+"      --fix-channels                    Valitse kanavien määrä ja "
+"kanavakartta nielusta, johon virtaa\n"
+"                                        yhdistetään.\n"
+"      --no-remix                        Älä yli- tai alimiksaa kanavia.\n"
+"      --no-remap                        Kartoita kanavat indeksin mukaan, "
+"älä nimen mukaan.\n"
+"      --latency=TAVUA                   Pyydä määritettyä latenssia "
+"tavuissa.\n"
+"      --process-time=TAVUA              Pyydä määritettyä prosessiaikaa "
+"pyyntöä kohti tavuissa.\n"
+"      --property=ASETUS=ARVO            Anna määritetylle asetukselle "
+"määritetty arvo.\n"
+"      --raw                             Tallenna/soita raakaa PCM-dataa.\n"
+"      --file-format=TMUOTO              Tallenna/soita tietyn muotoista PCM-"
+"dataa.\n"
+"      --list-file-formats               Luettele käytettävissä olevat "
+"tiedostomuodot.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Käännetty libpulsen versiolle %s\n"
+"Linkitetty libpulsen versiolle %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Virheellinen asiakasohjelman nimi ”%s”"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Virheellinen virran nimi ”%s”"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Virheellinen kanavakartta ”%s”"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Virheellinen latenssimääritys ”%s”"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Virheellinen prosessiajan määritys ”%s”"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Virheellinen asetus ”%s”"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Tuntematon tiedostomuoto %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "Virheellinen näytemääritys"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "Liian monta argumenttia."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "Näytemäärityksen generointi tiedostolle epäonnistui."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "Äänitiedoston avaaminen epäonnistui."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Varoitus: tiedostosta luettava näytemääritys korvaa annetun määrityksen."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "Näytemäärityksen selvittäminen tiedostosta epäonnistui."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Varoitus: Kanavakartan selvittäminen tiedostosta epäonnistui."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "Kanavakartta ei vastaa näytemääritystä"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "Varoitus: kanavakartan kirjoittaminen tiedostoon epäonnistui."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "Avataan %svirta näytemäärityksellä ”%s” ja kanavakartalla ”%s”."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "nauhoitus"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "toisto"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "Komentorivin jäsentäminen epäonnistui."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() epäonnistui."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() epäonnistui."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() epäonnistui."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() epäonnistui: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_new() epäonnistui."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() epäonnistui."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Keskeytys epäonnistui: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Palautus epäonnistui: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "VAROITUS: Äänipalvelin ei ole paikallinen, ei keskeytetä.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Yhteysvirhe: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Saatiin SIGINT, lopetetaan.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "VAROITUS: Lapsiprosessi lopetettiin signaalilla %u\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [valitsimet] ... \n"
+"\n"
+"  -h, --help                            Näytä tämä ohje\n"
+"      --version                         Näytä versio\n"
+"  -s, --server=PALVELIN                 Sen palvelimen nimi, johon\n"
+"                                        yhdistetään\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Käännetty libpulsen versiolle %s\n"
+"Linkitetty libpulsen versiolle %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() epäonnistui.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() epäonnistui.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() epäonnistui.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Tilastojen selvittäminen epäonnistui: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Nyt käytössä: %u lohkoa sisältäen yhteensä %s tavua.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Koko käyttöaikana varattu: %u lohkoa sisältäen yhteensä %s tavua.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Näytevälimuistin koko: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Palvelintietojen selvittäminen epäonnistui: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Käyttäjänimi: %s\n"
+"Konenimi: %s\n"
+"Palvelimen nimi: %s\n"
+"Palvelimen versio: %s\n"
+"Oletusnäytemäärittely: %s\n"
+"Oletuskanavakartta: %s\n"
+"Oletusnielu: %s\n"
+"Oletuslähde: %s\n"
+"Eväste: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Nielun tietojen nouto epäonnistui: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Nielu #%u\n"
+"\tTila: %s\n"
+"\tNimi: %s\n"
+"\tKuvaus: %s\n"
+"\tAjuri: %s\n"
+"\tNäytemäärittely: %s\n"
+"\tKanavakartta: %s\n"
+"\tOmistava moduuli: %u\n"
+"\tVaimennus: %s\n"
+"\tÄänenvoimakkuus: %s%s%s\n"
+"\t                 balanssi %0.2f\n"
+"\tPerusäänenvoimakkuus: %s%s%s\n"
+"\tTarkkailulähde: %s\n"
+"\tLatenssi: %0.0f μs, asetettu %0.0f μs\n"
+"\tLiput: %s%s%s%s%s%s\n"
+"\tOminaisuudet:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPortit:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tAktiivinen portti: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tPortit:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Lähteen tietojen nouto epäonnistui: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Lähde #%u\n"
+"\tTila: %s\n"
+"\tNimi: %s\n"
+"\tKuvaus: %s\n"
+"\tAjuri: %s\n"
+"\tNäytemäärittely: %s\n"
+"\tKanavakartta: %s\n"
+"\tOmistava moduuli: %u\n"
+"\tVaimennus: %s\n"
+"\tÄänenvoimakkuus: %s%s%s\n"
+"\t                 balanssi %0.2f\n"
+"\tPerusäänenvoimakkuus: %s%s%s\n"
+"\tTarkkailtava nielu: %s\n"
+"\tLatenssi: %0.0f μs, asetettu %0.0f μs\n"
+"\tLiput: %s%s%s%s%s%s\n"
+"\tOminaisuudet:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "-"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Moduulin tietojen nouto epäonnistui: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Moduuli #%u\n"
+"\tNimi: %s\n"
+"\tArgumentti: %s\n"
+"\tKäyttölaskuri: %s\n"
+"\tOminaisuudet:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Asiakkaan tietojen nouto epäonnistui: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Asiakas #%u\n"
+"\tAjuri: %s\n"
+"\tOmistava moduuli: %s\n"
+"\tOminaisuudet:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Kortin tietojen nouto epäonnistui: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Kortti #%u\n"
+"\tNimi: %s\n"
+"\tAjuri: %s\n"
+"\tOmistava moduuli: %s\n"
+"\tOminaisuudet:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfiilit:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tAktiivinen profiili: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Nielun sisääntulon tietojen nouto epäonnistui: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Nielun sisääntulo #%u\n"
+"\tAjuri: %s\n"
+"\tOmistava moduuli: %s\n"
+"\tAsiakas: %s\n"
+"\tNielu: %u\n"
+"\tNäytemäärittely: %s\n"
+"\tKanavakartta: %s\n"
+"\tVaimennus: %s\n"
+"\tÄänenvoimakkuus: %s\n"
+"\t                 %s\n"
+"\t                 balanssi %0.2f\n"
+"\tPuskurin latenssi: %0.0f μs\n"
+"\tNielun latenssi: %0.0f μs\n"
+"\tUudelleennäytteistyksen tapa: %s\n"
+"\tOminaisuudet:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Lähteen ulostulon tietojen nouto epäonnistui: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Nielun sisääntulo #%u\n"
+"\tAjuri: %s\n"
+"\tOmistava moduuli: %s\n"
+"\tAsiakas: %s\n"
+"\tNielu: %u\n"
+"\tNäytemäärittely: %s\n"
+"\tKanavakartta: %s\n"
+"\tVaimennus: %s\n"
+"\tÄänenvoimakkuus: %s\n"
+"\t                 %s\n"
+"\t                 balanssi %0.2f\n"
+"\tPuskurin latenssi: %0.0f μs\n"
+"\tNielun latenssi: %0.0f μs\n"
+"\tUudelleennäytteistyksen tapa: %s\n"
+"\tOminaisuudet:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Näytetietojen nouto epäonnistui: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Näyte #%u\n"
+"\tNimi: %s\n"
+"\tNäytemäärittely: %s\n"
+"\tKanavakartta: %s\n"
+"\tÄänenvoimakkuus: %s\n"
+"\t                 %s\n"
+"\t                 balanssi %0.2f\n"
+"\tKesto: %0.1fs\n"
+"\tKoko: %s\n"
+"\tLaiska: %s\n"
+"\tTiedostonimi: %s\n"
+"\tOminaisuudet:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "Epäonnistuminen: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Lähteen tietojen nouto epäonnistui: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Näytteen lähettäminen epäonnistui: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "Ennenaikainen tiedoston päättyminen"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr "nielu"
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr "lähde"
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+#, fuzzy
+msgid "source-output"
+msgstr "lähde"
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "Virheellinen palvelin"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "Saatiin SIGINT, lopetetaan."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "Virheellinen äänenvoimakkuuden määritys"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [valitsimet] ... \n"
+"\n"
+"  -h, --help                            Näytä tämä ohje\n"
+"      --version                         Näytä versio\n"
+"  -s, --server=PALVELIN                 Sen palvelimen nimi, johon\n"
+"                                        yhdistetään\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Käännetty libpulsen versiolle %s\n"
+"Linkitetty libpulsen versiolle %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "Anna ladattava näytetiedosto"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "Äänitiedoston avaaminen epäonnistui."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Varoitus: näytemäärityksen selvitys tiedostosta epäonnistui."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "Soitettavan näytteen nimi on annettava"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "Poistettavan näytteen nimi on annettava"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "Nielun syöteindeksi ja nielu on annettava"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "Lähteen ulostuloindeksi ja lähde on annettava"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "Moduulin nimi ja argumentit on annettava."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "Moduulin indeksi on annettava"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr "Ei voi antaa enempää kuin yhden nielun. Totuusarvo on annettava."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr "Ei voi antaa enempää kuin yhden lähteen. Totuusarvo on annettava."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Kortin nimi/indeksi ja profiilin nimi on annettava"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Nielun nimi/indeksi ja portin nimi on annettava"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "Lähteen nimi/indeksi ja portin nimi on annettava"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Nielun nimi/indeksi ja portin nimi on annettava"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "Lähteen nimi/indeksi ja äänenvoimakkuus on annettava"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "Nielun syöteindeksi ja äänenvoimakkuus on annettava"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "Virheellinen nielun syöteindeksi"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "Lähteen ulostuloindeksi ja lähde on annettava"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "Virheellinen nielun syöteindeksi"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "Nielun nimi/indeksi ja vaimennuksen totuusarvo on annettava"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "Virheellinen näytemääritys"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Lähteen nimi/indeksi ja vaimennuksen totuusarvo on annettava"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "Nielun syöteindeksi ja vaimennuksen totuusarvo on annettava"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "Virheellinen nielun syöteindeksin määritys"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "Lähteen nimi/indeksi ja vaimennuksen totuusarvo on annettava"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "Virheellinen nielun syöteindeksin määritys"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "Nielun nimi/indeksi ja vaimennuksen totuusarvo on annettava"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "Mitään kelvollista komentoa ei annettu."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D näyttö] [-S palvelin] [-O nielu] [-I lähde] [-c tiedosto]  [-d|-e|-i|-"
+"r]\n"
+"\n"
+" -d    Näytä nykyiseen X11-näyttöön yhdistetyn PulseAudion tiedot (oletus)\n"
+" -e    Vie paikalliset PulseAudio-tiedot X11-näytölle\n"
+" -i    Tuo PulseAudio-tiedot X11-näytöltä paikallisiin ympäristömuuttujiin "
+"ja\n"
+"       evästetiedostoon.\n"
+" -r    Poista PulseAudio-tiedostot X11-näytöltä\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Komentorivin jäsentäminen epäonnistui.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "Palvelin: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "Lähde: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Nielu: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Eväste: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Evästetietojen jäsennys epäonnistui\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Evästetietojen tallennus epäonnistui\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "Asiakasohjelman asetustiedoston lataaminen epäonnistui.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "Ympäristön asetustietojen lukeminen epäonnistui.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Verkkonimen saaminen epäonnistui.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Evästetietojen lataaminen epäonnistui\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Toteutusta ei vielä ole.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"PulseAudio-taustaprosessi ei ole käynnissä eikä PulseAudiota suoriteta "
+"istunnon taustaprosessina."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio-taustaprosessin lopettaminen epäonnistui."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "Taustaprosessi ei vastaa."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "Automaattisen käynnistyksen lukkoa ei voida käyttää."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA herätti taustaprosessin kirjoittamaan uutta dataa laitteelle, mutta "
+"mitään kirjoitettavaa ei ollut!\n"
+"Tämä on luultavasti ohjelmavirhe ALSA-ajurissa ”%s”. Raportoi tästä "
+"ongelmasta ALSA-kehittäjille. Taustaprosessi herätettiin POLLOUT "
+"asetettuna,  snd_pcm_avail() palautti kuitenkin 0 tai jonkin muun arvon, "
+"joka on < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA herätti taustaprosessin lukemaan uutta dataa laitteelta, mutta mitään "
+"luettavaa ei ollut!\n"
+"Tämä on luultavasti ohjelmavirhe ALSA-ajurissa ”%s”. Raportoi tästä "
+"ongelmasta ALSA-kehittäjille. Taustaprosessi herätettiin POLLIN asetettuna,  "
+"snd_pcm_avail() palautti kuitenkin 0 tai jonkin muun arvon, on < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "Poissa"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Korkean äänenlaadun toisto (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Korkean äänenlaadun tallennus (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Puhelut, molemmat suunnat (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio-äänipalvelin"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "Ulostulolaitteet"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "Sisääntulolaitteet"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "Ääni koneella @HOSTNAME@"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "Sisääntulo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "Telakan sisääntulo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "Telakan mikrofoni"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "Telakan sisääntulo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "Linjasisääntulo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "Mikrofoni"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "Telakan mikrofoni"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "Mikrofoni"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "Ulkoinen mikrofoni"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "Sisäinen mikrofoni"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "Video"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "Automaattinen äänenvoimakkuuden säätö"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "Ei automaattista äänenvoimakkuuden säätöä"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "Vahvistus"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "Ei vahvistusta"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "Vahvistin"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "Ei vahvistinta"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "Vahvistus"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "Ei vahvistusta"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "Analoginen kuulokeliitäntä"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "Analoginen sisääntulo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "Telakan mikrofoni"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "Analoginen ulostulo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "Analoginen ulostulo (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "Linjasisääntulo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "Analoginen monoulostulo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "Analoginen stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "Digitaalinen stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digitaalinen stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "Analoginen mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "Analoginen stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "Analoginen tilaääni 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "Analoginen tilaääni 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "Analoginen tilaääni 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "Analoginen tilaääni 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "Analoginen tilaääni 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "Analoginen tilaääni 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "Analoginen tilaääni 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "Analoginen tilaääni 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "Analoginen tilaääni 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "Analoginen tilaääni 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "Analoginen tilaääni 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "Digitaalinen stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digitaalinen stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digitaalinen tilaääni 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digitaalinen tilaääni 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "Digitaalinen stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digitaalinen tilaääni 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "Analoginen mono, molemmat suunnat"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "Analoginen stereo, molemmat suunnat"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Digitaalinen stereo, molemmat suunnat (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Tyhjä ulostulo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "Sisääntulo"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<nielun nimi> sink_properties=<nielun asetukset> "
+"master=<suodatettavan nielun nimi> format=<näytemuoto> "
+"rate=<näytteenottotaajuus> channels=<kanavien määrä> "
+"channel_map=<kanavakartta> plugin=<ladspa-liitännäisen nimi> label=<ladspa-"
+"liitännäisen nimiö (label)> control=<pilkulla erotettu luettelo "
+"syötteenhallinta-arvoja>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimitiä ei tueta tällä alustalla."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() epäonnistui"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Lähteen ulostulo #%u\n"
+#~ "\tAjuri: %s\n"
+#~ "\tOmistava moduuli: %s\n"
+#~ "\tAsiakas: %s\n"
+#~ "\tLähde: %u\n"
+#~ "\tNäytemäärittely: %s\n"
+#~ "\tKanavakartta: %s\n"
+#~ "\tPuskurin latenssi: %0.0f μs\n"
+#~ "\tLähteen latenssi: %0.0f μs\n"
+#~ "\tUudelleennäytteistyksen tapa: %s\n"
+#~ "\tOminaisuudet:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [valitsimet] stat\n"
+#~ "%s [valitsimet] list\n"
+#~ "%s [valitsimet] exit\n"
+#~ "%s [valitsimet] upload-sample TIEDOSTONIMI [NIMI]\n"
+#~ "%s [valitsimet] play-sample NIMI [NIELU]\n"
+#~ "%s [valitsimet] remove-sample NIMI\n"
+#~ "%s [valitsimet] move-sink-input NIELUSISÄÄNMENO NIELU\n"
+#~ "%s [valitsimet] move-source-output LÄHDEULOSTULO LÄHDE\n"
+#~ "%s [valitsimet] load-module NIMI [ARGUMENTIT ...]\n"
+#~ "%s [valitsimet] unload-module MODUULI\n"
+#~ "%s [valitsimet] suspend-sink NIELU 1|0\n"
+#~ "%s [valitsimet] suspend-source LÄHDE 1|0\n"
+#~ "%s [valitsimet] set-card-profile KORTTI PROFIILI\n"
+#~ "%s [valitsimet] set-sink-port NIELU PORTTI\n"
+#~ "%s [valitsimet] set-source-port LÄHDE PORTTI\n"
+#~ "%s [valitsimet] set-sink-volume NIELU VOIMAKKUUS\n"
+#~ "%s [valitsimet] set-source-volume LÄHDE VOIMAKKUUS\n"
+#~ "%s [valitsimet] set-sink-input-volume NIELUSISÄÄNMENO VOIMAKKUUS\n"
+#~ "%s [valitsimet] set-sink-mute NIELU 1|0\n"
+#~ "%s [valitsimet] set-source-mute LÄHDE 1|0\n"
+#~ "%s [valitsimet] set-sink-input-mute NIELUSISÄÄNMENO 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Näytä tämä ohje\n"
+#~ "      --version                         Näytä versio\n"
+#~ "\n"
+#~ "  -s, --server=PALVELIN                 Sen palvelimen nimi, johon "
+#~ "yhdistetään\n"
+#~ "  -n, --client-name=NIMI                Kuinka tätä asiakasohjelmaa "
+#~ "kutsutaan palvelimella\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Digitaalinen tilaääni 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Alataajuus"
+
+#~ msgid "Invalid client name '%s'\n"
+#~ msgstr "Virheellinen asiakasohjelman nimi ”%s”\n"
+
+#~ msgid "Failed to determine sample specification from file.\n"
+#~ msgstr "Näytemäärityksen selvitys tiedostosta epäonnistui: %s\n"
+
+#~ msgid "select(): %s"
+#~ msgstr "select(): %s"
+
+#~ msgid "Cannot connect to system bus: %s"
+#~ msgstr "Järjestelmäväylään ei voida yhdistää: %s"
+
+#~ msgid "Cannot get caller from PID: %s"
+#~ msgstr "Kutsujaa ei saada PID:stä: %s"
+
+#~ msgid "Cannot set UID on caller object."
+#~ msgstr "UID:tä ei voida asettaa kutsujaobjektille."
+
+#~ msgid "Failed to get CK session."
+#~ msgstr "CK-istunnon saaminen epäonnistui."
+
+#~ msgid "Cannot set UID on session object."
+#~ msgstr "UID:tä ei voida asettaa istunto-objektille."
+
+#~ msgid "Cannot allocate PolKitAction."
+#~ msgstr "Ei voida varata PolKitActionia."
+
+#~ msgid "Cannot set action_id"
+#~ msgstr "Ei voida asettaa action_id:tä"
+
+#~ msgid "Cannot allocate PolKitContext."
+#~ msgstr "Ei voida varata PolKitContextia."
+
+#~ msgid "Cannot initialize PolKitContext: %s"
+#~ msgstr "Ei voida alustaa PolKitContextia: %s"
+
+#~ msgid "Could not determine whether caller is authorized: %s"
+#~ msgstr "Ei voida päätellä onko kutsujalla käyttöoikeus: %s"
+
+#~ msgid "Cannot obtain auth: %s"
+#~ msgstr "Tunnistautumista ei saada: %s"
+
+#~ msgid "PolicyKit responded with '%s'"
+#~ msgstr "PolicyKit vastasi ”%s”"
+
+#~ msgid ""
+#~ "High-priority scheduling (negative Unix nice level) for the PulseAudio "
+#~ "daemon"
+#~ msgstr ""
+#~ "Korkean prioriteetin ajoitus (negatiivinen Unix-nice-taso) PulseAudio-"
+#~ "taustajärjestelmälle"
+
+#~ msgid "Real-time scheduling for the PulseAudio daemon"
+#~ msgstr "Reaaliaikainen ajoitus PulseAudio-taustajärjestelmälle"
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring high-priority scheduling."
+#~ msgstr ""
+#~ "Järjestelmäkäytäntö estää PulseAudiota saamasta korkean prioriteetin "
+#~ "ajoitusta."
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring real-time scheduling."
+#~ msgstr ""
+#~ "Järjestelmäkäytäntö estää PulseAudiota saamasta reaaliaikaista ajoitusta."
+
+#~ msgid "read() failed: %s\n"
+#~ msgstr "read() epäonnistui: %s\n"
+
+#, fuzzy
+#~ msgid "pa_context_connect() failed: %s\n"
+#~ msgstr "pa_context_connect() epäonnistui: %s"
+
+#~ msgid "We're in the group '%s', allowing high-priority scheduling."
+#~ msgstr "Ollaan ryhmässä ”%s”, korkean prioriteetin ajoitus on sallittua."
+
+#~ msgid "We're in the group '%s', allowing real-time scheduling."
+#~ msgstr "Ollaan ryhmässä ”%s”, tosiaikainen vuorottaminen on sallittua."
+
+#~ msgid "PolicyKit grants us acquire-high-priority privilege."
+#~ msgstr "PolicyKit myöntää acquire-high-priority-oikeuden."
+
+#~ msgid "PolicyKit refuses acquire-high-priority privilege."
+#~ msgstr "PolicyKit ei myönnä acquire-high-priority-oikeutta."
+
+#~ msgid "PolicyKit grants us acquire-real-time privilege."
+#~ msgstr "PolicyKit myöntää acquire-real-time-oikeuden."
+
+#~ msgid "PolicyKit refuses acquire-real-time privilege."
+#~ msgstr "PolicyKit ei myönnä acquire-real-time-oikeutta."
+
+#~ msgid ""
+#~ "Called SUID root and real-time and/or high-priority scheduling was "
+#~ "requested in the configuration. However, we lack the necessary "
+#~ "privileges:\n"
+#~ "We are not in group '%s', PolicyKit refuse to grant us the requested "
+#~ "privileges and we have no increase RLIMIT_NICE/RLIMIT_RTPRIO resource "
+#~ "limits.\n"
+#~ "For enabling real-time/high-priority scheduling please acquire the "
+#~ "appropriate PolicyKit privileges, or become a member of '%s', or increase "
+#~ "the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."
+#~ msgstr ""
+#~ "Ohjelmaa kutsuttiin SUID-rootina ja reaaliaikaista ja/tai korkean "
+#~ "prioriteetin ajoitusta pyydettiin asetuksissa. Tarvittavat oikeudet "
+#~ "kuitenkin puuttuvat:\n"
+#~ "Ei olla ryhmässä ”%s”, PolicyKit ei myönnä pyydettyjä oikeuksia ja "
+#~ "RLIMIT_NICE/RLIMIT_RTPRIO-resurssirajoja ei ole kasvatettu.\n"
+#~ "Hanki riittävät PolicyKit-oikeudet, liity ryhmään ”%s” tai kasvata tämän "
+#~ "käyttäjän RLIMIT_NICE/RLIMIT_RTPRIO-resurssirajoja reaaliaikaisen ja/tai "
+#~ "korkean prioriteetin ajoituksen ottamiseksi käyttöön."
+
+#~ msgid ""
+#~ "High-priority scheduling enabled in configuration but not allowed by "
+#~ "policy."
+#~ msgstr ""
+#~ "Korkean prioriteetin ajoitus otettu käyttöön asetuksissa, mutta käytännöt "
+#~ "eivät salli sitä."
+
+#~ msgid "Successfully increased RLIMIT_RTPRIO"
+#~ msgstr "RLIMIT_RTPRIO:n kasvatus onnistui"
+
+#~ msgid "RLIMIT_RTPRIO failed: %s"
+#~ msgstr "RLIMIT_RTPRIO epäonnistui: %s"
+
+#~ msgid "Giving up CAP_NICE"
+#~ msgstr "Luovutaan CAP_NICE:stä"
+
+#~ msgid ""
+#~ "Real-time scheduling enabled in configuration but not allowed by policy."
+#~ msgstr ""
+#~ "Tosiaikainen ajoitus otettu käyttöön asetuksissa, mutta käytännöt eivät "
+#~ "salli sitä."
+
+#~ msgid "Limited capabilities successfully to CAP_SYS_NICE."
+#~ msgstr "Oikeuksien rajoittaminen CAP_SYS_NICEen onnistui."
+
+#~ msgid "time_new() failed.\n"
+#~ msgstr "time_new() epäonnistui.\n"
+
+#~ msgid "Output %s + Input %s"
+#~ msgstr "Ulostulo %s + Sisääntulo %s"
+
+#~ msgid "Stream successfully created\n"
+#~ msgstr "Virran luonti onnistui\n"
+
+#~ msgid "Stream errror: %s\n"
+#~ msgstr "Virtavirhe: %s\n"
+
+#~ msgid "Connection established.\n"
+#~ msgstr "Yhteys muodostetettu.\n"
+
+#~ msgid ""
+#~ "%s [options] [FILE]\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -v, --verbose                         Enable verbose operation\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -d, --device=DEVICE                   The name of the sink to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ "      --stream-name=NAME                How to call this stream on the "
+#~ "server\n"
+#~ "      --volume=VOLUME                   Specify the initial (linear) "
+#~ "volume in range 0...65536\n"
+#~ "      --channel-map=CHANNELMAP          Set the channel map to the use\n"
+#~ msgstr ""
+#~ "%s [valitsimet] [TIEDOSTO]\n"
+#~ "\n"
+#~ "  -h, --help                            Näytä tämä ohje\n"
+#~ "      --version                         Näytä tiedostot\n"
+#~ "\n"
+#~ "  -v, --verbose                         Yksityiskohtaiset tulosteet\n"
+#~ "\n"
+#~ "  -s, --server=PALVELIN                 Sen palvelimen nimi, johon\n"
+#~ "                                        yhdistetään\n"
+#~ "  -d, --device=LAITE                    Sen nielun nimi, johon "
+#~ "yhdistetään\n"
+#~ "  -n, --client-name=NIMI                Kuinka tätä asiakasohjelmaa "
+#~ "kutsutaan\n"
+#~ "                                        palvelimella\n"
+#~ "      --stream-name=NIMI                Kuinka tätä virtaa kutsutaan\n"
+#~ "                                        palvelimella\n"
+#~ "      --volume=ÄÄNENVOIMAKKUUS          Määritä (lineaarinen) "
+#~ "aloitusäänen-\n"
+#~ "                                        voimakkuus väliltä 0...65536\n"
+#~ "      --channel-map=KANAVAKARTTA        Aseta käytettävä kanavakartta\n"
+
+#~ msgid ""
+#~ "paplay %s\n"
+#~ "Compiled with libpulse %s\n"
+#~ "Linked with libpulse %s\n"
+#~ msgstr ""
+#~ "paplay %s\n"
+#~ "Käännetty libpulsen versiolle %s\n"
+#~ "Linkitetty libpulsen versiolle %s\n"
+
+#~ msgid "Invalid channel map\n"
+#~ msgstr "Virheellinen kanavakartta\n"
+
+#~ msgid "Failed to open file '%s'\n"
+#~ msgstr "Tiedoston ”%s” avaaminen epäonnistui\n"
+
+#~ msgid "Channel map doesn't match file.\n"
+#~ msgstr "Kanavakartta ei täsmää tiedostoon.\n"
+
+#~ msgid "Using sample spec '%s'\n"
+#~ msgstr "Käytetään näytemäärittelyä ”%s”\n"
+
+#~ msgid "muted"
+#~ msgstr "vaimennettu"
+
+#~ msgid ""
+#~ "*** Autoload Entry #%u ***\n"
+#~ "Name: %s\n"
+#~ "Type: %s\n"
+#~ "Module: %s\n"
+#~ "Argument: %s\n"
+#~ msgstr ""
+#~ "*** Automaattilataustietue #%u ***\n"
+#~ "Nimi: %s\n"
+#~ "Tyyppi: %s\n"
+#~ "Moduuli: %s\n"
+#~ "Argumentto: %s\n"
diff --git a/po/fr.po b/po/fr.po
new file mode 100644 (file)
index 0000000..e859d65
--- /dev/null
+++ b/po/fr.po
@@ -0,0 +1,3179 @@
+# French translation of pulseaudio.
+# Copyright (C) 2006-2008 Lennart Poettering
+# This file is distributed under the same license as the pulseaudio package.
+# 
+# 
+# Robert-André Mauchin <zebob.m@pengzone.org>, 2008.
+# Michaël Ughetto <telimektar esraonline com>, 2008.
+# Pablo Martin-Gomez <pablo.martin-gomez@laposte.net>, 2008.
+# Corentin Perard <corentin.perard@gmail.com>, 2009.
+# Thomas Canniot <mrtom@fedoraproject.org>, 2009, 2012.
+# Sam Friedmann <sfriedma@redhat.com>, 2016. #zanata
+# Wim Taymans <wim.taymans@gmail.com>, 2016. #zanata
+# Edouard Duliege <edouard.duliege@gmail.com>, 2017. #zanata
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-10-06 16:57+0200\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"PO-Revision-Date: 2017-05-10 04:42-0400\n"
+"Last-Translator: Edouard Duliege <edouard.duliege@gmail.com>\n"
+"Language-Team: French <fedora-trans-fr@redhat.com>\n"
+"Language: fr\n"
+"Plural-Forms: nplurals=2; plural=n>1;\n"
+"X-Generator: Zanata 3.9.6\n"
+
+#: ../src/daemon/cmdline.c:111
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"COMMANDES :\n"
+"  -h, --help                            Afficher cette aide\n"
+"      --version                         Afficher la version\n"
+"      --dump-conf                       Vider la configuration par défaut\n"
+"      --dump-modules                    Vider la liste des modules "
+"disponibles\n"
+"      --dump-resample-methods           Vider les méthodes resample "
+"disponibles\n"
+"      --cleanup-shm                     Effacer les segments usés de la "
+"mémoire partagée\n"
+"      --start                           Lancer le démon s'il n'est pas en "
+"cours d'exécution\n"
+"  -k  --kill                            Kill un démon en cours d'exécution\n"
+"      --check                           Rechercher un démon en cours "
+"d'exécution (retourne uniquement un code de sortie)\n"
+"\n"
+"OPTIONS :\n"
+"      --system[=BOOL]                   Exécuter en tant qu'instance globale\n"
+"  -D, --daemonize[=BOOL]                Démoniser après le lancement\n"
+"      --fail[=BOOL]                     Quitter si le lancement échoue\n"
+"      --high-priority[=BOOL]            Tenter de définir un niveau nice "
+"élevé\n"
+"                                        (uniquement disponible en tant que "
+"root, lorsque SUID ou\n"
+"                                        avec RLIMIT_NICE élevé)\n"
+"      --realtime[=BOOL]                 Tenter d'activer la planification "
+"realtime\n"
+"                                        (uniquement disponible en tant que "
+"root, lorsque SUID ou\n"
+"                                        avec RLIMIT_RTPRIO élevé)\n"
+"      --disallow-module-loading[=BOOL]   Interdire le chargement ou "
+"déchargement de modules\n"
+"                                        requis par l'utilisateur de module "
+"après le lancement\n"
+"      --disallow-exit[=BOOL]            Interdire la sortie requise par "
+"l'utilisateur\n"
+"      --exit-idle-time=SECS             Quitter le démon lorsqu'inactif et "
+"que cette\n"
+"                                        période s'est écoulée\n"
+"      --scache-idle-time=SECS           Décharger les samples chargés "
+"automatiquement lorsqu'inactif et que cette\n"
+"                                        période s'est écoulée\n"
+"      --log-level[=LEVEL]               Augmenter ou définir le niveau de "
+"détail\n"
+"  -v  --verbose                         Augmenter le niveau de détail\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Indiquer la cible du journal\n"
+"      --log-meta[=BOOL]                 Inclure l'emplacement du code dans "
+"les messages journaux\n"
+"      --log-time[=BOOL]                 Inclure l'horodatage dans les "
+"messages journaux\n"
+"      --log-backtrace=FRAMES            Inclure un backtrace dans les "
+"messages journaux\n"
+"  -p, --dl-search-path=PATH             Définir le chemin de recherche pour "
+"les objets dynamiques\n"
+"                                        partagés (greffons)\n"
+"      --resample-method=METHOD          Utiliser la méthode resample "
+"spécifiée\n"
+"                                        (Voir --dump-resample-methods pour\n"
+"                                        les valeurs possibles)\n"
+"      --use-pid-file[=BOOL]             Créer un fichier PID\n"
+"      --no-cpu-limit[=BOOL]             Ne pas installer de limiteur de "
+"charge CPU sur\n"
+"                                        les platformes qui le prenne en "
+"charge\n"
+"      --disable-shm[=BOOL]              Désactiver la prise en charge de "
+"mémoire partagée.\n"
+"\n"
+"SCRIPT DE LANCEMENT :\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Charger le module du greffon "
+"spécifié avec\n"
+"                                        l'argument spécifié\n"
+"  -F, --file=FILENAME                   Lancer le script spécifié\n"
+"  -C                                    Ouvrir une ligne de commande sur le "
+"TTY en cours d'exécution\n"
+"                                        après le lancement\n"
+"\n"
+"  -n                                    Ne pas charger le fichier du script "
+"par défaut\n"
+
+#: ../src/daemon/cmdline.c:243
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize requiert un paramètre booléen"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail requiert un paramètre booléen"
+
+#: ../src/daemon/cmdline.c:262
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level requiert un paramètre de niveau de journal (soit numérique entre "
+"0 et 4, soit de débogage : info, notice, warn , error)."
+
+#: ../src/daemon/cmdline.c:274
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority requiert un paramètre booléen"
+
+#: ../src/daemon/cmdline.c:282
+msgid "--realtime expects boolean argument"
+msgstr "--realtime requiert un paramètre booléen"
+
+#: ../src/daemon/cmdline.c:290
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading requiert un paramètre booléen"
+
+#: ../src/daemon/cmdline.c:298
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit requiert un paramètre booléen"
+
+#: ../src/daemon/cmdline.c:306
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file requiert un paramètre booléen"
+
+#: ../src/daemon/cmdline.c:325
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Cible du journal invalide : veuillez utiliser «  syslog », "
+"« journal »,« stderr » ou « auto », ou un nom de fichier valide « file:"
+"<path> », « newfile:<path> »."
+
+#: ../src/daemon/cmdline.c:327
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Cible du journal invalide : veuillez utiliser «  syslog »,« stderr » ou "
+"« auto », ou un nom de fichier valide « file:<path> », « newfile:<path> »."
+
+#: ../src/daemon/cmdline.c:335
+msgid "--log-time expects boolean argument"
+msgstr "--log-time requiert un paramètre booléen"
+
+#: ../src/daemon/cmdline.c:343
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta requiert un paramètre booléen"
+
+#: ../src/daemon/cmdline.c:363
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Méthode de rééchantillonnage invalide « %s »."
+
+#: ../src/daemon/cmdline.c:370
+msgid "--system expects boolean argument"
+msgstr "--system requiert un paramètre booléen"
+
+#: ../src/daemon/cmdline.c:378
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit requiert un paramètre booléen"
+
+#: ../src/daemon/cmdline.c:386
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm requiert un paramètre booléen"
+
+#: ../src/daemon/daemon-conf.c:258
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Cible du journal « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:273
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Niveau du journal « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:288
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Méthode de rééchantillonnage « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:310
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] rlimit « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Format d'échantillon « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:347 ../src/daemon/daemon-conf.c:364
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Taux d'échantillonnage « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:387
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Canaux d'échantillonnage « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:404
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Plan de canaux « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:421
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Nombre de fragments « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:438
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Taille du fragment « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:455
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Niveau de priorité (nice) « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:498
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Type de serveur « %s » invalide."
+
+#: ../src/daemon/daemon-conf.c:611
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Échec lors de l'ouverture du fichier de configuration : %s"
+
+#: ../src/daemon/daemon-conf.c:627
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Le plan de canaux spécifié par défaut a un nombre de canaux différent du "
+"nombre spécifié par défaut."
+
+#: ../src/daemon/daemon-conf.c:714
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Lecture à partir du fichier de configuration : %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Nom : %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Aucune information de module disponible\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Version : %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Description : %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Auteur : %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Utilisation : %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Chargement unique : %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "AVERTISSEMENT D'OBSOLESCENCE : %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Chemin : %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Échec d'ouverture du module %s : %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Échec lors de la recherche du chargeur lt_dlopen original."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Échec lors de l'allocation du nouveau chargeur dl."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Échec lors de l'ajout du chargeur bind-now."
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Impossible de trouver l'utilisateur « %s »."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Impossible de trouver le groupe « %s »."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr ""
+"Le GID de l'utilisateur « %s » et du groupe « %s » ne sont pas identiques."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr ""
+"Le dossier personnel de l'utilisateur « %s » n'est pas « %s », ignoré."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Échec lors de la création de « %s » : %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Échec lors du changement de la liste du groupe : %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Échec lors du changement de GID : %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Échec lors du changement d'UID : %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "Mode système étendu non pris en charge sur cette plateforme."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Échec lors de l'analyse de la ligne de commande"
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Mode système refusé pour les utilisateurs non root. Lancement du service de "
+"recherche du serveur D-Bus uniquement."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Impossible de tuer le démon : %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Le programme n'est pas conçu pour être lancé en tant que root (sauf si --"
+"system est renseigné)."
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Les privilèges root sont nécessaires."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "--start n'est pas pris en charge pour les instances système."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+"Serveur configuré par l'utilisateur sur %s, refus du lancement start/"
+"autospawn."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Serveur configuré par l'utilisateur sur %s, qui semble être local. Analyse "
+"plus précise en cours."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr ""
+"Le démon s'exécute en mode système, mais --disallow-exit n'est pas défini."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"Le démon s'exécute en mode système, mais --disallow-module-loading n'est pas "
+"défini."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "Le démon s'exécute en mode système, désactivation forcée du mode SHM."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"Le démon s'exécute en mode système, désactivation forcée de la fermeture "
+"après délai d'inactivité."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "Échec lors de l'acquisition de stdio."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "Échec de pipe() : %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "Échec de fork() : %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:567
+#, c-format
+msgid "read() failed: %s"
+msgstr "Échec de read() : %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Échec lors du démarrage du démon."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "Échec de setsid() : %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Échec lors de l'obtention de l'ID de la machine"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Vous exécutez PA dans un mode système. Sachez que vous ne devriez pas faire "
+"cela.\n"
+"Si vous choisissez malgré tout de le faire, vous êtes responsable de tout "
+"dysfonctionnement inattendu.\n"
+"Veuillez lire http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ pour comprendre pourquoi le "
+"mode système est généralement une mauvaise idée."
+
+#: ../src/daemon/main.c:991
+msgid "pa_pid_file_create() failed."
+msgstr "Échec de pa_pid_file_create()."
+
+#: ../src/daemon/main.c:1021
+msgid "pa_core_new() failed."
+msgstr "Échec de pa_core_new()."
+
+#: ../src/daemon/main.c:1088
+msgid "Failed to initialize daemon."
+msgstr "Échec lors de l'initialisation du démon"
+
+#: ../src/daemon/main.c:1093
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Démarrage du démon sans aucun module chargé : refus de fonctionner."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Système de son PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Démarrer le système de son PulseAudio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2291
+msgid "Input"
+msgstr "Entrée"
+
+#: ../src/modules/alsa/alsa-mixer.c:2292
+msgid "Docking Station Input"
+msgstr "Entrée de la station d'accueil"
+
+#: ../src/modules/alsa/alsa-mixer.c:2293
+msgid "Docking Station Microphone"
+msgstr "Microphone de la station d'accueil"
+
+#: ../src/modules/alsa/alsa-mixer.c:2294
+msgid "Docking Station Line In"
+msgstr "Entrée ligne de la station d'accueil"
+
+#: ../src/modules/alsa/alsa-mixer.c:2295 ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Line In"
+msgstr "Entrée ligne"
+
+#: ../src/modules/alsa/alsa-mixer.c:2296 ../src/modules/alsa/alsa-mixer.c:2374
+#: ../src/modules/bluetooth/module-bluez4-device.c:2102
+#: ../src/modules/bluetooth/module-bluez5-device.c:1710
+msgid "Microphone"
+msgstr "Microphone"
+
+#: ../src/modules/alsa/alsa-mixer.c:2297 ../src/modules/alsa/alsa-mixer.c:2375
+msgid "Front Microphone"
+msgstr "Microphone avant"
+
+#: ../src/modules/alsa/alsa-mixer.c:2298 ../src/modules/alsa/alsa-mixer.c:2376
+msgid "Rear Microphone"
+msgstr "Microphone arrière"
+
+#: ../src/modules/alsa/alsa-mixer.c:2299
+msgid "External Microphone"
+msgstr "Microphone externe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2300 ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Internal Microphone"
+msgstr "Microphone interne"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301 ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2302 ../src/modules/alsa/alsa-mixer.c:2382
+msgid "Video"
+msgstr "Vidéo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2303
+msgid "Automatic Gain Control"
+msgstr "Contrôle automatique du gain"
+
+#: ../src/modules/alsa/alsa-mixer.c:2304
+msgid "No Automatic Gain Control"
+msgstr "Pas de contrôle automatique du gain"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Boost"
+msgstr "Boost"
+
+#: ../src/modules/alsa/alsa-mixer.c:2306
+msgid "No Boost"
+msgstr "Pas de boost"
+
+#: ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Amplifier"
+msgstr "Amplificateur"
+
+#: ../src/modules/alsa/alsa-mixer.c:2308
+msgid "No Amplifier"
+msgstr "Pas d'amplificateur"
+
+#: ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Bass Boost"
+msgstr "Booster de basses"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "No Bass Boost"
+msgstr "Pas de booster de basses"
+
+#: ../src/modules/alsa/alsa-mixer.c:2311
+#: ../src/modules/bluetooth/module-bluez4-device.c:2107
+#: ../src/modules/bluetooth/module-bluez5-device.c:1717
+msgid "Speaker"
+msgstr "Haut-parleur"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312 ../src/modules/alsa/alsa-mixer.c:2384
+msgid "Headphones"
+msgstr "Casque audio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2373
+msgid "Analog Input"
+msgstr "Entrée analogique"
+
+#: ../src/modules/alsa/alsa-mixer.c:2377
+msgid "Dock Microphone"
+msgstr "Microphone de la station d'accueil"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Headset Microphone"
+msgstr "Microphone casque"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383
+msgid "Analog Output"
+msgstr "Sortie analogique"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385
+msgid "LFE on Separate Mono Output"
+msgstr "Sortie analogique (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "Line Out"
+msgstr "Entrée ligne"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387
+msgid "Analog Mono Output"
+msgstr "Sortie mono analogique"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388
+msgid "Speakers"
+msgstr "Haut-parleurs"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Digital Output (S/PDIF)"
+msgstr "Sortie numérique (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "Digital Input (S/PDIF)"
+msgstr "Entrée numérique (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Relais numérique (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Analog Mono"
+msgstr "Mono analogique"
+
+#: ../src/modules/alsa/alsa-mixer.c:3899
+msgid "Analog Stereo"
+msgstr "Stéréo analogique"
+
+#: ../src/modules/alsa/alsa-mixer.c:3900
+msgid "Multichannel"
+msgstr "Multicanal"
+
+#: ../src/modules/alsa/alsa-mixer.c:3901
+msgid "Analog Surround 2.1"
+msgstr "Surround analogique 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3902
+msgid "Analog Surround 3.0"
+msgstr "Surround analogique 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3903
+msgid "Analog Surround 3.1"
+msgstr "Surround analogique 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3904
+msgid "Analog Surround 4.0"
+msgstr "Surround analogique 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3905
+msgid "Analog Surround 4.1"
+msgstr "Surround analogique 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3906
+msgid "Analog Surround 5.0"
+msgstr "Surround analogique 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3907
+msgid "Analog Surround 5.1"
+msgstr "Surround analogique 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3908
+msgid "Analog Surround 6.0"
+msgstr "Surround analogique 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3909
+msgid "Analog Surround 6.1"
+msgstr "Surround analogique 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3910
+msgid "Analog Surround 7.0"
+msgstr "Surround analogique 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3911
+msgid "Analog Surround 7.1"
+msgstr "Surround analogique 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3912
+msgid "Digital Stereo (IEC958)"
+msgstr "Stéréo numérique (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3913
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Relais numérique (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3914
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Surround numérique 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3915
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Surround numérique 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3916
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Surround numérique 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3917
+msgid "Digital Stereo (HDMI)"
+msgstr "Stéréo numérique (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3918
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Surround numérique 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4049
+msgid "Analog Mono Duplex"
+msgstr "Duplex Mono analogique"
+
+#: ../src/modules/alsa/alsa-mixer.c:4050
+msgid "Analog Stereo Duplex"
+msgstr "Duplex stéréo analogique"
+
+#: ../src/modules/alsa/alsa-mixer.c:4051
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Duplex stéréo numérique (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4052
+#: ../src/modules/alsa/module-alsa-card.c:190
+#: ../src/modules/bluetooth/module-bluez4-device.c:2298
+#: ../src/modules/bluetooth/module-bluez5-device.c:1941
+msgid "Off"
+msgstr "Éteint"
+
+#: ../src/modules/alsa/alsa-mixer.c:4151
+#, c-format
+msgid "%s Output"
+msgstr "Sortie %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:4159
+#, c-format
+msgid "%s Input"
+msgstr "Entrée %s"
+
+#: ../src/modules/alsa/alsa-sink.c:570 ../src/modules/alsa/alsa-sink.c:747
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nous a réveillé pour écrire de nouvelles données à partir du "
+"périphérique, mais il n'y avait en fait rien à écrire !\n"
+"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d'ALSA.\n"
+"Nous avons été réveillés avec POLLOUT actif, cependant un snd_pcm_avail() "
+"ultérieur a retourné 0 ou une autre valeur < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529 ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nous a réveillé pour lire de nouvelles données à partir du "
+"périphérique, mais il n'y avait en fait rien à lire !\n"
+"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d'ALSA.\n"
+"Nous avons été réveillés avec POLLIN actif, cependant un snd_pcm_avail() "
+"ultérieur a retourné 0 ou une autre valeur < min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1134 ../src/modules/alsa/alsa-util.c:1209
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() a retourné une valeur qui est exceptionnellement large : %lu "
+"octets (%lu ms).\n"
+"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d'ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1184
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
+"(%s%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() a retourné une valeur qui est exceptionnellement large : %li "
+"octets (%s%lu ms).\n"
+"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d'ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1225
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() a retourné des valeurs inhabituelles : le délai %lu "
+"est inférieur au %lu disponible.\n"
+"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d'ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1268
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() a retourné une valeur qui est exceptionnellement large :"
+" %lu octets (%lu·ms).\n"
+"Il s'agit très probablement d'un bogue dans le pilote ALSA « %s ». Veuillez "
+"rapporter ce problème aux développeurs d'ALSA."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2092
+#: ../src/modules/bluetooth/module-bluez5-device.c:1700
+msgid "Headset"
+msgstr "Casque"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2097
+#: ../src/modules/bluetooth/module-bluez5-device.c:1705
+msgid "Handsfree"
+msgstr "Mains-libres"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2112
+#: ../src/modules/bluetooth/module-bluez5-device.c:1723
+msgid "Headphone"
+msgstr "Écouteurs"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2117
+#: ../src/modules/bluetooth/module-bluez5-device.c:1728
+msgid "Portable"
+msgstr "Portable"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2122
+#: ../src/modules/bluetooth/module-bluez5-device.c:1733
+msgid "Car"
+msgstr "Voiture"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2127
+#: ../src/modules/bluetooth/module-bluez5-device.c:1738
+msgid "HiFi"
+msgstr "HiFi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2132
+#: ../src/modules/bluetooth/module-bluez5-device.c:1743
+msgid "Phone"
+msgstr "Téléphone"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2140
+#: ../src/modules/bluetooth/module-bluez5-device.c:1695
+#: ../src/modules/bluetooth/module-bluez5-device.c:1711
+#: ../src/modules/bluetooth/module-bluez5-device.c:1749
+msgid "Bluetooth Output"
+msgstr "Sortie Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2143
+#: ../src/modules/bluetooth/module-bluez5-device.c:1694
+#: ../src/modules/bluetooth/module-bluez5-device.c:1716
+#: ../src/modules/bluetooth/module-bluez5-device.c:1722
+#: ../src/modules/bluetooth/module-bluez5-device.c:1748
+msgid "Bluetooth Input"
+msgstr "Entrée Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2179
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Lecture haute fidélité (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2190
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Capture haute fidélité (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2201
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telephonie en duplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2213
+msgid "Handsfree Gateway"
+msgstr "Passerelle Mains-libres"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1786
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Lecture haute fidélité (A2DP Sink)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1797
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Capture haute fidélité (A2DP Source)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1808
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Unité centrale du casque (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1820
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Passerelle Audio du casque (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> aec_method="
+"<implementation to use> aec_args=<parameters for the AEC engine> save_aec="
+"<save AEC data in /tmp> autoloaded=<set if this module is being loaded "
+"automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"source_name=<nom de la source> source_properties=<propriétés de la source> "
+"source_master=<nom de la source à filtrer> sink_name=<nom de la  "
+"destination> sink_properties=<propriétés de la destination> sink_master=<nom "
+"de la destination à filtrer> adjust_time=<fréquence de réajustement des taux "
+"dans s> adjust_threshold=<décalage à réajuster en ms> format=<format des "
+"échantillons> rate=<taux d'échantillonnage> channels=<nombre de canaux> "
+"channel_map=<plan des canaux> aec_method=<implémentation à utiliser> "
+"aec_args=<paramètres du moteur AEC> save_aec=<enregistrer les données AEC "
+"dans /tmp> autoloaded=<définir si ce module est chargé automatiquement> "
+"use_volume_sharing=<oui ou non> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:754
+msgid "On"
+msgstr "Marche"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Garde toujours au moins une destination même si elle est vide"
+
+#: ../src/modules/module-always-sink.c:80
+msgid "Dummy Output"
+msgstr "Sortie factice"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Égaliseur à but général"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<nom de la destination> sink_properties=<propriétés de la "
+"destination> sink_master=<destination à laquelle se connecter> format="
+"<format de l'échantillon> rate=<taux d'échantillonage> channels=<nombre de "
+"canaux> channel_map=<plan des canaux> autoloaded=<définir si ce module est "
+"chargé automatiquement> use_volume_sharing=<oui ou non>"
+
+#: ../src/modules/module-filter-apply.c:46
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<décharger automatiquement les filtres non utilisés ?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Destination virtuelle LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<nom de la destination> sink_properties=<propriétés de la "
+"destination> master=<nom de la destination à filter> format=<format de "
+"l'échantillon> rate=<taux d'échantillonnage> channels=<nombre de canaux> "
+"channel_map=<plan des canaux> plugin=<nom de l'extension ladspa> label="
+"<étiquette de l'extension ladspa> control=<liste des valeurs de contrôle de "
+"l'entrée séparées par des virgules>"
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Horloge de la destination vide"
+
+#: ../src/modules/module-null-sink.c:278
+msgid "Null Output"
+msgstr "Sortie vide"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Périphériques de sortie"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Périphériques d'entrée"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Audio sur @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Tunnel pour %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:517
+#: ../src/modules/module-tunnel-source-new.c:516
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Tunnel vers %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Destination surround virtuelle"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> use_volume_sharing="
+"<yes or no> force_flat_volume=<yes or no> hrir=/path/to/left_hrir.wav "
+msgstr ""
+"sink_name=<nom de la destination> sink_properties=<propriétés de la "
+"destination> master=<nom de la destination à filtrer> format=<format de "
+"l'échantillon> rate=<taux d'échantillonnage> channels=<nombre de canaux> "
+"channel_map=<plan des canaux> use_volume_sharing=<oui ou non> "
+"force_flat_volume=<oui ou non> hrir=/path/to/left_hrir.wav "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "Serveur de son PulseAudio"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:758
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Avant centre"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Avant gauche"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Avant droit"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Arrière centre"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Arrière gauche"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Arrière droit"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Caisson de basses"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Avant à gauche du centre"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Avant à droite du centre"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Côté gauche"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Côté droit"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Auxiliaire 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Auxiliaire 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Auxiliaire 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Auxiliaire 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Auxiliaire 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Auxiliaire 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Auxiliaire 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Auxiliaire 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Auxiliaire 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Auxiliaire 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Auxiliaire 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Auxiliaire 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Auxiliaire 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Auxiliaire 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Auxiliaire 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Auxiliaire 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Auxiliaire 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Auxiliaire 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Auxiliaire 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Auxiliaire 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Auxiliaire 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Auxiliaire 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Auxiliaire 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Auxiliaire 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Auxiliaire 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Auxiliaire 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Auxiliaire 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Auxiliaire 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Auxiliaire 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Auxiliaire 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Auxiliaire 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Auxiliaire 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Centre haut"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Avant centre haut"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Avant gauche haut"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Avant droit haut"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Arrière centre haut"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Arrière gauche haut"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Arrière droit haut"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:175 ../src/pulse/volume.c:294
+#: ../src/pulse/volume.c:320 ../src/pulse/volume.c:340
+#: ../src/pulse/volume.c:372 ../src/pulse/volume.c:412
+#: ../src/pulse/volume.c:431
+msgid "(invalid)"
+msgstr "(invalide)"
+
+#: ../src/pulse/channelmap.c:762
+msgid "Stereo"
+msgstr "Stéréo"
+
+#: ../src/pulse/channelmap.c:767
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:773
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:779
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:785
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "Échec de xcb_connect()"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() a retourné une valeur true"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Échec lors de l'analyse des données du cookie"
+
+#: ../src/pulse/context.c:656
+#, c-format
+msgid "fork(): %s"
+msgstr "fork() : %s"
+
+#: ../src/pulse/context.c:711
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid() : %s"
+
+#: ../src/pulse/context.c:1412
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Message reçu pour une extension inconnue « %s »"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "entrée"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "sortie"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "bidirectionnel"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "non valide"
+
+#: ../src/pulsecore/core-util.c:1820
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"XDG_RUNTIME_DIR (%s) ne nous appartient pas (uid %d), mais appartient à uid "
+"%d! (Ceci peut se produire si par exemple vous tentez de vous connecter à un "
+"PulseAudio non root en tant qu'utilisateur root sur le protocole natif. "
+"Veuillez ne pas faire cela.)"
+
+#: ../src/pulsecore/core-util.h:93
+msgid "yes"
+msgstr "oui"
+
+#: ../src/pulsecore/core-util.h:93
+msgid "no"
+msgstr "non"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Impossible d'accèder au verrou autonome."
+
+#: ../src/pulsecore/log.c:153
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Échec de l'ouverture du fichier cible « %s »."
+
+#: ../src/pulsecore/log.c:176
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Tentative d'ouvrir le fichier cible « %s », « %s.1 », « %s.2 » ... « %s."
+"%d », mais tout a échoué."
+
+#: ../src/pulsecore/log.c:631
+msgid "Invalid log target."
+msgstr "Cible du journal non valide."
+
+#: ../src/pulsecore/sink.c:3427
+msgid "Built-in Audio"
+msgstr "Audio interne"
+
+#: ../src/pulsecore/sink.c:3432
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Accès refusé"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Commande inconnue"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Paramètre invalide"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "L'entité existe"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Aucune entité de ce type"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Connexion refusée"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Erreur du protocole"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Délai dépassé"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Pas de clé d'authentification"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Erreur interne"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Connexion terminée"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "L'entité a été tuée"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Serveur invalide"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Échec lors de l'initialisation du module"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "État incorrect"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Aucune donnée"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Version du protocole invalide"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Trop grand"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Non pris en charge"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Code d'erreur inconnu"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Aucune extension de ce type"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Fonctionnalité dépréciée"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Implantation manquante"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Le client s'est divisé (Client forked)"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Erreur d'entrée/sortie"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Périphérique ou ressource occupé"
+
+#: ../src/pulse/sample.c:177
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f Gio"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f Mio"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f Kio"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/tests/resampler-test.c:255
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Afficher cette aide\n"
+"-v, --verbose                         Imprimer les messages de débogage\n"
+"      --from-rate=SAMPLERATE          À partir du taux d'échantillonnage en "
+"Hz (par défaut 44100)\n"
+"      --from-format=SAMPLEFORMAT      À partir du type d'échantillon (par "
+"défaut s16le)\n"
+"      --from-channels=CHANNELS        À partir du nombre de canaux (par "
+"défaut 1)\n"
+"      --to-rate=SAMPLERATE            Vers le taux d'échantillonnage en Hz "
+"(par défaut 44100)\n"
+"      --to-format=SAMPLEFORMAT        Vers le type d'échantillon (par défaut "
+"s16le)\n"
+"      --to-channels=CHANNELS          Vers le nombre de canaux (par défaut "
+"1)\n"
+"      --resample-method=METHOD        Méthode de rééchantillonnage (par "
+"défaut auto)\n"
+"      --seconds=SECONDS               à partir de la durée du flux (par "
+"défaut 60)\n"
+"\n"
+"Si les formats ne sont pas indiqués, le test effectue toutes les "
+"combinaisons de formats,\n"
+"les unes après les autres.\n"
+"\n"
+"Le type d'échantillon doit être l'un de s16le, s16be, u8, float32le, "
+"float32be, ulaw, alaw,\n"
+"s24le, s24be, s24-32le, s24-32be, s32le, s32be (par défaut s16ne)\n"
+"\n"
+"Veuillez consulter --dump-resample-methods pour voir les valeurs possibles "
+"des méthodes de rééchantillonnage.\n"
+
+#: ../src/tests/resampler-test.c:354
+#, c-format
+msgid "%s %s\n"
+msgstr "%s %s\n"
+
+#: ../src/utils/pacat.c:116
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Échec lors du vidage du flux : %s"
+
+#: ../src/utils/pacat.c:121
+msgid "Playback stream drained."
+msgstr "Flux de lecture vidé."
+
+#: ../src/utils/pacat.c:132
+msgid "Draining connection to server."
+msgstr "Vidage de la connexion au serveur."
+
+#: ../src/utils/pacat.c:145
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain() : %s"
+
+#: ../src/utils/pacat.c:168
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "Échec de pa_stream_write() : %s"
+
+#: ../src/utils/pacat.c:209
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "Échec de pa_stream_begin_write() : %s"
+
+#: ../src/utils/pacat.c:259 ../src/utils/pacat.c:289
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "Échec de pa_stream_peek() : %s"
+
+#: ../src/utils/pacat.c:339
+msgid "Stream successfully created."
+msgstr "Création du flux réussie."
+
+#: ../src/utils/pacat.c:342
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "Échec de pa_stream_get_buffer_attr() : %s"
+
+#: ../src/utils/pacat.c:346
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Mesures du tampon : maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:349
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Mesures du tampon : maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr ""
+"Utilisation de la spécification d'échantillon « %s », plan des canaux « %s »."
+""
+
+#: ../src/utils/pacat.c:357
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Connecté au périphérique %s (index : %u, suspendu : %s)."
+
+#: ../src/utils/pacat.c:367
+#, c-format
+msgid "Stream error: %s"
+msgstr "Erreur du flux : %s"
+
+#: ../src/utils/pacat.c:377
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Périphérique de flux suspendu.%s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Périphérique de flux repris.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Flux vide.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Flux saturé.%s"
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream started.%s"
+msgstr "Flux démarré.%s"
+
+#: ../src/utils/pacat.c:408
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Flux déplacé vers le périphérique %s (%u, %ssuspendu).%s"
+
+#: ../src/utils/pacat.c:408
+msgid "not "
+msgstr "non "
+
+#: ../src/utils/pacat.c:415
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Des attributs du tampon de flux ont changé.%s"
+
+#: ../src/utils/pacat.c:430
+msgid "Cork request stack is empty: corking stream"
+msgstr "La pile de requêtes de bouchons est vide : bouchonnage du flux"
+
+#: ../src/utils/pacat.c:436
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "La pile de requêtes de bouchons est vide : débouchonnage du flux"
+
+#: ../src/utils/pacat.c:440
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+"Avertissement : il a été reçu davantage de requêtes de bouchonnage que de "
+"requêtes de débouchonnage !"
+
+#: ../src/utils/pacat.c:465
+#, c-format
+msgid "Connection established.%s"
+msgstr "Connexion établie.%s"
+
+#: ../src/utils/pacat.c:468
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "Échec de pa_stream_new() : %s"
+
+#: ../src/utils/pacat.c:506
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "Échec de pa_stream_connect_playback() : %s"
+
+#: ../src/utils/pacat.c:512
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "impossible de définir le flux du moniteur : %s"
+
+#: ../src/utils/pacat.c:516
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "Échec de pa_stream_connect_record() : %s"
+
+#: ../src/utils/pacat.c:529 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Échec lors de la connexion : %s"
+
+#: ../src/utils/pacat.c:562
+msgid "Got EOF."
+msgstr "EOF obtenu."
+
+#: ../src/utils/pacat.c:599
+#, c-format
+msgid "write() failed: %s"
+msgstr "Échec de write() : %s"
+
+#: ../src/utils/pacat.c:620
+msgid "Got signal, exiting."
+msgstr "Signal obtenu, fermeture."
+
+#: ../src/utils/pacat.c:634
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Échec lors de l'obtention de la latence : %s"
+
+#: ../src/utils/pacat.c:639
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Durée : %0.3f sec. ; Latence : %0.0f µsec."
+
+#: ../src/utils/pacat.c:660
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "Échec de pa_stream_update_timing_info() : %s"
+
+#: ../src/utils/pacat.c:670
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Afficher cette aide\n"
+"      --version                         Afficher la version\n"
+"\n"
+"  -r, --record                          Créer une connexion pour "
+"l'enregistrement\n"
+"  -p, --playback                        Créer une connexion pour la lecture\n"
+"\n"
+"  -v, --verbose                         Activer les opérations détaillées\n"
+"\n"
+"  -s, --server=SERVER                   Nom du serveur auquel se connecter\n"
+"  -d, --device=DEVICE                   Nom de la destination ou de la "
+"source à laquelle se connecter\n"
+"  -n, --client-name=NAME                Comment appeler ce client sur le "
+"serveur\n"
+"      --stream-name=NAME                Comment appeler ce flux sur le "
+"serveur\n"
+"      --volume=VOLUME                   Indiquer le volume (linéaire) "
+"initial dans la gamme 0...65536\n"
+"      --rate=SAMPLERATE                 Taux d'échantillonnage en Hz (par "
+"défaut 44100)\n"
+"      --format=SAMPLEFORMAT             Type d'échantillon, un de s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (par défaut "
+"s16ne)\n"
+"      --channels=CHANNELS               Nombre de canaux, 1 pour mono, 2 "
+"pour stéréo\n"
+"                                        (par défaut 2)\n"
+"      --channel-map=CHANNELMAP          Plan des canaux à utiliser à la "
+"place de la valeur par défaut\n"
+"      --fix-format                      Utiliser le format du sample de la "
+"destination ou de la source à laquelle le flux\n"
+"                                        est connecté.\n"
+"      --fix-rate                        Utiliser le taux d'échantillonnage "
+"de la destination ou de la source à laquelle le flux\n"
+"                                        est connecté.\n"
+"      --fix-channels                    Utiliser le nombre de canaux et le "
+"plan des canaux\n"
+"                                        de la destination ou de la source à "
+"laquelle le flux est connecté.\n"
+"      --no-remix                        Ne pas mélanger (upmix ou downmix) "
+"les canaux.\n"
+"      --no-remap                        Cartographier les canaux par index "
+"plutôt que par nom.\n"
+"      --latency=BYTES                   Demander la latence spécifiée en "
+"octets.\n"
+"      --process-time=BYTES              Demander le temps de traitement "
+"spécifié par requête en octets.\n"
+"      --latency-msec=MSEC               Demander la latence spécifiée en "
+"msec.\n"
+"      --process-time-msec=MSEC          Demander le temps de traitement "
+"spécifié par requête en msec.\n"
+"      --property=PROPERTY=VALUE         Définir la propriété spécifiée sur "
+"la valeur spécifiée.\n"
+"      --raw                             Enregistrer ou lire les données PCM "
+"brutes.\n"
+"      --passthrough                     Données passthrough.\n"
+"      --file-format[=FFORMAT]           Enregistrer ou lire les données PCM "
+"formatées.\n"
+"      --list-file-formats               Répertorier les formats de fichier "
+"disponibles.\n"
+"      --monitor-stream=INDEX            Enregistrer à partir de l'entrée "
+"sink avec l'index INDEX.\n"
+
+#: ../src/utils/pacat.c:808
+#, c-format
+msgid "pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr "pacat %s\n"
+"Compilé avec libpulse %s\n"
+"Lié avec libpulse %s\n"
+
+#: ../src/utils/pacat.c:841 ../src/utils/pactl.c:1642
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Nom du client invalide « %s »"
+
+#: ../src/utils/pacat.c:856
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Nom du flux invalide « %s »"
+
+#: ../src/utils/pacat.c:893
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Plan des canaux invalide « %s »"
+
+#: ../src/utils/pacat.c:922 ../src/utils/pacat.c:936
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Spécification de latence invalide « %s »"
+
+#: ../src/utils/pacat.c:929 ../src/utils/pacat.c:943
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Spécification de temps de traitement invalide « %s »"
+
+#: ../src/utils/pacat.c:955
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Propriété invalide « %s »"
+
+#: ../src/utils/pacat.c:974
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Format de fichier inconnu %s."
+
+#: ../src/utils/pacat.c:989
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Impossible d'analyser l'argument pour --monitor-stream"
+
+#: ../src/utils/pacat.c:1000
+msgid "Invalid sample specification"
+msgstr "Spécification d'échantillon invalide"
+
+#: ../src/utils/pacat.c:1010
+#, c-format
+msgid "open(): %s"
+msgstr "open() : %s"
+
+#: ../src/utils/pacat.c:1015
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2() : %s"
+
+#: ../src/utils/pacat.c:1022
+msgid "Too many arguments."
+msgstr "Trop de paramètres."
+
+#: ../src/utils/pacat.c:1033
+msgid "Failed to generate sample specification for file."
+msgstr ""
+"Échec lors de la génération des informations de l'échantillon du fichier."
+
+#: ../src/utils/pacat.c:1059
+msgid "Failed to open audio file."
+msgstr "Échec lors de l'ouverture du fichier audio."
+
+#: ../src/utils/pacat.c:1065
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Avertissement : les spécifications de l'échantillon spécifié seront écrasées "
+"par celles du fichier."
+
+#: ../src/utils/pacat.c:1068 ../src/utils/pactl.c:1706
+msgid "Failed to determine sample specification from file."
+msgstr ""
+"Échec lors de l'obtention des informations de l'échantillon du fichier."
+
+#: ../src/utils/pacat.c:1077
+msgid "Warning: Failed to determine channel map from file."
+msgstr ""
+"Avertissement : échec lors de l'obtention des informations du plan des "
+"canaux du fichier."
+
+#: ../src/utils/pacat.c:1088
+msgid "Channel map doesn't match sample specification"
+msgstr "Le plan des canaux ne correspond pas à la spécification d'échantillon"
+
+#: ../src/utils/pacat.c:1099
+msgid "Warning: failed to write channel map to file."
+msgstr ""
+"Avertissement : Échec lors de l'écriture du plan des canaux dans le fichier."
+
+#: ../src/utils/pacat.c:1114
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Ouverture d'un flux %s avec une spécification d'échantillon « %s » et un "
+"plan des canaux « %s »."
+
+#: ../src/utils/pacat.c:1115
+msgid "recording"
+msgstr "enregistrement"
+
+#: ../src/utils/pacat.c:1115
+msgid "playback"
+msgstr "lecture"
+
+#: ../src/utils/pacat.c:1139
+msgid "Failed to set media name."
+msgstr "Impossible de définir le nom du support."
+
+#: ../src/utils/pacat.c:1146 ../src/utils/pactl.c:2056
+msgid "pa_mainloop_new() failed."
+msgstr "Échec de pa_mainloop_new()."
+
+#: ../src/utils/pacat.c:1169
+msgid "io_new() failed."
+msgstr "Échec de io_new()."
+
+#: ../src/utils/pacat.c:1176 ../src/utils/pactl.c:2068
+msgid "pa_context_new() failed."
+msgstr "Échec de pa_context_new()."
+
+#: ../src/utils/pacat.c:1184 ../src/utils/pactl.c:2074
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "Échec de pa_context_connect() : %s"
+
+#: ../src/utils/pacat.c:1190
+msgid "pa_context_rttime_new() failed."
+msgstr "Échec de pa_context_rttime_new()."
+
+#: ../src/utils/pacat.c:1197 ../src/utils/pactl.c:2079
+msgid "pa_mainloop_run() failed."
+msgstr "Échec de pa_mainloop_run()."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1564
+msgid "NAME [ARGS ...]"
+msgstr "NAME [ARGS ...]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1565
+msgid "NAME|#N"
+msgstr "NAME|#N"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1563
+#: ../src/utils/pactl.c:1569
+msgid "NAME"
+msgstr "NAME"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "NAME|#N VOLUME"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N VOLUME"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1567
+msgid "NAME|#N 1|0"
+msgstr "NAME|#N 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "NAME|#N KEY=VALUE"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#N KEY=VALUE"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "NAME SINK|#N"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "NAME FILENAME"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "PATHNAME"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "FILENAME SINK|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1566
+msgid "#N SINK|SOURCE"
+msgstr "#N SINK|SOURCE"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1568
+msgid "CARD PROFILE"
+msgstr "CARD PROFILE"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1570
+msgid "NAME|#N PORT"
+msgstr "NAME|#N PORT"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1576
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "CARD-NAME|CARD-#N PORT OFFSET"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "TARGET"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC LEVEL"
+msgstr "NUMERIC LEVEL"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "FRAMES"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Afficher cette aide\n"
+"      --version                         Afficher la version\n"
+"Lorsqu'aucune command n'est donnée, pacmd est lancé en mode interactif.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid "pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr "pacmd %s\n"
+"Compilé avec libpulse %s\n"
+"Lié avec libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Aucun démon PulseAudio en cours d'exécution, ou ne s'exécutant pas dans une "
+"session de type démon."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0) : %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect() : %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Impossible de tuer le démon PulseAudio."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Le démon ne répond pas."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write() : %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll() : %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read() : %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Échec lors de l'obtention des statistiques : %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "En cours d'utilisation : %u blocs contenant au total %s octets.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Alloué pendant l'ensemble de la durée d'exécution : %u blocs contenant au "
+"total %s octets.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Taille du cache de l'échantillon : %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Échec lors de l'obtention des informations du serveur : %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Chaîne du serveur : %s\n"
+"Version du protocole de bibliothèque : %u\n"
+"Version du protocole du serveur : %u\n"
+"Local : %s\n"
+"Index client : %u\n"
+"Tile Size: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Nom d'utilisateur : %s\n"
+"Nom d'hôte : %s\n"
+"Nom du serveur : %s\n"
+"Version du serveur : %s\n"
+"Spécification d'échantillon par défaut : %s\n"
+"Plan de canaux par défaut : %s\n"
+"Destination par défaut : %s\n"
+"Source par défaut : %s\n"
+"Cookie : %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Échec lors de l'obtention des informations sur la destination : %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"      State: %s\n"
+"      Name: %s\n"
+"      Description: %s\n"
+"      Driver: %s\n"
+"      Sample Specification: %s\n"
+"      Channel Map: %s\n"
+"      Owner Module: %u\n"
+"      Mute: %s\n"
+"      Volume: %s\n"
+"              balance %0.2f\n"
+"      Base Volume: %s\n"
+"      Monitor Source: %s\n"
+"      Latency: %0.0f usec, configured %0.0f usec\n"
+"      Flags: %s%s%s%s%s%s%s\n"
+"      Properties:\n"
+"              %s\n"
+msgstr ""
+"Destination #%u\n"
+"      État : %s\n"
+"      Nom : %s\n"
+"      Description : %s\n"
+"      Pilote : %s\n"
+"      Spécification de l'échantillon : %s\n"
+"      Plan des canaux : %s\n"
+"      Module du propriétaire : %u\n"
+"      Sourdine : %s\n"
+"      Volume : %s\n"
+"              balance %0.2f\n"
+"      Volume de base : %s\n"
+"      Source du moniteur : %s\n"
+"      Latence : %0.0f usec, configuré %0.0f usec\n"
+"      Marqueurs : %s%s%s%s%s%s%s\n"
+"      Propriétés :\n"
+"              %s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "        Ports:\n"
+msgstr "       Ports :\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "        Active Port: %s\n"
+msgstr "       Port actif : %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "        Formats:\n"
+msgstr "       Formats :\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Échec lors de l'obtention des informations sur la source : %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"      State: %s\n"
+"      Name: %s\n"
+"      Description: %s\n"
+"      Driver: %s\n"
+"      Sample Specification: %s\n"
+"      Channel Map: %s\n"
+"      Owner Module: %u\n"
+"      Mute: %s\n"
+"      Volume: %s\n"
+"              balance %0.2f\n"
+"      Base Volume: %s\n"
+"      Monitor of Sink: %s\n"
+"      Latency: %0.0f usec, configured %0.0f usec\n"
+"      Flags: %s%s%s%s%s%s\n"
+"      Properties:\n"
+"              %s\n"
+msgstr ""
+"Source #%u\n"
+"      État : %s\n"
+"      Nom : %s\n"
+"      Description : %s\n"
+"      Pilote : %s\n"
+"      Spécification de l'échantillon : %s\n"
+"      Plan des canaux : %s\n"
+"      Module du propriétaire : %u\n"
+"      Sourdine : %s\n"
+"      Volume : %s\n"
+"              balance %0.2f\n"
+"      Volume de base : %s\n"
+"      Moniteur de la destination : %s\n"
+"      Latence : %0.0f usec, configuré %0.0f usec\n"
+"      Marqueurs : %s%s%s%s%s%s\n"
+"      Propriétés :\n"
+"              %s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "n/d"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Échec lors de l'obtention des informations du module : %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"      Name: %s\n"
+"      Argument: %s\n"
+"      Usage counter: %s\n"
+"      Properties:\n"
+"              %s\n"
+msgstr ""
+"Module #%u\n"
+"      Nom : %s\n"
+"      Paramètre : %s\n"
+"      Nombre d'utilisations : %s\n"
+"      Propriétés : \n"
+"              %s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Échec lors de l'obtention des informations du client : %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid "Client #%u\n"
+"      Driver: %s\n"
+"      Owner Module: %s\n"
+"      Properties:\n"
+"              %s\n"
+msgstr "Client #%u\n"
+"      Pilote : %s\n"
+"      Module propriétaire : %s\n"
+"      Propriétés :\n"
+"              %s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Impossible d'obtenir des informations sur la carte : %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid "Card #%u\n"
+"      Name: %s\n"
+"      Driver: %s\n"
+"      Owner Module: %s\n"
+"      Properties:\n"
+"              %s\n"
+msgstr ""
+"Carte #%u\n"
+"      Nom : %s\n"
+"      Pilote : %s\n"
+"      Module propriétaire : %s\n"
+"      Propriétés :\n"
+"              %s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "        Profiles:\n"
+msgstr "       Profils :\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "        Active Profile: %s\n"
+msgstr "       Profil actif : %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid "                        Properties:\n"
+"                              %s\n"
+msgstr "                       Propriétés :\n"
+"                              %s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "                        Part of profile(s): %s"
+msgstr "                       Partie du(des) profil(s) : %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr ""
+"Échec lors de l'obtention des informations de l'entrée de la destination : "
+"%s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"      Driver: %s\n"
+"      Owner Module: %s\n"
+"      Client: %s\n"
+"      Sink: %u\n"
+"      Sample Specification: %s\n"
+"      Channel Map: %s\n"
+"      Format: %s\n"
+"      Corked: %s\n"
+"      Mute: %s\n"
+"      Volume: %s\n"
+"              balance %0.2f\n"
+"      Buffer Latency: %0.0f usec\n"
+"      Sink Latency: %0.0f usec\n"
+"      Resample method: %s\n"
+"      Properties:\n"
+"              %s\n"
+msgstr ""
+"Entrée de la destination #%u\n"
+"      Pilote : %s\n"
+"      Module du propriétaire : %s\n"
+"      Client : %s\n"
+"      Destination : %u\n"
+"      Spécification de l'échantillon : %s\n"
+"      Plan des canaux : %s\n"
+"      Format : %s\n"
+"      Bouchonné : %s\n"
+"      Sourdine : %s\n"
+"      Volume : %s\n"
+"              balance %0.2f\n"
+"      Latence du tampon : %0.0f usec\n"
+"      Latence de la destination : %0.0f usec\n"
+"      Méthode de rééchantillonnage : %s\n"
+"      Propriétés :\n"
+"              %s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr ""
+"Échec lors de l'obtention des informations de la sortie de la source : %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"      Driver: %s\n"
+"      Owner Module: %s\n"
+"      Client: %s\n"
+"      Source: %u\n"
+"      Sample Specification: %s\n"
+"      Channel Map: %s\n"
+"      Format: %s\n"
+"      Corked: %s\n"
+"      Mute: %s\n"
+"      Volume: %s\n"
+"              balance %0.2f\n"
+"      Buffer Latency: %0.0f usec\n"
+"      Source Latency: %0.0f usec\n"
+"      Resample method: %s\n"
+"      Properties:\n"
+"              %s\n"
+msgstr ""
+"Sortie de la source #%u\n"
+"      Pilote : %s\n"
+"      Module du propriétaire : %s\n"
+"      Client : %s\n"
+"      Source : %u\n"
+"      Spécification de l'échantillon : %s\n"
+"      Plan des canaux : %s\n"
+"      Format : %s\n"
+"      Bouchonné : %s\n"
+"      Sourdine : %s\n"
+"      Volume : %s\n"
+"              balance %0.2f\n"
+"      Latence du tampon : %0.0f usec\n"
+"      Latence de la source : %0.0f usec\n"
+"      Méthode de rééchantillonnage : %s\n"
+"      Propriétés :\n"
+"              %s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Échec lors de l'obtention des informations de l'échantillon : %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"      Name: %s\n"
+"      Sample Specification: %s\n"
+"      Channel Map: %s\n"
+"      Volume: %s\n"
+"              balance %0.2f\n"
+"      Duration: %0.1fs\n"
+"      Size: %s\n"
+"      Lazy: %s\n"
+"      Filename: %s\n"
+"      Properties:\n"
+"              %s\n"
+msgstr ""
+"Échantillon #%u\n"
+"      Nom : %s\n"
+"      Spécification de l'échantillon : %s\n"
+"      Plan des canaux : %s\n"
+"      Volume : %s\n"
+"              balance %0.2f\n"
+"      Durée : %0.1fs\n"
+"      Taille : %s\n"
+"      Lazy : %s\n"
+"      Nom du fichier : %s\n"
+"      Propriétés :\n"
+"              %s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Échec : %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Impossible de décharger le module : module %s non chargé"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"impossible de définir le volume : vous avex tenté de définir les volumes de "
+"%d canaux, tandis que channel/s prenait en charge = %d\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Impossible de définir le format : format de la chaîne %s invalide"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Échec lors de l'envoi de l'échantillon : %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Fin prématurée du fichier"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "nouveau"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "changement"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "supprimer"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "inconnu(e)"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "destination"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "source"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "sink-input"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "source-output"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "module"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "client"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "sample-cache"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "serveur"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "carte"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Événement « %s » sur %s #%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT reçu, fermeture."
+
+#: ../src/utils/pactl.c:1479
+msgid "Invalid volume specification"
+msgstr "Spécification de volume invalide"
+
+#: ../src/utils/pactl.c:1502
+msgid "Volume outside permissible range.\n"
+msgstr "Le volume est au-delà de la plage admissible.\n"
+
+#: ../src/utils/pactl.c:1515
+msgid "Invalid number of volume specifications.\n"
+msgstr "Nombre de spécifications du volume invalide.\n"
+
+#: ../src/utils/pactl.c:1527
+msgid "Inconsistent volume specification.\n"
+msgstr "Spécification du volume incohérente.\n"
+
+#: ../src/utils/pactl.c:1557 ../src/utils/pactl.c:1558
+#: ../src/utils/pactl.c:1559 ../src/utils/pactl.c:1560
+#: ../src/utils/pactl.c:1561 ../src/utils/pactl.c:1562
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577
+msgid "[options]"
+msgstr "[options]"
+
+#: ../src/utils/pactl.c:1559
+msgid "[TYPE]"
+msgstr "[TYPE]"
+
+#: ../src/utils/pactl.c:1561
+msgid "FILENAME [NAME]"
+msgstr "FILENAME [NAME]"
+
+#: ../src/utils/pactl.c:1562
+msgid "NAME [SINK]"
+msgstr "NAME [SINK]"
+
+#: ../src/utils/pactl.c:1571
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "NAME|#N VOLUME [VOLUME ...]"
+
+#: ../src/utils/pactl.c:1572
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#N VOLUME [VOLUME ...]"
+
+#: ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0|toggle"
+msgstr "NAME|#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1574
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1575
+msgid "#N FORMATS"
+msgstr "#N FORMATS"
+
+#: ../src/utils/pactl.c:1578
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Les noms spéciaux @DEFAULT_SINK@, @DEFAULT_SOURCE@ et @DEFAULT_MONITOR@\n"
+"peuvent être utilisés pour indiquer la destination, la source, et le "
+"moniteur par défaut.\n"
+
+#: ../src/utils/pactl.c:1581
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Afficher cette aide\n"
+"      --version                         Afficher la version\n"
+"\n"
+"  -s, --server=SERVER                   Le nom du serveur auquel se "
+"connecter\n"
+"  -n, --client-name=NAME                Comment appeler ce client sur le "
+"serveur\n"
+
+#: ../src/utils/pactl.c:1622
+#, c-format
+msgid "pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr "pactl %s\n"
+"Compilé avec libpulse %s\n"
+"Lié avec libpulse %s\n"
+
+#: ../src/utils/pactl.c:1678
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Ne rien indiquer, ou l'un de : %s"
+
+#: ../src/utils/pactl.c:1688
+msgid "Please specify a sample file to load"
+msgstr "Veuillez indiquer un fichier d'échantillon à charger"
+
+#: ../src/utils/pactl.c:1701
+msgid "Failed to open sound file."
+msgstr "Échec lors de l'ouverture du fichier audio."
+
+#: ../src/utils/pactl.c:1713
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+"Avertissement : Échec lors de l'obtention des spécifications de "
+"l'échantillon du fichier."
+
+#: ../src/utils/pactl.c:1723
+msgid "You have to specify a sample name to play"
+msgstr "Vous devez indiquer un nom d'échantillon à lire"
+
+#: ../src/utils/pactl.c:1735
+msgid "You have to specify a sample name to remove"
+msgstr "Vous devez indiquer un nom d'échantillon à supprimer"
+
+#: ../src/utils/pactl.c:1744
+msgid "You have to specify a sink input index and a sink"
+msgstr ""
+"Vous devez indiquer un index d'entrée de destination et une destination"
+
+#: ../src/utils/pactl.c:1754
+msgid "You have to specify a source output index and a source"
+msgstr "Vous devez indiquer un index de sortie de source et une source"
+
+#: ../src/utils/pactl.c:1769
+msgid "You have to specify a module name and arguments."
+msgstr "Vous devez indiquer un nom de module et des paramètres."
+
+#: ../src/utils/pactl.c:1789
+msgid "You have to specify a module index or name"
+msgstr "Vous devez indiquer un index ou nom de module"
+
+#: ../src/utils/pactl.c:1802
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Vous ne pouvez pas indiquer plus d'une destination. Vous devez indiquer une "
+"valeur booléenne."
+
+#: ../src/utils/pactl.c:1807 ../src/utils/pactl.c:1827
+msgid "Invalid suspend specification."
+msgstr "Spécification de suspension invalide."
+
+#: ../src/utils/pactl.c:1822
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Vous ne pouvez pas indiquer plus d'une source. Vous devez indiquer une "
+"valeur booléenne."
+
+#: ../src/utils/pactl.c:1839
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Vous devez indiquer un nom/un index de carte et un nom de profil"
+
+#: ../src/utils/pactl.c:1850
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Vous devez indiquer un nom/un index de destination et un nom de port"
+
+#: ../src/utils/pactl.c:1861
+msgid "You have to specify a sink name"
+msgstr "Vous devez indiquer un nom de destination"
+
+#: ../src/utils/pactl.c:1871
+msgid "You have to specify a source name/index and a port name"
+msgstr "Vous devez indiquer un nom/un index de source et un nom de port"
+
+#: ../src/utils/pactl.c:1882
+msgid "You have to specify a source name"
+msgstr "Vous devez indiquer un nom de source"
+
+#: ../src/utils/pactl.c:1892
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Vous devez indiquer un nom/un index de destination et un volume"
+
+#: ../src/utils/pactl.c:1905
+msgid "You have to specify a source name/index and a volume"
+msgstr "Vous devez indiquer un nom/un index de source et un volume"
+
+#: ../src/utils/pactl.c:1918
+msgid "You have to specify a sink input index and a volume"
+msgstr "Vous devez indiquer un index d'entrée de destination et un volume"
+
+#: ../src/utils/pactl.c:1923
+msgid "Invalid sink input index"
+msgstr "Index invalide d'entrée de la destination"
+
+#: ../src/utils/pactl.c:1934
+msgid "You have to specify a source output index and a volume"
+msgstr "Vous devez indiquer un index de sortie de source et un volume"
+
+#: ../src/utils/pactl.c:1939
+msgid "Invalid source output index"
+msgstr "Index de sortie de source invalide"
+
+#: ../src/utils/pactl.c:1950
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "Vous devez indiquer un nom/un index de destination et un booléen muet"
+
+#: ../src/utils/pactl.c:1955 ../src/utils/pactl.c:1970
+#: ../src/utils/pactl.c:1990 ../src/utils/pactl.c:2008
+msgid "Invalid mute specification"
+msgstr "Spécification de sourdine invalide"
+
+#: ../src/utils/pactl.c:1965
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Vous devez indiquer un nom/un index de source et un booléen muet"
+
+#: ../src/utils/pactl.c:1980
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr ""
+"Vous devez indiquer un index d'entrée de destination et un booléen muet"
+
+#: ../src/utils/pactl.c:1985
+msgid "Invalid sink input index specification"
+msgstr "Spécification d'index d'entrée de la destination invalide"
+
+#: ../src/utils/pactl.c:1998
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "Vous devez indiquer un index de sortie de source et un booléen muet"
+
+#: ../src/utils/pactl.c:2003
+msgid "Invalid source output index specification"
+msgstr "Spécification d'index de sortie de source invalide"
+
+#: ../src/utils/pactl.c:2020
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Vous devez indiquer un index de destination et une liste des formats pris en "
+"charge séparée par des points-virgules"
+
+#: ../src/utils/pactl.c:2032
+msgid ""
+"You have to specify a card name/index, a port name and a latency offset"
+msgstr ""
+"Vous devez indiquer un nom ou index carte, un nom de port et un décalage de "
+"latence"
+
+#: ../src/utils/pactl.c:2039
+msgid "Could not parse latency offset"
+msgstr "Impossible d'analyser le décalage de la latence"
+
+#: ../src/utils/pactl.c:2051
+msgid "No valid command specified."
+msgstr "Aucune commande valide indiquée."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork() : %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp() : %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Échec lors de la reprise : %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Échec lors de la suspension : %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr ""
+"AVERTISSEMENT : le serveur de son n'est pas local, suspension annulée.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Échec lors de la connexion : %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT reçu, fermeture.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "AVERTISSEMENT : le processus fils a été terminé par le signal %u\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Affiche cette aide\n"
+"      --version                         Affiche la version\n"
+"  -s, --server=SERVEUR                  Le nom du serveur auquel se "
+"connecter\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid "pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr "pasuspender %s\n"
+"Compilé avec libpulse %s\n"
+"Lié avec libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "Échec de pa_mainloop_new().\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "Échec de pa_context_new().\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "Échec de pa_mainloop_run().\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D visuel] [-S serveur] [-O destination] [-I source] [-c fichier]  [-d|-"
+"e|-i|-r]\n"
+"\n"
+" -d    Affiche les données PulseAudio actuelles attachées au visuel X11 (par "
+"défaut)\n"
+" -e    Exporte les données PulseAudio locales vers le visuel X11\n"
+" -i    Importe les données PulseAudio depuis le visuel X11 vers les "
+"variables de l'environnement local et le fichier de cookie.\n"
+" -r    Enlève les données PulseAudio du visuel X11\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Échec lors de l'analyse de la ligne de commande.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Serveur : %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Source : %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Destination : %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie : %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Échec lors de l'analyse des données du cookie\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Échec lors de l'enregistrement des données du cookie\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Échec lors de l'obtention du FQDN (« nom de domaine complet »).\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Échec lors du chargement des données du cookie\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Pas encore implémenté.\n"
+
diff --git a/po/gl.po b/po/gl.po
new file mode 100644 (file)
index 0000000..6c33e8a
--- /dev/null
+++ b/po/gl.po
@@ -0,0 +1,3160 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# bassball93 <bassball93@gmail.com>, 2011.
+# mbouzada <mbouzada@gmail.com>, 2011.
+# Fran Dieguez <frandieguez@gnome.org>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PulseAudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-04-14 14:50+0200\n"
+"PO-Revision-Date: 2012-04-14 18:57+0200\n"
+"Last-Translator: Fran Dieguez <frandieguez@gnome.org>\n"
+"Language-Team: Galician <gnome-l10n-gl@gnome.org>\n"
+"Language: gl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() devolveu un valor que é excepcionalmente grande: %lu bytes "
+"(%lu ms).\n"
+"O máis probábel é que sexa un erro do controlador ALSA «%s». Informe disto "
+"aos desenvolvedores de ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() devolveu un valor que é excepcionalmente grande: %li bytes "
+"(%s%lu ms).\n"
+"O máis probábel é que sexa un erro do controlador ALSA «%s». Informe disto "
+"aos desenvolvedores de ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() devolveu valores estraños: o atraso de %lu é menor que "
+"o dispoñíbel %lu.\n"
+"O máis probábel é que sexa un erro do controlador ALSA «%s». Informe disto "
+"aos desenvolvedores de ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_beginl() devolveu un valor que é excepcionalmente grande: %lu "
+"bytes (%lu ms).\n"
+"O máis probábel é que sexa un erro do controlador ALSA «%s». Informe disto "
+"aos desenvolvedores de ALSA."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Manter sempre polo menos un destino cargado aínda que sexa nulo"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Saída parva"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "Destino virtual LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:52
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<nome para o destino> sink_properties=<propiedades para o destino> "
+"master=<nome do destino a filtrar> format=<formato de mostra> rate=<taxa de "
+"mostra> channels=<cantidade de canles> channel_map=<asignación de canles> "
+"plugin=<nome do engadido ladspa> label=<etiqueta do engadido ladspa> "
+"control=<lista separada por vírgula de valores de control de entrada> "
+"input_ladspaport_map=<lista separada por comas dos nomes de portos LADSPA de "
+"entrada> output_ladspaport_map=<lista separada por comas dos nomes dos "
+"portos LADSPA de saída> "
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "Destino nulo sincronizado"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Saída nula"
+
+#: ../src/pulsecore/sink.c:3377
+msgid "Built-in Audio"
+msgstr "Audio interno"
+
+#: ../src/pulsecore/sink.c:3382
+msgid "Modem"
+msgstr "Módem"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Produciuse un fallo ao buscar o cargador llt_dlopen orixinal."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "Produciuse un fallo ao asignar o cargador dl novo."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "Produciuse un fallo ao engadir o bind-now-loader."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "Obtívose o sinal %s."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "Saíndo."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Produciuse un fallo ao buscar o usuario «%s»."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Produciuse un fallo ao buscar o grupo «%s»."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Atopáronse o usuario «%s» (UID %lu) e o grupo «%s» (GID %lu)."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "O GID do usuario «%s» e do grupo «%s» non coinciden."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "O cartafol de inicio do usuario «%s» non é «%s», ignorando."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Produciuse un fallo ao crear «%s»: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Produciuse un fallo ao cambiar a lista do grupo: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Produciuse un fallo ao cambiar o GID: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Produciuse un fallo ao cambiar o UID: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "Liberáronse satisfactoriamente os privilexios de superusuario."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "O modo a todo do sistema non esta permitido nesta plataforma."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "Produciuse un fallo setrlimit(%s, (%u, %u)): %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "Produciuse un fallo ao analizar a liña de ordes."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"O modo de sistema foi rexeitado por un usuario sen privilexios. Soamente "
+"iniciarase o servizo de busca do servidor D-Bus."
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "O daemon non se está executando"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "O daemon está executándose como PID %u"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Non foi posíbel deter o daemon: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Este programa non precisa ser executado como superusuario (a non ser que se "
+"especifique --system)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Precísanse privilexios de superusuario."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start no está permitido para as instancias do sistema."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr "Servidor configurado polo usuario %s sen start/autospawning."
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Servidor configurado polo usuario %s que aparece como local. Probando máis "
+"polo miúdo."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr ""
+"Executándose en modo de sistema, máis non se configurou --disallow-exit!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"Executándose en modo de sistema, máis non se configurou --disallow-module-"
+"loading!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "Executándose en modo de sistema, forzase a desactivación do modo SHM!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"Executándose en modo de sistema, forzase a desactivación de exit idle time!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "Produciuse un fallo ao tentar adquirir stdio."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "Produciuse un fallo na canalización pipe(): %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "Produciuse un fallo na bifurcación fork(): %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "Produciuse un fallo na lectura read(): %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "Produciuse un fallo no inicio do daemon."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "O daemon iniciouse satisfactoriamente."
+
+#: ../src/daemon/main.c:816
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "Produciuse un fallo na sesión setsid(): %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Isto é PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Anfitrión de compilación: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "Compilación CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "Executándose no anfitrión: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "Atopáronse %u CPU."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "O tamaño da páxina é de %lu bytes"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Compilado con compatibilidade  con Valgrind: si"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Compilado con compatibilidade  con Valgrind: non"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Executándose en modo valgrind: %s"
+
+#: ../src/daemon/main.c:921
+#, c-format
+msgid "Running in VM: %s"
+msgstr "Executándose en VM: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Construción optimizada: si"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "Construción optimizada: non"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "Definido NDEBUG, desactivadas todas as comprobacións."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "Definido FASTPATH, só se desactivan as comprobacións rápidas de ruta."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "Activadas todas as comprobacións."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "Produciuse un fallo ao tentar obter o ID da máquina"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "O ID da máquina é %s"
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "O ID da sesión é %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Utilizando o directorio de tempo de execución %s."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "Utilizando o directorio de estado %s."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "Utilizando o directorio de módulos %s."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Executándose en modo de sistema: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://pulseaudio.org/wiki/WhatIsWrongWithSystemMode for an "
+"explanation why system mode is usually a bad idea."
+msgstr ""
+"Conforme, polo que se ve, está executando PA no modo de sistema de PA. Teña "
+"en conta que o máis probable é que non debería de estar facéndoo.\n"
+"Se, aínda así, segue facéndoo, entón será culpa súa se as cousas non "
+"funcionan como se agardaba.\n"
+"Por favor, lea http://pulseaudio.org/wiki/WhatIsWrongWithSystemMode para "
+"obter unha explicación de por qué o modo de sistema adoita ser unha mala "
+"idea."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "Fallou pa_pid_file_create()."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr ""
+"Existen cronómetros de alta resolución novos e dispoñíbeis! Bo proveito!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Meu, o seu núcleo mete medo! A recomendación de hoxe do chef é Linux con "
+"cronómetros de alta resolución activados!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "Produciuse un fallo en pa_core_new()."
+
+#: ../src/daemon/main.c:1089
+msgid "Failed to initialize daemon."
+msgstr "Produciuse un fallo ao tentar iniciar o daemon."
+
+#: ../src/daemon/main.c:1094
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"Iniciouse o daemon sen ningún módulo cargado, polo que se nega a funcionar."
+
+#: ../src/daemon/main.c:1132
+msgid "Daemon startup complete."
+msgstr "O daemon iniciouse completamente."
+
+#: ../src/daemon/main.c:1138
+msgid "Daemon shutdown initiated."
+msgstr "Comeza a apagarse o daemon."
+
+#: ../src/daemon/main.c:1169
+msgid "Daemon terminated."
+msgstr "O daemon rematou."
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [opcións]\n"
+"\n"
+"ORDES:\n"
+"  -h, --help                            Mostra esta axuda\n"
+"      --version                         Mostra a versión\n"
+"      --dump-conf                       Envorca  a configuración "
+"predeterminada\n"
+"      --dump-modules                    Envorca unha lista de módulos "
+"dispoñíbeis\n"
+"      --dump-resample-methods           Envorca os métodos dispoñíbeis de "
+"nova mostraxe\n"
+"      --cleanup-shm                     Limpa os segmentos de memoria "
+"compartidos\n"
+"      --start                           Inicia o «daemon», se aínda non está "
+"executándose\n"
+"  -k  --kill                            Detén un «daemon» que estea "
+"executándose\n"
+"      --check                           Verifica que «daemon» están "
+"executándose\n"
+"\n"
+"OPCIÓNS:\n"
+"      --system[=BOOL]                   Executase como única instancia a "
+"nivel do sistema\n"
+"  -D, --daemonize[=BOOL]                Convertese en daemon despois de "
+"iniciarse\n"
+"      --fail[=BOOL]                     Pechase cando falla o inicio\n"
+"      --high-priority[=BOOL]            Tenta estabelecer un nivel alto "
+"agradábel\n"
+"                                        (só dispoñíbel como root, cando o "
+"SUID ou\n"
+"                                        RLIMIT_NICE son elevados)\n"
+"      --realtime[=BOOL]                 Tenta activar a planificación en "
+"tempo real\n"
+"                                        (só dispoñíbel como root, cando o "
+"SUID ou\n"
+"                                        RLIMIT_NICE son elevados)\n"
+"      --disallow-module-loading[=BOOL]  Non permite a carga/descarga do "
+"módulo polo usuario\n"
+"                                        despois de que se teña iniciado\n"
+"      --disallow-exit[=BOOL]            Non permite a petición do usuario de "
+"abandonar o programa\n"
+"      --exit-idle-time=SECS             Desactiva un daemon cando está "
+"desocupado e\n"
+"                                        transcorreu esa cantidade de tempo\n"
+"      --module-idle-time=SECS           Descarga os módulos que foron "
+"cargados automaticamente cando están\n"
+"                                       desocupados e transcorreu esa "
+"cantidade de tempo\n"
+"      --scache-idle-time=SECS           Descarga mostras cargadas "
+"automaticamente cando están\n"
+"                                       desocupados e transcorreu esa "
+"cantidade de tempo\n"
+"      --log-level[=LEVEL]               Aumenta ou define o grao da saída a "
+"utilizar\n"
+"  -v                                    Aumenta o grao da saída\n"
+"      --log-target={auto,syslog,stderr} Especifica o destino do rexistro\n"
+"      --log-meta[=BOOL]                 Incluír a localización do código nos "
+"mensaxes de rexistro\n"
+"      --log-time[=BOOL]                 Incluír a pegada de tempo nos "
+"mensaxes de rexistro\n"
+"  -p, --dl-search-path=PATH             Estabelece a ruta de busca (search "
+"path) para engadidos\n"
+"                                        (plugins) compartidos\n"
+"      --resample-method=METHOD          Utiliza un método de nova mostraxe "
+"específico\n"
+"                                        (Ver en --dump-resample-methods os "
+"valores posibles)\n"
+"      --use-pid-file[=BOOL]             Crea o ficheiro PID\n"
+"      --no-cpu-limit[=BOOL]             Non instala un limitador de carga de "
+"CPU en\n"
+"                                        plataformas que o admitan.\n"
+"      --disable-shm[=BOOL]              Desactiva a compatibilidade para "
+"memoria compartida.\n"
+"\n"
+"SCRIPT DE INICIO:\n"
+"  -L, --load=\"ARGUMENTOS DO MODULO\"  Carga o módulo engadido cos "
+"parámetros indicados\n"
+"  -F, --file=FILENAME                   Executa o script especificado\n"
+"  -C                                    Abre unha liña de ordes na TTY "
+"actual despois de iniciar\n"
+"\n"
+"  -n                                    Non carga o ficheiro script "
+"predeterminado\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize agarda un argumento booleano"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail agarda un argumento booleano"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level agarda un argumento no nivel do rexistro (xa sexa numérico, "
+"dentro do rango de 0..4; xa sexa un de debug, info, notice, warn, ou error)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority agarda un argumento booleano"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime agarda un argumento booleano"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading agarda un argumento booleano"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit agarda un argumento booleano"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use pid-file agarda un argumento booleano"
+
+#: ../src/daemon/cmdline.c:318
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr ""
+"Rexistro de destino non válido: use «syslog»,  «stderr», «auto» ou un nome "
+"de ficheiro válido «file:<ruta>»."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time agarda un argumento booleano"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta agarda un argumento booleano"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Método de nova mostraxe incorrecto «%s»"
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system agarda un argumento booleano"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit agarda un argumento booleano"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm agarda un argumento booleano"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Nome: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "Non existe información dispoñíbel acerca do módulo\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Versión: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Descrición: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Utilización: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Carga unha vez: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "AVISO DE OBSOLESCENCIA: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Ruta: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Destino do rexistro incorrecto «%s»."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Nivel do rexistro incorrecto «%s»."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Método de nova mostraxe incorrecto «%s»."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Rlimit incorrecto «%s»."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Formato de mostra «%s» incorrecto"
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Taxa de mostraxe incorrecta «%s»."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Canles de mostra incorrectas «%s»."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Mapa de canles incorrecto «%s»."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Cantidade incorrecta de fragmentos «%s»."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Tamaño incorrecto de fragmento «%s»."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Nivel de amabilidade incorrecto «%s»."
+
+#: ../src/daemon/daemon-conf.c:528
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Tipo de servidor incorrecto «%s»."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Non foi posíbel abrir o ficheiro de configuración: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"O mapa de canles predeterminado especificado ten un número de canles "
+"distinto ao especificado como predeterminado."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Lendo desde o ficheiro de configuración: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "Limpeza de privilexios."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Sistema de son PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Iniciar o Sistema de son PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "Normativa de enrutado de KDE do Sistema de son PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "Iniciar o Sistema de son PulseAudio coa normativa de enrutado de KDE"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Fronte central"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Fronte esquerda"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Fronte dereita"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Traseira central"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Traseira esquerda"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Traseira dereita"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr "Subgraves"
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Frontal esquerda central"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Frontal dereita central"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Lateral esquerda"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Lateral dereita"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Auxiliar 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Auxiliar 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Auxiliar 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Auxiliar 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Auxiliar 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Auxiliar 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Auxiliar 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Auxiliar 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Auxiliar 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Auxiliar 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Auxiliar 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Auxiliar 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Auxiliar 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Auxiliar 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Auxiliar 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Auxiliar 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Auxiliar 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Auxiliar 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Auxiliar 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Auxiliar 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Auxiliar 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Auxiliar 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Auxiliar 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Auxiliar 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Auxiliar 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Auxiliar 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Auxiliar 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Auxiliar 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Auxiliar 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Auxiliar 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Auxiliar 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Auxiliar 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Arriba centro"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Central superior frontal"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Frontal superior esquerda"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Frontal superior dereito"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Traseira superior central"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Traseira superior esquerda"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Traseira superior dereita"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(incorrecto)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "Estéreo"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "Envolvente 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "Envolvente 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "Envolvente 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "Envolvente 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "Envolvente 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "Aceptar"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "Acceso denegado"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Orde descoñecida"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Argumento incorrecto"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "Entidade existente"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "Non existe esa entidade"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "Conexión rexeitada"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "Produciuse un erro de protocolo"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "Tempo límite"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "Sen chave de autorización"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "Produciuse un erro interno"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "Conexión rematada"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "Entidade rematada"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Servidor incorrecto"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Fallou a inicialización do módulo"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Estado defectuoso"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Sen datos"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "A versión de protocolo non é compatíbel"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Demasiado longo"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "Non admitido"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Código de erro descoñecido"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "Non existe esa extensión"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Funcionalidade obsoleta"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Non se atopa a implementación"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Cliente bifurcado"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Produciuse un erro de entrada/saída"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Dispositivo ou recurso ocupado"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() fallou"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr "devolveu_verdadeiro_xcb_connection() has error"
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "Produciuse un fallo ao analizar os datos da cookie"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "Produciuse un fallo ao abrir o ficheiro de configuración «%s»: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "Non se cargou ningunha cookie. Tentando conectar de todos os xeitos."
+
+#: ../src/pulse/context.c:679
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:734
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1435
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Recibiuse unha mensaxe para unha extensión descoñecida «%s»"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Produciuse un fallo ao drenar o fluxo: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "O fluxo de reprodución foi drenado."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "Drenando a conexión co servidor."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "Produciuse un fallo en pa_stream_write(): %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "Produciuse un fallo en pa_stream_write(): %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "Produciuse un fallo en pa_stream_peek(): %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "Creouse satisfactoriamente o fluxo (stream)."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "Produciuse un fallo en pa_stream_get_buffer_attr(): %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Métrica do búfer: maxlenght=%u, tlenghth=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr " Métrica do búfer: maxlenght=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Utilizando especificacións de mostra «%s», mapa de canles «%s»."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "Conectado a dispositivo %s (%u, %ssuspended)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "Produciuse un erro de fluxo: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Dispositivo de fluxo suspendido.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Dispositivo de fluxo restabelecido.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Fluxo esgotado.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Fluxo saturado.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "Fluxo iniciado.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Fluxo trasladado ao dispositivo %s (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "sen"
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Cambiaron os atributos do búfer do fluxo.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "Conexión estabelecida.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "Produciuse un fallo en pa_stream_new(): %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "Produciuse un fallo en pa_stream_connect_playback(): %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "Produciuse un fallo en pa_stream_connect_record(): %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1273
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Produciuse un fallo na conexión: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "Obtívose EOF."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "Produciuse un fallo en write(): %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "Obtívose sinal, saíndo."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Non se puido obter a latencia: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Tempo: %0.3f seg; latencia: %0.0f useg."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "Produciuse un fallo en pa_stream_update_timing_info(): %s"
+
+#: ../src/utils/pacat.c:653
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [opcións]\n"
+"\n"
+"  -h, --help                            Mostra esta axuda\n"
+"      --version                         Mostra a versión\n"
+"\n"
+"  -r, --record                          Crea unha conexión para gravar\n"
+"  -p, --playback                        Crea unha conexión para reproducir\n"
+"\n"
+"  -v, --verbose                         Activa operacións detalladas\n"
+"\n"
+"  -s, --server=SERVIDOR                 O nome do servidor co que "
+"conectarse\n"
+"  -d, --device=DISPOSITIVO              O nome da saída/orixe á que "
+"conectarse\n"
+"  -n, --client-name=NOME                Como chamar a este cliente no "
+"servidor\n"
+"      --stream-name=NOME                Como chamar a este fluxo no "
+"servidor\n"
+"      --volume=VOLUME                   Especifica o volume inicial (linear) "
+"dentro do rango 0...65536\n"
+"      --rate=TAXAMOSTRA                 Taxa da mostra en Hz (estabelecida "
+"en 44100 por omisión)\n"
+"      --format=FORMATOMOSTRA            O tipo de mostra, algún entre s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be "
+"(estabelecido en s16ne por omisión)\n"
+"      --channels=CANLES                 A cantidade de canles, 1 para mono, "
+"2 para estéreo\n"
+"                                        (establecido en 2 por omisión)\n"
+"      --channel-map=MAPACANLES          Mapa de canles a ser usado no canto "
+"do predeterminado\n"
+"      --fix-format                      Obter o formato da mostra desde a "
+"saída á que está\n"
+"                                        conectado o fluxo.\n"
+"      --fix-rate                        Obtén a taxa da mostra desde o "
+"destino ao que está\n"
+"                                        conectado o fluxo.\n"
+"      --fix-channels                    Obter o mapa e a cantidade de canles "
+"desde a saída\n"
+"                                        á que está conectado o fluxo.\n"
+"      --no-remix                        Non realiza un upmix ou un downmix "
+"das canles.\n"
+"      --no-remap                        Mapea canles por índices no canto de "
+"por nomes.\n"
+"      --latency=BYTES                   Solicita a latencia especificada en "
+"bytes.\n"
+"      --process-time=BYTES              Solicita o tempo dos procesos "
+"especificados en bytes.\n"
+"      --latency-msec=MSEC               Solicita a latencia especificada en "
+"msec.\n"
+"      --process-time-msec=MSEC          Solicita o tempo de proceso "
+"especificado por solicitude en msec.\n"
+"      --raw                             Grava/reproduce datos PCM con "
+"formato raw.\n"
+"      --passthrough                     pasar datos directamente\n"
+"      --file-format=FFORMATO            Grava/reproduce datos PCM "
+"formatados.\n"
+"      --list-file-formats               Mostra unha lista cos formatos de "
+"ficheiro dispoñíbeis.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Compilado con libpulse %s\n"
+"Vinculado con libpulse %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1421
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Nome do cliente «%s» incorrecto"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Nome do fluxo «%s» incorrecto"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Mapa de canles «%s» incorrecto"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Especificación da latencia «%s» incorrecta"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Especificación de tempo de proceso «%s» incorrecta"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Propiedade «%s» incorrecta"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Formato de ficheiro descoñecido %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "Especificación de mostra incorrecta"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "Demasiados argumentos."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "Fallou ao xerar a especificación de exemplo para o ficheiro."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "Produciuse un fallo ao abrir o ficheiro de son."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Aviso: o exemplo de especificación indicado vai ser sobrescrito coas "
+"especificacións do ficheiro."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1488
+msgid "Failed to determine sample specification from file."
+msgstr ""
+"Produciuse un fallo ao determinar a especificación de exemplo do ficheiro."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr ""
+"Aviso: produciuse un fallo ao determinar o mapa de canles desde o ficheiro."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "O mapa de canles non coincide coa especificación da mostra"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "Aviso: produciuse un fallo ao escribir o mapa de canles no ficheiro."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Abrindo un fluxo %s coa especificación da mostra «%s» e o mapa de canles "
+"«%s»."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "gravando"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "reproducir"
+
+#: ../src/utils/pacat.c:1110
+msgid "Failed to set media name."
+msgstr "Produciuse un fallo ao estabelecer o nome do multimedia."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1798
+msgid "pa_mainloop_new() failed."
+msgstr "Produciuse un fallo en pa_mainloop_new()."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "Produciuse un fallo en io_new()."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1810
+msgid "pa_context_new() failed."
+msgstr "Produciuse un fallo en pa_context_new()."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1816
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "Produciuse un fallo en pa_context_connect(): %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "Produciuse un fallo en pa_context_rttime_new()."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1821
+msgid "pa_mainloop_run() failed."
+msgstr "Produciuse un fallo en pa_mainloop_run()."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Produciuse un erro ao suspender: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Produciuse un erro ao continuar: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "AVISO: o servidor de son non é local, non se suspende.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Produciuse un erro na conexión: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Obtívose SIGINT, saíndo.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "AVISO: o proceso fillo foi rematado polo sinal %u\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [opcións] ... \n"
+"\n"
+"  -h, --help                            Mostra esta axuda\n"
+"      --version                         Mostra a versión\n"
+"  -s, --server=SERVIDOR                 O nome do servidor co que "
+"conectarse\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Compilado con libpulse %s\n"
+"Vinculado con libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "Produciuse un fallo en pa_mainloop_new().\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "Produciuse un fallo en pa_context_new() .\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "Produciuse un fallo en pa_mainloop_run().\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Produciuse un fallo ao tentar obter as estatísticas: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Actualmente en uso: %u bloques contendo %s bytes en total.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Asignados ao longo do tempo: %u bloques contendo %s bytes en total.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Tamaño da caché de mostra: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Produciuse un fallo ao tentar obter información do servidor: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Cadea do servidor: %s\n"
+"Versión do protocolo da biblioteca: %u\n"
+"Versión do protocolo do servidor: %u\n"
+"É local: %s\n"
+"Índice do cliente: %u\n"
+"Tamaño do recadro: %zu\n"
+
+#: ../src/utils/pactl.c:192
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Nome de usuario: %s\n"
+"Nome do equipo: %s\n"
+"Nome do servidor: %s\n"
+"Versión do servidor: %s\n"
+"Especificación de mostra predeterminada: %s\n"
+"Mapa de canles predeterminado: %s\n"
+"Destino predeterminado: %s\n"
+"Orixe predeterminada: %s\n"
+"Cookie: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:851
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Produciuse un fallo ao tentar obter información do destino: %s"
+
+#: ../src/utils/pactl.c:270
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Destino #%u\n"
+"\tEstado: %s\n"
+"\tNome: %s\n"
+"\tDescrición: %s\n"
+"\tControlador: %s\n"
+"\tEspecificación da mostra: %s\n"
+"\tMapa de canles: %s\n"
+"\tPropietario do módulo: %u\n"
+"\tSilenciado: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tVolume base: %s%s%s\n"
+"\tMonitorizar a orixe: %s\n"
+"\tLatencia: %0.0f usec, configurado %0.0f useg\n"
+"\tMarcas: %s%s%s%s%s%s%s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430 ../src/utils/pactl.c:588
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPortos:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tPorto activo: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tFormatos:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:870
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Produciuse un fallo ao tentar obter información da orixe: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Orixe #%u\n"
+"\tEstado: %s\n"
+"\tNome: %s\n"
+"\tDescrición: %s\n"
+"\tControlador: %s\n"
+"\tEspecificación da mostra: %s\n"
+"\tMapa de canles: %s\n"
+"\tPropietario do módulo: %u\n"
+"\tSilenciado: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tVolume base: %s%s%s\n"
+"\tMonitorización do destino: %s\n"
+"\tLatencia: %0.0f usec, configurado %0.0f useg\n"
+"\tMarcas: %s%s%s%s%s%s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:661 ../src/utils/pactl.c:662
+#: ../src/utils/pactl.c:673 ../src/utils/pactl.c:732 ../src/utils/pactl.c:733
+#: ../src/utils/pactl.c:744 ../src/utils/pactl.c:796 ../src/utils/pactl.c:797
+#: ../src/utils/pactl.c:804
+msgid "n/a"
+msgstr "n/d"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Produciuse un fallo ao tentar obter información do módulo: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Módulo #%u\n"
+"\tNome: %s\n"
+"\tArgumento: %s\n"
+"\tContador de uso: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Produciuse un fallo ao tentar obter información do cliente: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Cliente #%u\n"
+"\tControlador: %s\n"
+"\tPropietario do módulo: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Produciuse un fallo ao obter a información da tarxeta: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Tarxeta #%u\n"
+"\tNome: %s\n"
+"\tControlador: %s\n"
+"\tpropietario do módulo: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tPerfís:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tPerfil activo: %s\n"
+
+#: ../src/utils/pactl.c:591
+#, c-format
+msgid "\t\t%s: %s (priority: %u%s)\n"
+msgstr "\t\t%s: %s (prioridade: %u%s)\n"
+
+#: ../src/utils/pactl.c:595
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tParte do(s) perfil(es): %s"
+
+#: ../src/utils/pactl.c:614 ../src/utils/pactl.c:889
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr ""
+"Produciuse un fallo ao tentar obter información da entrada do destino: %s"
+
+#: ../src/utils/pactl.c:643
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Entrada do sumideiro #%u\n"
+"\tControlador: %s\n"
+"\tMódulo propietario: %s\n"
+"\tCliente: %s\n"
+"\tSumideiro: %u\n"
+"\tEspecificación da mostra: %s\n"
+"\tMapa de canles: %s\n"
+"\tFormato: %s\n"
+"\tSilenciado: %s\n"
+"\tVolume: %s\n"
+"\t         %s\n"
+"\t         balance %0.2f\n"
+"\tLatencia do búfer: %0.0f useg\n"
+"\tLatencia do sumideiro: %0.0f useg\n"
+"\tMétodo de nova mostraxe: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:684 ../src/utils/pactl.c:908
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Fallou ao obter información de saída da orixe: %s"
+
+#: ../src/utils/pactl.c:714
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Entrada do destino #%u\n"
+"\tControlador: %s\n"
+"\tPropietario do módulo: %s\n"
+"\tCliente: %s\n"
+"\tDestino: %u\n"
+"\tEspecificación da mostra: %s\n"
+"\tMapa de canles: %s\n"
+"\tFormato: %s\n"
+"\tSilenciado: %s\n"
+"\tVolume: %s\n"
+"\t         %s\n"
+"\t         balance %0.2f\n"
+"\tLatencia do búfer: %0.0f useg\n"
+"\tLatencia do destino: %0.0f useg\n"
+"\tMétodo de nova mostraxe: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:755
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Fallou ao obter información da mostra: %s"
+
+#: ../src/utils/pactl.c:782
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Mostra #%u\n"
+"\tNome: %s\n"
+"\tEspecificación da mostra: %s\n"
+"\tMapa de canles: %s\n"
+"\tVolume: %s\n"
+"\t         %s\n"
+"\t         balance %0.2f\n"
+"\tDuración: %0.1fs\n"
+"\tTamaño: %s\n"
+"\tPreguiza: %s\n"
+"\tNome do ficheiro: %s\n"
+"\tPropiedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:812 ../src/utils/pactl.c:822
+#, c-format
+msgid "Failure: %s"
+msgstr "Produciuse un fallo en: %s"
+
+#: ../src/utils/pactl.c:936
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr ""
+"Produciuse un fallo ao estabelecer o formato: cadea de formato non válida %s"
+
+#: ../src/utils/pactl.c:975
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Produciuse un fallo ao enviar a mostra: %s"
+
+#: ../src/utils/pactl.c:992
+msgid "Premature end of file"
+msgstr "Fin prematuro de ficheiro"
+
+#: ../src/utils/pactl.c:1012
+msgid "new"
+msgstr "novo"
+
+#: ../src/utils/pactl.c:1015
+msgid "change"
+msgstr "cambiar"
+
+#: ../src/utils/pactl.c:1018
+msgid "remove"
+msgstr "retirar"
+
+#: ../src/utils/pactl.c:1021 ../src/utils/pactl.c:1056
+msgid "unknown"
+msgstr "descoñecido"
+
+#: ../src/utils/pactl.c:1029
+msgid "sink"
+msgstr "destino"
+
+#: ../src/utils/pactl.c:1032
+msgid "source"
+msgstr "orixe"
+
+#: ../src/utils/pactl.c:1035
+msgid "sink-input"
+msgstr "entrada-destino"
+
+#: ../src/utils/pactl.c:1038
+msgid "source-output"
+msgstr "saída-orixe"
+
+#: ../src/utils/pactl.c:1041
+msgid "module"
+msgstr "módulo"
+
+#: ../src/utils/pactl.c:1044
+msgid "client"
+msgstr "cliente"
+
+#: ../src/utils/pactl.c:1047
+msgid "sample-cache"
+msgstr "caché-mostraxe"
+
+#: ../src/utils/pactl.c:1050 ../src/utils/pactl.c:1053
+msgid "server"
+msgstr "servidor"
+
+#: ../src/utils/pactl.c:1062
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Evento «%s» en %s #%u\n"
+
+#: ../src/utils/pactl.c:1279
+msgid "Got SIGINT, exiting."
+msgstr "Obtívose SIGINT, saíndo."
+
+#: ../src/utils/pactl.c:1306
+msgid "Invalid volume specification"
+msgstr "Especificación incorrecta de volume"
+
+#: ../src/utils/pactl.c:1329
+msgid "Volume outside permissible range.\n"
+msgstr "Volume fóra do rango permitido.\n"
+
+#: ../src/utils/pactl.c:1340 ../src/utils/pactl.c:1341
+#: ../src/utils/pactl.c:1342 ../src/utils/pactl.c:1343
+#: ../src/utils/pactl.c:1344 ../src/utils/pactl.c:1345
+#: ../src/utils/pactl.c:1346 ../src/utils/pactl.c:1347
+#: ../src/utils/pactl.c:1348 ../src/utils/pactl.c:1349
+#: ../src/utils/pactl.c:1350 ../src/utils/pactl.c:1351
+#: ../src/utils/pactl.c:1352 ../src/utils/pactl.c:1353
+#: ../src/utils/pactl.c:1354 ../src/utils/pactl.c:1355
+#: ../src/utils/pactl.c:1356 ../src/utils/pactl.c:1357
+#: ../src/utils/pactl.c:1358
+msgid "[options]"
+msgstr "[opcións]"
+
+#: ../src/utils/pactl.c:1342
+msgid "[TYPE]"
+msgstr "[TIPO]"
+
+#: ../src/utils/pactl.c:1344
+msgid "FILENAME [NAME]"
+msgstr "NOME_DE_FICHEIRO [NOME]"
+
+#: ../src/utils/pactl.c:1345
+msgid "NAME [SINK]"
+msgstr "NOME [SUMIDEIRO]"
+
+#: ../src/utils/pactl.c:1346
+msgid "NAME"
+msgstr "NOME"
+
+#: ../src/utils/pactl.c:1347
+msgid "NAME [ARGS ...]"
+msgstr "NOME [ARGS ...]"
+
+#: ../src/utils/pactl.c:1348
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pactl.c:1349
+msgid "#N SINK|SOURCE"
+msgstr "#N SUMIDEIRO|ORIXE"
+
+#: ../src/utils/pactl.c:1350 ../src/utils/pactl.c:1355
+msgid "NAME|#N 1|0"
+msgstr "NOME|#N 1|0"
+
+#: ../src/utils/pactl.c:1351
+msgid "CARD PROFILE"
+msgstr "PERFIL DA TARXETA"
+
+#: ../src/utils/pactl.c:1352
+msgid "NAME|#N PORT"
+msgstr "NOME|#N PORTO"
+
+#: ../src/utils/pactl.c:1353
+msgid "NAME|#N VOLUME"
+msgstr "NOME|#N VOLUME"
+
+#: ../src/utils/pactl.c:1354
+msgid "#N VOLUME"
+msgstr "#N VOLUME"
+
+#: ../src/utils/pactl.c:1356
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pactl.c:1357
+msgid "#N FORMATS"
+msgstr "#N FORMATOS"
+
+#: ../src/utils/pactl.c:1360
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Mostra esta axuda\n"
+"      --version                         Mostra a versión\n"
+"  -s, --server=SERVIDOR                 O nome do servidor co que "
+"conectarse\n"
+"\n"
+
+#: ../src/utils/pactl.c:1401
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Compilado con libpulse %s\n"
+"Vinculado con libpulse %s\n"
+
+#: ../src/utils/pactl.c:1460
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Non especifique nada, ou un de: %s"
+
+#: ../src/utils/pactl.c:1470
+msgid "Please specify a sample file to load"
+msgstr "Por favor, especifique un ficheiro de mostra para cargar"
+
+#: ../src/utils/pactl.c:1483
+msgid "Failed to open sound file."
+msgstr "Produciuse un fallo ao tentar abrir o ficheiro de son."
+
+#: ../src/utils/pactl.c:1495
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+"Aviso: produciuse un fallo ao tentar determinar a especificación da mostra "
+"desde o ficheiro."
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sample name to play"
+msgstr "Debe especificar un nome de mostra para reproducir"
+
+#: ../src/utils/pactl.c:1517
+msgid "You have to specify a sample name to remove"
+msgstr "Debe especificar un nome de mostra para eliminar"
+
+#: ../src/utils/pactl.c:1526
+msgid "You have to specify a sink input index and a sink"
+msgstr "Debe especificar un índice para a entrada ao destino e un destino"
+
+#: ../src/utils/pactl.c:1536
+msgid "You have to specify a source output index and a source"
+msgstr "Debe especificar un índice para a saída da orixe e unha orixe"
+
+#: ../src/utils/pactl.c:1551
+msgid "You have to specify a module name and arguments."
+msgstr "Debe especificar un nome de módulo e os argumentos."
+
+#: ../src/utils/pactl.c:1571
+msgid "You have to specify a module index"
+msgstr "Debe especificar un índice de módulo"
+
+#: ../src/utils/pactl.c:1581
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Non se pode especificar máis dun destino. Ten que especificar un valor "
+"booleano."
+
+#: ../src/utils/pactl.c:1594
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Non se pode especificar máis dunha orixe. Ten que especificar un valor "
+"booleano."
+
+#: ../src/utils/pactl.c:1606
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Debe especificar un nome/índice de tarxeta e un nome de perfil"
+
+#: ../src/utils/pactl.c:1617
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Debe especificar un nome/índice de destino e un nome de porto"
+
+#: ../src/utils/pactl.c:1628
+msgid "You have to specify a source name/index and a port name"
+msgstr "Debe especificar un nome/índice de orixe e un nome de porto"
+
+#: ../src/utils/pactl.c:1639
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Debe especificar un nome/índice de destino e un volume"
+
+#: ../src/utils/pactl.c:1652
+msgid "You have to specify a source name/index and a volume"
+msgstr "Debe especificar un nome/índice de orixe e un volume"
+
+#: ../src/utils/pactl.c:1665
+msgid "You have to specify a sink input index and a volume"
+msgstr "Debe especificar un índice de destino e un volume"
+
+#: ../src/utils/pactl.c:1670
+msgid "Invalid sink input index"
+msgstr "Índice de entrada a destino incorrecto"
+
+#: ../src/utils/pactl.c:1681
+msgid "You have to specify a source output index and a volume"
+msgstr "Debe especificar un índice de saída de orixe e un volume"
+
+#: ../src/utils/pactl.c:1686
+msgid "Invalid source output index"
+msgstr "Índice de saída de orixe non válido"
+
+#: ../src/utils/pactl.c:1698
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr ""
+"Debe especificar un nome/índice de destino e un booleano para silenciado"
+
+#: ../src/utils/pactl.c:1703 ../src/utils/pactl.c:1720
+#: ../src/utils/pactl.c:1742 ../src/utils/pactl.c:1763
+msgid "Invalid mute specification"
+msgstr "Especificación de silenciado non válida"
+
+#: ../src/utils/pactl.c:1715
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Debe especificar un nome/índice de orixe e un booleano para silenciado"
+
+#: ../src/utils/pactl.c:1732
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr ""
+"Debe especificar un índice de entrada a destino e un booleano para silenciado"
+
+#: ../src/utils/pactl.c:1737
+msgid "Invalid sink input index specification"
+msgstr "Especificación incorrecta de índice de entrada a destino"
+
+#: ../src/utils/pactl.c:1753
+msgid "You have to specify a source output index and a mute boolean"
+msgstr ""
+"Debe especificar un índice de saída de orixe e un booleano de silenciado"
+
+#: ../src/utils/pactl.c:1758
+msgid "Invalid source output index specification"
+msgstr "Especificación de índice de saída de orixe non válida"
+
+#: ../src/utils/pactl.c:1777
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Debe especificar un índice de sumideiro e unha lista separada por puntos e "
+"comas dos formatos admitidos"
+
+#: ../src/utils/pactl.c:1793
+msgid "No valid command specified."
+msgstr "Non se especificou ningunha orde correcta"
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D pantalla] [-S servidor] [-O destino] [-I orixe] [-c ficheiro]  [-d|-"
+"e|-i|-r]\n"
+"\n"
+" -d    Mostra os datos actuais de PulseAudio asociados a unha pantalla X11 "
+"(por omisión)\n"
+" -e    Exporta os datos locais de PulseAudio a unha pantalla X11\n"
+" -i    Importa os datos de PulseAudio dunha pantalla X11 cara ás variables "
+"de contorno local e o ficheiro de cookies.\n"
+" -r    Elimina todos os datos de PulseAudio dunha pantalla X11\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Fallou ao interpretar unha liña de ordes.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "Servidor: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "Orixe: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Destino: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Fallou ao interpretar os datos da cookie\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Produciuse un fallo ao tentar gardar os datos da cookie\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr ""
+"Produciuse un fallo ao tentar cargar o ficheiro de configuración do "
+"cliente.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr ""
+"Produciuse un fallo ao tentar ler os datos de configuración do contorno.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Produciuse un fallo ao obter FQDN.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Fallou ao cargar os datos da cookie\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Aínda non está implementado.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"O daemon PulseAudio no está executándose, ou non se está executando como un "
+"daemon de sesión."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Produciuse un fallo ao tentar deter o daemon de PulseAudio."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "O daemon non responde."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "Non se pode acceder ao bloqueo de autoxeración."
+
+#: ../src/modules/alsa/alsa-sink.c:558 ../src/modules/alsa/alsa-sink.c:724
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA espertounos para escribir novos datos no dispositivo, mais, certamente, "
+"non hai nada que escribir!\n"
+"Probablemente sexa un erro no controlador ALSA «%s». Por favor, informe "
+"disto aos desenvolvedores de ALSA.\n"
+"Espertounos con POLLOUT marcado -- aínda así, unha chamada a snd_pcm_avail() "
+"devolveu 0 ou outro valor < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:517 ../src/modules/alsa/alsa-source.c:670
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA espertounos para ler novos datos desde o dispositivo, mais, certamente, "
+"non hai nada que ler!\n"
+"Probablemente sexa un erro no controlador ALSA «%s». Por favor, informe "
+"disto aos desenvolvedores de ALSA.\n"
+"Espertounos con POLLIN marcado -- aínda así, unha chamada a snd_pcm_avail() "
+"devolveu 0 ou outro valor < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:173
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2793
+#: ../src/modules/alsa/alsa-mixer.c:4021
+msgid "Off"
+msgstr "Apagado"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2735
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Reprodución de alta fidelidade (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2749
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Captura de alta fidelidade (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2764
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telefonía dúplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2778
+msgid "Handsfree Gateway"
+msgstr "Pasarela do mans libres"
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "Servidor de son PulseAudio"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "Dispositivos de saída"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "Dispositivos de entrada"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "Son en @HOSTNAME@"
+
+#: ../src/modules/alsa/alsa-mixer.c:2280
+msgid "Input"
+msgstr "Entrada"
+
+#: ../src/modules/alsa/alsa-mixer.c:2281
+msgid "Docking Station Input"
+msgstr "Entrada de estación acoplada (Docking Station)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2282
+msgid "Docking Station Microphone"
+msgstr "Micrófono da estación acoplada (Docking Station)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2283
+msgid "Docking Station Line In"
+msgstr "Entrada de estación acoplada (Docking Station)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2284 ../src/modules/alsa/alsa-mixer.c:2368
+msgid "Line In"
+msgstr "Liña de entrada"
+
+#: ../src/modules/alsa/alsa-mixer.c:2285 ../src/modules/alsa/alsa-mixer.c:2363
+msgid "Microphone"
+msgstr "Micrófono"
+
+#: ../src/modules/alsa/alsa-mixer.c:2286 ../src/modules/alsa/alsa-mixer.c:2364
+msgid "Front Microphone"
+msgstr "Micrófono frontal"
+
+#: ../src/modules/alsa/alsa-mixer.c:2287 ../src/modules/alsa/alsa-mixer.c:2365
+msgid "Rear Microphone"
+msgstr "Micrófono traseiro"
+
+#: ../src/modules/alsa/alsa-mixer.c:2288
+msgid "External Microphone"
+msgstr "Micrófono externo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2289 ../src/modules/alsa/alsa-mixer.c:2367
+msgid "Internal Microphone"
+msgstr "Micrófono interno"
+
+#: ../src/modules/alsa/alsa-mixer.c:2290 ../src/modules/alsa/alsa-mixer.c:2369
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2291 ../src/modules/alsa/alsa-mixer.c:2370
+msgid "Video"
+msgstr "Vídeo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2292
+msgid "Automatic Gain Control"
+msgstr "Control automático de ganancia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2293
+msgid "No Automatic Gain Control"
+msgstr "Sen control automático de ganancia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2294
+msgid "Boost"
+msgstr "Enfatizador"
+
+#: ../src/modules/alsa/alsa-mixer.c:2295
+msgid "No Boost"
+msgstr "Sen enfatizador"
+
+#: ../src/modules/alsa/alsa-mixer.c:2296
+msgid "Amplifier"
+msgstr "Amplificador"
+
+#: ../src/modules/alsa/alsa-mixer.c:2297
+msgid "No Amplifier"
+msgstr "Sen amplificador"
+
+#: ../src/modules/alsa/alsa-mixer.c:2298
+msgid "Bass Boost"
+msgstr "Enfatizador baixo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2299
+msgid "No Bass Boost"
+msgstr "Sen enfatizador baixo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2300
+msgid "Speaker"
+msgstr "Altofalante"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301 ../src/modules/alsa/alsa-mixer.c:2372
+msgid "Headphones"
+msgstr "Auriculares"
+
+#: ../src/modules/alsa/alsa-mixer.c:2362
+msgid "Analog Input"
+msgstr "Entrada analóxica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2366
+msgid "Dock Microphone"
+msgstr "Micrófono do acople"
+
+#: ../src/modules/alsa/alsa-mixer.c:2371
+msgid "Analog Output"
+msgstr "Saída analóxica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2373
+msgid "LFE on Separate Mono Output"
+msgstr "LFE en Saída Mono analóxica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2374
+msgid "Line Out"
+msgstr "Liña de saída"
+
+#: ../src/modules/alsa/alsa-mixer.c:2375
+msgid "Analog Mono Output"
+msgstr "Saída monoaural analóxica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2376
+msgid "Speakers"
+msgstr "Altofalantes"
+
+#: ../src/modules/alsa/alsa-mixer.c:2377
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Digital Output (S/PDIF)"
+msgstr "Saída dixital (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "«Passthrough» dixital (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3878
+msgid "Analog Mono"
+msgstr "Monoaural analóxico"
+
+#: ../src/modules/alsa/alsa-mixer.c:3879
+msgid "Analog Stereo"
+msgstr "Estéreo analóxico"
+
+#: ../src/modules/alsa/alsa-mixer.c:3880
+msgid "Analog Surround 2.1"
+msgstr "Envolvente analóxico 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3881
+msgid "Analog Surround 3.0"
+msgstr "Envolvente analóxico 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3882
+msgid "Analog Surround 3.1"
+msgstr "Envolvente analóxico 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3883
+msgid "Analog Surround 4.0"
+msgstr "Envolvente analóxico 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3884
+msgid "Analog Surround 4.1"
+msgstr "Envolvente analóxico 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3885
+msgid "Analog Surround 5.0"
+msgstr "Envolvente analóxico 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3886
+msgid "Analog Surround 5.1"
+msgstr "Envolvente analóxico 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3887
+msgid "Analog Surround 6.0"
+msgstr "Envolvente analóxico 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3888
+msgid "Analog Surround 6.1"
+msgstr "Envolvente analóxico 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3889
+msgid "Analog Surround 7.0"
+msgstr "Envolvente analóxico 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3890
+msgid "Analog Surround 7.1"
+msgstr "Envolvente analóxico 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3891
+msgid "Digital Stereo (IEC958)"
+msgstr "Estéreo dixital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3892
+msgid "Digital Passthrough  (IEC958)"
+msgstr "«Passthrough» dixital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3893
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Envolvente dixital 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3894
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Envolvente dixital 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Envolvente dixital 5.1 (IEC958/ACDTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Digital Stereo (HDMI)"
+msgstr "Estéreo dixital (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Envolvente dixital 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Analog Mono Duplex"
+msgstr "Monoaural analóxico dúplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4019
+msgid "Analog Stereo Duplex"
+msgstr "Estéreo analóxico dúplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4020
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Estéreo dixital dúplex (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4120
+#, c-format
+msgid "%s Output"
+msgstr "Saída %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:4128
+#, c-format
+msgid "%s Input"
+msgstr "Entrada %s"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"source_name=<nome da orixe> source_properties=<propiedades da orixe> "
+"source_master=<nome da orixe a filtrar> sink_name=<nome do sumideiro> "
+"sink_properties=<propiedades para o sumideiro> sink_master=<nome do "
+"sumideiro a filtrar> adjust_time=<cada canto reaxustar as taxas en s> "
+"adjust_threshold=<canta derixa reaxustar despis en ms> format=<formato da "
+"mostra> rate=<taxa de mostraxe> channels=<número das canles> "
+"channel_map=<mapa de canles> aec_method=<implementación a usar> "
+"aec_args=<parámetros do motor AEC> save_aec=<gardar os datos AEC en /tmp> "
+"autoloaded=<estabeleza se este módulo se carga automaticamente> "
+"use_volume_sharing=<si ou non> "
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr "Ecualizador de propósito xeral"
+
+#: ../src/modules/module-equalizer-sink.c:76
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<nome para o destino> sink_properties=<propiedades para o destino> "
+"sink_master=<sumideiro ao que conectarse> format=<formato de mostra> "
+"rate=<taxa de mostra> channels=<cantidade de canles> channel_map=<asignación "
+"de canles> autoloaded=<estabelezao se este módulo se carga automaticamente> "
+"use_volume_sharing=<si ou non> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<descarga automaticamente os filtros non usados?>"
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+"%s [opcións]\n"
+"\n"
+"-h, --help                            Mostra esta axuda\n"
+"-v, --verbose                         Imprimir mensaxes de depuración\n"
+"      --from-rate=SAMPLERATE          Taxa de mostraxe da orixe en Hz (por "
+"omisión 44100)\n"
+"      --from-format=SAMPLEFORMAT      Tipo de mostraxe da orixe (defaults to "
+"s16le)\n"
+"      --from-channels=CHANNELS        Canles da orixe (defaults to 1)\n"
+"      --to-rate=SAMPLERATE            Taxa de mostraxe do destino en Hz "
+"(defaults to 44100)\n"
+"      --to-format=SAMPLEFORMAT        Tipo de mostraxe do destino (defaults "
+"to s16le)\n"
+"      --to-channels=CHANNELS          Canles do destino (defaults to 1)\n"
+"      --resample-method=METHOD        Método de remostraxe (defaults to "
+"auto)\n"
+"      --seconds=SECONDS               Duración do fluxo de orixe (defaults "
+"to 60)\n"
+"\n"
+"Se non se especifican os formatos, a proba leva a cabo todas as combinacións "
+"de\n"
+"formatos.\n"
+"\n"
+"O tipo de mostraxe debe ser s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (por omisión s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+
+#: ../src/tests/resampler-test.c:356
+#, c-format
+msgid "%s %s\n"
+msgstr "%s %s\n"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr "=== %d segundos: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+
+#: ../src/modules/module-virtual-surround-sink.c:49
+msgid "Virtual surround sink"
+msgstr "Sumideiro envolvente virtual"
+
+#: ../src/modules/module-virtual-surround-sink.c:53
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/"
+"left_hrir.wav "
+msgstr ""
+"sink_name=<nome para o destino> sink_properties=<propiedades para o destino> "
+"master=<nome do destino a filtrar> format=<formato de mostra> rate=<taxa de "
+"mostra> channels=<cantidade de canles> channel_map=<asignación de canles> "
+"use_volume_sharing=<si ou non> force_flat_volume=<si ou non> hrir=/ruta/ao/"
+"left_hrir.wav "
+
+#~ msgid "Internal Audio"
+#~ msgstr "Son interno"
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] Rlimit non está admitido nesta plataforma."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "Produciuse un fallo en XOpenDisplay()"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Saída da orixe #%u\n"
+#~ "\tControlador: %s\n"
+#~ "\tPropietario do módulo: %s\n"
+#~ "\tCliente: %s\n"
+#~ "\tOrixe: %u\n"
+#~ "\tEspecificación da mostra: %s\n"
+#~ "\tMapa de canles: %s\n"
+#~ "\tLatencia do búfer: %0.0f useg\n"
+#~ "\tLatencia da orixe: %0.0f useg\n"
+#~ "\tMétodo de nova mostraxe: %s\n"
+#~ "\tPropiedades:\n"
+#~ "\t\t%s\n"
+
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [opcións] estatística\n"
+#~ "%s [opcións] lista\n"
+#~ "%s [opcións] saída\n"
+#~ "%s [opcións] envío-exemplo NOMEFICHEIRO [NOME]\n"
+#~ "%s [opcións] reproducir-exemplo NOME [DESTINO]\n"
+#~ "%s [opcións] eliminar-exemplo NOME\n"
+#~ "%s [opcións] mover-entrada-destino ID DESTINO\n"
+#~ "%s [opcións] mover-saída-orixe ID ORIXE\n"
+#~ "%s [opcións] cargar-módulo NOME [ARGS ...]\n"
+#~ "%s [opcións] descargar-módulo ID\n"
+#~ "%s [opcións] suspender-destino [DESTINO] 1|0\n"
+#~ "%s [opcións] suspender-orixe [ORIXE] 1|0\n"
+#~ "%s [opcións] definir-perfil-tarxeta [TARXETA] [PERFIL] \n"
+#~ "%s [opcións] definir-porto-destino [DESTINO] [PORTO] \n"
+#~ "%s [opcións] definir-porto-orixe [ORIXE] [PORTO] \n"
+#~ "%s [opcións] definir-volume-destino DESTINO VOLUME\n"
+#~ "%s [opcións] definir-volume-orixe ORIXE VOLUME\n"
+#~ "%s [opcións] definir-volume-entrada-destino ENTRADADESTINO VOLUME\n"
+#~ "%s [opcións] definir-silenciado-destino DESTINO 1|0\n"
+#~ "%s [opcións] definir-silenciado-orixe ORIXE 1|0\n"
+#~ "%s [opcións] definir-silenciado-entrada-destino ENTRADADESTINO 1|0\n"
+#~ "%s [options] subscribirse\n"
+#~ "\n"
+#~ "  -h, --help                            Mostra esta axuda\n"
+#~ "      --version                         Mostra a versión\n"
+#~ "\n"
+#~ "  -s, --server=SERVIDOR                 O nome do servidor ao que "
+#~ "conectarse\n"
+#~ "  -n, --client-name=NOME                O nome deste cliente no servidor\n"
+
+#~ msgid "Analog Microphone"
+#~ msgstr "Micrófono analóxico"
+
+#~ msgid "Analog Line-In"
+#~ msgstr "Entrada en liña analóxica"
+
+#~ msgid "Analog Radio"
+#~ msgstr "Radio analóxica"
+
+#~ msgid "Analog Video"
+#~ msgstr "Vídeo analóxico"
+
+#~ msgid "Analog Headphones"
+#~ msgstr "Auriculares analóxicos"
+
+#~ msgid "Analog Output (LFE)"
+#~ msgstr "Saída analóxica (LFE)"
+
+#~ msgid "Analog Speakers"
+#~ msgstr "Altofalantes analóxicos"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Envolvente dixital 4.0 (IEC958)"
diff --git a/po/gu.po b/po/gu.po
new file mode 100644 (file)
index 0000000..7300602
--- /dev/null
+++ b/po/gu.po
@@ -0,0 +1,3014 @@
+# translation of PulseAudio.po to Gujarati
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Sweta Kothari <swkothar@redhat.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PulseAudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:53+0000\n"
+"Last-Translator: Sweta Kothari <swkothar@redhat.com>\n"
+"Language-Team: Gujarati\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() કિંમતને પાછુ મળેલ છે કે જે અપવાદ રીતે વિશાળ છે: %lu bytes (%lu ms).\n"
+"ALSA ડ્રાઇવર '%s' માં મોટેભાગે આ ભૂલ જેવુ છે. ALSA ડેવલ્પરોમાં આ સમસ્યાને મહેરબાની કરીને "
+"અહેવાલ કરો."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() કિંમતને પાછુ મળેલ છે કે જે અપવાદ રીતે વિશાળ છે: %li bytes (%s%lu "
+"ms).\n"
+"ALSA ડ્રાઇવર '%s' માં મોટેભાગે આ ભૂલ જેવુ છે. ALSA ડેવલ્પરોમાં આ સમસ્યાને મહેરબાની કરીને "
+"અહેવાલ કરો."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() કિંમતને પાછુ મળેલ છે કે જે અપવાદ રીતે વિશાળ છે: %lu bytes (%lu ms).\n"
+"ALSA ડ્રાઇવર '%s' માં મોટેભાગે આ ભૂલ જેવુ છે. ALSA ડેવલ્પરોમાં આ સમસ્યાને મહેરબાની કરીને "
+"અહેવાલ કરો."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() કિંમતને પાછુ મળેલ છે કે જે અપવાદ રીતે વિશાળ છે: %lu બાઇટો (%lu "
+"ms).\n"
+"ALSA ડ્રાઇવર '%s' માં મોટેભાગે આ ભૂલ જેવુ છે. ALSA ડેવલ્પરોમાં આ સમસ્યાને મહેરબાની કરીને "
+"અહેવાલ કરો."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "હંમેશા ઓછામાં ઓછુ એક સિંક લોડ થયેલ રાખો જો તે શૂન્ય હોય તો પણ"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "ડમી આઉટપુટ"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "વર્ચ્યુઅલ LADSPA સિંક"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "ક્લોક થયેલ NULL સિંક"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "શૂન્ય આઉટપુટ"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "આંતરિક ઓડિયો"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "મોડેમ"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "મૂળ lt_dlopen લોડરને શોધવામાં નિષ્ફળ."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "નવા dl લોડરને ફાળવવાનું નિષ્ફળ."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind-now-loader ને ઉમેરવાનું નિષ્ફળ."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "સંકેત %s મળ્યુ."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "બહાર નીકળી રહ્યા છે."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "વપરાશકર્તા '%s' ને શોધવામાં નિષ્ફળ."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "જૂથ '%s' ને શોધવામાં નિષ્ફળ."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "વપરાશકર્તા '%s' (UID %lu) અને જૂથ '%s' (GID %lu) શોધાયુ."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "વપરાશકર્તા '%s' અને જૂથ '%s' ની GID બંધબેસતુ નથી."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "વપરાશકર્તાઓ '%s' ની ઘર ડિરેક્ટરી '%s' નથી, અવગણી રહ્યા છે."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' ને બનાવવામાં નિષ્ફળ: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "જૂથ યાદીને બદલવામાં નિષ્ફળ: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID ને બદલવામાં નિષ્ફળ: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID ને બદલવામાં નિષ્ફળ: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "સફળતાપૂર્વક છોડી દીધેલ રુટ અધિકારો."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "આ પ્લેટફોર્મ પર બિનઆધારભૂત સિસ્ટમ વિશાળ સ્થિતિ."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) નિષ્ફળ: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "આદેશ વાક્યને પદચ્છેદન કરવામાં નિષ્ફળ."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "ડિમન ચાલી રહ્યુ નથી"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "PID %u તરીકે ડિમન ચાલી રહ્યુ છે"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "ડિમનને મારવાનું નિષ્ફળ: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"આ પ્રક્રિયાને રુટ તરીકે ચલાવવા માટે વિચાર થયેલ નથી (નહિં તો --system એ સ્પષ્ટ થયેલ છે)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "રુટ અધિકારો જરૂરી છે."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start એ સિસ્ટમ ઉદાહરણો માટે આધારભૂત નથી."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "સિસ્ટમ સ્થિતિમાં ચાલી રહ્યુ છે, પરંતુ --disallow-exit સુયોજિત નથી!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "સિસ્ટમ સ્થિતિમાં ચાલી રહ્યુ છે, પરંતુ --disallow-module-loading એ સુયોજિત નથી!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "સિસ્ટમ સ્થિતિમાં ચાલી રહ્યુ છે, SHM સ્થિતિને દબાણપૂર્વક નિષ્ક્રિય કરી રહ્યા છે!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"સિસ્ટમ સ્થિતિમાં ચાલી રહ્યુ છે, બહાર નીકળવનાં નિષ્કાર્ય સમયને દબાણપૂર્વક નિષ્ક્રિય કરી "
+"રહ્યા છે!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdio ને મેળવવામાં નિષ્ફળ."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "પાઇપ નિષ્ફળ: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() નિષ્ફળ: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() નિષ્ફળ: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "ડિમન શરૂઆત નિષ્ફળ."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "ડિમન શરૂઆત કરવુ સફળ છે."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() નિષ્ફળ: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "આ PulseAudio %s છે"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "કમ્પાઇલેશન યજમાન: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "કમ્પાઇલેશન CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "યજમાન પર ચાલી રહ્યુ છે: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u CPUs શોધાયુ."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "પાનાંનુ માપ %lu બાઇટો છે"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Valgrind આધાર સાથે કમ્પાઇલ થયેલ છે: હા"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Valgrind આધાર સાથે કમ્પાઇલ થયેલ છે: ના"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "valgrind સ્થિતિમાં ચાલી રહ્યુ છે: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "યજમાન પર ચાલી રહ્યુ છે: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "શ્રેષ્ટ થયેલ બિલ્ડ: હા"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "શ્રેષ્ટ થયેલ બિલ્ડ: ના"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG વ્યાખ્યાયિત થયેલ છે, બધા હકો નિષ્ક્રિય થયેલ છે."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH વ્યાખ્યાયિત થયેલ છે, ફક્ત ઝડપી પાથનાં હકો નિષ્ક્રિય થયેલ છે."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "બધા હકો સક્રિય થયેલ છે."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "મશીન ID ને મેળવવામાં નિષ્ફળ"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "મશીન ID %s છે."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "સત્ર ID %s છે."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "રનટાઇમ ડિરેક્ટરી %s ને વાપરી રહ્યા છે."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "સ્થિતિ ડિરેક્ટરી %s ને વાપરી રહ્યા છે."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "ઇોડ્યુલોમ ડિરેક્ટરી %s ને વાપરી રહ્યા છે."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "સિસ્ટમ સ્થિતિમાં ચાલી રહ્યુ છે: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"બરાબર, તેથી તમે સિસ્ટમ સ્થિતિમાં PA ચલાવી રહ્યા છો. મહેરબાની કરીને નોંધો કે જે તમારે "
+"મોટેભાગે કરવુ જોઇએ નહિં.\n"
+"જો તમે તેનાં વગર કરે તો પછી તે તમારી ભૂલ થે જો ઇચ્છિત રીતે તે કામ કરતુ ન હોય તો.\n"
+"શા માટે સિસ્ટમ સ્થિતિ સામાન્ય રીતે ખરાબ વિચાર છે તે માટે વિગતવાર જાણકારી માટે મહેરબાની "
+"કરીને http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/"
+"WhatIsWrongWithSystemWide/ આને વાંચો."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() નિષ્ફળ."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "તાજુ high-resolution ટાઇમરો ઉપલ્બધ છે! બોન એપેટાઇટ!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"મિત્ર, તમારુ કર્નલમાં ગડબડ છે! રસોઇયાનું આજે ભલામણ એ સક્રિય થયેલ high-resolution "
+"ટાઇમરો સાથે Linux નું છે!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() નિષ્ફળ."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "ડિમનને શરૂ કરવામાં નિષ્ફળ."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "કોઇપણ લોડ થયેલ મોડ્યુલો વગર ડિમનને શરૂ કરો, કામ કરવા માટે ફરી શરૂ કરી રહ્યા છે."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "ડિમન પારંભ કરવાનું સમાપ્ત છે."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "ડિમનને બંધ કરવાનું પ્રારંભ થયેલ છે."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "ડિમનનો અંત આવેલ છે."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            આ મદદ બતાવો\n"
+"      --version                         આવૃત્તિ બતાવો\n"
+"      --dump-conf                       મૂળભૂત રૂપરેખાંકનને ડમ્પ કરો\n"
+"      --dump-modules                    ઉપલ્બધ મોડ્યુલોની યાદીને ડમ્પ કરો\n"
+"      --dump-resample-methods           ઉપલ્બધ resample પદ્દતિઓને ડમ્પ કરો\n"
+"      --cleanup-shm                     વાપરેલ વહેંચાયેલ મેમરી સેગમેન્ટોને સાફ કરો\n"
+"      --start                           ડિમન ને શરૂ કરો જો તે ચાલી રહ્યુ ન હોય "
+"તો\n"
+"  -k  --kill                            ચાલી રહેલ ડિમનને મારો\n"
+"      --check                           ચાલતી ડિમન માટે ચકાસો (ફક્ત નીકાળેલ કોડ "
+"પાછો મળે છે)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   system-wide નમૂના તરીકે ચલાવો\n"
+"  -D, --daemonize[=BOOL]                શરૂઆત પછી Daemonize\n"
+"      --fail[=BOOL]                     બહાર નીકળો જ્યારે શરૂઆત જ નિષ્ફળ થાય\n"
+"      --high-priority[=BOOL]            ઊંચા સરસ સ્તરને સુયોજિત કરવાનો પ્રયત્ન "
+"કરો\n"
+"                                        (ફક્ત રુટ તરીકે, જ્યારે SUID અથવા\n"
+"                                        ઉચ્ચસ્તર થયેલ RLIMIT_NICE સાથે)\n"
+"      --realtime[=BOOL]                 રીઅલટાઇમ ગોઠવવાનું સક્રિય કરવા માટે "
+"પ્રયત્ન કરો\n"
+"                                        (ફક્ત રુટ તરીકે ઉપલ્બધ, જ્યારે SUID અથવા\n"
+"                                        ઉચ્ચસ્તર થયેલ RLIMIT_RTPRIO સાથે)\n"
+"      --disallow-module-loading[=BOOL]  સૂચિત મોડ્યુલ લોડીંગ/અનલોડીંગ ને શરૂઆત પછી\n"
+"                                        મોડ્યુલ વપરાશકર્તાને પરવાનગી ન આપો\n"
+"      --disallow-exit[=BOOL]            સૂચિત બહાર નીકળવા વપરાશકર્તાને પરવાનગી "
+"ન આપો\n"
+"      --exit-idle-time=SECS             ડિમનને બહાર કાઢો જ્યારે નિષ્ક્રિય હોય અને "
+"આ\n"
+"                                        સમય સમાપ્ત થયેલ હોય તો\n"
+"      --module-idle-time=SECS           આપમેળે લોડ થયેલ મોડ્યુલોને લોડ ન કરો જ્યારે "
+"તે નિષ્ક્રિય હોય અને\n"
+"                                        આ સમય પસાર થયેલ હોય\n"
+"      --scache-idle-time=SECS           આપમેળે લોડ થયેલ નમૂનાઓનો લોડ ન કરો જ્યારે "
+"નિષ્ક્રિય હોય અને\n"
+"                                        આ સમય પસાર થયેલ હોય\n"
+"      --log-level[=LEVEL]               વધારો અથવા વર્બોસીટી સ્તરને સુયોજિત કરો\n"
+"  -v                                    વર્બોસીટી સ્તરને વધારો\n"
+"      --log-target={auto,syslog,stderr} લોગ લક્ષ્યને સ્પષ્ટ કરો\n"
+"      --log-meta[=BOOL]                 સંદેશાઓમાં કોડ સ્થાનને સમાવો\n"
+"      --log-time[=BOOL]                 લોગ સંદેશાઓમાં ટાઇમસ્ટેમ્પોને સમાવો\n"
+"      --log-backtrace=FRAMES            લોગ સંદેશાઓમાં backtrace ને સમાવો\n"
+"  -p, --dl-search-path=PATH             ડાયનેમિક વહેંચાયેલ માટે શોધ પાથને સુયોજિત "
+"કરો\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          સ્પષ્ટ થયેલ resampling પદ્દતિને વાપરો\n"
+"                                        (શક્ય કિંમતો માટે --dump-resample-"
+"methods\n"
+"                                        ને જુઓ)\n"
+"      --use-pid-file[=BOOL]             PID ફાઇલને બનાવો\n"
+"      --no-cpu-limit[=BOOL]             પ્લેટફોર્મો પર CPU લોડ મર્યાદા રાખનારને "
+"સ્થાપિત ન કરો\n"
+"                                        કે જે તેને આધાર આપે છે.\n"
+"      --disable-shm[=BOOL]              વહેંચાયેલ મેમરી આધારને નિષ્ક્રિય કરો.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         સ્પષ્ટ થયેલ દલીલ સાથે સ્પષ્ટ થયેલ પલ્ગઇન "
+"મોડ્યુલને લોડ\n"
+"                                        કરો\n"
+"  -F, --file=FILENAME                   સ્પષ્ટ થયેલ સ્ક્રિપ્ટને ચલાવો\n"
+"  -C                                    શરૂઆત પછી TTY ચલાવવા પર આદેશ વાક્યને\n"
+"                                        ખોલો\n"
+"\n"
+"  -n                                    મૂળભૂત સ્ક્રિપ્ટ ફાઇલને લોડ કરો નહિં\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level એ લોગ સ્તર દલીલની ઇચ્છા રાખે છે (ક્યાંતો સીમા 0..4 માં પૂર્ણસંખ્યા છે અથવા "
+"ડિબગ, જાણકારી, સૂચના, ચેતવણી, ભૂલ નું એક)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "અયોગ્ય લોગ લક્ષ્ય: ક્યાંતો 'syslog', 'stderr' અથવા 'auto' ને વાપરો."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "અયોગ્ય resample પદ્દતિ '%s'."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm એ બુલિયન દલીલની ઇચ્છા રાખે છે"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "નામ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "મોડ્યુલ જાણકારી ઉપલ્બધ નથી\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "આવૃત્તિ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "વર્ણન: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "લેખક: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "વપરાશ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "એકવાર લોડ કરો: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "DEPRECATION WARNING: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "પાથ: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] અયોગ્ય લોગ લક્ષ્ય '%s'."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] અયોગ્ય લોગ સ્તર '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] અયોગ્ય resample પદ્દતિ '%s'."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] અયોગ્ય rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] અયોગ્ય નમૂના બંધારણ '%s'."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] અયોગ્ય નમૂના દર '%s'."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] અયોગ્ય નમૂના ચેનલો '%s'."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] અયોગ્ય ચેનલ મેપ '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] અયોગ્ય ફ્રેગમેન્ટોનાં નંબર '%s'."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] અયોગ્ય ફ્રેગમેન્ટ માપ '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] અયોગ્ય સારુ સ્તર '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] અયોગ્ય નમૂના દર '%s'."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "રૂપરેખાંકન ફાઇલને ખોલવાનું નિષ્ફળ: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"સ્પષ્ટ થયેલ મૂળભૂત ચેનલ મેપ પાસે સ્પષ્ટ થયેલ ચેનલોની મૂળભૂત સંખ્યા કરતા વિવિધ ચેનલોની સંખ્યા છે."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### રૂપરેખાંકન ફાઇલમાંથી વાંચો: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "અધિકારોને છોડી રહ્યા છે."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio સાઉન્ડ સિસ્ટમ"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "PulseAudio સાઉન્ડ સિસ્ટમને શરૂ કરો"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio સાઉન્ડ સિસ્ટમ"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "PulseAudio સાઉન્ડ સિસ્ટમને શરૂ કરો"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "મોનો"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "આગળનું કેન્દ્ર"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "આગળ ડાબે"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "આગળ જમણે"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "રિઅર કેન્દ્ર"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "રિઅર ડાબે"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "રિઅર જમણે"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "આગળ કેન્દ્રની ડાબે"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "આગળ કેન્દ્રની જમણે"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "ડાબી બાજુ"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "જમણી બાજુ"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "ઑગ્ઝિલિઅરિ 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "ઑગ્ઝિલિઅરિ 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "ઑગ્ઝિલિઅરિ 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "ઑગ્ઝિલિઅરિ 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "ઑગ્ઝિલિઅરિ 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "ઑગ્ઝિલિઅરિ 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "ઑગ્ઝિલિઅરિ 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "ઑગ્ઝિલિઅરિ 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "ઑગ્ઝિલિઅરિ 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "ઑગ્ઝિલિઅરિ 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "ઑગ્ઝિલિઅરિ 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "ઑગ્ઝિલિઅરિ 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "ઑગ્ઝિલિઅરિ 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "ઑગ્ઝિલિઅરિ 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "ઑગ્ઝિલિઅરિ 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "ઑગ્ઝિલિઅરિ 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "ઑગ્ઝિલિઅરિ 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "ઑગ્ઝિલિઅરિ 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "ઑગ્ઝિલિઅરિ 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "ઑગ્ઝિલિઅરિ 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "ઑગ્ઝિલિઅરિ 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "ઑગ્ઝિલિઅરિ 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "ઑગ્ઝિલિઅરિ 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "ઑગ્ઝિલિઅરિ 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "ઑગ્ઝિલિઅરિ 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "ઑગ્ઝિલિઅરિ 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "ઑગ્ઝિલિઅરિ 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "ઑગ્ઝિલિઅરિ 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "ઑગ્ઝિલિઅરિ 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "ઑગ્ઝિલિઅરિ 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "ઑગ્ઝિલિઅરિ 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "ઑગ્ઝિલિઅરિ 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "ઊંચે કેન્દ્ર"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "ઊંચે આગળ કેન્દ્ર"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "ઊંચે આગળ ડાબે"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "ઊંચે આગળ જમણે"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "ઊંચે રિઅર કેન્દ્ર"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "ઉપર રિઅર ડાબે"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "ઉપર રિઅર જમણે"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(અયોગ્ય)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "સ્ટેરિઓ"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "સરાઉન્ડ 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "સરાઉન્ડ 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "સરાઉન્ડ 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "સરાઉન્ડ 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "સરાઉન્ડ 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "બરાબર"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "પ્રવેશનો સ્વીકાર કરેલ નથી"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "અજ્ઞાત આદેશ"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "અયોગ્ય દલીલ"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "વસ્તુ અસ્તિત્વ ધરાવે છે"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "આવી વસ્તુ નથી"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "જોડાણને માન્ય ન કરવુ"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "પ્રોટોકોલ ભૂલ"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "સમય સમાપ્ત"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "સત્તાધિકરણ કી નથી"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "આંતરિક ભૂલ"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "જોડાણનો અંત થયેલ છે"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "વસ્તુને મારી નંખાયેલ છે"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "અયોગ્ય સર્વર"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "મોડ્યુલ શરૂઆત કરવાનું નિષ્ફળ"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "ખરાબ સ્થિતિ"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "માહિતી નથી"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "અસુસંગત પ્રોટોકોલ આવૃત્તિ"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "ઘણું લાંબુ છે"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "આધારભૂત નથી"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "અજ્ઞાત ભૂલ કોડ"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "આવુ એક્સટેન્શન નથી"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "અપ્રચલિત કાર્યત્મકતા"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "ગુમ થયેલ અમલીકરણ"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "ક્લાઇન્ટમાં ફાટા પડેલ છે"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "ઇનપુટ/આઉટપુટ ભૂલ"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "ઉપકરણ અથવા સ્ત્રોત વ્યસ્ત"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() નિષ્ફળ: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "કુકીની માહિતીને પદચ્છેદન કરવામાં નિષ્ફળ"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "રૂપરેખાંકન ફાઇલ '%s' ને ખોલવામાં નિષ્ફળ: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "કુકી લોડ થયેલ નથી. તેનાં વગર જોડવાનો પ્રયત્ન કરી રહ્યા છે."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "અજ્ઞાત એક્સટેન્શન '%s' માટે મળેલ સંદેશ"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "સ્ટ્રીમને નિકાલ કરવામાં નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "પ્લેબેક સ્ટ્રીમ ને નિકાલ કરેલ છે."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "સર્વરમાં જોડાણને નિકાલ કરી રહ્યા છે."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "સ્ટ્રીમ સફળતાપૂર્વક બનાવેલ છે."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "બફર મેટ્રિક્સ: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "બફર મેટ્રિક્સ: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "નમૂનો spec '%s' ને વાપરી રહ્યા છે, ચેનલ મેપ '%s'."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "ઉપકરણ %s (%u, %ssuspended) સાથે જોડાયેલ છે."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "સ્ટ્રીમ ભૂલ: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "સ્ટ્રીમ ઉપકરણ ને થોડા સમય માટે બંધ રાખેલ છે.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "સ્ટ્રીમ ઉપકરણને ફરી શરૂ કરેલ છે.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "સ્ટ્રીમ ચલાવવા હેઠળ છે.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "સ્ટ્રીમ ઉપર ચાલે છે.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "સ્ટ્રીમ શરૂ થયેલ છે.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "સ્ટ્રીમ એ ઉપકરણ %s (%u, %ssuspended) માં ખસેડેલ છે.%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "નથી "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "સ્ટ્રીમ બફર ગુણધર્મો બદલાયેલ છે.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "જોડાણ સ્થાપિત થયેલ છે.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "જોડાણ નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF મળ્યુ."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "સંકેત મળ્યું, બહાર નીકળી રહ્યા છે."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "ગુપ્તતા મેળવવામાં નિષ્ફળતા: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Time: %0.3f sec; Latency: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            આ મદદને બતાવો\n"
+"      --version                         આવૃત્તિને બતાવો\n"
+"\n"
+"  -r, --record                          રેકોર્ડીંગ માટે જોડાણને બનાવો\n"
+"  -p, --playback                        પ્લેબેક માટે જોડાણને બનાવો\n"
+"\n"
+"  -v, --verbose                         વર્બોસ ક્રિયાઓ ને સક્રિય કરો\n"
+"\n"
+"  -s, --server=SERVER                   તેમાં જોડાવા માટે સર્વરનું નામ\n"
+"  -d, --device=DEVICE                   તેમાં જોડાવા માટે સિંક/સ્ત્રોત નું નામ\n"
+"  -n, --client-name=NAME                સર્વર પર આ ક્લાઇન્ટને કેવી રીતે કોલ કરવો\n"
+"      --stream-name=NAME                સર્વર પર આ સ્ટ્રીમને કેવી રીતે કોલ કરવો\n"
+"      --volume=VOLUME                   સીમા 0...65536 માં પ્રારંભનાં (સીધા) "
+"વોલ્યુમને સ્પષ્ટ કરો\n"
+"      --rate=SAMPLERATE                 Hz માં નમૂનો(44100 નાં મૂળભૂતો)\n"
+"      --format=SAMPLEFORMAT             નમૂના પ્રકાર, s16le, s16be, u8, "
+"float32le, માંનો એક\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               ચેનલોની સંખ્યા, mono માટે 1, stereo માટે "
+"2\n"
+"                                        (2 એ મૂળભૂત છે)\n"
+"      --channel-map=CHANNELMAP          મૂળભૂત ને બદલે વાપરવા માટે ચેનલ મેપ\n"
+"      --fix-format                      સ્ટ્રીમ તેમાં જોડાયેલ છે તે સિંક માંથી નમૂના "
+"બંધારણને\n"
+"                                        લો.\n"
+"      --fix-rate                        સ્ટ્રીમ તેમાં જોડાયેલ છે તે સિંક માંથી નમૂના "
+"દરને લો.\n"
+"      --fix-channels                    સ્ટ્રીમ તેમાં જોડાયેલ છે તે સિંક માંથી ચેનલો "
+"અને ચેનલ માપને લો.\n"
+"      --no-remix                        ચેનલોને upmix અથવા downmix કરો નહિં.\n"
+"      --no-remap                        નામને બદલે અનુક્રમણિકા દ્દારા મેપ ચેનલો.\n"
+"      --latency=BYTES                   બાઇટોમાં સ્પષ્ટ થયેલ ગુપ્તતા ની માંગણી "
+"કરો.\n"
+"      --process-time=BYTES              બાઇટોમાં માંગણી ની સાથે સ્પષ્ટ થયેલ "
+"પ્રક્રિયા સમયની માંગણી કરો.\n"
+"      --property=PROPERTY=VALUE         સ્પષ્ટ થયેલ કિંમતમાં સ્પષ્ટ થયેલ ગુણધર્મને "
+"સુયોજિત કરો.\n"
+"      --raw                             કાચી PCM માહિતીનો રેકોર્ડ કરો/વગાડો.\n"
+"      --file-format=FFORMAT             બંધારણ થયેલ માહિતીનો રેકોર્ડ કરો/વગાડો.\n"
+"      --list-file-formats               ઉપલ્બધ ફાઇલ બંધારણોની યાદી.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"libpulse %s સાથે કમ્પાઇલ થયેલ છે\n"
+"libpulse %s સાથે કડી થયેલ છે\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "અયોગ્ય ક્લાઇન્ટ નામ '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "અયોગ્ય સ્ટ્રીમ નામ '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "અયોગ્ય ચેનલ મેપ '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "અયોગ્ય ગુપ્તતા સ્પષ્ટીકરણ '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "અયોગ્ય પ્રક્રિયા સમય સ્પષ્ટીકરણ '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "અયોગ્ય ગુણધર્મ '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "અજ્ઞાત ફાઇલ બંધારણ %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "અયોગ્ય નમૂના સ્પષ્ટીકરણ"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "ઘણી બધી દલીલો છે."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "ફાઇલ માટે નમૂના સ્પષ્ટીકરણ ને ઉત્પન્ન કરવામાં નિષ્ફળ."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "સાઉન્ડ ફાઇલને ખોલવામાં નિષ્ફળતા."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr "ચેતવણી: સ્પષ્ટ થયેલ નમૂના સ્પષ્ટીકરણ ફાઇલ માંથી સ્પષ્ટીકરણ સાથે ઉપર લખાયેલ હશે."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "ફાઇલ માંથી નમૂના સ્પષ્ટીકરણને નક્કી કરવામાં નિષ્ફળતા."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "ચેતવણી: ફાઇલમાંથી ચેનલ મેપને નક્કી કરવામાં નિષ્ફળતા."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "ચેનલ મેપ એ સ્પષ્ટીકરણ નમૂનાને બંધબેસતુ નથી"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "ચેતણી: ફાઇલમાં ચેનલ મેપને લખવામાં નિષ્ફળતા."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "નમૂના સ્પષ્ટીકરણ '%s' અને ચેનલ નક્ષા '%s' સાથે %s સ્ટ્રીમને ખોલી રહ્યા છે."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "રેકોર્ડ કરી રહ્યા છે"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "પ્લેબેક"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "આદેશ વાક્યને પદચ્છેદન કરવામાં નિષ્ફળ."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() નિષ્ફળ."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() નિષ્ફળ."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() નિષ્ફળ."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() નિષ્ફળ: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_new() નિષ્ફળ."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() નિષ્ફળ."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "થોડા સમય માટે બંધ કરવા માટે નિષ્ફળતા: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "ફરી શરૂ કરવામાં નિષ્ફળતા: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "ચેતવણી: સાઉન્ડ સર્વર એ સ્થાનિક નથી, થોડા સમય માટે બંધ કરવામાં આવ્યુ નથી.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "જોડાણ નિષ્ફળ: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT મળ્યુ, બહાર નીકળી રહ્યા છે.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "ચેતવણી: બાળ પ્રક્રિયાનો સંકેત %u દ્દારા અંત આવેલ છે\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            આ મદદ ને બતાવો\n"
+"      --version                         આવૃત્તિને બતાવો\n"
+"  -s, --server=SERVER                   જોડાવવા માટે સર્વરનું નામ\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"libpulse %s સાથે કમ્પાઇલ થયેલ છે\n"
+"libpulse %s સાથે કડી થયેલ છે\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() નિષ્ફળ.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() નિષ્ફળ.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() નિષ્ફળ.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "પરિસ્થિતિઓને મેળવવામાં નિષ્ફળતા: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "હાલમાં વપરાશમાં છે: %u બ્લોકો %s કુલ બાઇટોને સમાવી રહ્યા છે.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "આખી જીંદગી દરમ્યાન ફાળવેલ છે: %u બ્લોકો %s કુલ બાઇટોને સમાવી રહ્યા છે.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "નમૂના કેશ માપ: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "સર્વર જાણકારી મેળવવામાં નિષ્ફળતા: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"વપરાશકર્તા નામ: %s\n"
+"યજમાન નામ: %s\n"
+"સર્વર નામ: %s\n"
+"સર્વર આવૃત્તિ: %s\n"
+"મૂળભૂત નમૂના સ્પષ્ટીકરણ: %s\n"
+"મૂળભૂત ચેનલ મેપ: %s\n"
+"મૂળભૂત સિંક: %s\n"
+"મૂળભૂત સ્ત્રોત: %s\n"
+"કુકી: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "સિંક જાણકારી મેળવવામાં નિષ્ફળતા: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"સિંક #%u\n"
+"\tસ્થિતિ: %s\n"
+"\tનામ: %s\n"
+"\tવર્ણન: %s\n"
+"\tડ્રાઇવર: %s\n"
+"\tનમૂના સ્પષ્ટીકરણ: %s\n"
+"\tચેનલ મેપ: %s\n"
+"\tમાલિક મોડ્યુલ: %u\n"
+"\tમૂંગુ: %s\n"
+"\tવોલ્યુમ: %s%s%s\n"
+"\t        સમતુલન %0.2f\n"
+"\tઆધાર વોલ્યુમ: %s%s%s\n"
+"\tમોનિટર સ્ત્રોત: %s\n"
+"\tગુપ્તતા: %0.0f usec, configured %0.0f usec\n"
+"\tફ્લેગો: %s%s%s%s%s%s\n"
+"\tગુણધર્મો:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tપોર્ટો:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tસક્રિય પોર્ટ: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tપોર્ટો:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "સ્ત્રોત જાણકારીને મેળવવામાં નિષ્ફળતા: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"સ્ત્રોત #%u\n"
+"\tસ્થિતિ: %s\n"
+"\tનામ: %s\n"
+"\tવર્ણન: %s\n"
+"\tડ્રાઇવર: %s\n"
+"\tનમૂના સ્પષ્ટીકરણ: %s\n"
+"\tચેનલ મેપ: %s\n"
+"\tમાલિક મોડ્યુલ: %u\n"
+"\tમૂંગુ: %s\n"
+"\tવોલ્યુમ: %s%s%s\n"
+"\t        સમતુલન %0.2f\n"
+"\tઆધાર વોલ્યુમ: %s%s%s\n"
+"\tસિંકનું મોનિટર: %s\n"
+"\tગુપ્તતા: %0.0f usec, configured %0.0f usec\n"
+"\tફ્લેગો: %s%s%s%s%s%s\n"
+"\tગુણધર્મો:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "મોડ્યુલની જાણકારી મેળવવામાં નિષ્ફળતા: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"મોડ્યુલ #%u\n"
+"\tનામ: %s\n"
+"\tદલીલ: %s\n"
+"\tવપરાશ ગણતરી: %s\n"
+"\tગુણધર્મો:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "ક્લાઇન્ટ જાણકારી મેળવવામાં નિષ્ફળતા: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ક્લાઇન્ટ #%u\n"
+"\tડ્રાઇવર: %s\n"
+"\tમાલિક મોડ્યુલ: %s\n"
+"\tગુણધર્મો:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "કાર્ડ જાણકારી મેળવવામાં નિષ્ફળતા: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"કાર્ડ #%u\n"
+"\tનામ: %s\n"
+"\tડ્રાઇવર: %s\n"
+"\tમાલિક મોડ્યુલ: %s\n"
+"\tગુણધર્મો:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tરૂપરેખાઓ:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tસક્રિય રૂપરેખા: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "સિંક ઇનપુટ જાણકારી મેળવવામાં નિષ્ફળતા: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"સિંક ઇનપુટ #%u\n"
+"\tડ્રાઇવર: %s\n"
+"\tમાલિક મોડ્યુલ: %s\n"
+"\tક્લાઇન્ટ: %s\n"
+"\tસિંક: %u\n"
+"\tનમૂના સ્પષ્ટીકરણ: %s\n"
+"\tચેનલ મેપ %s\n"
+"\tમૂંગુ: %s\n"
+"\tવોલ્યુમ: %s\n"
+"\t        %s\n"
+"\t        સમતુલન %0.2f\n"
+"\tબફર ગુપ્તતા: %0.0f usec\n"
+"\tસિંક ગુપ્તતા: %0.0f usec\n"
+"\tResampl પદ્દતિ: %s\n"
+"\tગુણધર્મો:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "સ્ત્રોત આઉટપુટ જાણકારી મેળવવામાં નિષ્ફળ: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"સિંક ઇનપુટ #%u\n"
+"\tડ્રાઇવર: %s\n"
+"\tમાલિક મોડ્યુલ: %s\n"
+"\tક્લાઇન્ટ: %s\n"
+"\tસિંક: %u\n"
+"\tનમૂના સ્પષ્ટીકરણ: %s\n"
+"\tચેનલ મેપ %s\n"
+"\tમૂંગુ: %s\n"
+"\tવોલ્યુમ: %s\n"
+"\t        %s\n"
+"\t        સમતુલન %0.2f\n"
+"\tબફર ગુપ્તતા: %0.0f usec\n"
+"\tસિંક ગુપ્તતા: %0.0f usec\n"
+"\tResampl પદ્દતિ: %s\n"
+"\tગુણધર્મો:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "નમૂના જાણકારી મેળવવામાં નિષ્ફળ: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"નમૂના #%u\n"
+"\tનામ: %s\n"
+"\tનમૂના સ્પષ્ટીકરણ: %s\n"
+"\tચેનલ મેપ: %s\n"
+"\tવોલ્યુમ: %s\n"
+"\t        %s\n"
+"\t        સમતુલન %0.2f\n"
+"\tસમયગાળો: %0.1fs\n"
+"\tમાપ: %s\n"
+"\tઆળસુ: %s\n"
+"\tફાઇલનામ: %s\n"
+"\tગુણધર્મો:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "નિષ્ફળતા: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "સ્ત્રોત જાણકારીને મેળવવામાં નિષ્ફળતા: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "નમૂનાને અપલોડ કરવામાં નિષ્ફળ: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "ફાઇલનો નિયત સમય પહેલા અંત"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "અયોગ્ય સર્વર"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT મળ્યુ, બહાર નીકળી રહ્યા છે."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "અયોગ્ય નમૂના સ્પષ્ટીકરણ"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            આ મદદ ને બતાવો\n"
+"      --version                         આવૃત્તિને બતાવો\n"
+"  -s, --server=SERVER                   જોડાવવા માટે સર્વરનું નામ\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"libpulse %s સાથે કમ્પાઇલ થયેલ છે\n"
+"libpulse %s સાથે કડી થયેલ છે\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "મહેરબાની કરીને લોડ કરવા માટે નમૂના ફાઇલને સ્પષ્ટ કરો"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "સાઉન્ડ ફાઇલને ખોલવામાં નિષ્ફળ."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "ચેતવણી: ફાઇલ માંથી નમૂના સ્પષ્ટીકરણ કરવાનું નક્કી કરવામાં નિષ્ફળ."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "વગાડવા માટે તમારે નમૂના નામને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "દૂર કરવા માટે તમારે નમૂના નામને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "સિંક ઇનપુટ અનુક્રમણિકા અને સિંકને તમારે સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "તમારે સ્ત્રોત આઉટપુટ અનુક્રમણિકા અને સ્ત્રોતને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "તમારે મોડ્યુલ નામ અને દલીલોને સ્પષ્ટ કરવુ જ પડશે."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "તમારે મોડ્યુલ અનુક્રમણિકાને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"તમે એક સિંક કરતા વધારે સ્પષ્ટ કરી શકશો નહિં. તમારે બુલિયન કિંમતને સ્પષ્ટ કરવુ જ પડશે."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"તમે એક સ્ત્રોત કરતા વધારે સ્પષ્ટ કરી શકશો નહિં. તમારે બુલિયન કિંમતને સ્પષ્ટ કરવુ જ પડશે."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "તમારે કાર્ડ નામ/અનુક્રમણિકા અને પોર્ટ નામને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "તમારે કાર્ડ નામ/અનુક્રમણિકા અને પોર્ટ નામને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "તમારે કાર્ડ નામ/અનુક્રમણિકા અને પોર્ટ નામને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "તમારે કાર્ડ નામ/અનુક્રમણિકા અને વોલ્યુમને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "તમારે કાર્ડ નામ/અનુક્રમણિકા અને વોલ્યુમ સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "સિંક ઇનપુટ અનુક્રમણિકા અને વોલ્યુમને તમારે સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "અયોગ્ય સિંક ઇનપુટ અનુક્રમણિકા"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "તમારે સ્ત્રોત આઉટપુટ અનુક્રમણિકા અને સ્ત્રોતને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "અયોગ્ય સિંક ઇનપુટ અનુક્રમણિકા"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "તમારે સિંક નામ/અનુક્રમણિકા અને મૂંગા બુલિયનને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "અયોગ્ય નમૂના સ્પષ્ટીકરણ"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "તમારે સિંક નામ/અનુક્રમણિકા અને મૂંગા બુલિયનને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "સિંક ઇનપુટ અનુક્રમણિકા અને મૂંગા બુલિયનને તમારે સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "અયોગ્ય ઇનપુટ અનુક્રમણિકા સ્પષ્ટીકરણ"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "તમારે સિંક નામ/અનુક્રમણિકા અને મૂંગા બુલિયનને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "અયોગ્ય ઇનપુટ અનુક્રમણિકા સ્પષ્ટીકરણ"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "તમારે સિંક નામ/અનુક્રમણિકા અને મૂંગા બુલિયનને સ્પષ્ટ કરવુ જ પડશે"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "યોગ્ય આદેશ સ્પષ્ટ થયેલ નથી."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    X11 દેખાવ (મૂળભૂત) માં જોડાયેલ હાલની PulseAudio માહિતીને બતાવો\n"
+" -e    X11 દેખાવમાં સ્થાનિય PulseAudio માહિતીની નિકાસ કરો\n"
+" -i    સ્થાનિક પર્યાવરણ ચલો અને કુકી ફાઇલમાં X11 દેખાવમાંથી PulseAudio માહિતીને આયાત "
+"કરો.\n"
+" -r    X11 દેખાવમાંથી PulseAudio માહિતીને દૂર કરો\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "આદેશ વાક્યને પદચ્છેદન કરવામાં નિષ્ફળ.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "સર્વર: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "સ્ત્રોત: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "સિંક: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "કુકી: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "કુકી માહિતીને પદચ્છેદન કરવામાં નિષ્ફળ\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "કુકી માહિતીને સંગ્રહ કરવામાં નિષ્ફળ\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "ક્લાઇન્ટ રૂપરેખાંકન ફાઇલને લોડ કરવામાં નિષ્ફળ.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "પર્યાવરણ રૂપરેખાંકન માહિતીને વાંચવામાં નિષ્ફળ.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN ને મેળવવામાં નિષ્ફળ.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "કુકી માહિતીને લોડ કરવામાં નિષ્ફળ\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "હજુ અમલીકરણ થયેલ નથી.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "PulseAudio ડિમન ચાલી રહ્યુ નથી, અથવા સત્ર ડિમન તરીકે ચાલી રહ્યુ નથી."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio ડિમનને મારવામાં નિષ્ફળ."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "ડિમન એ જવાબ આપતુ નથી."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "autospawn તાળાને દાખલ કરી શકાતુ નથી."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA એ ઉપકરણમાં નવી માહિતીને લખવા માટે આપણને જગાડતુ હતુ, પરંતુ ત્યાં વાસ્તવમાં કંઇ જ લખાયુ "
+"ન હતુ!\n"
+"મોટેભાગે આ ALSA ડ્રાઇવર '%s' માં ભૂલ જેવુ છે. મહેરબાની કરીને આ મુદ્દાને ALSA ડેવલપરોમાં "
+"અહેવાલ કરો.\n"
+"POLLOUT સુયોજન સાથે આપણે જાગેલ હતા -- છતાંપણ ના પછીનું snd_pcm_avail() ને 0 પાછો મળે "
+"છે અથવા બીજી કિંમત < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA એ ઉપકરણમાં નવી માહિતીને વાંચવા માટે આપણને જગાડતુ હતુ, પરંતુ ત્યાં વાસ્તવમાં કંઇ જ "
+"વંચાયુ ન હતુ!\n"
+"મોટેભાગે આ ALSA ડ્રાઇવર '%s' માં ભૂલ જેવુ છે. મહેરબાની કરીને આ મુદ્દાને ALSA ડેવલપરોમાં "
+"અહેવાલ કરો.\n"
+"POLLOUT સુયોજન સાથે આપણે જાગેલ હતા -- છતાંપણ ના પછીનું snd_pcm_avail() ને 0 પાછો મળે "
+"છે અથવા બીજી કિંમત < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "બંધ"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "High Fidelity Playback (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "High Fidelity Capture (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telephony Duplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio સાઉન્ડ સર્વર"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "આઉટપુટ ઉપકરણો"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "ઇનપુટ ઉપકરણો"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@ પર ઓડિયો"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "ઇનપુટ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "ડોકિંગ સ્ટેશન ઇનપુટ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "ડોકિંગ સ્ટેશન માઇક્રોફોન"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "ડોકિંગ સ્ટેશન ઇનપુટ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "લાઇન-ઇન"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "માઇક્રોફોન"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "ડોકિંગ સ્ટેશન માઇક્રોફોન"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "માઇક્રોફોન"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "બહારનાં માઇક્રોફોન"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "આંતરિક માઇક્રોફોન"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "રેડિયો"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "વિડિયો"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "Automatic Gain Control"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "Automatic Gain Control નથી"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "બુસ્ટ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "બુસ્ટ નથી"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "પરિવર્ધક"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "પરિવર્ધક નથી"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "બુસ્ટ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "બુસ્ટ નથી"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "ઍનલૉગ હૅડફોનો"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "ઍનલૉગ ઇનપુટ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "ડોકિંગ સ્ટેશન માઇક્રોફોન"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "ઍનલૉગ આઉટપુટ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "ઍનલૉગ આઉટપુટ (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "લાઇન-ઇન"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "ઍનલૉગ મોનો આઉટપુટ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "ઍનલૉગ સ્ટેરિઓ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "ડિજિટલ સ્ટેરિઓ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "ડિજિટલ સ્ટેરિઓ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "ઍનલૉગ મોનો"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "ઍનલૉગ સ્ટેરિઓ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "ઍનલૉગ સરાઉન્ડ 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "ઍનલૉગ સરાઉન્ડ 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "ઍનલૉગ સરાઉન્ડ 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "ઍનલૉગ સરાઉન્ડ 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "ઍનલૉગ સરાઉન્ડ 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "ઍનલૉગ સરાઉન્ડ 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "ઍનલૉગ સરાઉન્ડ 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "ઍનલૉગ સરાઉન્ડ 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "ઍનલૉગ સરાઉન્ડ 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "ઍનલૉગ સરાઉન્ડ 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "ઍનલૉગ સરાઉન્ડ 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "ડિજિટલ સ્ટેરિઓ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "ડિજિટલ સ્ટેરિઓ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "ડિજિટલ સરાઉન્ડ 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "ડિજિટલ સરાઉન્ડ 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "ડિજિટલ સ્ટેરિઓ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "ડિજિટલ સરાઉન્ડ 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "ઍનલૉગ મોનો ડુપ્લેક્ષ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "ઍનલૉગ સ્ટેરિઓ ડુપ્લેક્ષ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "ડિજિટલ સ્ટેરિઓ ડુપ્લેક્ષ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "શૂન્ય આઉટપુટ"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "ઇનપુટ"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit એ આ પ્લેટફોર્મ પર આધારભૂત નથી."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() નિષ્ફળ"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "સ્ત્રોત આઉટપુટ #%u\n"
+#~ "\tડ્રાઇવર: %s\n"
+#~ "\tમાલિક મોડ્યુલ: %s\n"
+#~ "\tક્લાઇન્ટ: %s\n"
+#~ "\tસ્ત્રોત: %u\n"
+#~ "\tનમૂના સ્પષ્ટીકરણ: %s\n"
+#~ "\tચેનલ મેપ %s\n"
+#~ "\tબફર ગુપ્તતા: %0.0f usec\n"
+#~ "\tસિંક ગુપ્તતા: %0.0f usec\n"
+#~ "\tResampl પદ્દતિ: %s\n"
+#~ "\tગુણધર્મો:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "ડિજિટલ સરાઉન્ડ 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Low Frequency Emmiter"
diff --git a/po/he.po b/po/he.po
new file mode 100644 (file)
index 0000000..daf4a56
--- /dev/null
+++ b/po/he.po
@@ -0,0 +1,2602 @@
+#
+# Elad <el.il@doom.co.il>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:53+0000\n"
+"Last-Translator: Elad <el.il@doom.co.il>\n"
+"Language-Team: Hebrew <fedora-he-list@redhat.com>\n"
+"Language: he\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Poedit-Language: Hebrew\n"
+"X-Poedit-Country: Israel\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr ""
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr ""
+
+#: ../src/modules/module-ladspa-sink.c:52
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr ""
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr ""
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "צליל פנימי"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "מודם"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr ""
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr ""
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr ""
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr ""
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "יוצא."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr ""
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr ""
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr ""
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr ""
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr ""
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr ""
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr ""
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr ""
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr ""
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr ""
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr ""
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr ""
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr ""
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr ""
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr ""
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr ""
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, c-format
+msgid "pipe() failed: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr ""
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr ""
+
+#: ../src/daemon/main.c:816
+#, c-format
+msgid "setsid() failed: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr ""
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr ""
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr ""
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr ""
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr ""
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:921
+#, c-format
+msgid "Running in VM: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr ""
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr ""
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr ""
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr ""
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr ""
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr ""
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr ""
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr ""
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr ""
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr ""
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr ""
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr ""
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr ""
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr ""
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr ""
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr ""
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr ""
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr ""
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:318
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr ""
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr ""
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr ""
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr ""
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr ""
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr ""
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr ""
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr ""
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr ""
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr ""
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:528
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr ""
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr ""
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "מערכת הקול PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "התחל את מערכת הקול PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "מערכת הקול PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "התחל את מערכת הקול PulseAudio"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "מונו"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "מרכזי קדמי"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "שמאלי קדמי"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "ימני קדמי"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "מרכזי אחורי"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "שמאלי אחורי"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "ימני אחורי"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "צד שמאל"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "צד ימין"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "מרכזי עליון"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "מרכזי עליון קדמי"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "עליון שמאלי קדמי"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "עליון ימני קדמי"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "עליון מרכזי אחורי"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "עליון שמאלי אחורי"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "עליון ימני אחורי"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(לא תקף)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "סטראו"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "סראונד 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "סראונד 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "סראונד 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "סראונד 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "סראונד 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "אישור"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "הגישה נדחתה"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr ""
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr ""
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr ""
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr ""
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "החיבור נדחה"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "שגיאת פרוטוקול"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr ""
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr ""
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "שגיאה פנימית"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr ""
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr ""
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr ""
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr ""
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr ""
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr ""
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr ""
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "גדול מדי"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "לא נתמך"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "קוד שגיאה לא מוכר"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr ""
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr ""
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr ""
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr ""
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "שגיאת קלט/פלט"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "התקן או משאב עסוקים"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr ""
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr ""
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr ""
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr ""
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+msgid "xcb_connect() failed"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr ""
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr ""
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr ""
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr ""
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr ""
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr ""
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr ""
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr ""
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr ""
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr ""
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr ""
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr ""
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr ""
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "לא"
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr ""
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr ""
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr ""
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:653
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr ""
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr ""
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr ""
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr ""
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr ""
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr ""
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr ""
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr ""
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr ""
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr ""
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr ""
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr ""
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr ""
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr ""
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr ""
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "מקליט"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "נגינה"
+
+#: ../src/utils/pacat.c:1110
+msgid "Failed to set media name."
+msgstr ""
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr ""
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr ""
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr ""
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr ""
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr ""
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:270
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, c-format
+msgid "\tFormats:\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "לא זמין"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tפרופילים:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:622
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:693
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "כשל: %s"
+
+#: ../src/utils/pactl.c:915
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr ""
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+msgid "server"
+msgstr ""
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr ""
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr ""
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr ""
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr ""
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr ""
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr ""
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr ""
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr ""
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr ""
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr ""
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr ""
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr ""
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr ""
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr ""
+
+#: ../src/utils/pactl.c:1660
+msgid "You have to specify a source output index and a volume"
+msgstr ""
+
+#: ../src/utils/pactl.c:1665
+msgid "Invalid source output index"
+msgstr ""
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr ""
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+msgid "Invalid mute specification"
+msgstr ""
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr ""
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr ""
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr ""
+
+#: ../src/utils/pactl.c:1732
+msgid "You have to specify a source output index and a mute boolean"
+msgstr ""
+
+#: ../src/utils/pactl.c:1737
+msgid "Invalid source output index specification"
+msgstr ""
+
+#: ../src/utils/pactl.c:1756
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr ""
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr ""
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr ""
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr ""
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr ""
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr ""
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr ""
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr ""
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr ""
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "מכובה"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "שרת הקול PulseAudio"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "התקני פלט"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "התקני קלט"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "פלט"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "קלט של תחנת עגינה"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "מיקרופון של תחנת עגינה"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "קלט של תחנת עגינה"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "קו נכנס"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "מיקרופון"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "מיקרופון של תחנת עגינה"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "מיקרופון"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "מיקרופון חיצוני"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "מיקרופון פנימי"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "רדיו"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "וידאו"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "מגבר"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "אין מגבר"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+msgid "Bass Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+msgid "No Bass Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "אוזניות אנלוגיות"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "קלט אנלוגי"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "מיקרופון של תחנת עגינה"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "פלט אנלוגי"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "פלט אנלוגי (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "קו נכנס"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "פלט מונו אנלוגי"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "סטראו אנלוגי"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "סטראו דיגיטלי (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "סטראו דיגיטלי (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "מונו אנלוגי"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "סטראו אנלוגי"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "סראונד אנלוגי 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "סראונד אנלוגי 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "סראונד אנלוגי 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "סראונד אנלוגי 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "סראונד אנלוגי 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "סראונד אנלוגי 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "סראונד אנלוגי 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "סראונד אנלוגי 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "סראונד אנלוגי 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "סראונד אנלוגי 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "סראונד אנלוגי 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "סטראו דיגיטלי (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "סטראו דיגיטלי (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "סראונד דיגיטלי 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "סראונד דיגיטלי 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "סטראו דיגיטלי (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "סראונד דיגיטלי 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "מונו אנלוגי משולב"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "סטראו אנלוגי משולב"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "סטראו דיגיטלי משולב (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "פלט אנלוגי"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "פלט"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "סראונד דיגיטלי 4.0 (IEC958)"
diff --git a/po/hi.po b/po/hi.po
new file mode 100644 (file)
index 0000000..da46916
--- /dev/null
+++ b/po/hi.po
@@ -0,0 +1,3031 @@
+# translation of pulseaudio.master-tx.po to Hindi
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Rajesh Ranjan <rajesh672@gmail.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:54+0000\n"
+"Last-Translator: Rajesh Ranjan <rajesh672@gmail.com>\n"
+"Language-Team: Hindi <hindi.sf.net>\n"
+"Language: hi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ने एक मान दिया जो अप्रत्याशित रूप से बड़ा है: %lu बाइट (%lu ms).\n"
+"अधिक संभव है कि यह ALSA ड्राइवर '%s' में एक बग है. इस मुद्दे को ALSA डेवलेपर को रिपोर्ट "
+"करें."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() ने एक मान दिया जो अप्रत्याशित रूप से बड़ा है: %li बाइट (%s%lu ms).\n"
+"अधिक संभव है कि यह ALSA ड्राइवर '%s' में एक बग है. इस मुद्दे को ALSA डेवलेपर को रिपोर्ट "
+"करें."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ने एक मान दिया जो अप्रत्याशित रूप से बड़ा है: %lu बाइट (%lu ms).\n"
+"अधिक संभव है कि यह ALSA ड्राइवर '%s' में एक बग है. इस मुद्दे को ALSA डेवलेपर को रिपोर्ट "
+"करें."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() ने एक मान दिया जो अप्रत्याशित रूप से बड़ा है: %lu बाइट (%lu "
+"ms).\n"
+"अधिक संभव है कि यह ALSA ड्राइवर '%s' में एक बग है. इस मुद्दे को ALSA डेवलेपर को रिपोर्ट "
+"करें."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "हमेशा कम से मक एक सिंक को लोडेड रखें हालांकि यह एक रिक्त है"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "डमी आउटपुट"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "वर्चुअल LADSPA सिंक"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "क्लॉक्ड रिक्त सिंक"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "रिक्त आउटपुट"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "आंतरिक ऑडियो"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "मॉडेम"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "मौलिक ltdlopen लोडर ढूँढ़ने में विफल (_d)."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "नया dl लोडर आबंटित करने में विफल."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind-now-loader जोड़ने में विफल."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "%s संकेत पाया."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "बाहर हो रहा है."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "'%s' उपयोक्ता ढूंढ़ने में विफल."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "'%s' समूह ढूंढ़ने में विफल."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "'%s' (UID %lu) उपयोक्ता व '%s' (GID %lu) समूह पाया."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "'%s' उपयोक्ता और '%s' समूह का GID मेल नहीं खाता है"
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "'%s' उपयोक्ता की घर निर्देशिका '%s' नहीं है, अनदेखा कर रहा है."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' बनाने में विफल: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "समूह सूची पाने में विफल: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID बदलने में विफल: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID बदलने में विफल: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "रूट अधिकार सफलतापूर्वक छोड़ा."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "इस प्लैटफॉर्म पर असमर्थित तंत्र व्यापक विधि."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) विफल: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "कमांड लाइन विश्लेषण में विफल."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "डेमॉन नहीं कार्यशील"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "डेमॉन बतौर PID %u चल रहा है"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "डेमॉन हटाने में विफल: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"यह प्रोग्राम बतौर रूट चलाने के लिए इच्छित नहीं है (unless --system is specified)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "रूट अधिकार जरूरी."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start not supported for system instances."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "तंत्र मोड में चल रहा है, लेकिन --disallow-exit सेट नहीं!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "तंत्र मोड में चल रहा है, लेकिन --disallow-module-loading सेट नहीं!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "तंत्र मोड में चल रहा है, SHM मोड बाध्य रूप से निष्क्रिय!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "तंत्र मोड में चल रहा है, निकास निष्क्रिय समय बाध्य रूप से निष्क्रिय!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdio पाने में विफल."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "पाइप विफल: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() विफल: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() विफल: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "डेमॉन आरंभ विफल."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "डेमॉन आरंभ सफल."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() विफल: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "यह पल्सऑडियो %s है."
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Compilation host: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "Compilation CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "मेजबान पर चल रहा है: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u CPU पाया."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "पृष्ठ आकार %lu बाइट है."
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "वेलग्रिंड समर्थन से कंपाइल: हाँ"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "वेलग्रिंड समर्थन से कंपाइल: नहीं"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "वेलग्रिंड विधि में चल रहा है: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "मेजबान पर चल रहा है: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "अनुकूलित बिल्ड: हाँ"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "अनुकूलित बिल्ड: नहीं"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG परिभाषित, सभी निष्क्रिय."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH परिभाषित, केव तेज पथ एसर्ट निष्क्रिय."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "सभी एसर्ट सक्षम."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "मशीन ID पाने में विफल"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "मशीन ID %s है."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "सत्र ID %s है."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "रनटाइम निर्देशिका %s का प्रयोग कर रहा है."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "स्टेट निर्देशिका %s का प्रयोग कर रहा है."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "मॉड्यूल निर्देशिका %s का प्रयोग कर रहा है."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "तंत्र मोड में चल रहा है: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"तो आप PA तंत्र मोड में चला रहे हैं. कृपया नोट करें कि आपको ऐसा नहीं करना चाहिए.\n"
+"यदि आप इसे करते हैं तो यह आपकी गलती है यदि कुछ अप्रत्याशित होता है.\n"
+"कृपया http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/"
+"WhatIsWrongWithSystemWide/ को पढ़ें जानने के लिए कि क्यों तंत्र मोड एक बढ़िया विचार "
+"नहीं है."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() विफल."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "ताज़ा उच्च विभेदन टाइमर उपलब्ध! आनंद लें!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"आपका कर्नेल बुरी स्थिति में है! सलाह है कि उच्च विभेदन युक्त लिनक्स सक्रिय किया जाना चाहिए!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() विफल."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "डेमॉन आरंभ करने में विफल."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "बिना लोड मॉड्यूल के डेमॉन आरंभ, काम करने से अस्वीकार कर रहा है."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "डेमॉन आरंभन पूर्ण."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "डेमॉन बन्द किया जाना आरंभ."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "डेमॉन अवरोधित."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --module-idle-time=SECS           Unload autoloaded modules when idle "
+"and\n"
+"                                        this time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr} Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "अवैध लॉग लक्ष्य: use either 'syslog', 'stderr' or 'auto'."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "अवैध पुनः प्रतिदर्श विधि '%s'."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm के लिए बुलियन तर्क की आशा है"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "नाम: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "कोई मॉड्यूल सूचना उपलब्ध नहीं\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "संस्करण: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "विवरण: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "लेखक: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "उपयोग: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "एक बार लोड करें: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "अवमूल्यन चेतावनी: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "पथ: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] अवैध लॉग लक्ष्य '%s'."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] अवैध लॉग स्तर '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] अवैध पुनः नमूना विधि '%s'."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] अवैध rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] अवैध प्रतिदर्श प्रारूप '%s'."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] अवैध प्रतिदर्श दर '%s'."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] अवैध प्रतिदर्श चैनल '%s'."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] अवैध चैनल मानचित्र '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] टुकड़े '%s' की अवैध संख्या."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] अवैध खंड आकार '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] अवैध नाइस स्तर '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] अवैध प्रतिदर्श दर '%s'."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "विन्यास फ़ाइल खोलने में विफल: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"निर्दिष्ट तयशुदा चैनल मानचित्र के पास चैनल की भिन्न संख्या है चैनल की तयशुदा निर्दिष्ट संख्या "
+"के बनिस्पत."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### विन्यास फ़ाइल से पढ़ें: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "अधिकार छोड़ रहा है."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "पल्सऑडियो ध्वनि तंत्र"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "पल्सऑडियो ध्वनि तंत्र प्रारंभ करें"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "पल्सऑडियो ध्वनि तंत्र"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "पल्सऑडियो ध्वनि तंत्र प्रारंभ करें"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "मोनो"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "अग्र केंद्र"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "अग्र बायाँ"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "अग्र दाहिना"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "पश्च केंद्र"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "पश्च बायां"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "पश्च दाहिना"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "केंद्र का अग्र वाम"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "केंद्र का अग्र दक्षिण"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "किनारा वाम"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "किनारा दायाँ"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "सहायक 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "सहायक 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "सहायक 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "सहायक 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "सहायक 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "सहायक 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "सहायक 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "सहायक 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "सहायक 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "सहायक 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "सहायक 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "सहायक 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "सहायक 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "सहायक 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "सहायक 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "सहायक 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "सहायक 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "सहायक 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "सहायक 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "सहायक 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "सहायक 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "सहायक 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "सहायक 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "सहायक 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "सहायक 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "सहायक 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "सहायक 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "सहायक 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "सहायक 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "सहायक 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "सहायक 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "सहायक 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "शीर्ष केंद्र"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "शीर्ष अग्र केंद्र"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "शीर्ष अग्र वाम"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "ऊपर अग्र दायाँ"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "ऊपर पश्च केंद्र"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "ऊपर पश्च बायाँ"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "ऊपर पश्च दायाँ"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(अवैध)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "स्टीरियो"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "सर्राउंड 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "सर्राउंड 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "सर्राउंड 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "सर्राउंड 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "सर्राउंड 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "ठीक"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "पहुँच मनाही"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "अनजान कमांड"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "अवैध तर्क"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "एंटिटी मौजूद"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "कोई ऐसी एंटिटी नहीं"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "कनेक्शन अस्वीकृत"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "प्रोटोकाल त्रुटि"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "समय ख़त्म"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "कोई प्राधिकरण कुंजी नहीं"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "आंतरिक त्रुटि"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "कनेक्शन समाप्त"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "एंटिटी मृत"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "अवैध सर्वर"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "मॉडयूल आरंभीकरण असफल"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "बुरी स्थिति"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "कोई आँकड़ा नहीं"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "बेमेल प्रोटोकॉल संस्करण"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "काफी बड़ा"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "समर्थित नहीं है"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "अज्ञात त्रुटि कोड"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "कोई ऐसा विस्तार नहीं"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "पुरानी प्रकार्यात्मकता"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "अनुपस्थित कार्यान्वयन"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "क्लाएंट विभाजित"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "इनपुट/आउटपुट त्रुटि"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "युक्ति या संसाधन व्यस्त"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() विफल: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "कुकी आंकड़ा के विश्लेषण में विफल"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "विन्यास फ़ाइल '%s' खोलने में विफल: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "कोई कुकी नहीं लोड किया गया. इसके बिना कनेक्ट करने की कोशिश कर रहा हूँ."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "अज्ञात विस्तार '%s' के लिए संदेश प्राप्त"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "स्ट्रीम से खींचने में विफल: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "प्लेबैक स्ट्रीम खत्म."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "सर्वर में कनेक्शन ले जा रहा है."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() विफल: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() विफल: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() विफल: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "स्ट्रीम सफलतापूर्वक निर्मित."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() विफल: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "बफ़र मेट्रिक्स: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "बफ़र मेट्रिक्स: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "नमूना स्पेक '%s' का प्रयोग, चैनल मैप '%s'."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "युक्ति %s (%u, %ssuspended) से कनेक्टेड."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "स्ट्रीम त्रुटि: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "स्ट्रीम युक्ति स्थगित.%s "
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "स्ट्रीम युक्ति पुनर्बहाल.%s "
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "स्ट्रीम अंडररन.%s "
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "स्ट्रीम ओवररन.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "स्ट्रीम आरंभ.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "स्ट्रीम युक्ति %s (%u, %ssuspended).%s में खिसकाया गया"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "नहीं "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "स्ट्रीम बफ़र गुण परिवर्तित.%s "
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "कनेक्शन स्थापित.%s "
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() विफल: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() विफल: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() विफल: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "कनेक्शन विफल.%s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF पाया."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() विफल: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "संकेत पाया, निकल रहा है."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "लेटेंसी पाने में विफल: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "समय: %0.3f सेकेंड; लैटेंसी: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() विफल: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --file-format=FFORMAT             Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"लिबपल्स %s के साथ कंपाइल\n"
+"लिबपल्स %s के साथ लिंक\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "अवैध क्लाइंट नाम '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "अवैध स्ट्रीम नाम '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "अवैध चैनल मानचित्र '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "अवैध लैटेंसी विनिर्दिष्टता '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "अवैध प्रक्रिया समय विनिर्दिष्टता '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "अवैध गुण '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "अज्ञात फ़ाइल प्रारूप %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "अवैध नमूना विनिर्दिष्टता"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "कई वितर्क."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "फ़ाइल के लिए नमूना विनिर्दिष्टता पाने में विफल."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "ध्वनि फ़ाइल खोलने में विफल."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr "चेतावनी: निर्दिष्ट नमूना विनिर्दिष्टता को फ़ाइल की विनिर्दिष्टता से लिखा जाएगा."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "फ़ाइल से नमूना विनिर्दिष्टता निर्धारित करने में विफल."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "चेतावनी: फ़ाइल से चैनल मैप पाने में विफल."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "चैनल मैप नमूना विनिर्दिष्टता से मेल नहीं खाता है"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "चेतावनी: फ़ाइल में चैनल मैप लिखने में विफल."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "किसी %s स्ट्रीम को किसी नमूना विनिर्दिष्ता '%s' और चैनल मैप '%s' से खोल रहा है."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "रिकार्डिंग"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "प्लेबैक"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "कमांड लाइन विश्लेषण में विफल."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() विफल."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() विफल."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() विफल."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() विफल: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() विफल."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() विफल."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "स्थगन में विफल: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "पुनर्बहाली में विफल: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "चेतावनी: ध्वनि सर्वर स्थानीय नहीं है, स्थगित नहीं कर रहा है.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "कनेक्शन विफल.%s \n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT पाया, निकल रहा है.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "चेतावनी: संतति प्रक्रिया %u संकेत से रूका\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"लिबपल्स %s से कंपाइल\n"
+"लिबपल्स %s से कड़ीबद्ध\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() विफल.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() विफल.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() विफल.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "आंकड़े पाने में विफल: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "प्रयोग में मुद्रा: %u ब्लॉक %s बाइट कुल समाहित करता है.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "संपूर्ण जीवनचक्र के दौरान आबंटित: %u ब्लॉक %s बाइट कुल को समाहित करता है.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "नमूना कैश आकार: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "सर्वर सूचना पाने में विफल: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"उपयोक्ता नाम: %s\n"
+"मेजबान नाम: %s\n"
+"सर्वर नाम: %s\n"
+"सर्वर संस्करण: %s\n"
+"तयशुदा नमूना विनिर्दिष्टता: %s\n"
+"तयशुदा चैनल मानचित्र: %s\n"
+"तयशुदा सिंक: %s\n"
+"तयशुदा स्रोत: %s\n"
+"कुकी: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "सिंक सूचना पाने में विफल: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPorts:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tActive Port: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tPorts:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "स्रोत सूचना पाने में विफल: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "मॉड्यूल सूचना पाने में विफल: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "क्लाइंट सूचना पाने में विफल: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "कार्ड सूचना पाने में विफल: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfiles:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tActive Profile: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "सिंक इनपुट सूचना पाने में विफल: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "स्रोत आउटपुट सूचना पाने में विफल: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "नमूना सूचना पाने में विफल: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "विफलता: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "स्रोत सूचना पाने में विफल: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "नमूना अफलोड करने में विफल: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "फ़ाइल का असामयिक अंत"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "अवैध सर्वर"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT पाया, निकल रहा है."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "अवैध आयतन विनिर्दिष्टता"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"लिबपल्स %s से कंपाइल\n"
+"लिबपल्स %s से कड़ीबद्ध\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "लोड करने के लिए किसी नमूना फ़ाइल निर्दिष्ट करें"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "ध्वनि फ़ाइल खोलने में विफल."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "%s स्ट्रीम को किसी नमूना विनिर्दिष्टता '%s' के साथ खोल रहा है."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "आपको किसी नमूना नाम को बजाने के लिए निर्दिष्ट करना है"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "आपको किसी नमूना नाम को हटाने के लिए निर्दिष्ट करना है"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "आपको किसी सिंक इनपुट सूची और सिंक को निर्दिष्ट करना है"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "आपको किसी सिंक स्रोत आउटपुट और स्रोत को निर्दिष्ट करना है"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "आपको किसी मॉड्यूल नाम और वितर्क को निर्दिष्ट करना है"
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "आपको किसी मॉड्यूल सूची को निर्दिष्ट करना है"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"आप एक सिंक से अधिक निर्दिष्ट नहीं कर सकते हैं. आपको किसी बुलियन मान को निर्दिष्ट करना है."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"आप एक स्रोत से अधिक निर्दिष्ट नहीं कर सकते हैं. आपको किसी बुलियन मान को निर्दिष्ट करना "
+"है."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "आपको किसी कार्ड नाम/सूची और प्रोफ़ाइल नाम को निर्दिष्ट करना है."
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "आपको किसी कार्ड नाम/सूची और पोर्ट नाम को निर्दिष्ट करना है."
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "आपको किसी स्रोत नाम/सूची और पोर्ट नाम को निर्दिष्ट करना है."
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "आपको किसी सिंक नाम/सूची और वाल्यूम को निर्दिष्ट करना है."
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "आपको किसी स्रोत नाम/सूची और आयतन को निर्दिष्ट करना है."
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "आपने किसी सिंक इनपुट सूची और आयतन को निर्दिष्ट किया है"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "अवैध सिंक इनपुट सूची"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "आपको किसी सिंक स्रोत आउटपुट और स्रोत को निर्दिष्ट करना है"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "अवैध सिंक इनपुट सूची"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "आपको किसी कार्ड नाम/सूची और मूक बुलियन नाम को निर्दिष्ट करना है."
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "अवैध नमूना विनिर्दिष्टता"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "आपको किसी स्रोत नाम/सूची और मूल बुलियन को निर्दिष्ट करना है."
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "आपने किसी सिंक इनपुट सूची और मूल बुलियन को निर्दिष्ट किया है"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "अवैध सिंक इनपुट सूची विनिर्दिष्टता"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "आपको किसी स्रोत नाम/सूची और मूल बुलियन को निर्दिष्ट करना है."
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "अवैध सिंक इनपुट सूची विनिर्दिष्टता"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "आपको किसी कार्ड नाम/सूची और मूक बुलियन नाम को निर्दिष्ट करना है."
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "कोई वैध कमांड निर्दिष्ट नहीं."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "कमांड लाइन के विश्लेषण में असमर्थ.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "सर्वर: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "स्रोत: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "सिंक: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "कुकी: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "कुकी आंकड़ा के विश्लेषण में विफल\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "कुकी आंकड़ा के सहेजने में विफल\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "क्लाइंट विन्यास फ़ाइल लोड करने में विफल\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "वातावरण विन्यास आंकड़ा को पढ़ने में विफल.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN पाने में विफल.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "कुकी आंकड़ा लोड करने में विफल\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "अभी तक कार्यान्वित नहीं.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "कोई पल्सऑडियो डेमॉन चल रहा है, या चयन डेमॉन के तहत चल रहा है."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio डेमॉन को मारने में विफल."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "डेमॉन प्रतिक्रिया नहीं दे रहा है."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "autospawn लॉक की पहुँच नहीं ले सकता है."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA ने युक्ति में नया आंकड़ा लिखने के लिए हमें तैयार किया, लेकिन वहाँ वास्तव में लिखने के लिए "
+"कुछ नहीं था!\n"
+"अधिक संभव है कि यह ALSA ड्राइवर '%s' में एक बग है. कृपया इस मुद्दे को ALSA डेवलेपर को "
+"रिपोर्ट करें.\n"
+"हमें POLLOUT सेट के साथ तैयार किया गया है  -- हालांकि परवर्ती snd_pcm_avail() ने 0 या "
+"दूसरा मान < min_avail दिया."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA ने युक्ति में नया आंकड़ा पढ़ने के लिए हमें तैयार किया, लेकिन वहाँ वास्तव में पढ़ने के लिए कुछ "
+"नहीं था!\n"
+"अधिक संभव है कि यह ALSA ड्राइवर '%s' में एक बग है. कृपया इस मुद्दे को ALSA डेवलेपर को "
+"रिपोर्ट करें.\n"
+"हमें POLLIN सेट के साथ तैयार किया गया है  -- हालांकि परवर्ती snd_pcm_avail() ने 0 या "
+"दूसरा मान < min_avail दिया."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "बंद"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "उच्च विश्वसनीयतायुक्ति प्लेबैक (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "उच्च विश्वसनीयता कैप्चर (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "टेलिफोनी ड्यूप्लेक्स (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "पल्सऑडियो ध्वनि सर्वर"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "आउटपुट युक्ति"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "इनपुट युक्ति"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@ पर ऑडियो"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "इनपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "डॉकिंग स्टेशन इनपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "डॉकिंग स्टेशन माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "डॉकिंग स्टेशन इनपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "लाइन इन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "डॉकिंग स्टेशन माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "बाहरी माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "आंतरिक माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "रेडियो"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "वीडियो"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "स्वचालित प्राप्ति नियंत्रण"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "कोई स्वचालित प्राप्ति नियंत्रण नहीं"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "बूस्ट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "कोई बढ़ावा नहीं"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "एंप्लीफायर"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "कोई एंप्लीफायर नहीं"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "बूस्ट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "कोई बढ़ावा नहीं"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "एनालॉग हेडफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "एनालॉग इनपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "डॉकिंग स्टेशन माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "एनालॉग आउटपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "एनालॉग आउटपुट (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "लाइन इन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "एनालॉग एकल आउटपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "एनालॉग स्टीरियो"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "डिजिटल सेटअप (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "डिजिटल सेटअप (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "एनालॉग मोनो"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "एनालॉग स्टीरियो"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "एनालॉग सर्राउंड 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "एनालॉग सर्राउंड 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "एनालॉग सर्राउंड 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "एनालॉग सर्राउंड 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "एनालॉग सर्राउंड 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "एनालॉग सर्राउंड 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "एनालॉग सर्राउंड 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "एनालॉग सर्राउंड 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "एनालॉग सर्राउंड 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "एनालॉग सर्राउंड 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "एनालॉग सर्राउंड 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "डिजिटल स्टीरियो (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "डिजिटल स्टीरियो (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "डिजिटल सर्राउंड 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "डिजिटल सर्राउंड 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "डिजिटल सेटअप (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "डिजिटल सर्राउंड 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "एनालॉग एकल डुप्लेक्स"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "एनालॉग स्टीरियो डुप्लेक्स"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "डिजिटल स्टीरियो डुप्लेक्स (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "रिक्त आउटपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "इनपुट"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit इस प्लेटफॉर्म पर समर्थित नहीं."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() विफल"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "डिजिटल सर्राउंड 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "निम्न आवृत्ति निकासकर्ता"
diff --git a/po/hr.po b/po/hr.po
new file mode 100644 (file)
index 0000000..3afd720
--- /dev/null
+++ b/po/hr.po
@@ -0,0 +1,3215 @@
+# Croatian translation for pulseaudio
+# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
+# This file is distributed under the same license as the pulseaudio package.
+# gogo <trebelnik2@gmail.com>, 2017.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-04-27 14:44+0200\n"
+"PO-Revision-Date: 2017-04-27 14:48+0200\n"
+"Last-Translator: gogo <trebelnik2@gmail.com>\n"
+"Language-Team: Croatian <hr@li.org>\n"
+"Language: hr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2017-04-20 21:04+0000\n"
+"X-Generator: Poedit 1.8.7.1\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [mogućnosti]\n"
+"\n"
+"NAREDBE:\n"
+"  -h, --help                            Prikaži ovu pomoć\n"
+"      --version                         Prikaži inačicu\n"
+"      --dump-conf                       Ispiši zadano podešavanje\n"
+"      --dump-modules                    Ispiši popis dostupnih modula\n"
+"      --dump-resample-methods           Ispiši dostupne načine normalizacije "
+"zvuka\n"
+"      --cleanup-shm                     Čišćenje dijeljenih segmenata "
+"memorije\n"
+"      --start                           Pokreni pozadinski program ako nije "
+"pokrenut\n"
+"  -k  --kill                            Ubij pokrenuti pozadinski program\n"
+"      --check                           Provjeri pozadinski program (samo "
+"vraća izlazni kôd)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Pokreni na cijelom sustavu\n"
+"  -D, --daemonize[=BOOL]                Pokreni kao pozadinski program nakon "
+"pokretanja\n"
+"      --fail[=BOOL]                     Zatvori kada pokretanje ne uspije\n"
+"      --high-priority[=BOOL]            Pokušaj postaviti najvišu "
+"prihvatljivu razinu\n"
+"                                        (Samo je dostupno korijenskom "
+"korisniku, pri SUID-u ili\n"
+"                                        s povišenim RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Pokušaj omogućiti raspored u "
+"stvarnom vremenu\n"
+"                                        (samo je dostupno korijenskom "
+"korisniku, pri SUID-u ili\n"
+"                                        s povišenim RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Zabrani modulu korisnika zahtijevani "
+"modul\n"
+"                                        učitavanja/uklanjanja nakon "
+"pokretanja\n"
+"      --disallow-exit[=BOOL]            Zabrani zahtjev izlaza korisnika\n"
+"      --exit-idle-time=SEK             Zatvori pozadinski program u "
+"mirovanju i ovaj\n"
+"                                        puta proslijedi\n"
+"      --scache-idle-time=SEK           Ukloni automatski učitane uzorke u "
+"mirovanju i\n"
+"                                        ovaj puta proslijedi\n"
+"      --log-level[=RAZINA]               Povećaj ili postavi razinu "
+"opširnijeg zapisivanja\n"
+"  -v  --verbose                         Povećaj razinu opširnosti\n"
+"      --log-target={auto,syslog,stderr,datoteka:PUTANJA,nova datoteka:"
+"PUTANJA}\n"
+"                                        Određuje mete zapisa\n"
+"      --log-meta[=BOOL]                 Uključi kôd lokacije u poruke "
+"zapisa\n"
+"      --log-time[=BOOL]                 Uključi vremenske oznake u poruke "
+"zapisa\n"
+"      --log-backtrace=OKVIRI            Uključi povratno praćenje u poruke "
+"zapisa\n"
+"  -p, --dl-search-path=PUTANJA             Postavi putanju pretrage za "
+"promjenljivo dijeljene\n"
+"                                        objekte (priključke)\n"
+"      --resample-method=NAČIN          Koristi određeni način normalizacije "
+"zvuka\n"
+"                                        (Pogledaj --dump-resample-methods "
+"za\n"
+"                                        moguće vrijednosti)\n"
+"      --use-pid-file[=BOOL]             Stvori PID datoteku\n"
+"      --no-cpu-limit[=BOOL]             Nemoj instalirati CPU ograničivač "
+"učitavanja na\n"
+"                                        platforme koje ga podržavaju.\n"
+"      --disable-shm[=BOOL]              Onemogući podršku dijeljenja "
+"memorije.\n"
+"      --enable-memfd[=BOOL]             Omogući memfd podršku dijeljenja "
+"memorije.\n"
+"\n"
+"SKRIPTE POKRETANJA:\n"
+"  -L, --load=\"ARGUMENTI MODULA\"         Učitaj određeni priključak sa\n"
+"                                        određenim argumentom\n"
+"  -F, --file=NAZIV DATOTEKE                   Pokreni određenu skriptu\n"
+"  -C                                    Otvori naredbeni redak u pokrenutom "
+"TTY-jem\n"
+"                                        nakon pokretanja\n"
+"\n"
+"  -n                                    Ne učitavaj zadanu datoteku skripte\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "--deamonize očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "--fail očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level očekuje argument na razini zapisa (ili brojeve u rasponu 0..4 "
+"ili jedan od zapisa uklanjanja grešaka,informacija,bilješka,upozorenje,"
+"greška)"
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "--realtime očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Neispravno odredište zapisa: koristite ili 'syslog', 'journal','stderr',  "
+"'auto' ili valjani naziv datoteke 'datoteka:<putanja>', 'nova datoteka:"
+"<putanja>'."
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Neispravno odredište zapisa: koristite ili 'syslog', 'stderr' ili 'auto' ili "
+"valjani naziv datoteke 'datoteka:<putanja>', 'nova-datoteka:<putanja>'."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "--log-time očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Neispravan način normalizacije zvuka '%s'."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "--system očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm očekuje boolean argument"
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr "--enable-memfd očekuje boolean argument"
+
+#: ../src/daemon/daemon-conf.c:262
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] neispravno odredište zapisa '%s'."
+
+#: ../src/daemon/daemon-conf.c:277
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] neispravna razina zapisa '%s'."
+
+#: ../src/daemon/daemon-conf.c:292
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] neispravan način normalizacije zvuka '%s'."
+
+#: ../src/daemon/daemon-conf.c:314
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] neispravan rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:334
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] neispravan format uzorka '%s'."
+
+#: ../src/daemon/daemon-conf.c:351 ../src/daemon/daemon-conf.c:368
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] neispravna frekvencija '%s'."
+
+#: ../src/daemon/daemon-conf.c:391
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] neispravan uzorak kanala '%s'."
+
+#: ../src/daemon/daemon-conf.c:408
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] neispravno mapiranje kanala '%s'."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] neispravan broj fragmenta '%s'."
+
+#: ../src/daemon/daemon-conf.c:442
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] neispravna veličina fragmenta '%s'."
+
+#: ../src/daemon/daemon-conf.c:459
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] neispravna prihvatljiva razina '%s'."
+
+#: ../src/daemon/daemon-conf.c:502
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] neispravna vrsta poslužitelja '%s'."
+
+#: ../src/daemon/daemon-conf.c:620
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Nemoguće otvaranje datoteke podešavanja: %s"
+
+#: ../src/daemon/daemon-conf.c:636
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Određeno zadano mapiranje kanala ima različit broj kanala od određenih "
+"zadanih broja kanala."
+
+#: ../src/daemon/daemon-conf.c:723
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Čitano iz datoteke podešavanja: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Naziv: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Nedostupna informacija modula\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Inačica: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Opis: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Korištenje: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Učitano jednom: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "UPOZORENJE NEODOBRAVANJA: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Putanja: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Neuspjelo otvaranje modula %s: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Neuspjelo traženje izvornog lt_dlopen učitača."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Neuspjela dodijela novog dl učitača."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Neuspjelo dodavanje bind-now-loadera"
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Neuspjelo pronalaženje korisnika '%s'"
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Neuspjelo pronalaženje grupe '%s'"
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID korisnika '%s' i grupe '%s' se ne podudaraju."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Osobna mapa korisnika '%s' nije '%s', zanemarujem."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Neuspjelo stvaranje '%s': %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Neuspjela promjena grupnog popisa: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Neuspjela promjena GID-a: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Neuspjela promjena UID-a: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "Način širom sustava nije podržan na ovoj platformi."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Neuspjela obrada naredbenog redka."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Način rada sustava  je nedostupan za nekorijenske korinike. Samo pokreni D-"
+"Bus poslužitelja usluge pretraživanja."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Nuspjelo ubijanje pozadinskog programa: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Ovaj program nije namijenjen za korijensko pokretanje (osim -- ako je "
+"određeno sustavom)"
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Potrebne su korijenske ovlasti."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "-- start nije podržan za primjerke sustava"
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr "Korisnički podešen poslužitelj na %s, odbija pokretanje/auto-množenje."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Korisnički podešen poslužitelj na %s, izgleda da je lokalan. Dublje "
+"sondiranje."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr "Pokrenuto u načinu rada sustava, ali --disallow-exit nije postavljen."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr ""
+"Pokrenuto u načinu rada sustava, ali --disallow-module-loading nije "
+"postavljen."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr "Pokrenuto u načinu rada sustava, prisilno onemogućavanje SHM načina."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr ""
+"Pokrenuto u načinu rada sustava, prisilno onemogućavanje izlaza vremena "
+"mirovanja."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "Neuspjelo dobivanje stdio."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() neuspjelo: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() neuspjelo: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:562
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() neuspjelo: %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Neuspjelo pokretanje pozadinskog programa."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() neuspjelo: %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Neuspjelo dobivanje ID-a računala"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually "
+"do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"U redu, pokrenuli ste PA u načinu rada sustava. Pobrinite se da to zaista "
+"želite učiniti.\n"
+"Pročitajte http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/"
+"User/WhatIsWrongWithSystemWide/ za objašnjenje zašto je način rada sustava "
+"uobičajeno loša ideja."
+
+#: ../src/daemon/main.c:990
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() neuspjelo."
+
+#: ../src/daemon/main.c:1022
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() neuspjelo"
+
+#: ../src/daemon/main.c:1092
+msgid "Failed to initialize daemon."
+msgstr "Neuspjelo pokretanje pozadinskog programa."
+
+#: ../src/daemon/main.c:1097
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"Pozadinski program je pokrenut bez ijednog učitanog modula, odbija raditi."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio zvučni sustav"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Pokreni PulseAudio zvučni sustav"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Ulaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Ulaz priključne stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Mikrofon priključne stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Ulaz priključne stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Ulaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1696
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Prednji mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Stražnji mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Vanjski mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Unutarnji mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Video"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Automatska kontrola pojačanja"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Bez automatske kontrole pojačanja"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Pojačanje"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Bez pojačanja"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Pojačalo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Bez pojačala"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Pojačanje basa"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Bez pojačanja basa"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1703
+msgid "Speaker"
+msgstr "Zvučnik"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Slušalice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Analogni ulaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Ugrađeni mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Mikrofon sa slušalicama"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Analogni izlaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "(LFE) Efekti niske frekvencije na odvojenom mono izlazu"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Izlaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Analogni mono izlaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Zvučnici"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Digitalni izlaz (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Digitalni ulaz (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digitalni prolaz (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Višekanalni ulaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Višekanalni izlaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Analogni mono"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Analogni stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Višekanalni"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Analogni surround 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Analogni surround 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Analogni surround 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Analogni surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Analogni surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Analogni surround 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Analogni surround 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Analogni surround 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Analogni surround 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Analogni surround 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Analogni surround 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Digitalni stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digitalni prolaz  (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digitalni surround 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digitalni surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Digitalni surround 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Digitalni stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digitalni surround 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4151
+msgid "Analog Mono Duplex"
+msgstr "Analogni mono obostrani"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Stereo Duplex"
+msgstr "Analogni stereo obostrani"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Digitalni stereo obostrani (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Multichannel Duplex"
+msgstr "Višekanalni obostrani"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2324
+#: ../src/modules/bluetooth/module-bluez5-device.c:1951
+msgid "Off"
+msgstr "Isključeno"
+
+#: ../src/modules/alsa/alsa-mixer.c:4254
+#, c-format
+msgid "%s Output"
+msgstr "%s izlaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:4262
+#, c-format
+msgid "%s Input"
+msgstr "%s ulaz"
+
+#: ../src/modules/alsa/alsa-sink.c:572
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nas budi kako bi zapisali nove podatke na uređaj, ali zapravo se nema "
+"ništa za zapisati.\n"
+"To je zacijelo greška u ALSA upravljačkom programu '%s'. Prijavite ovaj "
+"problem ALSA razvijateljima.\n"
+"Probudila nas je POLLOUT postavka -- međutim naknadno snd_pcm_avail() je "
+"vratio 0 ili drugu vrijednost < min_avail."
+
+#: ../src/modules/alsa/alsa-sink.c:756
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nas budi kako bi zapisali nove podatke na uređaj, ali zapravo se nema "
+"ništa za zapisati!\n"
+"To je zacijelo greška u ALSA upravljačkom programu '%s'. Prijavite ovaj "
+"problem ALSA razvijateljima.\n"
+"Probudila nas je POLLOUT postavka -- međutim naknadno snd_pcm_avail() je "
+"vratio 0 ili drugu vrijednost < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nas budi kako bi pročitali nove podatke s uređaja, ali zapravo se nema "
+"ništa za pročitati.\n"
+"To je zacijelo greška u ALSA upravljačkom programu '%s'. Prijavite ovaj "
+"problem ALSA razvijateljima.\n"
+"Probudila nas je POLLIN postavka -- međutim naknadno snd_pcm_avail() je "
+"vratio 0 ili drugu vrijednost < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nas budi kako bi pročitali nove podatke s uređaja, ali zapravo se nema "
+"ništa za pročitati!\n"
+"To je zacijelo greška u ALSA upravljačkom programu '%s'. Prijavite ovaj "
+"problem ALSA razvijateljima.\n"
+"Probudila nas je POLLIN postavka -- međutim naknadno snd_pcm_avail() je "
+"vratio 0 ili drugu vrijednost < min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1168 ../src/modules/alsa/alsa-util.c:1243
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() je vratio vrijednost koja je iznimno velika: %lu bajta (%lu "
+"ms).\n"
+"Najvjerojatnije je ovo greška ALSA upravljačkog programa '%s'. Prijavite "
+"problem ALSA razvijateljima."
+
+#: ../src/modules/alsa/alsa-util.c:1218
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() je vratio vrijednost koja je iznimno velika: %li bajta (%s"
+"%lu ms).\n"
+"Najvjerojatnije je ovo greška ALSA upravljačkog programa '%s'. Prijavite "
+"problem ALSA razvijateljima."
+
+#: ../src/modules/alsa/alsa-util.c:1259
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() je vratio nepoznate vrijednosti: kašnjenje %lu je "
+"manje od %lu.\n"
+"Najvjerojatnije je ovo greška ALSA upravljačkog programa '%s'. Prijavite "
+"problem ALSA razvijateljima."
+
+#: ../src/modules/alsa/alsa-util.c:1302
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() je vratio vrijednost koja je iznimno velika: %lu bajta "
+"(%lu ms).\n"
+"Najvjerojatnije je ovo greška ALSA upravljačkog programa '%s'. Prijavite "
+"problem ALSA razvijateljima."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1686
+msgid "Headset"
+msgstr "Slušalice s mikrofonom"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1691
+msgid "Handsfree"
+msgstr "Bez-ruku"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1709
+msgid "Headphone"
+msgstr "Slušalice"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1714
+msgid "Portable"
+msgstr "Prijenosnik"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1719
+msgid "Car"
+msgstr "Automobil"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1724
+msgid "HiFi"
+msgstr "HiFi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2134
+#: ../src/modules/bluetooth/module-bluez5-device.c:1729
+msgid "Phone"
+msgstr "Telefon"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2142
+#: ../src/modules/bluetooth/module-bluez5-device.c:1681
+#: ../src/modules/bluetooth/module-bluez5-device.c:1697
+#: ../src/modules/bluetooth/module-bluez5-device.c:1735
+msgid "Bluetooth Output"
+msgstr "Bluetooth izlaz"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2145
+#: ../src/modules/bluetooth/module-bluez5-device.c:1680
+#: ../src/modules/bluetooth/module-bluez5-device.c:1702
+#: ../src/modules/bluetooth/module-bluez5-device.c:1708
+#: ../src/modules/bluetooth/module-bluez5-device.c:1734
+msgid "Bluetooth Input"
+msgstr "Bluetooth ulaz"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2186
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Reprodukcija visoke autentičnosti (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2198
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Snimanje visoke autentičnosti (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2210
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Obostrana telefonija (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2223
+msgid "Handsfree Gateway"
+msgstr "Bezručni pristupnik"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1776
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Reprodukcija visoke autentičnosti (A2DP slivnik)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1788
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Snimanje visoke autentičnosti (A2DP izvor)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1800
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Jedinica slušalice s mikrofonom (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1813
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Zvučni pristupnik slušalice s mikrofonom (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<naziv izvora> source_properties=<svojstva izvora> "
+"source_master=<naziv filtera izvora> sink_name=<naziv slivnika> "
+"sink_properties=<svojstva slivnika> sink_master=<naziv filtera slivnika> "
+"adjust_time=<koliko često prilagoditi frekvenciju u sek.> "
+"adjust_threshold=<koliko pomaknuti za naknadnu prilagodbu u msec.> "
+"format=<format uzorka> rate=<frekvencija> channels=<broj kanala> "
+"channel_map=<ulazno mapiranje kanala> aec_method=<implementacija za "
+"upotrebu> aec_args=<parameteri za AEC pogon> save_aec=<spremi AEC podatke u /"
+"tmp> autoloaded=<postavi ako se ovaj modul učitava automatski> "
+"use_volume_sharing=<da ili ne> use_master_format=<da ili ne> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "Uključi"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Lažni izlaz"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Uvijek drži bar jedan slivnik učitan čak iako se ne koristi"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Ekvalizator opće namjene"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<naziv slivnika> sink_properties=<svojstva slivnika> "
+"sink_master=<slivnik za povezivanje s> format=<format uzorka> "
+"rate=<frekvencija> channels=<broj kanala> channel_map=<mapiranje kanala> "
+"autoloaded=<postavi ako se ovaj modul učitava automatski> "
+"use_volume_sharing=<da ili ne> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<automatski uklanja nekorištene filtere?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Virtualni LADSPA slivnik"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<input channel map> plugin=<ladspa plugin name> label=<ladspa "
+"plugin label> control=<comma separated list of input control values> "
+"input_ladspaport_map=<comma separated list of input LADSPA port names> "
+"output_ladspaport_map=<comma separated list of output LADSPA port names> "
+"autoloaded=<set if this module is being loaded automatically> "
+msgstr ""
+"sink_name=<naziv slivnika> sink_properties=<svojstva slivnika> master=<naziv "
+"filtera slivnika> sink_master=<naziv slivnika za filtriranje> format=<format "
+"uzorka> rate=<frekvencija> channels=<broj kanala> channel_map=<ulazno "
+"mapiranje kanala> plugin=<naziv ladspa priključka> label=<oznaka ladspa "
+"priključka> control=<zarezom odvojeni popis vrijednosti ulaznog upravljanja> "
+"input_ladspaport_map=<zarezom odvojeni popis naziva LADSPA ulaza> "
+"output_ladspaport_map=<zarezom odvojeni popis naziva LADSPA izlaza> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Prikriven nekorišten slivnik"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Nema izlaza"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Izlazni uređaji"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Ulazni uređaji"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Zvuk na @NAZIVRAČUNALA@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Tunel za %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:521
+#: ../src/modules/module-tunnel-source-new.c:520
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Tunel do %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Virtalni surround slivnik"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<channel map> use_volume_sharing=<yes or no> "
+"force_flat_volume=<yes or no> hrir=/path/to/left_hrir.wav autoloaded=<set if "
+"this module is being loaded automatically> "
+msgstr ""
+"sink_name=<naziv slivnika> sink_properties=<svojstva slivnika> master=<naziv "
+"filtera slivnika> sink_master=<naziv slivnika za filtriranje> format=<format "
+"uzorka> rate=<frekvencija> channels=<broj kanala> channel_map=<ulazno "
+"mapiranje kanala> use_volume_sharing=<da ili ne> force_flat_volume=<da ili "
+"ne> hrir=/putanja/do/left_hrir.wav autoloaded=<postavi ako se ovaj modul "
+"učitava automatski> "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio zvučni poslužitelj"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Prednji srednji"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Prednji lijevi"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Prednji desni"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Zadnji srednji"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Zadnji lijevi"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Zadnji desni"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Dubokotonac"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Prednji lijevo-od-srednjeg"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Prednji desno-od-srednjeg"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Srednji lijevi"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Srednji desni"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Pomoćni 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Pomoćni 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Pomoćni 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Pomoćni 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Pomoćni 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Pomoćni 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Pomoćni 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Pomoćni 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Pomoćni 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Pomoćni 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Pomoćni 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Pomoćni 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Pomoćni 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Pomoćni 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Pomoćni 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Pomoćni 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Pomoćni 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Pomoćni 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Pomoćni 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Pomoćni 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Pomoćni 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Pomoćni 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Pomoćni 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Pomoćni 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Pomoćni 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Pomoćni 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Pomoćni 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Pomoćni 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Pomoćni 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Pomoćni 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Pomoćni 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Pomoćni 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Gornji srednji"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Gornji prednji srednji"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Gornji prednji lijevi"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Gornji prednji desni"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Gornji zadnji srednji"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Gornji zadnji lijevi"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Gornji zadnji desni"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:177 ../src/pulse/volume.c:294
+#: ../src/pulse/volume.c:320 ../src/pulse/volume.c:340
+#: ../src/pulse/volume.c:372 ../src/pulse/volume.c:412
+#: ../src/pulse/volume.c:431
+msgid "(invalid)"
+msgstr "(neispravno)"
+
+#: ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() neuspjelo"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() vratilo je 'true'"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Neuspjela obrada podataka kolačića"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Primljena poruka za nepoznato proširenje '%s'"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "ulaz"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "izlaz"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "dvosmjerno"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "neispravno"
+
+#: ../src/pulsecore/core-util.c:1856
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"XDG_RUNTIME_DIR (%s) nije u našem vlasništvu (uid %d), nego u uid %d! (To se "
+"npr. može dogoditi ako se pokušate povezati na nekorijenski PulseAudio kao "
+"korijenski korisnik, preko izvornog protokola. Ne činite to.)"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "da"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "ne"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Nemoguć pristup zaključavanju auto-množenja."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Nemoguće otvaranje odredišne datoteke '%s'."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Pokušaj otvaranja odredišne datoteke '%s', '%s.1', '%s.2' ... '%s.%d', ali "
+"ništa nije uspjelo."
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Neispravno odredište zapisa."
+
+#: ../src/pulsecore/sink.c:3457
+msgid "Built-in Audio"
+msgstr "Ugrađeni zvuk"
+
+#: ../src/pulsecore/sink.c:3462
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "U redu"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Pristup odbijen"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Nepoznata naredba"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Neispravan argument"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "Unos postoji"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Nepostojeći unos"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Povezivanje odbijeno"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Greška protokola"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Istek vremena"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Nema ključa ovjere"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Unutarnja greška"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Povezivanje prekinuto"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Entitet ubijen"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Neispravan poslužitelj"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Neuspjelo pokretanje modula"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Loše stanje"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Nema podataka"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Nekompatibilna inačica protokola"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Preveliko"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Nije podržano"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Nepoznat kôd greške"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Nema takvog proširenja"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Zastarjela funkcionalnost"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Implementacija nedostaje"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Klijent odvojen"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Izlazno/Ulazna greška"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Uređaj ili resurs zauzet"
+
+#: ../src/pulse/sample.c:179
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:197
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:134
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Neuspjeli istek toka: %s"
+
+#: ../src/utils/pacat.c:139
+msgid "Playback stream drained."
+msgstr "Reprodukcija toka istekla."
+
+#: ../src/utils/pacat.c:150
+msgid "Draining connection to server."
+msgstr "Istek povezivanja s poslužiteljem."
+
+#: ../src/utils/pacat.c:163
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:194 ../src/utils/pacat.c:543
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() neuspjelo: %s"
+
+#: ../src/utils/pacat.c:244 ../src/utils/pacat.c:274
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() neuspjelo: %s"
+
+#: ../src/utils/pacat.c:324
+msgid "Stream successfully created."
+msgstr "Tok uspješno stvoren"
+
+#: ../src/utils/pacat.c:327
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() neuspjelo: %s"
+
+#: ../src/utils/pacat.c:331
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr ""
+"Mjerenja međuspremnika: najveća-duljina=%u, ukupna-duljina=%u, "
+"predmeđuspremnik=%u, najmanji-zahtjev=%u"
+
+#: ../src/utils/pacat.c:334
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Mjerenja međuspremnika: najveća-duljina=%u, veličina-fragmenta=%u"
+
+#: ../src/utils/pacat.c:338
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Koriste se specifikacije uzorka '%s', mapiranje kanala '%s'."
+
+#: ../src/utils/pacat.c:342
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Povezano s uređajem %s (sadržaj: %u, suspendirano: %s)."
+
+#: ../src/utils/pacat.c:352
+#, c-format
+msgid "Stream error: %s"
+msgstr "Greška toka: %s"
+
+#: ../src/utils/pacat.c:362
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Uređaj toka suspendiran.%s"
+
+#: ../src/utils/pacat.c:364
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Uređaj toka ponovno pokrenut.%s"
+
+#: ../src/utils/pacat.c:372
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Tok neiskorišten.%s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Tok zauzet.%s"
+
+#: ../src/utils/pacat.c:386
+#, c-format
+msgid "Stream started.%s"
+msgstr "Tok pokrenut.%s"
+
+#: ../src/utils/pacat.c:393
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Tok premješten na uređaj %s (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:393
+msgid "not "
+msgstr "nije "
+
+#: ../src/utils/pacat.c:400
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Svojstva međuspremnika toka promijenjena.%s"
+
+#: ../src/utils/pacat.c:415
+msgid "Cork request stack is empty: corking stream"
+msgstr "Spremište zahtjeva zatvaranja je prazno: zatvaranje toka"
+
+#: ../src/utils/pacat.c:421
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Spremište zahtjeva zatvaranja je prazno: otvaranje toka"
+
+#: ../src/utils/pacat.c:425
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr ""
+"Upozorenje: primljeno je više zahtjeva za otvaranje od zahtjeva zatvaranja."
+
+#: ../src/utils/pacat.c:450
+#, c-format
+msgid "Connection established.%s"
+msgstr "Povezivanje uspostavljeno.%s"
+
+#: ../src/utils/pacat.c:453
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() neuspjelo: %s"
+
+#: ../src/utils/pacat.c:491
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() neuspjelo: %s"
+
+#: ../src/utils/pacat.c:497
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Neuspjelo postavljanje nadgeldanja toka: %s"
+
+#: ../src/utils/pacat.c:501
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() neuspjelo: %s"
+
+#: ../src/utils/pacat.c:514 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Povezivanje neuspjelo: %s"
+
+#: ../src/utils/pacat.c:557
+msgid "Got EOF."
+msgstr "EOF."
+
+#: ../src/utils/pacat.c:581
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() neuspjelo: %s"
+
+#: ../src/utils/pacat.c:605
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() neuspjelo: %s"
+
+#: ../src/utils/pacat.c:626
+msgid "Got signal, exiting."
+msgstr "Signal dobiven, izlazim."
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Neuspjelo dobivanje latencije: %s"
+
+#: ../src/utils/pacat.c:645
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Vrijeme: %0.3f sek; Latencija: %0.0f usek."
+
+#: ../src/utils/pacat.c:666
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() neuspjelo: %s"
+
+#: ../src/utils/pacat.c:676
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [mogućnosti]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Prikazuje ovu pomoć\n"
+"      --version                         Prikazuje inačicu\n"
+"\n"
+"  -r, --record                          Stvara povezivanje za snimanje\n"
+"  -p, --playback                        Stvara povezivanje za reprodukciju\n"
+"\n"
+"  -v, --verbose                         Omogućuje opširnije radnje\n"
+"\n"
+"  -s, --server=POSLUŽITELJ                   Naziv poslužitelja za "
+"povezivanje\n"
+"  -d, --device=UREĐAJ                   Naziv slivnika/izvora za "
+"povezivanje\n"
+"  -n, --client-name=NAZIV                Naziv klijenta na poslužitelju\n"
+"      --stream-name=NAZIV                Naziv toka na poslužitelju\n"
+"      --volume=GLASNOĆA ZVUKA                   Određuje početnu (linearnu) "
+"glasnoću zvuka u rasponu 0...65536\n"
+"      --rate=FREKVENCIJA                 Frekvencija u Hz (zadano 44100)\n"
+"      --format=FORMAT UZORKA             Vrsta uzorka, jedna od s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (zadano s16ne)\n"
+"      --channels=KANALI               Broj kanala, 1 za mono, 2 za stereo\n"
+"                                        (zadano 2)\n"
+"      --channel-map=MAPIRANJE KANALA          Korištenje mapiranja kanala "
+"umjesto zadanog\n"
+"      --fix-format                      Uzima uzorak formata iz slivnika/"
+"izvora s kojim\n"
+"                                        je tok povezan.\n"
+"      --fix-rate                        Uzima frekvenciju iz slivnika/izvora "
+"s kojim\n"
+"                                        je tok povezan.\n"
+"      --fix-channels                    Uzima broj kanala i mapiranje "
+"kanala\n"
+"                                        iz slivnika/izvora s kojim je tok "
+"povezan.\n"
+"      --no-remix                        Ne smanjuj ili pojačavaj kanale.\n"
+"      --no-remap                        Mapiraj kanale prema sadržaju "
+"umjesto nazivu.\n"
+"      --latency=BAJTOVI                   Zahtijevaj određenu latenciju u "
+"bajtima.\n"
+"      --process-time=BAJTOVI              Zahtijevaj određeno vrijeme "
+"procesa po zahtjevu u bajtima.\n"
+"      --latency-msec=MSEK               Zahtijevaj određenu latenciju u "
+"msek.\n"
+"      --process-time-msec=MSEK          Zahtijevaj određeno vrijeme procesa "
+"po zahtjevu u msek.\n"
+"      --property=VLASNIŠTVO=VRIJEDNOST         Postavi određeno vlasništvo "
+"za određenu vrijednost.\n"
+"      --raw                             Snimaj/Reproduciraj osnovne PCM "
+"podatke.\n"
+"      --passthrough                     Prolaz podataka.\n"
+"      --file-format[=FFORMAT]           Snimaj/Reproduciraj formatirane PCM "
+"podatke.\n"
+"      --list-file-formats               Prikaži dostupne formate datoteka.\n"
+"      --monitor-stream=SADRŽAJ            Snimaj s ulaza slivnika sa "
+"sadržajem SADRŽAJ.\n"
+
+#: ../src/utils/pacat.c:793
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr ""
+"Reproduciraj enkôdirane zvučne datoteke na PulseAudio zvučnom poslužitelju."
+
+#: ../src/utils/pacat.c:797
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr ""
+"Snimi zvučne podatke s PulseAudio zvučnog poslužitelja i spremi ih u "
+"datoteku."
+
+#: ../src/utils/pacat.c:801
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+"Snimi zvučne podatke s PulseAudio zvučnog poslužitelja i spremi ih u STDOUT "
+"ili određenu datoteku."
+
+#: ../src/utils/pacat.c:805
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+"Reproduciraj zvučne datoteke sa STDIN-a ili određene datoteke na PulseAudio "
+"zvučnom poslužitelju."
+
+#: ../src/utils/pacat.c:819
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Kompiliran s libpulse %s\n"
+"Povezan s libpulse %s\n"
+
+#: ../src/utils/pacat.c:852 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Neispravan naziv klijenta '%s'"
+
+#: ../src/utils/pacat.c:867
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Neispravan naziv toka '%s'"
+
+#: ../src/utils/pacat.c:904
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Neispravno mapiranje kanala '%s'"
+
+#: ../src/utils/pacat.c:933 ../src/utils/pacat.c:947
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Pogrešan opis latencije '%s'"
+
+#: ../src/utils/pacat.c:940 ../src/utils/pacat.c:954
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Neispravno vrijeme obrade opisa '%s'"
+
+#: ../src/utils/pacat.c:966
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Nevaljano vlasništvo '%s'"
+
+#: ../src/utils/pacat.c:985
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Nepoznata vrsta datoteke %s."
+
+#: ../src/utils/pacat.c:1000
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Neuspjela obrada argumenta za --monitor-stream"
+
+#: ../src/utils/pacat.c:1011
+msgid "Invalid sample specification"
+msgstr "Nepoznat opis uzorka"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1026
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1033
+msgid "Too many arguments."
+msgstr "Previše argumenata."
+
+#: ../src/utils/pacat.c:1044
+msgid "Failed to generate sample specification for file."
+msgstr "Neuspjelo generiranje opisa uzorka za datoteku."
+
+#: ../src/utils/pacat.c:1070
+msgid "Failed to open audio file."
+msgstr "Neuspjelo otvaranje zvučne datoteke."
+
+#: ../src/utils/pacat.c:1076
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Upozorenje: određeni opis uzorka biti će prepisan s opisom iz datoteke."
+
+#: ../src/utils/pacat.c:1079 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Neuspjelo određivanje opisa uzorka iz datoteke."
+
+#: ../src/utils/pacat.c:1088
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Upozorenje: neuspjelo određivanje mapiranja kanala iz datoteke."
+
+#: ../src/utils/pacat.c:1099
+msgid "Channel map doesn't match sample specification"
+msgstr "Mapiranje kanala se ne podudara s opisom uzorka"
+
+#: ../src/utils/pacat.c:1110
+msgid "Warning: failed to write channel map to file."
+msgstr "Upozorenje: neuspjelo zapisivanje mapiranja kanala u datoteku."
+
+#: ../src/utils/pacat.c:1125
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "Otvaranje %s toka s opisom uzorka '%s' i mapiranja kanala '%s'."
+
+#: ../src/utils/pacat.c:1126
+msgid "recording"
+msgstr "snimanje"
+
+#: ../src/utils/pacat.c:1126
+msgid "playback"
+msgstr "reprodukcija"
+
+#: ../src/utils/pacat.c:1150
+msgid "Failed to set media name."
+msgstr "Neuspjelo postavljanje medijskog naziva."
+
+#: ../src/utils/pacat.c:1160 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() neuspjelo."
+
+#: ../src/utils/pacat.c:1183
+msgid "io_new() failed."
+msgstr "io_new() neuspjelo."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() neuspjelo."
+
+#: ../src/utils/pacat.c:1198 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() neuspjelo: %s"
+
+#: ../src/utils/pacat.c:1204
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() neuspjelo."
+
+#: ../src/utils/pacat.c:1211 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() neuspjelo."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "NAZIV [ARGU ...]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "NAZIV|#N"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "NAZIV"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "NAZIV|#N UREĐAJ"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N UREĐAJ"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "NAZIV|#N 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "NAZIV|#N TIPKA=VRIJEDNOST"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#N TIPKA=VRIJEDNOST"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "NAZIV SLIVNIKA|#N"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "NAZIV NAZIV DATOTEKE"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "NAZIV PUTANJE"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "NAZIV DATOTEKE SLIVNIKA|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#N SLIVNIK|IZVOR"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "PROFIL KARTICE"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "NAZIV|#N ULAZ"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "NAZIV-KARTICE|KARTICA-#N POMAK ULAZA"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "ODREDIŠTE"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "BROJČANA-RAZINA"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "OKVIRI"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Prikaži ovu pomoć\n"
+"      --version                         Prikaži pomoć\n"
+"Kada nema zadanih naredbi pacmd se pokreće u interaktivnom načinu.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Kompilirano s libpulse %s\n"
+"Povezano s libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Nema PulseAudio pozadinskog programa pokrenutog, ili nije pokrenut kao "
+"pozadinski program sesije."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Neuspjelo ubijanje PulseAudio pozadinskog programa."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Pozadinski program ne regira."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Neuspjelo dobivanje statistike: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Trenutno u upotrebi: %u blokova sadrži %s ukupno bajta.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Dodijeljeno tijekom cijelog vijeka trajanja: %u blokova sadrži %s ukupno "
+"bajta.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Veličina predmemorije uzorka: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Neuspjelo dobivanje informacija poslužitelja: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Niz poslužitelja: %s\n"
+"Inačica biblioteke protokola: %u\n"
+"Inačica protokola poslužitelja: %u\n"
+"Je lokalan: %s\n"
+"Sadržaj klijenta: %u\n"
+"Veličina pločice: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Ime korisnika: %s\n"
+"Naziv računala: %s\n"
+"Naziv poslužitelja: %s\n"
+"Inačica poslužitelja: %s\n"
+"Zadane specifikacije uzorka: %s\n"
+"Zadano mapiranje kanala: %s\n"
+"Zadani slivnik: %s\n"
+"Zadani  izvor: %s\n"
+"Kolačić: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Neuspjelo dobivanje informacija slivnika: %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Slivnik #%u\n"
+"\tStanje: %s\n"
+"\tNaziv: %s\n"
+"\tOpis: %s\n"
+"\tUpravljački program: %s\n"
+"\tSpecifikacije uzorka: %s\n"
+"\tMapiranje kanala: %s\n"
+"\tVlasnik modula: %u\n"
+"\tUtišano: %s\n"
+"\tGlasnoća zvuka: %s\n"
+"\t        uravnoteženje %0.2f\n"
+"\tIzvorna glasnoća zvuka: %s\n"
+"\tNadgledanje izvora: %s\n"
+"\tLatencija: %0.0f usek, podešeno %0.0f usek\n"
+"\tOznake: %s%s%s%s%s%s%s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tUlazi:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tAktivni ulaz: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tVrste:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Neuspjelo dobivanje informacija izvora: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Izvor #%u\n"
+"\tStanje: %s\n"
+"\tNaziv: %s\n"
+"\tOpis: %s\n"
+"\tUpravljački program: %s\n"
+"\tSpecifikacije uzorka: %s\n"
+"\tMapiranje kanala: %s\n"
+"\tVlasnik modula: %u\n"
+"\tUtišano: %s\n"
+"\tGlasnoća zvuka: %s\n"
+"\t        uravnoteženje %0.2f\n"
+"\tIzvorna glasnoća zvuka: %s\n"
+"\tNadgledanje slivnika: %s\n"
+"\tLatencija: %0.0f usek, podešeno %0.0f usek\n"
+"\tOznake: %s%s%s%s%s%s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "nedostupno"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Neuspjelo dobivanje informacija modula: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modul #%u\n"
+"\tNaziv: %s\n"
+"\tArgument: %s\n"
+"\tBrojač korištenja: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Neuspjelo dobivanje informacija klijenta: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Klijent #%u\n"
+"\tUpravljački program: %s\n"
+"\tVlasnik modula: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Neuspjelo dobivanje informacija kartice: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Kartice #%u\n"
+"\tNaziv: %s\n"
+"\tUpravljački program: %s\n"
+"\tVlasnik modula: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfili:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr "\t\t%s: %s (slivnici: %u, izvori: %u, prioritet: %u, dostupno: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tAktivni profil: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tSvojstva:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tDio profila: %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Neuspjelo dobivanje informacija ulaza slivnika: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Ulaz slivnika #%u\n"
+"\tUpravljački program: %s\n"
+"\tVlasnik modula: %s\n"
+"\tKlijent: %s\n"
+"\tSlivnik: %u\n"
+"\tSpecifikacije uzorka: %s\n"
+"\tMapiranje kanala: %s\n"
+"\tFormat: %s\n"
+"\tZatvoreno: %s\n"
+"\tUtišano: %s\n"
+"\tGlasnoća zvuka: %s\n"
+"\t        uravnoteženje %0.2f\n"
+"\tLatencija međuspremnika: %0.0f usek\n"
+"\tLatencija slivnika: %0.0f usek\n"
+"\tNačin normalizacije zvuka: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Neuspjelo dobivanje informacija izvora izlaza: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Izlaz izvora #%u\n"
+"\tUpravljački program: %s\n"
+"\tVlasnik modula: %s\n"
+"\tKlijent: %s\n"
+"\tSlivnik: %u\n"
+"\tSpecifikacije uzorka: %s\n"
+"\tMapiranje kanala: %s\n"
+"\tFormat: %s\n"
+"\tZatvoreno: %s\n"
+"\tUtišano: %s\n"
+"\tGlasnoća zvuka: %s\n"
+"\t        uravnoteženje %0.2f\n"
+"\tLatencija međuspremnika: %0.0f usek\n"
+"\tLatencija izlaza: %0.0f usek\n"
+"\tNačin normalizacije zvuka: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Neuspjelo dobivanje informacija uzorka: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Uzorak #%u\n"
+"\tNaziv: %s\n"
+"\tSpecifikacije uzorka: %s\n"
+"\tMapiranje kanala: %s\n"
+"\tGlasnoća zvuka: %s\n"
+"\t        uravnoteženje %0.2f\n"
+"\tTrajanje: %0.1fs\n"
+"\tVeličina: %s\n"
+"\tNespremno: %s\n"
+"\tNaziv datoteke: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Neuspjeh: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Neuspjelo uklanjanje modula: Modul %s nije učitan"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Neuspjelo postavljanje glasnoće zvuka: pokušali ste postaviti glasnoću zvuka "
+"za %d kanale, dok su kanal/i podržani = %d\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Neuspjelo postavljanje formata: neispravni znak formata %s"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Neuspjelo učitavanje uzorka: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Prerani kraj datoteke"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "novi"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "promijeni"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "ukloni"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "nepoznat"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "slivnik"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "izvor"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "slivnik-izlaz"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "izvor-izlaz"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "modul"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "klijent"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "uzorak-predmemorija"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "poslužitelj"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "kartica"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Događaj '%s' na %s #%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT, izlazim."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Neispravan opis glasoće zvuka"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Glasnoća zvuka je izvan dopuštenog raspona.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "Pogrešan broj specifikacija glasnoće zvuka.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "Nedosljedna specifikacija glasnoće zvuka.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[mogućnosti]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[VRSTA]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "NAZIV-DATOTEKE [NAZIV]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "NAZIV [SLIVNIK]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "NAZIV|#N GLASNOĆA ZVUKA [GLASNOĆA ZVUKA ...]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#N GLASNOĆA ZVUKA [GLASNOĆA ZVUKA ...]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "NAZIV|#N 1|0|prebacivanje"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|prebacivanje"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "#N FORMATI"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Posebni nazivi @DEFAULT_SINK@, @DEFAULT_SOURCE@ i @DEFAULT_MONITOR@\n"
+"mogu se koristiti za određivanje zadanog slivnika, izvora i nadgledanja.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Prikazuje ovu pomoć\n"
+"      --version                         Prikazuje inačicu\n"
+"\n"
+"  -s, --server=SERVER                   Naziv poslužitelja za povezivanje\n"
+"  -n, --client-name=NAME                Kako nazvati ovaj klijent na "
+"poslužitelju\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Kompilirano s libpulse %s\n"
+"Povezano s libpulse %s\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Ne određuj ništa, ili jedan od: %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Odredi datoteku uzorka za učitavanje"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Neuspjelo otvaranje datoteke zvuka."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Upozorenje: neuspjelo otkrivanje specifikacija uzorka iz datoteke"
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Morate odrediti naziv uzorka za reprodukciju"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Morate odrediti naziv uzorka za uklanjanje"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "Morate odrediti sadržaj ulaza slivnika i slivnik"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "Morate odrediti sadržaj izlaza izvora i izvor"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Morate odrediti naziv modula i argumente"
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "Morate odrediti sadržaj modula ili naziv"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Možda niste odredili više od jednog slivnika. Morate odrediti boolean "
+"vrijednosti"
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Neispravna specifikacija suspendiranja."
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Možda niste odredili više od jednog izvora. Morate odrediti boolean "
+"vrijednosti"
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Morate odrediti naziv kartice/sadržaj i naziv profila"
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Morate odrediti naziv slivnika/sadržaj i naziv ulaza"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "Morate odrediti naziv slivnika"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "Morate odrediti naziv izvora/sadržaj i naziv ulaza"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Morate odrediti naziv izvora"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Morate odrediti naziv slivnika/sadržaj i glasnoću zvuka"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Morate odrediti naziv izvora/sadržaj i glasnoću zvuka"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "Morate odrediti sadržaj ulaza slivnika i glasnoću zvuka"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "Neispravan sadržaj ulaza slivnika"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "Morate odrediti sadržaj izlaza izvora i glasanoću zvuka"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Neispravan sadržaj izlaza izvora"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Morate odrediti naziv slivnika/sadržaj i radnju utišavanja (0, 1, ili "
+"'prebacivanje')"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Neispravne specifikacije utišavanja"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Morate odrediti naziv izvora/sadržaj i radnju utišavanja (0, 1, ili "
+"'prebacivanje')"
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Morate odrediti naziv sadržaj ulaza slivnika i radnju utišavanja (0, 1, ili "
+"'prebacivanje')"
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "Neispravna specifikacija sadržaja ulaza slivnika"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+"Morate odrediti sadržaj izlaza izvora i radnju utišavanja (0, 1, ili "
+"'prebacivanje')"
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Neispravna specifikacija sadržaja izlaza izvora"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Morate odrediti sadržaj slivnika i popis podržanih formata odvojenih točka-"
+"zarezom"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr "Morate odrediti naziv kartice/sadržaj, naziv ulaza i pomak latencije"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Nemoguća obrada pomaka latencije"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Nema određenih valjanih naredbi."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Neuspjelo ponovno pokretanje: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Neuspjela suspenzija: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "UPOZORENJE: zvučni poslužitelj nije lokalan, ne suspendira se.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Povezivanje neuspjelo: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT, izlazim.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "WARNING: podređeni proces je završen sa signalom %u\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [mogućnosti] ... \n"
+"\n"
+"  -h, --help                            Prikazuje ovu pomoć\n"
+"      --version                         Prikazuje inačicu\n"
+"  -s, --server=SERVER                   Naziv poslužitelja za povezivanje\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Kompilirano s libpulse %s\n"
+"Povezano s libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() neuspjelo.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() neuspjelo.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() neuspjelo.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D prikaz] [-S poslužitelj] [-O slivnik] [-I izvor] [-c datoteka]  [-d|-"
+"e|-i|-r]\n"
+"\n"
+" -d    Prikazuje trenutne PulseAudio podatke povezane s X11 zaslonom "
+"(zadano)\n"
+" -e    Izvezi lokalne PulseAudio podatke na X11 zaslon\n"
+" -i    Uvezi PulseAudio podatke s X11 zaslona u varijable lokalnog okruženja "
+"i datoteku kolačića.\n"
+" -r    Ukloni PulseAudio podatke s X11 zaslona\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Neuspjela obrada naredbenog redka.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Poslužitelj: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Izvor: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Slivnik: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Kolačić: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Neuspjela obrada podataka kolačića\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Neuspjelo spremanje podataka kolačića\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Neuspjelo FQDN dobivanje.\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Neuspjelo učitavanje podataka kolačića\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Još nije implementirano.\n"
+
+#~ msgid "wants to record audio."
+#~ msgstr "želi snimiti zvuk."
+
+#~ msgid ""
+#~ "OK, so you are running PA in system mode. Please note that you most "
+#~ "likely shouldn't be doing that.\n"
+#~ "If you do it nonetheless then it's your own fault if things don't work as "
+#~ "expected.\n"
+#~ "Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+#~ "Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why "
+#~ "system mode is usually a bad idea."
+#~ msgstr ""
+#~ "U redu, PA je pokrenut u načinu rada sustava. Zapamtite kako to uglavnom "
+#~ "ne biste trebali raditi.\n"
+#~ "Ako ste ipak to učinili, vi ste odgovorni ako stvari ne rade kako bi se "
+#~ "od njih očekivalo.\n"
+#~ "Pročitajte http://www.freedesktop.org/wiki/Software/PulseAudio/"
+#~ "Documentation/User/WhatIsWrongWithSystemWide/ za objašnjenje zašto je "
+#~ "način rada sustava uobičajeno loša ideja."
diff --git a/po/hu.po b/po/hu.po
new file mode 100644 (file)
index 0000000..bcb39d8
--- /dev/null
+++ b/po/hu.po
@@ -0,0 +1,3219 @@
+# Hungarian translation of PulseAudio
+# Copyright (C) 2012, 2016. Free Software Foundation, Inc.
+# This file is distributed under the same license as the PulseAudio package.
+#
+# KAMI <kami911@gmail.com>, 2012.
+# Gabor Kelemen <kelemeng at ubuntu dot com>, 2016.
+# Balázs Úr <urbalazs at gmail dot com>, 2016.
+msgid ""
+msgstr ""
+"Project-Id-Version: PulseAudio master\n"
+"Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
+"product=PulseAudio&keywords=I18N+L10N&component=misc\n"
+"POT-Creation-Date: 2016-08-23 02:28+0000\n"
+"PO-Revision-Date: 2016-08-23 12:10+0200\n"
+"Last-Translator: Balázs Úr <urbalazs@gmail.com>\n"
+"Language-Team: Hungarian <openscope at googlegroups dot com>\n"
+"Language: hu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Hungarian\n"
+"X-Poedit-Country: HUNGARY\n"
+"X-Poedit-SourceCharset: utf-8\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 1.2\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [kapcsolók]\n"
+"\n"
+"PARANCSOK:\n"
+"  -h, --help                            Ezen súgó megjelenítése\n"
+"      --version                         Verzió megjelenítése\n"
+"      --dump-conf                       Alapértelmezett beállítások kiírása\n"
+"      --dump-modules                    Elérhető modulok listájának kiírása\n"
+"      --dump-resample-methods           Elérhető újramintavételezési módok\n"
+"                                          kiírása\n"
+"      --cleanup-shm                     Árva megosztott memóriaszegmensek\n"
+"                                          tisztítása\n"
+"      --start                           A démon elindítása, ha nem fut\n"
+"  -k  --kill                            Futó démon kilövése\n"
+"      --check                           Futó démon keresése (csak "
+"visszatérési\n"
+"                                          kódot ad)\n"
+"\n"
+"KAPCSOLÓK:\n"
+"      --system[=LOGIKAI]                Futtatás rendszerszintű példányként\n"
+"  -D, --daemonize[=LOGIKAI]             Indítás után démon módba váltás\n"
+"      --fail[=LOGIKAI]                  Kilépés, ha az indítás sikertelen\n"
+"      --high-priority[=LOGIKAI]         Kísérlet magas nice szint "
+"beállítására\n"
+"                                          (csak rootként, SUID-ként vagy\n"
+"                                          emelt RLIMIT_NICE esetén)\n"
+"      --realtime[=LOGIKAI]              Kísérlet valós idejű ütemezés\n"
+"                                          bekapcsolására (csak rootként,\n"
+"                                          SUID-ként vagy emelt "
+"RLIMIT_RTPRIO\n"
+"                                          esetén)\n"
+"      --disallow-module-loading[=LOGIKAI]  Felhasználó által kért "
+"modulbetöltés/\n"
+"                                           -eltávolítás tiltása indítás "
+"után\n"
+"      --disallow-exit[=LOGIKAI]         Felhasználó által kért kilépés "
+"tiltása\n"
+"      --exit-idle-time=MP               A démon befejeztetése, ha "
+"üresjáratban\n"
+"                                          eltelt ennyi idő\n"
+"      --scache-idle-time=MP             Automatikusan betöltött minták\n"
+"                                          eltávolítása, ha üresjáratban "
+"eltelt\n"
+"                                          ennyi idő\n"
+"      --log-level[=SZINT]               Részletességi szint növelése vagy\n"
+"                                          beállítása\n"
+"  -v  --verbose                         Részletességi szint növelése \n"
+"      --log-target={auto,syslog,stderr,file:ÚTVONAL,newfile:ÚTVONAL}\n"
+"                                        Naplózási cél megadása\n"
+"      --log-meta[=LOGIKAI]              Kódhely bevétele a naplóüzenetekbe\n"
+"      --log-time[=LOGIKAI]              Időbélyeg bevétele a "
+"naplóüzenetekbe\n"
+"      --log-backtrace=KERETEK           Visszakövetés bevétele a "
+"naplóüzenetekbe\n"
+"  -p, --dl-search-path=ÚTVONAL          Keresési útvonal megadása dinamikus\n"
+"                                          megosztott objektumokhoz "
+"(bővítmények)\n"
+"      --resample-method=MÓD             A megadott újramintavételezési mód\n"
+"                                          használata (lehetséges értékekért "
+"lásd\n"
+"                                           a --dump-resample-methods "
+"kimenetét)\n"
+"      --use-pid-file[=LOGIKAI]          PID fájl létrehozása\n"
+"      --no-cpu-limit[=LOGIKAI]          Ne telepítsen CPU "
+"terheléskorlátozót\n"
+"                                          az azt támogató platformokon\n"
+"      --disable-shm[=LOGIKAI]           Megosztott memória támogatásának "
+"tiltása\n"
+"      --enable-memfd[=LOGIKAI]          A memfd megosztott memória\n"
+"                                          támogatásának engedélyezése\n"
+"\n"
+"INDÍTÓ PARANCSFÁJL:\n"
+"  -L, --load=\"MODUL ARGUMENTUMOK\"       A megadott bővítménymodul "
+"betöltése\n"
+"                                          a megadott argumentummal\n"
+"  -F, --file=FÁJLNÉV                    A megadott parancsfájl futtatása\n"
+"  -C                                    Parancssor megnyitása a futó "
+"terminálon\n"
+"                                          indítás után\n"
+"\n"
+"  -n                                    Ne töltse be az alapértelmezett\n"
+"                                           parancsfájlt\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "a --daemonize paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "a --fail paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"a --log-level paraméter a naplózás szintjének értékét várja (Ez lehet a 0..4 "
+"tartomány, vagy a következők egyike: debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "a --high-priority paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "a --realtime paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "a --disallow-module-loading paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "a --disallow-exit paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "a --use-pid-file paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Érvénytelen naplózási cél: használja a „syslog”, „journal”, „stderr” vagy az "
+"„auto” egyikét, vagy egy érvényes fájlnevet: „file:<útvonal>”, „newfile:"
+"<útvonal>”."
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Érvénytelen naplózási cél: használja a „syslog”, „stderr” vagy az „auto” "
+"egyikét, vagy egy érvényes fájlnevet: „file:<útvonal>”, „newfile:<útvonal>”."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "a --log-time paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "a --log-meta paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Érvénytelen újramintavételezési eljárás: „%s”."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "a --system paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "a --no-cpu-limit paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "a --disable-shm paraméter logikai értéket vár"
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr "a --enable-memfd paraméter logikai értéket vár"
+
+#: ../src/daemon/daemon-conf.c:260
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Érvénytelen naplózási cél: „%s”."
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Érvénytelen naplózási szint: „%s”."
+
+#: ../src/daemon/daemon-conf.c:290
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Érvénytelen újramintavételezési eljárás: „%s”."
+
+#: ../src/daemon/daemon-conf.c:312
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Érvénytelen rlimit érték: „%s”."
+
+#: ../src/daemon/daemon-conf.c:332
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Érvénytelen mintavételi formátum: „%s”."
+
+#: ../src/daemon/daemon-conf.c:349 ../src/daemon/daemon-conf.c:366
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Érvénytelen mintavételezési gyakoriság: „%s”."
+
+#: ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Érvénytelen minta csatornák: „%s”."
+
+#: ../src/daemon/daemon-conf.c:406
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Érvénytelen csatornaleképzés: „%s”."
+
+#: ../src/daemon/daemon-conf.c:423
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Érvénytelen a részek száma: „%s”."
+
+#: ../src/daemon/daemon-conf.c:440
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Érvénytelen a részek mérete: „%s”."
+
+#: ../src/daemon/daemon-conf.c:457
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Érvénytelen a prioritási érték: „%s”."
+
+#: ../src/daemon/daemon-conf.c:500
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Érvénytelen kiszolgálótípus: „%s”."
+
+#: ../src/daemon/daemon-conf.c:615
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Nem sikerült megnyitni a konfigurációs fájlt: %s"
+
+#: ../src/daemon/daemon-conf.c:631
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"A megadott alapértelmezett csatornaleképzés csatornáinak száma eltér az "
+"megadott alapértelmezett csatornaszámtól."
+
+#: ../src/daemon/daemon-conf.c:718
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Olvasás a következő konfigurációs fájlból: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Név: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Nem áll rendelkezésre modulinformáció\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Verzió: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Leírás: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Szerző: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Használat: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Betöltve: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "ELAVULÁSI FIGYELMEZTETÉS: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Útvonal: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Nem sikerült megnyitni a(z) „%s” modult: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Nem található az eredeti „lt_dlopen” betöltő."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Nem foglalható le hely az új dl betöltő számára."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Nem sikerült hozzáadni az azonnali betöltés csatolást."
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Nem található a(z) „%s” felhasználó."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Nem található a(z) „%s” csoport."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr ""
+"A(z) „%s” felhasználó GID azonosítója és „%s” csoportja nem egyezik meg."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "A(z) „%s” felhasználó Saját mappája nem „%s”. Kihagyás."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Nem sikerült létrehozni a(z) „%s” fájlt: %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Nem sikerült megváltoztatni a csoportlistát: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Nem sikerült megváltoztatni az GID azonosítót: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Nem sikerült megváltoztatni az UID azonosítót: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "A rendszer üzemmód nem támogatott ezen az operációs rendszeren."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Nem sikerült feldolgozni a parancssort."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Rendszer mód elutasítva a nem root felhasználóhoz. Csak a D-Bus kiszolgáló "
+"kikeresési szolgáltatás indul."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "A démon kilövése nem sikerült: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Ez a program nincsen felkészítve arra, hogy rendszergazdai jogosultságokkal "
+"fusson (kivéve, ha a --system paraméter megadásra kerül)."
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Rendszergazdai jogosultságok szükségesek."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "--start nem támogatott rendszer üzemmód használata esetén."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+"Felhasználó által indított kiszolgáló itt: %s, az indítás/automatikus "
+"indítás elutasítva."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Felhasználó által indított kiszolgáló itt: %s, helyinek tűnik. Mélyebb "
+"szondázás."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr ""
+"Futtatás rendszer üzemmódban, de a --disallow-exit paraméter nincs beállítva."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr ""
+"Futtatás rendszer üzemmódban, de a --disallow-module-loading paraméter nincs "
+"beállítva."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr ""
+"Futtatás rendszer üzemmódban, az SHM üzemmód kényszerített letiltásával."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr ""
+"Futtatás rendszer üzemmódban, a kilépési üresjárati idő kényszerített "
+"letiltásával."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr ""
+"Nem sikerült jogot szerezni az alapértelmezett ki- és bemenetre (stdio)."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "a pipe() hívás meghiúsult: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "a fork() hívás meghiúsult: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:568
+#, c-format
+msgid "read() failed: %s"
+msgstr "a read() hívás meghiúsult: %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "A démon elindítása nem sikerült."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "a setsid() hívás meghiúsult: %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Nem sikerült lekérni a számítógép azonosítóját"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually "
+"do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"A PulseAudio rendszer üzemmódban fut. Győződjön meg arról, hogy valóban ezt "
+"szeretné-e tenni.\n"
+"További tájékoztatás: http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ amelyből megtudhatja miért nem "
+"tanácsos a rendszer üzemmód használata."
+
+#: ../src/daemon/main.c:990
+msgid "pa_pid_file_create() failed."
+msgstr "A „pa_pid_file_create()” függvényhívás meghiúsult."
+
+#: ../src/daemon/main.c:1022
+msgid "pa_core_new() failed."
+msgstr "A „pa_core_new()” függvényhívás meghiúsult: %s"
+
+#: ../src/daemon/main.c:1090
+msgid "Failed to initialize daemon."
+msgstr "Nem sikerült előkészíteni a démont."
+
+#: ../src/daemon/main.c:1095
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"A démont noha elindult, de nem töltött be modulokat, így a hangrendszer nem "
+"üzemképes."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio hangrendszer"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "A PulseAudio hangrendszer elindítása"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Bemenet"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Dokkolóállomás bemenet"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Dokkolóállomás mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Dokkolóállomás vonalbemenet"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Vonalbemenet"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1710
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Első mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Hátsó mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Külső mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Belső mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Rádió"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Videó"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Automatikus erősítésszabályzás"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Nincs automatikus erősítésszabályzás"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Erősítés"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Nincs erősítés"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Erősítő"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Nincs erősítő"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Basszuskiemelés"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Nincs basszuskiemelés"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1717
+msgid "Speaker"
+msgstr "Hangszóró"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Analóg fejhallgató"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Analóg bemenet"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Dokkolóállomás mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Fejhallgató mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Analóg kimenet"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "Analóg kimenet (mély)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Vonalkimenet"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Analóg mono kimenet"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Hangszórók"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Digitális kimenet (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Digitális bemenet (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digitális Passthrough (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Többcsatornás bemenet"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Többcsatornás kimenet"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Analóg mono"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Analóg sztereó"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Többcsatornás"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Analóg térhatású 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Analóg térhatású 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Analóg térhatású 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Analóg térhatású 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Analóg térhatású 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Analóg térhatású 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Analóg térhatású 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Analóg térhatású 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Analóg térhatású 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Analóg térhatású 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Analóg térhatású 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Digitális sztereó (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digitális Passthrough (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digitális térhatású 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digitális térhatású 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Digitális térhatású 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Digitális térhatású (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digitális térhatású 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4151
+msgid "Analog Mono Duplex"
+msgstr "Analóg mono duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Stereo Duplex"
+msgstr "Analóg sztereó duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Analóg sztereó duplex (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Multichannel Duplex"
+msgstr "Többcsatornás duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2319
+#: ../src/modules/bluetooth/module-bluez5-device.c:1965
+msgid "Off"
+msgstr "Kikapcsolva"
+
+#: ../src/modules/alsa/alsa-mixer.c:4254
+#, c-format
+msgid "%s Output"
+msgstr "%s kimenet"
+
+#: ../src/modules/alsa/alsa-mixer.c:4262
+#, c-format
+msgid "%s Input"
+msgstr "%s bemenet"
+
+#: ../src/modules/alsa/alsa-sink.c:570
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Az ALSA modul értesítése nyomán új adatokat kellett volna írni az eszközre, "
+"de semmilyen írandó adat nem volt.\n"
+"Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse "
+"ezt a problémát az ALSA fejlesztői felé.\n"
+"Az értesítés érkezésekor a POLLOUT be volt állítva – viszont a későbbi "
+"„snd_pcm_avail()” függvény visszatérési értéke 0 vagy a min_avail-nál kisebb "
+"más érték volt."
+
+#: ../src/modules/alsa/alsa-sink.c:747
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Az ALSA modul értesítése nyomán új adatokat kellett volna írni az eszközre, "
+"de semmilyen írandó adat nem volt.\n"
+"Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse "
+"ezt a problémát az ALSA fejlesztői felé.\n"
+"Az értesítés érkezésekor a POLLOUT be volt állítva – viszont a későbbi "
+"„snd_pcm_avail()” függvény visszatérési értéke 0 vagy a min_avail-nál kisebb "
+"más érték volt."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Az ALSA modul értesítése nyomán új adatokat kellett volna olvasni az "
+"eszközről, de nem volt olvasandó adat.\n"
+"Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse "
+"ezt a problémát az ALSA fejlesztői felé.\n"
+"Az értesítés érkezésekor a POLLIN be volt állítva – viszont a későbbi "
+"„snd_pcm_avail()” függvény visszatérési értéke 0 vagy a min_avail-nál kisebb "
+"más érték volt."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Az ALSA modul értesítése nyomán új adatokat kellett volna olvasni az "
+"eszközről, de nem volt olvasandó adat.\n"
+"Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse "
+"ezt a problémát az ALSA fejlesztői felé.\n"
+"Az értesítés érkezésekor a POLLIN be volt állítva – viszont a későbbi "
+"„snd_pcm_avail()” függvény visszatérési értéke 0 vagy a min_avail-nál kisebb "
+"más érték volt."
+
+#: ../src/modules/alsa/alsa-util.c:1166 ../src/modules/alsa/alsa-util.c:1241
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"A „snd_pcm_avail()” függvény visszatérési értéke váratlanul nagy értékű: %lu "
+"bájt (%lu ms).\n"
+"Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse "
+"ezt a problémát az ALSA fejlesztői felé."
+
+#: ../src/modules/alsa/alsa-util.c:1216
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"A „snd_pcm_delay()” függvény visszatérési értéke váratlanul nagy értékű: %li "
+"bájt (%s%lu ms).\n"
+"Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse "
+"ezt a problémát az ALSA fejlesztői felé."
+
+#: ../src/modules/alsa/alsa-util.c:1257
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"A „snd_pcm_avail_delay()” függvény furcsa értékeket adott vissza: a "
+"késleltetés (%lu) kisebb, mint az elérhető %lu.\n"
+"Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse "
+"ezt a problémát az ALSA fejlesztői felé."
+
+#: ../src/modules/alsa/alsa-util.c:1300
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"A „snd_pcm_mmap_begin()” függvény visszatérési értéke kivételesen nagy: %lu "
+"bájt (%lu ms).\n"
+"Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse "
+"ezt a problémát az ALSA fejlesztői felé."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2089
+#: ../src/modules/bluetooth/module-bluez5-device.c:1700
+msgid "Headset"
+msgstr "Fejhallgató"
+
+# FIXME: utánanézni
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1705
+msgid "Handsfree"
+msgstr "Kihangosító"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1723
+msgid "Headphone"
+msgstr "Fülhallgató"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1728
+msgid "Portable"
+msgstr "Hordozható"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1733
+msgid "Car"
+msgstr "Autó"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1738
+msgid "HiFi"
+msgstr "Hi-Fi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1743
+msgid "Phone"
+msgstr "Telefon"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2137
+#: ../src/modules/bluetooth/module-bluez5-device.c:1695
+#: ../src/modules/bluetooth/module-bluez5-device.c:1711
+#: ../src/modules/bluetooth/module-bluez5-device.c:1749
+msgid "Bluetooth Output"
+msgstr "Bluetooth kimenet"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2140
+#: ../src/modules/bluetooth/module-bluez5-device.c:1694
+#: ../src/modules/bluetooth/module-bluez5-device.c:1716
+#: ../src/modules/bluetooth/module-bluez5-device.c:1722
+#: ../src/modules/bluetooth/module-bluez5-device.c:1748
+msgid "Bluetooth Input"
+msgstr "Bluetooth bemenet"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2181
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Hi-Fi lejátszás (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2193
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Hi-Fi felvétel (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2205
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telefon duplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2218
+msgid "Handsfree Gateway"
+msgstr "Kihangosító átjáró"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1790
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Hi-Fi lejátszás (A2DP bemenet)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1802
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Hi-Fi felvétel (A2DP forrás)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1814
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Fejhallgató fejegység (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1827
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Fejhallgató hangátjáró (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<a forrás neve> source_properties=<a forrás tulajdonságai> "
+"source_master=<a szűrendő forrás neve> sink_name=<a nyelő neve> "
+"sink_properties=<a nyelő tulajdonságai> sink_master=<a szűrendő nyelő neve> "
+"adjust_time=<milyen gyakran módosítsa a gyakoriságokat, mp-ben> "
+"adjust_threshold=<mennyi csúszás után igazítson, mp-ben> "
+"format=<mintaformátum> rate=<mintavételezési gyakoriság> channels=<csatornák "
+"száma> channel_map=<csatornaleképzés> aec_method=<használandó megvalósítás> "
+"aec_args=<az AEC alrendszer paraméterei> save_aec=<AEC adatok mentése a /tmp-"
+"be> autoloaded=<beállítva, ha ez a modul automatikusan betöltődik> "
+"use_volume_sharing=<igen vagy nem> use_master_format=<igen vagy nem> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "Be"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Látszólagos kimenet"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+"Mindig maradjon betöltve legalább egy nyelő, még ha az csak az üres nyelő is"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Általános célú hangszínszabályzó"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<nyelő neve> sink_properties=<nyelő tulajdonságai> "
+"sink_master=<csatlakozás ezen nyelőhöz> format=<mintaformátum> "
+"rate=<mintavételezési gyakoriság> channels=<csatornák száma> "
+"channel_map=<csatornaleképzés> autoloaded=<beállítva, ha ez a modul "
+"automatikusan betöltődik> use_volume_sharing=<igen vagy nem>"
+
+#: ../src/modules/module-filter-apply.c:47
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<nem használt szűrők automatikus eltávolítása?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Látszólagos LADSPA nyelő"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<nyelő neve> sink_properties=<nyelő tulajdonságai> "
+"master=<szűrendő nyelő neve> format=<mintaformátum> rate=<mintavételezési "
+"gyakoriság> channels=<csatornák száma> channel_map=<csatornaleképzés> "
+"plugin=<ladspa bővítmény neve> label=<ladspa bővítmény címkéje> "
+"control=<bemenetszabályzó értékek vesszővel elválasztott listája>  "
+"input_ladspaport_map=<bemeneti LADSPA portnevek vesszővel elválasztott "
+"listája> output_ladspaport_map=<kimeneti LADSPA portnevek vesszővel "
+"elválasztott listája>"
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Órajelezett NULL nyelő"
+
+#: ../src/modules/module-null-sink.c:280
+msgid "Null Output"
+msgstr "NULL kimenet"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Kimeneti eszközök"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Bemeneti eszközök"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Hangok ezen a számítógépen: @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Alagút ennek: %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:517
+#: ../src/modules/module-tunnel-source-new.c:516
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Alagút ehhez: %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Virtuális térhatású nyelő"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/"
+"left_hrir.wav "
+msgstr ""
+"sink_name=<nyelő neve> sink_properties=<nyelő tulajdonságai> "
+"master=<szűrendő nyelő neve> format=<mintavételi formátum> "
+"rate=<mintavételezési gyakoriság> channels=<csatornák száma> "
+"channel_map=<csatornaleképzés> use_volume_sharing=<igen vag nem> "
+"force_flat_volume=<igen vag nem> hrir=/útvonal/left_hrir.wav "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio hangkiszolgáló"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Első középső"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Első bal"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Első jobb"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Hátsó középső"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Hátsó bal"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Hátsó jobb"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Mélysugárzó"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Első közép-bal"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Első közép-jobb"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Bal oldalsó"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Jobb oldalsó"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Külső 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Külső 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Külső 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Külső 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Külső 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Külső 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Külső 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Külső 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Külső 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Külső 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Külső 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Külső 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Külső 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Külső 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Külső 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Külső 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Külső 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Külső 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Külső 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Külső 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Külső 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Külső 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Külső 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Külső 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Külső 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Külső 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Külső 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Külső 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Külső 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Külső 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Külső 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Külső 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Felső középső"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Felső első középső"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Felső első bal"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Felső első jobb"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Felső hátsó középső"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Felső hátsó bal"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Felső hátsó jobb"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:175 ../src/pulse/volume.c:294
+#: ../src/pulse/volume.c:320 ../src/pulse/volume.c:340
+#: ../src/pulse/volume.c:372 ../src/pulse/volume.c:412
+#: ../src/pulse/volume.c:431
+msgid "(invalid)"
+msgstr "(Érvénytelen)"
+
+#: ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Sztereó"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Térhatású 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Térhatású 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Térhatású 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Térhatású 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Térhatású 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "az xcb_connect() függvényhívás meghiúsult"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "az xcb_connection_has_error() igaz értéket adott vissza"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Nem sikerült feldolgozni a süti adatait"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "Programindítás: %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Üzenet érkezett az ismeretlen „%s” kiterjesztéstől"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "bemenet"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "kimenet"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "kétirányú"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "érvénytelen"
+
+#: ../src/pulsecore/core-util.c:1836
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"Az XDG_RUNTIME_DIR (%s) tulajdonosa nem a PulseAudio (uid %d), hanem ez az "
+"uid: %d! (Ez például akkor fordulhat elő, ha egy nem rootként futó "
+"PulseAudio-hoz root felhasználóként próbál csatlakozni a natív protokollon. "
+"Ne tegye.)"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "igen"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "nem"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Nem érhető zárolás az automatikus indításhoz."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Nem sikerült megnyitni a cél „%s” fájlt."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Kísérlet történt a cél fájlok megnyitására: „%s”, „%s.1”, „%s.2” ... „%s."
+"%d”, de mind meghiúsult."
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Érvénytelen naplózási cél."
+
+#: ../src/pulsecore/sink.c:3459
+msgid "Built-in Audio"
+msgstr "Belső hangforrás"
+
+#: ../src/pulsecore/sink.c:3464
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Hozzáférés megtagadva"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Ismeretlen parancs"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Érvénytelen paraméter"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "Az egység létezik"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Nincs ilyen egység"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Kapcsolat elutasítva"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Protokollhiba"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Időtúllépés"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Nem érhető el hitelesítési kulcs"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Belső hiba"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "A kapcsolat megszakadt."
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Egység kilőve"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Érvénytelen kiszolgáló"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "A modul előkészítése meghiúsult."
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Hibás állapot"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Nincs adat"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Inkompatibilis protokollverzió"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Túl nagy"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Nem támogatott"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Ismeretlen hibakód"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Nincs ilyen kiterjesztés"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Elavult funkcionalitás"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Nincs megvalósítva"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Kliens elindítva"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Kimeneti/bemeneti hiba"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Az eszköz vagy erőforrás foglalt"
+
+#: ../src/pulse/sample.c:177
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:117
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Nem sikerült megcsapolni a következő adatfolyamot: %s"
+
+#: ../src/utils/pacat.c:122
+msgid "Playback stream drained."
+msgstr "A lejátszási adatfolyam megcsapolva."
+
+#: ../src/utils/pacat.c:133
+msgid "Draining connection to server."
+msgstr "A kiszolgáló kapcsolatának megcsapolása."
+
+#: ../src/utils/pacat.c:146
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:169
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "A „pa_stream_write()” függvényhívás meghiúsult: %s"
+
+#: ../src/utils/pacat.c:210
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "A „pa_stream_begin_write()” függvényhívás meghiúsult: %s"
+
+#: ../src/utils/pacat.c:260 ../src/utils/pacat.c:290
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "A „pa_stream_peek()” függvényhívás meghiúsult: %s"
+
+#: ../src/utils/pacat.c:340
+msgid "Stream successfully created."
+msgstr "Az adatfolyam sikeresen létrejött."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "A „pa_stream_get_buffer_attr()” függvényhívás meghiúsult: %s"
+
+#: ../src/utils/pacat.c:347
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr ""
+"Pufferméretek: maximális hossz: %u, hossz: %u, előpufferelés: %u, minimum: %u"
+
+#: ../src/utils/pacat.c:350
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Pufferméretek: maximális hossz: %u, részek mérete: %u"
+
+#: ../src/utils/pacat.c:354
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "„%s” mintavételi leírás és „%s” csatornaleképzés használata."
+
+#: ../src/utils/pacat.c:358
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr ""
+"Csatlakozva a következő eszközhöz: „%s” (index: %u, felfüggesztve: %s)."
+
+#: ../src/utils/pacat.c:368
+#, c-format
+msgid "Stream error: %s"
+msgstr "Adatfolyamhiba: %s"
+
+#: ../src/utils/pacat.c:378
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Adatfolyam-eszköz készenléti állapotban. %s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Adatfolyam-eszköz visszatért a készenléti állapotból. %s"
+
+#: ../src/utils/pacat.c:388
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Adatfolyam-alulcsordulás. %s"
+
+#: ../src/utils/pacat.c:395
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Adatfolyam-túlcsordulás. %s"
+
+#: ../src/utils/pacat.c:402
+#, c-format
+msgid "Stream started.%s"
+msgstr "Adatfolyam elindítva. %s"
+
+#: ../src/utils/pacat.c:409
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr ""
+"Az adatfolyam áthelyezve a következő eszközre: „%s” (%u, %s felfüggesztve). "
+"%s"
+
+#: ../src/utils/pacat.c:409
+msgid "not "
+msgstr "nem"
+
+#: ../src/utils/pacat.c:416
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Az adatfolyampuffer attribútumai megváltoztak. %s"
+
+#: ../src/utils/pacat.c:431
+msgid "Cork request stack is empty: corking stream"
+msgstr "A dugókérés verem üres: adatfolyam eldugaszolása"
+
+#: ../src/utils/pacat.c:437
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "A dugókérés verem üres: adatfolyam eldugaszolásának megszüntetése"
+
+#: ../src/utils/pacat.c:441
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr ""
+"Figyelmeztetés: több eldugaszolás-megszüntetési kérés érkezett, mint "
+"eldugaszolási."
+
+#: ../src/utils/pacat.c:466
+#, c-format
+msgid "Connection established.%s"
+msgstr "Kapcsolat létrehozva. %s"
+
+#: ../src/utils/pacat.c:469
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "A „pa_stream_new()” függvényhívás meghiúsult: %s"
+
+#: ../src/utils/pacat.c:507
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "A „pa_stream_connect_playback()” függvényhívás meghiúsult: %s"
+
+#: ../src/utils/pacat.c:513
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Nem sikerült beállítani az adatfolyam megfigyelését: %s"
+
+#: ../src/utils/pacat.c:517
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "A „pa_stream_connect_record()” függvényhívás meghiúsult: %s"
+
+#: ../src/utils/pacat.c:530 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Kapcsolódási hiba: %s"
+
+#: ../src/utils/pacat.c:563
+msgid "Got EOF."
+msgstr "A fájl vége elérve."
+
+#: ../src/utils/pacat.c:600
+#, c-format
+msgid "write() failed: %s"
+msgstr "A „write()” függvényhívás sikertelen: %s"
+
+#: ../src/utils/pacat.c:621
+msgid "Got signal, exiting."
+msgstr "Kilépés, szignál hatására."
+
+#: ../src/utils/pacat.c:635
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Nem sikerült lekérdezni a késleltetést: %s"
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Idő: %0.3f másodperc, késleltetés: %0.0f ezredmásodperc."
+
+#: ../src/utils/pacat.c:661
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "A „pa_stream_update_timing_info()” függvényhívás meghiúsult: %s"
+
+#: ../src/utils/pacat.c:671
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [KAPCSOLÓ]\n"
+"\n"
+"%s\n"
+"\n"
+"  -h, --help                            Ezen súgó megjelenítése\n"
+"      --version                         Az alkalmazás verziószámának "
+"megjelenítése\n"
+"\n"
+"  -r, --record                          Kapcsolat létrehozása felvételhez\n"
+"  -p, --playback                        Kapcsolat létrehozása lejátszáshoz\n"
+"\n"
+"  -v, --verbose                         Részletes üzenetek\n"
+"\n"
+"  -s, --server=KISZOLGÁLÓ               Kapcsolódás a megadott KISZOLGÁLÓ "
+"kiszolgálóhoz\n"
+"  -d, --device=ESZKÖZ                   Kapcsolódás az ESZKÖZ nevű nyelőhöz "
+"vagy forráshoz\n"
+"  -n, --client-name=NÉV                 A kliens neve ezen a szerveren\n"
+"      --stream-name=NÉV                 Adatfolyam neve a kiszolgálón\n"
+"      --volume=HANGERŐ                  Kezdeti (lineáris) hangerő megadása "
+"0...65536 között\n"
+"      --rate=MINTAVÉTEL                 Mintavételezés gyakorisága Hz-ben "
+"(alapértelmezés: 44100)\n"
+"      --format=MINTAFORMÁTUM            Mintavételezés típusa, lehetséges "
+"értékek: s16le, s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (alapértelmezés: "
+"s16ne)\n"
+"      --channels=CSATORNÁK              Csatornák száma: 1 - mono, 2 - "
+"sztereó\n"
+"                                        (alapértelmezés: 2)\n"
+"      --channel-map=CSATORNALEKÉPZÉS    Az alapértelmezés helyett "
+"használandó csatornaleképzés\n"
+"      --fix-format                      A mintavételi formátum átvétele a "
+"nyelőtől/forrástól,\n"
+"                                        amelyhez az adatfolyam csatlakozik.\n"
+"      --fix-rate                        A mintavételi gyakoriság átvétele a "
+"nyelőtől/forrástól,\n"
+"                                        amelyhez az adatfolyam csatlakozik.\n"
+"      --fix-channels                    A csatornaszám és a csatornaleképzés "
+"átvétele a nyelőtől/forrástól,\n"
+"                                        amelyhez az adatfolyam csatlakozik.\n"
+"      --no-remix                        Ne keverjen fel vagy le "
+"csatornákat.\n"
+"      --no-remap                        Csatornák leképezése index és nem "
+"név szerint.\n"
+"      --latency=BÁJT                    A bájtokban megadott késleltetés "
+"kérése.\n"
+"      --process-time=BÁJT               A bájtokban megadott kérésenkénti "
+"feldolgozási idő kérése.\n"
+"      --latency-msec=MSEC               Az ezredmásodpercben megadott "
+"késleltetés kérése.\n"
+"      --process-time-msec=MSEC          Az ezredmásodpercben kérésenkénti "
+"feldolgozási idő kérése.\n"
+"      --property=TULAJDONSÁG=ÉRTÉK      A megadott tulajdonság beállítása az "
+"adott értékre.\n"
+"      --raw                             Nyers PCM adatok felvétele vagy "
+"lejátszása.\n"
+"      --passthrough                     Passthrough adatok.\n"
+"      --file-format[=FORMÁTUM]          Adott FORMÁTUMÚ PCM adatok felvétele "
+"vagy lejátszása.\n"
+"      --list-file-formats               Elérhető fájlformátumok listája.\n"
+"      --monitor-stream=INDEX            Felvétel az INDEX indexű "
+"nyelőbemenetről.\n"
+
+#: ../src/utils/pacat.c:788
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr "Kódolt hangfájlok lejátszása egy PulseAudio hangkiszolgálón."
+
+#: ../src/utils/pacat.c:792
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr ""
+"Hangadatok rögzítése egy PulseAudio hangkiszolgálóról, és fájlba írásuk."
+
+#: ../src/utils/pacat.c:796
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+"Hangadatok rögzítése egy PulseAudio hangkiszolgálóról, és szabványos "
+"kimenetre, vagy a megadott fájlba írásuk."
+
+#: ../src/utils/pacat.c:800
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+"Hangadatok lejátszása a szabványos bemenetről, vagy a megadott fájlból egy "
+"PulseAudio hangkiszolgálón."
+
+#: ../src/utils/pacat.c:814
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Lefordítva a libpulse %s programkönyvtárral\n"
+"Összeszerkesztve a libpulse %s programkönyvtárhoz\n"
+
+#: ../src/utils/pacat.c:847 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Érvénytelen kliensnév: „%s”"
+
+#: ../src/utils/pacat.c:862
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Érvénytelen adatfolyamnév: „%s”"
+
+#: ../src/utils/pacat.c:899
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Érvénytelen csatornaleképzés: „%s”"
+
+#: ../src/utils/pacat.c:928 ../src/utils/pacat.c:942
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Érvénytelen késleltetésmeghatározás: „%s”"
+
+#: ../src/utils/pacat.c:935 ../src/utils/pacat.c:949
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Érvénytelen feldolgozásiidő-meghatározás: „%s”"
+
+#: ../src/utils/pacat.c:961
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Érvénytelen tulajdonság: „%s”"
+
+#: ../src/utils/pacat.c:980
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Ismeretlen fájlformátum: „%s”."
+
+#: ../src/utils/pacat.c:995
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "A --monitor-stream argumentumának feldolgozása sikertelen"
+
+#: ../src/utils/pacat.c:1006
+msgid "Invalid sample specification"
+msgstr "Érvénytelen mintameghatározás"
+
+#: ../src/utils/pacat.c:1016
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1028
+msgid "Too many arguments."
+msgstr "Túl sok argumentum."
+
+#: ../src/utils/pacat.c:1039
+msgid "Failed to generate sample specification for file."
+msgstr "Nem sikerült létrehozni a mintavételi meghatározást a fájlhoz."
+
+#: ../src/utils/pacat.c:1065
+msgid "Failed to open audio file."
+msgstr "Nem sikerült megnyitni a hangfájlt."
+
+#: ../src/utils/pacat.c:1071
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Figyelmeztetés: a megadott mintavételi meghatározás felül lesz írva a "
+"fájlból származó meghatározással."
+
+#: ../src/utils/pacat.c:1074 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Nem sikerült meghatározni a mintavételi meghatározást a fájlból."
+
+#: ../src/utils/pacat.c:1083
+msgid "Warning: Failed to determine channel map from file."
+msgstr ""
+"Figyelmeztetés: Nem sikerült meghatározni a csatornaleképzést a fájlból."
+
+#: ../src/utils/pacat.c:1094
+msgid "Channel map doesn't match sample specification"
+msgstr "A csatornaleképzés nem feleltethető meg a mintavételi meghatározásnak"
+
+#: ../src/utils/pacat.c:1105
+msgid "Warning: failed to write channel map to file."
+msgstr "Hiba történt a csatornaleképzés fájlba írása közben."
+
+#: ../src/utils/pacat.c:1120
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"A(z) „%s” adatfolyam megnyitása a következő mintavételi meghatározás: „%s” "
+"és csatornaleképzés: „%s” használatával."
+
+#: ../src/utils/pacat.c:1121
+msgid "recording"
+msgstr "felvétel"
+
+#: ../src/utils/pacat.c:1121
+msgid "playback"
+msgstr "lejátszás"
+
+#: ../src/utils/pacat.c:1145
+msgid "Failed to set media name."
+msgstr "Nem sikerült beállítani a médianevet."
+
+#: ../src/utils/pacat.c:1152 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "A „pa_mainloop_new()” függvényhívás meghiúsult."
+
+#: ../src/utils/pacat.c:1175
+msgid "io_new() failed."
+msgstr "Az „io_new()” függvényhívás meghiúsult."
+
+#: ../src/utils/pacat.c:1182 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "A „pa_context_new()” függvényhívás meghiúsult."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "A „pa_context_connect()” függvényhívás meghiúsult: %s"
+
+#: ../src/utils/pacat.c:1196
+msgid "pa_context_rttime_new() failed."
+msgstr "A „pa_context_rttime_new()” függvényhívás meghiúsult."
+
+#: ../src/utils/pacat.c:1203 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "A „pa_mainloop_run()” függvényhívás meghiúsult."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "NÉV [ARGUMENTUMOK …]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "NÉV|#N"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "NÉV"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "NÉV|#N HANGERŐ"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N HANGERŐ"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "NÉV|#N 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "NÉV|#N KULCS=ÉRTÉK"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#N KULCS=ÉRTÉK"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "NÉV NYELŐ|#N"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "NÉV FÁJLNÉV"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "ÚTVONALNÉV"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "FÁJLNÉV NYELŐ|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#N NYELŐ|FORRÁS"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "KÁRTYAPROFIL"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "NÉV|#N PORT"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "KÁRTYANÉV|KÁRTYA-#N PORTELTOLÁS"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "CÉL"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "SZÁMOZOTT-SZINT"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "KERETEK"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Ezen súgó megjelenítése\n"
+"      --version                         Verziószám megjelenítése\n"
+"Ha nincs megadva parancs, a pacmd interaktív módban indul.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Lefordítva a libpulse %s programkönyvtárral\n"
+"Összeszerkesztve a libpulse %s programkönyvtárhoz\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "Nem fut a PulseAudio démon, vagy nem munkamenet-démonként fut."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "A PulseAudio démon kilövése nem sikerült."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "A démon nem válaszol."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Nem sikerült a statisztika lekérdezése: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Jelenleg használt: %u blokk, összesen %s bájt tartalommal.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"A futás során összesen lefoglalva: %u blokk, összesen %s bájt tartalommal.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Mintagyorsítótár mérete: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Nem sikerült lekérni a kiszolgáló adatait: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Kiszolgáló karakterlánc: %s\n"
+"Programkönyvtár protokollverzió: %u\n"
+"Kiszolgáló protokollverzió: %u\n"
+"Helyi: %s\n"
+"Kliensindex: %u\n"
+"Csempeméret: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Felhasználónév: %s\n"
+"Számítógépnév: %s\n"
+"Kiszolgálónév: %s\n"
+"Kiszolgáló verzió: %s\n"
+"Alapértelmezett mintavételi meghatározás: %s\n"
+"Alapértelmezett csatornaleképzés: %s\n"
+"Alapértelmezett nyelő: %s\n"
+"Alapértelmezett forrás: %s\n"
+"Süti: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Nem sikerült lekérni a nyelő adatait: %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. nyelő\n"
+"\tÁllapot: %s\n"
+"\tNév: %s\n"
+"\tLeírás: %s\n"
+"\tEszközmeghajtó: %s\n"
+"\tMintavételi meghatározás: %s\n"
+"\tCsatornaleképzés: %s\n"
+"\tTulajdonos modul: %u\n"
+"\tNémítás: %s\n"
+"\tHangerő: %s\n"
+"\t        egyensúly %0.2f\n"
+"\tAlap hangerő: %s\n"
+"\tForrás megfigyelése: %s\n"
+"\tKésleltetés: %0.0f ezredmásodperc, beállítva %0.0f ezredmásodperc\n"
+"\tJelzők: %s%s%s%s%s%s%s\n"
+"\tTulajdonságok:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPortok:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tAktív port: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tFormátumok:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Nem sikerült lekérni a forrás adatait: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. forrás\n"
+"\tÁllapot: %s\n"
+"\tNév: %s\n"
+"\tLeírás: %s\n"
+"\tIllesztőprogram: %s\n"
+"\tMintavételi meghatározás: %s\n"
+"\tCsatornaleképzés: %s\n"
+"\tTulajdonos modul: %u\n"
+"\tNémítás: %s\n"
+"\tHangerő: %s\n"
+"\t        egyensúly %0.2f\n"
+"\tAlap hangerő: %s\n"
+"\tNyelő megfigyelője: %s\n"
+"\tKésleltetés: %0.0f ezredmásodperc, beállítva %0.0f ezredmásodperc\n"
+"\tJelzők: %s%s%s%s%s%s\n"
+"\tTulajdonságok:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "---"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Nem sikerült lekérni a modul adatait: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. modul\n"
+"\tNév: %s\n"
+"\tArgumentum: %s\n"
+"\tHasználat száma: %s\n"
+"\tTulajdonságok:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Nem sikerült lekérni a kliens adatait: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. kliens\n"
+"\tIllesztőprogram: %s\n"
+"\tTulajdonos modul: %s\n"
+"\tTulajdonságok:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Nem sikerült lekérni a kártya adatait: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. kártya\n"
+"\tNév: %s\n"
+"\tIllesztőprogram: %s\n"
+"\tTulajdonos modul: %s\n"
+"\tTulajdonságok:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfilok:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr "\t\t%s: %s (nyelők: %u, források: %u, prioritás: %u, elérhető: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tAktív profil: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tTulajdonságok:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tProfilok része: %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Nem sikerült lekérni a nyelő bemeneti adatait: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. nyelő bemenet\n"
+"\tIllesztőprogram: %s\n"
+"\tTulajdonos modul: %s\n"
+"\tKliens: %s\n"
+"\tNyelő: %u\n"
+"\tMintavételi meghatározás: %s\n"
+"\tCsatornaleképzés: %s\n"
+"\tFormátum: %s\n"
+"\tEldugaszolva: %s\n"
+"\tNémítás: %s\n"
+"\tHangerő: %s\n"
+"\t        egyensúly %0.2f\n"
+"\tPuffer késleltetés: %0.0f usec\n"
+"\tNyelő késleltetés: %0.0f usec\n"
+"\tÚjramintavételezési eljárás: %s\n"
+"\tTulajdonságok:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Nem sikerült lekérni a forrás kimeneti adatait: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. nyelő bemenet\n"
+"\tIllesztőprogram: %s\n"
+"\tTulajdonos modul: %s\n"
+"\tKliens: %s\n"
+"\tNyelő: %u\n"
+"\tMintavételi meghatározás: %s\n"
+"\tCsatornaleképzés: %s\n"
+"\tFormátum: %s\n"
+"\tEldugaszolva: %s\n"
+"\tNémítás: %s\n"
+"\tHangerő: %s\n"
+"\t        egyensúly %0.2f\n"
+"\tPuffer késleltetés: %0.0f usec\n"
+"\tNyelő késleltetés: %0.0f usec\n"
+"\tÚjramintavételezési eljárás: %s\n"
+"\tTulajdonságok:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Nem sikerült lekérni a mintavétel adatait: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. minta\n"
+"\tNév: %s\n"
+"\tMintavételi meghatározás: %s\n"
+"\tCsatornaleképzés: %s\n"
+"\tHangerő: %s\n"
+"\t        egyensúly %0.2f\n"
+"\tIdőtartam: %0.1fs\n"
+"\tMéret: %s\n"
+"\tLusta: %s\n"
+"\tFájlnév: %s\n"
+"\tTulajdonságok:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Hiba: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Nem sikerült eltávolítani a modult: nincs betöltve %s nevű modul"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Nem sikerült a hangerő beállítása: kísérlet %d csatorna hangerejének "
+"beállítására, miközben a támogatott csatornák száma %d\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Nem sikerült beállítani a formátumot: érvénytelen formátumleírás: %s"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Nem sikerült feltölteni a mintát: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Túl korai fájlvég"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "új"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "módosítás"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "eltávolítás"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "ismeretlen"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "nyelő"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "forrás"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "nyelő-bemenet"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "forrás-kimenet"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "modul"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "kliens"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "mintagyorsítótár"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "kiszolgáló"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "kártya"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "„%1$s” esemény ezen: %3$u. %2$s\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "Kilépés, SIGINT szignál hatására."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "A hangerő megadása érvénytelen"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "A hangerő a megengedett tartományon kívül esik.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "A hangerőmegadások száma érvénytelen.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "A hangerő megadása inkonzisztens.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[kapcsolók]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[TÍPUS]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "FÁJLNÉV [NÉV]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "NÉV [NYELŐ]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "NÉV|#N HANGERŐ [HANGERŐ …]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#N HANGERŐ [HANGERŐ …]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "NÉV|#N 1|0|váltás"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|váltás"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "#N FORMÁTUMOK"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"A speciális @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@ nevek\n"
+"használhatók az alapértelmezett nyelő, forrás és megfigyelő megadására.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Ezen súgó megjelenítése\n"
+"      --version                         Verziószám megjelenítése\n"
+"  -s, --server=KISZOLGÁLÓ               Csatlakozás ehhez a kiszolgálóhoz\n"
+"  -n, --client-name=NÉV                 A kliens neve a kiszolgálón\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Lefordítva a libpulse %s programkönyvtárral\n"
+"Összeszerkesztve a libpulse %s programkönyvtárhoz\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Ne adjon meg semmit, vagy a következők egyikét: %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Adja meg a betöltendő mintafájlt"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Nem sikerült megnyitni a hangfájlt."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+"Figyelmeztetés: Nem sikerült meghatározni a mintavételi meghatározást a "
+"fájlból."
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Meg kell adnia a lejátszandó minta nevét"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Meg kell adnia az eltávolítandó minta nevét"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "Meg kell adnia egy nyelő bemeneti indexét és egy nyelőt"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "Meg kell adnia a forrás kimeneti indexét és egy forrást"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Meg kell adnia a modul nevét és argumentumait."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "Meg kell adnia a modul indexét vagy nevét"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr "Nem adhat meg egynél több nyelőt. Egy logikai értéket kell megadnia."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Érvénytelen felfüggesztési meghatározás."
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr "Nem adhat meg egynél több forrást. Egy logikai értéket kell megadnia."
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Meg kell adnia a kártya nevét vagy indexét és egy profil nevét"
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Meg kell adnia a nyelő nevét vagy indexét, és egy port nevét"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "Meg kell adnia egy nyelő nevét"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "Meg kell adnia egy forrás nevét vagy indexét, és egy port nevét"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Meg kell adnia egy forrás nevét"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Meg kell adnia egy nyelő nevét vagy indexét és egy hangerőt"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Meg kell adnia egy forrás nevét vagy indexét és egy hangerőt"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "Meg kell adnia egy nyelő bemenet indexét és egy hangerőt"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "A nyelőbemenet indexe érvénytelen"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "Meg kell adnia egy forráskimenet indexét és egy hangerőt"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "A nyelőkimenet indexe érvénytelen"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Meg kell adnia egy nyelő nevét vagy indexét, és a némítási műveletet (0, 1 "
+"vagy „toggle”)"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Érvénytelen némításmeghatározás"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Meg kell adnia egy forrás nevét vagy indexét, és a némítási műveletet (0, 1 "
+"vagy „toggle”)"
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Meg kell adnia egy nyelő bemeneti indexét, és a némítási műveletet (0, 1 "
+"vagy „toggle”)"
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "A nyelő bemeneti indexének megadása érvénytelen"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+"Meg kell adnia egy forráskimenet indexét, és a némítási műveletet (0, 1 vagy "
+"„toggle”)"
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "A forrás bemeneti indexének megadása érvénytelen"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Meg kell adnia egy nyelő indexét és a támogatott formátumok pontosvesszővel "
+"elválasztott listáját"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr ""
+"Meg kell adnia egy kártya nevét vagy indexét, egy port nevét és egy "
+"késleltetéseltolást"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Nem dolgozható fel a késleltetéseltolás"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Érvénytelen parancs lett megadva."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Hiba a folytatáskor: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Hiba a felfüggesztéskor: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "FIGYELMEZTETÉS: A hangkiszolgáló nem helyi, nem lesz felfüggesztve.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Kapcsolódási hiba: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Kilépés, SIGINT szignál hatására.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr ""
+"FIGYELMEZTETÉS: A gyermek folyamat a következő szignállal fejeződött be: %u\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [KAPCSOLÓK] ... \n"
+"\n"
+"  -h, --help                            Ezen súgó megjelenítése\n"
+"      --version                         Verziószám megjelenítése\n"
+"  -s, --server=KISZOLGÁLÓ               Csatlakozás ehhez a kiszolgálóhoz\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Lefordítva a libpulse %s programkönyvtárral\n"
+"Összeszerkesztve a libpulse %s programkönyvtárhoz\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "A „pa_mainloop_new()” függvényhívás meghiúsult.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "A „pa_context_new()” függvényhívás meghiúsult.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "A „pa_mainloop_run()” függvényhívás meghiúsult.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D megjelenítő] [-S kiszolgáló] [-O nyelő] [-I forrás] [-c fájl]  [-d|-"
+"e|-i|-r]\n"
+"\n"
+" -d    Megjeleníti az aktuális X11 megjelenítőhöz csatlakoztatott "
+"PulseAudio\n"
+"         adatokat (alapértelmezés)\n"
+" -e    Helyi PulseAudio adatok exportálása az X11 megjelenítőre\n"
+" -i    Helyi PulseAudio adatok importálása az X11 megjelenítőről helyi\n"
+"         környezeti változókba és süti fájlokba\n"
+" -r    Eltávolítja a PulseAudio adatokat az X11 megjelenítőről\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Nem sikerült feldolgozni a parancssort.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Kiszolgáló: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Forrás: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Nyelő: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Süti: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Nem sikerült feldolgozni a sütiadatokat\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Nem sikerült elmenteni a sütiadatokat\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Nem sikerült lekérdezni a teljes tartománynevet (FQDN).\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Nem sikerült betölteni a sütiadatokat\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Még nincs elkészítve.\n"
diff --git a/po/id.po b/po/id.po
new file mode 100644 (file)
index 0000000..f360805
--- /dev/null
+++ b/po/id.po
@@ -0,0 +1,2318 @@
+# Indonesian translation of pulseaudio
+# Copyright (C) 2011 THE pulseaudio'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the pulseaudio package.
+#
+# Translators:
+# Andika Triwidada <andika@gmail.com>, 2011, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: PulseAudio\n"
+"Report-Msgid-Bugs-To: http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Community/#bugspatchestranslations\n"
+"POT-Creation-Date: 2010-11-27 01:08+0000\n"
+"PO-Revision-Date: 2012-09-22 04:49+0000\n"
+"Last-Translator: Andika Triwidada <andika@gmail.com>\n"
+"Language-Team: Indonesia <id@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: id\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: ../src/modules/alsa/alsa-util.c:861 ../src/pulsecore/sink.c:2631
+#, c-format
+msgid "%s %s"
+msgstr "%s %s"
+
+#: ../src/modules/alsa/alsa-util.c:1109
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr "snd_pcm_avail() mengembalikan nilai yang luar biasa besar: %lu byte (%lu ms).\nSangat mungkin ini adalah kutu pada driver ALSA '%s'. Silakan laporkan hal ini ke para pengembang ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1150
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr "snd_pcm_delay() mengembalikan nilai yang luar biasa besar: %li byte (%s%lu ms).\nSangat mungkin ini adalah kutu pada driver ALSA '%s'. Silakan laporkan hal ini ke para pengembang ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1197
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr "snd_pcm_mmap_begin() mengembalikan nilai yang luar biasa besar: %lu byte (%lu ms).\nSangat mungkin ini adalah kutu pada driver ALSA '%s'. Silakan laporkan hal ini ke para pengembang ALSA."
+
+#: ../src/modules/module-always-sink.c:39
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Selalu jaga paling tidak satu muara bermuatan bahkan jika berupa null"
+
+#: ../src/modules/module-always-sink.c:83
+msgid "Dummy Output"
+msgstr "Keluaran Dummy"
+
+#: ../src/modules/module-ladspa-sink.c:49
+msgid "Virtual LADSPA sink"
+msgstr "Muara virtual LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:53
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma seperated list of "
+"input control values>"
+msgstr "sink_name=<nama bagi muara> sink_properties=<properti bagi muara> master=<nama muara untuk disaring> format=<format cuplikan> rate=<laju cuplikan> channels=<cacah kanal> channel_map=<peta kanal> plugin=<nama plugin ladspa> label=<label plugin ladspa> control=<daftar nilai kendali masukan yang dipisahkan dengan koma>"
+
+#: ../src/modules/module-null-sink.c:55
+msgid "Clocked NULL sink"
+msgstr "Muara NULL dengan clock"
+
+#: ../src/modules/module-null-sink.c:291
+msgid "Null Output"
+msgstr "Keluaran Null"
+
+#: ../src/pulsecore/sink.c:2615
+msgid "Internal Audio"
+msgstr "Audio Internal"
+
+#: ../src/pulsecore/sink.c:2620
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/daemon/ltdl-bind-now.c:124
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Gagal menemukan pemuat lt_dlopen asli."
+
+#: ../src/daemon/ltdl-bind-now.c:129
+msgid "Failed to allocate new dl loader."
+msgstr "Gagal mengalokasikan pemuat dl baru."
+
+#: ../src/daemon/ltdl-bind-now.c:142
+msgid "Failed to add bind-now-loader."
+msgstr "Gagal menambah bind-now-loader."
+
+#: ../src/daemon/main.c:146
+#, c-format
+msgid "Got signal %s."
+msgstr "Mendapat sinyal %s."
+
+#: ../src/daemon/main.c:173
+msgid "Exiting."
+msgstr "Sedang keluar."
+
+#: ../src/daemon/main.c:191
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Gagal mencari pengguna '%s'."
+
+#: ../src/daemon/main.c:196
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Gagal mencari grup '%s'."
+
+#: ../src/daemon/main.c:200
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Menemukan pengguna '%s' (UID %lu) dan grup '%s' (GID %lu)."
+
+#: ../src/daemon/main.c:205
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID dari pengguna '%s' dan dari grup '%s' tak cocok."
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Direktori rumah dari pengguna '%s' bukan '%s', mengabaikan."
+
+#: ../src/daemon/main.c:213 ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Gagal membuat '%s': %s"
+
+#: ../src/daemon/main.c:225
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Gagal mengubah daftar grup: %s"
+
+#: ../src/daemon/main.c:241
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Gagal mengubah GID: %s"
+
+#: ../src/daemon/main.c:257
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Gagal mengubah UID: %s"
+
+#: ../src/daemon/main.c:276
+msgid "Successfully dropped root privileges."
+msgstr "Sukses melepas hak root."
+
+#: ../src/daemon/main.c:284
+msgid "System wide mode unsupported on this platform."
+msgstr "Mode seluruh-sistem tak didukung pada platform ini."
+
+#: ../src/daemon/main.c:302
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) gagal: %s"
+
+#: ../src/daemon/main.c:502
+msgid "Failed to parse command line."
+msgstr "Gagal mengurai baris perintah."
+
+#: ../src/daemon/main.c:535
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup"
+" service."
+msgstr "Mode sistem ditolak bagi pengguna non root. Hanya memulai layanan pencarian server D-Bus."
+
+#: ../src/daemon/main.c:617
+msgid "Daemon not running"
+msgstr "Daemon tidak sedang berjalan"
+
+#: ../src/daemon/main.c:619
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "Daemon berjalan sebagai PID %u"
+
+#: ../src/daemon/main.c:634
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Gagal membunuh daemon: %s"
+
+#: ../src/daemon/main.c:662
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr "Program ini tidak diinginkan dijalankan sebagai root (kecuali dinyatakan --system)."
+
+#: ../src/daemon/main.c:665
+msgid "Root privileges required."
+msgstr "Dibutuhkan hak root."
+
+#: ../src/daemon/main.c:671
+msgid "--start not supported for system instances."
+msgstr "--start tak didukung bagi instansi sistem."
+
+#: ../src/daemon/main.c:676
+#, c-format
+msgid "User-configured server at %s, not autospawning."
+msgstr "Server yang ditata pengguna pada %s, bukan spawn otomatis."
+
+#: ../src/daemon/main.c:683
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "Berjalan pada mode sistem, tapi --disallow-exit tak ditata!"
+
+#: ../src/daemon/main.c:686
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "Berjalan pada mode sistem, tapi --disallow-module-loading tak ditata!"
+
+#: ../src/daemon/main.c:689
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "Berjalan pada mode sistem, memaksa mematikan mode SHM!"
+
+#: ../src/daemon/main.c:694
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "Berjalan pada mode sistem, memaksa mematikan waktu menganggur keluar!"
+
+#: ../src/daemon/main.c:720
+msgid "Failed to acquire stdio."
+msgstr "Gagal memperoleh stdio."
+
+#: ../src/daemon/main.c:726
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() gagal: %s"
+
+#: ../src/daemon/main.c:731 ../src/daemon/main.c:790
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() gagal: %s"
+
+#: ../src/daemon/main.c:745 ../src/utils/pacat.c:529
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() gagal: %s"
+
+#: ../src/daemon/main.c:751
+msgid "Daemon startup failed."
+msgstr "Gagal memulai daemon."
+
+#: ../src/daemon/main.c:753
+msgid "Daemon startup successful."
+msgstr "Sukses memulai daemon."
+
+#: ../src/daemon/main.c:778
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() gagal: %s"
+
+#: ../src/daemon/main.c:830
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Ini adalah PulseAudio %s"
+
+#: ../src/daemon/main.c:831
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Host kompilasi: %s"
+
+#: ../src/daemon/main.c:832
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "CFLAGS kompilasi: %s"
+
+#: ../src/daemon/main.c:835
+#, c-format
+msgid "Running on host: %s"
+msgstr "Dijalankan pada host: %s"
+
+#: ../src/daemon/main.c:838
+#, c-format
+msgid "Found %u CPUs."
+msgstr "Ditemukan %u CPU."
+
+#: ../src/daemon/main.c:840
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "Ukuran page adalah %lu byte"
+
+#: ../src/daemon/main.c:843
+msgid "Compiled with Valgrind support: yes"
+msgstr "Dikompail dengan dukungan Valgrind: ya"
+
+#: ../src/daemon/main.c:845
+msgid "Compiled with Valgrind support: no"
+msgstr "Dikompail dengan dukungan Valgrind: tidak"
+
+#: ../src/daemon/main.c:848
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Berjalan pada mode valgrind: %s"
+
+#: ../src/daemon/main.c:850
+#, c-format
+msgid "Running in VM: %s"
+msgstr "Dijalankan di VM: %s"
+
+#: ../src/daemon/main.c:853
+msgid "Optimized build: yes"
+msgstr "Pembangunan teroptimasi: ya"
+
+#: ../src/daemon/main.c:855
+msgid "Optimized build: no"
+msgstr "Pembangunan teroptimasi: tidak"
+
+#: ../src/daemon/main.c:859
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG didefinisikan, semua assert dimatikan."
+
+#: ../src/daemon/main.c:861
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH didefinisikan, hanya assert jalur cepat yang dimatikan."
+
+#: ../src/daemon/main.c:863
+msgid "All asserts enabled."
+msgstr "Semua assert diaktifkan."
+
+#: ../src/daemon/main.c:867
+msgid "Failed to get machine ID"
+msgstr "Gagal memperoleh ID mesin"
+
+#: ../src/daemon/main.c:870
+#, c-format
+msgid "Machine ID is %s."
+msgstr "ID mesin adalah %s."
+
+#: ../src/daemon/main.c:874
+#, c-format
+msgid "Session ID is %s."
+msgstr "ID sesi adalah %s."
+
+#: ../src/daemon/main.c:880
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Memakai direktori runtime %s."
+
+#: ../src/daemon/main.c:885
+#, c-format
+msgid "Using state directory %s."
+msgstr "Memakai direktori keadaan %s."
+
+#: ../src/daemon/main.c:888
+#, c-format
+msgid "Using modules directory %s."
+msgstr "Memakai direktori modul %s."
+
+#: ../src/daemon/main.c:890
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Berjalan pada mode sistem: %s"
+
+#: ../src/daemon/main.c:893
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"OK, jadi Anda menjalankan PA dalam mode sistem. Mohon catat bahwa sangat "
+"boleh jadi Anda tak perlu melakukan ini.\n"
+"Namun bila Anda melakukannya juga, salah Anda sendiri bila semua tak bekerja "
+"seperti yang diharapkan.\n"
+"Silakan baca http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ bagi penjelasan mengapa mode "
+"sistem biasanya adalah ide buruk."
+
+#: ../src/daemon/main.c:910
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() gagal."
+
+#: ../src/daemon/main.c:920
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "Pewaktu resolusi tinggi yang segar tersedia! Selamat makan!"
+
+#: ../src/daemon/main.c:922
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with "
+"high-resolution timers enabled!"
+msgstr "Bung, kernel Anda bau! Saran koki hari ini adalah Linux dengan pewaktu resolusi tinggi yang diaktifkan!"
+
+#: ../src/daemon/main.c:945
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() gagal."
+
+#: ../src/daemon/main.c:1008
+msgid "Failed to initialize daemon."
+msgstr "Gagal menginisialisasi daemon."
+
+#: ../src/daemon/main.c:1013
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Daemon dimulai tanpa modul apapun yang dimuat, menolak bekerja."
+
+#: ../src/daemon/main.c:1051
+msgid "Daemon startup complete."
+msgstr "Memulai daemon komplit."
+
+#: ../src/daemon/main.c:1057
+msgid "Daemon shutdown initiated."
+msgstr "Mulai mematikan daemon."
+
+#: ../src/daemon/main.c:1083
+msgid "Daemon terminated."
+msgstr "Daemon diakhiri."
+
+#: ../src/daemon/cmdline.c:115
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory segments\n"
+"      --start                           Start the daemon if it is not running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and this\n"
+"                                        time passed\n"
+"      --module-idle-time=SECS           Unload autoloaded modules when idle and\n"
+"                                        this time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr} Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr "%s [opsi]\n\nPERINTAH:\n  -h, --help                            Tampilkan bantuan ini\n      --version                         Tampilkan versi\n      --dump-conf                       Semua konfigurasi baku\n      --dump-modules                    Daftar semua modul tersedia\n      --dump-resample-methods           Semua metoda cuplik ulang yang tersedia\n      --cleanup-shm                     Bersihkan segmen memori dipakai bersama yang basi\n      --start                           Mulai daemon bila tidak sedang berjalan\n  -k  --kill                            Matikan daemon yang sedang berjalan\n      --check                           Periksa apakah ada daemon yang sedang berjalan\n                                        (hanya mengembalikan kode keluar)\n\nOPSI:\n      --system[=BOOL]                   Jalankan sebagai instansi seluruh sistem\n  -D, --daemonize[=BOOL]                Jadikan daemon setelah awalan\n      --fail[=BOOL]                     Keluar ketika awalan gagal\n      --high-priority[=BOOL]            Coba tata ke aras nice tinggi\n                                        (hanya tersedia sebagai root, ketika SUID\n                                        atau RLIMIT_NICE yang dinaikkan)\n      --realtime[=BOOL]                 Coba fungsikan penjadwalan waktu-nyata\n                                        (hanya tersedia sebagai root, ketika SUID\n                                        atau RLIMIT_RTPRIO yang dinaikkan)\n      --disallow-module-loading[=BOOL]  Larang muat/bongkar modul yang diminta\n                                        pengguna setelah awalan\n      --disallow-exit[=BOOL]            Larang permintaan keluar dari pengguna\n      --exit-idle-time=SECS             Matikan daemon ketika menganggur dan\n                                        waktu ini habis\n      --module-idle-time=SECS           Bongkar modul yang dimuat sendiri ketika\n                                        menganggur dan waktu ini habis\n      --scache-idle-time=SECS           Bongkar contoh yang dimuat sendiri ketika\n                                        menganggur dan waktu ini habis\n      --log-level[=LEVEL]               Naikkan atau tata aras kerincian\n  -v                                    Naikkan aras kerincian\n      --log-target={auto,syslog,stderr} Nyatakan target log\n      --log-meta[=BOOL]                 Sertakan lokasi kode dalam pesan log\n      --log-time[=BOOL]                 Sertakan penanda waktu dalam pesan log\n      --log-backtrace=FRAMES            Sertakan suatu backtrace dalam pesan log\n  -p, --dl-search-path=PATH             Tata path pencarian bagi objek dipakai\n                                        bersama yang dinamis (plugin)\n      --resample-method=METHOD          Gunakan metoda cuplik ulang yang dinyatakan\n                                        (Lihat --dump-resample-methods untuk\n                                        nilai yang mungkin)\n      --use-pid-file[=BOOL]             Buat suatu berkas PID\n      --no-cpu-limit[=BOOL]             Jangan pasang pembatas beban CPU\n                                        pada platform yang mendukungannya.\n      --disable-shm[=BOOL]              Matikan dukungan memori bersama.\n\nSKRIP AWALAN:\n  -L, --load=\"ARGUMEN MODUL\"            Muat modul plugin yang dinyatakan\n                                        dengan argumen yang disertakan\n  -F, --file=NAMABERKAS                 Jalankan skrip yang dinyatakan\n  -C                                    Buka baris perintah pada TTY berjalan\n                                        setelah awalan\n\n  -n                                    Jangan muat berkas skrip baku\n"
+
+#: ../src/daemon/cmdline.c:247
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize mengharapkan argumen bool"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "--fail  mengharapkan argumen bool"
+
+#: ../src/daemon/cmdline.c:264
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr "--log-level mengharapkan argumen aras log (bisa berupa angka 0..4 atau salah satu dari debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:276
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority mengharapkan argumen bool"
+
+#: ../src/daemon/cmdline.c:283
+msgid "--realtime expects boolean argument"
+msgstr "--realtime mengharapkan argumen bool"
+
+#: ../src/daemon/cmdline.c:290
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading mengharapkan argumen bool"
+
+#: ../src/daemon/cmdline.c:297
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit mengharapkan argumen bool"
+
+#: ../src/daemon/cmdline.c:304
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file mengharapkan argumen bool"
+
+#: ../src/daemon/cmdline.c:321
+msgid "Invalid log target: use either 'syslog', 'stderr' or 'auto'."
+msgstr "Tujuan log tak valid: gunakan saah satu dari 'syslog', 'stderr', atau 'auto'."
+
+#: ../src/daemon/cmdline.c:328
+msgid "--log-time expects boolean argument"
+msgstr "--log-time mengharapkan argumen bool"
+
+#: ../src/daemon/cmdline.c:335
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta mengharapkan argumen bool"
+
+#: ../src/daemon/cmdline.c:354
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Metode cuplik ulang tak valid '%s'."
+
+#: ../src/daemon/cmdline.c:361
+msgid "--system expects boolean argument"
+msgstr "--system mengharapkan argumen bool"
+
+#: ../src/daemon/cmdline.c:368
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit mengharapkan argumen bool"
+
+#: ../src/daemon/cmdline.c:375
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm mengharapkan argumen bool"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "Name: %s\n"
+msgstr "Nama: %s\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "No module information available\n"
+msgstr "Informasi modul tak tersedia\n"
+
+#: ../src/daemon/dumpmodules.c:66
+#, c-format
+msgid "Version: %s\n"
+msgstr "Versi: %s\n"
+
+#: ../src/daemon/dumpmodules.c:68
+#, c-format
+msgid "Description: %s\n"
+msgstr "Keterangan: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Author: %s\n"
+msgstr "Penulis: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Cara pakai: %s\n"
+
+#: ../src/daemon/dumpmodules.c:73
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Muat Sekali: %s\n"
+
+#: ../src/daemon/dumpmodules.c:75
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "PERINGATAN KADALUARSA: %s\n"
+
+#: ../src/daemon/dumpmodules.c:79
+#, c-format
+msgid "Path: %s\n"
+msgstr "Path: %s\n"
+
+#: ../src/daemon/daemon-conf.c:251
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Tujuan log tak valid '%s'."
+
+#: ../src/daemon/daemon-conf.c:267
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Aras log tak valid '%s'."
+
+#: ../src/daemon/daemon-conf.c:283
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Metoda cuplikan ulang tak valid '%s'."
+
+#: ../src/daemon/daemon-conf.c:306
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] rlimit tak valid'%s'."
+
+#: ../src/daemon/daemon-conf.c:313
+#, c-format
+msgid "[%s:%u] rlimit not supported on this platform."
+msgstr "[%s:%u] rlimit tak didukung pada platform ini."
+
+#: ../src/daemon/daemon-conf.c:329
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Bentuk cuplikan tak valid '%s'."
+
+#: ../src/daemon/daemon-conf.c:347
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Laju cuplikan tak valid '%s'."
+
+#: ../src/daemon/daemon-conf.c:371
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Kanal cuplikan tak valid '%s'."
+
+#: ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Peta kanal tak valid '%s'."
+
+#: ../src/daemon/daemon-conf.c:407
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Cacah fragmen tak valid '%s'."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Ukuran fragmen tak valid '%s'."
+
+#: ../src/daemon/daemon-conf.c:443
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Aras nice tak valid '%s'."
+
+#: ../src/daemon/daemon-conf.c:479
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Tipe server '%s' tak valid."
+
+#: ../src/daemon/daemon-conf.c:586
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Gagal membaca berkas konfigurasi: %s"
+
+#: ../src/daemon/daemon-conf.c:602
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr "Peta kanal baku yang dinyatakan memiliki cacah kanal yang berbeda dengan cacah kanal baku yang dinyatakan."
+
+#: ../src/daemon/daemon-conf.c:688
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Membaca dari berkas konfigurasi: %s ###\n"
+
+#: ../src/daemon/caps.c:62
+msgid "Cleaning up privileges."
+msgstr "Membersihkan hak khusus."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Sistem Suara PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Memulai Sistem Suara PulseAudio"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Depan Tengah"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Depan Kiri"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Depan Kanan"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Belakang Tengah"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Belakang Kiri"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Belakang Kanan"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr "Subwoofer"
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Depan Tengah agak ke kiri"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Depan Tengah agak ke kanan"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Samping Kiri"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Samping Kanan"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Tambahan 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Tambahan 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Tambahan 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Tambahan 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Tambahan 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Tambahan 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Tambahan 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Tambahan 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Tambahan 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Tambahan 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Tambahan 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Tambahan 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Tambahan 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Tambahan 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Tambahan 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Tambahan 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Tambahan 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Tambahan 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Tambahan 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Tambahan 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Tambahan 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Tambahan 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Tambahan 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Tambahan 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Tambahan 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Tambahan 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Tambahan 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Tambahan 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Tambahan 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Tambahan 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Tambahan 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Tambahan 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Puncak Tengah"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Puncak Depan Tengah"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Puncak Depan Kiri"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Puncak Depan Kanan"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Puncak Belakang Tengah"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Puncak Belakang Kiri"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Puncak Belakang Kanan"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:170
+#: ../src/pulse/volume.c:295 ../src/pulse/volume.c:321
+#: ../src/pulse/volume.c:341 ../src/pulse/volume.c:371
+msgid "(invalid)"
+msgstr "(tak valid)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/error.c:43
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:44
+msgid "Access denied"
+msgstr "Akses ditolak"
+
+#: ../src/pulse/error.c:45
+msgid "Unknown command"
+msgstr "Perintah tak dikenal"
+
+#: ../src/pulse/error.c:46
+msgid "Invalid argument"
+msgstr "Argumen tak valid"
+
+#: ../src/pulse/error.c:47
+msgid "Entity exists"
+msgstr "Entitas ada"
+
+#: ../src/pulse/error.c:48
+msgid "No such entity"
+msgstr "Entitas tak ada"
+
+#: ../src/pulse/error.c:49
+msgid "Connection refused"
+msgstr "Koneksi ditolak"
+
+#: ../src/pulse/error.c:50
+msgid "Protocol error"
+msgstr "Galat protokol"
+
+#: ../src/pulse/error.c:51
+msgid "Timeout"
+msgstr "Habis waktu"
+
+#: ../src/pulse/error.c:52
+msgid "No authorization key"
+msgstr "Tak ada kunci otorisasi"
+
+#: ../src/pulse/error.c:53
+msgid "Internal error"
+msgstr "Galat internal"
+
+#: ../src/pulse/error.c:54
+msgid "Connection terminated"
+msgstr "Sambungan diakhiri"
+
+#: ../src/pulse/error.c:55
+msgid "Entity killed"
+msgstr "Entitas dimatikan"
+
+#: ../src/pulse/error.c:56
+msgid "Invalid server"
+msgstr "Server tak valid"
+
+#: ../src/pulse/error.c:57
+msgid "Module initalization failed"
+msgstr "Inisialisasi modul gagal"
+
+#: ../src/pulse/error.c:58
+msgid "Bad state"
+msgstr "Kondisi buruk"
+
+#: ../src/pulse/error.c:59
+msgid "No data"
+msgstr "Tak ada data"
+
+#: ../src/pulse/error.c:60
+msgid "Incompatible protocol version"
+msgstr "Versi protokol tak kompatibel"
+
+#: ../src/pulse/error.c:61
+msgid "Too large"
+msgstr "Terlalu besar"
+
+#: ../src/pulse/error.c:62
+msgid "Not supported"
+msgstr "Tak didukung"
+
+#: ../src/pulse/error.c:63
+msgid "Unknown error code"
+msgstr "Kode galat tak dikenal"
+
+#: ../src/pulse/error.c:64
+msgid "No such extension"
+msgstr "Tak ada ekstensi demikian"
+
+#: ../src/pulse/error.c:65
+msgid "Obsolete functionality"
+msgstr "Fungsionalitas yang tak berlaku lagi"
+
+#: ../src/pulse/error.c:66
+msgid "Missing implementation"
+msgstr "Tak ada implementasi"
+
+#: ../src/pulse/error.c:67
+msgid "Client forked"
+msgstr "Klien di-fork"
+
+#: ../src/pulse/error.c:68
+msgid "Input/Output error"
+msgstr "Galat masukan/keluaran"
+
+#: ../src/pulse/error.c:69
+msgid "Device or resource busy"
+msgstr "Perangkat atau sumber daya sibuk"
+
+#: ../src/pulse/sample.c:172
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:184
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:186
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:188
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:190
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:55 ../src/utils/pax11publish.c:100
+msgid "XOpenDisplay() failed"
+msgstr "XOpenDisplay() gagal"
+
+#: ../src/pulse/client-conf-x11.c:93
+msgid "Failed to parse cookie data"
+msgstr "Gagal mengurai data cookie"
+
+#: ../src/pulse/client-conf.c:118
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "Gagal membuka berkas konfigurasi '%s': %s"
+
+#: ../src/pulse/context.c:539
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "Tak ada cookie yang dimuat. Mencoba menyambung tanpanya."
+
+#: ../src/pulse/context.c:682
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:737
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1434
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Menerima pesan bagi pengaya tak dikenal '%s'"
+
+#: ../src/utils/pacat.c:110
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Gagal menguras stream: %s"
+
+#: ../src/utils/pacat.c:115
+msgid "Playback stream drained."
+msgstr "Stream main ulang terkuras."
+
+#: ../src/utils/pacat.c:125
+msgid "Draining connection to server."
+msgstr "Menguras sambungan ke server."
+
+#: ../src/utils/pacat.c:138
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:161
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() gagal: %s"
+
+#: ../src/utils/pacat.c:202
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() gagal: %s"
+
+#: ../src/utils/pacat.c:252 ../src/utils/pacat.c:282
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() gagal: %s"
+
+#: ../src/utils/pacat.c:322
+msgid "Stream successfully created."
+msgstr "Stream sukses dibuat."
+
+#: ../src/utils/pacat.c:325
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() gagal: %s"
+
+#: ../src/utils/pacat.c:329
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Metrik penyangga: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Metrik penyangga: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:336
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Memakai spek cuplikan '%s', peta kanal '%s'."
+
+#: ../src/utils/pacat.c:340
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "Tersambung ke perangkat %s (%u, %stersuspensi)."
+
+#: ../src/utils/pacat.c:350
+#, c-format
+msgid "Stream error: %s"
+msgstr "Galat stream: %s"
+
+#: ../src/utils/pacat.c:360
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Perangkat stream disuspensi.%s"
+
+#: ../src/utils/pacat.c:362
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Perangkat stream dilanjutkan.%s"
+
+#: ../src/utils/pacat.c:370
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Stream underrun.%s"
+
+#: ../src/utils/pacat.c:377
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Stream tertimpa.%s"
+
+#: ../src/utils/pacat.c:384
+#, c-format
+msgid "Stream started.%s"
+msgstr "Stream dimulai.%s"
+
+#: ../src/utils/pacat.c:391
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Stream dipindah ke perangkat %s (%u, %sdisuspensi).%s"
+
+#: ../src/utils/pacat.c:391
+msgid "not "
+msgstr "tidak"
+
+#: ../src/utils/pacat.c:398
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Atribut penyangga stream diubah.%s"
+
+#: ../src/utils/pacat.c:430
+#, c-format
+msgid "Connection established.%s"
+msgstr "Koneksi terbentuk.%s"
+
+#: ../src/utils/pacat.c:433
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() gagal: %s"
+
+#: ../src/utils/pacat.c:471
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() gagal: %s"
+
+#: ../src/utils/pacat.c:477
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() gagal: %s"
+
+#: ../src/utils/pacat.c:491 ../src/utils/pactl.c:949
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Kegagalan koneksi: %s"
+
+#: ../src/utils/pacat.c:524
+msgid "Got EOF."
+msgstr "Mendapat EOF."
+
+#: ../src/utils/pacat.c:561
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() gagal: %s"
+
+#: ../src/utils/pacat.c:582
+msgid "Got signal, exiting."
+msgstr "Mendapat sinyal, keluar."
+
+#: ../src/utils/pacat.c:596
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Gagal mendapat latensi: %s"
+
+#: ../src/utils/pacat.c:601
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Waktu: %0.3f dtk; Latensi: %0.0f udtk."
+
+#: ../src/utils/pacat.c:620
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() gagal: %s"
+
+#: ../src/utils/pacat.c:630
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to connect to\n"
+"  -n, --client-name=NAME                How to call this client on the server\n"
+"      --stream-name=NAME                How to call this stream on the server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to 44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the default\n"
+"      --fix-format                      Take the sample format from the sink the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the channel map\n"
+"                                        from the sink the stream is being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of name.\n"
+"      --latency=BYTES                   Request the specified latency in bytes.\n"
+"      --process-time=BYTES              Request the specified process time per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr "%s [opsi]\n\n  -h, --help                            Tampilkan bantuan ini\n      --version                         Tampilkan versi\n\n  -r, --record                          Buat koneksi untuk perekaman\n  -p, --playback                        Buat koneksi untuk main ulang\n\n  -v, --verbose                         Aktifkan operasi cerewet\n\n  -s, --server=SERVER                   Nama server untuk dihubungi\n  -d, --device=DEVICE                   Nama muara/sumber untuk dihubungi\n  -n, --client-name=NAME                Bagaimana memanggil klien ini di server\n      --stream-name=NAME                Bagaimana memanggil stream ini di server\n      --volume=VOLUME                   Nyatakan volume awal (linier) dalam jangkauan 0...65536\n      --rate=SAMPLERATE                 Laju cuplikan dalam Hz (nilai baku 44100)\n      --format=SAMPLEFORMAT             Jenis cuplikan, salah satu dari s16le, s16be, u8, float32le,\n                                        float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n                                        s24-32le, s24-32be (nilai baku s16ne)\n      --channels=CHANNELS               Cacah kanal, 1 untuk mono, 2 untuk stereo\n                                        (nilai baku 2)      --channel-map=CHANNELMAP          Peta kanal yang dipakai sebagai pengganti baku\n      --fix-format                      Ambil format cuplikan dari muara stream\n                                        yang sedang tersambung.\n      --fix-rate                        Ambil laju cuplikan dari muara stream\n                                        yang sedang tersambung.\n      --fix-channels                    Ambil cacah kanal dan peta kanal dari muara stream\n                                        yang sedang tersambung.\n      --no-remix                        Jangan upmix atau downmix kanal.\n\n      --no-remap                        Petakan kanal berdasar indeks bukan nama.\n      --latency=BYTES                   Minta latensi yang dinyatakan, dalam byte.\n      --process-time=BYTES              Minta waktu proses yang dinyatakan bagi tiap permintaan\n                                        dalam byte.\n      --latency-msec=MSEC               Minta latensi yang dinyatakan, dalam milidetik.\n      --process-time-msec=MSEC          Minta waktu proses yang dinyatakan bagi tiap permintaan\n                                        dalam milidetik.\n      --property=PROPERTY=VALUE         Tata properti yang dinyatakan ke nilai yang dinyatakan.\n      --raw                             Rekam/mainkan data PCM mentah.\n      --file-format[=FFORMAT]           Rekam/mainkan data PCM terformat.\n      --list-file-formats               Tampilkan daftar format berkas yang tersedia.\n"
+
+#: ../src/utils/pacat.c:758
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr "pacat %s\nDikompail dengan libpulse %s\nDitaut dengan libpulse %s\n"
+
+#: ../src/utils/pacat.c:791 ../src/utils/pactl.c:1046
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Nama klien '%s' tak valid"
+
+#: ../src/utils/pacat.c:806
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Nama stream '%s' tak valid"
+
+#: ../src/utils/pacat.c:843
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Peta kanal '%s' tak valid"
+
+#: ../src/utils/pacat.c:872 ../src/utils/pacat.c:886
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Spesifikasi latensi '%s' tak valid"
+
+#: ../src/utils/pacat.c:879 ../src/utils/pacat.c:893
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Spesifikasi waktu proses '%s' tak valid"
+
+#: ../src/utils/pacat.c:905
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Properti '%s' tak valid"
+
+#: ../src/utils/pacat.c:922
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Format berkas %s tak dikenal."
+
+#: ../src/utils/pacat.c:941
+msgid "Invalid sample specification"
+msgstr "Spesifikasi cuplikan tak valid"
+
+#: ../src/utils/pacat.c:951
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:956
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:963
+msgid "Too many arguments."
+msgstr "Terlalu banyak argumen."
+
+#: ../src/utils/pacat.c:974
+msgid "Failed to generate sample specification for file."
+msgstr "Gagal menjangkitkan spesifikasi cuplikan bagi berkas."
+
+#: ../src/utils/pacat.c:994
+msgid "Failed to open audio file."
+msgstr "Gagal membuka berkas audio."
+
+#: ../src/utils/pacat.c:1000
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr "Peringatan: spesifikasi cuplikan yang dinyatakan akan ditimpa oleh spesifikasi dari berkas."
+
+#: ../src/utils/pacat.c:1003 ../src/utils/pactl.c:1090
+msgid "Failed to determine sample specification from file."
+msgstr "Gagal menentukan spesifikasi cuplikan dari berkas."
+
+#: ../src/utils/pacat.c:1012
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Peringatan: Gagal menentukan peta kanal dari berkas."
+
+#: ../src/utils/pacat.c:1023
+msgid "Channel map doesn't match sample specification"
+msgstr "Peta kanan tak cocok dengan spesifikasi cuplikan"
+
+#: ../src/utils/pacat.c:1034
+msgid "Warning: failed to write channel map to file."
+msgstr "Peringatan: gagal menulis peta kanal ke berkas."
+
+#: ../src/utils/pacat.c:1049
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "Sedang membuka stream %s dengan spesifikasi cuplikan '%s' dan peta kanal '%s'."
+
+#: ../src/utils/pacat.c:1050
+msgid "recording"
+msgstr "merekam"
+
+#: ../src/utils/pacat.c:1050
+msgid "playback"
+msgstr "memainkan"
+
+#: ../src/utils/pacat.c:1076 ../src/utils/pactl.c:1364
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() gagal."
+
+#: ../src/utils/pacat.c:1095
+msgid "io_new() failed."
+msgstr "io_new() gagal."
+
+#: ../src/utils/pacat.c:1102 ../src/utils/pactl.c:1376
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() gagal."
+
+#: ../src/utils/pacat.c:1110 ../src/utils/pactl.c:1382
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() gagal: %s"
+
+#: ../src/utils/pacat.c:1116
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() gagal."
+
+#: ../src/utils/pacat.c:1123 ../src/utils/pactl.c:1387
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() gagal."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Gagal mensuspensi: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Gagal melanjutkan: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "PERINGATAN: Server suara bukan lokal, tidak mensuspensi.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Kegagalan sambungan: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Mendapat SIGINT, keluar.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "PERINGATAN: Proses anak diakhiri oleh sinyal %u\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"\n"
+msgstr "%s [opsi] ... \n\n  -h, --help                            Tampilkan bantuan ini\n      --version                         Tampilkan versi\n  -s, --server=SERVER                   Nama server untuk dihubungi\n\n"
+
+#: ../src/utils/pasuspender.c:246
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr "pasuspender %s\nDikompail dengan libpulse %s\nDitaut dengan libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:275
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() gagal.\n"
+
+#: ../src/utils/pasuspender.c:288
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() gagal.\n"
+
+#: ../src/utils/pasuspender.c:296
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() gagal.\n"
+
+#: ../src/utils/pactl.c:134
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Gagal mendapat statistik: %s"
+
+#: ../src/utils/pactl.c:140
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Sedang dipakai: %u blok memuat total %s byte.\n"
+
+#: ../src/utils/pactl.c:143
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Dialokasikan dalam seluruh masa hidup: %u blok memuat total %s byte.\n"
+
+#: ../src/utils/pactl.c:146
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Ukuran singgahan cuplikan: %s\n"
+
+#: ../src/utils/pactl.c:155
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Gagal mendapat informasi server: %s"
+
+#: ../src/utils/pactl.c:160
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr "String Server: %s\nVersi Protokol Pustaka: %u\nVersi Protokol Server: %u\nDi Lokal: %s\nIndeks Klien: %u\nUkuran Ubin: %zu\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr "Nama Pengguna: %s\nNama Host: %s\nNama Server: %s\nVersi Server: %s\nSpesifikasi Cuplikan Baku: %s\nPeta Kanal Baku: %s\nMuara Baku: %s\nSumber Baku: %s\nCookie: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:218
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Gagal mendapat informasi muara: %s"
+
+#: ../src/utils/pactl.c:234
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr "Muara #%u\n\tKeadaan: %s\n\tNama: %s\n\tKeterangan: %s\n\tDriver: %s\n\tSpesifikasi Cuplikan: %s\n\tPeta Kanal: %s\n\tModul Pemilik: %u\n\tSenyap: %s\n\tVolume: %s%s%s\n\t        balans %0.2f\n\tVolume Dasar: %s%s%s\n\tSumber Pemantau: %s\n\tLatensi: %0.0f usec, dikonfigurasi %0.0f usec\n\tBendera: %s%s%s%s%s%s\n\tProperti:\n\t\t%s\n"
+
+#: ../src/utils/pactl.c:281 ../src/utils/pactl.c:373
+#, c-format
+msgid "\tPorts:\n"
+msgstr "»Port:\n"
+
+#: ../src/utils/pactl.c:287 ../src/utils/pactl.c:379
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "»Port Aktif: %s\n"
+
+#: ../src/utils/pactl.c:310
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Gagal mendapat informasi sumber: %s"
+
+#: ../src/utils/pactl.c:326
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr "Sumber #%u\n\tKeadaan: %s\n\tNama: %s\n\tKeterangan: %s\n\tDriver: %s\n\tSpesifikasi Cuplikan: %s\n\tPeta Kanal: %s\n\tModul Pemilik: %u\n\tSenyap: %s\n\tVolume: %s%s%s\n\t        balans %0.2f\n\tVolume Dasar: %s%s%s\n\tPemantau Muara: %s\n\tLatensi: %0.0f usec, dikonfigurasi %0.0f usec\n\tBendera: %s%s%s%s%s%s\n\tProperti:\n\t\t%s\n"
+
+#: ../src/utils/pactl.c:358 ../src/utils/pactl.c:414 ../src/utils/pactl.c:449
+#: ../src/utils/pactl.c:486 ../src/utils/pactl.c:545 ../src/utils/pactl.c:546
+#: ../src/utils/pactl.c:556 ../src/utils/pactl.c:600 ../src/utils/pactl.c:601
+#: ../src/utils/pactl.c:607 ../src/utils/pactl.c:650 ../src/utils/pactl.c:651
+#: ../src/utils/pactl.c:658
+msgid "n/a"
+msgstr "t/t"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Gagal mendapat informasi modul: %s"
+
+#: ../src/utils/pactl.c:406
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr "Modul #%u\n\tNama: %s\n\tArgumen: %s\n\tPencacah pemakaian: %s\n\tProperti:\n\t\t%s\n"
+
+#: ../src/utils/pactl.c:425
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Gagal mendapat informasi klien: %s"
+
+#: ../src/utils/pactl.c:443
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr "Klien #%u\n\tDriver: %s\n\tModul Pemilik: %s\n\tProperti:\n\t\t%s\n"
+
+#: ../src/utils/pactl.c:460
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Gagal mendapat informasi kartu: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr "Kartu #%u\n\tNama: %s\n\tDriver: %s\n\tModul Pemilik: %s\n\tProperti:\n\t\t%s\n"
+
+#: ../src/utils/pactl.c:492
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfil:\n"
+
+#: ../src/utils/pactl.c:498
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tProfil Aktif: %s\n"
+
+#: ../src/utils/pactl.c:509
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Gagal mendapat informasi masukan muara: %s"
+
+#: ../src/utils/pactl.c:528
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr "Masukan Muara #%u\n\tDriver: %s\n\tModul Pemilik: %s\n\tKlien: %s\n\tMuara: %u\n\tSpesifikasi Cuplikan: %s\n\tPeta Kanal: %s\n\tSenyap: %s\n\tVolume: %s\n\t        %s\n\t        balans %0.2f\n\tLatensi Penyangga: %0.0f usec\n\tLatensi Muara: %0.0f usec\n\tMetoda cuplik ulang: %s\n\tProperti:\n\t\t%s\n"
+
+#: ../src/utils/pactl.c:567
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Gagal mendapat informasi keluaran sumber: %s"
+
+#: ../src/utils/pactl.c:587
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr "Luaran Sumber #%u\n\tDriver: %s\n\tModul Pemilik: %s\n\tKlien: %s\n\tSumber: %u\n\tSpesifikasi Cuplikan: %s\n\tPeta Kanal: %s\n\tLatensi Penyangga: %0.0f usec\n\tLatensi Sumber: %0.0f usec\n\tMetoda cuplik ulang: %s\n\tProperti:\n\t\t%s\n"
+
+#: ../src/utils/pactl.c:618
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Gagal mendapat informasi cuplikan: %s"
+
+#: ../src/utils/pactl.c:636
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr "Cuplikan #%u\n\tNama: %s\n\tSpesifikasi Cuplikan: %s\n\tPeta Kanal: %s\n\tVolume: %s\n\t        %s\n\t        balans %0.2f\n\tDurasi: %0.1fs\n\tUkuran: %s\n\tMalas: %s\n\tNama Berkas: %s\n\tProperti:\n\t\t%s\n"
+
+#: ../src/utils/pactl.c:666 ../src/utils/pactl.c:676
+#, c-format
+msgid "Failure: %s"
+msgstr "Kegagalan: %s"
+
+#: ../src/utils/pactl.c:700
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Gagal mengunggah cuplikan: %s"
+
+#: ../src/utils/pactl.c:717
+msgid "Premature end of file"
+msgstr "Akhir berkas dini"
+
+#: ../src/utils/pactl.c:737
+msgid "new"
+msgstr "baru"
+
+#: ../src/utils/pactl.c:740
+msgid "change"
+msgstr "ubah"
+
+#: ../src/utils/pactl.c:743
+msgid "remove"
+msgstr "hapus"
+
+#: ../src/utils/pactl.c:746 ../src/utils/pactl.c:781
+msgid "unknown"
+msgstr "tak dikenal"
+
+#: ../src/utils/pactl.c:754
+msgid "sink"
+msgstr "muara"
+
+#: ../src/utils/pactl.c:757
+msgid "source"
+msgstr "sumber"
+
+#: ../src/utils/pactl.c:760
+msgid "sink-input"
+msgstr "masukan-muara"
+
+#: ../src/utils/pactl.c:763
+msgid "source-output"
+msgstr "sumber-keluaran"
+
+#: ../src/utils/pactl.c:766
+msgid "module"
+msgstr "modul"
+
+#: ../src/utils/pactl.c:769
+msgid "client"
+msgstr "klien"
+
+#: ../src/utils/pactl.c:772
+msgid "sample-cache"
+msgstr "singgahan-cuplikan"
+
+#: ../src/utils/pactl.c:775 ../src/utils/pactl.c:778
+msgid "server"
+msgstr "server"
+
+#: ../src/utils/pactl.c:787
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Kejadian '%s' pada %s #%u\n"
+
+#: ../src/utils/pactl.c:955
+msgid "Got SIGINT, exiting."
+msgstr "Mendapat SIGINT, keluar."
+
+#: ../src/utils/pactl.c:961
+#, c-format
+msgid ""
+"%s [options] stat\n"
+"%s [options] list\n"
+"%s [options] exit\n"
+"%s [options] upload-sample FILENAME [NAME]\n"
+"%s [options] play-sample NAME [SINK]\n"
+"%s [options] remove-sample NAME\n"
+"%s [options] move-sink-input SINKINPUT SINK\n"
+"%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+"%s [options] load-module NAME [ARGS ...]\n"
+"%s [options] unload-module MODULE\n"
+"%s [options] suspend-sink SINK 1|0\n"
+"%s [options] suspend-source SOURCE 1|0\n"
+"%s [options] set-card-profile CARD PROFILE\n"
+"%s [options] set-sink-port SINK PORT\n"
+"%s [options] set-source-port SOURCE PORT\n"
+"%s [options] set-sink-volume SINK VOLUME\n"
+"%s [options] set-source-volume SOURCE VOLUME\n"
+"%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+"%s [options] set-sink-mute SINK 1|0\n"
+"%s [options] set-source-mute SOURCE 1|0\n"
+"%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+"%s [options] subscribe\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"  -n, --client-name=NAME                How to call this client on the server\n"
+msgstr "%s [opsi] stat\n%s [opsi] list\n%s [opsi] exit\n%s [opsi] upload-sample NAMABERKAS [NAMA]\n%s [opsi] play-sample NAMA [MUARA]\n%s [opsi] remove-sample NAMA\n%s [opsi] move-sink-input MUARAINPUT MUARA\n%s [opsi] move-source-output SUMBEROUTPUT SUMBER\n%s [opsi] load-module NAMA [ARGS ...]\n%s [opsi] unload-module MODULE\n%s [opsi] suspend-sink MUARA 1|0\n%s [opsi] suspend-source SUMBER 1|0\n%s [opsi] set-card-profile CARD PROFILE\n%s [opsi] set-sink-port MUARA PORT\n%s [opsi] set-source-port SUMBER PORT\n%s [opsi] set-sink-volume MUARA VOLUME\n%s [opsi] set-source-volume SUMBER VOLUME\n%s [opsi] set-sink-input-volume MUARAINPUT VOLUME\n%s [opsi] set-sink-mute MUARA 1|0\n%s [opsi] set-source-mute SUMBER 1|0\n%s [opsi] set-sink-input-mute MUARAINPUT 1|0\n%s [opsi] subscribe\n\n  -h, --help                            Tampilkan bantuan ini\n      --version                         Tampilkan versi\n\n  -s, --server=SERVER                   Nama server untuk dihubungi  -n, --client-name=NAMA                Bagaimana memanggil klien ini di server\n"
+
+#: ../src/utils/pactl.c:1026
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr "pactl %s\nDikompail dengan libpulse %s\nDitaut dengan libpulse %s\n"
+
+#: ../src/utils/pactl.c:1072
+msgid "Please specify a sample file to load"
+msgstr "Nyatakan berkas cuplikan untuk dimuat"
+
+#: ../src/utils/pactl.c:1085
+msgid "Failed to open sound file."
+msgstr "Gagal membuka berkas suara."
+
+#: ../src/utils/pactl.c:1097
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Peringatan: Gagal menentukan spesifikasi cuplikan dari berkas."
+
+#: ../src/utils/pactl.c:1107
+msgid "You have to specify a sample name to play"
+msgstr "Anda mesti menyatakan nama cuplikan untuk diputar"
+
+#: ../src/utils/pactl.c:1119
+msgid "You have to specify a sample name to remove"
+msgstr "Anda mesti menyatakan nama cuplikan untuk dihapus"
+
+#: ../src/utils/pactl.c:1128
+msgid "You have to specify a sink input index and a sink"
+msgstr "Anda mesti menyatakan suatu indeks masukan muara dan suatu muara"
+
+#: ../src/utils/pactl.c:1138
+msgid "You have to specify a source output index and a source"
+msgstr "Anda mesti menyatakan suatu indeks keluaran sumber dan suatu sumber"
+
+#: ../src/utils/pactl.c:1153
+msgid "You have to specify a module name and arguments."
+msgstr "Anda mesti menyatakan nama modul dan argumen."
+
+#: ../src/utils/pactl.c:1173
+msgid "You have to specify a module index"
+msgstr "Anda mesti menyatakan indeks modul"
+
+#: ../src/utils/pactl.c:1183
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr "Anda tak boleh menyatakan lebih dari satu muara. Anda mesti menyatakan suatu nilai bool."
+
+#: ../src/utils/pactl.c:1196
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr "Anda tak boleh menyatakan lebih dari satu sumber. Anda mesti menyatakan suatu nilai bool."
+
+#: ../src/utils/pactl.c:1208
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Anda mesti menyatakan suatu indeks/nama kartu dan suatu nama profil"
+
+#: ../src/utils/pactl.c:1219
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Anda mesti menyatakan suatu indeks/nama muara dan suatu nama port"
+
+#: ../src/utils/pactl.c:1230
+msgid "You have to specify a source name/index and a port name"
+msgstr "Anda mesti menyatakan suatu indeks/nama sumber dan suatu nama port"
+
+#: ../src/utils/pactl.c:1242
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Anda mesti menyatakan suatu indeks/nama muara dan suatu volume"
+
+#: ../src/utils/pactl.c:1247 ../src/utils/pactl.c:1264
+#: ../src/utils/pactl.c:1286 ../src/utils/pactl.c:1302
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1341
+msgid "Invalid volume specification"
+msgstr "Spesifikasi volume tak valid"
+
+#: ../src/utils/pactl.c:1259
+msgid "You have to specify a source name/index and a volume"
+msgstr "Anda mesti menyatakan suatu indeks/nama sumber dan suatu volume"
+
+#: ../src/utils/pactl.c:1276
+msgid "You have to specify a sink input index and a volume"
+msgstr "Anda mesti menyatakan suatu indeks masukan muara dan suatu volume"
+
+#: ../src/utils/pactl.c:1281
+msgid "Invalid sink input index"
+msgstr "Indeks masukan muara tak valid"
+
+#: ../src/utils/pactl.c:1297
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "Anda mesti menyatakan suatu indeks/nama muara dan suatu bool mute"
+
+#: ../src/utils/pactl.c:1314
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Anda mesti menyatakan suatu indeks/nama sumber dan suatu bool mute"
+
+#: ../src/utils/pactl.c:1331
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "Anda mesti menyatakan indeks masukan muara dan suatu bool mute"
+
+#: ../src/utils/pactl.c:1336
+msgid "Invalid sink input index specification"
+msgstr "Spesifikasi index masukan muara tak valid"
+
+#: ../src/utils/pactl.c:1359
+msgid "No valid command specified."
+msgstr "Tak ada perintah valid yang dinyatakan."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr "%s [-D display] [-S server] [-O muara] [-I sumber] [-c file]  [-d|-e|-i|-r]\n\n -d    Tampilkan data PulseAudio yang kini dicantolkan ke tampilan X11 (baku)\n -e    Ekspor data PulseAudio lokal ke tampilan X11\n -i    Impor data PulseAudio dari tampilan X11 ke variabel lingkungan lokal dan berkas cookie.\n -r    Hapus data PulseAudio dari tampilan X11\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Gagal mengurai baris perintah.\n"
+
+#: ../src/utils/pax11publish.c:108
+#, c-format
+msgid "Server: %s\n"
+msgstr "Server: %s\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Source: %s\n"
+msgstr "Sumber: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Muara: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:132
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Gagal mengurai data cookie\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Gagal menyimpan data cookie\n"
+
+#: ../src/utils/pax11publish.c:152
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "Gagal memuat berkas konfigurasi klien.\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "Gagal membaca data konfigurasi lingkungan.\n"
+
+#: ../src/utils/pax11publish.c:174
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Gagal mendapatkan FQDN.\n"
+
+#: ../src/utils/pax11publish.c:194
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Gagal memuat data cookie\n"
+
+#: ../src/utils/pax11publish.c:211
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Belum diimplementasikan.\n"
+
+#: ../src/utils/pacmd.c:65
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "Tak ada daemon PulseAudio yang berjalan, atau tak dijalankan sebagai daemon sesi."
+
+#: ../src/utils/pacmd.c:70
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:87
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:95
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Gagal mematikan daemon PulseAudio."
+
+#: ../src/utils/pacmd.c:103
+msgid "Daemon not responding."
+msgstr "Daemon tidak merespon."
+
+#: ../src/utils/pacmd.c:178
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:189 ../src/utils/pacmd.c:209
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:231 ../src/utils/pacmd.c:249
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:134 ../src/pulsecore/lock-autospawn.c:217
+msgid "Cannot access autospawn lock."
+msgstr "Tak bisa akses kunci spawn sendiri."
+
+#: ../src/modules/alsa/alsa-sink.c:530 ../src/modules/alsa/alsa-sink.c:689
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."
+msgstr "ALSA bangun untuk menulis data baru ke perangkat, tapi sebenarnya tak ada sesuatu untuk ditulis!\nSangat mungkin ini adalah kutu pada driver ALSA '%s'. Silakan laporkan masalah ini ke para pengembang ALSA.\nKami dibangunkan dengan POLLOUT diset -- namun snd_pcm_avail() setelahnya mengembalikan 0 atau nilai lain < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:506 ../src/modules/alsa/alsa-source.c:656
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."
+msgstr "ALSA bangun untuk membaca data baru dari perangkat, tapi sebenarnya tak ada sesuatu untuk dibaca!\nSangat mungkin ini adalah kutu pada driver ALSA '%s'. Silakan laporkan masalah ini ke para pengembang ALSA.\nKami dibangunkan dengan POLLIN diset -- namun snd_pcm_avail() setelahnya mengembalikan 0 atau nilai lain < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:152
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2262
+#: ../src/modules/alsa/alsa-mixer.c:2936
+msgid "Off"
+msgstr "Mati"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2204
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Main Ulang High Fidelity (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2218
+msgid "High Fidelity Capture (A2DP)"
+msgstr "High Fidelity Capture (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2233
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Dupleks Teleponi (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2247
+msgid "Handsfree Gateway"
+msgstr "Gateway Handsfree"
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "Server Suara PulseAudio"
+
+#: ../src/modules/module-rygel-media-server.c:592
+#: ../src/modules/module-rygel-media-server.c:606
+msgid "Output Devices"
+msgstr "Perangkat Keluaran"
+
+#: ../src/modules/module-rygel-media-server.c:593
+#: ../src/modules/module-rygel-media-server.c:607
+msgid "Input Devices"
+msgstr "Perangkat Masukan"
+
+#: ../src/modules/module-rygel-media-server.c:797
+msgid "Audio on @HOSTNAME@"
+msgstr "Audio pada @HOSTNAME@"
+
+#: ../src/modules/alsa/alsa-mixer.c:1701
+msgid "Input"
+msgstr "Masukan"
+
+#: ../src/modules/alsa/alsa-mixer.c:1702
+msgid "Docking Station Input"
+msgstr "Masukan Docking Station"
+
+#: ../src/modules/alsa/alsa-mixer.c:1703
+msgid "Docking Station Microphone"
+msgstr "Mikrofon Docking Station"
+
+#: ../src/modules/alsa/alsa-mixer.c:1704
+msgid "Line-In"
+msgstr "Line-In"
+
+#: ../src/modules/alsa/alsa-mixer.c:1705
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:1706
+msgid "External Microphone"
+msgstr "Mikrofon Eksternal"
+
+#: ../src/modules/alsa/alsa-mixer.c:1707
+msgid "Internal Microphone"
+msgstr "Mikrofon Internal"
+
+#: ../src/modules/alsa/alsa-mixer.c:1708
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:1709
+msgid "Video"
+msgstr "Video"
+
+#: ../src/modules/alsa/alsa-mixer.c:1710
+msgid "Automatic Gain Control"
+msgstr "Kendali Penguatan Otomatis (AGC)"
+
+#: ../src/modules/alsa/alsa-mixer.c:1711
+msgid "No Automatic Gain Control"
+msgstr "Tanpa Kendali Penguatan Otomatis (AGC)"
+
+#: ../src/modules/alsa/alsa-mixer.c:1712
+msgid "Boost"
+msgstr "Boost"
+
+#: ../src/modules/alsa/alsa-mixer.c:1713
+msgid "No Boost"
+msgstr "Tanpa Boost"
+
+#: ../src/modules/alsa/alsa-mixer.c:1714
+msgid "Amplifier"
+msgstr "Penguat"
+
+#: ../src/modules/alsa/alsa-mixer.c:1715
+msgid "No Amplifier"
+msgstr "Tanpa Penguat"
+
+#: ../src/modules/alsa/alsa-mixer.c:1716
+msgid "Bass Boost"
+msgstr "Boost Bass"
+
+#: ../src/modules/alsa/alsa-mixer.c:1717
+msgid "No Bass Boost"
+msgstr "Tanpa Boost Bass"
+
+#: ../src/modules/alsa/alsa-mixer.c:1718
+msgid "Speaker"
+msgstr "Speaker"
+
+#: ../src/modules/alsa/alsa-mixer.c:1719
+msgid "Headphones"
+msgstr "Headphone"
+
+#: ../src/modules/alsa/alsa-mixer.c:1777
+msgid "Analog Input"
+msgstr "Masukan Analog"
+
+#: ../src/modules/alsa/alsa-mixer.c:1778
+msgid "Analog Microphone"
+msgstr "Mikrofon Analog"
+
+#: ../src/modules/alsa/alsa-mixer.c:1779
+msgid "Analog Line-In"
+msgstr "Line-In Analog"
+
+#: ../src/modules/alsa/alsa-mixer.c:1780
+msgid "Analog Radio"
+msgstr "Radio Analog"
+
+#: ../src/modules/alsa/alsa-mixer.c:1781
+msgid "Analog Video"
+msgstr "Video Analog"
+
+#: ../src/modules/alsa/alsa-mixer.c:1782
+msgid "Analog Output"
+msgstr "Keluaran Analog"
+
+#: ../src/modules/alsa/alsa-mixer.c:1783
+msgid "Analog Headphones"
+msgstr "Headphone Analog"
+
+#: ../src/modules/alsa/alsa-mixer.c:1784
+msgid "Analog Output (LFE)"
+msgstr "Keluaran Analog (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:1785
+msgid "Analog Mono Output"
+msgstr "Keluaran Mono Analog"
+
+#: ../src/modules/alsa/alsa-mixer.c:1786
+msgid "Analog Speakers"
+msgstr "Speaker Analog"
+
+#: ../src/modules/alsa/alsa-mixer.c:1986
+#, c-format
+msgid "%s+%s"
+msgstr "%s+%s"
+
+#: ../src/modules/alsa/alsa-mixer.c:1989 ../src/modules/alsa/alsa-mixer.c:3409
+#, c-format
+msgid "%s / %s"
+msgstr "%s / %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:2795
+msgid "Analog Mono"
+msgstr "Analog Mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:2796
+msgid "Analog Stereo"
+msgstr "Analog Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2797
+msgid "Analog Surround 2.1"
+msgstr "Analog Surround 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:2798
+msgid "Analog Surround 3.0"
+msgstr "Analog Surround 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:2799
+msgid "Analog Surround 3.1"
+msgstr "Analog Surround 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:2800
+msgid "Analog Surround 4.0"
+msgstr "Analog Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:2801
+msgid "Analog Surround 4.1"
+msgstr "Analog Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:2802
+msgid "Analog Surround 5.0"
+msgstr "Analog Surround 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:2803
+msgid "Analog Surround 5.1"
+msgstr "Analog Surround 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:2804
+msgid "Analog Surround 6.0"
+msgstr "Analog Surround 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:2805
+msgid "Analog Surround 6.1"
+msgstr "Analog Surround 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:2806
+msgid "Analog Surround 7.0"
+msgstr "Analog Surround 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:2807
+msgid "Analog Surround 7.1"
+msgstr "Analog Surround 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:2808
+msgid "Digital Stereo (IEC958)"
+msgstr "Digital Stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2809
+msgid "Digital Surround 4.0 (IEC958)"
+msgstr "Digital Surround 4.0 (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2810
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digital Surround 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2811
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digital Surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2812
+msgid "Digital Stereo (HDMI)"
+msgstr "Digital Stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2933
+msgid "Analog Mono Duplex"
+msgstr "Analog Mono Duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:2934
+msgid "Analog Stereo Duplex"
+msgstr "Analog Stereo Duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:2935
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Digital Stereo Duplex (IEC958)"
diff --git a/po/it.po b/po/it.po
new file mode 100644 (file)
index 0000000..d71d74c
--- /dev/null
+++ b/po/it.po
@@ -0,0 +1,3334 @@
+# Italian translation for PulseAudio.
+# Copyright (C) 2008, 2009, 2012, 2015 The Free Software Foundation, Inc
+# This file is distributed under the same license as the pulseaudio package.
+#
+# Luca Ferretti <elle.uca@libero.it>, 2008, 2009.
+# mario_santagiuliana <mario at marionline.it>, 2009.
+# Milo Casagrande <milo@ubuntu.com>, 2009, 2012, 2015.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-06-08 13:18+0200\n"
+"PO-Revision-Date: 2017-06-08 13:26+0200\n"
+"Last-Translator: Milo Casagrande <milo@milo.name>\n"
+"Language-Team: Italian <tp@lists.linux.it>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 1.8.12\n"
+
+# mamma mia che impressione
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [OPZIONI]\n"
+"\n"
+"COMANDI:\n"
+"  -h, --help                            Mostra questo aiuto\n"
+"      --version                         Mostra la versione\n"
+"      --dump-conf                       Riversa la configurazione "
+"predefinita\n"
+"      --dump-modules                    Riversa l'elenco dei moduli "
+"disponibili\n"
+"      --dump-resample-methods           Riversa i metodi di ricampionamento\n"
+"                                        disponibili\n"
+"      --cleanup-shm                     Pulisce i segmenti di memoria "
+"condivisa\n"
+"                                        esauriti\n"
+"      --start                           Avvia il demone se non è in "
+"esecuzione\n"
+"  -k  --kill                            Uccide un demone in esecuzione\n"
+"      --check                           Controlla la presenza di un demone "
+"in\n"
+"                                        esecuzione (restituisce solo il "
+"codice\n"
+"                                        di uscita)\n"
+"\n"
+"OPZIONI:\n"
+"      --system[=BOOL]                   Esegue un'istanza di sistema\n"
+"  -D, --daemonize[=BOOL]                Rende demone dopo l'avvio\n"
+"      --fail[=BOOL]                     Esce quando l'avvio non riesce\n"
+"      --high-priority[=BOOL]            Tenta di impostare un livello di "
+"nice\n"
+"                                        elevato (disponibile solo come "
+"root,\n"
+"                                        quando SUID o con RLIMIT_NICE "
+"elevato)\n"
+"      --realtime[=BOOL]                 Tenta di abilitare lo scheduling\n"
+"                                        realtime (disponibile solo come "
+"root,\n"
+"                                        quando SUID o con RLIMIT_RTPRIO "
+"elevato)\n"
+"      --disallow-module-loading[=BOOL]  Rifiuta il caricamento/rimozione "
+"dei\n"
+"                                        moduli richiesti dall'utente dopo \n"
+"                                        l'avvio\n"
+"      --disallow-exit[=BOOL]            Rifiuta le richieste utente di "
+"uscita\n"
+"      --exit-idle-time=SECONDI          Termina il demone quando inattivo e "
+"una\n"
+"                                        volta trascorso questo tempo\n"
+"      --scache-idle-time=SECONDI        Rimuove i campioni caricati in modo\n"
+"                                        automatico quando inattivo e una "
+"volta\n"
+"                                        trascorso questo tempo\n"
+"      --log-level[=LIVELLO]             Incrementa o imposta il livello di\n"
+"                                        verbosità\n"
+"  -v  --verbose                         Incrementa il livello di verbosità\n"
+"      --log-target={auto,syslog,stderr,file:PERC,newfile:PERC}\n"
+"                                        Specifica la destinazione del "
+"registro\n"
+"      --log-meta[=BOOL]                 Include la posizione del codice nei\n"
+"                                        messaggi di registro\n"
+"      --log-time[=BOOL]                 Include i marcatempo nei messaggi "
+"di\n"
+"                                        registro\n"
+"      --log-backtrace=FRAME             Include un backtrace nei messaggi "
+"di\n"
+"                                        registro\n"
+"  -p, --dl-search-path=PERCORSO         Imposta il percorso di ricerca per "
+"gli\n"
+"                                        oggetti condivisi dinamici (plugin)\n"
+"      --resample-method=METODO          Usa il metodo di ricampionamento "
+"indicato\n"
+"                                        (vedere --dump-resample-methods per "
+"i\n"
+"                                        valori ammessi)\n"
+"      --use-pid-file[=BOOL]             Crea un file PID\n"
+"      --no-cpu-limit[=BOOL]             Non installa un limitatore di "
+"carico\n"
+"                                        della CPU sulle piattaforme che lo\n"
+"                                        supportano.\n"
+"      --disable-shm[=BOOL]              Disabilita il supporto alla memoria\n"
+"                                        condivisa.\n"
+"      --enable-memfd[=BOOL]             Abilita il support alla memoria\n"
+"                                        condivisa memfd\n"
+"\n"
+"SCRIPT DI AVVIO:\n"
+"  -L, --load=\"MODULO ARGOMENTI\"       Carica il modulo di plugin "
+"specificato\n"
+"                                        con gli argomenti specificati\n"
+"  -F, --file=NOME_FILE                  Esegue lo script specificato\n"
+"  -C                                    Apre una riga di comando sul TTY in\n"
+"                                        esecuzione dopo l'avvio\n"
+"\n"
+"  -n                                    Non carica il file script "
+"predefinito\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "--fail richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level richiede il livello di registro come argomento (sia "
+"nell'intervallo numerico 0..4 oppure uno tra debug, info, notice, warn, "
+"error)."
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "--realtime richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Destinazione del registro non valida: usare \"syslog\", \"journal\", \"stderr"
+"\" o \"auto\" oppure un nome di file valido \"file:<percorso>\", \"newfile:"
+"<percorso>\"."
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Destinazione del registro non valida: usare \"syslog\", \"stderr\" o \"auto"
+"\" oppure un nome di file valido \"file:<percorso>\", \"newfile:<percorso>\"."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "--log-time richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Metodo di ricampionamento \"%s\" non valido."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "--system richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm richiede un argomento booleano"
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr "--enable-memfd richiede un argomento booleano"
+
+#: ../src/daemon/daemon-conf.c:262
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Destinazione di registro \"%s\" non valida."
+
+#: ../src/daemon/daemon-conf.c:277
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Livello di registro \"%s\" non valido."
+
+#: ../src/daemon/daemon-conf.c:292
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Metodo di ricampionamento \"%s\" non valido."
+
+#: ../src/daemon/daemon-conf.c:314
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] rlimit \"%s\" non valido."
+
+# o campionamento?? ma campionamento non è sampling?
+#: ../src/daemon/daemon-conf.c:334
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Formato di campionamento \"%s\" non valido."
+
+#: ../src/daemon/daemon-conf.c:351 ../src/daemon/daemon-conf.c:368
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Frequenza di campionamento '%s' non valida."
+
+#: ../src/daemon/daemon-conf.c:391
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Canali di campionamento \"%s\" non validi."
+
+#: ../src/daemon/daemon-conf.c:408
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Mappa del canale \"%s\" non valida."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Numero di frammenti \"%s\" non valido."
+
+#: ../src/daemon/daemon-conf.c:442
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Dimensione dei frammenti \"%s\" non valida."
+
+#: ../src/daemon/daemon-conf.c:459
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Livello di nice \"%s\" non valido."
+
+#: ../src/daemon/daemon-conf.c:502
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Tipo di server \"%s\" non valido."
+
+#: ../src/daemon/daemon-conf.c:620
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Apertura del file di configurazione non riuscita: %s"
+
+#: ../src/daemon/daemon-conf.c:636
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"La mappa del canale predefinita specificata presenta un numero diverso di "
+"canali rispetto a quello predefinito specificato."
+
+#: ../src/daemon/daemon-conf.c:723
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Lettura dal file di configurazione: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Nome: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Nessuna informazione disponibile sul modulo\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Versione: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Descrizione: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autore: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Uso: %s\n"
+
+# %s è sì/no
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Caricato una sola volta: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "ATTENZIONE, DEPRECATI: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Percorso: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Apertura del modulo %s non riuscita: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Ricerca del loader lt_dlopen originale non riuscita."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Allocazione del nuovo loader dl non riuscita."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Aggiunta di bind-now-loader non riuscita."
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Ricerca dell'utente \"%s\" non riuscita."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Ricerca del gruppo \"%s\" non riuscita."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "Il GID dell'utente \"%s\" e del gruppo \"%s\" non corrispondono."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "La directory home dell'utente \"%s\" non è \"%s\", ignorato."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Creazione di \"%s\" non riuscita: %s"
+
+# group list ????
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Cambio dell'elenco di gruppo non riuscito: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Cambio di GID non riuscito: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Cambio di UID non riuscito: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "Modalità di sistema non supportata su questa piattaforma."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Analisi della riga di comando non riuscita."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Modalità sistema non concessa a utenti non root. Viene avviato solamente il "
+"servizio di lookup del server D-Bus."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Terminazione del demone non riuscita: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Questo programma non è pensato per essere eseguito come root (a meno di "
+"specificare --system)."
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Richiesti privilegi di root."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "--start non supportato per le istanze di sistema."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+"Server configurato dall'utente in %s, si rifiuta di avviarsi o di eseguire "
+"autospawn."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Server configurato dall'utente in %s, sembra essere locale. Esame più "
+"approfondito."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr "In esecuzione in modalità sistema, ma --disallow-exit non impostato."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr ""
+"In esecuzione in modalità sistema, ma --disallow-module-loading non "
+"impostato."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr ""
+"In esecuzione in modalità sistema, disabilitata in modo forzato la modalità "
+"SHM."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr ""
+"In esecuzione in modalità sistema, disabilitato in modo forzato il tempo di "
+"uscita per inattività."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "Acquisizione di STDIO non riuscita."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() non riuscita: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() non riuscita: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:562
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() non riuscita: %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Avvio del demone non riuscito."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() non riuscita: %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Recupero dell'ID della macchina non riuscito"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually "
+"do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"PulseAudio è in esecuzione in modalità sistema. Assicurarsi che sia "
+"esattamente ciò che si desidera fare.\n"
+"Consultare http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/"
+"User/WhatIsWrongWithSystemWide/ per maggiori informazioni sul perché la "
+"modalità sistema è una pessima idea."
+
+#: ../src/daemon/main.c:990
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() non riuscita."
+
+#: ../src/daemon/main.c:1022
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() non riuscita."
+
+#: ../src/daemon/main.c:1092
+msgid "Failed to initialize daemon."
+msgstr "Inizializzazione del demone non riuscita."
+
+#: ../src/daemon/main.c:1097
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Avvio del demone senza alcun modulo caricato, rifiuta di lavorare."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Sistema sonoro PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Avvia il sistema sonoro PulseAudio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Ingresso"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Ingresso docking station"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Microfono docking station"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Linea in docking station"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Line-In"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1750
+msgid "Microphone"
+msgstr "Microfono"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Microfono anteriore"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Microfono posteriore"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Microfono esterno"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Microfono interno"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Video"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Controllo automatico del guadagno"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Nessun controllo automatico del guadagno"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Boost"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Nessun boost"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Amplificatore"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Nessun amplificatore"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Incremento bassi"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Nessun incremento bassi"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1757
+msgid "Speaker"
+msgstr "Altoparlante"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Cuffie analogiche"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Ingresso analogico"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Microfono docking station"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Microfono auricolare"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Uscita analogica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "LFE su uscita mono separata"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Line-Out"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Uscita mono analogica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Altoparlanti"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Uscita digitale (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Ingresso digitale (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Passthrough digitale (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Ingresso multi canale"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Uscita multi canale"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Mono analogico"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Stereo analogico"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Multi canale"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Surround analogico 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Surround analogico 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Surround analogico 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Surround analogico 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Surround analogico 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Surround analogico 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Surround analogico 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Surround analogico 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Surround analogico 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Surround analogico 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Surround analogico 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Stereo digitale (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Passthrough digitale (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Surround digitale 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Surround digitale 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Surround digitale 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Stereo digitale (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Surround digitale 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4019 ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Mono Duplex"
+msgstr "Duplex mono analogico"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Analog Stereo Duplex"
+msgstr "Duplex stereo analogico"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Duplex stereo digitale (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+msgid "Multichannel Duplex"
+msgstr "Duplex multi canale"
+
+#: ../src/modules/alsa/alsa-mixer.c:4156
+msgid "Stereo Duplex"
+msgstr "Duplex stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4157
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2324
+#: ../src/modules/bluetooth/module-bluez5-device.c:2005
+msgid "Off"
+msgstr "Spento"
+
+#: ../src/modules/alsa/alsa-mixer.c:4256
+#, c-format
+msgid "%s Output"
+msgstr "Uscita «%s»"
+
+#: ../src/modules/alsa/alsa-mixer.c:4264
+#, c-format
+msgid "%s Input"
+msgstr "Ingresso «%s»"
+
+#: ../src/modules/alsa/alsa-sink.c:572
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Attivazione da parte di ALSA per scrivere nuovi dati sul dispositivo, ma non "
+"c'era nulla da scrivere.\n"
+"Molto probabilmente si tratta di un bug nei driver ALSA \"%s\". Segnalare "
+"questo problema agli sviluppatori di ALSA.\n"
+"Attivazione avvenuta con POLLOUT impostato -- tuttavia, una successiva "
+"snd_pcm_avail() ha ritornato 0 o un altro valore < min_avail."
+
+#: ../src/modules/alsa/alsa-sink.c:756
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Attivazione da parte di ALSA per scrivere nuovi dati sul dispositivo, ma non "
+"c'era nulla da scrivere.\n"
+"Molto probabilmente si tratta di un bug nei driver ALSA \"%s\". Segnalare "
+"questo problema agli sviluppatori di ALSA.\n"
+"Attivazione avvenuta con POLLOUT impostato -- tuttavia, una successiva "
+"snd_pcm_avail() ha ritornato 0 o un altro valore < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Attivazione da parte di ALSA per leggere nuovi dati dal dispositivo, ma non "
+"c'era nulla da leggere.\n"
+"Molto probabilmente si tratta di un bug nei driver ALSA \"%s\". Segnalare "
+"questo problema agli sviluppatori di ALSA.\n"
+"Attivazione avvenuta con POLLIN impostato -- tuttavia, una successiva "
+"snd_pcm_avail() ha ritornato 0 o un altro valore < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Attivazione da parte di ALSA per leggere nuovi dati dal dispositivo, ma non "
+"c'era nulla da leggere.\n"
+"Molto probabilmente si tratta di un bug nei driver ALSA \"%s\". Segnalare "
+"questo problema agli sviluppatori di ALSA.\n"
+"Attivazione avvenuta con POLLIN impostato -- tuttavia, una successiva "
+"snd_pcm_avail() ha ritornato 0 o un altro valore < min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1168 ../src/modules/alsa/alsa-util.c:1243
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ha restituito un valore molto grande: %lu byte (%lu ms).\n"
+"Molto probabilmente si tratta di un bug nel driver ALSA \"%s\". Segnalare "
+"questo problema agli sviluppatori ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1218
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() ha restituito un valore molto grande: %li byte (%s%lu ms).\n"
+"Molto probabilmente si tratta di un bug nel driver ALSA \"%s\". Segnalare "
+"questo problema agli sviluppatori ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1259
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ha restituito dei valori strani: delay %lu è minore di avail "
+"%lu.\n"
+"Molto probabilmente si tratta di un bug nel driver ALSA \"%s\". Segnalare "
+"questo problema agli sviluppatori ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1302
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() ha restituito un valore molto grande: %lu byte (%lu "
+"ms).\n"
+"Molto probabilmente si tratta di un bug nel driver ALSA \"%s\". Segnalare "
+"questo problema agli sviluppatori ALSA."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1740
+msgid "Headset"
+msgstr "Cuffie con microfono"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1745
+msgid "Handsfree"
+msgstr "Sistema mani-libere"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1763
+msgid "Headphone"
+msgstr "Cuffie"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1768
+msgid "Portable"
+msgstr "Portabile"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1773
+msgid "Car"
+msgstr "Automobile"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1778
+msgid "HiFi"
+msgstr "HiFi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2134
+#: ../src/modules/bluetooth/module-bluez5-device.c:1783
+msgid "Phone"
+msgstr "Telefono"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2142
+#: ../src/modules/bluetooth/module-bluez5-device.c:1735
+#: ../src/modules/bluetooth/module-bluez5-device.c:1751
+#: ../src/modules/bluetooth/module-bluez5-device.c:1789
+msgid "Bluetooth Output"
+msgstr "Uscita Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2145
+#: ../src/modules/bluetooth/module-bluez5-device.c:1734
+#: ../src/modules/bluetooth/module-bluez5-device.c:1756
+#: ../src/modules/bluetooth/module-bluez5-device.c:1762
+#: ../src/modules/bluetooth/module-bluez5-device.c:1788
+msgid "Bluetooth Input"
+msgstr "Ingresso Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2186
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Riproduzione ad alta fedeltà (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2198
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Cattura ad alta fedeltà (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2210
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Doppino telefonico (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2223
+msgid "Handsfree Gateway"
+msgstr "Gateway handsfree"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1830
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Riproduzione ad alta fedeltà (sink A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1842
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Registrazione ad alta fedeltà (sorgente A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1854
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Unità headset head (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1867
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Gateway headset audio (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<nome della sorgente> source_properties=<proprietà della "
+"sorgente> source_master=<nome della sorgente da filtrare> sink_name=<nome "
+"del sink> sink_properties=<proprietà del sink> sink_master=<nome del sink da "
+"filtrare> adjust_time=<quando spesso regolare il campionamento in s> "
+"adjust_threshold=<di quanto regolare lo scansamento in ms> format=<formato "
+"compionamento> rate=<frequenza campionamento> channels=<numeo di canali> "
+"channel_map=<mappa canali ingresso> aec_method=<implementazione da usare> "
+"aec_args=<parametri per il motore AEC> save_aec=<salva i dati AEC in /tmp> "
+"autoloaded=<imposta se il modulo viene caricato automaticamente> "
+"use_volume_sharing=<yes o no> use_master_format=<yes o no> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "On"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Uscita dummy"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Mantiene sempre almeno un sink caricato anche se è nullo"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Equalizzatore generale"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<nome del sink> sink_properties=<proprietà del sink> "
+"sink_master=<sink a cui connettersi> format=<formato campionamento> "
+"rate=<frequenza campionamento> channels=<numero di canali> "
+"channel_map=<mappa canali> autoloaded=<imposta se il modulo viene caricato "
+"automaticamente> use_volume_sharing=<yes o no> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<rimuovere automaticamente i filtri non usati?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Sink LADSPA virtuale"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<input channel map> plugin=<ladspa plugin name> label=<ladspa "
+"plugin label> control=<comma separated list of input control values> "
+"input_ladspaport_map=<comma separated list of input LADSPA port names> "
+"output_ladspaport_map=<comma separated list of output LADSPA port names> "
+"autoloaded=<set if this module is being loaded automatically> "
+msgstr ""
+"sink_name=<nome del sink> sink_properties=<proprietà del sink> master=<nome "
+"del sink da filtrare> sink_master=<nome del sink da filtrare> "
+"format=<formato campionamento> rate=<frequenza campionamento> "
+"channels=<numero di canali> channel_map=<mappa canali ingresso> plugin=<nome "
+"plugin ladspa> label=<etichetta plugin ladspa> control=<valori di controllo "
+"separati da virgole> input_ladspaport_map=<nomi di porte d'ingresso LADSPA "
+"separati da virgole> output_ladspaport_map=<nomi di porte d'uscita LADSPA "
+"separati da virgole> autoloaded=<imposta se il modulo viene caricato "
+"automaticamente> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Sink NULL temporizzato"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Uscita nulla"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Dispositivi di uscita"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Dispositivi di ingresso"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Audio su @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Tunnel per %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:521
+#: ../src/modules/module-tunnel-source-new.c:520
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Tunnel verso %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Sink surround virtuale"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<channel map> use_volume_sharing=<yes or no> "
+"force_flat_volume=<yes or no> hrir=/path/to/left_hrir.wav autoloaded=<set if "
+"this module is being loaded automatically> "
+msgstr ""
+"sink_name=<nome del sink> sink_properties=<proprietà del sink> master=<nome "
+"del sink da filtrare> sink_master=<nome del sink da filtrare> "
+"format=<formato campionamento> rate=<frequenza campionamento> "
+"channels=<numero di canali> channel_map=<mappa dei canali> "
+"use_volume_sharing=<yes o no> force_flat_volume=<yes o no> hrir=/percorso/al/"
+"file/left_hrir.wav autoloaded=<imposta se il modulo viene caricato "
+"automaticamente> "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "Server sonoro PulseAudio"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Mono"
+
+# frontale centrale non si usa in HiFi
+# solo centrale.
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Centrale"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Frontale sinistro"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Frontale destro"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Centrale posteriore"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Posteriore sinistro"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Posteriore destro"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Subwoofer"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Frontale sinistra-del-centro"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Frontale destra-del-centro"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Laterale sinistro"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Laterale destro"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Ausiliario 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Ausiliario 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Ausiliario 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Ausiliario 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Ausiliario 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Ausiliario 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Ausiliario 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Ausiliario 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Ausiliario 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Ausiliario 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Ausiliario 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Ausiliario 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Ausiliario 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Ausiliario 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Ausiliario 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Ausiliario 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Ausiliario 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Ausiliario 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Ausiliario 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Ausiliario 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Ausiliario 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Ausiliario 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Ausiliario 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Ausiliario 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Ausiliario 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Ausiliario 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Ausiliario 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Ausiliario 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Ausiliario 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Ausiliario 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Ausiliario 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Ausiliario 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Centrale superiore"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Superiore frontale centrale"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Superiore frontale sinistro"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Superiore frontale destro"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Superiore posteriore centrale"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Superiore posteriore sinistro"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Superiore posteriore destro"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:177 ../src/pulse/volume.c:306
+#: ../src/pulse/volume.c:332 ../src/pulse/volume.c:352
+#: ../src/pulse/volume.c:384 ../src/pulse/volume.c:424
+#: ../src/pulse/volume.c:443
+msgid "(invalid)"
+msgstr "(non valido)"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() non riuscita"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() ha restituito VERO"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Analisi dei dati cookie non riuscita"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Ricevuto messaggio per l'estensione sconosciuta \"%s\""
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "ingresso"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "uscita"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "bidirezionale"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "non valido"
+
+#: ../src/pulsecore/core-util.c:1856
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"XDG_RUNTIME_DIR (%s) non è di nostra proprietà (uid %d), ma dallo uid %d "
+"(ciò potrebbe verificarsi se si tenta la connessione come utente root verso "
+"PulseAudio eseguito non da root, attraverso il protocollo nativo)."
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "sì"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "no"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Impossibile accedere al lock di autospawn."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Impossibile aprire il file di destinazione «%s»."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Tentativo di aprire i file di destinazione «%s», «%s.1», «%s.2» ... «%s.%d» "
+"non riuscito."
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Destinazione di registrazione non valida."
+
+#: ../src/pulsecore/sink.c:3469
+msgid "Built-in Audio"
+msgstr "Audio interno"
+
+#: ../src/pulsecore/sink.c:3474
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Accesso negato"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Comando sconosciuto"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Argomento non valido"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "L'entità esiste"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Entità inesistente"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Connessione rifiutata"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Errore di protocollo"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Timeout"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Nessuna chiave di autenticazione"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Errore interno"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Connessione terminata"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Entità uccisa"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Server non valido"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Inizializzazione del modulo non riuscita"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Stato errato"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Nessun dato"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Versione di protocollo incompatibile"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Troppo grande"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Non supportato"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Codice d'errore sconosciuto"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Estensione inesistente"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Funzionalità obsoleta"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Implementazione mancante"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Fork del client"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Errore di input/output"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Dispositivo o risorsa occupata"
+
+#: ../src/pulse/sample.c:179
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s ch %u %u Hz"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:197
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:134
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Svuotamento dello stream non riuscito: %s"
+
+#: ../src/utils/pacat.c:139
+msgid "Playback stream drained."
+msgstr "Stream di riproduzione svuotato."
+
+#: ../src/utils/pacat.c:150
+msgid "Draining connection to server."
+msgstr "Svuotamento della connessione sul server."
+
+#: ../src/utils/pacat.c:163
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:194 ../src/utils/pacat.c:543
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() non riuscita: %s"
+
+#: ../src/utils/pacat.c:244 ../src/utils/pacat.c:274
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() non riuscita: %s"
+
+#: ../src/utils/pacat.c:324
+msgid "Stream successfully created."
+msgstr "Creazione dello stream riuscita."
+
+#: ../src/utils/pacat.c:327
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() non riuscita: %s"
+
+# maxlength, fragsize e gli altri non so se vanno tradotti...
+#: ../src/utils/pacat.c:331
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Metriche del buffer: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+# maxlength e fragsize non so se vanno tradotti...
+#: ../src/utils/pacat.c:334
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Metriche del buffer: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:338
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "In uso specifica di campionamento \"%s\", mappa dei canali \"%s\"."
+
+#: ../src/utils/pacat.c:342
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Collegato al dispositivo %s (indice: %u, sospeso: %s)."
+
+#: ../src/utils/pacat.c:352
+#, c-format
+msgid "Stream error: %s"
+msgstr "Errore di stream: %s"
+
+#: ../src/utils/pacat.c:362
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Device stream sospeso.%s "
+
+#: ../src/utils/pacat.c:364
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Device stream ripristinato.%s "
+
+#: ../src/utils/pacat.c:372
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Underrun dello stream.%s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Overrun dello stream.%s"
+
+#: ../src/utils/pacat.c:386
+#, c-format
+msgid "Stream started.%s"
+msgstr "Stream avviato.%s"
+
+#: ../src/utils/pacat.c:393
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Stream spostato sul device %s (%u, %ssospeso).%s"
+
+#: ../src/utils/pacat.c:393
+msgid "not "
+msgstr "non "
+
+#: ../src/utils/pacat.c:400
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Attributi del buffer di stream cambiati.%s"
+
+#: ../src/utils/pacat.c:415
+msgid "Cork request stack is empty: corking stream"
+msgstr "Lo stack delle richieste di blocco è vuoto: viene bloccato il flusso"
+
+#: ../src/utils/pacat.c:421
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Lo stack delle richieste di blocco è vuoto: viene sbloccato il flusso"
+
+#: ../src/utils/pacat.c:425
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr "Attenzione: ricevute più richieste di sblocco che di blocco."
+
+#: ../src/utils/pacat.c:450
+#, c-format
+msgid "Connection established.%s"
+msgstr "Connessione stabilita.%s"
+
+#: ../src/utils/pacat.c:453
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() non riuscita: %s"
+
+#: ../src/utils/pacat.c:491
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() non riuscita: %s"
+
+#: ../src/utils/pacat.c:497
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Impostazione dello stream di monitor non riuscita: %s"
+
+#: ../src/utils/pacat.c:501
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() non riuscita: %s"
+
+#: ../src/utils/pacat.c:514 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Connessione non riuscita: %s"
+
+#: ../src/utils/pacat.c:557
+msgid "Got EOF."
+msgstr "Ricevuto EOF."
+
+#: ../src/utils/pacat.c:581
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() non riuscita: %s"
+
+#: ../src/utils/pacat.c:605
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() non riuscita: %s"
+
+#: ../src/utils/pacat.c:626
+msgid "Got signal, exiting."
+msgstr "Ricevuto il segnale, uscita."
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Recupero della latenza non riuscito: %s"
+
+# dubbio: tempo o durata??
+#: ../src/utils/pacat.c:645
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Tempo: %0.3f sec; Latenza: %0.0f microsec."
+
+#: ../src/utils/pacat.c:666
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() non riuscita: %s"
+
+#: ../src/utils/pacat.c:676
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [OPZIONI]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Mostra questo aiuto\n"
+"      --version                         Mostra la versione\n"
+"\n"
+"  -r, --record                          Crea una connessione per registrare\n"
+"  -p, --playback                        Crea una connessione per riprodurre\n"
+"\n"
+"  -v, --verbose                         Abilita la modalità prolissa\n"
+"\n"
+"  -s, --server=SERVER                   Il nome del server a cui "
+"connettersi\n"
+"  -d, --device=DEVICE                   Il nome del sink/sorgente a cui\n"
+"                                        connettersi\n"
+"  -n, --client-name=NOME                Come chiamare questo client sul "
+"server\n"
+"      --stream-name=NOME                Come chiamare questo stream sul "
+"server\n"
+"      --volume=VOLUME                   Specifica il volume iniziale "
+"(lineare) \n"
+"                                        nell'intervallo 0...65536\n"
+"      --rate=FREQ_CAMP                  La frequenza di campionamento in Hz\n"
+"                                        (44100 come predefinita)\n"
+"      --format=FORM_CAMP                Il tipo di campionamento. Valori "
+"ammessi\n"
+"                                        sono: s16le, s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be\n"
+"                                        s24le, s24be, s24-32le, s24-32be\n"
+"                                        (s16ne come predefinito)\n"
+"      --channels=CANALI                 Il numero di canali, 1 per mono, 2 "
+"per\n"
+"                                        stereo (2 come predefinito)\n"
+"      --channel-map=MAP_CANALI          La mappa dei canali da usare al "
+"posto di\n"
+"                                        quella predefinita\n"
+"      --fix-format                      Recupera il formato di "
+"campionamento\n"
+"                                        dal sink a cui lo stream sta per "
+"essere\n"
+"                                        connesso\n"
+"      --fix-rate                        Recupera la frequenza di "
+"campionamento\n"
+"                                        dal sink a cui lo stream sta per "
+"essere\n"
+"                                        connesso\n"
+"      --fix-channels                    Recupera il numero di canali e la "
+"mappa\n"
+"                                        dei canali dal sink a cui lo "
+"stream \n"
+"                                        sta per essere connesso\n"
+"      --no-remix                        Non esegue l'upmix o il downmix \n"
+"                                        dei canali\n"
+"      --no-remap                        Mappa i canali per indice invece "
+"che \n"
+"                                        per nome\n"
+"      --latency=BYTE                    Richiede la latenza specificata in "
+"byte\n"
+"      --process-time=BYTE               Richiede il tempo di elaborazione "
+"per\n"
+"                                        richiesta specificato in byte\n"
+"      --latency-msec=MSEC               Richiede la latenza specificata in "
+"msec\n"
+"      --process-time-msec=MSEC          Richiede il tempo di elaborazione "
+"per\n"
+"                                        richiesta specificato in msec\n"
+"      --property=PROPRIETÀ=VAL          Imposta la proprietà al valore "
+"specificato\n"
+"      --raw                             Registra/riproduce dati PCM grezzi\n"
+"      --passthrough                     Dati passthrough\n"
+"      --file-format=FFORMAT             Registra/riproduce dati PCM "
+"formattati\n"
+"      --list-file-formats               Elenca i formati disponibili\n"
+"      --monitor-stream=INDICE           Registra dall'input sink con INDICE\n"
+
+#: ../src/utils/pacat.c:793
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr "Riproduce file audio codificati su un server audio PulseAudio."
+
+#: ../src/utils/pacat.c:797
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr "Cattura dati audio da un server audio PulseAudio e lo scrive su file."
+
+#: ../src/utils/pacat.c:801
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+"Cattura dati audio da un server audio PulseAudio e lo scrive sullo STDOUT o "
+"sul file specificato."
+
+#: ../src/utils/pacat.c:805
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+"Riproduce dati audio dallo STDIN o dal file specificato su un server audio "
+"PulseAudio."
+
+#: ../src/utils/pacat.c:819
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Compilato con libpulse %s\n"
+"Link eseguito con libpulse %s\n"
+
+#: ../src/utils/pacat.c:852 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Nome del client \"%s\" non valido"
+
+#: ../src/utils/pacat.c:867
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Nome dello stream \"%s\" non valido"
+
+#: ../src/utils/pacat.c:904
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Mappa dei canali \"%s\" non valida"
+
+#: ../src/utils/pacat.c:933 ../src/utils/pacat.c:947
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Specifica di latenza \"%s\" non valida"
+
+# esecuzione???
+#: ../src/utils/pacat.c:940 ../src/utils/pacat.c:954
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Specifica di tempo di elaborazione \"%s\" non valida"
+
+#: ../src/utils/pacat.c:966
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Proprietà \"%s\" non valida"
+
+#: ../src/utils/pacat.c:985
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Formato file %s sconosciuto."
+
+#: ../src/utils/pacat.c:1000
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Analisi dell'argomento per --monitor-stream non riuscita"
+
+#: ../src/utils/pacat.c:1011
+msgid "Invalid sample specification"
+msgstr "Specifica di campionamento non valida"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1026
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1033
+msgid "Too many arguments."
+msgstr "Troppi argomenti."
+
+#: ../src/utils/pacat.c:1044
+msgid "Failed to generate sample specification for file."
+msgstr "Generazione della specifica di campionamento per il file non riuscita."
+
+#: ../src/utils/pacat.c:1070
+msgid "Failed to open audio file."
+msgstr "Apertura del file audio non riuscita."
+
+#: ../src/utils/pacat.c:1076
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Attenzione: la specifica di campionamento indicata verrà soprascritta con "
+"quella dal file."
+
+#: ../src/utils/pacat.c:1079 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Determinazione della specifica di campionamento dal file non riuscita."
+
+#: ../src/utils/pacat.c:1088
+msgid "Warning: Failed to determine channel map from file."
+msgstr ""
+"Attenzione: determinazione della mappa dei canali dal file non riuscita."
+
+#: ../src/utils/pacat.c:1099
+msgid "Channel map doesn't match sample specification"
+msgstr "La mappa dei canali non corrisponde alla specifica di campionamento"
+
+#: ../src/utils/pacat.c:1110
+msgid "Warning: failed to write channel map to file."
+msgstr "Attenzione: scrittura della mappa dei canali su file non riuscita."
+
+#: ../src/utils/pacat.c:1125
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Apertura di uno stream %s con specifica di campionamento \"%s\" e mappa dei "
+"canali \"%s\"."
+
+#: ../src/utils/pacat.c:1126
+msgid "recording"
+msgstr "registrazione"
+
+#: ../src/utils/pacat.c:1126
+msgid "playback"
+msgstr "riproduzione"
+
+#: ../src/utils/pacat.c:1150
+msgid "Failed to set media name."
+msgstr "Impostazione nome del supporto non riuscita."
+
+#: ../src/utils/pacat.c:1160 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() non riuscita."
+
+#: ../src/utils/pacat.c:1183
+msgid "io_new() failed."
+msgstr "io_new() non riuscita."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() non riuscita."
+
+#: ../src/utils/pacat.c:1198 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() non riuscita: %s"
+
+#: ../src/utils/pacat.c:1204
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() non riuscita."
+
+#: ../src/utils/pacat.c:1211 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() non riuscita."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "NOME [ARG ...]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "NOME|#N"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "NOME"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "NOME|#N VOLUME"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N VOLUME"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "NOME|#N 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "NOME|#N CHIAVE=VALORE"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#N CHIAVE=VALORE"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "NOME SINK|#N"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "NOME NOMEFILE"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "NOMEPERCORSO"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "NOMEFILE SINK|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#N SINK|SORGENTE"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "PROFILO SCHEDA"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "NOME|#N PORTA"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "NOME-SCHEDA|SCHEDA-#N PORTA OFFSET"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "OBIETTIVO"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "LIVELLO-NUMERICO"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "FRAME"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Mostra questo aiuto\n"
+"      --version                         Mostra la versione\n"
+"Quando non viene fornito alcun comando, pacmd si avvia in modalità "
+"interattiva.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Compilato con libpulse %s\n"
+"Collegato con libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Nessun demone PulseAudio in esecuzione o non in esecuzione come demone di "
+"sessione."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Uccisione del demone PulseAudio non riuscita."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Il demone non sta rispondendo."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Recupero delle statistiche non riuscito: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Attualmente in uso: %u blocchi contenenti %s byte in totale.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Allocati durante l'intera esecuzione: %u blocchi contenenti %s byte in "
+"totale.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Dimensione della cache dei campioni: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Recupero delle informazioni del server non riuscito: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Stringa server: %s\n"
+"Versione protocollo libreria: %u\n"
+"Versione protocollo server: %u\n"
+"Locale: %s\n"
+"Indice client: %u\n"
+"Dimensione tile: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Nome utente: %s\n"
+"Nome host: %s\n"
+"Nome server: %s\n"
+"Versione server: %s\n"
+"Specifica di campionamento predefinita: %s\n"
+"Mappa del canale predefinita: %s\n"
+"Sink predefinito: %s\n"
+"Sorgente predefinita: %s\n"
+"Cookie: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Recupero delle informazioni del sink non riuscito: %s"
+
+# nel relativo messaggio per il source
+# c'è "monitor of sink", quindi assumo che
+# qui dovesse essere "monitor of source"
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink #%u\n"
+"\tStato: %s\n"
+"\tNome: %s\n"
+"\tDescrizione: %s\n"
+"\tDriver: %s\n"
+"\tSpecifica di campionamento: %s\n"
+"\tMappa dei canali: %s\n"
+"\tModulo di appartenenza: %u\n"
+"\tMuto: %s\n"
+"\tVolume: %s\n"
+"\t        bilanciamento %0.2f\n"
+"\tVolume base: %s\n"
+"\tMonitor della sorgente: %s\n"
+"\tLatenza: %0.0f usec, configurata %0.0f usec\n"
+"\tFlag: %s%s%s%s%s%s%s\n"
+"\tProprietà:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPorte:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tPorta attiva: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tFormati:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Recupero delle informazioni della sorgente non riuscito: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sorgente #%u\n"
+"\tStato: %s\n"
+"\tNome: %s\n"
+"\tDescrizione: %s\n"
+"\tDriver: %s\n"
+"\tSpecifica di campionamento: %s\n"
+"\tMappa dei canali: %s\n"
+"\tModulo di appartenenza: %u\n"
+"\tMuto: %s\n"
+"\tVolume: %s\n"
+"\t        bilanciamento %0.2f\n"
+"\tVolume base: %s\n"
+"\tMonitor del sink: %s\n"
+"\tLatenza: %0.0f usec, configurata %0.0f usec\n"
+"\tFlag: %s%s%s%s%s%s\n"
+"\tProprietà:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "N/D"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Recupero delle informazioni del modulo non riuscito: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modulo #%u\n"
+"\tNome: %s\n"
+"\tArgomento: %s\n"
+"\tContatore utilizzi: %s\n"
+"\tProprietà:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Recupero delle informazioni del client non riuscito: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tModulo di appartenenza: %s\n"
+"\tProprietà:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Recupero delle informazioni della scheda non riuscito: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Scheda #%u\n"
+"\tNome: %s\n"
+"\tDriver: %s\n"
+"\tModulo di appartenenza: %s\n"
+"\tProprietà:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfili:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr "\t\t%s: %s (sink: %u, sorgenti: %u, priorità: %u, disponibile: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tProfilo attivo: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tProprietà:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tParte dei profili: %s"
+
+# Sink input
+# A stream that is connected to an output device, i.e. an input for a sink.
+#
+# from http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/WritingVolumeControlUIs/
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Recupero delle informazioni dell'ingresso per il sink non riuscito: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink d'ingresso #%u\n"
+"\tDriver: %s\n"
+"\tModulo di appartenenza: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSpecifica di campionamento: %s\n"
+"\tMappa dei canali: %s\n"
+"\tFormato: %s\n"
+"\tCorked: %s\n"
+"\tMuto: %s\n"
+"\tVolume: %s\n"
+"\t        bilanciamento %0.2f\n"
+"\tLatenza del buffer: %0.0f usec\n"
+"\tLatenza del sink: %0.0f usec\n"
+"\tMetodo di ricampionamento: %s\n"
+"\tProprietà:\n"
+"\t\t%s\n"
+
+# Source output
+# A stream that is connected to an input device, i.e. an output of a source.
+#
+# from http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/WritingVolumeControlUIs/
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr ""
+"Recupero delle informazioni dell'uscita per la sorgente non riuscito: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sorgente d'uscita #%u\n"
+"\tDriver: %s\n"
+"\tModulo di appartenenza: %s\n"
+"\tClient: %s\n"
+"\tSorgente: %u\n"
+"\tSpecifica di campionamento: %s\n"
+"\tMappa dei canali: %s\n"
+"\tFormato: %s\n"
+"\tCorked: %s\n"
+"\tMuto: %s\n"
+"\tVolume: %s\n"
+"\t        bilanciamento %0.2f\n"
+"\tLatenza del buffer: %0.0f usec\n"
+"\tLatenza del sink: %0.0f usec\n"
+"\tMetodo di ricampionamento: %s\n"
+"\tProprietà:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Recupero delle informazioni del campione non riuscito: %s"
+
+# campiona lazy??
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Campione #%u\n"
+"\tNome: %s\n"
+"\tSpecifica di campionamento: %s\n"
+"\tMappa dei canali: %s\n"
+"\tVolume: %s\n"
+"\t        bilanciamento %0.2f\n"
+"\tDurata: %0.1f s\n"
+"\tDimensione: %s\n"
+"\tLazy: %s\n"
+"\tNome file: %s\n"
+"\tProprietà:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Fallimento: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Scaricamento del modulo non riuscito: modulo %s non caricato"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Impostazione del volume non riuscita: tentata l'impostazione dei volumi per "
+"%d canali dove i canali supportati = %d\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Impostazione del formato non riuscita: stringa %s non valida"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Caricamento del campione non riuscito: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Fine del file prematura"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "nuovo"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "modifica"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "rimuovi"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "sconosciuto"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "sink"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "sorgente"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "sink-input"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "sorgente-output"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "modulo"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "client"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "sample-cache"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "server"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "scheda"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Evento \"%s\" su %s #%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "Ricevuto SIGINT, uscita."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Specifica di volume non valida"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Volume oltre l'intervallo permesso.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "Numero di specifiche volume non valido.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "Specifica di volume non consistente.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[opzioni]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[TIPO]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "NOMEFILE [NOME]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "NOME [SINK]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "NOME|#N VOLUME"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "Volume"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "NAME|#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "#N FORMATI"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"I nomi speciali @DEFAULT_SINK@, @DEFAULT_SOURCE@ e @DEFAULT_MONITOR@\n"
+"possono essere usati per specificare il sink, l'origine e il monitor "
+"predefiniti.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Mostra questo aiuto\n"
+"      --version                         Mostra la versione\n"
+"\n"
+"  -s, --server=SERVER                   Il nome del server a cui "
+"connettersi\n"
+"  -n, --client-name=NOME                Il nome da dare a questo client sul "
+"server\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Compilato con libpulse %s\n"
+"Link eseguito con libpulse %s\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Specificare nulla o uno di: %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Specificare un file campione da caricare"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Apertura del file audio non riuscita."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+"Attenzione: determinazione della specifica di campionamento dal file non "
+"riuscita."
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "È necessario specificare un nome di campione da riprodurre"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "È necessario specificare un nome di campione da rimuovere"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "È necessario specificare un indice di ingresso per sink e un sink"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr ""
+"È necessario specificare una indice di uscita per sorgente e una sorgente"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "È necessario specificare un nome di modulo e gli argomenti."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "È necessario specificare l'indice di un modulo o un nome"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Non è possibile specificare più di un sink. È necessario specificare un "
+"valore booleano."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Specifica di sospensione non valida."
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Non è possibile specificare più di una sorgente. È necessario specificare un "
+"valore booleano."
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr ""
+"È necessario specificare un nome/indice di scheda e un nome di profilo."
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "È necessario specificare un nome/indice di sink e un nome di porta"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "È necessario specificare un nome di sink"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "È necessario specificare un nome/indice di sorgente e un nome di porta"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "È necessario specificare il nome di una sorgente"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "È necessario specificare un nome/indice di sink e un nome di porta"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "È necessario specificare un nome/indice di sorgente e un nome di porta"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "È necessario specificare un indice di ingresso per sink e un sink"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "Indice dell'input del sink non valido"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr ""
+"È necessario specificare un indice di uscita per la sorgente e il volume"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Indice di uscita per la sorgente non valido"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"È necessario specificare un nome/indice di sink e un'azione per il muto (0, "
+"1 o \"toggle\")"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Specifica per il muto non valida"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"È necessario specificare un nome/indice di sorgente e un'azione per il muto "
+"(0, 1 o \"toggle\")"
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"È necessario specificare un indice d'ingresso per il sink e un'azione per il "
+"muto (0, 1 o \"toggle\")"
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "Specifica dell'indice di input del sink non valida"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+"È necessario specificare un indice di uscita per il sink e un'azione per il "
+"muto (0, 1 o \"toggle\")"
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Specifica di indice di uscita per la sorgente non valida"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"È necessario specificare un indice di sink e un elenco separato da punti e "
+"virgola di formati supportati"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr ""
+"È necessario specificare un nome o un indice per la scheda, un nome per la "
+"porta e un offset di latenza"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Impossibile analizzare l'offset della latenza"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Nessun comando valido specificato."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Ripristino non riuscito: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Sospensione non riuscita: %s\n"
+
+# cambiato un po' la parte finale...
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "ATTENZIONE: server audio non locale, impossibile sospendere.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Connessione non riuscita: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Ricevuto SIGINT, in uscita.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "ATTENZIONE: processo figlio terminato dal segnale %u\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [OPZIONI] ... \n"
+"\n"
+"  -h, --help                            Mostra questo aiuto\n"
+"      --version                         Mostra la versione\n"
+"  -s, --server=SERVER                   Il nome del server a cui "
+"connettersi\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Compilato con libpulse %s\n"
+"Link eseguito con libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() non riuscita.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() non riuscita.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() non riuscita.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D DISPLAY] [-S SERVER] [-O SINK] [-I SORGENTE] [-c FILE]  [-d|-e|-i|-"
+"r]\n"
+"\n"
+" -d    Mostra i dati PulseAudio attuali collegati al display X11 (predef)\n"
+" -e    Esporta i dati PulseAudio locali sul display X11\n"
+" -i    Importa i dati PulseAudio dal display X11 alle variabili d'ambiente "
+"e\n"
+"        al file cookie locali \n"
+" -r    Rimuove i dati PulseAudio dal display X11\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Analisi della riga di comando non riuscita.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Server: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Sorgente: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Sink: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Analisi dei dati cookie non riuscita\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Salvataggio dei dati cookie non riuscito\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Recupero del FQDN non riuscito.\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Caricamento dei dati cookie non riuscito\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Non ancora implementato.\n"
+
+#~ msgid ""
+#~ "%s [options]\n"
+#~ "\n"
+#~ "-h, --help                            Show this help\n"
+#~ "-v, --verbose                         Print debug messages\n"
+#~ "      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --from-format=SAMPLEFORMAT      From sample type (defaults to "
+#~ "s16le)\n"
+#~ "      --from-channels=CHANNELS        From number of channels (defaults "
+#~ "to 1)\n"
+#~ "      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+#~ "      --to-channels=CHANNELS          To number of channels (defaults to "
+#~ "1)\n"
+#~ "      --resample-method=METHOD        Resample method (defaults to auto)\n"
+#~ "      --seconds=SECONDS               From stream duration (defaults to "
+#~ "60)\n"
+#~ "\n"
+#~ "If the formats are not specified, the test performs all formats "
+#~ "combinations,\n"
+#~ "back and forth.\n"
+#~ "\n"
+#~ "Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+#~ "alaw,\n"
+#~ "s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+#~ "\n"
+#~ "See --dump-resample-methods for possible values of resample methods.\n"
+#~ msgstr ""
+#~ "%s [OPZIONI]\n"
+#~ "\n"
+#~ "-h, --help                               Visualizza questo aiuto\n"
+#~ "-v, --verbose                            Stampa messaggi di debug\n"
+#~ "      --from-rate=FREQUENZACAMPIONAMENTO Frequenza di campionamento "
+#~ "sorgente\n"
+#~ "                                         in Hz (predefinito 44100)\n"
+#~ "      --from-format=FORMATOCAMPIONE      Tipo di campione sorgente\n"
+#~ "                                         (predefinito s16le)\n"
+#~ "      --from-channels=CANALI             Numero di canali sorgente\n"
+#~ "                                         (predefinito 1)\n"
+#~ "      --to-rate=FREQUENZACAMPIONAMENTO   Frequenza di campionamento di\n"
+#~ "                                         destinazione in Hz (predefinito "
+#~ "44100)\n"
+#~ "      --to-format=FORMATOCAMPIONE        Tipo di campione di "
+#~ "destinazione\n"
+#~ "                                         (predefinito s16le)\n"
+#~ "      --to-channels=CANALI               Numero di canali di "
+#~ "destinazione\n"
+#~ "                                         (predefinito 1)\n"
+#~ "      --resample-method=METODO           Metodo di ricampionamento\n"
+#~ "                                         (predefinito auto)\n"
+#~ "      --seconds=SECONDI                  Durata dello stream sorgente\n"
+#~ "                                         (predefinito 60)\n"
+#~ "\n"
+#~ "Se i formati non vengono specificati, il test prova tutte le combinazioni "
+#~ "di\n"
+#~ "formati, ricorsivamente\n"
+#~ "\n"
+#~ "Il tipo di campione deve essere scelto tra: s16le, s16be, u8, float32le,\n"
+#~ "float32be, ulaw, alaw, s24le, s24be, s24-32le, s24-32be, s32le, s32be\n"
+#~ "(predefinito s16ne).\n"
+#~ "\n"
+#~ "Consultare --dump-resample-methods per i valori ammessi per i metodi di\n"
+#~ "ricampionamento.\n"
+
+#~ msgid "%s %s\n"
+#~ msgstr "%s %s\n"
diff --git a/po/ja.po b/po/ja.po
new file mode 100644 (file)
index 0000000..75d8296
--- /dev/null
+++ b/po/ja.po
@@ -0,0 +1,2955 @@
+# translation of ja.po to Japanese
+# PulseAudio
+# Copyright (C) 2009.
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Hyu_gabaru Ryu_ichi <hyu_gabaru@yahoo.co.jp>, 2009.
+# Kiyoto Hashida <khashida@redhat.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ja\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:54+0000\n"
+"Last-Translator: Kiyoto Hashida <khashida@redhat.com>\n"
+"Language-Team: Japanese <jp@li.org>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: Plural-Forms: nplurals=1; plural=0;\n"
+"\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() は 例外的に大きな値を返しました: %lu バイト(%lu ms)。\n"
+"これは多分、ALSA ドライバー '%s' 内のバグです。この問題は ALSA 開発者宛に報"
+"告を提出して下さい。"
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() は 例外的に大きな値を返しました: %li バイト(%s%lu ms)。\n"
+"これは多分、ALSA ドライバー '%s' 内のバグです。この問題は ALSA 開発者宛に報"
+"告を提出して下さい。"
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() がおかしな値を返しました: 遅延 %lu は有効な値 %lu 未満"
+"です。\n"
+"これは多分、ALSA ドライバー '%s' 内のバグです。この問題は ALSA 開発者宛に報"
+"告を提出して下さい。"
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() は 例外的に大きな値を返しました: %lu バイト(%lu "
+"ms)。\n"
+"これは多分、ALSA ドライバー '%s' 内のバグです。この問題は ALSA 開発者宛に報"
+"告を提出して下さい。"
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+"null である場合でも、常に最低でもシンクが1つロードされるように維持します"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "ダミー出力"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "仮想 LADSPA シンク"
+
+#: ../src/modules/module-ladspa-sink.c:52
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<シンクの名前> sink_properties=<シンクのプロパティ> master=<フィル"
+"タするシンク名> format=<サンプル形式> rate=<サンプルレート> channels=<チャン"
+"ネル数> channel_map=<入力チャンネルマップ> plugin=<ladspa plugin の名前> "
+"label=<ladspa plugin のラベル> control=<コンマで隔離した入力制御値の一覧> "
+"input_ladspaport_map=<コンマで隔離した入力 LADSPA ポート番号の一覧> "
+"output_ladspaport_map=<コンマで隔離した出力 LADSPA ポート番号の一覧> "
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "クロック付き NULL シンク"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Null 出力"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "内部オーディオ"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "モデム"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "オリジナルの lt_dlopen ローダーを見つけるのに失敗しました。"
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "新規の dl ローダーの割り当てに失敗しました。"
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind-now-loader の追加に失敗しました。"
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "信号 %s を得ました。"
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "終了しています。"
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "ユーザー '%s' が見つかりませんでした。"
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "グループ '%s' が見つかりませんでした。"
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "ユーザー '%s' (UID %lu) とグループ  '%s' (GID %lu) を見つけました。"
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "ユーザー '%s' と グループ '%s' の GID が一致しません。"
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "ユーザー '%s' のホームディレクトリは '%s' ではありません。無視します。"
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' の作成に失敗しました: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "グループ一覧の変更に失敗しました: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID の変更に失敗しました: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID の変更に失敗しました: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "root の権限を正しく破棄しました。"
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "このプラットフォームではシステム全域のモードはサポートがありません。"
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) は失敗: %s "
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "コマンドラインの構文解析に失敗しました。"
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"システムモードは非 root ユーザーを拒否しました。D-Bus サーバー照合サービスだ"
+"けを開始します。"
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "デーモンは稼働していません"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "デーモンは PID %u として稼働していません"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "デーモンのキルに失敗しました: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"このプログラムは root として実行されるように意図されていません(--system を "
+"指定していない限り)。"
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Root の権限が必要です。"
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start はシステムインスタンスではサポートがありません。"
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr "ユーザーが設定したサーバー %s は start/autospawn を拒否しています。"
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"ユーザーが設定したサーバー %s はローカルにあるようです。さらに調査します。"
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr ""
+"システムモードで実行中です、しかし --disallow-exit がセットされていません!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"システムモードで実行中です、しかし --disallow-module-loading がセットされてい"
+"ません!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "システムモードで実行中です、強制的に SHM モードを無効にしています!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"システムモードで実行中です、強制的に exit の遊び時間を無効にしています!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdio の取得に失敗しました。"
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() は失敗: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() は失敗: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() は失敗: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "デーモン開始に失敗しました。"
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "デーモンが正常に開始しました。"
+
+#: ../src/daemon/main.c:816
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() は失敗: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "これは PulseAudio %s です。"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "コンパイルホスト: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "コンパイル CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "ホスト上で実行中: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "CPU を %u 個見つけました。"
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "ページサイズは %lu バイトです"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Valgrind サポートでのコンパイル: はい"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Valgrind サポートでのコンパイル: いいえ"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "valgrind モードで実行中: %s"
+
+#: ../src/daemon/main.c:921
+#, c-format
+msgid "Running in VM: %s"
+msgstr "VM 上で実行中: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "最適化したビルド: はい"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "最適化したビルド: いいえ"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG は定義済みです。アサーションは全て無効です。"
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH は定義済みです。ファストパスアサーションのみが無効です。"
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "アサーションは全て有効です。"
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "マシン ID の取得に失敗"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "マシン ID は %s"
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "セッション ID は %s"
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "ランタイムディレクトリ %s を使用"
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "状態ディレクトリ %s を使用"
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "モジュールディレクトリ %s を使用"
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "システムモードで実行中: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"そうすると、ユーザーはシステムモードで PA を実行しているわけです。その場合、"
+"実際にはそうすべきでないことに注意して下さい。\n"
+"それでも実行するのでしたら、期待どおりに機能しなくても責任はユーザー自身にあ"
+"ります。\n"
+"システムモードの使用が通常は良くない方針であることの説明については、http://"
+"www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/"
+"WhatIsWrongWithSystemWide/ をお読み下さい。"
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() は失敗"
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "最新の高解像度タイマーが使用できます! 楽しんで下さい!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"あらあら、ユーザーのカーネルはダメですよ! 今日のシェフのお薦めは高解像度タイ"
+"マーが有効になっている Linux です!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() は失敗"
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "デーモンの初期化に失敗しました。"
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"デーモンはモジュールの読み込みなしで開始しており、動作を拒否しています。"
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "デーモンの開始が完了です。"
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "デーモンのシャットダウンが始まりました。"
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "デーモンは取り消されました。"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [オプション]\n"
+"\n"
+"コマンド:\n"
+"  -h, --help                            このヘルプを表示\n"
+"      --version                         バージョンを表示\n"
+"      --dump-conf                       デフォルト設定をダンプ\n"
+"      --dump-modules                    使用可能なモジュール一覧をダンプ\n"
+"      --dump-resample-methods           使用可能な再サンプル方法をダンプ\n"
+"      --cleanup-shm                     古い共有メモリーセグメントをクリーン"
+"アップ \n"
+"      --start                           実行していない場合、デーモンを開始 \n"
+"  -k  --kill                            実行中のデーモンをキル\n"
+"      --check                           実行中のデーモンをチェック (退出コー"
+"ドを返すのみ)\n"
+"\n"
+"オプション:\n"
+"      --system[=BOOL]                   システム全域インスタンスとして実行\n"
+"  -D, --daemonize[=BOOL]                開始後デーモン化\n"
+"      --fail[=BOOL]                     開始失敗なら終了\n"
+"      --high-priority[=BOOL]            高度なナイスレベルの設定を試行\n"
+"                                        (root としてのみ可能, SUID の時、\n"
+"                                        又は昇格した RLIMIT_NICE で)\n"
+"      --realtime[=BOOL]                 リアルタイムスケジュールを有効にする"
+"試行\n"
+"                                        (root としてのみ可能, SUID の時、\n"
+"                                        又は昇格した RLIMIT_RTPRIO で)\n"
+"      --disallow-module-loading[=BOOL]  起動後のユーザー要求のモジュールの"
+"ロード/アンロードを許可しない\n"
+"\n"
+"      --disallow-exit[=BOOL]            ユーザー要求の退出を許可しない\n"
+"      --exit-idle-time=SECS             遊休時と指定時間後にデーモンを終了\n"
+"\n"
+"      --scache-idle-time=SECS           遊休時と指定時間後に自動ロードサンプ"
+"ルをアンロード \n"
+"\n"
+"      --log-level[=LEVEL]               詳細レベルを上げるか、又はセット\n"
+"  -v                                    詳細レベルを上げる\n"
+"--log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        ログターゲットを指定\n"
+"--log-meta[=BOOL]                       ログメッセージ内にコードの場所を含む \n"
+"      --log-time[=BOOL]                 ログメッセージ内にタイムスタンプを含"
+"む\n"
+"      --log-backtrace=FRAMES            ログメッセージ内にバックトレースを含"
+"む\n"
+"  -p, --dl-search-path=PATH             動的共有オブジェクト(plugins)用に検索"
+"パスをセット\n"
+"\n"
+"      --resample-method=METHOD          指定した再サンプリング方法を使用\n"
+"                                        (使用可能な値は --dump-resample-"
+"methods で\n"
+"                                        参照)\n"
+"      --use-pid-file[=BOOL]             PID ファイルを作成\n"
+"      --no-cpu-limit[=BOOL]             サポートしているプラットフォームで CPU\n"
+"                                        ロードリミッターをインストールしない\n"
+"      --disable-shm[=BOOL]              共有メモリーサポートを無効にする\n"
+"\n"
+"スタートアップスクリプト:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"       指定した引数で指定したプラグインモ"
+"ジュールをロード\n"
+"\n"
+"  -F, --file=FILENAME                   指定したスクリプトを実行\n"
+"  -C                                    スタートアップ後に実行中の TTY 上でコ"
+"マンドラインを開く\n"
+"\n"
+"  -n                                    デフォルトのスクリプトファイルをロー"
+"ドしない\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize はブーリアン引数を予期します"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail はブーリアン引数を予期します。"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level はログレベル引数を予期します(数値幅0~4、又はデバグ、情報、注"
+"記、警告、エラーの中の1つ)"
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority は ブーリアン引数を予期します"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime はブーリアン引数を予期します "
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading はブーリアン引数を予期します "
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit はブーリアン引数を予期します "
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file はブーリアン引数を予期します "
+
+#: ../src/daemon/cmdline.c:318
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr ""
+"無効なログターゲット: 'syslog' か、 'stderr' か、'auto' か、正しいファイル名 "
+"'file:<path>' を使用して下さい。"
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time ブーリアン引数を予期します "
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta ブーリアン引数を予期します "
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "無効な再サンプル方法 '%s'"
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system はブーリアン引数を予期します"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit はブーリアン引数を予期します"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm はブーリアン引数を予期します"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "名前: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "モジュール情報が使用できません\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "バージョン: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "説明: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "著者: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "使用法: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "1度だけロード: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "破棄の警告: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "パス: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] 無効なログターゲット '%s'"
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] 無効なログレベル '%s'"
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] 無効な再サンプル方法 '%s'"
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] 無効な rlimit '%s'"
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] 無効なサンプル形式 '%s'"
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] 無効なサンプルレート '%s'"
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] 無効なサンプルチャンネル '%s'"
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] 無効なチャンネルマップ '%s'"
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] 無効なフラグメントの数 '%s'"
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] 無効なフラグメントサイズ '%s'"
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] 無効なナイスレベル '%s'"
+
+#: ../src/daemon/daemon-conf.c:528
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] 無効なサーバータイプ '%s'"
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "設定ファイルを開くのに失敗: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"指定されたデフォルトのチャンネルマップは、指定されたデフォルトのチャンネル数"
+"とは異なるチャンネル数を持っています。"
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### 設定ファイルから読み込み: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "権限をクリーンアップ"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio サウンドシステム"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "PulseAudio サウンドシステムを開始"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio サウンドシステム KDE ルーティングポリシー"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "KDE ルーティングポリシーを有効にして PulseAudio サウンドシステムを開始"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "モノ"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "中央前"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "左前"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "右前"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "中央後ろ"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "左後ろ"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "右後ろ"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr "サブウーファー"
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "中央の左前"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "中央の右前"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "左側"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "右側"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "補助 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "補助 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "補助 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "補助 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "補助 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "補助 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "補助 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "補助 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "補助 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "補助 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "補助 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "補助 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "補助 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "補助 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "補助 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "補助 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "補助 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "補助 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "補助 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "補助 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "補助 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "補助 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "補助 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "補助 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "補助 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "補助 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "補助 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "補助 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "補助 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "補助 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "補助 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "補助 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "上部中央"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "上部中央前"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "上部左前"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "上部右前"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "上部中央後ろ"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "上部左後ろ"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "上部右後ろ"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "無効)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "ステレオ"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "サラウンド 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "サラウンド 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "サラウンド 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "サラウンド 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "サラウンド 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "アクセス拒否"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "不明なコマンド"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "無効な引数"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "エンティティは存在します"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "そのようなエンティティはありません"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "接続拒否"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "プロトコルエラー"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "タイムアウト"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "認証キーがありません"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "内部エラー"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "接続切断"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "エンティティはキルされました"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "無効なサーバー"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "モジュール初期化失敗"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "悪い状態"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "データ無し"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "互換性のないプロトコルバージョン"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "大き過ぎます"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "サポートがありません"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "不明なエラーコード"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "そのような拡張子はありません"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "旧来の機能"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "実装の欠如"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "クライアントはフォークされています"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "入力/出力エラー"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "デバイスか、リソースがビジー"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() は失敗"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() は真を返しました"
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "クッキーデータの構文解析に失敗"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "設定ファイル'%s' を開くのに失敗: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "クッキーがロードされていません。無い状態で接続を試行"
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "不明な拡張子 '%s' のメッセージを受信"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "ストリームの排出に失敗: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "排出したストリームを再生"
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "サーバーへの排出接続"
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() は失敗: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() は失敗: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() は失敗: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "ストリームは正常に作成完了"
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() は失敗: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "バッファメトリックス: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "バッファメトリックス: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "サンプル仕様 '%s' 、チャンネルマップ '%s' を使用。"
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "デバイス %s に接続 (%u, %ssuspended)"
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "ストリームエラー: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "ストリームデバイス休止 %s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "ストリームデバイス復帰 %s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "ストリームアンダーラン %s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "ストリームオーバーラン %s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "ストリーム開始 %s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "ストリームはデバイス %s へ移動 (%u, %ssuspended)%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "not "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "ストリームバッファの属性変更 %s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr "Cork リクエストスタックは空です: corking stream"
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Cork リクエストスタックは空です: uncorking stream"
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr "警告: uncork リクエストを cork リクエストよりも多く受け取りました"
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "接続が確立 %s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() は失敗: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() は失敗: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() は失敗: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "接続失敗: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF 取得"
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() は失敗: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "信号取得、退出中"
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "レイテンシー取得に失敗: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "時間: %0.3f sec ; レイテンシー: %0.0f usec"
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() は失敗: %s"
+
+#: ../src/utils/pacat.c:653
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            このヘルプを表示\n"
+"      --version                         バージョンを表示\n"
+"\n"
+"  -r, --record                          録音用の接続を作成\n"
+"  -p, --playback                        再生用の接続を作成\n"
+"\n"
+"  -v, --verbose                         詳細操作を有効にする\n"
+"\n"
+"  -s, --server=SERVER                   接続先サーバーの名前\n"
+"  -d, --device=DEVICE                   接続先シンク/ソースの名前\n"
+"  -n, --client-name=NAME                サーバーでこのクライアントへのコール"
+"方法\n"
+"      --stream-name=NAME                サーバー上でこのストリームへのコール"
+"方法\n"
+"      --volume=VOLUME                   初期 (リニア) ボリューム幅を指定  "
+"0...65536\n"
+"      --rate=SAMPLERATE                 サンプルレートを Hz で表示 (デフォル"
+"トは 44100)\n"
+"      --format=SAMPLEFORMAT             サンプルタイプ, 次の1つ s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (デフォルトは "
+"s16ne)\n"
+"      --channels=CHANNELS               チャンネルの数, モノ用に1, ステレオ"
+"用に2\n"
+"                                        (デフォルトは 2)\n"
+"      --channel-map=CHANNELMAP          デフォルトの代わりに使用するチャンネ"
+"ルマップ \n"
+"      --fix-format                      ストリームの接続先であるシンクからサ"
+"ンプル形式を取る \n"
+"\n"
+"      --fix-rate                        ストリームの接続先であるシンクからサ"
+"ンプルレートを取る \n"
+"\n"
+"      --fix-channels                    ストリームの接続先であるシンクから"
+"チャンネル数とチャンネルマップを取る\n"
+"\n"
+"      --no-remix                        チャンネルのアップミックスやダウン"
+"ミックスをしない\n"
+"      --no-remap                        名前の代わりにインデックスでチャンネ"
+"ルをマップする\n"
+"      --latency=BYTES                   指定レイテンシーをバイトで要求\n"
+"      --process-time=BYTES              要求毎の指定プロセスタイムをバイトで"
+"要求 \n"
+"      --latency-msec=MSEC               指定レイテンシーをミリ秒で要求\n"
+"      --process-time-msec=MSEC          要求毎の指定プロセスタイムをミリ秒で"
+"要求 \n"
+"      --property=PROPERTY=VALUE         指定プロパティを指定値にセット\n"
+"      --raw                             録音/再生の生の PCM データ\n"
+"      --passthrough                     データをそのまま通す\n"
+"      --file-format=FFORMAT             録音/再生のフォーマットした PCM デー"
+"タ\n"
+"      --list-file-formats               利用可能なファイル形式を一覧表示\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"libpulse %s でコンパイル\n"
+"libpulse %s で接続\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "無効なクライアント名 '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "無効なストリーム名 '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "無効なチャンネルマップ '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "無効なレイテンシー仕様 '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "無効なプロセスタイム仕様 '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "無効なプロパティ '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "不明なファイル形式 '%s'"
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "無効なサンプル仕様"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "引数が多過ぎます。"
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "ファイル用のサンプル仕様の生成に失敗しました。"
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "オーディオファイルを開くのに失敗しました。"
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr "警告: 指定されたサンプルの仕様はファイルからの仕様で上書きされます。"
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "ファイルからのサンプル仕様の決定に失敗しました。"
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "警告: ファイルからのチャンネルマップの決定に失敗しました。"
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "チャンネルマップはサンプル仕様に一致しません。"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "警告: ファイルへのチャンネルマップ書き込みに失敗しました。"
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"サンプル仕様 '%s' とチャンネルマップ '%s' で  %s ストリームを開いています。"
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "録音"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "再生"
+
+#: ../src/utils/pacat.c:1110
+msgid "Failed to set media name."
+msgstr "メディア名のセットに失敗しました。"
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() は失敗"
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() は失敗"
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() は失敗"
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() は失敗: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() は失敗"
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() は失敗"
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "休止の失敗: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "復帰の失敗: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "警告: サウンドサーバーはローカルではありません。休止しません。\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "接続失敗 : %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT 取得、退出中\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "警告: 子プロセスは信号 %u で終了しました\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [オプション] ... \n"
+"\n"
+"  -h, --help                            このヘルプを表示\n"
+"      --version                         バージョンを表示\n"
+"  -s, --server=SERVER                   接続先サーバーの名前\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"libpulse %s でコンパイル\n"
+"libpulse %s でリンク\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() は失敗\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() は失敗\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() は失敗\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "統計の取得に失敗しました: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "現在使用中: %u ブロックは合計 %s バイトを含む\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "総寿命の期間中に割り当て: %u ブロックは合計 %s バイトを含む\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "サンプルのキャッシュサイズ: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "サーバー情報の取得に失敗 : %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"サーバー文字列: %s\n"
+"ライブラリプロトコルバージョン: %u\n"
+"サーバープロトコルバージョン: %u\n"
+"Is ローカル: %s\n"
+"クライアントインデックス: %u\n"
+"タイルサイズ: %zu\n"
+
+#: ../src/utils/pactl.c:192
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"ユーザー名: %s\n"
+"ホスト名: %s\n"
+"サーバー名: %s\n"
+"サーバーバージョン: %s\n"
+"デフォルトサンプル仕様: %s\n"
+"デフォルトチャンネルマップ: %s\n"
+"デフォルトシンク: %s\n"
+"デフォルトソース: %s\n"
+"クッキー: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "シンク情報の取得に失敗しました: %s"
+
+#: ../src/utils/pactl.c:270
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"シンク #%u\n"
+"\t状態: %s\n"
+"\t名前: %s\n"
+"\t説明: %s\n"
+"\tドライバー: %s\n"
+"\tサンプル仕様: %s\n"
+"\tチャンネルマップ: %s\n"
+"\tオーナーモジュール: %u\n"
+"\tミュート: %s\n"
+"\tボリューム: %s%s%s\n"
+"\t        バランス %0.2f\n"
+"\tベースボリューム: %s%s%s\n"
+"\tモニターソース: %s\n"
+"\tレイテンシー: %0.0f usec, 設定 %0.0f usec\n"
+"\tフラグ: %s%s%s%s%s%s%s\n"
+"\tプロパティ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tポート:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\t活動中ポート: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\t形式:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "ソース情報の取得に失敗しました: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ソース #%u\n"
+"\t状態: %s\n"
+"\t名前: %s\n"
+"\t説明: %s\n"
+"\tドライバー: %s\n"
+"\tサンプル仕様: %s\n"
+"\tチャンネルマップ: %s\n"
+"\tオーナーモジュール: %u\n"
+"\tミュート: %s\n"
+"\tボリューム: %s%s%s\n"
+"\t        バランス %0.2f\n"
+"\tベースボリューム: %s%s%s\n"
+"\tシンクのモニター: %s\n"
+"\tレイテンシー: %0.0f usec, 設定 %0.0f usec\n"
+"\tフラグ: %s%s%s%s%s%s\n"
+"\tプロパティ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "モジュール情報の取得に失敗しました: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"モジュール #%u\n"
+"\t名前: %s\n"
+"\t引数: %s\n"
+"\t使用度カウンター: %s\n"
+"\tプロパティ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "クライアント情報の取得に失敗しました: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"クライアント #%u\n"
+"\tドライバー: %s\n"
+"\tオーナーモジュール: %s\n"
+"\tプロパティ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "カード情報の取得に失敗しました: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"カード #%u\n"
+"\t名前: %s\n"
+"\tドライバー: %s\n"
+"\tモジュール: %s\n"
+"\tプロパティ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tプロフィール:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\t有効なプロフィール: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "シンク入力情報の取得に失敗しました: %s"
+
+#: ../src/utils/pactl.c:622
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"シンク入力 #%u\n"
+"\tドライバー: %s\n"
+"\tオーナーモジュール: %s\n"
+"\tクライアント: %s\n"
+"\tシンク: %u\n"
+"\tサンプル仕様: %s\n"
+"\tチャンネルマップ: %s\n"
+"\t形式: %s\n"
+"\tミュート: %s\n"
+"\tボリューム: %s\n"
+"\t            %s\n"
+"\t            バランス %0.2f\n"
+"\tバッファレイテンシー: %0.0f usec\n"
+"\tシンクレイテンシー: %0.0f usec\n"
+"\t再サンプル方法: %s\n"
+"\tプロパティ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "ソース出力情報の取得に失敗しました: %s"
+
+#: ../src/utils/pactl.c:693
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ソース出力 #%u\n"
+"\tドライバー: %s\n"
+"\tオーナーモジュール: %s\n"
+"\tクライアント: %s\n"
+"\tソース: %u\n"
+"\tサンプル仕様: %s\n"
+"\tチャンネルマップ: %s\n"
+"\t形式: %s\n"
+"\tミュート: %s\n"
+"\tボリューム: %s\n"
+"\t            %s\n"
+"\t            バランス %0.2f\n"
+"\tバッファレイテンシー: %0.0f usec\n"
+"\tソースレイテンシー: %0.0f usec\n"
+"\t再サンプル方法: %s\n"
+"\tプロパティ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "サンプル情報の取得に失敗しました: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"サンプル #%u\n"
+"\t名前: %s\n"
+"\tサンプル仕様: %s\n"
+"\tチャンネルマップ: %s\n"
+"\tボリューム: %s\n"
+"\t            %s\n"
+"\t            バランス %0.2f\n"
+"\t継続期間: %0.1fs\n"
+"\tサイズ: %s\n"
+"\tレイジー: %s\n"
+"\tファイル名: %s\n"
+"\tプロパティ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "失敗: %s"
+
+#: ../src/utils/pactl.c:915
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "形式のセットに失敗しました: 無効な形式文字列 %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "サンプルのアップロードに失敗しました: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "ファイルの早期終了"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr "新規"
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr "変更"
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr "削除"
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr "不明"
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr "シンク"
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr "ソース"
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr "シンク入力"
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr "ソース出力"
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr "モジュール"
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr "クライアント"
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr "サンプルキャッシュ"
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+msgid "server"
+msgstr "サーバー"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "イベント '%s' が %s #%u 上にあります\n"
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT を取得、退出中"
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "無効なボリューム仕様"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr "許容範囲外のボリューム\n"
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr "[オプション]"
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr "[タイプ]"
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr "ファイル名 [名前]"
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr "名前 [シンク]"
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr "名前"
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr "名前 [引数 ...]"
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr "#N シンク|ソース"
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr "名前|#N 1|0"
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr "カードプロフィール"
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr "名前|#N ポート"
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr "名前|#N ボリューム"
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr "#N ボリューム"
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr "#N 形式"
+
+#: ../src/utils/pactl.c:1339
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                    このヘルプを表示\n"
+"      --version                 バージョンを表示\n"
+"\n"
+"  -s, --server=SERVER           接続先サーバーの名前\n"
+"  -n, --client-name=NAME        サーバーでこのクライアントへのコール方法\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"libpulse %s でコンパイル\n"
+"libpulse %s でリンク\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "何も指定しないか以下から1つ指定してください: %s"
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "ロードするサンプルファイルを指定して下さい"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "サウンドファイルを開くのに失敗しました。"
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "警告: ファイルからサンプル仕様を決定するのに失敗しました。"
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "再生するサンプル名を指定する必要があります"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "削除するサンプル名を指定する必要があります。"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "シンク入力インデックスとシンクを指定する必要があります"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "ソース出力インデックスとソースを指定する必要があります"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "モジュール名と引数を指定する必要があります"
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "モジュールインデックスを指定する必要があります"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"シンクは1つ以上は指定できません。ブーリアン値を1つ指定する必要があります。"
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"ソースは1つ以上は指定できません。ブーリアン値を1つ指定する必要があります。"
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "カードの名前/インデックスとプロフィール名を指定する必要があります"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "シンクの名前/インデックスとポート名を指定する必要があります"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "ソースの名前/インデックスとポート名を指定する必要があります"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "シンクの名前/インデックスとボリュームを指定する必要があります"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "ソースの名前/インデックスとボリュームを指定する必要があります"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "シンク入力インデックスとボリュームを指定する必要があります"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "無効なシンク入力インデックス"
+
+#: ../src/utils/pactl.c:1660
+msgid "You have to specify a source output index and a volume"
+msgstr "ソース出力インデックスとボリュームを指定する必要があります"
+
+#: ../src/utils/pactl.c:1665
+msgid "Invalid source output index"
+msgstr "無効なソース出力インデックス"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "シンクの名前/インデックスとミュートブーリアンを指定する必要があります"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+msgid "Invalid mute specification"
+msgstr "無効なミュート仕様"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "ソースの名前/インデックスとミュートブーリアンを指定する必要があります"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "シンク入力インデックスとミュートブーリアンを指定する必要があります"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "無効なシンク入力インデックス仕様"
+
+#: ../src/utils/pactl.c:1732
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "ソース出力インデックスとミュートブーリアンを指定する必要があります"
+
+#: ../src/utils/pactl.c:1737
+msgid "Invalid source output index specification"
+msgstr "無効なソース出力インデックス仕様"
+
+#: ../src/utils/pactl.c:1756
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"シンクのインデックスとサポートしている形式のセミコロンで隔離した一覧を"
+"指定する必要があります"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "有効なコマンドが指定されていません"
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    X11 ディスプレイに接続した現在の PulseAudio のデータを表示 (デフォル"
+"ト)\n"
+" -e    X11 ディスプレイにローカル PulseAudio データをエキスポート\n"
+" -i    X11 ディスプレイからローカル環境変数とクッキーに PulseAudio データをイ"
+"ンポート\n"
+" -r    X11 ディスプレイから PulseAudio データを削除\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "コマンドラインの構文解析に失敗\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "サーバー: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "ソース: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "シンク: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "クッキー: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "クッキーデータの構文解析に失敗\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "クッキーデータの保存に失敗\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "クライアント設定ファイルのロードに失敗\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "環境設定データの読み込みに失敗\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN の取得に失敗\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "クッキーデータのロードに失敗\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "まだ実装されていません\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"PulseAudio デーモン自身が稼働していないか、又はセッションデーモンとして稼働し"
+"ていません。"
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "ソケット (PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio のキルに失敗"
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "デーモンが応答しません"
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "autospawn ロックにアクセスできません"
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA が新規のデータをデバイスに書き込むように催促しましたが、書き込むことが"
+"ありません!\n"
+"これは多分、ALSA ドライバー '%s' 内のバグです。この問題を ALSA 開発者に 報告"
+"して下さい。\n"
+"POLLOUT セットで呼び起こされましたが、その結果としての snd_pcm_avail() は 0 "
+"又は他の値 < min_avail を返しました。"
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA はデバイスから新規データを読み込むように催促しましたが、読み込むものが"
+"ありません!\n"
+"これは多分、ALSA ドライバー'%s' 内のバグです。この問題を ALSA 開発者に報告し"
+"て下さい。\n"
+"POLLIN セットで呼び起こされましたが、その結果としての snd_pcm_avail() は 0 又"
+"は他の値 < min_avail を返しました。"
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "オフ"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "ハイファイ再生 (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "ハイファイキャプチャ (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "テレフォニーデュプレックス (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr "ハンズフリーゲートウェイ"
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio サウンドサーバー"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "出力デバイス"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "入力デバイス"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@ 上のオーディオ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "入力"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "ドッキングステーション入力"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+msgid "Docking Station Microphone"
+msgstr "ドッキングステーションマイクロフォン"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+msgid "Docking Station Line In"
+msgstr "ドッキングステーションライン入力"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "ラインイン"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "マイクロフォン"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+msgid "Front Microphone"
+msgstr "フロントマイクロフォン"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+msgid "Rear Microphone"
+msgstr "リアマイクロフォン"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "外部マイクロフォン"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "内部マイクロフォン"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "ラジオ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "ビデオ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "自動ゲイン制御"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "自動ゲイン制御なし"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "ブースト"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "ブーストなし"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "アンプ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "アンプなし"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+msgid "Bass Boost"
+msgstr "低音ブースト"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+msgid "No Bass Boost"
+msgstr "低音ブーストなし"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr "スピーカー"
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "アナログヘッドフォン"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "アナログ入力"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "ドッキングステーションマイクロフォン"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "アナログ出力"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "アナログ出力 (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+msgid "Line Out"
+msgstr "ライン出力"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "アナログモノ出力"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+msgid "Speakers"
+msgstr "スピーカー"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+msgid "Digital Output (S/PDIF)"
+msgstr "デジタル出力 (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "デジタルパススルー (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "アナログモノ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "アナログステレオ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "アナログサラウンド 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "アナログサラウンド 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "アナログサラウンド 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "アナログサラウンド 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "アナログサラウンド 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "アナログサラウンド 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "アナログサラウンド 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "アナログサラウンド 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "アナログサラウンド 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "アナログサラウンド 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "アナログサラウンド 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "デジタルステレオ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+msgid "Digital Passthrough  (IEC958)"
+msgstr "デジタルパススルー (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "デジタルサラウンド 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "デジタルサラウンド 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "デジタルステレオ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "デジタルサラウンド 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "アナログモノデュプレックス"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "アナログステレオデュプレックス"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "デジタルステレオデュプレックス (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, c-format
+msgid "%s Output"
+msgstr "%s 出力"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, c-format
+msgid "%s Input"
+msgstr "%s 入力"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"source_name=<ソースの名前> source_properties=<ソースのプロパティ> "
+"source_master=<フィルタするソースの名前> sink_name=<シンクの名前> "
+"sink_properties=<シンクのプロパティ> sink_master=<フィルタするシンクの名前> "
+"adjust_time=<レートを再調整する頻度(秒)> adjust_threshold=<再調整するズレ幅の"
+"閾値(ミリ秒)> format=<サンプル形式> rate=<サンプルレート> channels=<チャンネ"
+"ル数> channel_map=<チャンネルマップ> aec_method=<使用する実装> aec_args=<AEC "
+"エンジンのパラメータ> save_aec=</tmp に AEC データを保存> autoloaded=<このモ"
+"ジュールが自動でロードされている場合にセット> use_volume_sharing=<yes 又は "
+"no> "
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr "多目的イコライザー"
+
+#: ../src/modules/module-equalizer-sink.c:76
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<シンクの名前> sink_properties=<シンクのプロパティ> sink_master=<"
+"接続先のシンク> format=<サンプル形式> rate=<サンプルレート> channels=<チャン"
+"ネル数> channel_map=<チャンネルマップ> autoloaded=<このモジュールが自動でロー"
+"ドされている場合にセット> use_volume_sharing=<yes 又は no> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<使っていないフィルターを自動でアンロードするかどうか>"
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+"%s [オプション]\n"
+"\n"
+"-h, --help                            このヘルプを表示\n"
+"-v, --verbose                         デバッグメッセージを表示\n"
+"      --from-rate=SAMPLERATE          変換前サンプルレート (Hz)\n"
+"                                      (デフォルトは 44100)\n"
+"      --from-format=SAMPLEFORMAT      変換前サンプルタイプ\n"
+"                                      (デフォルトは s16le)\n"
+"      --from-channels=CHANNELS        変換前チャンネル数\n"
+"                                      (デフォルトは 1)\n"
+"      --to-rate=SAMPLERATE            変換後サンプルレート (Hz)\n"
+"                                      (デフォルトは 44100)\n"
+"      --to-format=SAMPLEFORMAT        変換後サンプルタイプ\n"
+"                                      (デフォルトは s16le)\n"
+"      --to-channels=CHANNELS          変換後チャンネル数\n"
+"                                      (デフォルトは 1)\n"
+"      --resample-method=METHOD        再サンプリング方法\n"
+"                                      (デフォルトは auto)\n"
+"      --seconds=SECONDS               変換前ストリームの秒数\n"
+"                                      (デフォルトは 60)\n"
+"\n"
+"形式が指定されない場合はあらゆる形式の組み合わせをテストします\n"
+"\n"
+"サンプルタイプは s16le, s16be, u8, float32le, float32be, ulaw, alaw,\n"
+"32le, s32be からどれかを選択 (デフォルトは s16ne)\n"
+"\n"
+"再サンプリングに使用可能な値は --dump-resample-methods を参照\n"
+
+#: ../src/tests/resampler-test.c:356
+#, c-format
+msgid "%s %s\n"
+msgstr "%s %s\n"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr "=== %d 秒: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
diff --git a/po/kn.po b/po/kn.po
new file mode 100644 (file)
index 0000000..f75dd7f
--- /dev/null
+++ b/po/kn.po
@@ -0,0 +1,3039 @@
+# translation of pulseaudio.master-tx.kn.po to Kannada
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Shankar Prasad <svenkate@redhat.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx.kn\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:54+0000\n"
+"Last-Translator: Shankar Prasad <svenkate@redhat.com>\n"
+"Language-Team: Kannada <kde-l10n-kn@kde.org>\n"
+"Language: kn\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Lokalize 1.0\n"
+"Plural-Forms:  nplurals=2; plural=(n != 1);\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ದಿಂದ ಅತ್ಯಂತ ದೊಡ್ಡದಾದ ಮೌಲ್ಯವು ಮರಳಿದೆ: %lu ಬೈಟ್‌ಗಳು (%lu ms).\n"
+"ಇದಕ್ಕೆ ALSA ಚಾಲಕ '%s' ದಲ್ಲಿನ ಒಂದು ದೋಷದ ಕಾರಣವಿರಬಹುದು. ದಯವಿಟ್ಟುಈ ತೊಂದರೆಯನ್ನು ALSA "
+"ವಿಕಸನಗಾರರ ಗಮನಕ್ಕೆ ತನ್ನಿ."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() ದಿಂದ ಅತ್ಯಂತ ದೊಡ್ಡದಾದ ಮೌಲ್ಯವು ಮರಳಿದೆ: %li ಬೈಟ್‌ಗಳು (%s%lu ms).\n"
+"ಇದಕ್ಕೆ ALSA ಚಾಲಕ '%s' ದಲ್ಲಿನ ಒಂದು ದೋಷದ ಕಾರಣವಿರಬಹುದು. ದಯವಿಟ್ಟುಈ ತೊಂದರೆಯನ್ನು ALSA "
+"ವಿಕಸನಗಾರರ ಗಮನಕ್ಕೆ ತನ್ನಿ."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ದಿಂದ ಅತ್ಯಂತ ದೊಡ್ಡದಾದ ಮೌಲ್ಯವು ಮರಳಿದೆ: %lu ಬೈಟ್‌ಗಳು (%lu ms).\n"
+"ಇದಕ್ಕೆ ALSA ಚಾಲಕ '%s' ದಲ್ಲಿನ ಒಂದು ದೋಷದ ಕಾರಣವಿರಬಹುದು. ದಯವಿಟ್ಟುಈ ತೊಂದರೆಯನ್ನು ALSA "
+"ವಿಕಸನಗಾರರ ಗಮನಕ್ಕೆ ತನ್ನಿ."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() ದಿಂದ ಅತ್ಯಂತ ದೊಡ್ಡದಾದ ಮೌಲ್ಯವು ಮರಳಿದೆ: %lu ಬೈಟ್‌ಗಳು (%lu ms).\n"
+"ಇದಕ್ಕೆ ALSA ಚಾಲಕ '%s' ದಲ್ಲಿನ ಒಂದು ದೋಷದ ಕಾರಣವಿರಬಹುದು. ದಯವಿಟ್ಟುಈ ತೊಂದರೆಯನ್ನು ALSA "
+"ವಿಕಸನಗಾರರ ಗಮನಕ್ಕೆ ತನ್ನಿ."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "ಯಾವಾಗಲೂ ಒಂದು ಸಿಂಕ್‌ ಅನ್ನು ಲೋಡ್ ಮಾಡಿರುತ್ತದೆ, ಅದು ಶೂನ್ಯವಾಗಿದ್ದರೂ ಸಹ"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "ಡಮ್ಮಿ ಔಟ್‌ಪುಟ್"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "ವರ್ಚುವಲ್ LADSPA ಸಿಂಕ್"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<ಸಿಂಕ್‌ನ ಹೆಸರು> sink_properties=<ಸಿಂಕ್‌ನ ಗುಣಗಳು> master=<ಫಿಲ್ಟರ್ "
+"ಮಾಡಬೇಕಿರುವ ಸಿಂಕ್‌ನ ಹೆಸರು> format=<ನಮೂನೆ ವಿನ್ಯಾಸ> rate=<ನಮೂನೆ ದರ> "
+"channels=<ಚಾನಲ್‌ಗಳ ಸಂಖ್ಯೆ> channel_map=<ಚಾನಲ್ ನಕ್ಷೆ> plugin=<ladspa ಪ್ಲಗ್‌ಇನ್ ಹೆಸರು> "
+"label=<ladspa ಪ್ಲಗ್‌ಇನ್ ಹೆಸರು> control=<ವಿರಾಮ ಚಿಹ್ನೆಗಳನ್ನು ಹೊಂದಿರುವ ಇನ್‌ಪುಟ್ ನಿಯಂತ್ರಣ "
+"ಮೌಲ್ಯಗಳ ಪಟ್ಟಿ>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "ಕ್ಲಾಕ್‌ ಮಾಡಲಾದ NULL ಸಿಂಕ್"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "ಶೂನ್ಯ ಔಟ್‌ಪುಟ್"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "ಆಂತರಿಕ ಆಡಿಯೊ"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "ಮಾಡೆಮ್"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "ಮೂಲ lt_dlopen loader ಅನ್ನು ಲೋಡ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "ಹೊಸ dl ಲೋಡರ್ ಅನ್ನು ನಿಯೋಜಿಸುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind-now-ಲೋಡರ್ ಅನ್ನು ಸೇರಿಸಲಾಗಿಲ್ಲ."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "%s ನಿಂದ ಸಂಕೇತವು ದೊರೆತಿದೆ."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "ನಿರ್ಗಮಿಸುತ್ತಿದೆ."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "ಬಳಕೆದಾರ '%s' ಅನ್ನು ಪತ್ತೆ ಮಾಡಲು ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "ಗುಂಪು '%s' ಅನ್ನು ಪತ್ತೆ ಮಾಡಲು ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "ಬಳಕೆದಾರ '%s' (UID %lu) ಹಾಗು ಗುಂಪು '%s' (GID %lu) ಕಂಡುಬಂದಿದೆ."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "ಬಳಕೆದಾರ '%s' ರ GID ಹಾಗು ಗುಂಪು '%s' ತಾಳೆಯಾಗುತ್ತಿಲ್ಲ."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "ಬಳಕೆದಾರ '%s' ರ ನೆಲೆ ಕೋಶವು '%s' ಆಗಿಲ್ಲ, ಆಲಕ್ಷಿಸಲಾಗುತ್ತಿದೆ."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' ಅನ್ನು ರಚಿಸುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "ಗುಂಪಿನ ಪಟ್ಟಿಯನ್ನು ಬದಲಾಯಿಸಲು ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID ಅನ್ನು ಬದಲಾಯಿಸಲು ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID ಅನ್ನು ಬದಲಾಯಿಸಲು ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "ರೂಟ್ ಸವಲತ್ತುಗಳನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಬಿಡಲಾಗಿದೆ."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "ವ್ಯವಸ್ಥೆಯಾದ್ಯಂತದ ಕ್ರಮಕ್ಕೆ ಈ ಪ್ಲಾಟ್‌ಫಾರ್ಮಿನಲ್ಲಿ ಬೆಂಬಲವಿಲ್ಲ."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "ಆಜ್ಞಾ ಸಾಲನ್ನು ಪಾರ್ಸ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "ಡೀಮನ್ ಚಲಾಯಿತಗೊಳ್ಳುತ್ತಿದೆ"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "ಡೀಮನ್ PID %u ಯಾಗಿ ಚಲಾಯಿಗೊಳ್ಳುತ್ತಿದೆ"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "ಡೀಮನ್ ಅನ್ನು ಕೊಲ್ಲಲು ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"ಈ ಪ್ರೋಗ್ರಾಮನ್ನು ರೂಟ್‌ ಆಗಿ ಚಲಾಯಿಸುವ ಉದ್ಧೇಶವನ್ನು ಹೊಂದಿಲ್ಲ (--system ಅನ್ನು ಸೂಚಿಸದೆ "
+"ಇದ್ದಲ್ಲಿ ಮಾತ್ರ)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "ನಿರ್ವಾಹಕ ಸವಲತ್ತುಗಳ ಅಗತ್ಯವಿದೆ."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "ವ್ಯವಸ್ಥೆಯ ಸನ್ನಿವೇಶದಿಂದ --start ಬೆಂಬಲಿತವಾಗಿಲ್ಲ."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr ""
+"ವ್ಯವಸ್ಥೆಯ ಕ್ರಮದಲ್ಲಿ ಚಲಾಯಿತಗೊಳ್ಳುತ್ತಿದೆ, ಆದರೆ --disallow-exit ಅನ್ನು ಹೊಂದಿಸಲಾಗಿಲ್ಲ!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"ವ್ಯವಸ್ಥೆಯ ಕ್ರಮದಲ್ಲಿ ಚಲಾಯಿತಗೊಳ್ಳುತ್ತಿದೆ, ಆದರೆ --disallow-module-loading ಅನ್ನು "
+"ಹೊಂದಿಸಲಾಗಿಲ್ಲ!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr ""
+"ವ್ಯವಸ್ಥೆಯ ಕ್ರಮದಲ್ಲಿ ಚಲಾಯಿತಗೊಳ್ಳುತ್ತಿದ್ದು, SHM ಕ್ರಮವನ್ನು ಒತ್ತಾಯಪೂರ್ವಕವಾಗಿ "
+"ಅಶಕ್ತಗೊಳಿಸುತ್ತಿದೆ!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"ವ್ಯವಸ್ಥೆಯ ಕ್ರಮದಲ್ಲಿ ಚಲಾಯಿತಗೊಳ್ಳುತ್ತಿದ್ದು, ನಿರ್ಗಮಿಸುವ ಜಡ ಸಮಯವನ್ನು ಒತ್ತಾಯಪೂರ್ವಕವಾಗಿ "
+"ಅಶಕ್ತಗೊಳಿಸುತ್ತಿದೆ!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdio ಅನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "ಪೈಪ್‌ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "ಡೀಮನ್ ಆರಂಭಗೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "ಡೀಮನ್ ಅನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಆರಂಭಿಸಲಾಗಿದೆ."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "ಇದು PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "ಕಂಪೈಲ್ ಮಾಡುವ ಅತಿಥೇಯ: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "ಕಂಪೈಲ್ ಮಾಡುವ CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "ಅತಿಥೇಯದಲ್ಲಿ ಚಲಾಯಿತಗೊಳ್ಳುತ್ತಿದೆ: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u CPUಗಳು ಕಂಡುಬಂದಿವೆ."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "ಪುಟದ ಗಾತ್ರವು %lu ಬೈಟ್‌ಗಳಾಗಿವೆ"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Valgrind ಬೆಂಬಲದೊಂದಿಗೆ ಕಂಪೈಲ್ ಮಾಡಲಾಗಿದೆ: ಹೌದು"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Valgrind ಬೆಂಬಲದೊಂದಿಗೆ ಕಂಪೈಲ್ ಮಾಡಲಾಗಿದೆ: ಇಲ್ಲ"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "valgrind ಕ್ರಮದಲ್ಲಿ ಚಲಾಯಿಸಲಾಗುತ್ತಿದೆ: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "ಅತಿಥೇಯದಲ್ಲಿ ಚಲಾಯಿತಗೊಳ್ಳುತ್ತಿದೆ: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "ಪ್ರಶಸ್ತವಾದ ನಿರ್ಮಾಣ: ಹೌದು"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "ಪ್ರಶಸ್ತವಾದ ನಿರ್ಮಾಣ: ಇಲ್ಲ"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG ಅನ್ನು ಸೂಚಿಸಲಾಗಿದೆ, ಎಲ್ಲಾ ಪ್ರತಿಪಾದನೆಗಳನ್ನೂ ಅಶಕ್ತಗೊಳಿಸಲಾಗಿದೆ."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr ""
+"FASTPATH ಅನ್ನು ಸೂಚಿಸಲಾಗಿದೆ, ಕೇವಲ ವೇಗ ಮಾರ್ಗದ ಪ್ರತಿಪಾದನೆಗಳನ್ನೂ ಅಶಕ್ತಗೊಳಿಸಲಾಗಿದೆ."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "ಎಲ್ಲಾ ಪ್ರತಿಪಾದನೆಗಳನ್ನೂ ಶಕ್ತಗೊಳಿಸಲಾಗಿದೆ."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "ಮೆಶೀನ್ ID ಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "ಮೆಶೀನ್ ID ಯು %s ಆಗಿದೆ."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "ಅಧಿವೇಶನ ID ಯು %s ಆಗಿದೆ."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "ಚಲಾವಣಾಸಮಯ(ರನ್‌ಟೈಮ್) ಕೋಶ %s ಅನ್ನು ಬಳಸಿಕೊಂಡು."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "ಸ್ಥಿತಿ ಕೋಶ %s ಅನ್ನು ಬಳಸಿಕೊಂಡು."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "ಘಟಕಗಳ ಕೋಶ %s ಅನ್ನು ಬಳಸಿಕೊಂಡು."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "ವ್ಯವಸ್ಥೆಯ ಕ್ರಮದಲ್ಲಿ ಚಲಾಯಿಸಲಾಗುತ್ತಿದೆ: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"ಸರಿ, ನೀವು PA ಅನ್ನು ವ್ಯವಸ್ಥೆಯ ಕ್ರಮದಲ್ಲಿ (ಸಿಸ್ಟಮ್ ಮೋಡ್) ಚಲಾಯಿಸುತ್ತಿದ್ದೀರಿ. ಸಾಮಾನ್ಯವಾಗಿ "
+"ನೀವು ಹಾಗೆ ಮಾಡಬಾರದು ಎನ್ನುವುದನ್ನು ದಯವಿಟ್ಟು ನೆನಪಿಡಿ.\n"
+"ನೀವು ಹಾಗೆ ಮಾಡಿದಲ್ಲಿ, ಮುಂದೆ ಏನಾದರೂ ತೊಂದರೆ ಆದಲ್ಲಿ ಅದು ನಿಮ್ಮದೆ ತಪ್ಪಿನ "
+"ಕಾರಣದಿಂದಾಗಿರುತ್ತದೆ.\n"
+"ವ್ಯವಸ್ಥೆಯ ಕ್ರಮವು (ಸಿಸ್ಟಮ್ ಮೋಡ್) ಏಕೆ ಒಂದು ಸರಿಯಲ್ಲದ ಬಳಕೆ ಎಂದು ಅರಿಯಲು ದಯವಿಟ್ಟು http://"
+"www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/"
+"WhatIsWrongWithSystemWide/ ಅನ್ನು ನೋಡಿ."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "ತಾಜಾ ರೆಸಲ್ಯೂಶನ್ ಟೈಮರ್ ಲಭ್ಯವಿದೆ! Bon appetit!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"ಮಹಾಶಯರೆ, ನಿಮ್ಮ ಕರ್ನಲ್ ಕೊಳೆತುಹೋಗಿದೆ! ಅತ್ಯುತ್ತಮ ರೆಸಲ್ಯೂಶನ್ ಟೈಮರ್ ಅನ್ನು ಶಕ್ತಗೊಳಿಸಲಾದ "
+"ಲಿನಕ್ಸನ್ನು ಬಳಸುವಂತೆ ಅಡುಗೆಯವರು ಸಲಹೆ ಮಾಡುತ್ತಿದ್ದಾರೆ!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "ಡೀಮನ್ ಅನ್ನು ಆರಂಭಿಸಲು ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "ಲೋಡ್ ಮಾಡಲಾದ ಯಾವುದೆ ಡೀಮನ್ ಇಲ್ಲದೆ ಆರಂಭಗೊಂಡಿದೆ, ಕೆಲಸ ಮಾಡಲು ನಿರಾಕರಿಸಿದೆ."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "ಡೀಮನ್ ಆರಂಭಗೊಳಿಕೆ ಪೂರ್ಣಗೊಂಡಿದೆ."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "ಡೀಮನ್ ಸ್ಥಗಿತಗೊಳಿಕೆಯನ್ನು ಆರಂಭಿಸಲಾಗಿದೆ."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "ಡೀಮನ್ ಅನ್ನು ಅಂತ್ಯಗೊಳಿಸಲಾಗಿದೆ."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [ಆಯ್ಕೆಗಳು]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            ಈ ನೆರವನ್ನು ತೋರಿಸು\n"
+"      --version                         ಆವೃತ್ತಿಯನ್ನು ತೋರಿಸು\n"
+"      --dump-conf                       ಪೂರ್ವನಿಯೋಜಿತ ಸಂರಚನೆಯನ್ನು ಬಿಸುಡು\n"
+"      --dump-modules                    ಲಭ್ಯವಿರುವ ಘಟಕಗಳನ್ನು ಬಿಸುಡು\n"
+"      --dump-resample-methods           ಲಭ್ಯವಿರುವ ಮರುನಮೂನೆ ವಿಧಾನಗಳನ್ನು ಬಿಸುಡು\n"
+"      --cleanup-shm                     ಇಕ್ಕಟ್ಟಿನಲ್ಲಿ ಹಂಚಲಾದ ಮೆಮೊರಿ ಖಂಡಗಳನ್ನು "
+"ಸ್ವಚ್ಛಗೊಳಿಸು\n"
+"      --start                           ಡೀಮನ್ ಚಾಲನೆಯಲ್ಲಿಲ್ಲದೆ ಇದ್ದಲ್ಲಿ ಆರಂಭಿಸು\n"
+"  -k  --kill                            ಚಾಲನೆಯಲ್ಲಿರು ಡೀಮನ್ ಅನ್ನು ಕೊಲ್ಲು\n"
+"      --check                           ಚಾಲನೆಯಲ್ಲಿರುವ ಡೀಮನ್‌ಗಾಗಿ ಪರಿಶೀಲಿಸು "
+"(ನಿರ್ಗಮನಾ ಸಂಜ್ಞೆಯನ್ನು ಮಾತ್ರ ಮರಳಿಸುತ್ತದೆ)\n"
+"\n"
+"ಆಯ್ಕೆಗಳು:\n"
+"      --system[=BOOL]                   ವ್ಯವಸ್ಥೆಯಾದ್ಯಂತದ ಸನ್ನಿವೇಶವಾಗಿ ಚಲಾಯಿಸು\n"
+"  -D, --daemonize[=BOOL]                ಆರಂಭಗೊಂಡ ನಂತರ ಡೀಮನ್ ಆಗಿ ಮಾಡು\n"
+"      --fail[=BOOL]                     ಆರಂಭಗೊಳಿಕೆಯು ವಿಫಲಗೊಂಡಾಗ ನಿರ್ಗಮಿಸು\n"
+"      --high-priority[=BOOL]            ಹೆಚ್ಚಿನ ನೈಸ್‌ ಮಟ್ಟವನ್ನು ಹೊಂದಿಸಲು "
+"ಪ್ರಯತ್ನಿಸು\n"
+"                                        (SUID ಅಥವ ಓರೆಯಾದ RLIMIT_NICE ಇದ್ದಾಗ, "
+"ಕೇವಲ\n"
+"                                        ರೂಟ್ ಆಗಿ ಮಾತ್ರವೆ ಲಭ್ಯವಿರುತ್ತದೆ)\n"
+"      --realtime[=BOOL]                 ರಿಯಲ್ ಟೈಮ್ ಶೆಡ್ಯೂಲಿಂಗ್ ಅನ್ನು "
+"ಶಕ್ತಗೊಳಿಸಲಾಗುತ್ತಿದೆ\n"
+"                                        (SUID ಅಥವ ಓರೆಯಾದ RLIMIT_RTPRIO "
+"ಇದ್ದಾಗ, ಕೇವಲ\n"
+"                                        ರೂಟ್ ಆಗಿ ಮಾತ್ರವೆ ಲಭ್ಯವಿರುತ್ತದೆ)\n"
+"      --disallow-module-loading[=BOOL]  ಆರಂಭಗೊಂಡ ನಂತರ ಬಳಕೆದಾರರು ಮನವಿ "
+"ಸಲ್ಲಿಸಿದಂತಹ ಘಟಕವನ್ನು\n"
+"                                        ಲೋಡ್‌/ಅನ್‌ಲೋಡ್ ಮಾಡುವುದನ್ನು ಅನುಮತಿಸದಿರು\n"
+"      --disallow-exit[=BOOL]            ಬಳಕೆದಾರರು ಮನವಿ ಸಲ್ಲಿಸಿದ ನಿರ್ಗಮಿಸು "
+"ಮನವಿಯನ್ನು ಅನುಮತಿಸದಿರು\n"
+"      --exit-idle-time=SECS             ಡೀಮನ್ ಜಡಗೊಂಡು ಇಷ್ಟು ಸಮಯ ಕಳೆದಿದ್ದಲ್ಲಿ \n"
+"                                        ಅದನ್ನು ಅಂತ್ಯಗೊಳಿಸು\n"
+"      --module-idle-time=SECS           ತಾನಾಗಿಯೆ ಲೋಡ್‌ ಆದ ಘಟಕಗಳು ಜಡಗೊಂಡು ಇಷ್ಟು "
+"ಸಮಯ\n"
+"                                        ಕಳೆದೆದಿದ್ದಲ್ಲಿ ಅನ್‌ಲೋಡ್ ಮಾಡು\n"
+"      --scache-idle-time=SECS           ತಾನಾಗಿಯೆ ಲೋಡ್‌ ಆದ ನಮೂನೆಗಳು ಜಡಗೊಂಡು "
+"ಇಷ್ಟು ಸಮಯ\n"
+"                                        ಕಳೆದೆದಿದ್ದಲ್ಲಿ ಅನ್‌ಲೋಡ್ ಮಾಡು\n"
+"      --log-level[=LEVEL]               ವರ್ಬೋಸ್ ಮಟ್ಟವನ್ನು ಹೆಚ್ಚಿಸು ಅಥವ ಹೊಂದಿಸು\n"
+"  -v                                    ವರ್ಬೋಸ್ ಮಟ್ಟವನ್ನು ಹೆಚ್ಚಿಸು\n"
+"      --log-target={auto,syslog,stderr} ದಾಖಲೆಯ ಗುರಿಯನ್ನು ಸೂಚಿಸು\n"
+"      --log-meta[=BOOL]                 ದಾಖಲೆಯ ಸಂದೇಶಗಳಲ್ಲಿ ಸಂಜ್ಞೆಯು ಇರುವ "
+"ಸ್ಥಳವನ್ನು ಸೇರಿಸು\n"
+"      --log-time[=BOOL]                 ದಾಖಲೆಯ ಸಂದೇಶಗಳಲ್ಲಿ ಸಮಯದ ಮುದ್ರೆಯನ್ನು "
+"ಸೇರಿಸು\n"
+"      --log-backtrace=FRAMES            ದಾಖಲೆಯ ಸಂದೇಶಗಳಲ್ಲಿ ಹಿಂದಕ್ಕೆ "
+"ಹುಡುಕುವುದನ್ನು ಸೇರಿಸು\n"
+"  -p, --dl-search-path=PATH             ಡೈನಮಿಕ್ ಹಂಚಲಾದ ವಸ್ತುಗಳಿಗಾಗಿ (ಪ್ಲಗ್‌ಇನ್‌ಗಳು)"
+"ಹುಡುಕು\n"
+"                                        ಮಾರ್ಗವನ್ನು ಸೂಚಿಸು\n"
+"      --resample-method=METHOD          ಸೂಚಿಸಲಾದ ಮರು-ನಮೂನಾ ವಿಧಾನವನ್ನು ಬಳಸಿ\n"
+"                                        (ಸಾಧ್ಯವಿರುವ  ಮೌಲ್ಯಗಳಿಗಾಗಿ --dump-"
+"resample-methods ಅನ್ನು\n"
+"                                        ನೋಡಿ)\n"
+"      --use-pid-file[=BOOL]             ಒಂದು PID ಕಡತವನ್ನು ರಚಿಸು\n"
+"      --no-cpu-limit[=BOOL]             CPU ಲೋಡ್ ಅನ್ನು ಮಿತಿಗೊಳಿಸುವುದನ್ನು "
+"ಬೆಂಬಲಿಸುವ ಪ್ಲಾಟ್‌ಫಾರ್ಮಿನ \n"
+"                                        ಮೇಲೆ ಅದನ್ನು ಅನುಸ್ಥಾಪಿಸಬೇಡ..\n"
+"      --disable-shm[=BOOL]              ಹಂಚಲಾದ ಮೆಮೊರಿ ಅಶಕ್ತಗೊಳಿಸು.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         ಸೂಚಿಸಲಾದ ಪ್ಲಗ್‌ಇನ್ ಘಟಕವನ್ನು ಸೂಚಿತ\n"
+"                                        ಆರ್ಗುಮೆಂಟುಗಳೊಂದಿಗೆ ಲೋಡ್ ಮಾಡು\n"
+"  -F, --file=FILENAME                   ಸೂಚಿಸಲಾದ ಸ್ಕ್ರಿಪ್ಟನ್ನು ಚಲಾಯಿಸು\n"
+"  -C                                    ಆರಂಭಗೊಂಡ ನಂತರ TTY ಅನ್ನು ಚಲಾಯಿಸಿದ ನಂತರ "
+"ಒಂದು \n"
+"                                        ಆಜ್ಞಾ ಸಾಲನ್ನು ತೆರೆ\n"
+"\n"
+"  -n                                    ಪೂರ್ವನಿಯೋಜಿತ ಸ್ಕ್ರಿಪ್ಟಿನ ಕಡತವನ್ನು ಲೋಡ್ "
+"ಮಾಡಬೇಡ\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level ದಾಖಲೆ ಮಟ್ಟದ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ (0..4 ವ್ಯಾಪ್ತಿಯಲ್ಲಿನ ಅಂಕೆಯನ್ನು "
+"ಅಥವ debug, info, notice, warn, error ಅನ್ನು)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "ಅಮಾನ್ಯವಾದ ದಾಖಲೆ ಗುರಿ: 'syslog', 'stderr' ಅಥವ 'auto' ಅನ್ನು ಬಳಸಿ."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "ಅಮಾನ್ಯವಾದ ಮರುನಮೂನೆ ವಿಧಾನ '%s'."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm ಬೂಲಿಯನ್ ಆರ್ಗುಮೆಂಟನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತದೆ"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "ಹೆಸರು: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "ಯಾವುದೆ ಘಟಕ ಮಾಹಿತಿಯು ಲಭ್ಯವಿಲ್ಲ\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "ಆವೃತ್ತಿ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "ವಿವರಣೆ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "ಕತೃ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "ಬಳಕೆ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "ಒಮ್ಮೆ ಲೋಡ್ ಮಾಡು: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "DEPRECATION WARNING: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "ಮಾರ್ಗ: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ಯವಾದ ದಾಖಲೆ ಗುರಿ '%s'."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ಯವಾದ ದಾಖಲೆ ಮಟ್ಟ '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ಯವಾದ ಮರುನಮೂನೆ ವಿಧಾನ '%s'."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ಯವಾದ rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ಯವಾದ ನಮೂನೆ ರಚನೆ '%s'."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ಯವಾದ ನಮೂನೆ ದರ '%s'."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ಯವಾದ ನಮೂನೆ ಚಾನಲ್‌ಗಳು '%s'."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ಯವಾದ ಚಾನಲ್ ನಕ್ಷೆ '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ತವಾದ ಫ್ರಾಗ್ಮೆಂಟುಗಳ ಸಂಖ್ಯೆ '%s'."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ಯವಾದ ಫ್ರಾಗ್ಮೆಂಟ್ ಗಾತ್ರ '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ಯವಾದ ನೈಸ್‌ ಹಂತ '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] ಅಮಾನ್ಯವಾದ ನಮೂನೆ ದರ '%s'."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "ಸಂರಚನಾ ಕಡತವನ್ನು ತೆರೆಯಲು ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"ಸೂಚಿಸಲಾದ ಪೂರ್ವನಿಯೋಜಿತ ಚಾನಲ್ ನಕ್ಷೆಯು ಪೂರ್ವನಿಯೋಜಿತ ಚಾನಲ್‌ಗಳ ಸಂಖ್ಯೆಗಳಿಗಿಂತ ವಿಭಿನ್ನವಾದ "
+"ಮಾರ್ಗಗಳ ಸಂಖ್ಯೆಯನ್ನು ಹೊಂದಿದೆ."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### ಸಂರಚನಾ ಕಡತದಿಂದ ಓದು: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "ಸವಲತ್ತುಗಳನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಲಾಗುತ್ತಿದೆ."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio ಧ್ವನಿ ವ್ಯವಸ್ಥೆ"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "PulseAudio ಧ್ವನಿ ವ್ಯವಸ್ಥೆಯನ್ನು ಆರಂಭಿಸಿ"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio ಧ್ವನಿ ವ್ಯವಸ್ಥೆ"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "PulseAudio ಧ್ವನಿ ವ್ಯವಸ್ಥೆಯನ್ನು ಆರಂಭಿಸಿ"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "ಮೊನೊ"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "ಎದುರಿನ ಮಧ್ಯಭಾಗ"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "ಎದುರಿನ ಎಡಭಾಗ"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "ಎದುರಿನ ಬಲಭಾಗ"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "ಹಿಂಬದಿಯ ಮಧ್ಯಭಾಗ"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "ಹಿಂಬದಿಯ ಎಡಭಾಗ"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "ಹಿಂಬದಿಯ ಬಲಭಾಗ"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "ಮಧ್ಯದ ಎಡಭಾಗದ ಎದುರುಭಾಗ"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "ಮಧ್ಯದ ಬಲಭಾಗದ ಎದುರುಭಾಗ"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "ಬದಿಯ ಎಡಭಾಗ"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "ಬದಿಯ ಬಲಭಾಗ"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "ಸಹಾಯಕ 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "ಸಹಾಯಕ 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "ಸಹಾಯಕ 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "ಸಹಾಯಕ 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "ಸಹಾಯಕ 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "ಸಹಾಯಕ 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "ಸಹಾಯಕ 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "ಸಹಾಯಕ 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "ಸಹಾಯಕ 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "ಸಹಾಯಕ 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "ಸಹಾಯಕ 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "ಸಹಾಯಕ 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "ಸಹಾಯಕ 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "ಸಹಾಯಕ 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "ಸಹಾಯಕ 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "ಸಹಾಯಕ 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "ಸಹಾಯಕ 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "ಸಹಾಯಕ 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "ಸಹಾಯಕ 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "ಸಹಾಯಕ 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "ಸಹಾಯಕ 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "ಸಹಾಯಕ 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "ಸಹಾಯಕ 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "ಸಹಾಯಕ 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "ಸಹಾಯಕ 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "ಸಹಾಯಕ 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "ಸಹಾಯಕ 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "ಸಹಾಯಕ 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "ಸಹಾಯಕ 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "ಸಹಾಯಕ 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "ಸಹಾಯಕ 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "ಸಹಾಯಕ 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "ಮೇಲಿನ ಮಧ್ಯಭಾಗ"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "ಮೇಲಿನ ಎದುರಿನ ಮಧ್ಯಭಾಗ"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "ಮೇಲಿನ ಎದುರಿನ ಎಡಭಾಗ"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "ಮೇಲಿನ ಎದುರಿನ ಬಲಭಾಗ"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "ಮೇಲಿನ ಹಿಂಬದಿಯ ಮಧ್ಯಭಾಗ"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "ಮೇಲಿನ ಹಿಂಬದಿಯ ಎಡಭಾಗ"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "ಮೇಲಿನ ಹಿಂಬದಿಯ ಬಲಭಾಗ"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(ಅಮಾನ್ಯ)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "ಸ್ಟೀರಿಯೋ"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "ಸರೌಂಡ್‌ 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "ಸರೌಂಡ್‌ 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "ಸರೌಂಡ್‌ 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "ಸರೌಂಡ್‌ 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "ಸರೌಂಡ್‌ 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "ಸರಿ"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "ನಿಲುಕಣೆಯನ್ನು ತಿರಸ್ಕರಿಸಲಾಗಿದೆ"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "ಅಜ್ಞಾತ ಆಜ್ಞೆ"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "ಅಮಾನ್ಯವಾದ ಆರ್ಗ್ಯುಮೆಂಟ್"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "ನಮೂದು ಅಸ್ತಿತ್ವದಲ್ಲಿದೆ"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "ಅಂತಹ ಯಾವುದೆ ನಮೂದು ಇಲ್ಲ"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "ಸಂಪರ್ಕವನ್ನು ತಿರಸ್ಕರಿಸಲಾಗಿದೆ"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "ಪ್ರೊಟೊಕಾಲ್ ದೋಷ"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "ಕಾಲಾವಕಾಶ ಮುಗಿದಿದೆ"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "ಯಾವುದೆ ದೃಢೀಕರಣ ಕೀಲಿ ಇಲ್ಲ"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "ಆಂತರಿಕ ದೋಷ"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "ಸಂಪರ್ಕವನ್ನು ಅಂತ್ಯಗೊಳಿಸಲಾಗಿದೆ"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "ನಮೂದನ್ನು ಕೊಲ್ಲಲಾಗಿದೆ"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "ಅಮಾನ್ಯವಾದ ಪರಿಚಾರಕ"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "ಘಟಕವನ್ನು ಆರಂಭಿಸುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "ಸರಿಯಲ್ಲದ ಸ್ಥಿತಿ"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "ದತ್ತಾಂಶ ಇಲ್ಲ"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "ಸಹವರ್ತನೀಯವಲ್ಲದ ಪ್ರೊಟೋಕಾಲ್ ಆವೃತ್ತಿ"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "ಬಹಳ ದೊಡ್ಡದಾಗಿದೆ"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "ಬೆಂಬಲವಿಲ್ಲ"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "ಅಜ್ಞಾತ ದೋಷ ಸಂಜ್ಞೆ"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "ಅಂತಹ ಯಾವುದೆ ವಿಸ್ತರಣೆ ಇಲ್ಲ"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "ಪರಿಪೂರ್ಣ ಕ್ರಿಯಾಶೀಲತೆ"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "ಅನ್ವಯಿಸುವಿಕೆಯು ಕಾಣಿಸುತ್ತಿಲ್ಲ"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "ಕ್ಲೈಂಟ್ ಅನ್ನು ಫೋರ್ಕ್ ಮಾಡಲಾಗಿದೆ"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "ಇನ್‌ಪುಟ್/ಔಟ್‌ಪುಟ್ ದೋಷ"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "ಸಾಧನ ಅಥವ ಸಂಪನ್ಮೂಲವು ಕಾರ್ಯನಿರತವಾಗಿದೆ"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "ಕುಕಿ ದತ್ತಾಂಶವನ್ನು ಪಾರ್ಸ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "ಸಂರಚನಾ ಕಡತ '%s' ಅನ್ನು ಲೋಡ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr ""
+"ಯಾವುದೆ ಕುಕಿಯನ್ನು ಲೋಡ್ ಮಾಡಲಾಗಿಲ್ಲ. ಕುಕಿ ಇಲ್ಲದೆ ಸಂಪರ್ಕಸಾಧಿಸಲು ಪ್ರಯತ್ನಿಸಲಾಗುತ್ತಿದೆ."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "ಫೋರ್ಕ್(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "ಅಜ್ಞಾತ ವಿಸ್ತರಣೆ '%s' ಇಂದ ಸಂದೇಶವನ್ನು ಪಡೆದುಕೊಳ್ಳಲಾಗಿದೆ"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "ಸ್ಟ್ರೀಮನ್ನು ಬರಿದಾಗಿಸುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "ಪ್ಲೇಬ್ಯಾಕ್ ಸ್ಟ್ರೀಮನ್ನು ಬರಿದಾಗಿಸಲಾಗಿದೆ."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "ಪರಿಚಾರಕಕ್ಕೆ ಬರಿದಾಗಿಸುವ ಸಂಪರ್ಕ."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "ಸ್ಟ್ರೀಮನ್ನು ಯಶಸ್ವಿಯಾಗಿ ನಿರ್ಮಿಸಲಾಗಿದೆ."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "ಬಫರ್ ಮೆಟ್ರಿಕ್‌ಗಳು: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "ಬಫರ್ ಮೆಟ್ರಿಕ್‌ಗಳು: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "ನಮೂನೆಯ ವಿವರ '%s' ಅನ್ನು, ಚಾನಲ್‌ ನಕ್ಷೆ '%s' ಅನ್ನು ಬಳಸಿಕೊಂಡು."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "ಸಾಧನ %s ಕ್ಕೆ ಸಂಪರ್ಕ ಜೋಡಿಸಲಾಗಿದೆ (%u, %ssuspended)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "ಸ್ಟ್ರೀಮ್ ದೋಷ: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "ಸ್ಟ್ರೀಮ್ ಸಾಧನವನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ತಡೆಹಿಡಿಯಲಾಗಿದೆ.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "ಸ್ಟ್ರೀಮ್ ಸಾಧನವನ್ನು ಮರಳಿ ಆರಂಭಿಸಲಾಗಿದೆ.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಕಡಿಮೆ ಚಲಾಯಿಸಲಾಗಿದೆ.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಹೆಚ್ಚು ಚಲಾಯಿಸಲಾಗಿದೆ.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಆರಂಭಿಸಲಾಗಿದೆ.%s "
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "ಸಾಧನ %s ಕ್ಕೆ ಸ್ಟ್ರೀಮ್‌ ಅನ್ನು ಸ್ಥಳಾಂತರಿಸಲಾಗಿದೆ (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "ಇಲ್ಲ "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "ಸ್ಟ್ರೀಮ್ ಬಫರ್ ಗುಣವಿಶೇಷತೆಗಳನ್ನು ಬದಲಾಯಿಸಲಾಗಿದೆ.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "ಸಂಪರ್ಕವನ್ನು ಸಾಧಿಸಲಾಗಿದೆ.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "ಸಂಪರ್ಕದ ವಿಫಲತೆ: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF ಅನ್ನು ಪಡೆಯಲಾಗಿದೆ."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "ಸಂಕೇತ ದೊರೆತಿದೆ, ನಿರ್ಗಮಿಸುತ್ತಿದೆ."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "ಅಗೋಚರತೆಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "ಸಮಯ: %0.3f sec; ಅಗೋಚರತೆ: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [ಆಯ್ಕೆಗಳು]\n"
+"\n"
+"  -h, --help                            ಈ ನೆರವನ್ನು ತೋರಿಸು\n"
+"      --version                         ಆವೃತ್ತಿಯನ್ನು ತೋರಿಸು\n"
+"\n"
+"  -r, --record                          ರೆಕಾರ್ಡಿಂಗಿಗಾಗಿ ಒಂದು ಸಂಪರ್ಕವನ್ನು ರಚಿಸು\n"
+"  -p, --playback                        ಪ್ಲೇಬ್ಯಾಕಿಗಾಗಿ ಒಂದು ಸಂಪರ್ಕವನ್ನು ರಚಿಸು\n"
+"\n"
+"  -v, --verbose                         ವರ್ಬೋಸ್ ಕಾರ್ಯವನ್ನು ಶಕ್ತಗೊಳಿಸು\n"
+"\n"
+"  -s, --server=SERVER                   ಸಂಪರ್ಕಸಾಧಿಸಬೇಕಿರುವ ಪರಿಚಾರಕದ ಹೆಸರು\n"
+"  -d, --device=DEVICE                   ಸಂಪರ್ಕಸಾಧಿಸಬೇಕಿರುವ ಸಿಂಕಿನ/ಆಕರದ ಹೆಸರು\n"
+"  -n, --client-name=NAME                ಪರಿಚಾರಕದಲ್ಲಿ ಈ ಕ್ಲೈಂಟಿನಲ್ಲಿ ಏನೆಂದು "
+"ಕರೆಯಬೇಕು\n"
+"      --stream-name=NAME                ಪರಿಚಾರಕದಲ್ಲಿ ಈ ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಏನೆಂದು "
+"ಕರೆಯಬೇಕು\n"
+"      --volume=VOLUME                   ಆರಂಭಿಕ (ರೇಖೀಯ) ಪರಿಮಾಣವನ್ನು 0...65536 "
+"ವ್ಯಾಪ್ತಿಯಲ್ಲಿ ತೋರಿಸು\n"
+"      --rate=SAMPLERATE                 ನಮೂನೆಯ ದರ Hz ನಲ್ಲಿ (ಪೂರ್ವನಿಯೊಜಿತವು "
+"44100 ಆಗಿರುತ್ತದೆ)\n"
+"      --format=SAMPLEFORMAT             ನಮೂನೆಯ ಬಗೆ, s16le, s16be, u8, "
+"float32le ಗಳಲ್ಲಿ ಒಂದು,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be "
+"(ಪೂರ್ವನಿಯೋಜಿತವು s16ne ಆಗಿರುತ್ತದೆ)\n"
+"      --channels=CHANNELS               ಚಾನಲ್‌ಗಳ ಸಂಖ್ಯೆ, ಮೋನೊಗಾಗಿ 1, "
+"ಸ್ಟೀರಿಯೋಗಾಗಿ 2 ಆಗಿರುತ್ತದೆ\n"
+"                                        (ಪೂರ್ವನಿಯೊಜಿತವು 2 ಆಗಿರುತ್ತದೆ)\n"
+"      --channel-map=CHANNELMAP          ಪೂರ್ವನಿಯೋಜಿತದ ಬದಲಿಗೆ ಬಳಸಬೇಕಿರುವ ಚಾನಲ್ "
+"ನಕ್ಷೆ\n"
+"      --fix-format                      ಸ್ಟ್ರೀಮ್ ಸಂಪರ್ಕಿತಗೊಳ್ಳಲಿರುವ ಸಿಂಕಿನಿಂದ "
+"ನಮೂನೆಯ\n"
+"                                        ರಚನೆಯನ್ನು ತೆಗೆದುಕೊ.\n"
+"      --fix-rate                        ಸ್ಟ್ರೀಮ್ ಸಂಪರ್ಕಿತಗೊಳ್ಳಲಿರುವ ಸಿಂಕಿನಿಂದ "
+"ನಮೂನೆಯ\n"
+"                                        ದರವನ್ನು ತೆಗೆದುಕೊ.\n"
+"      --fix-channels                    ಸ್ಟ್ರೀಮ್ ಸಂಪರ್ಕಿತಗೊಳ್ಳಲಿರುವ ಸಿಂಕಿನಿಂದ "
+"ಚಾನಲ್‌ಗಳ ಸಂಖ್ಯೆ\n"
+"                                        ಹಾಗು ಚಾನಲ್‌ನ ನಕ್ಷೆಯನ್ನು ತೆಗೆದುಕೊ.\n"
+"      --no-remix                        ಚಾನಲ್‌ಗಳನ್ನು upmix ಅಥವ downmix ಮಾಡಬೇಡ.\n"
+"      --no-remap                        ಚಾನಲ್‌ಗಳನ್ನು ಹೆಸರುಗಳ ಬದಲಿಗೆ ಸೂಚಿಯಿಂದ "
+"ಮ್ಯಾಪ್ ಮಾಡು.\n"
+"      --latency=BYTES                   ಸೂಚಿಸಲಾದ ಅಗೋಚರತೆಯನ್ನು ಬೈಟ್‌ಗಳಲ್ಲಿ ಮನವಿ "
+"ಮಾಡು.\n"
+"      --process-time=BYTES              ಸೂಚಿಸಲಾದ ಪ್ರತಿ ಮನವಿಯ ಪ್ರಕ್ರಿಯೆಯ ಸಮಯವನ್ನು "
+"ಬೈಟ್‌ಗಳಲ್ಲಿ ಮನವಿ ಮಾಡು.\n"
+"      --property=PROPERTY=VALUE         ನಿಶ್ಚಿತ ಗುಣಲಕ್ಷಣವನ್ನು ನಿಶ್ಚಿತ ಮೌಲ್ಯವನ್ನು "
+"ಹೊಂದಿಸಿ.\n"
+"      --raw                             ಕಚ್ಛಾ PCM ದತ್ತಾಂಶವನ್ನು ರೆಕಾರ್ಡು ಮಾಡು/"
+"ಚಲಾಯಿಸು.\n"
+"      --file-format=FFORMAT             ಫಾರ್ಮಾಟ್ ಮಾಡಲಾದ PCM ದತ್ತಾಂಶವನ್ನು ರೆಕಾರ್ಡು "
+"ಮಾಡು/ಚಲಾಯಿಸು.\n"
+"      --list-file-formats               ಲಭ್ಯವಿರುವ ಕಡತ ವಿನ್ಯಾಸಗಳ ಪಟ್ಟಿ.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"libpulse %s ನೊಂದಿಗೆ ಕಂಪೈಲ್ ಮಾಡಲಾಗಿದೆ\n"
+"libpulse %s ನೊಂದಿಗೆ ಜೋಡಿಸಲಾಗಿದೆ\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "ಅಮಾನ್ಯವಾದ ಕ್ಲೈಂಟಿನ ಹೆಸರು '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "ಅಮಾನ್ಯವಾದ ಸ್ಟ್ರೀಮ್‌ನ ಹೆಸರು '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "ಅಮಾನ್ಯವಾದ ಚಾನಲ್ ನಕ್ಷೆ '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "ಅಮಾನ್ಯವಾದ ಅಗೋಚರತೆ ವಿವರಣೆ '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "ಅಮಾನ್ಯವಾದ ಪ್ರಕ್ರಿಯೆ ಸಮಯದ ವಿವರಣೆ '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "ಅಮಾನ್ಯವಾದ ಗುಣಲಕ್ಷಣ '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "ಅಮಾನ್ಯವಾದ ಕಡತ ವಿನ್ಯಾಸ %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "ಅಮಾನ್ಯವಾದ ನಮೂನೆ ವಿವರ"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "ಬಹಳಷ್ಟು ಆರ್ಗುಮೆಂಟ್‌ಗಳು."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "ನಮೂನೆಯ ಮಾಹಿತಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "ಧ್ವನಿ ಕಡತವನ್ನು ತೆರೆಯುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"ಎಚ್ಚರಿಕೆ: ಸೂಚಿಸಲಾದ ನಮೂನೆ ವಿವರಣೆಯನ್ನು ಕಡತದಲ್ಲಿನ ವಿವರಣೆಯಿಂದ ತಿದ್ದಿಬರೆಯಲಾಗುತ್ತದೆ."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "ಕಡತದಿಂದ ನಮೂನೆಯ ವಿವರಣೆಯನ್ನು ನಿರ್ಧರಿಸುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "ಎಚ್ಚರಿಕೆ: ಕಡತದಿಂದ ಚಾನಲ್ ನಕ್ಷೆಯನ್ನು ನಿರ್ಧರಿಸುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "ಚಾನಲ್ ನಕ್ಷೆಯು ನಮೂನೆಯ ವಿವರಣೆಯೊಂದಿಗೆ ತಾಳೆಯಾಗುತ್ತಿಲ್ಲ"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "ಎಚ್ಚರಿಕೆ: ಕಡತಕ್ಕೆ ಚಾನಲ್ ನಕ್ಷೆಯನ್ನು ಬರೆಯುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"ಒಂದು %s ಸ್ಟ್ರೀಮ್‌ ಅನ್ನು ನಮೂನೆ ವಿವರಣೆ '%s' ಯೊಂದಿಗೆ ಹಾಗು ಚಾನಲ್ ನಕ್ಷೆ '%s' ಯೊಂದಿಗೆ "
+"ತೆರೆಯಲಾಗುತ್ತಿದೆ."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "ರೆಕಾರ್ಡಿಂಗ್"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "ಪ್ಲೇಬ್ಯಾಕ್‌"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "ಆಜ್ಞಾ ಸಾಲನ್ನು ಪಾರ್ಸ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "ಫೋರ್ಕ್(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "ಸ್ಥಗಿತಗೊಳಿಸಲು ವಿಫಲಗೊಂಡಿದೆ: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "ಮರಳಿ ಆರಂಭಿಸಲು ವಿಫಲಗೊಂಡಿದೆ: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "ಎಚ್ಚರಿಕೆ: ಧ್ವನಿ ಪರಿಚಾರಕವು ಸ್ಥಳೀಯವಾಗಿಲ್ಲ, ತಾತ್ಕಾಲಿಕವಾಗಿ ತಡೆಹಿಡಿಯಲಾಗುತ್ತಿಲ್ಲ.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "ಸಂಪರ್ಕದ ವಿಫಲತೆ: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT ದೊರೆತಿದೆ, ನಿರ್ಗಮಿಸುತ್ತಿದೆ.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "ಎಚ್ಚರಿಕೆ: ಉಪ ಪ್ರಕ್ರಿಯೆಯು %u ಸಂಕೇತದೊಂದಿಗೆ ಅಂತ್ಯಗೊಂಡಿದೆ\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [ಆಯ್ಕೆಗಳು] ... \n"
+"\n"
+"  -h, --help                            ಈ ನೆರವನ್ನು ತೋರಿಸು\n"
+"      --version                         ಆವೃತ್ತಿಯನ್ನು ತೋರಿಸು\n"
+"  -s, --server=SERVER                   ಸಂಪರ್ಕಸಾಧಿಸಬೇಕಿರುವ ಪರಿಚಾರಕದ ಹೆಸರು\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"libpulse %s ನೊಂದಿಗೆ ಕಂಪೈಲ್ ಮಾಡಲಾಗಿದೆ\n"
+"libpulse %s ನೊಂದಿಗೆ ಜೋಡಿಸಲಾಗಿದೆ\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() ವಿಫಲಗೊಂಡಿದೆ.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() ವಿಫಲಗೊಂಡಿದೆ.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() ವಿಫಲಗೊಂಡಿದೆ.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "ಅಂಕಿಅಂಶಗಳನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "ಪ್ರಸಕ್ತ ಬಳಕೆಯಲ್ಲಿರುವುದು: %u ಖಂಡಗಳು ಒಟ್ಟು %s ಬೈಟ್‌ಗಳನ್ನು ಹೊಂದಿದೆ.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"ಸಂಪೂರ್ಣ ಜೀವಿತಾವಧಿಯ ಸಮಯದಲ್ಲಿ ನಿಯೋಜಿಸಲಾಗಿದ್ದು: %u ಖಂಡಗಳು ಒಟ್ಟು %s ಬೈಟ್‌ಗಳನ್ನು "
+"ಹೊಂದಿದೆ.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "ನಮೂನೆಯ ಕ್ಯಾಶೆ ಗಾತ್ರ: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "ಪರಿಚಾರಕದ ಮಾಹಿತಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"ಬಳಕೆದಾರ ಹೆಸರು: %s\n"
+"ಅತಿಥೇಯದ ಹೆಸರು: %s\n"
+"ಪರಿಚಾರಕದ ಹೆಸರು: %s\n"
+"ಪರಿಚಾರಕ ಆವೃತ್ತಿ: %s\n"
+"ಪೂರ್ವನಿಯೋಜಿತ ನಮೂನೆ ವಿವರಣೆ: %s\n"
+"ಪೂರ್ವನಿಯೋಜಿತ ಚಾನಲ್‌ ನಕ್ಷೆ: %s\n"
+"ಪೂರ್ವನಿಯೋಜಿತ ಸಿಂಕ್: %s\n"
+"ಪೂರ್ವನಿಯೋಜಿತ ಆಕರ: %s\n"
+"ಕುಕಿ: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "ಸಿಂಕ್‌ ಮಾಹಿತಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ಸಿಂಕ್ #%u\n"
+"\tಸ್ಥಿತಿ: %s\n"
+"\tಹೆಸರು: %s\n"
+"\tವಿವರಣೆ: %s\n"
+"\tಚಾಲಕ: %s\n"
+"\tನಮೂನೆ ವಿವರಣೆ: %s\n"
+"\tಚಾನಲ್‌ ನಕ್ಷೆ: %s\n"
+"\tಮಾಲಿಕ ಘಟಕ: %u\n"
+"\tಮೂಕ: %s\n"
+"\tಧ್ವನಿ ಪ್ರಮಾಣ: %s%s%s\n"
+"\t        ಸಮತೋಲನ %0.2f\n"
+"\tಮೂಲ ಧ್ವನಿ ಪ್ರಮಾಣ: %s%s%s\n"
+"\tಮೇಲ್ವಿಚಾರಕ ಆಕರ: %s\n"
+"\tಅಗೋಚರತೆ: %0.0f usec, ಸಂರಚಿತ %0.0f usec\n"
+"\tಗುರುತುಗಳು: %s%s%s%s%s%s\n"
+"\tಗುಣಗಳು:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tಸಂಪರ್ಕಸ್ಥಾನಗಳು:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tಸಕ್ರಿಯ ಸಂಪರ್ಕಸ್ಥಾನ: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tಸಂಪರ್ಕಸ್ಥಾನಗಳು:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "ಆಕರದ ಮಾಹಿತಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ಆಕರ #%u\n"
+"\tಸ್ಥಿತಿ: %s\n"
+"\tಹೆಸರು: %s\n"
+"\tವಿವರಣೆ: %s\n"
+"\tಚಾಲಕ: %s\n"
+"\tನಮೂನೆ ವಿವರಣೆ: %s\n"
+"\tಚಾನಲ್‌ ನಕ್ಷೆ: %s\n"
+"\tಮಾಲಿಕ ಘಟಕ: %u\n"
+"\tಮೂಕ: %s\n"
+"\tಧ್ವನಿ ಪ್ರಮಾಣ: %s%s%s\n"
+"\t        ಸಮತೋಲನ %0.2f\n"
+"\tಮೂಲ ಧ್ವನಿ ಪ್ರಮಾಣ: %s%s%s\n"
+"\tಸಿಂಕ್‌ನ ಮೇಲ್ವಿಚಾರಣೆ: %s\n"
+"\tಅಗೋಚರತೆ: %0.0f usec, ಸಂರಚಿತ %0.0f usec\n"
+"\tಗುರುತುಗಳು: %s%s%s%s%s%s\n"
+"\tಗುಣಗಳು:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "ಅನ್ವಯಿಸುವುದಿಲ್ಲ"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "ಘಟಕದ ಮಾಹಿತಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ಘಟಕ #%u\n"
+"\tಹೆಸರು: %s\n"
+"\tಆರ್ಗುಮೆಂಟ್‌: %s\n"
+"\tಬಳಕೆಯ ಲೆಕ್ಕಿಗ: %s\n"
+"\tಗುಣಗಳು:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "ಕ್ಲೈಂಟಿನ ಮಾಹಿತಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ಕ್ಲೈಂಟ್‌ #%u\n"
+"\tಚಾಲಕ: %s\n"
+"\tಮಾಲಿಕ ಘಟಕ: %s\n"
+"\tಗುಣಗಳು:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "ಕಾರ್ಡಿನ ಮಾಹಿತಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ಕಾರ್ಡ್ #%u\n"
+"\tಹೆಸರು: %s\n"
+"\tಚಾಲಕ: %s\n"
+"\tಮಾಲಿಕ ಘಟಕ: %s\n"
+"\tಗುಣಗಳು:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tಪ್ರೊಫೈಲುಗಳು:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tಸಕ್ರಿಯ ಪ್ರೊಫೈಲುಗಳು: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "ಸಿಂಕ್‌ ಇನ್‌ಪುಟ್ ಮಾಹಿತಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ಸಿಂಕ್ ಇನ್‌ಪುಟ್‌ #%u\n"
+"\tಚಾಲಕ: %s\n"
+"\tಮಾಲಿಕ ಘಟಕ: %s\n"
+"\tಕ್ಲೈಂಟ್‌: %s\n"
+"\tಸಿಂಕ್: %u\n"
+"\tನಮೂನೆ ವಿವರಣೆ: %s\n"
+"\tಚಾನಲ್‌ ನಕ್ಷೆ: %s\n"
+"\tಮೂಕ: %s\n"
+"\tಧ್ವನಿ ಪ್ರಮಾಣ: %s\n"
+"\t        %s\n"
+"\t        ಸಮತೋಲನ %0.2f\n"
+"\tಬಫರಿನ ಅಗೋಚರತೆ: %0.0f usec\n"
+"\tಸಿಂಕ್‌ನ ಅಗೋಚರತೆ: %0.0f usec\n"
+"\tಮರುನಮೂನೆ ವಿಧಾನ: %s\n"
+"\tಗುಣಗಳು:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "ಆಕರದ ಔಟ್‌ಪುಟ್ ಮಾಹಿತಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ಸಿಂಕ್ ಇನ್‌ಪುಟ್‌ #%u\n"
+"\tಚಾಲಕ: %s\n"
+"\tಮಾಲಿಕ ಘಟಕ: %s\n"
+"\tಕ್ಲೈಂಟ್‌: %s\n"
+"\tಸಿಂಕ್: %u\n"
+"\tನಮೂನೆ ವಿವರಣೆ: %s\n"
+"\tಚಾನಲ್‌ ನಕ್ಷೆ: %s\n"
+"\tಮೂಕ: %s\n"
+"\tಧ್ವನಿ ಪ್ರಮಾಣ: %s\n"
+"\t        %s\n"
+"\t        ಸಮತೋಲನ %0.2f\n"
+"\tಬಫರಿನ ಅಗೋಚರತೆ: %0.0f usec\n"
+"\tಸಿಂಕ್‌ನ ಅಗೋಚರತೆ: %0.0f usec\n"
+"\tಮರುನಮೂನೆ ವಿಧಾನ: %s\n"
+"\tಗುಣಗಳು:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "ನಮೂನೆಯ ಮಾಹಿತಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ನಮೂನೆ #%u\n"
+"\tಹೆಸರು: %s\n"
+"\tನಮೂನೆ ವಿವರಣೆ: %s\n"
+"\tಚಾನಲ್‌ ನಕ್ಷೆ: %s\n"
+"\tಧ್ವನಿ ಪ್ರಮಾಣ: %s\n"
+"\t        %s\n"
+"\t        ಸಮತೋಲನ %0.2f\n"
+"\tಕಾಲಾವಧಿ: %0.1fs\n"
+"\tಗಾತ್ರ: %s\n"
+"\tಜಡ: %s\n"
+"\tಕಡತಹೆಸರು: %s\n"
+"\tಗುಣಗಳು:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "ವಿಫಲತೆ: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "ಆಕರದ ಮಾಹಿತಿಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "ನಮೂನೆಯನ್ನು ಅಪ್‌ಲೋಡ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "ಕಡತದ ಅಪ್ರಾಪ್ತ ಸಮಯದಲ್ಲಿ ಅಂತ್ಯ"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "ಅಮಾನ್ಯವಾದ ಪರಿಚಾರಕ"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT ದೊರೆತಿದೆ, ನಿರ್ಗಮಿಸುತ್ತಿದೆ."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "ಅಮಾನ್ಯವಾದ ಧ್ವನಿ ಪ್ರಮಾಣದ ವಿವರ"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [ಆಯ್ಕೆಗಳು] ... \n"
+"\n"
+"  -h, --help                            ಈ ನೆರವನ್ನು ತೋರಿಸು\n"
+"      --version                         ಆವೃತ್ತಿಯನ್ನು ತೋರಿಸು\n"
+"  -s, --server=SERVER                   ಸಂಪರ್ಕಸಾಧಿಸಬೇಕಿರುವ ಪರಿಚಾರಕದ ಹೆಸರು\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"libpulse %s ನೊಂದಿಗೆ ಕಂಪೈಲ್ ಮಾಡಲಾಗಿದೆ\n"
+"libpulse %s ನೊಂದಿಗೆ ಜೋಡಿಸಲಾಗಿದೆ\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "ಲೋಡ್ ಮಾಡಬೇಕಿರುವ ಒಂದು ಕಡತದ ನಮೂನೆಯನ್ನು ಸೂಚಿಸಿ"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "ಧ್ವನಿ ಕಡತವನ್ನು ತೆರೆಯುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "ಎಚ್ಚರಿಕೆ: ಕಡತದಿಂದ ನಮೂನೆಯ ವಿವರವನ್ನು ನಿರ್ಧರಿಸುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "ಚಲಾಯಿಸಲು ನೀವು ಒಂದು ನಮೂನೆಯ ಹೆಸರನ್ನು ಸೂಚಿಸಬೇಕಾಗುತ್ತದೆ"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "ತೆಗೆದು ಹಾಕಲು ನೀವು ಒಂದು ನಮೂನೆಯ ಹೆಸರನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "ನೀವು ಒಂದು ಸಿಂಕ್ ಇನ್‌ಪುಟ್ ಸೂಚಿಯನ್ನು ಹಾಗು ಒಂದು ಸಿಂಕ್‌ ಅನ್ನು ಸೂಚಿಸಬೇಕು."
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "ನೀವು ಒಂದು ಆಕರ ಔಟ್‌ಪುಟ್ ಸೂಚಿಯನ್ನು ಹಾಗು ಒಂದು ಆಕರವನ್ನು ಸೂಚಿಸಬೇಕು."
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "ನೀವು ಒಂದು ಘಟಕದ ಹೆಸರನ್ನು ಹಾಗು ಆರ್ಗುಮೆಂಟುಗಳನ್ನು ಸೂಚಿಸಬೇಕು."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "ನೀವು ಒಂದು ಘಟಕ ಸೂಚಿಯನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"ನೀವು ಒಂದಕ್ಕಿಂತ ಹೆಚ್ಚಿನ ಸಿಂಕನ್ನು ಸೂಚಿಸಲಾಗುವುದಿಲ್ಲ. ನೀವು ಒಂದು ಬೂಲಿಯನ್‌ ಮೌಲ್ಯವನ್ನು "
+"ಸೂಚಿಸಬೇಕಾಗುತ್ತದೆ."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"ನೀವು ಒಂದಕ್ಕಿಂತ ಹೆಚ್ಚಿನ ಆಕರವನ್ನು ಸೂಚಿಸಲಾಗುವುದಿಲ್ಲ. ನೀವು ಒಂದು ಬೂಲಿಯನ್‌ ಮೌಲ್ಯವನ್ನು "
+"ಸೂಚಿಸಬೇಕಾಗುತ್ತದೆ."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "ಒಂದು ಕಾರ್ಡಿನ ಹೆಸರು/ಸೂಚಿಯನ್ನು ಹಾಗು ಪ್ರೊಫೈಲ್‌ ಹೆಸರನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "ನೀವು ಒಂದು ಸಿಂಕಿನ ಹೆಸರು/ಸೂಚಿಯನ್ನು ಹಾಗು ಸಂಪರ್ಕಸ್ಥಾನದ ಹೆಸರನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "ನೀವು ಒಂದು ಆಕರದ ಹೆಸರು/ಸೂಚಿಯನ್ನು ಹಾಗು ಸಂಪರ್ಕಸ್ಥಾನದ ಹೆಸರನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "ನೀವು ಒಂದು ಸಿಂಕಿನ ಹೆಸರು/ಸೂಚಿಯನ್ನು ಹಾಗು ಸಂಪರ್ಕಸ್ಥಾನದ ಹೆಸರನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "ನೀವು ಒಂದು ಆಕರದ ಹೆಸರು/ಸೂಚಿಯನ್ನು ಹಾಗು ಸಂಪರ್ಕಸ್ಥಾನದ ಹೆಸರನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "ನೀವು ಒಂದು ಸಿಂಕ್ ಇನ್‌ಪುಟ್ ಸೂಚಿಯನ್ನು ಹಾಗು ಒಂದು ಸಿಂಕ್‌ ಅನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "ಅಮಾನ್ಯವಾದ ಸಿಂಕ್ ಇನ್‌ಪುಟ್ ಸೂಚಿ"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "ನೀವು ಒಂದು ಆಕರ ಔಟ್‌ಪುಟ್ ಸೂಚಿಯನ್ನು ಹಾಗು ಒಂದು ಆಕರವನ್ನು ಸೂಚಿಸಬೇಕು."
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "ಅಮಾನ್ಯವಾದ ಸಿಂಕ್ ಇನ್‌ಪುಟ್ ಸೂಚಿ"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "ನೀವು ಒಂದು ಸಿಂಕಿನ ಹೆಸರು/ಸೂಚಿಯನ್ನು ಹಾಗು ಸಂಪರ್ಕಸ್ಥಾನದ ಹೆಸರನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "ಅಮಾನ್ಯವಾದ ನಮೂನೆ ವಿವರ"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "ನೀವು ಒಂದು ಆಕರದ ಹೆಸರು/ಸೂಚಿಯನ್ನು ಹಾಗು ಸಂಪರ್ಕಸ್ಥಾನದ ಹೆಸರನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "ನೀವು ಒಂದು ಸಿಂಕ್ ಇನ್‌ಪುಟ್ ಸೂಚಿಯನ್ನು ಹಾಗು ಒಂದು ಸಿಂಕ್‌ ಅನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "ಅಮಾನ್ಯವಾದ ಸಿಂಕ್ ಇನ್‌ಪುಟ್ ಸೂಚಿ ವಿವರ"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "ನೀವು ಒಂದು ಆಕರದ ಹೆಸರು/ಸೂಚಿಯನ್ನು ಹಾಗು ಸಂಪರ್ಕಸ್ಥಾನದ ಹೆಸರನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "ಅಮಾನ್ಯವಾದ ಸಿಂಕ್ ಇನ್‌ಪುಟ್ ಸೂಚಿ ವಿವರ"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "ನೀವು ಒಂದು ಸಿಂಕಿನ ಹೆಸರು/ಸೂಚಿಯನ್ನು ಹಾಗು ಸಂಪರ್ಕಸ್ಥಾನದ ಹೆಸರನ್ನು ಸೂಚಿಸಬೇಕು"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "ಮಾನ್ಯವಾದ ಯಾವುದೆ ಆಜ್ಞೆಯನ್ನು ಸೂಚಿಸಲಾಗಿಲ್ಲ."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    X11 ಪ್ರದರ್ಶಕಕ್ಕೆ ಲಗತ್ತಿಸಲಾದ ಈಗಿನ PulseAudio ದತ್ತಾಂಶವನ್ನು ತೋರಿಸು "
+"(ಪೂರ್ವನಿಯೋಜಿತ)\n"
+" -e    ಸ್ಥಳೀಯ PulseAudio ದತ್ತಾಂಶವನ್ನು X11 ಪ್ರದರ್ಶಕಕ್ಕೆ ರಫ್ತು ಮಾಡು\n"
+" -i    PulseAudio ದತ್ತಾಂಶವನ್ನು X11 ಪ್ರದರ್ಶಕದಿಂದ ಸ್ಥಳೀಯ ಪರಿಸರ ವೇರಿಯೇಬಲ್‌ಗಳಿಗೆ ಹಾಗು "
+"ಕುಕಿ ಕಡತಗಳಿಗೆ ಆಮದು ಮಾಡಿಕೊ.\n"
+" -r    PulseAudio ದತ್ತಾಂಶವನ್ನು X11 ಪ್ರದರ್ಶಕದಿಂದ ತೆಗೆದು ಹಾಕು\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "ಆಜ್ಞಾ ಸಾಲನ್ನು ಪಾರ್ಸ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲತೆ.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "ಪರಿಚಾರಕ: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "ಆಕರ: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "ಸಿಂಕ್: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "ಕುಕಿ: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "ಕುಕಿ ದತ್ತಾಂಶವನ್ನು ಪಾರ್ಸ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "ಕುಕಿ ದತ್ತಾಂಶವನ್ನು ಉಳಿಸುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "ಕ್ಲೈಂಟ್ ಸಂರಚನಾ ಕಡತವನ್ನು ಲೋಡ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "ಪರಿಸರ ಸಂರಚನಾ ದತ್ತಾಂಶವನ್ನು ಓದುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN ಅನ್ನು ಪಡೆಯಲು ವಿಫಲಗೊಂಡಿದೆ.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "ಕುಕಿ ದತ್ತಾಂಶವನ್ನು ಲೋಡ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "ಇನ್ನೂ ಸಹ ಅನ್ವಯಿಸಲಾಗಿಲ್ಲ.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"ಯಾವುದೆ PulseAudio ಡೆಮನ್ ಚಾಲಿತಗೊಳ್ಳುತ್ತಿಲ್ಲ, ಅಥವ ಅಧಿವೇಶನ ಡೆಮನ್ ಆಗಿ ಚಾಲಿತಗೊಳ್ಳುತ್ತಿಲ್ಲ."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "ಸಾಕೆಟ್(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "ಸಂಪರ್ಕಿಸು(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio ಡೀಮನ್ ಅನ್ನು ಕೊಲ್ಲುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "ಡೀಮನ್ ಪ್ರತಿಕ್ರಿಯಿಸುತ್ತಿಲ್ಲ."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "ಪೋಲ್(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "ಓದು(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "ಬರೆ(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "ಸ್ವಯಂಹೆಚ್ಚಿಸುವಿಕೆಯ ಲಾಕ್ ಅನ್ನು ನಿಲುಕಿಸಿಕೊಳ್ಳಲು ಸಾಧ್ಯವಿಲ್ಲ."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ಸಾಧನಕ್ಕೆ ಹೊಸ ದತ್ತಾಂಶವನ್ನು ಬರೆಯುವಂತೆ ALSA ತಿಳಿಸಿದೆ, ಆದರೆ ಅಲ್ಲಿ ಬರೆಯಲು ಏನೂ ಇಲ್ಲ!\n"
+"ಇದಕ್ಕೆ ALSA ಚಾಲಕ '%s' ದಲ್ಲಿನ ಒಂದು ದೋಷದ ಕಾರಣವಿರಬಹುದು. ದಯವಿಟ್ಟುಈ ತೊಂದರೆಯನ್ನು ALSA "
+"ವಿಕಸನಗಾರರ ಗಮನಕ್ಕೆ ತನ್ನಿ.POLLOUT ಸೆಟ್‌ನಿಂದ ನಾವು ಎಚ್ಚೆತ್ತುಗೊಂಡಿದ್ದೇವೆ -- ಆದರೆ ನಂತರದ "
+"snd_pcm_avail() 0 ಅಥವ min_avail ಕ್ಕಿಂತ ಚಿಕ್ಕದಾದ ಇನ್ನೊಂದು ಮೌಲ್ಯವನ್ನು ಮರಳಿಸಿದೆ."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ಸಾಧನಕ್ಕೆ ಹೊಸ ದತ್ತಾಂಶವನ್ನು ಓದುವಂತೆ ALSA ತಿಳಿಸಿದೆ, ಆದರೆ ಅಲ್ಲಿ ಓದಲು ಏನೂ ಇಲ್ಲ!\n"
+"ಇದಕ್ಕೆ ALSA ಚಾಲಕ '%s' ದಲ್ಲಿನ ಒಂದು ದೋಷದ ಕಾರಣವಿರಬಹುದು. ದಯವಿಟ್ಟುಈ ತೊಂದರೆಯನ್ನು ALSA "
+"ವಿಕಸನಗಾರರ ಗಮನಕ್ಕೆ ತನ್ನಿ.POLLIN ಸೆಟ್‌ನಿಂದ ನಾವು ಎಚ್ಚೆತ್ತುಗೊಂಡಿದ್ದೇವೆ -- ಆದರೆ ನಂತರದ "
+"snd_pcm_avail() 0 ಅಥವ min_avail ಕ್ಕಿಂತ ಚಿಕ್ಕದಾದ ಇನ್ನೊಂದು ಮೌಲ್ಯವನ್ನು ಮರಳಿಸಿದೆ."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "ಜಡ"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "ಹೈ ಫಿಡಿಲಿಟಿ ಪ್ಲೇಬ್ಯಾಕ್ (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "ಹೈ ಫಿಡಿಲಿಟಿ ಕ್ಯಾಪ್ಚರ್ (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "ಟೆಲಿಫೋನಿ ಡ್ಯೂಪ್ಲೆಕ್ಸ್‌ (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio ಧ್ವನಿ ಪರಿಚಾರಕ"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "ಔಟ್‌ಪುಟ್ ಸಾಧನಗಳು"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "ಇನ್‌ಪುಟ್‌ ಸಾಧನಗಳು"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@ ನಲ್ಲಿನ ಆಡಿಯೊ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "ಇನ್‌ಪುಟ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "ಡಾಕಿಂಗ್ ಸ್ಟೇಶನ್ ಇನ್‌ಪುಟ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "ಡಾಕಿಂಗ್ ಸ್ಟೇಶನ್ ಮೈಕ್ರೊಫೋನ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "ಡಾಕಿಂಗ್ ಸ್ಟೇಶನ್ ಇನ್‌ಪುಟ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "ಲೈನ್-ಇನ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "ಮೈಕ್ರೊಫೋನ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "ಡಾಕಿಂಗ್ ಸ್ಟೇಶನ್ ಮೈಕ್ರೊಫೋನ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "ಮೈಕ್ರೊಫೋನ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "ಬಾಹ್ಯ ಮೈಕ್ರೊಫೋನ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "ಆಂತರಿಕ  ಮೈಕ್ರೊಫೋನ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "ರೇಡಿಯೊ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "ವೀಡಿಯೊ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "ಆಟೊಮ್ಯಾಟಿಕ್ ಗೇನ್ ಕಂಟ್ರೋಲ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "ಯಾವುದೆ ಆಟೊಮ್ಯಾಟಿಕ್ ಗೇನ್ ಕಂಟ್ರೋಲ್ ಇಲ್ಲ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "ಬೂಸ್ಟ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "ಯಾವುದೆ ಬೂಸ್ಟ್ ಇಲ್ಲ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "ಆಂಪ್ಲಿಫಯರ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "ಯಾವುದೆ ಆಂಪ್ಲಿಫಯರ್ ಇಲ್ಲ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "ಬೂಸ್ಟ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "ಯಾವುದೆ ಬೂಸ್ಟ್ ಇಲ್ಲ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "ಅನಲಾಗ್ ಹೆಡ್‌ಫೋನ್‌ಗಳು"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "ಅನಲಾಗ್ ಇನ್‌ಪುಟ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "ಡಾಕಿಂಗ್ ಸ್ಟೇಶನ್ ಮೈಕ್ರೊಫೋನ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "ಅನಲಾಗ್ ಔಟ್‌ಪುಟ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "ಅನಲಾಗ್ ಔಟ್‌ಪುಟ್ (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "ಲೈನ್-ಇನ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "ಅನಲಾಗ್ ಮೊನೊ ಔಟ್‌ಪುಟ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "ಅನಲಾಗ್ ಸ್ಟೀರಿಯೋ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "ಡಿಜಿಟಲ್ ಸ್ಟೀರಿಯೊ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "ಡಿಜಿಟಲ್ ಸ್ಟೀರಿಯೊ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "ಅನಲಾಗ್ ಮೊನೊ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "ಅನಲಾಗ್ ಸ್ಟೀರಿಯೋ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "ಅನಲಾಗ್ ಸರೌಂಡ್‌ 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "ಅನಲಾಗ್ ಸರೌಂಡ್‌ 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "ಅನಲಾಗ್ ಸರೌಂಡ್‌ 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "ಅನಲಾಗ್ ಸರೌಂಡ್‌ 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "ಅನಲಾಗ್ ಸರೌಂಡ್‌ 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "ಅನಲಾಗ್ ಸರೌಂಡ್‌ 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "ಅನಲಾಗ್ ಸರೌಂಡ್‌ 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "ಅನಲಾಗ್ ಸರೌಂಡ್‌ 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "ಅನಲಾಗ್ ಸರೌಂಡ್‌ 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "ಅನಲಾಗ್ ಸರೌಂಡ್‌ 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "ಅನಲಾಗ್ ಸರೌಂಡ್‌ 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "ಡಿಜಿಟಲ್ ಸ್ಟೀರಿಯೊ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "ಡಿಜಿಟಲ್ ಸ್ಟೀರಿಯೊ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "ಡಿಜಿಟಲ್ ಸರೌಂಡ್ 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "ಡಿಜಿಟಲ್ ಸರೌಂಡ್ 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "ಡಿಜಿಟಲ್ ಸ್ಟೀರಿಯೊ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "ಡಿಜಿಟಲ್ ಸರೌಂಡ್ 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "ಅನಲಾಗ್ ಮೊನೊ ಡ್ಯೂಪ್ಲೆಕ್ಸ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "ಅನಲಾಗ್ ಸ್ಟೀರಿಯೊ ಡ್ಯೂಪ್ಲೆಕ್ಸ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "ಅನಲಾಗ್ ಸ್ಟೀರಿಯೊ ಡ್ಯೂಪ್ಲೆಕ್ಸ್ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "ಶೂನ್ಯ ಔಟ್‌ಪುಟ್"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "ಇನ್‌ಪುಟ್"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<ಸಿಂಕ್‌ನ ಹೆಸರು> sink_properties=<ಸಿಂಕ್‌ನ ಗುಣಗಳು> master=<ಫಿಲ್ಟರ್ "
+"ಮಾಡಬೇಕಿರುವ ಸಿಂಕ್‌ನ ಹೆಸರು> format=<ನಮೂನೆ ವಿನ್ಯಾಸ> rate=<ನಮೂನೆ ದರ> "
+"channels=<ಚಾನಲ್‌ಗಳ ಸಂಖ್ಯೆ> channel_map=<ಚಾನಲ್ ನಕ್ಷೆ> plugin=<ladspa ಪ್ಲಗ್‌ಇನ್ ಹೆಸರು> "
+"label=<ladspa ಪ್ಲಗ್‌ಇನ್ ಹೆಸರು> control=<ವಿರಾಮ ಚಿಹ್ನೆಗಳನ್ನು ಹೊಂದಿರುವ ಇನ್‌ಪುಟ್ ನಿಯಂತ್ರಣ "
+"ಮೌಲ್ಯಗಳ ಪಟ್ಟಿ>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit ಗೆ ಈ ಪ್ಲಾಟ್‌ಫಾರ್ಮಿನಲ್ಲಿ ಬೆಂಬಲವಿಲ್ಲ."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() ವಿಫಲಗೊಂಡಿದೆ"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "ಆಕರ ಔಟ್‌ಪುಟ್‌ #%u\n"
+#~ "\tಚಾಲಕ: %s\n"
+#~ "\tಮಾಲಿಕ ಘಟಕ: %s\n"
+#~ "\tಕ್ಲೈಂಟ್‌: %s\n"
+#~ "\tಆಕರ: %u\n"
+#~ "\tನಮೂನೆ ವಿವರಣೆ: %s\n"
+#~ "\tಚಾನಲ್‌ ನಕ್ಷೆ: %s\n"
+#~ "\tಬಫರಿನ ಅಗೋಚರತೆ: %0.0f usec\n"
+#~ "\tಆಕರದ ಅಗೋಚರತೆ: %0.0f usec\n"
+#~ "\tಮರುನಮೂನೆ ವಿಧಾನ: %s\n"
+#~ "\tಗುಣಗಳು:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [ಆಯ್ಕೆಗಳು] stat\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] list\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] exit\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] upload-sample FILENAME [NAME]\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] play-sample NAME [SINK]\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] remove-sample NAME\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] move-sink-input SINKINPUT SINK\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] load-module NAME [ARGS ...]\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] unload-module MODULE\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] suspend-sink SINK 1|0\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] suspend-source SOURCE 1|0\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] set-card-profile CARD PROFILE\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] set-sink-port SINK PORT\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] set-source-port SOURCE PORT\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] set-sink-volume SINK VOLUME\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] set-source-volume SOURCE VOLUME\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] set-sink-mute SINK 1|0\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] set-source-mute SOURCE 1|0\n"
+#~ "%s [ಆಯ್ಕೆಗಳು] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            ಈ ನೆರವನ್ನು ತೋರಿಸು\n"
+#~ "      --version                         ಆವೃತ್ತಿಯನ್ನು ತೋರಿಸು\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   ಸಂಪರ್ಕಸಾಧಿಸಬೇಕಿರುವ ಪರಿಚಾರಕದ ಹೆಸರು\n"
+#~ "  -n, --client-name=NAME                ಪರಿಚಾರಕದಲ್ಲಿ ಈ ಕ್ಲೈಂಟಿನಲ್ಲಿ ಏನೆಂದು "
+#~ "ಕರೆಯಬೇಕು\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "ಡಿಜಿಟಲ್ ಸರೌಂಡ್ 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "ಕೆಳಮಟ್ಟದ ಫ್ರೀಕ್ವೆನ್ಸಿ ಉತ್ಪಾದಕ"
diff --git a/po/ko.po b/po/ko.po
new file mode 100644 (file)
index 0000000..b3765d6
--- /dev/null
+++ b/po/ko.po
@@ -0,0 +1,2912 @@
+# eukim <eukim@redhat.com>, 2013. #zanata
+# KimJeongYeon <jeongyeon.kim@samsung.com>, 2017.
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-08-06 14:45+0530\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"PO-Revision-Date: 2017-04-23 02:11+0900\n"
+"Last-Translator: KimJeongYeon <jeongyeon.kim@samsung.com>\n"
+"Language-Team: Korean\n"
+"Language: ko\n"
+"X-Generator: Poedit 1.8.7.1\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: ../src/modules/alsa/alsa-util.c:1128 ../src/modules/alsa/alsa-util.c:1203
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_avail()이 %lu 바이트 (%lu ms)의 매우 큰 값을 반환했습니다.\n"
+"ALSA 드라이버 '%s'의 오류일 수 있습니다. ALSA 개발자에게 이 문제를 보고해주시기 바랍니다."
+
+#: ../src/modules/alsa/alsa-util.c:1178
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_delay()가 %li 바이트 (%s%lu ms)의 매우 큰 값을 반환했습니다.\n"
+"ALSA 드라이버 '%s'의 오류일 수 있습니다. ALSA 개발자에게 이 문제를 보고해주시기 바랍니다."
+
+#: ../src/modules/alsa/alsa-util.c:1219
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail %lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay()가 이상한 값을 반환했습니다: 지연 시간 %lu은 사용 가능한 시간 %lu 보다 작습니다.\n"
+"ALSA 드라이버 '%s'의 오류일 수 있습니다. ALSA 개발자에게 이 문제를 보고해 주시기 바랍니다."
+
+#: ../src/modules/alsa/alsa-util.c:1262
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin()이 %lu 바이트 (%lu ms)의 매우 큰 값을 반환했습니다.\n"
+"ALSA 드라이버 '%s'의 오류일 수 있습니다. ALSA 개발자에게 이 문제를 보고해 주시기 바랍니다."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "빈 싱크를 포함하여 최소한 하나 이상의 싱크가 존재해야 합니다."
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "가짜 출력"
+
+#: ../src/modules/module-ladspa-sink.c:53
+msgid "Virtual LADSPA sink"
+msgstr "가상 LADSPA 싱크"
+
+#: ../src/modules/module-ladspa-sink.c:57
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> master=<name of sink to filter> sink_master=<name of sink "
+"to filter> format=<sample format> rate=<sample rate> channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of input control values> input_ladspaport_map=<comma "
+"separated list of input LADSPA port names> output_ladspaport_map=<comma separated list of output LADSPA port names> autoloaded=<set "
+"if this module is being loaded automatically> "
+msgstr ""
+"sink_name=<싱크의 이름> sink_properties=<싱크에 속성들을 지정> master=<필터를 적용할 싱크의 이름> sink_master=<필터를 적용할 싱크의 "
+"이름> format=<샘플 형식> rate=<샘플 레이트> channels=<채널 수> channel_map=<입력 채널 맵> plugin=<ladspa 플러그인 이름> "
+"label=<ladspa 플러그인 레이블> control=<쉼표로 구분된 입력 제어값> input_ladspaport_map=<쉼표로 구분된 LADSPA 입력포트 이름의 목록> "
+"output_ladspaport_map=<쉼표로 구분된 LADSPA 출력포트 이름의 목록> autoloaded=<이 모듈이 자동으로 로드된다면 설정하십시오> "
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "클럭 사용 빈 싱크"
+
+#: ../src/modules/module-null-sink.c:280
+msgid "Null Output"
+msgstr "빈 출력"
+
+#: ../src/pulsecore/sink.c:3416
+msgid "Built-in Audio"
+msgstr "내장 오디오 "
+
+#: ../src/pulsecore/sink.c:3421
+msgid "Modem"
+msgstr "모뎀 "
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "기존 lt_dlopen 로더를 찾는데 실패했습니다."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "새 dl 로더를 할당하는데 실패했습니다."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind-now-loader를 추가하는데 실패했습니다."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "시그널 %s를 받았습니다."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "종료합니다."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "사용자 '%s'를 찾을 수 없습니다."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "그룹 '%s'를 찾을 수 없습니다."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "사용자 \"%s' (UID %lu)와 그룹 '%s' (GID %lu)를 찾았습니다."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "사용자 '%s'의 GID와 그룹 '%s'가 일치하지 않습니다."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "사용자 '%s'의 홈 디렉토리가 '%s'가 아닙니다, 무시됨."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' 생성 실패: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "그룹 리스트 변경 실패: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID 변경 실패: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID 변경 실패: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "root 권한을 올바르게 삭제했습니다."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "시스템 전역 모드는 이 플랫폼에서 지원되지 않습니다."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) 실패: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "명령어 행 분석 실패."
+
+#: ../src/daemon/main.c:529
+msgid "System mode refused for non-root user. Only starting the D-Bus server lookup service."
+msgstr "비 root 사용자에 대해 시스템 모드는 거부되었습니다. D-Bus 서버 검색 서비스만 시작합니다."
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "데몬이 실행중이지 않습니다."
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "데몬이 %u PID로 실행중입니다."
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "데몬 종료 실패: %s"
+
+#: ../src/daemon/main.c:657
+msgid "This program is not intended to be run as root (unless --system is specified)."
+msgstr "프로그램이 root로 실행되지 않습니다. (실행하려면 --system을 명기하십시오)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Root 권한이 필요합니다."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start는 시스템 인스턴스에 대해 지원되지 않습니다."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr "%s에서 사용자 설정한 서버, start/autospawn을 거부하고 있습니다."
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid "User-configured server at %s, which appears to be local. Probing deeper."
+msgstr "%s에 사용자가 설정한 서버, 이는 로컬에 있습니다. 상세히 조사합니다."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "시스템 모드에서 실행중입니다. 하지만 --disallow-exit가 설정되지 않았습니다!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "시스템 모드에서 실행 중입니다. 하지만 --disallow-module-loading이 설정되어 있지 않습니다!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "시스템 모드에서 실행 중입니다. 강제로 SHM 모드를 비활성화합니다!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "시스템 모드에서 실행 중입니다. 강제로 exit 유휴 시간을 비활성화합니다!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "표준 입출력을 얻을 수 없습니다."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() 실패: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() 실패: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() 실패: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "데몬 시작에 실패했습니다."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "데몬이 성공적으로 시작되었습니다."
+
+#: ../src/daemon/main.c:816
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() 실패: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "펄스오디오 %s 입니다"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "컴파일 호스트: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "컴파일 CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "호스트에서 실행 중: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u개의 CPU를 찾았습니다."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "페이지 크기 %lu 바이트"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Valgrind 지원하도록 컴파일: 예"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Valgrind 지원하도록 컴파일: 아니요"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Valgrind 모드로 실행중: %s"
+
+#: ../src/daemon/main.c:921
+#, c-format
+msgid "Running in VM: %s"
+msgstr "VM에서 실행 중: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "빌드 최적화: 예"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "빌드 최적화: 아니요"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG 정의되어, 모든 assert 비활성화됨."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH는 정의되어 있습니다. 빠른 경로 assert만 비활성화되어 있습니다."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "모든 assert 활성화됨."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "장치 ID 가져오기 실패"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "장치 ID는 %s입니다."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "세션 ID는 %s입니다."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "런타임 디렉토리 %s 사용 중"
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "상태 디렉토리 %s 사용 중"
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "모듈 디렉토리 %s 사용 중 "
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "시스템 모드로 실행중: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ for an explanation "
+"why system mode is usually a bad idea."
+msgstr ""
+"PA가 시스템 모드로 동작하고 있습니다. 하지만 이것은 권장되지 않습니다.\n"
+"만약 의도대로 정상동작하지 않더라도 그것은 당신의 잘못입니다.\n"
+"시스템 모드가 좋지 않은 이유에 대해서는 다음 문서를 확인하시기 바랍니다. http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ "
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() 실패."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "최신 고해상도 타이머가 사용 가능합니다! 사용해 보십시오!"
+
+#: ../src/daemon/main.c:993
+msgid "Dude, your kernel stinks! The chef's recommendation today is Linux with high-resolution timers enabled!"
+msgstr "커널이 좋지 않습니다! 고해상도 타이머가 활성화되어 있는 Linux를 추천합니다!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() 실패."
+
+#: ../src/daemon/main.c:1089
+msgid "Failed to initialize daemon."
+msgstr "데몬 초기화 실패."
+
+#: ../src/daemon/main.c:1094
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "아무런 모듈 없이 데몬이 실행되었습니다. 동작하지 않습니다."
+
+#: ../src/daemon/main.c:1132
+msgid "Daemon startup complete."
+msgstr "데몬 구동이 완료되었습니다."
+
+#: ../src/daemon/main.c:1138
+msgid "Daemon shutdown initiated."
+msgstr "데몬 종료가 초기화되었습니다."
+
+#: ../src/daemon/main.c:1169
+msgid "Daemon terminated."
+msgstr "데몬이 종료되었습니다."
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory segments\n"
+"      --start                           Start the daemon if it is not running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"명령:\n"
+"  -h, --help                            도움말을 표시\n"
+"      --version                         버전을 표시      \n"
+"      --dump-conf                       기본 설정을 덤프\n"
+"      --dump-modules                    사용 가능한 모듈 목록을 덤프\n"
+"      --dump-resample-methods           사용 가능한 리샘플링 방법을 덤프\n"
+"      --cleanup-shm                     이전 공유 메모리 세그먼트를 삭제\n"
+"      --start                           데몬이 실행하지 않는 경우 데몬을 시작\n"
+"      -k  --kill                            실행중인 데몬을 종료\n"
+"      --check                           실행중인 데몬을 확인 (종료 코드를 반환할 경우에만)\n"
+"\n"
+"옵션:\n"
+"      --system[=BOOL]                   시스템 전역 인스턴스로 실행\n"
+"  -D, --daemonize[=BOOL]                시작 후 데몬화\n"
+"       --fail[=BOOL]                     시작 실패 시 종료\n"
+"       --high-priority[=BOOL]            높고 좋은 수준 설정 시도\n"
+"                                         (root로만 사용 가능, SUID 또는\n"
+"                                         상승된 RLIMIT_NICE에서)\n"
+"        --realtime[=BOOL]                 실시간 예약 활성화 시도\n"
+"                                         (root로망 사용 가능, SUID 또는\n"
+"                                         상승된 RLIMIT_RTPRIO에서)\n"
+"        --disallow-module-loading[=BOOL]   시작 후 사용자 요청 모듈 로드/언로드를\n"
+"                                                  허용하지 않음\n"
+"        --disallow-exit[=BOOL]            사용자 요청 종료를 허용하지 않음\n"
+"        --exit-idle-time=SECS             유휴 시간 및 지정된 시간 후\n"
+"                                          데몬을 종료\n"
+"        --scache-idle-time=SECS           유휴 시간 및 지정된 시간 후\n"
+"                                          자동 로드된 샘플을 언로드\n"
+"        --log-level[=LEVEL]               상세 설명 수준을 올리거나 설정\n"
+"   -v                                    상세 설명 수준을 올림\n"
+"        --log-target={auto,syslog,stderr,file:PATH,newfile:PATH} \n"
+"                                         로그 대상을 지정\n"
+"        --log-meta[=BOOL]                 로그 메세지에 코드 위치를 포함\n"
+"        --log-time[=BOOL]                  로그 메세지에 타임스탬프를 포함\n"
+"        --log-backtrace=FRAMES            로그 메세지에 역추적을 포함\n"
+"  -p, --dl-search-path=PATH              동적 공유 객체 (plugins)에 \n"
+"                                          검색 경로를 설정\n"
+"      --resample-method=METHOD          지정한 재샘플링 방법을 사용\n"
+"                                          (사용 가능한 값은 --dump-resample-methods\n"
+"                                          에서 참조)\n"
+"       --use-pid-file[=BOOL]             PID 파일 생성\n"
+"       --no-cpu-limit[=BOOL]             지원하는 플랫폼에 CPU 로드 제한기를\n"
+"                                         설치하지 않음.\n"
+"       --disable-shm[=BOOL]              공유 메모리 지원을 비활성화함.\n"
+"\n"
+"시작 스크립트:\n"
+"   -L, --load=\"MODULE ARGUMENTS\"         지정된 인수와 함께 지정된 플러그인\n"
+"                                         모듈을 로드\n"
+"   -F, --file=FILENAME                   지정한 스크립트를 실행\n"
+"   -C                                    시작 후 실행 중인 TTY에서 명령행을\n"
+"                                         열음\n"
+"\n"
+"   -n                                    디폴트 스크립트 파일을 로드하지 않음\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize는 부울 인수를 예상합니다 "
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail은 부울 인수를 예상합니다."
+
+#: ../src/daemon/cmdline.c:261
+msgid "--log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error)."
+msgstr "--log-level은 로깅 수준 인수를 예상합니다 (0..4 숫자 범위 또는 디버그, 정보, 알림, 경고, 오류 중 하나)"
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority는 boolean 인수를 사용합니다."
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime은 boolean 인수를 사용합니다."
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading는 boolean 인수를 사용합니다."
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit는 boolean 인수를 사용합니다."
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file는 boolean 인수를 사용합니다."
+
+#: ../src/daemon/cmdline.c:318
+msgid "Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."
+msgstr "잘못된 로그 대상: 'syslog', 'stderr','auto' 또는 유효한 파일 이름 'file:<path>', 'newfile:<path>' 중에 하나를 사용하십시오."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time은 boolean 인수를 사용합니다."
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta는 boolean 인수를 사용합니다."
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "'%s'는 잘못된 리샘플링 방법입니다."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system는 boolean 인수를 사용합니다."
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit는 boolean 인수를 사용합니다."
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm는 boolean 인수를 사용합니다."
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "이름: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "사용 가능한 모듈 정보 없음\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "버전: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "설명: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "개발자: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "사용법: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "한 번 로딩하기: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "지원 중지 경고: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "경로: %s\n"
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] 잘못된 로그 대상 '%s'."
+
+#: ../src/daemon/daemon-conf.c:322
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] 잘못된 로그 레벨 '%s'."
+
+#: ../src/daemon/daemon-conf.c:337
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] 잘못된 리샘플링 방법 '%s'."
+
+#: ../src/daemon/daemon-conf.c:359
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] 잘못된 rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:379
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] 잘못된 샘플 형식 '%s'."
+
+#: ../src/daemon/daemon-conf.c:397 ../src/daemon/daemon-conf.c:415
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] 잘못된 샘플 비율 '%s'."
+
+#: ../src/daemon/daemon-conf.c:438
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] 잘못된 샘플 채널 '%s'."
+
+#: ../src/daemon/daemon-conf.c:455
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] 잘못된 채널 맵 '%s'."
+
+#: ../src/daemon/daemon-conf.c:472
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] 잘못된 fragment 수 '%s'."
+
+#: ../src/daemon/daemon-conf.c:489
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] 잘못된 fragment 크기 '%s'."
+
+#: ../src/daemon/daemon-conf.c:506
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] 잘못된 nice 레벨 '%s'."
+
+#: ../src/daemon/daemon-conf.c:549
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] 잘못된 서버 종류 '%s'."
+
+#: ../src/daemon/daemon-conf.c:662
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "설정 파일 열기 실패: %s"
+
+#: ../src/daemon/daemon-conf.c:678
+msgid "The specified default channel map has a different number of channels than the specified default number of channels."
+msgstr "지정된 기본 채널 맵은 지정된 기본 채널 수와는 다른 채널 수를 가지고 있습니다."
+
+#: ../src/daemon/daemon-conf.c:764
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### 설정 파일에서 읽기: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "권한을 삭제하고 있습니다."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "펄스오디오 사운드 시스템"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "펄스오디오 사운드 시스템을 시작합니다"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "펄스오디오 사운드 시스템 KDE 라우팅 정책 "
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "KDE 라우팅 정책을 사용하여 펄스오디오 사운드 시스템 시작 "
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "모노"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "전면 중앙"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "전면 왼쪽"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "전면 오른쪽"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "후면 중앙"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "후면 왼쪽"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "후면 오른쪽"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr "서브우퍼"
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "전면 중앙의 왼쪽"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "전면 중앙의 오른쪽"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "측면 왼쪽"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "측면 오른쪽"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "보조 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "보조 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "보조 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "보조 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "보조 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "보조 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "보조 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "보조 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "보조 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "보조 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "보조 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "보조 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "보조 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "보조 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "보조 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "보조 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "보조 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "보조 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "보조 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "보조 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "보조 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "보조 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "보조 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "보조 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "보조 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "보조 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "보조 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "보조 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "보조 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "보조 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "보조 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "보조 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "상단 중앙"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "상단 전면 중앙"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "상단 전면 왼쪽"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "상단 전면 오른쪽"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "상단 후면 중앙"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "상단 후면 왼쪽"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "상단 후면 오른쪽"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169 ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373 ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(잘못됨)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "스테레오"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "서라운드 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "서라운드 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "서라운드 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "서라운드 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "서라운드 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "접근 거부됨"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "알 수 없는 명령"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "잘못된 인수"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "엔티티가 있음 "
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "이러한 엔티티가 없음"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "연결 거부됨"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "프로토콜 오류"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "제한 시간"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "인증 키가 없음 "
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "내부 오류 "
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "연결 종료됨"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "엔티티가 종료됨"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "잘못된 서버"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "모듈 초기화 실패 "
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "잘못된 상태 "
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "데이터 없음 "
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "호환되지 않는 프로토콜 버전"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "너무 큽니다"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "지원되지 않음"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "알 수 없는 오류 코드"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "이러한 확장자가 없음 "
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "사용하지 않는 기능 "
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "누락된 실행 "
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "클라이언트가 포크됨 "
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "입/출력 오류"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "장치 또는 자원이 사용중입니다."
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() 실패 "
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error()가 true를 반환했습니다 "
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "쿠키 데이터 분석 실패"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "설정 파일 '%s' 열기 실패: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "쿠키가 로딩되지 않았습니다. 없는 상태에서 연결을 시도하고 있습니다."
+
+#: ../src/pulse/context.c:609
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:664
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1365
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "알 수 없는 확장자 '%s'에 대해 전송된 메세지 "
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "스트림 배출 실패: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "재생 스트림이 배출되었습니다."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "서버에 배출 연결"
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() 실패: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() 실패: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() 실패: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "스트림이 성공적으로 생성되었습니다."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() 실패: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "버퍼 지표: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "버퍼 지표: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "샘플 사양 '%s', 채널 맵 '%s' 사용. "
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "장치 %s에 연결 (%u, %s중단됨)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "스트림 오류: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "스트림 장치가 중단되었습니다.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "스트림 장치가 다시 시작되었습니다.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "스트림 언더런.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "스트림 오버런.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "스트림 시작됨.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "스트림이 장치 %s 로 이동했습니다 (%u, %s중단됨).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "없음 "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "스트림 버퍼 속성이 변경되었습니다.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr "Cork 요청 스택이 비어 있습니다: corking stream"
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Cork 요청 스택이 비어 있습니다: uncorking stream"
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr "경고: cork 요청 보다 uncork 요청을 더 많이 받았습니다!"
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "연결되었습니다.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() 실패: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() 실패: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() 실패: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1318
+#, c-format
+msgid "Connection failure: %s"
+msgstr "연결 실패: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF 받음"
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() 실패: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "시그널 수신, 종료합니다."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "지연시간 얻기 실패: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "시간: %0.3f sec; 지연: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() 실패: %s"
+
+#: ../src/utils/pacat.c:653
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to connect to\n"
+"  -n, --client-name=NAME                How to call this client on the server\n"
+"      --stream-name=NAME                How to call this stream on the server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to 44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the default\n"
+"      --fix-format                      Take the sample format from the sink the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the channel map\n"
+"                                        from the sink the stream is being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of name.\n"
+"      --latency=BYTES                   Request the specified latency in bytes.\n"
+"      --process-time=BYTES              Request the specified process time per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            도움말 표시\n"
+"      --version                         버전 표시\n"
+"\n"
+"  -r, --record                          기록을 위한 연결 생성\n"
+"  -p, --playback                        재생을 위한 연결 생성\n"
+"\n"
+"-v, --verbose                        실행 상세 설명을 활성화\n"
+"\n"
+"  -s, --server=SERVER                   연결할 서버 이름\n"
+"  -d, --device=DEVICE                   연결할 싱크/소스 이름\n"
+"  -n, --client-name=NAME                서버에서 클라이언트 호출 방법\n"
+"      --stream-name=NAME                서버에서 스트림 호출 방법\n"
+"      --volume=VOLUME                   초기 (선형) 볼륨을 0...65536 범위에서 지정\n"
+"      --rate=SAMPLERATE                 샘플 레이트를 Hz 단위로 지정 (기본값: 44100)\n"
+"      --format=SAMPLEFORMAT             샘플 유형을 다음 중 하나로 지정 s16le, s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
+"                                        s24-32le, s24-32be (기본값: s16ne)\n"
+"      --channels=CHANNELS               채널 수를 지정, 1 (모노의 경우), 2 (스테레오의 경우)\n"
+"                                        (기본값: 2)\n"
+"      --channel-map=CHANNELMAP          기본값 대신 사용할 채널 맵\n"
+"      --fix-format                      연결된 스트림이 싱크에서 샘플 포맷을\n"
+"                                        취득.\n"
+"      --fix-rate                        연결된 스트림이 싱크에서 샘플링 속도를\n"
+"                                        취득.\n"
+"      --fix-channels                    연결된 스트림이 싱크에서 채널 수와 채널\n"
+"                                        맵을 취득.\n"
+"      --no-remix                        채널을 업믹스 또는 다운믹스하지 않음.\n"
+"      --no-remap                        이름 대신 인덱스로 채널을 맵핑.\n"
+"      --latency=BYTES                   지정된 대기 시간을 바이트 단위로 요구.\n"
+"      --process-time=BYTES              요청당 지정된 처리 시간을 바이트 단위로 요구.\n"
+"      --latency-msec=MSEC               지정된 대기 시간을 msec 단위로 요구.\n"
+"      --process-time-msec=MSEC          요청당 지정된 처리 시간을 msec 단위로 요구.\n"
+"      --property=PROPERTY=VALUE         지정된 값에 지정된 속성 설정.\n"
+"      --raw                             원 PCM 데이터 녹음/재생.\n"
+"      --passthrough                     통과 데이터\n"
+"      --file-format[=FFORMAT]           포맷된 PCM 데이터 녹음/재생.\n"
+"      --list-file-formats               사용 가능한 파일 형식 목록.\n"
+
+#: ../src/utils/pacat.c:788
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"libpulse %s로 컴파일됨\n"
+"libpulse %s와 링크됨\n"
+
+#: ../src/utils/pacat.c:821 ../src/utils/pactl.c:1467
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "잘못된 클라이언트 이름 '%s'"
+
+#: ../src/utils/pacat.c:836
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "잘못된 스트림 이름 '%s'"
+
+#: ../src/utils/pacat.c:873
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "잘못된 채널 맵 '%s'"
+
+#: ../src/utils/pacat.c:902 ../src/utils/pacat.c:916
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "잘못된 지연 사양 '%s'"
+
+#: ../src/utils/pacat.c:909 ../src/utils/pacat.c:923
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "잘못된 처리 시간 사양 '%s'"
+
+#: ../src/utils/pacat.c:935
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "잘못된 속성 '%s'"
+
+#: ../src/utils/pacat.c:954
+#, c-format
+msgid "Unknown file format %s."
+msgstr "알 수 없는 파일 포맷 %s."
+
+#: ../src/utils/pacat.c:973
+msgid "Invalid sample specification"
+msgstr "잘못된 샘플 사양 "
+
+#: ../src/utils/pacat.c:983
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:988
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:995
+msgid "Too many arguments."
+msgstr "인수가 너무 많습니다."
+
+#: ../src/utils/pacat.c:1006
+msgid "Failed to generate sample specification for file."
+msgstr "파일의 샘플 사양 생성에 실패했습니다."
+
+#: ../src/utils/pacat.c:1032
+msgid "Failed to open audio file."
+msgstr "오디오 파일을 열 수 없습니다."
+
+#: ../src/utils/pacat.c:1038
+msgid "Warning: specified sample specification will be overwritten with specification from file."
+msgstr "경고: 지정된 샘플 사양은 파일에서의 사양을 덮어쓰기하게 됩니다."
+
+#: ../src/utils/pacat.c:1041 ../src/utils/pactl.c:1534
+msgid "Failed to determine sample specification from file."
+msgstr "파일에서 샘플 사양 지정에 실패했습니다."
+
+#: ../src/utils/pacat.c:1050
+msgid "Warning: Failed to determine channel map from file."
+msgstr "경고: 채널 맵을 파일에서 확인할 수 없습니다."
+
+#: ../src/utils/pacat.c:1061
+msgid "Channel map doesn't match sample specification"
+msgstr "채널 맵은 샘플 사양과 일치하지 않습니다."
+
+#: ../src/utils/pacat.c:1072
+msgid "Warning: failed to write channel map to file."
+msgstr "경고: 채널 맵을 파일에 기록할 수 없습니다."
+
+#: ../src/utils/pacat.c:1087
+#, c-format
+msgid "Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "샘플 사양 '%s', 채널 맵 '%s'으로 %s 스트림을 엽니다."
+
+#: ../src/utils/pacat.c:1088
+msgid "recording"
+msgstr "녹음"
+
+#: ../src/utils/pacat.c:1088
+msgid "playback"
+msgstr "재생"
+
+#: ../src/utils/pacat.c:1112
+msgid "Failed to set media name."
+msgstr "미디어 이름 설정에 실패했습니다."
+
+#: ../src/utils/pacat.c:1119 ../src/utils/pactl.c:1860
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() 실패."
+
+#: ../src/utils/pacat.c:1142
+msgid "io_new() failed."
+msgstr "io_new() 실패."
+
+#: ../src/utils/pacat.c:1149 ../src/utils/pactl.c:1872
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() 실패."
+
+#: ../src/utils/pacat.c:1157 ../src/utils/pactl.c:1878
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() 실패: %s"
+
+#: ../src/utils/pacat.c:1163
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() 실패."
+
+#: ../src/utils/pacat.c:1170 ../src/utils/pactl.c:1883
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() 실패."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "중지 실패: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "다시 시작하기 실패: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "경고: 사운드 서버가 로컬에 있지 않으며 정지하지 않습니다.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "연결 실패: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT 받음, 종료 중.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "경고: 자식 프로세스가 시그널 %u에 의해 종료되었습니다.\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"\n"
+msgstr ""
+"%s [옵션] ... \n"
+"\n"
+"  -h, --help                            도움말 표시\n"
+"      --version                         버전 표시\n"
+"  -s, --server=SERVER                   연결할 서버 이름\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"libpulse %s로 컴파일\n"
+"libpulse %s로 연결\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() 실패.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() 실패.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() 실패.\n"
+
+#: ../src/utils/pactl.c:153
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "통계 검색 실패: %s"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "현재 사용 중: %u 블록 (총 %s 바이트를 포함하고 있음).\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "전체 수명 기간 동안 할당: %u 블록 (총 %s 바이트를 포함하고 있음).\n"
+
+#: ../src/utils/pactl.c:165
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "샘플 캐쉬 크기: %s\n"
+
+#: ../src/utils/pactl.c:174
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "서버 정보 획득 실패: %s"
+
+#: ../src/utils/pactl.c:179
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"서버 이름: %s\n"
+"라이브러리 프로토콜 버전: %u\n"
+"서버 프로토콜 버전: %u\n"
+"로컬 동작: %s\n"
+"클라이언트 인덱스: %u\n"
+"타일 크기: %zu\n"
+
+#: ../src/utils/pactl.c:195
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"사용자 이름: %s\n"
+"호스트 이름: %s\n"
+"서버 이름: %s\n"
+"서버 버전: %s\n"
+"기본 샘플 사양: %s\n"
+"기본 채널 매핑: %s\n"
+"기본 싱크: %s\n"
+"기본 소스: %s\n"
+"쿠키: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:247 ../src/utils/pactl.c:889
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "싱크 정보를 가져올 수 없습니다: %s"
+
+#: ../src/utils/pactl.c:273
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"싱크 #%u\n"
+"\t상태: %s\n"
+"\t이름: %s\n"
+"\t설명: %s\n"
+"\t드라이버: %s\n"
+"\t샘플 사양: %s\n"
+"\t채널 맵: %s\n"
+"\t소유자 모듈: %u\n"
+"\t무음: %s\n"
+"\t볼륨: %s%s%s\n"
+"\t        균형 %0.2f\n"
+"\t기본 볼륨: %s%s%s\n"
+"\t모니터 소스: %s\n"
+"\t지연시간: %0.0f usec, 설정 %0.0f usec\n"
+"\t플래그: %s%s%s%s%s%s%s\n"
+"\t속성:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:321 ../src/utils/pactl.c:433 ../src/utils/pactl.c:593
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\t포트:\n"
+
+#: ../src/utils/pactl.c:328 ../src/utils/pactl.c:440
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\t활성 포트: %s\n"
+
+#: ../src/utils/pactl.c:334 ../src/utils/pactl.c:446
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\t형식:\n"
+
+#: ../src/utils/pactl.c:360 ../src/utils/pactl.c:908
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "소스 정보를 가져올 수 없습니다: %s"
+
+#: ../src/utils/pactl.c:386
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"소스 #%u\n"
+"\t상태: %s\n"
+"\t이름: %s\n"
+"\t설명: %s\n"
+"\t드라이버: %s\n"
+"\t샘플 사양: %s\n"
+"\t채널 맵: %s\n"
+"\t소유자 모듈: %u\n"
+"\t무음: %s\n"
+"\t볼륨: %s%s%s\n"
+"\t        균형 %0.2f\n"
+"\t기본 볼륨: %s%s%s\n"
+"\t싱크 모니터: %s\n"
+"\t지연시간: %0.0f usec, 설정 %0.0f usec\n"
+"\t플래그: %s%s%s%s%s%s\n"
+"\t속성:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:418 ../src/utils/pactl.c:488 ../src/utils/pactl.c:531 ../src/utils/pactl.c:573 ../src/utils/pactl.c:671
+#: ../src/utils/pactl.c:672 ../src/utils/pactl.c:684 ../src/utils/pactl.c:744 ../src/utils/pactl.c:745 ../src/utils/pactl.c:757
+#: ../src/utils/pactl.c:809 ../src/utils/pactl.c:810 ../src/utils/pactl.c:817
+msgid "n/a"
+msgstr "해당 없음 "
+
+#: ../src/utils/pactl.c:457 ../src/utils/pactl.c:864
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "모듈 정보를 가져올 수 없습니다: %s"
+
+#: ../src/utils/pactl.c:480
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"모듈 #%u\n"
+"\t이름: %s\n"
+"\t인수: %s\n"
+"\t사용자 카운터: %s\n"
+"\t속성:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:499
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "클라이언트 정보를 가져올 수 없습니다: %s"
+
+#: ../src/utils/pactl.c:525
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"클라이언트 #%u\n"
+"\t드라이버: %s\n"
+"\t소유자 모듈: %s\n"
+"\t속성:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:542
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "카드 정보를 가져올 수 없습니다: %s"
+
+#: ../src/utils/pactl.c:565
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"카드 #%u\n"
+"\t이름: %s\n"
+"\t드라이버: %s\n"
+"\t소유자 모듈: %s\n"
+"\t속성:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\t프로파일:\n"
+
+#: ../src/utils/pactl.c:587
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\t활성 프로파일: %s\n"
+
+#: ../src/utils/pactl.c:601
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\t속성:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:606
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\t프로파일 부분: %s"
+
+#: ../src/utils/pactl.c:623 ../src/utils/pactl.c:927
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "싱크 입력 정보를 가져올 수 없습니다: %s"
+
+#: ../src/utils/pactl.c:652
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"싱크 입력 #%u\n"
+"\t드라이버: %s\n"
+"\t소유자 모듈: %s\n"
+"\t클라이언트: %s\n"
+"\t싱크: %u\n"
+"\t샘플 사양: %s\n"
+"\t채널 맵: %s\n"
+"\t포맷: %s\n"
+"\t코르크: %s\n"
+"\t무음: %s\n"
+"\t볼륨: %s\n"
+"\t        %s\n"
+"\t        균형 %0.2f\n"
+"\t버퍼 지연 시간: %0.0f usec\n"
+"\t싱크 지연 시간: %0.0f usec\n"
+"\t리샘플링 방법: %s\n"
+"\t속성:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:695 ../src/utils/pactl.c:946
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "소스 출력 정보를 가져올 수 없습니다: %s"
+
+#: ../src/utils/pactl.c:725
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"소스 출력 #%u\n"
+"\t드라이버: %s\n"
+"\t소유자 모듈: %s\n"
+"\t클라이언트: %s\n"
+"\t소스: %u\n"
+"\t샘플 사양: %s\n"
+"\t채널 맵: %s\n"
+"\t포맷: %s\n"
+"\t코르크: %s\n"
+"\t무음: %s\n"
+"\t볼륨: %s\n"
+"\t        %s\n"
+"\t        균형 %0.2f\n"
+"\t버퍼 지연 시간: %0.0f usec\n"
+"\t소스 지연 시간: %0.0f usec\n"
+"\t리샘플링 방법: %s\n"
+"\t속성:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:768
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "샘플 정보를 가져올 수 없습니다: %s"
+
+#: ../src/utils/pactl.c:795
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"샘플 #%u\n"
+"\t이름: %s\n"
+"\t샘플 사양: %s\n"
+"\t채널맵: %s\n"
+"\t볼륨: %s\n"
+"\t        %s\n"
+"\t        균형 %0.2f\n"
+"\t길이: %0.1fs\n"
+"\t크기: %s\n"
+"\t레이지: %s\n"
+"\t파일 이름: %s\n"
+"\t속성:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:825 ../src/utils/pactl.c:835
+#, c-format
+msgid "Failure: %s"
+msgstr "오류: %s"
+
+#: ../src/utils/pactl.c:871
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "모듈 언로드에 실패했습니다: 모듈 %s 불러오기 실패 "
+
+#: ../src/utils/pactl.c:974
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "포맷 설정 실패: 잘못된 포맷 문자열 %s"
+
+#: ../src/utils/pactl.c:1013
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "샘플 업로드 실패: %s"
+
+#: ../src/utils/pactl.c:1030
+msgid "Premature end of file"
+msgstr "파일의 조기 종료"
+
+#: ../src/utils/pactl.c:1050
+msgid "new"
+msgstr "새로운 "
+
+#: ../src/utils/pactl.c:1053
+msgid "change"
+msgstr "변경 "
+
+#: ../src/utils/pactl.c:1056
+msgid "remove"
+msgstr "제거 "
+
+#: ../src/utils/pactl.c:1059 ../src/utils/pactl.c:1094
+msgid "unknown"
+msgstr "알 수 없음 "
+
+#: ../src/utils/pactl.c:1067
+msgid "sink"
+msgstr "싱크 "
+
+#: ../src/utils/pactl.c:1070
+msgid "source"
+msgstr "소스 "
+
+#: ../src/utils/pactl.c:1073
+msgid "sink-input"
+msgstr "싱크-입력 "
+
+#: ../src/utils/pactl.c:1076
+msgid "source-output"
+msgstr "소스-출력 "
+
+#: ../src/utils/pactl.c:1079
+msgid "module"
+msgstr "모듈 "
+
+#: ../src/utils/pactl.c:1082
+msgid "client"
+msgstr "클라이언트 "
+
+#: ../src/utils/pactl.c:1085
+msgid "sample-cache"
+msgstr "샘플-캐시 "
+
+#: ../src/utils/pactl.c:1088 ../src/utils/pactl.c:1091
+msgid "server"
+msgstr "서버 "
+
+#: ../src/utils/pactl.c:1100
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "이벤트 '%s'는 %s #%u 상에 있습니다.\n"
+
+#: ../src/utils/pactl.c:1324
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT를 받았습니다. 종료합니다."
+
+#: ../src/utils/pactl.c:1351
+msgid "Invalid volume specification"
+msgstr "잘못된 볼륨 사양 "
+
+#: ../src/utils/pactl.c:1374
+msgid "Volume outside permissible range.\n"
+msgstr "볼륨이 허용 범위를 벗어납니다.\n"
+
+#: ../src/utils/pactl.c:1385 ../src/utils/pactl.c:1386 ../src/utils/pactl.c:1387 ../src/utils/pactl.c:1388 ../src/utils/pactl.c:1389
+#: ../src/utils/pactl.c:1390 ../src/utils/pactl.c:1391 ../src/utils/pactl.c:1392 ../src/utils/pactl.c:1393 ../src/utils/pactl.c:1394
+#: ../src/utils/pactl.c:1395 ../src/utils/pactl.c:1396 ../src/utils/pactl.c:1397 ../src/utils/pactl.c:1398 ../src/utils/pactl.c:1399
+#: ../src/utils/pactl.c:1400 ../src/utils/pactl.c:1401 ../src/utils/pactl.c:1402 ../src/utils/pactl.c:1403 ../src/utils/pactl.c:1404
+msgid "[options]"
+msgstr "[옵션]"
+
+#: ../src/utils/pactl.c:1387
+msgid "[TYPE]"
+msgstr "[유형]"
+
+#: ../src/utils/pactl.c:1389
+msgid "FILENAME [NAME]"
+msgstr "파일 이름 [이름]"
+
+#: ../src/utils/pactl.c:1390
+msgid "NAME [SINK]"
+msgstr "이름 [싱크]"
+
+#: ../src/utils/pactl.c:1391 ../src/utils/pacmd.c:55 ../src/utils/pacmd.c:65
+msgid "NAME"
+msgstr "이름 "
+
+#: ../src/utils/pactl.c:1392 ../src/utils/pacmd.c:53
+msgid "NAME [ARGS ...]"
+msgstr "이름 [인수 ...]"
+
+#: ../src/utils/pactl.c:1393 ../src/utils/pacmd.c:54 ../src/utils/pacmd.c:62
+msgid "NAME|#N"
+msgstr "이름|#N"
+
+#: ../src/utils/pactl.c:1394 ../src/utils/pacmd.c:71
+msgid "#N SINK|SOURCE"
+msgstr "#N 싱크|소스"
+
+#: ../src/utils/pactl.c:1395 ../src/utils/pactl.c:1400 ../src/utils/pacmd.c:58 ../src/utils/pacmd.c:72
+msgid "NAME|#N 1|0"
+msgstr "이름|#N 1|0"
+
+#: ../src/utils/pactl.c:1396 ../src/utils/pacmd.c:74
+msgid "CARD PROFILE"
+msgstr "카드 프로파일 "
+
+#: ../src/utils/pactl.c:1397 ../src/utils/pacmd.c:75
+msgid "NAME|#N PORT"
+msgstr "이름|#N 포트"
+
+#: ../src/utils/pactl.c:1398 ../src/utils/pacmd.c:56
+msgid "NAME|#N VOLUME"
+msgstr "이름|#N 볼륨"
+
+#: ../src/utils/pactl.c:1399 ../src/utils/pacmd.c:57
+msgid "#N VOLUME"
+msgstr "#N 볼륨"
+
+#: ../src/utils/pactl.c:1401 ../src/utils/pacmd.c:59
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pactl.c:1402
+msgid "#N FORMATS"
+msgstr "#N 포맷"
+
+#: ../src/utils/pactl.c:1403 ../src/utils/pacmd.c:76
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "카드-이름|카드-#N 포트 오프셋"
+
+#: ../src/utils/pactl.c:1406
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"  -n, --client-name=NAME                How to call this client on the server\n"
+msgstr ""
+"\n"
+"  -h, --help                            도움말 표시\n"
+"      --version                         버전 표시\n"
+"\n"
+"  -s, --server=SERVER                   연결할 서버 이름\n"
+"  -n, --client-name=NAME                서버에서 클라이언트 호출 방법\n"
+
+#: ../src/utils/pactl.c:1447
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"libpulse %s로 컴파일 됨\n"
+"libpulse %s와 링크됨\n"
+
+#: ../src/utils/pactl.c:1506
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "아무것도 지정하지 않거나 다움 중 하나을 지정합니다: %s"
+
+#: ../src/utils/pactl.c:1516
+msgid "Please specify a sample file to load"
+msgstr "로딩할 샘플 파일을 지정하십시오 "
+
+#: ../src/utils/pactl.c:1529
+msgid "Failed to open sound file."
+msgstr "사운드 파일을 열 수 없습니다."
+
+#: ../src/utils/pactl.c:1541
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "경고: 파일에서 샘플 사양을 지정할 수 없습니다."
+
+#: ../src/utils/pactl.c:1551
+msgid "You have to specify a sample name to play"
+msgstr "재생할 샘플 이름을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1563
+msgid "You have to specify a sample name to remove"
+msgstr "제거할 샘플 이름을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1572
+msgid "You have to specify a sink input index and a sink"
+msgstr "싱크 입력 인덱스와 싱크를 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1582
+msgid "You have to specify a source output index and a source"
+msgstr "소스 출력 인덱스와 소스를 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1597
+msgid "You have to specify a module name and arguments."
+msgstr "모듈 이름과 인수를 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1617
+msgid "You have to specify a module index or name"
+msgstr "모듈 인덱스 또는 이름을 지정해야 합니다 "
+
+#: ../src/utils/pactl.c:1628
+msgid "You may not specify more than one sink. You have to specify a boolean value."
+msgstr "하나 이상의 싱크를 지정할 수 없습니다. 부울 값을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1641
+msgid "You may not specify more than one source. You have to specify a boolean value."
+msgstr "하나 이상의 소스를 지정할 수 없습니다. 부울 값을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1653
+msgid "You have to specify a card name/index and a profile name"
+msgstr "카드 이름/인덱스와 프로파일 이름을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1664
+msgid "You have to specify a sink name/index and a port name"
+msgstr "싱크 이름/인덱스와 포트 이름을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1675
+msgid "You have to specify a source name/index and a port name"
+msgstr "소스 이름/인덱스와 포트 이름을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1686
+msgid "You have to specify a sink name/index and a volume"
+msgstr "싱크 이름/인덱스와 볼륨을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1699
+msgid "You have to specify a source name/index and a volume"
+msgstr "소스 이름/인덱스와 볼륨을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1712
+msgid "You have to specify a sink input index and a volume"
+msgstr "싱크 입력 인덱스와 볼륨을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1717
+msgid "Invalid sink input index"
+msgstr "잘못된 싱크 입력 인덱스"
+
+#: ../src/utils/pactl.c:1728
+msgid "You have to specify a source output index and a volume"
+msgstr "소스 출력 인덱스와 볼륨을 지정해야 합니다 "
+
+#: ../src/utils/pactl.c:1733
+msgid "Invalid source output index"
+msgstr "잘못된 소스 출력 인덱스 "
+
+#: ../src/utils/pactl.c:1745
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "싱크 이름/인덱스와 무음 부울을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1750 ../src/utils/pactl.c:1767 ../src/utils/pactl.c:1789 ../src/utils/pactl.c:1810
+msgid "Invalid mute specification"
+msgstr "잘못된 무음 사양 "
+
+#: ../src/utils/pactl.c:1762
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "소스 이름/인덱스와 무음 부울을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1779
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "싱크 입력 인덱스와 무음 부울을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1784
+msgid "Invalid sink input index specification"
+msgstr "잘못된 싱크 입력 인덱스 사양 "
+
+#: ../src/utils/pactl.c:1800
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "소스 출력 인덱스와 무음 부울을 지정해야 합니다."
+
+#: ../src/utils/pactl.c:1805
+msgid "Invalid source output index specification"
+msgstr "잘못된 소스 출력 인덱스 사양 "
+
+#: ../src/utils/pactl.c:1824
+msgid "You have to specify a sink index and a semicolon-separated list of supported formats"
+msgstr "싱크 인덱스 및 지원하는 형식의 세미콜론으로 분리된 목록을 지정해야 합니다 "
+
+#: ../src/utils/pactl.c:1836
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr "카드 이름/인덱스, 포트 이름 및 지연 오프셋을 지정해야 합니다 "
+
+#: ../src/utils/pactl.c:1843
+msgid "Could not parse latency offset"
+msgstr "지연 오프셋을 분석할 수 없습니다 "
+
+#: ../src/utils/pactl.c:1855
+msgid "No valid command specified."
+msgstr "유효한 명령이 지정되어 있지 않습니다."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    X11 디스플레이에 연결된 최신 PulseAudio 데이터 표시 (기본값)\n"
+" -e    X11 디스플레이에 로컬 PulseAudio 데이터를 내보내기\n"
+" -i    X11 디스플레이에서 로컬 환경 변수 및 쿠키 파일에 PulseAudio 데이터 가져오기.\n"
+" -r    X11 디스플레이에서 PulseAudio 데이터 삭제\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "명령행 분석 실패\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "서버: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "소스: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "싱크: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "쿠키: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "쿠키 데이터 분석 실패\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "쿠키 데이터 저장 실패\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "클라이언트 설정 파일 읽기 실패\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "환경 구성 데이터를 가져올 수 없습니다\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN을 가져올 수 없습니다.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "쿠키 데이터 읽기 실패\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "아직 구현되지 않았습니다.\n"
+
+#: ../src/utils/pacmd.c:60
+msgid "NAME|#N KEY=VALUE"
+msgstr "이름|#N 키=값"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N KEY=VALUE"
+msgstr "#N 키=값"
+
+#: ../src/utils/pacmd.c:63
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:64
+msgid "NAME SINK|#N"
+msgstr "이름 싱크|#N"
+
+#: ../src/utils/pacmd.c:66 ../src/utils/pacmd.c:67
+msgid "NAME FILENAME"
+msgstr "이름 파일이름 "
+
+#: ../src/utils/pacmd.c:68
+msgid "PATHNAME"
+msgstr "경로 이름 "
+
+#: ../src/utils/pacmd.c:69
+msgid "FILENAME SINK|#N"
+msgstr "파일 이름 싱크|#N"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pacmd.c:79 ../src/utils/pacmd.c:80
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:77
+msgid "TARGET"
+msgstr "대상 "
+
+#: ../src/utils/pacmd.c:78
+msgid "NUMERIC LEVEL"
+msgstr "숫자 레벨 "
+
+#: ../src/utils/pacmd.c:81
+msgid "FRAMES"
+msgstr "프레임 "
+
+#: ../src/utils/pacmd.c:83
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacdm starts in the interactive mode\n"
+msgstr ""
+"\n"
+"  -h, --help                            도움말 표시\n"
+"      --version                         버전 표시\n"
+"명령이 없을 경우 pacdm은 상호 대화식 모드에서 시작합니다\n"
+
+#: ../src/utils/pacmd.c:131
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"libpulse %s로 컴파일\n"
+"libpulse %s로 연결\n"
+
+#: ../src/utils/pacmd.c:145
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "펄스오디오 데몬이 실행되고 있지 않거나, 세션 데몬으로 실행되고 있지 않습니다."
+
+#: ../src/utils/pacmd.c:150
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "소켓(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:167
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:176
+msgid "Failed to kill PulseAudio daemon."
+msgstr "펄스오디오 데몬 종료에 실패하였습니다."
+
+#: ../src/utils/pacmd.c:184
+msgid "Daemon not responding."
+msgstr "데몬이 응답하지 않습니다."
+
+#: ../src/utils/pacmd.c:264
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:275 ../src/utils/pacmd.c:295
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:317 ../src/utils/pacmd.c:335
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "autospawn 잠금에 액세스할 수 없습니다."
+
+#: ../src/modules/alsa/alsa-sink.c:562 ../src/modules/alsa/alsa-sink.c:728
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."
+msgstr ""
+"ALSA가 장치에 새 데이터를 쓰도록 재촉했지만 쓸 수 있는 것이 없습니다!\n"
+"이는 대부분 ALSA 드라이버 '%s'의 버그입니다. 이 문제를 ALSA 개발자에게 보고하십시오.\n"
+"POLLOUT 세트로 불러오려했지만 결과적으로 snd_pcm_avail()이 0 또는 다른 값 < min_avail을 반환했습니다. "
+
+#: ../src/modules/alsa/alsa-source.c:521 ../src/modules/alsa/alsa-source.c:674
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."
+msgstr ""
+"ALSA가 장치에 새 데이터를 읽도록 재촉했지만 읽을 수 있는 것이 없습니다!\n"
+"이는 대부분 ALSA 드라이버 '%s'의 버그입니다. 이 문제를 ALSA 개발자에게 보고하십시오.\n"
+"POLLIN 세트로 불러오려했지만 결과적으로 snd_pcm_avail()이 0 또는 다른 값 < min_avail을 반환했습니다. "
+
+#: ../src/modules/alsa/module-alsa-card.c:193 ../src/modules/bluetooth/module-bluetooth-device.c:2458
+#: ../src/modules/alsa/alsa-mixer.c:3949
+msgid "Off"
+msgstr "끄기 "
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2282 ../src/modules/bluetooth/module-bluetooth-device.c:2309
+msgid "Bluetooth Output"
+msgstr "블루투스 출력 "
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2294
+msgid "Bluetooth High Quality (A2DP)"
+msgstr "Bluetooth 고품질 (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2318
+msgid "Bluetooth Telephony (HSP/HFP)"
+msgstr "Bluetooth 전화 (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2328 ../src/modules/bluetooth/module-bluetooth-device.c:2336
+msgid "Bluetooth Handsfree Gateway"
+msgstr "블루투스 핸즈프리 게이트웨이 "
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2357
+msgid "High Fidelity Playback (A2DP)"
+msgstr "고품질 재생 (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2367
+msgid "High Fidelity Capture (A2DP)"
+msgstr "고품질 녹음 (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2377
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "양방향 전화통화 (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2387
+msgid "Handsfree Gateway"
+msgstr "핸즈프리 게이트웨이"
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "펄스오디오 사운드 서버"
+
+#: ../src/modules/module-rygel-media-server.c:510 ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "출력 장치"
+
+#: ../src/modules/module-rygel-media-server.c:511 ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "입력 장치"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "호스트 @HOSTNAME@의 오디오"
+
+#: ../src/modules/alsa/alsa-mixer.c:2251
+msgid "Input"
+msgstr "입력"
+
+#: ../src/modules/alsa/alsa-mixer.c:2252
+msgid "Docking Station Input"
+msgstr "도킹 스테이션 입력"
+
+#: ../src/modules/alsa/alsa-mixer.c:2253
+msgid "Docking Station Microphone"
+msgstr "도킹 스테이션 마이크"
+
+#: ../src/modules/alsa/alsa-mixer.c:2254
+msgid "Docking Station Line In"
+msgstr "도킹 스테이션 라인 입력 "
+
+#: ../src/modules/alsa/alsa-mixer.c:2255 ../src/modules/alsa/alsa-mixer.c:2339
+msgid "Line In"
+msgstr "라인 입력 "
+
+#: ../src/modules/alsa/alsa-mixer.c:2256 ../src/modules/alsa/alsa-mixer.c:2334
+msgid "Microphone"
+msgstr "마이크"
+
+#: ../src/modules/alsa/alsa-mixer.c:2257 ../src/modules/alsa/alsa-mixer.c:2335
+msgid "Front Microphone"
+msgstr "전면 마이크 "
+
+#: ../src/modules/alsa/alsa-mixer.c:2258 ../src/modules/alsa/alsa-mixer.c:2336
+msgid "Rear Microphone"
+msgstr "후면 마이크 "
+
+#: ../src/modules/alsa/alsa-mixer.c:2259
+msgid "External Microphone"
+msgstr "외부 마이크 "
+
+#: ../src/modules/alsa/alsa-mixer.c:2260 ../src/modules/alsa/alsa-mixer.c:2338
+msgid "Internal Microphone"
+msgstr "내부 마이크 "
+
+#: ../src/modules/alsa/alsa-mixer.c:2261 ../src/modules/alsa/alsa-mixer.c:2340
+msgid "Radio"
+msgstr "라디오 "
+
+#: ../src/modules/alsa/alsa-mixer.c:2262 ../src/modules/alsa/alsa-mixer.c:2341
+msgid "Video"
+msgstr "비디오 "
+
+#: ../src/modules/alsa/alsa-mixer.c:2263
+msgid "Automatic Gain Control"
+msgstr "자동 게인 컨트롤"
+
+#: ../src/modules/alsa/alsa-mixer.c:2264
+msgid "No Automatic Gain Control"
+msgstr "자동 게인 컨트롤 없음"
+
+#: ../src/modules/alsa/alsa-mixer.c:2265
+msgid "Boost"
+msgstr "부스트"
+
+#: ../src/modules/alsa/alsa-mixer.c:2266
+msgid "No Boost"
+msgstr "부스트 없음"
+
+#: ../src/modules/alsa/alsa-mixer.c:2267
+msgid "Amplifier"
+msgstr "증폭"
+
+#: ../src/modules/alsa/alsa-mixer.c:2268
+msgid "No Amplifier"
+msgstr "증폭 없음"
+
+#: ../src/modules/alsa/alsa-mixer.c:2269
+msgid "Bass Boost"
+msgstr "베이스 부스트"
+
+#: ../src/modules/alsa/alsa-mixer.c:2270
+msgid "No Bass Boost"
+msgstr "베이스 부스트 없음"
+
+#: ../src/modules/alsa/alsa-mixer.c:2271
+msgid "Speaker"
+msgstr "스피커"
+
+#: ../src/modules/alsa/alsa-mixer.c:2272 ../src/modules/alsa/alsa-mixer.c:2343
+msgid "Headphones"
+msgstr "헤드폰"
+
+#: ../src/modules/alsa/alsa-mixer.c:2333
+msgid "Analog Input"
+msgstr "아날로그 입력"
+
+#: ../src/modules/alsa/alsa-mixer.c:2337
+msgid "Dock Microphone"
+msgstr "도킹 스테이션 마이크 "
+
+#: ../src/modules/alsa/alsa-mixer.c:2342
+msgid "Analog Output"
+msgstr "아날로그 출력"
+
+#: ../src/modules/alsa/alsa-mixer.c:2344
+msgid "LFE on Separate Mono Output"
+msgstr "별도의 모노 출력 (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2345
+msgid "Line Out"
+msgstr "라인 출력 "
+
+#: ../src/modules/alsa/alsa-mixer.c:2346
+msgid "Analog Mono Output"
+msgstr "아날로그 모노 출력"
+
+#: ../src/modules/alsa/alsa-mixer.c:2347
+msgid "Speakers"
+msgstr "스피커"
+
+#: ../src/modules/alsa/alsa-mixer.c:2348
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2349
+msgid "Digital Output (S/PDIF)"
+msgstr "디지털 출력 (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2350
+msgid "Digital Input (S/PDIF)"
+msgstr "디지털 입력 (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2351
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "디지털 통과 (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3797
+msgid "Analog Mono"
+msgstr "아날로그 모노 "
+
+#: ../src/modules/alsa/alsa-mixer.c:3798
+msgid "Analog Stereo"
+msgstr "아날로그 스테레오 "
+
+#: ../src/modules/alsa/alsa-mixer.c:3799
+msgid "Analog Surround 2.1"
+msgstr "아날로그 서라운드 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3800
+msgid "Analog Surround 3.0"
+msgstr "아날로그 서라운드 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3801
+msgid "Analog Surround 3.1"
+msgstr "아날로그 서라운드 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3802
+msgid "Analog Surround 4.0"
+msgstr "아날로그 서라운드 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3803
+msgid "Analog Surround 4.1"
+msgstr "아날로그 서라운드 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3804
+msgid "Analog Surround 5.0"
+msgstr "아날로그 서라운드 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3805
+msgid "Analog Surround 5.1"
+msgstr "아날로그 서라운드 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3806
+msgid "Analog Surround 6.0"
+msgstr "아날로그 서라운드 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3807
+msgid "Analog Surround 6.1"
+msgstr "아날로그 서라운드 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3808
+msgid "Analog Surround 7.0"
+msgstr "아날로그 서라운드 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3809
+msgid "Analog Surround 7.1"
+msgstr "아날로그 서라운드 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3810
+msgid "Analog 4-channel Input"
+msgstr "아날로그 4-채널 입력"
+
+#: ../src/modules/alsa/alsa-mixer.c:3811
+msgid "Digital Stereo (IEC958)"
+msgstr "디지털 스테레오 (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3812
+msgid "Digital Passthrough  (IEC958)"
+msgstr "디지털 통과 (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3813
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "디지털 서라운드 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3814
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "디지털 서라운드 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3815
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "디지털 서라운드 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3816
+msgid "Digital Stereo (HDMI)"
+msgstr "디지털 스테레오 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3817
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "디지털 서라운드 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3946
+msgid "Analog Mono Duplex"
+msgstr "아날로그 양방향 모노"
+
+#: ../src/modules/alsa/alsa-mixer.c:3947
+msgid "Analog Stereo Duplex"
+msgstr "아날로그 양방향 스테레오"
+
+#: ../src/modules/alsa/alsa-mixer.c:3948
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "아날로그 양방향 스테레오 (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4048
+#, c-format
+msgid "%s Output"
+msgstr "%s 출력"
+
+#: ../src/modules/alsa/alsa-mixer.c:4056
+#, c-format
+msgid "%s Input"
+msgstr "%s 입력"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the source> source_master=<name of source to filter> "
+"sink_name=<name for the sink> sink_properties=<properties for the sink> sink_master=<name of sink to filter> adjust_time=<how often "
+"to readjust rates in s> adjust_threshold=<how much drift to readjust after in ms> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"source_name=<소스의 이름> source_properties=<소스에 속성들을 지정> source_master=<필터를 적용할 소스의 이름> sink_name=<싱크의 이름"
+"> sink_properties=<싱크에 속성들을 지정> sink_master=<필터를 적용할 싱크의 이름> adjust_time=<샘플 레이트 재조정을 몇 초 단위로 할 "
+"것인지 지정> adjust_threshold=<드리프트가 몇 ms 이후부터 재조정을 할 것인지 지정> format=<샘플 형식> rate=<샘플 레이트> channels=<채"
+"널 수> channel_map=<채널 맵> aec_method=<사용 할 구현체> aec_args=<AEC 엔진의 인자들> save_aec=<AEC 데이터를 /tmp 안에 저장> "
+"autoloaded=<이 모듈이 자동으로 로드된다면 설정하십시오> use_volume_sharing=<yes 또는 no> "
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr "일반적 목적의 이퀼라이저 "
+
+#: ../src/modules/module-equalizer-sink.c:76
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> sink_master=<sink to connect to> format=<sample format> "
+"rate=<sample rate> channels=<number of channels> channel_map=<channel map> autoloaded=<set if this module is being loaded "
+"automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<싱크의 이름> sink_properties=<싱크에 속성들을 지정> sink_master=<연결할 싱크> format=<샘플 형식> rate=<샘플 레이트> "
+"channels=<채널 수> channel_map=<채널 맵> autoloaded=<이 모듈이 자동으로 로드된다면 설정하십시오> use_volume_sharing=<yes 또는 no> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<automatically unload unused filters?>"
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to 44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to 1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to 44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"-h, --help                            도움말 표시\n"
+"-v, --verbose                         디버그 메세지 표시\n"
+"      --from-rate=SAMPLERATE          변환 전 샘플 레이트(Hz) (기본값 44100)\n"
+"      --from-format=SAMPLEFORMAT      변환 전 샘플 유형 (기본값 s16le)\n"
+"      --from-channels=CHANNELS        변환 전 채널 수 (기본값 1)\n"
+"      --to-rate=SAMPLERATE            변환 후 샘플 레이트 (Hz) (기본값 44100)\n"
+"      --to-format=SAMPLEFORMAT        변환 후 샘플 유형 (기본값 s16le)\n"
+"      --to-channels=CHANNELS          변환 후 채널 수 (기본값 1)\n"
+"      --resample-method=METHOD        다시 샘플링 방법 (기본값 auto)\n"
+"      --seconds=SECONDS               변환 전 스트림 시간 (기본값 60)\n"
+"\n"
+"형식이 지정되지 않은 경우 모든 형식의 조합을\n"
+"테스트합니다.\n"
+"\n"
+"샘플 유형은 s16le, s16be, u8, float32le, float32be, ulaw, alaw, 32le, s32be에서\n"
+"하나를 선택합니다 (기본값 s16ne)\n"
+"\n"
+"다시 샘플링 방법에 사용 가능한 값은 --dump-resample-methods에서 참조하십시오.\n"
+
+#: ../src/tests/resampler-test.c:356
+#, c-format
+msgid "%s %s\n"
+msgstr "%s %s\n"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr "=== %d 초: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+
+#: ../src/modules/module-virtual-surround-sink.c:49
+msgid "Virtual surround sink"
+msgstr "가상 서라운드 싱크 "
+
+#: ../src/modules/module-virtual-surround-sink.c:53
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> master=<name of sink to filter> sink_master=<name of sink "
+"to filter> format=<sample format> rate=<sample rate> channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/left_hrir.wav autoloaded=<set if this module is being "
+"loaded automatically> "
+msgstr ""
+"sink_name=<싱크의 이름> sink_properties=<싱크에 속성들을 지정> master=<필터를 적용할 싱크의 이름> sink_master=<필터를 적용할 싱크의 "
+"이름> format=<샘플 형식> rate=<샘플 레이트> channels=<채널 수> channel_map=<채널 맵> use_volume_sharing=<yes 또는 no> "
+"force_flat_volume=<yes 또는 no> hrir=/path/to/left_hrir.wav autoloaded=<이 모듈이 자동으로 로드된다면 설정하십시오> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:747
+msgid "On"
+msgstr "활성"
diff --git a/po/lt.po b/po/lt.po
new file mode 100644 (file)
index 0000000..5f5ee0f
--- /dev/null
+++ b/po/lt.po
@@ -0,0 +1,3234 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Moo, 2017.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
+"product=PulseAudio&keywords=I18N+L10N&component=misc\n"
+"POT-Creation-Date: 2017-07-25 22:04+0000\n"
+"PO-Revision-Date: 2017-08-04 19:15+0300\n"
+"Last-Translator: Moo\n"
+"Language-Team: \n"
+"Language: lt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.7.1\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n"
+"%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [parametrai]\n"
+"\n"
+"KOMANDOS:\n"
+"  -h, --help                            Rodyti šią pagalbą\n"
+"      --version                         Rodyti versiją\n"
+"      --dump-conf                       Sukurti numatytosios konfigūracijos "
+"išklotinę\n"
+"      --dump-modules                    Sukurti prieinamų modulių išklotinę\n"
+"      --dump-resample-methods           Sukurti prieinamų ėminių keitimo "
+"metodų išklotinę\n"
+"      --cleanup-shm                     Išvalyti pasenusius bendrinamos "
+"atminties segmentus\n"
+"      --start                           Paleisti tarnybą, jeigu ji nėra "
+"vykdoma\n"
+"  -k  --kill                            Nutraukti vykdomos tarnybos darbą\n"
+"      --check                           Patikrinti ar yra paleista tarnyba "
+"(grąžina tik išėjimo kodą)\n"
+"\n"
+"PARAMETRAI:\n"
+"      --system[=BOOL]                   Vykdyti egzempliorių sistemos mastu\n"
+"  -D, --daemonize[=BOOL]                Po paleidimo paversti tarnyba\n"
+"      --fail[=BOOL]                     Baigti darbą, kai paleidimas "
+"nepavyksta\n"
+"      --high-priority[=BOOL]            Pabandyti nustatyti aukštesnį nice "
+"lygį\n"
+"                                        (yra prieinama tik kaip root, su "
+"SUID arba\n"
+"                                        su iškeltu RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Pabandyti įjungti tikralaikį "
+"planavimą\n"
+"                                        (yra prieinama tik kaip root, su "
+"SUID arba\n"
+"                                        su iškeltu RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Neleisti naudotojo užklausto "
+"modulio\n"
+"                                        įkėlimą/iškėlimą po paleidimo\n"
+"      --disallow-exit[=BOOL]            Neleisti naudotojo užklausto "
+"išėjimo\n"
+"      --exit-idle-time=SEK.             Nutraukti tarnybą, kai ji yra "
+"neveikli ir praėjo\n"
+"                                        tiek laiko\n"
+"      --scache-idle-time=SEK.           Iškelti automatiškai įkeltus "
+"ėminius, kai\n"
+"                                        nėra veiklos ir praėjo tiek laiko\n"
+"      --log-level[=LYGIS]               Padidinti arba nustatyti išsamumo "
+"lygį\n"
+"  -v  --verbose                         Padidinti išsamumo lygį\n"
+"      --log-target={auto,syslog,stderr,file:KELIAS,newfile:KELIAS}\n"
+"                                        Nurodyti žurnalo paskirties vietą\n"
+"      --log-meta[=BOOL]                 Į žurnalo žinutes įtraukti kodo "
+"vietą\n"
+"      --log-time[=BOOL]                 Į žurnalo žinutes įtraukti laiko "
+"žymas\n"
+"      --log-backtrace=KADRAI            Į žurnalo žinutes įtraukti "
+"atgalinius pėdsakus\n"
+"  -p, --dl-search-path=KELIAS             Nustatyti paieškos kelią, skirtą "
+"dinaminiams\n"
+"                                        bendrinamiems objektams "
+"(įskiepiams)\n"
+"      --resample-method=METODAS          Naudoti nurodytą ėminių keitimo "
+"metodą\n"
+"                                        (Galimoms reikšmėms, žiūrėkite --"
+"dump-resample-methods\n"
+"      --use-pid-file[=BOOL]             Sukurti PID failą\n"
+"      --no-cpu-limit[=BOOL]             Nediegti procesoriaus apkrovos "
+"ribotuvą\n"
+"                                        platformose, kurios jį palaiko.\n"
+"      --disable-shm[=BOOL]              Išjungti bendrinamos atminties "
+"palaikymą.\n"
+"      --enable-memfd[=BOOL]             Įjungti memfd bendrinamo atminties "
+"palaikymą.\n"
+"\n"
+"PALEIDIMO SCENARIJUS:\n"
+"  -L, --load=\"MODULIO ARGUMENTAI\"         Įkelti nurodytą įskiepio modulį "
+"su\n"
+"                                        nurodytais argumentais\n"
+"  -F, --file=FILENAME                   Vykdyti nurodytą scenarijų\n"
+"  -C                                    Po paleidimo atverti komandų eilutę "
+"vykdomame TTY\n"
+"\n"
+"  -n                                    Neįkelti numatytojo scenarijaus "
+"failo\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "Parametrui --daemonize turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "Parametrui --fail turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"Parametrui --log-level turėtumėte nurodyti registravimo lygio argumentą "
+"(arba skaitmeninį rėžyje 0..4, arba vieną iš debug, info, notice, warn, "
+"error)."
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "Parametrui --high-priority turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "Parametrui --realtime turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr ""
+"Parametrui --disallow-module-loading turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "Parametrui --disallow-exit turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "Parametrui --use-pid-file turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Neteisinga žurnalo paskirtis: naudokite arba \"syslog\", \"journal\", "
+"\"stderr\", arba \"auto\", arba teisingą failo pavadinimą \"file:<kelias>\", "
+"\"newfile:<kelias>\"."
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Neteisinga žurnalo paskirtis: naudokite arba \"syslog\", \"stderr\", arba "
+"\"auto\", arba teisingą failo pavadinimą \"file:<kelias>\", \"newfile:"
+"<kelias>\"."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "Parametrui --log-time turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "Parametrui --log-meta turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Neteisingas ėminių keitimo metodas \"%s\"."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "Parametrui --system turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "Parametrui --no-cpu-limit turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "Parametrui --disable-shm turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr "Parametrui --enable-memfd turėtumėte nurodyti loginį argumentą"
+
+#: ../src/daemon/daemon-conf.c:262
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Neteisinga žurnalo paskirtis \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:277
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Neteisingas registravimo lygis \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:292
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Neteisingas ėminių keitimo metodas \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:314
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Neteisingas rlimit \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:334
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Neteisingas ėminio formatas \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:351 ../src/daemon/daemon-conf.c:368
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Neteisingas skaitmeninimo dažnis \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:391
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Neteisingi ėminio kanalai \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:408
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Neteisinga kanalų schema \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Neteisingas fragmentų skaičius \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:442
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Neteisingas fragmento dydis \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:459
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Neteisingas nice lygis \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:502
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Neteisingas serverio tipas \"%s\"."
+
+#: ../src/daemon/daemon-conf.c:620
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Nepavyko atverti konfigūracijos failo: %s"
+
+#: ../src/daemon/daemon-conf.c:636
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Nurodytoje numatytojoje kanalų schemoje yra skirtingas kanalų skaičius nei nu"
+
+#: ../src/daemon/daemon-conf.c:723
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Perskaityta iš konfigūracijos failo: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Pavadinimas: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Nėra prieinamos modulio informacijos\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Versija: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Aprašas: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autorius: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Naudojimas: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Įkelti kai: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "ĮSPĖJIMAS APIE PASENUSĮ MODULĮ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Kelias: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Nepavyko atverti modulio %s: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Nepavyko rasti pradinio lt_dlopen įkėliklio."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Nepavyko paskirstyti naujo dl įkėliklio."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Nepavyko pridėti bind-now-loader."
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Nepavyko rasti naudotojo \"%s\"."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Nepavyko rasti grupės \"%s\"."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "Naudoto \"%s\" ir grupės \"%s\" GID nesutampa."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "\"%s\" naudotojo namų katalogas nėra \"%s\", nepaisoma."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Nepavyko sukurti \"%s\": %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Nepavyko pakeisti grupės sąrašo: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Nepavyko pakeisti GID: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Nepavyko pakeisti UID: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "Sistemą apimanti veiksena šioje platformoje nepalaikoma."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Nepavyko analizuoti komandų eilutės."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Sistemos veiksena atsisakė pasileisti ne root naudotojui. Paleidžiama tik D-"
+"Bus serverio peržvalginė tarnyba."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Nepavyko nutraukti tarnybos: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Ši programa nėra skirta vykdyti administratoriaus teisėmis (nebent yra "
+"nurodyta --system)."
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Reikalaujamos pagrindinio naudotojo (root) teisės."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "Parametras --start nėra palaikomas sistemos egzemplioriams."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+"Naudotojo sukonfigūruotas serveris ties %s, atsisako pasileisti/automatiškai "
+"atnaujinti darbą."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Naudotojo sukonfigūruotas serveris ties %s, kuris, atrodo, yra vietinis. "
+"Tiriama išsamiau."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr "Vykdoma sistemos veiksenoje, tačiau nėra nustatytas --disallow-exit."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr ""
+"Vykdoma sistemos veiksenoje, tačiau nėra nustatytas --disallow-module-"
+"loading."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr "Vykdoma sistemos veiksenoje, priverstinai išjungiama SHM veiksena."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr ""
+"Vykdoma sistemos veiksenoje, priverstinai išjungiamas išėjimo laikas, esant "
+"neveiklumui."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "Nepavyko įgyti stdio."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() nepavyko: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() nepavyko: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:562
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() nepavyko: %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Tarnybos paleidimas nepavyko."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() nepavyko: %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Nepavyko gauti sistemos ID"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually "
+"do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Gerai, taigi jūs vykdote PA sistemos veiksenoje. Prašome įsitikinti, kad jūs "
+"tikrai to norite.\n"
+"Prašome skaityti http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ , kad sužinotumėte kodėl "
+"sistemos veiksena, dažniausiai, yra netikusi mintis."
+
+#: ../src/daemon/main.c:990
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() nepavyko."
+
+#: ../src/daemon/main.c:1022
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() nepavyko."
+
+#: ../src/daemon/main.c:1092
+msgid "Failed to initialize daemon."
+msgstr "Nepavyko inicijuoti tarnybos."
+
+#: ../src/daemon/main.c:1097
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Tarnybos paleidimas be jokių įkeltų modulių, tarnyba negalės veikti."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio garso sistema"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Paleisti PulseAudio garso sistemą"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Įvestis"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Sujungimo stoties įvestis"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Sujungimo stoties mikrofonas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Sujungimo stoties įvadinė linija"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Įvadinė linija"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1750
+msgid "Microphone"
+msgstr "Mikrofonas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Priekinis mikrofonas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Galinis mikrofonas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Išorinis mikrofonas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Vidinis mikrofonas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Radijas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Vaizdas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Automatinis stiprinimo reguliavimas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Be automatinio stiprinimo reguliavimo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Pastiprinimas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Be pastiprinimo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Stiprintuvas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Be stiprintuvo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Žemų tonų pastiprinimas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Be žemų tonų pastiprinimo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1757
+msgid "Speaker"
+msgstr "Garsiakalbis"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Ausinės"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Analoginė įvestis"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Doko mikrofonas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Ausinių mikrofonas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Analoginė išvestis"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "Žemųjų dažnių efektai atskiroje mono išvestyje"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Išvadinė linija"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Analoginė mono išvestis"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Garsiakalbiai"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Skaitmeninė išvestis (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Skaitmeninė įvestis (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Skaitmeninis persiuntimas (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Daugiakanalė įvestis"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Daugiakanalė išvestis"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Analoginė mono"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Analoginė stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Daugiakanalė"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Analoginė erdvinė 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Analoginė erdvinė 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Analoginė erdvinė 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Analoginė erdvinė 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Analoginė erdvinė 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Analoginė erdvinė 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Analoginė erdvinė 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Analoginė erdvinė 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Analoginė erdvinė 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Analoginė erdvinė 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Analoginė erdvinė 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Skaitmeninė stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Skaitmeninis persiuntimas (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Skaitmeninė erdvinė 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Skaitmeninė erdvinė 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Skaitmeninė erdvinė 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Skaitmeninė stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Skaitmeninė erdvinė 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4019 ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Mono Duplex"
+msgstr "Analoginė dvipusė mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Analog Stereo Duplex"
+msgstr "Analoginė dvipusė stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Skaitmeninė dvipusė stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+msgid "Multichannel Duplex"
+msgstr "Daugiakanalė dvipusė"
+
+#: ../src/modules/alsa/alsa-mixer.c:4156
+msgid "Stereo Duplex"
+msgstr "Dvipusė stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4157
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2324
+#: ../src/modules/bluetooth/module-bluez5-device.c:2005
+msgid "Off"
+msgstr "Išjungta"
+
+#: ../src/modules/alsa/alsa-mixer.c:4256
+#, c-format
+msgid "%s Output"
+msgstr "%s išvestis"
+
+#: ../src/modules/alsa/alsa-mixer.c:4264
+#, c-format
+msgid "%s Input"
+msgstr "%s įvestis"
+
+#: ../src/modules/alsa/alsa-sink.c:572
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA iškvietė mus, kad įrašytume naujus duomenis į įrenginį, tačiau, iš "
+"tikrųjų, nebuvo ką rašyti.\n"
+"Greičiausiai, tai yra klaida ALSA tvarkyklėje \"%s\". Prašome pranešti apie "
+"šią klaidą ALSA kūrėjams.\n"
+"Mes buvome iškviesti su nustatytu POLLOUT -- vis dėlto, vėlesnis "
+"snd_pcm_avail() grąžino 0 ar kitą reikšmę < min_avail."
+
+#: ../src/modules/alsa/alsa-sink.c:756
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA iškvietė mus, kad įrašytume naujus duomenis į įrenginį, tačiau, iš "
+"tikrųjų, nebuvo ką rašyti!\n"
+"Greičiausiai, tai yra klaida ALSA tvarkyklėje \"%s\". Prašome pranešti apie "
+"šią klaidą ALSA kūrėjams.\n"
+"Mes buvome iškviesti su nustatytu POLLOUT -- vis dėlto, vėlesnis "
+"snd_pcm_avail() grąžino 0 ar kitą reikšmę < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA iškvietė mus, kad perskaitytumėme naujus duomenis iš įrenginio, tačiau, "
+"iš tikrųjų, nebuvo ką skaityti.\n"
+"Greičiausiai, tai yra klaida ALSA tvarkyklėje \"%s\". Prašome pranešti apie "
+"šią klaidą ALSA kūrėjams.\n"
+"Mes buvome iškviesti su nustatytu POLLIN -- vis dėlto, vėlesnis "
+"snd_pcm_avail() grąžino 0 ar kitą reikšmę < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA iškvietė mus, kad perskaitytumėme naujus duomenis iš įrenginio, tačiau, "
+"iš tikrųjų, nebuvo ką skaityti!\n"
+"Greičiausiai, tai yra klaida ALSA tvarkyklėje \"%s\". Prašome pranešti apie "
+"šią klaidą ALSA kūrėjams.\n"
+"Mes buvome iškviesti su nustatytu POLLIN -- vis dėlto, vėlesnis "
+"snd_pcm_avail() grąžino 0 ar kitą reikšmę < min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1168 ../src/modules/alsa/alsa-util.c:1243
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() grąžino reikšmę, kuri yra išskirtinai didelė: %lu baitų (%lu "
+"ms).\n"
+"Greičiausiai, tai yra klaida ALSA \"'%s\" tvarkyklėje. Prašome apie šią "
+"klaidą pranešti ALSA kūrėjams."
+
+#: ../src/modules/alsa/alsa-util.c:1218
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() grąžino reikšmę, kuri yra išskirtinai didelė: %li baitų (%s"
+"%lu ms).\n"
+"Greičiausiai, tai yra klaida ALSA \"'%s\" tvarkyklėje. Prašome apie šią "
+"klaidą pranešti ALSA kūrėjams."
+
+#: ../src/modules/alsa/alsa-util.c:1259
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() grąžino keistas reikšmes: delsa %lu yra mažesnė, nei "
+"prieinama %lu.\n"
+"Greičiausiai, tai yra klaida ALSA \"'%s\" tvarkyklėje. Prašome apie šią "
+"klaidą pranešti ALSA kūrėjams."
+
+#: ../src/modules/alsa/alsa-util.c:1302
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() grąžino reikšmę, kuri yra išskirtinai didelė: %lu baitų "
+"(%lu ms).\n"
+"Greičiausiai, tai yra klaida ALSA \"'%s\" tvarkyklėje. Prašome apie šią "
+"klaidą pranešti ALSA kūrėjams."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1740
+msgid "Headset"
+msgstr "Ausinės su mikrofonu"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1745
+msgid "Handsfree"
+msgstr "Laisvų rankų įranga"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1763
+msgid "Headphone"
+msgstr "Ausinė"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1768
+msgid "Portable"
+msgstr "Portatyvi sistema"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1773
+msgid "Car"
+msgstr "Automobilis"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1778
+msgid "HiFi"
+msgstr "HiFi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2134
+#: ../src/modules/bluetooth/module-bluez5-device.c:1783
+msgid "Phone"
+msgstr "Telefonas"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2142
+#: ../src/modules/bluetooth/module-bluez5-device.c:1735
+#: ../src/modules/bluetooth/module-bluez5-device.c:1751
+#: ../src/modules/bluetooth/module-bluez5-device.c:1789
+msgid "Bluetooth Output"
+msgstr "Bluetooth išvestis"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2145
+#: ../src/modules/bluetooth/module-bluez5-device.c:1734
+#: ../src/modules/bluetooth/module-bluez5-device.c:1756
+#: ../src/modules/bluetooth/module-bluez5-device.c:1762
+#: ../src/modules/bluetooth/module-bluez5-device.c:1788
+msgid "Bluetooth Input"
+msgstr "Bluetooth įvestis"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2186
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Aukštos kokybės atkūrimas (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2198
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Aukštos kokybės paėmimas (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2210
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telefoninė dvipusė (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2223
+msgid "Handsfree Gateway"
+msgstr "Laisvų rankų įrangos tinklų sietuvas"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1830
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Aukštos kokybės atkūrimas (A2DP rinktuvas)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1842
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Aukštos kokybės paėmimas (A2DP šaltinis)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1854
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Ausinių su mikrofonu pagrindinis įtaisas (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1867
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Ausinių su mikrofonu garso tinklų sietuvas (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<šaltinio pavadinimas> source_properties=<šaltinio savybės> "
+"source_master=<šaltinio, kurį filtruoti, pavadinimas> sink_name=<rinktuvo "
+"pavadinimas> sink_properties=<rinktuvo savybės> sink_master=<rinktuvo, kurį "
+"filtruoti, pavadinimas> adjust_time=<kaip dažnai reguliuoti dažnius, reikšmė "
+"sekundėmis> adjust_threshold=<dreifo trukmė milisekundemis, kurią reikėtų "
+"sureguliuoti> format=<ėminio formatas> rate=<skaitmeninimo dažnis> "
+"channels=<kanalų skaičius> channel_map=<kanalų schema> "
+"aec_method=<įgyvendinimas, kurį naudoti> aec_args=<parametrai, skirti AEC "
+"moduliui> save_aec=<įrašyti AEC duomenis į /tmp> autoloaded=<nustatyti ar "
+"šis modulis bus įkeliamas automatiškai> use_volume_sharing=<yes arba no> "
+"use_master_format=<yes arba no> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "Įjungta"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Fiktyvi išvestis"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Visada palieka įkeltą bent vieną rinktuvą, netgi jei tai yra nulinis"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Universalusis glodintuvas"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<rinktuvo pavadinimas> sink_properties=<rinktuvo savybės> "
+"sink_master=<rinktuvas prie kurio jungtis> format=<ėminio formatas> "
+"rate=<skaitmeninimo dažnis> channels=<kanalų skaičius> channel_map=<kanalų "
+"schema> autoloaded=<nustatyti ar šis modulis bus įkeliamas automatiškai> "
+"use_volume_sharing=<yes arba no> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<ar automatiškai iškelti nenaudojamus filtrus?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Virtualus LADSPA rinktuvas"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<input channel map> plugin=<ladspa plugin name> label=<ladspa "
+"plugin label> control=<comma separated list of input control values> "
+"input_ladspaport_map=<comma separated list of input LADSPA port names> "
+"output_ladspaport_map=<comma separated list of output LADSPA port names> "
+"autoloaded=<set if this module is being loaded automatically> "
+msgstr ""
+"sink_name=<rinktuvo pavadinimas> sink_properties=<rinktuvo savybės> "
+"master=<rinktuvo, kurį filtruoti, pavadinimas> sink_master=<rinktuvo, kurį "
+"filtruoti, pavadinimas> format=<ėminio formatas> rate=<skaitmeninimo dažnis> "
+"channels=<kanalų skaičius> channel_map=<įvesties kanalų schema> "
+"plugin=<ladspa įskiepio pavadinimas> label=<ladspa įskiepio etiketė> "
+"control=<kableliais atskirtų įvesties valdymo reikšmių sąrašas> "
+"input_ladspaport_map=<kableliais atskirtų LADSPA įvesties prievadų "
+"pavadinimų sąrašas> output_ladspaport_map=<kableliais atskirtų LADSPA "
+"išvesties prievadų pavadinimų sąrašas> autoloaded=<nustatyti ar šis modulis "
+"bus įkeliamas automatiškai> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Sinchroninis tuščiasis rinktuvas"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Nulinė išvestis"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Išvesties įrenginiai"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Įvesties įrenginiai"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Garsas ties @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Tunelis, skirtas %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:521
+#: ../src/modules/module-tunnel-source-new.c:520
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Tunelis į %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Virtualus erdvinis rinktuvas"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<channel map> use_volume_sharing=<yes or no> "
+"force_flat_volume=<yes or no> hrir=/path/to/left_hrir.wav autoloaded=<set if "
+"this module is being loaded automatically> "
+msgstr ""
+"sink_name=<rinktuvo pavadinimas> sink_properties=<rinktuvo savybės> "
+"master=<rinktuvo, kurį filtruoti, pavadinimas> sink_master=<rinktuvo, kurį "
+"filtruoti, pavadinimas> format=<ėminio formatas> rate=<skaitmeninimo dažnis> "
+"channels=<kanalų skaičius> channel_map=<kanalų schema> "
+"use_volume_sharing=<yes arba no> force_flat_volume=<yes arba no> hrir=/"
+"kelias/iki/left_hrir.wav autoloaded=<nustatyti ar šis modulis bus įkeliamas "
+"automatiškai> "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio garso serveris"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Priekinė centrinė"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Priekinė kairioji"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Priekinė dešinioji"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Galinė centrinė"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Galinė kairioji"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Galinė dešinioji"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Po-žemadažnis garsiakalbis"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Priekinė kairioji nuo vidurio"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Priekinė dešinioji nuo vidurio"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Šoninė kairioji"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Šoninė dešinioji"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Pagalbinė 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Pagalbinė 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Pagalbinė 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Pagalbinė 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Pagalbinė 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Pagalbinė 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Pagalbinė 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Pagalbinė 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Pagalbinė 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Pagalbinė 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Pagalbinė 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Pagalbinė 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Pagalbinė 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Pagalbinė 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Pagalbinė 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Pagalbinė 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Pagalbinė 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Pagalbinė 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Pagalbinė 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Pagalbinė 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Pagalbinė 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Pagalbinė 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Pagalbinė 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Pagalbinė 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Pagalbinė 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Pagalbinė 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Pagalbinė 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Pagalbinė 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Pagalbinė 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Pagalbinė 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Pagalbinė 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Pagalbinė 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Viršutinė centrinė"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Viršutinė priekinė centrinė"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Viršutinė priekinė kairioji"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Viršutinė priekinė dešinioji"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Viršutinė galinė centrinė"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Viršutinė galinė kairioji"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Viršutinė galinė dešinioji"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:177 ../src/pulse/volume.c:306
+#: ../src/pulse/volume.c:332 ../src/pulse/volume.c:352
+#: ../src/pulse/volume.c:384 ../src/pulse/volume.c:424
+#: ../src/pulse/volume.c:443
+msgid "(invalid)"
+msgstr "(neteisinga)"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Erdvinė 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Erdvinė 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Erdvinė 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Erdvinė 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Erdvinė 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() nepavyko"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() grąžino reikšmę \"true\""
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Nepavyko analizuoti slapuko duomenų"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Gautas pranešimas nežinomam plėtiniui \"%s\""
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "įvestis"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "išvestis"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "dvikryptė"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "neteisinga"
+
+#: ../src/pulsecore/core-util.c:1856
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"XDG_RUNTIME_DIR (%s) savininkais esame ne mes (uid %d), o uid %d! (Tai gali "
+"nutikti, jeigu, pvz., jūs per savą protokolą, kaip pagrindinis (root) "
+"naudotojas bandote prisijungti prie ne pagrindinio (non-root) naudotojo "
+"vykdomo PulseAudio. Taip nedarykite.)"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "taip"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "ne"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Nepavyksta gauti prieigos prie automatinio darbo atnaujinimo užrakto."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Nepavyko atverti paskirties failo \"%s\"."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Bandyta atverti paskirties failus \"%s\", \"%s.1\", \"%s.2\" ... \"%s.%d\", "
+"bet nei vieno nepavyko."
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Neteisinga žurnalo paskirtis."
+
+#: ../src/pulsecore/sink.c:3466
+msgid "Built-in Audio"
+msgstr "Įtaisytas garsas"
+
+#: ../src/pulsecore/sink.c:3471
+msgid "Modem"
+msgstr "Modemas"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "Gerai"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Prieiga negalima"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Nežinoma komanda"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Neteisingas argumentas"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "Objektas yra"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Tokio objekto nėra"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Sujungimas atmestas"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Protokolo klaida"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Skirtojo laiko pabaiga"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Nėra tapatybės nustatymo rakto"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Vidinė klaida"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Sujungimas nutrauktas"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Objektas sunaikintas"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Neteisingas serveris"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Modulio inicijavimas nepavyko"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Bloga būsena"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Nėra duomenų"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Nesuderinama protokolo versija"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Pernelyg didelis"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Nepalaikoma"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Nežinomas klaidos kodas"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Tokio plėtinio nėra"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Pasenęs funkcionalumas"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Trūksta realizacijos"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Klientas atšakotas"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Įvesties/Išvesties klaida"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Įrenginys ar išteklius užimtas"
+
+#: ../src/pulse/sample.c:179
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %ukan. %uHz"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:197
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:134
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Nepavyko nutekinti srauto: %s"
+
+#: ../src/utils/pacat.c:139
+msgid "Playback stream drained."
+msgstr "Atkūrimo srautas nutekintas."
+
+#: ../src/utils/pacat.c:150
+msgid "Draining connection to server."
+msgstr "Nutekinamas ryšys su serveriu."
+
+#: ../src/utils/pacat.c:163
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:194 ../src/utils/pacat.c:543
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() nepavyko: %s"
+
+#: ../src/utils/pacat.c:244 ../src/utils/pacat.c:274
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() nepavyko: %s"
+
+#: ../src/utils/pacat.c:324
+msgid "Stream successfully created."
+msgstr "Srautas sėkmingai sukurtas."
+
+#: ../src/utils/pacat.c:327
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() nepavyko: %s"
+
+#: ../src/utils/pacat.c:331
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Buferio metrika: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:334
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Buferio metrika: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:338
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Naudojama ėminio specifikacija \"%s\", kanalų schema \"%s\"."
+
+#: ../src/utils/pacat.c:342
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Prisijungta prie įrenginio %s (indeksas: %u, pristabdyta: %s)."
+
+#: ../src/utils/pacat.c:352
+#, c-format
+msgid "Stream error: %s"
+msgstr "Srauto klaida: %s"
+
+#: ../src/utils/pacat.c:362
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Srauto įrenginys pristabdytas.%s"
+
+#: ../src/utils/pacat.c:364
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Srauto įrenginys pratęstas.%s"
+
+#: ../src/utils/pacat.c:372
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Srauto ištuštėjimas. %s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Srauto perpildymas. %s"
+
+#: ../src/utils/pacat.c:386
+#, c-format
+msgid "Stream started.%s"
+msgstr "Srautas paleistas.%s"
+
+#: ../src/utils/pacat.c:393
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Srautas perkeltas į įrenginį %s (%u, %spristabdytas).%s"
+
+#: ../src/utils/pacat.c:393
+msgid "not "
+msgstr "ne "
+
+#: ../src/utils/pacat.c:400
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Pasikeitė srauto buferio požymiai.%s"
+
+#: ../src/utils/pacat.c:415
+msgid "Cork request stack is empty: corking stream"
+msgstr "Kamščių užklausos dėklas yra tuščas: užkemšamas srautas"
+
+#: ../src/utils/pacat.c:421
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Kamščių užklausos dėklas yra tuščas: atkemšamas srautas"
+
+#: ../src/utils/pacat.c:425
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr "Įspėjimas: Gauta daugiau atkimšimo užklausų nei užkimšimo."
+
+#: ../src/utils/pacat.c:450
+#, c-format
+msgid "Connection established.%s"
+msgstr "Ryšys užmegztas.%s"
+
+#: ../src/utils/pacat.c:453
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() nepavyko: %s"
+
+#: ../src/utils/pacat.c:491
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() nepavyko: %s"
+
+#: ../src/utils/pacat.c:497
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Nepavyko nustatytį monitorinį srautą: %s"
+
+#: ../src/utils/pacat.c:501
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() nepavyko %s"
+
+#: ../src/utils/pacat.c:514 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Sujungimo nesėkmė: %s"
+
+#: ../src/utils/pacat.c:557
+msgid "Got EOF."
+msgstr "Gauta EOF."
+
+#: ../src/utils/pacat.c:581
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() nepavyko: %s"
+
+#: ../src/utils/pacat.c:605
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() nepavyko: %s"
+
+#: ../src/utils/pacat.c:626
+msgid "Got signal, exiting."
+msgstr "Gautas signalas, išeinama."
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Nepavyko gauti delsos: %s"
+
+#: ../src/utils/pacat.c:645
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Laikas: %0.3f sek.; Delsa: %0.0f mikrosek."
+
+#: ../src/utils/pacat.c:666
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() nepavyko: %s"
+
+#: ../src/utils/pacat.c:676
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [parametrai]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Rodyti šią pagalbą\n"
+"      --version                         Rodyti versiją\n"
+"\n"
+"  -r, --record                          Sukurti ryšį, skirtą įrašymui\n"
+"  -p, --playback                        Sukurti ryšį, skirtą atkūrimui\n"
+"\n"
+"  -v, --verbose                         Įjungti plačias operacijas\n"
+"\n"
+"  -s, --server=SERVERIS                   Serverio, prie kurio jungtis, "
+"pavadinimas\n"
+"  -d, --device=ĮRENGINYS                   Rinktuvo/šaltinio, prie kurio "
+"jungtis, pavadinimas\n"
+"  -n, --client-name=PAVADINIMAS                Kaip vadinti šį klientą "
+"serveryje\n"
+"      --stream-name=PAVADINIMAS                Kaip vadinti šį srautą "
+"serveryje\n"
+"      --volume=GARSIS                   Nurodyti pradinį (linijinį) garsį, "
+"rėžyje 0...65536\n"
+"      --rate=SKAITMENINIMO_DAŽNIS                 Skaitmeninimo dažnis, Hz "
+"(numatytasis yra 44100)\n"
+"      --format=ĖMINIO_FORMATAS             Ėminio tipas, vienas iš s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (numatytasis yra "
+"s16ne)\n"
+"      --channels=KANALAI               Kanalų skaičius, 1 — mono, 2 — "
+"stereo\n"
+"                                        (numatytasis yra 2)\n"
+"      --channel-map=KANALŲ_SCHEMA          Kanalų schema, naudojama vietoj "
+"numatytosios\n"
+"      --fix-format                      Paimti ėminio formatą iš rinktuvo/"
+"šaltinio, prie kurio yra\n"
+"                                        prijungtas srautas.\n"
+"      --fix-rate                        Paimti skaitmeninimo dažnį iš "
+"rinktuvo/šaltinio, prie kurio yra\n"
+"                                        prijungtas srautas.\n"
+"      --fix-channels                    Paimti kanalų skaičių ir kanalų "
+"schemą iš rinktuvo/šaltinio,\n"
+"                                        prie kurio yra prijungtas srautas.\n"
+"      --no-remix                        Nemaišyti kanalų.\n"
+"      --no-remap                        Vietoj pavadinimo, atvaizduoti "
+"kanalus pagal indeksą.\n"
+"      --latency=BAITAI                   Užklausti nurodytą delsą baitais.\n"
+"      --process-time=BAITAI              Užklausti užklausai skirtą nurodytą "
+"proceso laiką baitais.\n"
+"      --latency-msec=MILISEK.               Užklausti nurodytą delsą "
+"milisekundėmis.\n"
+"      --process-time-msec=MILISEK.          Užklausti užklausai skirtą "
+"nurodytą proceso laiką milisekundėmis.\n"
+"      --property=SAVYBĖ=REIKŠMĖ         Nustatyti nurodytą savybę į nurodytą "
+"reikšmę.\n"
+"      --raw                             Įrašyti/atkurti neapdorotus PCM "
+"duomenis.\n"
+"      --passthrough                     Perduoti duomenis.\n"
+"      --file-format[=FAILO_FORMATAS]           Įrašyti/atkurti formatuotus "
+"PCM duomenis.\n"
+"      --list-file-formats               Išvardyti prieinamus failo "
+"formatus.\n"
+"      --monitor-stream=INDEKSAS            Įrašyti iš rinktuvo įvesties su "
+"indeksu INDEKSAS.\n"
+
+#: ../src/utils/pacat.c:793
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr "PulseAudio garso serveryje atkurti užkoduotus garso failus."
+
+#: ../src/utils/pacat.c:797
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr ""
+"Pagauti garso duomenis iš PulseAudio garso serverio ir įrašyti juos į failą."
+
+#: ../src/utils/pacat.c:801
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+"Pagauti garso duomenis iš PulseAudio garso serverio ir įrašyti juos į STDOUT "
+"ar nurodytą failą."
+
+#: ../src/utils/pacat.c:805
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+"Atkurti garso duomenis PulseAudio garso serveryje iš STDIN ar nurodyto failo."
+
+#: ../src/utils/pacat.c:819
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Sukompiliuota su libpulse %s\n"
+"Susieta su libpulse %s\n"
+
+#: ../src/utils/pacat.c:852 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Neteisingas kliento pavadinimas \"%s\""
+
+#: ../src/utils/pacat.c:867
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Neteisingas srauto pavadinimas \"%s\""
+
+#: ../src/utils/pacat.c:904
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Neteisinga kanalų schema \"'%s\""
+
+#: ../src/utils/pacat.c:933 ../src/utils/pacat.c:947
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Neteisinga delsos specifikacija \"%s\""
+
+#: ../src/utils/pacat.c:940 ../src/utils/pacat.c:954
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Neteisinga proceso laiko specifikacija \"%s\""
+
+#: ../src/utils/pacat.c:966
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Neteisinga savybė \"%s\""
+
+#: ../src/utils/pacat.c:985
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Nežinomas failo formatas %s."
+
+#: ../src/utils/pacat.c:1000
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Nepavyko analizuoti argumentą, skirtą --monitor-stream"
+
+#: ../src/utils/pacat.c:1011
+msgid "Invalid sample specification"
+msgstr "Neteisinga ėminio specifikacija"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1026
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1033
+msgid "Too many arguments."
+msgstr "Pernelyg daug argumentų."
+
+#: ../src/utils/pacat.c:1044
+msgid "Failed to generate sample specification for file."
+msgstr "Nepavyko failui sukurti ėminio specifikaciją."
+
+#: ../src/utils/pacat.c:1070
+msgid "Failed to open audio file."
+msgstr "Nepavyko atverti garso failo."
+
+#: ../src/utils/pacat.c:1076
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Įspėjimas: nurodyta ėminio specifikacija bus perrašyta specifikacija iš "
+"failo."
+
+#: ../src/utils/pacat.c:1079 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Nepavyko iš failo nustatyti ėminio specifikaciją."
+
+#: ../src/utils/pacat.c:1088
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Įspėjimas: Nepavyko nustatyti kanalų schemos iš failo."
+
+#: ../src/utils/pacat.c:1099
+msgid "Channel map doesn't match sample specification"
+msgstr "Kanalų schema neatitinka ėminio specifikacijos"
+
+#: ../src/utils/pacat.c:1110
+msgid "Warning: failed to write channel map to file."
+msgstr "Įspėjimas: nepavyko įrašyti kanalų schemos į failą."
+
+#: ../src/utils/pacat.c:1125
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Atveriamas srautas %s su \"%s\" ėminio specifikacija ir \"%s\" kanalų schema."
+
+#: ../src/utils/pacat.c:1126
+msgid "recording"
+msgstr "įrašymas"
+
+#: ../src/utils/pacat.c:1126
+msgid "playback"
+msgstr "atkūrimas"
+
+#: ../src/utils/pacat.c:1150
+msgid "Failed to set media name."
+msgstr "Nepavyko nustatyti laikmenos pavadinimo."
+
+#: ../src/utils/pacat.c:1160 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() nepavyko."
+
+#: ../src/utils/pacat.c:1183
+msgid "io_new() failed."
+msgstr "io_new() nepavyko."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() nepavyko."
+
+#: ../src/utils/pacat.c:1198 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() nepavyko: %s"
+
+#: ../src/utils/pacat.c:1204
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() nepavyko."
+
+#: ../src/utils/pacat.c:1211 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() nepavyko."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "PAVADINIMAS [ARGUMENTAI ...]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "PAVADINIMAS|#NUMERIS"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "PAVADINIMAS"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "PAVADINIMAS|#NUMERIS GARSIS"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#NUMERIS GARSIS"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "PAVADINIMAS|#NUMERIS 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#NUMERIS 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "PAVADINIMAS|#NUMERIS RAKTAS=REIKŠMĖ"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#NUMERIS RAKTAS=REIKŠMĖ"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#NUMERIS"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "PAVADINIMAS RINKTUVAS|#NUMERIS"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "PAVADINIMAS FAILO_PAVADINIMAS"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "KELIAS"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "FAILO_PAVADINIMAS RINKTUVAS|#NUMERIS"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#NUMERIS RINKTUVAS|ŠALTINIS"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "PLOKŠTĖS PROFILIS"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "PAVADINIMAS|#NUMERIS PRIEVADAS"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "PLOKŠTĖS-PAVADINIMAS|PLOKŠTĖS-#NUMERIS PRIEVADAS POSLINKIS"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "PASKIRTIS"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "SKAITINIS-LYGIS"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "KADRAI"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Rodyti šią pagalbą\n"
+"      --version                         Rodyti versiją\n"
+"Kai nenurodyta jokia komanda, pacmd paleidžiama interaktyvioje veiksenoje.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Sukompiliuota su libpulse %s\n"
+"Susieta su libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "PulseAudio tarnyba nevykdoma arba nevykdoma kaip seanso tarnyba."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Nepavyko nutraukti PulseAudio tarnybos."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Tarnyba neatsako."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Nepavyko gauti statistikos: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Currently in use: %u blocks containing %s bytes total.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Paskirstyta per visą gyvavimo trukmę: %u blokų, kuriuose iš viso yra %s "
+"baitų.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Ėminių podėlio dydis: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Nepavyko gauti serverio informacijos: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Serverio eilutė: %s\n"
+"Bibliotekos protokolo versija: %u\n"
+"Serverio protokolo versija: %u\n"
+"Yra vietinis: %s\n"
+"Kliento indeksas: %u\n"
+"Plytelės dydis: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Naudotojo vardas: %s\n"
+"Kompiuterio pavadinimas: %s\n"
+"Serverio pavadinimas: %s\n"
+"Serverio versija: %s\n"
+"Numatytoji ėminio specifikacija: %s\n"
+"Numatytoji kanalų schema: %s\n"
+"Numatytasis rinktuvas: %s\n"
+"Numatytasis šaltinis: %s\n"
+"Slapukas: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Nepavyko gauti rinktuvo informacijos: %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Rinktuvas Nr.%u\n"
+"\tBūsena: %s\n"
+"\tPavadinimas: %s\n"
+"\tAprašas: %s\n"
+"\tTvarkyklė: %s\n"
+"\tĖminio specifikacija: %s\n"
+"\tKanalų schema: %s\n"
+"\tModulis savininkas: %u\n"
+"\tNutildyti: %s\n"
+"\tGarsis: %s\n"
+"\t        balansas %0.2f\n"
+"\tBazinis garsis: %s\n"
+"\tStebėjimo šaltinis: %s\n"
+"\tDelsa: %0.0f milisek., konfigūruota %0.0f milisek.\n"
+"\tVėliavėlės: %s%s%s%s%s%s%s\n"
+"\tSavybės:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPrievadai:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tAktyvus prievadas: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tFormatai:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Nepavyko gauti šaltinio informacijos: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Šaltinis Nr.%u\n"
+"\tBūsena: %s\n"
+"\tPavadinimas: %s\n"
+"\tAprašas: %s\n"
+"\tTvarkyklė: %s\n"
+"\tĖminio specifikacija: %s\n"
+"\tKanalų schema: %s\n"
+"\tModulis savininkas: %u\n"
+"\tNutildyti: %s\n"
+"\tGarsis: %s\n"
+"\t        balansas %0.2f\n"
+"\tBazinis garsis: %s\n"
+"\tRinktuvo stebėjimas: %s\n"
+"\tDelsa: %0.0f milisek., konfigūruota %0.0f milisek.\n"
+"\tVėliavėlės: %s%s%s%s%s%s\n"
+"\tSavybės:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "nėra"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Nepavyko gauti modulio informacijos: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modulis #%u\n"
+"\tPavadinimas: %s\n"
+"\tArgumentas: %s\n"
+"\tNaudojimo skaitiklis: %s\n"
+"\tSavybės:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Nepavyko gauti kliento informacijos: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Klientas #%u\n"
+"\tTvarkyklė: %s\n"
+"\tModulis savininkas: %s\n"
+"\tSavybės:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Nepavyko gauti plokštės informacijos: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Plokštė #%u\n"
+"\tPavadinimas: %s\n"
+"\tTvarkyklė: %s\n"
+"\tModulis-savininkas: %s\n"
+"\tSavybės:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfiliai:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr ""
+"\t\t%s: %s (rinktuvų: %u, šaltinių: %u, pirmenybė: %u, prieinama: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tAktyvus profilis: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tSavybės:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tProfilio(-ių) dalis: %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Nepavyko gauti rinktuvo įvesties informacijos: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Rinktuvo įvestis Nr.%u\n"
+"\tTvarkyklė: %s\n"
+"\tModulis savininkas: %s\n"
+"\tKlientas: %s\n"
+"\tRinktuvas: %u\n"
+"\tĘminio specifikacija: %s\n"
+"\tKanalų schema: %s\n"
+"\tFormatas: %s\n"
+"\tUžkimštas: %s\n"
+"\tNutildyti: %s\n"
+"\tGarsis: %s\n"
+"\t        balansas %0.2f\n"
+"\tBuferio delsa: %0.0f milisek.\n"
+"\tRinktuvo delsa: %0.0f milisek.\n"
+"\tĖminių keitimo metodas: %s\n"
+"\tSavybės:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Nepavyko gauti šaltinio išvesties informacijos: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Šaltinio išvestis Nr.%u\n"
+"\tTvarkyklė: %s\n"
+"\tModulis savininkas: %s\n"
+"\tKlientas: %s\n"
+"\tŠaltinis: %u\n"
+"\tĖminio specifikacija: %s\n"
+"\tKanalų schema: %s\n"
+"\tFormatas: %s\n"
+"\tUžkimštas: %s\n"
+"\tNutildyti: %s\n"
+"\tGarsis: %s\n"
+"\t        balansas %0.2f\n"
+"\tBuferio delsa: %0.0f milisek.\n"
+"\tŠaltinio delsa: %0.0f milisek.\n"
+"\tĖminių keitimo metodas: %s\n"
+"\tSavybės:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Nepavyko gauti ėminio informacijos: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Ėminys Nr.%u\n"
+"\tPavadinimas: %s\n"
+"\tĖminio specifikacija: %s\n"
+"\tKanalų schema: %s\n"
+"\tGarsis: %s\n"
+"\t        balansas %0.2f\n"
+"\tTrukmė: %0.1fs\n"
+"\tDydis: %s\n"
+"\tTingus: %s\n"
+"\tFailo pavadinimas: %s\n"
+"\tSavybės:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Triktis: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Nepavyko iškelti modulio: Modulis %s nėra įkeltas"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Nepavyko nustatyti garsio: Jūs bandėte nustatyti garsį %d kanalams, tuo "
+"tarpu yra palaikomi %d kanalai\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Nepavyko nustatyti formato: neteisinga formato eilutė %s"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Nepavyko įkelti ėminio: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Priešlaikinė failo pabaiga"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "nauja"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "pakeisti"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "šalinti"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "nežinoma"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "rinktuvas"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "šaltinis"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "rinktuvo-įvestis"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "šaltinio-išvestis"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "modulis"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "klientas"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "ėminių-podėlis"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "serveris"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "plokštė"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Įvykis \"%s\" ties %s Nr.%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "Gautas SIGINT, išeinama."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Neteisinga garsio specifikacija"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Garsis už leidžiamų ribų diapazono.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "Neteisingas garsio specifikacijų skaičius.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "Nesuderinama garsio specifikacija.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[parametrai]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[TIPAS]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "FAILO_PAVADINIMAS [PAVADINIMAS]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "PAVADINIMAS [RINKTUVAS]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "PAVADINIMAS|#NUMERIS GARSIS [GARSIS ...]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#NUMERIS GARSIS [GARSIS ...]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "PAVADINIMAS|#NUMERIS 1|0|toggle"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#NUMERIS 1|0|toggle"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "#NUMERIS FORMATAI"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Specialūs pavadinimai @DEFAULT_SINK@, @DEFAULT_SOURCE@ ir @DEFAULT_MONITOR@\n"
+"gali būti naudojami, norint nurodyti numatytąjį rinktuvą, šaltinį ir "
+"monitorių.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Rodyti šią pagalbą\n"
+"      --version                         Rodyti versiją\n"
+"\n"
+"  -s, --server=SERVER                   Serverio, prie kurio jungtis, "
+"pavadinimas\n"
+"  -n, --client-name=NAME                Kaip vadinti šį, serveryje esantį, "
+"klientą\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Sukompiliuota su libpulse %s\n"
+"Susieta su libpulse %s\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Nieko nenurodykite arba nurodykite vieną iš: %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Prašome nurodyti ėminio failą, kurį įkelti"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Nepavyko atverti garso failo."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Įspėjimas: Nepavyko iš failo nustatyti ėminio specifikacijos."
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Jūs turite nurodyti ėminio, kurį groti, pavadinimą"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Jūs turite nurodyti ėminio, kurį šalinti, pavadinimą"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "Jūs turite nurodyti rinktuvo įvesties indeksą ir rinktuvą"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "Jūs turite nurodyti šaltinio išvesties indeksą ir šaltinį"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Jūs turite nurodyti modulio pavadinimą ir argumentus."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "Jūs turite nurodyti modulio indeksą ar pavadinimą"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Jūs negalite nurodyti daugiau kaip vieną rinktuvą. Turite nurodyti loginę "
+"reikšmę."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Neteisinga pristabdymo specifikacija."
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Jūs negalite nurodyti daugiau kaip vieną šaltinį. Turite nurodyti loginę "
+"reikšmę."
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Jūs turite nurodyti plokštės pavadinimą/indeksą ir profilio pavadinimą"
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Jūs turite nurodyti rinktuvo pavadinimą/indeksą ir prievado pavadinimą"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "Jūs turite nurodyti rinktuvo pavadinimą"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "Jūs turite nurodyti šaltinio pavadinimą/indeksą ir prievado pavadinimą"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Jūs turite nurodyti šaltinio pavadinimą"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Jūs turite nurodyti rinktuvo pavadinimą/indeksą ir garsį"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Jūs turite nurodyti šaltinio pavadinimą/indeksą ir garsį"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "Jūs turite nurodyti rinktuvo įvesties indeksą ir garsį"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "Neteisingas rinktuvo įvesties indeksas"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "Jūs turite nurodyti šaltinio išvesties indeksą ir garsį"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Neteisingas šaltinio išvesties indeksas"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Jūs turite nurodyti rinktuvo pavadinimą/indeksą ir nutildymo veiksmą (0, 1 "
+"arba \"toggle\")"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Neteisinga nutildymo specifikacija"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Jūs turite nurodyti šaltinio pavadinimą/indeksą ir nutildymo veiksmą (0, 1 "
+"arba \"toggle\")"
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Jūs turite nurodyti rinktuvo įvesties indeksą ir nutildymo veiksmą (0, 1 "
+"arba \"toggle\")"
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "Neteisinga rinktuvo įvesties indekso specifikacija"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+"Jūs turite nurodyti šaltinio išvesties indeksą ir nutildymo veiksmą (0, 1 "
+"arba \"toggle\")"
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Neteisinga šaltinio išvesties indekso specifikacija"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Jūs turite nurodyti rinktuvo indeksą ir kabliataškiais atskirtų palaikomų "
+"formatų sąrašą"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr ""
+"Jūs turite nurodyti plokštės pavadinimą/indeksą, prievado pavadinimą ir "
+"delsos poslinkį"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Nepavyko analizuoti delsos poslinkio"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Nenurodyta taisyklinga komanda."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Nepavyko pratęsti: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Nepavyko pristabdyti: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "ĮSPĖJIMAS: Garso serveris nėra vietinis, nepristabdoma.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Sujungimo nesėkmė: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Gautas SIGINT, išeinama.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "ĮSPĖJIMAS: Vyksnis nutrauktas %u signalo\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [parametrai] ... \n"
+"\n"
+"  -h, --help                            Rodyti šią pagalbą\n"
+"      --version                         Rodyti versiją\n"
+"  -s, --server=SERVER                   Serverio, prie kurio jungtis, "
+"pavadinimas\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Sukompiliuota su libpulse %s\n"
+"Susieta su libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() nepavyko.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() nepavyko.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() nepavyko.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D ekranas] [-S serveris] [-O rinktuvas] [-I šaltinis] [-c failas]  [-d|-"
+"e|-i|-r]\n"
+"\n"
+" -d    Rodyti esamus PulseAudio duomenis, pridėtus prie X11 ekrano (pagal "
+"numatymą)\n"
+" -e    Eksportuoti vietinius PulseAudio duomenis į X11 ekraną\n"
+" -i    Importuoti PulseAudio duomenis iš X11 ekrano į vietinius aplinkos "
+"kintamuosius ir slapuko failą.\n"
+" -r    Šalinti PulseAudio duomenis iš X11 ekrano\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Nepavyko analizuoti komandų eilutės.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Serveris: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Šaltinis: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Rinktuvas: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Slapukas: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Nepavyko analizuoti slapuko duomenų\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Nepavyko įrašyti slapuko duomenų\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Nepavyko gauti FQDN.\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Nepavyko įkelti slapuko duomenų\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Kol kas neįgyvendinta.\n"
+
+#~ msgid ""
+#~ "OK, so you are running PA in system mode. Please note that you most "
+#~ "likely shouldn't be doing that.\n"
+#~ "If you do it nonetheless then it's your own fault if things don't work as "
+#~ "expected.\n"
+#~ "Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+#~ "Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why "
+#~ "system mode is usually a bad idea."
+#~ msgstr ""
+#~ "Gerai, taigi, dabar vykdote PA sistemos veiksenoje. Prašome turėti "
+#~ "omenyje, kad jums tikriausiai, nėra reikalo to daryti.\n"
+#~ "Vis dėlto, jeigu tai darote, visa atsakomybė už visus nesklandumus tenka "
+#~ "jums.\n"
+#~ "Prašome skaityti http://www.freedesktop.org/wiki/Software/PulseAudio/"
+#~ "Documentation/User/WhatIsWrongWithSystemWide/ , kad sužinotumėte, kodėl "
+#~ "sistemos veiksena nėra geras sumanymas."
+
+#~ msgid "%s %s\n"
+#~ msgstr "%s %s\n"
diff --git a/po/ml.po b/po/ml.po
new file mode 100644 (file)
index 0000000..4659d00
--- /dev/null
+++ b/po/ml.po
@@ -0,0 +1,3018 @@
+#
+#  <>, YEAR,ÀÀ²\ 1, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx.ml\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:41+0000\n"
+"Last-Translator: \n"
+"Language-Team:  <en@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() നല്‍കിയ മൂല്ല്യം വളരെ വലുതാണു്: %lu ബൈറ്റുകള്‍ (%lu ms).\n"
+"ഇതു് ALSA ഡ്രൈവര്‍ '%s'-ലുള്ള ഒരു ബഗാവാം. ദയവായി ഈ പ്രശ്നം ALSA ഡവലപ്പര്‍സിനെ അറിയിക്കുക."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() നല്‍കിയ മൂല്ല്യം വളരെ വലുതാണു്: %li ബൈറ്റുകള്‍ (%s%lu ms).\n"
+"ഇതു് ALSA ഡ്രൈവര്‍ '%s'-ലുള്ള ഒരു ബഗാവാം. ദയവായി ഈ പ്രശ്നം ALSA ഡവലപ്പര്‍സിനെ അറിയിക്കുക."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() നല്‍കിയ മൂല്ല്യം വളരെ വലുതാണു്: %lu ബൈറ്റുകള്‍ (%lu ms).\n"
+"ഇതു് ALSA ഡ്രൈവര്‍ '%s'-ലുള്ള ഒരു ബഗാവാം. ദയവായി ഈ പ്രശ്നം ALSA ഡവലപ്പര്‍സിനെ അറിയിക്കുക."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() നല്‍കിയ മൂല്ല്യം വളരെ വലുതാണു്: %lu ബൈറ്റുകള്‍(%lu ms).\n"
+"ഇതു് ALSA ഡ്രൈവര്‍ '%s'-ലുള്ള ഒരു ബഗാവാം. ദയവായി ഈ പ്രശ്നം ALSA ഡവലപ്പര്‍സിനെ അറിയിക്കുക."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "നള്‍ ആണെങ്കിലും ഒരു സിങ്കെങ്കിലും എപ്പോഴും ലഭ്യമാക്കുക"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "ഡമ്മി ഔട്ട്പുട്ട്"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "വിര്‍ച്ച്വല്‍ LADSPA സിങ്ക്"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "ക്ലോക്കഡ് NULL സിങ്ക്"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "നള്‍ ഔട്ട്പുട്ട്"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "ഇന്റേര്‍ണല്‍ ഓഡിയോ"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "മോഡം"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "യഥാര്‍ത്ഥ lt_dlopen ലോഡര്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "പുതിയ dl ലോഡര്‍ അനുവദിക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind-now-loader ചേര്‍ക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "%s സിഗ്നല്‍ ലഭ്യമായി."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "പുറത്തു് കടക്കുന്നു."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "'%s' എന്ന ഉപയോക്താവു് ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "'%s' എന്ന ഗ്രൂപ്പ് ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "ഉപയോക്താവു് '%s' (UID %lu) , ഗ്രൂപ്പ് '%s' (GID %lu) ലഭ്യമായി."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "ഉപയോക്താവു് '%s'-ന്റെയും ഗ്രൂപ്പ് '%s'-ന്റെയും GID ചേരുന്നില്ല."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "ഉപയോക്താവു് '%s'-ന്റെ ഹോം ഡയറക്ടറി '%s' അല്ല, ഉപേക്ഷിക്കുന്നു."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' ഉണ്ടാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "ഗ്രൂപ്പ് ലിസ്റ്റ് മാറ്റുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID മാറ്റുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID മാറ്റുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "റൂട്ട് ആനുകൂല്യങ്ങള്‍ വിജയകരമായി ഉപേക്ഷിച്ചിരിക്കുന്നു."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "ഈ പ്ലാറ്റ്ഫോമില്‍ സിസ്റ്റം വൈഡ് മോഡ് പിന്തുണയ്ക്കുന്നില്ല."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) പരാജയപ്പെട്ടു: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "കമാന്‍ഡ് ലൈന്‍ പാഴ്സ് ചെയ്യുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "ഡെമണ്‍ പ്രവര്‍ത്തനത്തിലില്ല"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "ഡെമണ്‍ PID %u ആയി പ്രവര്‍ത്തിക്കുന്നു"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "ഡെമണ്‍ നശിപ്പിക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"ഈ പ്രോഗ്രാം റൂട്ടായി പ്രവര്‍ത്തിപ്പിക്കേണ്ടതല്ല (--system എന്നു് പറഞ്ഞിട്ടുണ്ടെങ്കില്‍ മാത്രം റൂട്ട് "
+"ആവശ്യമുണ്ടു്)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "റൂട്ട് ആനുകൂല്യങ്ങള്‍ ആവശ്യമുണ്ടു്."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "സിസ്റ്റം ഇന്‍സ്റ്റന്‍സുകള്‍ക്ക് --start പിന്തുണയ്ക്കുന്നില്ല."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "സിസ്റ്റം മോഡില്‍ പ്രവര്‍ത്തിക്കുന്നു, പക്ഷേ --disallow-exit സജ്ജമാക്കിയിട്ടില്ല!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"സിസ്റ്റം മോഡില്‍ പ്രവര്‍ത്തിക്കുന്നു, പക്ഷേ --disallow-module-loading സജ്ജമാക്കിയിട്ടില്ല!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "സിസ്റ്റം മോഡില്‍ പ്രവര്‍ത്തിക്കുന്നു, നിര്‍ബന്ധമായും SHM മോഡ് പ്രവര്‍ത്ത രഹിതമാക്കുന്നു!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"സിസ്റ്റം മോഡില്‍ പ്രവര്‍ത്തിക്കുന്നു, നിര്‍ബന്ധമായും എക്സിറ്റ് ഐഡില്‍ സമയം പ്രവര്‍ത്ത രഹിതമാക്കുന്നു!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdio ലഭിക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe പരാജയപ്പെട്ടു: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "ഡെമണിന്റെ തുടക്കം പരാജയപ്പെട്ടു."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "ഡെമണിന്റെ തുടക്കം വിജയിച്ചു."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "ഇതു് PulseAudio %s ആണു്"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "കംപൈലേഷന്‍ ഹോസ്റ്റ്: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "കംപൈലേഷന്‍ CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "ഹോസ്റ്റില്‍ പ്രവര്‍ത്തിക്കുന്നു: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u സിപിയു ലഭ്യമായി."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "താളിന്റെ വ്യാപ്തി %lu ബൈറ്റുകളാണു്"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Valgrind പിന്തുണയോടെ കംപൈല്‍ ചെയ്തിരിക്കുന്നു: ഉവ്വു്"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Valgrind പിന്തുണയോടെ കംപൈല്‍ ചെയ്തിരിക്കുന്നു: ഇല്ല"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "valgrind മോഡില്‍ പ്രവര്‍ത്തിപ്പിക്കുന്നു: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "ഹോസ്റ്റില്‍ പ്രവര്‍ത്തിക്കുന്നു: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "ഒപ്ടിമൈസ്ഡ് ബിള്‍ഡ്: ഉവ്വു്"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "ഒപ്ടിമൈസ്ഡ് ബിള്‍ഡ്: ഇല്ല"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG നിഷ്കര്‍ഷിച്ചിരിക്കുന്നു, എല്ലാ asserts-ഉം പ്രവര്‍‌ത്ത രഹിതം."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH നിഷ്കര്‍ഷിച്ചിരിക്കുന്നു, fast path asserts മാത്രം പ്രവര്‍‌ത്ത രഹിതം."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "എല്ലാ asserts-ഉം പ്രവര്‍‌ത്ത സജ്ജം"
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "മഷീന്‍ ID ലഭ്യമാക്കുവാന്‍ സാധ്യമായില്ല"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "മഷീന്‍ ID %s ആണു്."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "സെഷന്‍ ID %s ആണു്."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "റണ്‍ടൈം ഡയറക്ടറി %s ഉപയോഗിക്കുന്നു."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "സ്റ്റേറ്റ് ഡയറക്ടറി %s ഉപയോഗിക്കുന്നു."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "മൊഡ്യൂള്‍സ് ഡയറക്ടറി %s ഉപയോഗിക്കുന്നു."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "സിസ്റ്റം മോഡില്‍ പ്രവര്‍ത്തിക്കുന്നു: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"ശരി, അപ്പോള്‍ നിങ്ങള്‍ പിഎ സിസ്റ്റം മോഡിലാണു് പ്രവര്‍ത്തിപ്പിക്കുന്നതു്. ദയവായി ഇതുപേക്ഷിക്കുക.\n"
+"ഇങ്ങനെ ചെയ്തുണ്ടാകുന്ന സകല തകരാറുകളുടേയും ഉത്തരവാദി നിങ്ങള്‍ സ്വയമാകുന്നു.\n"
+"സിസ്റ്റം മോഡിലുള്ള പ്രവര്‍ത്തനം ഉത്തമമല്ലാത്തതിന്റെ കാരണങ്ങള്‍ക്കായിhttp://www.freedesktop."
+"org/wiki/Software/PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ "
+"കാണുക."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() പരാജയപ്പെട്ടു."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "Fresh high-resolution timers available! Bon appetit!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() പരാജയപ്പെട്ടു."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "ഡെമണ്‍ ആരംഭിക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "ഒരു ഘടകങ്ങളും ലഭ്യമാകാതെ ഡെമണ്‍ ആരംഭിച്ചിരിക്കുന്നു, പ്രവര്‍ത്തനം നിഷേധിച്ചിരിക്കുന്നു."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "ഡെമണിന്റെ തുടക്കം പൂര്‍ണ്ണമായി."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "ഡെമണ്‍ അടച്ചുപൂട്ടുന്നതു് ആരംഭിച്ചിരിക്കുന്നു."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "ഡെമണ്‍ നിര്‍ത്തിയിരിക്കുന്നു."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --module-idle-time=SECS           Unload autoloaded modules when idle "
+"and\n"
+"                                        this time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr} Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level-നു് ലോഗ് ലവല്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു് (ഒന്നുകില്‍ 0..4 വരെയുള്ള ന്യൂമറിക് പരിധി "
+"അല്ലെങ്കില്‍ debug, info, notice, warn, error എന്നിവയില്‍ ഒന്നു്)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "തെറ്റായ ലോഗ് ടാര്‍ഗറ്റ്: 'syslog', 'stderr' അല്ലെങ്കില്‍ 'auto' ഉപയോഗിക്കുക."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "തെറ്റായ റീസാംപിള്‍ മാര്‍ഗ്ഗം '%s'."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm-നു് ബൂളിയന്‍ ആര്‍ഗ്യുമെന്റ് ആവശ്യമുണ്ടു്"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "പേരു്: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "ഘടകത്തെപ്പറ്റിയുള്ള വിവരം ലഭ്യമല്ല\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "പതിപ്പു്: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "വിവരണം: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "രചയിതാവു്: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "ഉപയോഗം: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "ഒരിക്കല്‍ ലഭ്യമാക്കുക: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "DEPRECATION WARNING: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "പാഥ്: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] തെറ്റായ ലോഗ് ടാര്‍ഗറ്റ് '%s'."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] തെറ്റായ ലോഗ് ലവല്‍ '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] തെറ്റായ റീസാംപിള്‍ മാര്‍ഗ്ഗം '%s'."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] അസാധുവായ rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] തെറ്റായ സാംപിള്‍ മാതൃക '%s'."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] തെറ്റായ സാംപിള്‍ റേറ്റ് '%s'."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] തെറ്റായ സാംപിള്‍ ചാനലുകള്‍ '%s'."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] അസാധുവായ ചാനല്‍ മാപ്പ് '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] '%s' ഫ്രാഗ്മെന്റുകളുടെ തെറ്റായ എണ്ണം."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] തെറ്റായ ഫ്രാഗ്മെന്റ് വ്യാപ്തി '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] അസാധുവായ nice സ്ഥാനം '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] തെറ്റായ സാംപിള്‍ റേറ്റ് '%s'."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "ക്രമീകരണ ഫയല്‍ തുറക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"വ്യക്തമാക്കിയിട്ടുള്ള സ്വതവേയുള്ള ചാനല്‍ മാപ്പിനുള്ള ചാനലുകളുടെ എണ്ണം നല്‍കിയിരിക്കുന്ന സ്വതവേയുള്ള "
+"ചാനലുകളുടെ എണ്ണത്തേക്കാള്‍ വ്യത്യസ്ഥമാണു്."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### ക്രമീകരണ ഫയലില്‍ നിന്നും ലഭ്യമാക്കുക: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "അനുമതികള്‍ വെടിപ്പാക്കുന്നു."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio സൌണ്ട് സിസ്റ്റം"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "PulseAudio സൌണ്ട് സിസ്റ്റം ആരംഭിക്കുക"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio സൌണ്ട് സിസ്റ്റം"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "PulseAudio സൌണ്ട് സിസ്റ്റം ആരംഭിക്കുക"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "മോണോ"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "മദ്ധ്യം മുന്നില്‍"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "ഇടതു്് മുന്നില്‍"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "വലതു് മുന്നില്‍"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "മദ്ധ്യം അവസാനം"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "ഇടതു് അവസാനം"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "വലതു് അവസാനം"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "മുമ്പില്‍ ഇടതു് മദ്ധ്യം"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "മുമ്പില്‍ വലതു് മദ്ധ്യം"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "ഇടത്തു് വശം"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "വലത്തു് വശം"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "ഓക്സിലറി 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "ഓക്സിലറി 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "ഓക്സിലറി 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "ഓക്സിലറി 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "ഓക്സിലറി 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "ഓക്സിലറി 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "ഓക്സിലറി 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "ഓക്സിലറി 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "ഓക്സിലറി 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "ഓക്സിലറി 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "ഓക്സിലറി 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "ഓക്സിലറി 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "ഓക്സിലറി 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "ഓക്സിലറി 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "ഓക്സിലറി 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "ഓക്സിലറി 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "ഓക്സിലറി 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "ഓക്സിലറി 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "ഓക്സിലറി 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "ഓക്സിലറി 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "ഓക്സിലറി 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "ഓക്സിലറി 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "ഓക്സിലറി 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "ഓക്സിലറി 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "ഓക്സിലറി 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "ഓക്സിലറി 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "ഓക്സിലറി 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "ഓക്സിലറി 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "ഓക്സിലറി 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "ഓക്സിലറി 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "ഓക്സിലറി 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "ഓക്സിലറി 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "മുകളില്‍ മുമ്പില്‍"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "മുകളില്‍ മുമ്പില്‍ മദ്ധ്യം"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "മുകളില്‍ മുമ്പില്‍ ഇടത്തു്"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "മുകളില്‍ മുമ്പില്‍ വലത്തു്"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "മുകളില്‍ അവസാനം മദ്ധ്യം"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "മുകളില്‍ അവസാനം ഇടത്തു്"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "മുകളില്‍ അവസാനം വലത്തു്"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(അസാധു)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "സ്റ്റീരിയോ"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "സറൌണ്ട് 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "സറൌണ്ട് 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "സറൌണ്ട് 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "സറൌണ്ട് 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "സറൌണ്ട് 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "ശരി"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "അനുമതി നിഷേധിച്ചിരിക്കുന്നു"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "അപരിചിതമായ കമാന്‍ഡ്"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "തെറ്റായ ആര്‍ഗ്യുമെന്റ്"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "എന്റിറ്റി നിലവിലുണ്ടു്"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "അത്തരം എന്റിറ്റിയില്ല"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "കണക്ഷന്‍ നിഷേധിച്ചിരിക്കുന്നു"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "പ്രോട്ടോക്കോളില്‍ പിശക്"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "സമയം കഴിഞ്ഞിരിക്കുന്നു"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "ഓഥറൈസേഷന്‍ കീ നിലവിലില്ല"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "ആന്തരിക പിശക്"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "കണക്ഷന്‍ വിഛേദിച്ചിരിക്കുന്നു"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "എന്റിറ്റി ഇല്ലാതാക്കിയിരിക്കുന്നു"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "തെറ്റായ സര്‍വര്‍"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "ഘടകം ആരംഭിക്കുന്നതില്‍ പരാജയപ്പെട്ടു"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "തെറ്റായ അവസ്ഥ"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "ഡേറ്റാ ലഭ്യമല്ല"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "പൊരുത്തപ്പെടാത്ത പ്രോട്ടോക്കോള്‍ പതിപ്പു്"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "വളരെ വലുതു്"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "പിന്തുണ ലഭ്യമല്ല"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "അപരിചിതമായ പിശക് കോഡ്"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "അത്തരം എക്സ്റ്റെന്‍ഷന്‍ ലഭ്യമല്ല"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "ഇല്ലാതാക്കിയ വിശേഷത"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "പ്രവര്‍ത്തനം ലഭ്യമല്ല"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "ക്ലൈന്റ് ഫോര്‍ക്ക് ചെയ്തിരിക്കുന്നു"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "ഇന്‍പുട്ട്/ഔട്ട്പുട്ട് പിശക്"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "ഉപകരണം അല്ലെങ്കില്‍ ഉറവിടം ഉപയോഗത്തില്‍"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "കുക്കി ഡേറ്റാ പാഴ്സ് ചെയ്യുന്നതില്‍ പരാജയപ്പെട്ടു"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "ക്രമീകരണ ഫയല്‍ '%s' തുറക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "ഒരു കുക്കിയും ലഭ്യമല്ല. അതില്ലാതെ കണക്ട് ചെയ്യുവാന്‍‌ ശ്രമിക്കുന്നു."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "അപരിചിതമായ എക്സ്റ്റെന്‍ഷന്‍ '%s'-നുള്ള സന്ദേശം ലഭിച്ചിരിക്കുന്നു"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "സ്ട്രീം ഡ്രെയിന്‍ ചെയ്യുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "പ്ലേബാക്ക് സ്ട്രീം ഡ്രെയിന്‍ ചെയ്തിരിക്കുന്നു."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "സര്‍വറിലേക്കുള്ള കണക്ഷന്‍ ഡ്രെയിന്‍ ചെയ്യുന്നു."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "സ്ട്രീം വിജയകരമായി ഉണ്ടാക്കിയിരിക്കുന്നു."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "ബഫര്‍ മെട്രിക്സ്: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "ബഫര്‍ മെട്രിക്സ്: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "സാംപിള്‍ സ്പെക് '%s', ചാനല്‍ മാപ്പ് '%s' ഉപയോഗിക്കുന്നു."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "ഡിവൈസ് %s-ലേക്ക് കണക്ട് ചെയ്തിരിക്കുന്നു (%u, %ssuspended)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "സ്ട്രീം പിശക്: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "സ്ട്രീം ഡിവൈസ് സസ്പെന്‍ഡ് ചെയ്തിരിക്കുന്നു.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "സ്ട്രീം ഡിവൈസ് വീണ്ടും ആരംഭിച്ചിരിക്കുന്നു.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "സ്ട്രീം അണ്ടര്‍റണ്‍.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "സ്ട്രീ ഓവര്‍റണ്‍.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "സ്ട്രീം ആരംഭിച്ചിരിക്കുന്നു.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "ഡിവൈസ് %s-ലേക്ക് സ്ട്രീം നീക്കം ചെയ്തിരിക്കുന്നു (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "അല്ല"
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "സ്ട്രീം ബഫര്‍ വിശേഷതകള്‍ മാറ്റിയിരിക്കുന്നു.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "കണക്ഷന്‍ സ്ഥാപിച്ചിരിക്കുന്നു.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "കണക്ഷനില്‍ തകരാര്‍: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF ലഭ്യമായിരിക്കുന്നു"
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "സിഗ്നല്‍ ലഭ്യമായി, പുറത്തു് കടക്കുന്നു."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "ലാറ്റന്‍സി ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "സമയം: %0.3f sec; ലാറ്റന്‍സി: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --file-format=FFORMAT             Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"libpulse %s-നൊപ്പം കംപൈല്‍ ചെയ്തിരിക്കുന്നു\n"
+"libpulse %s-നൊപ്പം ലിങ്ക് ചെയ്തിരിക്കുന്നു\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "തെറ്റായ ക്ലൈന്റ് നാമം '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "തെറ്റായ സ്ട്രീം നാമം '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "തെറ്റായ ചാനല്‍ മാപ്പ് '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "തെറ്റായ ലാറ്റന്‍സി വിവരണം '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "തെറ്റായ പ്രക്രിയ സമയ വിവരണം '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "തെറ്റായ വിശേഷത '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "അപരിചിതമായ ഫയല്‍ രീതി %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "തെറ്റായ മാതൃക വിവരണം"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "അനവധി ആര്‍ഗ്യുമെന്റുകള്‍."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "ഫയലിനുള്ള സാംപിള്‍ വിവരണം ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "ഓ‍ഡിയോ ഫയല്‍ തുറക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"മുന്നറിയിപ്പു്: ഫയലില്‍ നിന്നുള്ള വിവരണം വ്യക്തമാക്കിയിരിക്കുന്ന സാംപിള്‍ വിവരണം മാറ്റിയെഴുതുന്നു."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "ഫയലില്‍ നിന്നും സാംപിള്‍ വിവരണം ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "മുന്നറിയിപ്പു്: ഫയലില്‍ നിന്നും ചാനല്‍ മാപ്പ് ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "ചാനല്‍ മാപ്പ് സാംപിള്‍ വിവരണവുമായി ചേരുന്നില്ല"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "മുന്നറിയിപ്പു്: ഫയലിലേക്ക് ചാനല്‍ മാപ്പ് സൂക്ഷിക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "%s സ്ട്രീം തുറക്കുന്നു. ഇതിന്റെ സാംപിള്‍ വിവരണം '%s'-ഉം ചാനല്‍ മാപ്പ് '%s'-ഉം ആണു്."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "റിക്കോര്‍ഡ് ചെയ്യുന്നു"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "പ്ലേബാക്ക്"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "കമാന്‍ഡ് ലൈന്‍ പാഴ്സ് ചെയ്യുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "സസ്പെന്‍ഡ് ചെയ്യുന്നതില്‍ പരാജയപ്പെട്ടു: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "വീണ്ടും ആരംഭിക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "മുന്നറിയിപ്പു്: ശബ്ദ സര്‍വര്‍ ലോക്കലല്ല, സസ്പെന്‍ഡ് ചെയ്യുന്നില്ല.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "കണക്ഷനില്‍ തകരാര്‍: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT ലഭിച്ചു, പുറത്തു് കടക്കുന്നു.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "മുന്നറിയിപ്പു്: %u സിഗ്നല്‍ വഴി ചൈള്‍ പ്രക്രിയ അവസാനിച്ചിരിക്കുന്നു\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"libpulse %s-നൊപ്പം കംപൈല്‍ ചെയ്തിരിക്കുന്നു\n"
+"libpulse %s-നൊപ്പം ലിങ്ക് ചെയ്തിരിക്കുന്നു\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() പരാജയപ്പെട്ടു.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() പരാജയപ്പെട്ടു..\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() പരാജയപ്പെട്ടു.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "സ്ഥിതിവിവരക്കണക്കുകള്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "നിലവില്‍ ഉപയോഗത്തില്‍: %u ബ്ലോക്കുകള്‍, മൊത്തം %s ബൈറ്റുകള്‍ അടങ്ങുന്നു.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "കാലാവധിയ്ക്കുള്ളില്‍ അനുവദിക്കുന്നു: %u ബ്ലോക്കുകള്‍, മൊത്തം %s ബൈറ്റുകള്‍ അടങ്ങുന്നു.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "സാംപിള്‍ കാഷ് വ്യപ്തി: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "സര്‍വര്‍ വിവരങ്ങള്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"User name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "സിങ്ക് വിവരം ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tപോര്‍ട്ടുകള്‍:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tസജീവമായ പോര്‍ട്ട്: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tപോര്‍ട്ടുകള്‍:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "സോഴ്സ് വിവരങ്ങള്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "ഘടക വിവരങ്ങള്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "ക്ലൈന്റ് വിവരങ്ങള്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "കാര്‍ഡ് വിവരങ്ങള്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tപ്രൊഫൈലുകള്‍:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tസജീവമായ പ്രൊഫൈല്‍: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "സിങ്ക് ഇന്‍പുട്ട് വിവരങ്ങള്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "സോഴ്സ് ഔട്ട്പുട്ട് വിവരം ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "മാതൃകയുടെ വിവരങ്ങള്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "പരാജയം: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "സോഴ്സ് വിവരങ്ങള്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "മാതൃക അപ്ലോഡ് ചെയ്യുന്നതില്‍ പരാജയപ്പെട്ടു: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "ഫയല്‍ അനുചിതമായ അവസാനം"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "തെറ്റായ സര്‍വര്‍"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT ലഭ്യമായി, പുറത്തു് കടക്കുന്നു."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "തെറ്റായ വോള്യം വിവരണങ്ങള്‍"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"libpulse %s-നൊപ്പം കംപൈല്‍ ചെയ്തിരിക്കുന്നു\n"
+"libpulse %s-നൊപ്പം ലിങ്ക് ചെയ്തിരിക്കുന്നു\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "ലഭ്യമാക്കുന്നതിലുള്ള മാതൃകാ ഫയല്‍ ദയവായി വ്യക്തമാക്കുക"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "ശബ്ദ ഫയല്‍ തുറക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "മുന്നറിയിപ്പു്: ഫയലില്‍ നിന്നും മാതൃകയുടെ വിവരണം കണ്ടുപിടിക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "പ്രവര്‍ത്തിപ്പിക്കുവാനുള്ള മാതൃകയുടെ പേരു് നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "നീക്കം ചെയ്യുന്നതിനുള്ള മാതൃകയുടെ പേരു് നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "ഒരു സിങ്ക് ഇന്‍പുട്ട് ഇന്‍ഡക്സും സിങ്കും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "ഒരു സോഴ്സ് ഔട്ട്പുട്ട് ഇന്‍ഡക്സും സോഴ്സും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "ഒരു മൌഡ്യൂള്‍ നാമവും ആര്‍ഗ്യുമെന്റുകളും നല്‍കേണ്ടതുണ്ടു്."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "ഒരു മൌഡ്യൂള്‍ ഇന്‍ഡക്സ് നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"ഒരു സിങ്കില്‍ കൂടുതല്‍ നിങ്ങള്‍ നല്‍കേണ്ടതില്ല. കൂടാതെ, ഒരു ബൂളിയന്‍ മൂല്ല്യവും നിങ്ങള്‍ നല്‍കേണ്ടതാണു്."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"ഒരു സോഴ്സില്‍ കൂടുതല്‍ നിങ്ങള്‍ നല്‍കേണ്ടതില്ല. കൂടാതെ, ഒരു ബൂളിയന്‍ മൂല്ല്യവും നിങ്ങള്‍ നല്‍കേണ്ടതാണു്."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "ഒരു കാര്‍ഡ് നാമം/ഇന്‍ഡക്സും പ്രൊഫൈല്‍ നാമവും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "ഒരു സിങ്ക് നാമം/ഇന്‍ഡക്സും പോര്‍ട്ട് നാമവും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "ഒരു സോഴ്സ് നാമം/ഇന്‍ഡക്സും പോര്‍ട്ട് നാമവും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "ഒരു സിങ്ക് നാമം/ഇന്‍ഡക്സും വോള്യവും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "ഒരു സോഴ്സ് നാമം/ഇന്‍ഡക്സും വോള്യവും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "ഒരു സിങ്ക് ഇന്‍പുട്ട് ഇന്‍ഡക്സും വോള്യവും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "തെറ്റായ സിങ്ക് ഇന്‍പുട്ട് ഇന്‍ഡക്സ്"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "ഒരു സോഴ്സ് ഔട്ട്പുട്ട് ഇന്‍ഡക്സും സോഴ്സും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "തെറ്റായ സിങ്ക് ഇന്‍പുട്ട് ഇന്‍ഡക്സ്"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "ഒരു സിങ്ക് നാമം/ഇന്‍ഡക്സും മ്യൂട്ട് ബൂളിയനും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "തെറ്റായ മാതൃക വിവരണം"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "ഒരു സോഴ്സ് നാമം/ഇന്‍ഡക്സും മ്യൂട്ട് ബൂളിയനും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "ഒരു സിങ്ക് ഇന്‍പുട്ട് ഇന്‍ഡക്സും മ്യൂട്ട് ബൂളിയനും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "തെറ്റായ സിങ്ക് ഇന്‍പുട്ട് ഇന്‍ഡക്സ് സ്പെസിഫിക്കേഷന്‍"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "ഒരു സോഴ്സ് നാമം/ഇന്‍ഡക്സും മ്യൂട്ട് ബൂളിയനും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "തെറ്റായ സിങ്ക് ഇന്‍പുട്ട് ഇന്‍ഡക്സ് സ്പെസിഫിക്കേഷന്‍"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "ഒരു സിങ്ക് നാമം/ഇന്‍ഡക്സും മ്യൂട്ട് ബൂളിയനും നല്‍കേണ്ടതുണ്ടു്"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "ശരിയായ കമാന്‍ഡുകള്‍ നല്‍കിയിട്ടില്ല."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "കമാന്‍ഡ് ലൈന്‍ പാഴ്സ് ചെയ്യുന്നതില്‍ പരാജയപ്പെട്ടു.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "സര്‍വര്‍: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "സോഴ്സ്: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "സിങ്ക്: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "കുക്കി: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "കുക്കീ ഡേറ്റാ പാഴ്സ് ചെയ്യുന്നതില്‍ പരാജയപ്പെട്ടു\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "കുക്കീ ഡേറ്റാ സൂക്ഷിക്കുന്നതില്‍ പരാജയപ്പെട്ടു\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "ക്ലൈന്റ് ക്രമീകരണ ഫയല്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "എന്‍വയോണ്മെന്റ് ക്രമീകരണ ഡേറ്റാ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "കുക്കീ ഡേറ്റാ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "ഇതുവരെ ലഭ്യമാക്കിയിട്ടില്ല.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "പള്‍സ്ഓഡിയോ ഡെമണ്‍ പ്രവര്‍ത്തനതിലില്ല, സെഷന്‍ ഡെമണായും പ്രവര്‍ത്തിക്കുന്നില്ല."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "പള്‍സ്ഓഡിയോ ഡെമണ്‍ ഇല്ലാതാക്കുന്നതില്‍ പരാജയപ്പെട്ടു."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "ഡെമണ്‍ മറുപടി നല്‍കുന്നില്ല."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "ഓട്ടോസ്പൌണ്‍ ലോക്ക് ലഭ്യമാക്കുവാന്‍ സാധ്യമല്ല."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ഡിവൈസില്‍ നിന്നും പുതിയ ഡേറ്റാ ലഭ്യമാക്കുന്നതിനായി ALSA നമുക്കു് അറിയിപ്പു് നല്‍കിയിരിക്കുന്നു, "
+"പക്ഷേ ഡേറ്റാ ലഭ്യമല്ല!\n"
+"ഇതു് ALSA ഡ്രൈവര്‍ '%s'-ലുള്ള ഒരു ബഗാവാം. ദയവായി ഈ പ്രശ്നം ALSA ഡവലപ്പര്‍സിനെ അറിയിക്കുക.\n"
+"POLLOUT സെറ്റ് വഴി നമ്മെ അറിയിച്ചിരിക്കുന്നു -- പക്ഷേ, snd_pcm_avail() ലഭ്യമാക്കിയതു് 0 "
+"അല്ലെങ്കില്‍ മറ്റൊരു മൂല്ല്യം < min_avail ആണു്."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ഡിവൈസില്‍ നിന്നും പുതിയ ഡേറ്റാ ലഭ്യമാക്കുന്നതിനായി ALSA നമുക്കു് അറിയിപ്പു് നല്‍കിയിരിക്കുന്നു, "
+"പക്ഷേ ഡേറ്റാ ലഭ്യമല്ല!\n"
+"ഇതു് ALSA ഡ്രൈവര്‍ '%s'-ലുള്ള ഒരു ബഗാവാം. ദയവായി ഈ പ്രശ്നം ALSA ഡവലപ്പര്‍സിനെ അറിയിക്കുക.\n"
+"POLLIN സെറ്റ് വഴി നമ്മെ അറിയിച്ചിരിക്കുന്നു -- പക്ഷേ, snd_pcm_avail() ലഭ്യമാക്കിയതു് 0 "
+"അല്ലെങ്കില്‍ മറ്റൊരു മൂല്ല്യം < min_avail ആണു്."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "ഓഫ്"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "ഹൈ ഫിഡലിറ്റി പ്ലേബാക്ക് (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "ഹൈ ഫിഡലിറ്റി കാപ്ചര്‍ (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "ടെലിഫോണി ഡ്യൂപ്ലെക്സ് (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "പള്‍സ്ഓഡിയോ സൌണ്ട് സര്‍വര്‍"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "ഔട്ട്പുട്ട് ഡിവൈസുകള്‍"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "ഇന്‍പുട്ട് ഡിവൈസുകള്‍"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@-ലുള്ള ഓഡിയോ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "ഇന്‍പുട്ട്"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "ഡോക്കിങ് സ്റ്റേഷന്‍ ഇന്‍പുട്ട്"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "ഡോക്കിങ് സ്റ്റേഷന്‍ മൈക്രോഫോണ്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "ഡോക്കിങ് സ്റ്റേഷന്‍ ഇന്‍പുട്ട്"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "അനലോഗ് ലൈന്‍-ഇന്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "മൈക്രോഫോണ്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "ഡോക്കിങ് സ്റ്റേഷന്‍ മൈക്രോഫോണ്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "മൈക്രോഫോണ്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "എക്സ്റ്റേണല്‍ മൈക്രോഫോണ്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "ഇന്റേണല്‍ മൈക്രോഫോണ്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "റേഡിയോ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "വീഡിയോ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "ഓട്ടോമാറ്റിക് ഗെയിന്‍ കണ്ട്രോള്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "ഓട്ടോമാറ്റിക് ഗെയിന്‍ കണ്ട്രോള്‍ ലഭ്യമല്ല"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "ബൂസ്റ്റ്"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "ബൂസ്റ്റ് ലഭ്യമല്ല"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "ആംപ്ലിഫയര്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "ആംപ്ലിഫയര്‍ ലഭ്യമല്ല"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "ബൂസ്റ്റ്"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "ബൂസ്റ്റ് ലഭ്യമല്ല"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "അനലോഗ് ഹെഡ്ഫോണുകള്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "അനലോഗ് ഇന്‍പുട്ട്"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "ഡോക്കിങ് സ്റ്റേഷന്‍ മൈക്രോഫോണ്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "അനലോഗ് ഔട്ട്പുട്ട്"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "അനലോഗ് ഔട്ട്പുട്ട് (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "അനലോഗ് ലൈന്‍-ഇന്‍"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "അനലോഗ് മോണോ ഔട്ട്പുട്ട്"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "അനലോഗ് സ്റ്റീരിയോ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "ഡിജിറ്റല്‍ സ്റ്റീരിയോ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "ഡിജിറ്റല്‍ സ്റ്റീരിയോ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "അനലോഗ് മോണോ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "അനലോഗ് സ്റ്റീരിയോ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "അനലോഗ് സറൌണ്ട് 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "അനലോഗ് സറൌണ്ട് 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "അനലോഗ് സറൌണ്ട് 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "അനലോഗ് സറൌണ്ട് 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "അനലോഗ് സറൌണ്ട് 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "അനലോഗ് സറൌണ്ട് 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "അനലോഗ് സറൌണ്ട് 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "അനലോഗ് സറൌണ്ട് 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "അനലോഗ് സറൌണ്ട് 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "അനലോഗ് സറൌണ്ട് 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "അനലോഗ് സറൌണ്ട് 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "ഡിജിറ്റല്‍ സ്റ്റീരിയോ (IEC958) "
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "ഡിജിറ്റല്‍ സ്റ്റീരിയോ (IEC958) "
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "ഡിജിറ്റല്‍ സറൌണ്ട് 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "ഡിജിറ്റല്‍ സറൌണ്ട് 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "ഡിജിറ്റല്‍ സ്റ്റീരിയോ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "ഡിജിറ്റല്‍ സറൌണ്ട് 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "അനലോഗ് മോണോ ഡ്യൂപ്ലെക്സ്"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "അനലോഗ് സ്റ്റീരിയോ ഡ്യൂപ്ലെക്സ്"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "ഡിജിറ്റല്‍ സ്റ്റീരിയോ ഡ്യൂപ്ലെക്സ് (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "നള്‍ ഔട്ട്പുട്ട്"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "ഇന്‍പുട്ട്"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] ഈ പ്ലാറ്റ്ഫോമില്‍ rlimit-നുള്ള പിന്തുണ ലഭ്യമല്ല."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() പരാജയപ്പെട്ടു"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "ഡിജിറ്റല്‍ സറൌണ്ട് 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "ലോ ഫ്രീക്വന്‍സി എമ്മിറ്റര്‍"
diff --git a/po/mr.po b/po/mr.po
new file mode 100644 (file)
index 0000000..bcb5774
--- /dev/null
+++ b/po/mr.po
@@ -0,0 +1,3020 @@
+# translation of pulseaudio.master-tx.po to Marathi
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Sandeep Shedmake <sshedmak@redhat.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:54+0000\n"
+"Last-Translator: Sandeep Shedmake <sshedmak@redhat.com>\n"
+"Language-Team: Marathi <fedora-trans-mr@redhat.com>\n"
+"Language: mr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ने अपेक्षा पेक्षा मोठे मूल्य पूरवले: %lu बाईटस् (%lu ms).\n"
+"हे सहसा ALSA ड्राइवर '%s' अंतर्गत बग अशू शकते. कृपया या अडचणीस ALSA डेव्हलपर करीता "
+"कळवा."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() ने अपेक्षा पेक्षा मोठे मूल्य पूरवले: %li बाईटस् (%s% lu ms).\n"
+"हे सहसा ALSA ड्राइवर '%s' अंतर्गत बग अशू शकते. कृपया या अडचणीस ALSA डेव्हलपर करीता "
+"कळवा."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ने अपेक्षा पेक्षा मोठे मूल्य पूरवले: %lu बाईटस् (%lu ms).\n"
+"हे सहसा ALSA ड्राइवर '%s' अंतर्गत बग अशू शकते. कृपया या अडचणीस ALSA डेव्हलपर करीता "
+"कळवा."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_nmap_begin() ने अपेक्षा पेक्षा मोठे मूल्य पूरवले: %lu बाईटस् (%lu ms).\n"
+"हे सहसा ALSA ड्राइवर '%s' अंतर्गत बग अशू शकते. कृपया या अडचणीस ALSA डेव्हलपर करीता "
+"कळवा."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "नल्ल असल्यावरही नेहमी किमान एक सींक लोड करून ठेवा"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "डम्मी आऊटपुट"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "आभासी LADSPA सींक"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<सींक करीता नाव> sink_properties=<सींक करीता गुणधर्म> "
+"master=<फिल्टरजोगी सींकचे नाव> format=<चाचणी रूपण> rate=<चाचणी दर> "
+"channels=<वाहिनींची संख्या> channel_map=<वाहिनी नकाशा> plugin=<ladspa प्लगइन "
+"नाव> label=<ladspa प्लगइन लेबल> control=<इंपुट कंट्रोल मुल्यांची स्वल्पविराम विभाजीत "
+"सूची>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "क्लॉक्ड् NULL सींक"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Null आऊटपुट"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "आंतरीक ऑडिओ"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "मोडेम"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "मूळ lt_dlopen दाखलकर्ता शोधण्यास अपयशी."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "नवीन dl दाखलकर्ता वाटप करण्यास अपयशी."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind-now-loader समावेष करण्यास अपयशी."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "संकेत %s प्राप्त झाले."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "बाहेर पडत आहे."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "वापरकर्ता '%s' शोधणे अशक्य."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "गट '%s' शोधण्यास अपयशी."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "वापरकर्ता '%s' (UID %lu) व गट '%s' (GID %lu) आढळले."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "वापरकर्ता '%s' व गट '%s' चे GID जुळत नाही."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "वापरकर्ता '%s' ची मुख्य डिरेक्ट्री '%s' नाही, दुर्लक्ष करत आहे."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' बनवण्यास अपयशी: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "गट यादी बदलवण्यास अपयशी: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID बदलवण्यास अपयशी: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID बदलवण्यास अपयशी: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "रूट परवानगी यशस्वीरित्या वगळले."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "प्रणाली भर पद्धत या प्लॅटफॉर्म करीता समर्थीत नाही."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) अपयशी: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "आदेश ओळ वाचण्यास अपयशी."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "डिमन कार्यरत नाही"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "डिमन PID %u नुरूप कार्यरत आहे"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "डिमन नष्ट करण्यास अपयशी: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr "हा कार्यक्रम रूट नुरूप चालविण्याकरीता नाही (जोपर्यंत --system निश्चित नाही)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "रूट परवानगी आवश्यक."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "प्रणाली घटनांकरीता --start समर्थीत नाही."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "प्रणाली पद्धती अंतर्गत कार्यरत, परंतु --disallow-exit निश्चित केले नाही!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"प्रणाली पद्धती अंतर्गत कार्यरत, परंतु --disallow-module-loading निश्चित केले नाही!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "प्रणाली पद्धती अंतर्गत कार्यरत, SHM पद्धत जबरनरित्या अकार्यान्वीत करत आहे!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "प्रणाली पद्धती अंतर्गत कार्यरत, रिकामे वेळ जबरनरित्या अकार्यान्वीत करत आहे!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdio प्राप्त करण्यास अपयशी."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "पाइप अपयशी: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() अपयशी: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() अपयशी: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "डिमन स्टार्टअप अपयशी."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "डिमन स्टार्टअप यशस्वी."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() अपयशी: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "हे PulseAudio %s आहे"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "कंपाइलेशन यजमान: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "कंपाइलेशन CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "यजमान वर कार्यरत: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u CPUs आढळले."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "पान आकार %lu बाईटस् आहे"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Valgrind समर्थनशी कंपाईल केले: होय"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Valgrind समर्थनशी कंपाईल केले: नाही"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "valgrind पद्धतीत कार्यरत: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "यजमान वर कार्यरत: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "अनुकूल बिल्ड: होय"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "अनुकूल बिल्ड: नाही"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG वर्णीकृत, सर्व asserts अकार्यान्वीत."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH वर्णीकृत, फक्त जलद मार्गीय asserts अकार्यान्वीत केले."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "सर्व asserts कार्यान्वीत केले."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "मशीन ID प्राप्त करण्यास अपयशी"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "मशीन ID %s आहे."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "सत्र ID %s आहे."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "रनटाईम डिरेक्ट्री %s वापरत आहे."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "स्थिती डिरेक्ट्री %s वापरत आहे."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "घटक डिरेक्ट्री %s वापरत आहे."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "प्रणाली पद्धतीत कार्यरत: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"ठिक आहे, तुम्ही PA प्रणाली मोडमध्ये चालवत आहात. कृपया लक्षात ठेवा असे करण्यास फारशी "
+"आवश्यकता नाही.\n"
+"असे कार्यान्वीत केल्यास, काहिक घटक योग्यप्रकारे कार्य नसेल करत असल्यास त्याला तुम्हीच "
+"जबाबदार राहणार.\n"
+"प्रणाली मोड दोकादायक आहे यासाठी कृपया http://www.freedesktop.org/wiki/Software/"
+"PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ वाचा."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() अपयशी."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "नवीन उच्च-बिंदूता टाइमर उपलब्ध! Bon appetit!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() अपयशी."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "डिमन प्रारंभ करण्यास अपयशी."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "विना विभाग दाखल केल्यास डिमन प्रारंभ झाले, कार्य करण्यास नकार."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "डिमन स्टार्टअप पूर्ण झाले."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "डिमन पूर्णपणे बंद करण्यास प्रारंभ केले."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "डिमन नष्ट केले."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --module-idle-time=SECS           Unload autoloaded modules when idle "
+"and\n"
+"                                        this time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr} Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level यास लॉग स्तरीय बाब अपेक्षीत आहे (एकतर क्षेत्र 0..4 अंतर्गत संख्यायी किंवा "
+"debug, info, notice, warn, error पैकी एक)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "अवैध लॉग लक्ष्य: 'syslog', 'stderr' किंवा 'auto' पैकी एक वापरा."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "अवैध पुन्ह सॅम्पल पद्धत '%s'."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm यास बूलीयन बाब अपेक्षीत आहे"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "नाव: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "घटक माहिती उपलब्ध नाही\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "आवृत्ती: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "वर्णन: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "लेखक: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "वापरणी: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "एकदा दाखल करा: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "DEPRECATION WARNING: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "मार्ग: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] अवैध लॉग लक्ष्य '%s'."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] अवैध लॉग स्तर '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] अवैध पुन्ह सॅम्पल पद्धत '%s'."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] अवैध rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] अवैध सॅम्पल स्वरूप '%s'."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] अवैध सॅम्पल दर '%s'."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] अवैध सॅम्पल मार्ग '%s'."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] अवैध मार्ग मॅप '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] अवैध तुकडे '%s' यांची एकूण संख्या."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] अवैध तुकड्याचे आकार '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] अवैध nice स्तर '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] अवैध सॅम्पल दर '%s'."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "संयोजना फाइल उघडण्यास अपयशी: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"निश्चित मुलभूत वाहिनी मॅपकडे निश्चित एकूण मुलभूत वाहिनी पेक्षा वेगळे वाहिनी संख्या "
+"समाविष्टीत आहे."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### संयोजना फाइल: %s पासून वाचा ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "परवानगी वगळत आहे."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio आवाज प्रणाली"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "PulseAudio आवाज प्रणाली सुरू करा"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio आवाज प्रणाली"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "PulseAudio आवाज प्रणाली सुरू करा"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "मोनो"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "समोर मध्यभागी"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "समोर डावीकडे"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "समोर उजवीकडे"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "पाठीमागे भध्यभागी"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "पाठीमागे डावीकडे"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "पाठीमागे उजवीकडे"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "समोर डावी-कडील-मध्यभागी"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "समोर उजवी-कडील-मध्यभागी"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "डावी बाजू"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "उजवी बाजू"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "ऑक्जीलरी 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "ऑक्जीलरी 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "ऑक्जीलरी 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "ऑक्जीलरी 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "ऑक्जीलरी 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "ऑक्जीलरी 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "ऑक्जीलरी 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "ऑक्जीलरी 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "ऑक्जीलरी 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "ऑक्जीलरी 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "ऑक्जीलरी 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "ऑक्जीलरी 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "ऑक्जीलरी 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "ऑक्जीलरी 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "ऑक्जीलरी 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "ऑक्जीलरी 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "ऑक्जीलरी 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "ऑक्जीलरी 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "ऑक्जीलरी 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "ऑक्जीलरी 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "ऑक्जीलरी 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "ऑक्जीलरी 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "ऑक्जीलरी 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "ऑक्जीलरी 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "ऑक्जीलरी 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "ऑक्जीलरी 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "ऑक्जीलरी 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "ऑक्जीलरी 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "ऑक्जीलरी 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "ऑक्जीलरी 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "ऑक्जीलरी 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "ऑक्जीलरी 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "वरील मध्य"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "वरील समोरचे मध्य"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "वरील समोरचे डावे"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "वरील समोरचे उजवे"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "वरील पाठीमागचे मध्य"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "वरील पाठीमागचे डावे"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "वरील पाठीमागचे उजवे"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(अवैध)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "स्टिरीओ"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "सराऊन्ड 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "सराऊन्ड 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "सराऊन्ड 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "सराऊन्ड 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "सराऊन्ड 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "ठिक"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "प्रवेश नकारले"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "अपरिचीत आदेश"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "अवैध बाब"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "घटक अस्तित्वात आहे"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "घटक आढळले नाही"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "जुळवणी नकारली"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "प्रोटोकॉल त्रुटी"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "वेळसमाप्ती"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "ओळख पटवण्याकरीता कि आढळली नाही"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "आंतरीक त्रुटी"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "वेळसमाप्ती नष्ट झाली"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "घटक नष्ट झाले"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "अवैध सर्वर"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "घटक प्रारंभ अपयशी"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "अयोग्य स्तर"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "डेटा आढळला नाही"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "असहत्व प्रोटोकॉल आवृत्ती"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "खूप मोठे"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "समर्थीत नाही"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "अपरिचीत त्रुटी कोड"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "यानुरूप वाढ आढळली नाही"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "जुणी कार्यपद्धत"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "लागू केले आहे असे आढळले नाही"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "क्लाऐंट विभाजीत केले"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "इंपुट/आऊटपुट त्रुटी"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "उपकरन किंव स्रोत व्यस्थ"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() अपयशी: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "कुकी डेटा वाचण्यास अपयशी"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "संयोजना फाइल '%s' उघडण्यास अपयशी: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "कुकी दाखल केले नाही. जुळवणीचा प्रयत्न करत आहे."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "अपरिचीत वाढ '%s' करीता संदेश प्राप्त झाले"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "स्ट्रीम रिकामे करण्यास अपयशी: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "प्लेबॅक स्ट्रीम रिकामे झाले."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "सर्व्हर करीता जुळवणी ड्रेन केली."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() अपयशी: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() अपयशी: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() अपयशी: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "स्ट्रीम यशस्वीरित्या निर्माण केले."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() अपयशी: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "बफर मेट्रीक्स्: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "बफर मेट्रीक्स्: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "उदाहरणार्थ spec '%s', वाहिनी नकाशा '%s' वापरत आहे."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "साधन %s शी जुळले (%u, %s सस्पेंड केले)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "स्ट्रीम त्रुटी: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "स्ट्रीम साधन सस्पेंड केले.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "स्ट्रीम साधन पुनः सुरू केले.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "स्ट्रीम underrun.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "स्ट्रीम overrun.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "स्ट्रीम started.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "स्ट्रीम साधन %s कडे स्थानांतरीत केले (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "नाही "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "स्ट्रीम बफर गुणधर्म बदलले.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "जुळवणी स्थापीत केली.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() अपयशी: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() अपयशी: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() अपयशी: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "जुळवणी अपयशी: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF प्राप्त झाले."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() अपयशी: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "संकेत प्राप्त झाले, बाहेर पडत आहे."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "विलंब प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "वेळ: %0.3f sec; विलंब: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() अपयशी: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --file-format=FFORMAT             Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"libpulse %s शी कंपाई केले\n"
+"libpulse %s शी लिंक केले\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "अवैध क्लाएंटचे नाव '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "अवैध स्ट्रीमचे नाव '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "अवैध वाहिनी नकाशा '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "अवैध विलंब संयोजना '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "अवैध कार्य वेळ संयोजना '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "अवैध गुणधर्म '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "अपरिचीत फाइल रूपण %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "अवैध सॅम्पल संयोजना"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "खूप जास्त बाब."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "फाइलसाठी सॅम्पल माहिती प्राप्त करण्यास अपयशी."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "आवाज फाइल उघडण्यास अपयशी."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr "सावधानता: निर्देशीत चाचणी संयोजना फाइलमधील संयोजनाशी खोडून पुनः लिहीली जाईल."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "फाइलपासून चाचणी संयोजना माहिती प्राप्त करण्यास अपयशी."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "सावधानता: फाइलपासून वाहिनी नकाशा ओळखण्यास अपयशी."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "वाहिनी नकाशा चाचणी संयोजनाशी जुळत नाही"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "सावधानता: वाहिनी नकाशा फाइलमध्ये लिहण्यास अपयशी."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "%s स्ट्रीम चाचणी संयोजना '%s' व वाहिनी नकाशा '%s' सह उघडत आहे."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "रेकॉर्डींग"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "प्लेबॅक"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "आदेश ओळ वाचण्यास अपयशी."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() अपयशी."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() अपयशी."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() अपयशी."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() अपयशी: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rrttime_new() अपयशी."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() अपयशी."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "सस्पेंड करण्यास अपयशी: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "पुन्हा चालू करण्यास अपयशी: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "सावधानता: आवाज सर्वर स्थानीय नाही, सस्पेंड करत नाही.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "जुळवणी अपयशी: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT प्राप्त झाले, बाहेर पडत आहे.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "सावधानता: उप कार्य संकेत %u द्वारे नष्ट करण्यात आले\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"libpulse %s शी कंपाई केले\n"
+"libpulse %s शी लिंक केले\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() अपयशी.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() अपयशी.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() अपयशी.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "आकडेवारी प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "वर्तमानक्षणी वापरणीत आहे: %2$s बाईटस् समाविष्टीत एकूण %1$u ब्लॉक्स् .\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "संपूर्ण कार्यकाळवेळी लागू केले: %2$s बाईटस् समाविष्टीत एकूण %1$u ब्लॉक्स् .\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "सॅपल कॅशे आकार: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "सर्वर माहिती प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"वापरकर्ता नाव: %s\n"
+"आयोजक नाव: %s\n"
+"सर्वर नाव: %s\n"
+"सर्वर आवृत्ती: %s\n"
+"मुलभूत सॅम्पल संयोनजा: %s\n"
+"मुलभूत वाहिनी नकाशा: %s\n"
+"मुलभूत सींक: %s\n"
+"मुलभूत स्रोत: %s\n"
+"कुकीज: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "sink माहिती प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tपोर्टस्:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tसक्रीय पोर्ट: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tपोर्टस्:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "स्रोत माहिती प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "विभाग माहिती प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "क्लाऐंट माहिती प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "कार्ड माहिती प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tसंक्षिप्त माहिती:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tसक्रीय संक्षिप्त माहिती: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "सींक इंपुट माहिती प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "स्रोत आऊटपुट माहिती प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "सॅम्पल माहिती प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "अपयशी: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "स्रोत माहिती प्राप्त करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "सॅम्पल अपलोड करण्यास अपयशी: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "फाइलची अयोग्य समाप्ती"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "अवैध सर्वर"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT प्राप्त झाले, बाहेर पडत आहे."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "अवैध खंडाची संयोजना"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"libpulse %s सह कंपाईल केले\n"
+"libpulse %s सह जुळले\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "कृपया दाखल करण्याजोगी तात्पूर्ती फाइल निर्देशीत करा"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "आवाज फाइल उघडण्यास अपयशी."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "सावधानता: फाइलपासून चाचणी संयोजना ओळखण्यास अपयशी."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "चालवण्याकरीता तुम्हाला तात्पूर्ते नाव निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "काढून टाकण्याकरीता तुम्हाला तात्पूर्ते नाव निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "तुम्हाला सींक इंपुट निर्देशांक व सींक निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "तुम्हाला आऊटपुट इंडेक्स स्रोत व स्रोत निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "तुम्हाला विभागाचे नाव व बाब निश्चित करावे लागेल."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "तुम्हाला विभाग इंडेक्स् निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"तुम्ही एकापेक्षा जास्त सींक निश्चित करू शकत नाही. तुम्हाला बूलीयन मूल्य निश्चित करावे लागेल."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"तुम्ही एकापेक्षा जास्त स्रोत निश्चित करू शकत नाही. तुम्हाला बूलीयन मूल्य निश्चित करावे लागेल."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "तुम्हाला कार्डचे नाव/इंडेक्स् व प्रोफाइल नाव निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "तुम्हाला सींक नाव/इंडेक्स् व पोर्टचे नाव निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "तुम्हाला स्रोत नाव/इंडेक्स् व पोर्टचे नाव निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "तुम्हाला सींक नाव/इंडेक्स् व पोर्टचे नाव निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "तुम्हाला स्रोत नाव/इंडेक्स् व खंडाचे नाव निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "तुम्हाला सींक इंपुट इंडेक्स् व सींक निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "अवैध सींक इंपुट इंडेक्स्"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "तुम्हाला आऊटपुट इंडेक्स स्रोत व स्रोत निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "अवैध सींक इंपुट इंडेक्स्"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "तुम्हाला सींक नाव/इंडेक्स् व पोर्टचे नाव निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "अवैध सॅम्पल संयोजना"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "तुम्हाला स्रोत नाव/इंडेक्स् व पोर्टचे नाव निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "तुम्हाला सींक इंपुट निर्देशांक व सींक निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "अवैध सींक इंपुट इंडेक्स् संयोजना"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "तुम्हाला स्रोत नाव/इंडेक्स् व पोर्टचे नाव निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "अवैध सींक इंपुट इंडेक्स् संयोजना"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "तुम्हाला सींक नाव/इंडेक्स् व पोर्टचे नाव निश्चित करावे लागेल"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "वैध आदेश निश्चित केले नाही."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "आदेश ओळ वाचण्यास अपयशी.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "सर्वर: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "स्रोत: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "सींक: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "कुकीज: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "कुकीज माहिती वाचण्यास अपयशी\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "कुकी डेटा साठवण्यास अपयशी\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "क्लाऐंट संयोजना फाइल दाखल करण्यास अपयशी.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "वातावरण संयोजना डेटा वाचण्यास अपयशी.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN प्राप्त करण्यास अपयशी.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "कुकी डेटा दाखल करण्यास अपयशी\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "अजूनही लागू केले नाही.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "PulseAudio डिमन कार्यरत नाही, किंवा सत्र डिमन नुरूप कार्यरत नाही."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio डिमन पूर्णपणे नष्ट करण्यास अपयशी."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "डिमन प्रतिसाद देत नाही."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "autospawn कुलूप करीता प्रवेश प्राप्य अशक्य."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA साधनवर नवीन डेटा लिहण्याकरीता सज्ज झाले, परंतु लिहण्याकरीता काहीच आढळले नाही!\n"
+"हे सहसा ALSA ड्राइवर '%s' अंतर्गत बग आहे. कृपया ही अडचण ALSA डेव्हलपर करीता कळवा.\n"
+"POLLOUT द्वारे सज्ज होणे शक्य आहे -- तरी परस्पर snd_pcm_avail() ने 0 पूरविले किंवा इतर "
+"मूल्य < min_avail असावे."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA साधनवर नवीन डेटा लिहण्याकरीता सज्ज झाले, परंतु लिहण्याकरीता काहीच आढळले नाही!\n"
+"हे सहसा ALSA ड्राइवर '%s' अंतर्गत बग आहे. कृपया ही अडचण ALSA डेव्हलपर करीता कळवा.\n"
+"POLLIN द्वारे सज्ज होणे शक्य आहे -- तरी परस्पर snd_pcm_avail() ने 0 पूरविले किंवा इतर "
+"मूल्य < min_avail असावे."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "बंद करा"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "हाय फिडेलिटी प्लेबॅक (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "हाय फिडीलीटी कॅपचर (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "टेलिफोनी ड्युप्लेक्स् (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio आवाज सर्वर"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "आऊट साधणे"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "इंपुट साधणे"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@ वरील ऑडिओ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "इंपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "डॉकिंग स्टेशन इंपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "डॉकिंग स्टेशन माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "डॉकिंग स्टेशन इंपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "लाइन-इन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "डॉकिंग स्टेशन माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "बाहेरील माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "आंतरीक माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "रेडिओ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "विडिओ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "स्वयं गैन कंट्रोल"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "स्वयं गैन कंट्रोल अशक्य"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "बूस्ट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "बूस्ट अशक्य"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "ऍमप्लिफायर"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "ऍमप्लिफायर अशक्य"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "बूस्ट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "बूस्ट अशक्य"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "ऍनलॉग हेडफोन्स्"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "ऍनलॉग इंपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "डॉकिंग स्टेशन माइक्रोफोन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "ऍनलॉग आऊटपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "ऍनलॉग आऊटपुट (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "लाइन-इन"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "ऍनलॉग मोनो आऊटपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "ऍनलॉग स्टिरीओ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "डिजीटल स्टिरीओ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "डिजीटल स्टिरीओ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "ऍनलॉग मोनो"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "ऍनलॉग स्टिरीओ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "ऍनलॉग सर्राउंड 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "ऍनलॉग सर्राउंड 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "ऍनलॉग सर्राउंड 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "ऍनलॉग सर्राउंड 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "ऍनलॉग सर्राउंड 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "ऍनलॉग सर्राउंड 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "ऍनलॉग सर्राउंड 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "ऍनलॉग सर्राउंड 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "ऍनलॉग सर्राउंड 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "ऍनलॉग सर्राउंड 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "ऍनलॉग सर्राउंड 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "डिजीटल स्टिरीओ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "डिजीटल स्टिरीओ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "डिजीटल सर्राउंड 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "डिजीटल सर्राउंड 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "डिजीटल स्टिरीओ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "डिजीटल सर्राउंड 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "ऍनलॉग मोनो ड्युप्लेक्स्"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "ऍनलॉग स्टिरीओ ड्युप्लेक्स्"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "डिजीटल स्टिरीओ ड्युप्लेक्स् (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Null आऊटपुट"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "इंपुट"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<सींक करीता नाव> sink_properties=<सींक करीता गुणधर्म> "
+"master=<फिल्टरजोगी सींकचे नाव> format=<चाचणी रूपण> rate=<चाचणी दर> "
+"channels=<वाहिनींची संख्या> channel_map=<वाहिनी नकाशा> plugin=<ladspa प्लगइन "
+"नाव> label=<ladspa प्लगइन लेबल> control=<इंपुट कंट्रोल मुल्यांची स्वल्पविराम विभाजीत "
+"सूची>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit या प्लॅटफॉर्म वर समर्थीत नाही."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() अपयशी"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "डिजीटल सर्राउंड 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "कमी क्रिक्वेन्सी स्रोत"
diff --git a/po/nl.po b/po/nl.po
new file mode 100644 (file)
index 0000000..e5be84d
--- /dev/null
+++ b/po/nl.po
@@ -0,0 +1,3155 @@
+# Dutch translation of pulseaudio.master-tx.
+# Copyright (C) 2009 THE pulseaudio.master-tx'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the pulseaudio.master-tx package.
+# Geert Warrink <geert.warrink@onsnet.nu>, 2009.
+# Reinout van Schouwen <reinout@gmail.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:54+0000\n"
+"Last-Translator: Reinout van Schouwen <reinout@gmail.com>\n"
+"Language-Team: Dutch <vertaling@vrijschrift.org>\n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding:  \n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() gaf een waarde terug die uitzonderlijk groot is: %lu bytes "
+"(%lu ms).\n"
+"Waarschijnlijk is dit een fout in het ALSA-stuurprogramma ‘%s’. Meld dit "
+"probleem alstublieft aan de ALSA-ontwikkelaars."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() gaf een waarde terug die uitzonderlijk groot is: %li bytes "
+"(%s%lu ms).\n"
+"Waarschijnlijk is dit een fout in het ALSA-stuurprogramma ‘%s’. Meld dit "
+"probleem alstublieft aan de ALSA-ontwikkelaars."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() gaf een waarde terug die uitzonderlijk groot is: %lu bytes "
+"(%lu ms).\n"
+"Waarschijnlijk is dit een fout in het ALSA-stuurprogramma ‘%s’. Meld dit "
+"probleem alstublieft aan de ALSA-ontwikkelaars."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() gaf een waarde terug die uitzonderlijk groot is: %lu "
+"bytes (%lu ms).\n"
+"Waarschijnlijk is dit een fout in het ALSA-stuurprogramma ‘%s’. Meld dit "
+"probleem alstublieft aan de ALSA-ontwikkelaars."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+"Houdt altijd ten minste een afvoer ingeladen zelfs als het de null-afvoer is."
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Dummy-uitvoer"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "Virtuele LADSPA afvoer"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<naam voor de afvoer> sink_properties=<eigenschappen van de "
+"afvoer> master=<naam van de te filteren afvoer> format=<sampleformaat> "
+"rate=<sample snelheid> channels=<aantal kanalen> channel_map=<kanaalkaart> "
+"plugin=<ladspa pluginnaam> label=<ladspa pluginlabel> "
+"control=<kommagescheiden lijst van invoercontrolewaarden>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "Geklokte NULL afvoer"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Null-uitvoer"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "Intern geluid"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Kon de originele lt_dlopen lader niet vinden."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "Kon die nieuwe dl lader niet toekennen."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "Kon bind-now-loader niet toevoegen."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "Signaal %s ontvangen."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "Afsluiten."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Kon gebruiker '%s' niet vinden."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Kon groep ‘%s’ niet vinden."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Gebruiker ‘%s’ (UID %lu) en groep ‘%s’ (GID %lu) gevonden."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID van gebruiker ‘%s’ en van groep ‘%s’ passen niet bij elkaar."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Persoonlijke map van gebruiker ‘%s’ is niet ‘%s’, negeer het."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Aanmaken van ‘%s’ mislukt: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Veranderen van groepslijst mislukt: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Veranderen van GID mislukt: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Veranderen van UID mislukt: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "Beheerdersrechten met succes laten vervallen."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "Systeembrede modus wordt op dit platform niet ondersteund."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) mislukte: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "Analyseren van de opdrachtregel mislukte."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "Voorziening draait niet"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "Voorziening draait met PID %u"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Elimineren van voorziening mislukt: ‘%s’"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Dit programma is niet bedoeld om als root gedraaid te worden (behalve als --"
+"system is opgegeven)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Beheerdersrechten vereist."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start wordt niet ondersteund voor systeeminstanties"
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "Draaiend in systeemmodus, maar --disallow-exit is niet ingesteld!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"Draaiend in systeemmodus, maar --disallow-module-loading is niet gezet!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "Draaiend in systeemmodus, geforceerd uitzetten van SHM-modus!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "Draaiend in systeemmodus, geforceerd uitzetten van exit idle time!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "Verkrijgen van stdio mislukte."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe mislukte: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() mislukte: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() mislukte: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "Voorziening opstarten mislukt."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "Voorziening met succes opgestart."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() mislukte: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Dit is PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Compilatiehost: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "Compilatie-CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "Draaiend op host: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u CPU's gevonden."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "Pagina grootte is %lu bytes"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Gecompileerd met Valgrind ondersteuning: ja"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Gecompileerd met Valgrind ondersteuning: nee"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Draaiend in valgrind-modus: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "Draaiend op host: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Geoptimaliseerd gebouwd: ja"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "Geoptimaliseerd gebouwd: nee"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG gedefinieerd, alle asserts uitgezet."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH gedefinieerd, alleen fast path-asserts uitgezet."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "Alle asserts aangezet."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "Machine-ID verkrijgen mislukt"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "Machine-ID is: %s."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "Sessie-ID is: %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Gebruik van runtime-map %s."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "Verbruik van state-map %s."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "Gebruik van module-map %s."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Draaiend in systeemmodus: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"OK, dus u draait PA in systeemmodus. Merk op dat u dit waarschijnlijk beter "
+"niet kunt doen.\n"
+"Als u het toch doet dan is het uw eigen schuld als dingen niet werken zoals "
+"verwacht.\n"
+"Lees http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/"
+"WhatIsWrongWithSystemWide/ voor een uitleg waarom systeemmodus gewoonlijk "
+"een slecht idee is."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() mislukte."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "Verse high-resolution timers beschikbaar! Smakelijk eten!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Kerel, je kernel stinkt! De aanbeveling van de chef is vandaag Linux met "
+"aangezette high-resolution timers!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() mislukte."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "Initialiseren van de voorziening mislukt."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Voorziening opgestart zonder geladen modules, dat werkt niet."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "Voorziening opstarten is klaar."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "Voorziening afsluiten is begonnen."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "Voorziening is afgesloten."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [opties]\n"
+"\n"
+"COMMANDO'S:\n"
+"  -h, --help                            Laat deze hulp zien\n"
+"      --version                         Laat versie zien\n"
+"      --dump-conf                       Dump standaard configuratie\n"
+"      --dump-modules                    Dump lijst van beschikbare modules\n"
+"      --dump-resample-methods           Dump beschikbare resample methodes\n"
+"      --cleanup-shm                     Schoon oud gedeelde geheugen "
+"segmenten op\n"
+"      --start                           Start de daemon als deze niet "
+"draait\n"
+"  -k  --kill                            Schiet een draaiende daemon af\n"
+"      --check                           Controleer voor een draaiende daemon "
+"(geeft alleen uitgangs code terug)\n"
+"\n"
+"OPTIES:\n"
+"      --system[=BOOL]                   Draaieals systeem-brede instance\n"
+"  -D, --daemonize[=BOOL]                Maak daemon na opstarten\n"
+"      --fail[=BOOL]                     Verlaat als opstarten mislukt\n"
+"      --high-priority[=BOOL]            Probeer een hoog nice nivo in te "
+"stellen\n"
+"                                        (only beschikbaar voor, als SUID of\n"
+"                                        met verhoogde RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Probeer realtime scheduling aan te "
+"zetten \n"
+"                                        (alleen beschikbaar voor root, als "
+"SUID of\n"
+"                                        met verhoogde RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Sta de module gebruiker niet toe om "
+"de gevraagde\n"
+"                                        module te laden/verwijderen na "
+"opstarten\n"
+"      --disallow-exit[=BOOL]            Sta de gebruiker het gevraagde "
+"vertrek niet toe\n"
+"      --exit-idle-time=SECS             Sluit de daemon af deze niets doet "
+"en deze tijd\n"
+"                                        verstreken is\n"
+"      --module-idle-time=SECS           Verwijder automatisch geladen "
+"modules als deze niets\n"
+"                                        doen en deze tijd verstreken is\n"
+"      --scache-idle-time=SECS           Verwijder automatisch geladen "
+"samples als deze niets\n"
+"                                        doen en deze tijd verstreken is\n"
+"      --log-level[=LEVEL]               Verhoog of instellen van "
+"breedsprakigheids nivo\n"
+"  -v                                    Verhoog breedsprakigheids nivo\n"
+"      --log-target={auto,syslog,stderr} Specificeer het log doel\n"
+"      --log-meta[=BOOL]                 Voeg code locatie toe aan log "
+"boodschappen\n"
+"      --log-time[=BOOL]                 Voeg tijdstempels toe aan log "
+"boodschappen\n"
+"      --log-backtrace=FRAMES            Voeg een backtrace toe aan log "
+"boodscvhappen\n"
+"  -p, --dl-search-path=PATH             Stel het zoek pad voor dynamisch "
+"gedeelde\n"
+"                                        objecten in (plugins)\n"
+"      --resample-method=METHOD          Gebruik de opgegeven resampling "
+"methode\n"
+"                                        (Zie --dump-resample-methods voor\n"
+"                                        mogelijke waardes)\n"
+"      --use-pid-file[=BOOL]             Maak een PID bestand\n"
+"      --no-cpu-limit[=BOOL]             Installeer geen CPU load begrenzer "
+"op\n"
+"                                        platforms die dat ondersteunen.\n"
+"      --disable-shm[=BOOL]              Zet gedeeld heugen ondersteuning "
+"uit.\n"
+"\n"
+"OPSTART SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Laad de opgegeven plugin module "
+"met\n"
+"                                        de opgegeven argumenten\n"
+"  -F, --file=FILENAME                   Draai het opgegeven script\n"
+"  -C                                    Open een opdrachtregel op de "
+"draaiende TTY\n"
+"                                        na het opstarten\n"
+"\n"
+"  -n                                    Laad het standaard script bestand "
+"niet\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize verwacht een boolean argument"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail verwacht een boolean argument"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level verwacht een log level argument (numeriek uit de reeks 0..4 of "
+"een van type debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority verwacht een boolean argument"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime verwacht een boolean argument"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading verwacht een boolean argument"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit verwacht een bolean argument"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file verwacht een boolean argument"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "Ongeldig log doel: gebruik een van 'syslog', 'stderr', 'auto'."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time verwacht een boolean argument"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta verwacht een boolean argument"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Ongeldige resample methode '%s'."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system verwacht een boolean argument"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit verwacht een boolean argument"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm verwacht een boolean argument"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Naam: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "Geen module informatie beschikbaar\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Versie: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Beschrijving: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Auteur: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Gebruik: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Laad eenmaal: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "ACHTERHAALDHEIDSWAARSCHUWING: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Pad: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Ongeldig logdoel '%s'."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Ongeldig logniveau '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Ongeldige resample-methode ‘%s’."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Ongeldige rlimit ‘%s’."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Ongeldig sampleformaat ‘%s’."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Ongeldige samlperate ‘%s’."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Ongeldige sample-kanalen ‘%s’."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Ongeldige kanalenkaart ‘%s’."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Ongeldig aantal fragmenten '%s'."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Ongeldige fragmentgrootte ‘%s’."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Ongeldig nice niveau ‘%s’."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Ongeldige samlperate ‘%s’."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Openen van configuratiebestand %s mislukt."
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"De opgegeven standaard kanalenkaart heeft een ander aantal kanalen dan de "
+"opgegeven standaard aantal kanalen."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Lees uit het configuratiebestand: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "Rechten opschonen."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio geluidssysteem"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Start het PulseAudio geluidssysteem"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio geluidssysteem"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "Start het PulseAudio geluidssysteem"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Voor midden"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Voor links"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Voor rechts"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Achter midden"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Achter links"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Achter rechts"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Voor links-van-het-midden"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Voor rechts-van-het-midden"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Zijkant links"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Zijkant rechts"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Auxiliary 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Auxiliary 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Auxiliary 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Auxiliary 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Auxiliary 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Auxiliary 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Auxiliary 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Auxiliary 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Auxiliary 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Auxiliary 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Auxiliary 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Auxiliary 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Auxiliary 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Auxiliary 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Auxiliary 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Auxiliary· 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Auxiliary 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Auxiliary 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Auxiliary 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Auxiliary 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Auxiliary 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Auxiliary 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Auxiliary 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Auxiliary 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Auxiliary 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Auxiliary 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Auxiliary 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Auxiliary 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Auxiliary 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Auxiliary 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Auxiliary 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Auxiliary 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Boven midden"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Boven voor midden"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Boven voor links"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Boven voor rechts"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Boven achter midden"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "boven achter links"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "boven achter rechts"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(ongeldig)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "toegang geweigerd"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Onbekend commando"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Ongeldige argumenten"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "Eenheid bestaat"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "Eenheid onbekend"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "Verbinding geweigerd"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "Protocolfout"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "Tijd verstreken"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "Geen autorisatiesleutel"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "Interne fout"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "Verbinding verbroken"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "Eenheid geëlimineerd"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Ongeldige server"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Module-initialisatie mislukt"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Slechte toestand"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Geen data"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "Protocol versie niet compatibel"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Te groot"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "Niet ondersteund"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Onbekende fout code"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "Onbekende extentie"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Verouderde functionaliteit"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Implementatie ontbreekt"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Client afgesplitst"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Input/Output fout"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Apparaat of hulpbron is bezig"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() mislukte: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "Analyseren van cookie-data mislukt"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "Open van configuratiebestand ‘%s’ mislukte: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "Geen cookie geladen. Probeer zonder cookie te verbinden."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Ontving boodschap voor onbekende extensie ‘%s’"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Afvoeren stroom %s mislukte"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "Afspelen van afgevoerde stroom."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "Vervinding naar server afvoeren."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() mislukte: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() mislukte: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() mislukte: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "Stroom met succes aangemaakt."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() mislukte: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Buffermetriek: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Buffermetriek: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Gebruik sample-spec  '%s', kanaal map '%s'."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "Verbonden met apparaat %s (%u, %sopgeschort)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "Stroomfout: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Stroomapparaat opgeschort.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Stroomapparaat hervat.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Te weinig data voor stroom.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Data-overschrijding voor stroom.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "Stroom gestart.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Stroom verplaatst naar apparaat %s (%u, %sopgeschort).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "niet "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Stroom buffer attributen veranderden.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "Verbinding bereikt.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() mislukte: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() mislukte: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() mislukte: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Verbindingsfout: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "Kreeg EOF."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() mislukte: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "Ontving signaal, afsluiten."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Latentie krijgen mislukte: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Tijd: %0.3f sec; Latentie: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() mislukte: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [opties]\n"
+"\n"
+"  -h, --help                            Laat deze hulp zien\n"
+"      --version                         Laat versie zien\n"
+"\n"
+"  -r, --record                          Maak een verbinding voor opnemen\n"
+"  -p, --playback                        Maak een verbinding voor afspelen\n"
+"\n"
+"  -v, --verbose                         Zet breedsprakigheid aan\n"
+"\n"
+"  -s, --server=SERVER                   De naam van de server waarmee "
+"verbonden moet worden\n"
+"  -d, --device=DEVICE                   De naam van de afvoer/bron waarmee "
+"verbonden moet worden\n"
+"  -n, --client-name=NAME                Hoe wordt deze cliënt op de server "
+"genoemd\n"
+"      --stream-name=NAME                Hoe wordt deze stroom op de server "
+"genoemd\n"
+"      --volume=VOLUME                   Geef het begins (lineare) volume in "
+"reeks 0...65536\n"
+"      --rate=SAMPLERATE                 De samplerate in Hz (standaard "
+"44100)\n"
+"      --format=SAMPLEFORMAT             Het sampletype, een van s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (standaard "
+"s16ne)\n"
+"      --channels=CHANNELS               Het aantal kanalen, 1 voor mono, 2 "
+"voor stereo\n"
+"                                        (standaard 2)\n"
+"      --channel-map=CHANNELMAP          Kanaalkaart te gebruiken in plaats "
+"van de standaard\n"
+"      --fix-format                      Neem het sampleformaat over van de "
+"afvoer waar de stroom\n"
+"                                        mee verbonden is.\n"
+"      --fix-rate                        Neem de samplerate over van de "
+"afvoer waar de stroom\n"
+"                                        mee verbonden is.\n"
+"      --fix-channels                    Neem het aantal kanalen en de kanaal "
+"map over\n"
+"                                        van de afvoer waar de stroom mee "
+"verbonden is.\n"
+"      --no-remix                        Doe geen upmix of downmix van "
+"kanalen.\n"
+"      --no-remap                        Map kanalen met index in plaats van "
+"met naam.\n"
+"      --latency=BYTES                   Verzoek de opgegeven latentie in "
+"bytes.\n"
+"      --process-time=BYTES              Verzoek de opgegeven proces tijd per "
+"verzoek in bytes.\n"
+"      --property=PROPERTY=VALUE         Zet de opgegeven eigenschap op de "
+"opgegeven waarde.\n"
+"      --raw                             Opnemen/afspelen van ruwe PCM data.\n"
+"      --file-format=FFORMAT             Opnemen/afspelen van geformateerde "
+"data.\n"
+"      --list-file-formats               Laat beschikbare bestandsformaten "
+"zien.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Gecompileerd met libpulse %s\n"
+"Gelinkt met libpulse %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Ongeldige clientnaam ‘%s’."
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Ongeldige stroomnaam ‘%s’."
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Ongeldige kanaalkaart ‘%s’."
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Ongeldige latentie-specificatie ‘%s’."
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Ongeldige procestijdspecificatie ‘%s’."
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Ongeldige eigenschap ‘%s’."
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Ongeldig bestandsformaat %s"
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "Ongeldige samplespecificatie"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "Te veel argumenten."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "Aanmaken van samplespecificatie voor bestand mislukt."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "Openen van geluidsbestand mislukte."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Waarschuwing: opgegeven bemonster specificatie zal overschreven worden met "
+"de specificatie van het bestand."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "Bepalen van samplespecificatie van het bestand mislukte."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Waarschuwing: Bepalen van kanaalkaart van bestand mislukte."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "Kanaal map komt niet overeen met bemonster specificatie."
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "Waarschuwing: schrijven van kanaalkaart naar bestand mislukte."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Openen van een %s stroom met samplespecificatie ‘%s’ en kanaalkaart ‘%s’."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "opnemen"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "afspelen"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "Analyseren van de opdrachtregel mislukte."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() mislukte."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() mislukte."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_cotext_new() mislukte."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() mislukte: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() mislukte."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() mislukte."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Opschorten mislukte: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Vervolgen mislukte: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "WAARSCHUWING: Geluidsserver is niet lokaal, geen opschorten.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Verbonding mislukte: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Kreeg SIGINT, verlaten.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "WAARSCHUWING: kind proces afgesloten door signaal %u\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [opties] ... \n"
+"\n"
+"  -h, --help                            Laat deze hulp zien\n"
+"      --version                         Laat versie zien\n"
+"  -s, --server=SERVER                   De naam van de server waarmee "
+"verbonden wordt\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Gecompileerd met libpulse %s\n"
+"Gelinkt met libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() mislukte.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() mislukte.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() mislukte.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Verkrijgen van statistiek %s mislukte"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Op dit moment in gebruik: %u blokken bevattende in totaal %s bytes.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Toegewezen tijdens de gehele levensduur: %u blokken bevattende in totaal %s "
+"butes.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Sample-buffergrootte: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Server informatie verkrijgen mislukte: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Gebruikersnaam: %s\n"
+"Hostnaam: %s\n"
+"Servernaam: %s\n"
+"Serverversie: %s\n"
+"Standaard samplespecificatie: %s\n"
+"Standaard kanaal map: %s\n"
+"Standaard afvoer: %s\n"
+"Standaard bron: %s\n"
+"Cookie: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Verkrijgen afvoerinformatie mislukte: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Afvoer #%u\n"
+"\tToestand: %s\n"
+"\tNaam: %s\n"
+"\tBeschrijving: %s\n"
+"\tDriver: %s\n"
+"\tBemonsterings specificatie: %s\n"
+"\tKanaal map: %s\n"
+"\tModule eigenaar: %u\n"
+"\tDemping: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balans %0.2f\n"
+"\tBasis volume: %s%s%s\n"
+"\tMonitor bron: %s\n"
+"\tLatentie: %0.0f usec, ingesteld %0.0f usec\n"
+"\tVlaggen: %s%s%s%s%s%s\n"
+"\tEigenschappen:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPoorten:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tActieve poort: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tPoorten:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Verkrijgen van broninformatie mislukt: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Bron #%u\n"
+"\tToestand: %s\n"
+"\tNaam: %s\n"
+"\tBeschrijving: %s\n"
+"\tDriver: %s\n"
+"\tSamplespecificatie: %s\n"
+"\tKanaal map: %s\n"
+"\tModule eigenaar: %u\n"
+"\tDemping: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balans %0.2f\n"
+"\tBasis volume: %s%s%s\n"
+"\tMonitorafvoer: %s\n"
+"\tLatentie: %0.0f usec, ingesteld %0.0f usec\n"
+"\tVlaggen: %s%s%s%s%s%s\n"
+"\tEigenschappen:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n.v.t."
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Verkrijgen van module informatie mislukte: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Module #%u\n"
+"\tNaam: %s\n"
+"\tArgument: %s\n"
+"\tGebruiksteller: %s\n"
+"\tEigenschappen:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Verkrijgen van clientinformatie mislukt: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Client #%u\n"
+"\tStuurprogramma: %s\n"
+"\tModule-eigenaar: %s\n"
+"\tEigenschappen:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Verkrijgen van kaartinformatie mislukt: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Kaart #%u\n"
+"\tNaam: %s\n"
+"\tStuurprogramma: %s\n"
+"\tModule-eigenaar: %s\n"
+"\tEigenschappen:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfielen:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tActieve profiel: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Verkrijgen van afvoer-invoerinformatie mislukt: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Afvoer input #%u\n"
+"\tStuurprogramma: %s\n"
+"\tModule-eigenaar: %s\n"
+"\tClient: %s\n"
+"\tAfvoer: %u\n"
+"\tSamplespecificatie: %s\n"
+"\tKanaalkaart: %s\n"
+"\tDemping: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balans %0.2f\n"
+"\tBufferlatentie: %0.0f usec\n"
+"\tAfvoerlatentie: %0.0f usec\n"
+"\tResample-methode: %s\n"
+"\tEigenschappen:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Verkrijgen van bron-uitvoerinformatie mislukt: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Afvoer input #%u\n"
+"\tStuurprogramma: %s\n"
+"\tModule-eigenaar: %s\n"
+"\tClient: %s\n"
+"\tAfvoer: %u\n"
+"\tSamplespecificatie: %s\n"
+"\tKanaalkaart: %s\n"
+"\tDemping: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balans %0.2f\n"
+"\tBufferlatentie: %0.0f usec\n"
+"\tAfvoerlatentie: %0.0f usec\n"
+"\tResample-methode: %s\n"
+"\tEigenschappen:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Verkrijgen van sample-informatie mislukt: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sample #%u\n"
+"\tNaam: %s\n"
+"\tSamplespecificatie: %s\n"
+"\tKanaal map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balans %0.2f\n"
+"\tDuur: %0.1fs\n"
+"\tGrootte: %s\n"
+"\tTraagheid: %s\n"
+"\tBestandsnaam: %s\n"
+"\tEigenschappen:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "Mislukt: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Verkrijgen van broninformatie mislukt: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Uploaden van monster mislukte: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "Voortijdig einde van bestand"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "Ongeldige server"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "Ontving SIGINT, afsluiten."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "Ongeldige volume-opgave"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [opties] ... \n"
+"\n"
+"  -h, --help                            Laat deze hulp zien\n"
+"      --version                         Laat versie zien\n"
+"  -s, --server=SERVER                   De naam van de server waarmee "
+"verbonden wordt\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Gecompileerd met libpulse %s\n"
+"Gelinkt met libpulse %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "Geef een te laden samplebestand op"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "Openen geluidsbestand mislukt."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Waarschuwing: Bepalen van samplespecificatie van bestand mislukte."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "U dient een samplenaam op te geven om af te spelen"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "U dient een samplenaam op te geven om te verwijderen"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "U dient een afvoer-invoerindex en een afvoer op te geven"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "U dient een bron-uitvoerindex en een bron op te geven"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "U dient een modulenaam en argumenten op te geven."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "U dient een module index op te geven"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"U kunt niet meer dan een afvoer opgeven. U dient een boolean waarde op te "
+"geven."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"U kunt niet meer dan één bron opgeven. u dient een boolean waarde op te "
+"geven."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "U dient een kaartnaam/index en een profielnaam op te geven"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "U dient een afvoernaam/index en een poortnaam op te geven"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "U dient een bronnaam/index en een poortnaam op te geven"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "U dient een afvoernaam/index en een volume op te geven"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "U dient een bronnaam/index en een volume op te geven"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "U dient een afvoer-invoerindex en een volume op te geven"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "Ongeldige afvoer-invoerindex"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "U dient een bron-uitvoerindex en een bron op te geven"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "Ongeldige afvoer-invoerindex"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "U dient een afvoernaam/index en een dempingsboolean op te geven"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "Ongeldige samplespecificatie"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "U dient een bronnaam/index en een dempingsboolean op te geven"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "U dient een afvoer-invoerindex en een dempingsboolean op te geven"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "Ongeldige afvoer-invoerindex opgave"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "U dient een bronnaam/index en een dempingsboolean op te geven"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "Ongeldige afvoer-invoerindex opgave"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "U dient een afvoernaam/index en een dempingsboolean op te geven"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "Geen geldige opdracht opgegeven."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D scherm] [-S server] [-O afvoer] [-I bron] [-c bestand]  [-d|-e|-i|-"
+"r]\n"
+"\n"
+" -d    Laat huidige PulseAudio data zien horende bij X11 scherm (standaard)\n"
+" -e    Exporteer locale PulseAudio data naar X11 scherm\n"
+" -i    Importeer PulseAudio data van X11 scherm naar locale omgevings "
+"variabelen en cookie bestand.\n"
+" -r    Verwijder PulseAudio data van X11 scherm\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Opdrachtregel ontleden mislukt.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "Server: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "Bron: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Afvoer: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Cookiedata ontleden mislukt\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Cookiedata opslaan mislukt\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "Clientconfiguratiebestand laden mislukt.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "Omgevingsconfiguratiedata lezen mislukt.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN verkrijgen mislukte.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Cookiedata laden mislukt\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Nog niet geïmplementeerd.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Er draait geen PulseAudio-voorziening, of het draait niet als "
+"sessievoorziening."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio-voorziening uitzetten mislukt."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "Voorziening reageert niet."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "Kan geen toegang krijgen tot autospawn blokkade."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA maakte ons wakker om nieuwe data naar het apparaat te schrijven, maar "
+"er was niets om weg te schrijven!\n"
+"Waarschijnlijk is dit een fout in het ALSA-stuurprogramma ‘%s’. Meld dit "
+"probleem alstublieft aan de ALSA-ontwikkelaars.\n"
+"We werden gewekt met POLLOUT ingesteld -- echter een opvolgende snd_pcm_avail"
+"() gaf 0 terug of een andere waarde < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA maakte ons wakker om nieuwe data van het apparaat te lezen, maar er was "
+"niets om te lezen!\n"
+"Waarschijnlijk is dit een fout in het ALSA-stuurprogramma ‘%s’. Meld dit "
+"probleem alstublieft aan de ALSA-ontwikkelaars.\n"
+"We werden gewekt met POLLIN ingesteld -- echter een opvolgende snd_pcm_avail"
+"() gaf 0 terug of een andere waarde < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "Uit"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "High Fidelity Playback (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "High Fidelity afvangen (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telefonie duplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio-geluidsserver"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "Uitvoerapparaten"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "Invoerapparaten"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "Geluid op @HOSTNAME@"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "Invoer"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "Docking station-invoer"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "Docking station-microfoon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "Docking station-invoer"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "Lijn-in"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "Microfoon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "Docking station-microfoon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "Microfoon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "Externe microfoon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "Interne microfoon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "Video"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "Automatische gain-controle"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "Geen automatische gain-controle"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "Boostversterking"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "Geen boostversterking"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "Versterker"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "Geen versterker"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "Boostversterking"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "Geen boostversterking"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "Analoge koptelefoon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "Analoge invoer"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "Docking station-microfoon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "Analoge output"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "Analoge output (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "Lijn-in"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "Analoge mono-uitvoer"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "Analoog stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "Digitaal stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digitaal stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "Analoog mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "Analoog stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "Analoog surround 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "Analoog surround 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "Analoog surround 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "Analoog surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "Analoog surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "Analoog surround 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "Analoog surround 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "Analoog surround 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "Analoog surround 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "Analoog surround 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "Analoog surround 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "Digitaal stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digitaal stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digitaal surround 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digitaal surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "Digitaal stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digitaal surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "Analoog mono duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "Analoog stereo duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Digitaal stereo duplex (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Null-uitvoer"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "Invoer"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<naam voor de afvoer> sink_properties=<eigenschappen van de "
+"afvoer> master=<naam van de te filteren afvoer> format=<sampleformaat> "
+"rate=<sample snelheid> channels=<aantal kanalen> channel_map=<kanaalkaart> "
+"plugin=<ladspa pluginnaam> label=<ladspa pluginlabel> "
+"control=<kommagescheiden lijst van invoercontrolewaarden>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit wordt niet ondersteund op dit platform."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() mislukte"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Bronuitvoer #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tModule-eigenaaar: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tBron: %u\n"
+#~ "\tSamplespecificatie: %s\n"
+#~ "\tKanaalkaart: %s\n"
+#~ "\tBufferlatentie: %0.0f usec\n"
+#~ "\tBronlatentie: %0.0f usec\n"
+#~ "\tResample-methode: %s\n"
+#~ "\tEigenschappen:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [opties] stat\n"
+#~ "%s [opties] list\n"
+#~ "%s [opties] exit\n"
+#~ "%s [opties] upload-sample BESTANDNAAM [NAAM]\n"
+#~ "%s [opties] play-sample NAAM [AFVOER]\n"
+#~ "%s [opties] remove-sample NAAM\n"
+#~ "%s [opties] move-sink-input AFVOERINPUT AFVOER\n"
+#~ "%s [opties] move-source-output BRONOUTPUT BRON\n"
+#~ "%s [opties] load-module NAAM [ARG ...]\n"
+#~ "%s [opties] unload-module MODULE\n"
+#~ "%s [opties] suspend-sink AFVOER 1|0\n"
+#~ "%s [opties] suspend-source BRON 1|0\n"
+#~ "%s [opties] set-card-profile KAART PROFIEL\n"
+#~ "%s [opties] set-sink-port AFVOER POORT\n"
+#~ "%s [opties] set-source-port BRON POORT\n"
+#~ "%s [opties] set-sink-volume AFVOER VOLUME\n"
+#~ "%s [opties] set-source-volume BRON VOLUME\n"
+#~ "%s [opties] set-sink-input-volume AFVOERINPUT VOLUME\n"
+#~ "%s [opties] set-sink-mute AFVOER 1|0\n"
+#~ "%s [opties] set-source-mute BRON 1|0\n"
+#~ "%s [opties] set-sink-input-mute AFVOERINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Laat deze hulp zien\n"
+#~ "      --version                         Laat versie zien\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s/%s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Digitaal surround 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Lage-frequentiezender"
+
+#, fuzzy
+#~ msgid "Invalid client name '%s'\n"
+#~ msgstr "Ongeldige resample methode '%s'."
+
+#~ msgid "Cannot connect to system bus: %s"
+#~ msgstr "Kan niet verbinden met systeem bus: %s"
+
+#~ msgid "Cannot get caller from PID: %s"
+#~ msgstr "Kan geen bezoeker krijgen van PID: %s"
+
+#~ msgid "Cannot set UID on caller object."
+#~ msgstr "Kan UID niet instellen op caller object."
+
+#~ msgid "Failed to get CK session."
+#~ msgstr "Kon geen CK sessie krijgen."
+
+#~ msgid "Cannot set UID on session object."
+#~ msgstr "Kan UID niet instellen op sessie object."
+
+#~ msgid "Cannot allocate PolKitAction."
+#~ msgstr "Kan PolKitAction niet toekennen."
+
+#~ msgid "Cannot set action_id"
+#~ msgstr "Kan action_id niet instellen"
+
+#~ msgid "Cannot allocate PolKitContext."
+#~ msgstr "Kan PolKitContext niet toekennen."
+
+#~ msgid "Cannot initialize PolKitContext: %s"
+#~ msgstr "Kan PolKitContext niet intialiseren: %s"
+
+#~ msgid "Could not determine whether caller is authorized: %s"
+#~ msgstr "Kon niet bepalen of bezoeker gemachtigd is: %s"
+
+#~ msgid "Cannot obtain auth: %s"
+#~ msgstr "Kan geen authorisatie krijgen: %s"
+
+#~ msgid "PolicyKit responded with '%s'"
+#~ msgstr "PolicyKit antwoordde met '%s'"
+
+#~ msgid "We're in the group '%s', allowing high-priority scheduling."
+#~ msgstr ""
+#~ "Wij zijn in de groep '%s', wat plannen met hoge prioriteit toestaat."
+
+#~ msgid "We're in the group '%s', allowing real-time scheduling."
+#~ msgstr "Wij zijn in de groep '%s', war real-time planning toestaat."
+
+#~ msgid "PolicyKit grants us acquire-high-priority privilege."
+#~ msgstr "PolicyKit kent ons acquire-high-priority rechten toe."
+
+#~ msgid "PolicyKit refuses acquire-high-priority privilege."
+#~ msgstr "PolicyKit weigert ons acquire-high-priority rechten."
+
+#~ msgid "PolicyKit grants us acquire-real-time privilege."
+#~ msgstr "PolicyKit kent ons acquire-real-time rechten toe."
+
+#~ msgid "PolicyKit refuses acquire-real-time privilege."
+#~ msgstr "PolicyKit weigert ons acquire-real-time rechten."
+
+#~ msgid ""
+#~ "Called SUID root and real-time and/or high-priority scheduling was "
+#~ "requested in the configuration. However, we lack the necessary "
+#~ "privileges:\n"
+#~ "We are not in group '%s', PolicyKit refuse to grant us the requested "
+#~ "privileges and we have no increase RLIMIT_NICE/RLIMIT_RTPRIO resource "
+#~ "limits.\n"
+#~ "For enabling real-time/high-priority scheduling please acquire the "
+#~ "appropriate PolicyKit privileges, or become a member of '%s', or increase "
+#~ "the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."
+#~ msgstr ""
+#~ "Aangeroepen met SUID root en real-time en/of high-priority inplannen was "
+#~ "verzocht in de configuratie. Echter de nodige rechten ontbreken:\n"
+#~ "We zijn niet in groep '%s', PolicyKit weigert om ons de gevraagde rechten "
+#~ "te geven en we hebben geen rect om de RLIMIT_NICE/RLIMIT_RTPRIO limieten "
+#~ "te verhogen.\n"
+#~ "Voor het aanzetten van real-time/high-priority inplannen mort je juiste "
+#~ "PolicyKit privileges hebben, of lid van '%s\" worden, of de RLIMIT_NICE/"
+#~ "RLIMIT_RTPRIO limieten voor deze gebruiker verhogen."
+
+#~ msgid ""
+#~ "High-priority scheduling enabled in configuration but not allowed by "
+#~ "policy."
+#~ msgstr ""
+#~ "Hoge prioriteit inplannen is aangezet in de configuratie maar niet "
+#~ "toegestaan door de richtlijnen."
+
+#~ msgid "Successfully increased RLIMIT_RTPRIO"
+#~ msgstr "RLIMIT_RTPRIO met succes verhoogd"
+
+#~ msgid "RLIMIT_RTPRIO failed: %s"
+#~ msgstr "RLIMIT_RTPRIO mislukte: %s"
+
+#~ msgid "Giving up CAP_NICE"
+#~ msgstr "Opgeven CAP_NICE"
+
+#~ msgid ""
+#~ "Real-time scheduling enabled in configuration but not allowed by policy."
+#~ msgstr ""
+#~ "Real-time inplannen is aangezet in de configuratie maar niet toegestaan "
+#~ "door de richtlijnen."
diff --git a/po/nn.po b/po/nn.po
new file mode 100644 (file)
index 0000000..f6121d1
--- /dev/null
+++ b/po/nn.po
@@ -0,0 +1,3238 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Karl Ove Hufthammer <karl@huftis.org>, 2017.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-08-04 23:38+0200\n"
+"PO-Revision-Date: 2017-08-06 17:58+0100\n"
+"Last-Translator: Karl Ove Hufthammer <karl@huftis.org>\n"
+"Language-Team: Norwegian Nynorsk <i18n-nn@lister.ping.uio.no>\n"
+"Language: nn\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 2.0\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory segments\n"
+"      --start                           Start the daemon if it is not running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [val]\n"
+"\n"
+"KOMMANDOAR:\n"
+"  -h, --help                            Vis denne hjelpeteksten.\n"
+"      --version                         Vis versjonsinformasjon.\n"
+"      --dump-conf                       Vis standardoppsett.\n"
+"      --dump-modules                    Vis tilgjengelege modular.\n"
+"      --dump-resample-methods           Vis tilgjengelege resamplingsmetodar.\n"
+"      --cleanup-shm                     Rydd opp i utdaterte segment i delt minne.\n"
+"      --start                           Start tenesta viss ho ikkje køyrer.\n"
+"  -k  --kill                            Tvangsavslutt teneste som køyrer.\n"
+"      --check                           Sjekk om teneste køyrer (returnerer\n"
+"                                        berre avsluttingskodar).\n"
+"\n"
+"VAL:\n"
+"      --system[=BOOL]                   Køyr i systemmodus.\n"
+"  -D, --daemonize[=BOOL]                Gjer om til teneste etter oppstart.\n"
+"      --fail[=BOOL]                     Avslutt viss mislukka oppstart.\n"
+"      --high-priority[=BOOL]            Prøv å bruka høgt «nice»-nivå\n"
+"                                        (berre tilgjengeleg som rotbrukar, viss SUID\n"
+"                                        eller med forhøga RLIMIT_NICE-verdi).\n"
+"      --realtime[=BOOL]                 Prøv å bruka sanntids oppgåvestyring\n"
+"                                        (berre tilgjengeleg som rotbrukar, viss SUID\n"
+"                                        eller med forhøga RLIMIT_RTPRIO-verdi).\n"
+"      --disallow-module-loading[=BOOL]  Ikkje tillèt brukar å lasta (ut)\n"
+"                                        modular etter oppstart.\n"
+"      --disallow-exit[=BOOL]            Ikkje tillèt brukar å avslutta.\n"
+"      --exit-idle-time=SEKUND           Avslutt tenesta etter så lang tid med tomgang.\n"
+"      --scache-idle-time=SEKUND         Last ut autolasta samplar etter så lang tid med tomgang.\n"
+"      --log-level[=NIVÅ]                Auk eller still detaljnivå i loggmeldingar.\n"
+"  -v  --verbose                         Vis meir detaljerte meldingar.\n"
+"      --log-target={auto,syslog,stderr,file:ADRESSE,newfile:ADRESSE}\n"
+"                                        Vel mål for logg.\n"
+"      --log-meta[=BOOL]                 Ta med kodeposisjon i loggmeldingar.\n"
+"      --log-time[=BOOL]                 Ta med tidsinformasjon i loggmeldingar.\n"
+"      --log-backtrace=RAMMER            Ta med tilbakespor i loggmeldingar.\n"
+"  -p, --dl-search-path=ADRESSE          Vel søkjemappe for dynamiske delte\n"
+"                                        objekt (programtillegg).\n"
+"      --resample-method=METODE          Bruk valt resamplingsmetode.\n"
+"                                        (Sjå «--dump-resample-methods» for\n"
+"                                        moglege verdiar.)\n"
+"      --use-pid-file[=BOOL]             Lag PID-fil.\n"
+"      --no-cpu-limit[=BOOL]             Ikkje installer lastavgrensing for CPU\n"
+"                                        på plattformer som støttar dette.\n"
+"      --disable-shm[=BOOL]              Ikkje bruk delt minne.\n"
+"      --enable-memfd[=BOOL]             Bruk memfd-basert delt minne.\n"
+"\n"
+"OPPSTARTSSKRIPT:\n"
+"  -L, --load=\"MODULARGUMENT\"            Last den valde tilleggsmodulen med\n"
+"                                        valt argument.\n"
+"  -F, --file=FILNAMN                    Køyr det valde skriptet.\n"
+"  -C                                    Opna kommandolinje på gjeldande TTY\n"
+"                                        etter oppstart.\n"
+"\n"
+"  -n                                    Ikkje last standard skriptfil.\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "«--daemonize» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "«--fail» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:265
+msgid "--log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error)."
+msgstr "«--log-level» forventar loggnivå-argument (anten eit tal frå 0 til 4 eller «debug», «info», «notice», «warn» eller «error»)"
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "«--high-priority» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "«--realtime» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "«--disallow-module-loading» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "«--disallow-exit» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "«--use-pid-file» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:328
+msgid "Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."
+msgstr "Ugyldig loggmål: Bruk anten «syslog», «journal», «stderr», «auto» eller eit gyldig filnamn på forma «file:<adresse>» eller «newfile:<adresse>»."
+
+#: ../src/daemon/cmdline.c:330
+msgid "Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."
+msgstr "Ugyldig loggmål: Bruk anten «syslog», «stderr», «auto» eller eit gyldig filnamn på forma «file:<adresse>» eller «newfile:<adresse>»."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "«--log-time» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "«--log-meta» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+#| msgid "Invalid filename: '%1'"
+msgid "Invalid resample method '%s'."
+msgstr "Ugyldig resamplingsmetode «%s»."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "«--system» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "«--no-cpu-limit» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "«--disable-shm» forventar boolsk argument"
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr "«--enable-memfd» forventar boolsk argument"
+
+#: ../src/daemon/daemon-conf.c:262
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Ugyldig loggmål «%s»."
+
+#: ../src/daemon/daemon-conf.c:277
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Ugyldig loggnivå «%s»."
+
+#: ../src/daemon/daemon-conf.c:292
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Ugyldig resamplingsmetode «%s»."
+
+#: ../src/daemon/daemon-conf.c:314
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Ugyldig rlimit «%s»."
+
+#: ../src/daemon/daemon-conf.c:334
+#, c-format
+#| msgctxt "QCoreApplication|"
+#| msgid "Invalid type for parameter '%1'"
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Ugyldig samplingsformat «%s»."
+
+#: ../src/daemon/daemon-conf.c:351 ../src/daemon/daemon-conf.c:368
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Ugyldig samplingsrate «%s»."
+
+#: ../src/daemon/daemon-conf.c:391
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Ugyldige samplingskanalar «%s»."
+
+#: ../src/daemon/daemon-conf.c:408
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Ugyldig kanaldefinisjon «%s»."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+#| msgctxt "syntax error in KWin script"
+#| msgid "Invalid number of arguments"
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Ugyldig tal på fragmentar «%s»."
+
+#: ../src/daemon/daemon-conf.c:442
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Ugyldig fragmentstorleik «%s»."
+
+# Sjå «man nice».
+#: ../src/daemon/daemon-conf.c:459
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Ugyldig «nice»-nivå «%s»."
+
+#: ../src/daemon/daemon-conf.c:502
+#, c-format
+#| msgid "Invalid source type."
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Ugyldig tenartype «%s»."
+
+#: ../src/daemon/daemon-conf.c:620
+#, c-format
+#| msgid "Unable to write configuration file %1"
+msgid "Failed to open configuration file: %s"
+msgstr "Klarte ikkje opna oppsettfil: %s"
+
+#: ../src/daemon/daemon-conf.c:636
+msgid "The specified default channel map has a different number of channels than the specified default number of channels."
+msgstr "Den valde standard-kanaldefinisjonen har ikkje det same talet på kanalar som valt standard tal på kanalar."
+
+# «Read» er her preteritum.
+#: ../src/daemon/daemon-conf.c:723
+#, c-format
+#| msgid "project configuration file"
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Lese frå oppsettfila: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Namn: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+#| msgid "No about information available."
+msgid "No module information available\n"
+msgstr "Modulinformasjon er ikkje tilgjengeleg\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Versjon: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Skildring: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Opphavsperson: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Bruk: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+#| msgid "Name: %s\n"
+msgid "Load Once: %s\n"
+msgstr "Last éin gong: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "ÅTVARING OM UTDATERT FUNKSJONALITET: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+#| msgid "Path: %1"
+msgid "Path: %s\n"
+msgstr "Adresse: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+#| msgid "Failed to open file"
+msgid "Failed to open module %s: %s"
+msgstr "Klarte ikkje opna modulen %s: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+#| msgid "Failed to overwrite original file"
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Fann ikkje opphavleg lt_dlopen-lastar."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+#| msgid "Failed to create the template."
+msgid "Failed to allocate new dl loader."
+msgstr "Klarte ikkje reservera minne til ny dl-lastar."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+#| msgid "Failed to build index."
+msgid "Failed to add bind-now-loader."
+msgstr "Klarte ikkje leggja til bind-now-lastar."
+
+#: ../src/daemon/main.c:160
+#, c-format
+#| msgid "Failed to load script: '%1'."
+msgid "Failed to find user '%s'."
+msgstr "Fann ikkje brukaren «%s»."
+
+#: ../src/daemon/main.c:165
+#, c-format
+#| msgid "Failed to load script: '%1'."
+msgid "Failed to find group '%s'."
+msgstr "Fann ikkje gruppa «%s»."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID-verdiane til brukaren «%s» og gruppa «%s» er ikkje like."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Heimemappa til brukaren «%s» er ikkje «%s». Ignorerer."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+#| msgid "Failed to create resource: %1"
+msgid "Failed to create '%s': %s"
+msgstr "Klarte ikkje oppretta «%s»: %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+#| msgid "Failed to group devices: '%1'"
+msgid "Failed to change group list: %s"
+msgstr "Klarte ikkje endra gruppeliste: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+#| msgid "Failed to cancel '%1'"
+msgid "Failed to change GID: %s"
+msgstr "Klarte ikkje endra GID: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+#| msgid "Failed to cancel '%1'"
+msgid "Failed to change UID: %s"
+msgstr "Klarte ikkje endra UID: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "Systemmodus er ikkje støtta på denne plattforma."
+
+#: ../src/daemon/main.c:484
+#| msgctxt "@info"
+#| msgid "Failed to execute command"
+msgid "Failed to parse command line."
+msgstr "Klarte ikkje tolka kommandolinja."
+
+# Sjå https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/ConnectingToServer/
+#: ../src/daemon/main.c:523
+msgid "System mode refused for non-root user. Only starting the D-Bus server lookup service."
+msgstr "Systemmodus er ikkje tilgjengeleg for brukar som ikkje er rot. Startar berre teneste for D-Bus-tenaroppslag."
+
+#: ../src/daemon/main.c:622
+#, c-format
+#| msgid "Failed to mount %1"
+msgid "Failed to kill daemon: %s"
+msgstr "Klarte ikkje tvangsavslutta tenesta: %s"
+
+#: ../src/daemon/main.c:651
+msgid "This program is not intended to be run as root (unless --system is specified)."
+msgstr "Dette programmet er ikkje meint å køyrast som rotbrukar (med mindre du spesifiserer «--system»)."
+
+#: ../src/daemon/main.c:654
+#| msgid "Two arguments required"
+msgid "Root privileges required."
+msgstr "Krev rotløyve."
+
+#: ../src/daemon/main.c:661
+#| msgctxt "Error message in VPN import/export dialog"
+#| msgid "Operation not supported for this VPN type."
+msgid "--start not supported for system instances."
+msgstr "«--start» er ikkje støtta for systeminstansar."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr "Brukaroppsett tenar på «%s». Nektar å starta/autostarta."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, which appears to be local. Probing deeper."
+msgstr "Brukaroppsett tenar på «%s», som ser ut til å vera lokal. Undersøkjer nærmare."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr "Køyrer i systemmodus, men «--disallow-exit» er ikkje valt."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr "Køyrer i systemmodus, men «--disallow-module-loading» er ikkje valt."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr "Køyrer i systemmodus – tvingar SHM-modus av."
+
+# Sjå definisjon av «exit-idle-time» på https://wiki.archlinux.org/index.php/PulseAudio/Configuration
+# Dette er nok den beste kortomsettinga me kan ha.
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr "Køyrer i systemmodus – tvingar «exit-idle-time» av."
+
+#: ../src/daemon/main.c:756
+#| msgid "Failed to create Action."
+msgid "Failed to acquire stdio."
+msgstr "Fekk ikkje tilgang til stdio."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+#| msgid "Upload failed: %1"
+msgid "pipe() failed: %s"
+msgstr "Feil ved pipe()-kall: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+#| msgid "Upload failed: %1"
+msgid "fork() failed: %s"
+msgstr "Feil ved fork()-kall: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:562
+#, c-format
+#| msgid "read failed (%1)"
+msgid "read() failed: %s"
+msgstr "Feil ved read()-kall: %s"
+
+#: ../src/daemon/main.c:788
+#| msgid "The software update failed."
+msgid "Daemon startup failed."
+msgstr "Klarte ikkje starta tenesta."
+
+#: ../src/daemon/main.c:821
+#, c-format
+#| msgctxt "Assertion failed in KWin script with given value"
+#| msgid "Assertion failed: %1"
+msgid "setsid() failed: %s"
+msgstr "Feil ved setsid()-kall: %s"
+
+#: ../src/daemon/main.c:948
+#| msgid "Failed to fetch item."
+msgid "Failed to get machine ID"
+msgstr "Klarte ikkje henta maskin-ID"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system mode is usually a bad idea."
+msgstr ""
+"Du køyrer PulseAudio i systemmodus. Du bør sikra deg at du faktisk ønskjer å gjera dette.\n"
+"Les http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/for ei forklaring på korfor det vanlegvis ikkje er lurt å køyra i systemmodus."
+
+#: ../src/daemon/main.c:990
+#| msgid "The file reader failed."
+msgid "pa_pid_file_create() failed."
+msgstr "Mislukka pa_pid_file_create()."
+
+#: ../src/daemon/main.c:1022
+#| msgid "Operation failed."
+msgid "pa_core_new() failed."
+msgstr "Mislukka pa_core_new()."
+
+#: ../src/daemon/main.c:1092
+#| msgid "Failed to finalize"
+msgid "Failed to initialize daemon."
+msgstr "Klarte ikkje gjera klar tenesta."
+
+#: ../src/daemon/main.c:1097
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Oppstart av teneste utan nokon lasta modular. Nektar å arbeida."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+#| msgid "PulseAudio Sound Server"
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio lydsystem"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+#| msgid "PulseAudio Sound Server"
+msgid "Start the PulseAudio Sound System"
+msgstr "Start PulseAudio-lydsystemet"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+#| msgctxt "What the user inputs now will be taken as the new shortcut"
+#| msgid "Input"
+msgid "Input"
+msgstr "Lyd inn"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+#| msgid "Docking station"
+msgid "Docking Station Input"
+msgstr "Lyd inn frå dokkingstasjon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+#| msgid "Docking station"
+msgid "Docking Station Microphone"
+msgstr "Mikrofon på dokkingstasjon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+#| msgid "Docking station"
+msgid "Docking Station Line In"
+msgstr "Linje inn på dokkingstasjon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Linje inn"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1750
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+#| msgid "Mute Microphone"
+msgid "Front Microphone"
+msgstr "Frontmikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+#| msgid "Mute Microphone"
+msgid "Rear Microphone"
+msgstr "Bakmikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+#| msgid "Mute Microphone"
+msgid "External Microphone"
+msgstr "Ekstern mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+#| msgid "Mute Microphone"
+msgid "Internal Microphone"
+msgstr "Intern mikrofonen"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Video"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+#| msgid "Automatic Filter"
+msgid "Automatic Gain Control"
+msgstr "Automatisk lydnivåstyring (AGC)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+#| msgid "Navigation Control"
+msgid "No Automatic Gain Control"
+msgstr "Inga automatisk lydnivåstyring (AGC)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+#| msgid "Rooster"
+msgid "Boost"
+msgstr "Lydforsterking"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+#| msgid "No Bookmarks"
+msgid "No Boost"
+msgstr "Inga lydforsterking"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Forsterkar"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+#| msgid "Not specified"
+msgid "No Amplifier"
+msgstr "Ingen forsterkar"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+#| msgctxt "QShortcut"
+#| msgid "Bass Boost"
+msgid "Bass Boost"
+msgstr "Bassforsterking"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+#| msgctxt "QShortcut"
+#| msgid "Bass Boost"
+msgid "No Bass Boost"
+msgstr "Inga bassforsterking"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1757
+msgid "Speaker"
+msgstr "Høgtalar"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+#| msgctxt "This device is a Headphones"
+#| msgid "Headphones"
+msgid "Headphones"
+msgstr "Hovudtelefonar"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+#| msgid "Async Input"
+msgid "Analog Input"
+msgstr "Analog innlyd"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+#| msgid "Microphone"
+msgid "Dock Microphone"
+msgstr "Mikrofon på dokkingstasjon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+#| msgid "Mute Microphone"
+msgid "Headset Microphone"
+msgstr "Mikrofon på hovudsett"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+#| msgid "Page Output"
+msgid "Analog Output"
+msgstr "Analog utlyd"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "LFE på separat monokanal"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Linje ut"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+#| msgid "Page Output"
+msgid "Analog Mono Output"
+msgstr "Analog mono-utlyd"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+#| msgid "Speaker"
+msgid "Speakers"
+msgstr "Høgtalarar"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+#| msgid "Display format"
+msgid "HDMI / DisplayPort"
+msgstr "HDMI/DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Digital utlyd (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Digital innlyd (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digital vidaresending (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+#| msgid "Multi-Channel"
+msgid "Multichannel Input"
+msgstr "Multikanals innlyd"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+#| msgid "Multi-Channel"
+msgid "Multichannel Output"
+msgstr "Multikanals utlyd"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+#| msgctxt "Analog wireline modem"
+#| msgid "Analog"
+msgid "Analog Mono"
+msgstr "Analog mono"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+#| msgid "Joint Stereo"
+msgid "Analog Stereo"
+msgstr "Analog stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+#| msgid "Multi-Channel"
+msgid "Multichannel"
+msgstr "Multikanals"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Analog kringlyd 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Analog kringlyd 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Analog kringlyd 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Analog kringlyd 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Analog kringlyd 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Analog kringlyd 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Analog kringlyd 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Analog kringlyd 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Analog kringlyd 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Analog kringlyd 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Analog kringlyd 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Digital stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digital vidaresending (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digital kringlyd 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digital kringlyd 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Digital kringlyd 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+#| msgctxt "Comment"
+#| msgid "Digital Bees"
+msgid "Digital Stereo (HDMI)"
+msgstr "Digital stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digital kringlyd 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4019 ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+#| msgid "Manual Duplex"
+msgid "Analog Mono Duplex"
+msgstr "Analog mono dupleks"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+#| msgid "Manual Duplex"
+msgid "Analog Stereo Duplex"
+msgstr "Analog stereo dupleks"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Digital stereo duplex (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+#| msgid "Multi-Channel"
+msgid "Multichannel Duplex"
+msgstr "Multikanals dupleks"
+
+#: ../src/modules/alsa/alsa-mixer.c:4156
+#| msgid "Stereo Mode"
+msgid "Stereo Duplex"
+msgstr "Stereo dupleks"
+
+#: ../src/modules/alsa/alsa-mixer.c:4157
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2324
+#: ../src/modules/bluetooth/module-bluez5-device.c:2005
+msgid "Off"
+msgstr "Av"
+
+#: ../src/modules/alsa/alsa-mixer.c:4256
+#, c-format
+#| msgid "Outputs"
+msgid "%s Output"
+msgstr "%s utlyd"
+
+#: ../src/modules/alsa/alsa-mixer.c:4264
+#, c-format
+#| msgctxt "What the user inputs now will be taken as the new shortcut"
+#| msgid "Input"
+msgid "%s Input"
+msgstr "%s innlyd"
+
+#: ../src/modules/alsa/alsa-sink.c:572
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."
+msgstr ""
+"ALSA vekte oss for å skriva meir data til eininga, men det fanst ikkje noko data å skriva.\n"
+"Dette kjem truleg av ein feil i ALSA-drivaren «%s». Meld frå om problemet til ALSA-utviklarane.\n"
+"Me vart vekte med POLLOUT definert, men eit seinare snd_pcm_avail()-kall gav verdien 0 eller ein annan verdi < min_avail."
+
+#: ../src/modules/alsa/alsa-sink.c:756
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."
+msgstr ""
+"ALSA vekte oss for å skriva meir data til eininga, men det fanst ikkje noko data å skriva.\n"
+"Dette kjem truleg av ein feil i ALSA-drivaren «%s». Meld frå om problemet til ALSA-utviklarane.\n"
+"Me vart vekte med POLLOUT definert, men eit seinare snd_pcm_avail()-kall gav verdien 0 eller ein annan verdi < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."
+msgstr ""
+"ALSA vekte oss for å lesa meir data frå eininga, men det fanst ikkje noko data å lesa.\n"
+"Dette kjem truleg av ein feil i ALSA-drivaren «%s». Meld frå om problemet til ALSA-utviklarane.\n"
+"Me vart vekte med POLLIN definert, men eit seinare subsequent snd_pcm_avail()-kall gav verdien 0 eller ein annan verdi < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."
+msgstr ""
+"ALSA vekte oss for å lesa meir data frå eininga, men det fanst ikkje noko data å lesa.\n"
+"Dette kjem truleg av ein feil i ALSA-drivaren «%s». Meld frå om problemet til ALSA-utviklarane.\n"
+"Me vart vekte med POLLIN definert, men eit seinare subsequent snd_pcm_avail()-kall gav verdien 0 eller ein annan verdi < min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1168 ../src/modules/alsa/alsa-util.c:1243
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() gav ein verdi som er uvanleg stor: %lu byte (%lu ms).\n"
+"Dette kjem truleg av ein feil i ALSA-drivaren «%s». Meld frå om problemet til ALSA-utviklarane."
+
+#: ../src/modules/alsa/alsa-util.c:1218
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() gav ein verdi som er uvanleg stor: %li byte (%s%lu ms).\n"
+"Dette kjem truleg av ein feil i ALSA-drivaren «%s». Meld frå om problemet til ALSA-utviklarane."
+
+#: ../src/modules/alsa/alsa-util.c:1259
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail %lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() gav ein merkeleg verdi: delay %lu er mindre enn avail %lu.\n"
+"Dette kjem truleg av ein feil i ALSA-drivaren «%s». Meld frå om problemet til ALSA-utviklarane."
+
+#: ../src/modules/alsa/alsa-util.c:1302
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() gav ein verdi som er uvanleg stor: %lu byte (%lu ms).\n"
+"Dette kjem truleg av ein feil i ALSA-drivaren «%s». Meld frå om problemet til ALSA-utviklarane."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1740
+#| msgctxt "This device is a Headset"
+#| msgid "Headset"
+msgid "Headset"
+msgstr "Hovudsett"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1745
+#| msgctxt "For holding pieces"
+#| msgid "Hand"
+msgid "Handsfree"
+msgstr "Handfri"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1763
+msgid "Headphone"
+msgstr "Hovudtelefonar"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1768
+#| msgctxt "portugal_districts.kgm"
+#| msgid "Portalegre"
+msgid "Portable"
+msgstr "Portabel"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1773
+msgid "Car"
+msgstr "Bil"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1778
+msgid "HiFi"
+msgstr "Hi-fi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2134
+#: ../src/modules/bluetooth/module-bluez5-device.c:1783
+#| msgctxt "Name"
+#| msgid "Phone"
+msgid "Phone"
+msgstr "Telefon"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2142
+#: ../src/modules/bluetooth/module-bluez5-device.c:1735
+#: ../src/modules/bluetooth/module-bluez5-device.c:1751
+#: ../src/modules/bluetooth/module-bluez5-device.c:1789
+#| msgctxt "Bluetooth was enabled, keep short"
+#| msgid "Bluetooth On"
+msgid "Bluetooth Output"
+msgstr "Bluetooth-utlyd"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2145
+#: ../src/modules/bluetooth/module-bluez5-device.c:1734
+#: ../src/modules/bluetooth/module-bluez5-device.c:1756
+#: ../src/modules/bluetooth/module-bluez5-device.c:1762
+#: ../src/modules/bluetooth/module-bluez5-device.c:1788
+#| msgctxt "Bluetooth was enabled, keep short"
+#| msgid "Bluetooth On"
+msgid "Bluetooth Input"
+msgstr "Bluetooth-innlyd"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2186
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Hi-fi-avspeling (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2198
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Hi-fi-opptak (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2210
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telefon dupleks (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2223
+msgid "Handsfree Gateway"
+msgstr "Handfri-portnar"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1830
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Hi-fi-avspeling (A2DP-sluk)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1842
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Hi-fi-opptak (A2DP-kjelde)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1854
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Hovudsett-hovudeining (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1867
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Hovudsett-lydportnar (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid "source_name=<name for the source> source_properties=<properties for the source> source_master=<name of source to filter> sink_name=<name for the sink> sink_properties=<properties for the sink> sink_master=<name of sink to filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how much drift to readjust after in ms> format=<sample format> rate=<sample rate> channels=<number of channels> channel_map=<channel map> aec_method=<implementation to use> aec_args=<parameters for the AEC engine> save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes or no> "
+msgstr "source_name=<namn på kjelde> source_properties=<eigenskapar til kjelde> source_master=<namn på kjelde å filtrera> sink_name=<namn på sluk> sink_properties=<eigenskapar til sluk> sink_master=<namn på sluk å filtrera> adjust_time=<kor ofte ratar skal rejusterast, målt i sekund> adjust_threshold=<kor mykje drift å rejustera etter, målt i millisekund> format=<samplingsformat> rate=<samplingsrate> channels=<talet på kanalar> channel_map=<kanaldefinisjon> aec_method=<implementering å bruka> aec_args=<parametrar for AEC-motoren> save_aec=<lagra AEC-data i /tmp> autoloaded=<vel dette viss modulen vert lasta automatisk> use_volume_sharing=<yes eller no> use_master_format=<yes eller no> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "På"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+#| msgid "Debug Output"
+msgid "Dummy Output"
+msgstr "Dummy-uteining"
+
+#: ../src/modules/module-always-sink.c:36
+#| msgid "Always try to have at least one preloaded instance"
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Held alltid minst eitt sluk lasta, sjølv om det er eit null-sluk"
+
+#: ../src/modules/module-equalizer-sink.c:70
+#| msgid "General Properties"
+msgid "General Purpose Equalizer"
+msgstr "Generell lydbalansekontroll"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid "sink_name=<name of the sink> sink_properties=<properties for the sink> sink_master=<sink to connect to> format=<sample format> rate=<sample rate> channels=<number of channels> channel_map=<channel map> autoloaded=<set if this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr "sink_name=<namn på sluk> sink_properties=<eigenskapar til sluk> sink_master=<sluk å kopla til> format=<samplingsformat> rate=<samplingsrate> channels=<talet på kanalar> channel_map=<kanaldefinisjon> autoloaded=<vel dette viss modulen vert lasta automatisk> use_volume_sharing=<yes eller no> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<skal ubrukte filter automatisk lastast ut?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+#| msgctxt "process heading"
+#| msgid "Virtual Size"
+msgid "Virtual LADSPA sink"
+msgstr "Virtuelt LADSPA-sluk"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid "sink_name=<name for the sink> sink_properties=<properties for the sink> master=<name of sink to filter> sink_master=<name of sink to filter> format=<sample format> rate=<sample rate> channels=<number of channels> channel_map=<input channel map> plugin=<ladspa plugin name> label=<ladspa plugin label> control=<comma separated list of input control values> input_ladspaport_map=<comma separated list of input LADSPA port names> output_ladspaport_map=<comma separated list of output LADSPA port names> autoloaded=<set if this module is being loaded automatically> "
+msgstr "sink_name=<namn på sluk> sink_properties=<eigenskapar for sluk> master=<namn på sluk å filtrera> sink_master=<namn på sluk å filtrera> format=<samplingsformat> rate=<samplingsrate> channels=<talet på kanalar> channel_map=<kanaldefinisjon for inndata> plugin=<namn på LADSPA-tillegg> label=<skildring av LADSPA-tillegg> control=<kommadelt liste med kontrollverdiar for inndata> input_ladspaport_map=<kommadelt liste med portnamn for LADSP-inndata> output_ladspaport_map=<kommadelt liste med portnamn for LADSP-utdata> autoloaded=<vel dette viss modulen vert lasta automatisk> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Klokka NULL-sluk"
+
+#: ../src/modules/module-null-sink.c:284
+#| msgid "Full Output"
+msgid "Null Output"
+msgstr "Null-utdata"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+#| msgctxt "@title:tab"
+#| msgid "Output Devices"
+msgid "Output Devices"
+msgstr "Uteiningar"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+#| msgctxt "@title:tab"
+#| msgid "Input Devices"
+msgid "Input Devices"
+msgstr "Inneiningar"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Lyd på @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Tunell for %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:521
+#: ../src/modules/module-tunnel-source-new.c:520
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Tunell til %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+#| msgid "surround sound"
+msgid "Virtual surround sink"
+msgstr "Virtuelt kringlyd-sluk"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid "sink_name=<name for the sink> sink_properties=<properties for the sink> master=<name of sink to filter> sink_master=<name of sink to filter> format=<sample format> rate=<sample rate> channels=<number of channels> channel_map=<channel map> use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/left_hrir.wav autoloaded=<set if this module is being loaded automatically> "
+msgstr "sink_name=<namn på sluk> sink_properties=<eigenskapar for sluk> master=<namn på sluk å filtrera> sink_master=<namn på sluk å filtrera> format=<samplingsformat> rate=<samplingsrate> channels=<talet på kanalar> channel_map=<kanaldefinisjon> use_volume_sharing=<yes eller no> force_flat_volume=<yes eller no> hrir=/adresse/til/left_hrir.wav autoloaded=<vel dette viss modulen vert lasta automatisk> "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio-lydtenar"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Framme i midten"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Framme til venstre"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Framme til høgre"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Bak i midten"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Bak til venstre"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Bak til høgre"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Basshøgtalar"
+
+#: ../src/pulse/channelmap.c:115
+#| msgid "Front Left of Center"
+msgid "Front Left-of-center"
+msgstr "Framme til venstre for midten"
+
+#: ../src/pulse/channelmap.c:116
+#| msgid "Front Right of Center"
+msgid "Front Right-of-center"
+msgstr "Framme til høgre for midten"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Til venstre"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Til høgre"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Aux 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Aux 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Aux 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Aux 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Aux 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Aux 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Aux 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Aux 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Aux 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Aux 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Aux 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Aux 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Aux 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Aux 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Aux 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Aux 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Aux 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Aux 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Aux 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Aux 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Aux 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Aux 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Aux 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Aux 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Aux 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Aux 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Aux 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Aux 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Aux 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Aux 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Aux 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Aux 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Oppe og i midten"
+
+#: ../src/pulse/channelmap.c:156
+#| msgid "Front Center"
+msgid "Top Front Center"
+msgstr "Oppe og framme i midten"
+
+#: ../src/pulse/channelmap.c:157
+#| msgid "Front Left"
+msgid "Top Front Left"
+msgstr "Oppe og framme til venstre"
+
+#: ../src/pulse/channelmap.c:158
+#| msgid "Front Right"
+msgid "Top Front Right"
+msgstr "Oppe og framme til høgre"
+
+#: ../src/pulse/channelmap.c:160
+#| msgid "Rear Center"
+msgid "Top Rear Center"
+msgstr "Oppe og bak i midten"
+
+#: ../src/pulse/channelmap.c:161
+#| msgid "Rear Left"
+msgid "Top Rear Left"
+msgstr "Oppe og bak til venstre"
+
+#: ../src/pulse/channelmap.c:162
+#| msgid "Rear Right"
+msgid "Top Rear Right"
+msgstr "Oppe og bak til høgre"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:177 ../src/pulse/volume.c:306
+#: ../src/pulse/volume.c:332 ../src/pulse/volume.c:352
+#: ../src/pulse/volume.c:384 ../src/pulse/volume.c:424
+#: ../src/pulse/volume.c:443
+msgid "(invalid)"
+msgstr "(ugyldig)"
+
+#: ../src/pulse/channelmap.c:780
+#| msgctxt "Channel name"
+#| msgid "Surround Left"
+msgid "Surround 4.0"
+msgstr "Kringlyd 4.0"
+
+#: ../src/pulse/channelmap.c:786
+#| msgctxt "Channel name"
+#| msgid "Surround Left"
+msgid "Surround 4.1"
+msgstr "Kringlyd 4.1"
+
+#: ../src/pulse/channelmap.c:792
+#| msgctxt "Channel name"
+#| msgid "Surround Left"
+msgid "Surround 5.0"
+msgstr "Kringlyd 5.0"
+
+#: ../src/pulse/channelmap.c:798
+#| msgctxt "Channel name"
+#| msgid "Surround Left"
+msgid "Surround 5.1"
+msgstr "Kringlyd 5.1"
+
+#: ../src/pulse/channelmap.c:805
+#| msgctxt "Channel name"
+#| msgid "Surround Left"
+msgid "Surround 7.1"
+msgstr "Kringlyd 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+#| msgid "Connection Failed"
+msgid "xcb_connect() failed"
+msgstr "Feil ved xcb_connect()-kall"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() returnerte sann"
+
+#: ../src/pulse/client-conf-x11.c:94
+#| msgid "Failed to read data."
+msgid "Failed to parse cookie data"
+msgstr "Klarte ikkje tolka data i infokapsel"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+# Er her snakk om protokollutvidingar, ikkje filtetternamn.
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Fekk melding for ukjend protokollutviding «%s»"
+
+#: ../src/pulse/direction.c:37
+#| msgctxt "What the user inputs now will be taken as the new shortcut"
+#| msgid "Input"
+msgid "input"
+msgstr "inndata"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "utdata"
+
+#: ../src/pulse/direction.c:41
+#| msgid "Bidirectional"
+msgid "bidirectional"
+msgstr "tovegs"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "ugyldig"
+
+#: ../src/pulsecore/core-util.c:1856
+#, c-format
+msgid "XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could e g happen if you try to connect to a non-root PulseAudio as a root user, over the native protocol. Don't do that.)"
+msgstr "XDG_RUNTIME_DIR (%s) er ikkje eigd av oss (uid %d), men av UID-en %d. (Dette problemet kan oppstå viss du for eksempel som rotbrukar prøver å kopla til ein PulseAudio-instans som ikkje køyrer som rot og gjer dette over «native»-protokollen. Ikkje gjer dette.)"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "ja"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "nei"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+#| msgid "Cannot obtain lock"
+msgid "Cannot access autospawn lock."
+msgstr "Får ikkje tilgang til autostart-låsen."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+#| msgid "Failed to open the file %1"
+msgid "Failed to open target file '%s'."
+msgstr "Klarte ikkje opna målfila «%s»."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid "Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr "Prøvde å opna målfilene «%s», «%s.1», «%s.2», …, «%s.%d», men fekk ikkje tilgang til nokon av dei."
+
+#: ../src/pulsecore/log.c:651
+#| msgid "Invalid parent"
+msgid "Invalid log target."
+msgstr "Ugyldig loggmål."
+
+#: ../src/pulsecore/sink.c:3466
+#| msgctxt "Balancebox|"
+#| msgid "Built-in"
+msgid "Built-in Audio"
+msgstr "Innebygd lyd"
+
+#: ../src/pulsecore/sink.c:3471
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Nekta tilgang"
+
+#: ../src/pulse/error.c:40
+#| msgid "Unknown command '%1'"
+msgid "Unknown command"
+msgstr "Ukjend kommando"
+
+#: ../src/pulse/error.c:41
+#| msgid "Invalid document."
+msgid "Invalid argument"
+msgstr "Ugyldig argument"
+
+#: ../src/pulse/error.c:42
+#| msgid "File exists"
+msgid "Entity exists"
+msgstr "Entiteten finst"
+
+#: ../src/pulse/error.c:43
+#| msgid "No such item."
+msgid "No such entity"
+msgstr "Entiteten finst ikkje"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Nekta tilkopling"
+
+#: ../src/pulse/error.c:45
+#| msgid "Protocol error."
+msgid "Protocol error"
+msgstr "Protokollfeil"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Tidsavbrot"
+
+#: ../src/pulse/error.c:47
+#| msgid "Network authentication"
+msgid "No authentication key"
+msgstr "Manglar autentiseringsnøkkel"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Intern feil"
+
+#: ../src/pulse/error.c:49
+#| msgctxt "Name"
+#| msgid "Connection activated"
+msgid "Connection terminated"
+msgstr "Tilkoplinga vart avslutta"
+
+#: ../src/pulse/error.c:50
+#| msgid "Untitled"
+msgid "Entity killed"
+msgstr "Entiteten vart drepen"
+
+#: ../src/pulse/error.c:51
+#| msgid "Invalid Driver"
+msgid "Invalid server"
+msgstr "Ugyldig tenar"
+
+#: ../src/pulse/error.c:52
+#| msgid "Script initialization failed"
+msgid "Module initialization failed"
+msgstr "Feil ved modulstart"
+
+#: ../src/pulse/error.c:53
+#| msgid "Invalid state"
+msgid "Bad state"
+msgstr "Ugyldig tilstand"
+
+#: ../src/pulse/error.c:54
+#| msgid "No debug data"
+msgid "No data"
+msgstr "Ingen data"
+
+#: ../src/pulse/error.c:55
+#| msgid "Incompatible OpenGL version detected"
+msgid "Incompatible protocol version"
+msgstr "Inkompatibel protokollversjon"
+
+#: ../src/pulse/error.c:56
+#| msgid "large"
+msgid "Too large"
+msgstr "For stor"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Ikkje støtta"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Ukjend feilkode"
+
+#: ../src/pulse/error.c:59
+#| msgid "&Source extension:"
+msgid "No such extension"
+msgstr "Protokollutvidinga finst ikkje"
+
+#: ../src/pulse/error.c:60
+#| msgctxt "@label Number of obsolete translations"
+#| msgid "Obsolete Translations"
+msgid "Obsolete functionality"
+msgstr "Utdatert funksjonalitet"
+
+#: ../src/pulse/error.c:61
+#| msgid "Original implementation"
+msgid "Missing implementation"
+msgstr "Manglande implementasjon"
+
+#: ../src/pulse/error.c:62
+#| msgid "Client Features"
+msgid "Client forked"
+msgstr "Klient forka"
+
+#: ../src/pulse/error.c:63
+#| msgid "Disk Input/Output"
+msgid "Input/Output error"
+msgstr "Inndata/utdata-feil"
+
+#: ../src/pulse/error.c:64
+#| msgid "Service for source"
+msgid "Device or resource busy"
+msgstr "Eininga eller ressursen er oppteken"
+
+#: ../src/pulse/sample.c:179
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %u Hz"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+#| msgid "%1 GiB"
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+#| msgid "%1 MiB"
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+#| msgid "%1 KiB"
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:197
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:134
+#, c-format
+#| msgid "Failed to rip title %1"
+msgid "Failed to drain stream: %s"
+msgstr "Klarte ikkje tømma straumen: %s"
+
+#: ../src/utils/pacat.c:139
+#| msgid "Playback Streams"
+msgid "Playback stream drained."
+msgstr "Avspelingsstraumen er tømt."
+
+#: ../src/utils/pacat.c:150
+#| msgid "Error connecting to server."
+msgid "Draining connection to server."
+msgstr "Tømmer sambandet til tenaren."
+
+#: ../src/utils/pacat.c:163
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:194 ../src/utils/pacat.c:543
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "Feil ved pa_stream_begin_write()-kall: %s"
+
+#: ../src/utils/pacat.c:244 ../src/utils/pacat.c:274
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "Feil ved pa_stream_peek()-kall: %s"
+
+# Merk at det passar betre med «Straum er oppretta» enn «Straumen er no oppretta» her, sidan teksten står saman med fleire på liknande form.
+#: ../src/utils/pacat.c:324
+#| msgid "ISO image successfully created."
+msgid "Stream successfully created."
+msgstr "Straum er oppretta."
+
+#: ../src/utils/pacat.c:327
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "Feil ved pa_stream_get_buffer_attr()-kall: %s"
+
+#: ../src/utils/pacat.c:331
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Buffer-verdiar: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:334
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Buffer-verdiar: maxlength=%u, fragsize=%u"
+
+# Forklaring på «sample spec»: https://freedesktop.org/software/pulseaudio/doxygen/sample.html
+# Vil for eksempel sjå slik ut: «s16le 2ch 44100 Hz»
+#: ../src/utils/pacat.c:338
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Brukar samplingsspesifikasjonen «%s» og kanaldefinisjonen «%s»."
+
+#: ../src/utils/pacat.c:342
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Kopla til eininga «%s» (indeks: %u, kvilemodus: %s)."
+
+#: ../src/utils/pacat.c:352
+#, c-format
+#| msgid "Stream error"
+msgid "Stream error: %s"
+msgstr "Feil i straum: %s"
+
+#: ../src/utils/pacat.c:362
+#, c-format
+#| msgid "System device name:"
+msgid "Stream device suspended.%s"
+msgstr "Straumingseining sett i kvilemodus.%s"
+
+#: ../src/utils/pacat.c:364
+#, c-format
+#| msgid "System device name:"
+msgid "Stream device resumed.%s"
+msgstr "Straumingseining vekt frå kvilemodus.%s"
+
+#: ../src/utils/pacat.c:372
+#, c-format
+#| msgid "Stream error"
+msgid "Stream underrun.%s"
+msgstr "Tom straumbuffer.%s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+#| msgid "Stream error"
+msgid "Stream overrun.%s"
+msgstr "Full straumbuffer.%s"
+
+#: ../src/utils/pacat.c:386
+#, c-format
+#| msgid "Game started..."
+msgid "Stream started.%s"
+msgstr "Straum starta.%s"
+
+#: ../src/utils/pacat.c:393
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Straum flytta til eininga %s (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:393
+#| msgid "not in"
+msgid "not "
+msgstr "ikkje "
+
+#: ../src/utils/pacat.c:400
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Stream-bufferattributt endra.%s"
+
+# Korking er, så vidt eg har forstått, når lydeiningar/straumar vert slegne av, dvs. data vert ikkje send/motteken til/frå dei. Me snakkar jo om korking og avkorking på norsk òg, så dette er ei naturleg omsetting.
+#: ../src/utils/pacat.c:415
+msgid "Cork request stack is empty: corking stream"
+msgstr "Korkingsførespurnad-stabelen er tom: korkar straumen"
+
+#: ../src/utils/pacat.c:421
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Korkingsførespurnad-stabelen er tom: avkorkar straumen"
+
+#: ../src/utils/pacat.c:425
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr "Åtvaring: Fekk fleire avkorkingsførespurnadar enn korkingsførespurnadar."
+
+#: ../src/utils/pacat.c:450
+#, c-format
+#| msgid "Connection established."
+msgid "Connection established.%s"
+msgstr "Tilkopling er oppretta.%s"
+
+#: ../src/utils/pacat.c:453
+#, c-format
+#| msgid "Registration failed: %1"
+msgid "pa_stream_new() failed: %s"
+msgstr "Feil ved pa_stream_new()-kall: %s"
+
+#: ../src/utils/pacat.c:491
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "Feil ved pa_stream_connect_playback()-kall: %s"
+
+#: ../src/utils/pacat.c:497
+#, c-format
+#| msgid "Failed to create resource: %1"
+msgid "Failed to set monitor stream: %s"
+msgstr "Klarte ikkje velja avlyttingsstraum: %s"
+
+#: ../src/utils/pacat.c:501
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "Feil ved pa_stream_connect_record()-kall: %s"
+
+#: ../src/utils/pacat.c:514 ../src/utils/pactl.c:1446
+#, c-format
+#| msgctxt "Name"
+#| msgid "Connection failure"
+msgid "Connection failure: %s"
+msgstr "Tilkoplingsfeil: %s"
+
+#: ../src/utils/pacat.c:557
+msgid "Got EOF."
+msgstr "Fekk EOF (filslutt)."
+
+#: ../src/utils/pacat.c:581
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "Feil ved pa_stream_write()-kall: %s"
+
+#: ../src/utils/pacat.c:605
+#, c-format
+#| msgid "File writer failed."
+msgid "write() failed: %s"
+msgstr "Feil ved write()-kall: %s"
+
+#: ../src/utils/pacat.c:626
+msgid "Got signal, exiting."
+msgstr "Fekk signal. Avsluttar."
+
+#: ../src/utils/pacat.c:640
+#, c-format
+#| msgctxt "Name"
+#| msgid "Failed to get secrets"
+msgid "Failed to get latency: %s"
+msgstr "Klarte ikkje henta latenstid: %s"
+
+#: ../src/utils/pacat.c:645
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Tid: %0.3f s. Latenstid: %0.0f µs."
+
+#: ../src/utils/pacat.c:666
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "Feil ved pa_stream_update_timing_info()-kall: %s"
+
+# «How to call» ser ut til å vera feil på engelsk. Det er snakk om *kva*, ikkje *korleis*, ein skal kalla klienten og straumen.
+#: ../src/utils/pacat.c:676
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to connect to\n"
+"  -n, --client-name=NAME                How to call this client on the server\n"
+"      --stream-name=NAME                How to call this stream on the server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to 44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the default\n"
+"      --fix-format                      Take the sample format from the sink/source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the channel map\n"
+"                                        from the sink/source the stream is being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of name.\n"
+"      --latency=BYTES                   Request the specified latency in bytes.\n"
+"      --process-time=BYTES              Request the specified process time per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with index INDEX.\n"
+msgstr ""
+"%s [val]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Vis denne hjelpeteksten.\n"
+"      --version                         Vis versjonsinformasjon.\n"
+"\n"
+"  -r, --record                          Opprett tilkopling for lydopptak.\n"
+"  -p, --playback                        Opprett tilkopling for lydavspeling.\n"
+"\n"
+"  -v, --verbose                         Vis meir detaljerte meldingar.\n"
+"\n"
+"  -s, --server=TENAR                    Namnet på tenaren å kopla til.\n"
+"  -d, --device=EINING                   Namnet på sluket/kjelda å kopla til.\n"
+"  -n, --client-name=NAMN                Kva klienten skal kallast på tenaren.\n"
+"      --stream-name=NAMN                Kva straumen skal kallast på tenaren.\n"
+"      --volume=LYDSTYRKE                Vel startlydstyrke (lineær). Verdiar: 0–65536.\n"
+"      --rate=SAMPLINGSRATE              Samplingsrate i Hz (standard: 44100).\n"
+"      --format=SAMPLINGSFORMAT          Samplingsformat: s16le, s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
+"                                        s24-32le eller s24-32be (standard er s16ne).\n"
+"      --channels=KANALAR                Talet på kanalar: 1 for mono, 2 for stereo\n"
+"                                        (standard er 2).\n"
+"      --channel-map=KANALDEFINISJON     Kanaldefinisjon å bruka i staden for standard.\n"
+"      --fix-format                      Bruk samplingsformatet til sluket/kjelda som\n"
+"                                        straumen vert kopla til.\n"
+"      --fix-rate                        Bruk samlingsraten til sluket/kjelda som\n"
+"                                        straumen vert kopla til.\n"
+"      --fix-channels                    Bruk kanaltalet og kanaldefinisjonen frå\n"
+"                                        sluket/kjelda som straumen vert kopla til.\n"
+"      --no-remix                        Ikkje oppmiks eller nedmiks kanalar.\n"
+"      --no-remap                        Definer kanalar etter indeks, ikkje etter namn.\n"
+"      --latency=BYTE                    Be om valt latenstid, målt i byte.\n"
+"      --process-time=BYTE               Be om valt prosesstid per førespurnad, målt i byte.\n"
+"      --latency-msec=MS                 Be om valt latenstid, målt i millisekund.\n"
+"      --process-time-msec=MS            Be om valt prosesstid per førespurnad, målt i millisekund.\n"
+"      --property=EIGENSKAP=VERDI        Set den valde eigenskapen til valt verdi.\n"
+"      --raw                             Ta opp / spel av rå PCM-data.\n"
+"      --passthrough                     Vidaresend lyddata.\n"
+"      --file-format[=FFORMAT]           Ta opp / spel av formatert PCM-data.\n"
+"      --list-file-formats               Vis tilgjengelege filformat.\n"
+"      --monitor-stream=INDEKS           Ta opp frå sluk-inndata med indeksen INDEKS.\n"
+
+#: ../src/utils/pacat.c:793
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr "Spel av koda lydfiler på PulseAudio-lydtenar."
+
+#: ../src/utils/pacat.c:797
+msgid "Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr "Ta opp lyddata frå PulseAudio-lydtenar og lagra til fil."
+
+#: ../src/utils/pacat.c:801
+msgid "Capture audio data from a PulseAudio sound server and write it to STDOUT or the specified file."
+msgstr "Ta opp lyddata frå PulseAudio-lydtenar og send til STDOUT eller valt fil."
+
+#: ../src/utils/pacat.c:805
+msgid "Play back audio data from STDIN or the specified file on a PulseAudio sound server."
+msgstr "Spel av lyddata på PulseAudio-lydtenar frå STDIN eller valt fil."
+
+#: ../src/utils/pacat.c:819
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Kompilert med libpulse %s\n"
+"Lenkja til libpulse %s\n"
+
+#: ../src/utils/pacat.c:852 ../src/utils/pactl.c:1648
+#, c-format
+#| msgid "Invalid filename: '%1'"
+msgid "Invalid client name '%s'"
+msgstr "Ugyldig klientnamn «%s»"
+
+#: ../src/utils/pacat.c:867
+#, c-format
+#| msgid "Invalid filename: '%1'"
+msgid "Invalid stream name '%s'"
+msgstr "Ugyldig straumnamn «%s»"
+
+#: ../src/utils/pacat.c:904
+#, c-format
+#| msgid "Invalid chat name"
+msgid "Invalid channel map '%s'"
+msgstr "Ugyldig kanaldefinisjon «%s»"
+
+#: ../src/utils/pacat.c:933 ../src/utils/pacat.c:947
+#, c-format
+#| msgid "Invalid printer backend specification: %1"
+msgid "Invalid latency specification '%s'"
+msgstr "Ugyldig latenstid-spesifikasjon «%s»"
+
+#: ../src/utils/pacat.c:940 ../src/utils/pacat.c:954
+#, c-format
+#| msgid "Invalid component id specification"
+msgid "Invalid process time specification '%s'"
+msgstr "Ugyldig prosesstid-spesifikasjon «%s»"
+
+#: ../src/utils/pacat.c:966
+#, c-format
+#| msgid "Invalid property use"
+msgid "Invalid property '%s'"
+msgstr "Ugyldig eigenskap «%s»"
+
+#: ../src/utils/pacat.c:985
+#, c-format
+#| msgid "Unknown file format"
+msgid "Unknown file format %s."
+msgstr "Ukjent filformat %s."
+
+#: ../src/utils/pacat.c:1000
+#| msgctxt "KGAPI2::NewTokensFetchJob|"
+#| msgid "Failed to parse server response."
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Klarte ikkje tolka argument til «--monitor-stream»"
+
+#: ../src/utils/pacat.c:1011
+#| msgid "Invalid component id specification"
+msgid "Invalid sample specification"
+msgstr "Ugyldig samplingsspesifikasjon"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1026
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1033
+msgid "Too many arguments."
+msgstr "For mange oppgjevne argument."
+
+#: ../src/utils/pacat.c:1044
+#| msgid "Failed to generate a Package hash for %1"
+msgid "Failed to generate sample specification for file."
+msgstr "Klarte ikkje generera samplingsspesifikasjon for fil."
+
+#: ../src/utils/pacat.c:1070
+#| msgid "Failed to open file"
+msgid "Failed to open audio file."
+msgstr "Klarte ikkje opna lydfil."
+
+#: ../src/utils/pacat.c:1076
+msgid "Warning: specified sample specification will be overwritten with specification from file."
+msgstr "Åtvaring: Den valde samplingsspesifikasjonen vert overskriven av spesifikasjonen i fila."
+
+#: ../src/utils/pacat.c:1079 ../src/utils/pactl.c:1712
+#| msgid "Failed to determine interpreter for scriptfile \"%1\""
+msgid "Failed to determine sample specification from file."
+msgstr "Klarte ikkje fastsetja samplingsspesifikasjon frå fil."
+
+#: ../src/utils/pacat.c:1088
+#| msgid "Failed to determine interpreter for scriptfile \"%1\""
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Åtvaring: Klarte ikkje fastsetja kanaldefinisjon frå fil."
+
+#: ../src/utils/pacat.c:1099
+#| msgid "Cannot create empty component specification"
+msgid "Channel map doesn't match sample specification"
+msgstr "Kanaldefinisjonen er ikkje i samsvar med samplingsspesifikasjonen"
+
+#: ../src/utils/pacat.c:1110
+#| msgid "Unable to write the output file."
+msgid "Warning: failed to write channel map to file."
+msgstr "Åtvaring: Klarte ikkje lagra kanaldefinisjon til fil."
+
+# Det er ikkje ein skrivefeil med «%sstraum». «%s» skal setjast saman med ein av dei to følgjande tekstane («opptaks» og «avspelings»), utan mellomrom.
+#: ../src/utils/pacat.c:1125
+#, c-format
+msgid "Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "Opnar %sstraum med samplingsspesifikasjon «%s» og kanaldefinisjonen «%s»."
+
+# Heng saman med første tekst (første %s vert bytt ut med denne verdien).
+#: ../src/utils/pacat.c:1126
+#| msgctxt "@title:tab"
+#| msgid "Recording"
+msgid "recording"
+msgstr "opptaks"
+
+# Sjå kommentar for førre tekst.
+#: ../src/utils/pacat.c:1126
+#| msgid "Playback"
+msgid "playback"
+msgstr "avspelings"
+
+#: ../src/utils/pacat.c:1150
+#| msgid "Failed to save image"
+msgid "Failed to set media name."
+msgstr "Klarte ikkje velja medienamn."
+
+#: ../src/utils/pacat.c:1160 ../src/utils/pactl.c:2062
+#| msgid "Operation failed."
+msgid "pa_mainloop_new() failed."
+msgstr "Feil ved pa_mainloop_new()-kall."
+
+#: ../src/utils/pacat.c:1183
+#| msgid "Login failed."
+msgid "io_new() failed."
+msgstr "Feil ved io_new()-kall."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "Feil ved pa_context_new()-kall."
+
+#: ../src/utils/pacat.c:1198 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "Feil ved pa_context_connect()-kall: %s"
+
+#: ../src/utils/pacat.c:1204
+msgid "pa_context_rttime_new() failed."
+msgstr "Feil ved pa_context_rttime_new()-kall."
+
+#: ../src/utils/pacat.c:1211 ../src/utils/pactl.c:2085
+#| msgid "Operation failed."
+msgid "pa_mainloop_run() failed."
+msgstr "Feil ved pa_mainloop_run()-kall."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "NAMN [ARGUMENT ...]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "NAMN|#N"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+#| msgctxt "Email template placeholder for key id"
+#| msgid "UIDNAME"
+msgid "NAME"
+msgstr "NAMN"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "NAMN|#N LYDSTYRKE"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N LYDSTYRKE"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "NAMN|#N 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "NAME|#N NØKKEL=VERDI"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#N NØKKEL=VERDI"
+
+#: ../src/utils/pacmd.c:61
+#| msgid "#"
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "NAMN SLUK|#N"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+#| msgid "FILENAME()"
+msgid "NAME FILENAME"
+msgstr "NAMN FILNAMN"
+
+#: ../src/utils/pacmd.c:66
+#| msgid "DISPLAY NAME"
+msgid "PATHNAME"
+msgstr "MAPPEADRESSE"
+
+#: ../src/utils/pacmd.c:67
+#| msgid "FILENAME()"
+msgid "FILENAME SINK|#N"
+msgstr "FILNAMN SLUK|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#N SLUK|KJELDE"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+#| msgid "10"
+msgid "1|0"
+msgstr "1|0"
+
+# Er snakk om to val her, ikkje ordet «card profile». Derfor særskrivinga.
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "KORT PROFIL"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "NAMN|#N PORT"
+
+# «OFFSET is a number which represents the latency offset in microseconds»
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "KORTNAMN|KORT-#N PORT LATENSTILLEGG"
+
+#: ../src/utils/pacmd.c:75
+#| msgid "LARGE"
+msgid "TARGET"
+msgstr "MÅL"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "NUMERISK-NIVÅ"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "RAMMER"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Vis denne hjelpeteksten.\n"
+"      --version                         Vis versjonsinformasjon.\n"
+"Viss ingen kommandoar er oppgjevne, startar pacmd i interaktiv modus.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Kompilert med libpulse %s\n"
+"Lenkja til libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "PulseAudio-teneste køyrer ikkje, eller køyrer ikkje som øktteneste."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+#| msgid "connected"
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+#| msgid "Failed to set up account."
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Klarte ikkje tvangsavslutta PulseAudio-tenesta."
+
+#: ../src/utils/pacmd.c:180
+#| msgid "Daemon warning"
+msgid "Daemon not responding."
+msgstr "Tenesta svarar ikkje."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+#| msgid "Thread %1"
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+#| msgid "Failed to get a list of devices: '%1'"
+msgid "Failed to get statistics: %s"
+msgstr "Klarte ikkje henta statistikk: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "I bruk: %u blokker med til saman %s byte.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Reservert gjennom heile levetida: %u blokker med til saman %s byte.\n"
+
+# https://freedesktop.org/software/pulseaudio/doxygen/scache.html
+#: ../src/utils/pactl.c:176
+#, c-format
+#| msgid "Sample rate: %1 %2\n"
+msgid "Sample cache size: %s\n"
+msgstr "Samplingsmellomlager: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+#| msgctxt "@info"
+#| msgid "Failed to get server settings"
+msgid "Failed to get server information: %s"
+msgstr "Klarte ikkje henta tenarinformasjon: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Tenarstreng: %s\n"
+"Bibliotekprotokoll-versjon: %u\n"
+"Tenarprotokoll-versjon: %u\n"
+"Er lokal: %s\n"
+"Klientindeks: %u\n"
+"Flisstorleik: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Brukarnamn: %s\n"
+"Vertsnamn: %s\n"
+"Tenarnamn: %s\n"
+"Tenarversjon: %s\n"
+"Standard samplingsspesifikasjon: %s\n"
+"Standard kanaldefinisjon: %s\n"
+"Standardsluk: %s\n"
+"Standardkjelde: %s\n"
+"Infokapsel: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+#| msgid "Unable to retrieve Wikipedia information: %1"
+msgid "Failed to get sink information: %s"
+msgstr "Klarte ikkje henta slukinformasjon: %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sluk #%u\n"
+"\tTilstand: %s\n"
+"\tNamn: %s\n"
+"\tSkildring: %s\n"
+"\tDrivar: %s\n"
+"\tSamplingsspesifikasjon: %s\n"
+"\tKanaldefinisjon: %s\n"
+"\tEigarmodul: %u\n"
+"\tDempa: %s\n"
+"\tLydstyrke: %s\n"
+"\t           balanse %0.2f\n"
+"\tGrunn-lydstyrke: %s\n"
+"\tAvlyttingskjelde: %s\n"
+"\tLatenstid: %0.0f µs, oppsett %0.0f µs\n"
+"\tFlagg: %s%s%s%s%s%s%s\n"
+"\tEigenskapar:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+#| msgid "Port:"
+msgid "\tPorts:\n"
+msgstr "\tPortar:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+#| msgid "Active Torrents"
+msgid "\tActive Port: %s\n"
+msgstr "\tAktiv port: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+#| msgid "Format:"
+msgid "\tFormats:\n"
+msgstr "\tFormat:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+#| msgid "Failed to create resource instance: %1"
+msgid "Failed to get source information: %s"
+msgstr "Klarte ikkje henta kjeldeinformasjon: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Kjelde #%u\n"
+"\tTilstand: %s\n"
+"\tNamn: %s\n"
+"\tSkildring: %s\n"
+"\tDrivar: %s\n"
+"\tSamplingsspesifikasjon: %s\n"
+"\tKanaldefinisjon: %s\n"
+"\tEigarmodul: %u\n"
+"\tDempa: %s\n"
+"\tLydstyrke: %s\n"
+"\t           balanse %0.2f\n"
+"\tGrunn-lydstyrke: %s\n"
+"\tAvlyttingssluk: %s\n"
+"\tLatenstid: %0.0f µs, oppsett %0.0f µs\n"
+"\tFlagg: %s%s%s%s%s%s\n"
+"\tEigenskapar:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+#| msgctxt "Translators: Not Available"
+#| msgid "n/a"
+msgid "n/a"
+msgstr "–"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+#| msgid "Failed to fetch collection: %1"
+msgid "Failed to get module information: %s"
+msgstr "Klate ikkje henta modulinformasjon: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modul #%u\n"
+"\tNamn: %s\n"
+"\tArgument: %s\n"
+"\tBruksteljar: %s\n"
+"\tEigenskapar:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+#| msgid "Failed to get secrets for %1"
+msgid "Failed to get client information: %s"
+msgstr "Klarte ikkje henta klientinformasjon: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Klient #%u\n"
+"\tDrivar: %s\n"
+"\tEigarmodul: %s\n"
+"\tEigenskapar:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+#| msgid "Failed to get secrets for %1"
+msgid "Failed to get card information: %s"
+msgstr "Klarte ikkje henta kortinformasjon: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Kort #%u\n"
+"\tNamn: %s\n"
+"\tDrivar: %s\n"
+"\tEigarmodul: %s\n"
+"\tEigenskapar:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+#| msgid "Profile:"
+msgid "\tProfiles:\n"
+msgstr "\tProfilar:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr "\t\t%s: %s (sluk: %u, kjelder: %u, prioritet: %u, tilgjengeleg: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+#| msgid "Active profile"
+msgid "\tActive Profile: %s\n"
+msgstr "\tAktiv profil: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+#| msgid "Properties"
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tEigenskapar:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tDel av profil(ar): %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+#| msgid "Unable to retrieve Wikipedia information: %1"
+msgid "Failed to get sink input information: %s"
+msgstr "Klarte ikkje henta informasjon om sluk-inndata: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sluk-inndata #%u\n"
+"\tDrivar: %s\n"
+"\tEigarmodul: %s\n"
+"\tKlient: %s\n"
+"\tSluk: %u\n"
+"\tSamplingsspesifikasjon: %s\n"
+"\tKanaldefinisjon: %s\n"
+"\tFormat: %s\n"
+"\tKorka: %s\n"
+"\tDempa: %s\n"
+"\tLydstyrke: %s\n"
+"\t           balanse %0.2f\n"
+"\tLatenstid buffer: %0.0f usec\n"
+"\tLatenstid sluk: %0.0f usec\n"
+"\tResamplingsmetode: %s\n"
+"\tEigenskapar:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+#| msgid "Folder to store torrent information:"
+msgid "Failed to get source output information: %s"
+msgstr "Klarte ikkje henta informasjon om kjelde-utdata: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Kjelde-utdata #%u\n"
+"\tDrivar: %s\n"
+"\tEigarmodul: %s\n"
+"\tKlient: %s\n"
+"\tKjelde: %u\n"
+"\tSamplingsspesifikasjon: %s\n"
+"\tKanaldefinisjon: %s\n"
+"\tFormat: %s\n"
+"\tKorka: %s\n"
+"\tDempa: %s\n"
+"\tLydstyrke: %s\n"
+"\t           balanse %0.2f\n"
+"\tLatenstid buffer: %0.0f usec\n"
+"\tLatenstid kjelde: %0.0f usec\n"
+"\tResamplingsmetode: %s\n"
+"\tEigenskapar:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+#| msgid "Unable to read style information"
+msgid "Failed to get sample information: %s"
+msgstr "Klarte ikkje henta informasjon om sample: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sample #%u\n"
+"\tNamn: %s\n"
+"\tSamplingsspesifikasjon: %s\n"
+"\tKanaldefinisjon: %s\n"
+"\tLydstyrke: %s\n"
+"\t           balanse %0.2f\n"
+"\tTidslengd: %0.1fs\n"
+"\tStorleik: %s\n"
+"\tLat: %s\n"
+"\tFilnamn: %s\n"
+"\tEigenskapar:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+#| msgid "Failure reasons"
+msgid "Failure: %s"
+msgstr "Feil: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Klarte ikkje lasta ut modul: Modulen %s er ikkje lasta"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid "Failed to set volume: You tried to set volumes for %d channels, whereas channel/s supported = %d\n"
+msgstr "Klarte ikkje stilla lydstyrken: Du prøvde å stilla lydstyrken for %d kanalar, men det finst berre %d kanalar.\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+#| msgctxt "KAuth::ExecuteJob|"
+#| msgid "Tried to start an invalid action"
+msgid "Failed to set format: invalid format string %s"
+msgstr "Klarte ikkje velja format: ugyldig formatspesifikasjon %s"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+#| msgid "Failed to upload image"
+msgid "Failed to upload sample: %s"
+msgstr "Klarte ikkje lasta opp sample: %s"
+
+#: ../src/utils/pactl.c:1110
+#| msgid "Premature end of document."
+msgid "Premature end of file"
+msgstr "For tidleg filslutt"
+
+#: ../src/utils/pactl.c:1130
+#| msgid "New"
+msgid "new"
+msgstr "ny"
+
+#: ../src/utils/pactl.c:1133
+#| msgid "No change"
+msgid "change"
+msgstr "endra"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "fjern"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "ukjend"
+
+#: ../src/utils/pactl.c:1147
+#| msgctxt "City in Leinster Ireland"
+#| msgid "Dunsink"
+msgid "sink"
+msgstr "sluk"
+
+#: ../src/utils/pactl.c:1150
+#| msgctxt "SyntaxHighlightingCLI|"
+#| msgid "source"
+msgid "source"
+msgstr "kjelde"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "kjelde-inndata"
+
+#: ../src/utils/pactl.c:1156
+#| msgctxt "SyntaxHighlightingCLI|"
+#| msgid "source"
+msgid "source-output"
+msgstr "kjelde-utdata"
+
+#: ../src/utils/pactl.c:1159
+#| msgctxt "Tag Type"
+#| msgid "module"
+msgid "module"
+msgstr "modul"
+
+#: ../src/utils/pactl.c:1162
+#| msgid "kfmclient"
+msgid "client"
+msgstr "klient"
+
+#: ../src/utils/pactl.c:1165
+#| msgid "&Use cache"
+msgid "sample-cache"
+msgstr "sample-mellomlager"
+
+#: ../src/utils/pactl.c:1168
+#| msgid "Add server"
+msgid "server"
+msgstr "tenar"
+
+#: ../src/utils/pactl.c:1171
+#| msgid "Discard"
+msgid "card"
+msgstr "kort"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Hending «%s» på %s #%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "Fekk SIGINT. Avsluttar."
+
+#: ../src/utils/pactl.c:1485
+#| msgid "Invalid component id specification"
+msgid "Invalid volume specification"
+msgstr "Ugyldig lydstyrke-spesifikasjon"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Lydstyrken er utanfor gyldig område.\n"
+
+#: ../src/utils/pactl.c:1521
+#| msgctxt "syntax error in KWin script"
+#| msgid "Invalid number of arguments"
+msgid "Invalid number of volume specifications.\n"
+msgstr "Ugyldig tal på lydstyrke-spesifikasjonar.\n"
+
+#: ../src/utils/pactl.c:1533
+#| msgid "Invalid component body specification"
+msgid "Inconsistent volume specification.\n"
+msgstr "Inkonsekvent lydstyrke-spesifikasjon.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+#| msgid "[options] "
+msgid "[options]"
+msgstr "[val]"
+
+#: ../src/utils/pactl.c:1565
+#| msgid "TYPE(x)"
+msgid "[TYPE]"
+msgstr "[TYPE]"
+
+#: ../src/utils/pactl.c:1567
+#| msgid "FILENAME()"
+msgid "FILENAME [NAME]"
+msgstr "FILNAMN [NAMN]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "NAMN [SLUK]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "NAMN|#N LYDSTYRKE [LYDSTYRKE ...]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#N LYDSTYRKE [LYDSTYRKE ...]"
+
+# «toggle» er ein fast verdi, og skal ikkje settast om.
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "NAMN|#N 1|0|toggle"
+
+# «toggle» er ein fast verdi, og skal ikkje settast om.
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1581
+#| msgctxt "Constellation name (optional)"
+#| msgid "NORMA"
+msgid "#N FORMATS"
+msgstr "#N FORMAT"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Du kan bruka spesialnamna @DEFAULT_SINK@, @DEFAULT_SOURCE@ og @DEFAULT_MONITOR@\n"
+"for å velja høvesvis standard sluk, kjelde og avlytting.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"  -n, --client-name=NAME                How to call this client on the server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Vis denne hjelpemeldinga.\n"
+"      --version                         Vis versjonsinformasjon.\n"
+"\n"
+"  -s, --server=TENAR                    Namnet på tenaren å kopla til.\n"
+"  -n, --client-name=NAMN                Kva klienten skal kallast på tenaren.\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Kompilert med libpulse %s\n"
+"Lenkja til libpulse %s\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+#| msgid "Specify the new source for %1"
+msgid "Specify nothing, or one of: %s"
+msgstr "Vel ingenting eller ein av følgjande: %s"
+
+#: ../src/utils/pactl.c:1694
+#| msgid "Please specify a name for the command."
+msgid "Please specify a sample file to load"
+msgstr "Vel sample-fil å lasta"
+
+#: ../src/utils/pactl.c:1707
+#| msgid "Failed to open file"
+msgid "Failed to open sound file."
+msgstr "Klarte ikkje opna lydfil."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Åtvaring: Klarte ikkje fastsetja samplingsspesifikasjon frå fil."
+
+#: ../src/utils/pactl.c:1729
+#| msgid "You have to specify a valid cell."
+msgid "You have to specify a sample name to play"
+msgstr "Du må velja eit sample-namn å spela av"
+
+#: ../src/utils/pactl.c:1741
+#| msgid "You have to specify a valid region."
+msgid "You have to specify a sample name to remove"
+msgstr "Du må velja eit sample-namn å fjerna"
+
+#: ../src/utils/pactl.c:1750
+#| msgid "You have to specify a valid region."
+msgid "You have to specify a sink input index and a sink"
+msgstr "Du må velja ein indeks til sluk-inndata og eit sluk"
+
+#: ../src/utils/pactl.c:1760
+#| msgid "You have to specify a valid cell."
+msgid "You have to specify a source output index and a source"
+msgstr "Du må velja ein indeks til kjelde-utdata og ei kjelde"
+
+#: ../src/utils/pactl.c:1775
+#| msgid "You have to specify a valid region."
+msgid "You have to specify a module name and arguments."
+msgstr "Du må velja eit modulnamn og tilhøyrande argument."
+
+#: ../src/utils/pactl.c:1795
+#| msgid "You have to specify a valid region."
+msgid "You have to specify a module index or name"
+msgstr "Du må velja ein modulindeks eller eit modulnamn"
+
+#: ../src/utils/pactl.c:1808
+msgid "You may not specify more than one sink. You have to specify a boolean value."
+msgstr "Du kan ikkje velja meir enn eitt sluk. Du må oppgje ein boolsk verdi."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+#| msgid "Invalid component id specification"
+msgid "Invalid suspend specification."
+msgstr "Ugyldig kvilemodus-spesifikasjon."
+
+#: ../src/utils/pactl.c:1828
+msgid "You may not specify more than one source. You have to specify a boolean value."
+msgstr "Du kan ikkje velja meir enn éi kjelde. Du må oppgje ein boolsk verdi."
+
+#: ../src/utils/pactl.c:1845
+#| msgid "You have to specify a valid region."
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Du må velja eit kortnamn / ein kortindeks og eit profilnamn"
+
+#: ../src/utils/pactl.c:1856
+#| msgid "You have to specify a valid region."
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Du må velja eit sluknamn / ein slukindeks og eit portnamn"
+
+#: ../src/utils/pactl.c:1867
+#| msgid "You have to specify a valid cell."
+msgid "You have to specify a sink name"
+msgstr "Du må velja eit sluknamn"
+
+#: ../src/utils/pactl.c:1877
+#| msgid "You have to specify a valid region."
+msgid "You have to specify a source name/index and a port name"
+msgstr "Du må velja eit kjeldenamn / ein kjeldeindeks og eit portnamn"
+
+#: ../src/utils/pactl.c:1888
+#| msgid "You have to specify a valid region."
+msgid "You have to specify a source name"
+msgstr "Du må velja eit kjeldenamn"
+
+#: ../src/utils/pactl.c:1898
+#| msgid "You have to specify a valid cell."
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Du må velja eit sluknamn / ein slukindeks og ein lydstyrke"
+
+#: ../src/utils/pactl.c:1911
+#| msgid "You have to specify a valid cell."
+msgid "You have to specify a source name/index and a volume"
+msgstr "Du må velja eit kjeldenamn / ein kjeldeindeks og ein lydstyrke"
+
+#: ../src/utils/pactl.c:1924
+#| msgid "You have to select at least one column."
+msgid "You have to specify a sink input index and a volume"
+msgstr "Du må velja ein indeks til sluk-inndata og ein lydstyrke"
+
+#: ../src/utils/pactl.c:1929
+#| msgid "Invalid input"
+msgid "Invalid sink input index"
+msgstr "Ugyldig indeks til sluk-inndata"
+
+#: ../src/utils/pactl.c:1940
+#| msgctxt "@info"
+#| msgid "You have to enter an output filename."
+msgid "You have to specify a source output index and a volume"
+msgstr "Du må velja ein indeks til kjelde-utdata og ein lydstyrke"
+
+#: ../src/utils/pactl.c:1945
+#| msgid "Invalid source type."
+msgid "Invalid source output index"
+msgstr "Ugyldig indeks til kjelde-utdata"
+
+#: ../src/utils/pactl.c:1956
+msgid "You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr "Du må velja eit sluknamn / ein slukindeks og ei dempehanding (0, 1 eller «toggle»)"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+#| msgid "Invalid component id specification"
+msgid "Invalid mute specification"
+msgstr "Ugyldig dempespesifikasjon"
+
+#: ../src/utils/pactl.c:1971
+msgid "You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr "Du må velja eit kjeldenamn / ein kjeldeindeks og ei dempehanding (0, 1 eller «toggle»)"
+
+#: ../src/utils/pactl.c:1986
+msgid "You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr "Du må velja indeks til sluk-inndata og ei dempehanding (0, 1 eller «toggle»)"
+
+#: ../src/utils/pactl.c:1991
+#| msgid "Invalid component id specification"
+msgid "Invalid sink input index specification"
+msgstr "Ugyldig spesifikasjon av indeks til sluk-inndata"
+
+#: ../src/utils/pactl.c:2004
+msgid "You have to specify a source output index and a mute action (0, 1, or 'toggle')"
+msgstr "Du må velja indeks til kjelde-utdata og ei dempehanding (0, 1 eller «toggle»)"
+
+#: ../src/utils/pactl.c:2009
+#| msgid "Invalid component id specification"
+msgid "Invalid source output index specification"
+msgstr "Ugyldig spesifikasjon av indeks til kjelde-utdata"
+
+#: ../src/utils/pactl.c:2026
+msgid "You have to specify a sink index and a semicolon-separated list of supported formats"
+msgstr "Du må velja indeks til eit sluk og ei semikolondelt liste av støtta format"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr "Du må velja eit kortnamn / ein kortindeks, eit portnamn og eit latentstid-tillegg"
+
+#: ../src/utils/pactl.c:2045
+#| msgid "Could not parse the config file."
+msgid "Could not parse latency offset"
+msgstr "Klarte ikkje tolka latenstid-tillegg"
+
+#: ../src/utils/pactl.c:2057
+#| msgid "No command specified."
+msgid "No valid command specified."
+msgstr "Ingen gyldige kommandoar."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+#| msgid "Failed to request scan"
+msgid "Failure to resume: %s\n"
+msgstr "Klarte starta frå kvilemodus: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Klarte ikkje gå i kvilemodus: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "ÅTVARING: Lydtenaren er ikkje lokal. Går ikkje i kvilemodus.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+#| msgctxt "Name"
+#| msgid "Connection failure"
+msgid "Connection failure: %s\n"
+msgstr "Tilkoplingsfeil: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Fekk SIGINT. Avsluttar.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "ÅTVARING: Underprosess avslutta av signalet %u.\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"\n"
+msgstr ""
+"%s [val] ... \n"
+"\n"
+"  -h, --help                            Vis denne hjelpeteksten.\n"
+"      --version                         Vis versjonsinformasjon.\n"
+"  -s, --server=TENAR                    Namnet på tenaren å kopla til.\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Kompilert med libpulse %s\n"
+"Lenkja til libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+#| msgid "Operation failed."
+msgid "pa_mainloop_new() failed.\n"
+msgstr "Feil ved pa_mainloop_new()-kall.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "Feil ved pa_context_new()-kall.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+#| msgid "Operation failed."
+msgid "pa_mainloop_run() failed.\n"
+msgstr "Feil ved pa_mainloop_run()-kall.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D skjerm] [-S tenar] [-O sluk] [-I kjelde] [-c fil]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Vis gjeldande PulseAudio-data kopla til X11-skjerm (standard).\n"
+" -e    Eksporter lokale PulseAudio-data til X11-skjerm.\n"
+" -i    Importer PulseAudio-data frå X11-skjerm til lokale miljøvariablar og infokapsel-fil.\n"
+" -r    Fjern PulseAudio-data frå X11-skjerm.\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+#| msgctxt "@info"
+#| msgid "Failed to execute command"
+msgid "Failed to parse command line.\n"
+msgstr "Klarte ikkje tolka kommandolinja.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+#| msgid "Server: %1"
+msgid "Server: %s\n"
+msgstr "Tenar: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+#| msgctxt "'source' is a source file"
+#| msgid "Source: %1"
+msgid "Source: %s\n"
+msgstr "Kjelde: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+#| msgid "Version: %s\n"
+msgid "Sink: %s\n"
+msgstr "Sluk: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+#| msgid "Cookies"
+msgid "Cookie: %s\n"
+msgstr "Infokapsel: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+#| msgid "Failed to read data."
+msgid "Failed to parse cookie data\n"
+msgstr "Klarte ikkje tolka infokapsel-data\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+#| msgid "Failed to save image"
+msgid "Failed to save cookie data\n"
+msgstr "Klarte ikkje lagra infokapsel-data\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+#| msgid "Failed to fetch item."
+msgid "Failed to get FQDN.\n"
+msgstr "Klarte ikkje henta FQDN.\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+#| msgid "Failed to load avatar."
+msgid "Failed to load cookie data\n"
+msgstr "Klarte ikkje lasta infokapsel-data\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+#| msgid "Not yet implemented"
+msgid "Not yet implemented.\n"
+msgstr "Ikkje implementert enno.\n"
diff --git a/po/oc.po b/po/oc.po
new file mode 100644 (file)
index 0000000..4a65fb5
--- /dev/null
+++ b/po/oc.po
@@ -0,0 +1,2960 @@
+# French translation of pulseaudio.
+# Copyright (C) 2006-2008 Lennart Poettering
+# This file is distributed under the same license as the pulseaudio package.
+# Robert-André Mauchin <zebob.m@pengzone.org>, 2008.
+# Michaël Ughetto <telimektar esraonline com>, 2008.
+# Pablo Martin-Gomez <pablo.martin-gomez@laposte.net>, 2008.
+# Corentin Perard <corentin.perard@gmail.com>, 2009.
+# Thomas Canniot <mrtom@fedoraproject.org>, 2009, 2012.
+# Cédric Valmary (Tot en Òc) <cvalmary@yahoo.fr>, 2015.
+# Cédric Valmary (totenoc.eu) <cvalmary@yahoo.fr>, 2016.
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio trunk\n"
+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
+"POT-Creation-Date: 2016-09-19 19:24+0000\n"
+"PO-Revision-Date: 2016-10-12 22:20+0200\n"
+"Last-Translator: Cédric Valmary (totenoc.eu) <cvalmary@yahoo.fr>\n"
+"Language-Team: Tot En Òc\n"
+"Language: oc\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Virtaal 0.7.1\n"
+"X-Launchpad-Export-Date: 2016-10-12 20:12+0000\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "--fail requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level requesís un paramètre de nivèl de jornal (siá numeric entre 0 e "
+"4, siá de desbugatge : info, notice, warn , error)."
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "--realtime requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Cibla de jornal invalida : utilizatz « syslog », « stderr » o « auto », o un "
+"nom de fichièr valid « fichièr:<camin> », « fichièr_novèl:<camin> »."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "--log-time requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Metòde de reescandalhatge invalid « %s »."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "--system requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm requerís un paramètre boolean"
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:260
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Cibla del jornal « %s » invalida."
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Nivèl del jornal « %s » invalid."
+
+#: ../src/daemon/daemon-conf.c:290
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Metòde de reescandalhatge « %s » invalid."
+
+#: ../src/daemon/daemon-conf.c:312
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] rlimit « %s » invalid."
+
+#: ../src/daemon/daemon-conf.c:332
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Format d'escandalhatge « %s » invalid."
+
+#: ../src/daemon/daemon-conf.c:349 ../src/daemon/daemon-conf.c:366
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Taus d'escandalhatge « %s » invalid."
+
+#: ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Canals d'escandalhatge « %s » invalid."
+
+#: ../src/daemon/daemon-conf.c:406
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Plan de canals « %s » invalid."
+
+#: ../src/daemon/daemon-conf.c:423
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Nombre de fragments « %s » invalid."
+
+#: ../src/daemon/daemon-conf.c:440
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Talha del fragment « %s » invalida."
+
+#: ../src/daemon/daemon-conf.c:457
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Nivèl de prioritat (nice) « %s » invalid."
+
+#: ../src/daemon/daemon-conf.c:500
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Tipe de servidor invalid '%s'."
+
+#: ../src/daemon/daemon-conf.c:615
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Fracàs al moment de la dobertura del fichièr de configuracion : %s"
+
+#: ../src/daemon/daemon-conf.c:631
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Lo plan de canals especificat per defaut a un nombre de canals diferent del "
+"nombre especificat per defaut."
+
+#: ../src/daemon/daemon-conf.c:718
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Lectura a partir del fichièr de configuracion : %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Nom : %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Pas cap d'entresenha de modul disponibla\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Version : %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Descripcion : %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor : %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Utilizacion : %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Cargament unic : %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "AVERTIMENT D'OBSOLESCÉNCIA : %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Camin : %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Impossible de dobrir lo modul %s : %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Fracàs al moment de la recèrca del cargador lt_dlopen original."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Fracàs al moment de l'atribucion del cargador dl novèl."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Fracàs al moment de l'apondon del cargador bind-now."
+
+#: ../src/daemon/main.c:161
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Impossible de trobar l'utilizaire « %s »."
+
+#: ../src/daemon/main.c:166
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Impossible de trobar lo grop « %s »."
+
+#: ../src/daemon/main.c:175
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "Lo GID de l'utilizaire « %s » e del grop « %s » son pas identics."
+
+#: ../src/daemon/main.c:180
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Lo dorsièr personal de l'utilizaire « %s » es pas « %s », ignorat."
+
+#: ../src/daemon/main.c:183 ../src/daemon/main.c:188
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Impossible de crear « %s » : %s"
+
+#: ../src/daemon/main.c:195
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Fracàs al moment del cambiament de la lista del grop : %s"
+
+#: ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Fracàs al moment del cambiament de GID : %s"
+
+#: ../src/daemon/main.c:227
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Fracàs al moment del cambiament d'UID : %s"
+
+#: ../src/daemon/main.c:256
+msgid "System wide mode unsupported on this platform."
+msgstr "Mòde sistèma espandit pas pres en carga sus aquela plataforma."
+
+#: ../src/daemon/main.c:494
+msgid "Failed to parse command line."
+msgstr "Fracàs al moment de l'analisi de la linha de comanda"
+
+#: ../src/daemon/main.c:533
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Mòde sistèma refusat per un utilizaire qu'es pas superutilizaire. Aviada del "
+"servici de recèrca del servidor D-Bus unicament."
+
+#: ../src/daemon/main.c:632
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Impossible de tuar lo demòni : %s"
+
+#: ../src/daemon/main.c:661
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Lo programa es pas concebut per èsser aviat en tant que root (levat se --"
+"system es entresenhat)."
+
+#: ../src/daemon/main.c:664
+msgid "Root privileges required."
+msgstr "Los privilègis root son necessaris."
+
+#: ../src/daemon/main.c:671
+msgid "--start not supported for system instances."
+msgstr "--start es pas pres en carga per las instàncias del sistèma."
+
+#: ../src/daemon/main.c:711
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+"Lo servidor configurat per un utilizaire a l'adreça %s refusa d'aviar/de se "
+"lançar automaticament."
+
+#: ../src/daemon/main.c:717
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Servidor configurat per l'utilizaire a l'adreça %s, que sembla èsser local. "
+"Examèn mai aprigondit."
+
+#: ../src/daemon/main.c:722
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr ""
+
+#: ../src/daemon/main.c:725
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr ""
+
+#: ../src/daemon/main.c:728
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr ""
+
+#: ../src/daemon/main.c:733
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr ""
+
+#: ../src/daemon/main.c:766
+msgid "Failed to acquire stdio."
+msgstr "Fracàs al moment de l'aquisicion de stdio."
+
+#: ../src/daemon/main.c:772 ../src/daemon/main.c:843
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() a fracassat : %s"
+
+#: ../src/daemon/main.c:777 ../src/daemon/main.c:848
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fracàs de « fork » : %s"
+
+#: ../src/daemon/main.c:792 ../src/daemon/main.c:863 ../src/utils/pacat.c:568
+#, c-format
+msgid "read() failed: %s"
+msgstr "Fracàs de read() : %s"
+
+#: ../src/daemon/main.c:798
+msgid "Daemon startup failed."
+msgstr "Fracàs al moment de l'aviada del demòni."
+
+#: ../src/daemon/main.c:831
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() a fracassat : %s"
+
+#: ../src/daemon/main.c:958
+msgid "Failed to get machine ID"
+msgstr "Fracàs al moment de l'obtencion de l'ID de la maquina"
+
+#: ../src/daemon/main.c:984
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read "
+"http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/WhatIs"
+"WrongWithSystemWide/ for an explanation why system mode is usually a bad "
+"idea."
+msgstr ""
+"Vous exécutez PA dans un mode système. Sachez que vous ne devriez pas faire "
+"cela.\n"
+"Si vous choisissez malgré tout de lo faire, vous êtes responsable de tout "
+"dysfonctionnement inattendu.\n"
+"Veuillez lire "
+"http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/WhatIs"
+"WrongWithSystemWide/ pour comprendre pourquoi lo mode système est "
+"généralement une mauvaise idée."
+
+#: ../src/daemon/main.c:1001
+msgid "pa_pid_file_create() failed."
+msgstr "Fracàs de pa_pid_file_create()."
+
+#: ../src/daemon/main.c:1033
+msgid "pa_core_new() failed."
+msgstr "Fracàs de pa_core_new()."
+
+#: ../src/daemon/main.c:1104
+msgid "Failed to initialize daemon."
+msgstr "Fracàs al moment de l'inicializacion del demòni"
+
+#: ../src/daemon/main.c:1109
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Aviada del demòni sens cap de modul cargat : refús de foncionar."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Sistèma de son PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Aviar lo sistèma de son PulseAudio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Entrada"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Entrada de l'estacion d'acuèlh"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Microfòn de l'estacion d'acuèlh"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Entrada linha de l'estacion d'acuèlh"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Entrada linha"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2173
+#: ../src/modules/bluetooth/module-bluez5-device.c:1937
+msgid "Microphone"
+msgstr "Micrò"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Microfòn avant"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Microfòn arrièr"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Microfòn extèrne"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Microfòn intèrne"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Ràdio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Vidèo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Contraròtle automatic del ganh"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Pas de contraròtle automatic del ganh"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Boost"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Sens boost"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Amplificador"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Pas d'amplificador"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Amplificacion bassas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Pas d'amplificacion de las bassas"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2178
+#: ../src/modules/bluetooth/module-bluez5-device.c:1944
+msgid "Speaker"
+msgstr "Nautparlaire"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Escotadors"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Entrada analogica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Microfòn de l'estacion d'acuèlh"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Micro-casc"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Sortida analogica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "Sortida analogica (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Sortida linha"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Sortida Analogica Monò"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Nauts parlaires"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Sortida numerica (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Entrada numerica (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Connector numeric (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Monò analogic"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Esterèo analogic"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Surround analogic 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Surround analogic 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Surround analogic 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Surround analogic 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Surround analogic 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Surround analogic 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Surround analogic 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Surround analogic 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Surround analogic 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Surround analogic 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Surround analogic 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Esterèo numeric (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Connector numeric (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Surround numeric 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Surround numeric 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Digital Surround 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Esterèo numeric (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digital Surround 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4151
+msgid "Analog Mono Duplex"
+msgstr "Duplèx Mono analogic"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Stereo Duplex"
+msgstr "Duplèx esterèo analogic"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Duplèx estèreo numeric (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Multichannel Duplex"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2369
+#: ../src/modules/bluetooth/module-bluez5-device.c:2173
+#: ../src/modules/droid/module-droid-card.c:221
+msgid "Off"
+msgstr "Atudat"
+
+#: ../src/modules/alsa/alsa-mixer.c:4254
+#, c-format
+msgid "%s Output"
+msgstr "%s Sortida"
+
+#: ../src/modules/alsa/alsa-mixer.c:4262
+#, c-format
+msgid "%s Input"
+msgstr "%s Entrada"
+
+#: ../src/modules/alsa/alsa-sink.c:570
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-sink.c:747
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nos a desrevelhat per legir de donadas novèlas a partir del periferic, "
+"mas, en fait, i aviá pas res a escriure !\n"
+"S'agís fòrt probablament d'un bug dins lo pilòt ALSA « %s ». Raportatz "
+"aqueste problèma als desvolopaires d'ALSA. Sèm estats desrevelhats amb lo "
+"jòc POLLOUT -- çaquelà un snd_pcm_avail() ulterior a tornat 0 o una autra "
+"valor < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nos a desrevelhat per legir de donadas novèlas a partir del periferic, "
+"mas, en fait, i aviá pas res a escriure !\n"
+"S'agís fòrt probablament d'un bug dins lo pilòt ALSA « %s ». Raportatz "
+"aqueste problèma als desvolopaires d'ALSA. Sèm estats desrevelhats amb lo "
+"jòc POLLIN -- çaquelà un snd_pcm_avail() ulterior a tornat 0 o una autra "
+"valor < min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1166 ../src/modules/alsa/alsa-util.c:1241
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() a tornat una valor qu'es excepcionalament larga : %lu octets "
+"(%lu ms).\n"
+"S'agís fòrt probablament d'un bug dins lo pilòt ALSA « %s ». Raportatz "
+"aqueste problèma als desvolopaires d'ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1216
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
+"(%s%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() a tornat una valor qu'es excepcionalament larga : %li octets "
+"%s%lu ms).\n"
+"S'agís fòrt probablament d'un bug dins lo pilòt ALSA « %s ». Raportatz "
+"aqueste problèma als desvolopaires d'ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1257
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() a tornat de resultats anormals : lo relambi %lu es mai "
+"pichon que %lu.\n"
+"Es fòrt probablament un bug dins lo pilòt ALSA '%s'. Senhalatz-lo als "
+"desvolopaires d'ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1300
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() a tornat una valor qu'es excepcionalament larga : %lu "
+"octets (%lu·ms)..\n"
+"S'agís fòrt probablament d'un bug dins lo pilòt ALSA « %s ». Raportatz "
+"aqueste problèma als desvolopaires d'ALSA."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2163
+#: ../src/modules/bluetooth/module-bluez5-device.c:1927
+msgid "Headset"
+msgstr "Casc àudio"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2168
+#: ../src/modules/bluetooth/module-bluez5-device.c:1932
+msgid "Handsfree"
+msgstr "Mans liuras"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2183
+#: ../src/modules/bluetooth/module-bluez5-device.c:1950
+msgid "Headphone"
+msgstr "Escotadors"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2188
+#: ../src/modules/bluetooth/module-bluez5-device.c:1955
+msgid "Portable"
+msgstr "Portable"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2193
+#: ../src/modules/bluetooth/module-bluez5-device.c:1960
+msgid "Car"
+msgstr "Telefòn de veitura"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2198
+#: ../src/modules/bluetooth/module-bluez5-device.c:1965
+msgid "HiFi"
+msgstr "HiFi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2203
+#: ../src/modules/bluetooth/module-bluez5-device.c:1970
+msgid "Phone"
+msgstr "Telefòn"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2211
+#: ../src/modules/bluetooth/module-bluez5-device.c:1922
+#: ../src/modules/bluetooth/module-bluez5-device.c:1938
+#: ../src/modules/bluetooth/module-bluez5-device.c:1976
+msgid "Bluetooth Output"
+msgstr "Sortida Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2214
+#: ../src/modules/bluetooth/module-bluez5-device.c:1921
+#: ../src/modules/bluetooth/module-bluez5-device.c:1943
+#: ../src/modules/bluetooth/module-bluez5-device.c:1949
+#: ../src/modules/bluetooth/module-bluez5-device.c:1975
+msgid "Bluetooth Input"
+msgstr "Entrada Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2250
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Lectura nauta fidelitat (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2261
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Captura Hi-Fi (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2272
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telefonia en duplèx (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2284
+msgid "Handsfree Gateway"
+msgstr "Pòrt mans liuras"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:2013
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:2024
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:2035
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:2047
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr ""
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "Activar"
+
+#. / TRANSLATORS: The app icon and name appears above this string. If the phrase
+#. / can't be translated in this language, translate the whole sentence
+#. / 'This app wants to record audio.'
+#: ../src/modules/trust-store/module-trust-store.c:154
+msgid "wants to record audio."
+msgstr ""
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Garda totjorn al mens una destinacion quitament s'es voida"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Sortida factícia"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Egalizador d'usatge general"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-filter-apply.c:47
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autonetejatge=<descargar automaticament los filtres inutilizats ?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Destinacion virtuala LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<nom de la destinacion> sink_properties=<proprietats de la "
+"destinacion> master=<nom de la destinacion de filtrar> format=<format de "
+"l'escandalhatge> rate=<taus d'escandalhatge> channels=<nombre de canals> "
+"channel_map=<mapa dels canals d'entrada> plugin=<nom de l'empeuton ladspa> "
+"label=<etiqueta de l'empeuton ladspa> control=<lista de las valors de "
+"contraròtle d'entrada separadas per de virgulas> input_ladspaport_map=<lista "
+"dels noms dels pòrts d'entrada LADSPA separats per de virgulas> "
+"output_ladspaport_map=<lista dels noms dels pòrts de sortida LADSPA separats "
+"per de virgulas> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Relòtge de la destinacion void"
+
+#: ../src/modules/module-null-sink.c:280
+msgid "Null Output"
+msgstr "Sortida voida"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Periferics de sortida"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Periferics d'entrada"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Audio sus @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr ""
+
+#: ../src/modules/module-tunnel-sink-new.c:517
+#: ../src/modules/module-tunnel-source-new.c:516
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr ""
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Collector ambiofonic virtual"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> "
+"hrir=/path/to/left_hrir.wav "
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "Servidor de son PulseAudio"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Avant centre"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Avant esquèrra"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Avant dreita"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Arrièr centre"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Arrièr esquèrra"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Arrièr dreita"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Caisson de bassa"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Avant a esquèrra del centre"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Avant a dreita del centre"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Costat d'esquèrra"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Costat de dreita"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Auxiliar 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Auxiliar 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Auxiliar 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Auxiliar 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Auxiliar 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Auxiliar 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Auxiliar 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Auxiliar 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Auxiliar 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Auxiliar 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Auxiliar 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Auxiliar 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Auxiliar 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Auxiliar 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Auxiliar 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Auxiliar 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Auxiliar 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Auxiliar 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Auxiliar 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Auxiliar 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Auxiliar 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Auxiliar 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Auxiliar 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Auxiliar 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Auxiliar 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Auxiliar 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Auxiliar 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Auxiliar 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Auxiliar 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Auxiliar 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Auxiliar 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Auxiliar 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Amont al centre"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Avant centre naut"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Avant esquèrra naut"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Avant dreita naut"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Arrièr centre naut"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Arrièr esquèrra naut"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Arrièr dreita naut"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:175 ../src/pulse/volume.c:294
+#: ../src/pulse/volume.c:320 ../src/pulse/volume.c:340
+#: ../src/pulse/volume.c:372 ../src/pulse/volume.c:412
+#: ../src/pulse/volume.c:431
+msgid "(invalid)"
+msgstr "(invalid)"
+
+#: ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Esterèo"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() a fracassat"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() a tornat verai"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Fracàs al moment de l'analisi de las donadas del cookie"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork() : %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid() : %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Messatge recebut per una extension desconeguda « %s »"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr ""
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "sortida"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "bidireccional"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "invalid"
+
+#: ../src/pulsecore/core-util.c:1836
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "òc"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "non"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Impossible d'accedir al verrolh autonòme."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr ""
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr ""
+
+#: ../src/pulsecore/sink.c:3461
+msgid "Built-in Audio"
+msgstr "Àudio integrat"
+
+#: ../src/pulsecore/sink.c:3466
+msgid "Modem"
+msgstr "Modèm"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "D'acòrdi"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Accès refusat"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Comanda desconeguda"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Argument invalid"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "L'entitat existís"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Pas cap d'identitat d'aquel tipe"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Connexion refusada"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Error de protocòl"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Relambi d'espèra depassat"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr ""
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Error intèrna"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Connexion acabada"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "L'entitat es estada tuada"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Servidor invalid"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Fracàs al moment de l'inicializacion del modul"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Estat incorrècte"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Pas de donadas"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Version del protocòl invalida"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Tròp grand"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Pas pres en carga"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Còde d'error desconegut"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Pas cap d'extension d'aquel tipe"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Foncionalitat despreciada"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Implantacion mancanta"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Lo client s'es devesit (Client forked)"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Error d'entrada/sortida"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Periferic o ressorsa ocupat"
+
+#: ../src/pulse/sample.c:177
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f Gio"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f Mio"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f Kio"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:117
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Fracàs al moment del voidatge del flux : %s"
+
+#: ../src/utils/pacat.c:122
+msgid "Playback stream drained."
+msgstr "Flux de lectura voidat."
+
+#: ../src/utils/pacat.c:133
+msgid "Draining connection to server."
+msgstr "Drenatge de la connexion cap al servidor."
+
+#: ../src/utils/pacat.c:146
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain() : %s"
+
+#: ../src/utils/pacat.c:169
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() a fracassat : %s"
+
+#: ../src/utils/pacat.c:210
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() a fracassat : %s"
+
+#: ../src/utils/pacat.c:260 ../src/utils/pacat.c:290
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() a fracassat : %s"
+
+#: ../src/utils/pacat.c:340
+msgid "Stream successfully created."
+msgstr "Flux creat amb succès"
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() a fracassat : %s"
+
+#: ../src/utils/pacat.c:347
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Mesuras del tampon : maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:350
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Mesuras del tampon : maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:354
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr ""
+"Utilizacion de l'especificacion d'escandalhatge « %s », plan dels canals "
+"« %s »."
+
+#: ../src/utils/pacat.c:358
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr ""
+
+#: ../src/utils/pacat.c:368
+#, c-format
+msgid "Stream error: %s"
+msgstr "Error de flux : %s"
+
+#: ../src/utils/pacat.c:378
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Periferic de flux arrestat.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Periferic de flux reaviat.%s"
+
+#: ../src/utils/pacat.c:388
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Flux void.%s"
+
+#: ../src/utils/pacat.c:395
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Flux saturat.%s"
+
+#: ../src/utils/pacat.c:402
+#, c-format
+msgid "Stream started.%s"
+msgstr "Flux aviat.%s"
+
+#: ../src/utils/pacat.c:409
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Flux desplaçat cap al periferic %s (%u, %sarrestat).%s"
+
+#: ../src/utils/pacat.c:409
+msgid "not "
+msgstr "non "
+
+#: ../src/utils/pacat.c:416
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "D'atributs del tampon de flux an cambiat.%s"
+
+#: ../src/utils/pacat.c:431
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:437
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:441
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr ""
+
+#: ../src/utils/pacat.c:466
+#, c-format
+msgid "Connection established.%s"
+msgstr "Connexion establida.%s"
+
+#: ../src/utils/pacat.c:469
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() a fracassat : %s"
+
+#: ../src/utils/pacat.c:507
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() a fracassat : %s"
+
+#: ../src/utils/pacat.c:513
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:517
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() a fracassat : %s"
+
+#: ../src/utils/pacat.c:530 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Fracàs de connexion : %s"
+
+#: ../src/utils/pacat.c:563
+msgid "Got EOF."
+msgstr "Fin de fichièr atencha."
+
+#: ../src/utils/pacat.c:600
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() a fracassat : %s"
+
+#: ../src/utils/pacat.c:621
+msgid "Got signal, exiting."
+msgstr "Senhal recebut, sortida en cors."
+
+#: ../src/utils/pacat.c:635
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Fracàs al moment de l'obtencion de la laténcia : %s"
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Durada : %0.3f seg. ; Laténcia : %0.0f µseg."
+
+#: ../src/utils/pacat.c:661
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() a fracassat : %s"
+
+#: ../src/utils/pacat.c:671
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the "
+"sink/source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the "
+"sink/source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+
+#: ../src/utils/pacat.c:788
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr ""
+
+#: ../src/utils/pacat.c:792
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr ""
+
+#: ../src/utils/pacat.c:796
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+
+#: ../src/utils/pacat.c:800
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+
+#: ../src/utils/pacat.c:814
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Compilat amb libpulse %s\n"
+"Ligat amb libpulse %s\n"
+
+#: ../src/utils/pacat.c:847 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Nom del client invalid « %s »"
+
+#: ../src/utils/pacat.c:862
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Nom del flux invalid « %s »"
+
+#: ../src/utils/pacat.c:899
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Plan dels canals invalid « %s »"
+
+#: ../src/utils/pacat.c:928 ../src/utils/pacat.c:942
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Especificacion de laténcia invalida « %s »"
+
+#: ../src/utils/pacat.c:935 ../src/utils/pacat.c:949
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Especificacion de temps de tractament invalida « %s »"
+
+#: ../src/utils/pacat.c:961
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Proprietat invalida « %s »"
+
+#: ../src/utils/pacat.c:980
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Format de fichièr desconegut %s."
+
+#: ../src/utils/pacat.c:995
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:1006
+msgid "Invalid sample specification"
+msgstr "Especificacion d'escandalhatge invalida"
+
+#: ../src/utils/pacat.c:1016
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1028
+msgid "Too many arguments."
+msgstr "Tròp de paramètres."
+
+#: ../src/utils/pacat.c:1039
+msgid "Failed to generate sample specification for file."
+msgstr ""
+"Fracàs al moment de la generacion de las informacions de l'escandalhatge del "
+"fichièr."
+
+#: ../src/utils/pacat.c:1065
+msgid "Failed to open audio file."
+msgstr "Fracàs al moment de la dobertura del fichièr àudio."
+
+#: ../src/utils/pacat.c:1071
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Avertiment : las especificacions de l'escandalhatge especificat seràn "
+"espotidas per las del fichièr."
+
+#: ../src/utils/pacat.c:1074 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr ""
+"Fracàs al moment de l'obtencion de las informacions de l'escandalhatge del "
+"fichièr."
+
+#: ../src/utils/pacat.c:1083
+msgid "Warning: Failed to determine channel map from file."
+msgstr ""
+"Avertiment : fracàs al moment de l'obtencion de las informacions del plan "
+"dels canals del fichièr."
+
+#: ../src/utils/pacat.c:1094
+msgid "Channel map doesn't match sample specification"
+msgstr ""
+"Lo plan dels canals correspond pas a l'especificacion d'escandalhatge"
+
+#: ../src/utils/pacat.c:1105
+msgid "Warning: failed to write channel map to file."
+msgstr ""
+"Avertiment : Fracàs al moment de l'escritura del plan dels canals dins lo "
+"fichièr."
+
+#: ../src/utils/pacat.c:1120
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Dobertura d'un flux %s amb una especificacion d'escandalhatge « %s » e un "
+"plan dels canals « %s »."
+
+#: ../src/utils/pacat.c:1121
+msgid "recording"
+msgstr "enregistrament"
+
+#: ../src/utils/pacat.c:1121
+msgid "playback"
+msgstr "lectura"
+
+#: ../src/utils/pacat.c:1145
+msgid "Failed to set media name."
+msgstr "Impossible de definir lo nom del supòrt."
+
+#: ../src/utils/pacat.c:1152 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() a fracassat."
+
+#: ../src/utils/pacat.c:1175
+msgid "io_new() failed."
+msgstr "io_new() a fracassat."
+
+#: ../src/utils/pacat.c:1182 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() a fracassat."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "Fracàs de pa_context_connect() : %s"
+
+#: ../src/utils/pacat.c:1196
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() a fracassat."
+
+#: ../src/utils/pacat.c:1203 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() a fracassat."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "NOM [ARGS ...]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "NOM|#N"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "NOM"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "NOM|#N VOLUM"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N VOLUM"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "NOM|#N 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "NOM|#N KEY=VALUE"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#N KEY=VALUE"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "NAME SINK|#N"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "NOM NOMDELFICHIÈR"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "NOM D'ACCÈS"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "NOMDELFICHIÈR COLLECTOR|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#N COLLECTOR|FONT"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "PERFIL DE LA CARTA"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "NOM|#N PÒRT"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "NOM-DE-CARTA|CARTA-#N PÒRT OFFSET"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "CIBLA"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr ""
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "TRAMAS"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Compilat amb libpulse %s\n"
+"Ligat amb libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Pas cap de demòni PulseAudio en cors d'execucion, o que s'executa pas dins "
+"una session de tipe demòni."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0) : %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect() : %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Impossible de tuar lo demòni PulseAudio."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Lo demòni respond pas."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write() : %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll() : %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read() : %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Fracàs al moment de la recuperacion de las estatisticas : %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "En cors d'utilizacion : %u blòts que conten al total %s octets.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid ""
+"Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Atribuit pendent l'ensemble de la durada d'execucion : %u blòts que contenon "
+"al total %s octets.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Talha de l'amagatal de l'escandalhatge : %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr ""
+"Fracàs al moment de la recuperacion de las informacions del servidor : %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Nom del servidor : %s\n"
+"Version del protocòl de la bibliotèca : %u\n"
+"Version del protocòl del servidor : %u\n"
+"Es local : %s\n"
+"Indèx del client : %u\n"
+"Talha del teule : %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Nom d'utilizaire : %s\n"
+"Nom d'òste : %s\n"
+"Nom del servidor[nbsp): %s\n"
+"Version del servidor : %s\n"
+"Especificacion de l'escandalhatge per defaut : %s\n"
+"Correspondéncia dels canals per defaut : %s\n"
+"Sortida àudio per defaut : %s\n"
+"Font per defaut : %s\n"
+"Cookie : %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr ""
+"Fracàs al moment de l'obtencion de las informacions sus la destinacion : %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPòrts :\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tPòrt actiu : %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tFormats :\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Fracàs al moment de l'obtencion de las informacions sus la font : %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "n/d"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr ""
+"Fracàs al moment de la recuperacion de las informacions sul modul  : %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modul #%u\n"
+"\tNom : %s\n"
+"\tParamètre : %s\n"
+"\tNombre d'utilizacions : %s\n"
+"\tProprietats : \n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr ""
+"Fracàs al moment de la recuperacion de las informacions sul client  : %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Client #%u\n"
+"\tPilòt : %s\n"
+"\tModul proprietari : %s\n"
+"\tProprietats :\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr ""
+"Fracàs al moment de la recuperacion de las informacions sus la carta  : %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Carta #%u\n"
+"\tNom : %s\n"
+"\tPilòt : %s\n"
+"\tModul proprietari : %s\n"
+"\tProprietats :\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tPerfils :\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tPerfil actiu : %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tProprietats :\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tPartida del o dels perfil(s) : %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr ""
+"Fracàs al moment de la recuperacion de las informacions sus l'entrada de la "
+"destinacion  : %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr ""
+"Fracàs al moment de la recuperacion de las informacions sus la sortida de la "
+"font  : %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr ""
+"Fracàs al moment de la recuperacion de las informacions sus l'escandalhatge "
+" : %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Fracàs : %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Impossible de descargar lo modul : Modul %s pas cargat"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Impossible de definir le format : nom de format invalid %s"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Impossible de mandar l'escandalhatge : %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Fin de fichièr prematurada (EOF)."
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "novèl"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "cambiats"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "suprimir"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "desconegut"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "destinacion"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "font"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "entrada de collector"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "sortida de font"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "modul"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "client"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "cache per l'escandalhatge"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "servidor"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "Carta"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "eveniment '%s' sus %s #%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT recebut, tampadura."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Especificacion de volume invalid"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Volum de delà del sulhet autorizat.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[opcions]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[TIPE]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "NOM_DE_FICHIÈR [NOM]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "NOM [COLLECTOR]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "NOM|#N 1|0|bascular"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|bascular"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "#N FORMATS"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Los noms especials @DEFAULT_SINK@, @DEFAULT_SOURCE@ e @DEFAULT_MONITOR@\n"
+"pòdon èsser utilizats per especificar la destinacion, la font e lo monitor "
+"per defaut.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Mòstra aquesta ajuda\n"
+"      --version                         Mòstra la version\n"
+"\n"
+"  -s, --server=SERVER                   Lo nom del servidor al qual se "
+"connectar\n"
+"  -n, --client-name=NAME                Cossí apelar aqueste client sul "
+"servidor\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Compilat amb libpulse %s\n"
+"Ligat amb libpulse %s\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Especificar pas res, o una valor demest : %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Indicatz un fichièr d'escandalhatge de cargar"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Fracàs al moment de la dobertura d'un fichièr sonòr."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+"Avertiment : Fracàs al moment de l'obtencion de las especificacions de "
+"l'escandalhatge del fichièr."
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Vos cal indicar un nom d'escandalhatge de legir"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Vos cal indicar un nom d'escandalhatge de suprimir"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "Vos cal indicar un indèx d'entrada de destinacion e una destinacion"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "Vos cal indicar un indèx de sortida de font e una font"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Vos cal indicar un nom de modul e de paramètres."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "Vos cal especificar l'indèx o lo nom d'un modul"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Podètz pas indicar mai d'una destinacion. Vos cal indicar una valor booleana."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Especificacion de suspension invalida"
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Podètz pas indicar mai d'una font. Vos cal indicar una valor booleana."
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Vos cal indicar un nom/un indèx de mapa e un nom de perfil"
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Vos cal indicar un nom/un indèx de destinacion e un nom de pòrt"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "Vos cal especificar lo nom de la destinacion"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "Vos cal indicar un nom/un indèx de font e un nom de pòrt"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Vos cal especificar lo nom de la font"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Vos cal indicar un nom/un indèx de destinacion e un volum"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Vos cal indicar un nom/un indèx de font e un volum"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "Vos cal indicar un indèx d'entrada de destinacion e un volum"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "Indèx invalid d'entrada de la destinacion"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "Vos cal especificar un indèx de font àudio e un volum"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Indèx de font àudio invalid"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Especificacion indicador mut invalid"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "Especificacion d'indèx d'entrada de la destinacion invalida"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Especificacion d'indèx de sortida activa invalida"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Vos cal especificar un indèx de collector e una lista dels formats preses en "
+"carga separada per de punt-virgulas"
+
+#: ../src/utils/pactl.c:2038
+msgid ""
+"You have to specify a card name/index, a port name and a latency offset"
+msgstr ""
+"Vos cal especificar un nom/indèx  de carta, un nom de pòrt e una "
+"compensacion de laténcia"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Impossible d'analisar la compensacion de la laténcia"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Cap de comanda valida pas indicada."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork() : %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp() : %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Fracàs de la represa : %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Fracàs al moment de la suspension : %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "AVERTIMENT : lo servidor de son es pas local, suspension anullada.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Fracàs al moment de la connexion : %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT recebut, tampadura.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "AVERTIMENT : lo processus filh es estat acabat pel senhal %u\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [opcions] ... \n"
+"\n"
+"  -h, --help                            Aficha aquesta ajuda\n"
+"      --version                         Aficha la version\n"
+"  -s, --server=SERVIDOR                  Lo nom del servidor al qual se "
+"connectar\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Compilat amb libpulse %s\n"
+"Ligat amb libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "Fracàs de pa_mainloop_new().\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "Fracàs de pa_context_new().\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "Fracàs de pa_mainloop_run().\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D visual] [-S servidor] [-O destinacion] [-I font] [-c fichièr]  [-d|-"
+"e|-i|-r]\n"
+"\n"
+" -d    Aficha las donadas PulseAudio actualas estacadas al visual X11 (per "
+"defaut)\n"
+" -e    Expòrta las donadas PulseAudio localas cap al visual X11\n"
+" -i    Impòrta las donadas PulseAudio dempuèi lo visuel X11 cap a las "
+"variablas de l'environament local e lo fichièr de cookie.\n"
+" -r    Lèva las donadas PulseAudio del visual X11\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Fracàs al moment de l'analisi de la linha de comanda.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Servidor : %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Font : %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Destinacion : %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie : %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Fracàs al moment de l'analisi de las donadas del cookie\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Fracàs al moment de l'enregistrament de las donadas del cookie\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Fracàs al moment de l'obtencion del FQDN (« nom de domeni complet »).\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Fracàs al moment del cargament de las donadas del cookie\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Pas encara implementat.\n"
+
+#, c-format
+#~ msgid "%s %s\n"
+#~ msgstr "%s %s\n"
diff --git a/po/or.po b/po/or.po
new file mode 100644 (file)
index 0000000..68be2ec
--- /dev/null
+++ b/po/or.po
@@ -0,0 +1,3031 @@
+# translation of pulseaudio.master-tx.or.po to Oriya
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Manoj Kumar Giri <mgiri@redhat.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx.or\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:55+0000\n"
+"Last-Translator: Manoj Kumar Giri <mgiri@redhat.com>\n"
+"Language-Team: Oriya <oriya-it@googlegroups.com>\n"
+"Language: or\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms:  nplurals=2; plural=(n!=1);\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "ସର୍ବଦା ଅତିକମରେ ଗୋଟିଏ ସିଙ୍କକୁ ଧାରଣ କରନ୍ତୁ ଯଦିଚ ତାହା ଗୋଟିଏ ଶୂନ୍ୟ ଅଟେ"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "ପ୍ରତିରୂପି ଫଳାଫଳ"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "ଆଭାସୀ LADSPA ସିଙ୍କ"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<ସିଙ୍କର ନାମ> sink_properties=<ସିଙ୍କର ଗୁଣଧର୍ମ> master=<ସିଙ୍କ ଛାଣକର ନାମ> "
+"format=<ନମୁନା ସଜ୍ଜିକରଣ ଶୈଳୀ> rate=<ନମୁନା ହାର> channels=<ଚ୍ୟାନେଲ ସଂଖ୍ୟା> "
+"channel_map=<ଚ୍ୟାନେଲ ମ୍ୟାପ> plugin=<ladspa ପ୍ଲଗଇନ ନାମ> label=<ladspa ପ୍ଲଗଇନ "
+"ନାମପଟି> control=<କମା ଦ୍ୱାରା ପୃଥକ ନିବେଶ ନିୟନ୍ତ୍ରଣ ମୂଲ୍ୟ ତାଲିକା>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "ସମୟାନୁବର୍ତ୍ତି ଶୂନ୍ୟ ସିଙ୍କ"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "ଶୂନ୍ୟ ଫଳାଫଳ"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "ଆଭ୍ୟନ୍ତରୀଣ ଧ୍ୱନି"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "ମଡେମ"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "ପ୍ରକୃତ lt_dlopen ଧାରକକୁ ଖୋଜି ପାଇବାରେ ବିଫଳ।"
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "ନୂତନ dl ଧାରକକୁ ବଣ୍ଟନ କରିବାରେ ବିଫଳ।"
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind-now-loaderକୁ ଯୋଗ କରିବାରେ ବିଫଳ।"
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "ସଂକେତ %s ପାଇଲା।"
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "ଉତ୍ତେଜିତ କରୁଅଛି।"
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "ଚାଳକ '%s' କୁ ଖୋଜିବାରେ ବିଫଳ।"
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "ସମୂହ '%s' କୁ ଖୋଜି ପାଇବାରେ ବିଫଳ।"
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "ଚାଳକ '%s' (UID %lu) ଏବଂ ସମୂହ '%s' (GID %lu) ମିଳିଲା।"
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "ଚାଳକ '%s' ଏବଂ ସମୂହ '%s' ର GID ମେଳଖାଏନାହିଁ।"
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "ଚାଳକ '%s' ର ମୂଖ୍ୟ ଡିରେକ୍ଟୋରୀଟି '%s' ନୁହଁ, ଅଗ୍ରାହ୍ୟ କରୁଅଛି।"
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' ନିର୍ମାଣ କରିବାରେ ବିଫଳ: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "ସମୂହ ତାଲିକାକୁ ପରିବର୍ତ୍ତନ କରିବାରେ ବିଫଳ: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID କୁ ପରିବର୍ତ୍ତନ କରିବାରେ ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID କୁ ପରିବର୍ତ୍ତନ କରିବାରେ ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "ମୂଖ୍ୟ ଚାଳକ ଅଧିକାରକୁ ସଫଳତାର ସହିତ ପକାଯାଇଛି।"
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "ତନ୍ତ୍ରମୟ ଧାରା ଏହି ପ୍ଲାଟଫର୍ମରେ ଅସମର୍ଥିତ।"
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "ପାଠ୍ୟ ନିର୍ଦ୍ଦେଶକୁ ବିଶ୍ଳେଷଣ କରିବାରେ ବିଫଳ।"
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "ଡେମନ ଚାଲୁନାହିଁ"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "ଡେମନ PID %u ପରି ଚାଲୁଅଛି"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "ଡେମନକୁ ବନ୍ଦ କରିବାରେ ବିଫଳ: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"ଏହି ପ୍ରଗ୍ରାମଟି ମୂଖ୍ୟ ଚାଳକ ଭାବରେ ଚଲାଇବା ପାଇଁ ନିର୍ଦ୍ଦିଷ୍ଟ ହୋଇନାହିଁ (unless --system is "
+"specified)।"
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "ମୂଖ୍ୟ ଚାଳକ ଅଧିକାର ଆବଶ୍ୟକ।"
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start ତନ୍ତ୍ର ସ୍ଥିତି ପାଇଁ ସମର୍ଥିତ ନୁହଁ।"
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "ତନ୍ତ୍ର ଧାରାରେ ଚାଲୁଅଛି, କିନ୍ତୁ --disallow-exit କୁ ସେଟ କରାଯାଇନାହିଁ!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "ତନ୍ତ୍ର ଧାରାରେ ଚାଲୁଅଛି, କିନ୍ତୁ --disallow-module-loading କୁ ସେଟ କରାଯାଇନାହିଁ!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "ତନ୍ତ୍ର ଧାରାରେ ଚାଲୁଅଛି, SHM ଧାରାକୁ ବାଧ୍ଯତାମୁଳକ ଭାବରେ ନିଷ୍କ୍ରିୟ କରିଥାଏ!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "ତନ୍ତ୍ର ଧାରାରେ ଚାଲୁଅଛି, ପ୍ରସ୍ଥାନ ସ୍ଥିର ସମୟକୁ ବାଧ୍ଯତାମୁଳକ ଭାବରେ ନିଷ୍କ୍ରିୟ କରିଥାଏ!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdio କୁ ଅଧିକାର କରିବାରେ ବିଫଳ।"
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "ପାଇପ ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "ଡେମନ ଆରମ୍ଭ ବିଫଳ ହୋଇଛି।"
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "ଡେମନ ଆରମ୍ଭ ସଫଳ ହୋଇଛି।"
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "ଏହା ହେଉଛି PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "ସଂକଳନ ଆଧାର: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "ସଂକଳନ CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "ଆଧାରରେ ଚାଲୁଅଛି: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u CPUs ମିଳିଛି।"
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "ପୃଷ୍ଠା ଆକାରଟି ହେଉଛି %lu ବାଇଟ"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Valgrind ସମର୍ଥନ ସହିତ ସଂକଳନ ହୋଇଛି: yes"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Valgrind ସମର୍ଥନ ସହିତ ସଂକଳନ ହୋଇଛି: no"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "valgrind ଧାରାରେ ଚାଲୁଅଛି: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "ଆଧାରରେ ଚାଲୁଅଛି: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "ଉପଯୁକ୍ତ ନିର୍ମାଣ: yes"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "ଉପଯୁକ୍ତ ନିର୍ମାଣ: no"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG କୁ ବ୍ୟାଖ୍ୟା କରାଯାଇଛି, ସମସ୍ତ ନିଶ୍ଚୟକୁ ନିଷ୍କ୍ରିୟ କରାଯାଇଛି।"
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH କୁ ବ୍ୟାଖ୍ୟା କରାଯାଇଛି, କେବଳ ତୀବ୍ର ପଥ ନିଶ୍ଚୟକୁ ନିଷ୍କ୍ରିୟ କରାଯାଇଛି।"
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "ସମସ୍ତ ନିଶ୍ଚୟକୁ ସକ୍ରିୟ କରାଯାଇଛି।"
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "ଯନ୍ତ୍ର ID ପାଇବାରେ ବିଫଳ"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "ଯନ୍ତ୍ର ID ଟି ହେଉଛି %s।"
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "ଅଧିବେଶନ ID ଟି ହେଉଛି %s।"
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "ପ୍ରଚଳିତ ଡିରେକ୍ଟୋରୀ %s କୁ ବ୍ୟବହାର କରୁଅଛି।"
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "ସ୍ଥିତି ଡିରେକ୍ଟୋରୀ %s କୁ ବ୍ୟବହାର କରି।"
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "ଏକକାଂଶ ଡିରେକ୍ଟୋରୀ %s କୁ ବ୍ୟବହାର କରି।"
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "ତନ୍ତ୍ର ଧାରାରେ ଚାଲୁଅଛି: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"OK, ଆପଣ PA କୁ ତନ୍ତ୍ର ଧାରାରେ ଚଲାଉଛନ୍ତି। ଦୟାକରି ମନେ ରଖନ୍ତୁ ଯେ ଆପଣ ପ୍ରାୟତଃ ତାହା "
+"କରୁନାହାନ୍ତି।\n"
+"ଯଦି ଆପଣ ତାହା କରନ୍ତି ଏବଂ ସବୁକିଛି ଠିକରେ ନଚାଲେ ତେବେ ତାହା ଆପଣଙ୍କର ନିଜର ଦୋଷ।\n"
+"ତନ୍ତ୍ର ଧାରାଟି ସାଧାରଣତଃ କାହିଁକି ଖରାପ ତାହା ବିଷୟରେ ଜାଣିବା ପାଇଁ ଦୟାକରି http://www."
+"freedesktop.org/wiki/Software/PulseAudio/Documentation/User/"
+"WhatIsWrongWithSystemWide/ କୁ ପଢ଼ନ୍ତୁ।"
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() ବିଫଳ ହୋଇଛି।"
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "ସତେଜ ଉଚ୍ଚ-ବିଭେଦନ ସମୟ ମାପକ ଉପଲବ୍ଧ! Bon appetit!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() ବିଫଳ ହୋଇଛି।"
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "ଡେମନକୁ ଆରମ୍ଭ କରିବାରେ ବିଫଳ।"
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "ଧାରଣ ହୋଇଥିବା ଏକକାଂଶଗୁଡ଼ିକ ବିନା ଡେମନ ଆରମ୍ଭ ହୋଇଛି, କାର୍ଯ୍ୟ କରିବାକୁ ବାରଣ କରୁଅଛି।"
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "ଡେମନ ଆରମ୍ଭ ସମ୍ପୂର୍ଣ୍ଣ ହୋଇଛି।"
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "ଡେମନ ବନ୍ଦକୁ ଆରମ୍ଭ କରାଯାଇଛି।"
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "ଡେମନକୁ ସମାପ୍ତ କରାଯାଇଛି।"
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            ଏହି ସହାୟତା ଦର୍ଶାନ୍ତୁ\n"
+"      --version                         ସଂସ୍କରଣ ଦର୍ଶାନ୍ତୁ\n"
+"      --dump-conf                       ପୂର୍ବନିର୍ଦ୍ଧାରିତ ବିନ୍ୟାସକୁ ଡମ୍ପ କରନ୍ତୁ\n"
+"      --dump-modules                    ଉପଲବ୍ଧ ଏକକାଂଶଗୁଡ଼ିକର ଡମ୍ପ ତାଲିକା\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           ଡେମନକୁ ଆରମ୍ଭ କରନ୍ତୁ ଯଦି ତାହା ଚାଲୁନାହିଁ\n"
+"  -k  --kill                            ଚାଲୁଥିବା ଡେମନକୁ ବନ୍ଦ କରନ୍ତୁ\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --module-idle-time=SECS           Unload autoloaded modules when idle "
+"and\n"
+"                                        this time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr} Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "ଅବୈଧ ଲଗ ଲକ୍ଷ୍ଯସ୍ଥଳ: 'syslog', 'stderr' କିମ୍ବା 'auto' କୁ ବ୍ୟବହାର କରନ୍ତୁ।"
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "ଅବୈଧ ପୁନଃ ପ୍ରତିଦର୍ଶନ ଧାରା '%s'।"
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm ବୁଲିଆନ ସ୍ୱତନ୍ତ୍ରଚର ଆଶା କରୁଅଛି"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "ନାମ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "କୌଣସି ଏକକାଂଶ ସୂଚନା ଉପଲବ୍ଧ ନାହିଁ\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "ସଂସ୍କରଣ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "ବର୍ଣ୍ଣନା: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "ଲେଖକ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "ବ୍ୟବହାର ବିଧି: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "ଥରେ ଧାରଣ କରନ୍ତୁ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "DEPRECATION WARNING: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "ପଥ: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] ଅବୈଧ ଲଗ ଲକ୍ଷ୍ଯସ୍ଥଳ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] ଅବୈଧ ଲଗ ସ୍ତର%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] ଅବୈଧ ପୁନଃ ମିଶ୍ରଣ ଧାରା '%s'।"
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] ଅବୈଧ rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] ଅବୈଧ ନମୁନା ଶୈଳୀ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] ଅବୈଧ ନମୁନା ହାର '%s'।"
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] ଅବୈଧ ନମୁନା ଚ୍ୟାନେଲ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] ଅବୈଧ ଚ୍ୟାନେଲ ମ୍ୟାପ '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] ଅବୈଧ ସଂଖ୍ୟକ ଖଣ୍ଡଗୁଡ଼ିକ '%s'."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] ଅବୈଧ ଖଣ୍ଡ ଆକାର '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] ଅବୈଧ ସୁନ୍ଦର ସ୍ତର '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] ଅବୈଧ ନମୁନା ହାର '%s'।"
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "ବିନ୍ୟାସ ଫାଇଲ ଖୋଲିବାରେ ବିଫଳ: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"ନିର୍ଦ୍ଦିଷ୍ଟିତ ପୂର୍ବନିର୍ଦ୍ଧାରିତ ଚ୍ୟାନେଲ ମ୍ୟାପରେ ପୂର୍ବନିର୍ଦ୍ଧାରିତ ସଂଖ୍ୟକ ଚ୍ୟାନେଲ ବ୍ୟତିତ ଭିନ୍ନ ସଂଖ୍ୟକ "
+"ଚ୍ୟାନେଲ ଉଲ୍ଲେଖ ଅଛି।"
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### ବିନ୍ୟାସ ଫାଇଲରୁ ପଢ଼ନ୍ତୁ: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "ଅଧିକାରଗୁଡ଼ିକୁ ବାତିଲ କରୁଅଛି।"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio ଧ୍ୱନି ତନ୍ତ୍ର"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "PulseAudio ଧ୍ୱନି ତନ୍ତ୍ରକୁ ଆରମ୍ଭ କରନ୍ତୁ"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio ଧ୍ୱନି ତନ୍ତ୍ର"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "PulseAudio ଧ୍ୱନି ତନ୍ତ୍ରକୁ ଆରମ୍ଭ କରନ୍ତୁ"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "ମୋନୋ"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "ସାମ୍ନା ପାଖ କେନ୍ଦ୍ର"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "ସାମ୍ନା ବାମ ପାଖ"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "ସାମ୍ନା ଡ଼ାହାଣ ପାଖ"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "ପଛ ପାଖ କେନ୍ଦ୍ର"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "ପଛ ବାମ ପାଖ"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "ପଛ ଡ଼ାହାଣ ପାଖ"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "ସାମ୍ନା କେନ୍ଦ୍ର-ର-ବାମ ପାଖ"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "ସାମ୍ନା କେନ୍ଦ୍ର-ର-ଡ଼ାହାଣ ପାଖ"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "ବାମ ପାର୍ଶ୍ୱ"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "ଡ଼ାହାଣ ପାର୍ଶ୍ୱ"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "ସହାୟକ 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "ସହାୟକ 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "ସହାୟକ 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "ସହାୟକ 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "ସହାୟକ 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "ସହାୟକ 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "ସହାୟକ 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "ସହାୟକ 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "ସହାୟକ 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "ସହାୟକ 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "ସହାୟକ 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "ସହାୟକ 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "ସହାୟକ 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "ସହାୟକ 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "ସହାୟକ 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "ସହାୟକ 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "ସହାୟକ 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "ସହାୟକ 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "ସହାୟକ 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "ସହାୟକ 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "ସହାୟକ 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "ସହାୟକ 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "ସହାୟକ 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "ସହାୟକ 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "ସହାୟକ 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "ସହାୟକ 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "ସହାୟକ 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "ସହାୟକ 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "ସହାୟକ 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "ସହାୟକ 28"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "ସହାୟକ 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "ସହାୟକ 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "ଉପର କେନ୍ଦ୍ର"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "ଉପର ସମ୍ନା ପାଖ କେନ୍ଦ୍ର"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "ଉପର ସାମ୍ନା ବାମ ପାଖ"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "ଉପର ସାମ୍ନା ଡ଼ାହାଣ ପାଖ"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "ଉପର ପଛ ପାଖ କେନ୍ଦ୍ର"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "ଉପର ପଛ ବାମ ପାଖ"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "ଉପର ପଛ ଡ଼ାହାଣ ପାଖ"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(ଅବୈଧ)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "ଷ୍ଟେରିଓ"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "ଚତୁଃ ପାର୍ଶ୍ୱ 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "ଚତୁଃ ପାର୍ଶ୍ୱ 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "ଚତୁଃ ପାର୍ଶ୍ୱ 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "ଚତୁଃ ପାର୍ଶ୍ୱ 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "ଚତୁଃ ପାର୍ଶ୍ୱ 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "ଅଭିଗମ୍ୟତା ବାରଣ ହୋଇଛି"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "ଅଜଣା ନିର୍ଦ୍ଦେଶ"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "ଅବୈଧ ସ୍ୱତନ୍ତ୍ରଚର"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "ବସ୍ତୁ ଅବସ୍ଥିତ"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "ଏପରି କୌଣସି ବସ୍ତୁ ନାହିଁ"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "ସଂଯୋଗ ବାରଣ ହୋଇଛି"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "ପ୍ରୋଟୋକଲ ତ୍ରୁଟି"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "ସମୟ ସମାପ୍ତ"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "କୌଣସି ପ୍ରାଧିକରଣ କି ନାହିଁ"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "ଆଭ୍ୟନ୍ତରୀଣ ତ୍ରୁଟି"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "ସଂଯୋଗ ବନ୍ଦ ହୋଇଯାଇଛି"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "ବସ୍ତୁକୁ ବନ୍ଦକରାଯାଇଛି"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "ଅବୈଧ ସର୍ଭର"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "ଏକକାଂଶ ପ୍ରାରମ୍ଭିକରଣ ବିଫଳ ହୋଇଛି"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "ଖରାପ ସ୍ଥିତି"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "କୌଣସି ତଥ୍ୟ ନାହିଁ"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "ଅସଙ୍ଗତ ପ୍ରୋଟୋକଲ ସଂସ୍କରଣ"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "ଅତ୍ୟଧିକ ବଡ଼"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "ସମର୍ଥିତ ନୁହଁ"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "ଅଜଣା ତ୍ରୁଟି ସଂକେତ"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "ଏପରି କୌଣସି ଅନୁଲଗ୍ନ ନାହିଁ"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "ଅଚଳ କାର୍ଯ୍ୟକାରିତା"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "ଅନୁପସ୍ଥିତ ପ୍ରୟୋଗ"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "ଗ୍ରାହକ ଶାଖାଯୁକ୍ତ ହୋଇଛି"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "ନିବେଶ/ଫଳାଫଳ ତ୍ରୁଟି"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "ଉପକରଣ ଅଥବା ଉତ୍ସ ବ୍ୟସ୍ତ ଅଛି"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "କୁକି ତଥ୍ୟକୁ ବିଶ୍ଳେଷଣ କରିବାରେ ବିଫଳ"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "ବିନ୍ୟାସ ଫାଇଲ '%s' କୁ ଖୋଲିବାରେ ବିଫଳ: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "କୌଣସି କୁକି ଧାରଣ କରାଯାଇନାହିଁ। ତାହା ବିନା ସଂଯୋଗ କରିବାକୁ ପ୍ରଚେଷ୍ଚା କରୁଅଛି।"
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "ଅଜଣା ଅନୁଲଗ୍ନ '%s' ପାଇଁ ସନ୍ଦେଶ ଗ୍ରହଣ କରିଅଛି"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "ଧାରାକୁ ବାହାର କରିବାରେ ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "ପଛଚଲା ଧାରାକୁ ବାହାର କରାଯାଇଛି।"
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "ସର୍ଭର ପ୍ରତି ଡ୍ରେନିଙ୍ଗ ସଂଯୋଗ।"
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "ଧାରା ସଫଳତାର ସହିତ ନିର୍ମାଣ ହୋଇଛି।"
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "ବଫର ମେଟ୍ରିକସ: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "ବଫର ମେଟ୍ରିକସ: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "ନମୁନା spec '%s' ବ୍ୟବହାର କରି, ଚ୍ୟାନେଲ ମ୍ୟାପ '%s'।"
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "ଉପକରଣ %s ସହିତ ସଂଯୁକ୍ତ ହୋଇଛି (%u, %ssuspended)।"
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "ଧାରା ତ୍ରୁଟି: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "ଧାରା ଉପକରଣ ନିଲମ୍ବିତ ହୋଇଛି।%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "ଧାରା ଉପକରଣ ପୁନଃ ଚଳନ ହୋଇଛି।%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "ଧାରା underrun.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "ଧାରା overrun.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "ଧାରା ଆରମ୍ଭ ହୋଇଛି।%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "ଧାରା ଉପକରଣ %sକୁ ଗତି କରିଛି (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "not "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "ଧାରା ବଫର ଗୁଣଗୁଡ଼ିକ ପରିବର୍ତ୍ତନ ହୋଇଛି।%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "ସଂଯୋଗ ସ୍ଥାପିତ ହୋଇଛି।%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "ସଂଯୋଗ ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF ପାଇଅଛି।"
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "ସଂକେତ ପାଇଲା, ପ୍ରସ୍ଥାନ କରୁଅଛି।"
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "ବିଳମ୍ବତା ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "ସମୟ: %0.3f ସେକଣ୍ଡ; ବିଳମ୍ବତା: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [ବିକଳ୍ପଗୁଡ଼ିକ]\n"
+"\n"
+"  -h, --help                            ଏହି ସହାୟତା ଦର୍ଶାନ୍ତୁ\n"
+"      --version                         ସଂସ୍କରଣ ଦର୍ଶାନ୍ତୁ\n"
+"\n"
+"  -r, --record                          ଲେଖିବା ପାଇଁ ସଂଯୋଗ ନିର୍ମାଣ କରନ୍ତୁ\n"
+"  -p, --playback                        ପଛକୁ ଚଲାଇବା ପାଇଁ ଗୋଟିଏ ସଂଯୋଗ ନିର୍ମାଣ "
+"କରନ୍ତୁ\n"
+"\n"
+"  -v, --verbose                         verbose ପ୍ରକ୍ରିୟାଗୁଡ଼ିକୁ ସକ୍ରିୟ କରନ୍ତୁ\n"
+"\n"
+"  -s, --server=SERVER                   ସଂଯୋଗ କରିବା ପାଇଁ ସର୍ଭରର ନାମ\n"
+"  -d, --device=DEVICE                   ସଂଯୋଗ କରିବା ପାଇଁ ସିଙ୍କ/ଉତ୍ସର ନାମ\n"
+"  -n, --client-name=NAME                ସର୍ଭରରେ ଏହି ଗ୍ରାହକଙ୍କୁ କିପରି ଡାକିବେ\n"
+"      --stream-name=NAME                ସର୍ଭରରେ ଏହି ଧାରାକୁ କିପରି ଡାକିବେ\n"
+"      --volume=VOLUME                   ପ୍ରାରମ୍ଭିକ (ସିଧା) ଭଲ୍ୟୁମକୁ ସୀମା 0...65536 "
+"ମଧ୍ଯରେଉଲ୍ଲେଖ କରନ୍ତୁ\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be "
+"(defaults to s16ne)\n"
+"      --channels=CHANNELS               ଚ୍ୟାନେଲ ସଂଖ୍ୟା, ମୋନୋ ପାଇଁ 1, ଷ୍ଟେରିଓ ପାଇଁ "
+"2\n"
+"                                        (2 ପାଇଁ ପୂର୍ବନିର୍ଦ୍ଧାରିତ)\n"
+"      --channel-map=CHANNELMAP          ପୂର୍ବନିର୍ଦ୍ଧାରିତ ପରିବର୍ତ୍ତେ ବ୍ୟବହାର କରିବାକୁ ଥିବା "
+"ଚ୍ୟାନେଲ ମ୍ୟାପ\n"
+"      --fix-format                      ନମୁନା ବିଶେଷ ଲକ୍ଷଣ ସଜ୍ଜିକରଣ ଶୈଳୀକୁ ସିଙ୍କରୁ "
+"ଗ୍ରହଣ କରନ୍ତୁ\n"
+"                                        ଯାହା ସହିତ ଧାରାଟି ସଂଯୁକ୍ତ।\n"
+"      --fix-rate                        ନମୁନା ବିଶେଷ ଲକ୍ଷଣ ସଜ୍ଜିକରଣ ଶୈଳୀକୁ ସିଙ୍କରୁ "
+"ଗ୍ରହଣ କରନ୍ତୁ\n"
+"                                        ଯାହା ସହିତ ଧାରାଟି ସଂଯୁକ୍ତ।\n"
+"      --fix-channels                    ଚ୍ୟାନେଲ ସଂଖ୍ୟା ଏବଂ ଚ୍ୟାନେଲ ମ୍ୟାପକୁ "
+"ସିଙ୍କରୁଗ୍ରହଣ କରନ୍ତୁ\n"
+"                                        ଯାହା ସହିତ ଧାରାଟି ସଂଯୁକ୍ତ।\n"
+"      --no-remix                        ଚ୍ୟାନେଲଗୁଡ଼ିକୁ upmix ଅଥବା downmix କରନ୍ତୁ "
+"ନାହିଁ।\n"
+"      --no-remap                        ନାମ ପରିବର୍ତ୍ତେ ଅନୁକ୍ରମଣିକା ଅନୁସାରେ "
+"ଚ୍ୟାନେଲଗୁଡ଼ିକୁ ମ୍ୟାପ କରନ୍ତୁ।\n"
+"      --latency=BYTES                   ନିର୍ଦ୍ଦିଷ୍ଟିତ ବିଳମ୍ବତାକୁ ବାଇଟରେ ଆବେଦନ କରନ୍ତୁ।\n"
+"      --process-time=BYTES              ଅନୁରୋଧ ପ୍ରତି ନିର୍ଦ୍ଦିଷ୍ଟିତ ବିଳମ୍ବତାକୁ ବାଇଟରେ "
+"ଆବେଦନ କରନ୍ତୁ।\n"
+"      --property=PROPERTY=VALUE         ନିର୍ଦ୍ଦିଷ୍ଟିତ ମୂଲ୍ୟ ପାଇଁ ଉଲ୍ଲିଖିତ ଗୁଣଧର୍ମକୁ ସେଟ "
+"କରନ୍ତୁ।\n"
+"      --raw                             ଅପରିପକ୍ୱ PCM ତଥ୍ୟକୁ ଲିପିବଦ୍ଧ କରନ୍ତୁ/ଚଲାନ୍ତୁ।\n"
+"      --file-format=FFORMAT             ସଜ୍ଜିକୃତ PCM ତଥ୍ୟକୁ ଲିପିବଦ୍ଧ କରନ୍ତୁ/ଚଲାନ୍ତୁ।\n"
+"      --list-file-formats               ତାଲିକା ଉପଲବ୍ଧ ଫାଇଲ ସଜ୍ଜିକରଣ ଶୈଳୀ।\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"libpulse %s ସହିତ ସଂକଳିତ\n"
+"libpulse %s ସହିତ ସଂଯୁକ୍ତ\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "ଅବୈଧ କ୍ଲାଏଣ୍ଟ ନାମ '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "ଅବୈଧ ଧାରା ନାମ '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "ଅବୈଧ ଚ୍ୟାନେଲ ମ୍ୟାପ '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "ଅବୈଧ ବିଳମ୍ବତା ବିଶେଷ ଲକ୍ଷଣ '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "ଅବୈଧ ପଦ୍ଧତି ସମୟ ବିଶେଷ ଲକ୍ଷଣ '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "ଅବୈଧ ଗୁଣଧର୍ମ '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "ଅଜଣା ଫାଇଲ ସଜ୍ଜିକରଣ ଶୈଳୀ %s।"
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "ଅବୈଧ ନମୁନା ବିଶେଷ ଲକ୍ଷଣ"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "ଅତ୍ୟଧିକ ସ୍ୱତନ୍ତ୍ରଚର।"
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "ଫାଇଲ ପାଇଁ ନମୁନା ସୂଚନା ସୃଷ୍ଟି କରିବାରେ ବିଫଳ।"
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "ଧ୍ୱନି ଫାଇଲ ଖୋଲିବାରେ ବିଫଳ।"
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr "ଚେତାବନୀ: ଉଲ୍ଲିଖିତ ନମୁନା ବିଶେଷ ଲକ୍ଷଣକୁ ଫାଇଲରୁ ବିଶେଷ ଲକ୍ଷଣ ସହିତ ନବଲିଖନ କରାଯିବ।"
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "ଫାଇଲରୁ ନମୁନା ସୂଚନା ନିର୍ଦ୍ଧାରଣ କରିବାରେ ବିଫଳ।"
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "ଚେତାବନୀ: ଫାଇଲରୁ ଚ୍ୟାନେଲ ମ୍ୟାପ ନିର୍ଦ୍ଧାରଣ କରିବାରେ ବିଫଳ।"
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "ନମୁନା ବିଶେଷ ଲକ୍ଷଣ ସହିତ ଚ୍ୟାନେଲ ମ୍ୟାପ ମେଳ ଖାଉନାହିଁ"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "ଚେତାବନୀ: ଚ୍ୟାନେଲ ମ୍ୟାପକୁ ଫାଇଲରେ ଲେଖିବାରେ ବିଫଳ।"
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "ନମୁନା ବିଶେଷ ଲକ୍ଷଣ '%s' ଏବଂ ଚ୍ୟାନେଲ ମ୍ୟାପ '%s' ସହିତ ଗୋଟିଏ %s ଧାରାକୁ ଖୋଲୁଅଛି।"
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "ଅନୁଲିପି କରୁଅଛି"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "ପଛଚଲା"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "ପାଠ୍ୟ ନିର୍ଦ୍ଦେଶକୁ ବିଶ୍ଳେଷଣ କରିବାରେ ବିଫଳ।"
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() ବିଫଳ ହୋଇଛି।"
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() ବିଫଳ ହୋଇଛି।"
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() ବିଫଳ ହୋଇଛି।"
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() ବିଫଳ ହୋଇଛି: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() ବିଫଳ ହୋଇଛି।"
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() ବିଫଳ ହୋଇଛି।"
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "ନିଲମ୍ବନ କରିବାରେ ବିଫଳ: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "ପୁନଃ ଚଳନ କରିବାରେ ବିଫଳ: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "WARNING: ଧ୍ୱନି ସର୍ଭରଟି ସ୍ଥାନୀୟ ନୁହଁ, ନିଲମ୍ବିତ କରୁନାହିଁ।\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "ସଂଯୋଗ ବିଫଳ ହୋଇଛି: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT ପାଇଛି, ଉତ୍ସାହିତ କରୁଅଛି।\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "WARNING: ନିମ୍ନ ସ୍ତରର ପ୍ରକ୍ରିୟାଟି ସଂକେତ %u ଦ୍ୱାରା ସମାପ୍ତ\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            ଏହି ସହାୟତାକୁ ଦର୍ଶାନ୍ତୁ\n"
+"      --version                         ସଂସ୍କରଣ ଦର୍ଶାନ୍ତୁ\n"
+"  -s, --server=SERVER                   ସଂଯୋଗ କରିବା ପାଇଁ ସର୍ଭରର ନାମ\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"libpulse %s ସହିତ ସଂକଳିତ\n"
+"libpulse %s ସହିତ ସଂଯୁକ୍ତ\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() ବିଫଳ ହୋଇଛି।\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() ବିଫଳ ହୋଇଛି।\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() ବିଫଳ ହୋଇଛି।\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "ପରିସଂଖ୍ୟାନ ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "ବର୍ତ୍ତମାନ ବ୍ୟବହାରରେ ଅଛି: %u ବ୍ଲକ ସମୁଦାୟ %s ବାଇଟ ଧାରଣ କରିଥାଏ।\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "ସମଗ୍ର ଜୀବନରେ ବଣ୍ଟିତ ହୋଇଥାଏ: %u ବ୍ଲକ ସମୁଦାୟ %s ବାଇଟ ଧାରଣ କରିଥାଏ।\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "ନମୁନା କ୍ୟାଶେ ଆକାର: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "ସର୍ଭର ସୂଚନା ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"ଚାଳକ ନାମ: %s\n"
+"ଆଧାର ନାମ: %s\n"
+"ସର୍ଭର ନାମ: %s\n"
+"ସର୍ଭର ସଂସ୍କରଣ: %s\n"
+"ପୂର୍ବନିର୍ଦ୍ଧାରିତ ନମୁନା ବିଶିଷ୍ଟ: %s\n"
+"ପୂର୍ବନିର୍ଦ୍ଧାରିତ ଚ୍ୟାନେଲ ମ୍ୟାପ: %s\n"
+"ପୂର୍ବନିର୍ଦ୍ଧାରିତ ସିଙ୍କ: %s\n"
+"ପୂର୍ବନିର୍ଦ୍ଧାରିତ ଉତ୍ସ: %s\n"
+"କୁକି: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "ସିଙ୍କ ସୂଚନା ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ସିଙ୍କ #%u\n"
+"\tସ୍ଥିତି: %s\n"
+"\tନାମ: %s\n"
+"\tବର୍ଣ୍ଣନା: %s\n"
+"\tଡ୍ରାଇଭର: %s\n"
+"\tନମୁନା ବିଶେଷ ଲକ୍ଷଣ: %s\n"
+"\tଚ୍ୟାନେଲ ମ୍ୟାପ: %s\n"
+"\tନିଜ ଏକକାଂଶ: %u\n"
+"\tମୁକ: %s\n"
+"\tଭଲ୍ୟୁମ: %s%s%s\n"
+"\t        ସମତୁଲ %0.2f\n"
+"\tଆଧାର ଭଲ୍ୟୁମ: %s%s%s\n"
+"\tପ୍ରଦର୍ଶିକା ଉତ୍ସ: %s\n"
+"\tବିଲମ୍ବତା: %0.0f usec, ବିନ୍ୟାସିତ %0.0f usec\n"
+"\tଚିହ୍ନକ: %s%s%s%s%s%s\n"
+"\tଗୁଣଧର୍ମ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tସଂଯୋଗିକୀଗୁଡ଼ିକ:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tସକ୍ରିୟ ସଂଯୋଗିକୀ: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tସଂଯୋଗିକୀଗୁଡ଼ିକ:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "ଉତ୍ସ ସୂଚନା ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ଉତ୍ସ #%u\n"
+"\tସ୍ଥିତି: %s\n"
+"\tନାମ: %s\n"
+"\tବର୍ଣ୍ଣନା: %s\n"
+"\tଡ୍ରାଇଭର: %s\n"
+"\tନମୁନା ବିଶେଷ ଲକ୍ଷଣ: %s\n"
+"\tଚ୍ୟାନେଲ ମ୍ୟାପ: %s\n"
+"\tମାଲିକ ଏକକାଂଶ: %u\n"
+"\tମୁକ: %s\n"
+"\tଭଲ୍ୟୁମ: %s%s%s\n"
+"\t        ସମତୁଲ %0.2f\n"
+"\tଆଧାର ଭଲ୍ୟୁମ: %s%s%s\n"
+"\tସିଙ୍କର ପ୍ରଦର୍ଶିକା: %s\n"
+"\tବିଲମ୍ବତା: %0.0f usec, ବିନ୍ୟାସିତ %0.0f usec\n"
+"\tଚିହ୍ନକ: %s%s%s%s%s%s\n"
+"\tଗୁଣଧର୍ମ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "ଏକକାଂଶ ସୂଚନା ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ଏକକାଂଶ #%u\n"
+"\tନାମ: %s\n"
+"\tସ୍ୱତନ୍ତ୍ରଚର: %s\n"
+"\tଉପଯୋଗିତା ଗଣକ: %s\n"
+"\tଗୁଣଧର୍ମ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "ଗ୍ରାହକ ସୂଚନା ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ଗ୍ରାହକ #%u\n"
+"\tଡ୍ରାଇଭର: %s\n"
+"\tମାଲିକର ଏକକାଂଶ: %s\n"
+"\tଗୁଣଧର୍ମ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "କାର୍ଡ ସୂଚନା ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"କାର୍ଡ #%u\n"
+"\tନାମ: %s\n"
+"\tଡ୍ରାଇଭର: %s\n"
+"\tନିଜ ଏକକାଂଶ: %s\n"
+"\tଗୁଣଧର୍ମ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tରୂପରେଖଗୁଡ଼ିକ:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tସକ୍ରିୟ ରୂପରେଖା: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "ସିଙ୍କ ନିବେଶ ସୂଚନା ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ସିଙ୍କ ନିବେଶ #%u\n"
+"\tଡ୍ରାଇଭର: %s\n"
+"\tମାଲିକ ଏକକାଂଶ: %s\n"
+"\tଗ୍ରାହକ: %s\n"
+"\tସିଙ୍କ: %u\n"
+"\tନମୁନା ବିଶେଷ ଲକ୍ଷଣ: %s\n"
+"\tଚ୍ୟାନେଲ ମ୍ୟାପ: %s\n"
+"\tମୁକ: %s\n"
+"\tଭଲ୍ୟୁମ: %s\n"
+"\t        %s\n"
+"\t        ସମତୁଲ %0.2f\n"
+"\tବଫର ବିଳମ୍ବତା: %0.0f usec\n"
+"\tସିଙ୍କ ବିଳମ୍ବତା: %0.0f usec\n"
+"\tପୁନଃ ମିଶ୍ରଣ ପଦ୍ଧତି: %s\n"
+"\tଗୁଣଧର୍ମ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "ଉତ୍ସ ଫଳାଫଳ ସୂଚନା ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ସିଙ୍କ ନିବେଶ #%u\n"
+"\tଡ୍ରାଇଭର: %s\n"
+"\tମାଲିକ ଏକକାଂଶ: %s\n"
+"\tଗ୍ରାହକ: %s\n"
+"\tସିଙ୍କ: %u\n"
+"\tନମୁନା ବିଶେଷ ଲକ୍ଷଣ: %s\n"
+"\tଚ୍ୟାନେଲ ମ୍ୟାପ: %s\n"
+"\tମୁକ: %s\n"
+"\tଭଲ୍ୟୁମ: %s\n"
+"\t        %s\n"
+"\t        ସମତୁଲ %0.2f\n"
+"\tବଫର ବିଳମ୍ବତା: %0.0f usec\n"
+"\tସିଙ୍କ ବିଳମ୍ବତା: %0.0f usec\n"
+"\tପୁନଃ ମିଶ୍ରଣ ପଦ୍ଧତି: %s\n"
+"\tଗୁଣଧର୍ମ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "ନମୁନା ସୂଚନା ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"ନମୁନା #%u\n"
+"\tନାମ: %s\n"
+"\tନମୁନା ବିଶେଷ ଲକ୍ଷଣ: %s\n"
+"\tଚ୍ୟାନେଲ ମ୍ୟାପ: %s\n"
+"\tଭଲ୍ୟୁମ: %s\n"
+"\t        %s\n"
+"\t        ସମତୁଲ %0.2f\n"
+"\tଅବଧି: %0.1fs\n"
+"\tଆକାର: %s\n"
+"\tଶିଥିଳ: %s\n"
+"\tଫାଇଲ ନାମ: %s\n"
+"\tଗୁଣଧର୍ମ:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "ବିଫଳତା: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "ଉତ୍ସ ସୂଚନା ପାଇବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "ନମୁନାକୁ ଧାରଣ କରିବାରେ ବିଫଳ: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "ଫାଇଲର ସମୟ ପୂର୍ବ ସମାପ୍ତି"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "ଅବୈଧ ସର୍ଭର"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT ପାଇଛି, ଉତ୍ସାହିତ କରୁଅଛି।"
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "ଅବୈଧ ନମୁନା ବିଶେଷ ଲକ୍ଷଣ"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            ଏହି ସହାୟତାକୁ ଦର୍ଶାନ୍ତୁ\n"
+"      --version                         ସଂସ୍କରଣ ଦର୍ଶାନ୍ତୁ\n"
+"  -s, --server=SERVER                   ସଂଯୋଗ କରିବା ପାଇଁ ସର୍ଭରର ନାମ\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"libpulse %s ସହିତ ସଂକଳିତ\n"
+"libpulse %s ସହିତ ସଂଯୁକ୍ତ\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "ଧାରଣ କରିବା ପାଇଁ ଗୋଟିଏ ନୁମନା ଫାଇଲ ଉଲ୍ଲେଖ କରନ୍ତୁ"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "ଧ୍ୱନି ଫାଇଲ ଖୋଲିବାରେ ବିଫଳ।"
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "ଚେତାବନୀ: ଫାଇଲରୁ ନମୁନା ବିଶେଷ ଲକ୍ଷଣକୁ ନିର୍ଦ୍ଧାରଣ କରିବାରେ ବିଫଳ।"
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "ଚଲାଇବା ପାଇଁ ଆପଣଙ୍କୁ ଗୋଟିଏ ନମୁନା ନାମ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "କାଢ଼ିବା ପାଇଁ ଆପଣଙ୍କୁ ଗୋଟିଏ ନମୁନା ନାମ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ସିଙ୍କ ନିବେଶ ଅନୁକ୍ରମଣିକା ଏବଂ ଗୋଟିଏ ସିଙ୍କ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ଉତ୍ସ ନିର୍ଗମ ଅନୁକ୍ରମଣିକା ଏବଂ ଗୋଟିଏ ଉତ୍ସ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ଏକକାଂଶ ନାମ ଏବଂ ସ୍ୱତନ୍ତ୍ରଚରଗୁଡ଼ିକୁ ଉଲ୍ଲେଖ କରିବା ଉଚିତ।"
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ଏକକାଂଶ ଅନୁକ୍ରମଣିକାକୁ ଉଲ୍ଲେଖ କରିବା ଉଚିତ ନୁହଁ"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"ଆପଣଙ୍କୁ ଗୋଟିଏରୁ ଅଧିକ ସିଙ୍କ ଉଲ୍ଲେଖ କରିବାକୁ ପଡ଼ିନପାରେ। ଆପଣଙ୍କୁ ଗୋଟିଏ ବୁଲିଆନ ମୂଲ୍ୟ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ।"
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"ଆପଣଙ୍କୁ ଗୋଟିଏରୁ ଅଧିକ ଉତ୍ସ ଉଲ୍ଲେଖ କରିବାକୁ ପଡ଼ିନପାରେ। ଆପଣଙ୍କୁ ଗୋଟିଏ ବୁଲିଆନ ମୂଲ୍ୟ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ।"
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ କାର୍ଡ ନାମ/ଅନୁକ୍ରମଣିକା ଏବଂ ରୂପରେଖା ନାମ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ସିଙ୍କ ନାମ/ଅନୁକ୍ରମଣିକା ଏବଂ ସଂଯୋଗିକୀ ନାମ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ଉତ୍ସ ନାମ/ଅନୁକ୍ରମଣିକା ଏବଂ ସଂଯୋଗିକୀ ନାମ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ସିଙ୍କ ନାମ/ଅନୁକ୍ରମଣିକା ଏବଂ ସଂଯୋଗିକୀ ନାମ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ଉତ୍ସ ନାମ/ଅନୁକ୍ରମଣିକା ଏବଂ ସଂଯୋଗିକୀ ନାମ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ସିଙ୍କ ନିବେଶ ଅନୁକ୍ରମଣିକା ଏବଂ ଗୋଟିଏ ସିଙ୍କ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "ଅବୈଧ ସିଙ୍କ ନିବେଶ ଅନୁକ୍ରମଣିକା"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ଉତ୍ସ ନିର୍ଗମ ଅନୁକ୍ରମଣିକା ଏବଂ ଗୋଟିଏ ଉତ୍ସ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "ଅବୈଧ ସିଙ୍କ ନିବେଶ ଅନୁକ୍ରମଣିକା"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ସିଙ୍କ ନାମ/ଅନୁକ୍ରମଣିକା ଏବଂ ସଂଯୋଗିକୀ ନାମ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "ଅବୈଧ ନମୁନା ବିଶେଷ ଲକ୍ଷଣ"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ଉତ୍ସ ନାମ/ଅନୁକ୍ରମଣିକା ଏବଂ ସଂଯୋଗିକୀ ନାମ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ସିଙ୍କ ନିବେଶ ଅନୁକ୍ରମଣିକା ଏବଂ ଗୋଟିଏ ସିଙ୍କ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "ଅବୈଧ ସିଙ୍କ ନିବେଶ ଅନୁକ୍ରମଣିକା ବିଶେଷ ଲକ୍ଷଣ"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ଉତ୍ସ ନାମ/ଅନୁକ୍ରମଣିକା ଏବଂ ସଂଯୋଗିକୀ ନାମ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "ଅବୈଧ ସିଙ୍କ ନିବେଶ ଅନୁକ୍ରମଣିକା ବିଶେଷ ଲକ୍ଷଣ"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "ଆପଣଙ୍କୁ ଗୋଟିଏ ସିଙ୍କ ନାମ/ଅନୁକ୍ରମଣିକା ଏବଂ ସଂଯୋଗିକୀ ନାମ ଉଲ୍ଲେଖ କରିବାକୁ ହେବ"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "କୌଣସି ବୈଧ ନିର୍ଦ୍ଦେଶ ଉଲ୍ଲେଖ କରାଯାଇନାହିଁ।"
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    X11 ପ୍ରଦର୍ଶିକା ସହିତ ସଂଲଗ୍ନ ପ୍ରଚଳିତ PulseAudio ତଥ୍ୟ ଦର୍ଶାନ୍ତୁ (ପୂର୍ବନିର୍ଦ୍ଧାରିତ)\n"
+" -e    X11 ପ୍ରଦର୍ଶିକାକୁ ସ୍ଥାନୀୟ PulseAudio ତଥ୍ୟ ରପ୍ତାନି କରନ୍ତୁ\n"
+" -i    X11 ପ୍ରଦର୍ଶିକାରୁ ସ୍ଥାନୀୟ ପରିବେଶ ପ୍ରାଚଳ ଏବଂ କୁକି ଫାଇଲକୁ ଆମଦାନି କରନ୍ତୁ।\n"
+" -r    X11 ପ୍ରଦର୍ଶିକାରୁ PulseAudio ତଥ୍ୟକୁ କାଢ଼ନ୍ତୁ\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "ପାଠ୍ୟ ନିର୍ଦ୍ଦେଶକୁ ବିଶ୍ଳେଷଣ କରିବାରେ ବିଫଳ।\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "ସର୍ଭର: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "ଉତ୍ସ: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "ସିଙ୍କ: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "କୁକି: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "କୁକି ତଥ୍ୟକୁ ବିଶ୍ଳେଷଣ କରିବାରେ ବିଫଳ\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "କୁକି ତଥ୍ୟକୁ ସଂରକ୍ଷଣ କରିବାରେ ବିଫଳ\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "ଗ୍ରାହକ ବିନ୍ୟାସ ଫାଇଲକୁ ଧାରଣ କରିବାରେ ବିଫଳ।\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "ପରିବେଶ ବିନ୍ୟାସ ତଥ୍ୟକୁ ପଢ଼ିବାରେ ବିଫଳ।\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDNକୁ ପାଇବାରେ ବିଫଳ।\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "କୁକି ତଥ୍ୟ ଧାରଣ କରିବାରେ ବିଫଳ\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "ଅପର୍ଯ୍ୟନ୍ତ କାର୍ଯ୍ୟକାରୀ ହୋଇନାହିଁ।\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "କୌଣସି PulseAudio ଡେମନ ଚାଲୁନାହିଁ, କିମ୍ବା ଅଧିବେଶନ ଡେମନ ପରି ଚାଲୁନାହିଁ।"
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "ସକେଟ(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "ସଂଯୋଗ କରନ୍ତୁ(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio ଡେମନକୁ ବନ୍ଦ କରିବାରେ ବିଫଳ।"
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "ଡେମନ ଉତ୍ତର ଦେଉନାହିଁ।"
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "ପଢ଼ନ୍ତୁ(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "ଲେଖନ୍ତୁ(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "autospawn ଅପରିବର୍ତ୍ତନୀୟତାକୁ ଅଭିଗମ୍ୟ କରିହେଉ ନାହିଁ"
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "ଅଫ"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "ଉଚ୍ଚ ଫିଡିଲିଟି ପଛଚଲା (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "ଉଚ୍ଚ ଫିଡିଲିଟି ପଛଚଲା (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "ଟେଲିଫୋନି ଡ୍ୟୁପ୍ଲେକ୍ସ (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio ଧ୍ୱନି ସର୍ଭର"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "ଫଳାଫଳ ଉପକରଣ"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "ନିବେଶ ଉପକରଣ"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@ ରେ ଧ୍ୱନି"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "ନିବେଶ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "ଡକିଙ୍ଗ ଷ୍ଟେସନ ନିବେଶ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "ଡକିଙ୍ଗ ଷ୍ଟେସନ ମାଇକ୍ରୋଫୋନ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "ଡକିଙ୍ଗ ଷ୍ଟେସନ ନିବେଶ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "ଲାଇନ-ଇନ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "ମାଇକ୍ରୋଫୋନ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "ଡକିଙ୍ଗ ଷ୍ଟେସନ ମାଇକ୍ରୋଫୋନ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "ମାଇକ୍ରୋଫୋନ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "ବାହ୍ୟ ମାଇକ୍ରୋଫୋନ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "ଆଭ୍ୟନ୍ତରୀଣ ମାଇକ୍ରୋଫୋନ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "ରେଡିଓ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "ଭିଡ଼ିଓ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "ସ୍ୱୟଂଚାଳିତ ଲାଭ ନିୟନ୍ତ୍ରଣ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "କୌଣସି ସ୍ୱୟଂଚାଳିତ ଲାଭ ନିୟନ୍ତ୍ରଣ ନାହିଁ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "ବୃଦ୍ଧି"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "ବୃଦ୍ଧି ନାହିଁ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "ଏମ୍ପ୍ଲିଫାୟର"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "ଏମ୍ପ୍ଲିଫାୟର ନାହିଁ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "ବୃଦ୍ଧି"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "ବୃଦ୍ଧି ନାହିଁ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "ଏନାଲୋଗ ହେଡ଼ଫୋନଗୁଡ଼ିକ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "ଏନାଲୋଗ ନିବେଶ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "ଡକିଙ୍ଗ ଷ୍ଟେସନ ମାଇକ୍ରୋଫୋନ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "ଏନାଲୋଗ ଫଳାଫଳ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "ଏନାଲୋଗ ଫଳାଫଳ (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "ଲାଇନ-ଇନ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "ଏନାଲୋଗ ମୋନୋ ଫଳାଫଳ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "ଏନାଲୋଗ ଷ୍ଟେରିଓ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "ଡିଜିଟାଲ ଷ୍ଟେରିଓ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "ଡିଜିଟାଲ ଷ୍ଟେରିଓ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "ଏନାଲୋଗ ମୋନୋ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "ଏନାଲୋଗ ଷ୍ଟେରିଓ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "ଏନାଲୋଗ ଚତୁଃ ପାର୍ଶ୍ୱ 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "ଏନାଲୋଗ ଚତୁଃ ପାର୍ଶ୍ୱ 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "ଏନାଲୋଗ ଚତୁଃ ପାର୍ଶ୍ୱ 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "ଏନାଲୋଗ ଚତୁଃ ପାର୍ଶ୍ୱ 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "ଏନାଲୋଗ ଚତୁଃ ପାର୍ଶ୍ୱ 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "ଏନାଲୋଗ ଚତୁଃ ପାର୍ଶ୍ୱ 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "ଏନାଲୋଗ ଚତୁଃ ପାର୍ଶ୍ୱ 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "ଏନାଲୋଗ ଚତୁଃ ପାର୍ଶ୍ୱ 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "ଏନାଲୋଗ ଚତୁଃ ପାର୍ଶ୍ୱ 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "ଏନାଲୋଗ ଚତୁଃ ପାର୍ଶ୍ୱ 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "ଏନାଲୋଗ ଚତୁଃ ପାର୍ଶ୍ୱ 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "ଡିଜିଟାଲ ଷ୍ଟେରିଓ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "ଡିଜିଟାଲ ଷ୍ଟେରିଓ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "ଡିଜିଟାଲ ଚତୁଃ ପାର୍ଶ୍ୱ 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "ଡିଜିଟାଲ ଚତୁଃ ପାର୍ଶ୍ୱ 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "ଡିଜିଟାଲ ଷ୍ଟେରିଓ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "ଡିଜିଟାଲ ଚତୁଃ ପାର୍ଶ୍ୱ 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "ଏନାଲୋଗ ମୋନୋ ଡ଼ୁପ୍ଲେକ୍ସ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "ଏନାଲୋଗ ଷ୍ଟେରିଓ ଡ଼ୁପ୍ଲେକ୍ସ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "ଡିଜିଟାଲ ଷ୍ଟେରିଓ ଡ଼ୁପ୍ଲେକ୍ସ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "ଶୂନ୍ୟ ଫଳାଫଳ"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "ନିବେଶ"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<ସିଙ୍କର ନାମ> sink_properties=<ସିଙ୍କର ଗୁଣଧର୍ମ> master=<ସିଙ୍କ ଛାଣକର ନାମ> "
+"format=<ନମୁନା ସଜ୍ଜିକରଣ ଶୈଳୀ> rate=<ନମୁନା ହାର> channels=<ଚ୍ୟାନେଲ ସଂଖ୍ୟା> "
+"channel_map=<ଚ୍ୟାନେଲ ମ୍ୟାପ> plugin=<ladspa ପ୍ଲଗଇନ ନାମ> label=<ladspa ପ୍ଲଗଇନ "
+"ନାମପଟି> control=<କମା ଦ୍ୱାରା ପୃଥକ ନିବେଶ ନିୟନ୍ତ୍ରଣ ମୂଲ୍ୟ ତାଲିକା>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit ଏହି ପ୍ଲାଟଫର୍ମରେ ସମର୍ଥିତ ନୁହଁ।"
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() ବିଫଳ ହୋଇଛି"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "ଉତ୍ସ ନିର୍ଗମ #%u\n"
+#~ "\tଡ୍ରାଇଭର: %s\n"
+#~ "\tମାଲିକ ଏକକାଂଶ: %s\n"
+#~ "\tଗ୍ରାହକ: %s\n"
+#~ "\tଉତ୍ସ: %u\n"
+#~ "\tନମୁନା ବିଶେଷ ଲକ୍ଷଣ: %s\n"
+#~ "\tଚ୍ୟାନେଲ ମ୍ୟାପ: %s\n"
+#~ "\tବଫର ବିଳମ୍ବତା: %0.0f usec\n"
+#~ "\tଉତ୍ସ ବିଳମ୍ବତା: %0.0f usec\n"
+#~ "\tପୁନଃ ମିଶ୍ରଣ ପଦ୍ଧତି: %s\n"
+#~ "\tଗୁଣଧର୍ମ:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] stat\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] list\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] exit\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] upload-sample FILENAME [NAME]\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] play-sample NAME [SINK]\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] remove-sample NAME\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] move-sink-input ID SINK\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] move-source-output ID SOURCE\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] load-module NAME [ARGS ...]\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] unload-module ID\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] suspend-sink [SINK] 1|0\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] suspend-source [SOURCE] 1|0\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] set-card-profile [CARD] [PROFILE] \n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] set-sink-port [SINK] [PORT] \n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] set-source-port [SOURCE] [PORT] \n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] set-sink-volume SINK VOLUME\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] set-source-volume SOURCE VOLUME\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] set-sink-mute SINK 1|0\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] set-source-mute SOURCE 1|0\n"
+#~ "%s [ବିକଳ୍ପଗୁଡ଼ିକ] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            ଏହି ସହାୟତାକୁ ଦର୍ଶାନ୍ତୁ\n"
+#~ "      --version                         ସଂସ୍କରଣ ଦର୍ଶାନ୍ତୁ\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   ସଂଯୋଗ କରିବା ପାଇଁ ସର୍ଭରର ନାମ\n"
+#~ "  -n, --client-name=NAME                ସର୍ଭରରେ ଏହି କ୍ଲାଏଣ୍ଟକୁ କିପରି ଡାକିବେ\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "ଡିଜିଟାଲ ଚତୁଃ ପାର୍ଶ୍ୱ 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "ନିମ୍ନ ଆବୃତ୍ତି ପରିତ୍ୟାଗ କାରୀ"
diff --git a/po/pa.po b/po/pa.po
new file mode 100644 (file)
index 0000000..ac33eb0
--- /dev/null
+++ b/po/pa.po
@@ -0,0 +1,3016 @@
+# translation of pulseaudio.master-tx.pa.po to Punjabi
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Amanpreet Singh Alam <aalam@users.sf.net>, 2008.
+# A S Alam <aalam@users.sf.net>, 2009.
+# Jaswinder Singh <jsingh@redhat.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx.pa\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:55+0000\n"
+"Last-Translator: Jaswinder Singh <jsingh@redhat.com>\n"
+"Language-Team: Punjabi/Panjabi <kde-i18n-doc@kde.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Lokalize 1.0\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ਤੋਂ ਇੱਕ ਮੁੱਲ ਮਿਲਿਆ ਹੈ, ਜੋ ਬਹੁਤ ਵੱਡਾ ਹੈ: %lu ਬਾਈਟ (%lu ms)।\n"
+"ਇਹ ALSA ਡਰਾਈਵਰ '%s' ਵਿਚਲਾ ਬੱਗ ਲੱਗਦਾ ਹੈ। ਇਸ ਮੁੱਦੇ ਦੀ ALSA ਡਿਵੈਲਪਰਾਂ ਨੂੰ ਰਿਪੋਰਟ ਦਿਓ ਜੀ।"
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() ਤੋਂ ਇੱਕ ਮੁੱਲ ਮਿਲਿਆ ਹੈ, ਜੋ ਬਹੁਤ ਵੱਡਾ ਹੈ: %li ਬਾਈਟ (%s%lu ms)।\n"
+"ਇਹ ALSA ਡਰਾਈਵਰ '%s' ਵਿਚਲਾ ਬੱਗ ਲੱਗਦਾ ਹੈ। ਇਸ ਮੁੱਦੇ ਦੀ ALSA ਡਿਵੈਲਪਰਾਂ ਨੂੰ ਰਿਪੋਰਟ ਦਿਓ ਜੀ।"
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() ਤੋਂ ਇੱਕ ਮੁੱਲ ਮਿਲਿਆ ਹੈ, ਜੋ ਬਹੁਤ ਵੱਡਾ ਹੈ: %lu ਬਾਈਟ (%lu ms)।\n"
+"ਇਹ ALSA ਡਰਾਈਵਰ '%s' ਵਿਚਲਾ ਬੱਗ ਲੱਗਦਾ ਹੈ। ਇਸ ਮੁੱਦੇ ਦੀ ALSA ਡਿਵੈਲਪਰਾਂ ਨੂੰ ਰਿਪੋਰਟ ਦਿਓ ਜੀ।"
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() ਤੋਂ ਇੱਕ ਮੁੱਲ ਮਿਲਿਆ ਹੈ, ਜੋ ਬਹੁਤ ਵੱਡਾ ਹੈ: %lu ਬਾਈਟ (%lu ms)।\n"
+"ਇਹ ALSA ਡਰਾਈਵਰ '%s' ਵਿਚਲਾ ਬੱਗ ਲੱਗਦਾ ਹੈ। ਇਸ ਮੁੱਦੇ ਦੀ ALSA ਡਿਵੈਲਪਰਾਂ ਨੂੰ ਰਿਪੋਰਟ ਦਿਓ ਜੀ।"
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "ਹਮੇਸ਼ਾਂ ਘੱਟੋ-ਘੱਟ ਇੱਕ ਸਿੰਕ ਲੋਡ ਹੀ ਰੱਖੋ ਭਾਵੇਂ ਇਹ ਇੱਕ ਜ਼ੀਰੋ (null) ਹੇਵੋ"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "ਡੰਮੀ ਆਊਟਪੁੱਟ"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "ਵਰਚੁਅਲ LADSPA ਸਿੰਕ"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "ਕਲਾਕਡ NULL ਸਿੰਕ"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "ਜ਼ੀਰੋ (Null) ਆਉਟਪੁੱਟ"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "ਅੰਦਰੂਨੀ ਆਡੀਓ"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "ਮਾਡਮ"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "ਅਸਲੀ lt_dlopen ਲੋਡਰ ਲੱਭਣ ਵਿੱਚ ਫੇਲ੍ਹ ਹੋਇਆ।"
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "ਨਵਾਂ dl ਲੋਡਰ ਦੇਣ ਲਈ ਫੇਲ੍ਹ।"
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "ਬਾਈਂਡ-ਨਾਓ-ਲੋਡਰ ਜੋੜਨ ਵਿੱਚ ਫੇਲ੍ਹ ਹੋਇਆ।"
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "%s ਸਿਗਨਲ ਮਿਲਿਆ ਹੈ।"
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "ਬੰਦ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ।"
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "'%s' ਯੂਜ਼ਰ ਲੱਭਣ ਵਿੱਚ ਫੇਲ੍ਹ ਹੋਇਆ ਹੈ।"
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "'%s' ਗਰੁੱਪ ਲੱਭਣ ਵਿੱਚ ਫੇਲ ਹੋਇਆ ਹੈ।"
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "ਯੂਜ਼ਰ '%s' (UID %lu) ਅਤੇ ਗਰੁੱਪ '%s' (GID %lu) ਲੱਭੇ ਹਨ।"
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "ਯੂਜ਼ੂ '%s' ਅਤੇ ਗਰੁੱਪ '%s' ਦਾ GID ਮੇਲ ਨਹੀਂ ਖਾਂਦੇ।"
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "ਯੂਜ਼ੂ '%s' ਦੀ ਘਰ ਡਾਇਰੈਕਟਰੀ '%s' ਨਹੀਂ, ਅਣਡਿੱਠਾ ਕਰ ਰਿਹਾ।"
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' ਬਣਾਉਣ ਵਿੱਚ ਫੇਲ੍ਹ: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "ਗਰੁੱਪ ਲਿਸਟ ਬਦਲਣ ਲਈ ਫੇਲ੍ਹ: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID ਬਦਲਣ ਲਈ ਫੇਲ੍ਹ: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID ਬਦਲਣ ਲਈ ਫੇਲ੍ਹ: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "ਰੂਟ ਅਧਿਕਾਰ ਸਫਲਤਾਪੂਰਕ ਹਟਾਏ ਗਏ।"
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "ਇਸ ਪਲੇਟਫਾਰਮ ਤੇ ਸਿਸਟਮ ਸੰਬੰਧੀ ਮੋਡ ਨੂੰ ਸਹਿਯੋਗ ਨਹੀਂ ਹੈ।"
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) ਫੇਲ੍ਹ ਹੋਇਆ: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "ਕਮਾਂਡ ਲਾਈਨ ਪਾਰਸ ਕਰਨ ਵਿੱਚ ਫੇਲ੍ਹ।"
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "ਡੈਮਨ ਚੱਲ ਨਹੀਂ ਰਿਹਾ"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "ਡੈਮਨ PID %u ਤੌਰ ਤੇ ਚੱਲ ਰਿਹਾ ਹੈ"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "ਡੈਮਨ ਖਤਮ ਕਰਨ ਵਿੱਚ ਫੇਲ੍ਹ: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr "ਇਹ ਪਰੋਗਰਾਮ ਰੂਟ ਦੇ ਤੌਰ ਤੇ ਚਲਾਉਣ ਲਈ ਨਹੀਂ ਹੈ (ਜਦੋਂ ਤੱਕ --system ਦਿੱਤਾ ਨਹੀਂ ਜਾਂਦਾ)।"
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "ਰੂਟ ਅਧਿਕਾਰਾਂ ਦੀ ਲੋੜ ਹੈ।"
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start ਨੂੰ ਸਿਸਟਮ ਮੌਕਿਆਂ ਲਈ ਸਹਿਯੋਗ ਨਹੀਂ ਹੈ।"
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "ਸਿਸਟਮ ਮੋਡ ਵਿੱਚ ਚੱਲ ਰਿਹਾ ਹੈ, ਪਰ --disallow-exit ਸੈੱਟ ਨਹੀਂ ਕੀਤਾ!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "ਸਿਸਟਮ ਮੋਡ ਵਿੱਚ ਚੱਲ ਰਿਹਾ ਹੈ, ਪਰ --disallow-module-loading ਸੈੱਟ ਨਹੀਂ ਕੀਤਾ!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "ਸਿਸਟਮ ਮੋਡ ਵਿੱਚ ਚੱਲ ਰਿਹਾ ਹੈ, ਜ਼ਬਰਦਸਤੀ SHM ਮੋਡ ਨੂੰ ਅਯੋਗ ਕਰ ਰਿਹਾ ਹੈ!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "ਸਿਸਟਮ ਮੋਡ ਵਿੱਚ ਚੱਲ ਰਿਹਾ ਹੈ, ਜ਼ਬਰਦਸਤੀ idle ਟਾਈਲ ਬੰਦ ਨੂੰ ਅਯੋਗ ਕਰ ਰਿਹਾ ਹੈ!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "ਸਟੂਡੀਓ ਪ੍ਰਾਪਤ ਕਰਨ ਵਿੱਚ ਫੇਲ੍ਹ।"
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe ਫੇਲ੍ਹ: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "ਡੈਮਨ ਸ਼ੁਰੂਆਤੀ ਫੇਲ੍ਹ ਹੋਈ।"
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "ਡੈਮਨ ਸ਼ੁਰੂਆਤੀ ਸਫ਼ਲ ਹੋਈ।"
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "ਇਹ ਪਲਸਆਡੀਓ %s ਹੈ"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "ਕੰਪਾਈਲੇਸ਼ਨ ਹੋਸਟ: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "ਕੰਪਾਈਲੇਸ਼ਨ CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "ਹੋਸਟ ਤੇ ਚੱਲ ਰਿਹਾ ਹੈ: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u CPUs ਲੱਭੇ।"
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "ਪੇਜ਼ ਸਾਈਜ਼ %lu ਬਾਈਟ ਹੈ"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Valgrind ਸਹਿਯੋਗ ਨਾਲ ਕੰਪਾਈਲ: ਹਾਂ"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Valgrind ਸਹਿਯੋਗ ਨਾਲ ਕੰਪਾਈਲ: ਨਹੀਂ"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Valgrind ਮੋਡ ਵਿੱਚ ਚੱਲ ਰਿਹਾ ਹੈ: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "ਹੋਸਟ ਤੇ ਚੱਲ ਰਿਹਾ ਹੈ: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "ਓਪਟੀਮਾਈਜ਼ਡ ਬਿਲਡ: ਹਾਂ"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "ਓਪਟੀਮਾਈਜ਼ਡ ਬਿਲਡ: ਨਹੀਂ"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG ਪਰਿਭਾਸ਼ਤ, ਸਭ asserts ਅਯੋਗ ਹਨ।"
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH ਪਰਿਭਾਸ਼ਤ, ਸਿਰਫ ਫਾਸਟ ਪਾਥ asserts ਅਯੋਗ ਹਨ।"
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "ਸਭ asserts ਯੋਗ ਕੀਤੇ ਹਨ।"
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "ਮਸ਼ੀਨ ID ਪ੍ਰਾਪਤ ਕਰਨ ਵਿੱਚ ਫੇਲ੍ਹ"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "ਮਸ਼ੀਨ ID %s ਹੈ।"
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "ਸ਼ੈਸ਼ਨ ID %s ਹੈ।"
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "ਰਨਟਾਈਮ ਡਾਇਰੈਕਟਰੀ %s ਦੀ ਵਰਤੋਂ।"
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "ਸਟੇਟ ਡਾਇਰੈਕਟਰੀ %s ਦੀ ਵਰਤੋਂ।"
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "ਮੈਡਿਊਲ ਡਾਇਰੈਕਟਰੀ %s ਦੀ ਵਰਤੋਂ।"
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "ਸਿਸਟਮ ਮੋਡ ਵਿੱਚ ਚੱਲ ਰਿਹਾ ਹੈ: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"ਠੀਕ ਹੈ, ਤਾਂ ਤੁਸੀਂ PA ਨੂੰ ਸਿਸਟਮ ਮੋਡ ਵਿੱਚ ਚਲਾ ਰਹੇ ਹੋ। ਕਿਰਪਾ ਕਰਕੇ ਧਿਆਨ ਰੱਖੋ ਕਿ ਤੁਹਾਨੂੰ ਇਹ ਕਰਨਾ "
+"ਨਹੀਂ ਚਾਹੀਦਾ।\n"
+"ਜੇ ਤੁਸੀਂ ਅਜਿਹਾ ਕੀਤਾ ਹੈ ਤਾਂ ਇਹ ਤੁਹਾਡੀ ਗਲਤੀ ਹੈ ਜੇ ਲੋੜ-ਮੁਤਾਬਕ ਠੀਕ ਕੰਮ ਨਾ ਚੱਲਿਆ।\n"
+"ਕਿਰਪਾ ਕਰਕੇ ਸਿਸਟਮ ਮੋਡ ਦੇ ਗਲਤ ਹੋਣ ਬਾਰੇ ਵਧੇਰੇ ਜਾਣਕਾਰੀ ਲਈ http://www.freedesktop.org/"
+"wiki/Software/PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ ਵੇਖੋ।"
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() ਫੇਲ੍ਹ ਹੈ।"
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "ਤਾਜ਼ੀ ਹਾਈ-ਰੈਜ਼ੋਲੂਸ਼ਨ ਟਾਈਮਰ ਉਪਲੱਬਧ ਹੈ! Bon appetit!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"ਮਿੱਤਰਾ, ਤੇਰਾ ਕਰਨਲ ਪੁਰਾਣਾ ਹੈ! ਚੀਫ ਦੀ ਅੱਜ ਦੀ ਸਿਫਾਰਸ਼ ਹਾਈ-ਰੈਜ਼ੋਲੂਸ਼ਨ ਟਾਈਮਰ ਯੋਗ ਨਾਲ ਲੀਨਕਸ ਹੈ!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() ਫੇਲ੍ਹ ਹੈ।"
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "ਡੈਮਨ ਸ਼ੁਰੂ ਕਰਨ ਵਿੱਚ ਫੇਲ੍ਹ।"
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "ਡੈਮਨ ਸ਼ੁਰੂਆਤੀ ਬਿਨਾਂ ਕਿਸੇ ਲੋਡ ਕੀਤੇ ਮੈਡਿਊਲ, ਕੰਮ ਕਰਨ ਤੋਂ ਰੋਕ ਰਿਹਾ ਹੈ।"
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "ਡੈਮਨ ਸ਼ੁਰੂਆਤੀ ਮੁਕੰਮਲ।"
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "ਡੈਮਨ ਬੰਦ ਕਰਨਾ ਸ਼ੁਰੂ ਹੋ ਗਿਆ।"
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "ਡੈਮਨ ਬੰਦ ਹੋ ਗਿਆ।"
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --module-idle-time=SECS           Unload autoloaded modules when idle "
+"and\n"
+"                                        this time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr} Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level ਨੂੰ ਲਾਗ ਲੈਵਲ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ (ਜਾਂ ਤਾਂ ਅੰਕੀ ਰੇਂਜ 0..4 ਜਾਂ debug, info, "
+"notice, warn, error ਵਿੱਚੋਂ ਇੱਕ)।"
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "ਗਲਤ ਲਾਗ ਟਾਰਗੇਟ: 'syslog', 'stderr' ਜਾਂ 'auto' ਵਰਤੋਂ।"
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "ਅਢੁੱਕਵਾਂ ਰੀਸੈਂਪਲ ਢੰਗ '%s'।"
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm ਨੂੰ ਬੁਲੀਅਨ ਆਰਗੂਮੈਂਟ ਦੀ ਲੋੜ ਹੈ"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "ਨਾਂ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "ਕੋਈ ਮੋਡੀਊਲ ਜਾਣਕਾਰੀ ਉਪਲੱਬਧ ਨਹੀਂ\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "ਵਰਜਨ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "ਵੇਰਵਾ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "ਲੇਖਕ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "ਵਰਤੋਂ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "ਇੱਕ ਵਾਰ ਲੋਡ ਕਰੋ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "DEPRECATION WARNING: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "ਪਾਥ: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] ਗਲਤ ਲਾਗ ਟਾਰਗੇਟ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] ਗਲਤੀ ਲਾਗ ਲੈਵਲ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] ਅਢੁੱਕਵੀਂ ਰੀਸੈਂਪਲ ਢੰਗ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] ਅਢੁੱਕਵੀਂ rlimit '%s'।"
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] ਅਢੁੱਕਵਾਂ ਸੈਂਪਲ ਫਾਰਮੈਟ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] ਅਢੁੱਕਵਾਂ ਸੈਂਪਲ ਰੇਟ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] ਅਢੁੱਕਵਾਂ ਸੈਂਪਲ ਚੈਨਲ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] ਅਢੁੱਕਵਾਂ ਚੈਨਲ ਮੈਪ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] ਫਰੈਗਮੈਂਟਾਂ ਦਾ ਅਢੁੱਕਵਾਂ ਨੰਬਰ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] ਅਢੁੱਕਵਾਂ ਫਰੈਗਮੈਂਟ ਅਕਾਰ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] ਅਢੁੱਕਵਾਂ nice ਲੈਵਲ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] ਅਢੁੱਕਵਾਂ ਸੈਂਪਲ ਰੇਟ '%s'।"
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "ਸੰਰਚਨਾ ਫਾਇਲ ਖੋਲ੍ਹਣ ਵਿੱਚ ਫੇਲ੍ਹ: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr "ਦਿੱਤੇ ਡਿਫਾਲਟ ਚੈਨਲ ਮੈਪ ਦੀ ਦਿੱਤੇਤ ਚੈਨਲ ਗਿਣਤੀ ਨਾਲੋਂ ਇੱਕ ਵੱਖਰੀ ਚੈਨਲ ਗਿਣਤੀ ਹੈ।"
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### ਸੰਰਚਨਾ ਫਾਇਲ ਵਿੱਚੋਂ ਪੜਿਆ: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "ਅਧਿਕਾਰ ਹਟਾ ਰਿਹਾ ਹੈ।"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "ਪਲਸਆਡੀਓ ਸਾਊਂਡ ਸਿਸਟਮ"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "ਪਲਸਆਡੀਓ ਸਾਊਂਡ ਸਿਸਟਮ ਚਲਾਓ"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "ਪਲਸਆਡੀਓ ਸਾਊਂਡ ਸਿਸਟਮ"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "ਪਲਸਆਡੀਓ ਸਾਊਂਡ ਸਿਸਟਮ ਚਲਾਓ"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "ਮੋਨੋ"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "ਅੱਗੇ ਸੈਂਟਰ"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "ਅੱਗੇ ਖੱਬੇ"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "ਅੱਗੇ ਸੱਜਾ"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "ਪਿੱਛੇ ਸੈਂਟਰ"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "ਪਿੱਛੇ ਖੱਬਾ"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "ਪਿੱਛੇ ਸੱਜਾ"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "ਅੱਗੇ ਸੈਂਟਰ ਦਾ ਖੱਬੇ"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "ਅੱਗੇ ਸੈਂਟਰ ਦਾ ਸੱਜਾ"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "ਖੱਬੇ ਪਾਸੇ"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "ਸੱਜੇ ਪਾਸੇ"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "ਐਗਜਿਲਰੀ 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "ਐਗਜਿਲਰੀ 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "ਐਗਜਿਲਰੀ 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "ਐਗਜਿਲਰੀ 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "ਐਗਜਿਲਰੀ 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "ਐਗਜਿਲਰੀ 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "ਐਗਜਿਲਰੀ 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "ਐਗਜਿਲਰੀ 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "ਐਗਜਿਲਰੀ 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "ਐਗਜਿਲਰੀ 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "ਐਗਜਿਲਰੀ 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "ਐਗਜਿਲਰੀ 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "ਐਗਜਿਲਰੀ 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "ਐਗਜਿਲਰੀ 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "ਐਗਜਿਲਰੀ 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "ਐਗਜਿਲਰੀ 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "ਐਗਜਿਲਰੀ 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "ਐਗਜਿਲਰੀ 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "ਐਗਜਿਲਰੀ 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "ਐਗਜਿਲਰੀ 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "ਐਗਜਿਲਰੀ 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "ਐਗਜਿਲਰੀ 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "ਐਗਜਿਲਰੀ 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "ਐਗਜਿਲਰੀ 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "ਐਗਜਿਲਰੀ 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "ਐਗਜਿਲਰੀ 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "ਐਗਜਿਲਰੀ 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "ਐਗਜਿਲਰੀ 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "ਐਗਜਿਲਰੀ 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "ਐਗਜਿਲਰੀ 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "ਐਗਜਿਲਰੀ 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "ਐਗਜਿਲਰੀ 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "ਉੱਤੇ ਕੇਂਦਰੀ"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "ਉੱਤੇ ਅੱਗੇ ਸੈਂਟਰ"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "ਉੱਤੇ ਅੱਗੇ ਖੱਬੇ"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "ਉੱਤੇ ਅੱਗੇ ਸੱਜੇ"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "ਉੱਤੇ ਪਿੱਛੇ ਸੈਂਟਰ"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "ਉੱਤੇ ਪਿੱਛੇ ਖੱਬੇ"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "ਉੱਤੇ ਪਿੱਛੇ ਸੱਜੇ"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(ਅਢੁੱਕਵਾਂ)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "ਸਟੀਰੀਓ"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "ਸਰਾਊਂਡਿੰਗ 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "ਸਰਾਊਂਡਿੰਗ 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "ਸਰਾਊਂਡਿੰਗ 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "ਸਰਾਊਂਡਿੰਗ 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "ਸਰਾਊਂਡਿੰਗ 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "ਠੀਕ ਹੈ"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "ਅਸੈੱਸ ਪਾਬੰਦੀ ਹੈ"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "ਅਣਜਾਣ ਕਮਾਂਡ"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "ਅਢੁੱਕਵਾਂ ਆਰਗੂਮੈਂਟ"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "ਐਂਟਟੀ ਮੌਜੂਦ ਹੈ"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "ਕੋਈ ਐਂਟਟੀ ਨਹੀਂ"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "ਕੁਨੈਕਸ਼ਨ ਤੋਂ ਇਨਕਾਰ"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "ਪਰੋਟੋਕਾਲ ਗਲਤੀ"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "ਸਮਾਂ-ਸਮਾਪਤ"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "ਕੋਈ ਪ੍ਰਮਾਣਿਕਤਾ ਕੁੰਜੀ ਨਹੀਂ"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "ਅੰਦਰੂਨੀ ਗਲਤੀ"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "ਕੁਨੈਕਸ਼ਨ ਖਤਮ ਕੀਤਾ"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "ਐਂਟਟੀ ਖਤਮ ਹੋ ਗਈ"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "ਅਢੁੱਕਵਾਂ ਸਰਵਰ"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "ਮੋਡੀਊਲ ਸ਼ੁਰੂ ਕਰਨਾ ਫੇਲ੍ਹ"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "ਖਰਾਬ ਹਾਲਤ"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "ਕੋਈ ਡਾਟਾ ਨਹੀਂ"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "ਨਾ-ਅਨੁਕੂਲ ਪਰੋਟੋਕਾਲ ਵਰਜਨ"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "ਬਹੁਤ ਵੱਡਾ"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "ਸਹਾਇਕ ਨਹੀਂ"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "ਅਣਜਾਣ ਗਲਤੀ ਕੋਡ"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "ਕੋਈ ਅਜਿਹੀ ਇਕਸਟੈਂਸ਼ਨ ਨਹੀਂ"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "ਛੱਡੀ ਗਈ ਫੰਕਸ਼ਨੈਲਿਟੀ"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "ਗੈਰ-ਮੌਜੂਦ ਨਿਰਧਾਰਨ"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "ਕਲਾਇਟ ਅੱਡ ਕੀਤਾ"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "ਇੰਪੁੱਟ/ਆਊਟਪੁੱਟ ਗਲਤੀ"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "ਜਤੰਰ ਜਾਂ ਸਰੋਤ ਵਰਤੋਂ ਅਧੀਨ ਹੈ"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "ਕੂਕੀ ਡਾਟਾ ਪਾਰਸ ਕਰਨ ਵਿੱਚ ਫੇਲ੍ਹ"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "ਸੰਰਚਨਾ ਫਾਇਲ '%s' ਨੂੰ ਖੋਲ੍ਹਣ ਵਿੱਚ ਫੇਲ੍ਹ: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "ਕੋਈ ਕੂਕੀ ਲੋਡ ਨਹੀਂ ਕੀਤੀ। ਇਸ ਤੋਂ ਬਿਨਾਂ ਕੁਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ।"
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "ਅਣਜਾਣੀ ਇਕਸਟੈਂਸ਼ਨ '%s' ਲਈ ਸੁਨੇਹਾ ਮਿਲਿਆ ਹੈ"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "ਸਟਰੀਮ ਡਰੇਨ ਫੇਲ੍ਹ ਹੋਇਆ: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "ਪਲੇਬੈਕ ਸਟਰੀਮ ਡਰੇਨ ਕੀਤੀ।"
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "ਸਰਵਰ ਨਾਲ ਕੁਨੈਕਸ਼ਨ ਡਰੇਨ ਹੋ ਰਿਹਾ ਹੈ।"
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_write() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "ਸਟਰੀਮ ਸਫਲਤਾਪੂਰਕ ਬਣ ਗਈ ਹੈ।"
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Buffer metrics: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "ਸਧਾਰਨ spec '%s', ਚੈਨਲ ਮੈਪ '%s' ਦੀ ਵਰਤੋਂ।"
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "%s ਜੰਤਰ ਨਾਲ ਕੁਨਕੈਟ ਕੀਤਾ (%u, %ssuspended)।"
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "ਸਟਰੀਮ ਗਲਤੀ: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "ਸਟਰੀਮ ਜੰਤਰ ਸਸਪੈਂਡ ਕੀਤਾ ਹੈ।%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "ਸਟਰੀਮ ਜੰਤਰ ਮੁੜ-ਪ੍ਰਾਪਤ ਕੀਤਾ।%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "ਸਟਰੀਮ ਅੰਡਰਰਨ।%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "ਸਟਰੀਮ ਓਵਰਰਨ।%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "ਸਟਰੀਮ ਸ਼ੁਰੂ ਕੀਤੀ। %s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "ਸਟਰੀਮ ਨੂੰ ਜੰਤਰ %s ਤੋਂ ਤਬਦੀਲ ਕੀਤਾ ਗਿਆ ਹੈ (%u, %ssuspended)।%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "ਨਹੀਂ "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "ਸਟਰੀਮ ਬਫਰ ਐਟਰੀਬਿਊਟ ਤਬਦੀਲ ਕੀਤੇ ਗਏ।%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "ਕੁਨੈਕਸ਼ਨ ਬਣ ਗਿਆ।%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "ਕੁਨੈਕਸ਼ਨ ਫੇਲ: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF ਮਿਲਿਆ।"
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "ਸਿਗਨਲ ਮਿਲਿਆ, ਬੰਦ ਹੋ ਰਿਹਾ ਹੈ।"
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "ਵਕਫਾ ਪ੍ਰਾਪਤੀ ਫੇਲ ਹੋਈ: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "ਟਾਈਮ: %0.3f ਸਕਿੰਟ; ਵਕਫਾ: %0.0f usec।"
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --file-format=FFORMAT             Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"libpulse %s ਦੇ ਕੰਪਾਇਲ\n"
+"libpulse %s ਨਾਲ ਲਿੰਕ ਕੀਤਾ\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "ਅਢੁੱਕਵਾਂ ਚੈਨਲ ਮੈਪ '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "ਅਢੁੱਕਵਾਂ ਰੀਸੈਂਪਲ ਢੰਗ '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "ਅਢੁੱਕਵਾਂ ਚੈਨਲ ਮੈਪ '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "ਅਢੁੱਕਵਾਂ ਵਕਫਾ ਹਦਾਇਤ '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "ਅਢੁੱਕਵਾਂ ਪਰੋਸੈੱਸ ਟਾਈਮ ਹਦਾਇਤ '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "ਅਢੁੱਕਵਾਂ ਰੀਸੈਂਪਲ ਢੰਗ '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "ਅਣਜਾਣ ਫਾਇਲ ਫਾਰਮੈਟ %s"
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "ਅਢੁੱਕਵਾਂ ਸੈਂਪਲ ਹਦਾਇਤ"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "ਬਹੁਤ ਵੱਧ ਆਰਗੂਮੈਂਟ।"
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "ਸੈਂਪਲ ਜਾਣਕਾਰੀ ਲੈਣ ਵਿੱਚ ਫੇਲ: %s"
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "ਸਾਊਂਡ ਫਾਇਲ ਖੋਲ੍ਹਣ ਲਈ ਫੇਲ੍ਹ ਹੈ।"
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr "ਇੱਕ %s ਸਟਰੀਮ ਨੂੰ ਸੈਂਪਲ ਹਦਾਇਤ '%s' ਨਾਲ ਖੋਲ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ।"
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "ਸੈਂਪਲ ਜਾਣਕਾਰੀ ਲੈਣ ਵਿੱਚ ਫੇਲ: %s"
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "ਇੱਕ %s ਸਟਰੀਮ ਨੂੰ ਸੈਂਪਲ ਹਦਾਇਤ '%s' ਨਾਲ ਖੋਲ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ।"
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "ਚੈਨਲ ਮੈਪ ਸੈਂਪਲ ਹਦਾਇਤ ਨਾਲ ਨਹੀਂ ਮਿਲਦਾ"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "ਇੱਕ %s ਸਟਰੀਮ ਨੂੰ ਸੈਂਪਲ ਹਦਾਇਤ '%s' ਨਾਲ ਖੋਲ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ।"
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "ਇੱਕ %s ਸਟਰੀਮ ਨੂੰ ਸੈਂਪਲ ਹਦਾਇਤ '%s' ਅਤੇ ਚੈਨਲ ਮੈਪ '%s' ਨਾਲ ਖੋਲ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ।"
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "ਰਿਕਾਰਡਿੰਗ"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "ਪਲੇਅਬੈਕ"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "ਕਮਾਂਡ ਲਾਈਨ ਪਾਰਸ ਕਰਨ ਵਿੱਚ ਫੇਲ੍ਹ।"
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() ਫੇਲ੍ਹ ਹੈ।"
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() ਫੇਲ੍ਹ ਹੈ।"
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() ਫੇਲ੍ਹ ਹੈ।"
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() ਫੇਲ੍ਹ ਹੈ: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_new() ਫੇਲ੍ਹ ਹੈ।"
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() ਫੇਲ੍ਹ ਹੈ।"
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "ਸਸਪੈਂਡ ਕਰਨ ਵਿੱਚ ਫੇਲ: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "ਮੁੜ-ਪ੍ਰਾਪਤੀ ਫੇਲ: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "ਚੇਤਾਵਨੀ: ਸਾਊਂਡ ਸਰਵਰ ਲੋਕਲ ਨਹੀਂ ਹੈ, ਸਸਪੈਂਡ ਨਹੀਂ ਹੋ ਰਿਹਾ।\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "ਕੁਨੈਕਸ਼ਨ ਫੇਲ: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT ਮਿਲਿਆ, ਬੰਦ ਹੋ ਰਿਹਾ ਹੈ।\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "ਚੇਤਾਵਨੀ: ਚਲਾਈਡ ਪਰੋਸੈੱਸ ਨੂੰ ਸਿਗਨਲ %u ਵਲੋਂ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"libpulse %s ਨਾਲ ਕੰਪਾਇਲ\n"
+"libpulse %s ਨਾਲ ਲਿੰਕ\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() ਫੇਲ੍ਹ ਹੈ।\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() ਫੇਲ੍ਹ ਹੈ।\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() ਫੇਲ੍ਹ ਹੈ।\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "ਅੰਕੜੇ ਪ੍ਰਾਪਤੀ ਫੇਲ੍ਹ: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "ਹੁਣ ਵਰਤੋਂ ਵਿੱਚ ਹੈ: %u ਬਲਾਕ ਵਿੱਚ ਕੁੱਲ %s ਬਾਈਟ ਹਨ।\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "ਪੂਰੇ ਲਾਈਫਟਾਈਮ ਵਿੱਚ ਜਾਰੀ ਕੀਤਾ ਗਿਆ: %u ਬਲਾਕ ਵਿੱਚ ਕੁੱਲ %s ਬਾਈਟ ਹਨ।\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "ਸੈਂਪਲ ਕੈਸ਼ ਸਾਈਜ਼: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "ਸਰਵਰ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਕਰਨ ਵਿੱਚ ਫੇਲ ਹੋਇਆ: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"User name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "ਸਿੰਕ ਜਾਣਕਾਰੀ ਲੈਣ ਵਿੱਚ ਫੇਲ੍ਹ: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tਪੋਰਟ:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tਸਰਗਰਮ ਪੋਰਟ: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tਪੋਰਟ:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "ਸਰੋਤ ਜਾਣਕਾਰੀ ਲੈਣ ਵਿੱਚ ਫੇਲ: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "ਉਪਲੱਬਧ ਨਹੀਂ"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "ਮੋਡੀਊਲ ਜਾਣਕਾਰੀ ਲੈਣ ਵਿੱਚ ਫੇਲ: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "ਕਲਾਇਟ ਜਾਣਕਾਰੀ ਲੈਣ ਵਿੱਚ ਫੇਲ: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "ਕਾਰਡ ਜਾਣਕਾਰੀ ਲੈਣ ਵਿੱਚ ਫੇਲ: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tਪਰੋਫਾਈਲ:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tਸਰਗਰਮ ਪਰੋਫਾਈਲ: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "ਇੰਪੁੱਟ ਜਾਣਕਾਰੀ ਲੈਣ ਵਿੱਚ ਫੇਲ੍ਹ: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "ਸਰੋਤ ਆਉਟਪੁੱਟ ਜਾਣਕਾਰੀ ਲੈਣ ਵਿੱਚ ਫੇਲ: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "ਸੈਂਪਲ ਜਾਣਕਾਰੀ ਲੈਣ ਵਿੱਚ ਫੇਲ: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "ਫੇਲ੍ਹ: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "ਸਰੋਤ ਜਾਣਕਾਰੀ ਲੈਣ ਵਿੱਚ ਫੇਲ: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "ਸੈਂਪਲ ਅੱਪਲੋਡ ਕਰਨ ਵਿੱਚ ਫੇਲ: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "ਫਾਇਲ ਦਾ ਸਮੇਂ ਤੋਂ ਪਹਿਲਾਂ ਅੰਤ"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "ਅਢੁੱਕਵਾਂ ਸਰਵਰ"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT ਮਿਲਿਆ, ਬੰਦ ਹੋ ਰਿਹਾ ਹੈ।"
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "ਅਢੁੱਕਵਾਂ ਸੈਂਪਲ ਹਦਾਇਤ"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "ਲੋਡ ਕਰਨ ਲਈ ਸੈਂਪਲ ਫਾਇਲ ਦਿਓ"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "ਸਾਊਂਡ ਫਾਇਲ ਖੋਲ੍ਹਣ ਲਈ ਫੇਲ੍ਹ ਹੈ।"
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "ਇੱਕ %s ਸਟਰੀਮ ਨੂੰ ਸੈਂਪਲ ਹਦਾਇਤ '%s' ਨਾਲ ਖੋਲ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ।"
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "ਖੇਡਣ ਲਈ ਤੁਹਾਨੂੰ ਸੈਂਪਲ ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "ਹਟਾਉਣ ਲਈ ਤੁਹਾਨੂੰ ਸੈਂਪਲ ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "ਤੁਹਾਨੂੰ ਇੰਪੁੱਟ ਲਿਸਟ ਅਤੇ ਇੱਕ ਸਿੰਕ ਨੂੰ ਸਿੰਕ ਕਰਨਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਸਰੋਤ ਆਉਟਪੁੱਟ ਲਿਸਟ ਅਤੇ ਇੱਕ ਸਰੋਤ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਮੋਡੀਊਲ ਨਾਂ ਅਤੇ ਆਰਗੂਮੈਂਟ ਦੇਣਾ ਪਵੇਗਾ।"
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਮੈਡੀਊਲ ਲਿਸਟ ਦੇਣੀ ਪਵੇਗੀ"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr "ਤੁਸੀਂ ਇੱਕ ਤੋਂ ਵੱਧ ਸਿੰਕ ਨਹੀਂ ਦੇ ਸਕਦੇ। ਤੁਹਾਨੂੰ ਇੱਕ ਬੁਲੀਅਨ ਮੁੱਲ ਦੇਣਾ ਪਵੇਗਾ।"
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr "ਤੁਸੀਂ ਇੱਕ ਤੋਂ ਵੱਧ ਸਰੋਤ ਨਹੀਂ ਦੇ ਸਕਦੇ। ਤੁਹਾਨੂੰ ਬੁਲੀਅਨ ਮੁੱਲ ਦੇਣਾ ਪਵੇਗਾ।"
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਕਾਰਡ ਨਾਂ/ਲਿਸਟ ਅਤੇ ਪਰੋਫਾਈਲ ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਕਾਰਡ ਨਾਂ/ਲਿਸਟ ਅਤੇ ਪਰੋਫਾਈਲ ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਕਾਰਡ ਨਾਂ/ਲਿਸਟ ਅਤੇ ਪਰੋਫਾਈਲ ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਕਾਰਡ ਨਾਂ/ਲਿਸਟ ਅਤੇ ਪਰੋਫਾਈਲ ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਕਾਰਡ ਨਾਂ/ਲਿਸਟ ਅਤੇ ਪਰੋਫਾਈਲ ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "ਤੁਹਾਨੂੰ ਇੰਪੁੱਟ ਲਿਸਟ ਅਤੇ ਇੱਕ ਸਿੰਕ ਨੂੰ ਸਿੰਕ ਕਰਨਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "ਅਢੁੱਕਵੀਂ ਸਿੰਕ ਇੰਪੁੱਟ ਸੂਚੀ"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਸਰੋਤ ਆਉਟਪੁੱਟ ਲਿਸਟ ਅਤੇ ਇੱਕ ਸਰੋਤ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "ਅਢੁੱਕਵੀਂ ਸਿੰਕ ਇੰਪੁੱਟ ਸੂਚੀ"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਕਾਰਡ ਨਾਂ/ਲਿਸਟ ਅਤੇ ਪਰੋਫਾਈਲ ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "ਅਢੁੱਕਵਾਂ ਸੈਂਪਲ ਹਦਾਇਤ"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਕਾਰਡ ਨਾਂ/ਲਿਸਟ ਅਤੇ ਪਰੋਫਾਈਲ ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "ਤੁਹਾਨੂੰ ਇੰਪੁੱਟ ਲਿਸਟ ਅਤੇ ਇੱਕ ਸਿੰਕ ਨੂੰ ਸਿੰਕ ਕਰਨਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "ਅਢੁੱਕਵਾਂ ਸੈਂਪਲ ਹਦਾਇਤ"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਕਾਰਡ ਨਾਂ/ਲਿਸਟ ਅਤੇ ਪਰੋਫਾਈਲ ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "ਅਢੁੱਕਵਾਂ ਸੈਂਪਲ ਹਦਾਇਤ"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਕਾਰਡ ਨਾਂ/ਲਿਸਟ ਅਤੇ ਪਰੋਫਾਈਲ ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "ਕੋਈ ਯੋਗ ਕਮਾਂਡ ਨਹੀਂ ਦਿੱਤੀ।"
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "ਕਮਾਂਡ ਲਾਈਨ ਪਾਰਸ ਕਰਨ ਵਿੱਚ ਫੇਲ੍ਹ।\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "ਸਰਵਰ: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "ਸਰੋਤ: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "ਸਿੰਕ: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "ਕੂਕੀਜ਼: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "ਕੂਕੀ ਡਾਟਾ ਪਾਰਸ ਕਰਨ ਵਿੱਚ ਫੇਲ\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "ਕੂਕੀ ਡਾਟਾ ਸੰਭਾਲਣ ਵਿੱਚ ਫੇਲ\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "ਕਲਾਇਟ ਸੰਰਚਨਾ ਫਾਇਲ ਲੋਡ ਕਰਨ ਵਿੱਚ ਫੇਲ।\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "ਇੰਵਾਇਰਨਮੈਂਟ ਸੰਰਚਨਾ ਡਾਟਾ ਪੜ੍ਹਨ ਵਿੱਚ ਫੇਲ੍ਹ।\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN ਪ੍ਰਾਪਤ ਕਰਨ ਵਿੱਚ ਫੇਲ।\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "ਕੂਕੀ ਡਾਟਾ ਲੋਡ ਕਰਨ ਵਿੱਚ ਫੇਲ\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "ਹਾਲੇ ਬਣਾਇਆ ਨਹੀਂ।\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "ਕੋਈ ਪਲਸ-ਆਡੀਓ ਡੈਮਨ ਨਹੀਂ ਚੱਲ ਰਿਹਾ, ਜਾਂ ਸ਼ੈਸ਼ਨ ਡੈਮਨ ਤੌਰ ਤੇ ਨਹੀਂ ਚੱਲ ਰਿਹਾ।"
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "ਪਲਸਆਡੀਓ ਡੈਮਨ ਬੰਦ ਕਰਨ ਵਿੱਚ ਫੇਲ।"
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "ਡੈਮਨ ਜਵਾਬ ਨਹੀਂ ਦੇ ਰਹੀ।"
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "autospawn ਲਾਕ ਵਰਤ ਨਹੀਂ ਸਕਦਾ।"
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "ਬੰਦ"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "ਹਾਈ ਫਡੈਲਿਟੀ ਪਲੇਅਬੈਕ (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "ਹਾਈ ਫਡੈਲਿਟੀ ਪਲੇਅਬੈਕ (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "ਟੈਲੀਫੋਨੀ ਡੁਪਲੈਕਸ (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "ਪਲਸਆਡੀਓ ਸਾਊਂਡ ਡਰਾਇਵਰ"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "ਆਊਟਪੁੱਟ ਜੰਤਰ"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "ਇੰਪੁੱਟ ਜੰਤਰ"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@ ਉੱਪਰ ਆਡੀਓ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "ਇੰਪੁੱਟ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "ਡੌਕਿੰਗ ਸਟੇਸ਼ਨ ਇੰਪੁੱਟ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "ਡੌਕਿੰਗ ਸਟੇਸ਼ਨ ਮਾਈਕਰੋਫੋਨ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "ਡੌਕਿੰਗ ਸਟੇਸ਼ਨ ਇੰਪੁੱਟ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "ਲਾਈਨ-ਇਨ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "ਮਾਈਕਰੋਫੋਨ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "ਡੌਕਿੰਗ ਸਟੇਸ਼ਨ ਮਾਈਕਰੋਫੋਨ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "ਮਾਈਕਰੋਫੋਨ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "ਬਾਹਰੀ ਮਾਈਕਰੋਫੋਨ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "ਅੰਦਰੂਨੀ ਮਾਈਕਰੋਫੋਨ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "ਰੇਡੀਓ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "ਵੀਡੀਓ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "ਆਟੋਮੈਟਿਕ ਗੇਨ ਕੰਟਰੋਲ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "ਕੋਈ ਆਟੋਮੈਟਿਕ ਗੇਨ ਕੰਟਰੋਲ ਨਹੀਂ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "ਬੂਸਟ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "ਕੋਈ ਬੂਸਟ ਨਹੀਂ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "ਐਂਪਲੀਫਾਇਰ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "ਕੋਈ ਐਂਪਲੀਫਾਇਰ ਨਹੀਂ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "ਬੂਸਟ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "ਕੋਈ ਬੂਸਟ ਨਹੀਂ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "ਐਨਾਲਾਗ ਹੈੱਡਫੋਨ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "ਐਨਾਲਾਗ ਇੰਪੁੱਟ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "ਡੌਕਿੰਗ ਸਟੇਸ਼ਨ ਮਾਈਕਰੋਫੋਨ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "ਐਨਾਲਾਗ ਆਉਟਪੁੱਟ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "ਐਨਾਲਾਗ ਆਊਟਪੁੱਟ (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "ਲਾਈਨ-ਇਨ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "ਐਨਾਲਾਗ ਮੋਨੋ ਆਊਟਪੁੱਟ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "ਐਨਾਲਾਗ ਸਟੀਰੀਓ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "ਡਿਜ਼ੀਟਲ ਸਟੀਰੀਓ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "ਡਿਜ਼ੀਟਲ ਸਟੀਰੀਓ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "ਐਨਾਲਾਗ ਮੋਨੋ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "ਐਨਾਲਾਗ ਸਟੀਰੀਓ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "ਐਨਾਲਾਗ ਸਰਾਊਂਡ 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "ਐਨਾਲਾਗ ਸਰਾਊਂਡ 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "ਐਨਾਲਾਗ ਸਰਾਊਂਡ 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "ਐਨਾਲਾਗ ਸਰਾਊਂਡ 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "ਐਨਾਲਾਗ ਸਰਾਊਂਡ 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "ਐਨਾਲਾਗ ਸਰਾਊਂਡ 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "ਐਨਾਲਾਗ ਸਰਾਊਂਡ 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "ਐਨਾਲਾਗ ਸਰਾਊਂਡ 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "ਐਨਾਲਾਗ ਸਰਾਊਂਡ 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "ਐਨਾਲਾਗ ਸਰਾਊਂਡ 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "ਐਨਾਲਾਗ ਸਰਾਊਂਡ 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "ਡਿਜ਼ੀਟਲ ਸਟੀਰੀਓ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "ਡਿਜ਼ੀਟਲ ਸਟੀਰੀਓ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "ਡਿਜ਼ੀਟਲ ਸਰਾਊਂਡ 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "ਡਿਜ਼ੀਟਲ ਸਰਾਊਂਡ 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "ਡਿਜ਼ੀਟਲ ਸਟੀਰੀਓ (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "ਡਿਜ਼ੀਟਲ ਸਰਾਊਂਡ 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "ਐਨਾਲਾਗ ਮੋਨੋ ਡੁਪਲੈਕਸ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "ਐਨਾਲਾਗ ਸਟੀਰੀਓ ਡੁਪਲੈਕਸ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "ਡਿਜ਼ੀਟਲ ਸਟੀਰੀਓ ਡੁਪਲੈਕਸ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "ਜ਼ੀਰੋ (Null) ਆਉਟਪੁੱਟ"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "ਇੰਪੁੱਟ"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit ਨੂੰ ਇਸ ਪਲੇਟਫਾਰਮ ਤੇ ਸਹਿਯੋਗ ਨਹੀਂ ਹੈ।"
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() ਫੇਲ੍ਹ ਹੈ"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "ਡਿਜ਼ੀਟਲ ਸਰਾਊਂਡ 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "ਘੱਟ ਫਰੀਕਿਊਂਸੀ ਇੱਮਟਰ"
diff --git a/po/pl.po b/po/pl.po
new file mode 100644 (file)
index 0000000..cdc10ce
--- /dev/null
+++ b/po/pl.po
@@ -0,0 +1,3197 @@
+# Polish translation for pulseaudio.
+# Copyright © 2008-2017 the pulseaudio authors.
+# This file is distributed under the same license as the pulseaudio package.
+# Piotr Drąg <piotrdrag@gmail.com>, 2008, 2012-2017.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-07-25 17:04+0200\n"
+"PO-Revision-Date: 2017-07-25 17:05+0200\n"
+"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
+"Language-Team: Polish <trans-pl@lists.fedoraproject.org>\n"
+"Language: pl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [opcje]\n"
+"\n"
+"POLECENIA:\n"
+"  -h, --help                            Wyświetla tę pomoc\n"
+"      --version                         Wyświetla wersję\n"
+"      --dump-conf                       Zrzuca domyślną konfigurację\n"
+"      --dump-modules                    Zrzuca listę dostępnych modułów\n"
+"      --dump-resample-methods           Zrzuca dostępne metody resamplingu\n"
+"      --cleanup-shm                     Czyści stare fragmenty pamięci\n"
+"                                        współdzielonej\n"
+"      --start                           Uruchamia usługę, jeśli nie\n"
+"                                        jest uruchomiona\n"
+"  -k  --kill                            Niszczy uruchomioną usługę\n"
+"      --check                           Sprawdza, czy usługa jest\n"
+"                                        uruchomiona (zwraca tylko\n"
+"                                        kod wyjścia)\n"
+"\n"
+"OPCJE:\n"
+"      --system[=ZMIENNALOGICZNA]        Uruchamia w trybie systemowym\n"
+"  -D, --daemonize[=ZMIENNALOGICZNA]     Tworzy usługę po uruchomieniu\n"
+"      --fail[=ZMIENNALOGICZNA]          Wyłącza, kiedy uruchomienie\n"
+"                                        się nie powiedzie\n"
+"      --high-priority[=ZMIENNALOGICZNA] Próbuje ustawić wysoki poziom nice\n"
+"                                        (dostępne tylko jako root, na SUID\n"
+"                                        lub z podniesionym RLIMIT_NICE)\n"
+"      --realtime[=ZMIENNALOGICZNA]      Próbuje ustawić szeregowanie\n"
+"                                        w czasie rzeczywistym\n"
+"                                        (dostępne tylko jako root, na SUID\n"
+"                                        lub z podniesionym RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=ZMIENNALOGICZNA]  Nie zezwala\n"
+"                                        użytkownikowi modułu na żądanie\n"
+"                                        wczytania/usunięcia modułu\n"
+"                                        po uruchomieniu\n"
+"      --disallow-exit[=ZMIENNALOGICZNA] Nie zezwala użytkownikowi\n"
+"                                        na żądanie wyłączenia\n"
+"      --exit-idle-time=SEKUNDY          Niszczy usługę, kiedy jest zajęta\n"
+"                                        i upłynął podany czas\n"
+"      --scache-idle-time=SEKUNDY        Usuwa automatycznie wczytane\n"
+"                                        próbki, kiedy jest zajęty i upłynął\n"
+"                                        podany czas\n"
+"      --log-level[=POZIOM]              Zwiększa lub ustawia poziom\n"
+"                                        wyświetlanych informacji\n"
+"  -v  --verbose                         Zwiększa poziom wyświetlanych\n"
+"                                        informacji\n"
+"      --log-target={auto,syslog,stderr,file:ŚCIEŻKA,newfile:ŚCIEŻKA}\n"
+"                                        Określa dziennik docelowy\n"
+"      --log-meta[=ZMIENNALOGICZNA]      Dołącza położenie kodu\n"
+"                                        do komunikatów dziennika\n"
+"      --log-time[=ZMIENNALOGICZNA]      Dołącza czas w komunikatach\n"
+"                                        dziennika\n"
+"      --log-backtrace=RAMKI             Dołącza błąd w komunikatach\n"
+"                                        dziennika\n"
+"  -p, --dl-search-path=ŚCIEŻKA          Ustawia ścieżkę wyszukiwania dla\n"
+"                                        dynamicznie współdzielonych\n"
+"                                        obiektów (wtyczek)\n"
+"      --resample-method=METODA          Używa podanej metody resamplingu\n"
+"                                        (zobacz --dump-resample-methods,\n"
+"                                        aby poznać możliwe wartości)\n"
+"      --use-pid-file[=ZMIENNALOGICZNA]  Tworzy plik PID\n"
+"      --no-cpu-limit[=ZMIENNALOGICZNA]  Nie instaluje ograniczenia zasobów\n"
+"                                        procesora na obsługujących\n"
+"                                        je platformach.\n"
+"      --disable-shm[=ZMIENNALOGICZNA]   Wyłącza obsługę pamięci\n"
+"                                        współdzielonej.\n"
+"      --enable-memfd[=ZMIENNALOGICZNA]  Włącza obsługę pamięci\n"
+"                                        współdzielonej memfd.\n"
+"\n"
+"SKRYPT STARTOWY:\n"
+"  -L, --load=\"PARAMETRY MODUŁU\"         Wczytuje podany moduł wtyczki\n"
+"                                        z podanym parametrem\n"
+"  -F, --file=NAZWAPLIKU                 Wykonuje podany skrypt\n"
+"  -C                                    Otwiera wiersz poleceń na\n"
+"                                        uruchomionym TTY po uruchomieniu\n"
+"\n"
+"  -n                                    Nie wczytuje domyślnego\n"
+"                                        pliku skryptu\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "--fail oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level oczekuje parametru poziomu dziennika (numeryczny w zakresie 0..4 "
+"lub jeden z debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "--realtime oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Nieprawidłowy dziennik docelowy: należy użyć „syslog”, „journal”, „stderr”, "
+"„auto” lub prawidłowej nazwy pliku „file:<ścieżka>”, „newfile:<ścieżka>”."
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Nieprawidłowy dziennik docelowy: należy użyć „syslog”, „stderr”, „auto” lub "
+"prawidłowej nazwy pliku „file:<ścieżka>”, „newfile:<ścieżka>”."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "--log-time oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Nieprawidłowa metoda resamplingu „%s”."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "--system oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr "--enable-memfd oczekuje parametru zmiennej logicznej"
+
+#: ../src/daemon/daemon-conf.c:262
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Nieprawidłowy dziennik docelowy „%s”."
+
+#: ../src/daemon/daemon-conf.c:277
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Nieprawidłowy poziom dziennika „%s”."
+
+#: ../src/daemon/daemon-conf.c:292
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Nieprawidłowa metoda resamplingu „%s”."
+
+#: ../src/daemon/daemon-conf.c:314
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Nieprawidłowy rlimit „%s”."
+
+#: ../src/daemon/daemon-conf.c:334
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Nieprawidłowy format próbki „%s”."
+
+#: ../src/daemon/daemon-conf.c:351 ../src/daemon/daemon-conf.c:368
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Nieprawidłowa częstotliwość próbki „%s”."
+
+#: ../src/daemon/daemon-conf.c:391
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Nieprawidłowe kanały próbki „%s”."
+
+#: ../src/daemon/daemon-conf.c:408
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Nieprawidłowa mapa kanałów „%s”."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Nieprawidłowa liczba fragmentów „%s”."
+
+#: ../src/daemon/daemon-conf.c:442
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Nieprawidłowy rozmiar fragmentu „%s”."
+
+#: ../src/daemon/daemon-conf.c:459
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Nieprawidłowy poziom nice „%s”."
+
+#: ../src/daemon/daemon-conf.c:502
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Nieprawidłowy typ serwera „%s”."
+
+#: ../src/daemon/daemon-conf.c:620
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Otwarcie pliku konfiguracji się nie powiodło: %s"
+
+#: ../src/daemon/daemon-conf.c:636
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Podana domyślna mapa kanałów ma inną liczbę kanałów niż podana domyślna "
+"liczba kanałów."
+
+#: ../src/daemon/daemon-conf.c:723
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Odczytano z pliku konfiguracji: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Nazwa: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Brak dostępnych informacji o module\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Wersja: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Opis: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Użycie: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Wczytanie jednorazowe: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "OSTRZEŻENIE O PRZESTARZAŁOŚCI: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Ścieżka: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Otwarcie modułu %s się nie powiodło: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr ""
+"Odnalezienie pierwotnego programu wczytującego lt_dlopen się nie powiodło."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Przydzielenie nowego programu wczytującego dl się nie powiodło."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Dodanie bind-now-loader się nie powiodło."
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Odnalezienie użytkownika „%s” się nie powiodło."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Odnalezienie grupy „%s” się nie powiodło."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID użytkownika „%s” i grupy „%s” się nie zgadzają."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Katalogiem domowym użytkownika „%s” nie jest „%s”, ignorowanie."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Utworzenie „%s” się nie powiodło: %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Zmiana listy grup się nie powiodła: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Zmiana GID się nie powiodła: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Zmiana UID się nie powiodła: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "Tryb systemowy nie jest obsługiwany na tej platformie."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Przetworzenie wiersza poleceń się nie powiodło."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Odmówiono trybu systemowego dla użytkownika niebędącego rootem. Uruchamianie "
+"tylko usługi wyszukiwania serwera D-Bus."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Zniszczenie usługi się nie powiodło: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Ten program nie powinien być uruchamiany jako root (chyba że podano opcję --"
+"system)."
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Wymagane są uprawnienia roota."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "--start nie jest obsługiwane przy uruchamianiu systemowym."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+"Serwer skonfigurowany przez użytkownika w %s, odmawianie uruchomienia/"
+"automatycznego wznowienia."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Serwer skonfigurowany przez użytkownika w %s, który jest lokalny. Dalsze "
+"wykrywanie."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr ""
+"Uruchamianie w trybie systemowym, ale --disallow-exit nie jest ustawione."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr ""
+"Uruchamianie w trybie systemowym, ale --disallow-module-loading nie jest "
+"ustawione."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr "Uruchamianie w trybie systemowym, wymuszanie wyłączenia trybu SHM."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr ""
+"Uruchamianie w trybie systemowym, wymuszanie wyłączenia czasu oczekiwania na "
+"zakończenie."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "Uzyskanie standardowego wejścia/wyjścia się nie powiodło."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() się nie powiodło: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() się nie powiodło: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:562
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() się nie powiodło: %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Uruchomienie usługi się nie powiodło."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() się nie powiodło: %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Uzyskanie identyfikatora komputera się nie powiodło"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually "
+"do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"PulseAudio jest uruchomione w trybie systemowym. Proszę się upewnić, że na "
+"pewno tak ma być.\n"
+"Proszę przeczytać http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ , gdzie wyjaśniono, dlaczego "
+"tryb systemowy jest zwykle złym pomysłem."
+
+#: ../src/daemon/main.c:990
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() się nie powiodło."
+
+#: ../src/daemon/main.c:1022
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() się nie powiodło."
+
+#: ../src/daemon/main.c:1092
+msgid "Failed to initialize daemon."
+msgstr "Zainicjowanie usługi się nie powiodło."
+
+#: ../src/daemon/main.c:1097
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"Uruchamianie usługi bez żadnych wczytanych modułów, odmawianie działania."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "System dźwięku PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Uruchomienie systemu dźwięku PulseAudio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Wejście"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Wejście stacji dokującej"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Mikrofon stacji dokującej"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Wejście liniowe stacji dokującej"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Wejście liniowe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1750
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Przedni mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Tylny mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Zewnętrzny mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Wewnętrzny mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Wideo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Automatyczne sterowanie natężeniem"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Brak automatycznego sterowania natężeniem"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Podbicie"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Brak podbicia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Amplituner"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Brak amplitunera"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Podbicie basów"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Brak podbicia basów"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1757
+msgid "Speaker"
+msgstr "Głośnik"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Słuchawki"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Wejście analogowe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Mikrofon stacji dokującej"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Mikrofon na słuchawkach"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Wyjście analogowe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "Głośnik niskotonowy na oddzielnym wyjściu mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Wyjście liniowe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Analogowe wyjście mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Głośniki"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI/DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Wyjście cyfrowe (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Wejście cyfrowe (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Cyfrowe przekazywanie (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Wejście wielokanałowe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Wyjście wielokanałowe"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Analogowe mono"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Analogowe stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Wielokanałowe"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Analogowe przestrzenne 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Analogowe przestrzenne 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Analogowe przestrzenne 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Analogowe przestrzenne 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Analogowe przestrzenne 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Analogowe przestrzenne 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Analogowe przestrzenne 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Analogowe przestrzenne 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Analogowe przestrzenne 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Analogowe przestrzenne 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Analogowe przestrzenne 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Cyfrowe stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Cyfrowe przekazywanie (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Cyfrowe przestrzenne 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Cyfrowe przestrzenne 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Cyfrowe przestrzenne 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Cyfrowe stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Cyfrowe przestrzenne 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4019 ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Mono Duplex"
+msgstr "Analogowy dupleks mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Analog Stereo Duplex"
+msgstr "Analogowy dupleks stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Cyfrowy dupleks stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+msgid "Multichannel Duplex"
+msgstr "Dupleks wielokanałowy"
+
+#: ../src/modules/alsa/alsa-mixer.c:4156
+msgid "Stereo Duplex"
+msgstr "Dupleks stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4157
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2324
+#: ../src/modules/bluetooth/module-bluez5-device.c:2005
+msgid "Off"
+msgstr "Wyłączone"
+
+#: ../src/modules/alsa/alsa-mixer.c:4256
+#, c-format
+msgid "%s Output"
+msgstr "Wyjście %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:4264
+#, c-format
+msgid "%s Input"
+msgstr "Wejście %s"
+
+#: ../src/modules/alsa/alsa-sink.c:572
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Usługa ALSA została wybudzona, aby zapisać nowe dane do urządzenia, ale nie "
+"było nic do zapisania.\n"
+"Prawdopodobnie jest to błąd w sterowniku ALSA „%s”. Proszę zgłosić ten "
+"problem programistom usługi ALSA.\n"
+"Wybudzono za pomocą ustawienia POLLOUT — ale jednoczesne wywołanie "
+"snd_pcm_avail() zwróciło zero lub inną wartość < min_avail."
+
+#: ../src/modules/alsa/alsa-sink.c:756
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Usługa ALSA została wybudzona, aby zapisać nowe dane do urządzenia, ale nie "
+"było nic do zapisania.\n"
+"Prawdopodobnie jest to błąd w sterowniku ALSA „%s”. Proszę zgłosić ten "
+"problem programistom usługi ALSA.\n"
+"Wybudzono za pomocą ustawienia POLLOUT — ale jednoczesne wywołanie "
+"snd_pcm_avail() zwróciło zero lub inną wartość < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Usługa ALSA została wybudzona, aby odczytać nowe dane z urządzenia, ale nie "
+"było nic do odczytania.\n"
+"Prawdopodobnie jest to błąd w sterowniku ALSA „%s”. Proszę zgłosić ten "
+"problem programistom usługi ALSA.\n"
+"Wybudzono za pomocą ustawienia POLLIN — ale jednoczesne wywołanie "
+"snd_pcm_avail() zwróciło zero lub inną wartość < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"Usługa ALSA została wybudzona, aby odczytać nowe dane z urządzenia, ale nie "
+"było nic do odczytania.\n"
+"Prawdopodobnie jest to błąd w sterowniku ALSA „%s”. Proszę zgłosić ten "
+"problem programistom usługi ALSA.\n"
+"Wybudzono za pomocą ustawienia POLLIN — ale jednoczesne wywołanie "
+"snd_pcm_avail() zwróciło zero lub inną wartość < min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1168 ../src/modules/alsa/alsa-util.c:1243
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() zwróciło wyjątkowo dużą wartość: %lu B (%lu ms).\n"
+"Prawdopodobnie jest to błąd sterownika ALSA „%s”. Proszę zgłosić ten problem "
+"programistom usługi ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1218
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() zwróciło wyjątkowo dużą wartość: %li B (%s%lu ms).\n"
+"Prawdopodobnie jest to błąd sterownika ALSA „%s”. Proszę zgłosić ten problem "
+"programistom usługi ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1259
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() zwróciło dziwne wartości: opóźnienie %lu jest mniejsze "
+"niż korzyść %lu.\n"
+"Prawdopodobnie jest to błąd sterownika ALSA „%s”. Proszę zgłosić ten problem "
+"programistom usługi ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1302
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() zwróciło wyjątkowo dużą wartość: %lu B (%lu ms).\n"
+"Prawdopodobnie jest to błąd sterownika ALSA „%s”. Proszę zgłosić ten problem "
+"programistom usługi ALSA."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1740
+msgid "Headset"
+msgstr "Słuchawki z mikrofonem"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1745
+msgid "Handsfree"
+msgstr "Zestaw głośnomówiący"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1763
+msgid "Headphone"
+msgstr "Słuchawki"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1768
+msgid "Portable"
+msgstr "Przenośne"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1773
+msgid "Car"
+msgstr "Samochód"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1778
+msgid "HiFi"
+msgstr "HiFi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2134
+#: ../src/modules/bluetooth/module-bluez5-device.c:1783
+msgid "Phone"
+msgstr "Telefon"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2142
+#: ../src/modules/bluetooth/module-bluez5-device.c:1735
+#: ../src/modules/bluetooth/module-bluez5-device.c:1751
+#: ../src/modules/bluetooth/module-bluez5-device.c:1789
+msgid "Bluetooth Output"
+msgstr "Wyjście Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2145
+#: ../src/modules/bluetooth/module-bluez5-device.c:1734
+#: ../src/modules/bluetooth/module-bluez5-device.c:1756
+#: ../src/modules/bluetooth/module-bluez5-device.c:1762
+#: ../src/modules/bluetooth/module-bluez5-device.c:1788
+msgid "Bluetooth Input"
+msgstr "Wejście Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2186
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Odtwarzanie o wysokiej dokładności (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2198
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Przechwytywanie o wysokiej dokładności (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2210
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Dupleks telefoniczny (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2223
+msgid "Handsfree Gateway"
+msgstr "Zestaw głośnomówiący"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1830
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Odtwarzanie o wysokiej dokładności (odpływ A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1842
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Przechwytywanie o wysokiej dokładności (źródło A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1854
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Jednostka główna słuchawek z mikrofonem (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1867
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Zestaw słuchawek z mikrofonem (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<nazwa źródła> source_properties=<właściwości źródła> "
+"source_master=<nazwa źródła do filtrowania> sink_name=<nazwa odpływu> "
+"sink_properties=<właściwości odpływu> sink_master=<nazwa odpływu do "
+"filtrowania> adjust_time=<jak często odczytywać częstotliwości w sekundach> "
+"adjust_threshold=<jak daleko odchodzić do odczytania w milisekundach> "
+"format=<format próbki> rate=<częstotliwość próbki> channels=<liczba kanałów> "
+"channel_map=<map kanałów> aec_method=<używana implementacja> "
+"aec_args=<parametry dla mechanizmu AEC> save_aec=<zapisywanie danych AEC w /"
+"tmp> autoloaded=<należy ustawić, jeśli moduł jest automatycznie wczytywany> "
+"use_volume_sharing=<yes lub no> use_master_format=<yes lub no> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "Włączone"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Głuche wyjście"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+"Utrzymywanie zawsze co najmniej jednego wczytanego odpływu, nawet jeśli to "
+"pusty odpływ"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Korektor graficzny ogólnego przeznaczenia"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<nazwa odpływu> sink_properties=<właściwości odpływu> "
+"sink_master=<nazwa odpływu do filtrowania> format=<format próbki> "
+"rate=<częstotliwość próbki> channels=<liczba kanałów> channel_map=<mapa "
+"kanałów> autoloaded=<należy ustawić, jeśli ten moduł jest automatycznie "
+"uruchamiany> use_volume_sharing=<yes lub no> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<automatycznie usuwać nieużywane filtry?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Wirtualny odpływ LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<input channel map> plugin=<ladspa plugin name> label=<ladspa "
+"plugin label> control=<comma separated list of input control values> "
+"input_ladspaport_map=<comma separated list of input LADSPA port names> "
+"output_ladspaport_map=<comma separated list of output LADSPA port names> "
+"autoloaded=<set if this module is being loaded automatically> "
+msgstr ""
+"sink_name=<nazwa odpływu> sink_properties=<właściwości odpływu> "
+"master=<nazwa odpływu do filtrowania> sink_master=<nazwa odpływu do "
+"filtrowania> format=<format próbki> rate=<częstotliwość próbki> "
+"channels=<liczba kanałów> channel_map=<mapa kanałów wejściowych> "
+"plugin=<nazwa wtyczki ladspa> label=<etykieta wtyczki ladspa> control=<lista "
+"wartości sterowania wejściem oddzielona przecinkami> "
+"input_ladspaport_map=<lista nazw wejściowych portów LADSPA oddzielona "
+"przecinkami> output_ladspaport_map=<lista nazw wyjściowych portów LADSPA "
+"oddzielona przecinkami> autoloaded=<należy ustawić, jeśli ten moduł jest "
+"wczytywany automatycznie> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Zegarowy PUSTY odpływ"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Puste wyjście"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Urządzenia wyjściowe"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Urządzenia wejściowe"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Dźwięk na @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Tunel dla %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:521
+#: ../src/modules/module-tunnel-source-new.c:520
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Tunel do %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Wirtualny odpływ wielokanałowy"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<channel map> use_volume_sharing=<yes or no> "
+"force_flat_volume=<yes or no> hrir=/path/to/left_hrir.wav autoloaded=<set if "
+"this module is being loaded automatically> "
+msgstr ""
+"sink_name=<nazwa odpływu> sink_properties=<właściwości odpływu> "
+"master=<nazwa odpływu do filtrowania> sink_master=<nazwa odpływu do "
+"filtrowania> format=<format próbki> rate=<częstotliwość próbki> "
+"channels=<liczba kanałów> channel_map=<mapa kanałów> use_volume_sharing=<yes "
+"lub no> force_flat_volume=<yes lub no> hrir=/ścieżka/do/pliku/left_hrir.wav "
+"autoloaded=<należy ustawić, jeśli ten moduł jest wczytywany automatycznie> "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "Serwer dźwięku PulseAudio"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Przedni środkowy"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Przedni lewy"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Przedni prawy"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Tylny środkowy"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Tylny lewy"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Tylny prawy"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Głośnik niskotonowy"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Przedni lewy pośrodku"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Przedni prawy pośrodku"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Boczny lewy"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Boczny prawy"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "0. pomocniczy"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "1. pomocniczy"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "2. pomocniczy"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "3. pomocniczy"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "4. pomocniczy"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "5. pomocniczy"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "6. pomocniczy"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "7. pomocniczy"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "8. pomocniczy"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "9. pomocniczy"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "10. pomocniczy"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "11. pomocniczy"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "12. pomocniczy"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "13. pomocniczy"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "14. pomocniczy"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "15. pomocniczy"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "16. pomocniczy"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "17. pomocniczy"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "18. pomocniczy"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "19. pomocniczy"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "20. pomocniczy"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "21. pomocniczy"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "22. pomocniczy"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "23. pomocniczy"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "24. pomocniczy"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "25. pomocniczy"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "26. pomocniczy"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "27. pomocniczy"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "28. pomocniczy"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "29. pomocniczy"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "30. pomocniczy"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "31. pomocniczy"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Górny środkowy"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Górny przedni środkowy"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Górny przedni lewy"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Górny przedni prawy"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Górny tylny środkowy"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Górny tylny lewy"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Górny tylny prawy"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:177 ../src/pulse/volume.c:306
+#: ../src/pulse/volume.c:332 ../src/pulse/volume.c:352
+#: ../src/pulse/volume.c:384 ../src/pulse/volume.c:424
+#: ../src/pulse/volume.c:443
+msgid "(invalid)"
+msgstr "(nieprawidłowe)"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Przestrzenne 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Przestrzenne 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Przestrzenne 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Przestrzenne 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Przestrzenne 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() się nie powiodło: %s"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() zwróciło wartość „true”"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Przetworzenie danych ciasteczka się nie powiodło"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Otrzymano komunikat dla nieznanego rozszerzenia „%s”"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "wejście"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "wyjście"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "dwukierunkowe"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "nieprawidłowe"
+
+#: ../src/pulsecore/core-util.c:1856
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"Właścicielem XDG_RUNTIME_DIR (%s) nie jest UID %d, ale UID %d (może to być "
+"spowodowane próbą połączenia do kopii PulseAudio niebędącej rootem jako "
+"użytkownik root przez natywny protokół, czego nie należy robić)."
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "tak"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "nie"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Nie można uzyskać dostępu do blokady automatycznego wznawiania."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Otwarcie pliku docelowego „%s” się nie powiodło."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Próbowano otworzyć plik docelowy „%s”, „%s.1”, „%s.2” … „%s.%d”, ale "
+"wszystkie się nie powiodły."
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Nieprawidłowy dziennik docelowy."
+
+#: ../src/pulsecore/sink.c:3466
+msgid "Built-in Audio"
+msgstr "Wbudowany dźwięk"
+
+#: ../src/pulsecore/sink.c:3471
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Odmowa dostępu"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Nieznane polecenie"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Nieprawidłowy parametr"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "Jednostka istnieje"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Brak jednostki"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Odrzucono połączenie"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Błąd protokołu"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Przekroczono czas oczekiwania"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Brak klucza uwierzytelnienia"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Wewnętrzny błąd"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Zniszczono połączenie"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Zniszczono jednostkę"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Nieprawidłowy serwer"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Zainicjowanie modułu się nie powiodło"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Błędny stan"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Brak danych"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Niezgodna wersja protokołu"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Za duże"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Nieobsługiwane"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Nieznany kod błędu"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Nie ma takiego rozszerzenia"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Przestarzała funkcjonalność"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Brak implementacji"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Rozdzielono klienta"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Błąd wejścia/wyjścia"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Urządzenie lub zasób jest zajęty"
+
+#: ../src/pulse/sample.c:179
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %u k %u Hz"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:197
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:134
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Opróżnienie potoku się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:139
+msgid "Playback stream drained."
+msgstr "Opróżniono potok odtwarzania."
+
+#: ../src/utils/pacat.c:150
+msgid "Draining connection to server."
+msgstr "Opróżnianie połączenia z serwerem."
+
+#: ../src/utils/pacat.c:163
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:194 ../src/utils/pacat.c:543
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:244 ../src/utils/pacat.c:274
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:324
+msgid "Stream successfully created."
+msgstr "Pomyślnie utworzono potok."
+
+#: ../src/utils/pacat.c:327
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:331
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Metryka bufora: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:334
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Metryka bufora: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:338
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Używanie przykładowego określenia „%s”, mapa kanałów „%s”."
+
+#: ../src/utils/pacat.c:342
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Połączono z urządzeniem %s (indeks: %u, wstrzymane: %s)."
+
+#: ../src/utils/pacat.c:352
+#, c-format
+msgid "Stream error: %s"
+msgstr "Błąd potoku: %s"
+
+#: ../src/utils/pacat.c:362
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Wstrzymano urządzenie potoku.%s"
+
+#: ../src/utils/pacat.c:364
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Wznowiono urządzenie potoku.%s"
+
+#: ../src/utils/pacat.c:372
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Niedopełniony potok.%s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Przepełniony potok.%s"
+
+#: ../src/utils/pacat.c:386
+#, c-format
+msgid "Stream started.%s"
+msgstr "Utworzono potok.%s"
+
+#: ../src/utils/pacat.c:393
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Potok został przeniesiony do urządzenia %s (%u, %swstrzymane).%s"
+
+#: ../src/utils/pacat.c:393
+msgid "not "
+msgstr "nie"
+
+#: ../src/utils/pacat.c:400
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Zmieniono atrybuty bufora potoku.%s"
+
+#: ../src/utils/pacat.c:415
+msgid "Cork request stack is empty: corking stream"
+msgstr "Stos żądań korka jest pusty: zatykanie potoku"
+
+#: ../src/utils/pacat.c:421
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Stos żądań korka jest pusty: odtykanie potoku"
+
+#: ../src/utils/pacat.c:425
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr "Ostrzeżenie: otrzymano więcej żądań odetkania niż żądań zatkania."
+
+#: ../src/utils/pacat.c:450
+#, c-format
+msgid "Connection established.%s"
+msgstr "Nawiązano połączenie.%s"
+
+#: ../src/utils/pacat.c:453
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:491
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:497
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Ustawienie potoku monitora się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:501
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:514 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Połączenie się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:557
+msgid "Got EOF."
+msgstr "Otrzymano EOF."
+
+#: ../src/utils/pacat.c:581
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:605
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:626
+msgid "Got signal, exiting."
+msgstr "Otrzymano sygnał, kończenie działania."
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Uzyskanie opóźnienia się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:645
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Czas: %0.3f s, opóźnienie: %0.0f us."
+
+#: ../src/utils/pacat.c:666
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:676
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [opcje]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Wyświetla tę pomoc\n"
+"      --version                         Wyświetla wersję\n"
+"\n"
+"  -r, --record                          Tworzy połączenie do nagrywania\n"
+"  -p, --playback                        Tworzy połączenie do odtwarzania\n"
+"\n"
+"  -v, --verbose                         Wyświetla więcej informacji\n"
+"                                        o działaniu\n"
+"\n"
+"  -s, --server=SERWER                   Nazwa serwera do połączenia się\n"
+"  -d, --device=URZĄDZENIE               Nazwa odpływu/źródła\n"
+"                                        do połączenia się\n"
+"  -n, --client-name=NAZWA               Jak nazywać tego klienta\n"
+"                                        na serwerze\n"
+"      --stream-name=NAZWA               Jak nazwać ten potok na serwerze\n"
+"      --volume=POZIOMGŁOŚNOŚCI          Określa początkowy (liniowy)\n"
+"                                        poziom głośności w zakresie\n"
+"                                        0…65536\n"
+"      --rate=CZĘSTOTLIWOŚĆPRÓBKI        Częstotliwość próbki w Hz\n"
+"                                        (domyślnie 44100)\n"
+"      --format=FORMATPRÓBKI             Typ próbki, jeden z s16le, s16be,\n"
+"                                        u8, float32le, float32be, ulaw,\n"
+"                                        alaw, s32le, s32be, s24le, s24be,\n"
+"                                        s24-32le, s24-32be (domyślnie\n"
+"                                        s16ne)\n"
+"      --channels=KANAŁY                 Liczba kanałów, 1 dla mono, 2 dla\n"
+"                                        stereo\n"
+"                                        (domyślnie 2)\n"
+"      --channel-map=MAPAKANAŁÓW         Mapa kanałów używa zamiast\n"
+"                                        domyślnej\n"
+"      --fix-format                      Pobiera format próbki z odpływu/\n"
+"                                        źródła, z jakim połączony jest\n"
+"                                        potok.\n"
+"      --fix-rate                        Pobiera częstotliwość sampli\n"
+"                                        z odpływu/źródła, z jakim połączony\n"
+"                                        jest potok.\n"
+"      --fix-channels                    Pobiera liczbę kanałów i mapę\n"
+"                                        kanałów z odpływu/źródła, z jakim\n"
+"                                        połączony jest potok.\n"
+"      --no-remix                        Nie miesza kanałów w górę\n"
+"                                        lub w dół.\n"
+"      --no-remap                        Mapuje kanały przez indeks zamiast\n"
+"                                        przez nazwę.\n"
+"      --latency=BAJTY                   Żąda określonego opóźnienia\n"
+"                                        w bajtach.\n"
+"      --process-time=BAJTY              Żąda określonego czasu procesu\n"
+"                                        na żądanie w bajtach.\n"
+"      --latency-msec=MSEKUNDY           Żąda określonego opóźnienia\n"
+"                                        w milisekundach.\n"
+"      --process-time-msec=MSEKUNDY      Żąda określonego czasu procesu\n"
+"                                        na żądanie w milisekundach.\n"
+"      --property=WŁAŚCIWOŚĆ=WARTOŚĆ     Ustawia podaną właściwość na podaną\n"
+"                                        wartość.\n"
+"      --raw                             Nagrywa/odtwarza surowe dane PCM.\n"
+"      --passthrough                     Przekazuje dane.\n"
+"      --file-format=[=FFORMAT]          Nagrywa/odtwarza sformatowane dane\n"
+"                                        PCM.\n"
+"      --list-file-formats               Wyświetla listę dostępnych formatów\n"
+"                                        plików.\n"
+"      --monitor-stream=INDEKS           Nagrywa z odpływu wejścia\n"
+"                                        o INDEKSIE.\n"
+
+#: ../src/utils/pacat.c:793
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr ""
+"Odtwarza zakodowane pliki dźwiękowe za pomocą serwera dźwięku PulseAudio."
+
+#: ../src/utils/pacat.c:797
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr ""
+"Przechwytuje dane dźwiękowe z serwera dźwięku PulseAudio i zapisuje je do "
+"pliku."
+
+#: ../src/utils/pacat.c:801
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+"Przechwytuje dane dźwiękowe z serwera dźwięku PulseAudio i zapisuje je do "
+"STANDARDOWEGO-WYJŚCIA lub podanego pliku."
+
+#: ../src/utils/pacat.c:805
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+"Odtwarza dane dźwiękowe ze STANDARDOWEGO-WEJŚCIA lub podanego pliku za "
+"pomocą serwera dźwięku PulseAudio."
+
+#: ../src/utils/pacat.c:819
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Skompilowane za pomocą libpulse %s\n"
+"Skonsolidowane za pomocą libpulse %s\n"
+
+#: ../src/utils/pacat.c:852 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Nieprawidłowa nazwa klienta „%s”"
+
+#: ../src/utils/pacat.c:867
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Nieprawidłowa nazwa potoku „%s”"
+
+#: ../src/utils/pacat.c:904
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Nieprawidłowa mapa kanałów „%s”"
+
+#: ../src/utils/pacat.c:933 ../src/utils/pacat.c:947
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Nieprawidłowe określenie opóźnienia „%s”"
+
+#: ../src/utils/pacat.c:940 ../src/utils/pacat.c:954
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Nieprawidłowe określenie czasu procesu „%s”"
+
+#: ../src/utils/pacat.c:966
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Nieprawidłowa właściwość „%s”"
+
+#: ../src/utils/pacat.c:985
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Nieznany format pliku %s."
+
+#: ../src/utils/pacat.c:1000
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Przetworzenie parametru dla --monitor-stream się nie powiodło"
+
+#: ../src/utils/pacat.c:1011
+msgid "Invalid sample specification"
+msgstr "Nieprawidłowe określenie próbki"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1026
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1033
+msgid "Too many arguments."
+msgstr "Za dużo parametrów."
+
+#: ../src/utils/pacat.c:1044
+msgid "Failed to generate sample specification for file."
+msgstr "Utworzenie określenia próbki dla pliku się nie powiodło."
+
+#: ../src/utils/pacat.c:1070
+msgid "Failed to open audio file."
+msgstr "Otwarcie pliku dźwiękowego się nie powiodło."
+
+#: ../src/utils/pacat.c:1076
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Ostrzeżenie: podane określenie próbki zostanie zastąpione przez określenie "
+"z pliku."
+
+#: ../src/utils/pacat.c:1079 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Ustalenie określenia próbki z pliku nie się nie powiodło."
+
+#: ../src/utils/pacat.c:1088
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Ostrzeżenie: ustalenie mapy kanałów z pliku się nie powiodło."
+
+#: ../src/utils/pacat.c:1099
+msgid "Channel map doesn't match sample specification"
+msgstr "Mapa kanałów nie zgadza się z określeniem próbki"
+
+#: ../src/utils/pacat.c:1110
+msgid "Warning: failed to write channel map to file."
+msgstr "Ostrzeżenie: zapisanie mapy kanałów do pliku się nie powiodło."
+
+#: ../src/utils/pacat.c:1125
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Otwieranie potoku %s za pomocą określenie próbki „%s” i mapy kanałów „%s”."
+
+#: ../src/utils/pacat.c:1126
+msgid "recording"
+msgstr "nagrywanie"
+
+#: ../src/utils/pacat.c:1126
+msgid "playback"
+msgstr "odtwarzanie"
+
+#: ../src/utils/pacat.c:1150
+msgid "Failed to set media name."
+msgstr "Ustawienie nazwy nośnika się nie powiodło."
+
+#: ../src/utils/pacat.c:1160 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() się nie powiodło."
+
+#: ../src/utils/pacat.c:1183
+msgid "io_new() failed."
+msgstr "io_new() się nie powiodło."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() się nie powiodło."
+
+#: ../src/utils/pacat.c:1198 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() się nie powiodło: %s"
+
+#: ../src/utils/pacat.c:1204
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() się nie powiodło."
+
+#: ../src/utils/pacat.c:1211 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() się nie powiodło."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "NAZWA [PARAMETRY…]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "NAZWA|#N"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "NAZWA"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "NAZWA|#N GŁOŚNOŚĆ"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N GŁOŚNOŚĆ"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "NAZWA|#N 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "NAZWA|#N KLUCZ=WARTOŚĆ"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#N KLUCZ=WARTOŚĆ"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "NAZWA ODPŁYW|#N"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "NAZWA NAZWA-PLIKU"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "NAZWA-ŚCIEŻKI"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "NAZWA-PLIKU ODPŁYW|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#N ODPŁYW|ŹRÓDŁO"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "PROFIL KARTY"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "NAZWA|#N PORT"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "NAZWA-KARTY|KARTA-#N PORT OFFSET"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "CEL"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "POZIOM-NUMERYCZNY"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "RAMKI"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Wyświetla tę pomoc\n"
+"      --version                         Wyświetla wersję\n"
+"Jeśli nie podano polecenia, to program pacmd zostaje uruchomiony w trybie "
+"interaktywnym.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Skompilowane za pomocą libpulse %s\n"
+"Skonsolidowane za pomocą libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Usługa PulseAudio nie jest uruchomiona, lub nie jest uruchomiona jako usługa "
+"sesji."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "gniazdo(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Zniszczenie usługi PulseAudio się nie powiodło."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Usługa nie odpowiada."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Uzyskanie statystyk się nie powiodło: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Obecnie używane: %u bloków zawierających razem %s B.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Przydzielono podczas całego czasu uruchomienia: %u bloków zawierających "
+"razem %s B.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Rozmiar pamięci podręcznej próbek: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Uzyskanie informacji o serwerze się nie powiodło: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Ciąg serwera: %s\n"
+"Wersja protokołu biblioteki: %u\n"
+"Wersja protokołu serwera: %u\n"
+"Czy jest lokalny: %s\n"
+"Indeks klienta: %u\n"
+"Rozmiar kafla: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Nazwa użytkownika: %s\n"
+"Nazwa komputera: %s\n"
+"Nazwa serwera: %s\n"
+"Wersja serwera: %s\n"
+"Domyślne określenie próbki: %s\n"
+"Domyślna mapa kanałów: %s\n"
+"Domyślny odpływ: %s\n"
+"Domyślne źródło: %s\n"
+"Ciasteczko: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Uzyskanie informacji o odpływie się nie powiodło: %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. odpływ\n"
+"\tStan: %s\n"
+"\tNazwa: %s\n"
+"\tOpis: %s\n"
+"\tSterownik: %s\n"
+"\tOkreślenie próbki: %s\n"
+"\tMapa kanałów: %s\n"
+"\tWłaściciel modułu: %u\n"
+"\tWyciszenie: %s\n"
+"\tPoziom głośności: %s\n"
+"\t                  balans %0.2f\n"
+"\tGłośność podstawowa: %s\n"
+"\tŹródło monitora: %s\n"
+"\tOpóźnienie: %0.0f us, skonfigurowano %0.0f us\n"
+"\tFlagi: %s%s%s%s%s%s%s\n"
+"\tWłaściwości:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPorty:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tAktywny port: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tFormaty:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Uzyskanie informacji o źródle się nie powiodło: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. źródło\n"
+"\tStan: %s\n"
+"\tNazwa: %s\n"
+"\tOpis: %s\n"
+"\tSterownik: %s\n"
+"\tOkreślenie próbki: %s\n"
+"\tMapa kanałów: %s\n"
+"\tWłaściciel modułu: %u\n"
+"\tWyciszenie: %s\n"
+"\tPoziom głośności: %s\n"
+"\t                  balans %0.2f\n"
+"\tGłośność podstawowa: %s\n"
+"\tMonitor odpływu: %s\n"
+"\tOpóźnienie: %0.0f us, skonfigurowano %0.0f us\n"
+"\tFlagi: %s%s%s%s%s%s\n"
+"\tWłaściwości:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "nie dotyczy"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Uzyskanie informacji o module się nie powiodło: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. moduł\n"
+"\tNazwa: %s\n"
+"\tParametr: %s\n"
+"\tLicznik użycia: %s\n"
+"\tWłaściwości:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Uzyskanie informacji o kliencie się nie powiodło: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. klient\n"
+"\tSterownik: %s\n"
+"\tWłaściciel modułu: %s\n"
+"\tWłaściwości:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Uzyskanie informacji o karcie się nie powiodło: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. karta\n"
+"\tNazwa: %s\n"
+"\tSterownik: %s\n"
+"\tWłaściciel modułu: %s\n"
+"\tWłaściwości:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfile:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr "\t\t%s: %s (odpływy: %u, źródła: %u, priorytet: %u, dostępne: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tAktywny profil: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tWłaściwości:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tCzęść profilu: %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Uzyskanie informacji o odpływie wejścia się nie powiodło: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. odpływ wejścia\n"
+"\tSterownik: %s\n"
+"\tWłaściciel modułu: %s\n"
+"\tKlient: %s\n"
+"\tOdpływ: %u\n"
+"\tOkreślenie próbki: %s\n"
+"\tMapa kanałów: %s\n"
+"\tFormat: %s\n"
+"\tZakorkowane: %s\n"
+"\tWyciszenie: %s\n"
+"\tPoziom głośności: %s\n"
+"\t                  balans %0.2f\n"
+"\tOpóźnienie bufora: %0.0f us\n"
+"\tOpóźnienie odpływu: %0.0f us\n"
+"\tMetoda resamplingu: %s\n"
+"\tWłaściwości:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Uzyskanie informacji o wyjściu źródła się nie powiodło: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. odpływ wejścia\n"
+"\tSterownik: %s\n"
+"\tWłaściciel modułu: %s\n"
+"\tKlient: %s\n"
+"\tOdpływ: %u\n"
+"\tOkreślenie próbki: %s\n"
+"\tMapa kanałów: %s\n"
+"\tFormat: %s\n"
+"\tZakorkowane: %s\n"
+"\tWyciszenie: %s\n"
+"\tPoziom głośności: %s\n"
+"\t                  balans %0.2f\n"
+"\tOpóźnienie bufora: %0.0f us\n"
+"\tOpóźnienie odpływu: %0.0f us\n"
+"\tMetoda resamplingu: %s\n"
+"\tWłaściwości:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Uzyskanie informacji o próbce się nie powiodło: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"%u. próbka\n"
+"\tNazwa: %s\n"
+"\tOkreślenie próbki: %s\n"
+"\tMapa kanałów: %s\n"
+"\tPoziom głośności: %s\n"
+"\t                  balans %0.2f\n"
+"\tCzas trwania: %0.1f s\n"
+"\tRozmiar: %s\n"
+"\tLazy: %s\n"
+"\tNazwa pliku: %s\n"
+"\tWłaściwości:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Niepowodzenie: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr ""
+"Usunięcie modułu z pamięci się nie powiodło: moduł %s nie jest wczytany"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Uzyskanie głośności się nie powiodło: próbowano ustawić głośność dla %d "
+"kanałów, kiedy obsługiwane kanały = %d\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Ustawienie formatu się nie powiodło: nieprawidłowy ciąg formatu %s"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Wysłanie próbki się nie powiodło: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Przedwczesny koniec pliku"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "nowy"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "zmień"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "usuń"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "nieznany"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "odpływ"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "źródło"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "wejście-odpływu"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "wyjście-źródła"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "moduł"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "klient"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "bufor-próbki"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "serwer"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "karta"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Zdarzenie „%s” w %s #%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "Otrzymano SIGINT, kończenie działania."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Nieprawidłowe określenie głośności"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Głośność jest poza dozwolonym zakresem.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "Nieprawidłowa liczba określeń głośności.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "Niespójne określenie głośności.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[opcje]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[TYP]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "NAZWA-PLIKU [NAZWA]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "NAZWA [ODPŁYW]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "NAZWA|#N GŁOŚNOŚĆ [GŁOŚNOŚĆ…]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#N GŁOŚNOŚĆ [GŁOŚNOŚĆ…]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "NAZWA|#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "#N FORMATY"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Specjalne nazwy @DEFAULT_SINK@, @DEFAULT_SOURCE@ i @DEFAULT_MONITOR@\n"
+"mogą być używane do podania domyślnego odpływu, źródła i monitora.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Wyświetla tę pomoc\n"
+"      --version                         Wyświetla wersję\n"
+"\n"
+"  -s, --server=SERWER                   Nazwa serwera do połączenia się\n"
+"  -n, --client-name=NAZWA               Jak nazwać tego klienta w serwerze\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Skompilowane za pomocą libpulse %s\n"
+"Skonsolidowane za pomocą libpulse %s\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Należy podać nic lub jedno z: %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Proszę podać plik próbki do wczytania"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Otwarcie pliku dźwiękowego się nie powiodło."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Ostrzeżenie: ustalenie określenia próbki z pliku się nie powiodło."
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Należy podać nazwę próbki do odtworzenia"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Należy podać nazwę próbki do usunięcia"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "Należy podać indeks odpływu wejścia i odpływ"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "Należy podać indeks źródła wyjścia i źródło"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Należy podać nazwę modułu i parametry."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "Należy podać indeks lub nazwę modułu"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Nie można podać więcej niż jednego odpływu. Należy podać wartość logiczną."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Nieprawidłowe określenie uśpienia."
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Nie można podać więcej niż jednego źródła. Należy podać wartość logiczną."
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Należy podać nazwę karty/indeks i nazwę profilu"
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Należy podać nazwę odpływu/indeks i nazwę portu"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "Należy podać nazwę odpływu"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "Należy podać nazwę źródła/indeks i nazwę portu"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Należy podać nazwę źródła"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Należy podać nazwę odpływu/indeks i głośność"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Należy podać nazwę źródła/indeks i głośność"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "Należy podać indeks odpływu wejścia i głośność"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "Nieprawidłowy indeks odpływ wejścia"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "Należy podać indeks źródła wyjścia i głośność"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Nieprawidłowy indeks wejścia źródła"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Należy podać nazwę odpływu/indeks i działanie wyciszenia (0, 1 lub „toggle”)"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Nieprawidłowe określenie wyciszenia"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Należy podać nazwę źródła/indeks i działanie wyciszenia (0, 1 lub „toggle”)"
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Należy podać indeks odpływu wejścia i działanie wyciszenia (0, 1 lub "
+"„toggle”)"
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "Nieprawidłowe określenie indeksu odpływu wejścia"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+"Należy podać nazwę indeks wyjścia źródła i działanie wyciszenia (0, 1 lub "
+"„toggle”)"
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Nieprawidłowe określenie indeksu wyjścia źródła"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Należy podać nazwę indeks odpływu listę obsługiwanych formatów oddzielonych "
+"średnikami"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr "Należy podać nazwę karty/indeks, nazwę portu i offset opóźnienia"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Nie można przetworzyć offsetu opóźnienia"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Nie podano prawidłowego polecenia."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Wznowienie się nie powiodło: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Wstrzymanie się nie powiodło: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr ""
+"OSTRZEŻENIE: serwer dźwięku nie jest lokalny, nie zostanie wstrzymany.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Połączenie się nie powiodło: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Otrzymano SIGINT, kończenie działania.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "OSTRZEŻENIE: proces potomny został zniszczony przez sygnał %u\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [opcje] … \n"
+"\n"
+"  -h, --help                            Wyświetla tę pomoc\n"
+"      --version                         Wyświetla wersję\n"
+"  -s, --server=SERWER                   Nazwa serwera do połączenia się\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Skompilowane za pomocą libpulse %s\n"
+"Skonsolidowane za pomocą libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() się nie powiodło.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() się nie powiodło.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() się nie powiodło.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D ekran] [-S serwer] [-O odpływ] [-I źródło] [-c plik]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Wyświetla dane PulseAudio dołączone do ekranu X11 (domyślne)\n"
+" -e    Eksportuje lokalne dane PulseAudio na ekran X11\n"
+" -i    Importuje dane PulseAudio z ekranu X11 do lokalnych zmiennych\n"
+"       środowiskowych i pliku ciasteczka.\n"
+" -r    Usuwa dane PulseAudio z ekranu X11\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Przetworzenie wiersza poleceń się nie powiodło.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Serwer: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Źródło: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Odpływ: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Ciasteczko: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Przetworzenie danych ciasteczka się nie powiodło\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Zapisanie danych ciasteczka się nie powiodło\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Uzyskanie FQDN się nie powiodło.\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Wczytanie danych ciasteczka się nie powiodło\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Niezaimplementowane.\n"
diff --git a/po/pt.po b/po/pt.po
new file mode 100644 (file)
index 0000000..bb542c1
--- /dev/null
+++ b/po/pt.po
@@ -0,0 +1,3224 @@
+#
+# Rui Gouveia <rui.gouveia@globaltek.pt>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:55+0000\n"
+"Last-Translator: Rui Gouveia <rui.gouveia@globaltek.pt>\n"
+"Language-Team: pt <fedora-trans-pt@redhat.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Portuguese\n"
+"X-Poedit-Country: PORTUGAL\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() retornou um valor excepcionalmente elevado: %lu bytes (%lu "
+"ms).\n"
+"Provavelmente isto é um erro no driver ALSA '%s'. Por favor, reporte este "
+"problema aos programadores do ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() retornou um valor excepcionalmente elevado: %li bytes (%s%lu "
+"ms).\n"
+"Provavelmente isto é um erro no driver ALSA '%s'. Por favor, reporte este "
+"problema aos programadores do ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() retornou um valor excepcionalmente elevado: %lu bytes (%lu "
+"ms).\n"
+"Provavelmente isto é um erro no driver ALSA '%s'. Por favor, reporte este "
+"problema aos programadores do ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() retornou um valor excepcionalmente elevado: %lu bytes "
+"(%lu ms).\n"
+"Provavelmente isto é um erro no driver ALSA '%s'. Por favor, reporte este "
+"problema aos programadores do ALSA."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Mantém sempre pelo menos um depósito carregado mesmo que seja um nulo"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Saída Dummy"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "Depósito virtual LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<nome do depósito> sink_properties=<propriedades do depósito> "
+"master=<nome do depósito a filtrar> format=<formato exemplo> "
+"rate=<frequência de amostragem> channels=<número de canais> "
+"channel_map=<mapa de canais> plugin=<nome do plugin ladspa> label=<etiqueta "
+"do plugin ladspa> control=<Lista de valores de controlo de entrada separados "
+"por vírgulas>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr ""
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Saída nula"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "Áudio Interno"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Não foi possível encontrar o carregador \"lt_dlopen\"."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "Não foi possível alocar o novo carregador \"dl\"."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "Não foi possível adicionar \"bind-now-loader\"."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "Foi obtido o sinal %s."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "A sair."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Falha ao procurar o utilizador '%s'."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Falha ao procurar o grupo '%s'."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Foi encontrado utilizador '%s' (UID %lu) e grupo '%s' (GID %lu)."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID do utilizador '%s' e do grupo '%s' não coincidem."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Directório pessoal do utilizador '%s' não é '%s'. A ignorar."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Falha ao criar o '%s': %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Falhou a alteração da lista de grupos: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Não foi possível mudar o GID: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Não foi possível mudar o UID: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "Privilégios de root cedidos com sucesso."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "Modo de sistema não suportado nesta plataforma."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) falhou: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "Não foi possível processar linha de comando."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "Serviço não está a executar"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "Serviço a executar como PID %u"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Tentativa de matar serviço falhou: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Este programa não pretende ser executado como root (a não ser que a opção --"
+"system seja especificada)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "São necessários privilégios de root."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start não é suportado para instâncias do sistema."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "A executar em modo de sistema, mas --disallow-exit não está definido!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"A executar em modo de sistema, mas --disallow-module-loading não está "
+"definido!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "A executar em modo de sistema, a forçar a desactivação do modo SHM!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"A executar em modo de sistema, a forçar a desactivação da saída por "
+"inactividade!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "Não foi possível adquirir o stdio."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe falhou: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() falhou: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() falhou: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "Arranque do serviço falhou."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "Arranque do serviço sucedeu."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() falhou: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Isto é PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Máquina de compilação: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "CFLAGS utilizadas na compilação: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "A executar na máquina: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "Foram encontrados %u CPUs."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "Tamanho da página é %lu bytes"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Compilado com suporte para Valgrind: sim"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Compilado com suporte para Valgrind: não"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "A executar em modo \"valgrind\": %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "A executar na máquina: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Optimizado: sim"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "Compilação optimizada: não"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG definido, todas as declarações desactivadas."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH definido, apenas as declarações \"fast path\" desactivadas."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "Todas as declarações desactivadas."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "A tentativa de ler o ID da máquina falhou"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "O ID da máquina é %s."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "O ID da sessão é %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Execução a utilizar o directório %s"
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "A manter o estado no directório %s."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "A utilizar o directório de módulos %s"
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Execução em modo de sistema: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Ok, então está a correr PA em modo de sistema. Por favor repare que não "
+"deveria estar a fazê-lo.\n"
+"Se, na mesma, o continuar a fazer e as coisas não correrem como esperado, a "
+"culpa será sua.\n"
+"Por favor leia http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ para uma explicação de como o "
+"modo de sistema é usualmente uma má ideia."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() falhou."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "Timer \"frescos\" de alta resolução disponíveis. Bom apetite!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Oh pá, o teu kernel não presta! O prato do dia recomendado é Linux com "
+"timers de alta resolução activos!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() falhou."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "Falha ao inicializar serviço."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Serviço arrancou sem módulos carregados. A recusar trabalhar."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "Arranque do serviço completo."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "Encerramento do serviço iniciado."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "Serviço terminado."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [opções]\n"
+"\n"
+"COMANDOS:\n"
+"  -h, --help                            Mostra esta ajuda\n"
+"      --version                         Mostra versão\n"
+"      --dump-conf                       Despeja configuração por omissão\n"
+"      --dump-modules                    Despeja lista de módulos "
+"disponíveis\n"
+"      --dump-resample-methods           Despeja métodos \"resample\" "
+"disponíveis\n"
+"      --cleanup-shm                     Limpar segmentos de memória "
+"partilhados encravados\n"
+"      --start                           Inicia o serviço, se ainda não "
+"estiver a executar\n"
+"  -k  --kill                            Termina o serviço, se estiver a "
+"executar \n"
+"      --check                           Verifica se o serviço está a "
+"executar (apenas retorna um código de saída)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Executa em modo de sistema\n"
+"  -D, --daemonize[=BOOL]                Passa a serviço depois de executar\n"
+"      --fail[=BOOL]                     Termina quando o arranque falha\n"
+"      --high-priority[=BOOL]            Tenta definir um alto nível de "
+"execução\n"
+"                                        (apenas disponível como root, quando "
+"é SUID ou\n"
+"                                        com níveis elevados de RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Tenta activar escalonamento em tempo "
+"real\n"
+"                                        (apenas disponível como root, quando "
+"é SUID ou\n"
+"                                        com níveis elevados de "
+"RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Desautoriza o carregamento/"
+"descarregamento\n"
+"                                        de módulos, pelo utilizador, depois "
+"do arranque\n"
+"      --disallow-exit[=BOOL]            Desautoriza pedidos de saída do "
+"utilizador\n"
+"      --exit-idle-time=SECS             Termina o serviço quando inactivo e "
+"já passou\n"
+"                                        este tempo\n"
+"      --module-idle-time=SECS           Descarregar módulos de carregamento "
+"automático quando inactivo e\n"
+"                                        passou este tempo\n"
+"      --scache-idle-time=SECS           Descarregar amostras de carregamento "
+"automático quando inactivas e\n"
+"                                        passou este tempo\n"
+"      --log-level[=LEVEL]               Aumenta ou define o nível de "
+"verbosidade\n"
+"  -v                                    Aumenta o nível de verbosidade\n"
+"      --log-target={auto,syslog,stderr} Especifica o ficheiro de registo\n"
+"      --log-meta[=BOOL]                 Inclui código de localização na "
+"mensagem de registo\n"
+"      --log-time[=BOOL]                 Inclui tempo na mensagem de registo\n"
+"      --log-backtrace=FRAMES            Inclui informação de tracagem na "
+"mensagem de registo\n"
+"  -p, --dl-search-path=PATH             Define o caminho de procura para os "
+"plugins partilhados dinâmicos\n"
+"      --resample-method=METHOD          Utilizar o método de \"resampling\" "
+"especificado\n"
+"                                        (Ver --dump-resample-methods para\n"
+"                                        possíveis valores)\n"
+"      --use-pid-file[=BOOL]             Criar um ficheiro com o PID\n"
+"      --no-cpu-limit[=BOOL]             Não instala limitadores de load no "
+"CPU em\n"
+"                                        plataformas que o suportam.\n"
+"      --disable-shm[=BOOL]              Desactivar suporte para memória "
+"partilhada.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Carrega o módulo especificado com\n"
+"                                        o argumento especificado\n"
+"  -F, --file=FILENAME                   Executa o script especificado\n"
+"  -C                                    Abre uma linha de comando no TTY "
+"(consola) em execução\n"
+"                                        depois do arranque\n"
+"\n"
+"  -n                                    Não carrega o script por omissão\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level espera um argumento para o nível de log (numérico no intervalo "
+"0..4 ou um dos seguintes: debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr ""
+"Destino de ficheiro de registo inválido: utilize 'syslog', 'stderr' ou "
+"'auto'."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Método de resample inválido '%s'."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm espera argumento booleano"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Nome: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "Nenhuma informação de módulo disponível\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Versão: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Descrição: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Utilização: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Carregar Uma Vez: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "AVISO DE DESCONTINUIDADE: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Caminho: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] ficheiro registo de destino inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Nível do ficheiro de registo inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Método de reamostragem inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] rlimit inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Formato da amostra inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Taxa de amostragem '%s' inválida."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Canais de amostragem inválidos '%s'."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Mapa de canais inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Número inválido de fragmentos '%s'."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Tamanho do fragmento inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] nível nice inválido '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Taxa de amostragem '%s' inválida."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Falha ao abrir ficheiro de configuração: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"O mapa de canais especificado tem um número de canais diferente do número de "
+"canais definido por omissão."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Ler configuração a partir do ficheiro: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "A limpar privilégios."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Sistema de Som PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Inciar o Sistema de Som PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "Sistema de Som PulseAudio"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "Inciar o Sistema de Som PulseAudio"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Frontal Central"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Frontal Esquerda"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Frontal Direita"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Traseira Central"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Traseira Esquerda"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Traseira Direita"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Central Centro-Esquerda"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Central Centro-Direita"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Lateral Esquerda"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Lateral Direita"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Auxiliar 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Auxiliar 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Auxiliar 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Auxiliar 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Auxiliar 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Auxiliar 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Auxiliar 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Auxiliar 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Auxiliar 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Auxiliar 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Auxiliar 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Auxiliar 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Auxiliar 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Auxiliar 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Auxiliar 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Auxiliar 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Auxiliar 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Auxiliar 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Auxiliar 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Auxiliar 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Auxiliar 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Auxiliar 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Auxiliar 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Auxiliar 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Auxiliar 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Auxiliar 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Auxiliar 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Auxiliar 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Auxiliar 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Auxiliar 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Auxiliar 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Auxiliar 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Topo Centro"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Topo Central Centro"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Topo Frontal Esquerda"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Topo Frontal Direita"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Topo Traseira Centro"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Topo Traseira Esquerda"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Topo Traseira Direita"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(inválido)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "Estéreo"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "Acesso negado"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Comando desconhecido"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Argumento inválido"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "Entidade existe! "
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "Entidade não existe"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "Ligação recusada"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "Erro de protocolo"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "Tempo expirou"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "Sem chave de autorização"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "Erro interno"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "Ligação terminou"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "Entidade terminada"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Servidor Inválido"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Inicialização do módulo falhou"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Mau estado"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Nenhuns dados"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "Versão de protocolo incompatível"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Demasiado Grande"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "Não suportado"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Código de erro desconhecido"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "Extensão não existe"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Funcionalidade obsoleta"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Implementação em falta"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Cliente efectuou um fork"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Erro de entrada/saída"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Dispositivo ou recurso ocupado"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() falhou: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "Não foi possível processar dados da cookie"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "Falha ao abrir ficheiro de configuração '%s': %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "Nenhuma cookie carregada. A tentar ligar sem cookie."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Recebida mensagem para extensão desconhecida '%s'"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Falha ao esvaziar fluxo: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "Fluxo de leitura drenado."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "A drenar ligação ao servidor."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() falhou: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_write() falhou: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() falhou: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "Fluxo criado com sucesso."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() falhou: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Métricas do Buffer: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Métricas do Buffer: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Utilizando especificação da amostra '%s', mapa de canal '%s'."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "Ligado ao dispositivo %s (%u, %ssuspended)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "Erro de fluxo: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Dispositivo de fluxo suspenso.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Dispositivo de fluxo retomado.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Fluxo com falta de dados.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Fluxo com excesso de dados.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "Fluxo iniciado.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Fluxo movido para o dispositivo %s (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "negação"
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Atributos do buffer de fluxo alterados.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "Ligação estabelecida.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() falhou: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() falhou: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() falhou: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Ligação falhou: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "Obtive EOF."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() falhou: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "Recebido sinal, a sair."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Falhou a obtenção da cadência: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Tempo: %0.3f sec; Cadência: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() falhou: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [opções]\n"
+"\n"
+"  -h, --help                            Mostra esta ajuda\n"
+"      --version                         Mostra a versão\n"
+"\n"
+"  -r, --record                          Cria uma ligação para gravar\n"
+"  -p, --playback                        Cria uma ligação para lêr\n"
+"\n"
+"  -v, --verbose                         Activa operações verbose\n"
+"\n"
+"  -s, --server=SERVER                   Nome do servidor a qual ligar\n"
+"  -d, --device=DEVICE                   Nome do depósito/fonte a qual ligar\n"
+"  -n, --client-name=NAME                Como chamar este cliente no "
+"servidor\n"
+"      --stream-name=NAME                Como chamar este fluxo no servidor\n"
+"      --volume=VOLUME                   Especificar o volume (linear) "
+"inicial na gama 0...65536\n"
+"      --rate=SAMPLERATE                 A mesma taxa em Hz (por omissão "
+"44100)\n"
+"      --format=SAMPLEFORMAT             Tipo de amostragem, uma de s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (por omissão "
+"s16ne)\n"
+"      --channels=CHANNELS               Número de canais, 1 para mono, 2 "
+"para estéreo\n"
+"                                        (por omissão 2)\n"
+"      --channel-map=CHANNELMAP          Mapa de canais a usar em vez do mapa "
+"por omissão\n"
+"      --fix-format                      Usa o formato de amostragem do "
+"depósito para\n"
+"                                        onde o fluxo está a ligado.\n"
+"      --fix-rate                        Usa a taxa de amostragem do depósito "
+"para\n"
+"                                        onde o fluxo está ligado.\n"
+"      --fix-channels                    Pega no número de canais e no mapa "
+"de canais\n"
+"                                        do depósito para onde o fluxo está "
+"ligado.\n"
+"      --no-remix                        Não misturar os canais em cima nem "
+"em baixo.\n"
+"      --no-remap                        Mapeia os canais por índice em vez "
+"do nome.\n"
+"      --latency=BYTES                   Pede a cadência especificada em "
+"bytes.\n"
+"      --process-time=BYTES              Pede o tempo de processo por pedido "
+"em bytes.\n"
+"      --property=PROPERTY=VALUE         Coloca a propriedade específica com "
+"o valor especificado.\n"
+"      --raw                             Grava/Lê dados raw em PCM.\n"
+"      --file-format=FFORMAT             Grava/Lê dados formatados em PCM.\n"
+"      --list-file-formats               Lista o formato de ficheiros "
+"disponíveis.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Compilado com libpulse %s\n"
+"Ligado com libpulse %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Nome de cliente inválido '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Nome de fluxo inválido '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Mapa de canais inválido '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Especificação da cadência inválida '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Especificação de tempo de processamento inválido '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Propriedade inválida '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Formato de ficheiro desconhecido %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "Especificação de amostra inválida"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "Demasiados argumentos."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "Falha ao gerar especificação de amostra para o ficheiro."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "Falha ao abrir ficheiro de audio"
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Aviso: a especificação da amostra será sobrescrita com a especificação do "
+"ficheiro."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "Falha ao determinar a especificação da amostra a partir do ficheiro."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Aviso: Falha a determinar o mapa de canal do ficheiro."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "Mapa de canais não corresponde à especificação da amostra"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "Aviso: falha na escrita do mapa de canais no ficheiro."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Abrindo um %s fluxo com especificação da amostra '%s' e mapa de canais '%s'."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "a gravar"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "reprodução"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "Não foi possível processar linha de comando."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() falhou."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() falhou."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() falhou."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() falhou: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() falhou."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() falhou."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Falhou ao suspender: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Falhou ao restaurar: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "Atenção: Servidor de Som não local, suspender ignorado.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Ligação falhou: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Obtido SIGINT, a sair.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "Atenção: Processo filho terminado por sinal %u\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [opções] ... \n"
+"\n"
+"  -h, --help                            Mostra esta ajuda\n"
+"      --version                         Mostra a versão\n"
+"  -s, --server=SERVER                   Nome do servidor ao qual ligar\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Compilado com libpulse %s\n"
+"Ligado com libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() falhou.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() falhou.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() falhou.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Falhou a obtenção de estatísticas: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Correntemente em uso: %u blocos contendo %s bytes no total.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Alocado durante todo o tempo de vida: %u blocos contendo %s bytes no total.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Tamanho cache da amostra: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Falha ao obter informações do servidor: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Nome de utilizador: %s\n"
+"Nome da máquina: %s\n"
+"Nome do servidor: %s\n"
+"Versão do servidor: %s\n"
+"Especificação da amostra por omissão: %s\n"
+"Mapa de canais por omissão: %s\n"
+"Depósito por omissão: %s\n"
+"Fonte por omissão: %s\n"
+"Cookie: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Falha ao obter informações do depósito: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Depósito #%u\n"
+"\tEstado: %s\n"
+"\tNome: %s\n"
+"\tDescrição: %s\n"
+"\tDriver: %s\n"
+"\tEspecificação da Amostra: %s\n"
+"\tMapa de Canais: %s\n"
+"\tMódulo Dono: %u\n"
+"\tMudo: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balanço %0.2f\n"
+"\tVolume Base: %s%s%s\n"
+"\tMonitor de Fonte: %s\n"
+"\tCadência: %0.0f usec, configurado %0.0f usec\n"
+"\tBandeiras: %s%s%s%s%s%s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPorto:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tPorto Activo: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tPorto:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Falha ao obter informações da fonte: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Fonte #%u\n"
+"\tEstado: %s\n"
+"\tNome: %s\n"
+"\tDescrição: %s\n"
+"\tDriver: %s\n"
+"\tEspecificação da Amostra: %s\n"
+"\tMapa de canais: %s\n"
+"\tMódulo Dono: %u\n"
+"\tMudo: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balanço %0.2f\n"
+"\tVolume Base: %s%s%s\n"
+"\tMonitor do Depósito: %s\n"
+"\tCadência: %0.0f usec, configurado %0.0f usec\n"
+"\tBandeiras: %s%s%s%s%s%s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/d"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Falha ao obter informações do módulo: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Módulo #%u\n"
+"\tNome: %s\n"
+"\tArgumento: %s\n"
+"\tContador de utilização: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Falha ao obter informações do cliente: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Cliente #%u\n"
+"\tDriver: %s\n"
+"\tMódulo dono: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Falha ao obter informações da carta: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Placa #%u\n"
+"\tNome: %s\n"
+"\tDriver: %s\n"
+"\tMódulo dono: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tPrefis:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tPerfil Activo: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Falha ao obter informação de entrada do depósito: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Entrada de Depósito #%u\n"
+"\tDriver: %s\n"
+"\tMódulo Dono: %s\n"
+"\tCliente: %s\n"
+"\tDepósito: %u\n"
+"\tEspecificação da amostra: %s\n"
+"\tMapa de canais: %s\n"
+"\tMudo: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balanço %0.2f\n"
+"\tCadência do Buffer: %0.0f usec\n"
+"\tCadência do Depósito: %0.0f usec\n"
+"\tMétodo de reamostragem: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Falha ao obter informações da fonte: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Entrada de Depósito #%u\n"
+"\tDriver: %s\n"
+"\tMódulo Dono: %s\n"
+"\tCliente: %s\n"
+"\tDepósito: %u\n"
+"\tEspecificação da amostra: %s\n"
+"\tMapa de canais: %s\n"
+"\tMudo: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balanço %0.2f\n"
+"\tCadência do Buffer: %0.0f usec\n"
+"\tCadência do Depósito: %0.0f usec\n"
+"\tMétodo de reamostragem: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Falha ao obter informações da amostra: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Amostra #%u\n"
+"\tNome: %s\n"
+"\tEspecificação da Amostra: %s\n"
+"\tMapa de Canais: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balanço %0.2f\n"
+"\tDuração: %0.1fs\n"
+"\tTamanho: %s\n"
+"\tLento: %s\n"
+"\tNome do ficheiro: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "Falha: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Falha ao obter informações da fonte: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Falha ao enviar amostra: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "Fim prematuro do ficheiro"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "Servidor Inválido"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "Obtido SIGINT, a sair."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "Especificação de volume inválida"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [opções] ... \n"
+"\n"
+"  -h, --help                            Mostra esta ajuda\n"
+"      --version                         Mostra a versão\n"
+"  -s, --server=SERVER                   Nome do servidor ao qual ligar\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Compilado com libpulse %s\n"
+"Linkado com libpulse %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "Por favor, especifique um ficheiro de amostra para carregar"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "Falha ao abrir ficheiro de som."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Aviso: Falha ao determinar a especificação da amostra do ficheiro."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "Tem de especificar um nome de amostra para reproduzir"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "Tem de especificar um nome de amostra para remover"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "Tem de especificar um índice de entrada de depósito e um depósito"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "Tem de especificar um índice de saída de fonte e uma fonte"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "Tem de especificar um nome de módulo e argumentos."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "Tem de especificar um índice de módulo"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Não pode especificar mais do que um depósito.  Tem de especificar um valor "
+"booleano."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Não pode especificar mais do que uma fonte.  Tem de especificar um valor "
+"booleano."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Tem de especificar um nome/índice de placa e um nome de perfil"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Tem de especificar um nome/índice de depósito e nome de um porto"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "Tem de especificar um nome/índice de fonte e nome de um porto"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Tem de especificar um nome/índice de depósito e um volume"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "Tem de especificar um nome/índice de fonte e um volume"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "Tem de especificar um índice de entrada de depósito e um volume"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "Índice de depósito de entrada inválido"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "Tem de especificar um índice de saída de fonte e uma fonte"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "Índice de depósito de entrada inválido"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "Tem de especificar um nome/índice de depósito e um booleano mudo"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "Especificação de amostra inválida"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Tem de especificar um nome/índice de fonte e um booleano mudo"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "Tem de especificar um índice de entrada de depósito e um booleano mudo"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "Índice de entrada de depósito inválida"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "Tem de especificar um nome/índice de fonte e um booleano mudo"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "Índice de entrada de depósito inválida"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "Tem de especificar um nome/índice de depósito e um booleano mudo"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "O comando especificado é inválido."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D ecrã] [-S servidor] [-O depósito] [-I fonte] [-c ficheiro]  [-d|-e|-"
+"i|-r]\n"
+"\n"
+" -d    Mostra os dados correntes do PulseAudio anexados ao ecrã X11 "
+"(default)\n"
+" -e    Exporta dados locais do PulseAudio para o ecrã X11\n"
+" -i    Importa dados do PulseAudio do ecrã X11 para variáveis de ambiente "
+"locais e ficheiro de cookies.\n"
+" -r    Remove dados do PulseAudio do ecrã X11\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Não foi possível processar a linha de comando.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "Servidor: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "Fonte: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Sink: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Não foi possível processar os dados da cookie\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Não foi possível gravar os dados da cookie\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "Não foi possível carregar o ficheiro de configuração do cliente\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "Não foi possível ler os dados de configuração do ambiente\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Falhou ao obter FQDN.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Não foi possível carregar os dados da cookie\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Ainda não implementado.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Não existe um daemon PulseAudio a correr, ou não corre como daemon de sessão."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Não foi possível terminar o serviço PulseAudio."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "Serviço não responde."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "Impossível aceder ao lock \"autospawn\"."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA acordou-nos para escrever novos dados para o dispositivo, mas não havia "
+"nada para escrever!\n"
+"Provavelmente isto é um erro no driver ALSA '%s'. Por favor, reporte este "
+"problema aos programadores do ALSA.\n"
+"Fomos acordados pelo conjunto POLLOUT -- contudo uma chamada a seguir de "
+"snd_pcm_avail() retornou 0 ou outro valor < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA acordou-nos para ler novos dados do dispositivo, mas não havia nada "
+"para ler!\n"
+"Provavelmente isto é um erro no driver ALSA '%s'. Por favor, reporte este "
+"problema aos programadores do ALSA.\n"
+"Fomos acordados pelo conjunto POLLIN -- contudo uma chamada a seguir de "
+"snd_pcm_avail() retornou 0 ou outro valor < min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "Desligado"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Reprodução Alta Fidelidade (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Captação de Alta Fidelidade (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telefonia Duplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "Servidor de Som PulseAudio"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr ""
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+#, fuzzy
+msgid "Input Devices"
+msgstr "Entrada %s"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+#, fuzzy
+msgid "Input"
+msgstr "Entrada %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "Áudio Interno"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+msgid "Docking Station Line In"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "Áudio Interno"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "Áudio Interno"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+#, fuzzy
+msgid "Internal Microphone"
+msgstr "Áudio Interno"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+msgid "Bass Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+msgid "No Bass Boost"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+#, fuzzy
+msgid "Headphones"
+msgstr "Mono Analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+#, fuzzy
+msgid "Analog Input"
+msgstr "Mono Analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+#, fuzzy
+msgid "Analog Output"
+msgstr "Saída nula"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+msgid "Line Out"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+#, fuzzy
+msgid "Analog Mono Output"
+msgstr "Mono Analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "Estéreo Analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "Estéreo Digital (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Estéreo Digital (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "Mono Analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "Estéreo Analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+#, fuzzy
+msgid "Analog Surround 2.1"
+msgstr "Analog Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+#, fuzzy
+msgid "Analog Surround 3.0"
+msgstr "Analog Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+#, fuzzy
+msgid "Analog Surround 3.1"
+msgstr "Analog Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "Analog Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "Analog Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "Analog Surround 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "Analog Surround 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+#, fuzzy
+msgid "Analog Surround 6.0"
+msgstr "Analog Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+#, fuzzy
+msgid "Analog Surround 6.1"
+msgstr "Analog Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+#, fuzzy
+msgid "Analog Surround 7.0"
+msgstr "Analog Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "Analog Surround 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "Estéreo Digital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Estéreo Digital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Surround Digital 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Surround Digital 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "Estéreo Digital (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Surround Digital 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+#, fuzzy
+msgid "Analog Mono Duplex"
+msgstr "Mono Analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+#, fuzzy
+msgid "Analog Stereo Duplex"
+msgstr "Estéreo Analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+#, fuzzy
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Estéreo Digital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Saída nula"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "Entrada %s"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<nome do depósito> sink_properties=<propriedades do depósito> "
+"master=<nome do depósito a filtrar> format=<formato exemplo> "
+"rate=<frequência de amostragem> channels=<número de canais> "
+"channel_map=<mapa de canais> plugin=<nome do plugin ladspa> label=<etiqueta "
+"do plugin ladspa> control=<Lista de valores de controlo de entrada separados "
+"por vírgulas>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit não é suportado nesta plataforma."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() falhou"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Saída fonte #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tMódulo dono: %s\n"
+#~ "\tCliente: %s\n"
+#~ "\tFonte: %u\n"
+#~ "\tEspecificação da amostra: %s\n"
+#~ "\tMapa de Canais: %s\n"
+#~ "\tCadência do Buffer: %0.0f usec\n"
+#~ "\tCadência da Fonte: %0.0f usec\n"
+#~ "\tMétodo de reamostragem: %s\n"
+#~ "\tPropriedades:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [opções] stat\n"
+#~ "%s [opções] list\n"
+#~ "%s [opções] exit\n"
+#~ "%s [opções] upload-sample NOME_DO_FICHEIRO [NOME]\n"
+#~ "%s [opções] play-sample NOME [DEPÓSITO]\n"
+#~ "%s [opções] remove-sample NOME\n"
+#~ "%s [opções] move-sink-input ENTRADA_DO_DEPÓSITO DEPÓSITO\n"
+#~ "%s [opções] move-source-output SAÍDA_DA_FONTE FONTE\n"
+#~ "%s [opções] load-module NOME [ARGTOS ...]\n"
+#~ "%s [opções] unload-module MÓDULO\n"
+#~ "%s [opções] suspend-sink DEPÓSITO 1|0\n"
+#~ "%s [opções] suspend-source FONTE 1|0\n"
+#~ "%s [opções] set-card-profile PLACA PERFIL\n"
+#~ "%s [opções] set-sink-port DEPÓSITO PORTO\n"
+#~ "%s [opções] set-source-port FONTE PORTO\n"
+#~ "%s [opções] set-sink-volume DEPÓSITO VOLUME\n"
+#~ "%s [opções] set-source-volume FONTE VOLUME\n"
+#~ "%s [opções] set-sink-input-volume VOLUME_DE_ENTRADA DEPÓSITO\n"
+#~ "%s [opções] set-sink-mute DEPÓSITO 1|0\n"
+#~ "%s [opções] set-source-mute FONTE 1|0\n"
+#~ "%s [opções] set-sink-input-mute ENTRADA_DA_FONTE 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Mostra esta ajuda\n"
+#~ "      --version                         Mostra a versão\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   O nome do servidor ao qual ligar\n"
+#~ "  -n, --client-name=NAME                Como chamar este cliente no "
+#~ "servidor\n"
+
+#, fuzzy
+#~ msgid "%s+%s"
+#~ msgstr "%s %s"
+
+#, fuzzy
+#~ msgid "%s / %s"
+#~ msgstr "%s %s"
+
+#, fuzzy
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Surround Digital 4.0 (IEC958/AC3)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Emissor de Baixa Frequência"
+
+#, fuzzy
+#~ msgid "Invalid client name '%s'\n"
+#~ msgstr "Nome de máquina inválido"
+
+#, fuzzy
+#~ msgid "Failed to determine sample specification from file.\n"
+#~ msgstr "não foi possível obter informações da amostra: %s\n"
+
+#~ msgid "select(): %s"
+#~ msgstr "select(): %s"
+
+#~ msgid "Cannot connect to system bus: %s"
+#~ msgstr "Incapaz de se ligar ao bus de sistema: %s"
+
+#~ msgid "Cannot get caller from PID: %s"
+#~ msgstr "Não foi possível obter chamador a partir do PID: %s"
+
+#~ msgid "Cannot set UID on caller object."
+#~ msgstr "Não foi possível definir o UID no objecto chamador."
+
+#~ msgid "Failed to get CK session."
+#~ msgstr "Falha ao obter sessão CK."
+
+#~ msgid "Cannot set UID on session object."
+#~ msgstr "Não foi possível definir o UID no objecto da sessão."
+
+#~ msgid "Cannot allocate PolKitAction."
+#~ msgstr "Não é possível alocar PolKitAction."
+
+#~ msgid "Cannot set action_id"
+#~ msgstr "impossível definir action_id"
+
+#~ msgid "Cannot allocate PolKitContext."
+#~ msgstr "Não é possível alocar contexto PolKitContext."
+
+#~ msgid "Cannot initialize PolKitContext: %s"
+#~ msgstr "Incapaz de inicializar o PolKitContext: %s"
+
+#~ msgid "Could not determine whether caller is authorized: %s"
+#~ msgstr "Não foi possível determinar se o chamador está autorizado: %s"
+
+#~ msgid "Cannot obtain auth: %s"
+#~ msgstr "Não foi possível obter autenticação: %s"
+
+#~ msgid "PolicyKit responded with '%s'"
+#~ msgstr "PolicyKit respondeu com '%s'"
+
+#~ msgid ""
+#~ "High-priority scheduling (negative Unix nice level) for the PulseAudio "
+#~ "daemon"
+#~ msgstr ""
+#~ "Escalonamento de alta-prioridade (nível 'nice' negativo em Unix) para o "
+#~ "serviço PulseAudio"
+
+#~ msgid "Real-time scheduling for the PulseAudio daemon"
+#~ msgstr "Escalonamento em tempo-real para o serviço PulseAudio"
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring high-priority scheduling."
+#~ msgstr ""
+#~ "Politica do sistema impede o PulseAudio de obter escalonamento de alta-"
+#~ "prioridade."
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring real-time scheduling."
+#~ msgstr ""
+#~ "Politica do sistema impede o PulseAudio de obter escalonamento de tempo-"
+#~ "real."
+
+#~ msgid "read() failed: %s\n"
+#~ msgstr "read() falhou: %s\n"
+
+#, fuzzy
+#~ msgid "pa_context_connect() failed: %s\n"
+#~ msgstr "Impossível ligar ao servidor."
+
+#~ msgid "We're in the group '%s', allowing high-priority scheduling."
+#~ msgstr "Estamos no grupo '%s', permitindo escalonamento de alta-prioridade."
+
+#~ msgid "We're in the group '%s', allowing real-time scheduling."
+#~ msgstr "Estamos no grupo '%s', permitindo escalonamento em tempo real."
+
+#~ msgid "PolicyKit grants us acquire-high-priority privilege."
+#~ msgstr ""
+#~ "O PolicyKit permite-nos o privilégio \"acquire-high-priority\" (adquirir "
+#~ "alta prioridade)."
+
+#~ msgid "PolicyKit refuses acquire-high-priority privilege."
+#~ msgstr ""
+#~ "O PolicyKit recusa-nos o privilégio \"acquire-high-priority\" (adquirir "
+#~ "alta prioridade)."
+
+#~ msgid "PolicyKit grants us acquire-real-time privilege."
+#~ msgstr ""
+#~ "O PolicyKit permite-nos o privilégio \"acquire-real-time\" (adquirir "
+#~ "tempo real)."
+
+#~ msgid "PolicyKit refuses acquire-real-time privilege."
+#~ msgstr ""
+#~ "O PolicyKit recusa-nos o privilégio \"acquire-real-time\" (adquirir tempo "
+#~ "real)."
+
+#~ msgid ""
+#~ "High-priority scheduling enabled in configuration but not allowed by "
+#~ "policy."
+#~ msgstr ""
+#~ "Escalonamento de alta prioridade activo na configuração, mas não "
+#~ "permitido pela politica."
+
+#~ msgid "Successfully increased RLIMIT_RTPRIO"
+#~ msgstr "RLIMIT_RTPRIO aumentado com sucesso"
+
+#~ msgid "RLIMIT_RTPRIO failed: %s"
+#~ msgstr "RLIMIT_RTPRIO falhou: %s"
+
+#~ msgid "Giving up CAP_NICE"
+#~ msgstr "A desistir de CAP_NICE"
+
+#~ msgid ""
+#~ "Real-time scheduling enabled in configuration but not allowed by policy."
+#~ msgstr ""
+#~ "Escalonamento em tempo real activo na configuração, mas não permitido "
+#~ "pela politica."
+
+#~ msgid "Limited capabilities successfully to CAP_SYS_NICE."
+#~ msgstr "Capacidades limitadas com sucesso em CAP_SYS_NICE."
+
+#~ msgid "time_new() failed.\n"
+#~ msgstr "time_new() falhou.\n"
+
+#~ msgid "Stream successfully created\n"
+#~ msgstr "Fluxo criado com sucesso\n"
+
+#~ msgid "Stream errror: %s\n"
+#~ msgstr "Erro de fluxo: %s\n"
+
+#~ msgid "Connection established.\n"
+#~ msgstr "Ligação Estabelecida.\n"
+
+#~ msgid ""
+#~ "paplay %s\n"
+#~ "Compiled with libpulse %s\n"
+#~ "Linked with libpulse %s\n"
+#~ msgstr ""
+#~ "paplay %s\n"
+#~ "Compilado com libpulse %s\n"
+#~ "Linkado com libpulse %s\n"
+
+#~ msgid "Invalid channel map\n"
+#~ msgstr "Mapa de canais inválido\n"
+
+#~ msgid "Failed to open file '%s'\n"
+#~ msgstr "Falha ao abrir o ficheiro '%s'\n"
+
+#~ msgid "Output %s + Input %s"
+#~ msgstr "Saída %s + Entrada %s"
diff --git a/po/pt_BR.po b/po/pt_BR.po
new file mode 100644 (file)
index 0000000..a9fd40b
--- /dev/null
@@ -0,0 +1,3779 @@
+# Brazilian Portuguese translation for pulseaudio
+# Copyright (C) 2017 freedesktop.org
+# This file is distributed under the same license as the pulseaudio package.
+# Fabian Affolter <fab@fedoraproject.org>, 2008.
+# Igor Pires Soares <igor@projetofedora.org>, 2009, 2012.
+# Rafael Fontenelle <rafaelff@gnome.org>, 2013, 2014, 2017.
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
+"product=PulseAudio&keywords=I18N+L10N&component=misc\n"
+"POT-Creation-Date: 2017-03-01 15:47+0000\n"
+"PO-Revision-Date: 2017-04-09 19:55-0200\n"
+"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n"
+"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Virtaal 1.0.0-beta1\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [opções]\n"
+"\n"
+"COMANDOS:\n"
+"  -h, --help                            Mostra esta ajuda\n"
+"      --version                         Mostra a versão\n"
+"      --dump-conf                       Descarrega a configuração padrão\n"
+"      --dump-modules                    Descarrega a lista de módulos\n"
+"                                        disponíveis\n"
+"      --dump-resample-methods           Descarrega os métodos de "
+"reamostragem\n"
+"      --cleanup-shm                     Limpa os segmentos de memória\n"
+"                                        compartilhados\n"
+"      --start                           Inicia o daemon se ele não estiver "
+"em\n"
+"                                        execução\n"
+"  -k  --kill                            Encerra um daemon em execução\n"
+"      --check                           Verifica se há um daemon em "
+"execução\n"
+"                                        (retorna apenas o código de saída)\n"
+"\n"
+"OPÇÕES:\n"
+"      --system[=BOOL]                   Executa como uma instância do "
+"sistema\n"
+"                                        em escala ampla\n"
+"  -D, --daemonize[=BOOL]                Torna-se um daemon após o início\n"
+"      --fail[=BOOL]                     Sai quando a inicialização falhar\n"
+"      --high-priority[=BOOL]            Tenta definir um nível alto de nice\n"
+"                                        (disponível apenas, quando SUID ou\n"
+"                                        com RLIMIT_NICE elevado)\n"
+"      --realtime[=BOOL]                 Tenta habilitar o escalonamento em\n"
+"                                        tempo real (disponível apenas como\n"
+"                                        root quando SUID ou com "
+"RLIMIT_RTPRIO\n"
+"                                        elevado)\n"
+"      --disallow-module-loading[=BOOL]  Não permite carga/descarga de "
+"módulo\n"
+"                                        exigido pelo usuário depois da "
+"partida\n"
+"      --disallow-exit[=BOOL]            Não permite saída exigida pelo "
+"usuário\n"
+"      --exit-idle-time=SEGUNDOS         Termina um daemon quando ocioso e "
+"este\n"
+"                                        tempo foi decorrido\n"
+"      --module-idle-time=SEGUNDOS       Descarrega os módulos "
+"autocarregáveis\n"
+"                                        quando ociosos e este tempo foi\n"
+"                                        decorrido\n"
+"      --scache-idle-time=SEGUNDOS       Descarrega amostras quando ociosas "
+"e\n"
+"                                        este tempo foi decorrido\n"
+"      --log-level[=NÍVEL]               Aumenta ou define grau de "
+"detalhamento\n"
+"  -v  --verbose                         Aumenta o nível de detalhamento\n"
+"      --log-target={auto,syslog,stderr,file:CAMINHO,newfile:CAMINHO}\n"
+"                                        Especifica o destino do log\n"
+"      --log-meta[=BOOL]                 Inclui a localização do código na\n"
+"                                        mensagem de log\n"
+"      --log-time[=BOOL]                 Inclui carimbos de hora nas "
+"mensagens\n"
+"                                        de log\n"
+"      --log-backtrace=QUADROS           Inclui um backtrace na mensagem de "
+"log\n"
+"  -p, --dl-search-path=CAMINHO          Define o caminho de pesquisa para\n"
+"                                        objetos (plug-ins) dinamicamente\n"
+"                                        compartilhados\n"
+"      --resample-method=MÉTODO          Usa o método de reamostragem\n"
+"                                        especificado (Veja\n"
+"                                        --dump-resample-methods para "
+"valores\n"
+"                                        possíveis)\n"
+"      --use-pid-file[=BOOL]             Cria um arquivo PID\n"
+"      --no-cpu-limit[=BOOL]             Não instala um limitador de carga "
+"de\n"
+"                                        CPU em plataformas nas quais haja\n"
+"                                        suporte.\n"
+"      --disable-shm[=BOOL]              Desabilita o suporte à memória\n"
+"                                        compartilhada.\n"
+"      --enable-memfd[=BOOL]             Habilita o suporte à memória\n"
+"                                        compartilhada memfd\n"
+"\n"
+"SCRIPT DE INICIALIZAÇÃO:\n"
+"  -L, --load=\"ARGUMENTOS DO MÓDULO\"     Carrega um plug-in especificado "
+"com\n"
+"                                        o argumento especificado\n"
+"  -F, --file=NOME_DO_ARQUIVO            Executa o script especificado\n"
+"  -C                                    Abre uma linha de comando no TTY em\n"
+"                                        execução depois da inicialização\n"
+"\n"
+"  -n                                    Não carrega o arquivo de script "
+"padrão\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "--fail espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level espera um argumento em nível de log (seja numérico na faixa de "
+"0..4 seja algum entre debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority espera um argumento booleano"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "--realtime espera um argumento booleano"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading espera um argumento booleano"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit espera um argumento booleano"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Alvo de log inválido: use “syslog”, “journal”, “stderr” ou “auto” ou um nome "
+"de um arquivo válido “file:<caminho>”, “newfile:<caminho>”."
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Alvo de log inválido: use “syslog”, “stderr” ou “auto” ou um nome de arquivo "
+"válido “file:<caminho>”, “newfile:<caminho>”."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "--log-time espera um argumento booleano"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta espera um argumento booleano"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Método de reamostragem inválido “%s”."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "--system espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm espera argumento booleano"
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr "--enable-memfd espera um argumento booleano"
+
+#: ../src/daemon/daemon-conf.c:262
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Alvo do log inválido “%s”."
+
+#: ../src/daemon/daemon-conf.c:277
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Nível de log inválido “%s”."
+
+#: ../src/daemon/daemon-conf.c:292
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Método de reamostragem inválido “%s”."
+
+#: ../src/daemon/daemon-conf.c:314
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] rlimit inválido “%s”."
+
+#: ../src/daemon/daemon-conf.c:334
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Formato de amostragem inválido “%s”."
+
+#: ../src/daemon/daemon-conf.c:351 ../src/daemon/daemon-conf.c:368
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Taxa de amostragem inválida “%s”."
+
+#: ../src/daemon/daemon-conf.c:391
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Canais de amostragem inválidos “%s”."
+
+#: ../src/daemon/daemon-conf.c:408
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Mapa de canais inválido “%s”."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Números de fragmentos inválidos “%s”."
+
+#: ../src/daemon/daemon-conf.c:442
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Tamanho de fragmentos inválido “%s”."
+
+#: ../src/daemon/daemon-conf.c:459
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Número de nice inválido “%s”."
+
+#: ../src/daemon/daemon-conf.c:502
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Tipo de servidor inválido “%s”."
+
+#: ../src/daemon/daemon-conf.c:620
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Falha em abrir o arquivo de configuração: %s"
+
+#: ../src/daemon/daemon-conf.c:636
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"O mapa padrão dos canais especificado tem um número diferente de canais do "
+"que o número de canais padrão especificado."
+
+#: ../src/daemon/daemon-conf.c:723
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Lido do arquivo de configuração: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Nome: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Não há informação do módulo disponível\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Versão: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Descrição: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Uso: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Carrega uma vez: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "AVISO DE OBSOLESCÊNCIA: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Caminho: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Falha ao abrir o módulo %s: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Falha ao localizar o carregador original lt_dlopen."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Falha ao alocar o novo carregador dl."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Falha ao adicionar o bind-now-loader."
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Falha ao localizar o usuário “%s”."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Falha ao localizar o grupo “%s”."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "O GID do usuário “%s” e do grupo “%s” não combinam."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "O diretório pessoal do usuário “%s” não é “%s”, ignorando."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Falha ao criar “%s”: %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Falha ao alterar a lista de grupos: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Falha ao alterar o GID: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Falha ao alterar o UID: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "O modo ampliado do sistema não tem suporte nessa plataforma."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Falha ao analisar a linha de comando."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Modo de sistema recusado para usuário não root. Apenas iniciando o serviço D-"
+"Bus de procura de servidores."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Falha ao encerrar o daemon: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Este programa não é para ser executado como root (a não ser que --system "
+"seja especificado)."
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Privilégios de root requeridos."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "--start não tem suporte para instâncias de sistemas."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr "Servidor configurado por usuário em %s, recusando início/autogeração."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Servidor configurado por usuário em %s, que aparece ser local. Sondando mais "
+"fundo."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr "Executando no modo sistema, mas --disallow-exit não foi configurado."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr ""
+"Executando no modo sistema, mas --disallow-module-loading não foi "
+"configurado."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr "Executando no modo sistema, desabilitando forçadamente o modo SHM."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr ""
+"Executando no modo sistema, desabilitando forçadamente o exit idle time."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "Falha em adquirir o stdio."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() falhou: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() falhou: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:562
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() falhou: %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Falha na partida do daemon."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() falhou: %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Falha ao obter o ID da máquina"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually "
+"do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"OK, então você está executando o PA no modo de sistema. Por favor, "
+"certifique-se de que você realmente deseja fazer isso.\n"
+"Por favor, leia http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ para obter um explicação sobre "
+"porque o modo de sistema é uma má ideia."
+
+#: ../src/daemon/main.c:990
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() falhou."
+
+#: ../src/daemon/main.c:1022
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() falhou."
+
+#: ../src/daemon/main.c:1092
+msgid "Failed to initialize daemon."
+msgstr "Falha em iniciar o daemon."
+
+#: ../src/daemon/main.c:1097
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"O Daemon iniciou sem qualquer módulo carregado, recusando-se a trabalhar."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Sistema de som PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Iniciar o sistema de som PulseAudio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Entrada"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Entrada da base de encaixe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Microfone de estação de base de encaixe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Entrada de linha de estação de base de encaixe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Entrada de linha"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2103
+#: ../src/modules/bluetooth/module-bluez5-device.c:1716
+msgid "Microphone"
+msgstr "Microfone"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Microfone frontal"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Microfone posterior"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Microfone externo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Microfone interno"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Rádio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Vídeo"
+
+# https://pt.wikipedia.org/wiki/Controle_autom%C3%A1tico_de_ganho
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Controle automático de ganho"
+
+# https://pt.wikipedia.org/wiki/Controle_autom%C3%A1tico_de_ganho
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Sem controle automático de ganho"
+
+# Este contexto de Boost é "reforço" no áudio, e não "impulso".
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Reforço"
+
+# Este contexto de Boost é "reforço" no áudio, e não "impulso".
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Sem reforço"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Amplificador"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Sem amplificador"
+
+# Este contexto de Boost é "reforço" no áudio, e não "impulso".
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Reforço de graves"
+
+# Este contexto de Boost é "reforço" no áudio, e não "impulso".
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Sem reforço de graves"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2108
+#: ../src/modules/bluetooth/module-bluez5-device.c:1723
+msgid "Speaker"
+msgstr "Auto-falante"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Fones de ouvidos"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Entrada analógica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Microfone de base de encaixe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Microfone de headset"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Saída analógica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "Saída monofônica separada em LFE"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Saída de linha"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Saída analógica monofônica"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Alto-falantes"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Saída digital (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Entrada digital (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Conversor digital (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Entrada multicanal"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Saída multicanal"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Monofônico analógico"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Estéreo analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Multicanal"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Surround analógico 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Surround analógico 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Surround analógico 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Surround analógico 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Surround analógico 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Surround analógico 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Surround analógico 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Surround analógico 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Surround analógico 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Surround analógico 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Surround analógico 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Estéreo digital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Conversor digital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Surround digital 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Surround digital 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Surround digital 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Estéreo digital (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Surround digital 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4151
+msgid "Analog Mono Duplex"
+msgstr "Duplex monofônico analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Stereo Duplex"
+msgstr "Duplex estéreo analógico"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Duplex estéreo digital (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Multichannel Duplex"
+msgstr "Duplex multicanal"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2323
+#: ../src/modules/bluetooth/module-bluez5-device.c:1971
+msgid "Off"
+msgstr "Desligado"
+
+#: ../src/modules/alsa/alsa-mixer.c:4254
+#, c-format
+msgid "%s Output"
+msgstr "Saída de %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:4262
+#, c-format
+msgid "%s Input"
+msgstr "Entrada de %s"
+
+#: ../src/modules/alsa/alsa-sink.c:570
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"O ALSA nos acordou para gravar novos dados no dispositivo, mas não há nada a "
+"ser gravado.\n"
+"É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, "
+"relate esse problema para os desenvolvedores do ALSA.\n"
+"Nós fomos acordados com o conjunto POLLOUT -- entretanto, a snd_pcm_avail() "
+"subsequente retornou 0 ou outro valor < min_avail."
+
+#: ../src/modules/alsa/alsa-sink.c:747
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"O ALSA nos acordou para gravar novos dados no dispositivo, mas não há nada a "
+"ser gravado!\n"
+"É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, "
+"relate esse problema para os desenvolvedores do ALSA.\n"
+"Nós fomos acordados com o conjunto POLLOUT -- entretanto, a snd_pcm_avail() "
+"subsequente retornou 0 ou outro valor < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"O ALSA nos acordou para ler novos dados no dispositivo, mas não há nada a "
+"ser lido.\n"
+"É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, "
+"relate esse problema para os desenvolvedores do ALSA.\n"
+"Nós fomos acordados com o conjunto POLLIN -- entretanto, a snd_pcm_avail() "
+"subsequente retornou 0 ou outro valor < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"O ALSA nos acordou para ler novos dados no dispositivo, mas não há nada a "
+"ser lido!\n"
+"É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, "
+"relate esse problema para os desenvolvedores do ALSA.\n"
+"Nós fomos acordados com o conjunto POLLIN -- entretanto, a snd_pcm_avail() "
+"subsequente retornou 0 ou outro valor < min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1168 ../src/modules/alsa/alsa-util.c:1243
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() retornou um valor que é excepcionalmente grande: %lu bytes "
+"(%lu ms).\n"
+"É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, "
+"relate esse problema aos desenvolvedores do ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1218
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() retornou um valor que é excepcionalmente grande: %li bytes "
+"(%s%lu ms).\n"
+"É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, "
+"relate esse problema aos desenvolvedores do ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1259
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() retornou um valor estranho: o atraso de %lu é menor do que "
+"(%lu ms).\n"
+"É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, "
+"relate esse problema aos desenvolvedores do ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1302
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() retornou um valor que é excepcionalmente grande: %lu "
+"bytes (%lu ms).\n"
+"É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, "
+"relate esse problema aos desenvolvedores do ALSA."
+
+# Fone de ouvido não se encaixa como tradução aqui, pois há ou pode haver microfone junto.
+#: ../src/modules/bluetooth/module-bluez4-device.c:2093
+#: ../src/modules/bluetooth/module-bluez5-device.c:1706
+msgid "Headset"
+msgstr "Headset"
+
+# Desconheço tradução comum para esta palavra.
+#: ../src/modules/bluetooth/module-bluez4-device.c:2098
+#: ../src/modules/bluetooth/module-bluez5-device.c:1711
+msgid "Handsfree"
+msgstr "Handsfree"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2113
+#: ../src/modules/bluetooth/module-bluez5-device.c:1729
+msgid "Headphone"
+msgstr "Fones de ouvido"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2118
+#: ../src/modules/bluetooth/module-bluez5-device.c:1734
+msgid "Portable"
+msgstr "Portátil"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2123
+#: ../src/modules/bluetooth/module-bluez5-device.c:1739
+msgid "Car"
+msgstr "Carro"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2128
+#: ../src/modules/bluetooth/module-bluez5-device.c:1744
+msgid "HiFi"
+msgstr "HiFi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2133
+#: ../src/modules/bluetooth/module-bluez5-device.c:1749
+msgid "Phone"
+msgstr "Telefone"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2141
+#: ../src/modules/bluetooth/module-bluez5-device.c:1701
+#: ../src/modules/bluetooth/module-bluez5-device.c:1717
+#: ../src/modules/bluetooth/module-bluez5-device.c:1755
+msgid "Bluetooth Output"
+msgstr "Saída Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2144
+#: ../src/modules/bluetooth/module-bluez5-device.c:1700
+#: ../src/modules/bluetooth/module-bluez5-device.c:1722
+#: ../src/modules/bluetooth/module-bluez5-device.c:1728
+#: ../src/modules/bluetooth/module-bluez5-device.c:1754
+msgid "Bluetooth Input"
+msgstr "Entrada Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2185
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Reprodução de alta fidelidade (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2197
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Captura de alta fidelidade (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2209
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Duplex telefônico (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2222
+msgid "Handsfree Gateway"
+msgstr "Gateway de handsfree"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1796
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Reprodução de alta fidelidade (Destino A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1808
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Captura de alta fidelidade (Fonte A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1820
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Unidade de headset (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1833
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Gateway de Áudio do Headset (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<nome da fonte> source_properties=<propriedades da fonte> "
+"source_master=<nome de fonte para filtrar> sink_name=<nome do destino> "
+"sink_properties=<propriedades do destino> sink_master=<nome do destino para "
+"filtrara> adjust_time=<com qual frequência deve-se reajustar taxas em "
+"segundos> adjust_threshold=<quanta diferença até reajustar, em "
+"milissegundos> format=<formato da amostragem> rate=<taxa da amostragem> "
+"channels=<número de canais> channel_map=<mapa do canal> "
+"aec_method=<implementar para usar> aec_args=<parâmetros do mecanismo AEC> "
+"save_aec=<salvar dados AEC em /tmp> autoloaded=<define se este módulo está "
+"sendo carregado automaticamente> use_volume_sharing=<yes ou no> "
+"use_master_format=<yes ou no> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "Ligado"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Saída fictícia"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Sempre manter pelo menos um destino carregado mesmo se for nulo"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Equalizador de propósito geral"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<nome do destino> sink_properties=<propriedades do destino> "
+"master=<nome do destino a ser filtrado> format=<formato de amostragem> "
+"rate=<taxa da amostragem> channels=<número de canais> channel_map=<mapa dos "
+"canais> autoloaded=<define se este módulo está sendo carregado "
+"automaticamente> use_volume_sharing=<yes ou no> "
+
+#: ../src/modules/module-filter-apply.c:47
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<descarregar automaticamente filtros não usados?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Destino Virtual LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<nome do destino> sink_properties=<propriedades do destino> "
+"master=<nome do destino a ser filtrado> format=<formato de amostragem> "
+"rate=<taxa da amostragem> channels=<número de canais> channel_map=<mapa dos "
+"canais de entrada> plugin=<nome do plugin ladspa> label=<rótulo do plug-in "
+"ladspa> control=<lista separada por vírgulas dos valores de controle da "
+"entrada> input_ladspaport_map=<lista separada por vírgulas de nomes de porta "
+"de entrada LADSPA> output_ladspaport_map=<lista separada por vírgulas de "
+"nomes de porta de saída LADSPA> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Destino nulo temporizado"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Saída nula"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Dispositivos de saída"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Dispositivos de entrada"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Áudio em @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Túnel para %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:521
+#: ../src/modules/module-tunnel-source-new.c:520
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Túnel para %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Destino surround virtual"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/"
+"left_hrir.wav "
+msgstr ""
+"sink_name=<nome do destino> sink_properties=<propriedades do destino> "
+"master=<nome do destino a ser filtrado> format=<formato de amostragem> "
+"rate=<taxa da amostragem> channels=<número de canais> channel_map=<mapa dos "
+"canais> use_volume_sharing=<yes ou no> force_flat_volume=<yes ou no> hrir=/"
+"caminho/para/hrir_esquerdo.wav "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "Servidor de som PulseAudio"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Frontal central"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Frontal esquerdo"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Frontal direito"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Posterior central"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Posterior esquerdo"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Posterior direito"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Subwoofer"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Frontal esquerdo do centro"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Frontal direito do centro"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Lateral esquerdo"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Lateral direito"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Auxiliar 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Auxiliar 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Auxiliar 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Auxiliar 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Auxiliar 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Auxiliar 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Auxiliar 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Auxiliar 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Auxiliar 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Auxiliar 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Auxiliar 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Auxiliar 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Auxiliar 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Auxiliar13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Auxiliar 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Auxiliar 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Auxiliar 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Auxiliar 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Auxiliar 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Auxiliar 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Auxiliar 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Auxiliar 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Auxiliar 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Auxiliar 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Auxiliar 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Auxiliar 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Auxiliar 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Auxiliar 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Auxiliar 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Auxiliar 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Auxiliar 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Auxiliar 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Central superior"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Central frontal superior"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Frontal superior esquerdo"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Fontal superior direito"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Central superior posterior"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Posterior superior esquerdo"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Posterior superior direito"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:174 ../src/pulse/volume.c:294
+#: ../src/pulse/volume.c:320 ../src/pulse/volume.c:340
+#: ../src/pulse/volume.c:372 ../src/pulse/volume.c:412
+#: ../src/pulse/volume.c:431
+msgid "(invalid)"
+msgstr "(inválido)"
+
+#: ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Estéreo"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() falhou"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() retornou verdadeiro"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Falha ao analisar os dados do cookie"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Foi recebida uma mensagem para uma extensão desconhecida “%s”"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "entrada"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "saída"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "bidirecional"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "inválido"
+
+#: ../src/pulsecore/core-util.c:1856
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"XDG_RUNTIME_DIR (%s) não é propriedade nossa (uid %d), e sim do uid %d! "
+"(Isso poderia acontecer, por exemplo, se você tentar conectar a um "
+"PulseAudio não-root como um usuário root, por meio do protocolo nativo. Não "
+"faça isso.)"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "sim"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "não"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Não foi possível acessar a trava de autogeração."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Falha ao abrir o arquivo alvo “%s”."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Tentado abrir arquivo alvo “%s”, “%s.1”, “%s.2” ... “%s.%d”, mas tudo falhou."
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Alvo do log inválido."
+
+#: ../src/pulsecore/sink.c:3458
+msgid "Built-in Audio"
+msgstr "Áudio interno"
+
+#: ../src/pulsecore/sink.c:3463
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Acesso negado"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Comando desconhecido"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Argumento inválido"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "Entidade existente"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Não existe tal entidade"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Conexão recusada"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Erro de protocolo"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Tempo esgotado"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Nenhuma chave de autenticação"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Erro interno"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Conexão terminada"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Entidade terminada"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Servidor inválido"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "A inicialização do módulo falhou"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Mau estado"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Não há dados"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Versão de protocolo incompatível"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Muito grande"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Não há suporte"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Código de erro desconhecido"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Não existe tal extensão"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Funcionalidade obsoleta"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Implementação faltando"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Cliente bifurcado"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Erro de entrada/saída"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Dispositivo ou recurso ocupado"
+
+#: ../src/pulse/sample.c:176
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:188
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GB"
+
+#: ../src/pulse/sample.c:190
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MB"
+
+#: ../src/pulse/sample.c:192
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KB"
+
+#: ../src/pulse/sample.c:194
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:134
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Falha ao drenar o fluxo: %s"
+
+#: ../src/utils/pacat.c:139
+msgid "Playback stream drained."
+msgstr "Fluxo de reprodução drenado."
+
+#: ../src/utils/pacat.c:150
+msgid "Draining connection to server."
+msgstr "Drenando conexão para o servidor."
+
+#: ../src/utils/pacat.c:163
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:194 ../src/utils/pacat.c:543
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() falhou: %s"
+
+#: ../src/utils/pacat.c:244 ../src/utils/pacat.c:274
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() falhou: %s"
+
+#: ../src/utils/pacat.c:324
+msgid "Stream successfully created."
+msgstr "Fluxo criado com sucesso."
+
+#: ../src/utils/pacat.c:327
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() falhou: %s"
+
+#: ../src/utils/pacat.c:331
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Métricas do buffer: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:334
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Métricas do buffer: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:338
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Usando especificação de amostragem “%s”, mapa de canais “%s”."
+
+#: ../src/utils/pacat.c:342
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Conectado ao dispositivo %s (índice: %u, suspenso: %s)."
+
+#: ../src/utils/pacat.c:352
+#, c-format
+msgid "Stream error: %s"
+msgstr "Erro de fluxo: %s"
+
+#: ../src/utils/pacat.c:362
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Dispositivo de fluxo suspenso.%s"
+
+#: ../src/utils/pacat.c:364
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "O dispositivo de fluxo continuou.%s"
+
+#: ../src/utils/pacat.c:372
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Subestimação do fluxo.%s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Superestimação do fluxo.%s"
+
+#: ../src/utils/pacat.c:386
+#, c-format
+msgid "Stream started.%s"
+msgstr "Fluxo iniciado.%s"
+
+#: ../src/utils/pacat.c:393
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Fluxo movido para o dispositivo %s (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:393
+msgid "not "
+msgstr "não "
+
+#: ../src/utils/pacat.c:400
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Atributos do buffer de fluxo alterados.%s"
+
+# https://en.wikipedia.org/wiki/Cork_encoding
+#: ../src/utils/pacat.c:415
+msgid "Cork request stack is empty: corking stream"
+msgstr "Pilha de requisição cork está vazia: aplicando cork no fluxo"
+
+# https://en.wikipedia.org/wiki/Cork_encoding
+#: ../src/utils/pacat.c:421
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Pilha de requisição cork está vazia: desfazando cork no fluxo"
+
+#: ../src/utils/pacat.c:425
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr ""
+"Aviso: Recebidas mais requisições para desfazer cork do que requisições "
+"aplicá-la."
+
+#: ../src/utils/pacat.c:450
+#, c-format
+msgid "Connection established.%s"
+msgstr "Conexão estabelecida.%s"
+
+#: ../src/utils/pacat.c:453
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() falhou: %s"
+
+#: ../src/utils/pacat.c:491
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() falhou: %s"
+
+#: ../src/utils/pacat.c:497
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Falha ao definir o fluxo de monitoração: %s"
+
+#: ../src/utils/pacat.c:501
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() falhou: %s"
+
+#: ../src/utils/pacat.c:514 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Falha na conexão: %s"
+
+#: ../src/utils/pacat.c:557
+msgid "Got EOF."
+msgstr "Atingiu EOF."
+
+#: ../src/utils/pacat.c:581
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() falhou: %s"
+
+#: ../src/utils/pacat.c:605
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() falhou: %s"
+
+#: ../src/utils/pacat.c:626
+msgid "Got signal, exiting."
+msgstr "Sinal recebido, saindo."
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Falha ao obter a latência: %s"
+
+#: ../src/utils/pacat.c:645
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Tempo: %0.3f seg; Latência: %0.0f useg."
+
+#: ../src/utils/pacat.c:666
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() falhou: %s"
+
+#: ../src/utils/pacat.c:676
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [opções]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Mostra essa ajuda\n"
+"      --version                         Mostra a versão\n"
+"\n"
+"  -r, --record                          Cria uma conexão para gravação\n"
+"  -p, --playback                        Cria uma conexão para reprodução\n"
+"\n"
+"  -v, --verbose                         Habilita operações no modo "
+"detalhado\n"
+"\n"
+"  -s, --server=SERVIDOR                 O nome do servidor a conectar-se\n"
+"  -d, --device=DISPOSITIVO              O nome do destino/fonte a conectar-"
+"se\n"
+"  -n, --client-name=NOME                Como chamar este cliente no "
+"servidor\n"
+"      --stream-name=NOME                Como chamar este fluxo no servidor\n"
+"      --volume=VOLUME                   Especifica a faixa (linear) inicial\n"
+"                                        de volume no intervalo 0...65536\n"
+"      --rate=TAXA_DE_AMOSTRAGEM         Taxa de amostragem, Hz (padrão "
+"44100)\n"
+"      --format=FORMATO_DE_AMOSTRAGEM    Tipo de amostragem, um de s16le,\n"
+"                                        s16be, u8, float32le, float32be,\n"
+"                                        ulaw, alaw, s32le, s32be, s24le, "
+"s24be\n"
+"                                        s24-32le, s24-32be (padrão s16ne)\n"
+"      --channels=CANAIS                 O número de canais, 1 para mono,\n"
+"                                        2 para estéreo (o padrão é 2)\n"
+"      --channel-map=MAPA_DE_CANAIS      Mapeamento de canais a ser usado no\n"
+"                                        lugar do padrão\n"
+"      --fix-format                      Obtém o formato da amostragem do\n"
+"                                        destino/fonte onde o fluxo está\n"
+"                                        sendo conectado.\n"
+"      --fix-rate                        Obtém a taxa de amostragem do "
+"destino/fonte\n"
+"                                        onde o fluxo está sendo conectado.\n"
+"      --fix-channels                    Obtém o número de canais e o mapa "
+"de\n"
+"                                        canais do destino onde o fluxo está\n"
+"                                        sendo conectado.\n"
+"      --no-remix                        Não faz upmix nem downmix dos "
+"canais.\n"
+"      --no-remap                        Mapeia os canais por índice em vez\n"
+"                                        de nome\n"
+"      --latency=BYTES                   Requisita a latência especificada "
+"em\n"
+"                                        bytes.\n"
+"      --process-time=BYTES              Requisita o tempo de processo\n"
+"                                        especificado por requisições em "
+"bytes.\n"
+"      --latency-msec=MSEGUNDOS          Requisita a latência especificada "
+"em\n"
+"                                        milissegundos.\n"
+"      --process-time-msec=MSEGUNDOS     Requisita a o tempo do processo por\n"
+"                                        requisição em milissegundos.\n"
+"      --property=PROPRIEDADE=VALOR      Define a propriedade especificada "
+"para\n"
+"                                        o valor especificado.\n"
+"      --raw                             Grava/reproduz dados PCM não "
+"tratados.\n"
+"      --passthrough                     Dados para conversão.\n"
+"      --file-format[=FORMATO_ARQUIVO]   Grava/reproduz dados PCM "
+"formatados.\n"
+"      --list-file-formats               Lista formatos de arquivo "
+"disponíveis.\n"
+"      --monitor-stream=ÍNDICE           Grava da entrada do destino com "
+"índice.\n"
+
+#: ../src/utils/pacat.c:793
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr ""
+"Reproduz arquivos de áudio codificados em um servidor de som PulseAudio."
+
+#: ../src/utils/pacat.c:797
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr ""
+"Captura dados de áudio de um servidor de som PulseAudio e escreve-os para um "
+"arquivo."
+
+#: ../src/utils/pacat.c:801
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+"Captura dados de áudio de um servidor de som PulseAudio e escreve-os para "
+"STDOUT (saída padrão) ou o arquivo especificado."
+
+#: ../src/utils/pacat.c:805
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+"Reproduz dados de áudio de STDIN (entrada padrão) ou o arquivo especificado "
+"em um servidor de áudio PulseAudio."
+
+#: ../src/utils/pacat.c:819
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Compilado com libpulse %s\n"
+"Vinculado com libpulse %s\n"
+
+#: ../src/utils/pacat.c:852 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Nome do cliente “%s” inválido"
+
+#: ../src/utils/pacat.c:867
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Nome do fluxo “%s” inválido"
+
+#: ../src/utils/pacat.c:904
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Mapa de canais “%s” inválido"
+
+#: ../src/utils/pacat.c:933 ../src/utils/pacat.c:947
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Especificação de latência inválida “%s”"
+
+#: ../src/utils/pacat.c:940 ../src/utils/pacat.c:954
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Especificação do tempo de processo “%s” inválida"
+
+#: ../src/utils/pacat.c:966
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Propriedade “%s” inválida"
+
+#: ../src/utils/pacat.c:985
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Formato de arquivo %s desconhecido."
+
+#: ../src/utils/pacat.c:1000
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Falha ao analisar o argumento de --monitor-stream"
+
+#: ../src/utils/pacat.c:1011
+msgid "Invalid sample specification"
+msgstr "Especificação de amostragem inválida"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1026
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1033
+msgid "Too many arguments."
+msgstr "Argumentos em excesso."
+
+#: ../src/utils/pacat.c:1044
+msgid "Failed to generate sample specification for file."
+msgstr "Falha ao gerar a especificação de amostragem para o arquivo."
+
+#: ../src/utils/pacat.c:1070
+msgid "Failed to open audio file."
+msgstr "Falha ao abrir o arquivo de áudio."
+
+#: ../src/utils/pacat.c:1076
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Aviso: a especificação de amostragem especificada será sobrescrita pela "
+"especificação do arquivo."
+
+#: ../src/utils/pacat.c:1079 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Falha ao determinar a especificação de amostragem a partir do arquivo."
+
+#: ../src/utils/pacat.c:1088
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Aviso: Falha ao determinar o mapa de canais a partir do arquivo."
+
+#: ../src/utils/pacat.c:1099
+msgid "Channel map doesn't match sample specification"
+msgstr "O mapa de canais não combina com a especificação da amostragem"
+
+#: ../src/utils/pacat.c:1110
+msgid "Warning: failed to write channel map to file."
+msgstr "Aviso: falha ao gravar o mapa de canais no arquivo."
+
+#: ../src/utils/pacat.c:1125
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Abrindo um fluxo %s com a especificação de amostragem “%s” e mapa de canais "
+"“%s”."
+
+#: ../src/utils/pacat.c:1126
+msgid "recording"
+msgstr "gravando"
+
+#: ../src/utils/pacat.c:1126
+msgid "playback"
+msgstr "playback"
+
+#: ../src/utils/pacat.c:1150
+msgid "Failed to set media name."
+msgstr "Falha ao definir o nome da mídia."
+
+#: ../src/utils/pacat.c:1160 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() falhou."
+
+#: ../src/utils/pacat.c:1183
+msgid "io_new() failed."
+msgstr "io_new() falhou."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() falhou."
+
+#: ../src/utils/pacat.c:1198 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_new() falhou: %s"
+
+#: ../src/utils/pacat.c:1204
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() falhou."
+
+#: ../src/utils/pacat.c:1211 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() falhou."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "NOME [ARGS ...]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "NOME|#N"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "NOME"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "NOME|#N VOLUME"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N VOLUME"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "NOME|#N 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "NOME|#N CHAVE=VALOR"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#N CHAVE=VALOR"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "NOME DESTINO|#N"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "NOME NOME_DE_ARQUIVO"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "NOME_DE_CAMINHO"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "NOME_DE_ARQUIVO DESTINO|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#N DESTINO|FONTE"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "PLACA PERFIL"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "NOME|#N PORTA"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "NOME-PLACA|PLACA-#N PORTA POSIÇÃO"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "ALVO"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "NÍVEL-NUMÉRICO"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "QUADROS"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Mostra esta ajuda\n"
+"      --version                         Mostra a versão\n"
+"Quando nenhum comando é informado, pacmd inicia em modo interativo.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Compilado com libpulse %s\n"
+"Vinculado com libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Nenhum daemon do PulseAudio em execução ou não está em execução como daemon "
+"de sessão."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Falha ao matar o daemon do PulseAudio."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "O daemon não responde."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Falha ao obter estatísticas: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Em uso no momento: %u blocos contendo %s bytes no total.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Alocado por todo o tempo: %u blocos contendo %s bytes no total.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Tamanho do cache para amostragem: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Falha ao obter informações do servidor: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"String do servidor: %s\n"
+"Versão do protocolo da biblioteca: %u\n"
+"Versão do protocolo do servidor: %u\n"
+"É local: %s\n"
+"Índice do cliente: %u\n"
+"Tamanho de fragmento: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Nome do usuário: %s\n"
+"Nome da máquina: %s\n"
+"Nome do servidor: %s\n"
+"Versão do servidor: %s\n"
+"Especificação padrão de amostragem: %s\n"
+"Mapa de canais padrão: %s\n"
+"Destino padrão: %s\n"
+"Fonte padrão: %s\n"
+"Cookie: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Falha ao obter informações do destino: %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Destino #%u\n"
+"\tEstado: %s\n"
+"\tNome: %s\n"
+"\tDescrição: %s\n"
+"\tDriver: %s\n"
+"\tEspecificação da amostragem: %s\n"
+"\tMapa dos canais: %s\n"
+"\tMódulo proprietário: %u\n"
+"\tMudo: %s\n"
+"\tVolume: %s\n"
+"\t        balanço %0.2f\n"
+"\tVolume base: %s\n"
+"\tFonte de monitoração: %s\n"
+"\tLatência: %0.0f useg, %0.0f useg configurado\n"
+"\tSinalizadores: %s%s%s%s%s%s%s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPortas:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tPorta ativa: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tFormatos:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Falha ao obter informações da fonte: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Fonte #%u\n"
+"\tEstado: %s\n"
+"\tNome: %s\n"
+"\tDescrição: %s\n"
+"\tDriver: %s\n"
+"\tEspecificação da amostragem: %s\n"
+"\tMapa dos canais: %s\n"
+"\tMódulo proprietário: %u\n"
+"\tMudo: %s\n"
+"\tVolume: %s\n"
+"\t        balanço %0.2f\n"
+"\tVolume base: %s\n"
+"\tMonitor do destino: %s\n"
+"\tLatência: %0.0f useg, %0.0f useg configurado\n"
+"\tSinalizadores: %s%s%s%s%s%s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "n/d"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Falha ao obter informações do módulo: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Módulo #%u\n"
+"\tNome: %s\n"
+"\tArgumento: %s\n"
+"\tContador de uso: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Falha ao obter informações do cliente: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Cliente #%u\n"
+"\tDriver: %s\n"
+"\tMódulo proprietário: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Falha ao obter informações da placa: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Placa #%u\n"
+"\tNome: %s\n"
+"\tDriver: %s\n"
+"\tMódulo proprietário: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tPerfis:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr "\t\t%s: %s (destino: %u, fontes: %u, prioridade: %u, disponível: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tPerfil ativo: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tPropriedades:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tParte de perfil/perfis: %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Falha ao obter informações da entrada do destino: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Entrada do destino #%u\n"
+"\tDriver: %s\n"
+"\tMódulo proprietário: %s\n"
+"\tCliente: %s\n"
+"\tDestino: %u\n"
+"\tEspecificação da amostragem: %s\n"
+"\tMapa dos canais: %s\n"
+"\tFormato: %s\n"
+"\tCork: %s\n"
+"\tMudo: %s\n"
+"\tVolume: %s\n"
+"\t        balanço %0.2f\n"
+"\tLatência do buffer: %0.0f useg\n"
+"\tLatência do destino: %0.0f useg\n"
+"\tMétodo de reamostragem: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Falha ao obter informações da saída da fonte: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Saída da fonte #%u\n"
+"\tDriver: %s\n"
+"\tMódulo proprietário: %s\n"
+"\tCliente: %s\n"
+"\tFonte: %u\n"
+"\tEspecificação da amostragem: %s\n"
+"\tMapa dos canais: %s\n"
+"\tFormato: %s\n"
+"\tCork: %s\n"
+"\tMudo: %s\n"
+"\tVolume: %s\n"
+"\t        balanço %0.2f\n"
+"\tLatência do buffer: %0.0f useg\n"
+"\tLatência da fonte: %0.0f useg\n"
+"\tMétodo de reamostragem: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Falha ao obter informações sobre a amostragem: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Amostra #%u\n"
+"\tNome: %s\n"
+"\tEspecificação da amostragem: %s\n"
+"\tMapa dos canais: %s\n"
+"\tVolume: %s\n"
+"\t        balanço %0.2f\n"
+"\tDuração: %0.1fs\n"
+"\tTamanho: %s\n"
+"\tLazy: %s\n"
+"\tNome do arquivo: %s\n"
+"\tPropriedades:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Falha: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Falha ao descarregar o módulo: módulo %s não carregado"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Falha ao definir volume: Você tentou definir volumes para %d canais, havendo "
+"suporte a %d canais\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Falha ao definir formato: string %s de formato inválida"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Falha ao enviar a amostragem: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Fim prematuro do arquivo"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "novo"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "alterar"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "remover"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "desconhecido"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "destino"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "fonte"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "entrada-destino"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "saída-fonte"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "módulo"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "cliente"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "cache-amostragem"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "servidor"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "placa"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Evento “%s” em %s #%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT recebido, saindo."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Especificação de volume inválida"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Volume fora da faixa admissível.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "Número de especificações de volume inválido.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "Especificação de volume inconsistente.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[opções]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[TIPO]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "NOME_DE_ARQUIVO [NOME]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "NOME [DESTINO]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "NOME|#N VOLUME [VOLUME ...]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#N VOLUME [VOLUME ...]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "NOME|#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "#N FORMATOS"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Os nomes especiais @DEFAULT_SINK@, @DEFAULT_SOURCE@ e @DEFAULT_MONITOR@\n"
+"podem ser usados para especificar o destino, a fonte e a monitoração "
+"padrão.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Mostra esta ajuda\n"
+"      --version                         Mostra a versão\n"
+"\n"
+"  -s, --server=SERVIDOR                 Nome do servidor a ser conectado\n"
+"  -n, --client-name=NOME                Como chamar este cliente no "
+"servidor\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Compilado com libpulse %s\n"
+"Vinculado com libpulse %s\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Especifique nada ou uma de: %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Por favor, especifique um arquivo de amostragem a ser carregado"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Falha ao abrir o arquivo de som."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+"Aviso: Falha ao determinar a especificação da amostragem a partir do arquivo."
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Você deve especificar um nome para amostra a ser reproduzida"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Você deve especificar um nome para a amostra a ser removida"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "Você deve especificar a entrada do destino e um destino"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "Você deve especificar um índice de saída da fonte e uma fonte"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Você deve especificar um nome para o módulo e seus argumentos."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "Você deve especificar um nome ou índice do módulo"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Você não pode especificar mais de um destino. Você deve especificar um valor "
+"booleano."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Especificação de suspensão inválida."
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Você não pode especificar mais de uma fonte. Você deve especificar um valor "
+"booleano."
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Você deve especificar um nome/índice para a placa e um nome de perfil"
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Você deve especificar um nome/índice do destino e o nome da porta"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "Você deve especificar um nome de destino"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "Você deve especificar um nome/índice da fonte e o nome da porta"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Você deve especificar um nome de fonte"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Você deve especificar um nome/índice do destino e um volume"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Você deve especificar um nome/índice da fonte e um volume"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "Você deve especificar um índice de entrada para o destino e um volume"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "Índice de entrada de destino inválido"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "Você deve especificar um índice de saída da fonte e um volume"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Índice de saída de fonte inválido"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Você deve especificar um nome/índice do destino e uma ação de mudo (0, 1 ou "
+"“toogle”)"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Especificação de mudo inválida"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Você deve especificar um nome/índice da fonte e uma ação de mudo (0, 1 ou "
+"“toogle”)"
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Você deve especificar um índice de entrada do destino e uma ação de mudo (0, "
+"1 ou “toogle”)"
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "Especificação do índice de entrada de destino inválida"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+"Você deve especificar um índice de saída de fonte e uma ação de mudo (0, 1 "
+"ou “toogle”)"
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Especificação do índice de saída de fonte inválida"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Você deve especificar um índice do destino e uma lista separada por ponto-e-"
+"vírgulas de formatos aceitos"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr ""
+"Você deve especificar nome/índice de uma placa, um nome de porta e uma "
+"mudança de latência"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Não foi possível analisar a mudança da latência"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Nenhum comando válido especificado."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Falha ao prosseguir: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Falha em suspender: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "AVISO: O servidor de som não é local, e não será suspenso.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Falha na conexão: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Recebido o SIGINT, saindo.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "AVISO: O processo filho terminou pelo sinal %u\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [opções] ... \n"
+"\n"
+"  -h, --help                            Mostra esta ajuda\n"
+"      --version                         Mostra a versão\n"
+"  -s, --server=SERVIDOR                 Nome do servidor a ser conectado\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Compilado com libpulse %s\n"
+"Vinculado com libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() falhou.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() falhou.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() falhou.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S servidor] [-O destino] [-I fonte] [-c arq.] [-d|-e|-i|-"
+"r]\n"
+"\n"
+" -d    Mostra dados atuais do PulseAudio associados ao display X11 (padrão)\n"
+" -e    Exporta dados locais do PulseAudio para um display X11\n"
+" -i    Importa dados do PulseAudio de um display X11 para as variáveis de\n"
+"       ambiente locais e para o arquivo de cookie.\n"
+" -r    Remove os dados do PulseAudio do display X11\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Falha em interpretar a linha de comando.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Servidor: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Fonte: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Destino: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Falha ao analisar os dados do cookie\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Falha ao salvar os dados do cookie\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Falha ao obter FQDN.\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Falha ao carregar os dados do cookie\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Não implementado ainda.\n"
+
+#~ msgid "Cleaning up privileges."
+#~ msgstr "Limpando privilégios."
+
+#~ msgid "Got signal %s."
+#~ msgstr "Sinal %s recebido."
+
+#~ msgid "Exiting."
+#~ msgstr "Saindo."
+
+#~ msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+#~ msgstr "Usuário \"%s\" (UID %lu) e grupo \"%s\" (GID %lu) localizados."
+
+#~ msgid "Successfully changed user to \""
+#~ msgstr "Usuário alterado com sucesso para \""
+
+#~ msgid "setrlimit(%s, (%u, %u)) failed: %s"
+#~ msgstr "setrlimit(%s, (%u, %u)) falhou: %s"
+
+#~ msgid "Daemon not running"
+#~ msgstr "O daemon não está em execução"
+
+#~ msgid "Daemon running as PID %u"
+#~ msgstr "Daemon executando como PID %u"
+
+#~ msgid "Daemon startup successful."
+#~ msgstr "Os daemons foram iniciados com sucesso."
+
+#~ msgid "This is PulseAudio %s"
+#~ msgstr "Este é o PulseAudio %s"
+
+#~ msgid "Compilation host: %s"
+#~ msgstr "Máquina da compilação: %s"
+
+#~ msgid "Compilation CFLAGS: %s"
+#~ msgstr "CFLAGS da compilação: %s"
+
+#~ msgid "Running on host: %s"
+#~ msgstr "Executando no host: %s"
+
+#~ msgid "Found %u CPUs."
+#~ msgstr "%u CPUs localizadas."
+
+#~ msgid "Page size is %lu bytes"
+#~ msgstr "O tamanho da página é %lu bytes"
+
+#~ msgid "Compiled with Valgrind support: yes"
+#~ msgstr "Compilado com suporte do Valgrind: sim"
+
+#~ msgid "Compiled with Valgrind support: no"
+#~ msgstr "Compilado com suporte do Valgrind: não"
+
+#~ msgid "Running in valgrind mode: %s"
+#~ msgstr "Executando em modo valgrind: %s"
+
+#~ msgid "Running in VM: %s"
+#~ msgstr "Executando na VM: %s"
+
+#~ msgid "Optimized build: yes"
+#~ msgstr "Build otimizado: sim"
+
+#~ msgid "Optimized build: no"
+#~ msgstr "Build otimizado: não"
+
+#~ msgid "NDEBUG defined, all asserts disabled."
+#~ msgstr "NDEBUG definido, todas as declarações desabilitadas."
+
+#~ msgid "FASTPATH defined, only fast path asserts disabled."
+#~ msgstr ""
+#~ "FASTPATH definido, somente as declarações do \"fast path\" foram "
+#~ "desabilitadas."
+
+#~ msgid "All asserts enabled."
+#~ msgstr "Todas as declarações habilitadas."
+
+#~ msgid "Machine ID is %s."
+#~ msgstr "O ID da máquina é %s."
+
+#~ msgid "Session ID is %s."
+#~ msgstr "O ID da sessão é %s."
+
+#~ msgid "Using runtime directory %s."
+#~ msgstr "Usando o diretório de runtime %s."
+
+#~ msgid "Using state directory %s."
+#~ msgstr "Usando o diretório de estado %s."
+
+#~ msgid "Using modules directory %s."
+#~ msgstr "Usando o diretório de módulos %s."
+
+#~ msgid "Running in system mode: %s"
+#~ msgstr "Executando em modo do sistema: %s"
+
+#~ msgid "Fresh high-resolution timers available! Bon appetit!"
+#~ msgstr "Timers de alta resolução fresquinhos disponíveis! Bon appetit!"
+
+#~ msgid ""
+#~ "Dude, your kernel stinks! The chef's recommendation today is Linux with "
+#~ "high-resolution timers enabled!"
+#~ msgstr ""
+#~ "Cara, seu kernel fede! A recomendação do chef hoje é Linux com timers de "
+#~ "alta resolução habilitados!"
+
+#~ msgid "Daemon startup complete."
+#~ msgstr "A inicialização do daemon foi concluída."
+
+#~ msgid "Daemon shutdown initiated."
+#~ msgstr "O encerramento do daemon foi iniciado."
+
+#~ msgid "Daemon terminated."
+#~ msgstr "Daemon terminado."
+
+#~ msgid "No cookie loaded. Attempting to connect without."
+#~ msgstr "Nenhum cookie foi carregado. Tentativa de conexão sem eles."
+
+#~ msgid ""
+#~ "%s [options]\n"
+#~ "\n"
+#~ "-h, --help                            Show this help\n"
+#~ "-v, --verbose                         Print debug messages\n"
+#~ "      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --from-format=SAMPLEFORMAT      From sample type (defaults to "
+#~ "s16le)\n"
+#~ "      --from-channels=CHANNELS        From number of channels (defaults "
+#~ "to 1)\n"
+#~ "      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+#~ "      --to-channels=CHANNELS          To number of channels (defaults to "
+#~ "1)\n"
+#~ "      --resample-method=METHOD        Resample method (defaults to auto)\n"
+#~ "      --seconds=SECONDS               From stream duration (defaults to "
+#~ "60)\n"
+#~ "\n"
+#~ "If the formats are not specified, the test performs all formats "
+#~ "combinations,\n"
+#~ "back and forth.\n"
+#~ "\n"
+#~ "Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+#~ "alaw,\n"
+#~ "s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+#~ "\n"
+#~ "See --dump-resample-methods for possible values of resample methods.\n"
+#~ msgstr ""
+#~ "%s [opções]\n"
+#~ "\n"
+#~ "-h, --help                            Mostra essa ajuda\n"
+#~ "-v, --verbose                         Mostra mensagens de depuração\n"
+#~ "      --from-rate=TAXA_DE_AMOSTRAGEM  De taxa de amostragem em Hz\n"
+#~ "                                      (padrão 44100)\n"
+#~ "      --from-format=FORMATO_DE_AMOSTRAGEM\n"
+#~ "                                      De tipo de amostragem (padrão "
+#~ "s16le)\n"
+#~ "      --from-channels=CANAIS          De Número de canais (padrão 1)\n"
+#~ "      --to-rate=TAXA_DE_AMOSTRAGEM      Para taxa de amostragem em Hz\n"
+#~ "                                      (padrão 44100)\n"
+#~ "      --to-format=FORMATO_DE_AMOSTRAGEM\n"
+#~ "                                      Para tipo de amostragem (padrão "
+#~ "s16le)\n"
+#~ "      --to-channels=CANAIS            Para número de canais (padrão 1)\n"
+#~ "      --resample-method=MÉTODO        Método de reamostragem (padrão "
+#~ "auto)\n"
+#~ "      --seconds=SEGUNDOS              De duração de fluxo (padrão 60)\n"
+#~ "\n"
+#~ "Se os formatos não forem especificados, o teste realiza todas as "
+#~ "combinações\n"
+#~ "de formatos, para trás e para frente.\n"
+#~ "\n"
+#~ "O tipo de amostragem deve ser um entre s16le, s16be, u8, float32le,\n"
+#~ "float32be, ulaw, alaw, s24le, s24be, s24-32le, s24-32be, s32le e s32be\n"
+#~ "(padrão s16ne)\n"
+#~ "\n"
+#~ "Veja --dump-resample-methods para valores possíveis de métodos de "
+#~ "amostragem.\n"
+
+#~ msgid "%s %s\n"
+#~ msgstr "%s %s\n"
+
+#~ msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+#~ msgstr "=== %d segundos: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+
+#~ msgid "PulseAudio Sound System KDE Routing Policy"
+#~ msgstr "Política de roteamento do KDE para Sistema de som PulseAudio"
+
+#~ msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+#~ msgstr ""
+#~ "Iniciar o sistema de som PulseAudio com política de roteamento do KDE"
+
+#~ msgid "Failed to open configuration file '%s': %s"
+#~ msgstr "Falha em abrir o arquivo de configuração \"%s\": %s"
+
+#~ msgid "Failed to load client configuration file.\n"
+#~ msgstr "Falha ao carregar o arquivo de configuração do cliente.\n"
+
+#~ msgid "Failed to read environment configuration data.\n"
+#~ msgstr "Falha ao ler os dados de configuração do ambiente.\n"
+
+#~ msgid "Successfully dropped root privileges."
+#~ msgstr "Os privilégios do root foram retirados com sucesso."
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit não tem suporte nessa plataforma."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() falhou"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Saída da fonte #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tMódulo proprietário: %s\n"
+#~ "\tCliente: %s\n"
+#~ "\tFonte: %u\n"
+#~ "\tEspecificação da amostragem: %s\n"
+#~ "\tMapa dos canais: %s\n"
+#~ "\tLatência do buffer: %0.0f usec\n"
+#~ "\tLatência da fonte: %0.0f usec\n"
+#~ "\tMétodo de reamostragem: %s\n"
+#~ "\tPropriedades:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [opções] stat\n"
+#~ "%s [opções] list\n"
+#~ "%s [opções] exit\n"
+#~ "%s [opções] upload-sample NOME_DO_ARQUIVO [NOME]\n"
+#~ "%s [opções] play-sample NOME [DESTINO]\n"
+#~ "%s [opções] remove-sample NOME\n"
+#~ "%s [opções] move-sink-input ENTRADA DESTINO\n"
+#~ "%s [opções] move-source-output SAÍDA FONTE\n"
+#~ "%s [opções] load-module NOME [ARGS ...]\n"
+#~ "%s [opções] unload-module MÓDULO\n"
+#~ "%s [opções] suspend-sink [DESTINO] 1|0\n"
+#~ "%s [opções] suspend-source [FONTE] 1|0\n"
+#~ "%s [opções] set-card-profile [PLACA] [PERFIL]\n"
+#~ "%s [opções] set-sink-port [DESTINO] [PORTA]\n"
+#~ "%s [opções] set-source-port [FONTE] [PORTA]\n"
+#~ "%s [opções] set-sink-volume DESTINO VOLUME\n"
+#~ "%s [opções] set-source-volume FONTE VOLUME\n"
+#~ "%s [opções] set-sink-input-volume ENTRADA VOLUME\n"
+#~ "%s [opções] set-sink-mute DESTINO 1|0\n"
+#~ "%s [opções] set-source-mute FONTE 1|0\n"
+#~ "%s [opções] set-sink-input-mute ENTRADA 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Mostra essa ajuda\n"
+#~ "      --version                        Mostra a versão\n"
+#~ "\n"
+#~ "  -s, --server=SERVIDOR                   O nome do servidor a ser "
+#~ "conectado\n"
+#~ "  -n, --client-name=NOME                Como chamar este cliente no "
+#~ "servidor \n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Surround 4.0 digital (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Emissor de baixa freqüência"
+
+#~ msgid "Invalid client name '%s'\n"
+#~ msgstr "Nome do cliente \"%s\" inválido\n"
+
+#~ msgid "Failed to determine sample specification from file.\n"
+#~ msgstr ""
+#~ "Falha ao determinar a especificação de amostragem a partir do arquivo.\n"
+
+#~ msgid "select(): %s"
+#~ msgstr "select(): %s"
+
+#~ msgid "Cannot connect to system bus: %s"
+#~ msgstr "Não foi possível conectar com o barramento do sistema: %s"
+
+#~ msgid "Cannot get caller from PID: %s"
+#~ msgstr "Não foi possível obter quem chamou pelo PID: %s"
+
+#~ msgid "Cannot set UID on caller object."
+#~ msgstr "Não foi possível definir o UID sobre o objeto que chamou."
+
+#~ msgid "Failed to get CK session."
+#~ msgstr "Falha em obter a sessão CK."
+
+#~ msgid "Cannot set UID on session object."
+#~ msgstr "Não foi possível definir o UID do objeto da sessão."
+
+#~ msgid "Cannot allocate PolKitAction."
+#~ msgstr "Não foi possível alocar o PolKitAction."
+
+#~ msgid "Cannot set action_id"
+#~ msgstr "Não foi possível definir a action_id"
+
+#~ msgid "Cannot allocate PolKitContext."
+#~ msgstr "Não foi possível alocar o PolKitContext."
+
+#~ msgid "Cannot initialize PolKitContext: %s"
+#~ msgstr "Não foi possível iniciar o PolKitContext: %s"
+
+#~ msgid "Could not determine whether caller is authorized: %s"
+#~ msgstr "Não foi possível determinar se o solicitante está autorizado: %s"
+
+#~ msgid "Cannot obtain auth: %s"
+#~ msgstr "Não foi possível obter auth: %s"
+
+#~ msgid "PolicyKit responded with '%s'"
+#~ msgstr "PolicyKit respondeu com '%s'"
+
+#~ msgid ""
+#~ "High-priority scheduling (negative Unix nice level) for the PulseAudio "
+#~ "daemon"
+#~ msgstr ""
+#~ "Escalonamento de alta prioridade (nível de nice Unix negativo) para o "
+#~ "daemon do PulseAudio"
+
+#~ msgid "Real-time scheduling for the PulseAudio daemon"
+#~ msgstr "Escalonamento em tempo real para o daemon do PulseAudio"
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring high-priority scheduling."
+#~ msgstr ""
+#~ "Uma política do sistema impede que o PulseAudio adquira escalonamento de "
+#~ "alta prioridade."
+
+#~ msgid ""
+#~ "System policy prevents PulseAudio from acquiring real-time scheduling."
+#~ msgstr ""
+#~ "Uma política do sistema impede que o PulseAudio adquira o escalonamento "
+#~ "em tempo real."
+
+#~ msgid "read() failed: %s\n"
+#~ msgstr "read() falhou: %s\n"
+
+#~ msgid "pa_context_connect() failed: %s\n"
+#~ msgstr "pa_context_connect() falhou: %s\n"
+
+#~ msgid "We're in the group '%s', allowing high-priority scheduling."
+#~ msgstr "Estamos no grupo '%s', permitindo escalonamento de alta prioridade."
+
+#~ msgid "We're in the group '%s', allowing real-time scheduling."
+#~ msgstr "Estamos no grupo '%s', permitindo escalonamento em tempo real."
+
+#~ msgid "PolicyKit grants us acquire-high-priority privilege."
+#~ msgstr ""
+#~ "O PolicyKit assegura-nos a aquisição de privilégio de alta prioridade."
+
+#~ msgid "PolicyKit refuses acquire-high-priority privilege."
+#~ msgstr "O PolicyKit recusa a aquisição de privilégios de alta prioridade."
+
+#~ msgid "PolicyKit grants us acquire-real-time privilege."
+#~ msgstr "O PolicyKit assegura-nos a aquisição de privilégios de tempo-real."
+
+#~ msgid "PolicyKit refuses acquire-real-time privilege."
+#~ msgstr "O PolicyKit recusa a aquisição de privilégios de tempo real."
+
+#~ msgid ""
+#~ "High-priority scheduling enabled in configuration but not allowed by "
+#~ "policy."
+#~ msgstr ""
+#~ "O escalonamento de alta prioridade foi habilitado para esta configuração, "
+#~ "mas não é permitida pela política."
+
+#~ msgid "Successfully increased RLIMIT_RTPRIO"
+#~ msgstr "RLIMIT_RTPRIO aumentado com sucesso"
+
+#~ msgid "RLIMIT_RTPRIO failed: %s"
+#~ msgstr "RLIMIT_RTPRIO falhou: %s"
+
+#~ msgid "Giving up CAP_NICE"
+#~ msgstr "Abandonando CAP_NICE"
+
+#~ msgid ""
+#~ "Real-time scheduling enabled in configuration but not allowed by policy."
+#~ msgstr ""
+#~ "O escalonamento de tempo real foi habilitado pela configuração, mas não é "
+#~ "permitido pela política."
+
+#~ msgid "Limited capabilities successfully to CAP_SYS_NICE."
+#~ msgstr "As capacidades foram limitadas com sucesso para CAP_SYS_NICE."
+
+#~ msgid "time_new() failed.\n"
+#~ msgstr "time_new() falhou.\n"
+
+#~ msgid "Stream successfully created\n"
+#~ msgstr "Fluxo criado com sucesso\n"
+
+#~ msgid "Stream errror: %s\n"
+#~ msgstr "Erro de fluxo: %s\n"
+
+#~ msgid "Connection established.\n"
+#~ msgstr "Conexão estabelecida.\n"
+
+#~ msgid ""
+#~ "%s [options] [FILE]\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -v, --verbose                         Enable verbose operation\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -d, --device=DEVICE                   The name of the sink to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ "      --stream-name=NAME                How to call this stream on the "
+#~ "server\n"
+#~ "      --volume=VOLUME                   Specify the initial (linear) "
+#~ "volume in range 0...65536\n"
+#~ "      --channel-map=CHANNELMAP          Set the channel map to the use\n"
+#~ msgstr ""
+#~ "%s [options] [FILE]\n"
+#~ "\n"
+#~ "  -h, --help                            Mostra essa ajuda\n"
+#~ "      --version                         Mostra a versão\n"
+#~ "\n"
+#~ "  -v, --verbose                         Habilida a operação detalhada\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   O nome do servidor a ser "
+#~ "conectado\n"
+#~ "  -d, --device=DEVICE                   O nome do destino a ser "
+#~ "conectado\n"
+#~ "  -n, --client-name=NAME                Como chamar este cliente no "
+#~ "servidor\n"
+#~ "      --stream-name=NAME                Como chamar este fluxo no "
+#~ "servidor\n"
+#~ "      --volume=VOLUME                   Especifica o volume inicial "
+#~ "(linear) no intervalo 0...65536\n"
+#~ "      --channel-map=CHANNELMAP          Define o mapa do canal para uso\n"
+
+#~ msgid ""
+#~ "paplay %s\n"
+#~ "Compiled with libpulse %s\n"
+#~ "Linked with libpulse %s\n"
+#~ msgstr ""
+#~ "paplay %s\n"
+#~ "Compilado com libpulse %s\n"
+#~ "Linkado com  libpulse %s\n"
+
+#~ msgid "Invalid channel map\n"
+#~ msgstr "Mapa de canais inválido\n"
+
+#~ msgid "Channel map doesn't match file.\n"
+#~ msgstr "O mapa dos canais não coincide com o arquivo.\n"
+
+#~ msgid "Using sample spec '%s'\n"
+#~ msgstr "Usando a especificação da amostragem '%s'\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "Called SUID root and real-time and/or high-priority scheduling was "
+#~ "requested in the configuration. However, we lack the necessary "
+#~ "privileges:\n"
+#~ "We are not in group '"
+#~ msgstr ""
+#~ "A chamada de SUID root e tempo real/alta prioridade no escalonamento foi "
+#~ "requisitada pela configuração. Todavia, falta-nos os privilégios "
+#~ "necessários:\n"
+#~ "Não estamos no grupo'"
+
+#, fuzzy
+#~ msgid "--log-time boolean argument"
+#~ msgstr "--disallow-exit argumento booleano"
+
+#~ msgid "Default sink name (%s) does not exist in name register."
+#~ msgstr "O nome padrão do destino (%s) não existe no registro de nomes."
+
+#~ msgid "Buffer overrun, dropping incoming data\n"
+#~ msgstr "Houve estouro de buffer, os dados que chegaram foram descartados\n"
+
+#~ msgid "pa_stream_drop() failed: %s\n"
+#~ msgstr "pa_stream_drop() falhou: %s\n"
+
+#~ msgid "muted"
+#~ msgstr "mudo"
+
+#~ msgid ""
+#~ "*** Autoload Entry #%u ***\n"
+#~ "Name: %s\n"
+#~ "Type: %s\n"
+#~ "Module: %s\n"
+#~ "Argument: %s\n"
+#~ msgstr ""
+#~ "*** Entrada do Autoload #%u ***\n"
+#~ "Nome: %s\n"
+#~ "Tipo: %s\n"
+#~ "Módulo: %s\n"
+#~ "Argumento: %s\n"
+
+#~ msgid ""
+#~ "' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
+#~ "For enabling real-time scheduling please acquire the appropriate "
+#~ "PolicyKit priviliges, or become a member of '"
+#~ msgstr ""
+#~ "' e o PolicyKit recusa-nos a garantia de privilégios. Retirando o SUID "
+#~ "outra vez.\n"
+#~ " Para habilitar o escalonamento em tempo real, por favo, adquira os "
+#~ "privilégios adequados pelo PolicyKit, ou torne-se membro do'"
+
+#~ msgid ""
+#~ "', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this "
+#~ "user."
+#~ msgstr ""
+#~ "', ou eleve o RLIMIT_NICE/RLIMIT_RTPRIO dos limites do recurso para este "
+#~ "usuário."
+
+#~ msgid "socketpair(): %s"
+#~ msgstr "socketpair(): %s"
diff --git a/po/ru.po b/po/ru.po
new file mode 100644 (file)
index 0000000..2e81b07
--- /dev/null
+++ b/po/ru.po
@@ -0,0 +1,3504 @@
+# Russian translation of pulseaudio.
+# Copyright (C) 2010 pulseaudio
+# This file is distributed under the same license as the pulseaudio package.
+#
+# Leonid Kurbatov <llenchikk@rambler.ru>, 2010, 2012.
+# Alexander Potashev <aspotashev@gmail.com>, 2014.
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-05-12 01:28+0400\n"
+"PO-Revision-Date: 2014-05-12 02:33+0400\n"
+"Last-Translator: Alexander Potashev <aspotashev@gmail.com>\n"
+"Language-Team: Russian <kde-russian@lists.kde.ru>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Lokalize 1.5\n"
+
+#: ../src/daemon/caps.c:54
+msgid "Cleaning up privileges."
+msgstr "Отказ от привилегий администратора."
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [параметры]\n"
+"\n"
+"Команды:\n"
+"  -h, --help                            Показать эту справку о параметрах\n"
+"      --version                         Показать сведения о версии\n"
+"      --dump-conf                       Вывести конфигурацию по умолчанию\n"
+"      --dump-modules                    Вывести список доступных модулей\n"
+"      --dump-resample-methods           Вывести список доступных методов\n"
+"                                        передискретизации.\n"
+"      --cleanup-shm                     Очистить неиспользуемые блоки общей "
+"памяти\n"
+"      --start                           Запустить демон, если ещё не "
+"запущен\n"
+"  -k  --kill                            Завершить работу запущенного демона\n"
+"      --check                           Проверить, запущен ли демон\n"
+"                                        (возвращает только код завершения)\n"
+"\n"
+"Параметры:\n"
+"      --system[=ЛОГ.]                   Запустить в общесистемном режиме\n"
+"  -D, --daemonize[=ЛОГ.]                Запустить как демон\n"
+"      --fail[=ЛОГ.]                     Выйти при ошибке запуска\n"
+"      --high-priority[=ЛОГ.]            Попытаться установить высокий\n"
+"                                        приоритет (nice). Доступно только с\n"
+"                                        привилегиями администратора, с "
+"битом\n"
+"                                        SUID у файла, либо при повышенном\n"
+"                                        значении RLIMIT_NICE.\n"
+"      --realtime[=ЛОГ.]                 Попытаться включить планировщик\n"
+"                                        реального времени. Доступно только "
+"с\n"
+"                                        привилегиями администратора, с "
+"битом\n"
+"                                        SUID у файла, либо при повышенном\n"
+"                                        значении RLIMIT_RTPRIO.\n"
+"      --disallow-module-loading[=ЛОГ.]  Запретить загрузку запрошенных\n"
+"                                        пользователем модулей после "
+"запуска.\n"
+"      --disallow-exit[=ЛОГ.]            Запретить выход по запросу "
+"пользователя\n"
+"      --exit-idle-time=СЕКУНДЫ          Завершить работу демона после\n"
+"                                        бездействия в течение указанного\n"
+"                                        периода времени.\n"
+"      --scache-idle-time=СЕКУНДЫ        Выгрузить автоматически загруженные\n"
+"                                        сэмплы после бездействия в течение\n"
+"                                        указанного периода времени.\n"
+"      --log-level[=УРОВЕНЬ]             Повысить или установить уровень\n"
+"                                        подробности журналирования.\n"
+"  -v  --verbose                         Повысить уровень подробности\n"
+"                                        журналирования.\n"
+"      --log-target={auto,syslog,stderr,file:ПУТЬ,newfile:ПУТЬ}\n"
+"                                        Задать журнал\n"
+"      --log-meta[=ЛОГ.]                 Записывать в журнал позиции в "
+"исходном\n"
+"                                        коде.\n"
+"      --log-time[=ЛОГ.]                 Записывать в журнал отметки времени\n"
+"      --log-backtrace=КАДРОВ            Записывать в журнал стек вызовов\n"
+"  -p, --dl-search-path=ПУТЬ             Задать путь для поиска динамических\n"
+"                                        разделяемых объектов (расширений).\n"
+"      --resample-method=МЕТОД           Использовать указанный метод\n"
+"                                        передискретизации. Используйте\n"
+"                                        «--dump-resample-methods» для "
+"просмотра\n"
+"                                        списка возможных значений.\n"
+"      --use-pid-file[=ЛОГ.]             Создать файл с идентификатором "
+"процесса\n"
+"                                        (PID).\n"
+"      --no-cpu-limit[=ЛОГ.]             Не устанавливать ограничитель "
+"загрузки\n"
+"                                        процессора на платформах, на которых "
+"он\n"
+"                                        поддерживается.\n"
+"      --disable-shm[=ЛОГ.]              Выключить поддержку общей памяти.\n"
+"\n"
+"Сценарии запуска:\n"
+"  -L, --load=\"МОДУЛЬ АРГУМЕНТЫ\"         Загрузить указанный модуль с "
+"указанными\n"
+"                                        аргументами.\n"
+"  -F, --file=ИМЯ_ФАЙЛА                  Запустить указанный сценарий\n"
+"  -C                                    После запуска открыть командную "
+"строку\n"
+"                                        в текущем терминале.\n"
+"\n"
+"  -n                                    Не загружать файл сценария,\n"
+"                                        используемый по умолчанию.\n"
+
+#: ../src/daemon/cmdline.c:245
+msgid "--daemonize expects boolean argument"
+msgstr "«--daemonize» ожидает логический аргумент."
+
+#: ../src/daemon/cmdline.c:253
+msgid "--fail expects boolean argument"
+msgstr "«--fail» ожидает логический аргумент."
+
+#: ../src/daemon/cmdline.c:264
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"«--log-level» ожидает уровень журналирования — либо число в диапазоне от 0 "
+"до 4, либо одно из слов «debug», «info», «notice», «warn» или «error»."
+
+#: ../src/daemon/cmdline.c:276
+msgid "--high-priority expects boolean argument"
+msgstr "«--high-priority» ожидает логический аргумент."
+
+#: ../src/daemon/cmdline.c:284
+msgid "--realtime expects boolean argument"
+msgstr "«--realtime» ожидает логический аргумент."
+
+#: ../src/daemon/cmdline.c:292
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "«--disallow-module-loading» ожидает логический аргумент."
+
+#: ../src/daemon/cmdline.c:300
+msgid "--disallow-exit expects boolean argument"
+msgstr "«--disallow-exit» ожидает логический аргумент."
+
+#: ../src/daemon/cmdline.c:308
+msgid "--use-pid-file expects boolean argument"
+msgstr "«--use-pid-file» ожидает логический аргумент."
+
+#: ../src/daemon/cmdline.c:327
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Недопустимый журнал: используйте «syslog», «journal», «stderr», «auto» или "
+"файл — «file:<путь>», «newfile:<путь>»."
+
+#: ../src/daemon/cmdline.c:329
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Недопустимый журнал: используйте «syslog», «stderr», «auto» или файл — «file:"
+"<путь>», «newfile:<путь>»."
+
+#: ../src/daemon/cmdline.c:337
+msgid "--log-time expects boolean argument"
+msgstr "«--log-time» ожидает логический аргумент."
+
+#: ../src/daemon/cmdline.c:345
+msgid "--log-meta expects boolean argument"
+msgstr "«--log-meta» ожидает логический аргумент."
+
+#: ../src/daemon/cmdline.c:365
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Недопустимый метод передискретизации «%s»."
+
+#: ../src/daemon/cmdline.c:372
+msgid "--system expects boolean argument"
+msgstr "«--system» ожидает логический аргумент."
+
+#: ../src/daemon/cmdline.c:380
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "«--no-cpu-limit» ожидает логический аргумент."
+
+#: ../src/daemon/cmdline.c:388
+msgid "--disable-shm expects boolean argument"
+msgstr "«--disable-shm» ожидает логический аргумент."
+
+#: ../src/daemon/daemon-conf.c:260
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Недопустимое назначение журнала «%s»."
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Недопустимый уровень журналирования «%s»."
+
+#: ../src/daemon/daemon-conf.c:290
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Недопустимый метод передискретизации «%s»."
+
+#: ../src/daemon/daemon-conf.c:312
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr ""
+"[%s:%u] Недопустимое значение ограничения на используемые ресурсы (rlimit) "
+"«%s»."
+
+#: ../src/daemon/daemon-conf.c:332
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Недопустимый формат отсчётов «%s»."
+
+#: ../src/daemon/daemon-conf.c:350 ../src/daemon/daemon-conf.c:368
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Недопустимая частота дискретизации «%s»."
+
+#: ../src/daemon/daemon-conf.c:391
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Недопустимые каналы сэмпла «%s»."
+
+#: ../src/daemon/daemon-conf.c:408
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Недопустимая схема каналов «%s»."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Недопустимое число фрагментов «%s»."
+
+#: ../src/daemon/daemon-conf.c:442
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Недопустимый размер фрагмента «%s»."
+
+#: ../src/daemon/daemon-conf.c:459
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Недопустимый приоритет (nice) «%s»."
+
+#: ../src/daemon/daemon-conf.c:502
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Недопустимый тип сервера «%s»."
+
+#: ../src/daemon/daemon-conf.c:615
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Не удалось открыть файл конфигурации: %s"
+
+#: ../src/daemon/daemon-conf.c:631
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"В указанной схеме каналов по умолчанию число каналов отличается от числа "
+"каналов по умолчанию."
+
+#: ../src/daemon/daemon-conf.c:718
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Прочитано из файла конфигурации: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Имя: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "Нет информации о модуле\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Версия: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Описание: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Автор: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Использование: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Загружать только один раз: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "Предупреждение об устаревшем модуле: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Путь: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:77
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Не удалось открыть модуль %s: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:128
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Не удалось найти исходный загрузчик lt_dlopen."
+
+#: ../src/daemon/ltdl-bind-now.c:133
+msgid "Failed to allocate new dl loader."
+msgstr ""
+"Не удалось выделить память для нового загрузчика разделяемых библиотек."
+
+#: ../src/daemon/ltdl-bind-now.c:146
+msgid "Failed to add bind-now-loader."
+msgstr "Не удалось добавить новый загрузчик bind-now."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "Получен сигнал %s."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "Выход."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Не удалось найти пользователя «%s»."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Не удалось найти группу пользователей «%s»."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Найден пользователь «%s» (UID %lu) и группа «%s» (GID %lu)."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "Идентификаторы групп пользователя «%s» и группы «%s» не совпадают."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr ""
+"Домашний каталог пользователя «%s» не совпадает с «%s», проигнорировано."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Не удалось создать «%s»: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Не удалось изменить список групп: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Не удалось изменить идентификатор группы (GID): %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Не удалось изменить идентификатор пользователя (UID): %s"
+
+# BUGME: gettext understands only single string constants. --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78564
+#: ../src/daemon/main.c:271
+msgid "Successfully changed user to \""
+msgstr "Успешно произведена смена пользователя на «"
+
+#: ../src/daemon/main.c:279
+msgid "System wide mode unsupported on this platform."
+msgstr "Общесистемный режим не поддерживается на этой платформе."
+
+#: ../src/daemon/main.c:297
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "Не удалось выполнить setrlimit(%s, (%u, %u)): %s"
+
+#: ../src/daemon/main.c:498
+msgid "Failed to parse command line."
+msgstr "Ошибка разбора командной строки."
+
+#: ../src/daemon/main.c:537
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Общесистемный режим невозможно использовать без привилегий администратора. "
+"Будет запущена только служба обнаружения сервера D-Bus."
+
+#: ../src/daemon/main.c:619
+msgid "Daemon not running"
+msgstr "Демон не запущен."
+
+#: ../src/daemon/main.c:621
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "Демон запущен с идентификатором процесса (PID) %u."
+
+#: ../src/daemon/main.c:636
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Не удалось завершить работу демона: %s"
+
+#: ../src/daemon/main.c:665
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Эта программа не предназначена для запуска с привилегиями администратора "
+"(кроме случая, когда указан ключ «--system»)."
+
+#: ../src/daemon/main.c:668
+msgid "Root privileges required."
+msgstr "Необходимы привилегии администратора."
+
+#: ../src/daemon/main.c:675
+msgid "--start not supported for system instances."
+msgstr "«--start» не поддерживается для общесистемного режима."
+
+#: ../src/daemon/main.c:715
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr "Обнаружен настроенный вручную сервер на %s, отказ от запуска."
+
+#: ../src/daemon/main.c:721
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Обнаружен настроенный вручную сервер на %s, который работает на этом "
+"компьютере. Попытка запуска будет продолжена."
+
+# BUGME: remove exclamation --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78563
+#: ../src/daemon/main.c:726
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "Запущен в общесистемном режиме, но «--disallow-exit» не задан."
+
+# BUGME: remove exclamation --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78563
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"Запущен в общесистемном режиме, но «--disallow-module-loading» не задан."
+
+# BUGME: remove exclamation --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78563
+#: ../src/daemon/main.c:732
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr ""
+"Запущен в общесистемном режиме, поэтому режим SHM принудительно отключён."
+
+# BUGME: remove exclamation --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78563
+#: ../src/daemon/main.c:737
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"Запущен в общесистемном режиме, поэтому автоматическое завершение при долгом "
+"простое отключено."
+
+#: ../src/daemon/main.c:765
+msgid "Failed to acquire stdio."
+msgstr "Не удалось начать критическую секцию работы с вводом-выводом."
+
+#: ../src/daemon/main.c:771 ../src/daemon/main.c:842
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "Произошла ошибка при выполнении pipe(): %s"
+
+#: ../src/daemon/main.c:776 ../src/daemon/main.c:847
+#, c-format
+msgid "fork() failed: %s"
+msgstr "Произошла ошибка при выполнении fork(): %s"
+
+#: ../src/daemon/main.c:791 ../src/daemon/main.c:862 ../src/utils/pacat.c:569
+#, c-format
+msgid "read() failed: %s"
+msgstr "Произошла ошибка при выполнении read(): %s"
+
+#: ../src/daemon/main.c:797
+msgid "Daemon startup failed."
+msgstr "Не удалось запустить демон."
+
+#: ../src/daemon/main.c:799
+msgid "Daemon startup successful."
+msgstr "Демон успешно запущен."
+
+#: ../src/daemon/main.c:830
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "Произошла ошибка при выполнении setsid(): %s"
+
+#: ../src/daemon/main.c:916
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "PulseAudio %s"
+
+#: ../src/daemon/main.c:917
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Скомпилирован на хосте: %s"
+
+#: ../src/daemon/main.c:918 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "CFLAGS при компиляции: %s"
+
+#: ../src/daemon/main.c:921
+#, c-format
+msgid "Running on host: %s"
+msgstr "Запущен на хосте: %s"
+
+#: ../src/daemon/main.c:924
+#, c-format
+msgid "Found %u CPUs."
+msgstr "Найдено процессоров: %u."
+
+#: ../src/daemon/main.c:926
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "Размер страницы: %lu байт"
+
+#: ../src/daemon/main.c:929
+msgid "Compiled with Valgrind support: yes"
+msgstr "Скомпилировано с поддержкой Valgrind: да"
+
+#: ../src/daemon/main.c:931
+msgid "Compiled with Valgrind support: no"
+msgstr "Скомпилировано с поддержкой Valgrind: нет"
+
+#: ../src/daemon/main.c:934
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Запущен в режиме Valgrind: %s"
+
+#: ../src/daemon/main.c:936
+#, c-format
+msgid "Running in VM: %s"
+msgstr "Запущен в виртуальной машине: %s"
+
+#: ../src/daemon/main.c:939
+msgid "Optimized build: yes"
+msgstr "Оптимизированная сборка: да"
+
+#: ../src/daemon/main.c:941
+msgid "Optimized build: no"
+msgstr "Оптимизированная сборка: нет"
+
+#: ../src/daemon/main.c:945
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG задан, все проверки отключены."
+
+# BUGME: Should be "FASTPATH defined, only fast path asserts enabled." --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78568
+#: ../src/daemon/main.c:947
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH задан, включены только быстрые проверки «fast path»."
+
+#: ../src/daemon/main.c:949
+msgid "All asserts enabled."
+msgstr "Все проверки включены."
+
+#: ../src/daemon/main.c:953
+msgid "Failed to get machine ID"
+msgstr "Не удалось получить идентификатор компьютера."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Machine ID is %s."
+msgstr "Идентификатор компьютера: %s."
+
+#: ../src/daemon/main.c:960
+#, c-format
+msgid "Session ID is %s."
+msgstr "Идентификатор сеанса: %s."
+
+#: ../src/daemon/main.c:966
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Используется рабочий каталог %s."
+
+#: ../src/daemon/main.c:971
+#, c-format
+msgid "Using state directory %s."
+msgstr "Используется каталог хранения состояний %s."
+
+#: ../src/daemon/main.c:974
+#, c-format
+msgid "Using modules directory %s."
+msgstr "Используется каталог модулей %s."
+
+#: ../src/daemon/main.c:976
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Запущен в общесистемном режиме: %s"
+
+#: ../src/daemon/main.c:979
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Вы запустили PulseAudio в общесистемном режиме. Помните, что вам, скорее "
+"всего, не следует делать этого.\n"
+"Если вы это всё равно делаете, то ваша вина, если что-то работает не как "
+"ожидалось.\n"
+"Пожалуйста, прочитайте http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ для понимания, почему "
+"общесистемный режим обычно является плохой практикой."
+
+#: ../src/daemon/main.c:996
+msgid "pa_pid_file_create() failed."
+msgstr "Произошла ошибка при выполнении pa_pid_file_create()."
+
+# BUGME: remove exclamation --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78563
+#: ../src/daemon/main.c:1006
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "Доступны таймеры высокого разрешения."
+
+# BUGME: remove exclamation --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78563
+#: ../src/daemon/main.c:1008
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Рекомендуется включить в ядре Linux поддержку таймеров высокого разрешения."
+
+#: ../src/daemon/main.c:1026
+msgid "pa_core_new() failed."
+msgstr "Произошла ошибка при выполнении pa_core_new()."
+
+#: ../src/daemon/main.c:1104
+msgid "Failed to initialize daemon."
+msgstr "Не удалось инициализировать демон."
+
+#: ../src/daemon/main.c:1109
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Запуск демона без каких-либо загружаемых модулей, отказ от работы."
+
+#: ../src/daemon/main.c:1147
+msgid "Daemon startup complete."
+msgstr "Запуск демона завершён."
+
+#: ../src/daemon/main.c:1153
+msgid "Daemon shutdown initiated."
+msgstr "Начато завершение демона."
+
+#: ../src/daemon/main.c:1184
+msgid "Daemon terminated."
+msgstr "Демон завершён."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Звуковая система PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Запуск звуковой системы PulseAudio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2251
+msgid "Input"
+msgstr "Вход"
+
+#: ../src/modules/alsa/alsa-mixer.c:2252
+msgid "Docking Station Input"
+msgstr "Вход док-станции"
+
+#: ../src/modules/alsa/alsa-mixer.c:2253
+msgid "Docking Station Microphone"
+msgstr "Микрофон док-станции"
+
+#: ../src/modules/alsa/alsa-mixer.c:2254
+msgid "Docking Station Line In"
+msgstr "Линейный вход док-станции"
+
+#: ../src/modules/alsa/alsa-mixer.c:2255 ../src/modules/alsa/alsa-mixer.c:2340
+msgid "Line In"
+msgstr "Линейный вход"
+
+#: ../src/modules/alsa/alsa-mixer.c:2256 ../src/modules/alsa/alsa-mixer.c:2334
+#: ../src/modules/bluetooth/module-bluez4-device.c:2101
+#: ../src/modules/bluetooth/module-bluez5-device.c:1451
+msgid "Microphone"
+msgstr "Микрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2257 ../src/modules/alsa/alsa-mixer.c:2335
+msgid "Front Microphone"
+msgstr "Фронтальный микрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2258 ../src/modules/alsa/alsa-mixer.c:2336
+msgid "Rear Microphone"
+msgstr "Тыловой микрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2259
+msgid "External Microphone"
+msgstr "Внешний микрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2260 ../src/modules/alsa/alsa-mixer.c:2338
+msgid "Internal Microphone"
+msgstr "Встроенный микрофон"
+
+# BUGME: please clarify, is this FM Radio or some digital radio frequency channel? --aspotashev
+#: ../src/modules/alsa/alsa-mixer.c:2261 ../src/modules/alsa/alsa-mixer.c:2341
+msgid "Radio"
+msgstr "Радио"
+
+# BUGME: please clarify? --aspotashev
+#: ../src/modules/alsa/alsa-mixer.c:2262 ../src/modules/alsa/alsa-mixer.c:2342
+msgid "Video"
+msgstr "Видео"
+
+#: ../src/modules/alsa/alsa-mixer.c:2263
+msgid "Automatic Gain Control"
+msgstr "Автоматическая регулировка усиления"
+
+#: ../src/modules/alsa/alsa-mixer.c:2264
+msgid "No Automatic Gain Control"
+msgstr "Нет автоматической регулировки усиления"
+
+#: ../src/modules/alsa/alsa-mixer.c:2265
+msgid "Boost"
+msgstr "Усиление"
+
+#: ../src/modules/alsa/alsa-mixer.c:2266
+msgid "No Boost"
+msgstr "Нет усиления"
+
+#: ../src/modules/alsa/alsa-mixer.c:2267
+msgid "Amplifier"
+msgstr "Усилитель"
+
+#: ../src/modules/alsa/alsa-mixer.c:2268
+msgid "No Amplifier"
+msgstr "Нет усилителя"
+
+#: ../src/modules/alsa/alsa-mixer.c:2269
+msgid "Bass Boost"
+msgstr "Усиление басов"
+
+#: ../src/modules/alsa/alsa-mixer.c:2270
+msgid "No Bass Boost"
+msgstr "Нет усиления басов"
+
+#: ../src/modules/alsa/alsa-mixer.c:2271
+#: ../src/modules/bluetooth/module-bluez4-device.c:2106
+#: ../src/modules/bluetooth/module-bluez5-device.c:1458
+msgid "Speaker"
+msgstr "Динамик"
+
+#: ../src/modules/alsa/alsa-mixer.c:2272 ../src/modules/alsa/alsa-mixer.c:2344
+msgid "Headphones"
+msgstr "Аналоговые наушники"
+
+#: ../src/modules/alsa/alsa-mixer.c:2333
+msgid "Analog Input"
+msgstr "Аналоговый вход"
+
+#: ../src/modules/alsa/alsa-mixer.c:2337
+msgid "Dock Microphone"
+msgstr "Микрофон док-станции"
+
+#: ../src/modules/alsa/alsa-mixer.c:2339
+msgid "Headset Microphone"
+msgstr "Микрофон гарнитуры"
+
+#: ../src/modules/alsa/alsa-mixer.c:2343
+msgid "Analog Output"
+msgstr "Аналоговый выход"
+
+#: ../src/modules/alsa/alsa-mixer.c:2345
+msgid "LFE on Separate Mono Output"
+msgstr "Аналоговый выход для низких частот"
+
+#: ../src/modules/alsa/alsa-mixer.c:2346
+msgid "Line Out"
+msgstr "Линейный выход"
+
+#: ../src/modules/alsa/alsa-mixer.c:2347
+msgid "Analog Mono Output"
+msgstr "Аналоговый выход моно"
+
+#: ../src/modules/alsa/alsa-mixer.c:2348
+msgid "Speakers"
+msgstr "Динамики"
+
+#: ../src/modules/alsa/alsa-mixer.c:2349
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2350
+msgid "Digital Output (S/PDIF)"
+msgstr "Цифровой выход (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2351
+msgid "Digital Input (S/PDIF)"
+msgstr "Цифровой вход (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2352
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Сквозной цифровой канал (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3807
+msgid "Analog Mono"
+msgstr "Аналоговый моно"
+
+#: ../src/modules/alsa/alsa-mixer.c:3808
+msgid "Analog Stereo"
+msgstr "Аналоговый стерео"
+
+#: ../src/modules/alsa/alsa-mixer.c:3809
+msgid "Analog Surround 2.1"
+msgstr "Аналоговый объёмный 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3810
+msgid "Analog Surround 3.0"
+msgstr "Аналоговый объёмный 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3811
+msgid "Analog Surround 3.1"
+msgstr "Аналоговый объёмный 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3812
+msgid "Analog Surround 4.0"
+msgstr "Аналоговый объёмный 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3813
+msgid "Analog Surround 4.1"
+msgstr "Аналоговый объёмный 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3814
+msgid "Analog Surround 5.0"
+msgstr "Аналоговый объёмный 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3815
+msgid "Analog Surround 5.1"
+msgstr "Аналоговый объёмный 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3816
+msgid "Analog Surround 6.0"
+msgstr "Аналоговый объёмный 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3817
+msgid "Analog Surround 6.1"
+msgstr "Аналоговый объёмный 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3818
+msgid "Analog Surround 7.0"
+msgstr "Аналоговый объёмный 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3819
+msgid "Analog Surround 7.1"
+msgstr "Аналоговый объёмный 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3820
+msgid "Analog 4-channel Input"
+msgstr "4-канальный аналоговый вход"
+
+#: ../src/modules/alsa/alsa-mixer.c:3821
+msgid "Digital Stereo (IEC958)"
+msgstr "Цифровой стерео (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3822
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Сквозной цифровой канал (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3823
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Цифровой объёмный 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3824
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Цифровой объёмный 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3825
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Цифровой объёмный 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3826
+msgid "Digital Stereo (HDMI)"
+msgstr "Цифровой стерео (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3827
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Цифровой объёмный 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3956
+msgid "Analog Mono Duplex"
+msgstr "Аналоговый моно дуплекс"
+
+#: ../src/modules/alsa/alsa-mixer.c:3957
+msgid "Analog Stereo Duplex"
+msgstr "Аналоговый стерео дуплекс"
+
+#: ../src/modules/alsa/alsa-mixer.c:3958
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Цифровой стерео дуплекс (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3959
+#: ../src/modules/alsa/module-alsa-card.c:193
+#: ../src/modules/bluetooth/module-bluez4-device.c:2297
+#: ../src/modules/bluetooth/module-bluez5-device.c:1656
+msgid "Off"
+msgstr "Выключено"
+
+#: ../src/modules/alsa/alsa-mixer.c:4058
+#, c-format
+msgid "%s Output"
+msgstr "%s выход"
+
+#: ../src/modules/alsa/alsa-mixer.c:4066
+#, c-format
+msgid "%s Input"
+msgstr "%s вход"
+
+# BUGME: remove exclamation --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78563
+#: ../src/modules/alsa/alsa-sink.c:570 ../src/modules/alsa/alsa-sink.c:748
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA сообщила о возможности записи новых данных в устройство, но на самом\n"
+"деле писать было нечего. Скорее всего, это ошибка в драйвере ALSA «%s».\n"
+"Пожалуйста, сообщите об этой проблеме разработчикам ALSA. Процесс разбужен\n"
+"с установленным POLLOUT, однако последующий вызов snd_pcm_avail() вернул 0\n"
+"или другое значение, меньшее чем min_avail."
+
+# BUGME: remove exclamation --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78563
+#: ../src/modules/alsa/alsa-source.c:529 ../src/modules/alsa/alsa-source.c:681
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA сообщила о возможности чтения новых данных из устройства, но на самом\n"
+"деле читать было нечего. Скорее всего, это ошибка в драйвере ALSA «%s».\n"
+"Пожалуйста, сообщите об этой проблеме разработчикам ALSA. Процесс разбужен\n"
+"с установленным POLLIN, однако последующий вызов snd_pcm_avail() вернул 0\n"
+"или другое значение, меньшее чем min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1135 ../src/modules/alsa/alsa-util.c:1210
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() возвращает значение, которое является исключительно большим: "
+"%lu байт (%lu мс).\n"
+"Вероятно, это ошибка в драйвере ALSA «%s». Пожалуйста, сообщите об этой "
+"проблеме разработчикам ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1185
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() возвращает значение, которое является исключительно большим: "
+"%li байт (%s%lu мс).\n"
+"Вероятно, это ошибка в драйвере ALSA «%s». Пожалуйста, сообщите об этой "
+"проблеме разработчикам ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1226
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() возвращает странное значение: задержка %lu меньше "
+"доступных %lu.\n"
+"Вероятно, это ошибка в драйвере ALSA «%s». Пожалуйста, сообщите об этой "
+"проблеме разработчикам ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1269
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() возвращает значение, которое является исключительно "
+"большим: %lu байт (%lu мс).\n"
+"Вероятно, это ошибка в драйвере ALSA «%s». Пожалуйста, сообщите об этой "
+"проблеме разработчикам ALSA."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2091
+#: ../src/modules/bluetooth/module-bluez5-device.c:1441
+msgid "Headset"
+msgstr "Гарнитура"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2096
+#: ../src/modules/bluetooth/module-bluez5-device.c:1446
+msgid "Handsfree"
+msgstr "Хендс-фри"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2111
+#: ../src/modules/bluetooth/module-bluez5-device.c:1464
+msgid "Headphone"
+msgstr "Наушник"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2116
+#: ../src/modules/bluetooth/module-bluez5-device.c:1469
+msgid "Portable"
+msgstr "Портативный динамик"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2121
+#: ../src/modules/bluetooth/module-bluez5-device.c:1474
+msgid "Car"
+msgstr "Автомобильный динамик"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2126
+#: ../src/modules/bluetooth/module-bluez5-device.c:1479
+msgid "HiFi"
+msgstr "Hi-Fi"
+
+# BUGME: please clarify, does this mean a cell phone? --aspotashev
+#: ../src/modules/bluetooth/module-bluez4-device.c:2131
+#: ../src/modules/bluetooth/module-bluez5-device.c:1484
+msgid "Phone"
+msgstr "Телефон"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2139
+#: ../src/modules/bluetooth/module-bluez5-device.c:1436
+#: ../src/modules/bluetooth/module-bluez5-device.c:1452
+#: ../src/modules/bluetooth/module-bluez5-device.c:1490
+msgid "Bluetooth Output"
+msgstr "Выход Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2142
+#: ../src/modules/bluetooth/module-bluez5-device.c:1435
+#: ../src/modules/bluetooth/module-bluez5-device.c:1457
+#: ../src/modules/bluetooth/module-bluez5-device.c:1463
+#: ../src/modules/bluetooth/module-bluez5-device.c:1489
+msgid "Bluetooth Input"
+msgstr "Вход Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2178
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Воспроизведение высокого качества (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2189
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Запись высокого качества (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2200
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Дуплексная телефония (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2212
+msgid "Handsfree Gateway"
+msgstr "Шлюз передачи данных для хендс-фри"
+
+#. TODO: Change this profile's name to a2dp_sink, to reflect the remote
+#. * device's role and be consistent with the a2dp source profile
+#: ../src/modules/bluetooth/module-bluez5-device.c:1529
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Воспроизведение высокого качества (приёмник A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1540
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Запись высокого качества (передатчик A2DP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"source_name=<имя источника> source_properties=<свойства источника> "
+"source_master=<имя источника для фильтрации> sink_name=<имя аудиоприёмника> "
+"sink_properties=<свойства аудиоприёмника> sink_master=<имя аудиоприёмника "
+"для фильтрации> adjust_time=<частота выравнивания в секундах> "
+"adjust_threshold=<пороговая величина дрейфа в миллисекундах, после которой "
+"нужно проводить выравнивание> format=<формат отсчётов> rate=<частота "
+"дискретизации> channels=<число каналов> channel_map=<схема каналов> "
+"aec_method=<используемая реализация> aec_args=<параметры для алгоритмов "
+"эхоподавления> save_aec=<сохранять данные эхоподавления в /tmp> "
+"autoloaded=<нужно установить, если этот модуль загружен автоматически> "
+"use_volume_sharing=<использовать общий уровень громкости (yes или no)> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:756
+msgid "On"
+msgstr "Включено"
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+"Всегда оставлять хотя бы один аудиоприёмник загруженным, даже если он пустой."
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Фиктивный выход"
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr "Эквалайзер общего назначения"
+
+#: ../src/modules/module-equalizer-sink.c:76
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<имя аудиоприёмника> sink_properties=<свойства аудиоприёмника> "
+"sink_master=<аудиоприёмника, к которому нужно подключиться> format=<формат "
+"отсчётов> rate=<частота дискретизации> channels=<число каналов> "
+"channel_map=<схема каналов> autoloaded=<нужно установить, если этот модуль "
+"загружен автоматически> use_volume_sharing=<использовать общий уровень "
+"громкости (yes или no)> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<нужно ли автоматически выгружать неиспользуемые фильтры>"
+
+#: ../src/modules/module-ladspa-sink.c:53
+msgid "Virtual LADSPA sink"
+msgstr "Виртуальный аудиоприёмник LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:57
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<имя аудиоприёмника> sink_properties=<свойства аудиоприёмника> "
+"master=<имя аудиоприёмника для фильтрации> format=<формат отсчётов> "
+"rate=<частота дискретизации> channels=<число каналов> channel_map=<схема "
+"каналов> plugin=<имя расширения LADSPA> label=<метка расширения LADSPA> "
+"control=<разделенный запятыми список управляющих значений> "
+"input_ladspaport_map=<разделенный запятыми список имён входных портов "
+"LADSPA> output_ladspaport_map=<разделенный запятыми список имён выходных "
+"портов LADSPA> "
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "Синхронный пустой аудиоприёмник"
+
+#: ../src/modules/module-null-sink.c:280
+msgid "Null Output"
+msgstr "Пустой выход"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:907
+msgid "Output Devices"
+msgstr "Устройства вывода"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:908
+msgid "Input Devices"
+msgstr "Устройства ввода"
+
+#: ../src/modules/module-rygel-media-server.c:1065
+msgid "Audio on @HOSTNAME@"
+msgstr "Аудио на @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:293
+#: ../src/modules/module-tunnel-source-new.c:294
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Туннель для %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:483
+#: ../src/modules/module-tunnel-source-new.c:485
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Туннель к %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:49
+msgid "Virtual surround sink"
+msgstr "Виртуальный аудиоприёмник объёмного звука"
+
+#: ../src/modules/module-virtual-surround-sink.c:53
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/"
+"left_hrir.wav "
+msgstr ""
+"sink_name=<имя аудиоприёмника> sink_properties=<свойства аудиоприёмника> "
+"master=<имя аудиоприёмника для фильтрации> format=<формат отсчётов> "
+"rate=<частота дискретизации> channels=<число каналов> channel_map=<схема "
+"каналов> use_volume_sharing=<использовать общий уровень громкости (yes или "
+"no)> force_flat_volume=<yes или no> hrir=/путь/к/left_hrir.wav "
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "Аудиосервер PulseAudio"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:754
+msgid "Mono"
+msgstr "Моно"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Центральный фронтальный"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Левый фронтальный"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Правый фронтальный"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Центральный тыловой"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Левый тыловой"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Правый тыловой"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr "Сабвуфер"
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Фронтальный левее центра"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Фронтальный правее центра"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Левый боковой"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Правый боковой"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Вспомогательный 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Вспомогательный 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Вспомогательный 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Вспомогательный 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Вспомогательный 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Вспомогательный 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Вспомогательный 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Вспомогательный 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Вспомогательный 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Вспомогательный 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Вспомогательный 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Вспомогательный 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Вспомогательный 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Вспомогательный 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Вспомогательный 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Вспомогательный 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Вспомогательный 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Вспомогательный 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Вспомогательный 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Вспомогательный 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Вспомогательный 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Вспомогательный 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Вспомогательный 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Вспомогательный 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Вспомогательный 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Вспомогательный 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Вспомогательный 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Вспомогательный 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Вспомогательный 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Вспомогательный 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Вспомогательный 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Вспомогательный 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Верхний центральный"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Верхний центральный фронтальный"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Верхний левый фронтальный"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Верхний правый фронтальный"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Верхний центральный тыловой"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Верхний левый тыловой"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Верхний правый тыловой"
+
+#: ../src/pulse/channelmap.c:481 ../src/pulse/format.c:123
+#: ../src/pulse/sample.c:177 ../src/pulse/volume.c:296
+#: ../src/pulse/volume.c:322 ../src/pulse/volume.c:342
+#: ../src/pulse/volume.c:374 ../src/pulse/volume.c:414
+#: ../src/pulse/volume.c:433
+msgid "(invalid)"
+msgstr "(недействительно)"
+
+#: ../src/pulse/channelmap.c:758
+msgid "Stereo"
+msgstr "Стерео"
+
+#: ../src/pulse/channelmap.c:763
+msgid "Surround 4.0"
+msgstr "Объёмный 4.0"
+
+#: ../src/pulse/channelmap.c:769
+msgid "Surround 4.1"
+msgstr "Объёмный 4.1"
+
+#: ../src/pulse/channelmap.c:775
+msgid "Surround 5.0"
+msgstr "Объёмный 5.0"
+
+#: ../src/pulse/channelmap.c:781
+msgid "Surround 5.1"
+msgstr "Объёмный 5.1"
+
+#: ../src/pulse/channelmap.c:788
+msgid "Surround 7.1"
+msgstr "Объёмный 7.1"
+
+#: ../src/pulse/client-conf-x11.c:55 ../src/utils/pax11publish.c:99
+msgid "xcb_connect() failed"
+msgstr "Произошла ошибка при выполнении pa_context_connect()."
+
+#: ../src/pulse/client-conf-x11.c:60 ../src/utils/pax11publish.c:104
+msgid "xcb_connection_has_error() returned true"
+msgstr "Вызов xcb_connection_has_error() вернул «true»."
+
+#: ../src/pulse/client-conf-x11.c:96
+msgid "Failed to parse cookie data"
+msgstr "Не удалось разобрать данные cookie"
+
+#: ../src/pulse/context.c:529
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "Cookie не загружены. Попытка подключения без них."
+
+#: ../src/pulse/context.c:610
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:665
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1366
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Получено сообщение для неизвестного расширения «%s»."
+
+#: ../src/pulsecore/lock-autospawn.c:143 ../src/pulsecore/lock-autospawn.c:229
+msgid "Cannot access autospawn lock."
+msgstr ""
+"Не удалось получить доступ к блокировке, используемой для запуска сервера по "
+"требованию."
+
+#: ../src/pulsecore/log.c:155
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Не удалось открыть файл журнала «%s»."
+
+#: ../src/pulsecore/log.c:178
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Не удалось открыть ни один из файлов журналов «%s», «%s.1», «%s.2», ..., «%s."
+"%d»."
+
+#: ../src/pulsecore/log.c:633
+msgid "Invalid log target."
+msgstr "Недопустимый журнал."
+
+#: ../src/pulsecore/sink.c:3428
+msgid "Built-in Audio"
+msgstr "Встроенное аудио"
+
+#: ../src/pulsecore/sink.c:3433
+msgid "Modem"
+msgstr "Модем"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "ОК"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "Доступ запрещён"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Неизвестная команда"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Недопустимый параметр"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "Объект существует"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "Нет такого объекта"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "Соединение отвергнуто"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "Ошибка протокола"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "Время ожидания истекло"
+
+# BUGME: authorization -> authentication? --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78566
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "Нет ключа аутентификации"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "Внутренняя ошибка"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "Соединение завершено"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "Объект уничтожен"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Недопустимый сервер"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Не удалось инициализировать модуль"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Некорректное состояние"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Нет данных"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "Несовместимая версия протокола"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Слишком большое значение параметра"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "Не поддерживается"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Неизвестный код ошибки"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "Нет такого расширения"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Устаревшая функциональность"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Отсутствует реализация"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Клиент посылает запросы после вызова fork()"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Ошибка ввода/вывода"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Устройство или ресурс занято"
+
+#: ../src/pulse/sample.c:179
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %u-канальный %u Гц"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f ГиБ"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f МиБ"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f КиБ"
+
+#: ../src/pulse/sample.c:197
+#, c-format
+msgid "%u B"
+msgstr "%u Б"
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+"%s [параметры]\n"
+"\n"
+"-h, --help                            Показать эту справку о параметрах\n"
+"-v, --verbose                         Выводить отладочные сообщения\n"
+"      --from-rate=ЧАСТОТА             Исходная частота дискретизации в Гц\n"
+"                                      (по умолчанию 44100)\n"
+"      --from-format=ФОРМАТ_ОТСЧЁТОВ   Исходный формат отсчётов (по "
+"умолчанию\n"
+"                                      s16le)\n"
+"      --from-channels=КАНАЛОВ         Исходное число каналов (по умолчанию "
+"1)\n"
+"      --to-rate=ЧАСТОТА               Конечная частота дискретизации в Гц\n"
+"                                      (по умолчанию 44100)\n"
+"      --to-format=ФОРМАТ_ОТСЧЁТОВ     Конечный формат отсчётов (по "
+"умолчанию                                       s16le)\n"
+"      --to-channels=КАНАЛОВ           Конечное число каналов (по умолчанию "
+"1)\n"
+"      --resample-method=МЕТОД         Метод передискретизации (по "
+"умолчанию                                       «auto»)\n"
+"      --seconds=СЕКУНДЫ               Исходная длительность потока\n"
+"                                      (по умолчанию 60)\n"
+"\n"
+"Если форматы не указаны, но будут перебраны все комбинации форматов.\n"
+"\n"
+"Формат отсчётов должен принимать одно из значений s16le, s16be, u8, "
+"float32le,\n"
+"float32be, ulaw, alaw, s24le, s24be, s24-32le, s24-32be, s32le, s32be\n"
+"(по умолчанию s16ne).\n"
+"\n"
+"Используйте «--dump-resample-methods» для просмотра списка возможных "
+"методов\n"
+"передискретизации.\n"
+
+#: ../src/tests/resampler-test.c:356
+#, c-format
+msgid "%s %s\n"
+msgstr "%s %s\n"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr "=== %d секунд: %d Гц %d-канальный (%s) -> %d Hz %d-канальный (%s)"
+
+#: ../src/utils/pacat.c:118
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Не удалось передать остатки данных в потоке: %s"
+
+#: ../src/utils/pacat.c:123
+msgid "Playback stream drained."
+msgstr "Переданы остатки данных в потоке воспроизведения."
+
+#: ../src/utils/pacat.c:134
+msgid "Draining connection to server."
+msgstr "Закрытие соединения с сервером."
+
+#: ../src/utils/pacat.c:147
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:170
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "Произошла ошибка при выполнении pa_stream_write(): %s"
+
+#: ../src/utils/pacat.c:211
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "Произошла ошибка при выполнении pa_stream_begin_write(): %s"
+
+#: ../src/utils/pacat.c:261 ../src/utils/pacat.c:291
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "Произошла ошибка при выполнении pa_stream_peek(): %s"
+
+#: ../src/utils/pacat.c:341
+msgid "Stream successfully created."
+msgstr "Поток успешно создан."
+
+#: ../src/utils/pacat.c:344
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "Произошла ошибка при выполнении pa_stream_get_buffer_attr(): %s"
+
+#: ../src/utils/pacat.c:348
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Показатели буфера: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:351
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Показатели буфера: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:355
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Использование отсчётов «%s», схемы каналов «%s»."
+
+# https://bugs.freedesktop.org/show_bug.cgi?id=76529
+#: ../src/utils/pacat.c:359
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr ""
+"Установлено соединение с устройством %s (номер: %u, приостановлено: %s)."
+
+#: ../src/utils/pacat.c:369
+#, c-format
+msgid "Stream error: %s"
+msgstr "Ошибка потока: %s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Поток приостановлен.%s"
+
+#: ../src/utils/pacat.c:381
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Поток возобновлён.%s"
+
+#: ../src/utils/pacat.c:389
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Недостаток данных на входе потока.%s"
+
+#: ../src/utils/pacat.c:396
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Переполнение на выходе потока.%s"
+
+#: ../src/utils/pacat.c:403
+#, c-format
+msgid "Stream started.%s"
+msgstr "Поток запущен.%s"
+
+# BUGME: word puzzle with "not". --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=76529
+#: ../src/utils/pacat.c:410
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Поток перемещён на устройство %s (%u, %sприостановлено).%s"
+
+#: ../src/utils/pacat.c:410
+msgid "not "
+msgstr "не "
+
+#: ../src/utils/pacat.c:417
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Атрибуты буфера потока изменены.%s"
+
+#: ../src/utils/pacat.c:432
+msgid "Cork request stack is empty: corking stream"
+msgstr "Поток на данный момент не был приостановлен, приостановка потока."
+
+#: ../src/utils/pacat.c:438
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+"Поток на данный момент был приостановлен только по одному запросу, "
+"возобновление потока."
+
+# BUGME: remove exclamation --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78563
+#: ../src/utils/pacat.c:442
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+"Предупреждение: получено больше запросов на возобновление передачи данных в "
+"потоке, чем запросов на приостановку потока."
+
+#: ../src/utils/pacat.c:467
+#, c-format
+msgid "Connection established.%s"
+msgstr "Соединение установлено.%s"
+
+#: ../src/utils/pacat.c:470
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "Произошла ошибка при выполнении pa_stream_new(): %s"
+
+#: ../src/utils/pacat.c:508
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "Произошла ошибка при выполнении pa_stream_connect_playback(): %s"
+
+#: ../src/utils/pacat.c:514
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Не удалось установить мониторный поток: %s"
+
+#: ../src/utils/pacat.c:518
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "Произошла ошибка при выполнении pa_stream_connect_record(): %s"
+
+#: ../src/utils/pacat.c:531 ../src/utils/pactl.c:1455
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Ошибка подключения: %s"
+
+#: ../src/utils/pacat.c:564
+msgid "Got EOF."
+msgstr "Достигнут конец файла."
+
+#: ../src/utils/pacat.c:601
+#, c-format
+msgid "write() failed: %s"
+msgstr "Произошла ошибка при выполнении write(): %s"
+
+#: ../src/utils/pacat.c:622
+msgid "Got signal, exiting."
+msgstr "Получен сигнал, выход."
+
+#: ../src/utils/pacat.c:636
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Не удалось получить задержку: %s"
+
+#: ../src/utils/pacat.c:641
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Время: %0.3f с; задержка: %0.0f мкс."
+
+#: ../src/utils/pacat.c:662
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "Произошла ошибка при выполнении pa_stream_update_timing_info(): %s"
+
+#: ../src/utils/pacat.c:672
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [параметры]\n"
+"\n"
+"  -h, --help                            Показать эту справку о параметрах\n"
+"      --version                         Показать сведения о версии\n"
+"\n"
+"  -r, --record                          Создать соединение для записи\n"
+"  -p, --playback                        Создать соединение для "
+"воспроизведения\n"
+"\n"
+"  -v, --verbose                         Увеличить подробность "
+"информационных\n"
+"                                        сообщений.\n"
+"\n"
+"  -s, --server=СЕРВЕР                   Имя сервера, к которому нужно\n"
+"                                        подключиться.\n"
+"  -d, --device=УСТРОЙСТВО               Имя аудиоприёмника или источника, к\n"
+"                                        которому нужно подключиться.\n"
+"  -n, --client-name=ИМЯ                 Имя этого клиента, которое будет\n"
+"                                        представлено серверу.\n"
+"      --stream-name=ИМЯ                 Имя этого потока, которое будет\n"
+"                                        представлено серверу.\n"
+"      --volume=ГРОМКОСТЬ                Указать исходную (линейную) "
+"громкость\n"
+"                                        в диапазоне от 0 до 65536.\n"
+"      --rate=ЧАСТОТА                    Частота дискретизации в Гц\n"
+"                                        (по умолчанию 44100).\n"
+"      --format=ФОРМАТ_ОТСЧЁТОВ          Формат отсчётов, одно из значений\n"
+"                                        s16le, s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, "
+"s32be,\n"
+"                                        s24le, s24be, s24-32le, s24-32be\n"
+"                                        (по умолчанию s16ne).\n"
+"      --channels=КАНАЛОВ                Число каналов, 1 для моно, 2 для "
+"стерео\n"
+"                                        (по умолчанию 2).\n"
+"      --channel-map=СХЕМА_КАНАЛОВ       Схема каналов для использования "
+"вместо\n"
+"                                        стандартной.\n"
+"      --fix-format                      Использовать формат отсчётов как у\n"
+"                                        аудиоприёмника/источника, к которому "
+"подключён\n"
+"                                        поток.\n"
+"      --fix-rate                        Использовать частоту дискретизации\n"
+"                                        как у аудиоприёмника/источника, к "
+"которому\n"
+"                                        подключён поток.\n"
+"      --fix-channels                    Использовать число каналов и схему\n"
+"                                        каналов как у "
+"аудиоприёмника/источника,\n"
+"                                        к которому подключён поток.\n"
+"      --no-remix                        Не менять число каналов.\n"
+"      --no-remap                        Сопоставлять каналы по их номерам,\n"
+"                                        а не по именам.\n"
+"      --latency=БАЙТЫ                   Запросить указанную задержку в "
+"байтах.\n"
+"      --process-time=БАЙТЫ              Запросить указанное время обработки\n"
+"                                        в расчёте на запрос в байтах.\n"
+"      --latency-msec=МИЛЛИСЕКУНДЫ       Запросить указанную задержку\n"
+"                                        в миллисекундах.\n"
+"      --process-time-msec=МИЛЛИСЕКУНДЫ  Запросить указанное время обработки\n"
+"                                        в расчёте на запрос в "
+"миллисекундах.\n"
+"      --property=СВОЙСТВО=ЗНАЧЕНИЕ      Установить указанное значение\n"
+"                                        для указанного свойства.\n"
+"      --raw                             Записывать или воспроизводить\n"
+"                                        необработанные данные импульсно-"
+"кодовой\n"
+"                                        модуляции (PCM).\n"
+"      --passthrough                     Передавать данные насквозь\n"
+"                                        без изменений.\n"
+"      --file-format[=ФОРМАТ_ФАЙЛА]      Записывать или воспроизводить\n"
+"                                        форматированные данные\n"
+"                                        импульсно-кодовой модуляции (PCM).\n"
+"      --list-file-formats               Показать список доступных форматов\n"
+"                                        файлов.\n"
+"      --monitor-stream=НОМЕР            Выполнять запись из входа\n"
+"                                        аудиоприёмника с указанным номером.\n"
+
+#: ../src/utils/pacat.c:810
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Скомпилировано с libpulse %s\n"
+"Скомпоновано с libpulse %s\n"
+
+#: ../src/utils/pacat.c:843 ../src/utils/pactl.c:1651
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Недопустимое имя клиента «%s»."
+
+#: ../src/utils/pacat.c:858
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Недопустимое имя потока «%s»."
+
+#: ../src/utils/pacat.c:895
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Недопустимая схема каналов «%s»."
+
+#: ../src/utils/pacat.c:924 ../src/utils/pacat.c:938
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Недопустимое значение задержки «%s»."
+
+#: ../src/utils/pacat.c:931 ../src/utils/pacat.c:945
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Недопустимое время процесса «%s»."
+
+#: ../src/utils/pacat.c:957
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Недопустимое свойство «%s»."
+
+#: ../src/utils/pacat.c:976
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Неизвестный формат файла «%s»."
+
+#: ../src/utils/pacat.c:991
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Не удалось разобрать аргумент ключа «--monitor-stream»."
+
+#: ../src/utils/pacat.c:1002
+msgid "Invalid sample specification"
+msgstr "Недопустимая спецификация отсчётов."
+
+#: ../src/utils/pacat.c:1012
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1017
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1024
+msgid "Too many arguments."
+msgstr "Слишком много аргументов."
+
+#: ../src/utils/pacat.c:1035
+msgid "Failed to generate sample specification for file."
+msgstr "Не удалось создать спецификацию отсчётов для файла."
+
+#: ../src/utils/pacat.c:1061
+msgid "Failed to open audio file."
+msgstr "Не удалось открыть аудиофайл."
+
+#: ../src/utils/pacat.c:1067
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Предупреждение: указанная спецификация отсчётов будет заменена спецификацией "
+"из файла."
+
+#: ../src/utils/pacat.c:1070 ../src/utils/pactl.c:1718
+msgid "Failed to determine sample specification from file."
+msgstr "Не удалось определить спецификацию отсчётов из файла."
+
+#: ../src/utils/pacat.c:1079
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Предупреждение: не удалось определить схему каналов из файла."
+
+#: ../src/utils/pacat.c:1090
+msgid "Channel map doesn't match sample specification"
+msgstr "Схема каналов не соответствует спецификации отсчётов."
+
+#: ../src/utils/pacat.c:1101
+msgid "Warning: failed to write channel map to file."
+msgstr "Предупреждение: не удалось записать схему каналов в файл."
+
+# %s = "recording" or "playback" --aspotashev
+#: ../src/utils/pacat.c:1116
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Открытие потока %s со спецификацией отсчётов «%s» и схемой каналов «%s»."
+
+#: ../src/utils/pacat.c:1117
+msgid "recording"
+msgstr "записи"
+
+#: ../src/utils/pacat.c:1117
+msgid "playback"
+msgstr "воспроизведения"
+
+#: ../src/utils/pacat.c:1141
+msgid "Failed to set media name."
+msgstr "Не удалось установить имя потока."
+
+#: ../src/utils/pacat.c:1148 ../src/utils/pactl.c:2068
+msgid "pa_mainloop_new() failed."
+msgstr "Произошла ошибка при выполнении pa_mainloop_new()."
+
+#: ../src/utils/pacat.c:1171
+msgid "io_new() failed."
+msgstr "Произошла ошибка при выполнении io_new()."
+
+#: ../src/utils/pacat.c:1178 ../src/utils/pactl.c:2080
+msgid "pa_context_new() failed."
+msgstr "Произошла ошибка при выполнении pa_context_new()."
+
+#: ../src/utils/pacat.c:1186 ../src/utils/pactl.c:2086
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "Произошла ошибка при выполнении pa_context_connect(): %s"
+
+#: ../src/utils/pacat.c:1192
+msgid "pa_context_rttime_new() failed."
+msgstr "Произошла ошибка при выполнении pa_context_rttime_new()."
+
+#: ../src/utils/pacat.c:1199 ../src/utils/pactl.c:2091
+msgid "pa_mainloop_run() failed."
+msgstr "Произошла ошибка при выполнении pa_mainloop_run()."
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pactl.c:1573
+msgid "NAME [ARGS ...]"
+msgstr "ИМЯ [АРГУМЕНТЫ ...]"
+
+#: ../src/utils/pacmd.c:54 ../src/utils/pacmd.c:62 ../src/utils/pactl.c:1574
+msgid "NAME|#N"
+msgstr "ИМЯ|№"
+
+#: ../src/utils/pacmd.c:55 ../src/utils/pacmd.c:65 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1578
+msgid "NAME"
+msgstr "ИМЯ"
+
+#: ../src/utils/pacmd.c:56
+msgid "NAME|#N VOLUME"
+msgstr "ИМЯ|№ ГРОМКОСТЬ"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N VOLUME"
+msgstr "№ ГРОМКОСТЬ"
+
+#: ../src/utils/pacmd.c:58 ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1576
+msgid "NAME|#N 1|0"
+msgstr "ИМЯ|№ 1|0"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N 1|0"
+msgstr "№ 1|0"
+
+#: ../src/utils/pacmd.c:60
+msgid "NAME|#N KEY=VALUE"
+msgstr "ИМЯ|№ СВОЙСТВО=ЗНАЧЕНИЕ"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N KEY=VALUE"
+msgstr "№ СВОЙСТВО=ЗНАЧЕНИЕ"
+
+#: ../src/utils/pacmd.c:63
+msgid "#N"
+msgstr "№"
+
+#: ../src/utils/pacmd.c:64
+msgid "NAME SINK|#N"
+msgstr "ИМЯ АУДИОПРИЁМНИК|№"
+
+#: ../src/utils/pacmd.c:66 ../src/utils/pacmd.c:67
+msgid "NAME FILENAME"
+msgstr "ИМЯ ИМЯ_ФАЙЛА"
+
+#: ../src/utils/pacmd.c:68
+msgid "PATHNAME"
+msgstr "ПУТЬ"
+
+#: ../src/utils/pacmd.c:69
+msgid "FILENAME SINK|#N"
+msgstr "ИМЯ_ФАЙЛА АУДИОПРИЁМНИК|№"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pactl.c:1575
+msgid "#N SINK|SOURCE"
+msgstr "№ АУДИОПРИЁМНИК|ИСТОЧНИК"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pacmd.c:79 ../src/utils/pacmd.c:80
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1577
+msgid "CARD PROFILE"
+msgstr "ПЛАТА ПРОФИЛЬ"
+
+#: ../src/utils/pacmd.c:75 ../src/utils/pactl.c:1579
+msgid "NAME|#N PORT"
+msgstr "ИМЯ|№ ПОРТ"
+
+#: ../src/utils/pacmd.c:76 ../src/utils/pactl.c:1585
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "ИМЯ_ПЛАТЫ|№_ПЛАТЫ ПОРТ ЗАДЕРЖКА"
+
+#: ../src/utils/pacmd.c:77
+msgid "TARGET"
+msgstr "КУДА"
+
+# BUGME: change "NUMERIC LEVEL" to "NUMERIC-LEVEL", because it's a single argument. --aspotashev
+# https://bugs.freedesktop.org/show_bug.cgi?id=78565
+#: ../src/utils/pacmd.c:78
+msgid "NUMERIC LEVEL"
+msgstr "ЧИСЛОВОЙ-УРОВЕНЬ"
+
+#: ../src/utils/pacmd.c:81
+msgid "FRAMES"
+msgstr "КАДРОВ"
+
+#: ../src/utils/pacmd.c:83
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Показать эту справку о параметрах\n"
+"      --version                         Показать сведения о версии\n"
+"Если команда для выполнения не указана, pacmd будет запущен в интерактивном\n"
+"режиме.\n"
+
+#: ../src/utils/pacmd.c:130
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Скомпилировано с libpulse %s\n"
+"Скомпоновано с libpulse %s\n"
+
+#: ../src/utils/pacmd.c:144
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Нет запущенного демона PulseAudio, либо он не запущен в качестве сеансового "
+"демона."
+
+#: ../src/utils/pacmd.c:149
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:166
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:174
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Не удалось завершить работу демона PulseAudio."
+
+#: ../src/utils/pacmd.c:182
+msgid "Daemon not responding."
+msgstr "Демон не отвечает."
+
+#: ../src/utils/pacmd.c:214 ../src/utils/pacmd.c:323 ../src/utils/pacmd.c:341
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:270
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:281 ../src/utils/pacmd.c:301
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:166
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Не удалось получить статистику: %s"
+
+#: ../src/utils/pactl.c:172
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Сейчас используется: %u блоков, содержащих в совокупности %s байт.\n"
+
+#: ../src/utils/pactl.c:175
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Выделено за всё время: %u блоков, содержащих в совокупности %s байт.\n"
+
+#: ../src/utils/pactl.c:178
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Размер кэша сэмплов: %s\n"
+
+#: ../src/utils/pactl.c:187
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Не удалось получить информацию о сервере: %s"
+
+# Tile Size = PA_MEMPOOL_SLOT_SIZE in src/pulsecore/memblock.c. --aspotashev
+#: ../src/utils/pactl.c:192
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Строка сервера: %s\n"
+"Версия протокола библиотеки: %u\n"
+"Версия протокола сервера: %u\n"
+"Выполняется локально: %s\n"
+"Номер клиента: %u\n"
+"Размер блока памяти: %zu\n"
+
+#: ../src/utils/pactl.c:208
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Имя пользователя: %s\n"
+"Имя хоста: %s\n"
+"Имя сервера: %s\n"
+"Версия сервера: %s\n"
+"Спецификация отсчётов по умолчанию: %s\n"
+"Схема каналов по умолчанию: %s\n"
+"Аудиоприёмник по умолчанию: %s\n"
+"Источник по умолчанию: %s\n"
+"Cookie: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:257 ../src/utils/pactl.c:902 ../src/utils/pactl.c:980
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Не удалось получить информацию об аудиоприёмнике: %s"
+
+#: ../src/utils/pactl.c:283
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Аудиоприёмник №%u\n"
+"\tСостояние: %s\n"
+"\tИмя: %s\n"
+"\tОписание: %s\n"
+"\tДрайвер: %s\n"
+"\tСпецификация отсчётов: %s\n"
+"\tСхема каналов: %s\n"
+"\tМодуль-владелец: %u\n"
+"\tЗвук выключен: %s\n"
+"\tГромкость: %s\n"
+"\t        баланс %0.2f\n"
+"\tБазовая громкость: %s\n"
+"\tМониторный источник: %s\n"
+"\tЗадержка: %0.0f мкс, настроено на %0.0f мкс\n"
+"\tФлаги: %s%s%s%s%s%s%s\n"
+"\tСвойства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:327 ../src/utils/pactl.c:433 ../src/utils/pactl.c:594
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tПорты:\n"
+
+#: ../src/utils/pactl.c:334 ../src/utils/pactl.c:440
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tАктивный порт: %s\n"
+
+#: ../src/utils/pactl.c:340 ../src/utils/pactl.c:446
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tФорматы:\n"
+
+#: ../src/utils/pactl.c:364 ../src/utils/pactl.c:922 ../src/utils/pactl.c:995
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Не удалось получить информацию об источнике: %s"
+
+#: ../src/utils/pactl.c:390
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Источник №%u\n"
+"\tСостояние: %s\n"
+"\tИмя: %s\n"
+"\tОписание: %s\n"
+"\tДрайвер: %s\n"
+"\tСпецификация отсчётов: %s\n"
+"\tСхема каналов: %s\n"
+"\tМодуль-владелец: %u\n"
+"\tЗвук выключен: %s\n"
+"\tГромкость: %s\n"
+"\t        баланс %0.2f\n"
+"\tБазовая громкость: %s\n"
+"\tЯвляется монитором аудиоприёмника: %s\n"
+"\tЗадержка: %0.0f мкс, настроено на %0.0f мкс\n"
+"\tФлаги: %s%s%s%s%s%s\n"
+"\tСвойства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:418 ../src/utils/pactl.c:488 ../src/utils/pactl.c:531
+#: ../src/utils/pactl.c:573 ../src/utils/pactl.c:671 ../src/utils/pactl.c:672
+#: ../src/utils/pactl.c:683 ../src/utils/pactl.c:741 ../src/utils/pactl.c:742
+#: ../src/utils/pactl.c:753 ../src/utils/pactl.c:804 ../src/utils/pactl.c:805
+#: ../src/utils/pactl.c:811
+msgid "n/a"
+msgstr "н/д"
+
+#: ../src/utils/pactl.c:457 ../src/utils/pactl.c:861
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Не удалось получить информацию о модуле: %s"
+
+#: ../src/utils/pactl.c:480
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Модуль №%u\n"
+"\tИмя: %s\n"
+"\tАргумент: %s\n"
+"\tСчётчик использования: %s\n"
+"\tСвойства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:499
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Не удалось получить информацию о клиенте: %s"
+
+#: ../src/utils/pactl.c:525
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Клиент №%u\n"
+"\tДрайвер: %s\n"
+"\tМодуль-владелец: %s\n"
+"\tСвойства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:542
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Не удалось получить информацию о звуковой плате: %s"
+
+#: ../src/utils/pactl.c:565
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Звуковая плата №%u\n"
+"\tИмя: %s\n"
+"\tДрайвер: %s\n"
+"\tМодуль-владелец: %s\n"
+"\tСвойства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tПрофили:\n"
+
+#: ../src/utils/pactl.c:588
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tАктивный профиль: %s\n"
+
+#: ../src/utils/pactl.c:602
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tСвойства:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:607
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tВходит в профиль(и): %s"
+
+#: ../src/utils/pactl.c:624 ../src/utils/pactl.c:942 ../src/utils/pactl.c:1010
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Не удалось получить информацию о входе аудиоприёмника: %s"
+
+#: ../src/utils/pactl.c:653
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Вход аудиоприёмника №%u\n"
+"\tДрайвер: %s\n"
+"\tМодуль-владелец: %s\n"
+"\tКлиент: %s\n"
+"\tАудиоприёмник: %u\n"
+"\tСпецификация отсчётов: %s\n"
+"\tСхема каналов: %s\n"
+"\tФормат: %s\n"
+"\tПоток данных приостановлен: %s\n"
+"\tЗвук выключен: %s\n"
+"\tГромкость: %s\n"
+"\t        баланс %0.2f\n"
+"\tЗадержка буфера: %0.0f мкс\n"
+"\tЗадержка аудиоприёмника: %0.0f мкс\n"
+"\tМетод передискретизации: %s\n"
+"\tСвойства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:694 ../src/utils/pactl.c:962 ../src/utils/pactl.c:1025
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Не удалось получить информацию о выходе источника: %s"
+
+#: ../src/utils/pactl.c:723
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Выход источника №%u\n"
+"\tДрайвер: %s\n"
+"\tМодуль-владелец: %s\n"
+"\tКлиент: %s\n"
+"\tИсточник: %u\n"
+"\tСпецификация отсчётов: %s\n"
+"\tСхема каналов: %s\n"
+"\tФормат: %s\n"
+"\tПоток данных приостановлен: %s\n"
+"\tЗвук выключен: %s\n"
+"\tГромкость: %s\n"
+"\t        баланс %0.2f\n"
+"\tЗадержка буфера: %0.0f мкс\n"
+"\tЗадержка источника: %0.0f мкс\n"
+"\tМетод передискретизации: %s\n"
+"\tСвойства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:764
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Не удалось получить информацию о сэмплах: %s"
+
+#: ../src/utils/pactl.c:791
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Сэмпл №%u\n"
+"\tИмя: %s\n"
+"\tСпецификация отсчётов: %s\n"
+"\tСхема каналов: %s\n"
+"\tГромкость: %s\n"
+"\t        баланс %0.2f\n"
+"\tДлительность: %0.1f с\n"
+"\tРазмер: %s\n"
+"\tОтложенная загрузка: %s\n"
+"\tИмя файла: %s\n"
+"\tСвойства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:819 ../src/utils/pactl.c:829
+#, c-format
+msgid "Failure: %s"
+msgstr "Произошла ошибка: %s"
+
+#: ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Не удалось выгрузить модуль: модуль «%s» не загружен."
+
+#: ../src/utils/pactl.c:886
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Не удалось задать громкость: вы установить задать громкость для %d каналов, "
+"но число поддерживаемых каналов не совпадает и равно %d\n"
+
+#: ../src/utils/pactl.c:1052
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Не удалось задать формат: недопустимый формат «%s»."
+
+#: ../src/utils/pactl.c:1095
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Не удалось загрузить сэмпл в кэш: %s"
+
+#: ../src/utils/pactl.c:1112
+msgid "Premature end of file"
+msgstr "Неожиданный конец файла."
+
+#: ../src/utils/pactl.c:1132
+msgid "new"
+msgstr "появление"
+
+#: ../src/utils/pactl.c:1135
+msgid "change"
+msgstr "изменение"
+
+#: ../src/utils/pactl.c:1138
+msgid "remove"
+msgstr "удаление"
+
+#: ../src/utils/pactl.c:1141 ../src/utils/pactl.c:1176
+msgid "unknown"
+msgstr "(неизвестно)"
+
+# [event-facility] --aspotashev
+#: ../src/utils/pactl.c:1149
+msgid "sink"
+msgstr "аудиоприёмника"
+
+# [event-facility] --aspotashev
+#: ../src/utils/pactl.c:1152
+msgid "source"
+msgstr "источника"
+
+# [event-facility] --aspotashev
+#: ../src/utils/pactl.c:1155
+msgid "sink-input"
+msgstr "входа аудиоприёмника"
+
+# [event-facility] --aspotashev
+#: ../src/utils/pactl.c:1158
+msgid "source-output"
+msgstr "выхода источника"
+
+# [event-facility] --aspotashev
+#: ../src/utils/pactl.c:1161
+msgid "module"
+msgstr "модуля"
+
+# [event-facility] --aspotashev
+#: ../src/utils/pactl.c:1164
+msgid "client"
+msgstr "клиента"
+
+# [event-facility] --aspotashev
+#: ../src/utils/pactl.c:1167
+msgid "sample-cache"
+msgstr "кэшированного сэмпла"
+
+# [event-facility] --aspotashev
+#: ../src/utils/pactl.c:1170
+msgid "server"
+msgstr "сервера"
+
+# [event-facility] --aspotashev
+#: ../src/utils/pactl.c:1173
+msgid "card"
+msgstr "платы"
+
+# "Событие [event-type] в отношении [event-facility] #N", поэтому все строки [event-facility] выше пишем в родительном падеже. --aspotashev
+#: ../src/utils/pactl.c:1182
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Событие «%s» в отношении %s №%u\n"
+
+#: ../src/utils/pactl.c:1461
+msgid "Got SIGINT, exiting."
+msgstr "Получен сигнал для остановки (SIGINT), выход."
+
+#: ../src/utils/pactl.c:1488
+msgid "Invalid volume specification"
+msgstr "Недопустимое значение громкости."
+
+#: ../src/utils/pactl.c:1511
+msgid "Volume outside permissible range.\n"
+msgstr "Указанная громкость выходит за границы разрешённого диапазона.\n"
+
+#: ../src/utils/pactl.c:1524
+msgid "Invalid number of volume specifications.\n"
+msgstr "Недопустимое количество значений громкости.\n"
+
+#: ../src/utils/pactl.c:1536
+msgid "Inconsistent volume specification.\n"
+msgstr "Несогласованные способы указания значений громкости.\n"
+
+#: ../src/utils/pactl.c:1566 ../src/utils/pactl.c:1567
+#: ../src/utils/pactl.c:1568 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1570 ../src/utils/pactl.c:1571
+#: ../src/utils/pactl.c:1572 ../src/utils/pactl.c:1573
+#: ../src/utils/pactl.c:1574 ../src/utils/pactl.c:1575
+#: ../src/utils/pactl.c:1576 ../src/utils/pactl.c:1577
+#: ../src/utils/pactl.c:1578 ../src/utils/pactl.c:1579
+#: ../src/utils/pactl.c:1580 ../src/utils/pactl.c:1581
+#: ../src/utils/pactl.c:1582 ../src/utils/pactl.c:1583
+#: ../src/utils/pactl.c:1584 ../src/utils/pactl.c:1585
+#: ../src/utils/pactl.c:1586
+msgid "[options]"
+msgstr "[параметры]"
+
+#: ../src/utils/pactl.c:1568
+msgid "[TYPE]"
+msgstr "[ТИП]"
+
+#: ../src/utils/pactl.c:1570
+msgid "FILENAME [NAME]"
+msgstr "ИМЯ_ФАЙЛА [ИМЯ]"
+
+#: ../src/utils/pactl.c:1571
+msgid "NAME [SINK]"
+msgstr "ИМЯ [АУДИОПРИЁМНИК]"
+
+#: ../src/utils/pactl.c:1580
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "ИМЯ|№ ГРОМКОСТЬ [ГРОМКОСТЬ ...]"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "№ ГРОМКОСТЬ [ГРОМКОСТЬ ...]"
+
+#: ../src/utils/pactl.c:1582
+msgid "NAME|#N 1|0|toggle"
+msgstr "ИМЯ|№ 1|0|toggle"
+
+#: ../src/utils/pactl.c:1583
+msgid "#N 1|0|toggle"
+msgstr "№ 1|0|toggle"
+
+#: ../src/utils/pactl.c:1584
+msgid "#N FORMATS"
+msgstr "№ ФОРМАТЫ"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Специальные имена @DEFAULT_SINK@, @DEFAULT_SOURCE@ и @DEFAULT_MONITOR@\n"
+"можно использовать для указания аудиоприёмника, источника и монитора,\n"
+"используемых по умолчанию.\n"
+
+#: ../src/utils/pactl.c:1590
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Показать эту справку о параметрах\n"
+"      --version                         Показать сведения о версии\n"
+"\n"
+"  -s, --server=СЕРВЕР                   Имя сервера, к которому нужно\n"
+"                                        подключиться.\n"
+"  -n, --client-name=ИМЯ                 Имя этого клиента, которое будет\n"
+"                                        представлено серверу.\n"
+
+#: ../src/utils/pactl.c:1631
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Скомпилировано с libpulse %s\n"
+"Скомпоновано с libpulse %s\n"
+
+#: ../src/utils/pactl.c:1690
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Не указывайте ничего либо укажите одно из: %s"
+
+#: ../src/utils/pactl.c:1700
+msgid "Please specify a sample file to load"
+msgstr "Необходимо указать файл, из которого будет загружен сэмпл."
+
+#: ../src/utils/pactl.c:1713
+msgid "Failed to open sound file."
+msgstr "Не удалось открыть аудиофайл."
+
+#: ../src/utils/pactl.c:1725
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Предупреждение: не удалось определить спецификацию отсчётов из файла."
+
+#: ../src/utils/pactl.c:1735
+msgid "You have to specify a sample name to play"
+msgstr "Необходимо указать имя сэмпла для воспроизведения."
+
+#: ../src/utils/pactl.c:1747
+msgid "You have to specify a sample name to remove"
+msgstr "Необходимо указать имя сэмпла для удаления."
+
+#: ../src/utils/pactl.c:1756
+msgid "You have to specify a sink input index and a sink"
+msgstr "Необходимо указать номер входа аудиоприёмника и аудиоприёмник."
+
+#: ../src/utils/pactl.c:1766
+msgid "You have to specify a source output index and a source"
+msgstr "Необходимо указать номер выхода источника и источник."
+
+#: ../src/utils/pactl.c:1781
+msgid "You have to specify a module name and arguments."
+msgstr "Необходимо указать имя модуля и аргументы."
+
+#: ../src/utils/pactl.c:1801
+msgid "You have to specify a module index or name"
+msgstr "Необходимо указать номер или имя модуля."
+
+#: ../src/utils/pactl.c:1814
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Нельзя указывать больше одного аудиоприёмника. Необходимо указать логическое "
+"значение."
+
+#: ../src/utils/pactl.c:1819 ../src/utils/pactl.c:1839
+msgid "Invalid suspend specification."
+msgstr ""
+"Недопустимое значение операции приостановки, ожидалось логическое значение."
+
+#: ../src/utils/pactl.c:1834
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Нельзя указывать больше одного источника. Необходимо указать логическое "
+"значение."
+
+#: ../src/utils/pactl.c:1851
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Необходимо указать имя или номер звуковой платы и имя профиля."
+
+#: ../src/utils/pactl.c:1862
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Необходимо указать имя или номер аудиоприёмника и имя порта."
+
+#: ../src/utils/pactl.c:1873
+msgid "You have to specify a sink name"
+msgstr "Необходимо указать имя аудиоприёмника."
+
+#: ../src/utils/pactl.c:1883
+msgid "You have to specify a source name/index and a port name"
+msgstr "Необходимо указать имя или номер источника и имя порта."
+
+#: ../src/utils/pactl.c:1894
+msgid "You have to specify a source name"
+msgstr "Необходимо указать имя источника."
+
+#: ../src/utils/pactl.c:1904
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Необходимо указать имя или номер аудиоприёмника и громкость."
+
+#: ../src/utils/pactl.c:1917
+msgid "You have to specify a source name/index and a volume"
+msgstr "Необходимо указать имя или номер источника и громкость."
+
+#: ../src/utils/pactl.c:1930
+msgid "You have to specify a sink input index and a volume"
+msgstr "Необходимо указать номер входа аудиоприёмника и громкость."
+
+#: ../src/utils/pactl.c:1935
+msgid "Invalid sink input index"
+msgstr "Недопустимый номер входа аудиоприёмника."
+
+#: ../src/utils/pactl.c:1946
+msgid "You have to specify a source output index and a volume"
+msgstr "Необходимо указать номер выхода источника и громкость."
+
+#: ../src/utils/pactl.c:1951
+msgid "Invalid source output index"
+msgstr "Недопустимый номер выхода источника."
+
+#: ../src/utils/pactl.c:1962
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr ""
+"Необходимо указать имя или номер аудиоприёмника и логическое значение "
+"выключения звука."
+
+#: ../src/utils/pactl.c:1967 ../src/utils/pactl.c:1982
+#: ../src/utils/pactl.c:2002 ../src/utils/pactl.c:2020
+msgid "Invalid mute specification"
+msgstr "Недопустимое логическое значение выключения звука."
+
+#: ../src/utils/pactl.c:1977
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr ""
+"Необходимо указать имя или номер источника и логическое значение выключения "
+"звука."
+
+#: ../src/utils/pactl.c:1992
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr ""
+"Необходимо указать номер входа аудиоприёмника и логическое значение "
+"выключения звука."
+
+#: ../src/utils/pactl.c:1997
+msgid "Invalid sink input index specification"
+msgstr "Недопустимый номер входа аудиоприёмника."
+
+#: ../src/utils/pactl.c:2010
+msgid "You have to specify a source output index and a mute boolean"
+msgstr ""
+"Необходимо указать номер выхода источника и логическое значение выключения "
+"звука."
+
+#: ../src/utils/pactl.c:2015
+msgid "Invalid source output index specification"
+msgstr "Недопустимый номер выхода источника."
+
+#: ../src/utils/pactl.c:2032
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Необходимо указать номер аудиоприёмника и разделённый запятыми список "
+"поддерживаемых форматов."
+
+#: ../src/utils/pactl.c:2044
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr "Необходимо указать имя или номер звуковой платы, имя порта и задержку."
+
+#: ../src/utils/pactl.c:2051
+msgid "Could not parse latency offset"
+msgstr "Недопустимое значение задержки."
+
+#: ../src/utils/pactl.c:2063
+msgid "No valid command specified."
+msgstr "Имя команды не указано или не распознано."
+
+#: ../src/utils/pasuspender.c:81
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:94
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:113
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Не удалось возобновить: %s\n"
+
+#: ../src/utils/pasuspender.c:147
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Не удалось приостановить: %s\n"
+
+#: ../src/utils/pasuspender.c:172
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr ""
+"Предупреждение: аудиосервер не является локальным, не приостанавливается.\n"
+
+#: ../src/utils/pasuspender.c:185
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Ошибка соединения: %s\n"
+
+#: ../src/utils/pasuspender.c:203
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Получен сигнал для остановки (SIGINT), выход.\n"
+
+#: ../src/utils/pasuspender.c:221
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "Предупреждение: дочерний процесс завершён по сигналу %u\n"
+
+#: ../src/utils/pasuspender.c:230
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [параметры] ... \n"
+"\n"
+"  -h, --help                            Показать эту справку о параметрах\n"
+"      --version                         Показать сведения о версии\n"
+"  -s, --server=СЕРВЕР                   Имя сервера, к которому нужно\n"
+"                                        подключиться.\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:268
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Скомпилировано с libpulse %s\n"
+"Скомпоновано с libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:297
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "Произошла ошибка при выполнении pa_mainloop_new().\n"
+
+#: ../src/utils/pasuspender.c:310
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "Произошла ошибка при выполнении pa_context_new().\n"
+
+#: ../src/utils/pasuspender.c:322
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "Произошла ошибка при выполнении pa_mainloop_run().\n"
+
+#: ../src/utils/pax11publish.c:60
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D дисплей] [-S сервер] [-O аудиоприёмник] [-I источник] [-c файл]  [-d|-"
+"e|-i|-r]\n"
+"\n"
+" -d    Показать текущие данные PulseAudio, прикреплённые к дисплею X11 "
+"(действие по умолчанию)\n"
+" -e    Экспортировать локальные данные PulseAudio в дисплей X11\n"
+" -i    Импортировать данные PulseAudio из дисплея X11 в локальные переменные "
+"окружения и в файл cookie.\n"
+" -r    Удалить данные PulseAudio из дисплея X11\n"
+
+#: ../src/utils/pax11publish.c:93
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Не удалось разобрать командную строку.\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Server: %s\n"
+msgstr "Сервер: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Source: %s\n"
+msgstr "Источник: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Аудиоприёмник: %s\n"
+
+#: ../src/utils/pax11publish.c:118
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:136
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Не удалось разобрать данные cookie.\n"
+
+#: ../src/utils/pax11publish.c:141
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Не удалось сохранить данные cookie.\n"
+
+#: ../src/utils/pax11publish.c:156
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "Не удалось загрузить файл конфигурации клиента.\n"
+
+#: ../src/utils/pax11publish.c:161
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "Не удалось прочитать данные конфигурации окружения.\n"
+
+#: ../src/utils/pax11publish.c:178
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Не удалось получить полное имя домена (FQDN).\n"
+
+#: ../src/utils/pax11publish.c:198
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Не удалось загрузить данные cookie.\n"
+
+#: ../src/utils/pax11publish.c:216
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Не реализовано.\n"
+
+#~ msgid "PulseAudio Sound System KDE Routing Policy"
+#~ msgstr "Звуковая система PulseAudio с маршрутизацией для KDE"
+
+#~ msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+#~ msgstr "Запуск звуковой системы PulseAudio с маршрутизацией для KDE"
+
+#~ msgid "Failed to open configuration file '%s': %s"
+#~ msgstr "Не удалось открыть файл конфигурации «%s»: %s"
+
+#, fuzzy
+#~ msgid "Successfully dropped root privileges."
+#~ msgstr "Успешное удаление привилегий администратора."
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit не поддерживается на этой платформе."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() не удалось"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Выход источника #%u\n"
+#~ "\tДрайвер: %s\n"
+#~ "\tРодительский модуль: %s\n"
+#~ "\tКлиент: %s\n"
+#~ "\tИсточник: %u\n"
+#~ "\tСпецификация сэмплов: %s\n"
+#~ "\tСхема каналов: %s\n"
+#~ "\tЗадержка буфера: %0.0f мкс\n"
+#~ "\tЗадержка источника: %0.0f мкс\n"
+#~ "\tМетод ресэмплирования: %s\n"
+#~ "\tСвойства:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Цифровой объёмный 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Сабвуфер"
diff --git a/po/sk.po b/po/sk.po
new file mode 100644 (file)
index 0000000..730c234
--- /dev/null
+++ b/po/sk.po
@@ -0,0 +1,2818 @@
+# Slovak translation for PulseAudio.
+# Copyright (C) 2014 PulseAudio's COPYRIGHT HOLDER
+# This file is distributed under the same license as the PulseAudio package.
+# Dušan Kazik <prescott66@gmail.com>, 2014, 2015.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PulseAudio master\n"
+"Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
+"product=PulseAudio&keywords=I18N+L10N&component=misc\n"
+"POT-Creation-Date: 2016-06-22 13:54+0000\n"
+"PO-Revision-Date: 2016-07-06 17:20+0200\n"
+"Last-Translator: Dušan Kazik <prescott66@gmail.com>\n"
+"Language-Team: Slovak <gnome-sk-list@gnome.org>\n"
+"Language: sk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
+"X-Generator: Poedit 1.8.7.1\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Neplatná metóda prevzorkovania „%s“."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "Voľba --system očakáva booleovský parameter"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr ""
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr "Voľba --enable-memfd očakáva booleovský parameter"
+
+#: ../src/daemon/daemon-conf.c:260
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Neplatný cieľ záznamu „%s“."
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Neplatná úroveň záznamu „%s“."
+
+#: ../src/daemon/daemon-conf.c:290
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Neplatný spôsob prevzorkovania „%s“."
+
+#: ../src/daemon/daemon-conf.c:312
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Neplatný rlimit „%s“."
+
+#: ../src/daemon/daemon-conf.c:332
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Neplatný formát vzoriek „%s“."
+
+#: ../src/daemon/daemon-conf.c:349 ../src/daemon/daemon-conf.c:366
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Neplatná frekvencia vzoriek „%s“."
+
+#: ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr ""
+
+#: ../src/daemon/daemon-conf.c:406
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Neplatná mapa kanálov „%s“."
+
+#: ../src/daemon/daemon-conf.c:423
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Neplatný počet fragmentov „%s“."
+
+#: ../src/daemon/daemon-conf.c:440
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Neplatná veľkosť fragmentu „%s“."
+
+#: ../src/daemon/daemon-conf.c:457
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Neplatná úroveň nice „%s“."
+
+#: ../src/daemon/daemon-conf.c:500
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Neplatný typ servera „%s“."
+
+#: ../src/daemon/daemon-conf.c:615
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Zlyhalo otvorenie konfiguračného súboru: %s"
+
+#: ../src/daemon/daemon-conf.c:631
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Určená predvolená mapa kanálov obsahuje odlišný počet kanálov ako je určený "
+"predvolený počet kanálov."
+
+#: ../src/daemon/daemon-conf.c:718
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Čítať z konfiguračného súboru: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Názov: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Nie sú dostupné žiadne informácie o module\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Verzia: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Popis: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Využitie: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Načítané raz: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "UPOZORNENIE NA ZASTARANIE: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Cesta: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Zlyhalo otvorenie modulu %s: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr ""
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr ""
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr ""
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Zlyhalo nájdenie používateľa „%s“."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Zlyhalo nájdenie skupiny „%s“."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID používateľa „%s“ a skupiny „%s“ sa nezhodujú."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Domovský adresár používateľa „%s“ nie je „%s“. Ignoruje sa."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Zlyhalo vytvorenie „%s“: %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Zlyhala zmena zoznamu skupín: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Zlyhalo zmenenie GID: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Zlyhalo zmenenie UID: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr ""
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Zlyhalo analyzovanie príkazového riadku."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Zlyhalo zabitie démona: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Vyžadujú sa privilégiá správcu"
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "Voľba --start nie je podporovaná pre systémové inštancie."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr ""
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr ""
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr ""
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr ""
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:568
+#, c-format
+msgid "read() failed: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Zlyhalo spustenie démona."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr ""
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Zlyhalo získanie identifikátora stroja"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+
+#: ../src/daemon/main.c:991
+msgid "pa_pid_file_create() failed."
+msgstr ""
+
+#: ../src/daemon/main.c:1023
+msgid "pa_core_new() failed."
+msgstr ""
+
+#: ../src/daemon/main.c:1091
+msgid "Failed to initialize daemon."
+msgstr "Zlyhala inicializácia démona."
+
+#: ../src/daemon/main.c:1096
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Zvukový systém PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Spustenie zvukového systému PulseAudio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Vstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Vstup dokovacej stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Mikrofón dokovacej stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Vstupná linka dokovacej stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Vstupná linka"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1710
+msgid "Microphone"
+msgstr "Mikrofón"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Predný mikrofón"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Zadný mikrofón"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Externý mikrofón"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Vstavaný mikrofón"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Rádio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Video"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Automatické ovládanie zosilnenia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Bez automatického ovládania zosilnenia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Zosilnenie"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Bez zosilnenia"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Zosilňovač"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Bez zosilňovača"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Zosilnenie basov"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Bez zosilnenia basov"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1717
+msgid "Speaker"
+msgstr "Reproduktor"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Slúchadlá"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Analógový vstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Mikrofón doku"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Mikrofón headsetu"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Analógový výstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "Kanál LFE na oddelenom mono výstupe"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Výstupná linka"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Analógový mono výstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Reproduktory"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Digitálny výstup (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Digitálny vstup (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digitálny prechod (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Viackanálový vstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Viackanálový výstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Analógový mono"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Analógový stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Viackanálový"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Analógový priestorový 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Analógový priestorový 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Analógový priestorový 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Analógový priestorový 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Analógový priestorový 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Analógový priestorový 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Analógový priestorový 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Analógový priestorový 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Analógový priestorový 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Analógový priestorový 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Analógový priestorový 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Digitálne stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digitálny prechod  (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digitálny priestorový 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digitálny priestorový 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Digitálny priestorový 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Digitálny stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digitálny priestorový 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4151
+msgid "Analog Mono Duplex"
+msgstr "Obojsmerný analógový mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Stereo Duplex"
+msgstr "Obojsmerný analógový stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Obojsmerný digitálny stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Multichannel Duplex"
+msgstr "Obojsmerný viackanálový"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2295
+#: ../src/modules/bluetooth/module-bluez5-device.c:1941
+msgid "Off"
+msgstr "Vypnuté"
+
+#: ../src/modules/alsa/alsa-mixer.c:4254
+#, c-format
+msgid "%s Output"
+msgstr "%s výstup"
+
+#: ../src/modules/alsa/alsa-mixer.c:4262
+#, c-format
+msgid "%s Input"
+msgstr "%s vstup"
+
+#: ../src/modules/alsa/alsa-sink.c:570
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-sink.c:747
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-util.c:1166 ../src/modules/alsa/alsa-util.c:1241
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-util.c:1216
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-util.c:1257
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+
+#: ../src/modules/alsa/alsa-util.c:1300
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2089
+#: ../src/modules/bluetooth/module-bluez5-device.c:1700
+msgid "Headset"
+msgstr "Headset"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1705
+msgid "Handsfree"
+msgstr "Handsfree"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1723
+msgid "Headphone"
+msgstr "Slúchadlo"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1728
+msgid "Portable"
+msgstr "Prenosné"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1733
+msgid "Car"
+msgstr "Automobil"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1738
+msgid "HiFi"
+msgstr "HiFi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1743
+msgid "Phone"
+msgstr "Telefón"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2137
+#: ../src/modules/bluetooth/module-bluez5-device.c:1695
+#: ../src/modules/bluetooth/module-bluez5-device.c:1711
+#: ../src/modules/bluetooth/module-bluez5-device.c:1749
+msgid "Bluetooth Output"
+msgstr "Výstup cez Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2140
+#: ../src/modules/bluetooth/module-bluez5-device.c:1694
+#: ../src/modules/bluetooth/module-bluez5-device.c:1716
+#: ../src/modules/bluetooth/module-bluez5-device.c:1722
+#: ../src/modules/bluetooth/module-bluez5-device.c:1748
+msgid "Bluetooth Input"
+msgstr "Vstup cez Bluetooth"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2176
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Hi-Fi prehrávanie (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2187
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Hi-Fi zaznamenávanie (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2198
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2210
+msgid "Handsfree Gateway"
+msgstr "Brána pre handsfree"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1786
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1797
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1808
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr ""
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1820
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Brána zvuku pre handsfree (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "Zapnutý"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Fiktívny výstup"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Univerzálny ekvalizér"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-filter-apply.c:47
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr ""
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr ""
+
+#: ../src/modules/module-null-sink.c:280
+msgid "Null Output"
+msgstr "Prázdny výstup"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Výstupné zariadenia"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Vstupné zariadenia"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Zvuk na @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Tunel pre %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:517
+#: ../src/modules/module-tunnel-source-new.c:516
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Tunel do %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr ""
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/"
+"left_hrir.wav "
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "Zvukový server PulseAudio"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Predný stredový"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Predný ľavý"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Predný pravý"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Zadný stredový"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Zadný ľavý"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Zadný pravý"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Basový reproduktor"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Ľavá strana"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Pravá strana"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Aux 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Aux 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Aux 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Aux 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Aux 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Aux 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Aux 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Aux 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Aux 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Aux 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Aux 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Aux 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Aux 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Aux 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Aux 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Aux 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Aux 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Aux 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Aux 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Aux 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Aux 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Aux 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Aux 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Aux 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Aux 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Aux 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Aux 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Aux 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Aux 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Aux 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Aux 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Aux 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Horný stredový"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Horný predný stredový"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Horný predný ľavý"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Horný predný pravý"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Horný zadný stredový"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Horný zadný ľavý"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Horný zadný pravý"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:175 ../src/pulse/volume.c:294
+#: ../src/pulse/volume.c:320 ../src/pulse/volume.c:340
+#: ../src/pulse/volume.c:372 ../src/pulse/volume.c:412
+#: ../src/pulse/volume.c:431
+msgid "(invalid)"
+msgstr "(neplatné)"
+
+#: ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Priestorový 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Priestorový 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Priestorový 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Priestorový 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Priestorový 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr ""
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr ""
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr ""
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Prijatá správa pre neznáme rozšírenie „%s“"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "vstup"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "výstup"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "obojsmerný"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "neplatný"
+
+#: ../src/pulsecore/core-util.c:1836
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "áno"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "nie"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr ""
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Zlyhalo otvorenie cieľového súboru „%s“."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Neplatný cieľ záznamu."
+
+#: ../src/pulsecore/sink.c:3459
+msgid "Built-in Audio"
+msgstr "Vstavaný zvuk"
+
+#: ../src/pulsecore/sink.c:3464
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Prístup zamietnutý"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Neznámy príkaz"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Neplatný parameter"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "Entita existuje"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Entita neexistuje"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Pripojenie odmietnuté"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Chyba protokolu"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Vypršanie času"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Žiadny kľúč overenia totožnosti"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Interná chyba"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Pripojenie prerušené"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Entita zabitá"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Neplatný server"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Zlyhala inicializácia modulu"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Zlý stav"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Žiadne údaje"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Nekompatibilná verzia protokolu"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Príliš veľké"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Nepodporované"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Neznámy kód chyby"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Rozšírenie neexistuje"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Zastaraná funkčnosť"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Chýba implementácia"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr ""
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Vstupno/výstupná chyba"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Zariadenie alebo prostriedok je zaneprázdnený"
+
+#: ../src/pulse/sample.c:177
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uk %uHz"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:117
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:122
+msgid "Playback stream drained."
+msgstr ""
+
+#: ../src/utils/pacat.c:133
+msgid "Draining connection to server."
+msgstr ""
+
+#: ../src/utils/pacat.c:146
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:169
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:210
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:260 ../src/utils/pacat.c:290
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:340
+msgid "Stream successfully created."
+msgstr ""
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:347
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr ""
+
+#: ../src/utils/pacat.c:350
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr ""
+
+#: ../src/utils/pacat.c:354
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr ""
+
+#: ../src/utils/pacat.c:358
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr ""
+
+#: ../src/utils/pacat.c:368
+#, c-format
+msgid "Stream error: %s"
+msgstr "Chyba prúdu: %s"
+
+#: ../src/utils/pacat.c:378
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:388
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Podtečenie prúdu.%s"
+
+#: ../src/utils/pacat.c:395
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Pretečenie prúdu.%s"
+
+#: ../src/utils/pacat.c:402
+#, c-format
+msgid "Stream started.%s"
+msgstr "Spustenie prúdu.%s"
+
+#: ../src/utils/pacat.c:409
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:409
+msgid "not "
+msgstr ""
+
+#: ../src/utils/pacat.c:416
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr ""
+
+#: ../src/utils/pacat.c:431
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:437
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:441
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr ""
+
+#: ../src/utils/pacat.c:466
+#, c-format
+msgid "Connection established.%s"
+msgstr "Spojenie nadviazané.%s"
+
+#: ../src/utils/pacat.c:469
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:507
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:513
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:517
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:530 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Zlyhanie pripojenia: %s"
+
+#: ../src/utils/pacat.c:563
+msgid "Got EOF."
+msgstr "Získaný koniec súboru."
+
+#: ../src/utils/pacat.c:600
+#, c-format
+msgid "write() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:621
+msgid "Got signal, exiting."
+msgstr "Prijatý signál, ukončuje sa."
+
+#: ../src/utils/pacat.c:635
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Zlyhalo získanie oneskorenia: %s"
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Čas: %0.3f sek; Oneskorenie: %0.0f usek."
+
+#: ../src/utils/pacat.c:661
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:671
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+
+#: ../src/utils/pacat.c:788
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr ""
+
+#: ../src/utils/pacat.c:792
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr ""
+
+#: ../src/utils/pacat.c:796
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+
+#: ../src/utils/pacat.c:800
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+
+#: ../src/utils/pacat.c:814
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+
+#: ../src/utils/pacat.c:847 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Neplatný názov klienta „%s“"
+
+#: ../src/utils/pacat.c:862
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Neplatný názov prúdu „%s“"
+
+#: ../src/utils/pacat.c:899
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Neplatná mapa kanálov „%s“"
+
+#: ../src/utils/pacat.c:928 ../src/utils/pacat.c:942
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Neplatná špecifikácia oneskorenia „%s“"
+
+#: ../src/utils/pacat.c:935 ../src/utils/pacat.c:949
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Neplatná špecifikácia času spracovania „%s“"
+
+#: ../src/utils/pacat.c:961
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Neplatná vlastnosť „%s“"
+
+#: ../src/utils/pacat.c:980
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Neznámy formát súboru %s."
+
+#: ../src/utils/pacat.c:995
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Zlyhala analyzovanie argumentu pre --monitor-stream"
+
+#: ../src/utils/pacat.c:1006
+msgid "Invalid sample specification"
+msgstr "Neplatná špecifikácia vzoriek"
+
+#: ../src/utils/pacat.c:1016
+#, c-format
+msgid "open(): %s"
+msgstr "otvorenie(): %s"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "dup2(): %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:1028
+msgid "Too many arguments."
+msgstr "Príliš veľa parametrov."
+
+#: ../src/utils/pacat.c:1039
+msgid "Failed to generate sample specification for file."
+msgstr "Zlyhalo generovanie špecifikácie vzoriek pre súbor."
+
+#: ../src/utils/pacat.c:1065
+msgid "Failed to open audio file."
+msgstr "Zlyhalo otvorenie zvukového súboru."
+
+#: ../src/utils/pacat.c:1071
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Upozornenie: špecifikovaná špecifikácia vzoriek bude prepísaná špecifikáciou "
+"zo súboru."
+
+#: ../src/utils/pacat.c:1074 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Zlyhalo rozpoznanie špecifikácie vzoriek zo súboru."
+
+#: ../src/utils/pacat.c:1083
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Upozornenie: Zlyhalo rozpoznanie mapy kanálov zo súboru."
+
+#: ../src/utils/pacat.c:1094
+msgid "Channel map doesn't match sample specification"
+msgstr "Mapa kanálov sa nezhoduje so špecifikáciou vzoriek"
+
+#: ../src/utils/pacat.c:1105
+msgid "Warning: failed to write channel map to file."
+msgstr "Upozornenie: zlyhalo zapísanie mapy kanálov do súboru."
+
+#: ../src/utils/pacat.c:1120
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "Otvára sa prúd %s so špecifikáciou vzoriek „%s“ a mapou kanálov „%s“."
+
+#: ../src/utils/pacat.c:1121
+msgid "recording"
+msgstr "zaznamenávanie"
+
+#: ../src/utils/pacat.c:1121
+msgid "playback"
+msgstr "prehrávanie"
+
+#: ../src/utils/pacat.c:1145
+msgid "Failed to set media name."
+msgstr "Zlyhalo nastavenie názvu média."
+
+#: ../src/utils/pacat.c:1152 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr ""
+
+#: ../src/utils/pacat.c:1175
+msgid "io_new() failed."
+msgstr ""
+
+#: ../src/utils/pacat.c:1182 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr ""
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr ""
+
+#: ../src/utils/pacat.c:1196
+msgid "pa_context_rttime_new() failed."
+msgstr ""
+
+#: ../src/utils/pacat.c:1203 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr ""
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "NÁZOV [ARGUMENTY ...]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "NÁZOV|Č."
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "NÁZOV"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "NÁZOV|Č. HLASITOSŤ"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "Č. HLASITOSŤ"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "BÁZOV|Č. 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "Č. 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "NÁZOV|Č. KĽÚČ=HODNOTA"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "Č. KĽÚČ=HODNOTA"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "Č."
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr ""
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "NÁZOV NÁZOV-SÚBORU"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "NÁZOV-CESTY"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr ""
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "PROFIL KARTY"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "NÁZOV|Č. PORTU"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "NÁZOV-KARTY|KARTA-Č. PORT POSUNUTIE"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "CIEĽ"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "ČÍSLO-ÚROVEŇ"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "SNÍMKY"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Zobrazí tohoto pomocníka\n"
+"      --version                         Zobrazí verziu\n"
+"Keď nie je zadaný žiadny príkaz, bude program pacmd spustený v interaktívnom "
+"režime.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Nie je spustený žiadny démon PulseAudio, alebo nie je spustený ako démon "
+"relácie."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr ""
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "pripojenie(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Zlyhalo zabitie démona PulseAudio."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Démon neodpovedá."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "zapísanie(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr ""
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "čítanie(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Zlyhalo získanie štatistiky: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Aktuálne sa používa: %u blokov obsahujúcich celkovo %s bajtov.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Veľkosť vyrovnávacej pamäte vzoriek: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Zlyhalo získanie informácií o serveri: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPorty:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tAktívny port: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tFormáty:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Zlyhalo získanie informácií o zdroji: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "nedostupné"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Zlyhalo získavanie informácií o module: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modul #%u\n"
+"\tNázov: %s\n"
+"\tArgument: %s\n"
+"\tPočítadlo použití: %s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Zlyhalo získanie informácií o klientovi: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Klient #%u\n"
+"\tOvládač: %s\n"
+"\tVlastnícky modul: %s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Zlyhalo získavanie informácií o karte: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Karta #%u\n"
+"\tNázov: %s\n"
+"\tOvládač: %s\n"
+"\tVlastnícky modul: %s\n"
+"\tVlastnosti:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfily:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\\Aktívny profil: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\\Vlastnosti:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tČasť profilu(ov): %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Zlyhalo získanie informácií o vzorke: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Zlyhanie: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr ""
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Zlyhalo nastavenie hlasitosti: Pokúsili ste sa nastaviť hlasitosti pre %d "
+"kanály/ov, pričom podporovaných je kanálov je %d\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Zlyhalo nastavenie formátu: neplatný reťazec formátu %s"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Zlyhalo nahratie vzorku: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Predčasný koniec súboru"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "nový"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "zmeniť"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "odstrániť"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "neznámy"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "zdroj"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "modul"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "klient"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "server"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "karta"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Udalosť „%s“ na %s #%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "Získaný príkaz SIGINT. Ukončuje sa."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Neplatná špecifikácia hlasitosti"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Hlasitosť mimo povoleného rozsahu.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "Neplatný počet špecifikácií hlasitosti.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "Nekonzistentná špecifikácia hlasitosti.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[voľby]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[TYP]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "NÁZOV-SÚBORU [NÁZOV]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "NÁZOV|Č. HLASITOSŤ [HLASITOSŤ ...]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "Č. HLASITOSŤ [HLASITOSŤ ...]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "NÁZOV|Č. 1|0|prepínač"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "Č. 1|0|prepínač"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "Č. FORMÁTY"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Zobrazí tohoto pomocníka\n"
+"      --version                         Zobrazí verziu\n"
+"\n"
+"  -s, --server=SERVER                   Názov servera, na ktorý sa pripojiť\n"
+"  -n, --client-name=NÁZOV                Ako nazvať tohoto klienta na "
+"serveri\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr ""
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Zlyhalo otvorenie zvukového súboru."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Musíte určiť názov vzorky, ktorá sa má prehrať"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Musíte určiť názov vzorky, ktorá sa má odstrániť"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "Musíte určiť číslo výstupu zdroja a zdroj"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Musíte určiť názov modulu a argumenty."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr ""
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Neplatná špecifikácia uspania."
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr ""
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr ""
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr ""
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr ""
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Musíte určiť názov zdroja"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr ""
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Musíte určiť názov/číslo zdroja a hlasitosť"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr ""
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr ""
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "Musíte určiť číslo výstupu zdroja a hlasitosť"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Neplatné číslo výstupu zdroja"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Neplatná špecifikácia stlmenia hlasitosti"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr ""
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Neplatná špecifikácia čísla výstupu zdroja"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr "Musíte určiť názov/číslo karty, názov portu a posunutie oneskorenia"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Nepodarilo sa analyzovať posun oneskorenia"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Nebol učený žiadny platný príkaz."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Zlyhanie pri pokračovaní: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Zlyhanie pri uspávaní: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "UPOZORNENIE: Zvukový server nie je miestny. Neuspáva sa.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Zlyhanie pripojenia: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Získaný signál SIGINT, ukončuje sa.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "UPOZORNENIE: Podradený proces bol ukončený signálom %u\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [voľby] ... \n"
+"\n"
+"  -h, --help                            Zobrazí tohto pomocníka\n"
+"      --version                         Zobrazí verziu\n"
+"  -s, --server=SERVER                   Názov servera, na ktorý sa pripojiť\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr ""
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Zlyhalo analyzovanie príkazového riadku.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Server: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Zdroj: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr ""
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Zlyhalo analyzovanie údajov cookie\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Zlyhalo uloženie údajov cookie\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Zlyhalo získanie FQDN.\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Zlyhalo načítanie údajov súboru cookie\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Zatiaľ nie je implementované.\n"
+
+#~ msgid "%s %s\n"
+#~ msgstr "%s %s\n"
diff --git a/po/sr.po b/po/sr.po
new file mode 100644 (file)
index 0000000..c93976f
--- /dev/null
+++ b/po/sr.po
@@ -0,0 +1,3047 @@
+# Serbian translations for pulseaudio
+# Copyright (C) 2006 Lennart Poettering
+# This file is distributed under the same license as the pulseaudio package.
+# Igor Miletic (Игор Милетић) <grejigl-gnomeprevod@yahoo.ca>, 2009.
+# Miloš Komarčević <kmilos@gmail.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:55+0000\n"
+"Last-Translator: Miloš Komarčević <kmilos@gmail.com>\n"
+"Language-Team: Serbian (sr) <fedora-trans-sr@redhat.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() је вратио вредност која је необично велика: %lu бајтова (%lu "
+"ms).\n"
+"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
+"овај проблем ALSA програмерима."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() је вратио вредност која је необично велика: %li бајтова (%s"
+"%lu ms).\n"
+"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
+"овај проблем ALSA програмерима."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() је вратио вредност која је необично велика: %lu бајтова (%lu "
+"ms).\n"
+"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
+"овај проблем ALSA програмерима."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin()  је вратио вредност која је необично велика: %lu "
+"бајтова (%lu ms).\n"
+"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
+"овај проблем ALSA програмерима."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Увек одржава барем један сливник оптерећеним чак и када је празан"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Лажан излаз"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "Виртуелни LADSPA сливник"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<име сливника> sink_properties=<својства сливника> master=<име "
+"сливника за филтрирање> format=<формат узорка> rate=<учестаност "
+"дискретизације> channels=<број канала> channel_map=<мапа канала> plugin=<име "
+"ladspa додатка> label=<ознака ladspa додатка> control=<списак улазних "
+"контролних вредности раздвојених зарезом>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "Узорак NULL сливника"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Празан излаз"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "Унутрашњи звук"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "Модем"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Неуспешна претрага за оригиналним lt_dlopen учитавачем."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "Неуспешно смештање новог dl учитавача."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "Неуспешно додавање „повежи одмах“ учитавача."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "Добих сигнал %s."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "Напуштам."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Не могу наћи корисника „%s“."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Не могу наћи групу „%s“."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Нађени су корисник „%s“ (UID %lu) и група „%s“ (GID %lu)."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID корисника „%s“ се не поклапа са групом „%s“."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Лични директоријум корисника „%s“ није „%s“, занемарујем."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Неуспешно прављење „%s“: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Неуспешна промена групног списка: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Неуспешна промена GID-а: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Неуспешна промена UID-а: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "Успешно одбачена root овлашћења."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "Режим за читав систем није подржан на овој платформи."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) није успело: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "Неуспешно тумачење командне линије."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "Демон није покренут"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "Демон је покренут са PID-ом %u"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Неуспешно убијање демона: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Није намеравано да се овај програм покреће из root налога (осим у случају "
+"када је --system наведено)"
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Потребна су root овлашћења."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start није подржано за системске примерке."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "Покренуто у системском режиму, али --disallow-exit није постављено!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"Покренуто у системском режиму, али --disallow-module-loading није постављено!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "Покренуто у системском режиму, присилно онемогућујем SHM режим!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"Покренуто у системском режиму, присилно онемогућујем гашење после одређеног "
+"времена мировања!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "Неуспешно проналажење стандардног улаза/излаза."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "Неуспешно пуштање података кроз цев: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "Неуспела функција fork(): %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "Неуспела функција read(): %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "Неуспешно покретање демона."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "Демон успешно покренут."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "Неуспела функција read(): %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Ово је PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Домаћин компајлирања: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "CFLAGS компајлирања: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "Покренут на домаћину: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "Нашао %u процесор(а)"
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "Величина странице је %lu бајтова"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Компајлирано са подршком за Valgrind: да"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Компајлирано са подршком за Valgrind: не"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Покренут у Valgrind режиму: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "Покренут на домаћину: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Оптимизована изградња: да"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "Оптимизована изградња: не"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG дефинисан, сва обавештења искључена."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH дефинисан, само обавештења брзе путање искључена."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "Сва обавештења омогућена."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "Неуспешно добављање ИБ машине"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "ИБ машине је %s."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "ИБ сесије је %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Користи се %s извршни директоријум."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "Користи се %s директоријум стања."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "Користи се %s директоријум модула."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Покренуто у системском режиму: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"У реду, значи извршавате PA у системском режиму. Примите к знању да то "
+"вероватно не би требало да радите.\n"
+"Ако то свеједно чините онда је ваша кривица ако ствари не раде онако како се "
+"очекује.\n"
+"Прочитајте http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/"
+"User/WhatIsWrongWithSystemWide/ ради објашњења зашто је системски режим "
+"обично лоша идеја."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "Неуспела функција pa_pid_file_create()."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "Доступни су нови бројачи високе резолуције! Пријатно!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Ваше језгро није добро подешено за pulseaudio! Препоручује Вам се да "
+"користите Linux језгро са омогућеним бројачима високе резолуције."
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "Неуспела функција pa_core_new()."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "Неуспешно покретање демона."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Демон је покренут без иједног учитаног модула, одбија да ради."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "Покретање демона успешно."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "Покренуто гашење демона."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "Рад демона је прекинут."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [опције]\n"
+"\n"
+"НАРЕДБЕ:\n"
+"  -h, --help                            Прикажи ову помоћ\n"
+"      --version                         Прикажи верзију\n"
+"      --dump-conf                       Испиши подразумевана подешавања\n"
+"      --dump-modules                    Испиши списак доступних модула\n"
+"      --dump-resample-methods           Испиши доступне вредности "
+"дискретизације\n"
+"      --cleanup-shm                     Очисти бајате делове дељене "
+"меморије\n"
+"      --start                           Покрени демон ако већ није покренут\n"
+"  -k  --kill                            Убиј покренути демон\n"
+"      --check                           Провери постојање покренутог демона "
+"(враћа само излазни ко̑д)\n"
+"\n"
+"ОПЦИЈЕ:\n"
+"      --system[=логичка вредност]       Покрени као системски примерак\n"
+"  -D, --daemonize[=лог. вредност]       Покрени у позадини\n"
+"      --fail[=лог. вредност]            Изађи после неуспешног покретања\n"
+"      --high-priority[=лог. вредност]   Покушај поставити високоприоритетно "
+"распоређивање\n"
+"                                        (доступно само root-у, или преко "
+"SUID-а или\n"
+"                                        са повишеним RLIMIT_NICE нивоом)\n"
+"      --realtime[=лог. вредност]        Покушај омогућити стварновременско "
+"распоређивање\n"
+"                                        (доступно само root-у, или преко "
+"SUID-а или\n"
+"                                        са повишеним RLIMIT_RTPRIO нивоом)\n"
+"      --disallow-module-loading[=лог.]  Не дозвољавај учитавање/уклањање "
+"модула\n"
+"                                        на кориснички захтев после "
+"покретања\n"
+"      --disallow-exit[=лог. вредност]   Не дозвољавај излаз на кориснички "
+"захтев\n"
+"      --exit-idle-time=СЕКУНДИ          Прекини рад демона после мировања\n"
+"                                        од оволико секунди\n"
+"      --module-idle-time=СЕКУНДИ        Уклони самоучитане модуле после "
+"мировања\n"
+"                                        од оволико секунди\n"
+"      --scache-idle-time=СЕКУНДИ        Уклони самоучитане примерке после "
+"мировања\n"
+"                                        од оволико секунди\n"
+"      --log-level[=НИВО]                Повећај или постави ниво опширности\n"
+"  -v                                    Повећај ниво опширности\n"
+"      --log-target={auto,syslog,stderr} Наведи циљни дневник\n"
+"      --log-meta[=лог. вредност]        Укључи место у ко̑ду у порукама "
+"дневника\n"
+"      --log-time[=лог. вредност]        Укључи време у порукама дневника\n"
+"      --log-backtrace=FRAMES            Укључи трагове у порукама дневника\n"
+"  -p, --dl-search-path=ПУТАЊА           Постави путању претраге за динамички "
+"дељене\n"
+"                                        објекте (додаци)\n"
+"      --resample-method=НАЧИН           Користи наведени начин "
+"дискретизације\n"
+"                                        (Погледај --dump-resample-methods "
+"за\n"
+"                                        могуће вредности)\n"
+"      --use-pid-file[=лог. вредност]    Направи PID датотеку\n"
+"      --no-cpu-limit[=лог. вредност]    Немој инсталирати ограничавање "
+"процесорског\n"
+"                                        терета на платформама које то "
+"подржавају.\n"
+"      --disable-shm[=лог. вредност]     Онемогући подршку за дељену "
+"меморију.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"ПАРАМЕТРИ МОДУЛА\"         Учитај наведени модул додатка са\n"
+"                                        наведеним параметрима\n"
+"  -F, --file=ИМЕДАТОТЕКЕ                Покрени наведену скрипту\n"
+"  -C                                    Отвори командну линију на покренутом "
+"TTY-у\n"
+"                                        после покретања\n"
+"\n"
+"  -n                                    Не учитавај подразумевану датотеку "
+"скрипте.\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize очекује логички аргумент"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail очекује логички аргумент"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level очекује аргумент за ниво записа (или нумеричка вредност у опсегу "
+"0..4 или једно од debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority очекује логички аргумент"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime очекује логички аргумент"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading очекује логички аргумент"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit очекује логички аргумент"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file очекује логички аргумент"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr ""
+"Неисправан циљни дневник: користите једно од „syslog“, „stderr“ или „auto“."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time очекује логички аргумент"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta очекује логички аргумент"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Неисправан начин дискретизације „%s“."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system очекује логички аргумент"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit очекује логички аргумент"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm очекује логички аргумент"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Име: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "Подаци о модулу нису доступни\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Верзија: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Опис: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Аутор: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Употреба: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Учитај једном: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "УПОЗОРЕЊЕ О ПРЕВАЗИЛАЖЕЊУ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Путања: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Неисправан циљни дневник „%s“."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Неисправан ниво опширности у дневнику „%s“."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Неисправан начин дискретизације „%s“."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Неисправан rlimit „%s“."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Неисправан формат узорка „%s“."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Неисправна учестаност дискретизације „%s“."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Неисправни канали узорка „%s“."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Неисправна мапа канала „%s“."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Неисправан број одломака „%s“."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Неисправна величина одломка „%s“."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Неисправан ниво приоритета „%s“."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Неисправна учестаност дискретизације „%s“."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Неуспело отварање датотеке подешавања: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Наведена мапа канала има нема исти број канала као што је наведено у "
+"подразумеваном броју канала."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Прочитај из датотеке подешавања: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "Чистим повластице."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio звучни систем"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Покрени PulseAudio звучни систем"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio звучни систем"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "Покрени PulseAudio звучни систем"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "Моно"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Предњи централни"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Предњи леви"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Предњи десни"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Позадински централни"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Позадински леви"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Позадински десни"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Предњи лево од центра"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Предњи десно од центра"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Лева страна"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Десна страна"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Споредни 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Споредни 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Споредни 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Споредни 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Споредни 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Споредни 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Споредни 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Споредни 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Споредни 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Споредни 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Споредни 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Споредни 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Споредни 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Споредни 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Споредни 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Споредни 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Споредни 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Споредни 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Споредни 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Споредни 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Споредни 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Споредни 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Споредни 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Споредни 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Споредни 024"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Споредни 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Споредни 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Споредни 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Споредни 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Споредни 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Споредни 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Споредни 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Горњи централни"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Горњи предњи централни"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Горњи предњи леви"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Горњи предњи десни"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Горњи позадински централни"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Горњи позадински леви"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Горњи позадински десни"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(неисправно)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "Стерео"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "Окружујући 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "Окружујући 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "Окружујући 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "Окружујући 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "Окружујући 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "У реду"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "Забрањен приступ"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Непозната наредба"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Неисправан аргумент"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "Ентитет постоји"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "Не постоји такав ентитет"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "Веза одбијена"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "Грешка у протоколу"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "Време истекло"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "Нема кључа за овлашћење"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "Интерна грешка"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "Веза прекинута"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "Ентитет убијен"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Сервер неисправан"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Иницијализација модула није успела"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Лоше стање"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Нема података"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "Неусаглашена верзија протокола"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Превелико"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "Није подржано"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Ко̑д грешке је непознат"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "Не постоји такво проширење"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Избачена функционалност"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Није одрађено"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Клијент је израчван"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Улазна/излазна грешка"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Уређај или ресурс је заузет"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "Неуспела функција pa_context_connect(): %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "Неуспешно тумачење података из колачића"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "Неуспешно отварање датотеке подешавања „%s“: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "Колачић није учитан. Покушавам се повезати без колачића."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Примио поруку за непознати локал „%s“"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Неуспешно исушивање тока: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "Репродукциони ток је исушен."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "Веза до сервера се исушује."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "Неуспела функција pa_stream_write(): %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "Неуспела функција pa_stream_write(): %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "Неуспела функција pa_stream_peek(): %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "Ток је успешно направљен."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "Неуспела функција pa_stream_get_buffer_attr(): %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Мере бафера: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Мере бафера: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Користим следеће параметре узорка „%s“ и мапу канала „%s“."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "Прикључен на уређај %s (%u, %s обустављено)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "Грешка тока: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Уређај тока обустављен.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Уређај тока настављен.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Ток није попуњен.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Ток се прелива.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "Ток је покренут.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Ток пребачен на уређај %s (%u, %s обустављено).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "није"
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Параметри бафера тока су промењени.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "Веза успостављена.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "Неуспела функција pa_stream_new(): %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "Неуспела функција pa_stream_connect_playback(): %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "Неуспела функција pa_stream_connect_record(): %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Неуспешно повезивање: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "Добих EOF."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "Неуспела функција write(): %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "Добих сигнал, излазим."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Не могу добити вредност кашњења: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Време: %0.3f s; Кашњење: %0.0f us."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "Неуспела функција pa_stream_update_timing_info(): %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [опције]\n"
+"\n"
+"  -h, --help                            Прикажи ову помоћ\n"
+"      --version                         Прикажи верзију\n"
+"\n"
+"  -r, --record                          Направи везу за снимање\n"
+"  -p, --playback                        Направи везу за репродукцију\n"
+"\n"
+"  -v, --verbose                         Омогући опширан опис радње\n"
+"\n"
+"  -s, --server=СЕРВЕР                   Име сервера на који се треба "
+"повезати\n"
+"  -d, --device=УРЕЂАЈ                   Назив сливника/извора на који се "
+"треба повезати\n"
+"  -n, --client-name=ИМЕ                 Како назвати овог клијента на "
+"серверу\n"
+"      --stream-name=ИМЕ                 Како назвати овај ток на серверу\n"
+"      --volume=ЈАЧИНА                   Наведи почетну (линеарну) јачину "
+"звука у опсегу 0...65536\n"
+"      --rate=УЧЕСТАНОСТ                 Учестаност дискретизације у Hz "
+"(подразумевана 44100)\n"
+"      --format=ФОРМАТУЗОРКА             Врста узорка, једна од s16le, s16be, "
+"u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (подразумевано "
+"s16ne)\n"
+"      --channels=КАНАЛИ                 Број канала, 1 за моно, 2 за стерео\n"
+"                                        (подразумевано 2)\n"
+"      --channel-map=МАПАКАНАЛА          Мапу канала коју треба користити "
+"уместо подразумеване\n"
+"      --fix-format                      Преузми формат узорка из сливника на "
+"који се ток\n"
+"                                        прикључује.\n"
+"      --fix-rate                        Преузми учестаност дискретизације из "
+"одлива на који\n"
+"                                        се ток прикључује.\n"
+"      --fix-channels                    Преузми број и мапу канала из "
+"сливника на који се\n"
+"                                        ток прикључује.\n"
+"      --no-remix                        Без свођења или разлагања канала.\n"
+"      --no-remap                        Мапирај канале по индексу уместо по "
+"називу.\n"
+"      --latency=БАЈТОВА                 Тражи наведено кашњење у бајтовима.\n"
+"      --process-time=БАЈТОВА            Тражи наведено време процеса по "
+"захтеву у бајтовима.\n"
+"      --property=СВОЈСТВО=ВРЕДНОСТ      Постави наведено својство на "
+"наведену вредност.\n"
+"      --raw                             Снимај/репродукуј сирове PCM "
+"податке.\n"
+"      --file-format=ФОРМАТ              Снимај/репродукуј форматиране PCM "
+"податке.\n"
+"      --list-file-formats               Испиши све доступне формате "
+"података.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Компајлирано са libpulse %s\n"
+"Повезано са libpulse %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Неисправно име клијента „%s“"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Неисправно име тока „%s“"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Неисправна мапа канала „%s“"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Неисправан параметар кашњења „%s“"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Неисправан параметар за време процеса „%s“"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Неисправно својство „%s“"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Непознат %s формат датотеке."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "Неисправан параметар узорка"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "Превише аргумената."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "Није успело прављење параметара узорка за датотеку."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "Није успело отварање звучне датотеке."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Упозорење: наведени параметри узорка ће бити пребрисани параметрима из "
+"датотеке."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "Неуспешно утврђивање параметара узорка из датотеке."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Упозорење: Неуспешно утврђивање мапе канала из датотеке."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "Мапа канала се не поклапа са параметрима узорка"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "Упозорење: Неуспешно записивање мапе канала у датотеку."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "Отварам ток %s са параметрима узорка „%s“ и мапом канала „%s“."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "снима"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "пушта"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "Неуспешно тумачење командне линије."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "Неуспела функција pa_mainloop_new()."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "Неуспела функција io_new()."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "Неуспела функција pa_context_new()."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "Неуспела функција pa_context_connect(): %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "Неуспела функција pa_context_new()."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "Неуспела функција pa_mainloop_run()."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Неуспешно заустављање: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Неуспешно настављање: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "УПОЗОРЕЊЕ: Звучни сервер није локални, не заустављам.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Неуспешно повезивање: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Добих SIGINT, излазим.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "УПОЗОРЕЊЕ: Потлачени процес је прекинут сигналом %u\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [опције] ... \n"
+"\n"
+"  -h, --help                            Прикажи ову помоћ\n"
+"      --version                         Прикажи верзију\n"
+"  -s, --server=СЕРВЕР                   Име сервера на који се треба "
+"повезати\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Компајлирано са libpulse %s\n"
+"Повезано са libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "Неуспела функција pa_mainloop_new().\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "Неуспела функција pa_context_new().\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "Неуспела функција pa_mainloop_run().\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Неуспешно добављање статистике: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Тренутно у употреби: %u блокова садржи укупно %s бајтова.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Смештено од покретања: %u блокова садржи укупно %s бајтова.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Величина кеш меморије узорка: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Неуспешно добављање података о серверу: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Корисничко име: %s\n"
+"Име домаћина: %s\n"
+"Име сервера: %s\n"
+"Верзија сервера: %s\n"
+"Подразумевани параметри узорка: %s\n"
+"Подразумевана мапа канала: %s\n"
+"Подразумевано сливник: %s\n"
+"Подразумевани извор: %s\n"
+"Колачић: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Неуспешно добављање података о сливнику: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Сливник #%u\n"
+"\tСтање: %s\n"
+"\tИме: %s\n"
+"\tОпис: %s\n"
+"\tУправљачки програм: %s\n"
+"\tПараметри узорка: %s\n"
+"\tМапа канала: %s\n"
+"\tПрипада модулу: %u\n"
+"\tИскључен тон: %s\n"
+"\tЈачина звука: %s%s%s\n"
+"\t        баланс %0.2f\n"
+"\tГласност баса: %s%s%s\n"
+"\tИзвор контролора: %s\n"
+"\tКашњење: %0.0f μs, подешено %0.0f μs\n"
+"\tЗаставице: %s%s%s%s%s%s\n"
+"\tСвојства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tПортови:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tАктивни порт: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tПортови:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Неуспешно добављање података о извору: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Извор #%u\n"
+"\tСтање: %s\n"
+"\tИме: %s\n"
+"\tОпис: %s\n"
+"\tУправљачки програм: %s\n"
+"\tПараметри узорка: %s\n"
+"\tМапа канала: %s\n"
+"\tПрипада модулу: %u\n"
+"\tИскључен тон: %s\n"
+"\tЈачина звука: %s%s%s\n"
+"\t        баланс %0.2f\n"
+"\tГласност баса: %s%s%s\n"
+"\tКонтролер сливника: %s\n"
+"\tКашњење: %0.0f μs, подешено %0.0f μs\n"
+"\tЗаставице: %s%s%s%s%s%s\n"
+"\tСвојства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "непознато"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Неуспешно добављање података о модулу: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Модул #%u\n"
+"\tИме: %s\n"
+"\tАргумент: %s\n"
+"\tБројач коришћења: %s\n"
+"\tСвојства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Неуспешно добављање података о клијенту: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Клијент #%u\n"
+"\tУправљачки програм: %s\n"
+"\tПрипада модулу: %s\n"
+"\tСвојства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Неуспешно добављање података о картици: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Картица #%u\n"
+"\tИме: %s\n"
+"\tУправљачки програм: %s\n"
+"\tВласник модула: %s\n"
+"\tСвојства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tПрофили:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tАктивни профил: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Неуспешно добављање података о улазу сливника: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Улаз у сливник #%u\n"
+"\tУправљачки програм: %s\n"
+"\tПрипада модулу: %s\n"
+"\tКлијент: %s\n"
+"\tСливник: %u\n"
+"\tПараметри узорка: %s\n"
+"\tМапа канала: %s\n"
+"\tИскључен тон: %s\n"
+"\tЈачина звука: %s\n"
+"\t        %s\n"
+"\t        баланс %0.2f\n"
+"\tКашњење бафера: %0.0f μs\n"
+"\tКашњење сливника: %0.0f μs\n"
+"\tНачин дискретизације: %s\n"
+"\tСвојства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Неуспешно добављање података о излазу извора: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Улаз у сливник #%u\n"
+"\tУправљачки програм: %s\n"
+"\tПрипада модулу: %s\n"
+"\tКлијент: %s\n"
+"\tСливник: %u\n"
+"\tПараметри узорка: %s\n"
+"\tМапа канала: %s\n"
+"\tИскључен тон: %s\n"
+"\tЈачина звука: %s\n"
+"\t        %s\n"
+"\t        баланс %0.2f\n"
+"\tКашњење бафера: %0.0f μs\n"
+"\tКашњење сливника: %0.0f μs\n"
+"\tНачин дискретизације: %s\n"
+"\tСвојства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Неуспешно добављање података о узорку: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Узорак #%u\n"
+"\tИме: %s\n"
+"\tПараметри узорка: %s\n"
+"\tМапа канала: %s\n"
+"\tЈачина звука: %s\n"
+"\t        %s\n"
+"\t        баланс %0.2f\n"
+"\tДужина: %0.1fs\n"
+"\tВеличина: %s\n"
+"\tЛењ: %s\n"
+"\tИме датотеке: %s\n"
+"\tСвојства:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "Неуспех: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Неуспешно добављање података о извору: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Није успело постављање узорка: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "Прерани крај датотеке"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "Сервер неисправан"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "Добих SIGINT, излазим."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "Неисправан параметар јачине"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [опције] ... \n"
+"\n"
+"  -h, --help                            Прикажи ову помоћ\n"
+"      --version                         Прикажи верзију\n"
+"  -s, --server=СЕРВЕР                   Име сервера на који се треба "
+"повезати\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Компајлирано са libpulse %s\n"
+"Повезано са libpulse %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "Наведите датотеку узорка коју треба учитати"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "Није успело отварање звучне датотеке."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Упозорење: Неуспешно утврђивање параметара узорка из датотеке."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "Морате навести име узорка којег желите репродуковати"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "Морате навести име узорка којег желите уклонити"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "Морате навести индекс улаза сливника и сливник"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "Морате навести индекс излаза извора и извор"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "Морате навести име и аргументе модула."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "Морате навести индекс модула"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Не можете навести више од једног сливника. Морате навести логичку вредност."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Не можете навести више од једног извора. Морате навести логичку вредност."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Морате навести име/индекс картице и име профила"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Морате навести име/индекс сливника и име порта"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "Морате навести име/индекс извора и име порта"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Морате навести име/индекс сливника и јачину"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "Морате навести име/индекс извора и јачину"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "Морате навести индекс улаза сливника и јачину"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "Неисправан индекс улаза сливника"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "Морате навести индекс излаза извора и извор"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "Неисправан индекс улаза сливника"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "Морате навести име/индекс сливника и логичку вредност за искључивање"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "Неисправан параметар узорка"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Морате навести име/индекс извора и логичку вредност за искључивање"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "Морате навести индекс улаза сливника и логичку вредност за искључивање"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "Неисправан параметар индекса улаза сливника"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "Морате навести име/индекс извора и логичку вредност за искључивање"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "Неисправан параметар индекса улаза сливника"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "Морате навести име/индекс сливника и логичку вредност за искључивање"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "Није наведена исправна наредба."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D приказ] [-S сервер] [-O сливник] [-I извор] [-c датотека]  [-d|-e|-i|-"
+"r]\n"
+"\n"
+" -d    Прикажи тренутне PulseAudio податке закачене за X11 приказ "
+"(подразумевано)\n"
+" -e    Извези локалне PulseAudio податке на X11 приказ\n"
+" -i    Увези PulseAudio податке са X11 приказа у локалне променљиве окружења "
+"и датотеке колачића.\n"
+" -r    Уклони PulseAudio податке са X11 приказа\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Неуспешно тумачење командне линије.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "Сервер: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "Извор: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Сливник: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Колачић: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Неуспешно тумачење података из колачића\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Неуспешно записивање података колачића\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "Неуспешно учитавање клијентове датотеке подешавања.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "Неуспешно читање података подешавања за окружење.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Неуспешно добијање FQDN-а.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Неуспешно учитавање датотека колачића\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Није још имплементирано.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Нема покренутог PulseAudio демона, или се не извршава као демон сесије."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Није успело убијање PulseAudio демона."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "Демон се не одазива."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "Није могуће приступити датотеци закључавања за самоумножавање."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA нас је пробудила да би записала нове податке на уређај, али нема ништа "
+"да се запише!\n"
+"Ово је највероватније грешка у ALSA управљачком програму „%s“. Пријавите "
+"овај проблем програмерима ALSA-е.\n"
+"Пробуђени смо са постављеним POLLOUT-ом -- али следећи snd_pcm_avail() је "
+"вратио 0 или неку другу вредност мању од min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA нас је пробудила да би прочитала нове податке из уређаја, али нема "
+"ништа да се прочита!\n"
+"Ово је највероватније грешка у ALSA управљачком програму „%s“. Пријавите "
+"овај проблем програмерима ALSA-е.\n"
+"Пробуђени смо са постављеним POLLIN-ом -- али следећи snd_pcm_avail() је "
+"вратио 0 или неку другу вредност мању од min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "Искључено"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Репродукција високе тачности (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Снимање високе тачности (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Двосмерно телефонирање (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio звучни систем"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "Излазни уређаји"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "Улазни уређаји"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "Аудио на @HOSTNAME@"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "Улаз"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "Улаз прикључне станице"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "Микрофон прикључне станице"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "Улаз прикључне станице"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "Линија у"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "Микрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "Микрофон прикључне станице"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "Микрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "Спољни микрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "Унутрашњи микрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "Радио"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "Видео"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "Самостална контрола појачања"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "Без самосталне контроле појачања"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "Подизање"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "Без подизања"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "Појачало"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "Без појачала"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "Подизање"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "Без подизања"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "Аналогне слушалице"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "Аналогни улаз"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "Микрофон прикључне станице"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "Аналогни излаз"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "Аналогни излаз (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "Линија у"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "Аналогни моно излаз"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "Аналогни стерео"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "Дигитални стерео (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Дигитални стерео (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "Аналогни моно"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "Аналогни стерео"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "Аналогни окружујући 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "Аналогни окружујући 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "Аналогни окружујући 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "Аналогни окружујући 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "Аналогни окружујући 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "Аналогни окружујући 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "Аналогни окружујући 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "Аналогни окружујући 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "Аналогни окружујући 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "Аналогни окружујући 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "Аналогни окружујући 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "Дигитални стерео (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Дигитални стерео (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Дигитални окружујући 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Дигитални окружујући 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "Дигитални стерео (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Дигитални окружујући 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "Двосмерни аналогни моно"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "Двосмерни аналогни стерео"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Двосмерни дигитални стерео (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Празан излаз"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "Улаз"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<име сливника> sink_properties=<својства сливника> master=<име "
+"сливника за филтрирање> format=<формат узорка> rate=<учестаност "
+"дискретизације> channels=<број канала> channel_map=<мапа канала> plugin=<име "
+"ladspa додатка> label=<ознака ladspa додатка> control=<списак улазних "
+"контролних вредности раздвојених зарезом>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit није подржан на овој платформи."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "Неуспела функција XOpenDisplay()"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Излаз извора #%u\n"
+#~ "\tУправљачки програм: %s\n"
+#~ "\tПрипада модулу: %s\n"
+#~ "\tКлијент: %s\n"
+#~ "\tИзвор: %u\n"
+#~ "\tПараметри узорка: %s\n"
+#~ "\tМапа канала: %s\n"
+#~ "\tКашњење бафера: %0.0f μs\n"
+#~ "\tКашњење извора: %0.0f μs\n"
+#~ "\tНачин дискретизације: %s\n"
+#~ "\tСвојства:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [опције] stat\n"
+#~ "%s [опције] list\n"
+#~ "%s [опције] exit\n"
+#~ "%s [опције] upload-sample ИМЕДАТОТЕКЕ [ИМЕ]\n"
+#~ "%s [опције] play-sample ИМЕ [СЛИВНИК]\n"
+#~ "%s [опције] remove-sample ИМЕ\n"
+#~ "%s [опције] move-sink-input УЛАЗСЛИВНИКА СЛИВНИК\n"
+#~ "%s [опције] move-source-output ИЗЛАЗИЗВОРА ИЗВОР\n"
+#~ "%s [опције] load-module ИМЕ [АРГ ...]\n"
+#~ "%s [опције] unload-module МОДУЛ\n"
+#~ "%s [опције] suspend-sink СЛИВНИК 1|0\n"
+#~ "%s [опције] suspend-source ИЗВОР 1|0\n"
+#~ "%s [опције] set-card-profile КАРТИЦА ПРОФИЛ\n"
+#~ "%s [опције] set-sink-port СЛИВНИК ПОРТ\n"
+#~ "%s [опције] set-source-port ИЗВОР ПОРТ\n"
+#~ "%s [опције] set-sink-volume СЛИВНИК ЈАЧИНА\n"
+#~ "%s [опције] set-source-volume ИЗВОР ЈАЧИНА\n"
+#~ "%s [опције] set-sink-input-volume УЛАЗСЛИВНИКА ЈАЧИНА\n"
+#~ "%s [опције] set-sink-mute СЛИВНИК 1|0\n"
+#~ "%s [опције] set-source-mute ИЗВОР 1|0\n"
+#~ "%s [опције] set-sink-input-mute УЛАЗСЛИВНИКА 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Прикажи ову помоћ\n"
+#~ "      --version                         Прикажи верзију\n"
+#~ "\n"
+#~ "  -s, --server=СЕРВЕР                   Име сервера на који се треба "
+#~ "повезати\n"
+#~ "  -n, --client-name=ИМЕ                 Како назвати овог клијента на "
+#~ "серверу\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Дигитални окружујући 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Звучник за ниске фреквенције"
diff --git a/po/sr@latin.po b/po/sr@latin.po
new file mode 100644 (file)
index 0000000..428f2f5
--- /dev/null
@@ -0,0 +1,3050 @@
+# Serbian(Latin) translations for pulseaudio
+# Copyright (C) 2006 Lennart Poettering
+# This file is distributed under the same license as the pulseaudio package.
+# Igor Miletic (Igor Miletić) <grejigl-gnomeprevod@yahoo.ca>, 2009.
+# Miloš Komarčević <kmilos@gmail.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:55+0000\n"
+"Last-Translator: Miloš Komarčević <kmilos@gmail.com>\n"
+"Language-Team: Serbian (sr) <fedora-trans-sr@redhat.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() je vratio vrednost koja je neobično velika: %lu bajtova (%lu "
+"ms).\n"
+"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
+"ovaj problem ALSA programerima."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() je vratio vrednost koja je neobično velika: %li bajtova (%s"
+"%lu ms).\n"
+"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
+"ovaj problem ALSA programerima."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() je vratio vrednost koja je neobično velika: %lu bajtova (%lu "
+"ms).\n"
+"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
+"ovaj problem ALSA programerima."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin()  je vratio vrednost koja je neobično velika: %lu "
+"bajtova (%lu ms).\n"
+"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
+"ovaj problem ALSA programerima."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Uvek održava barem jedan slivnik opterećenim čak i kada je prazan"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Lažan izlaz"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "Virtuelni LADSPA slivnik"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<ime slivnika> sink_properties=<svojstva slivnika> master=<ime "
+"slivnika za filtriranje> format=<format uzorka> rate=<učestanost "
+"diskretizacije> channels=<broj kanala> channel_map=<mapa kanala> plugin=<ime "
+"ladspa dodatka> label=<oznaka ladspa dodatka> control=<spisak ulaznih "
+"kontrolnih vrednosti razdvojenih zarezom>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "Uzorak NULL slivnika"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Prazan izlaz"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "Unutrašnji zvuk"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Neuspešna pretraga za originalnim lt_dlopen učitavačem."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "Neuspešno smeštanje novog dl učitavača."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "Neuspešno dodavanje „poveži odmah“ učitavača."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "Dobih signal %s."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "Napuštam."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Ne mogu naći korisnika „%s“."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Ne mogu naći grupu „%s“."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "Nađeni su korisnik „%s“ (UID %lu) i grupa „%s“ (GID %lu)."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID korisnika „%s“ se ne poklapa sa grupom „%s“."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Lični direktorijum korisnika „%s“ nije „%s“, zanemarujem."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Neuspešno pravljenje „%s“: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Neuspešna promena grupnog spiska: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Neuspešna promena GID-a: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Neuspešna promena UID-a: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "Uspešno odbačena root ovlašćenja."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "Režim za čitav sistem nije podržan na ovoj platformi."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) nije uspelo: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "Neuspešno tumačenje komandne linije."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "Demon nije pokrenut"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "Demon je pokrenut sa PID-om %u"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Neuspešno ubijanje demona: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Nije nameravano da se ovaj program pokreće iz root naloga (osim u slučaju "
+"kada je --system navedeno)"
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Potrebna su root ovlašćenja."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start nije podržano za sistemske primerke."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "Pokrenuto u sistemskom režimu, ali --disallow-exit nije postavljeno!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"Pokrenuto u sistemskom režimu, ali --disallow-module-loading nije "
+"postavljeno!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "Pokrenuto u sistemskom režimu, prisilno onemogućujem SHM režim!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr ""
+"Pokrenuto u sistemskom režimu, prisilno onemogućujem gašenje posle određenog "
+"vremena mirovanja!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "Neuspešno pronalaženje standardnog ulaza/izlaza."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "Neuspešno puštanje podataka kroz cev: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "Neuspela funkcija fork(): %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "Neuspela funkcija read(): %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "Neuspešno pokretanje demona."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "Demon uspešno pokrenut."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "Neuspela funkcija read(): %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "Ovo je PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "Domaćin kompajliranja: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "CFLAGS kompajliranja: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "Pokrenut na domaćinu: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "Našao %u procesor(a)"
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "Veličina stranice je %lu bajtova"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Kompajlirano sa podrškom za Valgrind: da"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Kompajlirano sa podrškom za Valgrind: ne"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "Pokrenut u Valgrind režimu: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "Pokrenut na domaćinu: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Optimizovana izgradnja: da"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "Optimizovana izgradnja: ne"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG definisan, sva obaveštenja isključena."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH definisan, samo obaveštenja brze putanje isključena."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "Sva obaveštenja omogućena."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "Neuspešno dobavljanje IB mašine"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "IB mašine je %s."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "IB sesije je %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "Koristi se %s izvršni direktorijum."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "Koristi se %s direktorijum stanja."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "Koristi se %s direktorijum modula."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "Pokrenuto u sistemskom režimu: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"U redu, znači izvršavate PA u sistemskom režimu. Primite k znanju da to "
+"verovatno ne bi trebalo da radite.\n"
+"Ako to svejedno činite onda je vaša krivica ako stvari ne rade onako kako se "
+"očekuje.\n"
+"Pročitajte http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/"
+"User/WhatIsWrongWithSystemWide/ radi objašnjenja zašto je sistemski režim "
+"obično loša ideja."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "Neuspela funkcija pa_pid_file_create()."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "Dostupni su novi brojači visoke rezolucije! Prijatno!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Vaše jezgro nije dobro podešeno za pulseaudio! Preporučuje Vam se da "
+"koristite Linux jezgro sa omogućenim brojačima visoke rezolucije."
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "Neuspela funkcija pa_core_new()."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "Neuspešno pokretanje demona."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Demon je pokrenut bez ijednog učitanog modula, odbija da radi."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "Pokretanje demona uspešno."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "Pokrenuto gašenje demona."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "Rad demona je prekinut."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [opcije]\n"
+"\n"
+"NAREDBE:\n"
+"  -h, --help                            Prikaži ovu pomoć\n"
+"      --version                         Prikaži verziju\n"
+"      --dump-conf                       Ispiši podrazumevana podešavanja\n"
+"      --dump-modules                    Ispiši spisak dostupnih modula\n"
+"      --dump-resample-methods           Ispiši dostupne vrednosti "
+"diskretizacije\n"
+"      --cleanup-shm                     Očisti bajate delove deljene "
+"memorije\n"
+"      --start                           Pokreni demon ako već nije pokrenut\n"
+"  -k  --kill                            Ubij pokrenuti demon\n"
+"      --check                           Proveri postojanje pokrenutog demona "
+"(vraća samo izlazni kȏd)\n"
+"\n"
+"OPCIJE:\n"
+"      --system[=logička vrednost]       Pokreni kao sistemski primerak\n"
+"  -D, --daemonize[=log. vrednost]       Pokreni u pozadini\n"
+"      --fail[=log. vrednost]            Izađi posle neuspešnog pokretanja\n"
+"      --high-priority[=log. vrednost]   Pokušaj postaviti visokoprioritetno "
+"raspoređivanje\n"
+"                                        (dostupno samo rootu, ili preko SUID-"
+"a ili\n"
+"                                        sa povišenim RLIMIT_NICE nivoom)\n"
+"      --realtime[=log. vrednost]        Pokušaj omogućiti stvarnovremensko "
+"raspoređivanje\n"
+"                                        (dostupno samo rootu, ili preko SUID-"
+"a ili\n"
+"                                        sa povišenim RLIMIT_RTPRIO nivoom)\n"
+"      --disallow-module-loading[=log.]  Ne dozvoljavaj učitavanje/uklanjanje "
+"modula\n"
+"                                        na korisnički zahtev posle "
+"pokretanja\n"
+"      --disallow-exit[=log. vrednost]   Ne dozvoljavaj izlaz na korisnički "
+"zahtev\n"
+"      --exit-idle-time=SEKUNDI          Prekini rad demona posle mirovanja\n"
+"                                        od ovoliko sekundi\n"
+"      --module-idle-time=SEKUNDI        Ukloni samoučitane module posle "
+"mirovanja\n"
+"                                        od ovoliko sekundi\n"
+"      --scache-idle-time=SEKUNDI        Ukloni samoučitane primerke posle "
+"mirovanja\n"
+"                                        od ovoliko sekundi\n"
+"      --log-level[=NIVO]                Povećaj ili postavi nivo opširnosti\n"
+"  -v                                    Povećaj nivo opširnosti\n"
+"      --log-target={auto,syslog,stderr} Navedi ciljni dnevnik\n"
+"      --log-meta[=log. vrednost]        Uključi mesto u kȏdu u porukama "
+"dnevnika\n"
+"      --log-time[=log. vrednost]        Uključi vreme u porukama dnevnika\n"
+"      --log-backtrace=FRAMES            Uključi tragove u porukama dnevnika\n"
+"  -p, --dl-search-path=PUTANJA           Postavi putanju pretrage za "
+"dinamički deljene\n"
+"                                        objekte (dodaci)\n"
+"      --resample-method=NAČIN           Koristi navedeni način "
+"diskretizacije\n"
+"                                        (Pogledaj --dump-resample-methods "
+"za\n"
+"                                        moguće vrednosti)\n"
+"      --use-pid-file[=log. vrednost]    Napravi PID datoteku\n"
+"      --no-cpu-limit[=log. vrednost]    Nemoj instalirati ograničavanje "
+"procesorskog\n"
+"                                        tereta na platformama koje to "
+"podržavaju.\n"
+"      --disable-shm[=log. vrednost]     Onemogući podršku za deljenu "
+"memoriju.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"PARAMETRI MODULA\"         Učitaj navedeni modul dodatka sa\n"
+"                                        navedenim parametrima\n"
+"  -F, --file=IMEDATOTEKE                Pokreni navedenu skriptu\n"
+"  -C                                    Otvori komandnu liniju na pokrenutom "
+"TTY-u\n"
+"                                        posle pokretanja\n"
+"\n"
+"  -n                                    Ne učitavaj podrazumevanu datoteku "
+"skripte.\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize očekuje logički argument"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail očekuje logički argument"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level očekuje argument za nivo zapisa (ili numerička vrednost u opsegu "
+"0..4 ili jedno od debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority očekuje logički argument"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime očekuje logički argument"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading očekuje logički argument"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit očekuje logički argument"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file očekuje logički argument"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr ""
+"Neispravan ciljni dnevnik: koristite jedno od „syslog“, „stderr“ ili „auto“."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time očekuje logički argument"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta očekuje logički argument"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Neispravan način diskretizacije „%s“."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system očekuje logički argument"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit očekuje logički argument"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm očekuje logički argument"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "Ime: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "Podaci o modulu nisu dostupni\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "Verzija: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "Opis: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "Autor: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Upotreba: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Učitaj jednom: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "UPOZORENJE O PREVAZILAŽENJU: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "Putanja: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Neispravan ciljni dnevnik „%s“."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Neispravan nivo opširnosti u dnevniku „%s“."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Neispravan način diskretizacije „%s“."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Neispravan rlimit „%s“."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Neispravan format uzorka „%s“."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Neispravna učestanost diskretizacije „%s“."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Neispravni kanali uzorka „%s“."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Neispravna mapa kanala „%s“."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Neispravan broj odlomaka „%s“."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Neispravna veličina odlomka „%s“."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Neispravan nivo prioriteta „%s“."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Neispravna učestanost diskretizacije „%s“."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Neuspelo otvaranje datoteke podešavanja: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Navedena mapa kanala ima nema isti broj kanala kao što je navedeno u "
+"podrazumevanom broju kanala."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Pročitaj iz datoteke podešavanja: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "Čistim povlastice."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio zvučni sistem"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Pokreni PulseAudio zvučni sistem"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio zvučni sistem"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "Pokreni PulseAudio zvučni sistem"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "Prednji centralni"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "Prednji levi"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "Prednji desni"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "Pozadinski centralni"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "Pozadinski levi"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "Pozadinski desni"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "Prednji levo od centra"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "Prednji desno od centra"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "Leva strana"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "Desna strana"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Sporedni 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Sporedni 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Sporedni 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Sporedni 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Sporedni 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Sporedni 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Sporedni 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Sporedni 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Sporedni 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Sporedni 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Sporedni 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Sporedni 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Sporedni 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Sporedni 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Sporedni 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Sporedni 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Sporedni 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Sporedni 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Sporedni 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Sporedni 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Sporedni 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Sporedni 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Sporedni 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Sporedni 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Sporedni 024"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Sporedni 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Sporedni 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Sporedni 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Sporedni 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Sporedni 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Sporedni 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Sporedni 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "Gornji centralni"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "Gornji prednji centralni"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "Gornji prednji levi"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "Gornji prednji desni"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "Gornji pozadinski centralni"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "Gornji pozadinski levi"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "Gornji pozadinski desni"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(neispravno)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "Okružujući 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "Okružujući 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "Okružujući 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "Okružujući 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "Okružujući 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "U redu"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "Zabranjen pristup"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "Nepoznata naredba"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "Neispravan argument"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "Entitet postoji"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "Ne postoji takav entitet"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "Veza odbijena"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "Greška u protokolu"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "Vreme isteklo"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "Nema ključa za ovlašćenje"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "Interna greška"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "Veza prekinuta"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "Entitet ubijen"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "Server neispravan"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "Inicijalizacija modula nije uspela"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "Loše stanje"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "Nema podataka"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "Neusaglašena verzija protokola"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "Preveliko"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "Nije podržano"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "Kȏd greške je nepoznat"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "Ne postoji takvo proširenje"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "Izbačena funkcionalnost"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "Nije odrađeno"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "Klijent je izračvan"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "Ulazna/izlazna greška"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "Uređaj ili resurs je zauzet"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "Neuspela funkcija pa_context_connect(): %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "Neuspešno tumačenje podataka iz kolačića"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "Neuspešno otvaranje datoteke podešavanja „%s“: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "Kolačić nije učitan. Pokušavam se povezati bez kolačića."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Primio poruku za nepoznati lokal „%s“"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Neuspešno isušivanje toka: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "Reprodukcioni tok je isušen."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "Veza do servera se isušuje."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "Neuspela funkcija pa_stream_write(): %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "Neuspela funkcija pa_stream_write(): %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "Neuspela funkcija pa_stream_peek(): %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "Tok je uspešno napravljen."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "Neuspela funkcija pa_stream_get_buffer_attr(): %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Mere bafera: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Mere bafera: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Koristim sledeće parametre uzorka „%s“ i mapu kanala „%s“."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "Priključen na uređaj %s (%u, %s obustavljeno)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "Greška toka: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Uređaj toka obustavljen.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Uređaj toka nastavljen.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Tok nije popunjen.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Tok se preliva.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "Tok je pokrenut.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Tok prebačen na uređaj %s (%u, %s obustavljeno).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "nije"
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Parametri bafera toka su promenjeni.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "Veza uspostavljena.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "Neuspela funkcija pa_stream_new(): %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "Neuspela funkcija pa_stream_connect_playback(): %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "Neuspela funkcija pa_stream_connect_record(): %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Neuspešno povezivanje: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "Dobih EOF."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "Neuspela funkcija write(): %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "Dobih signal, izlazim."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Ne mogu dobiti vrednost kašnjenja: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Vreme: %0.3f s; Kašnjenje: %0.0f us."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "Neuspela funkcija pa_stream_update_timing_info(): %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [opcije]\n"
+"\n"
+"  -h, --help                            Prikaži ovu pomoć\n"
+"      --version                         Prikaži verziju\n"
+"\n"
+"  -r, --record                          Napravi vezu za snimanje\n"
+"  -p, --playback                        Napravi vezu za reprodukciju\n"
+"\n"
+"  -v, --verbose                         Omogući opširan opis radnje\n"
+"\n"
+"  -s, --server=SERVER                   Ime servera na koji se treba "
+"povezati\n"
+"  -d, --device=UREĐAJ                   Naziv slivnika/izvora na koji se "
+"treba povezati\n"
+"  -n, --client-name=IME                 Kako nazvati ovog klijenta na "
+"serveru\n"
+"      --stream-name=IME                 Kako nazvati ovaj tok na serveru\n"
+"      --volume=JAČINA                   Navedi početnu (linearnu) jačinu "
+"zvuka u opsegu 0...65536\n"
+"      --rate=UČESTANOST                 Učestanost diskretizacije u Hz "
+"(podrazumevana 44100)\n"
+"      --format=FORMATUZORKA             Vrsta uzorka, jedna od s16le, s16be, "
+"u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (podrazumevano "
+"s16ne)\n"
+"      --channels=KANALI                 Broj kanala, 1 za mono, 2 za stereo\n"
+"                                        (podrazumevano 2)\n"
+"      --channel-map=MAPAKANALA          Mapu kanala koju treba koristiti "
+"umesto podrazumevane\n"
+"      --fix-format                      Preuzmi format uzorka iz slivnika na "
+"koji se tok\n"
+"                                        priključuje.\n"
+"      --fix-rate                        Preuzmi učestanost diskretizacije iz "
+"odliva na koji\n"
+"                                        se tok priključuje.\n"
+"      --fix-channels                    Preuzmi broj i mapu kanala iz "
+"slivnika na koji se\n"
+"                                        tok priključuje.\n"
+"      --no-remix                        Bez svođenja ili razlaganja kanala.\n"
+"      --no-remap                        Mapiraj kanale po indeksu umesto po "
+"nazivu.\n"
+"      --latency=BAJTOVA                 Traži navedeno kašnjenje u "
+"bajtovima.\n"
+"      --process-time=BAJTOVA            Traži navedeno vreme procesa po "
+"zahtevu u bajtovima.\n"
+"      --property=SVOJSTVO=VREDNOST      Postavi navedeno svojstvo na "
+"navedenu vrednost.\n"
+"      --raw                             Snimaj/reprodukuj sirove PCM "
+"podatke.\n"
+"      --file-format=FORMAT              Snimaj/reprodukuj formatirane PCM "
+"podatke.\n"
+"      --list-file-formats               Ispiši sve dostupne formate "
+"podataka.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Kompajlirano sa libpulse %s\n"
+"Povezano sa libpulse %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Neispravno ime klijenta „%s“"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Neispravno ime toka „%s“"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Neispravna mapa kanala „%s“"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Neispravan parametar kašnjenja „%s“"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Neispravan parametar za vreme procesa „%s“"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Neispravno svojstvo „%s“"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Nepoznat %s format datoteke."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "Neispravan parametar uzorka"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "Previše argumenata."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "Nije uspelo pravljenje parametara uzorka za datoteku."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "Nije uspelo otvaranje zvučne datoteke."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Upozorenje: navedeni parametri uzorka će biti prebrisani parametrima iz "
+"datoteke."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "Neuspešno utvrđivanje parametara uzorka iz datoteke."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Upozorenje: Neuspešno utvrđivanje mape kanala iz datoteke."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "Mapa kanala se ne poklapa sa parametrima uzorka"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "Upozorenje: Neuspešno zapisivanje mape kanala u datoteku."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "Otvaram tok %s sa parametrima uzorka „%s“ i mapom kanala „%s“."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "snima"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "pušta"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "Neuspešno tumačenje komandne linije."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "Neuspela funkcija pa_mainloop_new()."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "Neuspela funkcija io_new()."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "Neuspela funkcija pa_context_new()."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "Neuspela funkcija pa_context_connect(): %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "Neuspela funkcija pa_context_new()."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "Neuspela funkcija pa_mainloop_run()."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Neuspešno zaustavljanje: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Neuspešno nastavljanje: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "UPOZORENJE: Zvučni server nije lokalni, ne zaustavljam.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Neuspešno povezivanje: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Dobih SIGINT, izlazim.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "UPOZORENJE: Potlačeni proces je prekinut signalom %u\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [opcije] ... \n"
+"\n"
+"  -h, --help                            Prikaži ovu pomoć\n"
+"      --version                         Prikaži verziju\n"
+"  -s, --server=SERVER                   Ime servera na koji se treba "
+"povezati\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Kompajlirano sa libpulse %s\n"
+"Povezano sa libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "Neuspela funkcija pa_mainloop_new().\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "Neuspela funkcija pa_context_new().\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "Neuspela funkcija pa_mainloop_run().\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Neuspešno dobavljanje statistike: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Trenutno u upotrebi: %u blokova sadrži ukupno %s bajtova.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Smešteno od pokretanja: %u blokova sadrži ukupno %s bajtova.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Veličina keš memorije uzorka: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Neuspešno dobavljanje podataka o serveru: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Korisničko ime: %s\n"
+"Ime domaćina: %s\n"
+"Ime servera: %s\n"
+"Verzija servera: %s\n"
+"Podrazumevani parametri uzorka: %s\n"
+"Podrazumevana mapa kanala: %s\n"
+"Podrazumevano slivnik: %s\n"
+"Podrazumevani izvor: %s\n"
+"Kolačić: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Neuspešno dobavljanje podataka o slivniku: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Slivnik #%u\n"
+"\tStanje: %s\n"
+"\tIme: %s\n"
+"\tOpis: %s\n"
+"\tUpravljački program: %s\n"
+"\tParametri uzorka: %s\n"
+"\tMapa kanala: %s\n"
+"\tPripada modulu: %u\n"
+"\tIsključen ton: %s\n"
+"\tJačina zvuka: %s%s%s\n"
+"\t        balans %0.2f\n"
+"\tGlasnost basa: %s%s%s\n"
+"\tIzvor kontrolora: %s\n"
+"\tKašnjenje: %0.0f μs, podešeno %0.0f μs\n"
+"\tZastavice: %s%s%s%s%s%s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPortovi:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tAktivni port: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tPortovi:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Neuspešno dobavljanje podataka o izvoru: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Izvor #%u\n"
+"\tStanje: %s\n"
+"\tIme: %s\n"
+"\tOpis: %s\n"
+"\tUpravljački program: %s\n"
+"\tParametri uzorka: %s\n"
+"\tMapa kanala: %s\n"
+"\tPripada modulu: %u\n"
+"\tIsključen ton: %s\n"
+"\tJačina zvuka: %s%s%s\n"
+"\t        balans %0.2f\n"
+"\tGlasnost basa: %s%s%s\n"
+"\tKontroler slivnika: %s\n"
+"\tKašnjenje: %0.0f μs, podešeno %0.0f μs\n"
+"\tZastavice: %s%s%s%s%s%s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "nepoznato"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Neuspešno dobavljanje podataka o modulu: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modul #%u\n"
+"\tIme: %s\n"
+"\tArgument: %s\n"
+"\tBrojač korišćenja: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Neuspešno dobavljanje podataka o klijentu: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Klijent #%u\n"
+"\tUpravljački program: %s\n"
+"\tPripada modulu: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Neuspešno dobavljanje podataka o kartici: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Kartica #%u\n"
+"\tIme: %s\n"
+"\tUpravljački program: %s\n"
+"\tVlasnik modula: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfili:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tAktivni profil: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Neuspešno dobavljanje podataka o ulazu slivnika: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Ulaz u slivnik #%u\n"
+"\tUpravljački program: %s\n"
+"\tPripada modulu: %s\n"
+"\tKlijent: %s\n"
+"\tSlivnik: %u\n"
+"\tParametri uzorka: %s\n"
+"\tMapa kanala: %s\n"
+"\tIsključen ton: %s\n"
+"\tJačina zvuka: %s\n"
+"\t        %s\n"
+"\t        balans %0.2f\n"
+"\tKašnjenje bafera: %0.0f μs\n"
+"\tKašnjenje slivnika: %0.0f μs\n"
+"\tNačin diskretizacije: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Neuspešno dobavljanje podataka o izlazu izvora: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Ulaz u slivnik #%u\n"
+"\tUpravljački program: %s\n"
+"\tPripada modulu: %s\n"
+"\tKlijent: %s\n"
+"\tSlivnik: %u\n"
+"\tParametri uzorka: %s\n"
+"\tMapa kanala: %s\n"
+"\tIsključen ton: %s\n"
+"\tJačina zvuka: %s\n"
+"\t        %s\n"
+"\t        balans %0.2f\n"
+"\tKašnjenje bafera: %0.0f μs\n"
+"\tKašnjenje slivnika: %0.0f μs\n"
+"\tNačin diskretizacije: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Neuspešno dobavljanje podataka o uzorku: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Uzorak #%u\n"
+"\tIme: %s\n"
+"\tParametri uzorka: %s\n"
+"\tMapa kanala: %s\n"
+"\tJačina zvuka: %s\n"
+"\t        %s\n"
+"\t        balans %0.2f\n"
+"\tDužina: %0.1fs\n"
+"\tVeličina: %s\n"
+"\tLenj: %s\n"
+"\tIme datoteke: %s\n"
+"\tSvojstva:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "Neuspeh: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Neuspešno dobavljanje podataka o izvoru: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Nije uspelo postavljanje uzorka: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "Prerani kraj datoteke"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "Server neispravan"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "Dobih SIGINT, izlazim."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "Neispravan parametar jačine"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [opcije] ... \n"
+"\n"
+"  -h, --help                            Prikaži ovu pomoć\n"
+"      --version                         Prikaži verziju\n"
+"  -s, --server=SERVER                   Ime servera na koji se treba "
+"povezati\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Kompajlirano sa libpulse %s\n"
+"Povezano sa libpulse %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "Navedite datoteku uzorka koju treba učitati"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "Nije uspelo otvaranje zvučne datoteke."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Upozorenje: Neuspešno utvrđivanje parametara uzorka iz datoteke."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "Morate navesti ime uzorka kojeg želite reprodukovati"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "Morate navesti ime uzorka kojeg želite ukloniti"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "Morate navesti indeks ulaza slivnika i slivnik"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "Morate navesti indeks izlaza izvora i izvor"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "Morate navesti ime i argumente modula."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "Morate navesti indeks modula"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Ne možete navesti više od jednog slivnika. Morate navesti logičku vrednost."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Ne možete navesti više od jednog izvora. Morate navesti logičku vrednost."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Morate navesti ime/indeks kartice i ime profila"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Morate navesti ime/indeks slivnika i ime porta"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "Morate navesti ime/indeks izvora i ime porta"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Morate navesti ime/indeks slivnika i jačinu"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "Morate navesti ime/indeks izvora i jačinu"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "Morate navesti indeks ulaza slivnika i jačinu"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "Neispravan indeks ulaza slivnika"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "Morate navesti indeks izlaza izvora i izvor"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "Neispravan indeks ulaza slivnika"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "Morate navesti ime/indeks slivnika i logičku vrednost za isključivanje"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "Neispravan parametar uzorka"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "Morate navesti ime/indeks izvora i logičku vrednost za isključivanje"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr ""
+"Morate navesti indeks ulaza slivnika i logičku vrednost za isključivanje"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "Neispravan parametar indeksa ulaza slivnika"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "Morate navesti ime/indeks izvora i logičku vrednost za isključivanje"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "Neispravan parametar indeksa ulaza slivnika"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "Morate navesti ime/indeks slivnika i logičku vrednost za isključivanje"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "Nije navedena ispravna naredba."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D prikaz] [-S server] [-O slivnik] [-I izvor] [-c datoteka]  [-d|-e|-i|-"
+"r]\n"
+"\n"
+" -d    Prikaži trenutne PulseAudio podatke zakačene za X11 prikaz "
+"(podrazumevano)\n"
+" -e    Izvezi lokalne PulseAudio podatke na X11 prikaz\n"
+" -i    Uvezi PulseAudio podatke sa X11 prikaza u lokalne promenljive "
+"okruženja i datoteke kolačića.\n"
+" -r    Ukloni PulseAudio podatke sa X11 prikaza\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Neuspešno tumačenje komandne linije.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "Server: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "Izvor: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Slivnik: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Kolačić: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Neuspešno tumačenje podataka iz kolačića\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Neuspešno zapisivanje podataka kolačića\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "Neuspešno učitavanje klijentove datoteke podešavanja.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "Neuspešno čitanje podataka podešavanja za okruženje.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Neuspešno dobijanje FQDN-a.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Neuspešno učitavanje datoteka kolačića\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Nije još implementirano.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Nema pokrenutog PulseAudio demona, ili se ne izvršava kao demon sesije."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Nije uspelo ubijanje PulseAudio demona."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "Demon se ne odaziva."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "Nije moguće pristupiti datoteci zaključavanja za samoumnožavanje."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nas je probudila da bi zapisala nove podatke na uređaj, ali nema ništa "
+"da se zapiše!\n"
+"Ovo je najverovatnije greška u ALSA upravljačkom programu „%s“. Prijavite "
+"ovaj problem programerima ALSA-e.\n"
+"Probuđeni smo sa postavljenim POLLOUT-om -- ali sledeći snd_pcm_avail() je "
+"vratio 0 ili neku drugu vrednost manju od min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA nas je probudila da bi pročitala nove podatke iz uređaja, ali nema "
+"ništa da se pročita!\n"
+"Ovo je najverovatnije greška u ALSA upravljačkom programu „%s“. Prijavite "
+"ovaj problem programerima ALSA-e.\n"
+"Probuđeni smo sa postavljenim POLLIN-om -- ali sledeći snd_pcm_avail() je "
+"vratio 0 ili neku drugu vrednost manju od min_avail."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "Isključeno"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Reprodukcija visoke tačnosti (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Snimanje visoke tačnosti (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Dvosmerno telefoniranje (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio zvučni sistem"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "Izlazni uređaji"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "Ulazni uređaji"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "Audio na @HOSTNAME@"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "Ulaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "Ulaz priključne stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "Mikrofon priključne stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "Ulaz priključne stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "Linija u"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "Mikrofon priključne stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "Spoljni mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "Unutrašnji mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "Video"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "Samostalna kontrola pojačanja"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "Bez samostalne kontrole pojačanja"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "Podizanje"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "Bez podizanja"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "Pojačalo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "Bez pojačala"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "Podizanje"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "Bez podizanja"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "Analogne slušalice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "Analogni ulaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "Mikrofon priključne stanice"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "Analogni izlaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "Analogni izlaz (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "Linija u"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "Analogni mono izlaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "Analogni stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "Digitalni stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digitalni stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "Analogni mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "Analogni stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "Analogni okružujući 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "Analogni okružujući 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "Analogni okružujući 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "Analogni okružujući 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "Analogni okružujući 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "Analogni okružujući 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "Analogni okružujući 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "Analogni okružujući 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "Analogni okružujući 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "Analogni okružujući 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "Analogni okružujući 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "Digitalni stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digitalni stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digitalni okružujući 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digitalni okružujući 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "Digitalni stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digitalni okružujući 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "Dvosmerni analogni mono"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "Dvosmerni analogni stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Dvosmerni digitalni stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Prazan izlaz"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "Ulaz"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<ime slivnika> sink_properties=<svojstva slivnika> master=<ime "
+"slivnika za filtriranje> format=<format uzorka> rate=<učestanost "
+"diskretizacije> channels=<broj kanala> channel_map=<mapa kanala> plugin=<ime "
+"ladspa dodatka> label=<oznaka ladspa dodatka> control=<spisak ulaznih "
+"kontrolnih vrednosti razdvojenih zarezom>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit nije podržan na ovoj platformi."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "Neuspela funkcija XOpenDisplay()"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Izlaz izvora #%u\n"
+#~ "\tUpravljački program: %s\n"
+#~ "\tPripada modulu: %s\n"
+#~ "\tKlijent: %s\n"
+#~ "\tIzvor: %u\n"
+#~ "\tParametri uzorka: %s\n"
+#~ "\tMapa kanala: %s\n"
+#~ "\tKašnjenje bafera: %0.0f μs\n"
+#~ "\tKašnjenje izvora: %0.0f μs\n"
+#~ "\tNačin diskretizacije: %s\n"
+#~ "\tSvojstva:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [opcije] stat\n"
+#~ "%s [opcije] list\n"
+#~ "%s [opcije] exit\n"
+#~ "%s [opcije] upload-sample IMEDATOTEKE [IME]\n"
+#~ "%s [opcije] play-sample IME [SLIVNIK]\n"
+#~ "%s [opcije] remove-sample IME\n"
+#~ "%s [opcije] move-sink-input ULAZSLIVNIKA SLIVNIK\n"
+#~ "%s [opcije] move-source-output IZLAZIZVORA IZVOR\n"
+#~ "%s [opcije] load-module IME [ARG ...]\n"
+#~ "%s [opcije] unload-module MODUL\n"
+#~ "%s [opcije] suspend-sink SLIVNIK 1|0\n"
+#~ "%s [opcije] suspend-source IZVOR 1|0\n"
+#~ "%s [opcije] set-card-profile KARTICA PROFIL\n"
+#~ "%s [opcije] set-sink-port SLIVNIK PORT\n"
+#~ "%s [opcije] set-source-port IZVOR PORT\n"
+#~ "%s [opcije] set-sink-volume SLIVNIK JAČINA\n"
+#~ "%s [opcije] set-source-volume IZVOR JAČINA\n"
+#~ "%s [opcije] set-sink-input-volume ULAZSLIVNIKA JAČINA\n"
+#~ "%s [opcije] set-sink-mute SLIVNIK 1|0\n"
+#~ "%s [opcije] set-source-mute IZVOR 1|0\n"
+#~ "%s [opcije] set-sink-input-mute ULAZSLIVNIKA 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Prikaži ovu pomoć\n"
+#~ "      --version                         Prikaži verziju\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   Ime servera na koji se treba "
+#~ "povezati\n"
+#~ "  -n, --client-name=IME                 Kako nazvati ovog klijenta na "
+#~ "serveru\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Digitalni okružujući 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Zvučnik za niske frekvencije"
diff --git a/po/sv.po b/po/sv.po
new file mode 100644 (file)
index 0000000..75db613
--- /dev/null
+++ b/po/sv.po
@@ -0,0 +1,3239 @@
+# Swedish translation for pulseaudio.
+# Copyright © 2008-2017 Free Software Foundation, Inc.
+# This file is distributed under the same license as the pulseaudio package.
+# Daniel Nylander <po@danielnylander.se>, 2008, 2012.
+# Josef Andersson <josef.andersson@fripost.org>, 2014, 2017.
+#
+#
+# Termer:
+# input/output: ingång/utgång (det handlar om ljud)
+# latency: latens
+# delay: fördröjning
+# boost: öka
+# gain: förstärkning
+# channel map: kanalmappning
+# passthrough: genomströmning
+# och en hel del termer som inte översätts inom ljuddomänen, ex. surround.
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio\n"
+"Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
+"product=PulseAudio&keywords=I18N+L10N&component=misc\n"
+"POT-Creation-Date: 2017-03-19 15:27+0000\n"
+"PO-Revision-Date: 2017-03-19 23:14+0100\n"
+"Last-Translator: Josef Andersson <l10nl18nsweja@gmail.com>\n"
+"Language-Team: Svenska <tp-sv@listor.tp-sv.se>\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 1.8.11\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [flaggor]\n"
+"\n"
+"KOMMANDON:\n"
+"  -h, --help                            Visa denna hjälp\n"
+"      --version                         Visa version\n"
+"      --dump-conf                       Dumpa standardkonfiguration\n"
+"      --dump-modules                    Dumpa lista över tillgängliga "
+"moduler\n"
+"      --dump-resample-methods           Dumpa tillgängliga "
+"omsamplingsmetoder\n"
+"      --cleanup-shm                     Städa upp utgångna delade "
+"minnessegment\n"
+"      --start                           Starta demonen om den inte redan "
+"körs\n"
+"  -k  --kill                            Döda en demon\n"
+"      --check                           Kontrollera om det finns någon "
+"körande demon (returnerar bara returkod)\n"
+"\n"
+"FLAGGOR:\n"
+"      --system[=BOOL]                   Kör som en systemomfattande instans\n"
+"  -D, --daemonize[=BOOL]                Demonisera efter start\n"
+"      --fail[=BOOL]                     Avsluta om start misslyckas\n"
+"      --high-priority[=BOOL]            Försök att sätta en hög nice-nivå\n"
+"                                        (bara tillgängligt som root, när "
+"SUID eller\n"
+"                                        med förhöjd RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Försök att aktivera "
+"realtidsschemaläggning\n"
+"                                        (bara tillgängligt som root, när "
+"SUID eller\n"
+"                                        med förhöjd RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Förbjud modulanvändning av "
+"användarbegärd\n"
+"                                        inläsning/urläsning efter start\n"
+"      --disallow-exit[=BOOL]            Förbjud användarbegärt avslut\n"
+"      --exit-idle-time=SECS             Avsluta demonen vid inaktivitet och "
+"efter\n"
+"                                        denna tid\n"
+"      --scache-idle-time=SECS           Inaktivera autoinlästa samplingar "
+"vid inaktivitet och\n"
+"                                        efter denna tid\n"
+"      --log-level[=LEVEL]               Öka eller bestäm informativa nivån\n"
+"  -v  --verbose                         Öka informativa nivån\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Ange mål för loggen\n"
+"      --log-meta[=BOOL]                 Inkludera kodplats i "
+"loggmeddelanden\n"
+"      --log-time[=BOOL]                 Inkludera tidsstämpel i "
+"loggmeddelanden\n"
+"      --log-backtrace=FRAMES            Inkluderar bakåtspårning i "
+"loggmeddelanden\n"
+"  -p, --dl-search-path=PATH             Ange sökvägen för dynamiskt delade\n"
+"                                        objekt (insticksmoduler)\n"
+"      --resample-method=METHOD          Använd den angivna "
+"omsamplingsmetoden\n"
+"                                        (Se --dump-resample-methods för\n"
+"                                        möjliga värden)\n"
+"      --use-pid-file[=BOOL]             Skapa en PID-fil\n"
+"      --no-cpu-limit[=BOOL]             Installera inte en CPU-"
+"belastningsbegränsare\n"
+"                                        på plattformar som stöder det.\n"
+"      --disable-shm[=BOOL]              Inaktivera stöd för delat minne.\n"
+"      --enable-memfd[=BOOL]             Aktivera stöd för memfd-delat "
+"minne.\n"
+"\n"
+"STARTSKRIPT:\n"
+"  -L, --load=”MODUL ARGUMENT”           Läs in den angivna insticksmodulen "
+"med\n"
+"                                        det specificerade argumentet\n"
+"  -F, --file=FILNAMN                    Kör det angivna skriptet\n"
+"  -C                                    Öppna en kommandorad på körande TTY\n"
+"                                        efter start\n"
+"\n"
+"  -n                                    Läs inte in standardskriptfil\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "--fail förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level förväntar sig loggnivåargument (antagligen numeriska i "
+"intervallet 0..4 eller en av debug, info, notice, warn, error)."
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "--realtime förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Ogiltigt mål för loggen: använd ”syslog”, ”journal”, ”stderr”, ”auto” eller "
+"ett giltigt filnamn ”file:<sökväg>”, ”newfile:<sökväg>”."
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Ogiltigt mål för loggen: använd ”syslog”, ”journal”, ”stderr”, ”auto” eller "
+"ett giltigt filnamn ”file:<sökväg>”, ”newfile:<sökväg>”."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "--log-time förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Ogiltig omsamplingsmetod ”%s”."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "--system förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm förväntar sig ett booleskt argument"
+
+#: ../src/daemon/cmdline.c:397
+msgid "--enable-memfd expects boolean argument"
+msgstr "--enable-memfd förväntar sig ett booleskt argument"
+
+#: ../src/daemon/daemon-conf.c:262
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Ogiltigt mål för loggen ”%s”."
+
+#: ../src/daemon/daemon-conf.c:277
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Ogiltig loggnivå ”%s”."
+
+#: ../src/daemon/daemon-conf.c:292
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Ogiltig omsamplingsmetod ”%s”."
+
+#: ../src/daemon/daemon-conf.c:314
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Ogiltig rlimit ”%s”."
+
+#: ../src/daemon/daemon-conf.c:334
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Ogiltigt samplingsformat ”%s”."
+
+#: ../src/daemon/daemon-conf.c:351 ../src/daemon/daemon-conf.c:368
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Ogiltig samplingsfrekvens ”%s”."
+
+#: ../src/daemon/daemon-conf.c:391
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Ogiltiga samplingskanaler ”%s”."
+
+#: ../src/daemon/daemon-conf.c:408
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Ogiltig kanalmappning ”%s”."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Ogiltigt antal fragment ”%s”."
+
+#: ../src/daemon/daemon-conf.c:442
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Ogiltig fragmentstorlek ”%s”."
+
+#: ../src/daemon/daemon-conf.c:459
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Ogiltig nice-nivå ”%s”."
+
+#: ../src/daemon/daemon-conf.c:502
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Ogiltig servertyp ”%s”."
+
+#: ../src/daemon/daemon-conf.c:620
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Misslyckades med att öppna konfigurationsfil: %s"
+
+#: ../src/daemon/daemon-conf.c:636
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Den angivna standardkanalmappningen har ett annat antal kanaler än den "
+"angivna standardkanalmappningens antal."
+
+#: ../src/daemon/daemon-conf.c:723
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Läs från konfigurationsfilen: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Namn: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Ingen modulinformation tillgänglig\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Version: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Beskrivning: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Upphovsman: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Användning: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Läs in en gång: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "FÖRÅLDRADVARNING: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Sökväg: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Misslyckades med att öppna modulen %s: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Misslyckades med att hitta original-lt_dlopen loader."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Misslyckades med att allokera en ny dl loader."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Misslyckades med att lägga till bind-now-loader."
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Misslyckades med att hitta användaren ”%s”."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Misslyckades med att hitta gruppen ”%s”."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID för användare ”%s” och för grupp ”%s” stämmer inte överens."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Hemkatalogen för användaren ”%s” är inte ”%s”, ignorerar."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Misslyckades med att skapa ”%s”: %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Misslyckades med att ändra grupplista: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Misslyckades med att ändra GID: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Misslyckades med att ändra UID: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "Systemomfattande läge stöds inte på denna plattform."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Misslyckades med att tolka kommandoraden."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Systemläge vägrades för icke-rootanvändare. Startar bara D-Bus-"
+"serveruppslagningstjänsten."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Misslyckades med att döda demonen: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Detta program är inte tänkt att köras som root (såvida inte --system har "
+"angivits)."
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Root-behörighet krävs."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "--start stöds inte för systeminstanser."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr "Användaranpassad server på %s, vägrar starta/autostarta."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Användaranpassad server på %s, som ser ut att vara lokal. Undersöker djupare."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr "Kör i systemläge, men --disallow-exit är inte angett."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr "Kör i systemläge, men --disallow-module-loading är inte angett."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr "Kör i systemläge, tvingar fram inaktivering av SHM-läge."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr "Kör i systemläge, tvingar fram avslut vid inaktivitet."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "Misslyckades med att få stdio."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() misslyckades: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() misslyckades: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:562
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() misslyckades: %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Demonstart misslyckades."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() misslyckades: %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Misslyckades med att hämta maskin-ID"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually "
+"do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Ok, du kör PA i systemläge. Försäkra dig om att du verkligen vill göra "
+"detta.\n"
+"Läs http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/"
+"WhatIsWrongWithSystemWide/ för en förklaring till varför systemläge "
+"vanligtvis är en dålig idé."
+
+#: ../src/daemon/main.c:990
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() misslyckades."
+
+#: ../src/daemon/main.c:1022
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() misslyckades."
+
+#: ../src/daemon/main.c:1092
+msgid "Failed to initialize daemon."
+msgstr "Misslyckades med att initiera demon."
+
+#: ../src/daemon/main.c:1097
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Demonen startade utan inlästa moduler, fungerar inte."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio ljudsystem"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Starta ljudsystemet PulseAudio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Ingång"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Ingång för dockningsstation"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Mikrofon för dockningsstation"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Linje in för dockningsstation"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Linje in"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2103
+#: ../src/modules/bluetooth/module-bluez5-device.c:1727
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Frontmikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Bakre mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Extern mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Intern mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Radio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Video"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Automatisk förstärkningskontroll"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Ingen automatisk förstärkningskontroll"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Ökning"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Ingen ökning"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Förstärkare"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Ingen förstärkare"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Basökning"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Ingen basökning"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2108
+#: ../src/modules/bluetooth/module-bluez5-device.c:1734
+msgid "Speaker"
+msgstr "Högtalare"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Hörlurar"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Analog ingång"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Dockmikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Headset-mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Analog utgång"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "LFE på separat monoutgång"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Linje ut"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Analog monoutgång"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Högtalare"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Digital utgång (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Digital ingång (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digital genomströmning (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Multikanalingång"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Multikanalutgång"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Analog mono"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Analog stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Multikanal"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Analog surround 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Analog surround 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Analog surround 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Analog surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Analog surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Analog surround 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Analog surround 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Analog surround 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Analog surround 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Analog surround 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Analog surround 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Digital stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digital genomströmning (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digital surround 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digital surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Digital surround 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Digital stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digital surround 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4151
+msgid "Analog Mono Duplex"
+msgstr "Analog mono duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Stereo Duplex"
+msgstr "Analog stereo duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Digital stereo duplex (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Multichannel Duplex"
+msgstr "Multikanalduplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2323
+#: ../src/modules/bluetooth/module-bluez5-device.c:1982
+msgid "Off"
+msgstr "Av"
+
+#: ../src/modules/alsa/alsa-mixer.c:4254
+#, c-format
+msgid "%s Output"
+msgstr "%s utgång"
+
+#: ../src/modules/alsa/alsa-mixer.c:4262
+#, c-format
+msgid "%s Input"
+msgstr "%s ingång"
+
+#: ../src/modules/alsa/alsa-sink.c:572
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA väckte oss för att skriva ny data till enheten, men det fanns inget att "
+"skriva.\n"
+"Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera "
+"detta problem till ALSA-utvecklarna.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+
+#: ../src/modules/alsa/alsa-sink.c:756
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA väckte oss för att skriva ny data till enheten, men det fanns inget att "
+"skriva!\n"
+"Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera "
+"detta problem till ALSA-utvecklarna.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA väckte oss för att läsa ny data från enheten, men det fanns inget att "
+"läsa.\n"
+"Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera "
+"detta problem till ALSA-utvecklarna.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA väckte oss för att läsa ny data från enheten, men det fanns inget att "
+"läsa!\n"
+"Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera "
+"detta problem till ALSA-utvecklarna.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1168 ../src/modules/alsa/alsa-util.c:1243
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() returnerade ett värde som är exceptionellt stort: %lu byte "
+"(%lu ms).\n"
+"Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera "
+"problemet till ALSA-utvecklarna."
+
+#: ../src/modules/alsa/alsa-util.c:1218
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() returnerade ett värde som är exceptionellt stort: %li byte "
+"(%s%lu ms).\n"
+"Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera "
+"problemet till ALSA-utvecklarna."
+
+#: ../src/modules/alsa/alsa-util.c:1259
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() returnerade konstiga värden: fördröjningen %lu är "
+"mindre än tillgängliga %lu.\n"
+"Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera "
+"problemet till ALSA-utvecklarna."
+
+#: ../src/modules/alsa/alsa-util.c:1302
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() returnerade ett värde som är exceptionellt stort: %lu "
+"byte (%lu ms).\n"
+"Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera "
+"problemet till ALSA-utvecklarna."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2093
+#: ../src/modules/bluetooth/module-bluez5-device.c:1717
+msgid "Headset"
+msgstr "Headset"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2098
+#: ../src/modules/bluetooth/module-bluez5-device.c:1722
+msgid "Handsfree"
+msgstr "Handsfree"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2113
+#: ../src/modules/bluetooth/module-bluez5-device.c:1740
+msgid "Headphone"
+msgstr "Hörlurar"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2118
+#: ../src/modules/bluetooth/module-bluez5-device.c:1745
+msgid "Portable"
+msgstr "Bärbar"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2123
+#: ../src/modules/bluetooth/module-bluez5-device.c:1750
+msgid "Car"
+msgstr "Bil"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2128
+#: ../src/modules/bluetooth/module-bluez5-device.c:1755
+msgid "HiFi"
+msgstr "HiFi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2133
+#: ../src/modules/bluetooth/module-bluez5-device.c:1760
+msgid "Phone"
+msgstr "Telefon"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2141
+#: ../src/modules/bluetooth/module-bluez5-device.c:1712
+#: ../src/modules/bluetooth/module-bluez5-device.c:1728
+#: ../src/modules/bluetooth/module-bluez5-device.c:1766
+msgid "Bluetooth Output"
+msgstr "Bluetooth-utgång"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2144
+#: ../src/modules/bluetooth/module-bluez5-device.c:1711
+#: ../src/modules/bluetooth/module-bluez5-device.c:1733
+#: ../src/modules/bluetooth/module-bluez5-device.c:1739
+#: ../src/modules/bluetooth/module-bluez5-device.c:1765
+msgid "Bluetooth Input"
+msgstr "Bluetooth-ingång"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2185
+msgid "High Fidelity Playback (A2DP)"
+msgstr "High fidelity playback (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2197
+msgid "High Fidelity Capture (A2DP)"
+msgstr "High fidelity capture (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2209
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telephony duplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2222
+msgid "Handsfree Gateway"
+msgstr "Handsfree gateway"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1807
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "High fidelity playback (A2DP Sink)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1819
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "High fidelity capture (A2DP Source)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1831
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Headset head unit (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1844
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Headset audio gateway (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<namn på källa> source_properties=<egenskaper för källa> "
+"source_master=<namn på källa att filtrera> sink_name=<namn på mottagare> "
+"sink_properties=<egenskaper för mottagare> sink_master=<namn på mottagare "
+"att filtrera> adjust_time=<hur ofta frekvens ska justeras i s> "
+"adjust_threshold=<hur mycket avsteg ska justeras i ms> "
+"format=<samplingsformat> rate=<samplingshastighet> channels=<antal kanaler> "
+"channel_map=<kanalmappning> aec_method=<implementation att använda> "
+"aec_args=<parametrar för AEC-motorn> save_aec=<spara AEC-data i /tmp> "
+"autoloaded=<ange om denna modul läses in automatiskt> use_volume_sharing=<ja "
+"eller nej> use_master_format=<ja eller nej> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "På"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Attrapputgång"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Håll alltid minst en mottagare inläst även om den är null"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Allmän equalizer"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<namn på mottagare> sink_properties=<egenskaper för mottagare> "
+"sink_master=<mottagare att ansluta till> format=<samplingsformat> "
+"rate=<samplingshastighet> channels=<antal kanaler> "
+"channel_map=<kanalmappning> autoloaded=<om denna modul läses in automatiskt> "
+"use_volume_sharing=<ja eller nej> "
+
+#: ../src/modules/module-filter-apply.c:47
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<automatiskt inaktivera oanvända filter?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Virtual LADSPA-mottagare"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<namn på mottagare> sink_properties=<egenskaper för mottagare> "
+"master=<namn på mottagare att filtrera> format=<samplingsformat> "
+"rate=<samplingshastighet> channels=<antal kanaler> "
+"channel_map=<ingångskanalmappning> plugin=<namn på ladspa-instick> "
+"label=<etikett för ladspa-instick> control=<kommaseparerad lista över "
+"ingångskontrollvärden> input_ladspaport_map=<kommaseparerad lista över "
+"LADSPA-portnamn för ingång> output_ladspaport_map=<kommaseparerad lista över "
+"LADSPA-portnamn för utgång> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Klockad NULL-mottagare"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Nullutgång"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Utgångsenheter"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Ingångsenheter"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Ljud på @HOSTNAME@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Tunnel för %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:521
+#: ../src/modules/module-tunnel-source-new.c:520
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Tunnel till %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Virtual surround-mottagare"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/"
+"left_hrir.wav "
+msgstr ""
+"sink_name=<namn för mottagaren> sink_properties=<egenskaper för mottagaren> "
+"master=<namn på mottagare att filtrera> format=<samplingsformat> "
+"rate=<samplingshastighet> channels=<antal kanaler> "
+"channel_map=<kanalmappning> use_volume_sharing=<ja eller nej> "
+"force_flat_volume=<ja eller nej> hrir=/path/to/left_hrir.wav "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio ljudserver"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Mono"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Center fram"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Vänster fram"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Höger fram"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Center bak"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Vänster bak"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Höger bak"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Subwoofer"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Vänster-om-center fram"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Höger-om-center fram"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Vänster sida"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Höger sida"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Auxiliary 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Auxiliary 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Auxiliary 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Auxiliary 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Auxiliary 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Auxiliary 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Auxiliary 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Auxiliary 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Auxiliary 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Auxiliary 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Auxiliary 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Auxiliary 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Auxiliary 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Auxiliary 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Auxiliary 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Auxiliary 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Auxiliary 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Auxiliary 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Auxiliary 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Auxiliary 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Auxiliary 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Auxiliary 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Auxiliary 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Auxiliary 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Auxiliary 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Auxiliary 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Auxiliary 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Auxiliary 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Auxiliary 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Auxiliary 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Auxiliary 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Auxiliary 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Topp mitten"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Topp fram mitten"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Topp fram vänster"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Upp fram höger"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Topp bak mitten"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Topp bak vänster"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Topp bak höger"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:174 ../src/pulse/volume.c:294
+#: ../src/pulse/volume.c:320 ../src/pulse/volume.c:340
+#: ../src/pulse/volume.c:372 ../src/pulse/volume.c:412
+#: ../src/pulse/volume.c:431
+msgid "(invalid)"
+msgstr "(ogiltig)"
+
+#: ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() misslyckades"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() returnerade true"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Misslyckades med att tolka kakdata"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Tog emot meddelande för okända tillägget ”%s”"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "ingång"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "utgång"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "dubbelriktad"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "ogiltig"
+
+#: ../src/pulsecore/core-util.c:1856
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"XDG_RUNTIME_DIR (%s) ägs inte av oss (uid %d), utan av uid %d! (Det kan "
+"hända om du exempelvis försöker att ansluta till en icke-root PulseAudio som "
+"en root-användare över det interna protokollet. Gör inte det.)"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "ja"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "nej"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Kan inte komma åt låset för autospawn."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Misslyckades med att öppna målfilen ”%s”."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Försökte att öppna målfilen ”%s”, ”%s.1”, ”%s.2” … ”%s.%d” men misslyckades."
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Ogiltigt mål för logg."
+
+#: ../src/pulsecore/sink.c:3458
+msgid "Built-in Audio"
+msgstr "Inbyggt ljud"
+
+#: ../src/pulsecore/sink.c:3463
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "OK"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Åtkomst nekad"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Okänt kommando"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Ogiltigt argument"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "Entiteten finns"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Ingen sådan entitet"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Anslutning nekades"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Protokollfel"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Tidsgräns nåddes"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Ingen autentiseringsnyckel"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Internt fel"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Anslutningen terminerad"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Entitet dödad"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Ogiltig server"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Misslyckades med att starta modul"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Felaktigt tillstånd"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Ingen data"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Inkompatibel protokollversion"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "För stor"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Stöds inte"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Okänd felkod"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Det finns inget sådant tillägg"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Föråldrad funktionalitet"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Implementering saknas"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Klienten förgrenad"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "In-/utgångsfel"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Enhet eller resurs upptagen"
+
+#: ../src/pulse/sample.c:176
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %u kan. %u Hz"
+
+#: ../src/pulse/sample.c:188
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:190
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:192
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:194
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:134
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Misslyckades med att dränera strömmen: %s"
+
+#: ../src/utils/pacat.c:139
+msgid "Playback stream drained."
+msgstr "Uppspelningsströmmen dränerad."
+
+#: ../src/utils/pacat.c:150
+msgid "Draining connection to server."
+msgstr "Dränerar anslutning till servern."
+
+#: ../src/utils/pacat.c:163
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:194 ../src/utils/pacat.c:543
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() misslyckades: %s"
+
+#: ../src/utils/pacat.c:244 ../src/utils/pacat.c:274
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() misslyckades: %s"
+
+#: ../src/utils/pacat.c:324
+msgid "Stream successfully created."
+msgstr "Strömmen skapad."
+
+#: ../src/utils/pacat.c:327
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() misslyckades: %s"
+
+#: ../src/utils/pacat.c:331
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Buffertmått: maxlength=%u tlength=%u prebuf=%u minreq=%u"
+
+#: ../src/utils/pacat.c:334
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Buffertmått: maxlength=%u fragsize=%u"
+
+#: ../src/utils/pacat.c:338
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Använder samplingsspec ”%s”, kanalmappning ”%s”."
+
+#: ../src/utils/pacat.c:342
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Ansluten till enheten %s (index:%u, vänteläge: %s)."
+
+#: ../src/utils/pacat.c:352
+#, c-format
+msgid "Stream error: %s"
+msgstr "Strömfel: %s"
+
+#: ../src/utils/pacat.c:362
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Strömenheten avstängd.%s"
+
+#: ../src/utils/pacat.c:364
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Strömenheten återaktiverad.%s"
+
+#: ../src/utils/pacat.c:372
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Strömmen underskriden.%s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Strömmen överskriden.%s"
+
+#: ../src/utils/pacat.c:386
+#, c-format
+msgid "Stream started.%s"
+msgstr "Strömmen startad.%s"
+
+#: ../src/utils/pacat.c:393
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Strömmen flyttad till enhet %s (%u, %savstängd).%s"
+
+#: ../src/utils/pacat.c:393
+msgid "not "
+msgstr "inte "
+
+#: ../src/utils/pacat.c:400
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Strömbuffertattribut ändrade.%s"
+
+#: ../src/utils/pacat.c:415
+msgid "Cork request stack is empty: corking stream"
+msgstr "Korkningsbegäransstacken är tom: Korkar igen ström"
+
+#: ../src/utils/pacat.c:421
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Korkningsbegäransstacken är tom: Korkar ur ström"
+
+#: ../src/utils/pacat.c:425
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr "Varning: Mottog fler urkorkningsbegäran än korkningsbegäran."
+
+#: ../src/utils/pacat.c:450
+#, c-format
+msgid "Connection established.%s"
+msgstr "Anslutning etablerad.%s"
+
+#: ../src/utils/pacat.c:453
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() misslyckades: %s"
+
+#: ../src/utils/pacat.c:491
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() misslyckades: %s"
+
+#: ../src/utils/pacat.c:497
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Misslyckades med att ange övervakningsströmmen: %s"
+
+#: ../src/utils/pacat.c:501
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() misslyckades: %s"
+
+#: ../src/utils/pacat.c:514 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Anslutningsfel: %s"
+
+#: ../src/utils/pacat.c:557
+msgid "Got EOF."
+msgstr "Fick filslut."
+
+#: ../src/utils/pacat.c:581
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() misslyckades: %s"
+
+#: ../src/utils/pacat.c:605
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() misslyckades: %s"
+
+#: ../src/utils/pacat.c:626
+msgid "Got signal, exiting."
+msgstr "Fick signal, avslutar."
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Misslyckades med att avgöra latens: %s"
+
+#: ../src/utils/pacat.c:645
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Tid: %0.3f s; Latens: %0.0f μs."
+
+#: ../src/utils/pacat.c:666
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() misslyckades: %s"
+
+#: ../src/utils/pacat.c:676
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [flaggor]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Visa denna hjälp\n"
+"      --version                         Visa version\n"
+"\n"
+"  -r, --record                          Skapa en anslutning för inspelning\n"
+"  -p, --playback                        Skapa en anslutning för uppspelning\n"
+"\n"
+"  -v, --verbose                         Aktivera informativa åtgärder\n"
+"\n"
+"  -s, --server=SERVER                   Namn på server att ansluta till\n"
+"  -d, --device=ENHET                    Namn på mottagare/källa att ansluta "
+"till\n"
+"  -n, --client-name=NAMN                Hur denna klient ska anropas på "
+"servern\n"
+"      --stream-name=NAMN                Hur denna ström ska anropas på "
+"servern\n"
+"      --volume=VOLUM                    Ange initiala (linjära) volymen i "
+"intervallet 0...65536\n"
+"      --rate=SAMPLINGSFREKVENS          Samplingsfrekvens i Hz (standard "
+"44100)\n"
+"      --format=SAMPLINGSFORMAT          Samplingstyp, en av s16le, s16be, "
+"u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=KANALER                Antalet kanaler, 1 för mono, 2 för "
+"stereo\n"
+"                                        (standard 2)\n"
+"      --channel-map=KANALMAPPNING       Kanalmappning att använda istället "
+"för standard\n"
+"      --fix-format                      Ta samplingsformatet från mottagaren/"
+"källan strömmen är\n"
+"                                        ansluten till.\n"
+"      --fix-rate                        Ta samplingsfrekvensen från "
+"mottagaren/källan strömmen är\n"
+"                                        ansluten till.\n"
+"      --fix-channels                    Ta antalet kanaler och "
+"kanalmappning\n"
+"                                        från mottagaren/källan strömmen är "
+"ansluten till.\n"
+"      --no-remix                        Mixa inte ner eller upp kanaler.\n"
+"      --no-remap                        Mappa kanaler med index istället för "
+"med namn.\n"
+"      --latency=BYTE                    Begär angiven latens i byte.\n"
+"      --process-time=BYTE               Begär angiven processtid per begäran "
+"i byte.\n"
+"      --latency-msec=MS                 Begär angiven latens i ms.\n"
+"      --process-time-msec=MS            Begär angiven processtid per begäran "
+"i ms.\n"
+"      --property=EGENSKAP=VÄRDE         Sätt angiven egenskap till angivet "
+"värde.\n"
+"      --raw                             Spela in/spela upp rå PCM-data.\n"
+"      --passthrough                     Genomströmningsdata.\n"
+"      --file-format[=FFORMAT]           Spela in/spela upp formaterad PCM-"
+"data.\n"
+"      --list-file-formats               Lista tillgängliga filformat.\n"
+"      --monitor-stream=INDEX            Spela in från mottagaringången med "
+"index INDEX.\n"
+
+#: ../src/utils/pacat.c:793
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr "Spela upp kodade ljudfiler på en PulseAudio-server."
+
+#: ../src/utils/pacat.c:797
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr "Fånga ljuddata från en PulseAudio-ljudserver och skriv den till fil."
+
+#: ../src/utils/pacat.c:801
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+"Fånga ljuddata från en PulseAudio-ljudserver och skriv den till STDOUT eller "
+"angiven fil."
+
+#: ../src/utils/pacat.c:805
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+"Spela upp ljuddata från STDIN eller den angivna filen, på en PulseAudio-"
+"server."
+
+#: ../src/utils/pacat.c:819
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Kompilerade med libpulse %s\n"
+"Länkade med libpulse %s\n"
+
+#: ../src/utils/pacat.c:852 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Ogiltigt klientnamn ”%s”"
+
+#: ../src/utils/pacat.c:867
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Ogiltigt namn på strömmen ”%s”"
+
+#: ../src/utils/pacat.c:904
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Ogiltig kanalmappning ”%s”"
+
+#: ../src/utils/pacat.c:933 ../src/utils/pacat.c:947
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Ogiltig latensangivelse ”%s”"
+
+#: ../src/utils/pacat.c:940 ../src/utils/pacat.c:954
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Ogiltig angivelse av processtid ”%s”"
+
+#: ../src/utils/pacat.c:966
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Ogiltig egenskap ”%s”"
+
+#: ../src/utils/pacat.c:985
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Okänt filformat %s."
+
+#: ../src/utils/pacat.c:1000
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Misslyckades med att tolka argumentet för --monitor-stream"
+
+#: ../src/utils/pacat.c:1011
+msgid "Invalid sample specification"
+msgstr "Ogiltig samplingsspecifikation"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1026
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1033
+msgid "Too many arguments."
+msgstr "För många argument."
+
+#: ../src/utils/pacat.c:1044
+msgid "Failed to generate sample specification for file."
+msgstr "Misslyckades med att generera samplingsspecifikation för filen."
+
+#: ../src/utils/pacat.c:1070
+msgid "Failed to open audio file."
+msgstr "Misslyckades med att öppna ljudfilen."
+
+#: ../src/utils/pacat.c:1076
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Varning: angiven samplingsspecifikation kommer att skrivas över med "
+"specifikation från filen."
+
+#: ../src/utils/pacat.c:1079 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Misslyckades med att avgöra samplingsspecifikation från filen."
+
+#: ../src/utils/pacat.c:1088
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Varning: Misslyckades med att avgöra kanalmappningen från filen."
+
+#: ../src/utils/pacat.c:1099
+msgid "Channel map doesn't match sample specification"
+msgstr "Kanalmappning stämmer inte överens med samplingsspecifikationen"
+
+#: ../src/utils/pacat.c:1110
+msgid "Warning: failed to write channel map to file."
+msgstr "Varning: Misslyckades med att skriva kanalmappningen till filen."
+
+#: ../src/utils/pacat.c:1125
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Öppnar en %s-ström med samplingsspecifikationen ”%s” och kanalmappningen "
+"”%s”."
+
+#: ../src/utils/pacat.c:1126
+msgid "recording"
+msgstr "inspelning"
+
+#: ../src/utils/pacat.c:1126
+msgid "playback"
+msgstr "uppspelning"
+
+#: ../src/utils/pacat.c:1150
+msgid "Failed to set media name."
+msgstr "Misslyckades med att ange medienamn."
+
+#: ../src/utils/pacat.c:1160 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() misslyckades."
+
+#: ../src/utils/pacat.c:1183
+msgid "io_new() failed."
+msgstr "io_new() misslyckades."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() misslyckades."
+
+#: ../src/utils/pacat.c:1198 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() misslyckades: %s"
+
+#: ../src/utils/pacat.c:1204
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() misslyckades."
+
+#: ../src/utils/pacat.c:1211 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() misslyckades."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "NAMN [ARG …]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "NAMN|#N"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "NAMN"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "NAMN|#N VOLYM"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N VOLYM"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "NAMN|#N 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "NAMN|#N NYCKEL=VÄRDE"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#N NYCKEL=VÄRDE"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "NAMN MOTTAGARE|#N"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "NAMN FILNAMN"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "SÖKVÄGSNAMN"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "FILNAMN MOTTAGARE|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#N MOTTAGARE|KÄLLA"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "KORT PROFIL"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "NAMN|#N PORT"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "KORT-NAMN|KORT-#N PORT OFFSET"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "MÅL"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "NUMERISK NIVÅ"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "LJUDRUTOR"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Visa denna hjälp\n"
+"      --version                         Visa version\n"
+"När inget kommando anges startar pacmd i det interaktiva läget.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Kompilerad med libpulse %s\n"
+"Länkad med libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "Ingen PulseAudio-demon körs, eller körs ej som en sessionsdemon."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "Misslyckades med att döda PulseAudio-demon."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Demon svarar ej."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Misslyckades med att hämta statistik: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Används för närvarande: %u block innehållande %s byte totalt.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Allokerade under hela livstiden: %u block innehållande %s byte totalt.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Samplingscachestorlek: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Misslyckades med att hämta serverinformation: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Serversträng: %s\n"
+"Biblioteksprotokollversion: %u\n"
+"Serverprotokollversion: %u\n"
+"Lokal: %s\n"
+"Klientindex: %u\n"
+"Blockstorlek: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Användarnamn: %s\n"
+"Värdnamn: %s\n"
+"Servernamn: %s\n"
+"Serverversion: %s\n"
+"Förvald samplingsspecifikation: %s\n"
+"Förvald kanalmappning: %s\n"
+"Förvald mottagare: %s\n"
+"Förvald källa: %s\n"
+"Kaka: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Misslyckades med att få information om mottagare: %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Mottagare #%u\n"
+"\tTillstånd: %s\n"
+"\tNamn: %s\n"
+"\tBeskrivning: %s\n"
+"\tDrivrutin: %s\n"
+"\tSamplingsspecifikation: %s\n"
+"\tKanalmappning: %s\n"
+"\tÄgarmodul: %u\n"
+"\tTyst: %s\n"
+"\tVolym: %s\n"
+"\t        balans %0.2f\n"
+"\tBasvolym: %s\n"
+"\tÖvervakarkälla: %s\n"
+"\tLatens: %0.0f µs, anpassad %0.0f µs\n"
+"\tFlaggor: %s%s%s%s%s%s%s\n"
+"\tEgenskaper:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPortar:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tAktiv port: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tFormat:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Misslyckades med att få information om källa: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Källa nr %u\n"
+"\tTillstånd: %s\n"
+"\tNamn: %s\n"
+"\tBeskrivning: %s\n"
+"\tDrivrutin: %s\n"
+"\tSamplingsspecifikation: %s\n"
+"\tKanalmappning: %s\n"
+"\tÄgarmodul: %u\n"
+"\tTystad: %s\n"
+"\tVolym: %s\n"
+"\t       balans %0.2f\n"
+"\tBasvolym: %s\n"
+"\tÖvervakare för mottagare: %s\n"
+"\tLatens: %0.0f µs, konfigurerad %0.0f µs\n"
+"\tFlaggor: %s%s%s%s%s%s\n"
+"\tEgenskaper:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "ej tillämpligt"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Misslyckades med att få modulinformation: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modul nr. %u\n"
+"\tNamn: %s\n"
+"\tArgument: %s\n"
+"\tAnvändningsräknare: %s\n"
+"\tEgenskaper:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Misslyckades med att få klientinformation: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Klient #%u\n"
+"\tDrivrutin: %s\n"
+"\tÄgarmodul: %s\n"
+"\tEgenskaper:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Misslyckades med att få kortinformation: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Kort #%u\n"
+"\tNamn: %s\n"
+"\tDrivrutin: %s\n"
+"\tÄgarmodul: %s\n"
+"\tEgenskaper:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfiler:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr ""
+"\t\t%s: %s (mottagare: %u, källor: %u, prioritet: %u, tillgängliga: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tAktiv profil: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tEgenskaper:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tDel av profil(er): %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Misslyckades med att få ingångsinformation för mottagaren: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Mottagaringång #%u\n"
+"\tDrivrutin: %s\n"
+"\tÄgarmodul: %s\n"
+"\tKlient: %s\n"
+"\tMottagare: %u\n"
+"\tSamplingsspecifikation: %s\n"
+"\tKanalmappning: %s\n"
+"\tFormat: %s\n"
+"\tKorkad: %s\n"
+"\tTyst: %s\n"
+"\tVolym: %s\n"
+"\t        balans %0.2f\n"
+"\tBuffertlatens: %0.0f µs\n"
+"\tMottagarlatens: %0.0f µs\n"
+"\tOmsamplingsmetod: %s\n"
+"\tEgenskaper:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Misslyckades med att få information om källutgång: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Källutgång #%u\n"
+"\tDrivrutin: %s\n"
+"\tÄgarmodul: %s\n"
+"\tKlient: %s\n"
+"\tKälla: %u\n"
+"\tSamplingsspecifikation: %s\n"
+"\tKanalmappning: %s\n"
+"\tFormat: %s\n"
+"\tKorkad: %s\n"
+"\tTyst: %s\n"
+"\tVolym: %s\n"
+"\t        balans %0.2f\n"
+"\tBuffertlatens: %0.0f µs\n"
+"\tKällatens: %0.0f µs\n"
+"\tOmsamplingsmetod: %s\n"
+"\tEgenskaper:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Misslyckades med att få samplingsinformation: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sampling nr. %u\n"
+"\tNamn: %s\n"
+"\tSamplingsspecifikation: %s\n"
+"\tKanalmappning: %s\n"
+"\tVolym: %s\n"
+"\t       balans %0.2f\n"
+"\tVaraktighet: %0.1f s\n"
+"\tStorlek: %s\n"
+"\tLat: %s\n"
+"\tFilnamn: %s\n"
+"\tEgenskaper:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Misslyckande: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Misslyckades med att stänga modul: Modulen %s är inte aktiv"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Misslyckades att sätta volym: Du försökte att sätta volymer för %d kanaler, "
+"medan antalet kanaler som stöds = %d\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Misslyckades med att ange format: ogiltig formatsträng %s"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Misslyckades med att skicka upp samplingen: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "För tidigt filslut"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "ny"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "ändra"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "ta bort"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "okänd"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "mottagare"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "källa"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "mottagaringång"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "källutgång"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "modul"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "klient"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "sample-cache"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "server"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "kort"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Händelse '%s' på %s #%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "Fick SIGINT, avslutar."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Ogiltig volymangivelse"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Volym utanför tillåtet intervall.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "Ogiltigt antal volymspecifikationer.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "Inkonsekvent volymspecifikation.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[flaggor]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[TYP]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "FILNAMN [NAMN]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "NAMN [MOTTAGARE]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "NAMN|#N VOLYM [VOLYM …]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#N VOLYM [VOLYM …]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "NAMN|#N 1|0|växla"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|växla"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "#N FORMAT"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Specialnamnen @DEFAULT_SINK@, @DEFAULT_SOURCE och @DEFAULT_MONITOR@\n"
+"kan användas för att ange standardmottagare, källa och övervakare.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Visa detta hjälpmeddelande\n"
+"      --version                         Visa version\n"
+"\n"
+"  -s, --server=SERVER                   Namnet på servern att ansluta till\n"
+"  -n, --client-name=NAMN                Vad klienten ska kallas på servern\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Kompilerad med libpulse %s\n"
+"Länkad med libpulse %s\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Specificera inget, eller endera av: %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Ange en samplingsfil att ladda"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Misslyckades med att öppna ljudfilen."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+"Varning: Misslyckades med att avgöra samplingsspecifikationen från filen."
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Du måste ange ett samplingsnamn att spela"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Du måste ange ett samplingsnamn att ta bort"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "Du måste ange ett index för en ingångsmottagare och en mottagare"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "Du måste ange ett index för en källutgång och en källa"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Du måste ange ett modulnamn och argument."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "Du måste ange ett modulindex eller namn"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Du får inte ange fler än en mottagare. Du måste ange ett booleskt värde."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Ogiltig avstängningsspecifikation."
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr "Du får inte ange fler än en källa. Du måste ange ett booleskt värde."
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Du måste ange ett kortnamn/-index och ett profilnamn"
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Du måste ange ett mottagarnamn/-index och ett portnamn"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "Du måste ange namn på en mottagare"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "Du måste ange ett källnamn/-index och ett portnamn"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Du måste ange namn på en källa"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Du måste ange ett mottagarnamn/-index och en volym"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Du måste ange ett källnamn/-index och en volym"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "Du måste ange ett index för en mottagaringång och en volym"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "Ogiltigt index för mottagaringång"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "Du måste ange ett källutgångsindex och en volym"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Ogiltigt källutgångsindex"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Du måste ange en mottagarnamn/-index och ett dämpningsvärde (0, 1, eller "
+"”toggle”)"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Ogiltig dämpningsspecifikation"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Du måste ange ett källnamn/-index och ett dämpningsvärde (0, 1, eller "
+"”toggle”)"
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Du måste ange ett index för en mottagaringång och ett dämpningsvärde (0, 1, "
+"eller ”toggle”)"
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "Ogiltig angivelse av index för mottagaringång"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+"Du måste ange ett källutgångsindex och ett dämpningsvärde (0, 1, eller "
+"”toggle”)"
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Ogiltig specificering av källutgångsindex"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Du måste ange ett mottagarindex och en semikolonavskild lista med format som "
+"stöds"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr "Du måste ange ett kortnamn/-index, ett portnamn och en latensoffset"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Kunde inte tolka latensoffset"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Inget giltigt kommando angett."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Misslyckades med att återuppta: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Misslyckades med vänteläge: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "VARNING: Ljudservern är inte lokal, försätter ej i vänteläge.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Anslutningsfel: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Erhöll SIGINT, avslutar.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "VARNING: Underordnad process avslutad av signalen %u\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [flaggor] … \n"
+"\n"
+"  -h, --help                            Visa denna hjälp\n"
+"      --version                         Visa version\n"
+"  -s, --server=SERVER                   Namnet på servern att ansluta till\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Kompilerad med libpulse %s\n"
+"Länkad med libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() misslyckades.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() misslyckades.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() misslyckades.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O mottagare] [-I källa] [-c fil]  [-d|-e|-i|-"
+"r]\n"
+"\n"
+" -d    Visa aktuell PulseAudio-data som hör till X11-display (standard)\n"
+" -e    Exportera lokal PulseAudio-data till X11-display\n"
+" -i    Importera PulseAudio-data från X11-display till lokala miljövariabler "
+"och kakfil.\n"
+" -r    Ta bort PulseAudio-data från X11-display\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Misslyckades med att tolka kommandorad.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Server: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Källa: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Mottagare: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Kaka: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Misslyckades med att tolka kakdata\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Misslyckades med att spara kakdata\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Misslyckades med att hämta FQDN.\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Misslyckades med att läsa in kakdata\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Ännu inte implementerad.\n"
+
+#~ msgid ""
+#~ "%s [options]\n"
+#~ "\n"
+#~ "-h, --help                            Show this help\n"
+#~ "-v, --verbose                         Print debug messages\n"
+#~ "      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --from-format=SAMPLEFORMAT      From sample type (defaults to "
+#~ "s16le)\n"
+#~ "      --from-channels=CHANNELS        From number of channels (defaults "
+#~ "to 1)\n"
+#~ "      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+#~ "      --to-channels=CHANNELS          To number of channels (defaults to "
+#~ "1)\n"
+#~ "      --resample-method=METHOD        Resample method (defaults to auto)\n"
+#~ "      --seconds=SECONDS               From stream duration (defaults to "
+#~ "60)\n"
+#~ "\n"
+#~ "If the formats are not specified, the test performs all formats "
+#~ "combinations,\n"
+#~ "back and forth.\n"
+#~ "\n"
+#~ "Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+#~ "alaw,\n"
+#~ "s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+#~ "\n"
+#~ "See --dump-resample-methods for possible values of resample methods.\n"
+#~ msgstr ""
+#~ "%s [flaggor]\n"
+#~ "\n"
+#~ "-h, --help                            Visa denna hjälp\n"
+#~ "-v, --verbose                         Skriv ut felmeddelanden\n"
+#~ "      --from-rate=SAMPLINGSFREKVENS   Från samplingsfrekvens i Hz "
+#~ "(standard 44100)\n"
+#~ "      --from-format=SAMPLINGSFORMAT   Från samplingstyp (standard s16le)\n"
+#~ "      --from-channels=KANALER         Från antal kanaler (standard 1)\n"
+#~ "      --to-rate=SAMPLINGSFREKVENS     Till samplingsfrekvens i Hz "
+#~ "(standard 44100)\n"
+#~ "      --to-format=SAMPLINGSFORMAT     Till samplingstyp (standard s16le)\n"
+#~ "      --to-channels=KANALER           Till antal kanaler (standard 1)\n"
+#~ "      --resample-method=METOD         Omsamplingsmetod (standard auto)\n"
+#~ "      --seconds=SEKUNDER              Från strömuthållighet (standard "
+#~ "60)\n"
+#~ "\n"
+#~ "Om formaten inte är angivna utför testet alla formatkombinationer,\n"
+#~ "fram och tillbaka.\n"
+#~ "\n"
+#~ "Samplingstyp måste vare en av s16le, s16be, u8, float32le, float32be, "
+#~ "ulaw, alaw,\n"
+#~ "s24le, s24be, s24-32le, s24-32be, s32le, s32be (standard s16ne)\n"
+#~ "\n"
+#~ "Se --dump-resample-methods för möjliga värden på omsamplingsmetoder.\n"
+
+#~ msgid "%s %s\n"
+#~ msgstr "%s %s\n"
diff --git a/po/ta.po b/po/ta.po
new file mode 100644 (file)
index 0000000..fc9716e
--- /dev/null
+++ b/po/ta.po
@@ -0,0 +1,3052 @@
+# translation of pulseaudio.master-tx.ta.po to Tamil
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# I. Felix <ifelix@redhat.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx.ta\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:56+0000\n"
+"Last-Translator: I. Felix <ifelix@redhat.com>\n"
+"Language-Team: Tamil <fedora-trans-ta@redhat.com>\n"
+"Language: ta\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\\n\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "ஒரு பூஜ்ஜியம் இருந்தாலும் குறைந்தது ஒன்றை மட்டும் வைத்திருக்கவும்"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "டம்மி வெளிப்பாடு"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "மெய்நிகர் LADSPA சின்க்"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "கடிகார பூஜ்ஜிய சிங்"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "பூஜ்ஜிய வெளிப்பாடு"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "உட்புற ஆடியோ"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "மாதிரி"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "அசல் lt_dlopen ஏற்றியை காண முடியவில்லை."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "புதிய dl ஏற்றுபவரை ஒதுக்கிருவதில் தோல்வி."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "இப்போது பிணைக்கும் ஏற்பியை சேர்ப்பதில் தோல்வி."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "%sக்கு சிக்னல் கிடைத்துவிட்டது."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "வெளியேறுதல்."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "'%s' பயனரை கண்டுபிடிப்பதில் தோல்வி."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "'%s' குழுவை கண்டுபிடிப்பதில் தோல்வி."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "'%s'பயனர் கண்டுபிடிக்கப்பட்டார் (UID %lu) மற்றும் குழு '%s' (GID %lu)."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID ன் பயனர் '%s' மற்றும் '%s' குழுவுடன் ஒத்து போகவில்லை."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "முகப்பு அடைவு பயனரான'%s' '%s'ஆல், புறக்கணிக்கப்படவில்லை."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s'ஐ உருவாக்க முடியவில்லை: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "குழுப் பட்டியலை மாற்ற முடியவில்லை: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GIDக்கு மாற்றுவதில் தோல்வி: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UIDக்கு மாற்றுவதில் தோல்வி: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "ரூட் முன்னுரிமைகள் வெற்றிகரமாக விடப்பட்டது."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "இந்த தளத்தில் கணினியின் திறந்த முறைமை துணைபுரியவில்லை."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) தோல்வியுற்றது: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "கட்டளை வரியை மாற்றுவதில் தோல்வி."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "டோமோன் இயங்கவில்லை"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "PID %uவாக டோமோன் இயங்குகிறது"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "டோமோனுக்கு முடிவு கட்டுவதில் தோல்வி: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr "இந்த நிரல் ரூட்டாக இயங்க முடியவில்லை (--system குறிப்பிடாத வரை)."
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "ரூட் முன்னுரிமைகள் தேவைப்படுகிறது."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start கணினி நிகழ்வில் துணைபுரியவில்லை."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "கணினி முறைமையில் இயங்குகிறது, ஆனால் --disallow-exit அமைக்கப்படவில்லை!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr ""
+"கணினி முறைமையில் இயங்குகிறது, ஆனால் --disallow-module-loading அமைக்கப்படவில்லை!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "கணினி முறைமையில் இயங்குகிறது, SHM முறைமை செயல்நீக்குதல் கட்டாயப்படுத்துகிறது!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "கணினி முறைமையில் இயங்குகிறது, வெறுமை நேரத்தை செயல்நீக்க கட்டாயப்படுத்துகிறது!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdioஐ பெற முடியவில்லை."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "பைப் தோல்வியுற்றது: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() தோல்வியுற்றது: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "வாசிப்பதில்() தோல்வியுற்றது: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "டோமோனை துவக்குவதில் தோல்வியுற்றது."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "டோமோனை வெற்றிகரமாக துவக்ககப்பட்டது."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "வாசிப்பதில்() தோல்வியுற்றது: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "இது தான் பள்ஸ் ஆடியோ %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "தொகுக்கப்பட்ட புரவலன்: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "தொகுப்பு CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "புரவலனாக இயங்குகிறத: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "CPUs %uவில் காணப்படுகிறது ."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "பக்க அளவுகள் %lu பைட்ஸ்"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Compiled with Valgrind support: yes"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Compiled with Valgrind support: no"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "valgrind முறைமையில் இயங்குகிறது: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "புரவலனாக இயங்குகிறத: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "Optimized build: yes"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "சுருக்கமான உருவாக்கம்: இல்லை"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG வரையறுக்கப்பட்டது, அனைத்தும் உறுதியாக செயல்நீக்கப்பட்டது."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH வரையறுக்கப்பட்டது, விரைவு பாதை மட்டும் உறுதியாக செயல்நீக்கப்பட்டது."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "அனைத்து உறுதியாக செயல்படுகிறது."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "கணினி குறியீடை பெறுவதில் தோல்வி"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "கணினி குறியீடு %s."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "அமர்வு குறியீடு %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "ஓடும்நேரம்  %s அடைவை பயன்படுத்துகிறது."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "%s நிலை அடைவினை பயன்படுத்துகிறது."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "தொகுதி %s அடைவை பயன்படுத்துகிறது."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "கணினியின் முறைமையில் இயங்குகிறது: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() தோல்வியுற்றது."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "உயர்ந்த திரைத்திறன் நேரம்காட்டி கிடைக்கிளது! Bon appetit!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() தோல்வியுற்றது."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "டோமோனை ஆரம்பிப்பதில் தோல்வி."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "டீமான் துவக்கம் எந்த தொகுதிகளும் ஏற்றப்படாமல், வேலையை நிராகரிக்கிறது."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "டோமோன் துவக்குவது முடிவடைந்தது."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "டோமோன் பணிநிறுத்தம் முனைகிறது."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "டோமோன் நீக்கப்பட்டுது."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --module-idle-time=SECS           Unload autoloaded modules when idle "
+"and\n"
+"                                        this time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr} Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level பதிவு நிலை அளவுருவை எதிர்பார்க்கிறது (எண் 0..4 அல்லது debug, info, "
+"notice, warn, errorஇல் ஒன்று)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "தவறான பதிவு இலக்கு: 'syslog', 'stderr' அல்லது 'auto'ஐ பயன்படுத்தவும்."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "தவறான மறுமாதிரி முறை '%s'."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm பூலியன் அளவுரு எதிர்பார்க்கிறது"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "பெயர்: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "தொகுதி தகவல் கிடைக்கப் பெறவில்லை\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "பதிப்பு: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "விளக்கம்: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "ஆசிரியர்: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "பயன்பாடு: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "ஒருமுறை ஏற்றப்பட்டது: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "DEPRECATION WARNING: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "பாதை: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] தவறான பதிவு இலக்கு '%s'."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] தவறான பதிவு இலக்கு '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] தவறான மறுமாதிரி முறை '%s'."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] தவறான rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] தவறான மாதிரி முறை '%s'."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] தவறான மாதிரி விலை '%s'."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] தவறான மாதிரி சேனல்கள் '%s'."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] தவறான சேனல் படம் '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] பகுப்பு '%s'க்கு தவறான எண்"
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] தவறான பகுப்பு அளவு '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] தவறான நல்ல நிலை '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] தவறான மாதிரி விலை '%s'."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "கட்டமைக்கப்பட்ட கோப்பினை திறப்பதில் தோல்வி: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"குறிப்பிட்ட முன்னிருப்பு சேனல் மேப் வேறுபட்ட சேனல்களின் எண்ணிக்கையை குறிப்பிட்ட "
+"முன்னிருப்பு சேனல்களின் எண்ணிக்கையை விட கொண்டுள்ளது"
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### கட்டமைப்பு கோப்பிலிருந்து வாசிக்கவும்: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "முன்னுரிமைகளை துடைக்கிறது."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "பள்ஸ் ஆடியோ ஒலி கணினி"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "பள்ஸ் ஆடியோ ஒலி கணினியை துவக்கவும"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "பள்ஸ் ஆடியோ ஒலி கணினி"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "பள்ஸ் ஆடியோ ஒலி கணினியை துவக்கவும"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "மோனோ"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "முன் நடுவில்"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "முன் இடது"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "முன் வலது"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "பின் நடுவில்"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "பின் இடது"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "பின் வலது"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "முன் இடது பக்கத்தின் நடுவில்"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "முன் வலது பக்கத்தின் நடுவில்"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "இடது பக்கம்"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "வலது பக்கம்"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "Auxiliary 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "Auxiliary 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "Auxiliary 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "Auxiliary 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "Auxiliary 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "Auxiliary 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "Auxiliary 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "Auxiliary 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "Auxiliary 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "Auxiliary 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "Auxiliary 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "Auxiliary 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "Auxiliary 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "Auxiliary 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "Auxiliary 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "Auxiliary 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "Auxiliary 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "Auxiliary 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "Auxiliary 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "Auxiliary 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "Auxiliary 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "Auxiliary 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "Auxiliary 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "Auxiliary 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "Auxiliary 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "Auxiliary 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "Auxiliary 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "Auxiliary 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "Auxiliary 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "Auxiliary 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "Auxiliary 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "Auxiliary 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "மேல் நடுவில"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "மேலை முன் நடுவில்"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "மேலே முன் இடது"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "மேலே முன் வலது"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "மேலே பின் நடுவில்"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "மேலே பின் இடது"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "மேலே பின் வலது"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(தவறான)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "ஸ்டிரியோ"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "Surround 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "Surround 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "Surround 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "Surround 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "Surround 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "சரி"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "அணுகல் மறுக்கப்பட்டது"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "தெரியாத கட்டளை"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "தவறான விவாதம்"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "உருப்படி உள்ளது"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "உருப்படி எதுவும் இல்லை"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "இணைப்பு மறுக்கப்பட்டது"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "அறிக்கை பிழை"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "நேரம் முடிவுற்றது"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "அங்கீகார விசை இல்லை"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "உட்புற பிழை"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "இணைப்பு துண்டிக்கப்பட்டது"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "உருப்படி நீக்கப்பட்டது"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "தவறான புரவலன்"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "தொகுதியை துவக்க முடியவில்லை"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "தவறான நிலை"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "தரவு இல்லை"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "நெறிமுறை பதிப்பு உகந்ததல்ல"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "மிகப் பெரியது"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "துணைப்புரியாத"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "தெரியாத பிழைக் குறியீடு"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "இது போன்ற தொடர்ச்சி இல்லை"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "நீக்கப்படும் செயல்பாடு"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "விடுபட்ட செயல்பாடு"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "கிளையன் நீக்கப்பட்டது"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "உள்ளீடு/வெளிப்பாடு பிழை"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "சாதனம் அல்லது மூலம் பணிமிகுதியில்"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() தோல்வி: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "குக்கீ தரவை மாற்றுவதில் தோல்வியுற்றது"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "'%s'கட்டமைக்கப்பட்ட கோப்பினை திறக்க முடியவில்லை: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "குக்கி ஏற்றப்படவில்லை. இணைப்பில்லாமல் முயற்சிக்கிறது."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "தெரியாத தொடரிச்சியிலிருந்து '%s'க்கு செய்திகள் பெறப்பட்டன"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "ஸ்டீரிமை இழக்க முடியவில்லை: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "பின்னணி ஸ்டீரிம் இழக்கப்படுகிறது."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "சேவையகத்திற்கு இணைப்பு இழக்கப்படுகிறது."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() தோல்வியுற்றது: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_write() தோல்வியுற்றது: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() தோல்வியுற்றது: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "ஸ்டிரீம் வெற்றிகரமாக உருவாக்கப்பட்டது."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() தோல்வியுற்றது: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Buffer metrics: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "குறிப்பிட்ட குறிப்பு '%s', சேனல் வரைபடத்தை '%s'ஐ பயன்படுத்துகிறது."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "%s சாதனத்துடன் இணைக்கப்பட்டது (%u, %ssuspended)."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "ஸ்டிரீம் பிழை: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "ஸ்டீரிம் சாதனம் இடைநீக்கப்பட்டது.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "ஸ்டீரிம் சாதனம் தொடர்கிறது.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "ஸ்டீரிம் இயங்குகிறது.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "ஸ்டீரிம் அதிகளவு இயங்கியது.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "ஸ்டிரீம் %s துவக்கப்பட்டது"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "ஸ்டிரீம் %s சாதனத்திற்கு நகர்ந்தது (%u, %ssuspended).%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "இல்லை"
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "ஸ்டீரிம் ஃபப்பர் பண்புகளை மாற்றப்பட்டது.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "இணைப்பு துவக்கப்பட்டது.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() தோல்வியுற்றது: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() தோல்வியுற்றது: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() தோல்வியுற்றது: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "இணைப்பதில் தோல்வி: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF கிடைக்கப் பெற்றது"
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "எழுதுவதில் () தோல்வியுற்றது: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "சிக்னல் கிடைத்தது, வெளியேறுகிறது."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "லடன்சியை பெற முடியவில்லை: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Time: %0.3f sec; Latency: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() தோல்வி: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --file-format=FFORMAT             Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "தவறான கிளையன் பெயர் '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "தவறான ஸ்டீரீம் பெயர் '%s'."
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "தவறான சேனல் வரைபடம் '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "தவறான லேடன்சி குறிப்பீடு '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "தவறான செயல் நேர குறிப்பீடு '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "தவறான தன்மை '%s'."
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "தெரியாத கோப்பு வடிவம் %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "தவறான மாதிரி குறிப்பீடு"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "நிறைய விவாதங்கள்."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "மாதிரி தகவலை பெற முடியவில்லை.: %s"
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "ஒலி கோப்பினை திறக்க முடியவில்லை."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"எச்சரிக்கை: கோப்பிலிருந்து குறிப்பீட்டுடன் குறிக்கிட்ட மாதிரி குறிப்பீடு மேலெழுதப்படலாம்."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "கோப்பிலிருந்து மாதிரி குறிப்பீட்டை  வரையறுக்க முடியவில்லை."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "எச்சரிக்கை: கோப்பிலிருந்து சேனல் வரைபடத்தை வரையறுக்க முடியவில்லை."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "சேனல் வரைபடம் மாதிரி குறிப்பீட்டுடன் பொருந்தவில்லை"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "எச்சரிக்கை: கோப்புக்கு சேனல் வரைபடத்தை எழுத முடியவில்லை."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"ஒரு %s ஸ்டீரம் மாதிரி குறிப்பீட்டை '%s' மற்றும் சேனல் வரைபட்டம் '%s' உடன் திறக்கிறது."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "ஒலிப்பதிவு"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "பிண்ணனி"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "கட்டளை வரியை மாற்றுவதில் தோல்வி."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() தோல்வி."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() தோல்வியுற்றது."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() தோல்வி."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() தோல்வி: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() தோல்வியுற்றது."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() தோல்வி."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "நீக்க முடியவில்லை: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "தொடர முடியவில்லை: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "எச்சரிக்கை: ஒலி சேவையம் உள்ளமைவாக இல்லை, இடைநிறுத்தப்படவில்லை.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "இணைப்பதில் தோல்வி: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT பெறப்பட்டது, வெளியேறுகிறது.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "எச்சரிக்கை: சேய் செயல் சிக்னல் %uஆல் முடிக்கப்பட்டது\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [விருப்பங்கள்] ... \n"
+"\n"
+"  -h, --உதவி                            இந்த உதவியை காட்டு\n"
+"      --பதிப்பு                         பதிப்பினைக் காட்டு\n"
+"  -s, --சேவையகம்=SERVER                   பெயரிடப்பட்ட சேவையகம் இணைக்கப்பட வேண்டும்\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() தோல்வி.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() தோல்வி.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() தோல்வி.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "புள்ளிவிவரத்தை பெற முடியவில்லை: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "தற்போது பயனிலுள்ளது: %u தொகுதிகள் %s பைட்களை மொத்தமாக கொண்டுள்ளது.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"வாழ்க்கை முழுவதும் ஒதுக்கப்பட்டது: %u தொகுதிகள் %s பைட்களை மொத்தமாக கொண்டுள்ளது.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "மாதிரி இடையக அளவு: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "சேவையகத்தின் தகவலை பெற முடியவில்லை: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"பயனர் பெயர்: %s\n"
+"புரவலன் பெயர்: %s\n"
+"Server பெயர்: %s\n"
+"Server பதிப்பு: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"முன்னிருப்பு மூலங்கள்: %s\n"
+"கூக்கி: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "சுருக்கமான தகவலை பெறு முடியவில்லை: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"சுருக்கம் #%u\n"
+"\tமாநிலம்: %s\n"
+"\tபெயர்: %s\n"
+"\tவிளக்கம்: %s\n"
+"\tஇயக்கி: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner முறைமை: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        மீதி %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tPorts:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tசெயல்பாட்டிலுள்ள விவரக்குறிப்புகள்: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tPorts:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "மூலத்தின் தகவலை பெற இயலவில்லை: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"மூலம் #%u\n"
+"\tநிலை: %s\n"
+"\tபெயர்: %s\n"
+"\tவிளக்கம்: %s\n"
+"\tஇயக்கி: %s\n"
+"\tமாதிரி குறிப்பிடுதல்: %s\n"
+"\tசேனல் வரைபடம்.: %s\n"
+"\tஉரிமையாளர் தொகுதி: %u\n"
+"\tநிறுத்தப்பட்டது: %s\n"
+"\tஒலி: %s%s%s\n"
+"\t        மீதி %0.2f\n"
+"\tஅடிப்படை ஒலி: %s%s%s\n"
+"\tகணினி சுருங்கப்பட்டது: %s\n"
+"\tLatency: %0.0f usec, கட்டமைக்கப்பட்டது %0.0f usec\n"
+"\tகொடிகள்: %s%s%s%s%s%s\n"
+"\tபண்புகள்:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "தொகுதி தகவலை பெற முடியவில்லை: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"தொகுதி #%u\n"
+"\tபெயர்: %s\n"
+"\tவிவாதம்: %s\n"
+"\tபயன்படுத்தும் கவுன்டர்: %s\n"
+"\tபண்புகள்:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "கிளையன்ட் தகவலை பெற முடியவில்லை: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"கிளையன்ட் #%u\n"
+"\tஇயக்கி: %s\n"
+"\tஉரிமையாளர் தொகுதி: %s\n"
+"\tபண்புகள்:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "அட்டை தகவலை பெற முடியவில்லை: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tவிவரக்குறிப்புகள்:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tசெயல்பாட்டிலுள்ள விவரக்குறிப்புகள்: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "சிங்க் தகவலை பெற முடியவில்லை: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "மூல வெளிப்பாடு தகவலை பெற முடியவில்லை: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "மாதிரி தகவலை பெற முடியவில்லை.: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "தோல்வி: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "மூலத்தின் தகவலை பெற இயலவில்லை: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "மாதிரியை மேம்படுத்த முடியவில்லை: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "முன்னாக கோப்பு முடித்தல்"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "தவறான புரவலன்"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT பெறப்பட்டது, வெளியேறுகிறது."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "தவறான தொகுதி குறிப்பீடு"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [விருப்பங்கள்] ... \n"
+"\n"
+"  -h, --உதவி                            இந்த உதவியை காட்டு\n"
+"      --பதிப்பு                         பதிப்பினைக் காட்டு\n"
+"  -s, --சேவையகம்=SERVER                   பெயரிடப்பட்ட சேவையகம் இணைக்கப்பட வேண்டும்\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "ஏற்றுவதற்கு ஒரு மாதிரி கோப்பினை குறிப்பிடவும்"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "ஒலி கோப்பினை திறக்க முடியவில்லை."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "எச்சரிக்கை: கோப்பிலிருந்து மாதிரி குறிப்பீட்டை வரையறுக்க முடியவில்லை."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "இயக்கிவதற்கு நீங்கள் ஒரு மாதிரி பெயர் குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "நீக்குவதற்கு நீங்கள் ஒரு மாதிரி பெயர் குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "நீங்கள் ஒரு சிங்க் உள்ளீடு சுட்டி மற்றும் ஒரு சிங்கை குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "நீங்கள் ஒரு மூல வெளிப்பாடு சுட்டி மற்றும் ஒரு மூலத்தை குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "தொகுதி பெயர் மற்றும் விவாதங்களை நீங்கள் குறிப்பிட வேண்டும்."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "தொகுதி அட்டவணையை நீங்கள் குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"ஒரு சிங்கிற்கு மேல் நீங்கள் குறிப்பிடக் கூடாது. பூலியன் மதிப்பை நீங்கள் குறிப்பிட வேண்டும்."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"ஒரு மூலத்திற்கு மேல் நீங்கள் குறிப்பிடக் கூடாது. பூலியன் மதிப்பை நீங்கள் குறிப்பிட வேண்டும்."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "ஒரு அட்டை பெயர்/ முன்பக்கம் மற்றும் ஒரு விவரச்சீட்டு பெயர் நீங்கள் குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "நீங்கள் ஒரு சிங்க் பெயர்/ முன்பக்கம் மற்றும் ஒரு துறைப் பெயரை குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "ஒரு மூலப் பெயர்/ முன்பக்கம் மற்றும் ஒரு துறைப் பெயர் நீங்கள் குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "நீங்கள் ஒரு சிங்க் பெயர்/ முன்பக்கம் மற்றும் ஒரு துறைப் பெயரை குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "ஒரு மூலப் பெயர்/ முன்பக்கம் மற்றும் ஒரு துறைப் பெயர் நீங்கள் குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "நீங்கள் ஒரு சிங்க் உள்ளீடு சுட்டி மற்றும் ஒரு சிங்கை குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "தவறான சுருக்க உள்ளீடு அட்டவணை"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "நீங்கள் ஒரு மூல வெளிப்பாடு சுட்டி மற்றும் ஒரு மூலத்தை குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "தவறான சுருக்க உள்ளீடு அட்டவணை"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "நீங்கள் ஒரு சிங்க் பெயர்/ முன்பக்கம் மற்றும் ஒரு துறைப் பெயரை குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "தவறான மாதிரி குறிப்பீடு"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "ஒரு மூலப் பெயர்/ முன்பக்கம் மற்றும் ஒரு துறைப் பெயர் நீங்கள் குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "நீங்கள் ஒரு சிங்க் உள்ளீடு சுட்டி மற்றும் ஒரு சிங்கை குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "தவறான சுருக்க உள்ளீடு அட்டவணை குறிப்பீடு"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "ஒரு மூலப் பெயர்/ முன்பக்கம் மற்றும் ஒரு துறைப் பெயர் நீங்கள் குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "தவறான சுருக்க உள்ளீடு அட்டவணை குறிப்பீடு"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "நீங்கள் ஒரு சிங்க் பெயர்/ முன்பக்கம் மற்றும் ஒரு துறைப் பெயரை குறிப்பிட வேண்டும்"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "சரியான கட்டளை குறிப்பிடபடவில்லை"
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "கட்டளை வரியை மாற்ற முடியவில்லை \n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "சேவையகம்: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "மூலம்: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "சுருங்குதல்: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "கூக்கீ: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "குக்கீ தரவை மாற்ற முடியவில்லை\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "குக்கீ தரவை சேமிக்க முடியவில்லை\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "கிளையன்ட் கட்டமைப்பு கோப்பினை ஏற்ற முடியவில்லை.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "சுற்றுப்புற கட்டமைப்பு தரவினை வாணிக்க முடியவில்லை.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDNஐ பெற முடியவில்லை.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "குக்கீ தரவை ஏற்ற முடியவில்லை\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "இன்னும் செயல்படுத்தபடவில்லை.\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "PulseAudio டீமான் இயங்கவில்லை, அல்லது அமர்வு டீமானாக இயங்கவில்லை."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio daemonஐ நிறுத்த முடியவில்லை."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "டோமோன் பதிலளிக்க மறுக்கிறது."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "autospawn பூட்டை அணுக முடியவில்லை."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA சாதனத்திற்கு புதிய தரவை எழுதுவதற்கு எங்களை எழுப்பி இருக்கவாம், ஆனால் இங்கே "
+"சரியாக எழுதுவதற்கு எதுவும் இல்லை!\n"
+"இந்த ஒரு பிழையானது ALSA இயக்கி '%s'. இந்த வெளிப்பாட்டை ALSA வல்லுநர்களுக்கு  "
+"அறிக்கையிடவும்.\n"
+"POLLOUT அமைவுடன் நாங்கள் எழுந்திருந்தோம்-- எப்படியிருந்தும் அடுத்தடுத்து snd_pcm_avail"
+"() r0 அல்லது வேறொரு மதிப்பு < min_avail திரும்பியது."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA சாதனத்திற்கு புதிய தரவை எழுதுவதற்கு எங்களை எழுப்பி இருக்கவாம், ஆனால் இங்கே "
+"சரியாக எழுதுவதற்கு எதுவும் இல்லை!\n"
+"இந்த ஒரு பிழையானது ALSA இயக்கி '%s'. இந்த வெளிப்பாட்டை ALSA வல்லுநர்களுக்கு  "
+"அறிக்கையிடவும்.\n"
+"POLLOUT அமைவுடன் நாங்கள் எழுந்திருந்தோம்-- எப்படியிருந்தும் அடுத்தடுத்து snd_pcm_avail"
+"() 0 அல்லது வேறொரு மதிப்பு < min_avail திரும்பியது."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "ஆஃப்"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "High Fidelity Playback (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "High Fidelity Capture (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Telephony Duplex (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio ஒலி சேவையகம்"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "வெளிப்பாடு சாதனங்கள்"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "உள்ளீடு சாதனங்கள்"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@இல் ஆடியோ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "உள்ளீடு"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "டாக்கிங் ஸ்டேஷன் உள்ளீடு"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "டாக்கிங் ஸ்டேஷன் மைக்ரோஃபோன்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "டாக்கிங் ஸ்டேஷன் உள்ளீடு"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "லைன்இன்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "மைக்ரோஃபோன்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "டாக்கிங் ஸ்டேஷன் மைக்ரோஃபோன்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "மைக்ரோஃபோன்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "வெளியார்ந்த மைக்ரோஃபோன்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "உட்புற மைக்ரோஃபோன்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "ரேடியோ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "வீடியோ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "தானியக்க லாப கட்டுப்பாடு"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "தானியக்க லாப கட்டுப்பாடு எதுவுமில்லை"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "பூஸ்ட்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "பூஸ்ட் இல்லை"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "ஆம்பிளிஃபையர்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "ஆம்ப்ளிஃபையர் இல்லை"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "பூஸ்ட்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "பூஸ்ட் இல்லை"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "அனலாக் ஹெட்ஃபோன்கள்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "அனலாக் உள்ளிடு"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "டாக்கிங் ஸ்டேஷன் மைக்ரோஃபோன்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "அனலாக் வெளிப்பாடு"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "அனலாக் வெளிப்பாடு (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "லைன்இன்"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "அனலாக் மோனோ வெளிப்பாடு"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "அனலாக் ஸ்டிரியோ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "Digital Stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Digital Stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "அனலாக் மோனோ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "அனலாக் ஸ்டிரியோ"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "Analog Surround 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "Analog Surround 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "Analog Surround 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "Analog Surround 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "Analog Surround 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "Analog Surround 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "Analog Surround 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "Analog Surround 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "Analog Surround 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "Analog Surround 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "Analog Surround 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "Digital Stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Digital Stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Digital Surround 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Digital Surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "Digital Stereo (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Digital Surround 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "Analog Mono Duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "Analog Stereo Duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Digital Stereo Duplex (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "பூஜ்ஜிய வெளிப்பாடு"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "உள்ளீடு"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit இந்த தளத்தில் துணைபுரியவில்லை."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenகாட்சி() தோல்வியுற்றது"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Digital Surround 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "குறைந்த அலைவரிசை எம்மிட்டர்"
diff --git a/po/te.po b/po/te.po
new file mode 100644 (file)
index 0000000..48e1245
--- /dev/null
+++ b/po/te.po
@@ -0,0 +1,3023 @@
+# translation of pulseaudio.master-tx.te.po to Telugu
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Krishna Babu K <kkrothap@redhat.com>, 2009, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx.te\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2012-01-30 09:56+0000\n"
+"Last-Translator: Krishna Babu K <kkrothap@redhat.com>\n"
+"Language-Team: Telugu <en@li.org>\n"
+"Language: te\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() అనునది పెద్ద విలువను యిచ్చినది: %lu bytes (%lu ms).\n"
+"సాదారణంగా యిది ALSA డ్రైవర్ '%s' నందలి బగ్ కావచ్చును. దయచేసి దీనిని ALSA అభివృద్ది కారులకు "
+"నివేదించుము."
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() అనునది పెద్ద విలువను యిచ్చినది: %li bytes (%s%lu ms).\n"
+"సాదారణంగా యిది ALSA డ్రైవర్ '%s' నందు బగ్ కావచ్చును . దయచేసి దీనిని ALSA అభివృద్దికారులక "
+"నివేదించుము."
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, fuzzy, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() అనునది పెద్ద విలువను యిచ్చినది: %lu bytes (%lu ms).\n"
+"సాదారణంగా యిది ALSA డ్రైవర్ '%s' నందలి బగ్ కావచ్చును. దయచేసి దీనిని ALSA అభివృద్ది కారులకు "
+"నివేదించుము."
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() అనునది పెద్ద విలువను యిచ్చినది: %lu bytes (%lu ms).\n"
+"సాదారణంగా యిది ALSA డ్రైవర్ '%s'నందలి బగ్ కావచ్చును. దయచేసి దీనిని ALSA అభివృద్దికారులను నివేదించండి."
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "ఒకవేళ అది null అయినా కూడా యెల్లప్పుడూ కనీసం వొక సింకు లోడైనట్లు వుంచుతుంది"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "డమ్మీ అవుట్పుట్"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "వర్చ్యువల్ LADSPA సింకు"
+
+#: ../src/modules/module-ladspa-sink.c:52
+#, fuzzy
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<సింక్ నామము> sink_properties=<సింకు లక్షణములు> master=<ఫిల్టర్‌కు సింకు "
+"నామము> format=<మాదిరి ఫార్మాట్> rate=<మాదిరి రేటు> channels=<చానల్సు సంఖ్య> "
+"channel_map=<చానల్ మాప్> plugin=<ladspa ప్లగిన్ నామము> label=<ladspa ప్లగిన్ లేబుల్> "
+"control=<ఇన్పుట్ నియంత్రణ విలువలయొక్క జాబితా>"
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "NULL సింక్ క్లాక్‌చేయబడింది"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Null అవుట్పుట్"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "అంతర్గత ఆడియో"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "మోడెమ్"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "వాస్తవ lt_dlopen లోడర్ కనుగొనుటలో విఫలమైంది."
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "కొత్త dl లోడర్ కేటాయించుటలో విఫలమైంది."
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "bind-now-loader జతచేయుటకు విఫలమైంది."
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "సంకేతము %s పొందినది."
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "నిష్క్రమించుచున్నది."
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "వినియోగదారి '%s'ను కనుగొనుటకు విఫలమైంది."
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "సమూహం '%s' కనుగొనుటకు విఫలమైంది."
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "వినియోగదారి '%s' (UID %lu) మరియు సమూహము '%s' (GID %lu) కనబడినవి."
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "వినియోగదారి '%s' మరియు సమూహము '%s' యొక్క GID సరితూగలేదు."
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "వినియోగదారి '%s' యొక్క నివాస డైరెక్టరీ '%s' కాదు, వదిలివేయుచున్నది."
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' సృష్టించుటకు విఫలమైంది: %s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "సమూహ జాబితా మార్చుటకు విఫలమైంది: %s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID మార్చుటకు విఫలమైంది: %s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID మార్చటకు విఫలమైంది: %s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "root అనుమతులు విజయవంతంగా తిసివేయబడినాయి."
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "ఈ ప్లాట్‌ఫాం నందు సిస్టమ్ తరహా రీతి మద్దతీయబడదు."
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) విఫలమైంది: %s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "ఆదేశ వరుసను పార్శ్ చేయుటకు విఫలమైంది."
+
+#: ../src/daemon/main.c:529
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "డెమోన్ నడుచుట లేదు"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "డెమోన్ PID %u వలె నడుచుచున్నది"
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "డెమోన్ చంపుటకు విఫలమైంది: %s"
+
+#: ../src/daemon/main.c:657
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr "ఈ ప్రోగ్రామ్ root లా నడుపవలసింది కాదు (--system తెలిపితే తప్ప)"
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "Root అనుమతులు అవసరము."
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start సిస్టమ్ సంభవాల ద్వారా మద్దతీయబడదు."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "సిస్టమ్ మోడ్ నందు నడుపుతోంది, అయితే --disallow-exit అమర్చలేదు!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "సిస్టమ్ రీతినందు నడుచుచున్నది, అయితే --disallow-module-loading అమర్చలేదు!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "సిస్టమ్ రీతినందు నడుపుచున్నది, బలవంతంగా SHM రీతిని అచేతనము చేస్తోంది!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "సిస్టమ్ రీతినందు నడుచుచున్నది, బలవంతంగా నిష్క్రమణ వృధా సమయాన్ని అచేతనము చేయుచున్నది!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "stdio పొందుటకు విఫలమైంది."
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, fuzzy, c-format
+msgid "pipe() failed: %s"
+msgstr "పైర్ విఫలమైంది: %s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() విఫలమైంది: %s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() విఫలమైంది: %s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "డెమోన్ ప్రారంభం విఫలమైంది."
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "డెమోన్ ప్రారంభము సఫలమైంది."
+
+#: ../src/daemon/main.c:816
+#, fuzzy, c-format
+msgid "setsid() failed: %s"
+msgstr "read() విఫలమైంది: %s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "ఇది PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "నిర్వర్తన హోస్టు: %s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "నిర్వర్తన CFLAGS: %s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "హోస్టును నడుపుచున్నది: %s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "%u CPUలను కనుగొన్నది."
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "పేజీ పరిమాణము %lu బైట్లు"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "Valgrind మద్దతుతో నిర్వర్తించబడింది: అవును"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "Valgrind మద్దతుతో నిర్వర్తించబడింది: లేదు"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "valgrind రీతినందు నడుపుచున్నది: %s"
+
+#: ../src/daemon/main.c:921
+#, fuzzy, c-format
+msgid "Running in VM: %s"
+msgstr "హోస్టును నడుపుచున్నది: %s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "ఆప్టిమైజ్డు బుల్డు: అవును"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "ఆప్టిమైజ్డు బుల్డు: కాదు"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG నిర్వచించబడింది, అన్ని స్థిరరాశులు అచేతనమైనవి."
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH నిర్వచించబడింది, ఫాస్ట్ పాత్ స్థిరరాశులు మాత్రమే అచేతనమైనవి."
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "అన్ని స్థిరరాశులు చేతనమైనవి."
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "మిషన్ ID పొందుటకు విఫలమైంది"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "మిషన్ ID %s."
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "సెషన్ ID %s."
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "రన్‌టైమ్ డైరెక్టరీను వుపయోగించుచున్నది %s."
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "స్థితి డైరెక్టరీను వుపయోగించుచున్నది %s."
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "మాడ్యూళ్ళ డైరెక్టరీ %s వుపయోగిస్తోంది."
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "సిస్టమ్ రీతినందు వుపయోగించుచున్నది: %s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"సరే, అయితే మీరు PAను సిస్టమ్ రీతినందు నడుపుతున్నారు. మీరు అలా చేయకూడదని దయచేసి గమనించండి.\n"
+"ఒకవేళ మీరు అలా చేస్తే తరువాత మీరు అనుకొన్నట్లు పనిచేయకపోతే అది యిక మీ తప్పే.\n"
+"సిస్టమ్ రీతి అనునది సరైనటువంటిది యెందుకు కాదో వివరణ కొరకు దయచేసి యిక్కడ చదవండి http://www."
+"freedesktop.org/wiki/Software/PulseAudio/Documentation/User/"
+"WhatIsWrongWithSystemWide/"
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() విఫలమైంది."
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "తాజా అధిక-తీవ్రత కాలసూచికలు అందుబాటులో వున్నాయి! బాన్ ఎపటైట్!"
+
+#: ../src/daemon/main.c:993
+msgid ""
+"Dude, your kernel stinks! The chef's recommendation today is Linux with high-"
+"resolution timers enabled!"
+msgstr "మిత్రమా, నీ కెర్నల్ చెడిపోయింది! అధిక-తీవ్రత కాలసూచకిలను చేతనము చేయమని సూచించడమైనది!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() విఫలమైంది."
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "డెమోన్ సిద్దముచేయుటకు విఫలమైంది."
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "ఏవిధమైన మాడ్యూళ్ళు లోడవకుండా డెమోన్ ప్రారంభము, పనిచేయుటకు తిరస్కరించబడింది."
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "డెమోన్ ప్రారంభము పూర్తైనది."
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "డెమోన్ మూసివేత సిద్దముచేయబడింది."
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "డెమోన్ అంతముచేయబడింది."
+
+#: ../src/daemon/cmdline.c:113
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --module-idle-time=SECS           Unload autoloaded modules when idle "
+"and\n"
+"                                        this time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr} Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/cmdline.c:261
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level లాగ్ స్థాయి ఆర్గుమెంట్‌ను కోరుకుంటోంది (సహజసంఖ్యను 0..4 విస్తృతిలో కాని లేదా డీబగ్‌, "
+"సమాచారము, నోటీసు, హెచ్చరిక, దోషము వీటిలో వొకటికాని)."
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/cmdline.c:318
+#, fuzzy
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>'."
+msgstr "చెల్లని లాగ్ టార్గెట్: 'syslog', 'stderr' లేదా 'auto' వుపయోగించుము."
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "చెల్లని పునఃవుదాహరణ పద్దతి '%s'."
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm బూలియన్ ఆర్గుమెంటును కోరుకుంటుంది"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "నామము: %s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "ఎటువంటి మాడ్యూల్ సమాచారము అందుబాటులోలేదు\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "వర్షన్: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "వివరణ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "మూలకర్త: %s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "వాడుక: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "ఒకసారి లోడుచేయుము: %s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "తీసివేత హెచ్చరిక: %s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "పాత్: %s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] చెల్లని లాగ్ లక్ష్యము '%s'."
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] చెల్లని లాగ్ స్థాయి '%s'."
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] చెల్లని పునఃవుదాహరణ పద్దతి '%s'."
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] చెల్లని rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] చెల్లని మాదిరి ఫార్మాట్ '%s'."
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] చెల్లని మాదిరి రేటు '%s'."
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] చెల్లని మాదిరి చానళ్ళు '%s'."
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] చెల్లని ఛానల్ మాప్ '%s'."
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] చెల్లని ముక్కలు సంఖ్య '%s'."
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] చెల్లని ముక్క పరిమాణము '%s'."
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] చెల్లని సాదా స్థాయి '%s'."
+
+#: ../src/daemon/daemon-conf.c:528
+#, fuzzy, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] చెల్లని మాదిరి రేటు '%s'."
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "ఆకృతీకరణ దస్త్రమును తెరుచుటకు విఫలమైంది: %s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"తెలుపబడిన అప్రమేయ ప్రాసారమార్గం మాప్ తెలుపబడిన అప్రమేయ ప్రసారమార్గముల కన్నా విభిన్న ప్రసారమార్గముల "
+"సంఖ్యను కలిగివుంది."
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### ఆకృతీకరణ దస్త్రమునుండి చదువుము: %s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "అనుమతులను శుభ్రపరచుచున్నది."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio శబ్దపు సిస్టమ్"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "PulseAudio శబ్దపు సిస్టమ్‌ను ప్రారంభించుము"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+#, fuzzy
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio శబ్దపు సిస్టమ్"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+#, fuzzy
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "PulseAudio శబ్దపు సిస్టమ్‌ను ప్రారంభించుము"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "మోనో"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "ముందు మధ్యన"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "ముందు ఎడమవైపు"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "ముందు కుడివైపు"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "వెనుక మధ్యన"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "వెనుక ఎడమవైపు"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "వెనుక కుడివైపు"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr ""
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "ముందు ఎడమ-మధ్య"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "ముందు కుడి-మధ్య"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "ఎడమ ప్రక్క"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "కుడి ప్రక్క"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "ఆక్సిలరి 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "ఆక్సిలరి 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "ఆక్సిలరి 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "ఆక్సిలరి 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "ఆక్సిలరి 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "ఆక్సిలరి 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "ఆక్సిలరి 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "ఆక్సిలరి 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "ఆక్సిలరి 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "ఆక్సిలరి 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "ఆక్సిలరి 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "ఆక్సిలరి 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "ఆక్సిలరి 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "ఆక్సిలరి 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "ఆక్సిలరి 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "ఆక్సిలరి 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "ఆక్సిలరి 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "ఆక్సిలరి 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "ఆక్సిలరి 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "ఆక్సిలరి 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "ఆక్సిలరి 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "ఆక్సిలరి 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "ఆక్సిలరి 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "ఆక్సిలరి 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "ఆక్సిలరి 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "ఆక్సిలరి 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "ఆక్సిలరి 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "ఆక్సిలరి 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "ఆక్సిలరి 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "ఆక్సిలరి 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "ఆక్సిలరి 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "ఆక్సిలరి 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "పై మధ్యన"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "పైన ముందు మధ్యన"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "పైన ముందు ఎడమవైపు"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "పైన ముందు కుడివైపు"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "పైన వెనుక మధ్యన"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "పైన వెనుక ఎడమవైపు"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "పైన వెనుక కుడివైపున"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(చెల్లని)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "స్టీరియో"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "సరౌండ్ 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "సరౌండ్ 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "సరౌండ్ 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "సరౌండ్ 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "సరౌండ్ 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "సరే"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "సాంగత్యం తిరస్కరించబడినది"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "తెలియని ఆదేశము"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "నిస్సారమైన క్రమానుగత సంకేతం"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "ఎంటిటి నిష్క్రమించినది"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "అటువంటి యెంటిటి లేదు"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "కనెక్షన్ తిరస్కరించబడింది"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "నియమం దోషం"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "సమయంముగిసింది"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "ఎటువంటి ధృవీకృత కీ లేదు"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "అంతర్గత దోషము"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "అనుసంధానము అంతముచేయబడింది"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "ఎంటిటి నాశనంచేయబడింది"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "చెల్లని సేవిక"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "మాడ్యూల్ సిద్దీకరణ విఫలమైంది"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "చెడ్డ స్థితి"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "దత్తాంశం లేదు"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "సారూప్యతలేని ప్రోటోకాల్ వర్షన్"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "మరీ పెద్దది"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "మద్దతీయబడదు"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "తెలియని దోషము కోడ్"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "అటువంటి పొడిగింపు లేదు"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "పనితీరు తీసివేయి"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "తప్పిపోయిన యింప్లిమెంటేషన్"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "కక్షిదారి పోర్క్ చేసిన"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "ఇన్పుట్/అవుట్పుట్ దోషము"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "పరికరము లేదా వనరు రద్దీగావుంది"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+#, fuzzy
+msgid "xcb_connect() failed"
+msgstr "pa_context_connect() విఫలమైంది: %s"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr ""
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "కుకీ డాటా పార్శ్ చేయుటకు విఫలమైంది"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "ఆకృతీకరణ దస్త్రము '%s' తెరువుటకు విఫలమైంది: %s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "ఏ కుకీ లోడవలేదు. లేకుండా అనుసంధానమగుటకు ప్రయత్నిస్తోంది."
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "తెలియని పొడిగింపు కొరకు సందేశము స్వీకరించింది '%s'"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "స్ట్రీమ్‌ను డ్రైయిన్ చేయుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "ప్లేబ్యాక్ స్ట్రీమ్ డ్రెయిన్ అయినది."
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "సేవికకు అనుసంధానమును ఎండగట్టుచున్నది."
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "స్ట్రీమ్ సమర్ధవంతంగా సృష్టించబడింది."
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "బఫర్ ప్రమాణాలు: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "బఫర్ ప్రమాణాలు: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "సాదారణ విశదీకరణ(స్పెక్) '%s' వుపయోగిస్తోంది, ప్రసారమార్గం మాప్ '%s'."
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "పరికరము %s (%u, %ssuspended) కు అనుసంధానించబడింది."
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "స్ట్రీమ్ దోషము: %s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "స్ట్రీమ్ పరికరము అర్దాంతరముగా నిలిపివేయబడింది.%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "స్ట్రీమ్ పరికరము తిరిగికొనసాగించబడింది.%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "స్ట్రీమ్ తక్కువగానడుచుచున్నది.%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "స్ట్రీమ్ మించినడుచుచున్నది.%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "స్ట్రీమ్ ప్రారంభమైంది.%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "స్ట్రీమ్ పరికరము %s (%u, %ssuspended) కు కదుపబడింది.%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "కాదు "
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "స్ట్రీమ్ బఫర్ యాట్రిబ్యూట్లు మార్చబడినవి.%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr ""
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr ""
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "అనుసంధానము ఏర్పడినది.%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "అనుసంధానము వైఫల్యము: %s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "EOF పొందింది."
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "సంకేతము పొందినది, నిష్క్రమించుచున్నది."
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "లేటెన్సీని పొందుటలో విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "సమయం: %0.3f సెకను; లెటెన్సీ: %0.0f usec."
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:653
+#, fuzzy, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink "
+"the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink the stream is being "
+"connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --file-format=FFORMAT             Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"libpulse తో నిర్వర్తించబడింది %s\n"
+"libpulse లింకైనది %s\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "చెల్లని కక్షిదారి నామము '%s'"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "చెల్లని స్ట్రీమ్ నామము '%s'"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "చెల్లని ప్రసారమార్గ మాప్ '%s'"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "చెల్లని లేటెన్సీ విశదీకరణము '%s'"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "చెల్లని కార్యక్రమము సమయ విశదీకరణ '%s'"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "చెల్లని లక్షణము '%s'"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "తెలియని ఫైలు ఫార్మాట్ %s."
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "చెల్లనటువంటి మాదిరి విశదీకరణ"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "చాలా యెక్కువ ఆర్గుమెంట్లు."
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "దస్త్రము కొరకు మాదిరి సమాచారము జనియింపచేయుటలో విఫలమైంది."
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "ఆడియో ఫైలును తెరువుటకు విఫలమైంది."
+
+#: ../src/utils/pacat.c:1036
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr "హెచ్చరిక: తెలుపబడిన మాదిరి విశదీకరణ దస్త్రమునుండి వచ్చు విశదీకరణతో తిరిగివ్రాయబడుతుంది."
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "దస్త్రమునుండి మాదిరి విశదీకరణను నిర్ధారించుటలో విఫలమైంది."
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "హెచ్చరిక: దస్త్రమునుండి ప్రసారమార్గ మాప్ నిర్ధారించుటలో విఫలమైంది."
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "ప్రసారమార్గ మాప్ మాదిరి విశదీకరణితో సరిపోలుటలేదు"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "హెచ్చరిక: ప్రసారమార్గ మాప్‌ను దస్త్రముకు వ్రాయుటలో విఫలమైంది."
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "%s స్ట్రీమ్‌ను మాదిరి విశదీకరణ '%s' మరియు ప్రసారమార్గ మాప్ '%s'తో తెరుచుచున్నది."
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "రికార్డింగు"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "ప్లేబాక్"
+
+#: ../src/utils/pacat.c:1110
+#, fuzzy
+msgid "Failed to set media name."
+msgstr "ఆదేశ వరుసను పార్శ్ చేయుటకు విఫలమైంది."
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() విఫలమైంది."
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() విఫలమైంది."
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() విఫలమైంది."
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() విఫలమైంది: %s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() విఫలమైంది."
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() విఫలమైంది."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "అర్ధాంతరనిలుపుదల వైఫల్యం: %s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "తిరిగికొనసాగింపు వైఫల్యము: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "హెచ్చరిక: శబ్ధపు సేవిక స్థానికం కాదు, అర్ధాంతరనిలుపుదల కావడంలేదు.\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "అనుసంధానము వైఫల్యము: %s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT పొందింది, నిష్క్రమించుచున్నది.\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "హెచ్చరిక: చైల్డు కార్యక్రమము సంకేతము %u ద్వారా అంతముచేయబడింది\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"libpulse తో నిర్వర్తించబడింది %s\n"
+"libpulse తో నిర్వర్తించబడింది %s\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() విఫలమైంది.\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() విఫలమైంది.\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() విఫలమైంది.\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "గణాంకాలను పొందుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "ప్రస్తుతం వుపయోగంలోవుంది: %u బ్లాక్‌లు %s బైట్లను మొత్తంగా కలిగి వున్నాయి.\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "మొత్తం లైఫ్‌టైములో కేటాయించబడింది: %u బ్లాకులు %s బైట్లను మొత్తంగా కలిగివున్నాయి.\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "మాదిరి క్యాచి పరిమాణము: %s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "సేవిక సమాచారమును పొందుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:192
+#, fuzzy, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"వినియోగదారి నామము: %s\n"
+"హోస్టు నామము: %s\n"
+"సేవిక నామము: %s\n"
+"సేవిక వర్షన్: %s\n"
+"అప్రమేయ మాదిరి విశదీకరణ: %s\n"
+"అప్రమేయ ప్రసారమార్గ మాప్: %s\n"
+"అప్రమేయ సింకు: %s\n"
+"అప్రమేయ మూలము: %s\n"
+"కుకీ: %08x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "సింక్ సమాచారమును పొందుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:270
+#, fuzzy, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"సింక్ #%u\n"
+"\tస్థితి: %s\n"
+"\tనామము: %s\n"
+"\tవివరణ: %s\n"
+"\tడ్రైవర్: %s\n"
+"\tమాదిరి విశదీకరణ: %s\n"
+"\tప్రసారమార్గ మాప్: %s\n"
+"\tయజమాని మాడ్యూల్: %u\n"
+"\tనిశ్శబ్దము: %s\n"
+"\tధ్వని: %s%s%s\n"
+"\t        సమతుల్యత %0.2f\n"
+"\tబేస్ ధ్వని: %s%s%s\n"
+"\tమానిటర్ మూలము: %s\n"
+"\tక్రియాహీనత: %0.0f usec, ఆకృతీకరించిన %0.0f usec\n"
+"\tఫ్లాగ్‌లు: %s%s%s%s%s%s\n"
+"\tలక్షణాలు:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tపోర్టులు:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tక్రియాశీల పోర్టు: %s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, fuzzy, c-format
+msgid "\tFormats:\n"
+msgstr "\tపోర్టులు:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "మూలము సమాచారము పొందుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"మూలము #%u\n"
+"\tస్థితి: %s\n"
+"\tనామము: %s\n"
+"\tవివరణ: %s\n"
+"\tడ్రైవర్: %s\n"
+"\tమాదిరి విశదీకరణ: %s\n"
+"\tప్రసారమార్గ మాప్: %s\n"
+"\tయజమాని మాడ్యూల్: %u\n"
+"\tనిశ్శబ్దము: %s\n"
+"\tధ్వని: %s%s%s\n"
+"\t        సమతుల్యత %0.2f\n"
+"\tబేస్ ధ్వని: %s%s%s\n"
+"\tసింక్ యొక్క మానిటర్: %s\n"
+"\tక్రియాహీన: %0.0f usec, ఆకృతీకరించిన %0.0f usec\n"
+"\tఫ్లాగ్‌లు: %s%s%s%s%s%s\n"
+"\tలక్షణాలు:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "వర్తించదు"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "మాడ్యూల్ సమాచారము పొందుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"మాడ్యూల్ #%u\n"
+"\tనామము: %s\n"
+"\tఆర్గుమెంట్: %s\n"
+"\tవినియోగం లెక్కించునది: %s\n"
+"\tలక్షణాలు:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "కక్షిదారి సమాచారము పొందుటలో విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"కక్షిదారి #%u\n"
+"\tడ్రైవర్: %s\n"
+"\tయజమాని మాడ్యూల్: %s\n"
+"\tలక్షణాలు:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "కార్డు సమాచారము పొందుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"కార్డు #%u\n"
+"\tనామము: %s\n"
+"\tడ్రైవర్: %s\n"
+"\tయజమాని మాడ్యూల్: %s\n"
+"\tలక్షణాలు:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tప్రోఫైల్సు:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tక్రియాశీల ప్రొఫైల్: %s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "సింక్ ఇన్పుట్ సమాచారము పొందుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:622
+#, fuzzy, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"సింక్ ఇన్పుట్ #%u\n"
+"\tడ్రైవర్: %s\n"
+"\tయజమాని మాడ్యూల్: %s\n"
+"\tకక్షిదారి: %s\n"
+"\tసింక్: %u\n"
+"\tమాదిరి విశదీకరణము: %s\n"
+"\tప్రసారమార్గ మాప్: %s\n"
+"\tనిశ్శబ్ధము: %s\n"
+"\tవాల్యూమ్: %s\n"
+"\t        %s\n"
+"\t        సమతుల్యత %0.2f\n"
+"\tబఫర్ క్రియాహీనత: %0.0f usec\n"
+"\tసింక్ క్రియాహీనత: %0.0f usec\n"
+"\tపునఃవుదాహరణ పద్దతి: %s\n"
+"\tలక్షణాలు:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "మూలపు అవుట్పుట్ సమాచారము పొందుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:693
+#, fuzzy, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"సింక్ ఇన్పుట్ #%u\n"
+"\tడ్రైవర్: %s\n"
+"\tయజమాని మాడ్యూల్: %s\n"
+"\tకక్షిదారి: %s\n"
+"\tసింక్: %u\n"
+"\tమాదిరి విశదీకరణము: %s\n"
+"\tప్రసారమార్గ మాప్: %s\n"
+"\tనిశ్శబ్ధము: %s\n"
+"\tవాల్యూమ్: %s\n"
+"\t        %s\n"
+"\t        సమతుల్యత %0.2f\n"
+"\tబఫర్ క్రియాహీనత: %0.0f usec\n"
+"\tసింక్ క్రియాహీనత: %0.0f usec\n"
+"\tపునఃవుదాహరణ పద్దతి: %s\n"
+"\tలక్షణాలు:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "మాదిరి సమాచారము పొందుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"మాదిరి #%u\n"
+"\tనామము: %s\n"
+"\tమాదిరి విశదీకరణ: %s\n"
+"\tప్రసారమార్గము మాప్: %s\n"
+"\tధ్వని: %s\n"
+"\t        %s\n"
+"\t        సమతుల్యత %0.2f\n"
+"\tనిడివి: %0.1fs\n"
+"\tపరిమాణము: %s\n"
+"\tలేటు: %s\n"
+"\tదస్త్రనామము: %s\n"
+"\tలక్షణాలు:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "వైఫైల్యము: %s"
+
+#: ../src/utils/pactl.c:915
+#, fuzzy, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "మూలము సమాచారము పొందుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "మాదిరి అప్‌లోడు చేయుటకు విఫలమైంది: %s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "దస్త్రము యొక్క అపరిపక్వ ముగింపు"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr ""
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr ""
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr ""
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr ""
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr ""
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr ""
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr ""
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr ""
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr ""
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr ""
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr ""
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+#, fuzzy
+msgid "server"
+msgstr "చెల్లని సేవిక"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT పొందింది, నిష్క్రమించుచున్నది."
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "చెల్లనటువంటి వాల్యూమ్ విశదీకరణ"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr ""
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr ""
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr ""
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr ""
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr ""
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr ""
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr ""
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr ""
+
+#: ../src/utils/pactl.c:1339
+#, fuzzy, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"libpulse తో నిర్వర్తించబడింది%s\n"
+"libpulse తో లింకుచేయబడింది %s\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr ""
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "లోడువ్వుటకు దయచేసి మాదిరి దస్త్రమును తెలుపుము"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "శబ్దపు దస్త్రమును తెరువుటకు విఫలమైంది."
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "హెచ్చరిక: దస్త్రమునుండి మాదిరి విశదీకరణను నిర్ణయించుటకు విఫలమైంది."
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "ప్లే చేయుటకు మీరు మాదిరి నామమును తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "తొలగించుటకు మీరు మాదిరి నామమును తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "మీరు సింక్ ఇన్పుట్ విషయసూచిక మరియు సింక్ తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "మీరు మూలము అవుట్పుట్ విషయసూచిక మరియు మూలము తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "మీరు మాడ్యూల్ నామము మరియు ఆర్గుమెంట్లు తెలుపవలసి వుంది."
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "మీరు మాడ్యూల్ విషయసూచిక తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1560
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"మీరు వొక సింకు కన్నా యెక్కువ తెలుపవలసి వుండకపోవచ్చు. మీరు బూలియన్ విలువను తెలుపవలసి వుంది."
+
+#: ../src/utils/pactl.c:1573
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"మీరు వొక మూలము కన్నా యెక్కువ తెలుపవలసి వుండకపోవచ్చు. మీరు బూలియన్ విలువను తెలుపవలసి వుంది."
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "మీరు కార్డ్ నామము/విషయసూచిక మరియు ప్రొఫైల్ నామము తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "మీరు సింక్‌ నామము/విషయసూచిక మరియు ప్రొఫైల్ నామము తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "మీరు మూలము నామము/విషయసూచిక మరియు ప్రొఫైల్ నామము తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "మీరు సింక్ నామము/విషయసూచిక మరియు ప్రొఫైల్ నామము తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "మీరు మూలము నామము/విషయసూచిక మరియు ప్రొఫైల్ నామము తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "మీరు సింక్ ఇన్పుట్ విషయసూచిక మరియు వాల్యూమ్ తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "చెల్లని సింకు యిన్పుట్ విషయసూచిక"
+
+#: ../src/utils/pactl.c:1660
+#, fuzzy
+msgid "You have to specify a source output index and a volume"
+msgstr "మీరు మూలము అవుట్పుట్ విషయసూచిక మరియు మూలము తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1665
+#, fuzzy
+msgid "Invalid source output index"
+msgstr "చెల్లని సింకు యిన్పుట్ విషయసూచిక"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "మీరు సింక్‌ నామము/విషయసూచిక మరియు మ్యూట్ బూలియన్ తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+#, fuzzy
+msgid "Invalid mute specification"
+msgstr "చెల్లనటువంటి మాదిరి విశదీకరణ"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "మీరు మూలపు నామము/విషయసూచిక మరియు మ్యూట్ బూలియన్ తెలుపవలసివుంది"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "మీరు సింక్ ఇన్పుట్ విషయసూచిక మరియు మ్యూట్ బూలియన్ తెలుపవలసివుంది"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "చెల్లనటువంటి సింకు యిన్పుట్ విషయసూచిక విశదీకరణ"
+
+#: ../src/utils/pactl.c:1732
+#, fuzzy
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "మీరు మూలపు నామము/విషయసూచిక మరియు మ్యూట్ బూలియన్ తెలుపవలసివుంది"
+
+#: ../src/utils/pactl.c:1737
+#, fuzzy
+msgid "Invalid source output index specification"
+msgstr "చెల్లనటువంటి సింకు యిన్పుట్ విషయసూచిక విశదీకరణ"
+
+#: ../src/utils/pactl.c:1756
+#, fuzzy
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "మీరు సింక్‌ నామము/విషయసూచిక మరియు మ్యూట్ బూలియన్ తెలుపవలసి వుంది"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "ఎటువంటి విలువైన ఆదేశము తెలుపబడలేదు."
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "ఆదేశ వరుసను పార్శ్ చేయుటకు విఫలమైంది.\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "సేవిక: %s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "మూలము: %s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "సింక్: %s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "కుకీ: %s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "కుకీ డాటా పార్శ్ చేయుటకు విఫలమైంది\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "కుకీ డాటా దాయుటకు విఫలమైంది\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "కక్షిదారి ఆకృతీకరణ దస్త్రమును లోడు చేయుటకు విఫలమైంది.\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "ఎన్విరాన్మెంట్ ఆకృతీకరణ డాటాను చదువుటకు విఫలమైంది.\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN పొందుటకు విఫలమైంది.\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "కుకీ డాటా లోడు చేయుటకు విఫలమైంది\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "ఇంకా యింప్లిమెంట్ చేయలేదు\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "PulseAudio డెమోన్ నడుచుటలేదు, లేదా సెషన్ డెమోన్ వలె నడుచుటలేదు."
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio డెమోన్ నాశనం చేయుటలో విఫలమైంది."
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "డెమోన్ స్పందించుటలేదు."
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "ఆటోస్పాన్ తాళంను యాక్సిస్ చేయలేదు."
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA కొత్త డాటాను పరికరముకు వ్రాయుటకు మనలను జాగరూక పరిచింది, అయితే అక్కడ వాస్తవంగా వ్రాయుటకు యేమి "
+"లేదు!\n"
+"సాదారణం యిది ALSA డ్రైవర్ %s నందు బగ్ కావచ్చును. దయచేసి దీనిని ALSA అభివృద్దికారులకు తెలియపరచండి.\n"
+"మనము POLLOUT అమర్పు ద్వారా జాగరూక పరచబడినాము -- ఏమైనప్పటికి snd_pcm_avail() అనునది 0 ను "
+"యిస్తుంది లేదా వేరొక విలువ < min_avail యిస్తుంది."
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA కొత్త డాటాను పరికరమునుండి చదువుటకు మనలను జాగరూక పరిచింది, అయితే అక్కడ వాస్తవంగా "
+"చదువుటకు యేమి లేదు!\n"
+"సాదారణం యిది ALSA డ్రైవర్ %s నందు బగ్ కావచ్చును. దయచేసి దీనిని ALSA అభివృద్దికారులకు తెలియపరచండి.\n"
+"మనము POLLOUT అమర్పు ద్వారా జాగరూక పరచబడినాము -- ఏమైనప్పటికి snd_pcm_avail() అనునది 0 ను "
+"యిస్తుంది లేదా వేరొక విలువ < min_avail యిస్తుంది."
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "ఆఫ్"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "హై ఫెడిలిటి ప్లేబ్యాక్ (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "హై ఫెడిలిటి కాప్చర్ (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "టెలిఫోనీ డూప్లెక్స్ (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr ""
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "పల్స్ ఆడియో సౌండ్ సేవిక"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "అవుట్పుట్ పరికరములు"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "ఇన్పుట్ పరికరములు"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@ పై ఆడియో"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "ఇన్పుట్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "డాకింగ్ స్టేషన్ ఇన్పుట్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+#, fuzzy
+msgid "Docking Station Microphone"
+msgstr "డాకింగ్ స్టేషన్ మైక్రోఫోన్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+#, fuzzy
+msgid "Docking Station Line In"
+msgstr "డాకింగ్ స్టేషన్ ఇన్పుట్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "లైన్-యిన్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "మైక్రోఫోన్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+#, fuzzy
+msgid "Front Microphone"
+msgstr "డాకింగ్ స్టేషన్ మైక్రోఫోన్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+#, fuzzy
+msgid "Rear Microphone"
+msgstr "మైక్రోఫోన్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "బహిర్గత మైక్రోఫోన్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "అంతర్గత మైక్రోఫోన్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "రేడియో"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "వీడియో"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "స్వయంచాలకంగా పొందు నియంత్రణ"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "స్వయంచాలకంగా పొందు ఏ నియంత్రణ లేదు"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "బూస్ట్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "బూస్ట్ లేదు"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "ఎంప్లిఫైర్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "ఎంప్లిఫైర్ లేదు"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+#, fuzzy
+msgid "Bass Boost"
+msgstr "బూస్ట్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+#, fuzzy
+msgid "No Bass Boost"
+msgstr "బూస్ట్ లేదు"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "ఎనలాగ్ హెడ్‌ఫోన్స్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "ఎనలాగ్ యిన్పుట్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "డాకింగ్ స్టేషన్ మైక్రోఫోన్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "ఎనలాగ్ అవుట్పుట్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "ఎనలాగ్ అవుట్పుట్ (LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+#, fuzzy
+msgid "Line Out"
+msgstr "లైన్-యిన్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "ఎనలాగ్ మోనో అవుట్పుట్"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+#, fuzzy
+msgid "Speakers"
+msgstr "ఎనలాగ్ స్టీరియో"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr ""
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+#, fuzzy
+msgid "Digital Output (S/PDIF)"
+msgstr "డిజిటల్ స్టీరియో (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+#, fuzzy
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "డిజిటల్ స్టీరియో (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "ఎనలాగ్ మోనో"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "ఎనలాగ్ స్టీరియో"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "ఎనలాగ్ సరౌండ్ 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "ఎనలాగ్ సరౌండ్ 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "ఎనలాగ్ సరౌండ్ 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "ఎనలాగ్ సరౌండ్ 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "ఎనలాగ్ సరౌండ్ 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "ఎనలాగ్ సరౌండ్ 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "ఎనలాగ్ సరౌండ్ 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "ఎనలాగ్ సరౌండ్ 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "ఎనలాగ్ సరౌండ్ 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "ఎనలాగ్ సరౌండ్ 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "ఎనలాగ్ సరౌండ్ 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "డిజిటల్ స్టీరియో (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+#, fuzzy
+msgid "Digital Passthrough  (IEC958)"
+msgstr "డిజిటల్ స్టీరియో (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "డిజిటల్ సరౌండ్ 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "డిజిటల్ సరౌండ్ 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "డిజిటల్ స్టీరియో (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+#, fuzzy
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "డిజిటల్ సరౌండ్ 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "ఎనలాగ్ మోనో డుప్లెక్స్"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "ఎనలాగ్ స్టీరియో డుప్లెక్స్"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "డిజిటల్ స్టీరియో డుప్లెక్స్ (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, fuzzy, c-format
+msgid "%s Output"
+msgstr "Null అవుట్పుట్"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, fuzzy, c-format
+msgid "%s Input"
+msgstr "ఇన్పుట్"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr ""
+
+#: ../src/modules/module-equalizer-sink.c:76
+#, fuzzy
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<సింక్ నామము> sink_properties=<సింకు లక్షణములు> master=<ఫిల్టర్‌కు సింకు "
+"నామము> format=<మాదిరి ఫార్మాట్> rate=<మాదిరి రేటు> channels=<చానల్సు సంఖ్య> "
+"channel_map=<చానల్ మాప్> plugin=<ladspa ప్లగిన్ నామము> label=<ladspa ప్లగిన్ లేబుల్> "
+"control=<ఇన్పుట్ నియంత్రణ విలువలయొక్క జాబితా>"
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+"44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to "
+"1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+"44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats "
+"combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+"alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+
+#: ../src/tests/resampler-test.c:356
+#, fuzzy, c-format
+msgid "%s %s\n"
+msgstr "%s %s"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr ""
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit అనునది ఈ ప్లాట్‌ఫాం నందు మద్దతివ్వబడదు."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() విఫలమైంది"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "మూలము అవుట్పుట్ #%u\n"
+#~ "\tడ్రైవర్: %s\n"
+#~ "\tయజమాని మాడ్యూల్: %s\n"
+#~ "\tకక్షిదారి: %s\n"
+#~ "\tమూలము: %u\n"
+#~ "\tమాదిరి విశదీకరణ: %s\n"
+#~ "\tప్రసారమార్గ మాప్: %s\n"
+#~ "\tబఫర్ క్రియాహీనత: %0.0f usec\n"
+#~ "\tమూలము క్రియాహీనత: %0.0f usec\n"
+#~ "\tపునఃవుదాహరణ విశదీకరణ: %s\n"
+#~ "\tలక్షణాలు:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "డిజిటల్ సరౌండ్ 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "తక్కువ తరచుదనం వెలువరించునది"
diff --git a/po/tr.po b/po/tr.po
new file mode 100644 (file)
index 0000000..9a72759
--- /dev/null
+++ b/po/tr.po
@@ -0,0 +1,3357 @@
+# Turkish translation for PulseAudio.
+# Copyright (C) 2014 PulseAudio's COPYRIGHT HOLDER
+# This file is distributed under the same license as the PulseAudio package.
+# Necdet Yücel <necdetyucel@gmail.com>, 2014.
+# Kaan Özdinçer <kaanozdincer@gmail.com>, 2014.
+# Muhammet Kara <muhammetk@gmail.com>, 2015, 2016, 2017.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PulseAudio master\n"
+"Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
+"product=PulseAudio&keywords=I18N+L10N&component=misc\n"
+"POT-Creation-Date: 2017-05-02 15:27+0000\n"
+"PO-Revision-Date: 2017-05-20 11:16+0300\n"
+"Last-Translator: Muhammet Kara <muhammetk@gmail.com>\n"
+"Language-Team: Türkçe <gnome-turk@gnome.org>\n"
+"Language: tr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Gtranslator 2.91.7\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+#| msgid ""
+#| "%s [options]\n"
+#| "\n"
+#| "COMMANDS:\n"
+#| "  -h, --help                            Show this help\n"
+#| "      --version                         Show version\n"
+#| "      --dump-conf                       Dump default configuration\n"
+#| "      --dump-modules                    Dump list of available modules\n"
+#| "      --dump-resample-methods           Dump available resample methods\n"
+#| "      --cleanup-shm                     Cleanup stale shared memory "
+#| "segments\n"
+#| "      --start                           Start the daemon if it is not "
+#| "running\n"
+#| "  -k  --kill                            Kill a running daemon\n"
+#| "      --check                           Check for a running daemon (only "
+#| "returns exit code)\n"
+#| "\n"
+#| "OPTIONS:\n"
+#| "      --system[=BOOL]                   Run as system-wide instance\n"
+#| "  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+#| "      --fail[=BOOL]                     Quit when startup fails\n"
+#| "      --high-priority[=BOOL]            Try to set high nice level\n"
+#| "                                        (only available as root, when "
+#| "SUID or\n"
+#| "                                        with elevated RLIMIT_NICE)\n"
+#| "      --realtime[=BOOL]                 Try to enable realtime "
+#| "scheduling\n"
+#| "                                        (only available as root, when "
+#| "SUID or\n"
+#| "                                        with elevated RLIMIT_RTPRIO)\n"
+#| "      --disallow-module-loading[=BOOL]  Disallow module user requested "
+#| "module\n"
+#| "                                        loading/unloading after startup\n"
+#| "      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+#| "      --exit-idle-time=SECS             Terminate the daemon when idle "
+#| "and this\n"
+#| "                                        time passed\n"
+#| "      --scache-idle-time=SECS           Unload autoloaded samples when "
+#| "idle and\n"
+#| "                                        this time passed\n"
+#| "      --log-level[=LEVEL]               Increase or set verbosity level\n"
+#| "  -v  --verbose                         Increase the verbosity level\n"
+#| "      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+#| "                                        Specify the log target\n"
+#| "      --log-meta[=BOOL]                 Include code location in log "
+#| "messages\n"
+#| "      --log-time[=BOOL]                 Include timestamps in log "
+#| "messages\n"
+#| "      --log-backtrace=FRAMES            Include a backtrace in log "
+#| "messages\n"
+#| "  -p, --dl-search-path=PATH             Set the search path for dynamic "
+#| "shared\n"
+#| "                                        objects (plugins)\n"
+#| "      --resample-method=METHOD          Use the specified resampling "
+#| "method\n"
+#| "                                        (See --dump-resample-methods for\n"
+#| "                                        possible values)\n"
+#| "      --use-pid-file[=BOOL]             Create a PID file\n"
+#| "      --no-cpu-limit[=BOOL]             Do not install CPU load limiter "
+#| "on\n"
+#| "                                        platforms that support it.\n"
+#| "      --disable-shm[=BOOL]              Disable shared memory support.\n"
+#| "\n"
+#| "STARTUP SCRIPT:\n"
+#| "  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin "
+#| "module with\n"
+#| "                                        the specified argument\n"
+#| "  -F, --file=FILENAME                   Run the specified script\n"
+#| "  -C                                    Open a command line on the "
+#| "running TTY\n"
+#| "                                        after startup\n"
+#| "\n"
+#| "  -n                                    Don't load default script file\n"
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [seçenekler]\n"
+"\n"
+"KOMUTLAR:\n"
+"  -h, --help                            Bu yardımı gösterir\n"
+"      --version                         Sürümü gösterir\n"
+"      --dump-conf                       Öntanımlı yapılandırmayı döker\n"
+"      --dump-modules                    Mevcut modüllerin listesini döker\n"
+"      --dump-resample-methods           Kullanılabilir yeniden örnekleme "
+"yöntemlerini döker\n"
+"      --cleanup-shm                     Eski paylaşımlı bellek segmentlerini "
+"temizler\n"
+"      --start                           Eğer çalışmıyorsa artalan işlemleri "
+"başlatır\n"
+"  -k  --kill                             Çalışan bir artalan işlemi "
+"sonlandırır\n"
+"      --check                         Çalışan bir artalan işlemi kontrol "
+"eder (sadece çıkış kodu döner)\n"
+"\n"
+"SEÇENEKLER:\n"
+"      --system[=BOOL]                   Sistem çapında örnek olarak "
+"çalıştırır\n"
+"  -D, --daemonize[=BOOL]                Başladıktan sonra bir artalan işlem "
+"gibi çalıştırır\n"
+"      --fail[=BOOL]                     Başlangıç başarısız olduğunda çıkar\n"
+"      --high-priority[=BOOL]            Yüksek seviye ayarlamayı dener\n"
+"                                        (SUID ya da yüksek RLIMIT_NICE ile\n"
+"                                        sadece root olarak kullanılabilir)\n"
+"      --realtime[=BOOL]                 Gerçek zamanlı zamanlamayı "
+"etkinleştirmeyi dener\n"
+"                                        (SUID ya da yüksek RLIMIT_RTPRIO "
+"ile\n"
+"                                        sadece root olarak kullanılabilir)\n"
+"      --disallow-module-loading[=BOOL]  Başlangıçtan sonra kullanıcının\n"
+"                                        yükleme/kaldırma istediği modüllere "
+"izin vermez\n"
+"      --disallow-exit[=BOOL]            Kullanıcının çıkış isteğine izin "
+"vermez\n"
+"      --exit-idle-time=SECS             Boştayken ve  zamanı geçtiğinde\n"
+"                                        artalan işlemi sonlandırır\n"
+"      --scache-idle-time=SECS           Boştayken ve zamanı geçtiğinde "
+"otomatik\n"
+"                                        yüklenmiş örnekleri kaldırır\n"
+"      --log-level[=LEVEL]               Ayrıntı seviyesini ayarlar ya da "
+"artırır\n"
+"  -v  --verbose                         Ayrıntı seviyesini artırır\n"
+"      --log-target={auto,syslog,stderr,dosya:PATH,yenidosya:PATH}\n"
+"                                        Logların bulunacağı hedefi yolu "
+"belirtir\n"
+"      --log-meta[=BOOL]                 Log iletilerinde kod konumları "
+"bulunur\n"
+"      --log-time[=BOOL]                  Log iletilerinde zaman damgası "
+"bulunur\n"
+"      --log-backtrace=FRAMES            Log iletilerinde bir geri izleme "
+"bulunur\n"
+"  -p, --dl-search-path=PATH             Dinamik paylaşımlı nesneler "
+"(eklentiler)\n"
+"                                        için arama yolu ayarlar\n"
+"      --resample-method=METHOD          Belirtilen yeniden örneklendirme "
+"yöntemini kullanır\n"
+"                                        (Olası değerler için şuna bakın:\n"
+"                                         --dump-resample-methods)\n"
+"      --use-pid-file[=BOOL]             Bir PID dosyası oluştur\n"
+"      --no-cpu-limit[=BOOL]             Desteklendiği platformlarda\n"
+"                                        CPU yükü sınırlayıcı kurmaz.\n"
+"      --disable-shm[=BOOL]              Paylaşımlı bellek desteğini devre "
+"dışı bırakır.\n"
+"      --enable-memfd[=BOOL]             memfd paylaşılan bellek desteğini "
+"etkinleştirir.\n"
+"\n"
+"BAŞLATMA KOMUT DOSYALARI:\n"
+"  -L, --load=\"MODÜL DEĞİŞKENLERİ\"       Belirtilen değişkenler ile\n"
+"                                        belirtilen eklenti modüllerini "
+"yükler.\n"
+"  -F, --file=DOSYAADI                   Belirtilen betiği çalıştırır\n"
+"  -C                                    Başlangıçtan sonra çalışan TTY "
+"üzerinde\n"
+"                                        bir komut satırı açar\n"
+"\n"
+"  -n                                    Öntanımlı betik dosyasını yüklemez\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "--fail boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level log seviyesi değişkeni bekler (ya 0..4 aralığında sayısal değer "
+"ya da debug, info, notice, warn, error değişkenlerinden birini)."
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "--realtime boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Geçersiz log hedefi: ya 'syslog', 'journal', 'stderr', 'auto' ya da geçerli "
+"bir dosya adı 'dosya: <path>','yenidosya:<path>' kullan."
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Geçersiz log hedefi: ya 'syslog', 'stderr', 'auto' ya da geçerli bir dosya "
+"adı 'dosya: <path>','yenidosya:<path>' kullan."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "--log-time boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Geçersiz yeniden örneklendirme yöntemi '%s'."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "--system boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm boolean değişken bekler"
+
+#: ../src/daemon/cmdline.c:397
+#| msgid "--realtime expects boolean argument"
+msgid "--enable-memfd expects boolean argument"
+msgstr "--enable-memfd boolean değişken bekler"
+
+#: ../src/daemon/daemon-conf.c:262
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Geçersiz log hedefi '%s'."
+
+#: ../src/daemon/daemon-conf.c:277
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Geçersiz log seviyesi '%s'."
+
+#: ../src/daemon/daemon-conf.c:292
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Geçersiz yeniden örneklendirme yöntemi '%s'."
+
+#: ../src/daemon/daemon-conf.c:314
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Geçersiz rlimit '%s'."
+
+#: ../src/daemon/daemon-conf.c:334
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Geçersiz örnekleme biçimi '%s'."
+
+#: ../src/daemon/daemon-conf.c:351 ../src/daemon/daemon-conf.c:368
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Geçersiz örnekleme oranı '%s'."
+
+#: ../src/daemon/daemon-conf.c:391
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Geçersiz örnekleme kanalları '%s'."
+
+#: ../src/daemon/daemon-conf.c:408
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Geçersiz kanal adresleme '%s'."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Geçersiz bölümlenme sayısı '%s'."
+
+#: ../src/daemon/daemon-conf.c:442
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Geçersiz bölümlenme boyutu '%s'."
+
+#: ../src/daemon/daemon-conf.c:459
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Geçersiz nice seviyesi '%s'."
+
+#: ../src/daemon/daemon-conf.c:502
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Geçersiz sunucu türü '%s'."
+
+#: ../src/daemon/daemon-conf.c:620
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Yapılandırma dosyası açılamadı: %s"
+
+#: ../src/daemon/daemon-conf.c:636
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"Belirtilen öntanımlı kanal adresleme belirtilmiş öntanımlı kanal sayısından "
+"farklı sayıda kanala sahiptir."
+
+#: ../src/daemon/daemon-conf.c:723
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Yapılandırma dosyasından oku: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Ad: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Hiçbir modül bilgisi bulunamadı\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Sürüm: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Açıklama: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Yazar: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Kullanım: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Bir kez Yükle: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "UYGUNSUZLUK UYARISI: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Yol: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "'%s' modülü açılamadı: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "Orjinal lt_dlopen yükleyicisi bulunamadı."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "Yeni dl yükleyici ayırma işlemi başarısız oldu."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Bind-now-loader eklenemedi."
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Kullanıcı '%s' bulunamadı."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Grup '%s' bulunamadı."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "'%s' kullanıcısı ve '%s' grubunun GID eşleşmiyor."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Kullanıcı '%s''nin ev dizini '%s' değildir, yoksayılıyor."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "'%s' oluşturulamadı: %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Grup listesi değiştirilemedi: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "GID değiştirilemedi: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "UID değiştirilemedi: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "Sistem geneli kipi bu platformda desteklenmiyor."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Komut satırı çözümlenemedi."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Sistem kipi root olmayan kullanıcıyı reddetti. Sadece D-Bus sunucu arama "
+"servisi başlatıyor."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Artalan işlem durdurulamadı: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Bu programın root kullanıcı ile çalıştırılması amaçlanmamıştır (--system "
+"belirtilmediyse)."
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Root hakları gereklidir."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "--start sistem örnekleri için desteklenmiyor."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+"Kullanıcı tarafından yapılandırılmış şuradaki sunucu: %s, başlamayı/otomatik "
+"başlamayı reddediyor."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Kullanıcı tarafından yapılandırılmış, yerel olarak görülen şuradaki sunucu: "
+"%s. "
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr "Sistem kipinde çalıştırılıyor fakat --disallow-exit ayarlı değil."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr ""
+"Sistem kipinde çalıştırılıyor fakat --disallow-module-loading ayarlı değil."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr "Sistem kipinde çalıştırılıyor, SHM kipi zorla devre dışı bırakılıyor."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr ""
+"Sistem kipinde çalıştırılıyor, boşta kalma süresi çıkışı zorla devre dışı "
+"bırakılıyor."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "Stdio alınamadı."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() başarısız oldu: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() başarısız oldu: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:562
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() başarısız oldu: %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Artalan işlemleri başlatma başarısız oldu."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() başarısız oldu: %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Makine kimliği alınamadı"
+
+#: ../src/daemon/main.c:974
+#| msgid ""
+#| "OK, so you are running PA in system mode. Please note that you most "
+#| "likely shouldn't be doing that.\n"
+#| "If you do it nonetheless then it's your own fault if things don't work as "
+#| "expected.\n"
+#| "Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+#| "Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why "
+#| "system mode is usually a bad idea."
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually "
+"do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Tamam, sistem kipinde PA çalıştırıyorsunuz. Lütfen gerçekten bunu yapmak "
+"istediğinizden emin olun.\n"
+"Lütfen sistem kipinin neden genellikle kötü bir fikir olduğunun açıklamasını "
+"http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/"
+"WhatIsWrongWithSystemWide/ adresinden okuyunuz."
+
+#: ../src/daemon/main.c:990
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() başarısız oldu."
+
+#: ../src/daemon/main.c:1022
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() başarısız oldu."
+
+#: ../src/daemon/main.c:1092
+msgid "Failed to initialize daemon."
+msgstr "Artalan işlem başlatılamadı."
+
+#: ../src/daemon/main.c:1097
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "Hiç yüklü modül olmadan artalan işlemlerin çalışması reddediliyor."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio Ses Sistemi"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "PulseAudio Ses Sistemini Başlat"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Giriş"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Yerleştirme İstasyonu Girişi"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Yerleştirme İstasyonu Mikrofonu"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Yerleştirme İstasyonu Hat Girişi"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Hat girişi"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1696
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Ön Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Arka Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Harici Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Dahili Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Radyo"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Görüntü"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Otomatik Kazanç Kontrolü"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Otomatik Kazanç Kontrolü Yok"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Artır"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Artırma Yok"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Yükseltici"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Anfi Yok"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Bas Artır"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Bas Artırma"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1703
+msgid "Speaker"
+msgstr "Hoparlör"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Kulaklıklar"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Analog Giriş"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Yapışık Mikrofon"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Mikrofonlu Kulaklık"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Analog Çıkış"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "Ayrı Tekli Çıktılar üzerinde LFE"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Hat Çıkışı"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Analog Tekli Çıkış"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Hoparlörler"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / ÇıkışıNoktası"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Dijital Çıkış (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Dijital Giriş (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Sayısal Düzgeçiş (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Çok Kanallı Girdi"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Çok Kanallı Çıktı"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Analog Mono"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Analog Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Çok kanallı"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Analog Çevresel Ses 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Analog Çevresel Ses 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Analog Çevresel Ses 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Analog Çevresel Ses 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Analog Çevresel Ses 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Analog Çevresel Ses 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Analog Çevresel Ses 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Analog Çevresel Ses 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Analog Çevresel Ses 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Analog Çevresel Ses 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Analog Çevresel Ses 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Dijital İki Kanallı (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Sayısal Düzgeçiş  (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Dijital Çevresel Ses 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Dijital Çevresel Ses 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Dijital Çevresel Ses 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Dijital İki Kanallı (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Dijital Çevresel Ses 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4019 ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Mono Duplex"
+msgstr "Analog Tekli İkili"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Analog Stereo Duplex"
+msgstr "Analog İkili Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Sayısal İkili Stereo (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+msgid "Multichannel Duplex"
+msgstr "Çok Kanallı Duplex"
+
+#: ../src/modules/alsa/alsa-mixer.c:4156
+#| msgid "Analog Stereo Duplex"
+msgid "Stereo Duplex"
+msgstr "İkili Stereo"
+
+#: ../src/modules/alsa/alsa-mixer.c:4157
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2324
+#: ../src/modules/bluetooth/module-bluez5-device.c:1951
+msgid "Off"
+msgstr "Kapalı"
+
+#: ../src/modules/alsa/alsa-mixer.c:4256
+#, c-format
+msgid "%s Output"
+msgstr "%s Çıkışı"
+
+#: ../src/modules/alsa/alsa-mixer.c:4264
+#, c-format
+msgid "%s Input"
+msgstr "%s Girişi"
+
+#: ../src/modules/alsa/alsa-sink.c:572
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA aygıta yeni veri yazmamız için bizi uyardı fakat aslında yazılacak "
+"hiçbir şey yoktu.\n"
+"Büyük ihtimalle bu ALSA '%s' sürücüsünde bir hatadır. Lütfen bu sorunu ALSA "
+"geliştiricilerine bildirin.\n"
+"Biz POLLOUT ayarı ile uyandırıldık -- bununla birlikte sonraki "
+"snd_pcm_avail() 0 ya da min_avail değerinden küçük başka bir değer döndü."
+
+#: ../src/modules/alsa/alsa-sink.c:756
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA aygıta yeni veri yazmamız için bizi uyardı fakat aslında yazılacak "
+"hiçbir şey yok!\n"
+"Büyük ihtimalle bu ALSA '%s' sürücüsünde bir hatadır. Lütfen bu sorunu ALSA "
+"geliştiricilerine bildirin.\n"
+"Biz POLLOUT ayarı ile anladık -- ayrıca sonraki bir snd_pcm_avail() 0 ya da "
+"min_avail değerinden küçük başka bir değer döndü."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA aygıttan yeni veri okumamız için bizi uyardı fakat aslında okunacak "
+"hiçbir şey yok.\n"
+"Büyük ihtimalle bu bir ALSA '%s' sürücüsü hatasıdır. Lütfen bu sorunu ALSA "
+"geliştiricilerine bildirin.\n"
+"Biz POLLIN ayarı ile uyandırıldık -- bununla birlikte sonraki bir "
+"snd_pcm_avail() 0 ya da min_avail değerinden küçük başka bir değer döndü."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA aygıttan yeni veri okumamız için bizi uyardı fakat aslında okunacak "
+"hiçbir şey yok!\n"
+"Büyük ihtimalle bu bir ALSA '%s' sürücüsü hatasıdır. Lütfen bu sorunu ALSA "
+"geliştiricilerine bildirin.\n"
+"Biz POLLIN ayarı ile anladık -- ayrıca sonraki snd_pcm_avail() 0 ya da "
+"min_avail değerinden küçük başka bir değer döndü."
+
+#: ../src/modules/alsa/alsa-util.c:1168 ../src/modules/alsa/alsa-util.c:1243
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() son derece büyük bir değer döndü: %lu bayt (%lu ms).\n"
+"Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA "
+"geliştiricilerine bildirin."
+
+#: ../src/modules/alsa/alsa-util.c:1218
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() son derece büyük bir değer döndü: %li bayt (%s%lu ms).\n"
+"Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA "
+"geliştiricilerine bildirin."
+
+#: ../src/modules/alsa/alsa-util.c:1259
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() garip değerler döndü: gecikme %lu kazançtan %lu daha "
+"azdır.\n"
+"Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA "
+"geliştiricilerine bildirin."
+
+#: ../src/modules/alsa/alsa-util.c:1302
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() son derece büyük bir değer döndü: %lu bayt (%lu ms).\n"
+"Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA "
+"geliştiricilerine bildirin."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1686
+msgid "Headset"
+msgstr "Kulaklık"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1691
+msgid "Handsfree"
+msgstr "Ahizesiz"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1709
+msgid "Headphone"
+msgstr "Kulaklık"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1714
+msgid "Portable"
+msgstr "Taşınabilir"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1719
+msgid "Car"
+msgstr "Araba"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1724
+msgid "HiFi"
+msgstr "Yüksek duyarlılık"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2134
+#: ../src/modules/bluetooth/module-bluez5-device.c:1729
+msgid "Phone"
+msgstr "Telefon"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2142
+#: ../src/modules/bluetooth/module-bluez5-device.c:1681
+#: ../src/modules/bluetooth/module-bluez5-device.c:1697
+#: ../src/modules/bluetooth/module-bluez5-device.c:1735
+msgid "Bluetooth Output"
+msgstr "Bluetooth Çıkışı"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2145
+#: ../src/modules/bluetooth/module-bluez5-device.c:1680
+#: ../src/modules/bluetooth/module-bluez5-device.c:1702
+#: ../src/modules/bluetooth/module-bluez5-device.c:1708
+#: ../src/modules/bluetooth/module-bluez5-device.c:1734
+msgid "Bluetooth Input"
+msgstr "Bluetooth Girişi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2186
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Yüksek Kaliteli Çalma (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2198
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Yüksek Kaliteli Yakalama (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2210
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Çift yönlü Telefon (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2223
+msgid "Handsfree Gateway"
+msgstr "Eller Serbest Geçidi"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1776
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Yüksek Kaliteli Çalma (A2DP Alıcı)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1788
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Yüksek Kaliteli Yakalama (A2DP Kaynak)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1800
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Kulaklık Ana Birimi (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1813
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Kulanlık Ses Geçidi (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<kaynak adı> source_properties=<kaynak özellikleri> "
+"source_master=<süzülecek kaynağın adı> sink_name=<alıcı adı> "
+"sink_properties=<alıcı özellikleri> sink_master=<süzülecek alıcının adı> "
+"adjust_time=<hızların kaç saniyede bir yeniden ayarlanacağı> "
+"adjust_threshold=<kaç ms sonra kaymanın yeniden ayarlanacağı> format=<örnek "
+"biçimi> rate=<örnek hızı> channels=<kaynak sayısı> channel_map=<kaynak "
+"eşlem> aec_method=<kullanılacak uygulanış> aec_args=<AEC motorunun "
+"parametreleri> save_aec=<AEC verilerini /tmp içine kaydet> autoloaded=<bu "
+"modülün otomatik olarak yüklenip yüklenmeyeceğini belirle> "
+"use_volume_sharing=<yes ya da no> use_master_format=<yes ya da no> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "Açık"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Sahte Çıkış"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "Yüklenen alıcı boş bile olsa her zaman en az bir tanesini korur"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Genel Amaçlı Denkleştirici"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"alıcı_adı=<name of the sink> alıcı_özellikleri=<properties for the sink> "
+"ana_alıcı=<sink to connect to> biçim=<sample format> hız=<sample rate> "
+"kanallar=<number of channels> kanal_adresleme=<channel map> "
+"otomatikyüklenmiş=<set if this module is being loaded automatically> "
+"ses_paylaşım_kullan=<yes or no> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "otomatik temizle=<automatically unload unused filters?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Sanal LADSPA alıcısı"
+
+#: ../src/modules/module-ladspa-sink.c:55
+#| msgid ""
+#| "sink_name=<name for the sink> sink_properties=<properties for the sink> "
+#| "master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+#| "channels=<number of channels> channel_map=<input channel map> "
+#| "plugin=<ladspa plugin name> label=<ladspa plugin label> control=<comma "
+#| "separated list of input control values> input_ladspaport_map=<comma "
+#| "separated list of input LADSPA port names> output_ladspaport_map=<comma "
+#| "separated list of output LADSPA port names> "
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<input channel map> plugin=<ladspa plugin name> label=<ladspa "
+"plugin label> control=<comma separated list of input control values> "
+"input_ladspaport_map=<comma separated list of input LADSPA port names> "
+"output_ladspaport_map=<comma separated list of output LADSPA port names> "
+"autoloaded=<set if this module is being loaded automatically> "
+msgstr ""
+"sink_name=<alıcı adı> sink_properties=<alıcı özellikleri> master=<süzülecek "
+"alıcının adı> sink_master=<süzülecek alıcının adı> format=<örnek biçimi> "
+"rate=<örnek hızı> channels=<kanal sayısı> channel_map=<giriş kanal haritası> "
+"plugin=<ladspa eklenti adı> label=<ladspa eklenti etiketi> control=<giriş "
+"denetim değerlerinin virgülle ayrılmış listesi> input_ladspaport_map=<giriş "
+"LADSPA bağlantı noktası adlarının virgülle ayrılmış listesi> "
+"output_ladspaport_map=<çıkış LADSPA bağlantı noktası adlarının virgülle "
+"ayrılmış listesi> autoloaded=<eğer bu modül kendiliğinden yükleniyorsa bunu "
+"ayarlayın> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "Zamanlanmış BOŞ alıcı"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Boş Çıkış"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Çıkış Aygıtları"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Girdi Aygıtları"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@ üzerindeki SES"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "%s@%s için tünel"
+
+#: ../src/modules/module-tunnel-sink-new.c:521
+#: ../src/modules/module-tunnel-source-new.c:520
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "%s/%s tünel"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Sanal çevresel ses alıcısı"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+#| msgid ""
+#| "sink_name=<name for the sink> sink_properties=<properties for the sink> "
+#| "master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+#| "channels=<number of channels> channel_map=<channel map> "
+#| "use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/"
+#| "to/left_hrir.wav "
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<channel map> use_volume_sharing=<yes or no> "
+"force_flat_volume=<yes or no> hrir=/path/to/left_hrir.wav autoloaded=<set if "
+"this module is being loaded automatically> "
+msgstr ""
+"sink_name=<alıcı adı> sink_properties=<alıcı özellikleri> master=<süzülecek "
+"alıcı adı> sink_master=<süzülecek alıcı adı> format=<örnek biçimi> "
+"rate=<örnek hızı> channels=<kanal sayısı> channel_map=<kanal haritası> "
+"use_volume_sharing=<evet ya da hayır> force_flat_volume=<evet ya da hayır> "
+"hrir=/dosyaya/gidenyol/left_hrir.wav autoloaded=<eğer bu modül kendiliğinden "
+"yükleniyorsa bunu ayarlayın> "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio Ses Sunucusu"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Tek Kanallı"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Ön Orta"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Ön Sol"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Ön Sağ"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Arka Orta"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Arka Sol"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Arka Sağ"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Derin Bas Hoparlör"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Ön Ortanın Solu"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Ön Ortanın Sağı"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Sol Yan"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Sağ Yan"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Harici 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Harici 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Harici 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Harici 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Harici 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Harici 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Harici 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Harici 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Harici 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Harici 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Harici 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Harici 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Harici 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Harici 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Harici 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Harici 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Harici 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Harici 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Harici 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Harici 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Harici 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Harici 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Harici 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Harici 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Harici 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Harici 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Harici 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Harici 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Harici 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Harici 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Harici 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Harici 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Üst Orta"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Üst Ön Orta"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Üst Ön Sol"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Üst Ön Sağ"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Üst Arka Orta"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Üst Arka Sol"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Üst Arka Sağ"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:177 ../src/pulse/volume.c:306
+#: ../src/pulse/volume.c:332 ../src/pulse/volume.c:352
+#: ../src/pulse/volume.c:384 ../src/pulse/volume.c:424
+#: ../src/pulse/volume.c:443
+msgid "(invalid)"
+msgstr "(geçersiz)"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Çevresel Ses 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Çevresel Ses 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Çevresel Ses 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Çevresel Ses 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Çevresel Ses 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() başarısız oldu"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() doğru değer döndü"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Çerez veri ayrıştırılamadı"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Bilinmeyen eklenti '%s' için ileti alındı"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "girdi"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "çıktı"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "çift yönlü"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "geçersiz"
+
+#: ../src/pulsecore/core-util.c:1856
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"XDG_RUNTIME_DIR (%s) bize ait değil (uid %d), ancak şunun uid %d! (Eğer bir "
+"root kullanıcısı olarak root olmayan bir PulseAudio ya bağlanmaya "
+"çalışırsanız, doğal protokol üstüne bu bir örnek olabilirdi. Bunu yapmayın.)"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "evet"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "hayır"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Otomatik oluşturma kilidine erişim yok."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Hedef dosya '%s' açılamadı."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Hedef dosyaları '%s', '%s.1', '%s.2' ... '%s.%d' açmayı denedi fakat hiçbiri "
+"açılamadı."
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Geçersiz log hedefi."
+
+#: ../src/pulsecore/sink.c:3464
+msgid "Built-in Audio"
+msgstr "Dahili Ses"
+
+#: ../src/pulsecore/sink.c:3469
+msgid "Modem"
+msgstr "Modem"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "TAMAM"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Erişim engellendi"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Bilinmeyen komut"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Geçersiz değişken"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "Giriş var"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Hiçbir giriş yok"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "Bağlantı reddedildi"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Protokol hatası"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Zaman aşımı"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Kimlik doğrulama anahtarı yok"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "İç hata"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "Bağlantı sonlandırıldı"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Giriş durduruldu"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Geçersiz sunucu"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Modül başlatma başarısız oldu"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Kötü durum"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Veri yok"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Uyumsuz protokol sürümü"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Çok büyük"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Desteklenmeyen"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Bilinmeyen hata kodu"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Böyle bir uzantı yok"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Kullanılmayan işlev"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Eksik uygulama"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Müşteri çatallandı"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Giriş/Çıkış hatası"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Aygıt ya da kaynak meşgul"
+
+#: ../src/pulse/sample.c:179
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:197
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:134
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Akış boşaltma başarısız oldu: %s"
+
+#: ../src/utils/pacat.c:139
+msgid "Playback stream drained."
+msgstr "Playback akışı boşaltıldı."
+
+#: ../src/utils/pacat.c:150
+msgid "Draining connection to server."
+msgstr "Sunucuya akış bağlantısı."
+
+#: ../src/utils/pacat.c:163
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:194 ../src/utils/pacat.c:543
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() başarısız oldu: %s"
+
+#: ../src/utils/pacat.c:244 ../src/utils/pacat.c:274
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() başarısız oldu: %s"
+
+#: ../src/utils/pacat.c:324
+msgid "Stream successfully created."
+msgstr "Akış başarılı bir şekilde oluşturuldu."
+
+#: ../src/utils/pacat.c:327
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() başarısız oldu: %s"
+
+#: ../src/utils/pacat.c:331
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Tampon ölçüleri: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:334
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Tampon ölçüleri: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:338
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Örnekleme tanımı '%s', kanal listesi '%s' kullanma."
+
+#: ../src/utils/pacat.c:342
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "Aygıta %s bağlanıldı (dizin: %u, askıda kalan: %s)."
+
+#: ../src/utils/pacat.c:352
+#, c-format
+msgid "Stream error: %s"
+msgstr "Akış hatası: %s"
+
+#: ../src/utils/pacat.c:362
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Akış aygıtı askıda. %s"
+
+#: ../src/utils/pacat.c:364
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Akış aygıtı devam ettirildi.%s"
+
+#: ../src/utils/pacat.c:372
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Akış yetersiz.%s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Akış taşması.%s"
+
+#: ../src/utils/pacat.c:386
+#, c-format
+msgid "Stream started.%s"
+msgstr "Akış başladı. %s"
+
+#: ../src/utils/pacat.c:393
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Akış %s aygıtına taşındı (%u, %sertelenmiş).%s"
+
+#: ../src/utils/pacat.c:393
+msgid "not "
+msgstr "değil "
+
+#: ../src/utils/pacat.c:400
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Akış tampon bellek özellikleri değişti. %s"
+
+#: ../src/utils/pacat.c:415
+msgid "Cork request stack is empty: corking stream"
+msgstr "Durdurma isteği yığını boş: akış durduruluyor"
+
+#: ../src/utils/pacat.c:421
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Durdurma isteği yığını boş: akış durdurma sonlandırılıyor"
+
+#: ../src/utils/pacat.c:425
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr "Uyarı: Durdurma isteğinden daha fazla devam ettirme isteği alındı."
+
+#: ../src/utils/pacat.c:450
+#, c-format
+msgid "Connection established.%s"
+msgstr "Bağlantı kuruldu.%s"
+
+#: ../src/utils/pacat.c:453
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() başarısız oldu: %s"
+
+#: ../src/utils/pacat.c:491
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() başarısız oldu: %s"
+
+#: ../src/utils/pacat.c:497
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Akış izleme ayarlanamadı: %s"
+
+#: ../src/utils/pacat.c:501
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() başarısız oldu: %s"
+
+#: ../src/utils/pacat.c:514 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Bağlantı hatası: %s"
+
+#: ../src/utils/pacat.c:557
+msgid "Got EOF."
+msgstr "EOF Al."
+
+#: ../src/utils/pacat.c:581
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() başarısız oldu: %s"
+
+#: ../src/utils/pacat.c:605
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() başarısız oldu: %s"
+
+#: ../src/utils/pacat.c:626
+msgid "Got signal, exiting."
+msgstr "Sinyal alındı, çıkılıyor."
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Gecikme alınamadı: %s"
+
+#: ../src/utils/pacat.c:645
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Zaman: %0.3f saniye; Gecikme: %0.0f usec."
+
+#: ../src/utils/pacat.c:666
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() başarısız oldu: %s"
+
+#: ../src/utils/pacat.c:676
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [seçenekler]\n"
+"%s\n"
+"\n"
+"  -h, --help                          Yardımı gösterir\n"
+"      --version                       Sürümü gösterir\n"
+"\n"
+"  -r, --record                        Kayıt için bir bağlantı oluşturur\n"
+"  -p, --playback                      Çalmak için bir bağlantı oluşturur\n"
+"\n"
+"  -v, --verbose                       Ayrıntılı işlemleri etkinleştirir\n"
+"\n"
+"  -s, --server=SUNUCU                 Bağlanılacak sunucunun adı\n"
+"  -d, --device=AYGIT                  Bağlanılacak alıcı/kaynak adı\n"
+"  -n, --client-name=AD                Sunucu üzerinde bu istemciye ne ad "
+"verileceği\n"
+"      --stream-name=AD                Sunucu üzerinde bu akışa ne ad "
+"verileceği\n"
+"      --volume=SESDÜZEYİ              0...65536 aralığında başlangıç ses "
+"düzeyini (doğrusal) belirtir\n"
+"      --rate=ÖRNEKLEMEHIZI            Hz cinsinde örnekleme oranı (öntanımlı "
+"değer 44100)\n"
+"      --format=ÖRNEKLEMEBİÇİMİ        Örnekleme türü, s16le, s16be, u8, "
+"float32le,\n"
+"                                      float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                      s24-32le, s24-32be değerlerinden biri "
+"(öntanımlı: s16ne)\n"
+"      --channels=KANALLAR             Kanal sayısı, mono için 1, stereo için "
+"2\n"
+"                                      (öntanımlı değer: 2)\n"
+"      --channel-map=KANALADRESLEME    Öntanımlı yerine kullanılacak kanal "
+"eşleme\n"
+"      --fix-format                    Alıcı/kaynaktan bağlı olunan akışın "
+"örnekleme türünü alır.\n"
+"      --fix-rate                      Alıcı/kaynaktan bağlı olunan akışın "
+"örnekleme türünü alır.\n"
+"      --fix-channels                  Alıcı/kaynaktan bağlı olunan akışın "
+"kanal eşlemesini\n"
+"                                      ve kanal sayısını alır.\n"
+"      --no-remix                      Kanalları indirgemez ya da "
+"çoğaltamaz.\n"
+"      --no-remap                      Ad yerine dizin ile kanalları eşler.\n"
+"      --latency=BAYT                  Bayt cinsinden belirtilen gecikmeyi "
+"ister.\n"
+"      --process-time=BAYT             Bayt cinsinden her talep başına "
+"belirtilen işlem\n"
+"                                      zamanını ister.\n"
+"      --latency-msec=MİLİSANİYE       Milisaniye cinsinde belirtilen "
+"gecikmeyi ister.\n"
+"      --process-time-msec=MİLİSANİYE  Milisaniye cinsinde her talep başına "
+"belirtilen\n"
+"                                      işlem zamanını ister.\n"
+"      --property=ÖZELLİK=DEĞER        Belirtilen özelliği, belirtilen değere "
+"ayarlar.\n"
+"      --raw                           Ham PCM veri kaydeder/çalar.\n"
+"      --passthrough                   Doğrudan veri geçişi.\n"
+"      --file-format[=DBİÇİMİ]         Biçimlendirilmiş PCM verilerini "
+"kaydeder/çalar.\n"
+"      --list-file-formats             Kullanılabilir dosya biçimlerini "
+"listeler.\n"
+"      --monitor-stream=INDEKS         INDEKS indeksine sahip alıcı "
+"girişinden kayıt yapar.\n"
+
+#: ../src/utils/pacat.c:793
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr ""
+"Bir PulseAudio ses sunucusu üzerinde kodlanmış ses dosyalarını oynatın."
+
+#: ../src/utils/pacat.c:797
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr ""
+"Bir PulseAudio ses sunucusundan ses verilerini yakalayın ve bir dosyaya "
+"yazın."
+
+#: ../src/utils/pacat.c:801
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+"Bir PulseAudio ses sunucusundan ses verilerini yakalayın ve STDOUT'a ya da "
+"belirtilen dosyaya yazın."
+
+#: ../src/utils/pacat.c:805
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+"Bir PulseAudio ses sunucusu üzerinde STDIN'den ya da belirtilen dosyadan ses "
+"verilerini oynatın."
+
+#: ../src/utils/pacat.c:819
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Libpulse %s ile derlendi\n"
+"Libpulse %s ile bağlantılı\n"
+
+#: ../src/utils/pacat.c:852 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Geçersiz istemci adı '%s'"
+
+#: ../src/utils/pacat.c:867
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Geçersiz akış adı '%s'"
+
+#: ../src/utils/pacat.c:904
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Geçersiz kanal adresleme '%s'"
+
+#: ../src/utils/pacat.c:933 ../src/utils/pacat.c:947
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Geçersiz gecikme tanımı '%s'"
+
+#: ../src/utils/pacat.c:940 ../src/utils/pacat.c:954
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Geçersiz işlem zaman tanımı '%s'"
+
+#: ../src/utils/pacat.c:966
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Geçersiz özellik '%s'"
+
+#: ../src/utils/pacat.c:985
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Bilinmeyen dosya biçimi %s."
+
+#: ../src/utils/pacat.c:1000
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "--monitor-stream için değişken ayrıştırılamadı"
+
+#: ../src/utils/pacat.c:1011
+msgid "Invalid sample specification"
+msgstr "Geçersiz örnekleme tanımı"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1026
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1033
+msgid "Too many arguments."
+msgstr "Çok fazla değişken."
+
+#: ../src/utils/pacat.c:1044
+msgid "Failed to generate sample specification for file."
+msgstr "Dosya için örnekleme tanımı oluşturulamadı."
+
+#: ../src/utils/pacat.c:1070
+msgid "Failed to open audio file."
+msgstr "Ses dosyası açılamadı."
+
+#: ../src/utils/pacat.c:1076
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Uyarı: belirtilmiş örnek tanımlama, dosyadan alınacak başka bir tanımlama "
+"ile üzerine yazılacak."
+
+#: ../src/utils/pacat.c:1079 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Dosyadan örnekleme tanımı belirlenemedi."
+
+#: ../src/utils/pacat.c:1088
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Uyarı: Dosyadan kanal adresleme belirlenemedi."
+
+#: ../src/utils/pacat.c:1099
+msgid "Channel map doesn't match sample specification"
+msgstr "Kanal adresleme örnekleme tanımı ile eşleşmiyor"
+
+#: ../src/utils/pacat.c:1110
+msgid "Warning: failed to write channel map to file."
+msgstr "Uyarı: kanal adresleme dosyaya yazılamadı."
+
+#: ../src/utils/pacat.c:1125
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Örnekleme tanımı '%s' ve kanal adresleme '%s' ile bir %s akışı açılıyor."
+
+#: ../src/utils/pacat.c:1126
+msgid "recording"
+msgstr "kaydediliyor"
+
+#: ../src/utils/pacat.c:1126
+msgid "playback"
+msgstr "çal"
+
+#: ../src/utils/pacat.c:1150
+msgid "Failed to set media name."
+msgstr "Ortam adı ayarlanamadı."
+
+#: ../src/utils/pacat.c:1160 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() başarısız oldu."
+
+#: ../src/utils/pacat.c:1183
+msgid "io_new() failed."
+msgstr "io_new() başarısız oldu."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() başarısız oldu."
+
+#: ../src/utils/pacat.c:1198 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() başarısız oldu: %s"
+
+#: ../src/utils/pacat.c:1204
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() başarısız oldu."
+
+#: ../src/utils/pacat.c:1211 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() başarısız oldu."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "AD [DEĞİŞKENLER ...]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "AD|#A"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "AD"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "AD|#A SES"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N SES"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "AD|#A 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#A 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "AD|#A ANAHTAR=DEĞER"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#A ANAHTAR=DEĞER"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#A"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "AD ALICI|#A"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "AD DOSYAADI"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "YOLADI"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "DOSYAADI ALICI|#A"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#A ALICI|KAYNAK"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "KART PROFİLİ"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "AD|#A BAĞLANTI NOKTASI"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "KART-ADI|KART-#N PORT UZAKLIK"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "HEDEF"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "SAYISAL-SEVİYE"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "KARELER"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Yardım gösterir\n"
+"      --version                         Sürüm gösterir\n"
+"pacmd için komut verilmediğinde karşılıklı konuşma kipinde başlar.\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"libpulse %s ile derlendi\n"
+"libpulse %s ile bağlantılı\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Hiçbir PulseAudio artalan işlemi çalıştırılmıyor ya da bir artalan işlem "
+"oturumu olarak çalıştırılmıyor."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "PulseAudio artalan işlemi durdurulamadı."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Artalan işlem yanıt vermiyor."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "İstatistikler alınamadı: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Şu anda kullanımda: %u bloklar toplamda %s bayt içeriyor.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "Tüm ömrü boyunca ayrılmış: %u blokları toplamda %s bayt içeriyor.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Örnekleme ön bellek boyutu: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Sunucu bilgisi alınamadı: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Sunucu Karakter Dizisi: %s\n"
+"Kütüphane Protokol Sürümü: %u\n"
+"Sunucu Protokol Sürümü: %u\n"
+"Yerelde Mi: %s\n"
+"İstemci Dizini: %u\n"
+"Desen Boyutu: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Kullanıcı Adı: %s\n"
+"Makine Adı: %s\n"
+"Sunucu Adı: %s\n"
+"Sunucu Sürümü: %s\n"
+"Varsayılan Örnekleme Tanımı: %s\n"
+"Varsayılan Kanal Adresleme: %s\n"
+"Varsayılan Alıcı: %s\n"
+"Varsayılan Kaynak: %s\n"
+"Çerez: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Alıcı bilgisi alınamadı: %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Alıcı #%u\n"
+"\tDurum: %s\n"
+"\tAd: %s\n"
+"\tAçıklama: %s\n"
+"\tSürücü: %s\n"
+"\tÖrnekleme Tanımı: %s\n"
+"\tKanal Adresleme: %s\n"
+"\tModül Sahibi: %u\n"
+"\tSessiz: %s\n"
+"\tSes: %s\n"
+"\t        denge %0.2f\n"
+"\tTemel Ses: %s\n"
+"\tİzleme Kaynağı: %s\n"
+"\tGecikme: %0.0f usec, yapılandırılmış %0.0f usec\n"
+"\tBayraklar: %s%s%s%s%s%s%s\n"
+"\tÖzellikler:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tBağlantı Noktaları:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tEtkin Bağlantı Noktası: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tBiçimler:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Kaynak bilgisi alınamadı: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Kaynak #%u\n"
+"\tDurum: %s\n"
+"\tAd: %s\n"
+"\tAçıklama: %s\n"
+"\tSürücü: %s\n"
+"\tÖrnekleme Tanımı: %s\n"
+"\tKanal Adresleme: %s\n"
+"\tModül Sahibi: %u\n"
+"\tSessiz: %s\n"
+"\tSes: %s\n"
+"\t        denge %0.2f\n"
+"\tTemel Ses: %s\n"
+"\tAlıcı İzleme: %s\n"
+"\tGecikme: %0.0f usec, yapılandırılmış %0.0f usec\n"
+"\tBayraklar: %s%s%s%s%s%s\n"
+"\tÖzellikler:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "yok"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Modül bilgisi alınamadı: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Modül #%u\n"
+"\tAdı: %s\n"
+"\tDeğişken: %s\n"
+"\tKullanım Sayacı: %s\n"
+"\tÖzellikler:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "İstemci bilgisi alınamadı: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"İstemci #%u\n"
+"\tSürücü: %s\n"
+"\tModül Sahibi: %s\n"
+"\tÖzellikler:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Kart bilgisi alınamadı: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Kart #%u\n"
+"\tAdı: %s\n"
+"\tSürücü: %s\n"
+"\tModül Sahibi: %s\n"
+"\tÖzellikler:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tProfiller:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr ""
+"\t\t%s: %s (alıcılar: %u, kaynaklar: %u, öncelik: %u, kullanılabilir: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tEtkin Profil: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tÖzellikler:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\\Profil(ler)in parçası: %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Alıcı giriş bilgisi alınamadı: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Alıcı Girişi #%u\n"
+"\tSürücü: %s\n"
+"\tModül Sahibi: %s\n"
+"\tİstemci: %s\n"
+"\tAlıcı: %u\n"
+"\tÖrnekleme Tanımı: %s\n"
+"\tKanal Adresleme: %s\n"
+"\tBiçim: %s\n"
+"\tDurduruldu: %s\n"
+"\tSessiz: %s\n"
+"\tSes: %s\n"
+"\t        denge %0.2f\n"
+"\tTampon Bellek Gecikmesi: %0.0f usec\n"
+"\tAlıcı Gecikmesi: %0.0f usec\n"
+"\tÖrnekleme Yöntemi: %s\n"
+"\tÖzellikler:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Kaynak çıktı bilgileri alınamadı: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Kaynak Çıktısı #%u\n"
+"\tSürücü: %s\n"
+"\tModül Sahibi: %s\n"
+"\tİstemci: %s\n"
+"\tKaynak: %u\n"
+"\tÖrnekleme Tanımı: %s\n"
+"\tKanal Adresleme: %s\n"
+"\tBiçim: %s\n"
+"\tVeri Akışı Durduruldu: %s\n"
+"\tSessiz: %s\n"
+"\tSes: %s\n"
+"\t        denge %0.2f\n"
+"\tTampon Bellek Gecikmesi: %0.0f usec\n"
+"\tKaynak Gecikmesi: %0.0f usec\n"
+"\tYeniden Örnekleme yöntemi: %s\n"
+"\tÖzellikler:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Örnekleme bilgisi alınamadı: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Örnekleme #%u\n"
+"\tAd: %s\n"
+"\tÖrnekleme Tanımı: %s\n"
+"\tKanal Adresleme: %s\n"
+"\tSes: %s\n"
+"\t        denge %0.2f\n"
+"\tSüre: %0.1fs\n"
+"\tBoyut: %s\n"
+"\tYavaş: %s\n"
+"\tDosya adı: %s\n"
+"\tÖzellikler:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Hata: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Modül kaldırılamadı: Modül %s yüklenemedi"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Ses ayarlanamadı: %d kanal için ses ayarlamayı denediniz, halbuki "
+"desteklenen kanal sayısı %d\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Biçim ayarlanamadı: geçersiz biçim dizisi %s"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Örnekleme yüklenemedi: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Dosyanın erken bitişi"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "yeni"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "değiştir"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "kaldır"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "bilinmeyen"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "alıcı"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "kaynak"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "alıcı-girişi"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "alıcı-çıkışı"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "modül"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "istemci"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "örnek-önbellek"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "sunucu"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "kart"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "%2$s #%3$u üzerinde '%1$s' olayı\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "SIGINT alındı, çıkılıyor."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Geçersiz ses tanımı"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "İzin verilebilir aralık dışındaki ses.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "Geçersiz ses tanımı numarası.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "Tutarsız ses tanımı.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[seçenekler]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[TÜR]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "DOSYAADI [AD]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "AD [ALICI]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "AD|#A SES [SES ...]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#A SES [SES ...]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "İSİM|#N 1|0|geçiş"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|geçiş"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "#A BİÇİMLER"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Özel isimler @DEFAULT_SINK@, @DEFAULT_SOURCE@ ve @DEFAULT_MONITOR@\n"
+"varsayılan alıcıyı, kaynağı ve ekranı belirtmek için kullanılabilir.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Yardım gösterir\n"
+"      --version                         Sürüm gösterir\n"
+"\n"
+"  -s, --server=SUNUCU                   Bağlantı kurulacak sunucu adı\n"
+"  -n, --client-name=AD                Sunucu üzerinde bu istemci nasıl "
+"çağrılacak\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Libpulse %s ile derlendi\n"
+"Libpulse %s ile bağlantılı\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Şunlardan birini belirtin ya da hiçbir şey belirtmeyin: %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Lütfen yüklemek için bir örnekleme dosyası belirtin"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Ses dosyası açılamadı."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "Uyarı: Dosyadan örnekleme tanımı belirlenemedi."
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Çalmak için örnek ad belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Kaldırmak için örnek ad belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "Bir alıcı giriş göstergesi ve alıcı belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "Bir kaynak çıkış indeksi ve kaynak belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Bir modül adı ve değişken belirtmelisiniz."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "Bir modül dizini ya da adı belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Birden daha fazla alıcı belirtemezsiniz. Bir boolean değer belirtmelisiniz."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Geçersiz bekletme tanımlaması"
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Bir kaynaktan daha fazlasını belirtemezsiniz. Bir boolean değer "
+"belirtmelisiniz."
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Bir kart adı/dizin ve bir profil adı belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Bir alıcı adı/indeksi ve bağlantı noktası adı belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "Bir alıcı adı belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "Bir kaynak adı/dizini ve bir bağlantı noktası adı belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Bir kaynak adı belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Bir alıcı adı/indeksi ve ses belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Bir kaynak adı/dizini ve bir ses belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "Bir alıcı girdisi indeksi ve bir ses belirtmek zorundasınız"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "Geçersiz alıcı giriş indeksi"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "Bir kaynak çıktı dizini ve bir ses belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Geçersiz kaynak çıktı dizini"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Bir alıcı adı/indeksi ve bir sessizlik eylemi (0, 1 ya da 'toggle') "
+"belirtmek zorundasınız"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Geçersiz sessiz tanımı"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Bir kaynak adı/dizini ve bir sessizlik eylemi (0, 1 yada 'toggle') "
+"belirtmelisiniz"
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Bir alıcı girdi dizini ve bir sessizlik eylemi (0, 1 ya da 'toggle') "
+"belirtmek zorundasınız"
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "Geçersiz alıcı girişi indeks tanımı"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+"Bir kaynak çıktı dizini ve bir sessizlik eylemi (0, 1 ya da 'toggle') "
+"belirtmek zorundasınız"
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Geçersiz kaynak çıktısı indeksi tanımlaması"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Bir alıcı indeksi ve desteklenen biçimlerin, noktalı virgülle ayrılmış "
+"listesini belirtmek zorundasınız"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr ""
+"Bir kart adı/indeksi, bir bağlantı noktası adı ve bir gecikme uzaklığı "
+"belirtmek zorundasınız"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Gecikme uzaklığı ayrıştırılamadı"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Belirtilen geçerli komut yok."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Devam edilemedi: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Bekletilemedi: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "UYARI: Ses sunucusu yerel değil, askıya alınamıyor.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Bağlantı hatası: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "SIGINT sinyali alındı, çıkılıyor.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "UYARI: Alt işlem %u sinyali ile sonlandırıldı\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [seçenekler] ... \n"
+"\n"
+"  -h, --help                            Yardımı gösterir\n"
+"      --version                         Sürüm gösterir\n"
+"  -s, --server=SUNUCU                   Bağlanılacak sunucunun adı\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Libpulse %s ile derlendi\n"
+"Libpulse %s ile bağlantılı\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() başarısız oldu.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() başarısız oldu.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() başarısız oldu.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D ekran] [-S sunucu] [-O alıcı] [-I kaynak] [-c dosya]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    X11 ekranına bağlı mevcut PulseAudio verilerini gösterir "
+"(varsayılan)\n"
+" -e    X11 ekranına bağlı yerel PulseAudio verilerini dışa aktarır\n"
+" -i    X11 ekranından PulseAudio verilerini yerel çevresel değişkenlere ve "
+"çerez dosyalarına aktarır.\n"
+" -r    X11 ekranından PulseAudio verilerini kaldırır\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Komut satırı ayrıştırılamadı.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Sunucu: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Kaynak: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Alıcı: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Çerez: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Çerez veriler çözümlenemedi\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Çerez veriler kaydedilemedi\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "FQDN alınamadı.\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Çerez veriler yüklenemedi\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Henüz uygulanmadı.\n"
+
+#~ msgid ""
+#~ "%s [options]\n"
+#~ "\n"
+#~ "-h, --help                            Show this help\n"
+#~ "-v, --verbose                         Print debug messages\n"
+#~ "      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --from-format=SAMPLEFORMAT      From sample type (defaults to "
+#~ "s16le)\n"
+#~ "      --from-channels=CHANNELS        From number of channels (defaults "
+#~ "to 1)\n"
+#~ "      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+#~ "      --to-channels=CHANNELS          To number of channels (defaults to "
+#~ "1)\n"
+#~ "      --resample-method=METHOD        Resample method (defaults to auto)\n"
+#~ "      --seconds=SECONDS               From stream duration (defaults to "
+#~ "60)\n"
+#~ "\n"
+#~ "If the formats are not specified, the test performs all formats "
+#~ "combinations,\n"
+#~ "back and forth.\n"
+#~ "\n"
+#~ "Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+#~ "alaw,\n"
+#~ "s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+#~ "\n"
+#~ "See --dump-resample-methods for possible values of resample methods.\n"
+#~ msgstr ""
+#~ "%s [seçenekler]\n"
+#~ "\n"
+#~ "-h, --help                            Yardımı gösterir\n"
+#~ "-v, --verbose                         Hata ayıklama iletilerini yazdırır\n"
+#~ "      --from-rate=ÖRNEKLEMEHIZ          Hz cinsinde örnekleme hızından "
+#~ "(varsayılan - 44100)\n"
+#~ "      --from-format=ÖRNEKLEMEBİÇİMİ      Örnekleme türünden (varsayılan - "
+#~ "s16le)\n"
+#~ "      --from-channels=KANALLAR        Kanal sayılarından (varsayılan - "
+#~ "1)\n"
+#~ "      --to-rate=ÖRNEKLEMEHIZI            Hz cinsinde örnekleme hızına "
+#~ "(varsayılan - 44100)\n"
+#~ "      --to-format=ÖRNEKLEMEBİÇİMİ        Örnekleme türüne (varsayılan - "
+#~ "s16le)\n"
+#~ "      --to-channels=KANALLAR          Kanal sayılarına (varsayılan - 1)\n"
+#~ "      --resample-method=YÖNTEM        Yöntemi yeniden örnekleme "
+#~ "(varsayılan otomatik)\n"
+#~ "      --seconds=SANİYE               Akış süresinden (varsayılan - 60)\n"
+#~ "\n"
+#~ "Eğer biçimler belirtilmezse, test bütün biçimlerin birleşiminde ileri "
+#~ "geri\n"
+#~ "gerçekleştirilir.\n"
+#~ "\n"
+#~ "Örnek tür şunlardan biri olmalıdır; s16le, s16be, u8, float32le, "
+#~ "float32be, ulaw, alaw,\n"
+#~ "s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+#~ "\n"
+#~ "Yeniden örnekleme yöntemlerinin olası değerlerini --dump-resample-methods "
+#~ "gösterir.\n"
+
+#~ msgid "%s %s\n"
+#~ msgstr "%s %s\n"
diff --git a/po/uk.po b/po/uk.po
new file mode 100644 (file)
index 0000000..f2187fa
--- /dev/null
+++ b/po/uk.po
@@ -0,0 +1,3648 @@
+# Copyright (C) 2009 Free Software Foundation, Inc.
+# This file is distributed under the same license as the pulseaudio.master-tx package.
+#
+# Yuri Chornoivan <yurchor@ukr.net>, 2009, 2012, 2013, 2014, 2015, 2017.
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx\n"
+"Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
+"product=PulseAudio&keywords=I18N+L10N&component=misc\n"
+"POT-Creation-Date: 2017-05-22 15:43+0000\n"
+"PO-Revision-Date: 2017-06-25 22:26+0300\n"
+"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
+"Language-Team: Ukrainian <translation@linux.org.ua>\n"
+"Language: uk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Lokalize 1.5\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+#| msgid ""
+#| "%s [options]\n"
+#| "\n"
+#| "COMMANDS:\n"
+#| "  -h, --help                            Show this help\n"
+#| "      --version                         Show version\n"
+#| "      --dump-conf                       Dump default configuration\n"
+#| "      --dump-modules                    Dump list of available modules\n"
+#| "      --dump-resample-methods           Dump available resample methods\n"
+#| "      --cleanup-shm                     Cleanup stale shared memory "
+#| "segments\n"
+#| "      --start                           Start the daemon if it is not "
+#| "running\n"
+#| "  -k  --kill                            Kill a running daemon\n"
+#| "      --check                           Check for a running daemon (only "
+#| "returns exit code)\n"
+#| "\n"
+#| "OPTIONS:\n"
+#| "      --system[=BOOL]                   Run as system-wide instance\n"
+#| "  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+#| "      --fail[=BOOL]                     Quit when startup fails\n"
+#| "      --high-priority[=BOOL]            Try to set high nice level\n"
+#| "                                        (only available as root, when "
+#| "SUID or\n"
+#| "                                        with elevated RLIMIT_NICE)\n"
+#| "      --realtime[=BOOL]                 Try to enable realtime "
+#| "scheduling\n"
+#| "                                        (only available as root, when "
+#| "SUID or\n"
+#| "                                        with elevated RLIMIT_RTPRIO)\n"
+#| "      --disallow-module-loading[=BOOL]  Disallow module user requested "
+#| "module\n"
+#| "                                        loading/unloading after startup\n"
+#| "      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+#| "      --exit-idle-time=SECS             Terminate the daemon when idle "
+#| "and this\n"
+#| "                                        time passed\n"
+#| "      --scache-idle-time=SECS           Unload autoloaded samples when "
+#| "idle and\n"
+#| "                                        this time passed\n"
+#| "      --log-level[=LEVEL]               Increase or set verbosity level\n"
+#| "  -v  --verbose                         Increase the verbosity level\n"
+#| "      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+#| "                                        Specify the log target\n"
+#| "      --log-meta[=BOOL]                 Include code location in log "
+#| "messages\n"
+#| "      --log-time[=BOOL]                 Include timestamps in log "
+#| "messages\n"
+#| "      --log-backtrace=FRAMES            Include a backtrace in log "
+#| "messages\n"
+#| "  -p, --dl-search-path=PATH             Set the search path for dynamic "
+#| "shared\n"
+#| "                                        objects (plugins)\n"
+#| "      --resample-method=METHOD          Use the specified resampling "
+#| "method\n"
+#| "                                        (See --dump-resample-methods for\n"
+#| "                                        possible values)\n"
+#| "      --use-pid-file[=BOOL]             Create a PID file\n"
+#| "      --no-cpu-limit[=BOOL]             Do not install CPU load limiter "
+#| "on\n"
+#| "                                        platforms that support it.\n"
+#| "      --disable-shm[=BOOL]              Disable shared memory support.\n"
+#| "\n"
+#| "STARTUP SCRIPT:\n"
+#| "  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin "
+#| "module with\n"
+#| "                                        the specified argument\n"
+#| "  -F, --file=FILENAME                   Run the specified script\n"
+#| "  -C                                    Open a command line on the "
+#| "running TTY\n"
+#| "                                        after startup\n"
+#| "\n"
+#| "  -n                                    Don't load default script file\n"
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [параметри]\n"
+"\n"
+"КОМАНДИ:\n"
+"  -h, --help                            Показати цю довідку\n"
+"      --version                         Показати дані щодо версії\n"
+"      --dump-conf                       Створити знімок поточних "
+"налаштувань\n"
+"      --dump-modules                    Створити перелік можливих модулів\n"
+"      --dump-resample-methods           Створити перелік можливих методів "
+"зміни частотних характеристик\n"
+"      --cleanup-shm                     Спорожнити непотрібні сегменти "
+"пам’яті спільного використання\n"
+"      --start                           Запустити фонову службу, якщо її ще "
+"не запущено\n"
+"  -k  --kill                            Завершити роботу запущеної фонової "
+"служби\n"
+"      --check                           Перевірити, чи запущено фонову "
+"службу (буде повернуто лише код виходу)\n"
+"\n"
+"ПАРАМЕТРИ:\n"
+"      --system[=BOOL]                   Загальносистемний режим\n"
+"  -D, --daemonize[=BOOL]                Перетворити на фонову службу після "
+"запуску\n"
+"      --fail[=BOOL]                     Завершити роботу, якщо запуск був "
+"невдалим\n"
+"      --high-priority[=BOOL]            Спробувати встановити вищий рівень "
+"nice\n"
+"                                        (доступне лише для root, з SUID або\n"
+"                                        підвищеним RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Спробувати увімкнути планування у "
+"режимі реального часу\n"
+"                                        (доступне лише для root, з SUID або\n"
+"                                        підвищеним RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Заборонити для вказаного "
+"користувачем модуля\n"
+"                                        завантаження/вивантаження після "
+"запуску\n"
+"      --disallow-exit[=BOOL]            Заборонити завершення роботи "
+"користувачем\n"
+"      --exit-idle-time=СЕК.             Перервати виконання фонової служби, "
+"якщо не спостерігатиметься\n"
+"                                        активності протягом вказаного часу\n"
+"      --scache-idle-time=СЕК.           Вивантажити автоматично завантажені "
+"фрагменти, якщо не спостерігатиметься\n"
+"                                        активності протягом вказаного часу\n"
+"      --log-level[=РІВЕНЬ]              Підвищити або встановити рівень "
+"докладності виводу\n"
+"  -v  --verbose                         Підвищити рівень докладності виводу\n"
+"      --log-target={auto,syslog,stderr,file:ШЛЯХ,newfile:ШЛЯХ}\n"
+"                                        Вказати журнал\n"
+"      --log-meta[=BOOL]                 Додати повідомлення про місце у коді "
+"до повідомлень журналу\n"
+"      --log-time[=BOOL]                 Додати час до повідомлень журналу\n"
+"      --log-backtrace=FRAMES            Додати зворотне трасування до "
+"повідомлень журналу\n"
+"  -p, --dl-search-path=ШЛЯХ             Встановити шлях пошуку для об’єктів "
+"динамічного\n"
+"                                        спільного використання (додатків)\n"
+"      --resample-method=МЕТОД           Використовувати вказаний метод зміни "
+"частотних характеристик\n"
+"                                        (Про можливі варіанти можна "
+"дізнатися за допомогою\n"
+"                                        параметра --dump-resample-methods)\n"
+"      --use-pid-file[=BOOL]             Створювати файл PID\n"
+"      --no-cpu-limit[=BOOL]             Не встановлювати обмеження на "
+"використання процесора\n"
+"                                        на платформах, які його "
+"підтримують.\n"
+"      --disable-shm[=BOOL]              Вимкнути підтримку спільного "
+"використання пам’яті.\n"
+"      --enable-memfd[=BOOL]             Увімкнути підтримку спільного "
+"використання пам’яті memfd.\n"
+"\n"
+"СКРИПТ ЗАПУСКУ:\n"
+"  -L, --load=\"ПАРАМЕТРИ МОДУЛЯ\"         Завантажити вказаний модуль "
+"додатка з\n"
+"                                        вказаними параметрами\n"
+"  -F, --file=НАЗВА_ФАЙЛА                Виконати вказаний скрипт\n"
+"  -C                                    Відкрити командний рядок на "
+"запущеному TTY\n"
+"                                        після запуску\n"
+"\n"
+"  -n                                    Не завантажувати типовий файл "
+"скрипту\n"
+
+#: ../src/daemon/cmdline.c:246
+msgid "--daemonize expects boolean argument"
+msgstr "Для параметра --daemonize слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:254
+msgid "--fail expects boolean argument"
+msgstr "Для параметра --fail слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:265
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"Для параметра --log-level слід вказувати значення рівня журналювання "
+"(числове у діапазоні 0..4 або одне зі значень debug, info, notice, warn, "
+"error)."
+
+#: ../src/daemon/cmdline.c:277
+msgid "--high-priority expects boolean argument"
+msgstr "Для параметра --high-priority слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:285
+msgid "--realtime expects boolean argument"
+msgstr "Для параметра --realtime слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:293
+msgid "--disallow-module-loading expects boolean argument"
+msgstr ""
+"Для параметра --disallow-module-loading слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--disallow-exit expects boolean argument"
+msgstr "Для параметра --disallow-exit слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:309
+msgid "--use-pid-file expects boolean argument"
+msgstr "Для параметра --use-pid-file слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:328
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Журнал вказано неправильно: можливі варіанти «syslog», «journal», «stderr», "
+"«auto» і чинна назва файла «file:<шлях>» або «newfile:<шлях>»."
+
+#: ../src/daemon/cmdline.c:330
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"Журнал вказано неправильно: можливі варіанти «syslog», «stderr», «auto» і "
+"чинна назва файла «file:<шлях>» або «newfile:<шлях>»."
+
+#: ../src/daemon/cmdline.c:338
+msgid "--log-time expects boolean argument"
+msgstr "Для параметра --log-time слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:346
+msgid "--log-meta expects boolean argument"
+msgstr "Для параметра --log-meta слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:366
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "Некоректний метод зміни частотних характеристик «%s»."
+
+#: ../src/daemon/cmdline.c:373
+msgid "--system expects boolean argument"
+msgstr "Для параметра --system слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:381
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "Для параметра --no-cpu-limit слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:389
+msgid "--disable-shm expects boolean argument"
+msgstr "Для параметра --disable-shm слід вказувати булевий аргумент"
+
+#: ../src/daemon/cmdline.c:397
+#| msgid "--realtime expects boolean argument"
+msgid "--enable-memfd expects boolean argument"
+msgstr "Для параметра --enable-memfd слід вказувати булевий аргумент"
+
+#: ../src/daemon/daemon-conf.c:262
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] Некоректний журнал «%s»."
+
+#: ../src/daemon/daemon-conf.c:277
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] Некоректний рівень журналювання «%s»."
+
+#: ../src/daemon/daemon-conf.c:292
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] Некоректний метод зміни частотних характеристик «%s»."
+
+#: ../src/daemon/daemon-conf.c:314
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] Некоректне значення rlimit «%s»."
+
+#: ../src/daemon/daemon-conf.c:334
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] Некоректний формат фрагмента «%s»."
+
+#: ../src/daemon/daemon-conf.c:351 ../src/daemon/daemon-conf.c:368
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] Некоректна частота вибірки «%s»."
+
+#: ../src/daemon/daemon-conf.c:391
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] Некоректні канали фрагмента «%s»."
+
+#: ../src/daemon/daemon-conf.c:408
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] Некоректна карта каналів «%s»'."
+
+#: ../src/daemon/daemon-conf.c:425
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] Некоректна кількість фрагментів «%s»."
+
+#: ../src/daemon/daemon-conf.c:442
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] Некоректний розмір фрагмента «%s»."
+
+#: ../src/daemon/daemon-conf.c:459
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] Некоректний рівень nice «%s»."
+
+#: ../src/daemon/daemon-conf.c:502
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] Некоректний тип сервера «%s»."
+
+#: ../src/daemon/daemon-conf.c:620
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "Не вдалося відкрити файл налаштувань: %s"
+
+#: ../src/daemon/daemon-conf.c:636
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr ""
+"У вказаній типовій карті каналів визначається інша кількість каналів, ніж "
+"типова кількість каналів."
+
+#: ../src/daemon/daemon-conf.c:723
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### Прочитано з файла налаштувань: %s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "Назва: %s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "Дані щодо модуля недоступні\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "Версія: %s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "Опис: %s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "Автор: %s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "Використання: %s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "Завантаження при: %s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "ПОПЕРЕДЖЕННЯ ПРО ЗАСТАРІЛІСТЬ: %s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "Шлях: %s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "Не вдалося відкрити модуль %s: %s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr ""
+"Спроба знайти початковий інструмент завантаження lt_dlopen зазнала невдачі."
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr ""
+"Спроба виділення пам’яті для нового інструменту завантаження dl зазнала "
+"невдачі."
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "Не вдалося додати bind-now-loader."
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "Не вдалося знайти користувача «%s»."
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "Не вдалося знайти групу «%s»."
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "GID користувача «%s» і групи «%s» не збігаються."
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "Домашнім каталогом користувача «%s» не є «%s», дані проігноровано."
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "Не вдалося створити «%s»: %s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "Не вдалося змінити список груп: %s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "Не вдалося змінити GID: %s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "Не вдалося змінити UID: %s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "Загальносистемний режим не підтримується на цій платформі."
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "Не вдалося обробити рядок команди."
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr ""
+"Запуск у системному режимі для неадміністративного користувача неможливий. "
+"Буде запущено лише службу виявлення пристроїв сервера D-Bus."
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "Не вдалося завершити роботу фонової служби: %s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr ""
+"Цю програму не призначено для запуску від імені користувача root (якщо не "
+"вказано параметра --system)."
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "Потрібні права доступу користувача root."
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr ""
+"Параметр --start не підтримується для загальносистемних екземплярів програми."
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr ""
+"Налаштований користувачем сервер на %s, не вдалося запустити/автоматично "
+"відновити роботу."
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr ""
+"Налаштований користувачем сервер на %s, який, здається, є локальним. "
+"Виконуємо докладнішу діагностику."
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr ""
+"Запуск у загальносистемному режимі, але не встановлено --disallow-exit."
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr ""
+"Запуск у загальносистемному режимі, але не встановлено --disallow-module-"
+"loading."
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr "Запуск у загальносистемному режимі, примусове вимикання режиму SHM."
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr ""
+"Запуск у загальносистемному режимі, примусове вимикання режиму параметрів "
+"часу виходу, якщо немає активності."
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "Не вдалося отримати stdio."
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "Спроба виконання pipe() завершилася невдало: %s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "Спроба виконання fork() завершилася невдало: %s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:562
+#, c-format
+msgid "read() failed: %s"
+msgstr "Спроба виконання read() завершилася невдало: %s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "Спроба запуску фонової служби завершилася невдало."
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "Спроба виконання setsid() завершилася невдало: %s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "Спроба отримати ідентифікатор системи завершилася невдало"
+
+#: ../src/daemon/main.c:974
+#| msgid ""
+#| "OK, so you are running PA in system mode. Please note that you most "
+#| "likely shouldn't be doing that.\n"
+#| "If you do it nonetheless then it's your own fault if things don't work as "
+#| "expected.\n"
+#| "Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+#| "Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why "
+#| "system mode is usually a bad idea."
+msgid ""
+"OK, so you are running PA in system mode. Please make sure that you actually "
+"do want to do that.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"Гаразд, ви запустили PA у системному режимі. Будь ласка, переконайтеся, що це "
+"саме те, що вам потрібно.\n"
+"Будь ласка, ознайомтеся зі статтею http://www.freedesktop.org/wiki/Software/"
+"PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ , щоб дізнатися про "
+"те, чому не варто використовувати системний режим."
+
+#: ../src/daemon/main.c:990
+msgid "pa_pid_file_create() failed."
+msgstr "Спроба виконання pa_pid_file_create() зазнала невдачі."
+
+#: ../src/daemon/main.c:1022
+msgid "pa_core_new() failed."
+msgstr "Спроба виконання pa_core_new() зазнала невдачі."
+
+#: ../src/daemon/main.c:1092
+msgid "Failed to initialize daemon."
+msgstr "Не вдалося ініціалізувати фонову службу."
+
+#: ../src/daemon/main.c:1097
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr ""
+"Запуск фонової служби без жодного завантаженого модуля, служба не буде "
+"працездатною."
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "Звукова система PulseAudio"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "Запустити звукову систему PulseAudio"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "Вхід"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "Вхідний канал док-станції"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "Мікрофон док-станції"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "Лінійний вхід док-станції"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "Лінійний вхід"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1750
+msgid "Microphone"
+msgstr "Мікрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "Передній мікрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "Задній мікрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "Зовнішній мікрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "Вбудований мікрофон"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "Радіо"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "Відео"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "Автоматичне керування підсиленням"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "Без автоматичного керування підсиленням"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "Підсилення"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "Без пісилення"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "Підсилювач"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "Без підсилювача"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "Підсилення басів"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "Без підсилення"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1757
+msgid "Speaker"
+msgstr "Гучномовець"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "Аналогові навушники"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "Аналогових вхід"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "Мікрофон стикувальної станції"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "Мікрофон гарнітури"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "Аналогове відтворення"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "Аналоговий вихід (сабвуфер)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "Лінійний вихід"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "Аналоговий моно-вихід"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "Гучномовці"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "Цифровий вихід (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "Цифровий вхід (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "Цифрове передавання (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "Багатоканальний вхід"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "Багатоканальний вихід"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "Аналогове моно"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "Аналогове стерео"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "Багатоканальний"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "Аналоговий об'ємний 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "Аналоговий об'ємний 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "Аналоговий об'ємний 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "Аналоговий об'ємний 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "Аналоговий об'ємний 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "Аналоговий об'ємний 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "Аналоговий об'ємний 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "Аналоговий об'ємний 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "Аналоговий об'ємний 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "Аналоговий об'ємний 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "Аналоговий об'ємний 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "Цифрове стерео (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "Цифрове передавання (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "Цифровий об’ємний 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "Цифровий об’ємний 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "Цифровий об’ємний 5.1 (IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "Цифровий стерео (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "Цифровий об’ємний 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4019 ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "Стерео"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Mono Duplex"
+msgstr "Аналогове двобічне моно"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Analog Stereo Duplex"
+msgstr "Аналогове двобічне стерео"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "Цифрове двобічне стерео (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+msgid "Multichannel Duplex"
+msgstr "Багатоканальний двобічний"
+
+#: ../src/modules/alsa/alsa-mixer.c:4156
+#| msgid "Analog Stereo Duplex"
+msgid "Stereo Duplex"
+msgstr "Двобічне стерео"
+
+#: ../src/modules/alsa/alsa-mixer.c:4157
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2324
+#: ../src/modules/bluetooth/module-bluez5-device.c:2005
+msgid "Off"
+msgstr "Вимкнено"
+
+#: ../src/modules/alsa/alsa-mixer.c:4256
+#, c-format
+msgid "%s Output"
+msgstr "вихід %s"
+
+#: ../src/modules/alsa/alsa-mixer.c:4264
+#, c-format
+msgid "%s Input"
+msgstr "вхід %s"
+
+#: ../src/modules/alsa/alsa-sink.c:572
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA викликала службу запису нових даних на пристрій, але насправді ніяких "
+"даних для запису не виявлено!\n"
+"Ймовірно, це пов’язано з вадою у драйвері ALSA «%s». Будь ласка, повідомте "
+"про цю ваду розробникам ALSA.\n"
+"Службу було викликано зі встановленим POLLOUT, але наступний виклик "
+"snd_pcm_avail() повернув 0 або інше значення < min_avail."
+
+#: ../src/modules/alsa/alsa-sink.c:756
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA викликала службу запису нових даних на пристрій, але насправді ніяких "
+"даних для запису не виявлено!\n"
+"Ймовірно, це пов’язано з вадою у драйвері ALSA «%s». Будь ласка, повідомте "
+"про цю ваду розробникам ALSA.\n"
+"Службу було викликано зі встановленим POLLOUT, але наступний виклик "
+"snd_pcm_avail() повернув 0 або інше значення < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA викликала службу читання нових даних з пристрою, але насправді ніяких "
+"даних для читання не виявлено!\n"
+"Ймовірно, це пов’язано з вадою у драйвері ALSA «%s». Будь ласка, повідомте "
+"про цю ваду розробникам ALSA.\n"
+"Службу було викликано зі встановленим POLLIN, але наступний виклик "
+"snd_pcm_avail() повернув 0 або інше значення < min_avail."
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA викликала службу читання нових даних з пристрою, але насправді ніяких "
+"даних для читання не виявлено!\n"
+"Ймовірно, це пов’язано з вадою у драйвері ALSA «%s». Будь ласка, повідомте "
+"про цю ваду розробникам ALSA.\n"
+"Службу було викликано зі встановленим POLLIN, але наступний виклик "
+"snd_pcm_avail() повернув 0 або інше значення < min_avail."
+
+#: ../src/modules/alsa/alsa-util.c:1168 ../src/modules/alsa/alsa-util.c:1243
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"Функція snd_pcm_avail() повернула винятково велике значення: %lu байтів (%lu "
+"мс).\n"
+"Ймовірно, ви натрапили на ваду у драйвері ALSA «%s». Будь ласка, повідомте "
+"про цю ваду розробникам ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1218
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"Функція snd_pcm_delay() повернула винятково велике значення: %li байтів (%s"
+"%lu мс).\n"
+"Ймовірно, ви натрапили на ваду у драйвері ALSA «%s». Будь ласка, повідомте "
+"про цю ваду розробникам ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1259
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() повернуто дивні значення: затримка %lu є меншою за "
+"доступну, %lu.\n"
+"Ймовірно, це пов’язано з вадою у драйвері ALSA «%s». Будь ласка, повідомте "
+"про цю ваду розробникам ALSA."
+
+#: ../src/modules/alsa/alsa-util.c:1302
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"Функція snd_pcm_mmap_begin() повернула винятково велике значення: %lu байтів "
+"(%lu мс).\n"
+"Ймовірно, ви натрапили на ваду у драйвері ALSA «%s». Будь ласка, повідомте "
+"про цю ваду розробникам ALSA."
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1740
+msgid "Headset"
+msgstr "Гарнітура"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1745
+msgid "Handsfree"
+msgstr "Пристрій гучного зв’язку"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1763
+msgid "Headphone"
+msgstr "Навушники"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1768
+msgid "Portable"
+msgstr "Портативна система"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1773
+msgid "Car"
+msgstr "Автомобільна система"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1778
+msgid "HiFi"
+msgstr "HiFi"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2134
+#: ../src/modules/bluetooth/module-bluez5-device.c:1783
+msgid "Phone"
+msgstr "Телефон"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2142
+#: ../src/modules/bluetooth/module-bluez5-device.c:1735
+#: ../src/modules/bluetooth/module-bluez5-device.c:1751
+#: ../src/modules/bluetooth/module-bluez5-device.c:1789
+msgid "Bluetooth Output"
+msgstr "Bluetooth (відтворення)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2145
+#: ../src/modules/bluetooth/module-bluez5-device.c:1734
+#: ../src/modules/bluetooth/module-bluez5-device.c:1756
+#: ../src/modules/bluetooth/module-bluez5-device.c:1762
+#: ../src/modules/bluetooth/module-bluez5-device.c:1788
+msgid "Bluetooth Input"
+msgstr "Bluetooth (вхід)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2186
+msgid "High Fidelity Playback (A2DP)"
+msgstr "Високоточне відтворення (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2198
+msgid "High Fidelity Capture (A2DP)"
+msgstr "Високоточне захоплення (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2210
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "Телефонний дуплекс (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2223
+msgid "Handsfree Gateway"
+msgstr "Пристрій гучного зв’язку"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1830
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "Високоточне відтворення (приймач A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1842
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "Високоточне захоплення (джерело A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1854
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "Головний модуль гарнітури (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1867
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "Звуковий шлюз гарнітури (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+#| msgid ""
+#| "source_name=<name for the source> source_properties=<properties for the "
+#| "source> source_master=<name of source to filter> sink_name=<name for the "
+#| "sink> sink_properties=<properties for the sink> sink_master=<name of sink "
+#| "to filter> adjust_time=<how often to readjust rates in s> "
+#| "adjust_threshold=<how much drift to readjust after in ms> format=<sample "
+#| "format> rate=<sample rate> channels=<number of channels> "
+#| "channel_map=<channel map> aec_method=<implementation to use> "
+#| "aec_args=<parameters for the AEC engine> save_aec=<save AEC data in /tmp> "
+#| "autoloaded=<set if this module is being loaded automatically> "
+#| "use_volume_sharing=<yes or no> "
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<назва джерела> source_properties=<властивості джерела> "
+"source_master=<назва джерелад для фільтрування> sink_name=<назва приймача> "
+"sink_properties=<властивості приймача> sink_master=<назва приймача для "
+"фільтрування> adjust_time=<частота коригування швидкостей у секундах> "
+"adjust_threshold=<тривалість зсуву у мс, яку слід узгодити> format=<формат "
+"фрагмента> rate=<частота дискретизації> channels=<кількість каналів> "
+"channel_map=<карта каналів> aec_method=<реалізація обробки> "
+"aec_args=<параметри рушія придушення луни> save_aec=<чи слід зберігати дані "
+"придушення луни до /tmp> autoloaded=<визначити, чи слід завантажувати модуль "
+"у автоматичному режимі> use_volume_sharing=<yes або no> use_master_format=<"
+"yes "
+"або no> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "Увімкнено"
+
+#: ../src/modules/module-allow-passthrough.c:73
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Тестове відтворення"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr ""
+"Завжди підтримувати принаймні один завантажений приймач, навіть якщо він "
+"буде нульовим"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "Еквалайзер загального призначення"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<назва приймача> sink_properties=<властивості приймача> "
+"sink_master=<назва приймача, з яким слід встановти з’єднання> format=<формат "
+"фрагмента> rate=<частота вибірки> channels=<кількість каналів> "
+"channel_map=<карта каналів> autoloaded=<визначити, чи слід завантажувати цей "
+"модуль у автоматичному режимі> use_volume_sharing=<yes або no> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<чи слід автоматично вивантажувати невикористані фільтри?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "Віртуальний приймач LADSPA"
+
+#: ../src/modules/module-ladspa-sink.c:55
+#| msgid ""
+#| "sink_name=<name for the sink> sink_properties=<properties for the sink> "
+#| "master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+#| "channels=<number of channels> channel_map=<input channel map> "
+#| "plugin=<ladspa plugin name> label=<ladspa plugin label> control=<comma "
+#| "separated list of input control values> input_ladspaport_map=<comma "
+#| "separated list of input LADSPA port names> output_ladspaport_map=<comma "
+#| "separated list of output LADSPA port names> "
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<input channel map> plugin=<ladspa plugin name> label=<ladspa "
+"plugin label> control=<comma separated list of input control values> "
+"input_ladspaport_map=<comma separated list of input LADSPA port names> "
+"output_ladspaport_map=<comma separated list of output LADSPA port names> "
+"autoloaded=<set if this module is being loaded automatically> "
+msgstr ""
+"sink_name=<назва приймача> sink_properties=<властивості приймача> "
+"master=<назва приймача для фільтрування> sink_master=<назва приймача для "
+"фільтрування> format=<формат фрагмента> "
+"rate=<частота вибірки> channels=<кількість каналів> channel_map=<карта "
+"каналів вхідних даних> plugin=<назва додатка ladspa> label=<мітка додатка "
+"ladspa> control=<розділений комами список значень вхідних параметрів> "
+"input_ladspaport_map=<список назв портів вхідних даних LADSPA, відокремлених "
+"комами> output_ladspaport_map=<список назв портів вихідних даних LADSPA, "
+"відокремлених комами> autoloaded=<встановіть, якщо цей модуль завантажується "
+"автоматично> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "NULL-приймач з годинником"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Нуль-відтворення"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "Пристрої відтворення"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "Пристрої отримання"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "Звук на @НАЗВАВУЗЛА@"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "Тунель для %s@%s"
+
+#: ../src/modules/module-tunnel-sink-new.c:521
+#: ../src/modules/module-tunnel-source-new.c:520
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "Тунель до %s/%s"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "Віртуальний навколишній приймач"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+#| msgid ""
+#| "sink_name=<name for the sink> sink_properties=<properties for the sink> "
+#| "master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+#| "channels=<number of channels> channel_map=<channel map> "
+#| "use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/"
+#| "to/left_hrir.wav "
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> sink_master=<name of sink to filter> "
+"format=<sample format> rate=<sample rate> channels=<number of channels> "
+"channel_map=<channel map> use_volume_sharing=<yes or no> "
+"force_flat_volume=<yes or no> hrir=/path/to/left_hrir.wav autoloaded=<set if "
+"this module is being loaded automatically> "
+msgstr ""
+"sink_name=<назва приймача> sink_properties=<властивості приймача> "
+"master=<назва приймача для фільтрування> sink_master=<назва приймача для "
+"фільтрування> format=<формат фрагмента> "
+"rate=<частота вибірки> channels=<кількість каналів> channel_map=<карта "
+"каналів> use_volume_sharing=<yes або no> force_flat_volume=<yes або no> "
+"hrir=/шлях/до/лівого_hrir.wav autoloaded=<встановіть, якщо цей модуль "
+"завантажується автоматично> "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "Звуковий сервер PulseAudio"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "Моно"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "Передній центральний"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "Передній лівий"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "Передній правий"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "Задній центральний"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "Задній лівий"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "Задній правий"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "Низькочастотний динамік"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "Передній лівоцентральний"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "Передній правоцентральний"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "Боковий лівий"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "Боковий правий"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "Допоміжний 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "Допоміжний 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "Допоміжний 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "Допоміжний 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "Допоміжний 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "Допоміжний 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "Допоміжний 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "Допоміжний 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "Допоміжний 8"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "Допоміжний 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "Допоміжний 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "Допоміжний 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "Допоміжний 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "Допоміжний 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "Допоміжний 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "Допоміжний 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "Допоміжний 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "Допоміжний 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "Допоміжний 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "Допоміжний 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "Допоміжний 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "Допоміжний 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "Допоміжний 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "Допоміжний 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "Допоміжний 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "Допоміжний 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "Допоміжний 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "Допоміжний 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "Допоміжний 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "Допоміжний 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "Допоміжний 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "Допоміжний 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "Верхній центральний"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "Верхній передній центральний"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "Верхній передній лівий"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "Верхній передній правий"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "Верхній задній центральний"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "Верхній задній лівий"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "Верхній задній правий"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:121
+#: ../src/pulse/sample.c:177 ../src/pulse/volume.c:306
+#: ../src/pulse/volume.c:332 ../src/pulse/volume.c:352
+#: ../src/pulse/volume.c:384 ../src/pulse/volume.c:424
+#: ../src/pulse/volume.c:443
+msgid "(invalid)"
+msgstr "(некоректний)"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "Об'ємний 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "Об'ємний 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "Об'ємний 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "Об'ємний 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "Об'ємний 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "помилка xcb_connect()"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() повернуто true"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "Не вдалося обробити дані куки"
+
+#: ../src/pulse/context.c:702
+#, c-format
+msgid "fork(): %s"
+msgstr "fork(): %s"
+
+#: ../src/pulse/context.c:757
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid(): %s"
+
+#: ../src/pulse/context.c:1463
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "Отримано повідомлення про невідомий додаток «%s»"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "вхід"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "вихід"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "двобічний"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "некоректний"
+
+#: ../src/pulsecore/core-util.c:1856
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"XDG_RUNTIME_DIR (%s) не належить нашому користувачу (uid %d), а належить "
+"користувачу uid %d! (Таке, наприклад, може трапитися, якщо ви намагаєтеся "
+"встановити з’єднання із PulseAudio, запущеного не від імені користувача "
+"root, від імені користувача root за допомогою вбудованого протоколу. Не "
+"робіть так.)"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "так"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "ні"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "Не вдалося зняти блокування автоматичного розгалуження."
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "Не вдалося відкрити файл призначення, «%s»."
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr ""
+"Було виконано спроби відкрити файли призначення «%s», «%s.1», «%s.2» ... «%s."
+"%d», але усі вони завершилися невдачею."
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "Некоректна адреса файла журналу."
+
+#: ../src/pulsecore/sink.c:3469
+msgid "Built-in Audio"
+msgstr "Вбудоване аудіо"
+
+#: ../src/pulsecore/sink.c:3474
+msgid "Modem"
+msgstr "Модем"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "Гаразд"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "Доступ заборонено"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "Невідома команда"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "Некоректний аргумент"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "Об’єкт існує"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "Такого об’єкта не існує"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "У з'єднанні відмовлено"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "Помилка протоколу"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "Перевищення часу очікування"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "Немає ключа розпізнавання"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "Внутрішня помилка"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "З’єднання перервано"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "Об’єкт вилучено"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "Некоректний сервер"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "Спроба ініціалізації модуля завершилася невдало"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "Стан помилки"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "Немає даних"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "Несумісна версія протоколу"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "Завеликий"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "Не підтримується"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "Помилка з невідомим кодом"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "Такого додатка немає"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "Застарілі функціональні можливості"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "Відсутня реалізація"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "Клієнт розгалужено"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "Помилка вводу/виводу"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "Пристрій або ресурс зайнято"
+
+#: ../src/pulse/sample.c:179
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uкан. %uГц"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f ГБ"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f МіБ"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f КіБ"
+
+#: ../src/pulse/sample.c:197
+#, c-format
+msgid "%u B"
+msgstr "%u Б"
+
+#: ../src/utils/pacat.c:134
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "Не вдалося створити стік для потоку: %s"
+
+#: ../src/utils/pacat.c:139
+msgid "Playback stream drained."
+msgstr "Потік відтворення спрямовано до стоку."
+
+#: ../src/utils/pacat.c:150
+msgid "Draining connection to server."
+msgstr "Спрямовуємо з’єднання з сервером до стоку."
+
+#: ../src/utils/pacat.c:163
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:194 ../src/utils/pacat.c:543
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "Спроба виконання pa_stream_write() завершилася невдало: %s"
+
+#: ../src/utils/pacat.c:244 ../src/utils/pacat.c:274
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "Спроба виконання pa_stream_peek() завершилася невдало: %s"
+
+#: ../src/utils/pacat.c:324
+msgid "Stream successfully created."
+msgstr "Потік було успішно створено."
+
+#: ../src/utils/pacat.c:327
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "Спроба виконання pa_stream_get_buffer_attr() завершилася невдало: %s"
+
+#: ../src/utils/pacat.c:331
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "Метрика буфера: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:334
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "Метрика буфера: maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:338
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "Використання частотної специфікації «%s», карта каналів «%s»."
+
+#: ../src/utils/pacat.c:342
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "З’єднано з пристроєм %s (індекс: %u, призупинено: %s)."
+
+#: ../src/utils/pacat.c:352
+#, c-format
+msgid "Stream error: %s"
+msgstr "Помилка потоку: %s"
+
+#: ../src/utils/pacat.c:362
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "Призупинено пристрій потоку. %s"
+
+#: ../src/utils/pacat.c:364
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "Відновлено пристрій потоку. %s"
+
+#: ../src/utils/pacat.c:372
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "Недовантаження потоку. %s"
+
+#: ../src/utils/pacat.c:379
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "Перевантаження потоку. %s"
+
+#: ../src/utils/pacat.c:386
+#, c-format
+msgid "Stream started.%s"
+msgstr "Потік запущено. %s"
+
+#: ../src/utils/pacat.c:393
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "Потік пересунуто на пристрій %s (%u, %s призупинено). %s"
+
+#: ../src/utils/pacat.c:393
+msgid "not "
+msgstr "не "
+
+#: ../src/utils/pacat.c:400
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "Недовантаження потоку. %s"
+
+#: ../src/utils/pacat.c:415
+msgid "Cork request stack is empty: corking stream"
+msgstr "Стос запитів щодо блокування порожній: блокуємо потік"
+
+#: ../src/utils/pacat.c:421
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Стос запитів щодо блокування порожній: розблоковуємо потік"
+
+#: ../src/utils/pacat.c:425
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr ""
+"Попередження: отримано більше запитів щодо розблокування, ніж запитів щодо "
+"блокування."
+
+#: ../src/utils/pacat.c:450
+#, c-format
+msgid "Connection established.%s"
+msgstr "Встановлено з’єднання. %s"
+
+#: ../src/utils/pacat.c:453
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "Спроба виконання pa_stream_new() зазнала невдачі: %s"
+
+#: ../src/utils/pacat.c:491
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "Спроба виконання pa_stream_connect_playback() зазнала невдачі: %s"
+
+#: ../src/utils/pacat.c:497
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "Не вдалося встановити потік спостереження: %s"
+
+#: ../src/utils/pacat.c:501
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "Спроба виконання pa_stream_connect_record() зазнала невдачі: %s"
+
+#: ../src/utils/pacat.c:514 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "Спроба встановлення з’єднання зазнала невдачі: %s"
+
+#: ../src/utils/pacat.c:557
+msgid "Got EOF."
+msgstr "Отримано EOF."
+
+#: ../src/utils/pacat.c:581
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "Спроба виконання pa_stream_write() завершилася невдало: %s"
+
+#: ../src/utils/pacat.c:605
+#, c-format
+msgid "write() failed: %s"
+msgstr "Спроба виконання write() завершилася невдало: %s"
+
+#: ../src/utils/pacat.c:626
+msgid "Got signal, exiting."
+msgstr "Отримано сигнал, завершення роботи."
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "Не вдалося отримати латентність: %s"
+
+#: ../src/utils/pacat.c:645
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "Час: %0.3f сек.; Латентність: %0.0f мкс."
+
+#: ../src/utils/pacat.c:666
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "Помилка pa_stream_update_timing_info(): %s"
+
+#: ../src/utils/pacat.c:676
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [параметри]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Показати цю довідку\n"
+"      --version                         Показати дані щодо версії\n"
+"\n"
+"  -r, --record                          Встановити з’єднання для запису\n"
+"  -p, --playback                        Встановити з’єднання для "
+"відтворення\n"
+"\n"
+"  -v, --verbose                         Увімкнути докладний режим\n"
+"\n"
+"  -s, --server=СЕРВЕР                   Назва сервера, з яким слід "
+"встановити з’єднання\n"
+"  -d, --device=ПРИСТРІЙ                 Назва приймача або джерела даних, з "
+"яким слід встановити з’єднання\n"
+"  -n, --client-name=НАЗВА               Назва цього клієнта на сервері\n"
+"      --stream-name=НАЗВА               Назва цього потоку на сервері\n"
+"      --volume=ГУЧНІСТЬ                 Вказати початкову (лінійну) гучність "
+"у діапазоні 0...65536\n"
+"      --rate=ЧАСТОТА ДИСКРЕТИЗАЦІЇ      Частота дискретизації у Гц (типовою "
+"є 44100)\n"
+"      --format=ФОРМАТ ДАНИХ             Тип даних, варіанти: s16le, s16be, "
+"u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (типовим є "
+"s16ne)\n"
+"      --channels=КІЛЬКІСТЬ КАНАЛІВ      Кількість каналів, 1 — моно, 2 — "
+"стерео\n"
+"                                        (типове значення — 2)\n"
+"      --channel-map=КАРТА КАНАЛІВ       Карта каналів, яку слід "
+"використовувати замість типової\n"
+"      --fix-format                      Запозичити формат фрагмента з "
+"приймача, з яким з’єднано\n"
+"                                        потік.\n"
+"      --fix-rate                        Запозичити дані щодо частоти "
+"дискретизації з приймача, з яким з’єднано\n"
+"                                        потік.\n"
+"      --fix-channels                    Запозичити дані щодо кількості "
+"каналів та карти каналів\n"
+"                                        з приймача, з яким з’єднано потік.\n"
+"      --no-remix                        Не збільшувати і не зменшувати "
+"кількість каналів.\n"
+"      --no-remap                        Встановлювати відповідність каналів "
+"за номером, а не за назвою.\n"
+"      --latency=БАЙТИ                   Вимагати вказаної латентності у "
+"байтах.\n"
+"      --process-time=БАЙТИ              Вимагати вказаного часу обробки на "
+"запит у байтах.\n"
+"      --latency-msec=МС                 Вимагати вказаної латентності у "
+"мілісекундах.\n"
+"      --process-time-msec=МС            Вимагати вказаного часу обробки "
+"запиту у мілісекундах.\n"
+"      --property=ВЛАСТИВІСТЬ=ЗНАЧЕННЯ   Встановити для вказаної властивості "
+"вказане значення.\n"
+"      --raw                             Записувати і відтворювати "
+"неформатовані дані PCM.\n"
+"      --passthrough                     Передати дані без обробки.\n"
+"      --file-format[=ФОРМАТ ФАЙЛА]      Записувати і відтворювати "
+"форматовані дані PCM.\n"
+"      --list-file-formats               Показати список можливих форматів "
+"даних.\n"
+"     --monitor-stream=НОМЕР             Записувати дані з входу приймача з "
+"номером НОМЕР.\n"
+
+#: ../src/utils/pacat.c:793
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr "Відтворити закодовані звукові файли на звуковому сервері PulseAudio."
+
+#: ../src/utils/pacat.c:797
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr ""
+"Захопити звукові дані зі звукового сервера PulseAudio і записати їх до файла."
+
+#: ../src/utils/pacat.c:801
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr ""
+"Захопити звукові дані зі звукового сервера PulseAudio і записати їх до "
+"STDOUT або вказаного файла."
+
+#: ../src/utils/pacat.c:805
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr ""
+"Відтворити звукові дані з STDIN або вказаного файла на звуковому сервері "
+"PulseAudio."
+
+#: ../src/utils/pacat.c:819
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"Зібрано з libpulse %s\n"
+"З’єднано з libpulse %s\n"
+
+#: ../src/utils/pacat.c:852 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "Некоректна назва клієнта «%s»"
+
+#: ../src/utils/pacat.c:867
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "Некоректна назва потоку «%s»"
+
+#: ../src/utils/pacat.c:904
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "Некоректна карта каналів «%s»"
+
+#: ../src/utils/pacat.c:933 ../src/utils/pacat.c:947
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "Некоректна специфікація латентності «%s»"
+
+#: ../src/utils/pacat.c:940 ../src/utils/pacat.c:954
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "Некоректна часова специфікація «%s»"
+
+#: ../src/utils/pacat.c:966
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "Некоректна властивість «%s»"
+
+#: ../src/utils/pacat.c:985
+#, c-format
+msgid "Unknown file format %s."
+msgstr "Невідомий формат файлів %s."
+
+#: ../src/utils/pacat.c:1000
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "Не вдалося обробити аргумент --monitor-stream"
+
+#: ../src/utils/pacat.c:1011
+msgid "Invalid sample specification"
+msgstr "Некоректна частотна специфікація"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "open(): %s"
+msgstr "open(): %s"
+
+#: ../src/utils/pacat.c:1026
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2(): %s"
+
+#: ../src/utils/pacat.c:1033
+msgid "Too many arguments."
+msgstr "Забагато аргументів."
+
+#: ../src/utils/pacat.c:1044
+msgid "Failed to generate sample specification for file."
+msgstr "Не вдалося створити частотну специфікацію для файла."
+
+#: ../src/utils/pacat.c:1070
+msgid "Failed to open audio file."
+msgstr "Не вдалося відкрити звуковий файл."
+
+#: ../src/utils/pacat.c:1076
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr ""
+"Попередження: вказану частотну специфікацію буде перезаписано специфікацією "
+"з файла."
+
+#: ../src/utils/pacat.c:1079 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "Не вдалося отримати дані щодо частотної специфікації з файла."
+
+#: ../src/utils/pacat.c:1088
+msgid "Warning: Failed to determine channel map from file."
+msgstr "Попередження: не вдалося отримати дані щодо карти каналів з файла."
+
+#: ../src/utils/pacat.c:1099
+msgid "Channel map doesn't match sample specification"
+msgstr "Карта каналів не відповідає частотній специфікації"
+
+#: ../src/utils/pacat.c:1110
+msgid "Warning: failed to write channel map to file."
+msgstr "Попередження: не вдалося записати карту каналів до файла."
+
+#: ../src/utils/pacat.c:1125
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr ""
+"Відкриття потоку %s з частотною специфікацією «%s» і картою каналів «%s»."
+
+#: ../src/utils/pacat.c:1126
+msgid "recording"
+msgstr "запис"
+
+#: ../src/utils/pacat.c:1126
+msgid "playback"
+msgstr "відтворення"
+
+#: ../src/utils/pacat.c:1150
+msgid "Failed to set media name."
+msgstr "Не вдалося встановити назву носія даних."
+
+#: ../src/utils/pacat.c:1160 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "Спроба виконання pa_mainloop_new() завершилася невдало."
+
+#: ../src/utils/pacat.c:1183
+msgid "io_new() failed."
+msgstr "Спроба виконання io_new() завершилася невдало."
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "Спроба виконання pa_context_new() завершилася невдало."
+
+#: ../src/utils/pacat.c:1198 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "Спроба виконання pa_context_connect() завершилася невдало: %s"
+
+#: ../src/utils/pacat.c:1204
+msgid "pa_context_rttime_new() failed."
+msgstr "Спроба виконання pa_context_new() завершилася невдало."
+
+#: ../src/utils/pacat.c:1211 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "Спроба виконання pa_mainloop_run() завершилася невдало."
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "НАЗВА [АРГУМЕНТИ...]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "НАЗВА|НОМЕР"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "НАЗВА"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "НАЗВА|НОМЕР ГУЧНІСТЬ"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "НОМЕР ГУЧНІСТЬ"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "НАЗВА|НОМЕР 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "НОМЕР 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "НАЗВА|НОМЕР КЛЮЧ=ЗНАЧЕННЯ"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "НОМЕР КЛЮЧ=ЗНАЧЕННЯ"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "НОМЕР"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "НАЗВА ПРИЙМАЧ|НОМЕР"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "НАЗВА ФАЙЛ"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "ШЛЯХ"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "НАЗВА_ФАЙЛА ПРИЙМАЧ|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "НОМЕР ПРИЙМАЧ|ДЖЕРЕЛО"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "ПРОФІЛЬ КАРТКИ"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "НАЗВА|НОМЕР ПОРТУ"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "НАЗВА-КАРТКИ|№КАРТКИ ПОРТ ЗСУВ"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "ПРИЗНАЧЕННЯ"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "ЧИСЛОВИЙ РІВЕНЬ"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "БЛОКИ"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            Показати цю довідку\n"
+"      --version                         Показати відомості щодо версії\n"
+"Якщо команду не буде вказано, pacmd буде запущено у інтерактивному режимі\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"Зібрано з libpulse %s\n"
+"З’єднано з libpulse %s\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr ""
+"Фонову службу PulseAudio не запущено, або цю службу не запущено як фонову "
+"службу сеансу."
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect(): %s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr ""
+"Спроба завершення роботи фонової служби PulseAudio завершилася невдало."
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "Фонова служба не відповідає."
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write(): %s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll(): %s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read(): %s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "Не вдалося отримати статистичні дані: %s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "Зараз використано: %u блоків, що містять загалом %s байтів.\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr ""
+"Виділено протягом виконання загалом: %u блоків, що містять %s байтів.\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "Розмір кешу фрагментів: %s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "Не вдалося отримати дані щодо сервера: %s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"Рядок сервера: %s\n"
+"Версія протоколу бібліотеки: %u\n"
+"Версія протоколу сервера: %u\n"
+"Локальний: %s\n"
+"Номер клієнта: %u\n"
+"Розмір фрагмента: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"Користувач: %s\n"
+"Назва вузла: %s\n"
+"Назва сервера: %s\n"
+"Версія сервера: %s\n"
+"Типова частотна специфікація: %s\n"
+"Типова карта каналів: %s\n"
+"Типовий приймач: %s\n"
+"Типове джерело: %s\n"
+"Кука: %04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "Не вдалося отримати дані щодо приймача: %s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Приймач даних №%u\n"
+"\tСтан: %s\n"
+"\tНазва: %s\n"
+"\tОпис: %s\n"
+"\tДрайвер: %s\n"
+"\tЧастотна специфікація: %s\n"
+"\tКарта каналів: %s\n"
+"\tМодуль-власник: %u\n"
+"\tСтан вимикання: %s\n"
+"\tГучність: %s\n"
+"\t        баланс %0.2f\n"
+"\tБазова гучність: %s\n"
+"\tДжерело спостереження: %s\n"
+"\tЛатентність: %0.0f мкс, налаштовано %0.0f мкс\n"
+"\tПрапорці: %s%s%s%s%s%s%s\n"
+"\tВластивості:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\tПорти:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\tАктивний порт: %s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\tФормати:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "Не вдалося отримати дані щодо джерела: %s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Джерело №%u\n"
+"\tСтан: %s\n"
+"\tНазва: %s\n"
+"\tОпис: %s\n"
+"\tДрайвер: %s\n"
+"\tЧастотна специфікація: %s\n"
+"\tКарта каналів: %s\n"
+"\tМодуль-власник: %u\n"
+"\tСтан вимикання: %s\n"
+"\tГучність: %s\n"
+"\t        баланс %0.2f\n"
+"\tБазова гучність: %s\n"
+"\tСпостереження: %s\n"
+"\tЛатентність: %0.0f мкс, налаштовано %0.0f мкс\n"
+"\tПрапорці: %s%s%s%s%s%s\n"
+"\tВластивості:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "н/д"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "Не вдалося отримати дані щодо модуля: %s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Модуль №%u\n"
+"\tНазва: %s\n"
+"\tАргумент: %s\n"
+"\tЛічильник використання: %s\n"
+"\tВластивості:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "Не вдалося отримати дані щодо клієнта: %s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Клієнт №%u\n"
+"\tДрайвер: %s\n"
+"\tМодуль-власник: %s\n"
+"\tВластивості:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "Не вдалося отримати дані щодо карти: %s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Карта №%u\n"
+"\tНазва: %s\n"
+"\tДрайвер: %s\n"
+"\tМодуль-власник: %s\n"
+"\tВластивості:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\tПрофілі:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr "\t\t%s: %s (приймачів: %u, джерел: %u, пріоритет: %u, доступно: %s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\tАктивний профіль: %s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\tВластивості:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\tЧастина профілів: %s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "Не вдалося отримати відомостей щодо вхідного каналу приймача: %s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Вхідний канал приймача даних №%u\n"
+"\tДрайвер: %s\n"
+"\tМодуль-власник: %s\n"
+"\tКлієнт: %s\n"
+"\tПриймач: %u\n"
+"\tЧастотна специфікація: %s\n"
+"\tКарта каналів: %s\n"
+"\tФормат: %s\n"
+"\tСтан призупинення: %s\n"
+"\tСтан вимикання: %s\n"
+"\tГучність: %s\n"
+"\t        баланс %0.2f\n"
+"\tЛатентність буфера: %0.0f мкс\n"
+"\tЛатентність приймача: %0.0f мкс\n"
+"\tМетод зміни частоти: %s\n"
+"\tВластивості:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "Не вдалося отримати дані щодо відтворення джерела: %s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Відтворення джерела #%u\n"
+"\tДрайвер: %s\n"
+"\tМодуль-власник: %s\n"
+"\tКлієнт: %s\n"
+"\tДжерело: %u\n"
+"\tЧастотна специфікація: %s\n"
+"\tКарта каналів: %s\n"
+"\tФормат: %s\n"
+"\tСтан призупинення: %s\n"
+"\tСтан вимикання: %s\n"
+"\tГучність: %s\n"
+"\t          баланс %0.2f\n"
+"\tЛатентність буфера: %0.0f мкс\n"
+"\tЛатентність джерела: %0.0f мкс\n"
+"\tСпосіб зміни частоти: %s\n"
+"\tВластивості:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "Не вдалося отримати дані щодо фрагмента: %s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Фрагмент №%u\n"
+"\tНазва: %s\n"
+"\tЧастотна специфікація: %s\n"
+"\tКарта каналів: %s\n"
+"\tГучність: %s\n"
+"\t          баланс %0.2f\n"
+"\tТривалість: %0.1f с\n"
+"\tРозмір: %s\n"
+"\tЛінивість: %s\n"
+"\tНазва файла: %s\n"
+"\tВластивості:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "Помилка: %s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "Не вдалося вивантажити модуль: модуль %s не завантажено"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr ""
+"Не вдалося встановити гучність: ви намагалися встановити гучність для %d "
+"каналів, хоча передбачено підтримку %d каналів\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "Не вдалося встановити формат: некоректний рядок формату %s"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "Не вдалося вивантажити зразок: %s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "Передчасне завершення файла"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "створити"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "змінити"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "вилучити"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "невідомий"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "приймач"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "джерело"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "вхід приймача"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "відтворення джерела"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "модуль"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "клієнт"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "кеш семплів"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "сервер"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "картка"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "Подія «%s» на %s №%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "Отримано сигнал SIGINT, завершення роботи."
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "Некоректна специфікація гучності"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "Гучність поза межами дозволеного діапазону.\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "Некоректна кількість специфікацій гучності.\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "Несумісна специфікація гучності.\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[параметри]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[ТИП]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "НАЗВА_ФАЙЛА [НАЗВА]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "НАЗВА [ПРИЙМАЧ]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "НАЗВА|НОМЕР ГУЧНІСТЬ [ГУЧНІСТЬ ...]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "НОМЕР ГУЧНІСТЬ [ГУЧНІСТЬ ...]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "НАЗВА|НОМЕР 1|0|toggle"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "НОМЕР 1|0|toggle"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "НОМЕР ФОРМАТИ"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"Спеціальними назвами, @DEFAULT_SINK@, @DEFAULT_SOURCE@ та "
+"@DEFAULT_MONITOR@,\n"
+"можна скористатися для визначення типового приймача, джерела та монітора.\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            Показати цю довідку\n"
+"      --version                         Показати відомості щодо версії\n"
+"  -s, --server=СЕРВЕР                   Назва сервера, з яким слід "
+"з’єднатися\n"
+"  -n, --client-name=НАЗВА               Назва цього клієнта на сервері\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"Зібрано з libpulse %s\n"
+"З’єднано з libpulse %s\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "Нічого не вказуйте або вкажіть один з варіантів: %s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "Будь ласка, вкажіть файл фрагмента для завантаження"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "Не вдалося відкрити звуковий файл."
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr ""
+"Попередження: не вдалося отримати дані щодо частотної специфікації з файла."
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "Вам слід вказати назву зразкового файла, який слід відтворити"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "Вам слід вказати назву зразкового файла, який слід вилучити"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "Вам слід вказати номер вхідного каналу приймача даних і приймач"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "Вам слід вказати номер джерела відтворення і джерело"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "Вам слід вказати назву модуля і аргументи."
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "Вам слід вказати номер або назву модуля"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr ""
+"Не можна вказувати більше одного приймача. Вам слід вказати булівське "
+"значення."
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "Некоректна специфікація призупинення."
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr ""
+"Не можна вказувати більше одного джерела. Вам слід вказати булівське "
+"значення."
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "Вам слід вказати назву/номер карти і назву профілю"
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "Вам слід вказати назву/номер приймача і назву порту"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "Вам слід вказати назву приймача"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "Вам слід вказати назву/номер джерела і назву порту"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "Вам слід вказати назву джерела"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "Вам слід вказати назву/номер приймача і гучність"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "Вам слід вказати назву/номер джерела і гучність"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "Вам слід вказати номер вхідного каналу приймача даних і гучність"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "Некоректний номер вхідного каналу приймача даних"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "Вам слід вказати номер каналу відтворення і гучність"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "Некоректний номер джерела відтворення"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Вам слід вказати назву/номер приймача і значення вимикання звуку (0, 1 або "
+"«toggle»)"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "Некоректна специфікація вимикання звуку"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Вам слід вказати назву/номер джерела і значення вимикання звуку (0, 1 або "
+"«toggle»)"
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr ""
+"Вам слід вказати індекс входу приймача і значення вимикання звуку (0, 1 або "
+"«toggle»)"
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "Некоректна специфікація номера вхідного каналу приймача даних"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr ""
+"Вам слід вказати індекс виходу джерела і значення вимикання звуку (0, 1 або "
+"«toggle»)"
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "Некоректна специфікація номера джерела відтворення"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr ""
+"Вам слід вказати номер приймача та список підтримуваних каналів, "
+"відокремлених комами"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr "Вам слід вказати назву/номер карти, назву порту і зсув латентності"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "Не вдалося обробити дані щодо зсуву латентності"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "Не вказано коректної команди."
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork(): %s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp(): %s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "Невдала спроба відновлення: %s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "Невдала спроба призупинки: %s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr ""
+"ПОПЕРЕДЖЕННЯ: звуковий сервер не є локальним, його не можна призупинити.\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "Спроба встановлення з’єднання зазнала невдачі: %s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "Отримано сигнал SIGINT, завершення роботи.\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr ""
+"ПОПЕРЕДЖЕННЯ: виконання дочірнього процесу було перервано з сигналом %u\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [параметри] ... \n"
+"\n"
+"  -h, --help                            Показати цю довідку\n"
+"      --version                         Показати відомості щодо версії\n"
+"  -s, --server=СЕРВЕР                   Назва сервера, з яким слід "
+"з’єднатися\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"Зібрано з libpulse %s\n"
+"З’єднано з libpulse %s\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "Спроба виконання pa_mainloop_new() завершилася невдало.\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "Спроба виконання pa_context_new() завершилася невдало.\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "Спроба виконання pa_mainloop_run() завершилася невдало.\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D клієнт] [-S сервер] [-O приймач] [-I джерело] [-c файл]  [-d|-e|-i|-"
+"r]\n"
+"\n"
+" -d    Показати поточні дані PulseAudio, приєднаного до клієнта X11 "
+"(типово)\n"
+" -e    Експортувати локальні дані PulseAudio на клієнт X11\n"
+" -i    Імпортувати дані PulseAudio з клієнта X11 до локальних змінних і "
+"файла кук.\n"
+" -r    Вилучити дані PulseAudio з клієнта X11\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "Не вдалося обробити рядок команди.\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "Сервер: %s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "Джерело: %s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Приймач: %s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Кука: %s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "Не вдалося обробити дані куки\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "Не вдалося зберегти дані куки\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "Не вдалося отримати FQDN.\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "Не вдалося завантажити дані куки\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "Ще не реалізовано.\n"
+
+#~ msgid ""
+#~ "%s [options]\n"
+#~ "\n"
+#~ "-h, --help                            Show this help\n"
+#~ "-v, --verbose                         Print debug messages\n"
+#~ "      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --from-format=SAMPLEFORMAT      From sample type (defaults to "
+#~ "s16le)\n"
+#~ "      --from-channels=CHANNELS        From number of channels (defaults "
+#~ "to 1)\n"
+#~ "      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+#~ "      --to-channels=CHANNELS          To number of channels (defaults to "
+#~ "1)\n"
+#~ "      --resample-method=METHOD        Resample method (defaults to auto)\n"
+#~ "      --seconds=SECONDS               From stream duration (defaults to "
+#~ "60)\n"
+#~ "\n"
+#~ "If the formats are not specified, the test performs all formats "
+#~ "combinations,\n"
+#~ "back and forth.\n"
+#~ "\n"
+#~ "Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+#~ "alaw,\n"
+#~ "s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+#~ "\n"
+#~ "See --dump-resample-methods for possible values of resample methods.\n"
+#~ msgstr ""
+#~ "%s [параметри]\n"
+#~ "\n"
+#~ "-h, --help                            Показати цю довідку\n"
+#~ "-v, --verbose                         Показувати діагностичні "
+#~ "повідомлення\n"
+#~ "      --from-rate=ЧАСТОТА ДИСКР.      Початкова частота дискретизації у "
+#~ "Гц (типовою є 44100)\n"
+#~ "      --from-format=ФОРМАТ ДАНИХ      Початковий формат даних (типовим є "
+#~ "s16le)\n"
+#~ "      --from-channels=КТЬ КАНАЛІВ     Початкова кількість каналів "
+#~ "(типовою є 1)\n"
+#~ "      --to-rate=ЧАСТОТА ДИСКР.        Частота дискретизації після обробки "
+#~ "у Гц (типовою є 44100)\n"
+#~ "      --to-format=ФОРМАТ ДАНИХ        Формат даних після обробки (типовим "
+#~ "є s16le)\n"
+#~ "      --to-channels=КТЬ КАНАЛІВ       Кількість каналів після обробки "
+#~ "(типовою є 1)\n"
+#~ "      --resample-method=СПОСІБ        Спосіб зміни частоти (типовим є "
+#~ "auto)\n"
+#~ "      --seconds=КТЬ СЕКУНД            Початкова тривалість потоку "
+#~ "(типовою є 60)\n"
+#~ "\n"
+#~ "Якщо формати не буде вказано, перевірка виконуватиметься для всіх "
+#~ "комбінацій форматів,\n"
+#~ "у обох напрямках.\n"
+#~ "\n"
+#~ "Можливі формати даних: s16le, s16be, u8, float32le, float32be, ulaw, "
+#~ "alaw,\n"
+#~ "s24le, s24be, s24-32le, s24-32be, s32le, s32be (типовим є s16ne)\n"
+#~ "\n"
+#~ "Перелік можливих значень способів зміни частоти можна отримати за "
+#~ "допомогою параметра --dump-resample-methods.\n"
+
+#~ msgid "%s %s\n"
+#~ msgstr "%s %s\n"
+
+#~ msgid "Cleaning up privileges."
+#~ msgstr "Позбуваємося прав доступу."
+
+#~ msgid "Got signal %s."
+#~ msgstr "Отримано сигнал %s."
+
+#~ msgid "Exiting."
+#~ msgstr "Завершення роботи."
+
+#~ msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+#~ msgstr "Знайдено користувача «%s» (UID %lu) і групу «%s» (GID %lu)."
+
+#~ msgid "Successfully changed user to \""
+#~ msgstr "Користувача успішно змінено на «"
+
+#~ msgid "setrlimit(%s, (%u, %u)) failed: %s"
+#~ msgstr "Спроба виконати setrlimit(%s, (%u, %u)) була невдалою: %s"
+
+#~ msgid "Daemon not running"
+#~ msgstr "Фонову службу не запущено"
+
+#~ msgid "Daemon running as PID %u"
+#~ msgstr "Фонову службу запущено як PID %u"
+
+#~ msgid "Daemon startup successful."
+#~ msgstr "Фонову службу успішно запущено."
+
+#~ msgid "This is PulseAudio %s"
+#~ msgstr "Це PulseAudio %s"
+
+#~ msgid "Compilation host: %s"
+#~ msgstr "Вузол збирання: %s"
+
+#~ msgid "Compilation CFLAGS: %s"
+#~ msgstr "CFLAGS збирання: %s"
+
+#~ msgid "Running on host: %s"
+#~ msgstr "Запущено на вузлі: %s"
+
+#~ msgid "Found %u CPUs."
+#~ msgstr "Знайдено %u процесорів."
+
+#~ msgid "Page size is %lu bytes"
+#~ msgstr "Розмір сторінки дорівнює %lu байтам"
+
+#~ msgid "Compiled with Valgrind support: yes"
+#~ msgstr "Зібрано з підтримкою Valgrind: так"
+
+#~ msgid "Compiled with Valgrind support: no"
+#~ msgstr "Зібрано з підтримкою Valgrind: ні"
+
+#~ msgid "Running in valgrind mode: %s"
+#~ msgstr "Запуск у режимі valgrind: %s"
+
+#~ msgid "Running in VM: %s"
+#~ msgstr "Запущено у віртуальній машині: %s"
+
+#~ msgid "Optimized build: yes"
+#~ msgstr "Зібрано з оптимізацією: так"
+
+#~ msgid "Optimized build: no"
+#~ msgstr "Зібрано з оптимізацією: ні"
+
+#~ msgid "NDEBUG defined, all asserts disabled."
+#~ msgstr "Визначено NDEBUG, всі додавання вимкнено."
+
+#~ msgid "FASTPATH defined, only fast path asserts disabled."
+#~ msgstr "Визначено FASTPATH, вимкнено лише додавання швидких шляхів."
+
+#~ msgid "All asserts enabled."
+#~ msgstr "Увімкнено всі додавання."
+
+#~ msgid "Machine ID is %s."
+#~ msgstr "Ідентифікатор системи %s."
+
+#~ msgid "Session ID is %s."
+#~ msgstr "Ідентифікатор сеансу — %s."
+
+#~ msgid "Using runtime directory %s."
+#~ msgstr "Каталог запуску: %s."
+
+#~ msgid "Using state directory %s."
+#~ msgstr "Каталог стану: %s."
+
+#~ msgid "Using modules directory %s."
+#~ msgstr "Каталог модулів: %s."
+
+#~ msgid "Running in system mode: %s"
+#~ msgstr "Запуску у загальносистемному режимі: %s"
+
+#~ msgid "Fresh high-resolution timers available! Bon appetit!"
+#~ msgstr "Доступні свіжі високоточні таймери! Смачного!"
+
+#~ msgid ""
+#~ "Dude, your kernel stinks! The chef's recommendation today is Linux with "
+#~ "high-resolution timers enabled!"
+#~ msgstr ""
+#~ "Хлопче, від твого ядра вже тхне! Варто нарешті встановити Linux з "
+#~ "увімкненими високоточними таймерами!"
+
+#~ msgid "Daemon startup complete."
+#~ msgstr "Запуск фонової служби завершено."
+
+#~ msgid "Daemon shutdown initiated."
+#~ msgstr "Ініційовано завершення роботи фонової служби."
+
+#~ msgid "Daemon terminated."
+#~ msgstr "Виконання фонової служби перервано."
+
+#~ msgid "Analog 4-channel Input"
+#~ msgstr "Аналогових 4-канальний вхід"
+
+#~ msgid "No cookie loaded. Attempting to connect without."
+#~ msgstr ""
+#~ "Куків не завантажено. Буде виконано спробу з’єднання за їх відсутності."
+
+#~ msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+#~ msgstr "=== %d секунд: %d Гц %d кан. (%s) -> %d Гц %d кан. (%s)"
+
+#~ msgid "Failed to load client configuration file.\n"
+#~ msgstr "Не вдалося завантажити файл налаштувань клієнта.\n"
+
+#~ msgid "Failed to read environment configuration data.\n"
+#~ msgstr "Не вдалося прочитати дані налаштування середовища.\n"
+
+#~ msgid "PulseAudio Sound System KDE Routing Policy"
+#~ msgstr "Правила маршрутизації звукової системи PulseAudio у KDE"
+
+#~ msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+#~ msgstr "Запустити звукову систему PulseAudio з правилами маршрутизації KDE"
+
+#~ msgid "Failed to open configuration file '%s': %s"
+#~ msgstr "Не вдалося відкрити файл налаштування «%s»: %s"
+
+#~ msgid "Successfully dropped root privileges."
+#~ msgstr "Програма успішно позбулася прав доступу користувача root."
+
+#~ msgid "[%s:%u] rlimit not supported on this platform."
+#~ msgstr "[%s:%u] rlimit не підтримується на цій платформі."
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "Спроба виконання XOpenDisplay() завершилася невдало"
+
+#~ msgid ""
+#~ "Source Output #%u\n"
+#~ "\tDriver: %s\n"
+#~ "\tOwner Module: %s\n"
+#~ "\tClient: %s\n"
+#~ "\tSource: %u\n"
+#~ "\tSample Specification: %s\n"
+#~ "\tChannel Map: %s\n"
+#~ "\tBuffer Latency: %0.0f usec\n"
+#~ "\tSource Latency: %0.0f usec\n"
+#~ "\tResample method: %s\n"
+#~ "\tProperties:\n"
+#~ "\t\t%s\n"
+#~ msgstr ""
+#~ "Відтворення джерела #%u\n"
+#~ "\tДрайвер: %s\n"
+#~ "\tМодуль власника: %s\n"
+#~ "\tКлієнт: %s\n"
+#~ "\tДжерело: %u\n"
+#~ "\tЧастотна специфікація: %s\n"
+#~ "\tКарта каналів: %s\n"
+#~ "\tЛатентність буфера: %0.0f мкс\n"
+#~ "\tЛатентність джерела: %0.0f мкс\n"
+#~ "\tСпосіб зміни частоти: %s\n"
+#~ "\tВластивості:\n"
+#~ "\t\t%s\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "%s [options] stat\n"
+#~ "%s [options] list\n"
+#~ "%s [options] exit\n"
+#~ "%s [options] upload-sample FILENAME [NAME]\n"
+#~ "%s [options] play-sample NAME [SINK]\n"
+#~ "%s [options] remove-sample NAME\n"
+#~ "%s [options] move-sink-input SINKINPUT SINK\n"
+#~ "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
+#~ "%s [options] load-module NAME [ARGS ...]\n"
+#~ "%s [options] unload-module MODULE\n"
+#~ "%s [options] suspend-sink SINK 1|0\n"
+#~ "%s [options] suspend-source SOURCE 1|0\n"
+#~ "%s [options] set-card-profile CARD PROFILE\n"
+#~ "%s [options] set-sink-port SINK PORT\n"
+#~ "%s [options] set-source-port SOURCE PORT\n"
+#~ "%s [options] set-sink-volume SINK VOLUME\n"
+#~ "%s [options] set-source-volume SOURCE VOLUME\n"
+#~ "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+#~ "%s [options] set-sink-mute SINK 1|0\n"
+#~ "%s [options] set-source-mute SOURCE 1|0\n"
+#~ "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
+#~ "%s [options] subscribe\n"
+#~ "\n"
+#~ "  -h, --help                            Show this help\n"
+#~ "      --version                         Show version\n"
+#~ "\n"
+#~ "  -s, --server=SERVER                   The name of the server to connect "
+#~ "to\n"
+#~ "  -n, --client-name=NAME                How to call this client on the "
+#~ "server\n"
+#~ msgstr ""
+#~ "%s [параметри] stat\n"
+#~ "%s [параметри] list\n"
+#~ "%s [параметри] exit\n"
+#~ "%s [параметри] upload-sample НАЗВА_ФАЙЛА [НАЗВА]\n"
+#~ "%s [параметри] play-sample НАЗВА [ПРИЙМАЧ]\n"
+#~ "%s [параметри] remove-sample НАЗВА\n"
+#~ "%s [параметри] move-sink-input ІДЕНТИФІКАТОР ПРИЙМАЧ\n"
+#~ "%s [параметри] move-source-output ІДЕНТИФІКАТОР ДЖЕРЕЛО\n"
+#~ "%s [параметри] load-module НАЗВА [АРГУМЕНТИ ...]\n"
+#~ "%s [параметри] unload-module ІДЕНТИФІКАТОР\n"
+#~ "%s [параметри] suspend-sink ПРИЙМАЧ 1|0\n"
+#~ "%s [параметри] suspend-source ДЖЕРЕЛО 1|0\n"
+#~ "%s [параметри] set-card-profile КАРТА ПРОФІЛЬ \n"
+#~ "%s [параметри] set-sink-port ПРИЙМАЧ ПОРТ \n"
+#~ "%s [параметри] set-source-port ДЖЕРЕЛО ПОРТ\n"
+#~ "%s [параметри] set-sink-volume ПРИЙМАЧ ГУЧНІСТЬ\n"
+#~ "%s [параметри] set-source-volume ДЖЕРЕЛО ГУЧНІСТЬ\n"
+#~ "%s [параметри] set-sink-input-volume ВХІДПРИЙМАЧА ГУЧНІСТЬ\n"
+#~ "%s [параметри] set-sink-mute ПРИЙМАЧ 1|0\n"
+#~ "%s [параметри] set-source-mute ДЖЕРЕЛО 1|0\n"
+#~ "%s [параметри] set-sink-input-mute ВХІДПРИЙМАЧА 1|0\n"
+#~ "\n"
+#~ "  -h, --help                            Показати цю довідку\n"
+#~ "      --version                         Показати дані щодо версії\n"
+#~ "\n"
+#~ "  -s, --server=СЕРВЕР                   Назва сервера, з яким слід "
+#~ "з’єднатися\n"
+#~ "  -n, --client-name=НАЗВА               Спосіб виклику клієнта на "
+#~ "сервері\n"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "Цифровий об’ємний 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "Джерело низьких частот"
diff --git a/po/zh_CN.po b/po/zh_CN.po
new file mode 100644 (file)
index 0000000..19a7ae5
--- /dev/null
@@ -0,0 +1,3117 @@
+# Simplified Chinese translation for PulseAudio.
+# Copyright (C) 2008 PULSEAUDIO COPYRIGHT HOLDER
+# This file is distributed under the same license as the pulseaudio package.
+# 闫丰刚 <sainry@gmail.com>, 2008, 2009.
+# Leah Liu <lliu@redhat.com>, 2009, 2012.
+# Cheng-Chia Tseng <pswo10680@gmail.com>, 2010, 2012.
+# Frank Hill <hxf.prc@gmail.com>, 2015.
+# Mingye Wang (Arthur2e5) <arthur200126@gmail.com>, 2015.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pulseaudio.master-tx\n"
+"Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
+"product=PulseAudio&keywords=I18N+L10N&component=misc\n"
+"POT-Creation-Date: 2016-03-26 02:46+0000\n"
+"PO-Revision-Date: 2016-03-26 13:00+0800\n"
+"Last-Translator: Luo Lei <luolei@ubuntukylin.com>\n"
+"Language-Team: Chinese (Simplified, GNOME) <i18n-zh@googlegroups.com>\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2016-03-22 13:23+0000\n"
+"X-Generator: Poedit 1.7.6\n"
+
+#: ../src/daemon/cmdline.c:111
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory "
+"segments\n"
+"      --start                           Start the daemon if it is not "
+"running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only "
+"returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID "
+"or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested "
+"module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and "
+"this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle "
+"and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v  --verbose                         Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log "
+"messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic "
+"shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module "
+"with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running "
+"TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [选项]\n"
+"\n"
+"命令:\n"
+"  -h, --help                            显示此帮助\n"
+"      --version                         显示版本号\n"
+"      --dump-conf                       转储默认设置\n"
+"      --dump-modules                    转储可用的模块列表\n"
+"      --dump-resample-methods           转储可用的重采样方法\n"
+"      --cleanup-shm                     清理滞留的共享内存段\n"
+"      --start                           如果后台程序没有运行则启动后台程序\n"
+"  -k  --kill                            杀死运行中的后台程序\n"
+"      --check                           寻找运行中的后台程序(只返回退出"
+"码)\n"
+"\n"
+"选项:\n"
+"      --system[=BOOL]                   作为系统范围实例运行\n"
+"  -D, --daemonize[=BOOL]                启动后转为后台运行\n"
+"      --fail[=BOOL]                     启动失败则退出\n"
+"      --high-priority[=BOOL]            尝试设定高静态优先级\n"
+"                                        (仅以 root 运行时,处于 SUID 或者\n"
+"                                        在 RLIMIT_NICE 提升时可用)\n"
+"      --realtime[=BOOL]                 尝试启用实时调度\n"
+"                                        (仅以 root 运行时,处于 SUID 或者\n"
+"                                        在 RLIMIT_PTPRIO 提升时可用)\n"
+"      --disallow-module-loading[=BOOL]  启动后拒绝模块用户的\n"
+"                                        加载/卸载请求\n"
+"      --disallow-exit[=BOOL]            拒绝用户的退出请求\n"
+"      --exit-idle-time=SECS             空闲指定时长后终止后台程序\n"
+"      --module-idle-time=SECS           空闲指定时长后卸载自动加载的模块\n"
+"      --scache-idle-time=SECS           空闲指定时长后卸载自动加载的采样\n"
+"      --log-level[=LEVEL]               提高或者设定详细度\n"
+"  -v  --verbose                          提高详细度\n"
+"      --log-target={auto,syslog,stderr,file:路径,newfile:路径}\n"
+"                                        指定日志目标\n"
+"      --log-meta[=BOOL]                 将代码位置加入日志\n"
+"      --log-time[=BOOL]                 将时间戳加入日志\n"
+"      --log-backtrace=FRAMES            将回溯跟踪加入日志\n"
+"  -p, --dl-search-path=PATH             为动态共享对象(插件)设定\n"
+"                                        搜索路径\n"
+"      --resample-method=METHOD          使用指定的重采样方法\n"
+"                                        (使用 --dump-resample-methods 查看\n"
+"                                        可能的值)\n"
+"      --use-pid-file[=BOOL]             创建一个PID文件\n"
+"      --no-cpu-limit[=BOOL]             不在支持的平台上载入 CPU 负载显示"
+"器。\n"
+"       --disable-shm[=BOOL]             禁用共享内存支持\n"
+"\n"
+"启动脚本:\n"
+"  -L, --load=\"模块 参数...\"            用指定的参数加载指定的插件模块\n"
+"  -F, --file=文件名                      运行指定的脚本\n"
+"  -C                                    启动后在运行的 tty 上打开命令行\n"
+"\n"
+"  -n                                    不加载默认的脚本文件\n"
+
+#: ../src/daemon/cmdline.c:243
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize 布尔参数"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail 布尔参数"
+
+#: ../src/daemon/cmdline.c:262
+msgid ""
+"--log-level expects log level argument (either numeric in range 0..4 or one "
+"of debug, info, notice, warn, error)."
+msgstr ""
+"--log-level 日志级别参数(可以是数字 0~4 或者 debug、info、notice、warn 和 "
+"error 中的一个)"
+
+#: ../src/daemon/cmdline.c:274
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority 布尔参数"
+
+#: ../src/daemon/cmdline.c:282
+msgid "--realtime expects boolean argument"
+msgstr "--realtime 布尔参数"
+
+#: ../src/daemon/cmdline.c:290
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading 布尔参数"
+
+#: ../src/daemon/cmdline.c:298
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit 布尔参数"
+
+#: ../src/daemon/cmdline.c:306
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file 布尔参数"
+
+#: ../src/daemon/cmdline.c:325
+msgid ""
+"Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a "
+"valid file name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"无效的日志目标:要么使用 syslog、journal、stderr 或 auto,要么给定正确的文件"
+"名:file:<路径>、newfile:<路径>。"
+
+#: ../src/daemon/cmdline.c:327
+msgid ""
+"Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file "
+"name 'file:<path>', 'newfile:<path>'."
+msgstr ""
+"无效的日志目标:要么使用 syslog、stderr 或 auto,要么给定正确的文件名:file:<"
+"路径>、newfile:<路径>。"
+
+#: ../src/daemon/cmdline.c:335
+msgid "--log-time expects boolean argument"
+msgstr "--log-time 布尔参数"
+
+#: ../src/daemon/cmdline.c:343
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta 布尔参数"
+
+#: ../src/daemon/cmdline.c:363
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "无效的重采样方法 %s。"
+
+#: ../src/daemon/cmdline.c:370
+msgid "--system expects boolean argument"
+msgstr "--system 布尔参数"
+
+#: ../src/daemon/cmdline.c:378
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit 布尔参数"
+
+#: ../src/daemon/cmdline.c:386
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm 布尔参数"
+
+#: ../src/daemon/daemon-conf.c:259
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] 无效的日志目标 %s。"
+
+#: ../src/daemon/daemon-conf.c:274
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] 无效的日志级别 '%s。"
+
+#: ../src/daemon/daemon-conf.c:289
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] 无效的重采样方法 %s。"
+
+#: ../src/daemon/daemon-conf.c:311
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] 无效的运行限制 %s。"
+
+#: ../src/daemon/daemon-conf.c:331
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] 无效的采样格式 %s。"
+
+#: ../src/daemon/daemon-conf.c:348 ../src/daemon/daemon-conf.c:365
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] 无效的采样率 %s。"
+
+#: ../src/daemon/daemon-conf.c:388
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] 无效的采样声道 %s。"
+
+#: ../src/daemon/daemon-conf.c:405
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] 无效声道映射 '%s'。"
+
+#: ../src/daemon/daemon-conf.c:422
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] 无效的分片数 %s。"
+
+#: ../src/daemon/daemon-conf.c:439
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] 无效的分片大小 %s。"
+
+#: ../src/daemon/daemon-conf.c:456
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] 无效的优先级 %s。"
+
+#: ../src/daemon/daemon-conf.c:499
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] 无效的服务器类型 '%s'。"
+
+#: ../src/daemon/daemon-conf.c:613
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "打开配置文件失败:%s"
+
+#: ../src/daemon/daemon-conf.c:629
+msgid ""
+"The specified default channel map has a different number of channels than "
+"the specified default number of channels."
+msgstr "指定的默认声道映射的声道数与指定的默认声道数不同。"
+
+#: ../src/daemon/daemon-conf.c:716
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### 从配置文件读取:%s ###\n"
+
+#: ../src/daemon/dumpmodules.c:57
+#, c-format
+msgid "Name: %s\n"
+msgstr "名称:%s\n"
+
+#: ../src/daemon/dumpmodules.c:60
+#, c-format
+msgid "No module information available\n"
+msgstr "没有可用的模块信息\n"
+
+#: ../src/daemon/dumpmodules.c:63
+#, c-format
+msgid "Version: %s\n"
+msgstr "版本:%s\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Description: %s\n"
+msgstr "描述:%s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Author: %s\n"
+msgstr "作者:%s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Usage: %s\n"
+msgstr "用法:%s\n"
+
+#: ../src/daemon/dumpmodules.c:70
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "加载一次:%s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "旧接口警告:%s\n"
+
+#: ../src/daemon/dumpmodules.c:76
+#, c-format
+msgid "Path: %s\n"
+msgstr "路径:%s\n"
+
+#: ../src/daemon/ltdl-bind-now.c:75
+#, c-format
+msgid "Failed to open module %s: %s"
+msgstr "打开模块 %s 失败:%s"
+
+#: ../src/daemon/ltdl-bind-now.c:126
+msgid "Failed to find original lt_dlopen loader."
+msgstr "查找原始 lt_dlopen 加载器失败。"
+
+#: ../src/daemon/ltdl-bind-now.c:131
+msgid "Failed to allocate new dl loader."
+msgstr "分配新的动态加载器失败。"
+
+#: ../src/daemon/ltdl-bind-now.c:144
+msgid "Failed to add bind-now-loader."
+msgstr "添加 bind-now-loader 失败。"
+
+#: ../src/daemon/main.c:160
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "找不到用户 %s。"
+
+#: ../src/daemon/main.c:165
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "找不到组 %s。"
+
+#: ../src/daemon/main.c:174
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "用户 %s 与组 %s 的 GID 不匹配。"
+
+#: ../src/daemon/main.c:179
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "用户 %s 的主文件夹不是 %s,忽略。"
+
+#: ../src/daemon/main.c:182 ../src/daemon/main.c:187
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "创建 %s 失败:%s"
+
+#: ../src/daemon/main.c:194
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "更改组列表失败:%s"
+
+#: ../src/daemon/main.c:210
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "更改 GID 失败:%s"
+
+#: ../src/daemon/main.c:226
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "更改 UID 失败:%s"
+
+#: ../src/daemon/main.c:255
+msgid "System wide mode unsupported on this platform."
+msgstr "此平台不支持系统全局模式。"
+
+#: ../src/daemon/main.c:484
+msgid "Failed to parse command line."
+msgstr "命令行解析失败"
+
+#: ../src/daemon/main.c:523
+msgid ""
+"System mode refused for non-root user. Only starting the D-Bus server lookup "
+"service."
+msgstr "已拒绝非超级用户使用系统模式,仅启动 D-Bus 服务器查找服务。"
+
+#: ../src/daemon/main.c:622
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "杀死守护进程失败:%s"
+
+#: ../src/daemon/main.c:651
+msgid ""
+"This program is not intended to be run as root (unless --system is "
+"specified)."
+msgstr "不应以 root 身份运行本程序(除非指定 --system 参数)。"
+
+#: ../src/daemon/main.c:654
+msgid "Root privileges required."
+msgstr "需要 root 权限。"
+
+#: ../src/daemon/main.c:661
+msgid "--start not supported for system instances."
+msgstr "系统实例不支持 --start 参数。"
+
+#: ../src/daemon/main.c:701
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr "用户配置的服务器 %s,拒绝启动/自动派生。"
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid ""
+"User-configured server at %s, which appears to be local. Probing deeper."
+msgstr "用户配置的服务器 %s,看起来是本地服务器。正在进一步探测。"
+
+#: ../src/daemon/main.c:712
+msgid "Running in system mode, but --disallow-exit not set."
+msgstr "正在以系统模式运行,但未设定 --disallow-exit。"
+
+#: ../src/daemon/main.c:715
+msgid "Running in system mode, but --disallow-module-loading not set."
+msgstr "正在以系统模式运行,但未设定 --disallow-module-loading。"
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, forcibly disabling SHM mode."
+msgstr "正在以系统模式运行,强制禁用 SHM 模式!"
+
+#: ../src/daemon/main.c:723
+msgid "Running in system mode, forcibly disabling exit idle time."
+msgstr "正在以系统模式运行,强制禁用退出空闲时间!"
+
+#: ../src/daemon/main.c:756
+msgid "Failed to acquire stdio."
+msgstr "获取 stdio 失败。"
+
+#: ../src/daemon/main.c:762 ../src/daemon/main.c:833
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() 失败:%s"
+
+#: ../src/daemon/main.c:767 ../src/daemon/main.c:838
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() 失败:%s"
+
+#: ../src/daemon/main.c:782 ../src/daemon/main.c:853 ../src/utils/pacat.c:568
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() 失败:%s"
+
+#: ../src/daemon/main.c:788
+msgid "Daemon startup failed."
+msgstr "守护程序启动失败。"
+
+#: ../src/daemon/main.c:821
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() 失败:%s"
+
+#: ../src/daemon/main.c:948
+msgid "Failed to get machine ID"
+msgstr "获取机器 ID 失败"
+
+#: ../src/daemon/main.c:974
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"好的,那么您现在是在系统模式中运行 PA。请注意:您很可能不应该这样做。\n"
+"如果您坚持这么做,请做好接受意外情况的准备。\n"
+"请阅读 http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/"
+"User/WhatIsWrongWithSystemWide/ 以了解为什么系统模式通常不是个好主意。"
+
+#: ../src/daemon/main.c:991
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() 失败。"
+
+#: ../src/daemon/main.c:1021
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() 失败。"
+
+#: ../src/daemon/main.c:1089
+msgid "Failed to initialize daemon."
+msgstr "初始化守护进程失败。"
+
+#: ../src/daemon/main.c:1094
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "守护进程未加载任何负载模块,拒绝工作。"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio 声音系统"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "启动 PulseAudio 声音系统"
+
+#: ../src/modules/alsa/alsa-mixer.c:2378
+msgid "Input"
+msgstr "输入"
+
+#: ../src/modules/alsa/alsa-mixer.c:2379
+msgid "Docking Station Input"
+msgstr "扩展坞输入"
+
+#: ../src/modules/alsa/alsa-mixer.c:2380
+msgid "Docking Station Microphone"
+msgstr "扩展坞话筒"
+
+#: ../src/modules/alsa/alsa-mixer.c:2381
+msgid "Docking Station Line In"
+msgstr "扩展坞线输入"
+
+#: ../src/modules/alsa/alsa-mixer.c:2382 ../src/modules/alsa/alsa-mixer.c:2467
+msgid "Line In"
+msgstr "输入插孔"
+
+#: ../src/modules/alsa/alsa-mixer.c:2383 ../src/modules/alsa/alsa-mixer.c:2461
+#: ../src/modules/bluetooth/module-bluez4-device.c:2099
+#: ../src/modules/bluetooth/module-bluez5-device.c:1710
+msgid "Microphone"
+msgstr "话筒"
+
+#: ../src/modules/alsa/alsa-mixer.c:2384 ../src/modules/alsa/alsa-mixer.c:2462
+msgid "Front Microphone"
+msgstr "前麦克风"
+
+#: ../src/modules/alsa/alsa-mixer.c:2385 ../src/modules/alsa/alsa-mixer.c:2463
+msgid "Rear Microphone"
+msgstr "后麦克风"
+
+#: ../src/modules/alsa/alsa-mixer.c:2386
+msgid "External Microphone"
+msgstr "外部话筒"
+
+#: ../src/modules/alsa/alsa-mixer.c:2387 ../src/modules/alsa/alsa-mixer.c:2465
+msgid "Internal Microphone"
+msgstr "内部话筒"
+
+#: ../src/modules/alsa/alsa-mixer.c:2388 ../src/modules/alsa/alsa-mixer.c:2468
+msgid "Radio"
+msgstr "无线电"
+
+#: ../src/modules/alsa/alsa-mixer.c:2389 ../src/modules/alsa/alsa-mixer.c:2469
+msgid "Video"
+msgstr "视频"
+
+#: ../src/modules/alsa/alsa-mixer.c:2390
+msgid "Automatic Gain Control"
+msgstr "自动增益控制"
+
+#: ../src/modules/alsa/alsa-mixer.c:2391
+msgid "No Automatic Gain Control"
+msgstr "无自动增益控制"
+
+#: ../src/modules/alsa/alsa-mixer.c:2392
+msgid "Boost"
+msgstr "增强"
+
+#: ../src/modules/alsa/alsa-mixer.c:2393
+msgid "No Boost"
+msgstr "无增强"
+
+#: ../src/modules/alsa/alsa-mixer.c:2394
+msgid "Amplifier"
+msgstr "功放"
+
+#: ../src/modules/alsa/alsa-mixer.c:2395
+msgid "No Amplifier"
+msgstr "无功放"
+
+#: ../src/modules/alsa/alsa-mixer.c:2396
+msgid "Bass Boost"
+msgstr "重低音增强"
+
+#: ../src/modules/alsa/alsa-mixer.c:2397
+msgid "No Bass Boost"
+msgstr "无重低音增强"
+
+#: ../src/modules/alsa/alsa-mixer.c:2398
+#: ../src/modules/bluetooth/module-bluez4-device.c:2104
+#: ../src/modules/bluetooth/module-bluez5-device.c:1717
+msgid "Speaker"
+msgstr "扬声器"
+
+#: ../src/modules/alsa/alsa-mixer.c:2399 ../src/modules/alsa/alsa-mixer.c:2471
+msgid "Headphones"
+msgstr "模拟耳机"
+
+#: ../src/modules/alsa/alsa-mixer.c:2460
+msgid "Analog Input"
+msgstr "模拟输入"
+
+#: ../src/modules/alsa/alsa-mixer.c:2464
+msgid "Dock Microphone"
+msgstr "扩展坞麦克风"
+
+#: ../src/modules/alsa/alsa-mixer.c:2466
+msgid "Headset Microphone"
+msgstr "头挂麦克风"
+
+#: ../src/modules/alsa/alsa-mixer.c:2470
+msgid "Analog Output"
+msgstr "模拟输出"
+
+#: ../src/modules/alsa/alsa-mixer.c:2472
+msgid "LFE on Separate Mono Output"
+msgstr "模拟输出(LFE)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2473
+msgid "Line Out"
+msgstr "线缆输出"
+
+#: ../src/modules/alsa/alsa-mixer.c:2474
+msgid "Analog Mono Output"
+msgstr "模拟单声道输出"
+
+#: ../src/modules/alsa/alsa-mixer.c:2475
+msgid "Speakers"
+msgstr "扬声器"
+
+#: ../src/modules/alsa/alsa-mixer.c:2476
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2477
+msgid "Digital Output (S/PDIF)"
+msgstr "数字输出 (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2478
+msgid "Digital Input (S/PDIF)"
+msgstr "数字输入 (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2479
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "数字直通 (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2480
+msgid "Multichannel Input"
+msgstr "多声道输入"
+
+#: ../src/modules/alsa/alsa-mixer.c:2481
+msgid "Multichannel Output"
+msgstr "多声道输出"
+
+#: ../src/modules/alsa/alsa-mixer.c:3990
+msgid "Analog Mono"
+msgstr "模拟单声道"
+
+#. Note: Not translated to "Analog Stereo Input", because the source
+#. * name gets "Input" appended to it automatically, so adding "Input"
+#. * here would lead to the source name to become "Analog Stereo Input
+#. * Input". The same logic applies to analog-stereo-output,
+#. * multichannel-input and multichannel-output.
+#: ../src/modules/alsa/alsa-mixer.c:3991 ../src/modules/alsa/alsa-mixer.c:3997
+#: ../src/modules/alsa/alsa-mixer.c:3998
+msgid "Analog Stereo"
+msgstr "模拟立体声"
+
+#: ../src/modules/alsa/alsa-mixer.c:3999 ../src/modules/alsa/alsa-mixer.c:4000
+msgid "Multichannel"
+msgstr "多声道"
+
+#: ../src/modules/alsa/alsa-mixer.c:4001
+msgid "Analog Surround 2.1"
+msgstr "模拟环绕 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4002
+msgid "Analog Surround 3.0"
+msgstr "模拟环绕 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4003
+msgid "Analog Surround 3.1"
+msgstr "模拟环绕 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4004
+msgid "Analog Surround 4.0"
+msgstr "模拟环绕 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+msgid "Analog Surround 4.1"
+msgstr "模拟环绕 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4006
+msgid "Analog Surround 5.0"
+msgstr "模拟环绕 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4007
+msgid "Analog Surround 5.1"
+msgstr "模拟环绕 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4008
+msgid "Analog Surround 6.0"
+msgstr "模拟环绕 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4009
+msgid "Analog Surround 6.1"
+msgstr "模拟环绕 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4010
+msgid "Analog Surround 7.0"
+msgstr "模拟环绕 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:4011
+msgid "Analog Surround 7.1"
+msgstr "模拟环绕 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:4012
+msgid "Digital Stereo (IEC958)"
+msgstr "数字立体声(IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4013
+msgid "Digital Passthrough  (IEC958)"
+msgstr "数字直通(IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4014
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "数字环绕 4.0(IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4015
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "数字环绕 5.1(IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4016
+msgid "Digital Surround 5.1 (IEC958/DTS)"
+msgstr "数字环绕 5.1(IEC958/DTS)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4017
+msgid "Digital Stereo (HDMI)"
+msgstr "数字立体声(HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4018
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "数字环绕 5.1(HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4151
+msgid "Analog Mono Duplex"
+msgstr "模拟单声道双工"
+
+#: ../src/modules/alsa/alsa-mixer.c:4152
+msgid "Analog Stereo Duplex"
+msgstr "模拟立体声双工"
+
+#: ../src/modules/alsa/alsa-mixer.c:4153
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "数字立体声双工(IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:4154
+msgid "Multichannel Duplex"
+msgstr "多声道双工"
+
+#: ../src/modules/alsa/alsa-mixer.c:4155
+#: ../src/modules/alsa/module-alsa-card.c:186
+#: ../src/modules/bluetooth/module-bluez4-device.c:2295
+#: ../src/modules/bluetooth/module-bluez5-device.c:1941
+msgid "Off"
+msgstr "关"
+
+#: ../src/modules/alsa/alsa-mixer.c:4254
+#, c-format
+msgid "%s Output"
+msgstr "%s 输出"
+
+#: ../src/modules/alsa/alsa-mixer.c:4262
+#, c-format
+msgid "%s Input"
+msgstr "%s 输入"
+
+#: ../src/modules/alsa/alsa-sink.c:570
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA 提醒我们在该设备中写入新数据,但实际上没有什么可以写入的!\n"
+"这很可能是 ALSA 驱动程序 '%s' 中的一个 bug。请向 ALSA 开发人员报告这个问"
+"题。\n"
+"我们因 POLLOUT 被设置而唤醒 -- 但结果是 snd_pcm_avail() 返回 0 或者另一个小于"
+"最小可用值的数值。"
+
+#: ../src/modules/alsa/alsa-sink.c:747
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually "
+"nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA 提醒我们在该设备中写入新数据,但实际上没有什么可以写入的!\n"
+"这很可能是 ALSA 驱动程序 %s 中的一个 bug。请向 ALSA 开发人员报告这个问题。\n"
+"提醒我们设置 POLLOUT - 但结果是 snd_pcm_avail() 返回 0 或者另一个小于最小可用"
+"值的数值。"
+
+#: ../src/modules/alsa/alsa-source.c:529
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA 提醒我们从该设备中读取新数据,但实际上没有什么可以读取的!\n"
+"这很可能是 ALSA 驱动程序 '%s' 中的一个 bug。请向 ALSA 开发人员报告这个问"
+"题。\n"
+"我们因 POLLOUT 被设置而唤醒 -- 但结果是 snd_pcm_avail() 返回 0 或者另一个小于"
+"最小可用值的数值。"
+
+#: ../src/modules/alsa/alsa-source.c:680
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually "
+"nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() "
+"returned 0 or another value < min_avail."
+msgstr ""
+"ALSA 提醒我们从该设备中读取新数据,但实际上没有什么可以读取的!\n"
+"这很可能是 ALSA 驱动程序 %s 中的一个 bug。请向 ALSA 开发人员报告这个问题。\n"
+"提醒我们设置 POLLIN - 但结果是 snd_pcm_avail() 返回 0 或者另一个小于最小可用"
+"值的数值。"
+
+#: ../src/modules/alsa/alsa-util.c:1166 ../src/modules/alsa/alsa-util.c:1241
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() 返回的值非常大:%lu 字节(%lu 毫秒)。\n"
+"这很可能是由 ALSA 驱动程序 %s 的缺陷导致的。请向 ALSA 开发者报告这个问题。"
+
+#: ../src/modules/alsa/alsa-util.c:1216
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() 返回的值非常大:%li 字节(%s%lu 毫秒)。\n"
+"这很可能是由 ALSA 驱动程序 %s 的缺陷导致的。请向 ALSA 开发者报告这个问题。"
+
+#: ../src/modules/alsa/alsa-util.c:1257
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() 返回的值非常很奇怪:延迟 %lu 小于可用 (avail) %lu。\n"
+"很可能是 ALSA 驱动程序 '%s' 中的 bug。请向 ALSA 开发者举报这个问题。"
+
+#: ../src/modules/alsa/alsa-util.c:1300
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() 返回的值非常大:%lu 字节(%lu ms)。\n"
+"这很可能是由 ALSA 驱动程序 %s 中的 bug。请向 ALSA 开发者举报这个问题。"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2089
+#: ../src/modules/bluetooth/module-bluez5-device.c:1700
+msgid "Headset"
+msgstr "耳机"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2094
+#: ../src/modules/bluetooth/module-bluez5-device.c:1705
+msgid "Handsfree"
+msgstr "免手操作"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2109
+#: ../src/modules/bluetooth/module-bluez5-device.c:1723
+msgid "Headphone"
+msgstr "头戴耳机"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2114
+#: ../src/modules/bluetooth/module-bluez5-device.c:1728
+msgid "Portable"
+msgstr "便携式"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2119
+#: ../src/modules/bluetooth/module-bluez5-device.c:1733
+msgid "Car"
+msgstr "车内"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2124
+#: ../src/modules/bluetooth/module-bluez5-device.c:1738
+msgid "HiFi"
+msgstr "高保真"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2129
+#: ../src/modules/bluetooth/module-bluez5-device.c:1743
+msgid "Phone"
+msgstr "电话"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2137
+#: ../src/modules/bluetooth/module-bluez5-device.c:1695
+#: ../src/modules/bluetooth/module-bluez5-device.c:1711
+#: ../src/modules/bluetooth/module-bluez5-device.c:1749
+msgid "Bluetooth Output"
+msgstr "蓝牙输出"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2140
+#: ../src/modules/bluetooth/module-bluez5-device.c:1694
+#: ../src/modules/bluetooth/module-bluez5-device.c:1716
+#: ../src/modules/bluetooth/module-bluez5-device.c:1722
+#: ../src/modules/bluetooth/module-bluez5-device.c:1748
+msgid "Bluetooth Input"
+msgstr "蓝牙输入"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2176
+msgid "High Fidelity Playback (A2DP)"
+msgstr "高保真回放(A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2187
+msgid "High Fidelity Capture (A2DP)"
+msgstr "高保真采集(A2DP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2198
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "双工电话(HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez4-device.c:2210
+msgid "Handsfree Gateway"
+msgstr "蓝牙音频网关"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1786
+msgid "High Fidelity Playback (A2DP Sink)"
+msgstr "高保真回放 (A2DP 信宿)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1797
+msgid "High Fidelity Capture (A2DP Source)"
+msgstr "高保真采集(A2DP 信源)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1808
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr "头戴式耳机单元 (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluez5-device.c:1820
+msgid "Headset Audio Gateway (HSP/HFP)"
+msgstr "头戴式音频网关 (HSP/HFP)"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:61
+msgid ""
+"source_name=<name for the source> source_properties=<properties for the "
+"source> source_master=<name of source to filter> sink_name=<name for the "
+"sink> sink_properties=<properties for the sink> sink_master=<name of sink to "
+"filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how "
+"much drift to readjust after in ms> format=<sample format> rate=<sample "
+"rate> channels=<number of channels> channel_map=<channel map> "
+"aec_method=<implementation to use> aec_args=<parameters for the AEC engine> "
+"save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being "
+"loaded automatically> use_volume_sharing=<yes or no> use_master_format=<yes "
+"or no> "
+msgstr ""
+"source_name=<信源名称> source_properties=<信源属性> source_master=<工作信源名"
+"称> sink_name=<信宿名称> sink_properties=<信宿属性> sink_master=<工作信宿名称"
+"> adjust_time=<多少秒重新调整一次频率> adjust_threshold=<重新调整时的偏移量阀"
+"值 (微秒)> format=<采样格式> rate=<采样率> channels=<声道数> channel_map=<声"
+"道映射> aec_method=<要使用的实现> aec_args=<AEC 引擎的参数> save_aec=<在 /"
+"tmp 下保存 AEC 数据> autoloaded=<若为自动加载则会设置> "
+"use_volume_sharing=<yes 或 no> use_master_format=<yes or no> "
+
+#. add on profile
+#: ../src/modules/macosx/module-coreaudio-device.c:811
+msgid "On"
+msgstr "启用"
+
+#: ../src/modules/module-always-sink.c:36
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "总是保持至少载入一个信宿,即使它并不真实存在"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "伪输出"
+
+#: ../src/modules/module-equalizer-sink.c:70
+msgid "General Purpose Equalizer"
+msgstr "通用均衡器"
+
+#: ../src/modules/module-equalizer-sink.c:74
+msgid ""
+"sink_name=<name of the sink> sink_properties=<properties for the sink> "
+"sink_master=<sink to connect to> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> autoloaded=<set if "
+"this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr ""
+"sink_name=<信宿名称> sink_properties=<信宿属性> sink_master=<要连接的信宿> "
+"format=<采样格式> rate=<采样率> channels=<声道数> channel_map=<声道映射> "
+"autoloaded=<若为自动加载则会设置> use_volume_sharing=<yes 或 no> "
+
+#: ../src/modules/module-filter-apply.c:46
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<是否自动卸载未使用的滤波器?>"
+
+#: ../src/modules/module-ladspa-sink.c:51
+msgid "Virtual LADSPA sink"
+msgstr "虚拟 LDASPA 信宿"
+
+#: ../src/modules/module-ladspa-sink.c:55
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<input channel map> plugin=<ladspa "
+"plugin name> label=<ladspa plugin label> control=<comma separated list of "
+"input control values> input_ladspaport_map=<comma separated list of input "
+"LADSPA port names> output_ladspaport_map=<comma separated list of output "
+"LADSPA port names> "
+msgstr ""
+"sink_name=<信宿名称> sink_properties=<信宿属性> master=<工作信宿名称> "
+"format=<样本格式> rate=<采样率> channels=<声道数> channel_map=<输入声道映射> "
+"plugin=<ladspa 插件名称> label=<ladspa 插件标签> control=<以半角逗号分隔的输"
+"入控制值列表> input_ladspaport_map=<以半角逗号分隔的输入 LADSPA 连接端口名称"
+"列表> output_ladspaport_map=<以半角逗号分隔的输出 LADSPA 连接端口名称列表> "
+
+#: ../src/modules/module-null-sink.c:47
+msgid "Clocked NULL sink"
+msgstr "定时的空信宿"
+
+#: ../src/modules/module-null-sink.c:280
+msgid "Null Output"
+msgstr "空输出"
+
+#: ../src/modules/module-rygel-media-server.c:508
+#: ../src/modules/module-rygel-media-server.c:546
+#: ../src/modules/module-rygel-media-server.c:905
+msgid "Output Devices"
+msgstr "输出设备"
+
+#: ../src/modules/module-rygel-media-server.c:509
+#: ../src/modules/module-rygel-media-server.c:547
+#: ../src/modules/module-rygel-media-server.c:906
+msgid "Input Devices"
+msgstr "输入设备"
+
+#: ../src/modules/module-rygel-media-server.c:1063
+msgid "Audio on @HOSTNAME@"
+msgstr "@HOSTNAME@ 中的音频"
+
+#. TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus'
+#. TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus'
+#: ../src/modules/module-tunnel-sink-new.c:307
+#: ../src/modules/module-tunnel-source-new.c:305
+#, c-format
+msgid "Tunnel for %s@%s"
+msgstr "%s@%s 的通道"
+
+#: ../src/modules/module-tunnel-sink-new.c:517
+#: ../src/modules/module-tunnel-source-new.c:516
+#, c-format
+msgid "Tunnel to %s/%s"
+msgstr "到远程信宿 %s/%s 的通道"
+
+#: ../src/modules/module-virtual-surround-sink.c:47
+msgid "Virtual surround sink"
+msgstr "虚拟环绕声信宿"
+
+#: ../src/modules/module-virtual-surround-sink.c:51
+msgid ""
+"sink_name=<name for the sink> sink_properties=<properties for the sink> "
+"master=<name of sink to filter> format=<sample format> rate=<sample rate> "
+"channels=<number of channels> channel_map=<channel map> "
+"use_volume_sharing=<yes or no> force_flat_volume=<yes or no> hrir=/path/to/"
+"left_hrir.wav "
+msgstr ""
+"sink_name=<信宿名称> sink_properties=<信宿属性> master=<工作信宿名> format=<"
+"采样格式> rate=<采样率> channels=<声道数> channel_map=<声道映射> "
+"use_volume_sharing=<yes 或 no> force_flat_volume=<yes 或 no> hrir=/path/to/"
+"left_hrir.wav "
+
+#: ../src/modules/reserve-wrap.c:149
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio 声音服务器"
+
+#: ../src/pulse/channelmap.c:103 ../src/pulse/channelmap.c:771
+msgid "Mono"
+msgstr "单声道"
+
+#: ../src/pulse/channelmap.c:105
+msgid "Front Center"
+msgstr "正前"
+
+#: ../src/pulse/channelmap.c:106
+msgid "Front Left"
+msgstr "左前"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Right"
+msgstr "右前"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Rear Center"
+msgstr "正后"
+
+#: ../src/pulse/channelmap.c:110
+msgid "Rear Left"
+msgstr "左后"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Right"
+msgstr "右后"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Subwoofer"
+msgstr "低音音箱"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Front Left-of-center"
+msgstr "前左中央"
+
+#: ../src/pulse/channelmap.c:116
+msgid "Front Right-of-center"
+msgstr "前右中央"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Side Left"
+msgstr "左侧"
+
+#: ../src/pulse/channelmap.c:119
+msgid "Side Right"
+msgstr "右侧"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Auxiliary 0"
+msgstr "辅助 0"
+
+#: ../src/pulse/channelmap.c:122
+msgid "Auxiliary 1"
+msgstr "辅助 1"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 2"
+msgstr "辅助 2"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 3"
+msgstr "辅助 3"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 4"
+msgstr "辅助 4"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 5"
+msgstr "辅助 5"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 6"
+msgstr "辅助 6"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 7"
+msgstr "辅助 7"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 8"
+msgstr "辅助 7"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 9"
+msgstr "辅助 9"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 10"
+msgstr "辅助 10"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 11"
+msgstr "辅助 11"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 12"
+msgstr "辅助 12"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 13"
+msgstr "辅助 13"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 14"
+msgstr "辅助 14"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 15"
+msgstr "辅助 15"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 16"
+msgstr "辅助 16"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 17"
+msgstr "辅助 17"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 18"
+msgstr "辅助 18"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 19"
+msgstr "辅助 19"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 20"
+msgstr "辅助 20"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 21"
+msgstr "辅助 21"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 22"
+msgstr "辅助 22"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 23"
+msgstr "辅助 23"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 24"
+msgstr "辅助 24"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 25"
+msgstr "辅助 25"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 26"
+msgstr "辅助 26"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 27"
+msgstr "辅助 27"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 28"
+msgstr "辅助 28"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 29"
+msgstr "辅助 29"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 30"
+msgstr "辅助 30"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 31"
+msgstr "辅助 31"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Top Center"
+msgstr "上中"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Front Center"
+msgstr "上中前"
+
+#: ../src/pulse/channelmap.c:157
+msgid "Top Front Left"
+msgstr "上左前"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Right"
+msgstr "上右前"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Rear Center"
+msgstr "上中后"
+
+#: ../src/pulse/channelmap.c:161
+msgid "Top Rear Left"
+msgstr "上左后"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Right"
+msgstr "上右后"
+
+#: ../src/pulse/channelmap.c:479 ../src/pulse/format.c:127
+#: ../src/pulse/sample.c:175 ../src/pulse/volume.c:294
+#: ../src/pulse/volume.c:320 ../src/pulse/volume.c:340
+#: ../src/pulse/volume.c:372 ../src/pulse/volume.c:412
+#: ../src/pulse/volume.c:431
+msgid "(invalid)"
+msgstr "(无效)"
+
+#: ../src/pulse/channelmap.c:775
+msgid "Stereo"
+msgstr "立体声"
+
+#: ../src/pulse/channelmap.c:780
+msgid "Surround 4.0"
+msgstr "环绕 4.0"
+
+#: ../src/pulse/channelmap.c:786
+msgid "Surround 4.1"
+msgstr "环绕 4.1"
+
+#: ../src/pulse/channelmap.c:792
+msgid "Surround 5.0"
+msgstr "环绕 5.0"
+
+#: ../src/pulse/channelmap.c:798
+msgid "Surround 5.1"
+msgstr "环绕 5.1"
+
+#: ../src/pulse/channelmap.c:805
+msgid "Surround 7.1"
+msgstr "环绕 7.1"
+
+#: ../src/pulse/client-conf-x11.c:53 ../src/utils/pax11publish.c:97
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() 失败"
+
+#: ../src/pulse/client-conf-x11.c:58 ../src/utils/pax11publish.c:102
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() 返回真"
+
+#: ../src/pulse/client-conf-x11.c:94
+msgid "Failed to parse cookie data"
+msgstr "cookie 数据解析失败"
+
+#: ../src/pulse/context.c:660
+#, c-format
+msgid "fork(): %s"
+msgstr "fork():%s"
+
+#: ../src/pulse/context.c:715
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid():%s"
+
+#: ../src/pulse/context.c:1421
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "收到未知扩展 %s 的信息"
+
+#: ../src/pulse/direction.c:37
+msgid "input"
+msgstr "输入"
+
+#: ../src/pulse/direction.c:39
+msgid "output"
+msgstr "输出"
+
+#: ../src/pulse/direction.c:41
+msgid "bidirectional"
+msgstr "双向"
+
+#: ../src/pulse/direction.c:43
+msgid "invalid"
+msgstr "无效"
+
+#: ../src/pulsecore/core-util.c:1836
+#, c-format
+msgid ""
+"XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! (This could "
+"e g happen if you try to connect to a non-root PulseAudio as a root user, "
+"over the native protocol. Don't do that.)"
+msgstr ""
+"XDG_RUNTIME_DIR (%s) 不属于本进程 (uid %d),而属于 uid %d 号进程! (这可能是在"
+"原生协议下通过 root 用户连接一个非 root 用户的 PulseAudio 导致的,请不要这样"
+"做。)"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "yes"
+msgstr "是"
+
+#: ../src/pulsecore/core-util.h:97
+msgid "no"
+msgstr "否"
+
+#: ../src/pulsecore/lock-autospawn.c:141 ../src/pulsecore/lock-autospawn.c:227
+msgid "Cannot access autospawn lock."
+msgstr "不能访问自动执行锁。"
+
+#: ../src/pulsecore/log.c:165
+#, c-format
+msgid "Failed to open target file '%s'."
+msgstr "打开目标文件 %s 失败。"
+
+#: ../src/pulsecore/log.c:188
+#, c-format
+msgid ""
+"Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."
+msgstr "尝试打开目标文件 '%s','%s.1','%s.2'…'%s.%d',但均失败。"
+
+#: ../src/pulsecore/log.c:651
+msgid "Invalid log target."
+msgstr "无效的日志目标。"
+
+#: ../src/pulsecore/sink.c:3460
+msgid "Built-in Audio"
+msgstr "内置音频"
+
+#: ../src/pulsecore/sink.c:3465
+msgid "Modem"
+msgstr "调制解调器"
+
+#: ../src/pulse/error.c:38
+msgid "OK"
+msgstr "确定"
+
+#: ../src/pulse/error.c:39
+msgid "Access denied"
+msgstr "拒绝访问"
+
+#: ../src/pulse/error.c:40
+msgid "Unknown command"
+msgstr "未知命令"
+
+#: ../src/pulse/error.c:41
+msgid "Invalid argument"
+msgstr "无效参数"
+
+#: ../src/pulse/error.c:42
+msgid "Entity exists"
+msgstr "实体存在"
+
+#: ../src/pulse/error.c:43
+msgid "No such entity"
+msgstr "无此实体"
+
+#: ../src/pulse/error.c:44
+msgid "Connection refused"
+msgstr "拒绝连接"
+
+#: ../src/pulse/error.c:45
+msgid "Protocol error"
+msgstr "协议错误"
+
+#: ../src/pulse/error.c:46
+msgid "Timeout"
+msgstr "超时"
+
+#: ../src/pulse/error.c:47
+msgid "No authentication key"
+msgstr "没有授权密钥"
+
+#: ../src/pulse/error.c:48
+msgid "Internal error"
+msgstr "内部错误"
+
+#: ../src/pulse/error.c:49
+msgid "Connection terminated"
+msgstr "连接终止"
+
+#: ../src/pulse/error.c:50
+msgid "Entity killed"
+msgstr "实体已被杀死"
+
+#: ../src/pulse/error.c:51
+msgid "Invalid server"
+msgstr "无效服务器"
+
+#: ../src/pulse/error.c:52
+msgid "Module initialization failed"
+msgstr "模块初始化失败"
+
+#: ../src/pulse/error.c:53
+msgid "Bad state"
+msgstr "错误状态"
+
+#: ../src/pulse/error.c:54
+msgid "No data"
+msgstr "无数据"
+
+#: ../src/pulse/error.c:55
+msgid "Incompatible protocol version"
+msgstr "不兼容的协议版本"
+
+#: ../src/pulse/error.c:56
+msgid "Too large"
+msgstr "太大"
+
+#: ../src/pulse/error.c:57
+msgid "Not supported"
+msgstr "不支持"
+
+#: ../src/pulse/error.c:58
+msgid "Unknown error code"
+msgstr "未知错误码"
+
+#: ../src/pulse/error.c:59
+msgid "No such extension"
+msgstr "没有该扩展"
+
+#: ../src/pulse/error.c:60
+msgid "Obsolete functionality"
+msgstr "废弃的功能"
+
+#: ../src/pulse/error.c:61
+msgid "Missing implementation"
+msgstr "缺少实现"
+
+#: ../src/pulse/error.c:62
+msgid "Client forked"
+msgstr "客户端分支"
+
+#: ../src/pulse/error.c:63
+msgid "Input/Output error"
+msgstr "输入/输出错误"
+
+#: ../src/pulse/error.c:64
+msgid "Device or resource busy"
+msgstr "设备或者资源忙"
+
+#: ../src/pulse/sample.c:177
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:191
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:193
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:195
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/utils/pacat.c:117
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "排空流失败:%s"
+
+#: ../src/utils/pacat.c:122
+msgid "Playback stream drained."
+msgstr "回放流枯竭。"
+
+#: ../src/utils/pacat.c:133
+msgid "Draining connection to server."
+msgstr "到服务器的 Draining 连接。"
+
+#: ../src/utils/pacat.c:146
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain(): %s"
+
+#: ../src/utils/pacat.c:169
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() 失败:%s"
+
+#: ../src/utils/pacat.c:210
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() 失败:%s"
+
+#: ../src/utils/pacat.c:260 ../src/utils/pacat.c:290
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() 失败:%s"
+
+#: ../src/utils/pacat.c:340
+msgid "Stream successfully created."
+msgstr "成功创建流。"
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() 失败:%s"
+
+#: ../src/utils/pacat.c:347
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "缓冲量度:maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:350
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "缓冲量度:maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:354
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "正在使用采样规格 %s,声道映射 %s。"
+
+#: ../src/utils/pacat.c:358
+#, c-format
+msgid "Connected to device %s (index: %u, suspended: %s)."
+msgstr "已连接至设备 %s (索引: %u,已挂起: %s)。"
+
+#: ../src/utils/pacat.c:368
+#, c-format
+msgid "Stream error: %s"
+msgstr "流错误:%s"
+
+#: ../src/utils/pacat.c:378
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "流设备挂起。%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "流设备恢复。%s"
+
+#: ../src/utils/pacat.c:388
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "流欠载运行。%s"
+
+#: ../src/utils/pacat.c:395
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "流超限运行。%s"
+
+#: ../src/utils/pacat.c:402
+#, c-format
+msgid "Stream started.%s"
+msgstr "流已启动。%s"
+
+#: ../src/utils/pacat.c:409
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "流移至设备 %s (%u,%s 挂起)。%s"
+
+#: ../src/utils/pacat.c:409
+msgid "not "
+msgstr "非 "
+
+#: ../src/utils/pacat.c:416
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "更改流缓冲属性。%s"
+
+#: ../src/utils/pacat.c:431
+msgid "Cork request stack is empty: corking stream"
+msgstr "抑制请求列表为空:正在抑制音频流"
+
+#: ../src/utils/pacat.c:437
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "抑制请求列表为空:正在反抑制音频流"
+
+#: ../src/utils/pacat.c:441
+msgid "Warning: Received more uncork requests than cork requests."
+msgstr "警告:收到比抑制请求更多的反抑制请求!"
+
+#: ../src/utils/pacat.c:466
+#, c-format
+msgid "Connection established.%s"
+msgstr "连接已建立。%s"
+
+#: ../src/utils/pacat.c:469
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() 失败:%s"
+
+#: ../src/utils/pacat.c:507
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() 失败:%s"
+
+#: ../src/utils/pacat.c:513
+#, c-format
+msgid "Failed to set monitor stream: %s"
+msgstr "设置监视器流失败: %s"
+
+#: ../src/utils/pacat.c:517
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() 失败:%s"
+
+#: ../src/utils/pacat.c:530 ../src/utils/pactl.c:1446
+#, c-format
+msgid "Connection failure: %s"
+msgstr "连接失败:%s"
+
+#: ../src/utils/pacat.c:563
+msgid "Got EOF."
+msgstr "到达 EOF。"
+
+#: ../src/utils/pacat.c:600
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() 失败:%s"
+
+#: ../src/utils/pacat.c:621
+msgid "Got signal, exiting."
+msgstr "收到信号,退出。"
+
+#: ../src/utils/pacat.c:635
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "获取传输延迟失败:%s"
+
+#: ../src/utils/pacat.c:640
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "时间:%0.3f 秒;延迟:%0.0f 微秒。"
+
+#: ../src/utils/pacat.c:661
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() 失败:%s"
+
+#: ../src/utils/pacat.c:671
+#, c-format
+msgid ""
+"%s [options]\n"
+"%s\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to "
+"connect to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+"      --stream-name=NAME                How to call this stream on the "
+"server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume "
+"in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to "
+"44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, "
+"s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, "
+"s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to "
+"s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, "
+"2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the "
+"default\n"
+"      --fix-format                      Take the sample format from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink/"
+"source the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the "
+"channel map\n"
+"                                        from the sink/source the stream is "
+"being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of "
+"name.\n"
+"      --latency=BYTES                   Request the specified latency in "
+"bytes.\n"
+"      --process-time=BYTES              Request the specified process time "
+"per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in "
+"msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time "
+"per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the "
+"specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     Passthrough data.\n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+"      --monitor-stream=INDEX            Record from the sink input with "
+"index INDEX.\n"
+msgstr ""
+"%s [选项]\n"
+"%s\n"
+"  -h, --help                        显示此帮助\n"
+"      --version                     显示版本\n"
+"\n"
+"  -r, --record                      为录制创建连接\n"
+"  -p, --playback                    为回放创建连接\n"
+"\n"
+"  -v, --verbose                     启用详述操作\n"
+"\n"
+"  -s, --server=服务器               要连接的服务器名\n"
+"  -d, --device=设备名               要连接的信宿/信源名称\n"
+"  -n, --client-name=名称            如何在服务器中调用此客户端\n"
+"      --stream-name=名称            如何在服务器中调用这个流\n"
+"      --volume=音量                 指定初始(线性)音量,取值在0...65536之"
+"间\n"
+"      --rate=SAMPLERATE             采样频率(单位 Hz,默认为44100)\n"
+"      --format=SAMPLEFORMAT         采样类型,s16le、s16be、u8、float32le 之"
+"一\n"
+"                                    float32be、ulaw、alaw、s32le、s32be 中取"
+"(默认为 s16ne)\n"
+"      --channels=CHANNELS           通道数,1为单声道,2为立体声(默认为2)\n"
+"      --channel-map=CHANNELMAP      取代默认值的通道映射表\n"
+"      --fix-format                  从流连接的信宿中提取采样格式。\n"
+"      --fix-rate                    从流连接的信宿中提取采样率。\n"
+"      --fix-channels                从流连接的信宿中提取通道数和通道映射"
+"表。\n"
+"      --no-remix                    不要对通道进行 upmix 或者 downmix 操"
+"作。\n"
+"      --no-remap                    根据下标而非名称来映射通道。\n"
+"      --latency=BYTES               请求指定字节数的延迟。\n"
+"      --process-time=BYTES          每次请求指定字节数的处理时间。\n"
+
+#: ../src/utils/pacat.c:788
+msgid "Play back encoded audio files on a PulseAudio sound server."
+msgstr "在 PulseAudio 声音服务器回放音频编码文件。"
+
+#: ../src/utils/pacat.c:792
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to a file."
+msgstr "从 PulseAudio 声音服务器获取音频数据并写入文件。"
+
+#: ../src/utils/pacat.c:796
+msgid ""
+"Capture audio data from a PulseAudio sound server and write it to STDOUT or "
+"the specified file."
+msgstr "从 PulseAudio 声音服务器获取音频数据并写入 STDOUT 或指定的文件。"
+
+#: ../src/utils/pacat.c:800
+msgid ""
+"Play back audio data from STDIN or the specified file on a PulseAudio sound "
+"server."
+msgstr "在 PulseAudio 声音服务器回放 STDIN 或指定文件中的音频数据。"
+
+#: ../src/utils/pacat.c:814
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"使用 libpulse %s 编译\n"
+"与 libpulse %s 链接\n"
+
+#: ../src/utils/pacat.c:847 ../src/utils/pactl.c:1648
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "无效的客户端名称 '%s'"
+
+#: ../src/utils/pacat.c:862
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "无效的流名称 '%s'"
+
+#: ../src/utils/pacat.c:899
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "无效的通道映射 '%s'"
+
+#: ../src/utils/pacat.c:928 ../src/utils/pacat.c:942
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "无效的延迟规格 %s'"
+
+#: ../src/utils/pacat.c:935 ../src/utils/pacat.c:949
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "无效的处理时间规格 '%s'"
+
+#: ../src/utils/pacat.c:961
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "无效的属性 %s"
+
+#: ../src/utils/pacat.c:980
+#, c-format
+msgid "Unknown file format %s."
+msgstr "未知文件格式 %s。"
+
+#: ../src/utils/pacat.c:995
+msgid "Failed to parse the argument for --monitor-stream"
+msgstr "--monitor-stream 的参数解析失败"
+
+#: ../src/utils/pacat.c:1006
+msgid "Invalid sample specification"
+msgstr "无效的采样规格"
+
+#: ../src/utils/pacat.c:1016
+#, c-format
+msgid "open(): %s"
+msgstr "open():%s"
+
+#: ../src/utils/pacat.c:1021
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2():%s"
+
+#: ../src/utils/pacat.c:1028
+msgid "Too many arguments."
+msgstr "参数过多。"
+
+#: ../src/utils/pacat.c:1039
+msgid "Failed to generate sample specification for file."
+msgstr "为文件生成采样规格失败。"
+
+#: ../src/utils/pacat.c:1065
+msgid "Failed to open audio file."
+msgstr "打开声音文件失败。"
+
+#: ../src/utils/pacat.c:1071
+msgid ""
+"Warning: specified sample specification will be overwritten with "
+"specification from file."
+msgstr "警告:指定的采样规格将覆盖文件中的说明。"
+
+#: ../src/utils/pacat.c:1074 ../src/utils/pactl.c:1712
+msgid "Failed to determine sample specification from file."
+msgstr "从文件中确定采样规格失败。"
+
+#: ../src/utils/pacat.c:1083
+msgid "Warning: Failed to determine channel map from file."
+msgstr "警告:从文件中确定通道映射失败。"
+
+#: ../src/utils/pacat.c:1094
+msgid "Channel map doesn't match sample specification"
+msgstr "通道映射与采样规格不匹配"
+
+#: ../src/utils/pacat.c:1105
+msgid "Warning: failed to write channel map to file."
+msgstr "警告:在文件中写入通道映射失败。"
+
+#: ../src/utils/pacat.c:1120
+#, c-format
+msgid ""
+"Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "使用采样规格 %s 和通道映射 %s 打开 %s 流。"
+
+#: ../src/utils/pacat.c:1121
+msgid "recording"
+msgstr "正在录制"
+
+#: ../src/utils/pacat.c:1121
+msgid "playback"
+msgstr "回放"
+
+#: ../src/utils/pacat.c:1145
+msgid "Failed to set media name."
+msgstr "设置媒体名失败。"
+
+#: ../src/utils/pacat.c:1152 ../src/utils/pactl.c:2062
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() 失败。"
+
+#: ../src/utils/pacat.c:1175
+msgid "io_new() failed."
+msgstr "io_new() 失败。"
+
+#: ../src/utils/pacat.c:1182 ../src/utils/pactl.c:2074
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() 失败。"
+
+#: ../src/utils/pacat.c:1190 ../src/utils/pactl.c:2080
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() 失败:%s"
+
+#: ../src/utils/pacat.c:1196
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() 失败。"
+
+#: ../src/utils/pacat.c:1203 ../src/utils/pactl.c:2085
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() 失败。"
+
+#: ../src/utils/pacmd.c:51 ../src/utils/pactl.c:1570
+msgid "NAME [ARGS ...]"
+msgstr "名称 [参数 ...]"
+
+#: ../src/utils/pacmd.c:52 ../src/utils/pacmd.c:60 ../src/utils/pactl.c:1571
+msgid "NAME|#N"
+msgstr "名称|#N"
+
+#: ../src/utils/pacmd.c:53 ../src/utils/pacmd.c:63 ../src/utils/pactl.c:1569
+#: ../src/utils/pactl.c:1575
+msgid "NAME"
+msgstr "名称"
+
+#: ../src/utils/pacmd.c:54
+msgid "NAME|#N VOLUME"
+msgstr "名称|#N 音量"
+
+#: ../src/utils/pacmd.c:55
+msgid "#N VOLUME"
+msgstr "#N 音量"
+
+#: ../src/utils/pacmd.c:56 ../src/utils/pacmd.c:70 ../src/utils/pactl.c:1573
+msgid "NAME|#N 1|0"
+msgstr "名称|#N 1|0"
+
+#: ../src/utils/pacmd.c:57
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pacmd.c:58
+msgid "NAME|#N KEY=VALUE"
+msgstr "名称|#N 键=值"
+
+#: ../src/utils/pacmd.c:59
+msgid "#N KEY=VALUE"
+msgstr "#N 键=值"
+
+#: ../src/utils/pacmd.c:61
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pacmd.c:62
+msgid "NAME SINK|#N"
+msgstr "名称 信宿|#N"
+
+#: ../src/utils/pacmd.c:64 ../src/utils/pacmd.c:65
+msgid "NAME FILENAME"
+msgstr "名称 文件名"
+
+#: ../src/utils/pacmd.c:66
+msgid "PATHNAME"
+msgstr "路径名"
+
+#: ../src/utils/pacmd.c:67
+msgid "FILENAME SINK|#N"
+msgstr "文件名 信宿|#N"
+
+#: ../src/utils/pacmd.c:69 ../src/utils/pactl.c:1572
+msgid "#N SINK|SOURCE"
+msgstr "#N 信宿|信号源"
+
+#: ../src/utils/pacmd.c:71 ../src/utils/pacmd.c:77 ../src/utils/pacmd.c:78
+msgid "1|0"
+msgstr "1|0"
+
+#: ../src/utils/pacmd.c:72 ../src/utils/pactl.c:1574
+msgid "CARD PROFILE"
+msgstr "声卡配置文件"
+
+#: ../src/utils/pacmd.c:73 ../src/utils/pactl.c:1576
+msgid "NAME|#N PORT"
+msgstr "名称|#N 端口"
+
+#: ../src/utils/pacmd.c:74 ../src/utils/pactl.c:1582
+msgid "CARD-NAME|CARD-#N PORT OFFSET"
+msgstr "声卡名|卡号-#N 端口 偏移量"
+
+#: ../src/utils/pacmd.c:75
+msgid "TARGET"
+msgstr "目标"
+
+#: ../src/utils/pacmd.c:76
+msgid "NUMERIC-LEVEL"
+msgstr "级别 (数字)"
+
+#: ../src/utils/pacmd.c:79
+msgid "FRAMES"
+msgstr "FRAMES"
+
+#: ../src/utils/pacmd.c:81
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"When no command is given pacmd starts in the interactive mode.\n"
+msgstr ""
+"\n"
+"  -h, --help                            显示此帮助\n"
+"     --version                     显示版本\n"
+"不给定命令的话 pacmd 会启动入交互模式。\n"
+
+#: ../src/utils/pacmd.c:128
+#, c-format
+msgid ""
+"pacmd %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacmd %s\n"
+"使用 libpulse %s 编译\n"
+"与 libpulse %s 链接\n"
+
+#: ../src/utils/pacmd.c:142
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "没有 PulseAudio 守护进程在运行,或者没有作为会话守护进程运行。"
+
+#: ../src/utils/pacmd.c:147
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0):%s"
+
+#: ../src/utils/pacmd.c:164
+#, c-format
+msgid "connect(): %s"
+msgstr "connect():%s"
+
+#: ../src/utils/pacmd.c:172
+msgid "Failed to kill PulseAudio daemon."
+msgstr "杀死 PulseAudio 守护程序失败。"
+
+#: ../src/utils/pacmd.c:180
+msgid "Daemon not responding."
+msgstr "守护进程未响应"
+
+#: ../src/utils/pacmd.c:212 ../src/utils/pacmd.c:321 ../src/utils/pacmd.c:339
+#, c-format
+msgid "write(): %s"
+msgstr "write():%s"
+
+#: ../src/utils/pacmd.c:268
+#, c-format
+msgid "poll(): %s"
+msgstr "poll():%s"
+
+#: ../src/utils/pacmd.c:279 ../src/utils/pacmd.c:299
+#, c-format
+msgid "read(): %s"
+msgstr "read():%s"
+
+#: ../src/utils/pactl.c:164
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "获取统计数据失败:%s"
+
+#: ../src/utils/pactl.c:170
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "当前使用:%u 块,总共 %s 字节。\n"
+
+#: ../src/utils/pactl.c:173
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "整个生命周期所得分配:%u 块,总共 %s 字节。\n"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "采样缓存大小:%s\n"
+
+#: ../src/utils/pactl.c:185
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "获取服务器信息失败:%s"
+
+#: ../src/utils/pactl.c:190
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"服务器字串:%s\n"
+"程序库协议版本:%u\n"
+"服务器协议版本:%u\n"
+"是否本地服务器:%s\n"
+"客户端索引:%u\n"
+"区块大小: %zu\n"
+
+#: ../src/utils/pactl.c:206
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"用户名:%s\n"
+"主机名:%s\n"
+"服务器名:%s\n"
+"服务器版本:%s\n"
+"默认采样规格:%s\n"
+"默认声道映射:%s\n"
+"默认音频入口:%s\n"
+"默认信源: %s\n"
+"Cookie:%04x:%04x\n"
+
+#: ../src/utils/pactl.c:255 ../src/utils/pactl.c:900 ../src/utils/pactl.c:978
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "获取音频出口信息失败:%s"
+
+#: ../src/utils/pactl.c:281
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"信宿 #%u\n"
+"\t状态:%s\n"
+"\t名称:%s\n"
+"\t描述:%s\n"
+"\t驱动程序:%s\n"
+"\t采样规格:%s\n"
+"\t声道映射:%s\n"
+"\t所有者模块:%u\n"
+"\t静音:%s\n"
+"\t音量:%s\n"
+"\t        平衡 %0.2f\n"
+"\t基础音量:%s\n"
+"\t监视器信源:%s\n"
+"\t延迟:%0.0f 微秒,设置为 %0.0f 微秒\n"
+"\t标记:%s%s%s%s%s%s%s\n"
+"\t属性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:431 ../src/utils/pactl.c:592
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\t端口:\n"
+
+#: ../src/utils/pactl.c:332 ../src/utils/pactl.c:438
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\t活动端口:%s\n"
+
+#: ../src/utils/pactl.c:338 ../src/utils/pactl.c:444
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\t格式:\n"
+
+#: ../src/utils/pactl.c:362 ../src/utils/pactl.c:920 ../src/utils/pactl.c:993
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "获取音频入口信息失败:%s"
+
+#: ../src/utils/pactl.c:388
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"信源 #%u\n"
+"\t状态:%s\n"
+"\t名称:%s\n"
+"\t描述:%s\n"
+"\t驱动程序:%s\n"
+"\t采样规格:%s\n"
+"\t声道映射:%s\n"
+"\t所有者模块:%u\n"
+"\t静音:%s\n"
+"\t音量:%s\n"
+"\t        平衡 %0.2f\n"
+"\t基础音量:%s\n"
+"\t信宿的监视器:%s\n"
+"\t延迟:%0.0f 微秒,已设置 %0.0f 微秒\n"
+"\t标记:%s%s%s%s%s%s\n"
+"\t属性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:416 ../src/utils/pactl.c:486 ../src/utils/pactl.c:529
+#: ../src/utils/pactl.c:571 ../src/utils/pactl.c:669 ../src/utils/pactl.c:670
+#: ../src/utils/pactl.c:681 ../src/utils/pactl.c:739 ../src/utils/pactl.c:740
+#: ../src/utils/pactl.c:751 ../src/utils/pactl.c:802 ../src/utils/pactl.c:803
+#: ../src/utils/pactl.c:809
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:455 ../src/utils/pactl.c:859
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "获取模块信息失败:%s"
+
+#: ../src/utils/pactl.c:478
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"模块 #%u\n"
+"\t名称:%s\n"
+"\t参数:%s\n"
+"\t使用计数:%s\n"
+"\t属性\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:497
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "获取客户端信息失败:%s"
+
+#: ../src/utils/pactl.c:523
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"客户端 #%u\n"
+"\t驱动:%s\n"
+"\t拥有者模块:%s\n"
+"\t属性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:540
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "获取声卡信息失败:%s"
+
+#: ../src/utils/pactl.c:563
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"卡 #%u\n"
+"\t名称:%s\n"
+"\t驱动:%s\n"
+"\t拥有者模块:%s\n"
+"\t属性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:579
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\t配置文件:\n"
+
+#: ../src/utils/pactl.c:581
+#, c-format
+msgid "\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"
+msgstr "\t\t%s: %s (信宿:%u,信源:%u,优先级:%u,可用性:%s)\n"
+
+#: ../src/utils/pactl.c:586
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\t活动配置:%s\n"
+
+#: ../src/utils/pactl.c:600
+#, c-format
+msgid ""
+"\t\t\tProperties:\n"
+"\t\t\t\t%s\n"
+msgstr ""
+"\t\t\t属性:\n"
+"\t\t\t\t%s\n"
+
+#: ../src/utils/pactl.c:605
+#, c-format
+msgid "\t\t\tPart of profile(s): %s"
+msgstr "\t\t\t属于配置文件:%s"
+
+#: ../src/utils/pactl.c:622 ../src/utils/pactl.c:940 ../src/utils/pactl.c:1008
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "获取音频信宿输入信息失败:%s"
+
+#: ../src/utils/pactl.c:651
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"信宿输入 #%u\n"
+"\t驱动程序:%s\n"
+"\t所有者模块:%s\n"
+"\t客户端:%s\n"
+"\t信宿:%u\n"
+"\t采样规格:%s\n"
+"\t声道映射:%s\n"
+"\t格式:%s\n"
+"\t抑制: %s\n"
+"\t静音:%s\n"
+"\t音量:%s\n"
+"\t        平衡 %0.2f\n"
+"\t缓冲延迟:%0.0f 微秒\n"
+"\t信宿延迟:%0.0f 微秒\n"
+"\t重采样方法:%s\n"
+"\t属性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:692 ../src/utils/pactl.c:960 ../src/utils/pactl.c:1023
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "获取音频信源输出信息失败:%s"
+
+#: ../src/utils/pactl.c:721
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tCorked: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"信源输出 #%u\n"
+"\t驱动程序:%s\n"
+"\t所有者模块:%s\n"
+"\t客户端:%s\n"
+"\t信源:%u\n"
+"\t采样规格:%s\n"
+"\t声道映射:%s\n"
+"\t格式:%s\n"
+"\t抑制:%s\n"
+"\t静音:%s\n"
+"\t音量:%s\n"
+"\t        平衡 %0.2f\n"
+"\t缓冲延迟:%0.0f 微秒\n"
+"\t信源延迟:%0.0f 微秒\n"
+"\t重采样方法:%s\n"
+"\t属性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:762
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "获取采样信息失败:%s"
+
+#: ../src/utils/pactl.c:789
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"样本 #%u\n"
+"\t名称:%s\n"
+"\t采样规格:%s\n"
+"\t声道映射:%s\n"
+"\t音量:%s\n"
+"\t        平衡 %0.2f\n"
+"\t时长:%0.1fs\n"
+"\t大小:%s\n"
+"\tLazy:%s\n"
+"\t文件名称:%s\n"
+"\t属性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:817 ../src/utils/pactl.c:827
+#, c-format
+msgid "Failure: %s"
+msgstr "失败:%s"
+
+#: ../src/utils/pactl.c:866
+#, c-format
+msgid "Failed to unload module: Module %s not loaded"
+msgstr "未能卸载模块:模块 %s 未加载"
+
+#: ../src/utils/pactl.c:884
+#, c-format
+msgid ""
+"Failed to set volume: You tried to set volumes for %d channels, whereas "
+"channel/s supported = %d\n"
+msgstr "设置音量失败:您尝试设置 %d 个声道的音量,而支持的声道数为 %d。\n"
+
+#: ../src/utils/pactl.c:1050
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "未能设置格式:无效的格式字串 %s"
+
+#: ../src/utils/pactl.c:1093
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "上传采样失败:%s"
+
+#: ../src/utils/pactl.c:1110
+msgid "Premature end of file"
+msgstr "文件过早结束"
+
+#: ../src/utils/pactl.c:1130
+msgid "new"
+msgstr "新"
+
+#: ../src/utils/pactl.c:1133
+msgid "change"
+msgstr "变更"
+
+#: ../src/utils/pactl.c:1136
+msgid "remove"
+msgstr "移除"
+
+#: ../src/utils/pactl.c:1139 ../src/utils/pactl.c:1174
+msgid "unknown"
+msgstr "未知"
+
+#: ../src/utils/pactl.c:1147
+msgid "sink"
+msgstr "信宿"
+
+#: ../src/utils/pactl.c:1150
+msgid "source"
+msgstr "信源"
+
+#: ../src/utils/pactl.c:1153
+msgid "sink-input"
+msgstr "信宿-输入"
+
+#: ../src/utils/pactl.c:1156
+msgid "source-output"
+msgstr "信源-输出"
+
+#: ../src/utils/pactl.c:1159
+msgid "module"
+msgstr "模块"
+
+#: ../src/utils/pactl.c:1162
+msgid "client"
+msgstr "客户端"
+
+#: ../src/utils/pactl.c:1165
+msgid "sample-cache"
+msgstr "采样-缓冲"
+
+#: ../src/utils/pactl.c:1168
+msgid "server"
+msgstr "服务器"
+
+#: ../src/utils/pactl.c:1171
+msgid "card"
+msgstr "声卡"
+
+#: ../src/utils/pactl.c:1180
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "事件“%s”于 %s #%u\n"
+
+#: ../src/utils/pactl.c:1452
+msgid "Got SIGINT, exiting."
+msgstr "收到 SIGINT 信号,退出。"
+
+#: ../src/utils/pactl.c:1485
+msgid "Invalid volume specification"
+msgstr "无效采样规格"
+
+#: ../src/utils/pactl.c:1508
+msgid "Volume outside permissible range.\n"
+msgstr "音量超出允许范围。\n"
+
+#: ../src/utils/pactl.c:1521
+msgid "Invalid number of volume specifications.\n"
+msgstr "无效音量规格数。\n"
+
+#: ../src/utils/pactl.c:1533
+msgid "Inconsistent volume specification.\n"
+msgstr "无效音量规格。\n"
+
+#: ../src/utils/pactl.c:1563 ../src/utils/pactl.c:1564
+#: ../src/utils/pactl.c:1565 ../src/utils/pactl.c:1566
+#: ../src/utils/pactl.c:1567 ../src/utils/pactl.c:1568
+#: ../src/utils/pactl.c:1569 ../src/utils/pactl.c:1570
+#: ../src/utils/pactl.c:1571 ../src/utils/pactl.c:1572
+#: ../src/utils/pactl.c:1573 ../src/utils/pactl.c:1574
+#: ../src/utils/pactl.c:1575 ../src/utils/pactl.c:1576
+#: ../src/utils/pactl.c:1577 ../src/utils/pactl.c:1578
+#: ../src/utils/pactl.c:1579 ../src/utils/pactl.c:1580
+#: ../src/utils/pactl.c:1581 ../src/utils/pactl.c:1582
+#: ../src/utils/pactl.c:1583
+msgid "[options]"
+msgstr "[选项]"
+
+#: ../src/utils/pactl.c:1565
+msgid "[TYPE]"
+msgstr "[类型]"
+
+#: ../src/utils/pactl.c:1567
+msgid "FILENAME [NAME]"
+msgstr "文件名 [名称]"
+
+#: ../src/utils/pactl.c:1568
+msgid "NAME [SINK]"
+msgstr "名称 [信宿]"
+
+#: ../src/utils/pactl.c:1577
+msgid "NAME|#N VOLUME [VOLUME ...]"
+msgstr "名称|#N 音量 [音量 ...]"
+
+#: ../src/utils/pactl.c:1578
+msgid "#N VOLUME [VOLUME ...]"
+msgstr "#N 音量 [音量 ...]"
+
+#: ../src/utils/pactl.c:1579
+msgid "NAME|#N 1|0|toggle"
+msgstr "名称|#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1580
+msgid "#N 1|0|toggle"
+msgstr "#N 1|0|toggle"
+
+#: ../src/utils/pactl.c:1581
+msgid "#N FORMATS"
+msgstr "#N 格式列表"
+
+#: ../src/utils/pactl.c:1584
+#, c-format
+msgid ""
+"\n"
+"The special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+"can be used to specify the default sink, source and monitor.\n"
+msgstr ""
+"\n"
+"指定名称 @DEFAULT_SINK@,@DEFAULT_SOURCE@ 和 @DEFAULT_MONITOR@\n"
+"可用于指定默认的信宿、信源和监视器。\n"
+
+#: ../src/utils/pactl.c:1587
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"  -n, --client-name=NAME                How to call this client on the "
+"server\n"
+msgstr ""
+"\n"
+"  -h, --help                            显示此帮助\n"
+"      --version                         显示版本\n"
+"  -s, --server=服务器                   要连接的服务器名\n"
+"  -n, --client-name=名称                向服务器提供的客户端自称\n"
+
+#: ../src/utils/pactl.c:1628
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"使用 libpulse %s 编译\n"
+"与 libpules %s 链接\n"
+
+#: ../src/utils/pactl.c:1684
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "不指定,或指定成下列之一:%s"
+
+#: ../src/utils/pactl.c:1694
+msgid "Please specify a sample file to load"
+msgstr "请指定要加载的采样文件"
+
+#: ../src/utils/pactl.c:1707
+msgid "Failed to open sound file."
+msgstr "打开声音文件失败。"
+
+#: ../src/utils/pactl.c:1719
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "警告:从文件中确定采样规格失败。"
+
+#: ../src/utils/pactl.c:1729
+msgid "You have to specify a sample name to play"
+msgstr "您必须指定要播放的采样名"
+
+#: ../src/utils/pactl.c:1741
+msgid "You have to specify a sample name to remove"
+msgstr "您必须指定要删除的采样名"
+
+#: ../src/utils/pactl.c:1750
+msgid "You have to specify a sink input index and a sink"
+msgstr "您必须指定信宿输入索引和信宿"
+
+#: ../src/utils/pactl.c:1760
+msgid "You have to specify a source output index and a source"
+msgstr "您必须指定信源输出索引和信源"
+
+#: ../src/utils/pactl.c:1775
+msgid "You have to specify a module name and arguments."
+msgstr "必须指定模块名和参数。"
+
+#: ../src/utils/pactl.c:1795
+msgid "You have to specify a module index or name"
+msgstr "必须指定模块索引或名称"
+
+#: ../src/utils/pactl.c:1808
+msgid ""
+"You may not specify more than one sink. You have to specify a boolean value."
+msgstr "不可指定多个信宿。必须指定一个布尔值。"
+
+#: ../src/utils/pactl.c:1813 ../src/utils/pactl.c:1833
+msgid "Invalid suspend specification."
+msgstr "无效挂起规范。"
+
+#: ../src/utils/pactl.c:1828
+msgid ""
+"You may not specify more than one source. You have to specify a boolean "
+"value."
+msgstr "不可指定多个信源。必须指定一个布尔值。"
+
+#: ../src/utils/pactl.c:1845
+msgid "You have to specify a card name/index and a profile name"
+msgstr "您必须指定声卡名称/索引和侧写名称"
+
+#: ../src/utils/pactl.c:1856
+msgid "You have to specify a sink name/index and a port name"
+msgstr "您必须指定信宿名称/索引和端口名称"
+
+#: ../src/utils/pactl.c:1867
+msgid "You have to specify a sink name"
+msgstr "必须指定接收器名"
+
+#: ../src/utils/pactl.c:1877
+msgid "You have to specify a source name/index and a port name"
+msgstr "您必须指定信源名称/索引和端口名称"
+
+#: ../src/utils/pactl.c:1888
+msgid "You have to specify a source name"
+msgstr "必须指定信号源索引"
+
+#: ../src/utils/pactl.c:1898
+msgid "You have to specify a sink name/index and a volume"
+msgstr "您必须指定信宿名称/索引和音量"
+
+#: ../src/utils/pactl.c:1911
+msgid "You have to specify a source name/index and a volume"
+msgstr "您必须指定源名称/索引和音量"
+
+#: ../src/utils/pactl.c:1924
+msgid "You have to specify a sink input index and a volume"
+msgstr "您必须指定信宿输入索引和音量"
+
+#: ../src/utils/pactl.c:1929
+msgid "Invalid sink input index"
+msgstr "无效信宿输入索引"
+
+#: ../src/utils/pactl.c:1940
+msgid "You have to specify a source output index and a volume"
+msgstr "您必须指定信源输出索引 (index) 和音量"
+
+#: ../src/utils/pactl.c:1945
+msgid "Invalid source output index"
+msgstr "无效信源输出索引"
+
+#: ../src/utils/pactl.c:1956
+msgid ""
+"You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"
+msgstr "您必须指定信宿名称/索引和静音动作 (0, 1, 或 'toggle' 切换)"
+
+#: ../src/utils/pactl.c:1961 ../src/utils/pactl.c:1976
+#: ../src/utils/pactl.c:1996 ../src/utils/pactl.c:2014
+msgid "Invalid mute specification"
+msgstr "无效静音说明"
+
+#: ../src/utils/pactl.c:1971
+msgid ""
+"You have to specify a source name/index and a mute action (0, 1, or 'toggle')"
+msgstr "您必须指定信源名称/索引和静音动作 (0, 1, 或 'toggle' 切换)"
+
+#: ../src/utils/pactl.c:1986
+msgid ""
+"You have to specify a sink input index and a mute action (0, 1, or 'toggle')"
+msgstr "您必须指定信宿输入索引和静音动作 (0, 1, 或 'toggle' 切换)"
+
+#: ../src/utils/pactl.c:1991
+msgid "Invalid sink input index specification"
+msgstr "无效信宿输入索引规格"
+
+#: ../src/utils/pactl.c:2004
+msgid ""
+"You have to specify a source output index and a mute action (0, 1, or "
+"'toggle')"
+msgstr "您必须指定信源输出索引和静音动作 (0, 1, 或 'toggle' 切换)"
+
+#: ../src/utils/pactl.c:2009
+msgid "Invalid source output index specification"
+msgstr "无效信源输出索引说明"
+
+#: ../src/utils/pactl.c:2026
+msgid ""
+"You have to specify a sink index and a semicolon-separated list of supported "
+"formats"
+msgstr "您必须指定信宿名称及以英文分号分隔的其所支持格式的列表"
+
+#: ../src/utils/pactl.c:2038
+msgid "You have to specify a card name/index, a port name and a latency offset"
+msgstr "您必须指定声卡名称/索引、端口名称和延迟偏移"
+
+#: ../src/utils/pactl.c:2045
+msgid "Could not parse latency offset"
+msgstr "无法解析延迟偏移"
+
+#: ../src/utils/pactl.c:2057
+msgid "No valid command specified."
+msgstr "未指定有效的命令。"
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork():%s\n"
+
+#: ../src/utils/pasuspender.c:92
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp():%s\n"
+
+#: ../src/utils/pasuspender.c:111
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "恢复失败:%s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "挂起失败:%s\n"
+
+#: ../src/utils/pasuspender.c:170
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "警告:非本地声音服务器,不会挂起。\n"
+
+#: ../src/utils/pasuspender.c:183
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "连接失败:%s\n"
+
+#: ../src/utils/pasuspender.c:201
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "收到 SIGINT 信号,退出。\n"
+
+#: ../src/utils/pasuspender.c:219
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "警告:子进程被信号 %u 终止\n"
+
+#: ../src/utils/pasuspender.c:228
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect "
+"to\n"
+"\n"
+msgstr ""
+"%s [选项] ... \n"
+"\n"
+"  -h, --help                            显示此帮助\n"
+"      --version                         显示版本\n"
+"  -s, --server=SERVER                   要连接的服务器名\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:266
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"使用 libpulse %s 编译\n"
+"与 libpulse %s 链接\n"
+
+#: ../src/utils/pasuspender.c:295
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() 失败。\n"
+
+#: ../src/utils/pasuspender.c:308
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() 失败。\n"
+
+#: ../src/utils/pasuspender.c:320
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() 失败。\n"
+
+#: ../src/utils/pax11publish.c:58
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment "
+"variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D 显示] [-S 服务器] [-O 信宿] [-I 信源] [-c 文件]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    显示与 X11 显示关联的当前 PulseAudio 数据(默认)\n"
+" -e    将本地 PulseAudio 数据导出至 X11 显示器\n"
+" -i    将 PulseAudio 数据由 X11 显示器导入至本地环境变量和 cookie 文件。\n"
+" -r    从 X11 显示中清除 PulseAudio 数据\n"
+
+#: ../src/utils/pax11publish.c:91
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "解析命令行失败。\n"
+
+#: ../src/utils/pax11publish.c:110
+#, c-format
+msgid "Server: %s\n"
+msgstr "服务器:%s\n"
+
+#: ../src/utils/pax11publish.c:112
+#, c-format
+msgid "Source: %s\n"
+msgstr "信源:%s\n"
+
+#: ../src/utils/pax11publish.c:114
+#, c-format
+msgid "Sink: %s\n"
+msgstr "信宿:%s\n"
+
+#: ../src/utils/pax11publish.c:116
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie:%s\n"
+
+#: ../src/utils/pax11publish.c:134
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "解析 cookie 数据失败\n"
+
+#: ../src/utils/pax11publish.c:139
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "保存 cookie 数据失败\n"
+
+#: ../src/utils/pax11publish.c:168
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "获取 FQDN 失败。\n"
+
+#: ../src/utils/pax11publish.c:188
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "加载 cookie 数据失败\n"
+
+#: ../src/utils/pax11publish.c:206
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "尚未实现。\n"
+
+#~ msgid "wants to record audio."
+#~ msgstr "试图录制音频。"
+
+#~ msgid ""
+#~ "%s [options]\n"
+#~ "\n"
+#~ "-h, --help                            Show this help\n"
+#~ "-v, --verbose                         Print debug messages\n"
+#~ "      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --from-format=SAMPLEFORMAT      From sample type (defaults to "
+#~ "s16le)\n"
+#~ "      --from-channels=CHANNELS        From number of channels (defaults "
+#~ "to 1)\n"
+#~ "      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to "
+#~ "44100)\n"
+#~ "      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+#~ "      --to-channels=CHANNELS          To number of channels (defaults to "
+#~ "1)\n"
+#~ "      --resample-method=METHOD        Resample method (defaults to auto)\n"
+#~ "      --seconds=SECONDS               From stream duration (defaults to "
+#~ "60)\n"
+#~ "\n"
+#~ "If the formats are not specified, the test performs all formats "
+#~ "combinations,\n"
+#~ "back and forth.\n"
+#~ "\n"
+#~ "Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, "
+#~ "alaw,\n"
+#~ "s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+#~ "\n"
+#~ "See --dump-resample-methods for possible values of resample methods.\n"
+#~ msgstr ""
+#~ "%s [选项]\n"
+#~ "\n"
+#~ "-h, --help                            显示帮助\n"
+#~ "-v, --verbose                         输出调试信息\n"
+#~ "      --from-rate=SAMPLERATE          音源频率(默认为 44100)赫兹\n"
+#~ "      --from-format=SAMPLEFORMAT      音源格式(默认为 s16le)\n"
+#~ "      --from-channels=CHANNELS        音源声道(默认为 1)\n"
+#~ "      --to-rate=SAMPLERATE           转换为频率(默认 44100)赫兹\n"
+#~ "      --to-format=SAMPLEFORMAT        转换为格式(默认为 s16le)\n"
+#~ "      --to-channels=CHANNELS          转换到声道(默认为 1)\n"
+#~ "      --resample-method=METHOD        重采样方法(默认为自动)\n"
+#~ "      --seconds=SECONDS               音源流长度(默认为 60 秒)\n"
+#~ "\n"
+#~ "如果格式未指定,将会测试进行所有格式组合来回测试。\n"
+#~ "\n"
+#~ "采样类型有这几种:s16le,s16be,u8,float32le,float32be,ulaw,alaw,\n"
+#~ "s24le,s24be,s24-32le,s24-32be,s32le,s32be。\n"
+#~ "\n"
+#~ "使用 --dump-resample-methods 参数可列出可能的采样方法。\n"
+
+#~ msgid "%s %s\n"
+#~ msgstr "%s %s\n"
diff --git a/po/zh_TW.po b/po/zh_TW.po
new file mode 100644 (file)
index 0000000..2a0a0ec
--- /dev/null
@@ -0,0 +1,2771 @@
+# Chinese (Taiwan) translation for pulseaudio.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Cheng-Chia Tseng <pswo10680@gmail.com>, 2010, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PulseAudio Volume Control\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-30 10:10+0000\n"
+"PO-Revision-Date: 2014-11-11 22:10+0800\n"
+"Last-Translator: Cheng-Chia Tseng <pswo10680@gmail.com>\n"
+"Language-Team: zh_TW\n"
+"Language: zh_TW\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.6.10\n"
+
+#: ../src/modules/alsa/alsa-util.c:1136 ../src/modules/alsa/alsa-util.c:1204
+#, c-format
+msgid ""
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_avail() 傳回超出預期的大值:%lu bytes (%lu ms)。\n"
+"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
+
+#: ../src/modules/alsa/alsa-util.c:1179
+#, c-format
+msgid ""
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_delay() 傳回超出預期的大值:%li bytes (%s%lu ms)。\n"
+"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
+
+#: ../src/modules/alsa/alsa-util.c:1220
+#, c-format
+msgid ""
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail %lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_avail_delay() 傳回超出預期的大值:延遲 %lu 少於可用的 %lu。\n"
+"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
+
+#: ../src/modules/alsa/alsa-util.c:1263
+#, c-format
+msgid ""
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+msgstr ""
+"snd_pcm_mmap_begin() 傳回超出預期的大值:%lu bytes (%lu ms)。\n"
+"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
+
+#: ../src/modules/module-always-sink.c:38
+msgid "Always keeps at least one sink loaded even if it's a null one"
+msgstr "即使 sink 為空的,也總是維持至少載入一個 sink"
+
+#: ../src/modules/module-always-sink.c:82
+msgid "Dummy Output"
+msgstr "Dummy Output"
+
+#: ../src/modules/module-ladspa-sink.c:48
+msgid "Virtual LADSPA sink"
+msgstr "虛擬 LADSPA sink"
+
+#: ../src/modules/module-ladspa-sink.c:52
+msgid "sink_name=<name for the sink> sink_properties=<properties for the sink> master=<name of sink to filter> format=<sample format> rate=<sample rate> channels=<number of channels> channel_map=<input channel map> plugin=<ladspa plugin name> label=<ladspa plugin label> control=<comma separated list of input control values> input_ladspaport_map=<comma separated list of input LADSPA port names> output_ladspaport_map=<comma separated list of output LADSPA port names> "
+msgstr "sink_name=<sink 的名稱> sink_properties=<sink 的屬性> master=<要過濾的 sink 名稱> format=<樣本格式> rate=<取樣率> channels=<聲道數> channel_map=<輸入聲道對應表> plugin=<ladspa 插件名稱> label=<ladspa 插件標籤> control=<以半形逗號分隔的輸入控制值清單> input_ladspaport_map=<以半形逗號分隔的輸入 LADSPA 連接埠名稱清單> output_ladspaport_map=<以半形逗號分隔的輸出 LADSPA 連接埠名稱清單> "
+
+#: ../src/modules/module-null-sink.c:49
+msgid "Clocked NULL sink"
+msgstr "Clocked Null sink"
+
+#: ../src/modules/module-null-sink.c:284
+msgid "Null Output"
+msgstr "Null Output"
+
+#: ../src/pulsecore/sink.c:3349
+msgid "Built-in Audio"
+msgstr "內部音效"
+
+#: ../src/pulsecore/sink.c:3354
+msgid "Modem"
+msgstr "數據機"
+
+#: ../src/daemon/ltdl-bind-now.c:127
+msgid "Failed to find original lt_dlopen loader."
+msgstr "找不到 original lt_dlopen loader。"
+
+#: ../src/daemon/ltdl-bind-now.c:132
+msgid "Failed to allocate new dl loader."
+msgstr "未能分配新的 dl loader。"
+
+#: ../src/daemon/ltdl-bind-now.c:145
+msgid "Failed to add bind-now-loader."
+msgstr "未能加入 bind-now-loader。"
+
+#: ../src/daemon/main.c:139
+#, c-format
+msgid "Got signal %s."
+msgstr "取得信號 %s。"
+
+#: ../src/daemon/main.c:166
+msgid "Exiting."
+msgstr "正在退出。"
+
+#: ../src/daemon/main.c:184
+#, c-format
+msgid "Failed to find user '%s'."
+msgstr "找不到使用者「%s」。"
+
+#: ../src/daemon/main.c:189
+#, c-format
+msgid "Failed to find group '%s'."
+msgstr "找不到群組「%s」。"
+
+#: ../src/daemon/main.c:193
+#, c-format
+msgid "Found user '%s' (UID %lu) and group '%s' (GID %lu)."
+msgstr "找到使用者「%s」(UID %lu) 與群組「%s」(GID %lu)。"
+
+#: ../src/daemon/main.c:198
+#, c-format
+msgid "GID of user '%s' and of group '%s' don't match."
+msgstr "使用者「%s」的 GID 與群組「%s」的 GID 不相符。"
+
+#: ../src/daemon/main.c:203
+#, c-format
+msgid "Home directory of user '%s' is not '%s', ignoring."
+msgstr "使用者「%s」的家目錄不是「%s」,忽略中。"
+
+#: ../src/daemon/main.c:206 ../src/daemon/main.c:211
+#, c-format
+msgid "Failed to create '%s': %s"
+msgstr "未能建立「%s」:%s"
+
+#: ../src/daemon/main.c:218
+#, c-format
+msgid "Failed to change group list: %s"
+msgstr "未能變更群組清單:%s"
+
+#: ../src/daemon/main.c:234
+#, c-format
+msgid "Failed to change GID: %s"
+msgstr "未能變更 GIC:%s"
+
+#: ../src/daemon/main.c:250
+#, c-format
+msgid "Failed to change UID: %s"
+msgstr "未能變更 UID:%s"
+
+#: ../src/daemon/main.c:269
+msgid "Successfully dropped root privileges."
+msgstr "成功地放下 root 特權。"
+
+#: ../src/daemon/main.c:277
+msgid "System wide mode unsupported on this platform."
+msgstr "本平台未支援系統域模式。"
+
+#: ../src/daemon/main.c:295
+#, c-format
+msgid "setrlimit(%s, (%u, %u)) failed: %s"
+msgstr "setrlimit(%s, (%u, %u)) 失敗:%s"
+
+#: ../src/daemon/main.c:496
+msgid "Failed to parse command line."
+msgstr "未能解析命令列。"
+
+#: ../src/daemon/main.c:529
+msgid "System mode refused for non-root user. Only starting the D-Bus server lookup service."
+msgstr "系統模式拒絕非 root 使用者。僅啟動 D-Bus 伺服器查看服務。"
+
+#: ../src/daemon/main.c:611
+msgid "Daemon not running"
+msgstr "幕後程式沒有在執行中"
+
+#: ../src/daemon/main.c:613
+#, c-format
+msgid "Daemon running as PID %u"
+msgstr "幕後程式正在執行中,PID 為 %u "
+
+#: ../src/daemon/main.c:628
+#, c-format
+msgid "Failed to kill daemon: %s"
+msgstr "未能結束幕後程式:%s"
+
+#: ../src/daemon/main.c:657
+msgid "This program is not intended to be run as root (unless --system is specified)."
+msgstr "本程式不預期以 root 身份執行(除非有指定 --system)。"
+
+#: ../src/daemon/main.c:660
+msgid "Root privileges required."
+msgstr "需要 root 特權。"
+
+#: ../src/daemon/main.c:667
+msgid "--start not supported for system instances."
+msgstr "--start 不支援系統實體。"
+
+#: ../src/daemon/main.c:707
+#, c-format
+msgid "User-configured server at %s, refusing to start/autospawn."
+msgstr "使用者於 %s 設定的伺服器,拒絕啟動/autospawn。"
+
+#: ../src/daemon/main.c:713
+#, c-format
+msgid "User-configured server at %s, which appears to be local. Probing deeper."
+msgstr "使用者設定的伺服器位於 %s,它似乎是本機。正在深入探測。"
+
+#: ../src/daemon/main.c:718
+msgid "Running in system mode, but --disallow-exit not set!"
+msgstr "正以系統模式執行中,但是未設定 --disallow-exit!"
+
+#: ../src/daemon/main.c:721
+msgid "Running in system mode, but --disallow-module-loading not set!"
+msgstr "正以系統模式執行中,但是未設定 --disallow-module-loading!"
+
+#: ../src/daemon/main.c:724
+msgid "Running in system mode, forcibly disabling SHM mode!"
+msgstr "正以系統模式執行中,強迫停用 SHM 模式!"
+
+#: ../src/daemon/main.c:729
+msgid "Running in system mode, forcibly disabling exit idle time!"
+msgstr "正以系統模式執行中,強迫停用離開閒置時間!"
+
+#: ../src/daemon/main.c:757
+msgid "Failed to acquire stdio."
+msgstr "未能獲取 stdio。"
+
+#: ../src/daemon/main.c:763 ../src/daemon/main.c:828
+#, c-format
+msgid "pipe() failed: %s"
+msgstr "pipe() 失敗:%s"
+
+#: ../src/daemon/main.c:768 ../src/daemon/main.c:833
+#, c-format
+msgid "fork() failed: %s"
+msgstr "fork() 失敗:%s"
+
+#: ../src/daemon/main.c:783 ../src/daemon/main.c:848 ../src/utils/pacat.c:550
+#, c-format
+msgid "read() failed: %s"
+msgstr "read() 失敗:%s"
+
+#: ../src/daemon/main.c:789
+msgid "Daemon startup failed."
+msgstr "幕後程式啟動失敗。"
+
+#: ../src/daemon/main.c:791
+msgid "Daemon startup successful."
+msgstr "幕後程式啟動成功。"
+
+#: ../src/daemon/main.c:816
+#, c-format
+msgid "setsid() failed: %s"
+msgstr "setsid() 失敗:%s"
+
+#: ../src/daemon/main.c:901
+#, c-format
+msgid "This is PulseAudio %s"
+msgstr "這是 PulseAudio %s"
+
+#: ../src/daemon/main.c:902
+#, c-format
+msgid "Compilation host: %s"
+msgstr "彙編主機:%s"
+
+#: ../src/daemon/main.c:903 ../src/tests/resampler-test.c:418
+#, c-format
+msgid "Compilation CFLAGS: %s"
+msgstr "彙編 CFLAGS:%s"
+
+#: ../src/daemon/main.c:906
+#, c-format
+msgid "Running on host: %s"
+msgstr "正執行於此主機:%s"
+
+#: ../src/daemon/main.c:909
+#, c-format
+msgid "Found %u CPUs."
+msgstr "找到 %u 個 CPU。"
+
+#: ../src/daemon/main.c:911
+#, c-format
+msgid "Page size is %lu bytes"
+msgstr "分頁大小為 %lu bytes"
+
+#: ../src/daemon/main.c:914
+msgid "Compiled with Valgrind support: yes"
+msgstr "以 Valgrind 支援進行編譯:是"
+
+#: ../src/daemon/main.c:916
+msgid "Compiled with Valgrind support: no"
+msgstr "以 Valgrind 支援進行編譯:否"
+
+#: ../src/daemon/main.c:919
+#, c-format
+msgid "Running in valgrind mode: %s"
+msgstr "正以何 valgrind 模式執行中:%s"
+
+#: ../src/daemon/main.c:921
+#, c-format
+msgid "Running in VM: %s"
+msgstr "正執行於 VM 上:%s"
+
+#: ../src/daemon/main.c:924
+msgid "Optimized build: yes"
+msgstr "最佳化的建置版本:是"
+
+#: ../src/daemon/main.c:926
+msgid "Optimized build: no"
+msgstr "最佳化的建置版本:否"
+
+#: ../src/daemon/main.c:930
+msgid "NDEBUG defined, all asserts disabled."
+msgstr "NDEBUG 已定義,所有宣告已停用。"
+
+#: ../src/daemon/main.c:932
+msgid "FASTPATH defined, only fast path asserts disabled."
+msgstr "FASTPATH 已定義,僅已停用快速路徑宣告。"
+
+#: ../src/daemon/main.c:934
+msgid "All asserts enabled."
+msgstr "所有宣告已啟用。"
+
+#: ../src/daemon/main.c:938
+msgid "Failed to get machine ID"
+msgstr "未能取得機器 ID"
+
+#: ../src/daemon/main.c:941
+#, c-format
+msgid "Machine ID is %s."
+msgstr "機器 ID 為 %s。"
+
+#: ../src/daemon/main.c:945
+#, c-format
+msgid "Session ID is %s."
+msgstr "作業階段 ID 為 %s。"
+
+#: ../src/daemon/main.c:951
+#, c-format
+msgid "Using runtime directory %s."
+msgstr "正使用執行時期目錄 %s。"
+
+#: ../src/daemon/main.c:956
+#, c-format
+msgid "Using state directory %s."
+msgstr "正使用狀態目錄 %s。"
+
+#: ../src/daemon/main.c:959
+#, c-format
+msgid "Using modules directory %s."
+msgstr "使用模組目錄 %s。"
+
+#: ../src/daemon/main.c:961
+#, c-format
+msgid "Running in system mode: %s"
+msgstr "以系統模式執行中:%s"
+
+#: ../src/daemon/main.c:964
+msgid ""
+"OK, so you are running PA in system mode. Please note that you most likely "
+"shouldn't be doing that.\n"
+"If you do it nonetheless then it's your own fault if things don't work as "
+"expected.\n"
+"Please read http://www.freedesktop.org/wiki/Software/PulseAudio/"
+"Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system "
+"mode is usually a bad idea."
+msgstr ""
+"好的,所以您正以系統模式執行 PA。請注意,您很不應該那麼做。\n"
+"若您仍執意這樣做,那麼當事情的進展非如您所願時是您自己的過錯。\n"
+"請閱讀 http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/"
+"User/WhatIsWrongWithSystemWide/ 來瞭解為何採用系統模式是個壞主意。"
+
+#: ../src/daemon/main.c:981
+msgid "pa_pid_file_create() failed."
+msgstr "pa_pid_file_create() 失敗。"
+
+#: ../src/daemon/main.c:991
+msgid "Fresh high-resolution timers available! Bon appetit!"
+msgstr "有新鮮的高解析度計時器可用!期望您有個好食慾!"
+
+#: ../src/daemon/main.c:993
+msgid "Dude, your kernel stinks! The chef's recommendation today is Linux with high-resolution timers enabled!"
+msgstr "先生,您的核心糟透了!今日主廚推薦是啟用高解析度計時器的 Linux!"
+
+#: ../src/daemon/main.c:1011
+msgid "pa_core_new() failed."
+msgstr "pa_core_new() 失敗。"
+
+#: ../src/daemon/main.c:1087
+msgid "Failed to initialize daemon."
+msgstr "未能初始化幕後程式。"
+
+#: ../src/daemon/main.c:1092
+msgid "Daemon startup without any loaded modules, refusing to work."
+msgstr "幕後程式啟動而沒有任何載入的模組,拒絕運作。"
+
+#: ../src/daemon/main.c:1130
+msgid "Daemon startup complete."
+msgstr "幕後程式啟動完成。"
+
+#: ../src/daemon/main.c:1136
+msgid "Daemon shutdown initiated."
+msgstr "幕後程式已開始關閉。"
+
+#: ../src/daemon/main.c:1167
+msgid "Daemon terminated."
+msgstr "幕後程式已終止。"
+
+#: ../src/daemon/cmdline.c:113
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"      --dump-conf                       Dump default configuration\n"
+"      --dump-modules                    Dump list of available modules\n"
+"      --dump-resample-methods           Dump available resample methods\n"
+"      --cleanup-shm                     Cleanup stale shared memory segments\n"
+"      --start                           Start the daemon if it is not running\n"
+"  -k  --kill                            Kill a running daemon\n"
+"      --check                           Check for a running daemon (only returns exit code)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   Run as system-wide instance\n"
+"  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+"      --fail[=BOOL]                     Quit when startup fails\n"
+"      --high-priority[=BOOL]            Try to set high nice level\n"
+"                                        (only available as root, when SUID or\n"
+"                                        with elevated RLIMIT_NICE)\n"
+"      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+"                                        (only available as root, when SUID or\n"
+"                                        with elevated RLIMIT_RTPRIO)\n"
+"      --disallow-module-loading[=BOOL]  Disallow module user requested module\n"
+"                                        loading/unloading after startup\n"
+"      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+"      --exit-idle-time=SECS             Terminate the daemon when idle and this\n"
+"                                        time passed\n"
+"      --scache-idle-time=SECS           Unload autoloaded samples when idle and\n"
+"                                        this time passed\n"
+"      --log-level[=LEVEL]               Increase or set verbosity level\n"
+"  -v                                    Increase the verbosity level\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        Specify the log target\n"
+"      --log-meta[=BOOL]                 Include code location in log messages\n"
+"      --log-time[=BOOL]                 Include timestamps in log messages\n"
+"      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+"  -p, --dl-search-path=PATH             Set the search path for dynamic shared\n"
+"                                        objects (plugins)\n"
+"      --resample-method=METHOD          Use the specified resampling method\n"
+"                                        (See --dump-resample-methods for\n"
+"                                        possible values)\n"
+"      --use-pid-file[=BOOL]             Create a PID file\n"
+"      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+"                                        platforms that support it.\n"
+"      --disable-shm[=BOOL]              Disable shared memory support.\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module with\n"
+"                                        the specified argument\n"
+"  -F, --file=FILENAME                   Run the specified script\n"
+"  -C                                    Open a command line on the running TTY\n"
+"                                        after startup\n"
+"\n"
+"  -n                                    Don't load default script file\n"
+msgstr ""
+"%s [選項]\n"
+"\n"
+"COMMANDS:\n"
+"  -h, --help                            顯示本幫助\n"
+"      --version                         顯示版本\n"
+"      --dump-conf                       傾印預設組態\n"
+"      --dump-modules                    傾印可用模組清單\n"
+"      --dump-resample-methods           傾印可用重新取樣方法\n"
+"      --cleanup-shm                     清理狀態共享記憶體區段\n"
+"      --start                           啟動幕後程式如果它未在執行中\n"
+"  -k  --kill                            終結執行中的幕後程式\n"
+"      --check                           檢查執行中的幕後程式 (僅傳回離開代碼)\n"
+"\n"
+"OPTIONS:\n"
+"      --system[=BOOL]                   以系統域實體執行\n"
+"  -D, --daemonize[=BOOL]                在初始啟動後幕後化\n"
+"      --fail[=BOOL]                     當初始啟動失敗後退出\n"
+"      --high-priority[=BOOL]            試著設定較高良好值\n"
+"                                        (僅 root 可使用,當 SUID 或\n"
+"                                        以 RLIMIT_NICE 提升時)\n"
+"      --realtime[=BOOL]                 試著啟用實時排程\n"
+"                                        (僅 root 可使用,當 SUID 或\n"
+"                                        以 RLIMIT_RTPRIO 提升時)\n"
+"      --disallow-module-loading[=BOOL]  禁止模組使用者在初始啟動後\n"
+"                                        請求模組載入/取消載入\n"
+"      --disallow-exit[=BOOL]            禁止使用者請求離開\n"
+"      --exit-idle-time=SECS             閒置且經過這段時間後終止\n"
+"                                        幕後程式\n"
+"      --scache-idle-time=SECS           閒置且經過這段時間後卸載\n"
+"                                        自動載入的樣本\n"
+"      --log-level[=LEVEL]               增加或設定冗長層級\n"
+"  -v                                    增加冗長層級\n"
+"      --log-target={auto,syslog,stderr,file:PATH}\n"
+"                                        指定紀錄目標\n"
+"      --log-meta[=BOOL]                 在紀錄訊息中包含代碼位置\n"
+"      --log-time[=BOOL]                 在紀錄訊息中包含時間戳記\n"
+"      --log-backtrace=FRAMES            在紀錄訊息中包含回溯資訊\n"
+"  -p, --dl-search-path=PATH             設定動態分享物件 (插件) \n"
+"                                        的搜尋路徑\n"
+"      --resample-method=METHOD          使用指定的重新取樣方法\n"
+"                                        (查看 --dump-resample-methods 以了解\n"
+"                                        可能的值)\n"
+"      --use-pid-file[=BOOL]             建立一個 PID 檔案\n"
+"      --no-cpu-limit[=BOOL]             不要在支援 CPU 載入限制器的\n"
+"                                        平台上安裝它。\n"
+"      --disable-shm[=BOOL]              停用共享記憶體支援。\n"
+"\n"
+"STARTUP SCRIPT:\n"
+"  -L, --load=\"MODULE ARGUMENTS\"         以指定的引數載入指定\n"
+"                                        的插件模組\n"
+"  -F, --file=FILENAME                   執行指定的指令槁\n"
+"  -C                                    在初始啟動後在執行中的 TTY \n"
+"                                        開啟指令列\n"
+"\n"
+"  -n                                    不要載入預設指令稿檔案\n"
+
+#: ../src/daemon/cmdline.c:244
+msgid "--daemonize expects boolean argument"
+msgstr "--daemonize 預期布林引數"
+
+#: ../src/daemon/cmdline.c:251
+msgid "--fail expects boolean argument"
+msgstr "--fail 預期布林引數"
+
+#: ../src/daemon/cmdline.c:261
+msgid "--log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error)."
+msgstr "--log-level 預期記錄層級引數 (範圍 0..4 間的數字,或是後列之一: debug、info、notice、warn、error)。"
+
+#: ../src/daemon/cmdline.c:273
+msgid "--high-priority expects boolean argument"
+msgstr "--high-priority 預期布林引數"
+
+#: ../src/daemon/cmdline.c:280
+msgid "--realtime expects boolean argument"
+msgstr "--realtime 預期布林引數"
+
+#: ../src/daemon/cmdline.c:287
+msgid "--disallow-module-loading expects boolean argument"
+msgstr "--disallow-module-loading 預期布林引數"
+
+#: ../src/daemon/cmdline.c:294
+msgid "--disallow-exit expects boolean argument"
+msgstr "--disallow-exit 預期布林引數"
+
+#: ../src/daemon/cmdline.c:301
+msgid "--use-pid-file expects boolean argument"
+msgstr "--use-pid-file 預期布林引數"
+
+#: ../src/daemon/cmdline.c:318
+msgid "Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>'."
+msgstr "無效的記錄目標:使用「syslog」、「stderr」或「auto」或有效的檔名「file:<path>」。"
+
+#: ../src/daemon/cmdline.c:325
+msgid "--log-time expects boolean argument"
+msgstr "--log-time 預期布林引數"
+
+#: ../src/daemon/cmdline.c:332
+msgid "--log-meta expects boolean argument"
+msgstr "--log-meta 預期布林引數"
+
+#: ../src/daemon/cmdline.c:351
+#, c-format
+msgid "Invalid resample method '%s'."
+msgstr "無效的重新取樣方法「%s」"
+
+#: ../src/daemon/cmdline.c:358
+msgid "--system expects boolean argument"
+msgstr "--system 預期布林引數"
+
+#: ../src/daemon/cmdline.c:365
+msgid "--no-cpu-limit expects boolean argument"
+msgstr "--no-cpu-limit 預期布林引數"
+
+#: ../src/daemon/cmdline.c:372
+msgid "--disable-shm expects boolean argument"
+msgstr "--disable-shm 預期布林引數"
+
+#: ../src/daemon/dumpmodules.c:59
+#, c-format
+msgid "Name: %s\n"
+msgstr "名稱:%s\n"
+
+#: ../src/daemon/dumpmodules.c:62
+#, c-format
+msgid "No module information available\n"
+msgstr "沒有可用的模組資訊\n"
+
+#: ../src/daemon/dumpmodules.c:65
+#, c-format
+msgid "Version: %s\n"
+msgstr "版本:%s\n"
+
+#: ../src/daemon/dumpmodules.c:67
+#, c-format
+msgid "Description: %s\n"
+msgstr "描述:%s\n"
+
+#: ../src/daemon/dumpmodules.c:69
+#, c-format
+msgid "Author: %s\n"
+msgstr "作者:%s\n"
+
+#: ../src/daemon/dumpmodules.c:71
+#, c-format
+msgid "Usage: %s\n"
+msgstr "用法:%s\n"
+
+#: ../src/daemon/dumpmodules.c:72
+#, c-format
+msgid "Load Once: %s\n"
+msgstr "載入一次:%s\n"
+
+#: ../src/daemon/dumpmodules.c:74
+#, c-format
+msgid "DEPRECATION WARNING: %s\n"
+msgstr "反對警告:%s\n"
+
+#: ../src/daemon/dumpmodules.c:78
+#, c-format
+msgid "Path: %s\n"
+msgstr "路徑:%s\n"
+
+#: ../src/daemon/daemon-conf.c:275
+#, c-format
+msgid "[%s:%u] Invalid log target '%s'."
+msgstr "[%s:%u] 無效的記錄目標「%s」。"
+
+#: ../src/daemon/daemon-conf.c:291
+#, c-format
+msgid "[%s:%u] Invalid log level '%s'."
+msgstr "[%s:%u] 無效的紀錄層級「%s」。"
+
+#: ../src/daemon/daemon-conf.c:307
+#, c-format
+msgid "[%s:%u] Invalid resample method '%s'."
+msgstr "[%s:%u] 無效的重新取樣方法「%s」。"
+
+#: ../src/daemon/daemon-conf.c:330
+#, c-format
+msgid "[%s:%u] Invalid rlimit '%s'."
+msgstr "[%s:%u] 無效的 rlimit「%s」。"
+
+#: ../src/daemon/daemon-conf.c:351
+#, c-format
+msgid "[%s:%u] Invalid sample format '%s'."
+msgstr "[%s:%u] 無效的樣本格式「%s」。"
+
+#: ../src/daemon/daemon-conf.c:370 ../src/daemon/daemon-conf.c:389
+#, c-format
+msgid "[%s:%u] Invalid sample rate '%s'."
+msgstr "[%s:%u] 無的取樣率「%s」。"
+
+#: ../src/daemon/daemon-conf.c:413
+#, c-format
+msgid "[%s:%u] Invalid sample channels '%s'."
+msgstr "[%s:%u] 無效的取樣聲道「%s」。"
+
+#: ../src/daemon/daemon-conf.c:431
+#, c-format
+msgid "[%s:%u] Invalid channel map '%s'."
+msgstr "[%s:%u] 無效的聲道對應表「%s」。"
+
+#: ../src/daemon/daemon-conf.c:449
+#, c-format
+msgid "[%s:%u] Invalid number of fragments '%s'."
+msgstr "[%s:%u] 無效的片段數量「%s」。"
+
+#: ../src/daemon/daemon-conf.c:467
+#, c-format
+msgid "[%s:%u] Invalid fragment size '%s'."
+msgstr "[%s:%u] 無效的片段大小「%s」。"
+
+#: ../src/daemon/daemon-conf.c:485
+#, c-format
+msgid "[%s:%u] Invalid nice level '%s'."
+msgstr "[%s:%u] 無效的良好層級「%s」。"
+
+#: ../src/daemon/daemon-conf.c:528
+#, c-format
+msgid "[%s:%u] Invalid server type '%s'."
+msgstr "[%s:%u] 無效的伺服器類型「%s」。"
+
+#: ../src/daemon/daemon-conf.c:641
+#, c-format
+msgid "Failed to open configuration file: %s"
+msgstr "未能開啟組態檔:%s"
+
+#: ../src/daemon/daemon-conf.c:657
+msgid "The specified default channel map has a different number of channels than the specified default number of channels."
+msgstr "指定的預設聲道對應表的聲道數與指定的預設聲道數不同。"
+
+#: ../src/daemon/daemon-conf.c:743
+#, c-format
+msgid "### Read from configuration file: %s ###\n"
+msgstr "### 從此組態檔讀取:%s ###\n"
+
+#: ../src/daemon/caps.c:58
+msgid "Cleaning up privileges."
+msgstr "正在清除特權。"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:1
+msgid "PulseAudio Sound System"
+msgstr "PulseAudio 音效系統"
+
+#: ../src/daemon/pulseaudio.desktop.in.h:2
+msgid "Start the PulseAudio Sound System"
+msgstr "啟動 PulseAudio 音效系統"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:1
+msgid "PulseAudio Sound System KDE Routing Policy"
+msgstr "PulseAudio 音效系統 KDE 路由方針"
+
+#: ../src/daemon/pulseaudio-kde.desktop.in.h:2
+msgid "Start the PulseAudio Sound System with KDE Routing Policy"
+msgstr "以 KDE 路由方針啟動 PulseAudio 音效系統"
+
+#: ../src/pulse/channelmap.c:105 ../src/pulse/channelmap.c:757
+msgid "Mono"
+msgstr "單聲道"
+
+#: ../src/pulse/channelmap.c:107
+msgid "Front Center"
+msgstr "正前方"
+
+#: ../src/pulse/channelmap.c:108
+msgid "Front Left"
+msgstr "左前方"
+
+#: ../src/pulse/channelmap.c:109
+msgid "Front Right"
+msgstr "右前方"
+
+#: ../src/pulse/channelmap.c:111
+msgid "Rear Center"
+msgstr "正後方"
+
+#: ../src/pulse/channelmap.c:112
+msgid "Rear Left"
+msgstr "左後方"
+
+#: ../src/pulse/channelmap.c:113
+msgid "Rear Right"
+msgstr "右後方"
+
+#: ../src/pulse/channelmap.c:115
+msgid "Subwoofer"
+msgstr "Subwoofer"
+
+#: ../src/pulse/channelmap.c:117
+msgid "Front Left-of-center"
+msgstr "前方中央偏左"
+
+#: ../src/pulse/channelmap.c:118
+msgid "Front Right-of-center"
+msgstr "前方中央偏右"
+
+#: ../src/pulse/channelmap.c:120
+msgid "Side Left"
+msgstr "左側"
+
+#: ../src/pulse/channelmap.c:121
+msgid "Side Right"
+msgstr "右側"
+
+#: ../src/pulse/channelmap.c:123
+msgid "Auxiliary 0"
+msgstr "輔助 0"
+
+#: ../src/pulse/channelmap.c:124
+msgid "Auxiliary 1"
+msgstr "輔助 1"
+
+#: ../src/pulse/channelmap.c:125
+msgid "Auxiliary 2"
+msgstr "輔助 2"
+
+#: ../src/pulse/channelmap.c:126
+msgid "Auxiliary 3"
+msgstr "輔助 3"
+
+#: ../src/pulse/channelmap.c:127
+msgid "Auxiliary 4"
+msgstr "輔助 4"
+
+#: ../src/pulse/channelmap.c:128
+msgid "Auxiliary 5"
+msgstr "輔助 5"
+
+#: ../src/pulse/channelmap.c:129
+msgid "Auxiliary 6"
+msgstr "輔助 6"
+
+#: ../src/pulse/channelmap.c:130
+msgid "Auxiliary 7"
+msgstr "輔助 7"
+
+#: ../src/pulse/channelmap.c:131
+msgid "Auxiliary 8"
+msgstr "輔助 8"
+
+#: ../src/pulse/channelmap.c:132
+msgid "Auxiliary 9"
+msgstr "輔助 9"
+
+#: ../src/pulse/channelmap.c:133
+msgid "Auxiliary 10"
+msgstr "輔助 10"
+
+#: ../src/pulse/channelmap.c:134
+msgid "Auxiliary 11"
+msgstr "輔助 11"
+
+#: ../src/pulse/channelmap.c:135
+msgid "Auxiliary 12"
+msgstr "輔助 12"
+
+#: ../src/pulse/channelmap.c:136
+msgid "Auxiliary 13"
+msgstr "輔助 13"
+
+#: ../src/pulse/channelmap.c:137
+msgid "Auxiliary 14"
+msgstr "輔助 14"
+
+#: ../src/pulse/channelmap.c:138
+msgid "Auxiliary 15"
+msgstr "輔助 15"
+
+#: ../src/pulse/channelmap.c:139
+msgid "Auxiliary 16"
+msgstr "輔助 16"
+
+#: ../src/pulse/channelmap.c:140
+msgid "Auxiliary 17"
+msgstr "輔助 17"
+
+#: ../src/pulse/channelmap.c:141
+msgid "Auxiliary 18"
+msgstr "輔助 18"
+
+#: ../src/pulse/channelmap.c:142
+msgid "Auxiliary 19"
+msgstr "輔助 19"
+
+#: ../src/pulse/channelmap.c:143
+msgid "Auxiliary 20"
+msgstr "輔助 20"
+
+#: ../src/pulse/channelmap.c:144
+msgid "Auxiliary 21"
+msgstr "輔助 21"
+
+#: ../src/pulse/channelmap.c:145
+msgid "Auxiliary 22"
+msgstr "輔助 22"
+
+#: ../src/pulse/channelmap.c:146
+msgid "Auxiliary 23"
+msgstr "輔助 23"
+
+#: ../src/pulse/channelmap.c:147
+msgid "Auxiliary 24"
+msgstr "輔助 24"
+
+#: ../src/pulse/channelmap.c:148
+msgid "Auxiliary 25"
+msgstr "輔助 25"
+
+#: ../src/pulse/channelmap.c:149
+msgid "Auxiliary 26"
+msgstr "輔助 26"
+
+#: ../src/pulse/channelmap.c:150
+msgid "Auxiliary 27"
+msgstr "輔助 27"
+
+#: ../src/pulse/channelmap.c:151
+msgid "Auxiliary 28"
+msgstr "輔助 28"
+
+#: ../src/pulse/channelmap.c:152
+msgid "Auxiliary 29"
+msgstr "輔助 29"
+
+#: ../src/pulse/channelmap.c:153
+msgid "Auxiliary 30"
+msgstr "輔助 30"
+
+#: ../src/pulse/channelmap.c:154
+msgid "Auxiliary 31"
+msgstr "輔助 31"
+
+#: ../src/pulse/channelmap.c:156
+msgid "Top Center"
+msgstr "正上方"
+
+#: ../src/pulse/channelmap.c:158
+msgid "Top Front Center"
+msgstr "頂端正上方"
+
+#: ../src/pulse/channelmap.c:159
+msgid "Top Front Left"
+msgstr "頂端左前方"
+
+#: ../src/pulse/channelmap.c:160
+msgid "Top Front Right"
+msgstr "頂端右前方"
+
+#: ../src/pulse/channelmap.c:162
+msgid "Top Rear Center"
+msgstr "頂端正後方"
+
+#: ../src/pulse/channelmap.c:163
+msgid "Top Rear Left"
+msgstr "頂端左後方"
+
+#: ../src/pulse/channelmap.c:164
+msgid "Top Rear Right"
+msgstr "頂端右後方"
+
+#: ../src/pulse/channelmap.c:484 ../src/pulse/sample.c:169
+#: ../src/pulse/volume.c:297 ../src/pulse/volume.c:323
+#: ../src/pulse/volume.c:343 ../src/pulse/volume.c:373
+#: ../src/pulse/format.c:125
+msgid "(invalid)"
+msgstr "(無效)"
+
+#: ../src/pulse/channelmap.c:761
+msgid "Stereo"
+msgstr "立體聲"
+
+#: ../src/pulse/channelmap.c:766
+msgid "Surround 4.0"
+msgstr "環繞聲 4.0"
+
+#: ../src/pulse/channelmap.c:772
+msgid "Surround 4.1"
+msgstr "環繞聲 4.1"
+
+#: ../src/pulse/channelmap.c:778
+msgid "Surround 5.0"
+msgstr "環繞聲 5.0"
+
+#: ../src/pulse/channelmap.c:784
+msgid "Surround 5.1"
+msgstr "環繞聲 5.1"
+
+#: ../src/pulse/channelmap.c:791
+msgid "Surround 7.1"
+msgstr "環繞聲 7.1"
+
+#: ../src/pulse/error.c:40
+msgid "OK"
+msgstr "確定"
+
+#: ../src/pulse/error.c:41
+msgid "Access denied"
+msgstr "拒絕存取"
+
+#: ../src/pulse/error.c:42
+msgid "Unknown command"
+msgstr "未知指令"
+
+#: ../src/pulse/error.c:43
+msgid "Invalid argument"
+msgstr "無效的參數"
+
+#: ../src/pulse/error.c:44
+msgid "Entity exists"
+msgstr "實體存在"
+
+#: ../src/pulse/error.c:45
+msgid "No such entity"
+msgstr "無此實體"
+
+#: ../src/pulse/error.c:46
+msgid "Connection refused"
+msgstr "拒絕連線"
+
+#: ../src/pulse/error.c:47
+msgid "Protocol error"
+msgstr "協定錯誤"
+
+#: ../src/pulse/error.c:48
+msgid "Timeout"
+msgstr "逾時"
+
+#: ../src/pulse/error.c:49
+msgid "No authorization key"
+msgstr "沒有認證金鑰"
+
+#: ../src/pulse/error.c:50
+msgid "Internal error"
+msgstr "內部錯誤"
+
+#: ../src/pulse/error.c:51
+msgid "Connection terminated"
+msgstr "連線已終止"
+
+#: ../src/pulse/error.c:52
+msgid "Entity killed"
+msgstr "實體已結束"
+
+#: ../src/pulse/error.c:53
+msgid "Invalid server"
+msgstr "無效的伺服器"
+
+#: ../src/pulse/error.c:54
+msgid "Module initialization failed"
+msgstr "模組初始化失敗"
+
+#: ../src/pulse/error.c:55
+msgid "Bad state"
+msgstr "不良狀態"
+
+#: ../src/pulse/error.c:56
+msgid "No data"
+msgstr "無資料"
+
+#: ../src/pulse/error.c:57
+msgid "Incompatible protocol version"
+msgstr "不相容的協定版本"
+
+#: ../src/pulse/error.c:58
+msgid "Too large"
+msgstr "過大"
+
+#: ../src/pulse/error.c:59
+msgid "Not supported"
+msgstr "不支援"
+
+#: ../src/pulse/error.c:60
+msgid "Unknown error code"
+msgstr "未知的錯誤碼"
+
+#: ../src/pulse/error.c:61
+msgid "No such extension"
+msgstr "無此擴展功能"
+
+#: ../src/pulse/error.c:62
+msgid "Obsolete functionality"
+msgstr "淘汰的功能"
+
+#: ../src/pulse/error.c:63
+msgid "Missing implementation"
+msgstr "遺失的實作"
+
+#: ../src/pulse/error.c:64
+msgid "Client forked"
+msgstr "客戶端已分支"
+
+#: ../src/pulse/error.c:65
+msgid "Input/Output error"
+msgstr "輸入/輸出 錯誤"
+
+#: ../src/pulse/error.c:66
+msgid "Device or resource busy"
+msgstr "裝置或資源忙碌"
+
+#: ../src/pulse/sample.c:171
+#, c-format
+msgid "%s %uch %uHz"
+msgstr "%s %uch %uHz"
+
+#: ../src/pulse/sample.c:183
+#, c-format
+msgid "%0.1f GiB"
+msgstr "%0.1f GiB"
+
+#: ../src/pulse/sample.c:185
+#, c-format
+msgid "%0.1f MiB"
+msgstr "%0.1f MiB"
+
+#: ../src/pulse/sample.c:187
+#, c-format
+msgid "%0.1f KiB"
+msgstr "%0.1f KiB"
+
+#: ../src/pulse/sample.c:189
+#, c-format
+msgid "%u B"
+msgstr "%u B"
+
+#: ../src/pulse/client-conf-x11.c:54 ../src/utils/pax11publish.c:100
+msgid "xcb_connect() failed"
+msgstr "xcb_connect() 失敗"
+
+#: ../src/pulse/client-conf-x11.c:59 ../src/utils/pax11publish.c:105
+msgid "xcb_connection_has_error() returned true"
+msgstr "xcb_connection_has_error() 傳回真值"
+
+#: ../src/pulse/client-conf-x11.c:97
+msgid "Failed to parse cookie data"
+msgstr "未能解析 cookie 資料"
+
+#: ../src/pulse/client-conf.c:117
+#, c-format
+msgid "Failed to open configuration file '%s': %s"
+msgstr "未能開啟組態檔「%s」:%s"
+
+#: ../src/pulse/context.c:528
+msgid "No cookie loaded. Attempting to connect without."
+msgstr "沒有載入 cookie。試圖連接而不用 cookie。"
+
+#: ../src/pulse/context.c:675
+#, c-format
+msgid "fork(): %s"
+msgstr "fork():%s"
+
+#: ../src/pulse/context.c:730
+#, c-format
+msgid "waitpid(): %s"
+msgstr "waitpid():%s"
+
+#: ../src/pulse/context.c:1431
+#, c-format
+msgid "Received message for unknown extension '%s'"
+msgstr "已接收到未知擴展功能的訊息「%s」"
+
+#: ../src/utils/pacat.c:112
+#, c-format
+msgid "Failed to drain stream: %s"
+msgstr "未能排出串流:%s"
+
+#: ../src/utils/pacat.c:117
+msgid "Playback stream drained."
+msgstr "播放控制串流已排出。"
+
+#: ../src/utils/pacat.c:128
+msgid "Draining connection to server."
+msgstr "正在排出連線到伺服器。"
+
+#: ../src/utils/pacat.c:141
+#, c-format
+msgid "pa_stream_drain(): %s"
+msgstr "pa_stream_drain():%s"
+
+#: ../src/utils/pacat.c:164
+#, c-format
+msgid "pa_stream_write() failed: %s"
+msgstr "pa_stream_write() failed:%s"
+
+#: ../src/utils/pacat.c:205
+#, c-format
+msgid "pa_stream_begin_write() failed: %s"
+msgstr "pa_stream_begin_write() 失敗:%s"
+
+#: ../src/utils/pacat.c:255 ../src/utils/pacat.c:285
+#, c-format
+msgid "pa_stream_peek() failed: %s"
+msgstr "pa_stream_peek() 失敗:%s"
+
+#: ../src/utils/pacat.c:325
+msgid "Stream successfully created."
+msgstr "已成功建立串流。"
+
+#: ../src/utils/pacat.c:328
+#, c-format
+msgid "pa_stream_get_buffer_attr() failed: %s"
+msgstr "pa_stream_get_buffer_attr() 失敗:%s"
+
+#: ../src/utils/pacat.c:332
+#, c-format
+msgid "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+msgstr "緩衝矩陣:maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"
+
+#: ../src/utils/pacat.c:335
+#, c-format
+msgid "Buffer metrics: maxlength=%u, fragsize=%u"
+msgstr "緩衝矩陣:maxlength=%u, fragsize=%u"
+
+#: ../src/utils/pacat.c:339
+#, c-format
+msgid "Using sample spec '%s', channel map '%s'."
+msgstr "使用取樣規格「%s」,聲道對應表「%s」"
+
+#: ../src/utils/pacat.c:343
+#, c-format
+msgid "Connected to device %s (%u, %ssuspended)."
+msgstr "正在連接到裝置 %s (%u, %ssuspended)。"
+
+#: ../src/utils/pacat.c:353
+#, c-format
+msgid "Stream error: %s"
+msgstr "串流錯誤:%s"
+
+#: ../src/utils/pacat.c:363
+#, c-format
+msgid "Stream device suspended.%s"
+msgstr "串流裝置已暫停。%s"
+
+#: ../src/utils/pacat.c:365
+#, c-format
+msgid "Stream device resumed.%s"
+msgstr "串流裝置已恢復。%s"
+
+#: ../src/utils/pacat.c:373
+#, c-format
+msgid "Stream underrun.%s"
+msgstr "串流欠載運行。%s"
+
+#: ../src/utils/pacat.c:380
+#, c-format
+msgid "Stream overrun.%s"
+msgstr "串流超載運行。%s"
+
+#: ../src/utils/pacat.c:387
+#, c-format
+msgid "Stream started.%s"
+msgstr "串流已開始。%s"
+
+#: ../src/utils/pacat.c:394
+#, c-format
+msgid "Stream moved to device %s (%u, %ssuspended).%s"
+msgstr "串流移至裝置 %s (%u,%s已暫停)。%s"
+
+#: ../src/utils/pacat.c:394
+msgid "not "
+msgstr "不是"
+
+#: ../src/utils/pacat.c:401
+#, c-format
+msgid "Stream buffer attributes changed.%s"
+msgstr "串流緩衝特徵已變更。%s"
+
+#: ../src/utils/pacat.c:416
+msgid "Cork request stack is empty: corking stream"
+msgstr "Cork 請求堆疊為空:corking 串流"
+
+#: ../src/utils/pacat.c:422
+msgid "Cork request stack is empty: uncorking stream"
+msgstr "Cork 請求堆疊為空:uncorking 串流"
+
+#: ../src/utils/pacat.c:426
+msgid "Warning: Received more uncork requests than cork requests!"
+msgstr "警告:比起  cork 請求,接收到更多 uncork 請求!"
+
+#: ../src/utils/pacat.c:451
+#, c-format
+msgid "Connection established.%s"
+msgstr "連線已建立。%s"
+
+#: ../src/utils/pacat.c:454
+#, c-format
+msgid "pa_stream_new() failed: %s"
+msgstr "pa_stream_new() 失敗:%s"
+
+#: ../src/utils/pacat.c:492
+#, c-format
+msgid "pa_stream_connect_playback() failed: %s"
+msgstr "pa_stream_connect_playback() 失敗:%s"
+
+#: ../src/utils/pacat.c:498
+#, c-format
+msgid "pa_stream_connect_record() failed: %s"
+msgstr "pa_stream_connect_record() 失敗:%s"
+
+#: ../src/utils/pacat.c:512 ../src/utils/pactl.c:1252
+#, c-format
+msgid "Connection failure: %s"
+msgstr "連線失敗:%s"
+
+#: ../src/utils/pacat.c:545
+msgid "Got EOF."
+msgstr "已取得 EOF。"
+
+#: ../src/utils/pacat.c:582
+#, c-format
+msgid "write() failed: %s"
+msgstr "write() 失敗:%s"
+
+#: ../src/utils/pacat.c:603
+msgid "Got signal, exiting."
+msgstr "已取得訊號,正在退出。"
+
+#: ../src/utils/pacat.c:617
+#, c-format
+msgid "Failed to get latency: %s"
+msgstr "未能取得傳輸延遲:%s"
+
+#: ../src/utils/pacat.c:622
+#, c-format
+msgid "Time: %0.3f sec; Latency: %0.0f usec."
+msgstr "時間:%0.3f 秒;延遲:%0.0f 微秒。"
+
+#: ../src/utils/pacat.c:643
+#, c-format
+msgid "pa_stream_update_timing_info() failed: %s"
+msgstr "pa_stream_update_timing_info() 失敗:%s"
+
+#: ../src/utils/pacat.c:653
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -r, --record                          Create a connection for recording\n"
+"  -p, --playback                        Create a connection for playback\n"
+"\n"
+"  -v, --verbose                         Enable verbose operations\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"  -d, --device=DEVICE                   The name of the sink/source to connect to\n"
+"  -n, --client-name=NAME                How to call this client on the server\n"
+"      --stream-name=NAME                How to call this stream on the server\n"
+"      --volume=VOLUME                   Specify the initial (linear) volume in range 0...65536\n"
+"      --rate=SAMPLERATE                 The sample rate in Hz (defaults to 44100)\n"
+"      --format=SAMPLEFORMAT             The sample type, one of s16le, s16be, u8, float32le,\n"
+"                                        float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
+"                                        s24-32le, s24-32be (defaults to s16ne)\n"
+"      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"
+"                                        (defaults to 2)\n"
+"      --channel-map=CHANNELMAP          Channel map to use instead of the default\n"
+"      --fix-format                      Take the sample format from the sink the stream is\n"
+"                                        being connected to.\n"
+"      --fix-rate                        Take the sampling rate from the sink the stream is\n"
+"                                        being connected to.\n"
+"      --fix-channels                    Take the number of channels and the channel map\n"
+"                                        from the sink the stream is being connected to.\n"
+"      --no-remix                        Don't upmix or downmix channels.\n"
+"      --no-remap                        Map channels by index instead of name.\n"
+"      --latency=BYTES                   Request the specified latency in bytes.\n"
+"      --process-time=BYTES              Request the specified process time per request in bytes.\n"
+"      --latency-msec=MSEC               Request the specified latency in msec.\n"
+"      --process-time-msec=MSEC          Request the specified process time per request in msec.\n"
+"      --property=PROPERTY=VALUE         Set the specified property to the specified value.\n"
+"      --raw                             Record/play raw PCM data.\n"
+"      --passthrough                     passthrough data \n"
+"      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+"      --list-file-formats               List available file formats.\n"
+msgstr ""
+"%s [選項]\n"
+"\n"
+"  -h, --help                            顯示此幫助\n"
+"      --version                         顯示版本\n"
+"\n"
+"  -r, --record                          建立錄製的連線\n"
+"  -p, --playback                        建立播放控制的連線\n"
+"\n"
+"  -v, --verbose                         啟用冗長操作\n"
+"\n"
+"  -s, --server=SERVER                   要連接的伺服器名稱\n"
+"  -d, --device=DEVICE                   要連接的 sink/來源名稱\n"
+"  -n, --client-name=NAME                如何稱呼伺服器上的這個客戶端\n"
+"      --stream-name=NAME                如何稱呼伺服器上的這個串流\n"
+"      --volume=VOLUME                   指定初始 (線性) 音量,範圍為 0...65536\n"
+"      --rate=SAMPLERATE                 以 Hz  為單位的取樣率 (預設值為 44100)\n"
+"      --format=SAMPLEFORMAT             取樣類型,右列之一 s16le、s16be、u8、float32le、\n"
+"                                        float32be、ulaw、alaw、s32le、s32be、s24le、s24be、\n"
+"                                        s24-32le、s24-32be (預設值為 s16ne)\n"
+"      --channels=CHANNELS               聲道數量,1 為單聲道,2 為立體聲\n"
+"                                        (預設值為 2)\n"
+"      --channel-map=CHANNELMAP          要改用的聲道對應表 (不用預設值)\n"
+"      --fix-format                      從串流正連接的 sink 接管\n"
+"                                        樣本格式。\n"
+"      --fix-rate                        從串流正連接的 sink 接管\n"
+"                                        取樣率。\n"
+"      --fix-channels                    從串流正連接的 sink 接管\n"
+"                                        聲道數量與聲道對應表。\n"
+"      --no-remix                        不要 upmix 或 downmix 聲道。\n"
+"      --no-remap                        用索引來對應聲道而不是用名稱。\n"
+"      --latency=BYTES                   以 bytes 請求指定的延遲。\n"
+"      --process-time=BYTES              每個請求以 bytes 請求指定的程序時間。\n"
+"      --latency-msec=MSEC               以毫秒請求指定的延遲。\n"
+"      --process-time-msec=MSEC          每個請求以毫秒請求指定的程序時間。\n"
+"      --property=PROPERTY=VALUE         設定指定的屬性給指定的值。\n"
+"      --raw                             錄製/播放 raw PCM 資料。\n"
+"      --passthrough                     通透資料 \n"
+"      --file-format[=FFORMAT]           錄製/播放格式化的 PCM 資料。\n"
+"      --list-file-formats               列出可用的檔案格式。\n"
+
+#: ../src/utils/pacat.c:786
+#, c-format
+msgid ""
+"pacat %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pacat %s\n"
+"以 libpulse %s 編譯\n"
+"以 libpulse %s 連結\n"
+
+#: ../src/utils/pacat.c:819 ../src/utils/pactl.c:1400
+#, c-format
+msgid "Invalid client name '%s'"
+msgstr "無效的客戶端名稱「%s」"
+
+#: ../src/utils/pacat.c:834
+#, c-format
+msgid "Invalid stream name '%s'"
+msgstr "無效的串流名稱「%s」"
+
+#: ../src/utils/pacat.c:871
+#, c-format
+msgid "Invalid channel map '%s'"
+msgstr "無效的聲道對應表「%s」"
+
+#: ../src/utils/pacat.c:900 ../src/utils/pacat.c:914
+#, c-format
+msgid "Invalid latency specification '%s'"
+msgstr "無效的延遲規格「%s」"
+
+#: ../src/utils/pacat.c:907 ../src/utils/pacat.c:921
+#, c-format
+msgid "Invalid process time specification '%s'"
+msgstr "無效的程序時間規格「%s」"
+
+#: ../src/utils/pacat.c:933
+#, c-format
+msgid "Invalid property '%s'"
+msgstr "無效的屬性「%s」"
+
+#: ../src/utils/pacat.c:952
+#, c-format
+msgid "Unknown file format %s."
+msgstr "未知檔案格式 %s。"
+
+#: ../src/utils/pacat.c:971
+msgid "Invalid sample specification"
+msgstr "無效的取樣規格"
+
+#: ../src/utils/pacat.c:981
+#, c-format
+msgid "open(): %s"
+msgstr "open():%s"
+
+#: ../src/utils/pacat.c:986
+#, c-format
+msgid "dup2(): %s"
+msgstr "dup2():%s"
+
+#: ../src/utils/pacat.c:993
+msgid "Too many arguments."
+msgstr "太多參數。"
+
+#: ../src/utils/pacat.c:1004
+msgid "Failed to generate sample specification for file."
+msgstr "未能替檔案產生取樣規格。"
+
+#: ../src/utils/pacat.c:1030
+msgid "Failed to open audio file."
+msgstr "未能開啟音效檔。"
+
+#: ../src/utils/pacat.c:1036
+msgid "Warning: specified sample specification will be overwritten with specification from file."
+msgstr "警告:指定的取樣規格將會覆寫從檔案得到的規格。"
+
+#: ../src/utils/pacat.c:1039 ../src/utils/pactl.c:1467
+msgid "Failed to determine sample specification from file."
+msgstr "未能從檔案得知取樣規格。"
+
+#: ../src/utils/pacat.c:1048
+msgid "Warning: Failed to determine channel map from file."
+msgstr "警告:未能從檔案取得聲道對應表。"
+
+#: ../src/utils/pacat.c:1059
+msgid "Channel map doesn't match sample specification"
+msgstr "聲道對應表與取樣規格不符"
+
+#: ../src/utils/pacat.c:1070
+msgid "Warning: failed to write channel map to file."
+msgstr "警告:未能將聲道對應表寫入檔案。"
+
+#: ../src/utils/pacat.c:1085
+#, c-format
+msgid "Opening a %s stream with sample specification '%s' and channel map '%s'."
+msgstr "正在開啟一道 %s 串流,取樣規格為「%s」,聲道對應表為「%s」。"
+
+#: ../src/utils/pacat.c:1086
+msgid "recording"
+msgstr "錄製中"
+
+#: ../src/utils/pacat.c:1086
+msgid "playback"
+msgstr "播放控制"
+
+#: ../src/utils/pacat.c:1110
+msgid "Failed to set media name."
+msgstr "未能設定媒體名稱。"
+
+#: ../src/utils/pacat.c:1117 ../src/utils/pactl.c:1777
+msgid "pa_mainloop_new() failed."
+msgstr "pa_mainloop_new() 失敗。"
+
+#: ../src/utils/pacat.c:1136
+msgid "io_new() failed."
+msgstr "io_new() 失敗。"
+
+#: ../src/utils/pacat.c:1143 ../src/utils/pactl.c:1789
+msgid "pa_context_new() failed."
+msgstr "pa_context_new() 失敗。"
+
+#: ../src/utils/pacat.c:1151 ../src/utils/pactl.c:1795
+#, c-format
+msgid "pa_context_connect() failed: %s"
+msgstr "pa_context_connect() 失敗:%s"
+
+#: ../src/utils/pacat.c:1157
+msgid "pa_context_rttime_new() failed."
+msgstr "pa_context_rttime_new() 失敗。"
+
+#: ../src/utils/pacat.c:1164 ../src/utils/pactl.c:1800
+msgid "pa_mainloop_run() failed."
+msgstr "pa_mainloop_run() 失敗。"
+
+#: ../src/utils/pasuspender.c:79
+#, c-format
+msgid "fork(): %s\n"
+msgstr "fork():%s\n"
+
+#: ../src/utils/pasuspender.c:90
+#, c-format
+msgid "execvp(): %s\n"
+msgstr "execvp():%s\n"
+
+#: ../src/utils/pasuspender.c:107
+#, c-format
+msgid "Failure to suspend: %s\n"
+msgstr "未能暫停:%s\n"
+
+#: ../src/utils/pasuspender.c:122
+#, c-format
+msgid "Failure to resume: %s\n"
+msgstr "未能恢復:%s\n"
+
+#: ../src/utils/pasuspender.c:145
+#, c-format
+msgid "WARNING: Sound server is not local, not suspending.\n"
+msgstr "警告:音效伺服器並非本機,不會暫停。\n"
+
+#: ../src/utils/pasuspender.c:157
+#, c-format
+msgid "Connection failure: %s\n"
+msgstr "連線失敗:%s\n"
+
+#: ../src/utils/pasuspender.c:174
+#, c-format
+msgid "Got SIGINT, exiting.\n"
+msgstr "已取得 SIGINT,正在退出。\n"
+
+#: ../src/utils/pasuspender.c:192
+#, c-format
+msgid "WARNING: Child process terminated by signal %u\n"
+msgstr "警告:子代程序已被訊號 %u 所終止\n"
+
+#: ../src/utils/pasuspender.c:210
+#, c-format
+msgid ""
+"%s [options] ... \n"
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"\n"
+msgstr ""
+"%s [選項] ... \n"
+"\n"
+"  -h, --help                            顯示此幫助\n"
+"      --version                         顯示版本\n"
+"  -s, --server=SERVER                   要連接的伺服器名稱\n"
+"\n"
+
+#: ../src/utils/pasuspender.c:248
+#, c-format
+msgid ""
+"pasuspender %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pasuspender %s\n"
+"以 libpulse %s 編譯\n"
+"以 libpulse %s 連結\n"
+
+#: ../src/utils/pasuspender.c:277
+#, c-format
+msgid "pa_mainloop_new() failed.\n"
+msgstr "pa_mainloop_new() 失敗。\n"
+
+#: ../src/utils/pasuspender.c:290
+#, c-format
+msgid "pa_context_new() failed.\n"
+msgstr "pa_context_new() 失敗。\n"
+
+#: ../src/utils/pasuspender.c:298
+#, c-format
+msgid "pa_mainloop_run() failed.\n"
+msgstr "pa_mainloop_run() 失敗。\n"
+
+#: ../src/utils/pactl.c:150
+#, c-format
+msgid "Failed to get statistics: %s"
+msgstr "未能取得統計:%s"
+
+#: ../src/utils/pactl.c:156
+#, c-format
+msgid "Currently in use: %u blocks containing %s bytes total.\n"
+msgstr "目前使用中:%u 個區塊共包含 %s bytes。\n"
+
+#: ../src/utils/pactl.c:159
+#, c-format
+msgid "Allocated during whole lifetime: %u blocks containing %s bytes total.\n"
+msgstr "在整個生命週期間分配:%u 個區塊共包含 %s bytes。\n"
+
+#: ../src/utils/pactl.c:162
+#, c-format
+msgid "Sample cache size: %s\n"
+msgstr "取樣快取大小:%s\n"
+
+#: ../src/utils/pactl.c:171
+#, c-format
+msgid "Failed to get server information: %s"
+msgstr "未能取得伺服器資訊:%s"
+
+#: ../src/utils/pactl.c:176
+#, c-format
+msgid ""
+"Server String: %s\n"
+"Library Protocol Version: %u\n"
+"Server Protocol Version: %u\n"
+"Is Local: %s\n"
+"Client Index: %u\n"
+"Tile Size: %zu\n"
+msgstr ""
+"伺服器字串:%s\n"
+"函式庫協定版本:%u\n"
+"伺服器協定版本:%u\n"
+"是否為本地端:%s\n"
+"客戶端索引:%u\n"
+"Tile 大小:%zu\n"
+
+#: ../src/utils/pactl.c:192
+#, c-format
+msgid ""
+"User Name: %s\n"
+"Host Name: %s\n"
+"Server Name: %s\n"
+"Server Version: %s\n"
+"Default Sample Specification: %s\n"
+"Default Channel Map: %s\n"
+"Default Sink: %s\n"
+"Default Source: %s\n"
+"Cookie: %04x:%04x\n"
+msgstr ""
+"使用者名稱:%s\n"
+"主機名稱:%s\n"
+"伺服器名稱:%s\n"
+"伺服器版本:%s\n"
+"預設樣本規格:%s\n"
+"預設聲道對應表:%s\n"
+"預設 Sink:%s\n"
+"預設來源:%s\n"
+"Cookie:%04x:%04x\n"
+
+#: ../src/utils/pactl.c:244 ../src/utils/pactl.c:830
+#, c-format
+msgid "Failed to get sink information: %s"
+msgstr "未能取得 sink 資訊:%s"
+
+#: ../src/utils/pactl.c:270
+#, c-format
+msgid ""
+"Sink #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor Source: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink #%u\n"
+"\t狀態:%s\n"
+"\t名稱:%s\n"
+"\t描述:%s\n"
+"\t驅動程式:%s\n"
+"\t取樣規格:%s\n"
+"\t聲道對應表:%s\n"
+"\t擁有者模組:%u\n"
+"\t靜音:%s\n"
+"\t音量:%s%s%s\n"
+"\t        平衡 %0.2f\n"
+"\t基礎音量:%s%s%s\n"
+"\t監視器來源:%s\n"
+"\t延遲:%0.0f 微秒,已設定 %0.0f 微秒\n"
+"\t旗標:%s%s%s%s%s%s%s\n"
+"\t屬性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:318 ../src/utils/pactl.c:430
+#, c-format
+msgid "\tPorts:\n"
+msgstr "\t連接埠:\n"
+
+#: ../src/utils/pactl.c:325 ../src/utils/pactl.c:437
+#, c-format
+msgid "\tActive Port: %s\n"
+msgstr "\t使用中連接埠:%s\n"
+
+#: ../src/utils/pactl.c:331 ../src/utils/pactl.c:443
+#, c-format
+msgid "\tFormats:\n"
+msgstr "\t格式:\n"
+
+#: ../src/utils/pactl.c:357 ../src/utils/pactl.c:849
+#, c-format
+msgid "Failed to get source information: %s"
+msgstr "未能取得來源資訊:%s"
+
+#: ../src/utils/pactl.c:383
+#, c-format
+msgid ""
+"Source #%u\n"
+"\tState: %s\n"
+"\tName: %s\n"
+"\tDescription: %s\n"
+"\tDriver: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tOwner Module: %u\n"
+"\tMute: %s\n"
+"\tVolume: %s%s%s\n"
+"\t        balance %0.2f\n"
+"\tBase Volume: %s%s%s\n"
+"\tMonitor of Sink: %s\n"
+"\tLatency: %0.0f usec, configured %0.0f usec\n"
+"\tFlags: %s%s%s%s%s%s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Source #%u\n"
+"\t狀態:%s\n"
+"\t名稱:%s\n"
+"\t描述:%s\n"
+"\t驅動程式:%s\n"
+"\t取樣規格:%s\n"
+"\t聲道對應表:%s\n"
+"\t擁有者模組:%u\n"
+"\t靜音:%s\n"
+"\t音量:%s%s%s\n"
+"\t        平衡 %0.2f\n"
+"\t基礎音量:%s%s%s\n"
+"\tSink 的監視器:%s\n"
+"\t延遲:%0.0f 微秒,已設定 %0.0f 微秒\n"
+"\t旗標:%s%s%s%s%s%s\n"
+"\t屬性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:415 ../src/utils/pactl.c:485 ../src/utils/pactl.c:528
+#: ../src/utils/pactl.c:570 ../src/utils/pactl.c:640 ../src/utils/pactl.c:641
+#: ../src/utils/pactl.c:652 ../src/utils/pactl.c:711 ../src/utils/pactl.c:712
+#: ../src/utils/pactl.c:723 ../src/utils/pactl.c:775 ../src/utils/pactl.c:776
+#: ../src/utils/pactl.c:783
+msgid "n/a"
+msgstr "n/a"
+
+#: ../src/utils/pactl.c:454
+#, c-format
+msgid "Failed to get module information: %s"
+msgstr "未能取得模組資訊:%s"
+
+#: ../src/utils/pactl.c:477
+#, c-format
+msgid ""
+"Module #%u\n"
+"\tName: %s\n"
+"\tArgument: %s\n"
+"\tUsage counter: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Module #%u\n"
+"\t名稱:%s\n"
+"\t引數:%s\n"
+"\t用量計數器:%s\n"
+"\t屬性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:496
+#, c-format
+msgid "Failed to get client information: %s"
+msgstr "未能取得客戶端資訊:%s"
+
+#: ../src/utils/pactl.c:522
+#, c-format
+msgid ""
+"Client #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Client #%u\n"
+"\t驅動程式:%s\n"
+"\t使用者模組:%s\n"
+"\t屬性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:539
+#, c-format
+msgid "Failed to get card information: %s"
+msgstr "未能取得音效卡資訊:%s"
+
+#: ../src/utils/pactl.c:562
+#, c-format
+msgid ""
+"Card #%u\n"
+"\tName: %s\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Card #%u\n"
+"\t名稱:%s\n"
+"\t驅動程式:%s\n"
+"\t使用者模組:%s\n"
+"\t屬性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:576
+#, c-format
+msgid "\tProfiles:\n"
+msgstr "\t個人設定檔:\n"
+
+#: ../src/utils/pactl.c:582
+#, c-format
+msgid "\tActive Profile: %s\n"
+msgstr "\t啟用的個人設定檔:%s\n"
+
+#: ../src/utils/pactl.c:593 ../src/utils/pactl.c:868
+#, c-format
+msgid "Failed to get sink input information: %s"
+msgstr "未能取得 sink 輸入資訊:%s"
+
+#: ../src/utils/pactl.c:622
+#, c-format
+msgid ""
+"Sink Input #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSink: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSink Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sink Input #%u\n"
+"\t驅動程式:%s\n"
+"\t擁有者模組:%s\n"
+"\t客戶端:%s\n"
+"\tSink:%u\n"
+"\t取樣規格:%s\n"
+"\t聲道對應表:%s\n"
+"\t格式:%s\n"
+"\t靜音:%s\n"
+"\t音量:%s\n"
+"\t        %s\n"
+"\t        平衡 %0.2f\n"
+"\t緩衝延遲:%0.0f usec\n"
+"\tSink 延遲:%0.0f usec\n"
+"\t重新取樣方法:%s\n"
+"\t屬性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:663 ../src/utils/pactl.c:887
+#, c-format
+msgid "Failed to get source output information: %s"
+msgstr "未能取得來源輸出資訊:%s"
+
+#: ../src/utils/pactl.c:693
+#, c-format
+msgid ""
+"Source Output #%u\n"
+"\tDriver: %s\n"
+"\tOwner Module: %s\n"
+"\tClient: %s\n"
+"\tSource: %u\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tFormat: %s\n"
+"\tMute: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tBuffer Latency: %0.0f usec\n"
+"\tSource Latency: %0.0f usec\n"
+"\tResample method: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Source Output #%u\n"
+"\t驅動程式:%s\n"
+"\t擁有者模組:%s\n"
+"\t客戶端:%s\n"
+"\t來源:%u\n"
+"\t取樣規格:%s\n"
+"\t聲道對應表:%s\n"
+"\t格式:%s\n"
+"\t靜音:%s\n"
+"\t音量:%s\n"
+"\t        %s\n"
+"\t        平衡 %0.2f\n"
+"\t緩衝延遲:%0.0f usec\n"
+"\tSink 延遲:%0.0f usec\n"
+"\t重新取樣方法:%s\n"
+"\t屬性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:734
+#, c-format
+msgid "Failed to get sample information: %s"
+msgstr "未能取得取樣資訊:%s"
+
+#: ../src/utils/pactl.c:761
+#, c-format
+msgid ""
+"Sample #%u\n"
+"\tName: %s\n"
+"\tSample Specification: %s\n"
+"\tChannel Map: %s\n"
+"\tVolume: %s\n"
+"\t        %s\n"
+"\t        balance %0.2f\n"
+"\tDuration: %0.1fs\n"
+"\tSize: %s\n"
+"\tLazy: %s\n"
+"\tFilename: %s\n"
+"\tProperties:\n"
+"\t\t%s\n"
+msgstr ""
+"Sample #%u\n"
+"\t名稱:%s\n"
+"\t取樣規格:%s\n"
+"\t聲道對應表:%s\n"
+"\t音量:%s\n"
+"\t        %s\n"
+"\t        平衡 %0.2f\n"
+"\t時間長度:%0.1fs\n"
+"\t大小:%s\n"
+"\tLazy:%s\n"
+"\t檔案名稱:%s\n"
+"\t屬性:\n"
+"\t\t%s\n"
+
+#: ../src/utils/pactl.c:791 ../src/utils/pactl.c:801
+#, c-format
+msgid "Failure: %s"
+msgstr "失敗:%s"
+
+#: ../src/utils/pactl.c:915
+#, c-format
+msgid "Failed to set format: invalid format string %s"
+msgstr "未能設定格式:無效的格式字串 %s"
+
+#: ../src/utils/pactl.c:954
+#, c-format
+msgid "Failed to upload sample: %s"
+msgstr "未能上傳樣本:%s"
+
+#: ../src/utils/pactl.c:971
+msgid "Premature end of file"
+msgstr "未完成的檔案結尾"
+
+#: ../src/utils/pactl.c:991
+msgid "new"
+msgstr "新增"
+
+#: ../src/utils/pactl.c:994
+msgid "change"
+msgstr "變更"
+
+#: ../src/utils/pactl.c:997
+msgid "remove"
+msgstr "移除"
+
+#: ../src/utils/pactl.c:1000 ../src/utils/pactl.c:1035
+msgid "unknown"
+msgstr "未知"
+
+#: ../src/utils/pactl.c:1008
+msgid "sink"
+msgstr "sink"
+
+#: ../src/utils/pactl.c:1011
+msgid "source"
+msgstr "source"
+
+#: ../src/utils/pactl.c:1014
+msgid "sink-input"
+msgstr "sink-input"
+
+#: ../src/utils/pactl.c:1017
+msgid "source-output"
+msgstr "source-output"
+
+#: ../src/utils/pactl.c:1020
+msgid "module"
+msgstr "module"
+
+#: ../src/utils/pactl.c:1023
+msgid "client"
+msgstr "client"
+
+#: ../src/utils/pactl.c:1026
+msgid "sample-cache"
+msgstr "sample-cache"
+
+#: ../src/utils/pactl.c:1029 ../src/utils/pactl.c:1032
+msgid "server"
+msgstr "server"
+
+#: ../src/utils/pactl.c:1041
+#, c-format
+msgid "Event '%s' on %s #%u\n"
+msgstr "事件「%s」 於  %s #%u\n"
+
+#: ../src/utils/pactl.c:1258
+msgid "Got SIGINT, exiting."
+msgstr "已取得 SIGINT,正在退出。"
+
+#: ../src/utils/pactl.c:1285
+msgid "Invalid volume specification"
+msgstr "無效的音量規格"
+
+#: ../src/utils/pactl.c:1308
+msgid "Volume outside permissible range.\n"
+msgstr "音量外部可允許範圍。\n"
+
+#: ../src/utils/pactl.c:1319 ../src/utils/pactl.c:1320
+#: ../src/utils/pactl.c:1321 ../src/utils/pactl.c:1322
+#: ../src/utils/pactl.c:1323 ../src/utils/pactl.c:1324
+#: ../src/utils/pactl.c:1325 ../src/utils/pactl.c:1326
+#: ../src/utils/pactl.c:1327 ../src/utils/pactl.c:1328
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1330
+#: ../src/utils/pactl.c:1331 ../src/utils/pactl.c:1332
+#: ../src/utils/pactl.c:1333 ../src/utils/pactl.c:1334
+#: ../src/utils/pactl.c:1335 ../src/utils/pactl.c:1336
+#: ../src/utils/pactl.c:1337
+msgid "[options]"
+msgstr "[選項]"
+
+#: ../src/utils/pactl.c:1321
+msgid "[TYPE]"
+msgstr "[TYPE]"
+
+#: ../src/utils/pactl.c:1323
+msgid "FILENAME [NAME]"
+msgstr "FILENAME [NAME]"
+
+#: ../src/utils/pactl.c:1324
+msgid "NAME [SINK]"
+msgstr "NAME [SINK]"
+
+#: ../src/utils/pactl.c:1325
+msgid "NAME"
+msgstr "NAME"
+
+#: ../src/utils/pactl.c:1326
+msgid "NAME [ARGS ...]"
+msgstr "NAME [ARGS ...]"
+
+#: ../src/utils/pactl.c:1327
+msgid "#N"
+msgstr "#N"
+
+#: ../src/utils/pactl.c:1328
+msgid "#N SINK|SOURCE"
+msgstr "#N SINK|SOURCE"
+
+#: ../src/utils/pactl.c:1329 ../src/utils/pactl.c:1334
+msgid "NAME|#N 1|0"
+msgstr "NAME|#N 1|0"
+
+#: ../src/utils/pactl.c:1330
+msgid "CARD PROFILE"
+msgstr "CARD PROFILE"
+
+#: ../src/utils/pactl.c:1331
+msgid "NAME|#N PORT"
+msgstr "NAME|#N PORT"
+
+#: ../src/utils/pactl.c:1332
+msgid "NAME|#N VOLUME"
+msgstr "NAME|#N VOLUME"
+
+#: ../src/utils/pactl.c:1333
+msgid "#N VOLUME"
+msgstr "#N VOLUME"
+
+#: ../src/utils/pactl.c:1335
+msgid "#N 1|0"
+msgstr "#N 1|0"
+
+#: ../src/utils/pactl.c:1336
+msgid "#N FORMATS"
+msgstr "#N FORMATS"
+
+#: ../src/utils/pactl.c:1339
+#, c-format
+msgid ""
+"\n"
+"  -h, --help                            Show this help\n"
+"      --version                         Show version\n"
+"\n"
+"  -s, --server=SERVER                   The name of the server to connect to\n"
+"  -n, --client-name=NAME                How to call this client on the server\n"
+msgstr ""
+"\n"
+"  -h, --help                            顯示此幫助\n"
+"      --version                         顯示版本\n"
+"\n"
+"  -s, --server=SERVER                   要連接的伺服器名稱\n"
+"  -n, --client-name=NAME                如何稱呼伺服器上的這個客戶端\n"
+
+#: ../src/utils/pactl.c:1380
+#, c-format
+msgid ""
+"pactl %s\n"
+"Compiled with libpulse %s\n"
+"Linked with libpulse %s\n"
+msgstr ""
+"pactl %s\n"
+"以 libpulse %s 編譯\n"
+"以 libpulse %s 連結\n"
+
+#: ../src/utils/pactl.c:1439
+#, c-format
+msgid "Specify nothing, or one of: %s"
+msgstr "沒有指定,或者為右述之一:%s"
+
+#: ../src/utils/pactl.c:1449
+msgid "Please specify a sample file to load"
+msgstr "請指定要載入的取樣檔"
+
+#: ../src/utils/pactl.c:1462
+msgid "Failed to open sound file."
+msgstr "未能開啟音效檔。"
+
+#: ../src/utils/pactl.c:1474
+msgid "Warning: Failed to determine sample specification from file."
+msgstr "警告:未能從檔案得知取樣規格。"
+
+#: ../src/utils/pactl.c:1484
+msgid "You have to specify a sample name to play"
+msgstr "您必須指定一個要播放的樣本名稱"
+
+#: ../src/utils/pactl.c:1496
+msgid "You have to specify a sample name to remove"
+msgstr "您必須指定一個要移除的樣本名稱"
+
+#: ../src/utils/pactl.c:1505
+msgid "You have to specify a sink input index and a sink"
+msgstr "您必須指定一項 sink 輸入索引與一個 sink"
+
+#: ../src/utils/pactl.c:1515
+msgid "You have to specify a source output index and a source"
+msgstr "您必須指定一項來源輸出索引與一個來源"
+
+#: ../src/utils/pactl.c:1530
+msgid "You have to specify a module name and arguments."
+msgstr "您必須指定一個模組名稱與一些參數。"
+
+#: ../src/utils/pactl.c:1550
+msgid "You have to specify a module index"
+msgstr "您必須指定一項模組索引"
+
+#: ../src/utils/pactl.c:1560
+msgid "You may not specify more than one sink. You have to specify a boolean value."
+msgstr "您指定的 sink 數不能超過一個。您必須指定一項布林值。"
+
+#: ../src/utils/pactl.c:1573
+msgid "You may not specify more than one source. You have to specify a boolean value."
+msgstr "您指定的來源數不能超過一個。您必須指定一項布林值。"
+
+#: ../src/utils/pactl.c:1585
+msgid "You have to specify a card name/index and a profile name"
+msgstr "您必須指定一個音效卡名稱/索引,以及設定組合名稱"
+
+#: ../src/utils/pactl.c:1596
+msgid "You have to specify a sink name/index and a port name"
+msgstr "您必須指定一個 sink 名稱/索引,以及連接埠名稱"
+
+#: ../src/utils/pactl.c:1607
+msgid "You have to specify a source name/index and a port name"
+msgstr "您必須指定一個來源名稱/索引,以及連接埠名稱"
+
+#: ../src/utils/pactl.c:1618
+msgid "You have to specify a sink name/index and a volume"
+msgstr "您必須指定一個 sink 名稱/索引,以及一項音量"
+
+#: ../src/utils/pactl.c:1631
+msgid "You have to specify a source name/index and a volume"
+msgstr "您必須指定一個來源名稱/索引,以及一項音量"
+
+#: ../src/utils/pactl.c:1644
+msgid "You have to specify a sink input index and a volume"
+msgstr "您必須指定一個 sink 輸入索引,以及一項音量"
+
+#: ../src/utils/pactl.c:1649
+msgid "Invalid sink input index"
+msgstr "無效的 sink 輸入索引"
+
+#: ../src/utils/pactl.c:1660
+msgid "You have to specify a source output index and a volume"
+msgstr "您必須指定一個來源輸出索引,以及一項音量"
+
+#: ../src/utils/pactl.c:1665
+msgid "Invalid source output index"
+msgstr "無效的來源輸出索引"
+
+#: ../src/utils/pactl.c:1677
+msgid "You have to specify a sink name/index and a mute boolean"
+msgstr "您必須指定一個 sink 名稱/索引,以及一項靜音布林值"
+
+#: ../src/utils/pactl.c:1682 ../src/utils/pactl.c:1699
+#: ../src/utils/pactl.c:1721 ../src/utils/pactl.c:1742
+msgid "Invalid mute specification"
+msgstr "無效的靜音規格"
+
+#: ../src/utils/pactl.c:1694
+msgid "You have to specify a source name/index and a mute boolean"
+msgstr "您必須指定一個來源名稱/索引,以及一項靜音布林值"
+
+#: ../src/utils/pactl.c:1711
+msgid "You have to specify a sink input index and a mute boolean"
+msgstr "您必須指定一個 sink 輸入索引,以及一項靜音布林值"
+
+#: ../src/utils/pactl.c:1716
+msgid "Invalid sink input index specification"
+msgstr "無效的 sink 輸入索引規格"
+
+#: ../src/utils/pactl.c:1732
+msgid "You have to specify a source output index and a mute boolean"
+msgstr "您必須指定一個來源輸出索引,以及一項靜音布林值"
+
+#: ../src/utils/pactl.c:1737
+msgid "Invalid source output index specification"
+msgstr "無效的來源輸出索引規格"
+
+#: ../src/utils/pactl.c:1756
+msgid "You have to specify a sink index and a semicolon-separated list of supported formats"
+msgstr "您必須指定一個 sink 索引,以及一份以半形分號分隔、列有支援格式的清單"
+
+#: ../src/utils/pactl.c:1772
+msgid "No valid command specified."
+msgstr "沒有指定有效的命令。"
+
+#: ../src/utils/pax11publish.c:61
+#, c-format
+msgid ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    Show current PulseAudio data attached to X11 display (default)\n"
+" -e    Export local PulseAudio data to X11 display\n"
+" -i    Import PulseAudio data from X11 display to local environment variables and cookie file.\n"
+" -r    Remove PulseAudio data from X11 display\n"
+msgstr ""
+"%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n"
+"\n"
+" -d    顯示目前連接至 X11 顯示的 PulseAudio 資料 (預設)\n"
+" -e    輸出本地端 PulseAudio 資料至 X11 顯示\n"
+" -i    從 X11 顯示匯入 PulseAudio 資料至本地端環境變數與 cookie 檔案。\n"
+" -r    從 X11 顯示移除 PulseAudio 資料\n"
+
+#: ../src/utils/pax11publish.c:94
+#, c-format
+msgid "Failed to parse command line.\n"
+msgstr "未能解析命令列。\n"
+
+#: ../src/utils/pax11publish.c:113
+#, c-format
+msgid "Server: %s\n"
+msgstr "伺服器:%s\n"
+
+#: ../src/utils/pax11publish.c:115
+#, c-format
+msgid "Source: %s\n"
+msgstr "來源:%s\n"
+
+#: ../src/utils/pax11publish.c:117
+#, c-format
+msgid "Sink: %s\n"
+msgstr "Sink:%s\n"
+
+#: ../src/utils/pax11publish.c:119
+#, c-format
+msgid "Cookie: %s\n"
+msgstr "Cookie:%s\n"
+
+#: ../src/utils/pax11publish.c:137
+#, c-format
+msgid "Failed to parse cookie data\n"
+msgstr "未能解析 cookie 資料\n"
+
+#: ../src/utils/pax11publish.c:142
+#, c-format
+msgid "Failed to save cookie data\n"
+msgstr "未能儲存 cookie 資料\n"
+
+#: ../src/utils/pax11publish.c:157
+#, c-format
+msgid "Failed to load client configuration file.\n"
+msgstr "未能載入客戶端組態檔。\n"
+
+#: ../src/utils/pax11publish.c:162
+#, c-format
+msgid "Failed to read environment configuration data.\n"
+msgstr "未能讀取環境組態資料。\n"
+
+#: ../src/utils/pax11publish.c:179
+#, c-format
+msgid "Failed to get FQDN.\n"
+msgstr "未能取得 FQDN。\n"
+
+#: ../src/utils/pax11publish.c:199
+#, c-format
+msgid "Failed to load cookie data\n"
+msgstr "未能載入 cookie 資料\n"
+
+#: ../src/utils/pax11publish.c:217
+#, c-format
+msgid "Not yet implemented.\n"
+msgstr "尚未實作。\n"
+
+#: ../src/utils/pacmd.c:66
+msgid "No PulseAudio daemon running, or not running as session daemon."
+msgstr "無 PulseAudio 幕後程式正執行中,或者未以作業階段幕後程式執行中。"
+
+#: ../src/utils/pacmd.c:71
+#, c-format
+msgid "socket(PF_UNIX, SOCK_STREAM, 0): %s"
+msgstr "socket(PF_UNIX, SOCK_STREAM, 0):%s"
+
+#: ../src/utils/pacmd.c:88
+#, c-format
+msgid "connect(): %s"
+msgstr "connect():%s"
+
+#: ../src/utils/pacmd.c:96
+msgid "Failed to kill PulseAudio daemon."
+msgstr "未能終止 PulseAudio 幕後程式。"
+
+#: ../src/utils/pacmd.c:104
+msgid "Daemon not responding."
+msgstr "幕後程式沒有回應。"
+
+#: ../src/utils/pacmd.c:184
+#, c-format
+msgid "poll(): %s"
+msgstr "poll():%s"
+
+#: ../src/utils/pacmd.c:195 ../src/utils/pacmd.c:215
+#, c-format
+msgid "read(): %s"
+msgstr "read():%s"
+
+#: ../src/utils/pacmd.c:237 ../src/utils/pacmd.c:255
+#, c-format
+msgid "write(): %s"
+msgstr "write():%s"
+
+#: ../src/pulsecore/lock-autospawn.c:136 ../src/pulsecore/lock-autospawn.c:222
+msgid "Cannot access autospawn lock."
+msgstr "無法存取 autospawn 鎖。"
+
+#: ../src/modules/alsa/alsa-sink.c:560 ../src/modules/alsa/alsa-sink.c:726
+#, c-format
+msgid ""
+"ALSA woke us up to write new data to the device, but there was actually nothing to write!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+"We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."
+msgstr ""
+"ALSA 喚醒我們以寫入新資料至該裝置,但實際上沒有要寫入的資料!\n"
+"這很可能是 ALSA 驅動程式「%s」的臭蟲。請回報此問題給 ALSA 開發者。\n"
+"我們被 POLLOUT 設定喚醒 -- 然而後續的 snd_pcm_avail() 傳回 0 或另一個值 < min_avail。"
+
+#: ../src/modules/alsa/alsa-source.c:519 ../src/modules/alsa/alsa-source.c:672
+#, c-format
+msgid ""
+"ALSA woke us up to read new data from the device, but there was actually nothing to read!\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+"We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."
+msgstr ""
+"ALSA 喚醒我們從該裝置讀取新資料,但實際上沒有可讀取的資料!\n"
+"這很可能是 ALSA 驅動程式「%s」的臭蟲。請回報此問題給 ALSA 開發者。\n"
+"我們被 POLLIN 設定喚醒 -- 然而後續的 snd_pcm_avail() 傳回 0 或另一個值 < min_avail。"
+
+#: ../src/modules/alsa/module-alsa-card.c:167
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2796
+#: ../src/modules/alsa/alsa-mixer.c:3898
+msgid "Off"
+msgstr "關閉"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2738
+msgid "High Fidelity Playback (A2DP)"
+msgstr "高傳真播放裝置 (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2752
+msgid "High Fidelity Capture (A2DP)"
+msgstr "高傳真擷取裝置 (A2DP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2767
+msgid "Telephony Duplex (HSP/HFP)"
+msgstr "電話雙工 (HSP/HFP)"
+
+#: ../src/modules/bluetooth/module-bluetooth-device.c:2781
+msgid "Handsfree Gateway"
+msgstr "免持閘道"
+
+#: ../src/modules/reserve-wrap.c:151
+msgid "PulseAudio Sound Server"
+msgstr "PulseAudio 音效伺服器"
+
+#: ../src/modules/module-rygel-media-server.c:510
+#: ../src/modules/module-rygel-media-server.c:548
+#: ../src/modules/module-rygel-media-server.c:903
+msgid "Output Devices"
+msgstr "輸出裝置"
+
+#: ../src/modules/module-rygel-media-server.c:511
+#: ../src/modules/module-rygel-media-server.c:549
+#: ../src/modules/module-rygel-media-server.c:904
+msgid "Input Devices"
+msgstr "輸入裝置"
+
+#: ../src/modules/module-rygel-media-server.c:1056
+msgid "Audio on @HOSTNAME@"
+msgstr "音效位於 @HOSTNAME@"
+
+#: ../src/modules/alsa/alsa-mixer.c:2219
+msgid "Input"
+msgstr "輸入"
+
+#: ../src/modules/alsa/alsa-mixer.c:2220
+msgid "Docking Station Input"
+msgstr "Docking Station 輸入"
+
+#: ../src/modules/alsa/alsa-mixer.c:2221
+msgid "Docking Station Microphone"
+msgstr "Docking Station 麥克風"
+
+#: ../src/modules/alsa/alsa-mixer.c:2222
+msgid "Docking Station Line In"
+msgstr "Docking Station 線路輸入"
+
+#: ../src/modules/alsa/alsa-mixer.c:2223 ../src/modules/alsa/alsa-mixer.c:2307
+msgid "Line In"
+msgstr "線路輸入"
+
+#: ../src/modules/alsa/alsa-mixer.c:2224 ../src/modules/alsa/alsa-mixer.c:2302
+msgid "Microphone"
+msgstr "麥克風"
+
+#: ../src/modules/alsa/alsa-mixer.c:2225 ../src/modules/alsa/alsa-mixer.c:2303
+msgid "Front Microphone"
+msgstr "前方麥克風"
+
+#: ../src/modules/alsa/alsa-mixer.c:2226 ../src/modules/alsa/alsa-mixer.c:2304
+msgid "Rear Microphone"
+msgstr "後方麥克風"
+
+#: ../src/modules/alsa/alsa-mixer.c:2227
+msgid "External Microphone"
+msgstr "外接麥克風"
+
+#: ../src/modules/alsa/alsa-mixer.c:2228 ../src/modules/alsa/alsa-mixer.c:2306
+msgid "Internal Microphone"
+msgstr "內建麥克風"
+
+#: ../src/modules/alsa/alsa-mixer.c:2229 ../src/modules/alsa/alsa-mixer.c:2308
+msgid "Radio"
+msgstr "無線電"
+
+#: ../src/modules/alsa/alsa-mixer.c:2230 ../src/modules/alsa/alsa-mixer.c:2309
+msgid "Video"
+msgstr "視訊"
+
+#: ../src/modules/alsa/alsa-mixer.c:2231
+msgid "Automatic Gain Control"
+msgstr "自動增益控制"
+
+#: ../src/modules/alsa/alsa-mixer.c:2232
+msgid "No Automatic Gain Control"
+msgstr "無自動增益控制"
+
+#: ../src/modules/alsa/alsa-mixer.c:2233
+msgid "Boost"
+msgstr "增強"
+
+#: ../src/modules/alsa/alsa-mixer.c:2234
+msgid "No Boost"
+msgstr "無增強"
+
+#: ../src/modules/alsa/alsa-mixer.c:2235
+msgid "Amplifier"
+msgstr "擴大器"
+
+#: ../src/modules/alsa/alsa-mixer.c:2236
+msgid "No Amplifier"
+msgstr "無擴大器"
+
+#: ../src/modules/alsa/alsa-mixer.c:2237
+msgid "Bass Boost"
+msgstr "低音增強"
+
+#: ../src/modules/alsa/alsa-mixer.c:2238
+msgid "No Bass Boost"
+msgstr "無低音增強"
+
+#: ../src/modules/alsa/alsa-mixer.c:2239
+msgid "Speaker"
+msgstr "喇叭"
+
+#: ../src/modules/alsa/alsa-mixer.c:2240 ../src/modules/alsa/alsa-mixer.c:2311
+msgid "Headphones"
+msgstr "頭戴式耳機"
+
+#: ../src/modules/alsa/alsa-mixer.c:2301
+msgid "Analog Input"
+msgstr "類比輸入"
+
+#: ../src/modules/alsa/alsa-mixer.c:2305
+msgid "Dock Microphone"
+msgstr "臺座麥克風"
+
+#: ../src/modules/alsa/alsa-mixer.c:2310
+msgid "Analog Output"
+msgstr "類比輸出"
+
+#: ../src/modules/alsa/alsa-mixer.c:2312
+msgid "LFE on Separate Mono Output"
+msgstr "LFE 於分隔單聲道輸出"
+
+#: ../src/modules/alsa/alsa-mixer.c:2313
+msgid "Line Out"
+msgstr "線路輸出"
+
+#: ../src/modules/alsa/alsa-mixer.c:2314
+msgid "Analog Mono Output"
+msgstr "類比單聲道輸出"
+
+#: ../src/modules/alsa/alsa-mixer.c:2315
+msgid "Speakers"
+msgstr "喇叭"
+
+#: ../src/modules/alsa/alsa-mixer.c:2316
+msgid "HDMI / DisplayPort"
+msgstr "HDMI / DisplayPort"
+
+#: ../src/modules/alsa/alsa-mixer.c:2317
+msgid "Digital Output (S/PDIF)"
+msgstr "數位輸出 (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:2318
+msgid "Digital Passthrough (S/PDIF)"
+msgstr "數位通透 (S/PDIF)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3756
+msgid "Analog Mono"
+msgstr "類比單聲道"
+
+#: ../src/modules/alsa/alsa-mixer.c:3757
+msgid "Analog Stereo"
+msgstr "類比立體聲"
+
+#: ../src/modules/alsa/alsa-mixer.c:3758
+msgid "Analog Surround 2.1"
+msgstr "類比環繞聲 2.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3759
+msgid "Analog Surround 3.0"
+msgstr "類比環繞聲 3.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3760
+msgid "Analog Surround 3.1"
+msgstr "類比環繞聲 3.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3761
+msgid "Analog Surround 4.0"
+msgstr "類比環繞聲 4.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3762
+msgid "Analog Surround 4.1"
+msgstr "類比環繞聲 4.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3763
+msgid "Analog Surround 5.0"
+msgstr "類比環繞聲 5.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3764
+msgid "Analog Surround 5.1"
+msgstr "類比環繞聲 5.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3765
+msgid "Analog Surround 6.0"
+msgstr "類比環繞聲 6.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3766
+msgid "Analog Surround 6.1"
+msgstr "類比環繞聲 6.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3767
+msgid "Analog Surround 7.0"
+msgstr "類比環繞聲 7.0"
+
+#: ../src/modules/alsa/alsa-mixer.c:3768
+msgid "Analog Surround 7.1"
+msgstr "類比環繞聲 7.1"
+
+#: ../src/modules/alsa/alsa-mixer.c:3769
+msgid "Digital Stereo (IEC958)"
+msgstr "數位立體聲 (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3770
+msgid "Digital Passthrough  (IEC958)"
+msgstr "數位通透 (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3771
+msgid "Digital Surround 4.0 (IEC958/AC3)"
+msgstr "數位環繞聲 4.0 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3772
+msgid "Digital Surround 5.1 (IEC958/AC3)"
+msgstr "數位環繞聲 5.1 (IEC958/AC3)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3773
+msgid "Digital Stereo (HDMI)"
+msgstr "數位立體聲 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3774
+msgid "Digital Surround 5.1 (HDMI)"
+msgstr "數位環繞聲 5.1 (HDMI)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3895
+msgid "Analog Mono Duplex"
+msgstr "類比單聲道雙工"
+
+#: ../src/modules/alsa/alsa-mixer.c:3896
+msgid "Analog Stereo Duplex"
+msgstr "類比立體聲雙工"
+
+#: ../src/modules/alsa/alsa-mixer.c:3897
+msgid "Digital Stereo Duplex (IEC958)"
+msgstr "數位立體聲雙工 (IEC958)"
+
+#: ../src/modules/alsa/alsa-mixer.c:3997
+#, c-format
+msgid "%s Output"
+msgstr "%s 輸出"
+
+#: ../src/modules/alsa/alsa-mixer.c:4005
+#, c-format
+msgid "%s Input"
+msgstr "%s 輸入"
+
+#: ../src/modules/echo-cancel/module-echo-cancel.c:63
+msgid "source_name=<name for the source> source_properties=<properties for the source> source_master=<name of source to filter> sink_name=<name for the sink> sink_properties=<properties for the sink> sink_master=<name of sink to filter> adjust_time=<how often to readjust rates in s> adjust_threshold=<how much drift to readjust after in ms> format=<sample format> rate=<sample rate> channels=<number of channels> channel_map=<channel map> aec_method=<implementation to use> aec_args=<parameters for the AEC engine> save_aec=<save AEC data in /tmp> autoloaded=<set if this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr "source_name=<來源的名稱> source_properties=<來源的屬性> source_master=<要過濾的來源名稱> sink_name=<sink 的名稱> sink_properties=<sink 的屬性> sink_master=<要過濾的 sink 名稱> adjust_time=<多久 (秒) 重新調整一次頻率> adjust_threshold=<在多少個 drift 後 (單位是微秒) 要重新調整> format=<取樣格式> rate=<取樣率> channels=<聲道數> channel_map=<聲道對應表> aec_method=<要使用的實作> aec_args=<AEC 引擎的參數> save_aec=<在 /tmp 內儲存 AEC 資料> autoloaded=<設定此模組是否正被自動載入> use_volume_sharing=<yes 或 no> "
+
+#: ../src/modules/module-equalizer-sink.c:72
+msgid "General Purpose Equalizer"
+msgstr "通用等化器"
+
+#: ../src/modules/module-equalizer-sink.c:76
+msgid "sink_name=<name of the sink> sink_properties=<properties for the sink> sink_master=<sink to connect to> format=<sample format> rate=<sample rate> channels=<number of channels> channel_map=<channel map> autoloaded=<set if this module is being loaded automatically> use_volume_sharing=<yes or no> "
+msgstr "sink_name=<sink 的名稱> sink_properties=<sink 的屬性> sink_master=<要連接的 sink> format=<取樣格式> rate=<取樣率> channels=<聲道數> channel_map=<聲道對應表> autoloaded=<設定此模組是否正被自動載入> use_volume_sharing=<yes 或 no> "
+
+#: ../src/modules/module-filter-apply.c:48
+msgid "autoclean=<automatically unload unused filters?>"
+msgstr "autoclean=<是否自動取消未使用過濾器的載入?>"
+
+#: ../src/tests/resampler-test.c:257
+#, c-format
+msgid ""
+"%s [options]\n"
+"\n"
+"-h, --help                            Show this help\n"
+"-v, --verbose                         Print debug messages\n"
+"      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to 44100)\n"
+"      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+"      --from-channels=CHANNELS        From number of channels (defaults to 1)\n"
+"      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to 44100)\n"
+"      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+"      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+"      --resample-method=METHOD        Resample method (defaults to auto)\n"
+"      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+"\n"
+"If the formats are not specified, the test performs all formats combinations,\n"
+"back and forth.\n"
+"\n"
+"Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, alaw,\n"
+"32le, s32be (defaults to s16ne)\n"
+"\n"
+"See --dump-resample-methods for possible values of resample methods.\n"
+msgstr ""
+"%s [選項]\n"
+"\n"
+"-h, --help                            顯示此幫助\n"
+"-v, --verbose                         列印除錯訊息\n"
+"      --from-rate=SAMPLERATE          從取樣率,以 Hz 為單位 (預設值 44100)\n"
+"      --from-format=SAMPLEFORMAT      從取樣類型 (預設為 s16le)\n"
+"      --from-channels=CHANNELS        從聲道數 (預設為 1)\n"
+"      --to-rate=SAMPLERATE            至取樣率,以 Hz 為單位 (預設為 44100)\n"
+"      --to-format=SAMPLEFORMAT        至取樣類型 (預設為 s16le)\n"
+"      --to-channels=CHANNELS          至聲道數 (預設為 1)\n"
+"      --resample-method=METHOD        重新取樣方法 (預設為 auto)\n"
+"      --seconds=SECONDS               從串流時間長度 (預設為 60)\n"
+"\n"
+"如果格式未指定,測試會執行所有格式組合,\n"
+"交叉比對。\n"
+"\n"
+"取樣類型必須為右列之一 s16le、s16be、u8、float32le、float32be、ulaw、\n"
+"alaw、32le、s32be (預設為 s16ne)\n"
+"\n"
+"查看 --dump-resample-methods 以取得重新取樣方法可用的值。\n"
+
+#: ../src/tests/resampler-test.c:356
+#, c-format
+msgid "%s %s\n"
+msgstr "%s %s\n"
+
+#: ../src/tests/resampler-test.c:419
+#, c-format
+msgid "=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+msgstr "=== %d 秒:%d Hz %d ch (%s) -> %d Hz %d ch (%s)"
+
+#~ msgid "XOpenDisplay() failed"
+#~ msgstr "XOpenDisplay() 失敗"
+
+#~ msgid "%s+%s"
+#~ msgstr "%s+%s"
+
+#~ msgid "%s / %s"
+#~ msgstr "%s / %s"
+
+#~ msgid "Digital Surround 4.0 (IEC958)"
+#~ msgstr "數位環繞聲 4.0 (IEC958)"
+
+#~ msgid "Low Frequency Emmiter"
+#~ msgstr "低頻率揚聲器"
diff --git a/pulseaudio-text.svg b/pulseaudio-text.svg
new file mode 100644 (file)
index 0000000..0e12613
--- /dev/null
@@ -0,0 +1,388 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="48px"
+   height="48px"
+   id="svg2161"
+   sodipodi:version="0.32"
+   inkscape:version="0.45"
+   sodipodi:docbase="/home/lennart/projects/pulseaudio"
+   sodipodi:docname="pulseaudio.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   sodipodi:modified="TRUE">
+  <defs
+     id="defs2163">
+    <linearGradient
+       id="linearGradient3093">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop3095" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop3097" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3093"
+       id="radialGradient2472"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.266476,0,283.9565)"
+       cx="224.5"
+       cy="387.11252"
+       fx="224.5"
+       fy="387.11252"
+       r="174.5" />
+    <linearGradient
+       id="linearGradient2503">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop2505" />
+      <stop
+         style="stop-color:#141413;stop-opacity:1;"
+         offset="1"
+         id="stop2507" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2503"
+       id="linearGradient1476"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       id="linearGradient2495">
+      <stop
+         style="stop-color:#0a0a09;stop-opacity:1;"
+         offset="0"
+         id="stop2497" />
+      <stop
+         style="stop-color:#282927;stop-opacity:1;"
+         offset="1"
+         id="stop2499" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2495"
+       id="linearGradient1474"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="674"
+       y1="276.11252"
+       x2="505"
+       y2="199.11252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2399"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       id="linearGradient2535">
+      <stop
+         id="stop2537"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:0.36078432;" />
+      <stop
+         id="stop2539"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2397"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-291.933,627.3998)"
+       x1="532"
+       y1="131.40625"
+       x2="667.5"
+       y2="357.40625" />
+    <linearGradient
+       id="linearGradient3072">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3074" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3076" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3072"
+       id="linearGradient2395"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="76.360481"
+       x2="585"
+       y2="170.3912" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3093"
+       id="radialGradient2234"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.266476,0,283.9565)"
+       cx="224.5"
+       cy="387.11252"
+       fx="224.5"
+       fy="387.11252"
+       r="174.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2495"
+       id="linearGradient2236"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="674"
+       y1="276.11252"
+       x2="505"
+       y2="199.11252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2503"
+       id="linearGradient2238"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3072"
+       id="linearGradient2240"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="76.360481"
+       x2="585"
+       y2="170.3912" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2242"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-291.933,627.3998)"
+       x1="532"
+       y1="131.40625"
+       x2="667.5"
+       y2="357.40625" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2244"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2495"
+       id="linearGradient2255"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="674"
+       y1="276.11252"
+       x2="505"
+       y2="199.11252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2503"
+       id="linearGradient2257"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3093"
+       id="radialGradient2260"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.266476,-145.39702,-74.948037)"
+       cx="224.5"
+       cy="387.11252"
+       fx="224.5"
+       fy="387.11252"
+       r="174.5" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="4.9497475"
+     inkscape:cx="16.230436"
+     inkscape:cy="-2.4336194"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="2043"
+     inkscape:window-height="794"
+     inkscape:window-x="180"
+     inkscape:window-y="140"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <metadata
+     id="metadata2166">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <cc:license
+           rdf:resource="http://www.gnu.org/copyleft/gpl.html" />
+        <dc:title>PulseAudio logotype</dc:title>
+        <dc:date>2006-08-28</dc:date>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Pierre Ossman &lt;ossman@cendio.se&gt; for Cendio AB</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:rights>
+          <cc:Agent>
+            <dc:title />
+          </cc:Agent>
+        </dc:rights>
+        <dc:contributor>
+          <cc:Agent>
+            <dc:title>Rafael Jannone (basic idea)</dc:title>
+          </cc:Agent>
+        </dc:contributor>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <rect
+       ry="6.5049205"
+       y="2.2257283"
+       x="5.4760308"
+       height="37.047943"
+       width="37.047943"
+       id="rect2371"
+       style="fill:url(#linearGradient2255);fill-opacity:1;stroke:url(#linearGradient2257);stroke-width:0.99792439;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       inkscape:export-filename="/home/lennart/test.png"
+       inkscape:export-xdpi="165.5896"
+       inkscape:export-ydpi="165.5896" />
+    <g
+       transform="matrix(0.124741,0,0,0.124741,-61.69688,-99.94425)"
+       id="g2415"
+       inkscape:export-filename="/home/lennart/test.png"
+       inkscape:export-xdpi="165.5896"
+       inkscape:export-ydpi="165.5896">
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path2417"
+         sodipodi:cx="-1"
+         sodipodi:cy="863.61249"
+         sodipodi:rx="23"
+         sodipodi:ry="23"
+         d="M 22 863.61249 A 23 23 0 1 1  -24,863.61249 A 23 23 0 1 1  22 863.61249 z"
+         transform="matrix(1.676363,0,0,1.676363,688.6772,-480.168)" />
+      <path
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="M 666.92273,892.01313 C 633.50485,900.88553 608.86021,931.34683 608.86023,967.54442 C 608.86023,1003.7419 633.50486,1034.2345 666.92273,1043.1069 C 642.81497,1032.2877 625.48523,1002.5195 625.48523,967.54442 C 625.48522,932.56943 642.81496,902.83233 666.92273,892.01313 z M 707.07898,892.01313 C 731.18675,902.83233 748.51648,932.56933 748.51648,967.54442 C 748.51648,1002.5195 731.18674,1032.2877 707.07898,1043.1069 C 740.49686,1034.2345 765.1415,1003.7419 765.14148,967.54442 C 765.14148,931.34693 740.49687,900.88553 707.07898,892.01313 z "
+         id="path2419" />
+      <path
+         id="path2421"
+         d="M 655.64705,849.58672 C 603.46201,863.44178 564.97718,911.00985 564.97721,967.53562 C 564.97721,1024.0613 603.46203,1071.6783 655.64705,1085.5333 C 618.0006,1068.6381 590.93865,1022.1524 590.93865,967.53562 C 590.93863,912.91905 618.00059,866.48188 655.64705,849.58672 z M 718.35466,849.58672 C 756.00112,866.48188 783.06306,912.91889 783.06306,967.53562 C 783.06306,1022.1524 756.00111,1068.6381 718.35466,1085.5333 C 770.5397,1071.6783 809.02453,1024.0613 809.0245,967.53562 C 809.0245,911.01001 770.53972,863.44178 718.35466,849.58672 z "
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    </g>
+    <g
+       id="g1494"
+       transform="matrix(0.124741,0,0,0.124741,-13.36814,-87.21636)"
+       inkscape:export-filename="/home/lennart/test.png"
+       inkscape:export-xdpi="165.5896"
+       inkscape:export-ydpi="165.5896">
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         d="M 495.15625,93.84375 C 468.52243,93.84375 447.21875,115.11921 447.21875,141.75 L 447.21875,334.46875 C 447.21875,361.09954 468.52545,382.40625 495.15625,382.40625 L 687.84375,382.40625 C 714.47454,382.40625 735.78125,361.09955 735.78125,334.46875 L 735.78125,141.75 C 735.78125,115.11921 714.47755,93.84375 687.84375,93.84375 L 495.15625,93.84375 z "
+         id="path2373"
+         style="fill:url(#linearGradient2240);fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         inkscape:original="M 495.15625 89.625 C 466.26648 89.625 443 112.86023 443 141.75 L 443 334.46875 C 443 363.35852 466.26647 386.625 495.15625 386.625 L 687.84375 386.625 C 716.73352 386.625 740 363.35853 740 334.46875 L 740 141.75 C 740 112.86023 716.7335 89.625 687.84375 89.625 L 495.15625 89.625 z "
+         inkscape:radius="-4.2074337"
+         sodipodi:type="inkscape:offset"
+         transform="translate(-291.933,627.3998)" />
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         style="fill:url(#linearGradient2242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="M 436.05138,821.4365 C 397.62524,862.62866 358.12861,865.874 299.93097,865.874 C 242.63828,865.874 199.11564,893.22114 163.06701,927.96775 L 163.06701,961.86851 C 163.06701,985.0884 181.12504,1001.9935 203.22326,1001.9935 L 395.91076,1006.0248 C 420.50531,1006.0248 436.03576,986.46307 436.03576,961.86851 L 436.05138,821.4365 z "
+         id="path2375"
+         sodipodi:nodetypes="cscccccc" />
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         sodipodi:type="inkscape:offset"
+         inkscape:radius="-8"
+         inkscape:original="M 495.15625 89.625 C 466.26648 89.625 443 112.86023 443 141.75 L 443 334.46875 C 443 363.35852 466.26647 386.625 495.15625 386.625 L 687.84375 386.625 C 716.73352 386.625 740 363.35853 740 334.46875 L 740 141.75 C 740 112.86023 716.7335 89.625 687.84375 89.625 L 495.15625 89.625 z "
+         style="fill:none;fill-opacity:1;stroke:url(#linearGradient2244);stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path2377"
+         d="M 495.15625,97.625 C 470.55593,97.625 451,117.15545 451,141.75 L 451,334.46875 C 451,359.0633 470.56169,378.625 495.15625,378.625 L 687.84375,378.625 C 712.4383,378.625 732,359.06331 732,334.46875 L 732,141.75 C 732,117.15545 712.44405,97.625 687.84375,97.625 L 495.15625,97.625 z "
+         transform="translate(-291.933,627.3998)" />
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-size:30.33161926px;font-style:normal;font-weight:normal;line-height:125%;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="49.689053"
+       y="37.570499"
+       id="text2188"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/lennart/test.png"
+       inkscape:export-xdpi="165.5896"
+       inkscape:export-ydpi="165.5896"><tspan
+         sodipodi:role="line"
+         id="tspan2190"
+         x="49.689053"
+         y="37.570499"
+         style="font-size:30.33161926px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Tuffy"><tspan
+   style="font-size:30.33161926px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Tuffy"
+   id="tspan2196">Pulse</tspan>Audio</tspan></text>
+  </g>
+</svg>
diff --git a/pulseaudio.supp b/pulseaudio.supp
new file mode 100644 (file)
index 0000000..83f9ad1
--- /dev/null
@@ -0,0 +1,50 @@
+# This file contains error suppression rules for Valgrind. These rules suppress
+# errors in alsa-lib that are caused by Valgrind not handling the
+# SNDRV_CTL_IOCTL_TLV_READ ioctl. More information about the Valgrind bug:
+# http://thread.gmane.org/gmane.comp.debugging.valgrind/11888
+#
+# To use this file, pass the command line option --suppressions=<filename> to
+# Valgrind.
+#
+# If you find new false positives, and want to add new suppressions, the
+# --gen-suppression=all option can be very useful. It prints automatically
+# generated suppression rules that can be copy-pasted here.
+
+{
+        alsa-lib/snd_tlv_get_dB_range
+        Memcheck:Cond
+        fun:snd_tlv_get_dB_range
+}
+
+{
+        alsa-lib/snd_tlv_convert_to_dB
+        Memcheck:Cond
+        fun:snd_tlv_convert_to_dB
+}
+
+{
+        alsa-lib/snd_tlv_convert_from_dB
+        Memcheck:Cond
+        fun:snd_tlv_convert_from_dB
+}
+
+{
+        alsa-lib/set_volume_ops
+        Memcheck:Cond
+        fun:set_volume_ops
+}
+
+{
+        alsa-lib/snd_ctl_hw_elem_write
+        Memcheck:Param
+        ioctl(generic)
+        fun:ioctl
+        fun:snd_ctl_hw_elem_write
+}
+
+{
+        alsa-lib/selem_read
+        Memcheck:Cond
+        fun:bcmp
+        fun:selem_read
+}
diff --git a/pulseaudio.svg b/pulseaudio.svg
new file mode 100644 (file)
index 0000000..a79e03d
--- /dev/null
@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="48px"
+   height="48px"
+   id="svg2161"
+   sodipodi:version="0.32"
+   inkscape:version="0.44"
+   sodipodi:docbase="/home/ossman/devel/pulseaudio"
+   sodipodi:docname="pulseaudio.svg">
+  <defs
+     id="defs2163">
+    <linearGradient
+       id="linearGradient3093">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop3095" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop3097" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3093"
+       id="radialGradient2472"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.266476,0,283.9565)"
+       cx="224.5"
+       cy="387.11252"
+       fx="224.5"
+       fy="387.11252"
+       r="174.5" />
+    <linearGradient
+       id="linearGradient2503">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop2505" />
+      <stop
+         style="stop-color:#141413;stop-opacity:1;"
+         offset="1"
+         id="stop2507" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2503"
+       id="linearGradient1476"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       id="linearGradient2495">
+      <stop
+         style="stop-color:#0a0a09;stop-opacity:1;"
+         offset="0"
+         id="stop2497" />
+      <stop
+         style="stop-color:#282927;stop-opacity:1;"
+         offset="1"
+         id="stop2499" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2495"
+       id="linearGradient1474"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="674"
+       y1="276.11252"
+       x2="505"
+       y2="199.11252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2399"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       id="linearGradient2535">
+      <stop
+         id="stop2537"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:0.36078432;" />
+      <stop
+         id="stop2539"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2397"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-291.933,627.3998)"
+       x1="532"
+       y1="131.40625"
+       x2="667.5"
+       y2="357.40625" />
+    <linearGradient
+       id="linearGradient3072">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3074" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3076" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3072"
+       id="linearGradient2395"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="76.360481"
+       x2="585"
+       y2="170.3912" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="4.9497475"
+     inkscape:cx="20.060638"
+     inkscape:cy="18.992734"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="872"
+     inkscape:window-height="624"
+     inkscape:window-x="325"
+     inkscape:window-y="224" />
+  <metadata
+     id="metadata2166">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <cc:license
+           rdf:resource="http://www.gnu.org/copyleft/gpl.html" />
+        <dc:title>PulseAudio logotype</dc:title>
+        <dc:date>2006-08-28</dc:date>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Pierre Ossman &lt;ossman@cendio.se&gt; for Cendio AB</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:rights>
+          <cc:Agent>
+            <dc:title></dc:title>
+          </cc:Agent>
+        </dc:rights>
+        <dc:contributor>
+          <cc:Agent>
+            <dc:title>Rafael Jannone (basic idea)</dc:title>
+          </cc:Agent>
+        </dc:contributor>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.36679538;color:black;fill:url(#radialGradient2472);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:38.81499863;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       id="path2470"
+       sodipodi:cx="224.5"
+       sodipodi:cy="387.11252"
+       sodipodi:rx="174.5"
+       sodipodi:ry="46.5"
+       d="M 399 387.11252 A 174.5 46.5 0 1 1  50,387.11252 A 174.5 46.5 0 1 1  399 387.11252 z"
+       transform="matrix(0.137443,0,0,0.154237,-6.855952,-20.43595)"
+       inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+       inkscape:export-xdpi="44.099998"
+       inkscape:export-ydpi="44.099998" />
+    <rect
+       ry="6.5049205"
+       y="2.2257283"
+       x="5.4760308"
+       height="37.047943"
+       width="37.047943"
+       id="rect2371"
+       style="fill:url(#linearGradient1474);fill-opacity:1;stroke:url(#linearGradient1476);stroke-width:0.99792439;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+       inkscape:export-xdpi="44.099998"
+       inkscape:export-ydpi="44.099998" />
+    <g
+       transform="matrix(0.124741,0,0,0.124741,-61.69688,-99.94425)"
+       id="g2415"
+       inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+       inkscape:export-xdpi="44.099998"
+       inkscape:export-ydpi="44.099998">
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path2417"
+         sodipodi:cx="-1"
+         sodipodi:cy="863.61249"
+         sodipodi:rx="23"
+         sodipodi:ry="23"
+         d="M 22 863.61249 A 23 23 0 1 1  -24,863.61249 A 23 23 0 1 1  22 863.61249 z"
+         transform="matrix(1.676363,0,0,1.676363,688.6772,-480.168)" />
+      <path
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="M 666.92273,892.01313 C 633.50485,900.88553 608.86021,931.34683 608.86023,967.54442 C 608.86023,1003.7419 633.50486,1034.2345 666.92273,1043.1069 C 642.81497,1032.2877 625.48523,1002.5195 625.48523,967.54442 C 625.48522,932.56943 642.81496,902.83233 666.92273,892.01313 z M 707.07898,892.01313 C 731.18675,902.83233 748.51648,932.56933 748.51648,967.54442 C 748.51648,1002.5195 731.18674,1032.2877 707.07898,1043.1069 C 740.49686,1034.2345 765.1415,1003.7419 765.14148,967.54442 C 765.14148,931.34693 740.49687,900.88553 707.07898,892.01313 z "
+         id="path2419" />
+      <path
+         id="path2421"
+         d="M 655.64705,849.58672 C 603.46201,863.44178 564.97718,911.00985 564.97721,967.53562 C 564.97721,1024.0613 603.46203,1071.6783 655.64705,1085.5333 C 618.0006,1068.6381 590.93865,1022.1524 590.93865,967.53562 C 590.93863,912.91905 618.00059,866.48188 655.64705,849.58672 z M 718.35466,849.58672 C 756.00112,866.48188 783.06306,912.91889 783.06306,967.53562 C 783.06306,1022.1524 756.00111,1068.6381 718.35466,1085.5333 C 770.5397,1071.6783 809.02453,1024.0613 809.0245,967.53562 C 809.0245,911.01001 770.53972,863.44178 718.35466,849.58672 z "
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    </g>
+    <g
+       id="g1494"
+       transform="matrix(0.124741,0,0,0.124741,-13.36814,-87.21636)">
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         d="M 495.15625,93.84375 C 468.52243,93.84375 447.21875,115.11921 447.21875,141.75 L 447.21875,334.46875 C 447.21875,361.09954 468.52545,382.40625 495.15625,382.40625 L 687.84375,382.40625 C 714.47454,382.40625 735.78125,361.09955 735.78125,334.46875 L 735.78125,141.75 C 735.78125,115.11921 714.47755,93.84375 687.84375,93.84375 L 495.15625,93.84375 z "
+         id="path2373"
+         style="fill:url(#linearGradient2395);fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         inkscape:original="M 495.15625 89.625 C 466.26648 89.625 443 112.86023 443 141.75 L 443 334.46875 C 443 363.35852 466.26647 386.625 495.15625 386.625 L 687.84375 386.625 C 716.73352 386.625 740 363.35853 740 334.46875 L 740 141.75 C 740 112.86023 716.7335 89.625 687.84375 89.625 L 495.15625 89.625 z "
+         inkscape:radius="-4.2074337"
+         sodipodi:type="inkscape:offset"
+         transform="translate(-291.933,627.3998)" />
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         style="fill:url(#linearGradient2397);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="M 436.05138,821.4365 C 397.62524,862.62866 358.12861,865.874 299.93097,865.874 C 242.63828,865.874 199.11564,893.22114 163.06701,927.96775 L 163.06701,961.86851 C 163.06701,985.0884 181.12504,1001.9935 203.22326,1001.9935 L 395.91076,1006.0248 C 420.50531,1006.0248 436.03576,986.46307 436.03576,961.86851 L 436.05138,821.4365 z "
+         id="path2375"
+         sodipodi:nodetypes="cscccccc" />
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         sodipodi:type="inkscape:offset"
+         inkscape:radius="-8"
+         inkscape:original="M 495.15625 89.625 C 466.26648 89.625 443 112.86023 443 141.75 L 443 334.46875 C 443 363.35852 466.26647 386.625 495.15625 386.625 L 687.84375 386.625 C 716.73352 386.625 740 363.35853 740 334.46875 L 740 141.75 C 740 112.86023 716.7335 89.625 687.84375 89.625 L 495.15625 89.625 z "
+         style="fill:none;fill-opacity:1;stroke:url(#linearGradient2399);stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path2377"
+         d="M 495.15625,97.625 C 470.55593,97.625 451,117.15545 451,141.75 L 451,334.46875 C 451,359.0633 470.56169,378.625 495.15625,378.625 L 687.84375,378.625 C 712.4383,378.625 732,359.06331 732,334.46875 L 732,141.75 C 732,117.15545 712.44405,97.625 687.84375,97.625 L 495.15625,97.625 z "
+         transform="translate(-291.933,627.3998)" />
+    </g>
+  </g>
+</svg>
diff --git a/scripts/benchmark_memory_usage.sh b/scripts/benchmark_memory_usage.sh
new file mode 100755 (executable)
index 0000000..f053d7e
--- /dev/null
@@ -0,0 +1,133 @@
+#!/bin/bash
+
+# This file is part of PulseAudio.
+#
+# Copyright 2015 Ahmed S. Darwish <darwish.07@gmail.com>
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+#
+# Measure PulseAudio memory usage (VmSize, Private+Shared_Dirty)
+# while increasing the number of connected clients over time.
+#
+# To avoid premature client exits, please ensure giving a long
+# wave file as the script's first parameter.
+#
+
+_bold="\x1B[1m"
+_error="\x1B[1;31m"
+_reset="\x1B[0m"
+
+BASENAME=$(basename $0)
+PROMPT="${_bold}[$BASENAME]${_reset}"
+error() {
+    echo -e "$PROMPT: ** Error: ${_error}$1${_reset}" >&2; exit -1
+}
+msg() {
+    echo -e "$PROMPT: $1"
+}
+
+_absolute_dirname="$(dirname `readlink -f $0`)"
+PA_HOME="${_absolute_dirname%/scripts}"
+[ -d "$PA_HOME/src" -a -d "$PA_HOME/scripts" ] ||
+    error "This script can only be executed from PulseAudio source tree"
+
+PA=${PA_HOME}/src/pulseaudio
+PA_CAT=${PA_HOME}/src/pacat
+PA_PLAY=${PA_HOME}/src/paplay
+PA_FLAGS="-n -F ${PA_HOME}/src/default.pa -p ${PA_HOME}/src/"
+
+[ -L "$PA_PLAY" ] || ln -sf .libs/lt-pacat $PA_PLAY
+PA_PLAY_PROCESS_NAME="paplay"
+
+SCRIPTS_DIR=${PA_HOME}/scripts
+BENCHMARKS_DIR=${SCRIPTS_DIR}/benchmarks
+GNUPLOT_SCRIPT=${SCRIPTS_DIR}/plot_memory_usage.gp
+OUTPUT_FILE=${BENCHMARKS_DIR}/memory-usage-`date -Iseconds`.txt
+SYMLINK_LATEST_OUTPUT_FILE=${BENCHMARKS_DIR}/memory-usage-LATEST.txt
+
+MAX_CLIENTS=30
+
+[ -e "$PA" ] || error "$PA does not exist. Compile PulseAudio tree first."
+[ -x "$PA" ] || error "$PA cannot be executed"
+[ -x "$PA_CAT" ] || error "$PA_CAT cannot be executed"
+
+AUDIO_FILE="$1"
+[ -n "$AUDIO_FILE" ] || error "Usage: $BASENAME AUDIO_FILE"
+[ -e "$AUDIO_FILE" ] || error "$AUDIO_FILE does not exist"
+[ -f "$AUDIO_FILE" ] || error "$AUDIO_FILE is not a regular file"
+
+$PA --check >/dev/null 2>&1
+[ "$?" -ne "0" ] || {
+    msg "A PulseAudio daemon is already running on your system"
+    msg "To use this script, please do the following first:"
+    msg " 1. Add autospawn=no to $HOME/.pulse/client.conf"
+    msg " 2. Kill current daemon instance using 'pulseaudio --kill'"
+    error "Failed to start PulseAudio daemon"
+}
+
+msg "Hello. Benchmarking PulseAudio daemon memory usage over time"
+
+# Check Linux Kernel's Documentation/sysctl/vm.txt for details.
+msg "Ensuring consistent results by dropping all VM caches!"
+sudo bash -c "sync && echo 3 >/proc/sys/vm/drop_caches"
+
+msg "Starting PulseAudio daemon"
+PULSE_LOG=0 PULSE_LOG_COLORS= PULSE_LOG_META= $PA $PA_FLAGS &
+_pid=$!
+
+# Give PA daemon time to initialize everything and vacuum. We want
+# to make the _starting_ dirty RSS memory usage (0 clients) as
+# equivalent as possible for multiple trials.
+sleep 12
+
+$PA --check >/dev/null 2>&1
+[ "$?" -eq "0" ] || error "Failed to start PulseAudio daemon"
+
+echo "# ClientCount    VmSize (KiB)     DirtyRSS (KiB)" >$OUTPUT_FILE
+
+msg "Starting PulseAudio clients"
+_i=1;
+while true; do
+    [ "$_i" -le "$MAX_CLIENTS" ] || break
+
+    _vmsize=`ps -o vsize= $_pid`
+    _drss=`awk '/(Shared|Private)_Dirty:/{ sum += $2 } END { print sum }' /proc/$_pid/smaps`
+    [ "$?" -eq "0" ] || error "Error sampling PulseAudio RSS memory usage"
+
+    echo "  $_i              $_vmsize           $_drss" >>$OUTPUT_FILE
+
+    $PA_PLAY $AUDIO_FILE 2>/dev/null &
+    _i=$((_i + 1))
+
+    sleep 1
+done
+msg "Finished starting ${MAX_CLIENTS} PulseAudio clients over time"
+
+_n_clients_still_alive=$(ps -C $PA_PLAY_PROCESS_NAME --no-headers | wc -l)
+[ "$_n_clients_still_alive" -ge "$MAX_CLIENTS" ] || {
+    msg "You did not provide a long-enough wave file for clients to play"
+    msg "Only $_n_clients_still_alive clients are now active; expected $MAX_CLIENTS"
+    error "Please provide a large wave file (~ $((MAX_CLIENTS*2))s) then redo the benchmarks"
+}
+
+msg "Killing PulseAudio daemon"
+$PA --kill >/dev/null 2>&1
+
+rm -f $SYMLINK_LATEST_OUTPUT_FILE
+ln -s $OUTPUT_FILE $SYMLINK_LATEST_OUTPUT_FILE
+
+msg "Sampling daemon memory usage done!"
+msg "Check the results at $SYMLINK_LATEST_OUTPUT_FILE"
+msg "Plot these results using 'gnuplot $GNUPLOT_SCRIPT'"
diff --git a/scripts/benchmarks/.gitignore b/scripts/benchmarks/.gitignore
new file mode 100644 (file)
index 0000000..612f79e
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Benchmarks directory .gitignore
+#
+
+# Ignore everything here ..
+*
+
+# Except these files
+!README
+!.gitignore
diff --git a/scripts/benchmarks/README b/scripts/benchmarks/README
new file mode 100644 (file)
index 0000000..d935b0d
--- /dev/null
@@ -0,0 +1,4 @@
+This is an empty directory for collecting users PulseAudio daemon
+performance benchmarks, as created by the tools under `scripts/' .
+
+Please do not commit any file here except this README and .gitignore.
diff --git a/scripts/plot_memory_usage.gp b/scripts/plot_memory_usage.gp
new file mode 100644 (file)
index 0000000..33cc4e1
--- /dev/null
@@ -0,0 +1,63 @@
+
+# This file is part of PulseAudio.
+#
+# Copyright 2015 Ahmed S. Darwish <darwish.07@gmail.com>
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+#
+# PulseAudio memory usage plotting script
+#
+# Before invocation, generate necessary data by running the
+# 'benchmark_memory_usage.sh' bash script. Afterwards, you can plot
+# such data by running:
+#
+#       gnuplot plot_memory_usage.gp
+#
+# Note! To avoid scaling issues, memory is plotted in a "double y axis"
+# form, with VM usage on the left, and dirty RSS memory usage on the
+# right. The scales are different.
+#
+
+# Print our user messages to the stdout
+set print "-"
+
+benchDir = system('dirname ' .ARG0) .'/benchmarks/'
+inputFile = benchDir ."memory-usage-LATEST.txt"
+outputFile = benchDir ."pulse-memory-usage.png"
+
+set title "PulseAudio Memory Usage Over Time"
+set xlabel "Number of councurrent 'paplay' clients"
+
+set ylabel "Virtual memory consumption (GiB)"
+set y2label "Dirty RSS consumption (MiB)"
+set ytics nomirror
+set y2tics
+
+# Finer granulrity for x-axis ticks ...
+set xtics 1,1
+set grid
+
+# Use Cairo's PNG backend. This produce images which are way
+# better-rendered than the barebone classical png backend
+set terminal pngcairo enhanced size 1000,768 font 'Verdana,10'
+set output outputFile
+
+print "Plotting data from input file: ", inputFile
+print "..."
+
+plot inputFile using 1:($2/1024/1024) title "VmSize" axes x1y1 with linespoints, \
+     inputFile using 1:($3/1024) title "Dirty RSS" axes x1y2 with linespoints
+
+print "Done! Check our performance at: ", outputFile
diff --git a/shell-completion/bash/pulseaudio b/shell-completion/bash/pulseaudio
new file mode 100644 (file)
index 0000000..e473b9c
--- /dev/null
@@ -0,0 +1,568 @@
+#!/bin/bash
+
+__cards () {
+    while IFS=$'\t' read idx name _; do
+        printf "%s %s\n" "$idx" "$name"
+    done < <(pactl list cards short 2> /dev/null)
+}
+
+__sinks () {
+    while IFS=$'\t' read _ name _ _ _; do
+        printf "%s\n" "$name"
+    done < <(pactl list sinks short 2> /dev/null)
+}
+
+__sinks_idx () {
+    while IFS=$'\t' read idx _ _ _ _; do
+        printf "%s\n" "$idx"
+    done < <(pactl list sinks short 2> /dev/null)
+}
+
+__sources () {
+    while IFS=$'\t' read _ name _ _ _; do
+        printf "%s\n" "$name"
+    done < <(pactl list sources short 2> /dev/null)
+}
+
+__sink_inputs () {
+    while IFS=$'\t' read idx _ _ _ _; do
+        printf "%s\n" "$idx"
+    done < <(pactl list sink-inputs short 2> /dev/null)
+}
+
+__source_outputs () {
+    while IFS=$'\t' read idx _ _ _ _; do
+        printf "%s\n" "$idx"
+    done < <(pactl list source-outputs short 2> /dev/null)
+}
+
+__ports () {
+    pactl list cards 2> /dev/null | awk -e \
+        '/^\tPorts:/ {
+            flag=1; next
+         }
+
+         /^\t[A-Za-z]/ {
+             flag=0
+         }
+
+         flag {
+             if (/^\t\t[A-Za-z]/)
+                 ports = ports substr($0, 3, index($0, ":")-3) " "
+         }
+
+         END {
+             print ports
+         }'
+}
+
+__profiles () {
+    pactl list cards 2> /dev/null | awk -e \
+        '/^\tProfiles:/ {
+            flag=1; next
+        }
+
+        /^\t[A-Za-z]/ {
+            flag=0
+        }
+
+        flag {
+            if (/^\t\t[A-Za-z]/)
+                profiles = profiles substr($0, 3, index($0, ": ")-3) " "
+        }
+
+        END {
+            print profiles
+        }'
+}
+
+__all_modules () {
+    while read name; do
+        name=${name%% *}
+        printf "%s\n" "$name"
+    done < <(pulseaudio --dump-modules 2> /dev/null)
+}
+
+__loaded_modules () {
+    while IFS=$'\t' read idx name _; do
+        printf "%s %s\n" "$idx" "$name"
+    done < <(pactl list modules short 2> /dev/null)
+}
+
+__resample_methods () {
+    while read name; do
+        printf "%s\n" "$name"
+    done < <(pulseaudio --dump-resample-methods 2> /dev/null)
+}
+
+_pacat_file_formats () {
+    while IFS=$'\t' read name _; do
+        printf "%s\n" "$name"
+    done < <(pacat --list-file-formats 2> /dev/null)
+}
+
+in_array() {
+    local i
+    for i in "${@:2}"; do
+        [[ $1 = "$i" ]] && return
+    done
+}
+
+_pactl() {
+    local cur prev words cword preprev command
+    local comps
+    local flags='-h --help --version -s --server= --client-name='
+    local list_types='short sinks sources sink-inputs source-outputs cards
+                    modules samples clients'
+    local commands=(stat info list exit upload-sample play-sample remove-sample
+                    load-module unload-module move-sink-input move-source-output
+                    suspend-sink suspend-source set-card-profile set-sink-port
+                    set-source-port set-sink-volume set-source-volume
+                    set-sink-input-volume set-source-output-volume set-sink-mute
+                    set-source-mute set-sink-input-mute set-source-output-mute
+                    set-sink-formats set-port-latency-offset subscribe help)
+
+    _init_completion -n = || return
+    preprev=${words[$cword-2]}
+
+    for word in "${COMP_WORDS[@]}"; do
+        if in_array "$word" "${commands[@]}"; then
+            command=$word
+            break
+        fi
+    done
+
+    case $preprev in
+        list) COMPREPLY=($(compgen -W 'short' -- "$cur")) ;;
+
+        play-sample)
+            comps=$(__sinks)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        move-sink-input)
+            comps=$(__sinks)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        move-source-output)
+            comps=$(__sources)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-card-profile)
+            comps=$(__profiles)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-*-port)
+            comps=$(__ports)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-*-mute) COMPREPLY=($(compgen -W 'true false toggle' -- "$cur")) ;;
+
+        set-sink-formats)
+            ;; #TODO
+
+        set-port-*)
+            comps=$(__ports)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+        --server)
+            compopt +o nospace
+            _known_hosts_real "$cur"
+            ;;
+    esac
+    [[ $COMPREPLY ]] && return 0
+
+    case $prev in
+        list) COMPREPLY=($(compgen -W '${list_types[*]}' -- "$cur")) ;;
+
+        upload-sample) _filedir ;;
+
+        play-sample) ;; # TODO
+
+        remove-sample) ;; # TODO
+
+        load-module)
+            comps=$(__all_modules)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        unload-module)
+            comps=$(__loaded_modules)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-card*)
+            comps=$(__cards)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        *sink-input*)
+            comps=$(__sink_inputs)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        *source-output*)
+            comps=$(__source_outputs)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-sink-formats)
+            comps=$(__sinks_idx)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        *sink*)
+            comps=$(__sinks)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        *source*)
+            comps=$(__sources)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-port*)
+            comps=$(__cards)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        -s)
+            _known_hosts_real "$cur" ;;
+    esac
+    [[ $COMPREPLY ]] && return 0
+
+    case $cur in
+        --server=*)
+            cur=${cur#*=}
+            _known_hosts_real "$cur"
+            ;;
+
+        -*)
+            COMPREPLY=($(compgen -W '${flags[*]}' -- "$cur"))
+            [[ $COMPREPLY == *= ]] && compopt -o nospace
+            ;;
+
+        *)
+            [[ -z $command ]] && COMPREPLY=($(compgen -W '${commands[*]}' -- "$cur"))
+            ;;
+    esac
+}
+complete -F _pactl pactl
+
+_pacmd() {
+    local cur prev words cword preprev command
+    local comps
+    local flags='-h --help --version'
+    local commands=(exit help list-modules list-cards list-sinks list-sources list-clients
+                    list-samples list-sink-inputs list-source-outputs stat info
+                    load-module unload-module describe-module set-sink-volume
+                    set-source-volume set-sink-input-volume set-source-output-volume
+                    set-sink-mute set-source-mut set-sink-input-mute
+                    set-source-output-mute update-sink-proplist update-source-proplist
+                    update-sink-input-proplist update-source-output-proplist
+                    set-default-sink set-default-source kill-client kill-sink-input
+                    kill-source-output play-sample remove-sample load-sample
+                    load-sample-lazy load-sample-dir-lazy play-file dump
+                    move-sink-input move-source-output suspend-sink suspend-source
+                    suspend set-card-profile set-sink-port set-source-port
+                    set-port-latency-offset set-log-target set-log-level set-log-meta
+                    set-log-time set-log-backtrace)
+    _init_completion -n = || return
+    preprev=${words[$cword-2]}
+
+    for word in "${COMP_WORDS[@]}"; do
+        if in_array "$word" "${commands[@]}"; then
+            command=$word
+            break
+        fi
+    done
+
+    case $preprev in
+        play-sample|play-file)
+            comps=$(__sinks)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        load-sample*) _filedir ;;
+
+        move-sink-input)
+            comps=$(__sinks)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        move-source-output)
+            comps=$(__sources)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-card-profile)
+            comps=$(__profiles)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-*port*)
+            comps=$(__ports)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-*-mute) COMPREPLY=($(compgen -W 'true false' -- "$cur"));;
+
+        set-sink-formats)
+            ;; #TODO
+    esac
+
+    case $prev in
+        list-*) ;;
+        describe-module|load-module)
+            comps=$(__all_modules)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        unload-module)
+            comps=$(__loaded_modules)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        load-sample-dir-lazy) _filedir -d ;;
+        play-file) _filedir ;;
+
+        *sink-input*)
+            comps=$(__sink_inputs)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        *source-output*)
+            comps=$(__source_outputs)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        *sink*)
+            comps=$(__sinks)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        *source*)
+            comps=$(__sources)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-card*)
+            comps=$(__cards)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-port-*)
+            comps=$(__cards)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        set-log-target)
+            COMPREPLY=($(compgen -W 'auto syslog stderr file: newfile:' -- "$cur"))
+            ;;
+
+        set-log-level)
+            COMPREPLY=($(compgen -W '{0..4}' -- "$cur"))
+            ;;
+
+        set-log-meta|set-log-time|suspend)
+            COMPREPLY=($(compgen -W 'true false' -- "$cur"))
+            ;;
+    esac
+
+    case $cur in
+        -*) COMPREPLY=($(compgen -W '${flags[*]}' -- "$cur")) ;;
+        suspend)
+            COMPREPLY=($(compgen -W 'suspend suspend-sink suspend-source' -- "$cur"))
+            ;;
+
+        load-sample)
+            COMPREPLY=($(compgen -W 'load-sample load-sample-lazy load-sample-dir-lazy' -- "$cur"))
+            ;;
+
+        *)
+            [[ -z $command ]] && COMPREPLY=($(compgen -W '${commands[*]}' -- "$cur"))
+            ;;
+    esac
+}
+complete -F _pacmd pacmd
+
+_pasuspender () {
+    local cur prev
+    local flags='-h --help --version -s --server='
+
+    _init_completion -n = || return
+
+    case $cur in
+        --server=*)
+            cur=${cur#*=}
+            _known_hosts_real "$cur"
+            ;;
+
+        -*)
+            COMPREPLY=($(compgen -W '${flags[*]}' -- "$cur"))
+            [[ $COMPREPLY == *= ]] && compopt -o nospace
+            ;;
+    esac
+
+    case $prev in
+        -s) _known_hosts_real "$cur" ;;
+    esac
+}
+complete -F _pasuspender pasuspender
+
+_padsp () {
+    local cur prev
+    local flags='-h -s -n -m -M -S -D -d'
+
+    _get_comp_words_by_ref cur prev
+
+    case $cur in
+        -*) COMPREPLY=($(compgen -W '${flags[*]}' -- "$cur")) ;;
+    esac
+
+    case $prev in
+        -s) _known_hosts_real "$cur" ;;
+    esac
+}
+complete -F _padsp padsp
+
+_pacat () {
+    local cur prev comps
+    local flags='-h --help --version -r --record -p --playback -v --verbose -s
+                --server= -d --device= -n --client-name= --stream-name= --volume=
+                --rate= --format= --channels= --channel-map= --fix-format --fix-rate
+                --fix-channels --no-remix --no-remap --latency= --process-time=
+                --latency-msec= --process-time-msec= --property= --raw --passthrough
+                --file-format= --list-file-formats --monitor-stream='
+
+    _init_completion -n = || return
+
+    case $cur in
+        --server=*)
+            cur=${cur#*=}
+            _known_hosts_real "$cur"
+            ;;
+
+        --device=*)
+            cur=${cur#*=}
+            comps=$(__sinks)
+            comps+=" "$(__sources)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        --monitor-stream=*)
+            cur=${cur#*=}
+            comps=$(__sink_inputs)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        --rate=*)
+            cur=${cur#*=}
+            COMPREPLY=($(compgen -W '32000 44100 48000 9600 192000' -- "$cur"))
+            ;;
+
+        --file-format=*)
+            cur=${cur#*=}
+            comps=$(_pacat_file_formats)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        --*=*)
+            ;;
+
+        -*)
+            COMPREPLY=($(compgen -W '${flags[*]}' -- "$cur"))
+            [[ $COMPREPLY == *= ]] && compopt -o nospace
+            ;;
+        *) _filedir ;;
+    esac
+
+    case $prev in
+        -s) _known_hosts_real "$cur" ;;
+        -d)
+            comps=$(__sinks)
+            comps+=" "$(__sources)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+    esac
+}
+complete -F _pacat pacat paplay parec parecord
+
+_pulseaudio()
+{
+    local cur prev words cword
+    local flags='-h --help --version --dump-conf --dump-resample-methods --cleanup-shm
+                --start -k --kill --check --system= -D --daemonize= --fail= --high-priority=
+                --realtime= --disallow-module-loading= --disallow-exit= --exit-idle-time=
+                --scache-idle-time= --log-level= -v --log-target= --log-meta= --log-time=
+                --log-backtrace= -p --dl-search-path= --resample-method= --use-pit-file=
+                --no-cpu-limit= --disable-shm= --enable-memfd= -L --load= -F --file= -C -n'
+    _init_completion -n = || return
+
+    case $cur in
+        --system=*|--daemonize=*|--fail=*|--high-priority=*|--realtime=*| \
+            --disallow-*=*|--log-meta=*|--log-time=*|--use-pid-file=*| \
+            --no-cpu-limit=*|--disable-shm=*|--enable-memfd=*)
+            cur=${cur#*=}
+            COMPREPLY=($(compgen -W 'true false' -- "$cur"))
+            ;;
+
+        --log-target=*)
+            cur=${cur#*=}
+            COMPREPLY=($(compgen -W 'auto syslog stderr file: newfile:' -- "$cur"))
+            ;;
+
+        --log-level=*)
+            cur=${cur#*=}
+            COMPREPLY=($(compgen -W '{0..4}' -- "$cur"))
+            ;;
+
+        --dl-search-path=*)
+            cur=${cur#*=}
+            _filedir -d
+            ;;
+
+        --file=*)
+            cur=${cur#*=}
+            _filedir
+            ;;
+
+        --resample-method=*)
+            cur=${cur#*=}
+            comps=$(__resample_methods)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        --load=*)
+            cur=${cur#*=}
+            comps=$(__all_modules)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+
+        --*=*)
+            ;;
+
+        -*)
+            COMPREPLY=($(compgen -W '${flags[*]}' -- "$cur"))
+            [[ $COMPREPLY == *= ]] && compopt -o nospace
+            ;;
+    esac
+
+    case $prev in
+        -D) COMPREPLY=($(compgen -W 'true false' -- "$cur")) ;;
+        -p) _filedir -d ;;
+        -F) _filedir ;;
+        -L)
+            cur=${cur#*=}
+            comps=$(__all_modules)
+            COMPREPLY=($(compgen -W '${comps[*]}' -- "$cur"))
+            ;;
+    esac
+}
+complete -F _pulseaudio pulseaudio
+
+#vim: set ft=zsh sw=4 ts=4 noet
diff --git a/shell-completion/zsh/_pulseaudio b/shell-completion/zsh/_pulseaudio
new file mode 100644 (file)
index 0000000..0e9e89b
--- /dev/null
@@ -0,0 +1,744 @@
+#compdef pulseaudio pactl pacmd pacat paplay parec parecord padsp pasuspender
+
+_set_remote() {
+    for (( i = 0; i < ${#words[@]}; i++ )) do
+        if [[ ${words[$i]} == -s || ${words[$i]} == --server ]]; then
+            remote="-s ${words[$i+1]}"
+            break;
+        elif [[ ${words[$i]} == --server=* ]]; then
+            remote=${words[$i]}
+        fi
+    done
+}
+
+_devices() {
+    local -a _device_list
+    local cmd _device _device_description
+
+    if [[ $service == pactl  || $service == pacmd ]]; then
+        case $words[$((CURRENT - 1))] in
+            set-sink-input-*) cmd=('sink-inputs');;
+            set-sink-*) cmd=('sinks');;
+            set-default-sink) cmd=('sinks');;
+            set-default-source) cmd=('sources');;
+            set-source-output-*) cmd=('source-outputs');;
+            set-source-*) cmd=('sources');;
+            suspend-sink) cmd=('sinks');;
+            suspend-source) cmd=('sources');;
+            move-sink-input) cmd=('sink-inputs');;
+            move-source-output) cmd=('source-outputs');;
+            kill-sink-input) cmd=('sink-inputs');;
+            kill-source-output) cmd=('source-outputs');;
+        esac
+
+        case $words[$((CURRENT - 2))] in
+            move-sink-input) cmd=('sinks');;
+            move-source-output) cmd=('sources');;
+        esac
+
+    elif [[ $service == (pacat|paplay|parec|parecord) ]]; then
+        case $words[$((CURRENT))] in
+            --device=*)
+                if [[ $words == *(--playback|-p)[[:space:]]* ||
+                    $service == paplay ]]; then
+                    cmd=('sinks')
+                elif [[ $words == *(--record|-r)[[:space:]]* ||
+                    $service == (parec|parecord) ]]; then
+                    cmd=('sources')
+                else
+                    cmd=('sinks' 'sources')
+                fi
+                ;;
+            --monitor-stream=*) cmd=('sink-inputs');;
+        esac
+
+        case $words[$((CURRENT - 1))] in
+            -d)
+                if [[ $words == *(--playback|-p)[[:space:]]* ||
+                    $service == paplay ]]; then
+                    cmd=('sinks')
+                elif [[ $words == *(--record|-r)[[:space:]]* ||
+                    $service == (parec|parecord) ]]; then
+                    cmd=('sources')
+                else
+                    cmd=('sinks' 'sources')
+                fi
+                ;;
+        esac
+
+    fi
+
+    for target in $cmd; do
+        for device_info in ${(ps:\n\n:)"$(_call_program device_tag "pactl $remote list $target 2> /dev/null")"}; do
+            for line in ${(f)device_info}; do
+                if [[ $target == (sink-inputs|source-outputs) ]]; then
+                    if [[ $line == (Sink*Input|Source*Output)* ]]; then
+                        _device=${line#*\#}
+                    elif [[ $line == *application.name* ]]; then
+                        _device_description=${line#*= }
+                    fi
+
+                else
+                    if [[ $words[$((CURRENT - 1))] == *set-sink-formats* ]]; then
+                        if [[ $line == Sink* ]]; then
+                            _device=${line#*\#}
+                        elif [[ $line == *Description:* ]]; then
+                            _device_description=${line#*: }
+                        fi
+
+                    else
+                        if [[ $line == *Name:* ]]; then
+                            _device=${line#*: }
+                        elif [[ $line == *Description:* ]]; then
+                            _device_description=${line#*: }
+                        fi
+                    fi
+                fi
+            done
+            _device_list+=($_device:$_device_description)
+        done
+    done
+
+    _describe 'device list' _device_list
+}
+
+_profiles() {
+    local -a _profile_list
+    local _current_card _raw_profiles _profile_name _profile_description
+
+    _current_card=$words[$((CURRENT - 1))]
+
+    for card in ${(ps:\n\n:)"$(_call_program profiles_tag "pactl $remote list cards 2> /dev/null")"}; do
+        if [[ $card == *$_current_card* ]]; then
+            _raw_profiles=${card##*Profiles:}
+            _raw_profiles=${_raw_profiles%%Active Profile:*}
+            for profile in ${(f)_raw_profiles}; do
+                if [[ $profile != [[:blank:]] ]]; then
+                    _profile_name=${profile%%: *}
+                    _profile_name=${_profile_name//[[:blank:]]/}
+                    _profile_name=${_profile_name//:/\\:}
+                    _profile_description=${profile#*: }
+                    _profile_list+=($_profile_name:$_profile_description)
+                fi
+            done
+        fi
+    done
+
+    _describe 'profile list' _profile_list
+}
+
+_ports() {
+    local -a _port_list
+    local _raw_ports _port_name _port_description _current_device
+
+    case $words[$((CURRENT - 2))] in
+        set-sink-port) cmd="sinks";;
+        set-source-port) cmd="sources";;
+        set-port-latency-offset) cmd="cards";;
+    esac
+
+    _current_device=$words[$((CURRENT - 1))]
+
+    for device in ${(ps:\n\n:)"$(_call_program port_tag "pactl $remote list $cmd 2> /dev/null")"}; do
+        if [[ $device == *Ports:* && $device == *$_current_device* ]]; then
+            _raw_ports=${device##*Ports:}
+            _raw_ports=${_raw_ports%%Active Port:*}
+            for line in ${(f)_raw_ports}; do
+                if [[ $line != [[:blank:]] &&
+                    $line != (*Part?of*|*Properties:*|*device.icon_name*) ]]; then
+                    _port_name=${line%%: *}
+                    _port_name=${_port_name//[[:blank:]]/}
+                    _port_description=${line#*: }
+                    _port_list+=($_port_name:$_port_description)
+                fi
+            done
+        fi
+    done
+
+    _describe 'port list' _port_list
+}
+
+_cards(){
+    local -a _card_list
+    local _card _cad_name
+
+    for card_info in ${(ps:\n\n:)"$(_call_program card_tag "pactl $remote list cards 2> /dev/null")"}; do
+        for line in ${(f)card_info}; do
+            if [[ $line == *Name:* ]]; then
+                _card=${line#*: }
+            elif [[ $line == *alsa.long_card_name* ]]; then
+                _card_name=${line#*= \"}
+                _card_name=${_card_name%at*}
+            fi
+        done
+        _card_list+=($_card:$_card_name)
+    done
+
+    _describe 'card list' _card_list
+}
+
+_all_modules(){
+    local -a _all_modules_list
+    for module in ${(f)"$(_call_program modules_tag "pulseaudio --dump-modules 2> /dev/null")"}; do
+        _all_modules_list+=${module%% *}
+    done
+    _describe 'module list' _all_modules_list
+}
+
+_loaded_modules(){
+    local -a _loaded_modules_list
+
+    for module in ${(f)"$(_call_program modules_tag "pactl $remote list modules short 2> /dev/null")"}; do
+        _loaded_modules_list+=(${${(ps:\t:)module}[1]}:${${(ps:\t:)module}[2]})
+    done
+    _describe 'module list' _loaded_modules_list
+}
+
+_resample_methods() {
+    local -a _resample_method_list
+    for method in ${(f)"$(_call_program modules_tag "pulseaudio --dump-resample-methods 2> /dev/null")"}; do
+        _resample_method_list+=$method
+    done
+    _describe 'resample method list' _resample_method_list
+}
+
+_clients() {
+    local -a _client_list
+    local _client _client_description
+
+    for client_info in ${(ps:\n\n:)"$(_call_program clients_tag "pactl $remote list clients 2> /dev/null")"}; do
+        for line in ${(f)client_info}; do
+            if [[ $line == Client[[:space:]]#* ]]; then
+                _client=${line#*\#}
+            elif [[ $line == *application.name* ]]; then
+                _client_description=${line#*=}
+            fi
+        done
+        _client_list+=($_client:$_client_description)
+    done
+    _describe 'client list' _client_list
+}
+
+_pacat_file_formats() {
+    local -a _file_format_list
+    for format in ${(f)"$(_call_program fformats_tag "pacat --list-file-formats")"}; do
+        _file_format_list+=(${${(ps:\t:)format}[1]}:${${(ps:\t:)format}[2]})
+    done
+    _describe 'file format list' _file_format_list
+}
+
+_pactl_completion() {
+    _set_remote
+
+    _pactl_command(){
+        local -a _pactl_commands
+
+        _pactl_commands=(
+            'help: show help and exit'
+            'stat: dump statistics about the PulseAudio daemon'
+            'info: dump info about the PulseAudio daemon'
+            'list: list modules/sources/streams/cards etc...'
+            'exit: ask the PulseAudio daemon to exit'
+            'upload-sample: upload a sound from a file into the sample cache'
+            'play-sample: play the specified sample from the sample cache'
+            'remove-sample: remove the specified sample from the sample cache'
+            'load-module: load a module'
+            'unload-module: unload a module'
+            'move-sink-input: move a stream to a sink'
+            'move-source-output: move a recording stream to a source'
+            'suspend-sink: suspend or resume a sink'
+            'suspend-source: suspend or resume a source'
+            'set-card-profile: set a card profile'
+            'set-default-sink: set the default sink'
+            'set-default-source: set the default source'
+            'set-sink-port: set the sink port of a sink'
+            'set-source-port: set the source port of a source'
+            'set-port-latency-offset: set a latency offset on a port'
+            'set-sink-volume: set the volume of a sink'
+            'set-source-volume: set the volume of a source'
+            'set-sink-input-volume: set the volume of a stream'
+            'set-source-output-volume: set the volume of a recording stream'
+            'set-sink-mute: mute a sink'
+            'set-source-mute: mute a source'
+            'set-sink-input-mute: mute a stream'
+            'set-source-output-mute: mute a recording stream'
+            'set-sink-formats: set supported formats of a sink'
+            'subscribe: subscribe to events'
+        )
+
+        _describe 'pactl commands' _pactl_commands
+    }
+
+    _pactl_command_parameter() {
+        local _command
+
+        _list_parameter() {
+            local -a _objects;
+
+            _objects=(
+                'modules: list loaded modules'
+                'sinks: list available sinks'
+                'sources: list available sources'
+                'sink-inputs: list connected sink inputs'
+                'source-outputs: list connected source outputs'
+                'clients: list connected clients'
+                'samples: list samples'
+                'cards: list available cards'
+            )
+
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after "list".
+                # "pactl list cards short" and "pactl list short cards" are
+                # treated as equivalent by pactl, but here we only support the
+                # first form, so "short" isn't a valid completion.
+                _describe 'objects' _objects
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after "list". As
+                # explained in the previous comment, we only support the
+                # "pactl list cards short" form, so "short" is the only valid
+                # completion here.
+                compadd short
+            fi
+        }
+
+        _play_sample_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after "play-sample".
+                # TODO: Implement sample name completion.
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after "play-sample".
+                # TODO: Implement sink name completion.
+            fi
+        }
+
+        _load_module_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after "load-module".
+                _all_modules
+            else
+                # We're completing the second or later parameter after
+                # "load-module", i.e. the module arguments.
+                # TODO: Implement module argument completion.
+            fi
+        }
+
+        _move_sink_input_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after "move-sink-input".
+                # Even though the function name is "_devices", it actually
+                # completes the sink input index. _devices is magical like
+                # that.
+                _devices
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after
+                # "move-sink-input".
+                _devices
+            fi
+        }
+
+        _move_source_output_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after
+                # "move-source-output". Even though the function name is
+                # "_devices", it actually completes the source output index.
+                # _devices is magical like that.
+                _devices
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after
+                # "move-source-output".
+                _devices
+            fi
+        }
+
+        _suspend_sink_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after "suspend-sink".
+                _devices
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after "suspend-sink".
+                compadd true false
+            fi
+        }
+
+        _suspend_source_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after "suspend-source".
+                _devices
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after "suspend-source".
+                compadd true false
+            fi
+        }
+
+        _set_card_profile_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after
+                # "set-card-profile".
+                _cards
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after
+                # "set-card-profile".
+                _profiles
+            fi
+        }
+
+        _set_sink_port_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after "set-sink-port".
+                _devices
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after "set-sink-port".
+                _ports
+            fi
+        }
+
+        _set_source_port_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after "set-source-port".
+                _devices
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after
+                # "set-source-port".
+                _ports
+            fi
+        }
+
+        _set_sink_mute_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after "set-sink-mute".
+                _devices
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after "set-sink-mute".
+                compadd true false toggle
+            fi
+        }
+
+        _set_source_mute_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after "set-source-mute".
+                _devices
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after
+                # "set-source-mute".
+                compadd true false toggle
+            fi
+        }
+
+        _set_sink_input_mute_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after
+                # "set-sink-input-mute". Even though the function name is
+                # "_devices", it actually completes the sink input index.
+                # _devices is magical like that.
+                _devices
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after
+                # "set-sink-input-mute".
+                compadd true false toggle
+            fi
+        }
+
+        _set_source_output_mute_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after
+                # "set-source-output-mute". Even though the function name is
+                # "_devices", it actually completes the source output index.
+                # _devices is magical like that.
+                _devices
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after
+                # "set-source-output-mute".
+                compadd true false toggle
+            fi
+        }
+
+        _set_port_latency_offset_parameter() {
+            if ((CURRENT == 2)); then
+                # We're completing the first parameter after
+                # "set-port-latency-offset".
+                _cards
+            elif ((CURRENT == 3)); then
+                # We're completing the second parameter after
+                # "set-port-latency-offset".
+                _ports
+            fi
+        }
+
+        _command=$words[1]
+
+        case $_command in
+            list)                                  _list_parameter;;
+            upload-sample)                         if ((CURRENT == 2)); then _files; fi;;
+            play-sample)                           _play_sample_parameter;;
+            remove-sample)                         ;; # TODO: Implement sample name completion.
+            load-module)                           _load_module_parameter;;
+            unload-module)                         if ((CURRENT == 2)); then _loaded_modules; fi;;
+            move-sink-input)                       _move_sink_input_parameter;;
+            move-source-output)                    _move_source_output_parameter;;
+            suspend-sink)                          _suspend_sink_parameter;;
+            suspend-source)                        _suspend_source_parameter;;
+            set-card-profile)                      _set_card_profile_parameter;;
+            set-default-sink)                      if ((CURRENT == 2)); then _devices; fi;;
+            set-default-source)                    if ((CURRENT == 2)); then _devices; fi;;
+            set-sink-port)                         _set_sink_port_parameter;;
+            set-source-port)                       _set_source_port_parameter;;
+            set-sink-volume)                       if ((CURRENT == 2)); then _devices; fi;;
+            set-source-volume)                     if ((CURRENT == 2)); then _devices; fi;;
+            set-sink-input-volume)                 if ((CURRENT == 2)); then _devices; fi;;
+            set-source-output-volume)              if ((CURRENT == 2)); then _devices; fi;;
+            set-sink-mute)                         _set_sink_mute_parameter;;
+            set-source-mute)                       _set_source_mute_parameter;;
+            set-sink-input-mute)                   _set_sink_input_mute_parameter;;
+            set-source-output-mute)                _set_source_output_mute_parameter;;
+            set-sink-formats)                      if ((CURRENT == 2)); then _devices; fi;;
+            set-port-latency-offset)               _set_port_latency_offset_parameter;;
+        esac
+    }
+
+    _arguments -C -S -A '-*' \
+        {-h,--help}'[display help and exit]' \
+        '--version[show version and exit]' \
+        {-s,--server=}'[name of server to connect to]:host:_hosts' \
+        {-n,--client-name=}'[client name to use]:name' \
+        '::pactl command:_pactl_command' \
+        '*::pactl command parameter:_pactl_command_parameter'
+}
+
+_pacmd_completion() {
+    _pacmd_command(){
+        _pacmd_commands=(
+            'help: show help and exit'
+            'list-modules: list modules'
+            'list-cards: list cards'
+            'list-sinks: list sinks'
+            'list-sources: list sources'
+            'list-clients: list clients'
+            'list-sink-inputs: list sink-inputs'
+            'list-source-outputs: list source-outputs'
+            'stat: dump statistics about the PulseAudio daemon'
+            'info: dump info about the PulseAudio daemon'
+            'load-module: load a module'
+            'unload-module: unload a module'
+            'describe-module: print info for a module'
+            'set-sink-volume: set the volume of a sink'
+            'set-source-volume: set the volume of a source'
+            'set-sink-mute: mute a sink'
+            'set-source-mute: mute a source'
+            'set-sink-input-volume: set the volume of a stream'
+            'set-source-output-volume: set the volume of a recording stream'
+            'set-sink-input-mute: mute a stream'
+            'set-source-output-mute: mute a recording stream'
+            'set-default-sink: set the default sink'
+            'set-default-source: set the default source'
+            'set-card-profile: set a card profile'
+            'set-sink-port: set the sink port of a sink'
+            'set-source-port: set the source port of a source'
+            'set-port-latency-offset: set a latency offset on a port'
+            'suspend-sink: suspend or resume a sink'
+            'suspend-source: suspend or resume a source'
+            'suspend: suspend all sinks and sources'
+            'move-sink-input: move a stream to a sink'
+            'move-source-output: move a recording stream to a source'
+            'update-sink-proplist: update the properties of a sink'
+            'update-source-proplist: update the properties of a source'
+            'update-sink-input-proplist: update the properties of a sink-input'
+            'update-source-output-proplist: update the properties of a source-output'
+            'list-samples: list samples'
+            'play-sample: play the specified sample from the sample cache' # TODO
+            'remove-sample: remove the specified sample from the sample cache' # TODO
+            'load-sample: upload a sound from a file into the sample cache'
+            'load-sample-lazy: lazily upload a sound file into the sample cache'
+            'load-sample-dir-lazy: lazily upload all sound files in a directory into the sample cache'
+            'kill-client: kill a client'
+            'kill-sink-input: kill a sink input'
+            'kill-source-output: kill a source output'
+            'set-log-target: change the log target'
+            'set-log-level: change the log level'
+            'set-log-meta: show source code location in log messages'
+            'set-log-time: show timestamps in log messages'
+            'set-log-backtrace: show backtrace in log messages'
+            'play-file: play a sound file'
+            'dump: show daemon configuration'
+            'dump-volumes: show the state of all volumes'
+            'shared: show shared properties'
+            'exit: ask the PulseAudio daemon to exit'
+        )
+        _describe 'pacmd commands' _pacmd_commands
+    }
+
+    _arguments -C -S -A "-*" \
+        {-h,--help}'[display help and exit]' \
+        '--version[show version and exit]' \
+        '::pacmd commands:_pacmd_command' \
+
+    case $words[$((CURRENT - 1))] in
+        set-card-profile) _cards;;
+        set-sink-*) _devices;;
+        set-source-*) _devices;;
+        load-module) _all_modules;;
+        describe-module) _all_modules;;
+        unload-module) _loaded_modules;;
+        suspend-*) _devices;;
+        move-*) _devices;;
+        set-port-latency-offset) _cards;;
+        load-sample*) _files;;
+        kill-client) _clients;;
+        kill-(sink|source)-*) _devices;;
+        set-log-target) compadd null auto syslog stderr file:;;
+        set-log-*) compadd true false;;
+        play-file) _files;;
+    esac
+
+    case $words[$((CURRENT - 2))] in
+        set-card-profile) _profiles;;
+        set-(sink|source)-port) _ports;;
+        set-port-latency-offset) _ports;;
+        set-*-mute) compadd true false;;
+        suspend-*) compadd true false;;
+        move-*) _devices;;
+    esac
+}
+
+_pasuspender_completion() {
+    # parenthesis expressions specify which options should not get suggested afterwards
+    # e.g. after -s, help and version will not get suggested, -- remains the only valid argument
+    # after "--" external commands get suggested
+    _arguments -C \
+        '(-)'{-h,--help}'[display help and exit]' \
+        '(-)--version[show version and exit]' \
+        '(-h --help --version -s --server)'{-s,--server=}'[name of server to connect to]:host:_hosts' \
+        '(-)--[program]:program: _command_names -e' && return 0
+    # check if "--" is one of the words in the command line
+    # if so, ${words[(i)--]} is the position of "--"
+    # shift the arguments and reduce CURRENT, such that further completions see only what follows "--"
+    if [[ ${words[(r)--]} == "--" ]]; then
+        for ((j = ${words[(i)--]}; j > 0; j--));
+        do
+            shift words
+            (( CURRENT-- ))
+        done
+        # proceed with normal completion
+        _normal
+    fi
+}
+
+_padsp_completion() {
+    _arguments -C -S -A "-*" \
+        '-h[display help and exit]' \
+        '-s[name of server to connect to]:host:_hosts' \
+        '-n[client name to use]:name:' \
+        '-m[stream name to use]:name:' \
+        '-M[disable /dev/mixer emulation]' \
+        '-S[disable /dev/sndstat emulation]' \
+        '-D[disable /dev/dsp emulation]' \
+        '-d[enable debug output]' \
+        '--[disable further command line parsing]' \
+}
+
+# TODO channel map completion
+_pacat_completion() {
+    _set_remote
+
+    _pacat_sample_formats=('s16le' 's16be' 'u8' 'float32le' 'float32be'
+        'ulaw' 'alaw' 's32le' 's32be' 's24le' 's24-32le' 's24-32be')
+
+    _arguments -C -S -A "-*" \
+        {-h,--help}'[display this help and exit]' \
+        '--version[show version and exit]' \
+        {-r,--record}'[create a connection for recording]' \
+        {-p,--playback}'[create a connection for playback]' \
+        {-s,--server=}'[name of server to connect to]:host:_hosts' \
+        {-d,--device=}'[name of sink/source to connect to]:device:_devices' \
+        '--monitor-stream=[index of the sink input to record from]:device:_devices' \
+        {-n,--client-name=}'[client name to use]:name' \
+        '--stream-name=[how to call this stream]:name' \
+        '--volume=[initial volume to use]:volume' \
+        '--rate=[sample rate to use]:rate:(44100 48000 96000)' \
+        '--format=[sample type to use]:format:((${(q)_pacat_sample_formats}))' \
+        '--channels=[number of channels to use]:number:(1 2)' \
+        '--channel-map=[channel map to use]:map' \
+        '--fix-format[use the sample format of the sink]' \
+        '--fix-rate[use the rate of the sink]' \
+        '--fix-channels[channel map of the sink]' \
+        '--no-remix[do not upmix or downmix channels]' \
+        '--no-remap[map channels by index instead of name]' \
+        '--latency=[request the specified latency]:bytes' \
+        '--process-time=[request the specified process time]:bytes' \
+        '--latency-msec=[request the specified latency in msec]:msec' \
+        '--process-time-msec=[request the specified process time in msec]:msec' \
+        '--property=[set the specified property]:property' \
+        '--raw[record/play raw PCM data]' \
+        '--passthrough[passtrough data]' \
+        '--file-format=[record/play formatted PCM data]:format:_pacat_file_formats' \
+        '--list-file-formats[list available formats]' \
+        '::files:_files' \
+}
+
+# TODO log-target file completion
+_pulseaudio_completion() {
+    _arguments -C \
+        {-h,--help}'[display this help and exit]' \
+        '--version[show version and exit]' \
+        '--dump-conf[show default configuration]' \
+        '--dump-modules[show available modules]' \
+        '--dump-resample-methods[show available resample methods]' \
+        '--cleanup-shm[cleanup shared memory]' \
+        '--start[start the daemon]' \
+        {-k,--kill}'[kill a running daemon]' \
+        '--check[check for a running daemon]' \
+        '--system=[run as systemd-wide daemon]:bool:(true false)' \
+        {-D,--daemonize=}'[daemonize after startup]:bool:(true false)' \
+        '--fail=[quit when startup fails]:bool:(true false)' \
+        '--high-priority=[try to set high nice level]:bool:(true false)' \
+        '--realtime=[try to enable rt scheduling]:bool:(true false)' \
+        '--disallow-module-loading=[disallow module loading]:bool:(true false)' \
+        '--disallow-exit=[disallow user requested exit]' \
+        '--exit-idle-time=[terminate the daemon on passed idle time]:time' \
+        '--scache-idle-time=[unload autoloaded samples on passed idle time]:time' \
+        '--log-level=[set the verbosity level]:level' \
+        '-v[increase the verbosity level]' \
+        '--log-target=[set the log target]:target:(auto syslog stderr file\: new_file\:):file' \
+        '--log-meta=[include code location in log messages]:bool:(true false)' \
+        '--log-time=[include timestamps in log messages]:bool:(true false)' \
+        '--log-backtrace=[include backtrace in log messages]:frames' \
+        {-p,--dl-search-path=}'[set the search path for plugins]:dir:_files' \
+        '--resample-method=[set the resample method]:method:_resample_methods' \
+        '--use-pid-file=[create a PID file]:bool:(true false)' \
+        '--no-cpu-limit=[do not install CPU load limiter]:bool:(true false)' \
+        '--disable-shm=[disable shared memory support]:bool:(true false)' \
+        '--enable-memfd=[enable memfd shared memory support]:bool:(true false)' \
+        {-L,--load=}'[load the specified module]:modules:_all_modules' \
+        {-F,--file=}'[run the specified script]:file:_files' \
+        '-C[open a command line on the running tty]' \
+        '-n[do not load the default script file]' \
+}
+
+_pulseaudio() {
+    local state line curcontext="$curcontext"
+
+    # Some commands, like pactl and pacat, have an option for specifying the
+    # server address, like "--server=somehost". If that option is set, then the
+    # helper commands that are run as part of the autocompletion need to use
+    # that same option. The option is saved in this variable in _set_remote(),
+    # which is called in the beginning of _pactl_completion() and others. The
+    # autocompletion commands can then find the option in that variable if the
+    # option is set.
+    local remote
+
+    case $service in
+        pulseaudio) _pulseaudio_completion;;
+        pactl) _pactl_completion;;
+        pacmd) _pacmd_completion;;
+        pacat) _pacat_completion;;
+        paplay)_pacat_completion;;
+        parec) _pacat_completion;;
+        parecord)_pacat_completion;;
+        padsp) _padsp_completion;;
+        pasuspender) _pasuspender_completion;;
+        *) _message "Err";;
+    esac
+}
+
+_pulseaudio "$@"
+
+#vim: set ft=zsh sw=4 ts=4 noet
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644 (file)
index 0000000..f7ec1bc
--- /dev/null
@@ -0,0 +1,85 @@
+TAGS
+*.lo
+*.o
+*.la
+*.gcno
+*.trs
+*.log
+.deps
+.libs
+/Makefile
+/Makefile.in
+client.conf
+daemon.conf
+default.pa
+echo-cancel-test
+esdcompat
+gconf-helper
+pacat
+pacmd
+pactl
+padsp
+paplay
+pasuspender
+pax11publish
+pulseaudio
+pulseaudio.service
+start-pulseaudio-x11
+*-symdef.h
+*-orc-gen.[ch]
+# tests
+alsa-mixer-path-test
+alsa-time-test
+asyncmsgq-test
+asyncq-test
+channelmap-test
+close-test
+connect-stress
+core-util-test
+cpulimit-test
+cpulimit-test2
+cpu-sconv-test
+cpu-remap-test
+cpu-mix-test
+cpu-volume-test
+extended-test
+flist-test
+format-test
+get-binary-name-test
+gtk-test
+hook-list-test
+interpol-test
+ipacl-test
+json-test
+lfe-filter-test
+lock-autospawn-test
+lo-latency-test
+mainloop-test
+mainloop-test-glib
+mcalign-test
+memblockq-test
+memblock-test
+mix-test
+once-test
+pacat-simple
+parec-simple
+proplist-test
+queue-test
+remix-test
+resampler-test
+rtpoll-test
+rtstutter
+sig2str-test
+sigbus-test
+smoother-test
+srbchannel-test
+stripnul
+strlist-test
+sync-playback
+system.pa
+thread-mainloop-test
+thread-test
+usergroup-test
+utf8-test
+volume-test
+mult-s16-test
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..3ff1139
--- /dev/null
@@ -0,0 +1,2314 @@
+# This file is part of PulseAudio.
+#
+# Copyright 2004-2006 Lennart Poettering
+# Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+# Copyright 2006 Diego Pettenò
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+###################################
+#       Extra directories         #
+###################################
+
+pulseincludedir=$(includedir)/pulse
+pulsecoreincludedir=$(includedir)/pulsecore
+pulselibexecdir=$(libexecdir)/pulse
+if HAVE_X11
+xdgautostartdir=$(sysconfdir)/xdg/autostart
+endif
+if HAVE_ALSA
+alsaprofilesetsdir=$(datadir)/pulseaudio/alsa-mixer/profile-sets
+alsapathsdir=$(datadir)/pulseaudio/alsa-mixer/paths
+endif
+if HAVE_DBUS
+dbuspolicydir=$(sysconfdir)/dbus-1/system.d
+endif
+
+###################################
+#     Compiler/linker flags       #
+###################################
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src \
+       -I$(top_srcdir)/src/modules \
+       -I$(top_builddir)/src/modules \
+       -DPA_ALSA_PATHS_DIR=\"$(alsapathsdir)\" \
+       -DPA_ALSA_PROFILE_SETS_DIR=\"$(alsaprofilesetsdir)\" \
+       -DPA_SRCDIR=\"$(abs_srcdir)\" \
+       -DPA_BUILDDIR=\"$(abs_builddir)\" \
+       -DPULSE_LOCALEDIR=\"$(localedir)\"
+AM_CFLAGS = -std=gnu11 \
+       $(PTHREAD_CFLAGS)
+AM_CXXFLAGS = -std=c++11 \
+       $(PTHREAD_CFLAGS)
+SERVER_CFLAGS = -D__INCLUDED_FROM_PULSE_AUDIO
+
+AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS)
+AM_LDADD = $(PTHREAD_LIBS) $(INTLLIBS)
+AM_LDFLAGS =
+# Should only be applied to libraries.
+AM_LIBLDFLAGS = $(NODELETE_LDFLAGS)
+
+if HAVE_GCOV
+AM_CFLAGS+=$(GCOV_CFLAGS)
+AM_CXXFLAGS+=$(GCOV_CFLAGS)
+AM_LDFLAGS+=$(GCOV_LIBS)
+endif
+
+if STATIC_BINS
+BINLDFLAGS = -static
+endif
+
+if OS_IS_WIN32
+AM_LDFLAGS+=-Wl,--export-all-symbols,--enable-auto-import -no-undefined
+WINSOCK_LIBS=-lwsock32 -lws2_32 -lwininet
+endif
+
+if OS_IS_DARWIN
+AM_LDFLAGS+=-Wl,-headerpad_max_install_names -headerpad_max_install_names
+endif
+
+FOREIGN_CFLAGS = -w
+
+MODULE_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -module -disable-static -avoid-version $(NOUNDEFINED_LDFLAGS)
+MODULE_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
+
+###################################
+#          Extra files            #
+###################################
+
+EXTRA_DIST = \
+               pulse/client.conf.in \
+               pulse/version.h.in \
+               pulsecore/filter/LICENSE.WEBKIT \
+               daemon/daemon.conf.in \
+               daemon/default.pa.in \
+               daemon/system.pa.in \
+               depmod.py \
+               daemon/esdcompat.in \
+               daemon/start-pulseaudio-x11.in \
+               daemon/systemd/user/pulseaudio.service.in \
+               daemon/systemd/user/pulseaudio.socket \
+               utils/padsp.in \
+               utils/qpaeq \
+               modules/module-defs.h.m4 \
+               daemon/pulseaudio.desktop.in \
+               map-file \
+               daemon/pulseaudio-system.conf \
+               modules/echo-cancel/adrian-license.txt
+
+pulseconf_DATA = \
+               default.pa \
+               system.pa \
+               daemon.conf \
+               client.conf
+
+if HAVE_DBUS
+dbuspolicy_DATA = \
+               daemon/pulseaudio-system.conf
+endif
+
+if HAVE_X11
+xdgautostart_in_files = \
+               daemon/pulseaudio.desktop.in
+xdgautostart_DATA = $(xdgautostart_in_files:.desktop.in=.desktop)
+@INTLTOOL_DESKTOP_RULE@
+endif
+
+
+###################################
+#          Includes               #
+###################################
+
+BUILT_SOURCES =
+CLEANFILES =
+include $(top_srcdir)/orc.mak
+ORC_SOURCE =
+
+###################################
+#          Main daemon            #
+###################################
+
+bin_PROGRAMS = pulseaudio
+
+pulseaudio_SOURCES = \
+               daemon/caps.c daemon/caps.h \
+               daemon/cmdline.c daemon/cmdline.h \
+               daemon/cpulimit.c daemon/cpulimit.h \
+               daemon/daemon-conf.c daemon/daemon-conf.h \
+               daemon/dumpmodules.c daemon/dumpmodules.h \
+               daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
+               daemon/main.c
+
+pulseaudio_CFLAGS = $(AM_CFLAGS) $(CAP_CFLAGS)
+pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la $(LIBLTDL) $(CAP_LIBS)
+# This is needed because automake doesn't properly expand the foreach below
+pulseaudio_DEPENDENCIES = libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la $(PREOPEN_LIBS)
+
+if HAVE_DBUS
+pulseaudio_CFLAGS += $(DBUS_CFLAGS)
+pulseaudio_SOURCES += daemon/server-lookup.c daemon/server-lookup.h
+pulseaudio_LDADD += $(DBUS_LIBS)
+endif
+
+if PREOPEN_MODS
+PREOPEN_LIBS = $(PREOPEN_MODS)
+else
+PREOPEN_LIBS = $(modlibexec_LTLIBRARIES)
+endif
+
+if FORCE_PREOPEN
+pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(IMMEDIATE_LDFLAGS) -dlpreopen force $(foreach f,$(PREOPEN_LIBS),-dlpreopen $(f))
+else
+pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(IMMEDIATE_LDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f))
+endif
+
+if HAVE_SYSTEMD_DAEMON
+pulseaudio_CFLAGS += $(SYSTEMD_FLAGS) $(SYSTEMDDAEMON_FLAGS)
+pulseaudio_LDFLAGS += $(SYSTEMD_LIBS) $(SYSTEMDDAEMON_LIBS)
+endif
+
+###################################
+#       Utility programs          #
+###################################
+
+bin_SCRIPTS = esdcompat
+
+bin_PROGRAMS += \
+               pacat \
+               pactl
+
+if !OS_IS_WIN32
+bin_PROGRAMS += pasuspender
+endif
+
+if HAVE_AF_UNIX
+bin_PROGRAMS += pacmd
+endif
+
+if HAVE_X11
+bin_PROGRAMS += pax11publish
+bin_SCRIPTS += start-pulseaudio-x11
+endif
+
+pacat_SOURCES = utils/pacat.c
+pacat_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la $(LIBSNDFILE_LIBS)
+pacat_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
+pacat_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pactl_SOURCES = utils/pactl.c
+pactl_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la $(LIBSNDFILE_LIBS)
+pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
+pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pasuspender_SOURCES = utils/pasuspender.c
+pasuspender_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+pasuspender_CFLAGS = $(AM_CFLAGS)
+pasuspender_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pacmd_SOURCES = utils/pacmd.c
+pacmd_CFLAGS = $(AM_CFLAGS)
+pacmd_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+pacmd_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pax11publish_SOURCES = utils/pax11publish.c
+pax11publish_CFLAGS = $(AM_CFLAGS) $(X11_CFLAGS)
+pax11publish_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la $(X11_LIBS)
+pax11publish_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+if HAVE_SYSTEMD_DAEMON
+
+systemduserunit_DATA = \
+    pulseaudio.service \
+    daemon/systemd/user/pulseaudio.socket
+
+endif
+
+###################################
+#         Test programs           #
+###################################
+noinst_LTLIBRARIES =
+
+TESTS_default = \
+               core-util-test \
+               mainloop-test \
+               strlist-test \
+               close-test \
+               memblockq-test \
+               channelmap-test \
+               thread-mainloop-test \
+               utf8-test \
+               format-test \
+               json-test \
+               get-binary-name-test \
+               hook-list-test \
+               memblock-test \
+               asyncq-test \
+               asyncmsgq-test \
+               queue-test \
+               rtpoll-test \
+               resampler-test \
+               smoother-test \
+               thread-test \
+               volume-test \
+               mix-test \
+               proplist-test \
+               cpu-mix-test \
+               cpu-remap-test \
+               cpu-sconv-test \
+               cpu-volume-test \
+               lock-autospawn-test \
+               mult-s16-test \
+               lfe-filter-test
+
+TESTS_norun = \
+               ipacl-test \
+               mcalign-test \
+               pacat-simple \
+               parec-simple \
+               flist-test \
+               remix-test \
+               rtstutter \
+               sig2str-test \
+               stripnul \
+               echo-cancel-test \
+               lo-latency-test
+
+# These tests need a running pulseaudio daemon
+TESTS_daemon = \
+               connect-stress \
+               extended-test \
+               interpol-test \
+               sync-playback
+
+if !OS_IS_WIN32
+TESTS_default += \
+               sigbus-test \
+               usergroup-test
+endif
+
+if HAVE_SYS_EVENTFD_H
+TESTS_default += \
+               srbchannel-test
+endif
+
+if !OS_IS_DARWIN
+TESTS_default += \
+               once-test
+endif
+
+if HAVE_SIGXCPU
+TESTS_norun += \
+               cpulimit-test \
+               cpulimit-test2
+endif
+
+if HAVE_GLIB20
+TESTS_default += \
+               mainloop-test-glib
+
+if HAVE_GTK30
+# gtk-test depends on both glib and gtk
+TESTS_norun += \
+               gtk-test
+endif
+endif
+
+if HAVE_ALSA
+TESTS_norun += \
+               alsa-time-test
+TESTS_default += \
+               alsa-mixer-path-test
+endif
+
+if HAVE_TESTS
+TESTS_ENVIRONMENT=MAKE_CHECK=1
+TESTS = $(TESTS_default)
+
+if BUILD_TESTS_DEFAULT
+noinst_PROGRAMS = $(TESTS_default) $(TESTS_norun) $(TESTS_daemon)
+else
+check_PROGRAMS = $(TESTS_default) $(TESTS_norun)
+endif
+
+check-daemon: $(TESTS_daemon)
+       PATH=$(builddir):${PATH} $(top_srcdir)/src/tests/test-daemon.sh $(TESTS_daemon)
+
+else
+TESTS_ENVIRONMENT=
+TESTS =
+noinst_PROGRAMS =
+check_PROGRAMS =
+
+check-daemon:
+       @echo "Tests are disabled!"
+       @echo "Pass option \"--enable-tests\" to configure and install \"check\" library properly!"
+       false
+
+endif
+
+core_util_test_SOURCES = tests/core-util-test.c
+core_util_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+core_util_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+core_util_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+mainloop_test_SOURCES = tests/mainloop-test.c
+mainloop_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+mainloop_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+thread_mainloop_test_SOURCES = tests/thread-mainloop-test.c
+thread_mainloop_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+thread_mainloop_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+thread_mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+utf8_test_SOURCES = tests/utf8-test.c
+utf8_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+utf8_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+utf8_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+format_test_SOURCES = tests/format-test.c
+format_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+format_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+format_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+json_test_SOURCES = tests/json-test.c
+json_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+json_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+json_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+srbchannel_test_SOURCES = tests/srbchannel-test.c
+srbchannel_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+srbchannel_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+srbchannel_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+get_binary_name_test_SOURCES = tests/get-binary-name-test.c
+get_binary_name_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+get_binary_name_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+get_binary_name_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+ipacl_test_SOURCES = tests/ipacl-test.c
+ipacl_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+ipacl_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+ipacl_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+hook_list_test_SOURCES = tests/hook-list-test.c
+hook_list_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+hook_list_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+hook_list_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+memblock_test_SOURCES = tests/memblock-test.c
+memblock_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+memblock_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+memblock_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+thread_test_SOURCES = tests/thread-test.c
+thread_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+thread_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+thread_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+once_test_SOURCES = tests/once-test.c
+once_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+once_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+once_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+flist_test_SOURCES = tests/flist-test.c
+flist_test_CFLAGS = $(AM_CFLAGS)
+flist_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+flist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+asyncq_test_SOURCES = tests/asyncq-test.c
+asyncq_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+asyncq_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+asyncq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+asyncmsgq_test_SOURCES = tests/asyncmsgq-test.c
+asyncmsgq_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+asyncmsgq_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+asyncmsgq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+queue_test_SOURCES = tests/queue-test.c
+queue_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+queue_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+queue_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+rtpoll_test_SOURCES = tests/rtpoll-test.c
+rtpoll_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+rtpoll_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+rtpoll_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+mcalign_test_SOURCES = tests/mcalign-test.c
+mcalign_test_CFLAGS = $(AM_CFLAGS)
+mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+mcalign_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pacat_simple_SOURCES = tests/pacat-simple.c
+pacat_simple_LDADD = $(AM_LDADD) libpulse.la libpulse-simple.la
+pacat_simple_CFLAGS = $(AM_CFLAGS)
+pacat_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+parec_simple_SOURCES = tests/parec-simple.c
+parec_simple_LDADD = $(AM_LDADD) libpulse.la libpulse-simple.la
+parec_simple_CFLAGS = $(AM_CFLAGS)
+parec_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+extended_test_SOURCES = tests/extended-test.c
+extended_test_LDADD = $(AM_LDADD) libpulse.la
+extended_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+extended_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+strlist_test_SOURCES = tests/strlist-test.c
+strlist_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+strlist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+close_test_SOURCES = tests/close-test.c
+close_test_CFLAGS = $(AM_CFLAGS)
+close_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+close_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+volume_test_SOURCES = tests/volume-test.c
+volume_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+volume_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+volume_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+channelmap_test_SOURCES = tests/channelmap-test.c
+channelmap_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+channelmap_test_LDADD = $(AM_LDADD) libpulse.la
+channelmap_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+cpulimit_test_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h
+cpulimit_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+cpulimit_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+cpulimit_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+cpulimit_test2_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h
+cpulimit_test2_CFLAGS = $(AM_CFLAGS) -DTEST2 $(LIBCHECK_CFLAGS)
+cpulimit_test2_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+cpulimit_test2_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+mainloop_test_glib_SOURCES = $(mainloop_test_SOURCES)
+mainloop_test_glib_CFLAGS = $(mainloop_test_CFLAGS) $(LIBCHECK_CFLAGS) $(GLIB20_CFLAGS) -DGLIB_MAIN_LOOP
+mainloop_test_glib_LDADD = $(mainloop_test_LDADD) $(GLIB20_LIBS) libpulse-mainloop-glib.la
+mainloop_test_glib_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+memblockq_test_SOURCES = tests/memblockq-test.c
+memblockq_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+memblockq_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+memblockq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+sync_playback_SOURCES = tests/sync-playback.c
+sync_playback_LDADD = $(AM_LDADD) libpulse.la
+sync_playback_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+sync_playback_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+interpol_test_SOURCES = tests/interpol-test.c
+interpol_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+interpol_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+interpol_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+sig2str_test_SOURCES = tests/sig2str-test.c
+sig2str_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+sig2str_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+sig2str_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+resampler_test_SOURCES = tests/resampler-test.c
+resampler_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+resampler_test_CFLAGS = $(AM_CFLAGS)
+resampler_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+mix_test_SOURCES = tests/mix-test.c
+mix_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+mix_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+mix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+remix_test_SOURCES = tests/remix-test.c
+remix_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+remix_test_CFLAGS = $(AM_CFLAGS)
+remix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+smoother_test_SOURCES = tests/smoother-test.c
+smoother_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+smoother_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+smoother_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+proplist_test_SOURCES = tests/proplist-test.c
+proplist_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+proplist_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+proplist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+cpu_mix_test_SOURCES = tests/cpu-mix-test.c tests/runtime-test-util.h
+cpu_mix_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+cpu_mix_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+cpu_mix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+cpu_remap_test_SOURCES = tests/cpu-remap-test.c tests/runtime-test-util.h
+cpu_remap_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+cpu_remap_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+cpu_remap_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+cpu_sconv_test_SOURCES = tests/cpu-sconv-test.c tests/runtime-test-util.h
+cpu_sconv_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+cpu_sconv_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+cpu_sconv_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+cpu_volume_test_SOURCES = tests/cpu-volume-test.c tests/runtime-test-util.h
+cpu_volume_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+cpu_volume_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+cpu_volume_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+mult_s16_test_SOURCES = tests/mult-s16-test.c tests/runtime-test-util.h
+mult_s16_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+mult_s16_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+mult_s16_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+lfe_filter_test_SOURCES = tests/lfe-filter-test.c
+lfe_filter_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+lfe_filter_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+lfe_filter_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+rtstutter_SOURCES = tests/rtstutter.c
+rtstutter_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+rtstutter_CFLAGS = $(AM_CFLAGS)
+rtstutter_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+stripnul_SOURCES = tests/stripnul.c
+stripnul_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+stripnul_CFLAGS = $(AM_CFLAGS)
+stripnul_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+lock_autospawn_test_SOURCES = tests/lock-autospawn-test.c
+lock_autospawn_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+lock_autospawn_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+lock_autospawn_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+sigbus_test_SOURCES = tests/sigbus-test.c
+sigbus_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+sigbus_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+sigbus_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+gtk_test_SOURCES = tests/gtk-test.c
+gtk_test_LDADD = $(AM_LDADD) $(GTK30_LIBS) libpulse-mainloop-glib.la libpulse.la
+gtk_test_CFLAGS = $(AM_CFLAGS) $(GTK30_CFLAGS)
+gtk_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+alsa_time_test_SOURCES = tests/alsa-time-test.c
+alsa_time_test_LDADD = $(AM_LDADD) $(ASOUNDLIB_LIBS)
+alsa_time_test_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+alsa_time_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+alsa_mixer_path_test_SOURCES = tests/alsa-mixer-path-test.c
+alsa_mixer_path_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS) $(ASOUNDLIB_CFLAGS)
+alsa_mixer_path_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la libalsa-util.la
+alsa_mixer_path_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+usergroup_test_SOURCES = tests/usergroup-test.c
+usergroup_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+usergroup_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+usergroup_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+connect_stress_SOURCES = tests/connect-stress.c
+connect_stress_LDADD = $(AM_LDADD) libpulse.la
+connect_stress_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+connect_stress_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+echo_cancel_test_SOURCES = $(module_echo_cancel_la_SOURCES)
+nodist_echo_cancel_test_SOURCES = $(nodist_module_echo_cancel_la_SOURCES)
+echo_cancel_test_LDADD = $(module_echo_cancel_la_LIBADD)
+echo_cancel_test_CFLAGS = $(module_echo_cancel_la_CFLAGS) -DECHO_CANCEL_TEST=1
+if HAVE_WEBRTC
+echo_cancel_test_CXXFLAGS = $(module_echo_cancel_la_CXXFLAGS) -DECHO_CANCEL_TEST=1
+endif
+echo_cancel_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+liblo_test_util_la_SOURCES = tests/lo-test-util.h tests/lo-test-util.c
+liblo_test_util_la_LIBADD = libpulsecore-@PA_MAJORMINOR@.la
+liblo_test_util_la_LDFLAGS = -avoid-version
+noinst_LTLIBRARIES += liblo-test-util.la
+
+lo_latency_test_SOURCES = tests/lo-latency-test.c
+lo_latency_test_LDADD = $(AM_LDADD) libpulse.la liblo-test-util.la
+lo_latency_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+lo_latency_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
+###################################
+#         Common library          #
+###################################
+
+commonlibdir = $(pkglibdir)
+commonlib_LTLIBRARIES = \
+               libpulsecommon-@PA_MAJORMINOR@.la
+
+# We duplicate files from pulse/ in this to allow as-needed linking. If we did
+# not do this, in situations where code in libpulsecommon uses code in
+# libpulse, we would then need to link libpulsecommon to libpulse (in addition
+# to the existing libpulse being linked to libpulsecommon). Duplicating the
+# code allows us to prevent this circular linking.
+libpulsecommon_@PA_MAJORMINOR@_la_SOURCES = \
+               pulse/client-conf.c pulse/client-conf.h \
+               pulse/fork-detect.c pulse/fork-detect.h \
+               pulse/format.c pulse/format.h \
+               pulse/json.c pulse/json.h \
+               pulse/xmalloc.c pulse/xmalloc.h \
+               pulse/proplist.c pulse/proplist.h \
+               pulse/utf8.c pulse/utf8.h \
+               pulse/channelmap.c pulse/channelmap.h \
+               pulse/sample.c pulse/sample.h \
+               pulse/util.c pulse/util.h \
+               pulse/timeval.c pulse/timeval.h \
+               pulse/rtclock.c pulse/rtclock.h \
+               pulse/volume.c pulse/volume.h \
+               pulsecore/atomic.h \
+               pulsecore/authkey.c pulsecore/authkey.h \
+               pulsecore/conf-parser.c pulsecore/conf-parser.h \
+               pulsecore/core-error.c pulsecore/core-error.h \
+               pulsecore/core-format.c pulsecore/core-format.h \
+               pulsecore/core-rtclock.c pulsecore/core-rtclock.h \
+               pulsecore/core-util.c pulsecore/core-util.h \
+               pulsecore/creds.h \
+               pulsecore/dynarray.c pulsecore/dynarray.h \
+               pulsecore/endianmacros.h \
+               pulsecore/fdsem.c pulsecore/fdsem.h \
+               pulsecore/flist.c pulsecore/flist.h \
+               pulsecore/g711.c pulsecore/g711.h \
+               pulsecore/hashmap.c pulsecore/hashmap.h \
+               pulsecore/i18n.c pulsecore/i18n.h \
+               pulsecore/idxset.c pulsecore/idxset.h \
+               pulsecore/arpa-inet.c pulsecore/arpa-inet.h \
+               pulsecore/iochannel.c pulsecore/iochannel.h \
+               pulsecore/ioline.c pulsecore/ioline.h \
+               pulsecore/ipacl.c pulsecore/ipacl.h \
+               pulsecore/llist.h \
+               pulsecore/lock-autospawn.c pulsecore/lock-autospawn.h \
+               pulsecore/log.c pulsecore/log.h \
+               pulsecore/ratelimit.c pulsecore/ratelimit.h \
+               pulsecore/macro.h \
+               pulsecore/mcalign.c pulsecore/mcalign.h \
+               pulsecore/memblock.c pulsecore/memblock.h \
+               pulsecore/memblockq.c pulsecore/memblockq.h \
+               pulsecore/memchunk.c pulsecore/memchunk.h \
+               pulsecore/native-common.c pulsecore/native-common.h \
+               pulsecore/once.c pulsecore/once.h \
+               pulsecore/packet.c pulsecore/packet.h \
+               pulsecore/parseaddr.c pulsecore/parseaddr.h \
+               pulsecore/pdispatch.c pulsecore/pdispatch.h \
+               pulsecore/pid.c pulsecore/pid.h \
+               pulsecore/pipe.c pulsecore/pipe.h \
+               pulsecore/memtrap.c pulsecore/memtrap.h \
+               pulsecore/aupdate.c pulsecore/aupdate.h \
+               pulsecore/proplist-util.c pulsecore/proplist-util.h \
+               pulsecore/pstream-util.c pulsecore/pstream-util.h \
+               pulsecore/pstream.c pulsecore/pstream.h \
+               pulsecore/queue.c pulsecore/queue.h \
+               pulsecore/random.c pulsecore/random.h \
+               pulsecore/refcnt.h \
+               pulsecore/srbchannel.c pulsecore/srbchannel.h \
+               pulsecore/sample-util.c pulsecore/sample-util.h \
+               pulsecore/mem.h \
+               pulsecore/shm.c pulsecore/shm.h \
+               pulsecore/bitset.c pulsecore/bitset.h \
+               pulsecore/socket-client.c pulsecore/socket-client.h \
+               pulsecore/socket-server.c pulsecore/socket-server.h \
+               pulsecore/socket-util.c pulsecore/socket-util.h \
+               pulsecore/strbuf.c pulsecore/strbuf.h \
+               pulsecore/strlist.c pulsecore/strlist.h \
+               pulsecore/svolume_c.c pulsecore/svolume_arm.c \
+               pulsecore/svolume_mmx.c pulsecore/svolume_sse.c \
+               pulsecore/tagstruct.c pulsecore/tagstruct.h \
+               pulsecore/time-smoother.c pulsecore/time-smoother.h \
+               pulsecore/tokenizer.c pulsecore/tokenizer.h \
+               pulsecore/usergroup.c pulsecore/usergroup.h \
+               pulsecore/sndfile-util.c pulsecore/sndfile-util.h \
+               pulsecore/socket.h
+
+if OS_IS_WIN32
+libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += \
+               pulsecore/poll-win32.c pulsecore/poll.h \
+               pulsecore/winerrno.h
+else
+libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += pulsecore/poll-posix.c pulsecore/poll.h
+endif
+
+libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
+libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
+libpulsecommon_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSNDFILE_LIBS)
+
+if HAVE_MEMFD
+libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += \
+               pulsecore/memfd-wrappers.h
+endif
+
+if HAVE_X11
+libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += \
+               pulse/client-conf-x11.c pulse/client-conf-x11.h \
+               pulsecore/x11prop.c pulsecore/x11prop.h
+libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(X11_CFLAGS)
+libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS += $(X11_LIBS)
+endif
+
+if HAVE_SYSTEMD_DAEMON
+libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(SYSTEMD_FLAGS) $(SYSTEMDDAEMON_FLAGS)
+libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS += $(SYSTEMD_LIBS) $(SYSTEMDDAEMON_LIBS)
+endif
+if HAVE_SYSTEMD_JOURNAL
+libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(SYSTEMD_FLAGS) $(SYSTEMDJOURNAL_FLAGS)
+libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS += $(SYSTEMD_LIBS) $(SYSTEMDJOURNAL_LIBS)
+endif
+
+# proplist-util.h uses these header files, but not the library itself!
+libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(GLIB20_CFLAGS) $(GTK30_CFLAGS)
+
+## Please note that libpulsecommon implicitly also depends on<
+## libpulse! i.e. we have a cyclic dependancy here. Which is intended
+## since libpulse only includes stable, official APIs, while
+## libpulsecommon only includes unofficial APIs.
+
+if OS_IS_WIN32
+libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += \
+               pulsecore/mutex-win32.c pulsecore/mutex.h \
+               pulsecore/thread-win32.c pulsecore/thread.h \
+               pulsecore/semaphore-win32.c pulsecore/semaphore.h
+else !OS_IS_WIN32
+if OS_IS_DARWIN
+libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += \
+               pulsecore/mutex-posix.c pulsecore/mutex.h \
+               pulsecore/thread-posix.c pulsecore/thread.h \
+               pulsecore/semaphore-osx.c pulsecore/semaphore.h
+else !OS_IS_DARWIN
+libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += \
+               pulsecore/mutex-posix.c pulsecore/mutex.h \
+               pulsecore/thread-posix.c pulsecore/thread.h \
+               pulsecore/semaphore-posix.c pulsecore/semaphore.h
+endif !OS_IS_DARWIN
+endif !OS_IS_WIN32
+
+if HAVE_LIBASYNCNS
+libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(LIBASYNCNS_CFLAGS)
+libpulsecommon_@PA_MAJORMINOR@_la_LIBADD += $(LIBASYNCNS_LIBS)
+endif
+
+if OS_IS_WIN32
+libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += pulsecore/dllmain.c
+endif
+
+if HAVE_DBUS
+libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += \
+               pulsecore/dbus-util.c pulsecore/dbus-util.h \
+               pulsecore/rtkit.c pulsecore/rtkit.h
+libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(DBUS_CFLAGS)
+libpulsecommon_@PA_MAJORMINOR@_la_LIBADD += $(DBUS_LIBS)
+endif
+
+###################################
+#         Client library          #
+###################################
+
+pulseinclude_HEADERS = \
+               pulse/cdecl.h \
+               pulse/channelmap.h \
+               pulse/context.h \
+               pulse/def.h \
+               pulse/direction.h \
+               pulse/error.h \
+               pulse/ext-device-manager.h \
+               pulse/ext-device-restore.h \
+               pulse/ext-stream-restore.h \
+               pulse/format.h \
+               pulse/gccmacro.h \
+               pulse/introspect.h \
+               pulse/mainloop-api.h \
+               pulse/mainloop-signal.h \
+               pulse/mainloop.h \
+               pulse/operation.h \
+               pulse/proplist.h \
+               pulse/pulseaudio.h \
+               pulse/rtclock.h \
+               pulse/sample.h \
+               pulse/scache.h \
+               pulse/simple.h \
+               pulse/stream.h \
+               pulse/subscribe.h \
+               pulse/thread-mainloop.h \
+               pulse/timeval.h \
+               pulse/utf8.h \
+               pulse/util.h \
+               pulse/version.h \
+               pulse/volume.h \
+               pulse/xmalloc.h
+
+lib_LTLIBRARIES = \
+               libpulse.la \
+               libpulse-simple.la
+
+if HAVE_GLIB20
+pulseinclude_HEADERS += \
+               pulse/glib-mainloop.h
+
+lib_LTLIBRARIES += \
+               libpulse-mainloop-glib.la
+endif
+
+# Public interface
+libpulse_la_SOURCES = \
+               pulse/cdecl.h \
+               pulse/channelmap.c pulse/channelmap.h \
+               pulse/context.c pulse/context.h \
+               pulse/def.h \
+               pulse/direction.c pulse/direction.h \
+               pulse/error.c pulse/error.h \
+               pulse/ext-device-manager.c pulse/ext-device-manager.h \
+               pulse/ext-device-restore.c pulse/ext-device-restore.h \
+               pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
+               pulse/format.c pulse/format.h \
+               pulse/gccmacro.h \
+               pulse/internal.h \
+               pulse/introspect.c pulse/introspect.h \
+               pulse/mainloop-api.c pulse/mainloop-api.h \
+               pulse/mainloop-signal.c pulse/mainloop-signal.h \
+               pulse/mainloop.c pulse/mainloop.h \
+               pulse/operation.c pulse/operation.h \
+               pulse/proplist.c pulse/proplist.h \
+               pulse/pulseaudio.h \
+               pulse/rtclock.c pulse/rtclock.h \
+               pulse/sample.c pulse/sample.h \
+               pulse/scache.c pulse/scache.h \
+               pulse/stream.c pulse/stream.h \
+               pulse/subscribe.c pulse/subscribe.h \
+               pulse/thread-mainloop.c pulse/thread-mainloop.h \
+               pulse/timeval.c pulse/timeval.h \
+               pulse/utf8.c pulse/utf8.h \
+               pulse/util.c pulse/util.h \
+               pulse/volume.c pulse/volume.h \
+               pulse/xmalloc.c pulse/xmalloc.h
+
+libpulse_la_CFLAGS = $(AM_CFLAGS)
+libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINOR@.la
+libpulse_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO)
+
+if HAVE_DBUS
+libpulse_la_CFLAGS += $(DBUS_CFLAGS)
+libpulse_la_LIBADD += $(DBUS_LIBS)
+endif
+
+libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h
+libpulse_simple_la_CFLAGS = $(AM_CFLAGS)
+libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+libpulse_simple_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_SIMPLE_VERSION_INFO)
+
+libpulse_mainloop_glib_la_SOURCES = pulse/glib-mainloop.h pulse/glib-mainloop.c
+libpulse_mainloop_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB20_CFLAGS)
+libpulse_mainloop_glib_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la $(GLIB20_LIBS)
+libpulse_mainloop_glib_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO)
+
+###################################
+#         OSS emulation           #
+###################################
+
+if HAVE_OSS_WRAPPER
+padsplibdir = $(pkglibdir)
+padsplib_LTLIBRARIES = libpulsedsp.la
+bin_SCRIPTS += padsp
+
+edit = @SED@ \
+       -e 's|@PULSEDSP_LOCATION[@]|$(PULSEDSP_LOCATION)|g'
+
+padsp: utils/padsp.in
+       $(edit) $< > $@
+
+CLEANFILES += padsp
+
+endif
+
+libpulsedsp_la_SOURCES = utils/padsp.c
+libpulsedsp_la_CFLAGS = $(AM_CFLAGS)
+libpulsedsp_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
+libpulsedsp_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version -disable-static
+
+###################################
+#      Daemon core library        #
+###################################
+
+pkglib_LTLIBRARIES = libpulsecore-@PA_MAJORMINOR@.la
+
+# Pure core stuff
+libpulsecore_@PA_MAJORMINOR@_la_SOURCES = \
+               pulsecore/filter/lfe-filter.c pulsecore/filter/lfe-filter.h \
+               pulsecore/filter/biquad.c pulsecore/filter/biquad.h \
+               pulsecore/filter/crossover.c pulsecore/filter/crossover.h \
+               pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h \
+               pulsecore/asyncq.c pulsecore/asyncq.h \
+               pulsecore/auth-cookie.c pulsecore/auth-cookie.h \
+               pulsecore/cli-command.c pulsecore/cli-command.h \
+               pulsecore/cli-text.c pulsecore/cli-text.h \
+               pulsecore/client.c pulsecore/client.h \
+               pulsecore/typedefs.h \
+               pulsecore/card.c pulsecore/card.h \
+               pulsecore/core-scache.c pulsecore/core-scache.h \
+               pulsecore/core-subscribe.c pulsecore/core-subscribe.h \
+               pulsecore/core.c pulsecore/core.h \
+               pulsecore/hook-list.c pulsecore/hook-list.h \
+               pulsecore/ltdl-helper.c pulsecore/ltdl-helper.h \
+               pulsecore/modargs.c pulsecore/modargs.h \
+               pulsecore/modinfo.c pulsecore/modinfo.h \
+               pulsecore/module.c pulsecore/module.h \
+               pulsecore/msgobject.c pulsecore/msgobject.h \
+               pulsecore/namereg.c pulsecore/namereg.h \
+               pulsecore/object.c pulsecore/object.h \
+               pulsecore/play-memblockq.c pulsecore/play-memblockq.h \
+               pulsecore/play-memchunk.c pulsecore/play-memchunk.h \
+               pulsecore/remap.c pulsecore/remap.h \
+               pulsecore/remap_mmx.c pulsecore/remap_sse.c \
+               pulsecore/resampler.c pulsecore/resampler.h \
+               pulsecore/resampler/ffmpeg.c pulsecore/resampler/peaks.c \
+               pulsecore/resampler/trivial.c \
+               pulsecore/rtpoll.c pulsecore/rtpoll.h \
+               pulsecore/stream-util.c pulsecore/stream-util.h \
+               pulsecore/mix.c pulsecore/mix.h \
+               pulsecore/cpu.c pulsecore/cpu.h \
+               pulsecore/cpu-arm.c pulsecore/cpu-arm.h \
+               pulsecore/cpu-x86.c pulsecore/cpu-x86.h \
+               pulsecore/cpu-orc.c pulsecore/cpu-orc.h \
+               pulsecore/sconv-s16be.c pulsecore/sconv-s16be.h \
+               pulsecore/sconv-s16le.c pulsecore/sconv-s16le.h \
+               pulsecore/sconv_sse.c \
+               pulsecore/sconv.c pulsecore/sconv.h \
+               pulsecore/shared.c pulsecore/shared.h \
+               pulsecore/sink-input.c pulsecore/sink-input.h \
+               pulsecore/sink.c pulsecore/sink.h \
+               pulsecore/device-port.c pulsecore/device-port.h \
+               pulsecore/sioman.c pulsecore/sioman.h \
+               pulsecore/sound-file-stream.c pulsecore/sound-file-stream.h \
+               pulsecore/sound-file.c pulsecore/sound-file.h \
+               pulsecore/source-output.c pulsecore/source-output.h \
+               pulsecore/source.c pulsecore/source.h \
+               pulsecore/start-child.c pulsecore/start-child.h \
+               pulsecore/thread-mq.c pulsecore/thread-mq.h \
+               pulsecore/database.h
+
+libpulsecore_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) $(LIBSNDFILE_CFLAGS) $(WINSOCK_CFLAGS)
+libpulsecore_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
+libpulsecore_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINOR@.la libpulse.la libpulsecore-foreign.la
+
+if HAVE_NEON
+noinst_LTLIBRARIES += libpulsecore_sconv_neon.la libpulsecore_mix_neon.la libpulsecore_remap_neon.la
+libpulsecore_sconv_neon_la_SOURCES = pulsecore/sconv_neon.c
+libpulsecore_sconv_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_CFLAGS)
+libpulsecore_mix_neon_la_SOURCES = pulsecore/mix_neon.c
+libpulsecore_mix_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_CFLAGS)
+libpulsecore_remap_neon_la_SOURCES = pulsecore/remap_neon.c
+libpulsecore_remap_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_CFLAGS)
+libpulsecore_@PA_MAJORMINOR@_la_LIBADD += libpulsecore_sconv_neon.la libpulsecore_mix_neon.la libpulsecore_remap_neon.la
+endif
+
+ORC_SOURCE += pulsecore/svolume
+if HAVE_ORC
+libpulsecore_@PA_MAJORMINOR@_la_SOURCES += pulsecore/svolume_orc.c
+nodist_libpulsecore_@PA_MAJORMINOR@_la_SOURCES = pulsecore/svolume-orc-gen.c pulsecore/svolume-orc-gen.h
+libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += $(ORC_CFLAGS)
+libpulsecore_@PA_MAJORMINOR@_la_LIBADD += $(ORC_LIBS)
+endif
+
+if HAVE_X11
+libpulsecore_@PA_MAJORMINOR@_la_SOURCES += pulsecore/x11wrap.c pulsecore/x11wrap.h
+libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += $(X11_CFLAGS)
+libpulsecore_@PA_MAJORMINOR@_la_LDFLAGS += $(X11_LIBS)
+endif
+
+if HAVE_DBUS
+libpulsecore_@PA_MAJORMINOR@_la_SOURCES += \
+               pulsecore/dbus-shared.c pulsecore/dbus-shared.h \
+               pulsecore/protocol-dbus.c pulsecore/protocol-dbus.h
+libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += $(DBUS_CFLAGS)
+libpulsecore_@PA_MAJORMINOR@_la_LIBADD += $(DBUS_LIBS)
+endif
+
+if HAVE_GDBM
+libpulsecore_@PA_MAJORMINOR@_la_SOURCES += pulsecore/database-gdbm.c
+libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += $(GDBM_CFLAGS)
+libpulsecore_@PA_MAJORMINOR@_la_LIBADD += $(GDBM_LIBS)
+endif
+
+if HAVE_TDB
+libpulsecore_@PA_MAJORMINOR@_la_SOURCES += pulsecore/database-tdb.c
+libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += $(TDB_CFLAGS)
+libpulsecore_@PA_MAJORMINOR@_la_LIBADD += $(TDB_LIBS)
+endif
+
+if HAVE_SIMPLEDB
+libpulsecore_@PA_MAJORMINOR@_la_SOURCES += pulsecore/database-simple.c
+endif
+
+if HAVE_SPEEX
+libpulsecore_@PA_MAJORMINOR@_la_SOURCES += pulsecore/resampler/speex.c
+libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += $(LIBSPEEX_CFLAGS)
+libpulsecore_@PA_MAJORMINOR@_la_LIBADD += $(LIBSPEEX_LIBS)
+endif
+
+if HAVE_SOXR
+libpulsecore_@PA_MAJORMINOR@_la_SOURCES += pulsecore/resampler/soxr.c
+libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += $(LIBSOXR_CFLAGS)
+libpulsecore_@PA_MAJORMINOR@_la_LIBADD += $(LIBSOXR_LIBS)
+endif
+
+if HAVE_LIBSAMPLERATE
+libpulsecore_@PA_MAJORMINOR@_la_SOURCES += pulsecore/resampler/libsamplerate.c
+libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += $(LIBSAMPLERATE_CFLAGS)
+libpulsecore_@PA_MAJORMINOR@_la_LIBADD += $(LIBSAMPLERATE_LIBS)
+endif
+
+# We split the foreign code off to not be annoyed by warnings we don't care about
+noinst_LTLIBRARIES += libpulsecore-foreign.la
+
+libpulsecore_foreign_la_SOURCES = \
+               pulsecore/ffmpeg/resample2.c pulsecore/ffmpeg/avcodec.h pulsecore/ffmpeg/dsputil.h
+
+libpulsecore_foreign_la_CFLAGS = $(AM_CFLAGS) $(FOREIGN_CFLAGS)
+
+###################################
+#   Plug-in support libraries     #
+###################################
+
+### Warning! Due to an obscure bug in libtool/automake it is required
+### that the libraries in modlibexec_LTLIBRARIES are specified in-order,
+### i.e. libraries near the end of the list depend on libraries near
+### the head, and not the other way!
+
+modlibexec_LTLIBRARIES = \
+               libcli.la \
+               libprotocol-cli.la \
+               libprotocol-simple.la \
+               libprotocol-http.la \
+               libprotocol-native.la
+
+if HAVE_WEBRTC
+modlibexec_LTLIBRARIES += libwebrtc-util.la
+endif
+
+if HAVE_ESOUND
+modlibexec_LTLIBRARIES += \
+               libprotocol-esound.la
+endif
+
+# We need to emulate sendmsg/recvmsg to support this on Win32
+if !OS_IS_WIN32
+modlibexec_LTLIBRARIES += \
+               librtp.la
+endif
+
+if HAVE_AVAHI
+modlibexec_LTLIBRARIES += \
+               libavahi-wrap.la
+endif
+
+libprotocol_simple_la_SOURCES = pulsecore/protocol-simple.c pulsecore/protocol-simple.h
+libprotocol_simple_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
+libprotocol_simple_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
+
+libcli_la_SOURCES = pulsecore/cli.c pulsecore/cli.h
+libcli_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
+libcli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
+
+libprotocol_cli_la_SOURCES = pulsecore/protocol-cli.c pulsecore/protocol-cli.h
+libprotocol_cli_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
+libprotocol_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la libcli.la
+
+libprotocol_http_la_SOURCES = pulsecore/protocol-http.c pulsecore/protocol-http.h pulsecore/mime-type.c pulsecore/mime-type.h
+libprotocol_http_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
+libprotocol_http_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
+
+libprotocol_native_la_SOURCES = pulsecore/protocol-native.c pulsecore/protocol-native.h pulsecore/native-common.h
+libprotocol_native_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS)
+libprotocol_native_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
+libprotocol_native_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
+if HAVE_DBUS
+libprotocol_native_la_CFLAGS += $(DBUS_CFLAGS)
+libprotocol_native_la_LIBADD += $(DBUS_LIBS)
+endif
+
+if HAVE_ESOUND
+libprotocol_esound_la_SOURCES = pulsecore/protocol-esound.c pulsecore/protocol-esound.h pulsecore/esound.h
+libprotocol_esound_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
+libprotocol_esound_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
+endif
+
+librtp_la_SOURCES = \
+               modules/rtp/rtp.c modules/rtp/rtp.h \
+               modules/rtp/sdp.c modules/rtp/sdp.h \
+               modules/rtp/sap.c modules/rtp/sap.h \
+               modules/rtp/rtsp_client.c modules/rtp/rtsp_client.h \
+               modules/rtp/headerlist.c modules/rtp/headerlist.h
+librtp_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
+librtp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
+
+libraop_la_SOURCES = \
+        modules/raop/raop-util.c modules/raop/raop-util.h \
+        modules/raop/raop-crypto.c modules/raop/raop-crypto.h \
+        modules/raop/raop-packet-buffer.h modules/raop/raop-packet-buffer.c \
+        modules/raop/raop-client.c modules/raop/raop-client.h \
+        modules/raop/raop-sink.c modules/raop/raop-sink.h
+
+libraop_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS) -I$(top_srcdir)/src/modules/rtp
+libraop_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
+libraop_la_LIBADD = $(AM_LIBADD) $(OPENSSL_LIBS) libpulsecore-@PA_MAJORMINOR@.la librtp.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
+
+# Avahi
+libavahi_wrap_la_SOURCES = pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h
+libavahi_wrap_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
+libavahi_wrap_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
+
+###################################
+#        Plug-in libraries        #
+###################################
+
+if HAVE_DBUS
+# Serveral module (e.g. libalsa-util.la)
+modlibexec_LTLIBRARIES += \
+               module-console-kit.la
+endif
+
+modlibexec_LTLIBRARIES += \
+               module-cli.la \
+               module-cli-protocol-tcp.la \
+               module-simple-protocol-tcp.la \
+               module-null-sink.la \
+               module-null-source.la \
+               module-sine-source.la \
+               module-detect.la \
+               module-volume-restore.la \
+               module-device-manager.la \
+               module-device-restore.la \
+               module-stream-restore.la \
+               module-card-restore.la \
+               module-default-device-restore.la \
+               module-always-sink.la \
+               module-rescue-streams.la \
+               module-intended-roles.la \
+               module-suspend-on-idle.la \
+               module-echo-cancel.la \
+               module-http-protocol-tcp.la \
+               module-sine.la \
+               module-native-protocol-tcp.la \
+               module-native-protocol-fd.la \
+               module-combine.la \
+               module-combine-sink.la \
+               module-remap-sink.la \
+               module-remap-source.la \
+               module-ladspa-sink.la \
+               module-tunnel-sink-new.la \
+               module-tunnel-source-new.la \
+               module-tunnel-sink.la \
+               module-tunnel-source.la \
+               module-position-event-sounds.la \
+               module-augment-properties.la \
+               module-role-cork.la \
+               module-loopback.la \
+               module-virtual-sink.la \
+               module-virtual-source.la \
+               module-virtual-surround-sink.la \
+               module-switch-on-connect.la \
+               module-switch-on-port-available.la \
+               module-filter-apply.la \
+               module-filter-heuristics.la \
+               module-role-ducking.la \
+               module-allow-passthrough.la
+
+if HAVE_ESOUND
+modlibexec_LTLIBRARIES += \
+               module-esound-protocol-tcp.la \
+               module-esound-sink.la
+endif
+
+# See comment at librtp.la above
+if !OS_IS_WIN32
+modlibexec_LTLIBRARIES += \
+               module-rtp-send.la \
+               module-rtp-recv.la
+endif
+
+if HAVE_AF_UNIX
+modlibexec_LTLIBRARIES += \
+               module-cli-protocol-unix.la \
+               module-simple-protocol-unix.la \
+               module-http-protocol-unix.la \
+               module-native-protocol-unix.la
+if HAVE_ESOUND
+modlibexec_LTLIBRARIES += \
+               module-esound-protocol-unix.la
+endif
+endif
+
+if HAVE_MKFIFO
+modlibexec_LTLIBRARIES += \
+               module-pipe-sink.la \
+               module-pipe-source.la
+endif
+
+if !OS_IS_WIN32
+if HAVE_ESOUND
+modlibexec_LTLIBRARIES += \
+               module-esound-compat-spawnfd.la \
+               module-esound-compat-spawnpid.la
+endif
+endif
+
+if HAVE_REGEX
+modlibexec_LTLIBRARIES += \
+               module-match.la
+endif
+
+if HAVE_X11
+modlibexec_LTLIBRARIES += \
+               module-x11-bell.la \
+               module-x11-publish.la \
+               module-x11-xsmp.la \
+               module-x11-cork-request.la
+endif
+
+if HAVE_OSS_OUTPUT
+modlibexec_LTLIBRARIES += \
+               liboss-util.la \
+               module-oss.la
+endif
+
+if HAVE_COREAUDIO
+modlibexec_LTLIBRARIES += \
+               module-coreaudio-detect.la \
+               module-coreaudio-device.la
+endif
+
+pulselibexec_PROGRAMS =
+
+if HAVE_ALSA
+modlibexec_LTLIBRARIES += \
+               libalsa-util.la \
+               module-alsa-sink.la \
+               module-alsa-source.la \
+               module-alsa-card.la
+
+dist_alsaprofilesets_DATA = \
+               modules/alsa/mixer/profile-sets/default.conf \
+               modules/alsa/mixer/profile-sets/force-speaker.conf \
+               modules/alsa/mixer/profile-sets/force-speaker-and-int-mic.conf \
+               modules/alsa/mixer/profile-sets/maudio-fasttrack-pro.conf \
+               modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf \
+               modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf \
+               modules/alsa/mixer/profile-sets/native-instruments-traktor-audio2.conf \
+               modules/alsa/mixer/profile-sets/native-instruments-traktor-audio6.conf \
+               modules/alsa/mixer/profile-sets/native-instruments-traktor-audio10.conf \
+               modules/alsa/mixer/profile-sets/native-instruments-traktorkontrol-s4.conf \
+               modules/alsa/mixer/profile-sets/native-instruments-korecontroller.conf \
+               modules/alsa/mixer/profile-sets/kinect-audio.conf \
+               modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf
+
+if HAVE_UDEV
+dist_udevrules_DATA = \
+               modules/alsa/mixer/profile-sets/90-pulseaudio.rules
+endif
+
+dist_alsapaths_DATA = \
+               modules/alsa/mixer/paths/analog-input-aux.conf \
+               modules/alsa/mixer/paths/analog-input.conf \
+               modules/alsa/mixer/paths/analog-input.conf.common \
+               modules/alsa/mixer/paths/analog-input-fm.conf \
+               modules/alsa/mixer/paths/analog-input-linein.conf \
+               modules/alsa/mixer/paths/analog-input-mic.conf \
+               modules/alsa/mixer/paths/analog-input-dock-mic.conf \
+               modules/alsa/mixer/paths/analog-input-front-mic.conf \
+               modules/alsa/mixer/paths/analog-input-headphone-mic.conf \
+               modules/alsa/mixer/paths/analog-input-headset-mic.conf \
+               modules/alsa/mixer/paths/analog-input-internal-mic.conf \
+               modules/alsa/mixer/paths/analog-input-internal-mic-always.conf \
+               modules/alsa/mixer/paths/analog-input-rear-mic.conf \
+               modules/alsa/mixer/paths/analog-input-mic.conf.common \
+               modules/alsa/mixer/paths/analog-input-mic-line.conf \
+               modules/alsa/mixer/paths/analog-input-tvtuner.conf \
+               modules/alsa/mixer/paths/analog-input-video.conf \
+               modules/alsa/mixer/paths/analog-output.conf \
+               modules/alsa/mixer/paths/analog-output-speaker.conf \
+               modules/alsa/mixer/paths/analog-output-speaker-always.conf \
+               modules/alsa/mixer/paths/analog-output.conf.common \
+               modules/alsa/mixer/paths/analog-output-headphones.conf \
+               modules/alsa/mixer/paths/analog-output-headphones-2.conf \
+               modules/alsa/mixer/paths/analog-output-lineout.conf \
+               modules/alsa/mixer/paths/analog-output-mono.conf \
+               modules/alsa/mixer/paths/iec958-stereo-output.conf \
+               modules/alsa/mixer/paths/hdmi-output-0.conf \
+               modules/alsa/mixer/paths/hdmi-output-1.conf \
+               modules/alsa/mixer/paths/hdmi-output-2.conf \
+               modules/alsa/mixer/paths/hdmi-output-3.conf \
+               modules/alsa/mixer/paths/hdmi-output-4.conf \
+               modules/alsa/mixer/paths/hdmi-output-5.conf \
+               modules/alsa/mixer/paths/hdmi-output-6.conf \
+               modules/alsa/mixer/paths/hdmi-output-7.conf
+
+endif
+
+if HAVE_SOLARIS
+modlibexec_LTLIBRARIES += \
+               module-solaris.la
+endif
+
+if HAVE_AVAHI
+modlibexec_LTLIBRARIES += \
+               module-zeroconf-publish.la \
+               module-zeroconf-discover.la
+endif
+
+if HAVE_BONJOUR
+modlibexec_LTLIBRARIES += \
+               module-bonjour-publish.la
+endif
+
+if HAVE_LIRC
+modlibexec_LTLIBRARIES += \
+               module-lirc.la
+endif
+
+if HAVE_EVDEV
+modlibexec_LTLIBRARIES += \
+               module-mmkbd-evdev.la
+endif
+
+if HAVE_JACK
+modlibexec_LTLIBRARIES += \
+               module-jack-sink.la \
+               module-jack-source.la
+
+if HAVE_DBUS
+modlibexec_LTLIBRARIES += \
+               module-jackdbus-detect.la
+endif
+
+endif
+
+if HAVE_GCONF
+modlibexec_LTLIBRARIES += \
+               module-gconf.la
+
+pulselibexec_PROGRAMS += \
+               gconf-helper
+endif
+
+if HAVE_WAVEOUT
+modlibexec_LTLIBRARIES += \
+               module-waveout.la
+endif
+
+if HAVE_HAL_COMPAT
+modlibexec_LTLIBRARIES += \
+               module-hal-detect.la
+endif
+
+if HAVE_UDEV
+modlibexec_LTLIBRARIES += \
+               module-udev-detect.la
+endif
+
+if HAVE_SYSTEMD_LOGIN
+modlibexec_LTLIBRARIES += \
+               module-systemd-login.la
+endif
+
+if HAVE_DBUS
+modlibexec_LTLIBRARIES += \
+               module-rygel-media-server.la \
+               module-dbus-protocol.la
+endif
+
+if HAVE_BLUEZ
+modlibexec_LTLIBRARIES += \
+               module-bluetooth-discover.la \
+               module-bluetooth-policy.la
+endif
+
+if HAVE_BLUEZ_4
+modlibexec_LTLIBRARIES += \
+               libbluez4-util.la \
+               module-bluez4-discover.la \
+               module-bluez4-device.la
+endif
+
+if HAVE_BLUEZ_5
+modlibexec_LTLIBRARIES += \
+               libbluez5-util.la \
+               module-bluez5-discover.la \
+               module-bluez5-device.la
+endif
+
+# RAOP depends on RTP, and we don't support RTP on Windows, see comment at
+# librtp.la above.
+if !OS_IS_WIN32
+if HAVE_OPENSSL
+modlibexec_LTLIBRARIES += \
+               libraop.la \
+               module-raop-sink.la
+if HAVE_AVAHI
+modlibexec_LTLIBRARIES += \
+               module-raop-discover.la
+endif
+endif
+endif
+
+if HAVE_DBUS
+if HAVE_FFTW
+modlibexec_LTLIBRARIES += \
+               module-equalizer-sink.la
+bin_SCRIPTS += utils/qpaeq
+endif
+endif
+
+# These are generated by an M4 script
+SYMDEF_FILES = \
+               module-cli-symdef.h \
+               module-cli-protocol-tcp-symdef.h \
+               module-cli-protocol-unix-symdef.h \
+               module-pipe-sink-symdef.h \
+               module-pipe-source-symdef.h \
+               module-simple-protocol-tcp-symdef.h \
+               module-simple-protocol-unix-symdef.h \
+               module-native-protocol-tcp-symdef.h \
+               module-native-protocol-unix-symdef.h \
+               module-native-protocol-fd-symdef.h \
+               module-sine-symdef.h \
+               module-combine-symdef.h \
+               module-combine-sink-symdef.h \
+               module-remap-sink-symdef.h \
+               module-remap-source-symdef.h \
+               module-ladspa-sink-symdef.h \
+               module-equalizer-sink-symdef.h \
+               module-match-symdef.h \
+               module-tunnel-sink-new-symdef.h \
+               module-tunnel-source-new-symdef.h \
+               module-tunnel-sink-symdef.h \
+               module-tunnel-source-symdef.h \
+               module-null-sink-symdef.h \
+               module-null-source-symdef.h \
+               module-sine-source-symdef.h \
+               module-zeroconf-publish-symdef.h \
+               module-zeroconf-discover-symdef.h \
+               module-bonjour-publish-symdef.h \
+               module-lirc-symdef.h \
+               module-mmkbd-evdev-symdef.h \
+               module-http-protocol-tcp-symdef.h \
+               module-http-protocol-unix-symdef.h \
+               module-rygel-media-server-symdef.h \
+               module-x11-bell-symdef.h \
+               module-x11-publish-symdef.h \
+               module-x11-xsmp-symdef.h \
+               module-x11-cork-request-symdef.h \
+               module-oss-symdef.h \
+               module-alsa-sink-symdef.h \
+               module-alsa-source-symdef.h \
+               module-alsa-card-symdef.h \
+               module-coreaudio-detect-symdef.h \
+               module-coreaudio-device-symdef.h \
+               module-solaris-symdef.h \
+               module-waveout-symdef.h \
+               module-detect-symdef.h \
+               module-rtp-send-symdef.h \
+               module-rtp-recv-symdef.h \
+               module-jackdbus-detect-symdef.h \
+               module-jack-sink-symdef.h \
+               module-jack-source-symdef.h \
+               module-volume-restore-symdef.h \
+               module-device-manager-symdef.h \
+               module-device-restore-symdef.h \
+               module-stream-restore-symdef.h \
+               module-card-restore-symdef.h \
+               module-default-device-restore-symdef.h \
+               module-always-sink-symdef.h \
+               module-rescue-streams-symdef.h \
+               module-intended-roles-symdef.h \
+               module-suspend-on-idle-symdef.h \
+               module-echo-cancel-symdef.h \
+               module-hal-detect-symdef.h \
+               module-udev-detect-symdef.h \
+               module-systemd-login-symdef.h \
+               module-bluetooth-policy-symdef.h \
+               module-bluetooth-discover-symdef.h \
+               module-bluez4-discover-symdef.h \
+               module-bluez4-device-symdef.h \
+               module-bluez5-discover-symdef.h \
+               module-bluez5-device-symdef.h \
+               module-raop-sink-symdef.h \
+               module-raop-discover-symdef.h \
+               module-gconf-symdef.h \
+               module-position-event-sounds-symdef.h \
+               module-role-ducking-symdef.h \
+               module-augment-properties-symdef.h \
+               module-role-cork-symdef.h \
+               module-console-kit-symdef.h \
+               module-dbus-protocol-symdef.h \
+               module-loopback-symdef.h \
+               module-virtual-sink-symdef.h \
+               module-virtual-source-symdef.h \
+               module-virtual-surround-sink-symdef.h \
+               module-switch-on-connect-symdef.h \
+               module-switch-on-port-available-symdef.h \
+               module-filter-apply-symdef.h \
+               module-filter-heuristics-symdef.h \
+               module-allow-passthrough-symdef.h
+
+if HAVE_ESOUND
+SYMDEF_FILES += \
+               module-esound-protocol-tcp-symdef.h \
+               module-esound-protocol-unix-symdef.h \
+               module-esound-compat-spawnfd-symdef.h \
+               module-esound-compat-spawnpid-symdef.h \
+               module-esound-sink-symdef.h
+endif
+
+EXTRA_DIST += $(SYMDEF_FILES)
+BUILT_SOURCES += $(SYMDEF_FILES) builddirs
+
+$(SYMDEF_FILES): modules/module-defs.h.m4
+       $(AM_V_at)$(MKDIR_P) modules
+       $(AM_V_GEN)$(M4) -Dfname="$@" $< > $@
+
+.PHONY: builddirs
+builddirs:
+       $(AM_V_at)$(MKDIR_P) daemon modules
+
+# Simple protocol
+
+module_simple_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
+module_simple_protocol_tcp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_simple_protocol_tcp_la_LIBADD = $(MODULE_LIBADD) libprotocol-simple.la
+
+module_simple_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
+module_simple_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_simple_protocol_unix_la_LIBADD = $(MODULE_LIBADD) libprotocol-simple.la
+
+# CLI protocol
+
+module_cli_la_SOURCES = modules/module-cli.c
+module_cli_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_cli_la_LIBADD = $(MODULE_LIBADD) libcli.la
+
+module_cli_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
+module_cli_protocol_tcp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_cli_protocol_tcp_la_LIBADD = $(MODULE_LIBADD) libprotocol-cli.la
+
+module_cli_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
+module_cli_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_cli_protocol_unix_la_LIBADD = $(MODULE_LIBADD) libprotocol-cli.la
+
+# HTTP protocol
+
+module_http_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_http_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
+module_http_protocol_tcp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_http_protocol_tcp_la_LIBADD = $(MODULE_LIBADD) libprotocol-http.la
+
+module_http_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
+module_http_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_http_protocol_unix_la_LIBADD = $(MODULE_LIBADD) libprotocol-http.la
+
+# D-Bus protocol
+
+module_dbus_protocol_la_SOURCES = \
+               modules/dbus/iface-card.c modules/dbus/iface-card.h \
+               modules/dbus/iface-card-profile.c modules/dbus/iface-card-profile.h \
+               modules/dbus/iface-client.c modules/dbus/iface-client.h \
+               modules/dbus/iface-core.c modules/dbus/iface-core.h \
+               modules/dbus/iface-device.c modules/dbus/iface-device.h \
+               modules/dbus/iface-device-port.c modules/dbus/iface-device-port.h \
+               modules/dbus/iface-memstats.c modules/dbus/iface-memstats.h \
+               modules/dbus/iface-module.c modules/dbus/iface-module.h \
+               modules/dbus/iface-sample.c modules/dbus/iface-sample.h \
+               modules/dbus/iface-stream.c modules/dbus/iface-stream.h \
+               modules/dbus/module-dbus-protocol.c
+module_dbus_protocol_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+module_dbus_protocol_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_dbus_protocol_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
+
+# Native protocol
+
+module_native_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
+module_native_protocol_tcp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_native_protocol_tcp_la_LIBADD = $(MODULE_LIBADD) libprotocol-native.la
+
+module_native_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
+module_native_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_native_protocol_unix_la_LIBADD = $(MODULE_LIBADD) libprotocol-native.la
+
+module_native_protocol_fd_la_SOURCES = modules/module-native-protocol-fd.c
+module_native_protocol_fd_la_CFLAGS = $(AM_CFLAGS)
+module_native_protocol_fd_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_native_protocol_fd_la_LIBADD = $(MODULE_LIBADD) libprotocol-native.la
+
+# EsounD protocol
+
+if HAVE_ESOUND
+module_esound_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
+module_esound_protocol_tcp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_esound_protocol_tcp_la_LIBADD = $(MODULE_LIBADD) libprotocol-esound.la
+
+module_esound_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
+module_esound_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_esound_protocol_unix_la_LIBADD = $(MODULE_LIBADD) libprotocol-esound.la
+
+module_esound_compat_spawnfd_la_SOURCES = modules/module-esound-compat-spawnfd.c
+module_esound_compat_spawnfd_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_esound_compat_spawnfd_la_LIBADD = $(MODULE_LIBADD)
+
+module_esound_compat_spawnpid_la_SOURCES = modules/module-esound-compat-spawnpid.c
+module_esound_compat_spawnpid_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_esound_compat_spawnpid_la_LIBADD = $(MODULE_LIBADD)
+
+module_esound_sink_la_SOURCES = modules/module-esound-sink.c
+module_esound_sink_la_LDFLAGS = $(MODULE_LDFLAGS) $(WINSOCK_LIBS)
+module_esound_sink_la_LIBADD = $(MODULE_LIBADD)
+endif
+
+# Pipes
+
+module_pipe_sink_la_SOURCES = modules/module-pipe-sink.c
+module_pipe_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_pipe_sink_la_LIBADD = $(MODULE_LIBADD)
+
+module_pipe_source_la_SOURCES = modules/module-pipe-source.c
+module_pipe_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_pipe_source_la_LIBADD = $(MODULE_LIBADD)
+
+# Fake sources/sinks
+
+module_sine_la_SOURCES = modules/module-sine.c
+module_sine_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_sine_la_LIBADD = $(MODULE_LIBADD)
+
+module_null_sink_la_SOURCES = modules/module-null-sink.c
+module_null_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_null_sink_la_LIBADD = $(MODULE_LIBADD)
+
+module_null_source_la_SOURCES = modules/module-null-source.c
+module_null_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_null_source_la_LIBADD = $(MODULE_LIBADD)
+
+module_sine_source_la_SOURCES = modules/module-sine-source.c
+module_sine_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_sine_source_la_LIBADD = $(MODULE_LIBADD)
+
+# Couplings
+
+module_combine_la_SOURCES = modules/module-combine.c
+module_combine_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_combine_la_LIBADD = $(MODULE_LIBADD)
+
+module_combine_sink_la_SOURCES = modules/module-combine-sink.c
+module_combine_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_combine_sink_la_LIBADD = $(MODULE_LIBADD)
+
+module_switch_on_connect_la_SOURCES = modules/module-switch-on-connect.c
+module_switch_on_connect_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_switch_on_connect_la_LIBADD = $(MODULE_LIBADD)
+
+module_switch_on_port_available_la_SOURCES = modules/module-switch-on-port-available.c
+module_switch_on_port_available_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_switch_on_port_available_la_LIBADD = $(MODULE_LIBADD)
+
+module_filter_apply_la_SOURCES = modules/module-filter-apply.c
+module_filter_apply_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_filter_apply_la_LIBADD = $(MODULE_LIBADD)
+
+module_filter_heuristics_la_SOURCES = modules/module-filter-heuristics.c
+module_filter_heuristics_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_filter_heuristics_la_LIBADD = $(MODULE_LIBADD)
+
+module_remap_sink_la_SOURCES = modules/module-remap-sink.c
+module_remap_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_remap_sink_la_LIBADD = $(MODULE_LIBADD)
+
+module_remap_source_la_SOURCES = modules/module-remap-source.c
+module_remap_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_remap_source_la_LIBADD = $(MODULE_LIBADD)
+
+module_ladspa_sink_la_SOURCES = modules/module-ladspa-sink.c modules/ladspa.h
+module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa\" $(AM_CFLAGS) $(SERVER_CFLAGS)
+module_ladspa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_ladspa_sink_la_LIBADD = $(MODULE_LIBADD) $(LIBLTDL)
+
+if HAVE_DBUS
+module_ladspa_sink_la_CFLAGS += $(DBUS_CFLAGS)
+module_ladspa_sink_la_LIBADD += $(DBUS_LIBS)
+endif
+
+module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c
+module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) $(DBUS_CFLAGS) $(FFTW_CFLAGS)
+module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_equalizer_sink_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) $(FFTW_LIBS)
+
+module_match_la_SOURCES = modules/module-match.c
+module_match_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_match_la_LIBADD = $(MODULE_LIBADD)
+
+module_tunnel_sink_new_la_SOURCES = modules/module-tunnel-sink-new.c
+module_tunnel_sink_new_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_tunnel_sink_new_la_LIBADD = $(MODULE_LIBADD)
+
+module_tunnel_source_new_la_SOURCES = modules/module-tunnel-source-new.c
+module_tunnel_source_new_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_tunnel_source_new_la_LIBADD = $(MODULE_LIBADD)
+
+module_tunnel_sink_la_SOURCES = modules/module-tunnel.c
+module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS) $(X11_CFLAGS)
+module_tunnel_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_tunnel_sink_la_LIBADD = $(MODULE_LIBADD) $(X11_LIBS)
+
+module_tunnel_source_la_SOURCES = modules/module-tunnel.c
+module_tunnel_source_la_LDFLAGS = $(MODULE_LDFLAGS) $(X11_CFLAGS)
+module_tunnel_source_la_LIBADD = $(MODULE_LIBADD) $(X11_LIBS)
+
+module_loopback_la_SOURCES = modules/module-loopback.c
+module_loopback_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_loopback_la_LIBADD = $(MODULE_LIBADD)
+
+module_virtual_sink_la_SOURCES = modules/module-virtual-sink.c
+module_virtual_sink_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS)
+module_virtual_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_virtual_sink_la_LIBADD = $(MODULE_LIBADD)
+
+module_virtual_source_la_SOURCES = modules/module-virtual-source.c
+module_virtual_source_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS)
+module_virtual_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_virtual_source_la_LIBADD = $(MODULE_LIBADD)
+
+module_virtual_surround_sink_la_SOURCES = modules/module-virtual-surround-sink.c
+module_virtual_surround_sink_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS)
+module_virtual_surround_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_virtual_surround_sink_la_LIBADD = $(MODULE_LIBADD)
+
+# X11
+
+module_x11_bell_la_SOURCES = modules/x11/module-x11-bell.c
+module_x11_bell_la_CFLAGS = $(AM_CFLAGS) $(X11_CFLAGS)
+module_x11_bell_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_x11_bell_la_LIBADD = $(MODULE_LIBADD) $(X11_LIBS)
+
+module_x11_publish_la_SOURCES = modules/x11/module-x11-publish.c
+module_x11_publish_la_CFLAGS = $(AM_CFLAGS) $(X11_CFLAGS)
+module_x11_publish_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_x11_publish_la_LIBADD = $(MODULE_LIBADD) libprotocol-native.la $(X11_LIBS)
+
+module_x11_xsmp_la_SOURCES = modules/x11/module-x11-xsmp.c
+module_x11_xsmp_la_CFLAGS = $(AM_CFLAGS) $(X11_CFLAGS)
+module_x11_xsmp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_x11_xsmp_la_LIBADD = $(MODULE_LIBADD) $(X11_LIBS)
+
+module_x11_cork_request_la_SOURCES = modules/x11/module-x11-cork-request.c
+module_x11_cork_request_la_CFLAGS = $(AM_CFLAGS) $(X11_CFLAGS)
+module_x11_cork_request_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_x11_cork_request_la_LIBADD = $(MODULE_LIBADD) $(X11_LIBS)
+
+# OSS
+
+liboss_util_la_SOURCES = modules/oss/oss-util.c modules/oss/oss-util.h
+liboss_util_la_LDFLAGS = -avoid-version
+liboss_util_la_LIBADD = $(MODULE_LIBADD)
+
+module_oss_la_SOURCES = modules/oss/module-oss.c
+module_oss_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_oss_la_LIBADD = $(MODULE_LIBADD) liboss-util.la
+
+# COREAUDIO
+
+module_coreaudio_detect_la_SOURCES = modules/macosx/module-coreaudio-detect.c
+module_coreaudio_detect_la_LDFLAGS = $(MODULE_LDFLAGS) \
+               -Wl,-framework -Wl,Cocoa -framework CoreAudio \
+               -Wl,-framework -Wl,AudioUnit -framework AudioUnit
+module_coreaudio_detect_la_LIBADD = $(MODULE_LIBADD)
+
+module_coreaudio_device_la_SOURCES = modules/macosx/module-coreaudio-device.c
+module_coreaudio_device_la_LDFLAGS = $(MODULE_LDFLAGS) \
+               -Wl,-framework -Wl,Cocoa -framework CoreAudio \
+               -Wl,-framework -Wl,AudioUnit -framework AudioUnit
+module_coreaudio_device_la_LIBADD = $(MODULE_LIBADD)
+
+# ALSA
+
+libalsa_util_la_SOURCES = \
+               modules/alsa/alsa-util.c modules/alsa/alsa-util.h \
+               modules/alsa/alsa-ucm.c modules/alsa/alsa-ucm.h \
+               modules/alsa/alsa-mixer.c modules/alsa/alsa-mixer.h \
+               modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h \
+               modules/alsa/alsa-source.c modules/alsa/alsa-source.h \
+               modules/reserve-wrap.c modules/reserve-wrap.h
+libalsa_util_la_LDFLAGS = -avoid-version
+libalsa_util_la_LIBADD = $(MODULE_LIBADD) $(ASOUNDLIB_LIBS)
+libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+if HAVE_UDEV
+libalsa_util_la_SOURCES += modules/udev-util.h modules/udev-util.c
+libalsa_util_la_LIBADD += $(UDEV_LIBS)
+libalsa_util_la_CFLAGS += $(UDEV_CFLAGS)
+endif
+
+if HAVE_DBUS
+libalsa_util_la_SOURCES += modules/reserve.h modules/reserve.c modules/reserve-monitor.h modules/reserve-monitor.c
+libalsa_util_la_LIBADD += $(DBUS_LIBS)
+libalsa_util_la_CFLAGS += $(DBUS_CFLAGS)
+endif
+
+module_alsa_sink_la_SOURCES = modules/alsa/module-alsa-sink.c
+module_alsa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_alsa_sink_la_LIBADD = $(MODULE_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
+module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+module_alsa_source_la_SOURCES = modules/alsa/module-alsa-source.c
+module_alsa_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_alsa_source_la_LIBADD = $(MODULE_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
+module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+module_alsa_card_la_SOURCES = modules/alsa/module-alsa-card.c
+module_alsa_card_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_alsa_card_la_LIBADD = $(MODULE_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
+module_alsa_card_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+# Solaris
+
+module_solaris_la_SOURCES = modules/module-solaris.c
+module_solaris_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_solaris_la_LIBADD = $(MODULE_LIBADD)
+
+# Avahi
+
+module_zeroconf_publish_la_SOURCES = modules/module-zeroconf-publish.c
+module_zeroconf_publish_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_zeroconf_publish_la_LIBADD = $(MODULE_LIBADD) $(AVAHI_LIBS) $(DBUS_LIBS) libavahi-wrap.la libprotocol-native.la
+module_zeroconf_publish_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS) $(DBUS_CFLAGS)
+
+module_zeroconf_discover_la_SOURCES = modules/module-zeroconf-discover.c
+module_zeroconf_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_zeroconf_discover_la_LIBADD = $(MODULE_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la
+module_zeroconf_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+
+# Bonjour
+
+module_bonjour_publish_la_SOURCES = modules/macosx/module-bonjour-publish.c
+module_bonjour_publish_la_LDFLAGS = $(MODULE_LDFLAGS) \
+                       -Wl,-framework -Wl,CoreFoundation -framework CoreFoundation
+module_bonjour_publish_la_LIBADD = $(MODULE_LIBADD) libprotocol-native.la
+
+# LIRC
+
+module_lirc_la_SOURCES = modules/module-lirc.c
+module_lirc_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_lirc_la_LIBADD = $(MODULE_LIBADD) $(LIRC_LIBS)
+module_lirc_la_CFLAGS = $(AM_CFLAGS) $(LIRC_CFLAGS)
+
+
+# Linux evdev
+
+module_mmkbd_evdev_la_SOURCES = modules/module-mmkbd-evdev.c
+module_mmkbd_evdev_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_mmkbd_evdev_la_LIBADD = $(MODULE_LIBADD)
+module_mmkbd_evdev_la_CFLAGS = $(AM_CFLAGS)
+
+# Windows waveout
+module_waveout_la_SOURCES = modules/module-waveout.c
+module_waveout_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_waveout_la_LIBADD = $(MODULE_LIBADD) -lwinmm
+module_waveout_la_CFLAGS = $(AM_CFLAGS)
+
+# Hardware autodetection module
+module_detect_la_SOURCES = modules/module-detect.c
+module_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_detect_la_LIBADD = $(MODULE_LIBADD)
+module_detect_la_CFLAGS = $(AM_CFLAGS)
+
+# Volume restore module
+module_volume_restore_la_SOURCES = modules/module-volume-restore.c
+module_volume_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_volume_restore_la_LIBADD = $(MODULE_LIBADD)
+module_volume_restore_la_CFLAGS = $(AM_CFLAGS)
+
+# Position event sounds in space
+module_position_event_sounds_la_SOURCES = modules/module-position-event-sounds.c
+module_position_event_sounds_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_position_event_sounds_la_LIBADD = $(MODULE_LIBADD)
+module_position_event_sounds_la_CFLAGS = $(AM_CFLAGS)
+
+# Ducking effect based on stream roles
+module_role_ducking_la_SOURCES = modules/module-role-ducking.c \
+                                 modules/stream-interaction.c modules/stream-interaction.h
+module_role_ducking_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_role_ducking_la_LIBADD = $(MODULE_LIBADD)
+module_role_ducking_la_CFLAGS = $(AM_CFLAGS)
+
+# Augment properties from XDG .desktop files
+module_augment_properties_la_SOURCES = modules/module-augment-properties.c
+module_augment_properties_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_augment_properties_la_LIBADD = $(MODULE_LIBADD)
+#module_augment_properties_la_CFLAGS = $(AM_CFLAGS) -DDESKTOPFILEDIR=\"$(datadir)/applications\"
+module_augment_properties_la_CFLAGS = $(AM_CFLAGS) -DDESKTOPFILEDIR=\"/usr/share/applications\"
+
+# Cork certain streams while others are active (e.g. cork music when phone streams appear)
+module_role_cork_la_SOURCES = modules/module-role-cork.c \
+                              modules/stream-interaction.c modules/stream-interaction.h
+module_role_cork_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_role_cork_la_LIBADD = $(MODULE_LIBADD)
+module_role_cork_la_CFLAGS = $(AM_CFLAGS)
+
+# Device description restore module
+module_device_manager_la_SOURCES = modules/module-device-manager.c
+module_device_manager_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_device_manager_la_LIBADD = $(MODULE_LIBADD) libprotocol-native.la
+module_device_manager_la_CFLAGS = $(AM_CFLAGS)
+
+# Device volume/muted restore module
+module_device_restore_la_SOURCES = modules/module-device-restore.c
+module_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_device_restore_la_LIBADD = $(MODULE_LIBADD) libprotocol-native.la
+module_device_restore_la_CFLAGS = $(AM_CFLAGS)
+
+if HAVE_DBUS
+module_device_restore_la_LIBADD += $(DBUS_LIBS)
+module_device_restore_la_CFLAGS += $(DBUS_CFLAGS)
+endif
+
+# Stream volume/muted/device restore module
+module_stream_restore_la_SOURCES = modules/module-stream-restore.c
+module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_stream_restore_la_LIBADD = $(MODULE_LIBADD) libprotocol-native.la
+module_stream_restore_la_CFLAGS = $(AM_CFLAGS)
+
+if HAVE_DBUS
+module_stream_restore_la_LIBADD += $(DBUS_LIBS)
+module_stream_restore_la_CFLAGS += $(DBUS_CFLAGS)
+endif
+
+# Card profile restore module
+module_card_restore_la_SOURCES = modules/module-card-restore.c
+module_card_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_card_restore_la_LIBADD = $(MODULE_LIBADD)
+module_card_restore_la_CFLAGS = $(AM_CFLAGS)
+
+# Default sink/source restore module
+module_default_device_restore_la_SOURCES = modules/module-default-device-restore.c
+module_default_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_default_device_restore_la_LIBADD = $(MODULE_LIBADD)
+module_default_device_restore_la_CFLAGS = $(AM_CFLAGS)
+
+# Always Sink module
+module_always_sink_la_SOURCES = modules/module-always-sink.c
+module_always_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_always_sink_la_LIBADD = $(MODULE_LIBADD)
+module_always_sink_la_CFLAGS = $(AM_CFLAGS)
+
+# Rescue streams module
+module_rescue_streams_la_SOURCES = modules/module-rescue-streams.c
+module_rescue_streams_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_rescue_streams_la_LIBADD = $(MODULE_LIBADD)
+module_rescue_streams_la_CFLAGS = $(AM_CFLAGS)
+
+# Automatically move streams to devices that are intended for their roles
+module_intended_roles_la_SOURCES = modules/module-intended-roles.c
+module_intended_roles_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_intended_roles_la_LIBADD = $(MODULE_LIBADD)
+module_intended_roles_la_CFLAGS = $(AM_CFLAGS)
+
+# Suspend-on-idle module
+module_suspend_on_idle_la_SOURCES = modules/module-suspend-on-idle.c
+module_suspend_on_idle_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_suspend_on_idle_la_LIBADD = $(MODULE_LIBADD)
+module_suspend_on_idle_la_CFLAGS = $(AM_CFLAGS)
+
+# echo-cancel module
+module_echo_cancel_la_SOURCES = \
+               modules/echo-cancel/module-echo-cancel.c \
+               modules/echo-cancel/null.c \
+               modules/echo-cancel/echo-cancel.h
+module_echo_cancel_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_echo_cancel_la_LIBADD = $(MODULE_LIBADD)
+module_echo_cancel_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS)
+if HAVE_ADRIAN_EC
+module_echo_cancel_la_SOURCES += \
+               modules/echo-cancel/adrian-aec.c modules/echo-cancel/adrian-aec.h \
+               modules/echo-cancel/adrian.c modules/echo-cancel/adrian.h
+module_echo_cancel_la_CFLAGS += -DHAVE_ADRIAN_EC=1
+ORC_SOURCE += modules/echo-cancel/adrian-aec
+if HAVE_ORC
+nodist_module_echo_cancel_la_SOURCES = \
+               modules/echo-cancel/adrian-aec-orc-gen.c \
+               modules/echo-cancel/adrian-aec-orc-gen.h
+module_echo_cancel_la_LIBADD += $(ORC_LIBS)
+module_echo_cancel_la_CFLAGS += $(ORC_CFLAGS) -I$(top_builddir)/src/modules/echo-cancel
+endif
+endif
+if HAVE_SPEEX
+module_echo_cancel_la_SOURCES += modules/echo-cancel/speex.c
+module_echo_cancel_la_CFLAGS += $(LIBSPEEX_CFLAGS)
+module_echo_cancel_la_LIBADD += $(LIBSPEEX_LIBS)
+endif
+if HAVE_WEBRTC
+# The webrtc code is split off into a helper library to avoid having automake
+# link module-echo-cancel with C++ (which it does if there are any C++ deps,
+# even conditional ones).
+
+libwebrtc_util_la_SOURCES = modules/echo-cancel/webrtc.cc
+libwebrtc_util_la_CXXFLAGS = $(AM_CXXFLAGS) $(SERVER_CFLAGS) $(WEBRTC_CFLAGS) -DHAVE_WEBRTC=1
+libwebrtc_util_la_LIBADD = libpulsecore-@PA_MAJORMINOR@.la $(WEBRTC_LIBS)
+libwebrtc_util_la_LDFLAGS = -avoid-version
+
+module_echo_cancel_la_CFLAGS += -DHAVE_WEBRTC=1
+module_echo_cancel_la_LIBADD += libwebrtc-util.la
+endif
+
+# RTP modules
+module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c
+module_rtp_send_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_rtp_send_la_LIBADD = $(MODULE_LIBADD) librtp.la
+module_rtp_send_la_CFLAGS = $(AM_CFLAGS)
+
+module_rtp_recv_la_SOURCES = modules/rtp/module-rtp-recv.c
+module_rtp_recv_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_rtp_recv_la_LIBADD = $(MODULE_LIBADD) librtp.la
+module_rtp_recv_la_CFLAGS = $(AM_CFLAGS)
+
+# JACK
+
+module_jackdbus_detect_la_SOURCES = modules/jack/module-jackdbus-detect.c
+module_jackdbus_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_jackdbus_detect_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) $(JACK_LIBS)
+module_jackdbus_detect_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(JACK_CFLAGS)
+
+module_jack_sink_la_SOURCES = modules/jack/module-jack-sink.c
+module_jack_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_jack_sink_la_LIBADD = $(MODULE_LIBADD) $(JACK_LIBS)
+module_jack_sink_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
+
+module_jack_source_la_SOURCES = modules/jack/module-jack-source.c
+module_jack_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_jack_source_la_LIBADD = $(MODULE_LIBADD) $(JACK_LIBS)
+module_jack_source_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
+
+module_hal_detect_la_SOURCES = modules/module-hal-detect-compat.c
+module_hal_detect_la_LIBADD = $(MODULE_LIBADD)
+module_hal_detect_la_CFLAGS = $(AM_CFLAGS)
+module_hal_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
+
+module_udev_detect_la_SOURCES = modules/module-udev-detect.c
+module_udev_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_udev_detect_la_LIBADD = $(MODULE_LIBADD) $(UDEV_LIBS)
+module_udev_detect_la_CFLAGS = $(AM_CFLAGS) $(UDEV_CFLAGS)
+
+module_console_kit_la_SOURCES = modules/module-console-kit.c
+module_console_kit_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_console_kit_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
+module_console_kit_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
+module_systemd_login_la_SOURCES = modules/module-systemd-login.c
+module_systemd_login_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_systemd_login_la_LIBADD = $(MODULE_LIBADD) $(SYSTEMD_LIBS) $(SYSTEMDLOGIN_LIBS)
+module_systemd_login_la_CFLAGS = $(AM_CFLAGS) $(SYSTEMD_CFLAGS) $(SYSTEMDLOGIN_CFLAGS)
+
+# GConf support
+module_gconf_la_SOURCES = modules/gconf/module-gconf.c
+module_gconf_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_gconf_la_LIBADD = $(MODULE_LIBADD)
+module_gconf_la_CFLAGS = $(AM_CFLAGS) -DPA_GCONF_HELPER=\"$(pulselibexecdir)/gconf-helper\"
+
+gconf_helper_SOURCES = modules/gconf/gconf-helper.c
+gconf_helper_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la $(GCONF_LIBS)
+gconf_helper_CFLAGS = $(AM_CFLAGS) $(GCONF_CFLAGS)
+gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+# Bluetooth policy
+module_bluetooth_policy_la_SOURCES = modules/bluetooth/module-bluetooth-policy.c
+module_bluetooth_policy_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_bluetooth_policy_la_LIBADD = $(MODULE_LIBADD)
+module_bluetooth_policy_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
+# Bluetooth discover
+module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c
+module_bluetooth_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_bluetooth_discover_la_LIBADD = $(MODULE_LIBADD)
+module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS)
+
+# Bluetooth BlueZ 4 sink / source
+module_bluez4_discover_la_SOURCES = modules/bluetooth/module-bluez4-discover.c
+module_bluez4_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_bluez4_discover_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libbluez4-util.la
+module_bluez4_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
+libbluez4_util_la_SOURCES = \
+               modules/bluetooth/a2dp-codecs.h \
+               modules/bluetooth/bluez4-util.c \
+               modules/bluetooth/bluez4-util.h
+libbluez4_util_la_LDFLAGS = -avoid-version
+libbluez4_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
+libbluez4_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
+module_bluez4_device_la_SOURCES = modules/bluetooth/module-bluez4-device.c modules/bluetooth/rtp.h
+module_bluez4_device_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_bluez4_device_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) $(SBC_LIBS) libbluez4-util.la
+module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS)
+
+# Bluetooth BlueZ 5 sink / source
+libbluez5_util_la_SOURCES = \
+               modules/bluetooth/bluez5-util.c \
+               modules/bluetooth/bluez5-util.h \
+               modules/bluetooth/a2dp-codecs.h
+if HAVE_BLUEZ_5_OFONO_HEADSET
+libbluez5_util_la_SOURCES += \
+               modules/bluetooth/backend-ofono.c
+endif
+if HAVE_BLUEZ_5_NATIVE_HEADSET
+libbluez5_util_la_SOURCES += \
+               modules/bluetooth/backend-native.c
+endif
+
+libbluez5_util_la_LDFLAGS = -avoid-version
+libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
+libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
+module_bluez5_discover_la_SOURCES = modules/bluetooth/module-bluez5-discover.c
+module_bluez5_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_bluez5_discover_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libbluez5-util.la
+module_bluez5_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
+module_bluez5_device_la_SOURCES = modules/bluetooth/module-bluez5-device.c
+module_bluez5_device_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_bluez5_device_la_LIBADD = $(MODULE_LIBADD) $(SBC_LIBS) libbluez5-util.la
+module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS)
+
+# Apple Airtunes/RAOP
+module_raop_sink_la_SOURCES = modules/raop/module-raop-sink.c
+module_raop_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_raop_sink_la_LIBADD = $(MODULE_LIBADD) librtp.la libraop.la
+module_raop_sink_la_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/src/modules/rtp
+
+module_raop_discover_la_SOURCES = modules/raop/module-raop-discover.c
+module_raop_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_raop_discover_la_LIBADD = $(MODULE_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la
+module_raop_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+
+# Rygel
+module_rygel_media_server_la_SOURCES = modules/module-rygel-media-server.c
+module_rygel_media_server_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_rygel_media_server_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libprotocol-http.la
+module_rygel_media_server_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
+# Allow passthrough module
+module_allow_passthrough_la_SOURCES = modules/module-allow-passthrough.c
+module_allow_passthrough_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_allow_passthrough_la_LIBADD = $(MODULE_LIBADD)
+module_allow_passthrough_la_CFLAGS = $(AM_CFLAGS)
+
+###################################
+#        Some minor stuff         #
+###################################
+
+CLEANFILES += daemon/pulseaudio.desktop
+DISTCLEANFILES = esdcompat client.conf default.pa system.pa daemon.conf start-pulseaudio-x11 pulseaudio.service
+
+if OS_IS_WIN32
+SYMLINK_PROGRAM=cd $(DESTDIR)$(bindir) && cp
+else
+SYMLINK_PROGRAM=ln -sf
+endif
+install-exec-hook:
+       $(SYMLINK_PROGRAM) pacat$(EXEEXT) $(DESTDIR)$(bindir)/parec$(EXEEXT)
+       $(SYMLINK_PROGRAM) pacat$(EXEEXT) $(DESTDIR)$(bindir)/pamon$(EXEEXT)
+       $(SYMLINK_PROGRAM) pacat$(EXEEXT) $(DESTDIR)$(bindir)/paplay$(EXEEXT)
+       $(SYMLINK_PROGRAM) pacat$(EXEEXT) $(DESTDIR)$(bindir)/parecord$(EXEEXT)
+       rm -f $(DESTDIR)$(libdir)/libpulsedsp.la
+       rm -f $(DESTDIR)$(modlibexecdir)/*.la
+
+uninstall-hook:
+       rm -f $(DESTDIR)$(bindir)/parec$(EXEEXT)
+       rm -f $(DESTDIR)$(bindir)/pamon$(EXEEXT)
+       rm -f $(DESTDIR)$(bindir)/paplay$(EXEEXT)
+       rm -f $(DESTDIR)$(bindir)/parecord$(EXEEXT)
+       rm -f $(DESTDIR)$(libdir)/libpulsedsp.*
+       rm -f $(DESTDIR)$(modlibexecdir)/*.so
+
+massif: pulseaudio
+       libtool --mode=execute valgrind --tool=massif --depth=6  --alloc-fn=pa_xmalloc --alloc-fn=pa_xmalloc0 --alloc-fn=pa_xrealloc --alloc-fn=dbus_realloc --alloc-fn=pa_xnew0_internal --alloc-fn=pa_xnew_internal ./pulseaudio
+
+update-ffmpeg:
+       wget -O pulsecore/ffmpeg/resample2.c http://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec/resample2.c?view=co
+
+update-reserve:
+       for i in reserve.c reserve.h reserve-monitor.c reserve-monitor.h ; do \
+               wget -O $(top_srcdir)/src/modules/$$i http://git.0pointer.de/\?p=reserve.git\;a=blob_plain\;f=$$i\;hb=master ; \
+       done
+
+update-rtkit:
+       for i in rtkit.c rtkit.h ; do \
+               wget -O $(top_srcdir)/src/pulsecore/$$i http://git.0pointer.de/\?p=rtkit.git\;a=blob_plain\;f=$$i\;hb=master ; \
+       done
+
+# Automatically generate linker version script. We use the same one for all public .sos
+update-map-file:
+       ( echo "PULSE_0 {" ; \
+         echo "global:" ; \
+         ctags -I PA_GCC_MALLOC,PA_GCC_ALLOC_SIZE2,PA_GCC_ALLOC_SIZE,PA_GCC_PURE,PA_GCC_CONST,PA_GCC_DEPRECATED,PA_GCC_PRINTF_ATTR -f - --c-kinds=p $(pulseinclude_HEADERS) | awk '/^pa_/ { print $$1 ";" }' | sort ; \
+         echo "local:" ;  \
+         echo "*;" ; \
+         echo "};" ) > $(srcdir)/map-file
+
+update-all: update-ffmpeg update-map-file
+
+# Force installation order of libraries. libtool relinks on install time, in
+# which case libpulsecommon has to be installed before others, but the padsp
+# preload library has to be done after the normal libraries (e.g. libpulse)
+# ...
+# Unfortunately automake behaviour means that rules without commands also
+# override build-in rules, so it's not trivial to add dependencies.
+# See http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7328 for the workaround
+# ...
+# Isn't libtool/autotools fun!
+
+installlibLTLIBRARIES = install-libLTLIBRARIES
+$(installlibLTLIBRARIES): install-commonlibLTLIBRARIES
+
+installpkglibLTLIBRARIES = install-pkglibLTLIBRARIES
+$(installpkglibLTLIBRARIES): install-libLTLIBRARIES
+
+installmodlibexecLTLIBRARIES = install-modlibexecLTLIBRARIES
+$(installmodlibexecLTLIBRARIES): install-pkglibLTLIBRARIES install-libLTLIBRARIES
+
+installpadsplibLTLIBRARIES = install-padsplibLTLIBRARIES
+$(installpadsplibLTLIBRARIES): install-libLTLIBRARIES
+
+if HAVE_GCOV
+coverage:
+       @echo ""
+       @echo "Don't forget to run 'make check' before generating coverage stats."
+       @echo ""
+       lcov --capture --directory . --output-file $(builddir)/gcov-all.info
+       -rm -r $(builddir)/coverage
+       genhtml --output-directory $(builddir)/coverage gcov-all.info
+       @echo ""
+       @echo "Coverage data now available at: $(abs_builddir)/coverage/index.html"
+else
+coverage:
+       @echo ""
+       @echo "To generate coverage stats, rerun configure with '--enable-gcov',"
+       @echo "and don't forget to disable it again for regular builds."
+       @echo ""
+endif
+
+.PHONY: massif update-all update-ffmpeg update-map-file coverage
diff --git a/src/daemon/.gitignore b/src/daemon/.gitignore
new file mode 100644 (file)
index 0000000..0efa55b
--- /dev/null
@@ -0,0 +1,2 @@
+org.pulseaudio.policy
+pulseaudio.desktop
diff --git a/src/daemon/Makefile b/src/daemon/Makefile
new file mode 120000 (symlink)
index 0000000..f5b1dba
--- /dev/null
@@ -0,0 +1 @@
+../modules/Makefile
\ No newline at end of file
diff --git a/src/daemon/caps.c b/src/daemon/caps.c
new file mode 100644 (file)
index 0000000..fd135c0
--- /dev/null
@@ -0,0 +1,99 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
+#include "caps.h"
+
+/* Glibc <= 2.2 has broken unistd.h */
+#if defined(__linux__) && (__GLIBC__ <= 2 && __GLIBC_MINOR__ <= 2)
+int setresgid(gid_t r, gid_t e, gid_t s);
+int setresuid(uid_t r, uid_t e, uid_t s);
+#endif
+
+/* Drop root rights when called SUID root */
+void pa_drop_root(void) {
+
+#ifdef HAVE_GETUID
+    uid_t uid;
+    gid_t gid;
+
+    pa_log_debug("Cleaning up privileges.");
+    uid = getuid();
+    gid = getgid();
+
+#if defined(HAVE_SETRESUID)
+    pa_assert_se(setresuid(uid, uid, uid) >= 0);
+    pa_assert_se(setresgid(gid, gid, gid) >= 0);
+#elif defined(HAVE_SETREUID)
+    pa_assert_se(setreuid(uid, uid) >= 0);
+    pa_assert_se(setregid(gid, gid) >= 0);
+#else
+    pa_assert_se(setuid(uid) >= 0);
+    pa_assert_se(seteuid(uid) >= 0);
+    pa_assert_se(setgid(gid) >= 0);
+    pa_assert_se(setegid(gid) >= 0);
+#endif
+
+    pa_assert_se(getuid() == uid);
+    pa_assert_se(geteuid() == uid);
+    pa_assert_se(getgid() == gid);
+    pa_assert_se(getegid() == gid);
+
+    if (uid != 0)
+        pa_drop_caps();
+#endif
+}
+
+void pa_drop_caps(void) {
+#ifdef HAVE_SYS_CAPABILITY_H
+#if defined(__linux__)
+    cap_t caps;
+    pa_assert_se(caps = cap_init());
+    pa_assert_se(cap_clear(caps) == 0);
+    pa_assert_se(cap_set_proc(caps) == 0);
+    pa_assert_se(cap_free(caps) == 0);
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+    /* FreeBSD doesn't have this functionality, even though sys/capability.h is
+     * available. See https://bugs.freedesktop.org/show_bug.cgi?id=72580 */
+    pa_log_warn("FreeBSD cannot drop extra capabilities, implementation needed.");
+#else
+#error "Don't know how to do capabilities on your system.  Please send a patch."
+#endif /* __linux__ */
+#else /* HAVE_SYS_CAPABILITY_H */
+    pa_log_warn("Normally all extra capabilities would be dropped now, but "
+                "that's impossible because PulseAudio was built without "
+                "capabilities support.");
+#endif
+}
diff --git a/src/daemon/caps.h b/src/daemon/caps.h
new file mode 100644 (file)
index 0000000..9b88241
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef foocapshfoo
+#define foocapshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/macro.h>
+
+void pa_drop_root(void);
+
+void pa_drop_caps(void);
+
+#endif
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
new file mode 100644 (file)
index 0000000..0454b6d
--- /dev/null
@@ -0,0 +1,420 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/macro.h>
+
+#include "cmdline.h"
+
+/* Argument codes for getopt_long() */
+enum {
+    ARG_HELP = 256,
+    ARG_VERSION,
+    ARG_DUMP_CONF,
+    ARG_DUMP_MODULES,
+    ARG_DAEMONIZE,
+    ARG_FAIL,
+    ARG_LOG_LEVEL,
+    ARG_HIGH_PRIORITY,
+    ARG_REALTIME,
+    ARG_DISALLOW_MODULE_LOADING,
+    ARG_DISALLOW_EXIT,
+    ARG_EXIT_IDLE_TIME,
+    ARG_SCACHE_IDLE_TIME,
+    ARG_LOG_TARGET,
+    ARG_LOG_META,
+    ARG_LOG_TIME,
+    ARG_LOG_BACKTRACE,
+    ARG_LOAD,
+    ARG_FILE,
+    ARG_DL_SEARCH_PATH,
+    ARG_RESAMPLE_METHOD,
+    ARG_KILL,
+    ARG_USE_PID_FILE,
+    ARG_CHECK,
+    ARG_NO_CPU_LIMIT,
+    ARG_DISABLE_SHM,
+    ARG_ENABLE_MEMFD,
+    ARG_DUMP_RESAMPLE_METHODS,
+    ARG_SYSTEM,
+    ARG_CLEANUP_SHM,
+    ARG_START
+};
+
+/* Table for getopt_long() */
+static const struct option long_options[] = {
+    {"help",                        0, 0, ARG_HELP},
+    {"version",                     0, 0, ARG_VERSION},
+    {"dump-conf",                   0, 0, ARG_DUMP_CONF},
+    {"dump-modules",                0, 0, ARG_DUMP_MODULES},
+    {"daemonize",                   2, 0, ARG_DAEMONIZE},
+    {"fail",                        2, 0, ARG_FAIL},
+    {"verbose",                     2, 0, ARG_LOG_LEVEL},
+    {"log-level",                   2, 0, ARG_LOG_LEVEL},
+    {"high-priority",               2, 0, ARG_HIGH_PRIORITY},
+    {"realtime",                    2, 0, ARG_REALTIME},
+    {"disallow-module-loading",     2, 0, ARG_DISALLOW_MODULE_LOADING},
+    {"disallow-exit",               2, 0, ARG_DISALLOW_EXIT},
+    {"exit-idle-time",              1, 0, ARG_EXIT_IDLE_TIME},
+    {"scache-idle-time",            1, 0, ARG_SCACHE_IDLE_TIME},
+    {"log-target",                  1, 0, ARG_LOG_TARGET},
+    {"log-meta",                    2, 0, ARG_LOG_META},
+    {"log-time",                    2, 0, ARG_LOG_TIME},
+    {"log-backtrace",               1, 0, ARG_LOG_BACKTRACE},
+    {"load",                        1, 0, ARG_LOAD},
+    {"file",                        1, 0, ARG_FILE},
+    {"dl-search-path",              1, 0, ARG_DL_SEARCH_PATH},
+    {"resample-method",             1, 0, ARG_RESAMPLE_METHOD},
+    {"kill",                        0, 0, ARG_KILL},
+    {"start",                       0, 0, ARG_START},
+    {"use-pid-file",                2, 0, ARG_USE_PID_FILE},
+    {"check",                       0, 0, ARG_CHECK},
+    {"system",                      2, 0, ARG_SYSTEM},
+    {"no-cpu-limit",                2, 0, ARG_NO_CPU_LIMIT},
+    {"disable-shm",                 2, 0, ARG_DISABLE_SHM},
+    {"enable-memfd",                2, 0, ARG_ENABLE_MEMFD},
+    {"dump-resample-methods",       2, 0, ARG_DUMP_RESAMPLE_METHODS},
+    {"cleanup-shm",                 2, 0, ARG_CLEANUP_SHM},
+    {NULL, 0, 0, 0}
+};
+
+void pa_cmdline_help(const char *argv0) {
+    pa_assert(argv0);
+
+    printf(_("%s [options]\n\n"
+           "COMMANDS:\n"
+           "  -h, --help                            Show this help\n"
+           "      --version                         Show version\n"
+           "      --dump-conf                       Dump default configuration\n"
+           "      --dump-modules                    Dump list of available modules\n"
+           "      --dump-resample-methods           Dump available resample methods\n"
+           "      --cleanup-shm                     Cleanup stale shared memory segments\n"
+           "      --start                           Start the daemon if it is not running\n"
+           "  -k  --kill                            Kill a running daemon\n"
+           "      --check                           Check for a running daemon (only returns exit code)\n\n"
+
+           "OPTIONS:\n"
+           "      --system[=BOOL]                   Run as system-wide instance\n"
+           "  -D, --daemonize[=BOOL]                Daemonize after startup\n"
+           "      --fail[=BOOL]                     Quit when startup fails\n"
+           "      --high-priority[=BOOL]            Try to set high nice level\n"
+           "                                        (only available as root, when SUID or\n"
+           "                                        with elevated RLIMIT_NICE)\n"
+           "      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+           "                                        (only available as root, when SUID or\n"
+           "                                        with elevated RLIMIT_RTPRIO)\n"
+           "      --disallow-module-loading[=BOOL]  Disallow module user requested module\n"
+           "                                        loading/unloading after startup\n"
+           "      --disallow-exit[=BOOL]            Disallow user requested exit\n"
+           "      --exit-idle-time=SECS             Terminate the daemon when idle and this\n"
+           "                                        time passed\n"
+           "      --scache-idle-time=SECS           Unload autoloaded samples when idle and\n"
+           "                                        this time passed\n"
+           "      --log-level[=LEVEL]               Increase or set verbosity level\n"
+           "  -v  --verbose                         Increase the verbosity level\n"
+           "      --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+           "                                        Specify the log target\n"
+           "      --log-meta[=BOOL]                 Include code location in log messages\n"
+           "      --log-time[=BOOL]                 Include timestamps in log messages\n"
+           "      --log-backtrace=FRAMES            Include a backtrace in log messages\n"
+           "  -p, --dl-search-path=PATH             Set the search path for dynamic shared\n"
+           "                                        objects (plugins)\n"
+           "      --resample-method=METHOD          Use the specified resampling method\n"
+           "                                        (See --dump-resample-methods for\n"
+           "                                        possible values)\n"
+           "      --use-pid-file[=BOOL]             Create a PID file\n"
+           "      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+           "                                        platforms that support it.\n"
+           "      --disable-shm[=BOOL]              Disable shared memory support.\n"
+           "      --enable-memfd[=BOOL]             Enable memfd shared memory support.\n\n"
+
+           "STARTUP SCRIPT:\n"
+           "  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module with\n"
+           "                                        the specified argument\n"
+           "  -F, --file=FILENAME                   Run the specified script\n"
+           "  -C                                    Open a command line on the running TTY\n"
+           "                                        after startup\n\n"
+
+           "  -n                                    Don't load default script file\n"),
+           pa_path_get_filename(argv0));
+}
+
+int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) {
+    pa_strbuf *buf = NULL;
+    int c;
+    int b;
+
+    pa_assert(conf);
+    pa_assert(argc > 0);
+    pa_assert(argv);
+
+    buf = pa_strbuf_new();
+
+    if (conf->script_commands)
+        pa_strbuf_puts(buf, conf->script_commands);
+
+    while ((c = getopt_long(argc, argv, "L:F:ChDnp:kv", long_options, NULL)) != -1) {
+        switch (c) {
+            case ARG_HELP:
+            case 'h':
+                conf->cmd = PA_CMD_HELP;
+                break;
+
+            case ARG_VERSION:
+                conf->cmd = PA_CMD_VERSION;
+                break;
+
+            case ARG_DUMP_CONF:
+                conf->cmd = PA_CMD_DUMP_CONF;
+                break;
+
+            case ARG_DUMP_MODULES:
+                conf->cmd = PA_CMD_DUMP_MODULES;
+                break;
+
+            case ARG_DUMP_RESAMPLE_METHODS:
+                conf->cmd = PA_CMD_DUMP_RESAMPLE_METHODS;
+                break;
+
+            case ARG_CLEANUP_SHM:
+                conf->cmd = PA_CMD_CLEANUP_SHM;
+                break;
+
+            case 'k':
+            case ARG_KILL:
+                conf->cmd = PA_CMD_KILL;
+                break;
+
+            case ARG_START:
+                conf->cmd = PA_CMD_START;
+                conf->daemonize = true;
+                break;
+
+            case ARG_CHECK:
+                conf->cmd = PA_CMD_CHECK;
+                break;
+
+            case ARG_LOAD:
+            case 'L':
+                pa_strbuf_printf(buf, "load-module %s\n", optarg);
+                break;
+
+            case ARG_FILE:
+            case 'F': {
+                char *p;
+                pa_strbuf_printf(buf, ".include %s\n", p = pa_make_path_absolute(optarg));
+                pa_xfree(p);
+                break;
+            }
+
+            case 'C':
+                pa_strbuf_puts(buf, "load-module module-cli exit_on_eof=1\n");
+                break;
+
+            case ARG_DAEMONIZE:
+            case 'D':
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--daemonize expects boolean argument"));
+                    goto fail;
+                }
+                conf->daemonize = !!b;
+                break;
+
+            case ARG_FAIL:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--fail expects boolean argument"));
+                    goto fail;
+                }
+                conf->fail = !!b;
+                break;
+
+            case 'v':
+            case ARG_LOG_LEVEL:
+
+                if (optarg) {
+                    if (pa_daemon_conf_set_log_level(conf, optarg) < 0) {
+                        pa_log(_("--log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error)."));
+                        goto fail;
+                    }
+                } else {
+                    if (conf->log_level < PA_LOG_LEVEL_MAX-1)
+                        conf->log_level++;
+                }
+
+                break;
+
+            case ARG_HIGH_PRIORITY:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--high-priority expects boolean argument"));
+                    goto fail;
+                }
+                conf->high_priority = !!b;
+                break;
+
+            case ARG_REALTIME:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--realtime expects boolean argument"));
+                    goto fail;
+                }
+                conf->realtime_scheduling = !!b;
+                break;
+
+            case ARG_DISALLOW_MODULE_LOADING:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--disallow-module-loading expects boolean argument"));
+                    goto fail;
+                }
+                conf->disallow_module_loading = !!b;
+                break;
+
+            case ARG_DISALLOW_EXIT:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--disallow-exit expects boolean argument"));
+                    goto fail;
+                }
+                conf->disallow_exit = !!b;
+                break;
+
+            case ARG_USE_PID_FILE:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--use-pid-file expects boolean argument"));
+                    goto fail;
+                }
+                conf->use_pid_file = !!b;
+                break;
+
+            case 'p':
+            case ARG_DL_SEARCH_PATH:
+                pa_xfree(conf->dl_search_path);
+                conf->dl_search_path = pa_xstrdup(optarg);
+                break;
+
+            case 'n':
+                conf->load_default_script_file = false;
+                break;
+
+            case ARG_LOG_TARGET:
+                if (pa_daemon_conf_set_log_target(conf, optarg) < 0) {
+#ifdef HAVE_SYSTEMD_JOURNAL
+                    pa_log(_("Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."));
+#else
+                    pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."));
+#endif
+                    goto fail;
+                }
+                break;
+
+            case ARG_LOG_TIME:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--log-time expects boolean argument"));
+                    goto fail;
+                }
+                conf->log_time = !!b;
+                break;
+
+            case ARG_LOG_META:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--log-meta expects boolean argument"));
+                    goto fail;
+                }
+                conf->log_meta = !!b;
+                break;
+
+            case ARG_LOG_BACKTRACE:
+                conf->log_backtrace = (unsigned) atoi(optarg);
+                break;
+
+            case ARG_EXIT_IDLE_TIME:
+                conf->exit_idle_time = atoi(optarg);
+                break;
+
+            case ARG_SCACHE_IDLE_TIME:
+                conf->scache_idle_time = atoi(optarg);
+                break;
+
+            case ARG_RESAMPLE_METHOD:
+                if (pa_daemon_conf_set_resample_method(conf, optarg) < 0) {
+                    pa_log(_("Invalid resample method '%s'."), optarg);
+                    goto fail;
+                }
+                break;
+
+            case ARG_SYSTEM:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--system expects boolean argument"));
+                    goto fail;
+                }
+                conf->system_instance = !!b;
+                break;
+
+            case ARG_NO_CPU_LIMIT:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--no-cpu-limit expects boolean argument"));
+                    goto fail;
+                }
+                conf->no_cpu_limit = !!b;
+                break;
+
+            case ARG_DISABLE_SHM:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--disable-shm expects boolean argument"));
+                    goto fail;
+                }
+                conf->disable_shm = !!b;
+                break;
+
+            case ARG_ENABLE_MEMFD:
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+                    pa_log(_("--enable-memfd expects boolean argument"));
+                    goto fail;
+                }
+                conf->disable_memfd = !b;
+                break;
+
+            default:
+                goto fail;
+        }
+    }
+
+    pa_xfree(conf->script_commands);
+    conf->script_commands = pa_strbuf_to_string_free(buf);
+
+    *d = optind;
+
+    return 0;
+
+fail:
+    if (buf)
+        pa_strbuf_free(buf);
+
+    return -1;
+}
diff --git a/src/daemon/cmdline.h b/src/daemon/cmdline.h
new file mode 100644 (file)
index 0000000..771c14d
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef foocmdlinehfoo
+#define foocmdlinehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "daemon-conf.h"
+
+/* Parse the command line and store its data in *c. Return the index
+ * of the first unparsed argument in *d. */
+int pa_cmdline_parse(pa_daemon_conf*c, int argc, char *const argv [], int *d);
+
+/* Show the command line help. The command name is extracted from
+ * argv[0] which should be passed in argv0. */
+void pa_cmdline_help(const char *argv0);
+
+#endif
diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c
new file mode 100644 (file)
index 0000000..a3deaf2
--- /dev/null
@@ -0,0 +1,244 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "cpulimit.h"
+
+#ifdef HAVE_SIGXCPU
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+/* This module implements a watchdog that makes sure that the current
+ * process doesn't consume more than 70% CPU time for 10 seconds. This
+ * is very useful when using SCHED_FIFO scheduling which effectively
+ * disables multitasking. */
+
+/* Method of operation: Using SIGXCPU a signal handler is called every
+ * 10s process CPU time. That function checks if less than 14s system
+ * time have passed. In that case, it tries to contact the main event
+ * loop through a pipe. After two additional seconds it is checked
+ * whether the main event loop contact was successful. If not, the
+ * program is terminated forcibly. */
+
+/* Utilize this much CPU time at maximum */
+#define CPUTIME_PERCENT 70
+
+/* Check every 10s */
+#define CPUTIME_INTERVAL_SOFT (10)
+
+/* Recheck after 5s */
+#define CPUTIME_INTERVAL_HARD (5)
+
+/* Time of the last CPU load check */
+static pa_usec_t last_time = 0;
+
+/* Pipe for communicating with the main loop */
+static int the_pipe[2] = {-1, -1};
+
+/* Main event loop and IO event for the FIFO */
+static pa_mainloop_api *api = NULL;
+static pa_io_event *io_event = NULL;
+
+/* Saved sigaction struct for SIGXCPU */
+static struct sigaction sigaction_prev;
+
+/* Nonzero after pa_cpu_limit_init() */
+static bool installed = false;
+
+/* The current state of operation */
+static enum {
+    PHASE_IDLE,   /* Normal state */
+    PHASE_SOFT    /* After CPU overload has been detected */
+} phase = PHASE_IDLE;
+
+/* Reset the SIGXCPU timer to the next t seconds */
+static void reset_cpu_time(int t) {
+    long n;
+    struct rlimit rl;
+    struct rusage ru;
+
+    /* Get the current CPU time of the current process */
+    pa_assert_se(getrusage(RUSAGE_SELF, &ru) >= 0);
+
+    n = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec + t;
+    pa_assert_se(getrlimit(RLIMIT_CPU, &rl) >= 0);
+
+    rl.rlim_cur = (rlim_t) n;
+    pa_assert_se(setrlimit(RLIMIT_CPU, &rl) >= 0);
+}
+
+/* A simple, thread-safe puts() work-alike */
+static void write_err(const char *p) {
+    pa_loop_write(2, p, strlen(p), NULL);
+}
+
+/* The signal handler, called on every SIGXCPU */
+static void signal_handler(int sig) {
+    int saved_errno;
+
+    saved_errno = errno;
+    pa_assert(sig == SIGXCPU);
+
+    if (phase == PHASE_IDLE) {
+        pa_usec_t now, elapsed;
+
+#ifdef PRINT_CPU_LOAD
+        char t[256];
+#endif
+
+        now = pa_rtclock_now();
+        elapsed = now - last_time;
+
+#ifdef PRINT_CPU_LOAD
+        pa_snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", ((double) CPUTIME_INTERVAL_SOFT * (double) PA_USEC_PER_SEC) / (double) elapsed * 100.0);
+        write_err(t);
+#endif
+
+        if (((double) CPUTIME_INTERVAL_SOFT * (double) PA_USEC_PER_SEC) >= ((double) elapsed * (double) CPUTIME_PERCENT / 100.0)) {
+            static const char c = 'X';
+
+            write_err("Soft CPU time limit exhausted, terminating.\n");
+
+            /* Try a soft cleanup */
+            (void) pa_write(the_pipe[1], &c, sizeof(c), NULL);
+            phase = PHASE_SOFT;
+            reset_cpu_time(CPUTIME_INTERVAL_HARD);
+
+        } else {
+
+            /* Everything's fine */
+            reset_cpu_time(CPUTIME_INTERVAL_SOFT);
+            last_time = now;
+        }
+
+    } else if (phase == PHASE_SOFT) {
+        write_err("Hard CPU time limit exhausted, terminating forcibly.\n");
+        abort(); /* Forced exit */
+    }
+
+    errno = saved_errno;
+}
+
+/* Callback for IO events on the FIFO */
+static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata) {
+    char c;
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(f == PA_IO_EVENT_INPUT);
+    pa_assert(e == io_event);
+    pa_assert(fd == the_pipe[0]);
+
+    pa_log("Received request to terminate due to CPU overload.");
+
+    (void) pa_read(the_pipe[0], &c, sizeof(c), NULL);
+    m->quit(m, 1); /* Quit the main loop */
+}
+
+/* Initializes CPU load limiter */
+int pa_cpu_limit_init(pa_mainloop_api *m) {
+    struct sigaction sa;
+
+    pa_assert(m);
+    pa_assert(!api);
+    pa_assert(!io_event);
+    pa_assert(the_pipe[0] == -1);
+    pa_assert(the_pipe[1] == -1);
+    pa_assert(!installed);
+
+    last_time = pa_rtclock_now();
+
+    /* Prepare the main loop pipe */
+    if (pa_pipe_cloexec(the_pipe) < 0) {
+        pa_log("pipe() failed: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_make_fd_nonblock(the_pipe[0]);
+    pa_make_fd_nonblock(the_pipe[1]);
+
+    api = m;
+    io_event = api->io_new(m, the_pipe[0], PA_IO_EVENT_INPUT, callback, NULL);
+
+    phase = PHASE_IDLE;
+
+    /* Install signal handler for SIGXCPU */
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = signal_handler;
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = SA_RESTART;
+
+    if (sigaction(SIGXCPU, &sa, &sigaction_prev) < 0) {
+        pa_cpu_limit_done();
+        return -1;
+    }
+
+    installed = true;
+
+    reset_cpu_time(CPUTIME_INTERVAL_SOFT);
+
+    return 0;
+}
+
+/* Shutdown CPU load limiter */
+void pa_cpu_limit_done(void) {
+
+    if (io_event) {
+        pa_assert(api);
+        api->io_free(io_event);
+        io_event = NULL;
+        api = NULL;
+    }
+
+    pa_close_pipe(the_pipe);
+
+    if (installed) {
+        pa_assert_se(sigaction(SIGXCPU, &sigaction_prev, NULL) >= 0);
+        installed = false;
+    }
+}
+
+#else /* HAVE_SIGXCPU */
+
+int pa_cpu_limit_init(pa_mainloop_api *m) {
+    return 0;
+}
+
+void pa_cpu_limit_done(void) {
+}
+
+#endif
diff --git a/src/daemon/cpulimit.h b/src/daemon/cpulimit.h
new file mode 100644 (file)
index 0000000..460f8c5
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef foocpulimithfoo
+#define foocpulimithfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/mainloop-api.h>
+
+/* This kills the pulseaudio process if it eats more than 70% of the
+ * CPU time. This is build around setrlimit() and SIGXCPU. It is handy
+ * in case of using SCHED_FIFO which may freeze the whole machine  */
+
+int pa_cpu_limit_init(pa_mainloop_api *m);
+void pa_cpu_limit_done(void);
+
+#endif
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
new file mode 100644 (file)
index 0000000..9883126
--- /dev/null
@@ -0,0 +1,817 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/version.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/conf-parser.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
+
+#include "daemon-conf.h"
+
+#define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "default.pa"
+#define DEFAULT_SCRIPT_FILE_USER PA_PATH_SEP "default.pa"
+#define DEFAULT_SYSTEM_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "system.pa"
+
+#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf"
+#define DEFAULT_CONFIG_FILE_USER PA_PATH_SEP "daemon.conf"
+
+#define ENV_SCRIPT_FILE "PULSE_SCRIPT"
+#define ENV_CONFIG_FILE "PULSE_CONFIG"
+#define ENV_DL_SEARCH_PATH "PULSE_DLPATH"
+
+static const pa_daemon_conf default_conf = {
+    .cmd = PA_CMD_DAEMON,
+    .daemonize = false,
+    .fail = true,
+    .high_priority = true,
+    .nice_level = -11,
+    .realtime_scheduling = true,
+    .realtime_priority = 5,  /* Half of JACK's default rtprio */
+    .disallow_module_loading = false,
+    .disallow_exit = false,
+    .flat_volumes = true,
+    .exit_idle_time = 20,
+    .scache_idle_time = 20,
+    .script_commands = NULL,
+    .dl_search_path = NULL,
+    .load_default_script_file = true,
+    .default_script_file = NULL,
+    .log_target = NULL,
+    .log_level = PA_LOG_NOTICE,
+    .log_backtrace = 0,
+    .log_meta = false,
+    .log_time = false,
+    .resample_method = PA_RESAMPLER_AUTO,
+    .avoid_resampling = false,
+    .disable_remixing = false,
+    .remixing_use_all_sink_channels = true,
+    .disable_lfe_remixing = true,
+    .lfe_crossover_freq = 0,
+    .config_file = NULL,
+    .use_pid_file = true,
+    .system_instance = false,
+#ifdef HAVE_DBUS
+    .local_server_type = PA_SERVER_TYPE_UNSET, /* The actual default is _USER, but we have to detect when the user doesn't specify this option. */
+#endif
+    .no_cpu_limit = true,
+    .disable_shm = false,
+    .disable_memfd = false,
+    .lock_memory = false,
+    .deferred_volume = true,
+    .default_n_fragments = 4,
+    .default_fragment_size_msec = 25,
+    .deferred_volume_safety_margin_usec = 8000,
+    .deferred_volume_extra_delay_usec = 0,
+    .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 },
+    .alternate_sample_rate = 48000,
+    .default_channel_map = { .channels = 2, .map = { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } },
+    .shm_size = 0
+#ifdef HAVE_SYS_RESOURCE_H
+   ,.rlimit_fsize = { .value = 0, .is_set = false },
+    .rlimit_data = { .value = 0, .is_set = false },
+    .rlimit_stack = { .value = 0, .is_set = false },
+    .rlimit_core = { .value = 0, .is_set = false }
+#ifdef RLIMIT_RSS
+   ,.rlimit_rss = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_NPROC
+   ,.rlimit_nproc = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_NOFILE
+   ,.rlimit_nofile = { .value = 256, .is_set = true }
+#endif
+#ifdef RLIMIT_MEMLOCK
+   ,.rlimit_memlock = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_AS
+   ,.rlimit_as = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_LOCKS
+   ,.rlimit_locks = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_SIGPENDING
+   ,.rlimit_sigpending = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_MSGQUEUE
+   ,.rlimit_msgqueue = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_NICE
+   ,.rlimit_nice = { .value = 31, .is_set = true }     /* nice level of -11 */
+#endif
+#ifdef RLIMIT_RTPRIO
+   ,.rlimit_rtprio = { .value = 9, .is_set = true }    /* One below JACK's default for the server */
+#endif
+#ifdef RLIMIT_RTTIME
+   ,.rlimit_rttime = { .value = 200*PA_USEC_PER_MSEC, .is_set = true } /* rtkit's limit is 200 ms */
+#endif
+#endif
+};
+
+pa_daemon_conf *pa_daemon_conf_new(void) {
+    pa_daemon_conf *c;
+
+    c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
+
+#ifdef OS_IS_WIN32
+    c->dl_search_path = pa_sprintf_malloc("%s" PA_PATH_SEP "lib" PA_PATH_SEP "pulse-%d.%d" PA_PATH_SEP "modules",
+                                          pa_win32_get_toplevel(NULL), PA_MAJOR, PA_MINOR);
+#else
+    if (pa_run_from_build_tree()) {
+        pa_log_notice("Detected that we are run from the build tree, fixing search path.");
+        c->dl_search_path = pa_xstrdup(PA_BUILDDIR);
+    } else
+        c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
+#endif
+
+    return c;
+}
+
+void pa_daemon_conf_free(pa_daemon_conf *c) {
+    pa_assert(c);
+
+    pa_xfree(c->script_commands);
+    pa_xfree(c->dl_search_path);
+    pa_xfree(c->default_script_file);
+
+    if (c->log_target)
+        pa_log_target_free(c->log_target);
+
+    pa_xfree(c->config_file);
+    pa_xfree(c);
+}
+
+int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) {
+    pa_log_target *log_target = NULL;
+
+    pa_assert(c);
+    pa_assert(string);
+
+    if (!pa_streq(string, "auto")) {
+        log_target = pa_log_parse_target(string);
+
+        if (!log_target)
+            return -1;
+    }
+
+    c->log_target = log_target;
+
+    return 0;
+}
+
+int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) {
+    uint32_t u;
+    pa_assert(c);
+    pa_assert(string);
+
+    if (pa_atou(string, &u) >= 0) {
+        if (u >= PA_LOG_LEVEL_MAX)
+            return -1;
+
+        c->log_level = (pa_log_level_t) u;
+    } else if (pa_startswith(string, "debug"))
+        c->log_level = PA_LOG_DEBUG;
+    else if (pa_startswith(string, "info"))
+        c->log_level = PA_LOG_INFO;
+    else if (pa_startswith(string, "notice"))
+        c->log_level = PA_LOG_NOTICE;
+    else if (pa_startswith(string, "warn"))
+        c->log_level = PA_LOG_WARN;
+    else if (pa_startswith(string, "err"))
+        c->log_level = PA_LOG_ERROR;
+    else
+        return -1;
+
+    return 0;
+}
+
+int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {
+    int m;
+    pa_assert(c);
+    pa_assert(string);
+
+    if ((m = pa_parse_resample_method(string)) < 0)
+        return -1;
+
+    c->resample_method = m;
+    return 0;
+}
+
+int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string) {
+    pa_assert(c);
+    pa_assert(string);
+
+    if (pa_streq(string, "user"))
+        c->local_server_type = PA_SERVER_TYPE_USER;
+    else if (pa_streq(string, "system")) {
+        c->local_server_type = PA_SERVER_TYPE_SYSTEM;
+    } else if (pa_streq(string, "none")) {
+        c->local_server_type = PA_SERVER_TYPE_NONE;
+    } else
+        return -1;
+
+    return 0;
+}
+
+static int parse_log_target(pa_config_parser_state *state) {
+    pa_daemon_conf *c;
+
+    pa_assert(state);
+
+    c = state->data;
+
+    if (pa_daemon_conf_set_log_target(c, state->rvalue) < 0) {
+        pa_log(_("[%s:%u] Invalid log target '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int parse_log_level(pa_config_parser_state *state) {
+    pa_daemon_conf *c;
+
+    pa_assert(state);
+
+    c = state->data;
+
+    if (pa_daemon_conf_set_log_level(c, state->rvalue) < 0) {
+        pa_log(_("[%s:%u] Invalid log level '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int parse_resample_method(pa_config_parser_state *state) {
+    pa_daemon_conf *c;
+
+    pa_assert(state);
+
+    c = state->data;
+
+    if (pa_daemon_conf_set_resample_method(c, state->rvalue) < 0) {
+        pa_log(_("[%s:%u] Invalid resample method '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    return 0;
+}
+
+#ifdef HAVE_SYS_RESOURCE_H
+static int parse_rlimit(pa_config_parser_state *state) {
+    struct pa_rlimit *r;
+
+    pa_assert(state);
+
+    r = state->data;
+
+    if (state->rvalue[strspn(state->rvalue, "\t ")] == 0) {
+        /* Empty string */
+        r->is_set = 0;
+        r->value = 0;
+    } else {
+        int32_t k;
+        if (pa_atoi(state->rvalue, &k) < 0) {
+            pa_log(_("[%s:%u] Invalid rlimit '%s'."), state->filename, state->lineno, state->rvalue);
+            return -1;
+        }
+        r->is_set = k >= 0;
+        r->value = k >= 0 ? (rlim_t) k : 0;
+    }
+
+    return 0;
+}
+#endif
+
+static int parse_sample_format(pa_config_parser_state *state) {
+    pa_daemon_conf *c;
+    pa_sample_format_t f;
+
+    pa_assert(state);
+
+    c = state->data;
+
+    if ((f = pa_parse_sample_format(state->rvalue)) < 0) {
+        pa_log(_("[%s:%u] Invalid sample format '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    c->default_sample_spec.format = f;
+    return 0;
+}
+
+static int parse_sample_rate(pa_config_parser_state *state) {
+    pa_daemon_conf *c;
+    uint32_t r;
+
+    pa_assert(state);
+
+    c = state->data;
+
+    if (pa_atou(state->rvalue, &r) < 0 || !pa_sample_rate_valid(r)) {
+        pa_log(_("[%s:%u] Invalid sample rate '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    c->default_sample_spec.rate = r;
+    return 0;
+}
+
+static int parse_alternate_sample_rate(pa_config_parser_state *state) {
+    pa_daemon_conf *c;
+    uint32_t r;
+
+    pa_assert(state);
+
+    c = state->data;
+
+    if (pa_atou(state->rvalue, &r) < 0 || !pa_sample_rate_valid(r)) {
+        pa_log(_("[%s:%u] Invalid sample rate '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    c->alternate_sample_rate = r;
+    return 0;
+}
+
+struct channel_conf_info {
+    pa_daemon_conf *conf;
+    bool default_sample_spec_set;
+    bool default_channel_map_set;
+};
+
+static int parse_sample_channels(pa_config_parser_state *state) {
+    struct channel_conf_info *i;
+    int32_t n;
+
+    pa_assert(state);
+
+    i = state->data;
+
+    if (pa_atoi(state->rvalue, &n) < 0 || !pa_channels_valid(n)) {
+        pa_log(_("[%s:%u] Invalid sample channels '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    i->conf->default_sample_spec.channels = (uint8_t) n;
+    i->default_sample_spec_set = true;
+    return 0;
+}
+
+static int parse_channel_map(pa_config_parser_state *state) {
+    struct channel_conf_info *i;
+
+    pa_assert(state);
+
+    i = state->data;
+
+    if (!pa_channel_map_parse(&i->conf->default_channel_map, state->rvalue)) {
+        pa_log(_("[%s:%u] Invalid channel map '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    i->default_channel_map_set = true;
+    return 0;
+}
+
+static int parse_fragments(pa_config_parser_state *state) {
+    pa_daemon_conf *c;
+    int32_t n;
+
+    pa_assert(state);
+
+    c = state->data;
+
+    if (pa_atoi(state->rvalue, &n) < 0 || n < 2) {
+        pa_log(_("[%s:%u] Invalid number of fragments '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    c->default_n_fragments = (unsigned) n;
+    return 0;
+}
+
+static int parse_fragment_size_msec(pa_config_parser_state *state) {
+    pa_daemon_conf *c;
+    int32_t n;
+
+    pa_assert(state);
+
+    c = state->data;
+
+    if (pa_atoi(state->rvalue, &n) < 0 || n < 1) {
+        pa_log(_("[%s:%u] Invalid fragment size '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    c->default_fragment_size_msec = (unsigned) n;
+    return 0;
+}
+
+static int parse_nice_level(pa_config_parser_state *state) {
+    pa_daemon_conf *c;
+    int32_t level;
+
+    pa_assert(state);
+
+    c = state->data;
+
+    if (pa_atoi(state->rvalue, &level) < 0 || level < -20 || level > 19) {
+        pa_log(_("[%s:%u] Invalid nice level '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    c->nice_level = (int) level;
+    return 0;
+}
+
+static int parse_rtprio(pa_config_parser_state *state) {
+#if !defined(OS_IS_WIN32) && defined(HAVE_SCHED_H)
+    pa_daemon_conf *c;
+    int32_t rtprio;
+#endif
+
+    pa_assert(state);
+
+#ifdef OS_IS_WIN32
+    pa_log("[%s:%u] Realtime priority not available on win32.", state->filename, state->lineno);
+#else
+# ifdef HAVE_SCHED_H
+    c = state->data;
+
+    if (pa_atoi(state->rvalue, &rtprio) < 0 || rtprio < sched_get_priority_min(SCHED_FIFO) || rtprio > sched_get_priority_max(SCHED_FIFO)) {
+        pa_log("[%s:%u] Invalid realtime priority '%s'.", state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    c->realtime_priority = (int) rtprio;
+# endif
+#endif /* OS_IS_WIN32 */
+
+    return 0;
+}
+
+#ifdef HAVE_DBUS
+static int parse_server_type(pa_config_parser_state *state) {
+    pa_daemon_conf *c;
+
+    pa_assert(state);
+
+    c = state->data;
+
+    if (pa_daemon_conf_set_local_server_type(c, state->rvalue) < 0) {
+        pa_log(_("[%s:%u] Invalid server type '%s'."), state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    return 0;
+}
+#endif
+
+int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
+    int r = -1;
+    FILE *f = NULL;
+    struct channel_conf_info ci;
+    pa_config_item table[] = {
+        { "daemonize",                  pa_config_parse_bool,     &c->daemonize, NULL },
+        { "fail",                       pa_config_parse_bool,     &c->fail, NULL },
+        { "high-priority",              pa_config_parse_bool,     &c->high_priority, NULL },
+        { "realtime-scheduling",        pa_config_parse_bool,     &c->realtime_scheduling, NULL },
+        { "disallow-module-loading",    pa_config_parse_bool,     &c->disallow_module_loading, NULL },
+        { "allow-module-loading",       pa_config_parse_not_bool, &c->disallow_module_loading, NULL },
+        { "disallow-exit",              pa_config_parse_bool,     &c->disallow_exit, NULL },
+        { "allow-exit",                 pa_config_parse_not_bool, &c->disallow_exit, NULL },
+        { "use-pid-file",               pa_config_parse_bool,     &c->use_pid_file, NULL },
+        { "system-instance",            pa_config_parse_bool,     &c->system_instance, NULL },
+#ifdef HAVE_DBUS
+        { "local-server-type",          parse_server_type,        c, NULL },
+#endif
+        { "no-cpu-limit",               pa_config_parse_bool,     &c->no_cpu_limit, NULL },
+        { "cpu-limit",                  pa_config_parse_not_bool, &c->no_cpu_limit, NULL },
+        { "disable-shm",                pa_config_parse_bool,     &c->disable_shm, NULL },
+        { "enable-shm",                 pa_config_parse_not_bool, &c->disable_shm, NULL },
+        { "enable-memfd",               pa_config_parse_not_bool, &c->disable_memfd, NULL },
+        { "flat-volumes",               pa_config_parse_bool,     &c->flat_volumes, NULL },
+        { "lock-memory",                pa_config_parse_bool,     &c->lock_memory, NULL },
+        { "enable-deferred-volume",     pa_config_parse_bool,     &c->deferred_volume, NULL },
+        { "exit-idle-time",             pa_config_parse_int,      &c->exit_idle_time, NULL },
+        { "scache-idle-time",           pa_config_parse_int,      &c->scache_idle_time, NULL },
+        { "realtime-priority",          parse_rtprio,             c, NULL },
+        { "dl-search-path",             pa_config_parse_string,   &c->dl_search_path, NULL },
+        { "default-script-file",        pa_config_parse_string,   &c->default_script_file, NULL },
+        { "log-target",                 parse_log_target,         c, NULL },
+        { "log-level",                  parse_log_level,          c, NULL },
+        { "verbose",                    parse_log_level,          c, NULL },
+        { "resample-method",            parse_resample_method,    c, NULL },
+        { "default-sample-format",      parse_sample_format,      c, NULL },
+        { "default-sample-rate",        parse_sample_rate,        c, NULL },
+        { "alternate-sample-rate",      parse_alternate_sample_rate, c, NULL },
+        { "default-sample-channels",    parse_sample_channels,    &ci,  NULL },
+        { "default-channel-map",        parse_channel_map,        &ci,  NULL },
+        { "default-fragments",          parse_fragments,          c, NULL },
+        { "default-fragment-size-msec", parse_fragment_size_msec, c, NULL },
+        { "deferred-volume-safety-margin-usec",
+                                        pa_config_parse_unsigned, &c->deferred_volume_safety_margin_usec, NULL },
+        { "deferred-volume-extra-delay-usec",
+                                        pa_config_parse_int,      &c->deferred_volume_extra_delay_usec, NULL },
+        { "nice-level",                 parse_nice_level,         c, NULL },
+        { "avoid-resampling",           pa_config_parse_bool,     &c->avoid_resampling, NULL },
+        { "disable-remixing",           pa_config_parse_bool,     &c->disable_remixing, NULL },
+        { "enable-remixing",            pa_config_parse_not_bool, &c->disable_remixing, NULL },
+        { "remixing-use-all-sink-channels",
+                                        pa_config_parse_bool,     &c->remixing_use_all_sink_channels, NULL },
+        { "disable-lfe-remixing",       pa_config_parse_bool,     &c->disable_lfe_remixing, NULL },
+        { "enable-lfe-remixing",        pa_config_parse_not_bool, &c->disable_lfe_remixing, NULL },
+        { "lfe-crossover-freq",         pa_config_parse_unsigned, &c->lfe_crossover_freq, NULL },
+        { "load-default-script-file",   pa_config_parse_bool,     &c->load_default_script_file, NULL },
+        { "shm-size-bytes",             pa_config_parse_size,     &c->shm_size, NULL },
+        { "log-meta",                   pa_config_parse_bool,     &c->log_meta, NULL },
+        { "log-time",                   pa_config_parse_bool,     &c->log_time, NULL },
+        { "log-backtrace",              pa_config_parse_unsigned, &c->log_backtrace, NULL },
+#ifdef HAVE_SYS_RESOURCE_H
+        { "rlimit-fsize",               parse_rlimit,             &c->rlimit_fsize, NULL },
+        { "rlimit-data",                parse_rlimit,             &c->rlimit_data, NULL },
+        { "rlimit-stack",               parse_rlimit,             &c->rlimit_stack, NULL },
+        { "rlimit-core",                parse_rlimit,             &c->rlimit_core, NULL },
+#ifdef RLIMIT_RSS
+        { "rlimit-rss",                 parse_rlimit,             &c->rlimit_rss, NULL },
+#endif
+#ifdef RLIMIT_NOFILE
+        { "rlimit-nofile",              parse_rlimit,             &c->rlimit_nofile, NULL },
+#endif
+#ifdef RLIMIT_AS
+        { "rlimit-as",                  parse_rlimit,             &c->rlimit_as, NULL },
+#endif
+#ifdef RLIMIT_NPROC
+        { "rlimit-nproc",               parse_rlimit,             &c->rlimit_nproc, NULL },
+#endif
+#ifdef RLIMIT_MEMLOCK
+        { "rlimit-memlock",             parse_rlimit,             &c->rlimit_memlock, NULL },
+#endif
+#ifdef RLIMIT_LOCKS
+        { "rlimit-locks",               parse_rlimit,             &c->rlimit_locks, NULL },
+#endif
+#ifdef RLIMIT_SIGPENDING
+        { "rlimit-sigpending",          parse_rlimit,             &c->rlimit_sigpending, NULL },
+#endif
+#ifdef RLIMIT_MSGQUEUE
+        { "rlimit-msgqueue",            parse_rlimit,             &c->rlimit_msgqueue, NULL },
+#endif
+#ifdef RLIMIT_NICE
+        { "rlimit-nice",                parse_rlimit,             &c->rlimit_nice, NULL },
+#endif
+#ifdef RLIMIT_RTPRIO
+        { "rlimit-rtprio",              parse_rlimit,             &c->rlimit_rtprio, NULL },
+#endif
+#ifdef RLIMIT_RTTIME
+        { "rlimit-rttime",              parse_rlimit,             &c->rlimit_rttime, NULL },
+#endif
+#endif
+        { NULL,                         NULL,                     NULL, NULL },
+    };
+
+    pa_xfree(c->config_file);
+    c->config_file = NULL;
+
+    f = filename ?
+        pa_fopen_cloexec(c->config_file = pa_xstrdup(filename), "r") :
+        pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
+
+    if (!f && errno != ENOENT) {
+        pa_log_warn(_("Failed to open configuration file: %s"), pa_cstrerror(errno));
+        goto finish;
+    }
+
+    ci.default_channel_map_set = ci.default_sample_spec_set = false;
+    ci.conf = c;
+
+    r = f ? pa_config_parse(c->config_file, f, table, NULL, true, NULL) : 0;
+
+    if (r >= 0) {
+
+        /* Make sure that channel map and sample spec fit together */
+
+        if (ci.default_sample_spec_set &&
+            ci.default_channel_map_set &&
+            c->default_channel_map.channels != c->default_sample_spec.channels) {
+            pa_log_error(_("The specified default channel map has a different number of channels than the specified default number of channels."));
+            r = -1;
+            goto finish;
+        } else if (ci.default_sample_spec_set)
+            pa_channel_map_init_extend(&c->default_channel_map, c->default_sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+        else if (ci.default_channel_map_set)
+            c->default_sample_spec.channels = c->default_channel_map.channels;
+    }
+
+finish:
+    if (f)
+        fclose(f);
+
+    return r;
+}
+
+int pa_daemon_conf_env(pa_daemon_conf *c) {
+    char *e;
+    pa_assert(c);
+
+    if ((e = getenv(ENV_DL_SEARCH_PATH))) {
+        pa_xfree(c->dl_search_path);
+        c->dl_search_path = pa_xstrdup(e);
+    }
+    if ((e = getenv(ENV_SCRIPT_FILE))) {
+        pa_xfree(c->default_script_file);
+        c->default_script_file = pa_xstrdup(e);
+    }
+
+    return 0;
+}
+
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c) {
+    pa_assert(c);
+
+    if (!c->default_script_file) {
+        if (c->system_instance)
+            c->default_script_file = pa_find_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE);
+        else
+            c->default_script_file = pa_find_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE);
+    }
+
+    return c->default_script_file;
+}
+
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c) {
+    FILE *f;
+    pa_assert(c);
+
+    if (!c->default_script_file) {
+        if (c->system_instance)
+            f = pa_open_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE, &c->default_script_file);
+        else
+            f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file);
+    } else
+        f = pa_fopen_cloexec(c->default_script_file, "r");
+
+    return f;
+}
+
+char *pa_daemon_conf_dump(pa_daemon_conf *c) {
+    static const char* const log_level_to_string[] = {
+        [PA_LOG_DEBUG] = "debug",
+        [PA_LOG_INFO] = "info",
+        [PA_LOG_NOTICE] = "notice",
+        [PA_LOG_WARN] = "warning",
+        [PA_LOG_ERROR] = "error"
+    };
+
+#ifdef HAVE_DBUS
+    static const char* const server_type_to_string[] = {
+        [PA_SERVER_TYPE_UNSET] = "!!UNSET!!",
+        [PA_SERVER_TYPE_USER] = "user",
+        [PA_SERVER_TYPE_SYSTEM] = "system",
+        [PA_SERVER_TYPE_NONE] = "none"
+    };
+#endif
+
+    pa_strbuf *s;
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char *log_target = NULL;
+
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    if (c->config_file)
+        pa_strbuf_printf(s, _("### Read from configuration file: %s ###\n"), c->config_file);
+
+    pa_assert(c->log_level < PA_LOG_LEVEL_MAX);
+
+    if (c->log_target)
+        log_target = pa_log_target_to_string(c->log_target);
+
+    pa_strbuf_printf(s, "daemonize = %s\n", pa_yes_no(c->daemonize));
+    pa_strbuf_printf(s, "fail = %s\n", pa_yes_no(c->fail));
+    pa_strbuf_printf(s, "high-priority = %s\n", pa_yes_no(c->high_priority));
+    pa_strbuf_printf(s, "nice-level = %i\n", c->nice_level);
+    pa_strbuf_printf(s, "realtime-scheduling = %s\n", pa_yes_no(c->realtime_scheduling));
+    pa_strbuf_printf(s, "realtime-priority = %i\n", c->realtime_priority);
+    pa_strbuf_printf(s, "allow-module-loading = %s\n", pa_yes_no(!c->disallow_module_loading));
+    pa_strbuf_printf(s, "allow-exit = %s\n", pa_yes_no(!c->disallow_exit));
+    pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file));
+    pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance));
+#ifdef HAVE_DBUS
+    pa_strbuf_printf(s, "local-server-type = %s\n", server_type_to_string[c->local_server_type]);
+#endif
+    pa_strbuf_printf(s, "cpu-limit = %s\n", pa_yes_no(!c->no_cpu_limit));
+    pa_strbuf_printf(s, "enable-shm = %s\n", pa_yes_no(!c->disable_shm));
+    pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes));
+    pa_strbuf_printf(s, "lock-memory = %s\n", pa_yes_no(c->lock_memory));
+    pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
+    pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
+    pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
+    pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c)));
+    pa_strbuf_printf(s, "load-default-script-file = %s\n", pa_yes_no(c->load_default_script_file));
+    pa_strbuf_printf(s, "log-target = %s\n", pa_strempty(log_target));
+    pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
+    pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
+    pa_strbuf_printf(s, "avoid-resampling = %s\n", pa_yes_no(!c->avoid_resampling));
+    pa_strbuf_printf(s, "enable-remixing = %s\n", pa_yes_no(!c->disable_remixing));
+    pa_strbuf_printf(s, "remixing-use-all-sink-channels = %s\n", pa_yes_no(c->remixing_use_all_sink_channels));
+    pa_strbuf_printf(s, "enable-lfe-remixing = %s\n", pa_yes_no(!c->disable_lfe_remixing));
+    pa_strbuf_printf(s, "lfe-crossover-freq = %u\n", c->lfe_crossover_freq);
+    pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format));
+    pa_strbuf_printf(s, "default-sample-rate = %u\n", c->default_sample_spec.rate);
+    pa_strbuf_printf(s, "alternate-sample-rate = %u\n", c->alternate_sample_rate);
+    pa_strbuf_printf(s, "default-sample-channels = %u\n", c->default_sample_spec.channels);
+    pa_strbuf_printf(s, "default-channel-map = %s\n", pa_channel_map_snprint(cm, sizeof(cm), &c->default_channel_map));
+    pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments);
+    pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec);
+    pa_strbuf_printf(s, "enable-deferred-volume = %s\n", pa_yes_no(c->deferred_volume));
+    pa_strbuf_printf(s, "deferred-volume-safety-margin-usec = %u\n", c->deferred_volume_safety_margin_usec);
+    pa_strbuf_printf(s, "deferred-volume-extra-delay-usec = %d\n", c->deferred_volume_extra_delay_usec);
+    pa_strbuf_printf(s, "shm-size-bytes = %lu\n", (unsigned long) c->shm_size);
+    pa_strbuf_printf(s, "log-meta = %s\n", pa_yes_no(c->log_meta));
+    pa_strbuf_printf(s, "log-time = %s\n", pa_yes_no(c->log_time));
+    pa_strbuf_printf(s, "log-backtrace = %u\n", c->log_backtrace);
+#ifdef HAVE_SYS_RESOURCE_H
+    pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1);
+    pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
+    pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
+    pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
+#ifdef RLIMIT_RSS
+    pa_strbuf_printf(s, "rlimit-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1);
+#endif
+#ifdef RLIMIT_AS
+    pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
+#endif
+#ifdef RLIMIT_NPROC
+    pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1);
+#endif
+#ifdef RLIMIT_NOFILE
+    pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
+#endif
+#ifdef RLIMIT_MEMLOCK
+    pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1);
+#endif
+#ifdef RLIMIT_LOCKS
+    pa_strbuf_printf(s, "rlimit-locks = %li\n", c->rlimit_locks.is_set ? (long int) c->rlimit_locks.value : -1);
+#endif
+#ifdef RLIMIT_SIGPENDING
+    pa_strbuf_printf(s, "rlimit-sigpending = %li\n", c->rlimit_sigpending.is_set ? (long int) c->rlimit_sigpending.value : -1);
+#endif
+#ifdef RLIMIT_MSGQUEUE
+    pa_strbuf_printf(s, "rlimit-msgqueue = %li\n", c->rlimit_msgqueue.is_set ? (long int) c->rlimit_msgqueue.value : -1);
+#endif
+#ifdef RLIMIT_NICE
+    pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_nice.is_set ? (long int) c->rlimit_nice.value : -1);
+#endif
+#ifdef RLIMIT_RTPRIO
+    pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_rtprio.is_set ? (long int) c->rlimit_rtprio.value : -1);
+#endif
+#ifdef RLIMIT_RTTIME
+    pa_strbuf_printf(s, "rlimit-rttime = %li\n", c->rlimit_rttime.is_set ? (long int) c->rlimit_rttime.value : -1);
+#endif
+#endif
+
+    pa_xfree(log_target);
+
+    return pa_strbuf_to_string_free(s);
+}
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
new file mode 100644 (file)
index 0000000..953ea33
--- /dev/null
@@ -0,0 +1,167 @@
+#ifndef foodaemonconfhfoo
+#define foodaemonconfhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+/* The actual command to execute */
+typedef enum pa_daemon_conf_cmd {
+    PA_CMD_DAEMON,  /* the default */
+    PA_CMD_START,
+    PA_CMD_HELP,
+    PA_CMD_VERSION,
+    PA_CMD_DUMP_CONF,
+    PA_CMD_DUMP_MODULES,
+    PA_CMD_KILL,
+    PA_CMD_CHECK,
+    PA_CMD_DUMP_RESAMPLE_METHODS,
+    PA_CMD_CLEANUP_SHM
+} pa_daemon_conf_cmd_t;
+
+#ifdef HAVE_SYS_RESOURCE_H
+typedef struct pa_rlimit {
+    rlim_t value;
+    bool is_set;
+} pa_rlimit;
+#endif
+
+/* A structure containing configuration data for the PulseAudio server . */
+typedef struct pa_daemon_conf {
+    pa_daemon_conf_cmd_t cmd;
+    bool daemonize,
+        fail,
+        high_priority,
+        realtime_scheduling,
+        disallow_module_loading,
+        use_pid_file,
+        system_instance,
+        no_cpu_limit,
+        disable_shm,
+        disable_memfd,
+        avoid_resampling,
+        disable_remixing,
+        remixing_use_all_sink_channels,
+        disable_lfe_remixing,
+        load_default_script_file,
+        disallow_exit,
+        log_meta,
+        log_time,
+        flat_volumes,
+        lock_memory,
+        deferred_volume;
+    pa_server_type_t local_server_type;
+    int exit_idle_time,
+        scache_idle_time,
+        realtime_priority,
+        nice_level,
+        resample_method;
+    char *script_commands, *dl_search_path, *default_script_file;
+    pa_log_target *log_target;
+    pa_log_level_t log_level;
+    unsigned log_backtrace;
+    char *config_file;
+
+#ifdef HAVE_SYS_RESOURCE_H
+    pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core;
+#ifdef RLIMIT_RSS
+    pa_rlimit rlimit_rss;
+#endif
+#ifdef RLIMIT_NOFILE
+    pa_rlimit rlimit_nofile;
+#endif
+#ifdef RLIMIT_AS
+    pa_rlimit rlimit_as;
+#endif
+#ifdef RLIMIT_NPROC
+    pa_rlimit rlimit_nproc;
+#endif
+#ifdef RLIMIT_MEMLOCK
+    pa_rlimit rlimit_memlock;
+#endif
+#ifdef RLIMIT_LOCKS
+    pa_rlimit rlimit_locks;
+#endif
+#ifdef RLIMIT_SIGPENDING
+    pa_rlimit rlimit_sigpending;
+#endif
+#ifdef RLIMIT_MSGQUEUE
+    pa_rlimit rlimit_msgqueue;
+#endif
+#ifdef RLIMIT_NICE
+    pa_rlimit rlimit_nice;
+#endif
+#ifdef RLIMIT_RTPRIO
+    pa_rlimit rlimit_rtprio;
+#endif
+#ifdef RLIMIT_RTTIME
+    pa_rlimit rlimit_rttime;
+#endif
+#endif
+
+    unsigned default_n_fragments, default_fragment_size_msec;
+    unsigned deferred_volume_safety_margin_usec;
+    int deferred_volume_extra_delay_usec;
+    unsigned lfe_crossover_freq;
+    pa_sample_spec default_sample_spec;
+    uint32_t alternate_sample_rate;
+    pa_channel_map default_channel_map;
+    size_t shm_size;
+} pa_daemon_conf;
+
+/* Allocate a new structure and fill it with sane defaults */
+pa_daemon_conf* pa_daemon_conf_new(void);
+void pa_daemon_conf_free(pa_daemon_conf*c);
+
+/* Load configuration data from the specified file overwriting the
+ * current settings in *c. If filename is NULL load the default daemon
+ * configuration file */
+int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename);
+
+/* Pretty print the current configuration data of the daemon. The
+ * returned string has to be freed manually. The output of this
+ * function may be parsed with pa_daemon_conf_load(). */
+char *pa_daemon_conf_dump(pa_daemon_conf *c);
+
+/* Load the configuration data from the process' environment
+ * overwriting the current settings in *c. */
+int pa_daemon_conf_env(pa_daemon_conf *c);
+
+/* Set these configuration variables in the structure by passing a string */
+int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string);
+int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string);
+int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
+int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string);
+
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c);
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
+
+#endif
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
new file mode 100644 (file)
index 0000000..a955523
--- /dev/null
@@ -0,0 +1,94 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for
+## more information. Default values are commented out.  Use either ; or # for
+## commenting.
+changequote(`[', `]')dnl Set up m4 quoting
+
+; daemonize = no
+; fail = yes
+; allow-module-loading = yes
+; allow-exit = yes
+; use-pid-file = yes
+; system-instance = no
+ifelse(@HAVE_DBUS@, 1, [dnl
+; local-server-type = user
+])dnl
+; enable-shm = yes
+; enable-memfd = yes
+; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
+; lock-memory = no
+; cpu-limit = no
+
+; high-priority = yes
+; nice-level = -11
+
+; realtime-scheduling = yes
+; realtime-priority = 5
+
+; exit-idle-time = 20
+; scache-idle-time = 20
+
+; dl-search-path = (depends on architecture)
+
+; load-default-script-file = yes
+; default-script-file = @PA_DEFAULT_CONFIG_DIR@/default.pa
+
+; log-target = auto
+; log-level = notice
+; log-meta = no
+; log-time = no
+; log-backtrace = 0
+
+; resample-method = speex-float-1
+; avoid-resampling = false
+; enable-remixing = yes
+; remixing-use-all-sink-channels = yes
+; enable-lfe-remixing = no
+; lfe-crossover-freq = 0
+
+; flat-volumes = yes
+
+ifelse(@HAVE_SYS_RESOURCE_H@, 1, [dnl
+; rlimit-fsize = -1
+; rlimit-data = -1
+; rlimit-stack = -1
+; rlimit-core = -1
+; rlimit-as = -1
+; rlimit-rss = -1
+; rlimit-nproc = -1
+; rlimit-nofile = 256
+; rlimit-memlock = -1
+; rlimit-locks = -1
+; rlimit-sigpending = -1
+; rlimit-msgqueue = -1
+; rlimit-nice = 31
+; rlimit-rtprio = 9
+; rlimit-rttime = 200000
+])dnl
+
+; default-sample-format = s16le
+; default-sample-rate = 44100
+; alternate-sample-rate = 48000
+; default-sample-channels = 2
+; default-channel-map = front-left,front-right
+
+; default-fragments = 4
+; default-fragment-size-msec = 25
+
+; enable-deferred-volume = yes
+; deferred-volume-safety-margin-usec = 8000
+; deferred-volume-extra-delay-usec = 0
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
new file mode 100755 (executable)
index 0000000..7a68653
--- /dev/null
@@ -0,0 +1,166 @@
+#!@PA_BINARY@ -nF
+#
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+# This startup script is used only if PulseAudio is started per-user
+# (i.e. not in system mode)
+changequote(`[', `]')dnl Set up m4 quoting
+
+.fail
+
+### Automatically restore the volume of streams and devices
+load-module module-device-restore
+load-module module-stream-restore
+load-module module-card-restore
+
+### Automatically augment property information from .desktop files
+### stored in /usr/share/application
+load-module module-augment-properties
+
+### Should be after module-*-restore but before module-*-detect
+load-module module-switch-on-port-available
+
+### Load audio drivers statically
+### (it's probably better to not load these drivers manually, but instead
+### use module-udev-detect -- see below -- for doing this automatically)
+ifelse(@HAVE_ALSA@, 1, [dnl
+#load-module module-alsa-sink
+#load-module module-alsa-source device=hw:1,0
+])dnl
+ifelse(@HAVE_OSS_OUTPUT@, 1, [dnl
+#load-module module-oss device="/dev/dsp" sink_name=output source_name=input
+#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
+])dnl
+ifelse(@HAVE_WAVEOUT@, 1, [dnl
+load-module module-waveout sink_name=output source_name=input
+])dnl
+#load-module module-null-sink
+ifelse(@HAVE_MKFIFO@, 1, [dnl
+#load-module module-pipe-sink
+])dnl
+
+### Automatically load driver modules depending on the hardware available
+ifelse(@HAVE_UDEV@, 1, [dnl
+.ifexists module-udev-detect@PA_SOEXT@
+load-module module-udev-detect
+.else
+], @HAVE_COREAUDIO@, 1, [dnl
+.ifexists module-coreaudio-detect@PA_SOEXT@
+load-module module-coreaudio-detect
+.else
+], [dnl
+.ifexists module-detect@PA_SOEXT@
+])dnl
+### Use the static hardware detection module (for systems that lack udev support)
+load-module module-detect
+.endif
+
+### Automatically connect sink and source if JACK server is present
+.ifexists module-jackdbus-detect@PA_SOEXT@
+.nofail
+load-module module-jackdbus-detect channels=2
+.fail
+.endif
+
+ifelse(@HAVE_BLUEZ@, 1, [dnl
+### Automatically load driver modules for Bluetooth hardware
+.ifexists module-bluetooth-policy@PA_SOEXT@
+load-module module-bluetooth-policy
+.endif
+
+.ifexists module-bluetooth-discover@PA_SOEXT@
+load-module module-bluetooth-discover
+.endif
+])dnl
+
+ifelse(@HAVE_AF_UNIX@, 1, [dnl
+### Load several protocols
+.ifexists module-esound-protocol-unix@PA_SOEXT@
+load-module module-esound-protocol-unix
+.endif
+load-module module-native-protocol-unix
+])dnl
+
+### Network access (may be configured with paprefs, so leave this commented
+### here if you plan to use paprefs)
+#load-module module-esound-protocol-tcp
+#load-module module-native-protocol-tcp
+ifelse(@HAVE_AVAHI@, 1, [dnl
+#load-module module-zeroconf-publish
+])dnl
+
+ifelse(@OS_IS_WIN32@, 0, [dnl
+### Load the RTP receiver module (also configured via paprefs, see above)
+#load-module module-rtp-recv
+
+### Load the RTP sender module (also configured via paprefs, see above)
+#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'"
+#load-module module-rtp-send source=rtp.monitor
+
+### Load additional modules from GConf settings. This can be configured with the paprefs tool.
+### Please keep in mind that the modules configured by paprefs might conflict with manually
+### loaded modules.
+.ifexists module-gconf@PA_SOEXT@
+.nofail
+load-module module-gconf
+.fail
+.endif
+
+### Automatically restore the default sink/source when changed by the user
+### during runtime
+### NOTE: This should be loaded as early as possible so that subsequent modules
+### that look up the default sink/source get the right value
+load-module module-default-device-restore
+
+### Automatically move streams to the default sink if the sink they are
+### connected to dies, similar for sources
+load-module module-rescue-streams
+
+### Make sure we always have a sink around, even if it is a null sink.
+load-module module-always-sink
+
+### Honour intended role device property
+load-module module-intended-roles
+
+### Automatically suspend sinks/sources that become idle for too long
+load-module module-suspend-on-idle
+
+### If autoexit on idle is enabled we want to make sure we only quit
+### when no local session needs us anymore.
+.ifexists module-console-kit@PA_SOEXT@
+load-module module-console-kit
+.endif
+.ifexists module-systemd-login@PA_SOEXT@
+load-module module-systemd-login
+.endif
+
+### Enable positioned event sounds
+load-module module-position-event-sounds
+
+### Cork music/video streams when a phone stream is active
+load-module module-role-cork
+
+### Modules to allow autoloading of filters (such as echo cancellation)
+### on demand. module-filter-heuristics tries to determine what filters
+### make sense, and module-filter-apply does the heavy-lifting of
+### loading modules and rerouting streams.
+load-module module-filter-heuristics
+load-module module-filter-apply
+])dnl
+
+### Make some devices default
+#set-default-sink output
+#set-default-source input
diff --git a/src/daemon/dumpmodules.c b/src/daemon/dumpmodules.c
new file mode 100644 (file)
index 0000000..8410bbc
--- /dev/null
@@ -0,0 +1,154 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <ltdl.h>
+
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/modinfo.h>
+
+#include "dumpmodules.h"
+
+#define PREFIX "module-"
+
+static void short_info(const char *name, const char *path, pa_modinfo *i) {
+    pa_assert(name);
+    pa_assert(i);
+
+    printf("%-40s%s\n", name, i->description ? i->description : "n/a");
+}
+
+static void long_info(const char *name, const char *path, pa_modinfo *i) {
+    static int nl = 0;
+    pa_assert(name);
+    pa_assert(i);
+
+    if (nl)
+        printf("\n");
+
+    nl = 1;
+
+    printf(_("Name: %s\n"), name);
+
+    if (!i->description && !i->version && !i->author && !i->usage)
+        printf(_("No module information available\n"));
+    else {
+        if (i->version)
+            printf(_("Version: %s\n"), i->version);
+        if (i->description)
+            printf(_("Description: %s\n"), i->description);
+        if (i->author)
+            printf(_("Author: %s\n"), i->author);
+        if (i->usage)
+            printf(_("Usage: %s\n"), i->usage);
+        printf(_("Load Once: %s\n"), pa_yes_no(i->load_once));
+        if (i->deprecated)
+            printf(_("DEPRECATION WARNING: %s\n"), i->deprecated);
+    }
+
+    if (path)
+        printf(_("Path: %s\n"), path);
+}
+
+static void show_info(const char *name, const char *path, void (*info)(const char *name, const char *path, pa_modinfo*i)) {
+    pa_modinfo *i;
+
+    pa_assert(name);
+
+    if ((i = pa_modinfo_get_by_name(path ? path : name))) {
+        info(name, path, i);
+        pa_modinfo_free(i);
+    }
+}
+
+static int is_preloaded(const char *name) {
+    const lt_dlsymlist *l;
+
+    for (l = lt_preloaded_symbols; l->name; l++) {
+        char buf[64], *e;
+
+        if (l->address)
+            continue;
+
+        pa_snprintf(buf, sizeof(buf), "%s", l->name);
+        if ((e = strrchr(buf, '.')))
+            *e = 0;
+
+        if (pa_streq(name, buf))
+            return 1;
+    }
+
+    return 0;
+}
+
+static int callback(const char *path, lt_ptr data) {
+    const char *e;
+    pa_daemon_conf *c = (data);
+
+    e = pa_path_get_filename(path);
+
+    if (strlen(e) <= sizeof(PREFIX)-1 || strncmp(e, PREFIX, sizeof(PREFIX)-1))
+        return 0;
+
+    if (is_preloaded(e))
+        return 0;
+
+    show_info(e, path, c->log_level >= PA_LOG_INFO ? long_info : short_info);
+    return 0;
+}
+
+void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) {
+    pa_assert(c);
+
+    if (argc > 0) {
+        int i;
+        for (i = 0; i < argc; i++)
+            show_info(argv[i], NULL, long_info);
+    } else {
+        const lt_dlsymlist *l;
+
+        for (l = lt_preloaded_symbols; l->name; l++) {
+            char buf[64], *e;
+
+            if (l->address)
+                continue;
+
+            if (strlen(l->name) <= sizeof(PREFIX)-1 || strncmp(l->name, PREFIX, sizeof(PREFIX)-1))
+                continue;
+
+            pa_snprintf(buf, sizeof(buf), "%s", l->name);
+            if ((e = strrchr(buf, '.')))
+                *e = 0;
+
+            show_info(buf, NULL, c->log_level >= PA_LOG_INFO ? long_info : short_info);
+        }
+
+        lt_dlforeachfile(NULL, callback, c);
+    }
+}
diff --git a/src/daemon/dumpmodules.h b/src/daemon/dumpmodules.h
new file mode 100644 (file)
index 0000000..289f35d
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef foodumpmoduleshfoo
+#define foodumpmoduleshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "daemon-conf.h"
+
+/* Dump all available modules to STDOUT. If argc > 0 print information
+ * about the modules specified in argv[] instead. */
+void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]);
+
+#endif
diff --git a/src/daemon/esdcompat.in b/src/daemon/esdcompat.in
new file mode 100755 (executable)
index 0000000..361ca63
--- /dev/null
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+VERSION_STRING="@PACKAGE_NAME@ esd wrapper @PACKAGE_VERSION@"
+
+fail() {
+    echo "ERROR: $1"
+    exit 1
+}
+
+ARGS=" --log-target=syslog"
+
+while [ "$#" -gt "0" ]; do
+
+    case "$1" in
+        "")
+            ;;
+
+        -v|--version)
+            echo "$VERSION_STRING"
+            exit 0
+            ;;
+
+        -h|--help)
+            cat <<EOF
+$VERSION_STRING
+
+Usage: $0 [options]
+
+  -v --version  print version information
+  -h --help     show this help
+
+Ignored directives:
+
+  -tcp          use tcp/ip sockets in addition to unix domain
+  -promiscuous  don't require authentication
+  -d DEVICE     force esd to use sound device DEVICE
+  -b            run server in 8 bit sound mode
+  -r RATE       run server at sample rate of RATE
+  -as SECS      free audio device after SECS of inactivity
+  -unix         use unix domain sockets instead of tcp/ip
+  -public       make tcp/ip access public (other than localhost)
+  -terminate    terminate esd daemone after last client exits
+  -nobeeps      disable startup beeps
+  -trust        start esd even if use of /tmp/.esd can be insecure
+  -port PORT    listen for connections at PORT (only for tcp/ip)
+  -bind ADDRESS binds to ADDRESS (only for tcp/ip)
+EOF
+            exit 0
+            ;;
+
+        -spawnpid)
+            shift
+            ARGS="$ARGS '-Lmodule-esound-compat-spawnpid pid=$1'"
+            ;;
+
+        -spawnfd)
+            shift
+            ARGS="$ARGS '-Lmodule-esound-compat-spawnfd fd=$1'"
+            ;;
+        
+        -unix|-b|-public|-terminate|-nobeeps|-trust|-tcp|-promiscuous)  
+            # Ignore these commands
+            ;; 
+
+        -d|-r|-as|-port|-bind)
+            # Ignore these commands and their arguments
+            shift
+
+            ;;
+
+        *)
+            fail "Unknown command: $1"
+            ;;
+    esac
+
+    shift
+done
+
+eval "exec '@PA_BINARY@'$ARGS"
diff --git a/src/daemon/ltdl-bind-now.c b/src/daemon/ltdl-bind-now.c
new file mode 100644 (file)
index 0000000..3492571
--- /dev/null
@@ -0,0 +1,159 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#ifdef HAVE_SYS_DL_H
+#include <sys/dl.h>
+#endif
+
+#include <string.h>
+
+#include <ltdl.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+
+#include "ltdl-bind-now.h"
+
+#ifdef RTLD_NOW
+#define PA_BIND_NOW RTLD_NOW
+#elif defined(DL_NOW)
+#define PA_BIND_NOW DL_NOW
+#else
+#undef PA_BIND_NOW
+#endif
+
+#ifdef OS_IS_WIN32
+#undef PA_BIND_NOW
+#endif
+
+#ifdef PA_BIND_NOW
+
+/*
+  To avoid lazy relocations during runtime in our RT threads we add
+  our own shared object loader with uses RTLD_NOW if it is
+  available. The standard ltdl loader prefers RTLD_LAZY.
+
+  Please note that this loader doesn't have any influence on
+  relocations on any libraries that are already loaded into our
+  process, i.e. because the pulseaudio binary links directly to
+  them. To disable lazy relocations for those libraries it is possible
+  to set $LT_BIND_NOW before starting the pulseaudio binary.
+*/
+
+static lt_module bind_now_open(lt_user_data d, const char *fname, lt_dladvise advise) {
+    lt_module m;
+
+    pa_assert(fname);
+
+    if (!(m = dlopen(fname, PA_BIND_NOW))) {
+        pa_log(_("Failed to open module %s: %s"), fname, dlerror());
+        lt_dlseterror(LT_ERROR_CANNOT_OPEN);
+        return NULL;
+    }
+
+    return m;
+}
+
+static int bind_now_close(lt_user_data d, lt_module m) {
+
+    pa_assert(m);
+
+    if (dlclose(m) != 0) {
+        lt_dlseterror(LT_ERROR_CANNOT_CLOSE);
+        return 1;
+    }
+
+    return 0;
+}
+
+static lt_ptr bind_now_find_sym(lt_user_data d, lt_module m, const char *symbol) {
+    lt_ptr ptr;
+
+    pa_assert(m);
+    pa_assert(symbol);
+
+    if (!(ptr = dlsym(m, symbol))) {
+        lt_dlseterror(LT_ERROR_SYMBOL_NOT_FOUND);
+        return NULL;
+    }
+
+    return ptr;
+}
+
+static lt_dlvtable *bindnow_loader = NULL;
+#endif
+
+void pa_ltdl_init(void) {
+
+#ifdef PA_BIND_NOW
+    const lt_dlvtable *dlopen_loader;
+#endif
+
+    pa_assert_se(lt_dlinit() == 0);
+
+#ifdef PA_BIND_NOW
+    /* Already initialised */
+    if (bindnow_loader)
+        return;
+
+    if (!(dlopen_loader = lt_dlloader_find((char*) "lt_dlopen"))) {
+        pa_log_warn(_("Failed to find original lt_dlopen loader."));
+        return;
+    }
+
+    if (!(bindnow_loader = malloc(sizeof(lt_dlvtable)))) {
+        pa_log_error(_("Failed to allocate new dl loader."));
+        return;
+    }
+
+    memcpy(bindnow_loader, dlopen_loader, sizeof(*bindnow_loader));
+    bindnow_loader->name = "bind-now-loader";
+    bindnow_loader->module_open = bind_now_open;
+    bindnow_loader->module_close = bind_now_close;
+    bindnow_loader->find_sym = bind_now_find_sym;
+    bindnow_loader->priority = LT_DLLOADER_PREPEND;
+
+    /* Add our BIND_NOW loader as the default module loader. */
+    if (lt_dlloader_add(bindnow_loader) != 0) {
+        pa_log_warn(_("Failed to add bind-now-loader."));
+        free(bindnow_loader);
+        bindnow_loader = NULL;
+    }
+#endif
+}
+
+void pa_ltdl_done(void) {
+    pa_assert_se(lt_dlexit() == 0);
+
+#ifdef PA_BIND_NOW
+    /* lt_dlexit() will free our loader vtable, hence reset our
+     * pointer to it here */
+    bindnow_loader = NULL;
+#endif
+}
diff --git a/src/daemon/ltdl-bind-now.h b/src/daemon/ltdl-bind-now.h
new file mode 100644 (file)
index 0000000..bb6d83f
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef foopulsecoreltdlbindnowhfoo
+#define foopulsecoreltdlbindnowhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+void pa_ltdl_init(void);
+void pa_ltdl_done(void);
+
+#endif
+
diff --git a/src/daemon/main.c b/src/daemon/main.c
new file mode 100644 (file)
index 0000000..f35252d
--- /dev/null
@@ -0,0 +1,1219 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stddef.h>
+#include <ltdl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <locale.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_LIBWRAP
+#include <syslog.h>
+#include <tcpd.h>
+#endif
+
+#ifdef HAVE_DBUS
+#include <dbus/dbus.h>
+#endif
+
+#ifdef HAVE_SYSTEMD_DAEMON
+#include <systemd/sd-daemon.h>
+#endif
+
+#include <pulse/client-conf.h>
+#include <pulse/mainloop.h>
+#include <pulse/mainloop-signal.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/lock-autospawn.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/cli-command.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sioman.h>
+#include <pulsecore/cli-text.h>
+#include <pulsecore/pid.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/shm.h>
+#include <pulsecore/memtrap.h>
+#include <pulsecore/strlist.h>
+#ifdef HAVE_DBUS
+#include <pulsecore/dbus-shared.h>
+#endif
+#include <pulsecore/cpu.h>
+
+#include "cmdline.h"
+#include "cpulimit.h"
+#include "daemon-conf.h"
+#include "dumpmodules.h"
+#include "caps.h"
+#include "ltdl-bind-now.h"
+#include "server-lookup.h"
+
+#ifdef HAVE_LIBWRAP
+/* Only one instance of these variables */
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif
+
+#ifdef HAVE_OSS_WRAPPER
+/* padsp looks for this symbol in the running process and disables
+ * itself if it finds it and it is set to 7 (which is actually a bit
+ * mask). For details see padsp. */
+int __padsp_disabled__ = 7;
+#endif
+
+static void signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
+    pa_log_info("Got signal %s.", pa_sig2str(sig));
+
+    switch (sig) {
+#ifdef SIGUSR1
+        case SIGUSR1:
+            pa_module_load(userdata, "module-cli", NULL);
+            break;
+#endif
+
+#ifdef SIGUSR2
+        case SIGUSR2:
+            pa_module_load(userdata, "module-cli-protocol-unix", NULL);
+            break;
+#endif
+
+#ifdef SIGHUP
+        case SIGHUP: {
+            char *c = pa_full_status_string(userdata);
+            pa_log_notice("%s", c);
+            pa_xfree(c);
+            return;
+        }
+#endif
+
+        case SIGINT:
+        case SIGTERM:
+        default:
+            pa_log_info("Exiting.");
+            m->quit(m, 0);
+            break;
+    }
+}
+
+#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
+
+static int change_user(void) {
+    struct passwd *pw;
+    struct group * gr;
+    int r;
+
+    /* This function is called only in system-wide mode. It creates a
+     * runtime dir in /var/run/ with proper UID/GID and drops privs
+     * afterwards. */
+
+    if (!(pw = getpwnam(PA_SYSTEM_USER))) {
+        pa_log(_("Failed to find user '%s'."), PA_SYSTEM_USER);
+        return -1;
+    }
+
+    if (!(gr = getgrnam(PA_SYSTEM_GROUP))) {
+        pa_log(_("Failed to find group '%s'."), PA_SYSTEM_GROUP);
+        return -1;
+    }
+
+    pa_log_info("Found user '%s' (UID %lu) and group '%s' (GID %lu).",
+                PA_SYSTEM_USER, (unsigned long) pw->pw_uid,
+                PA_SYSTEM_GROUP, (unsigned long) gr->gr_gid);
+
+    if (pw->pw_gid != gr->gr_gid) {
+        pa_log(_("GID of user '%s' and of group '%s' don't match."), PA_SYSTEM_USER, PA_SYSTEM_GROUP);
+        return -1;
+    }
+
+    if (!pa_streq(pw->pw_dir, PA_SYSTEM_RUNTIME_PATH))
+        pa_log_warn(_("Home directory of user '%s' is not '%s', ignoring."), PA_SYSTEM_USER, PA_SYSTEM_RUNTIME_PATH);
+
+    if (pa_make_secure_dir(PA_SYSTEM_RUNTIME_PATH, 0755, pw->pw_uid, gr->gr_gid, true) < 0) {
+        pa_log(_("Failed to create '%s': %s"), PA_SYSTEM_RUNTIME_PATH, pa_cstrerror(errno));
+        return -1;
+    }
+
+    if (pa_make_secure_dir(PA_SYSTEM_STATE_PATH, 0700, pw->pw_uid, gr->gr_gid, true) < 0) {
+        pa_log(_("Failed to create '%s': %s"), PA_SYSTEM_STATE_PATH, pa_cstrerror(errno));
+        return -1;
+    }
+
+    /* We don't create the config dir here, because we don't need to write to it */
+
+    if (initgroups(PA_SYSTEM_USER, gr->gr_gid) != 0) {
+        pa_log(_("Failed to change group list: %s"), pa_cstrerror(errno));
+        return -1;
+    }
+
+#if defined(HAVE_SETRESGID)
+    r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid);
+#elif defined(HAVE_SETEGID)
+    if ((r = setgid(gr->gr_gid)) >= 0)
+        r = setegid(gr->gr_gid);
+#elif defined(HAVE_SETREGID)
+    r = setregid(gr->gr_gid, gr->gr_gid);
+#else
+#error "No API to drop privileges"
+#endif
+
+    if (r < 0) {
+        pa_log(_("Failed to change GID: %s"), pa_cstrerror(errno));
+        return -1;
+    }
+
+#if defined(HAVE_SETRESUID)
+    r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid);
+#elif defined(HAVE_SETEUID)
+    if ((r = setuid(pw->pw_uid)) >= 0)
+        r = seteuid(pw->pw_uid);
+#elif defined(HAVE_SETREUID)
+    r = setreuid(pw->pw_uid, pw->pw_uid);
+#else
+#error "No API to drop privileges"
+#endif
+
+    if (r < 0) {
+        pa_log(_("Failed to change UID: %s"), pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_drop_caps();
+
+    pa_set_env("USER", PA_SYSTEM_USER);
+    pa_set_env("USERNAME", PA_SYSTEM_USER);
+    pa_set_env("LOGNAME", PA_SYSTEM_USER);
+    pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
+
+    /* Relevant for pa_runtime_path() */
+    if (!getenv("PULSE_RUNTIME_PATH"))
+        pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
+
+    if (!getenv("PULSE_CONFIG_PATH"))
+        pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_CONFIG_PATH);
+
+    if (!getenv("PULSE_STATE_PATH"))
+        pa_set_env("PULSE_STATE_PATH", PA_SYSTEM_STATE_PATH);
+
+    pa_log_info("Successfully changed user to \"" PA_SYSTEM_USER "\".");
+
+    return 0;
+}
+
+#else /* HAVE_PWD_H && HAVE_GRP_H */
+
+static int change_user(void) {
+    pa_log(_("System wide mode unsupported on this platform."));
+    return -1;
+}
+
+#endif /* HAVE_PWD_H && HAVE_GRP_H */
+
+#ifdef HAVE_SYS_RESOURCE_H
+
+static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
+    struct rlimit rl;
+    pa_assert(r);
+
+    if (!r->is_set)
+        return 0;
+
+    rl.rlim_cur = rl.rlim_max = r->value;
+
+    if (setrlimit(resource, &rl) < 0) {
+        pa_log_info("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+static void set_all_rlimits(const pa_daemon_conf *conf) {
+    set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE");
+    set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
+    set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK");
+    set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
+#ifdef RLIMIT_RSS
+    set_one_rlimit(&conf->rlimit_rss, RLIMIT_RSS, "RLIMIT_RSS");
+#endif
+#ifdef RLIMIT_NPROC
+    set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
+#endif
+#ifdef RLIMIT_NOFILE
+    set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
+#endif
+#ifdef RLIMIT_MEMLOCK
+    set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK");
+#endif
+#ifdef RLIMIT_AS
+    set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
+#endif
+#ifdef RLIMIT_LOCKS
+    set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS");
+#endif
+#ifdef RLIMIT_SIGPENDING
+    set_one_rlimit(&conf->rlimit_sigpending, RLIMIT_SIGPENDING, "RLIMIT_SIGPENDING");
+#endif
+#ifdef RLIMIT_MSGQUEUE
+    set_one_rlimit(&conf->rlimit_msgqueue, RLIMIT_MSGQUEUE, "RLIMIT_MSGQUEUE");
+#endif
+#ifdef RLIMIT_NICE
+    set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE");
+#endif
+#ifdef RLIMIT_RTPRIO
+    set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO");
+#endif
+#ifdef RLIMIT_RTTIME
+    set_one_rlimit(&conf->rlimit_rttime, RLIMIT_RTTIME, "RLIMIT_RTTIME");
+#endif
+}
+#endif
+
+static char *check_configured_address(void) {
+    char *default_server = NULL;
+    pa_client_conf *c = pa_client_conf_new();
+
+    pa_client_conf_load(c, true, true);
+
+    if (c->default_server && *c->default_server)
+        default_server = pa_xstrdup(c->default_server);
+
+    pa_client_conf_free(c);
+
+    return default_server;
+}
+
+#ifdef HAVE_DBUS
+static pa_dbus_connection *register_dbus_name(pa_core *c, DBusBusType bus, const char* name) {
+    DBusError error;
+    pa_dbus_connection *conn;
+
+    dbus_error_init(&error);
+
+    if (!(conn = pa_dbus_bus_get(c, bus, &error)) || dbus_error_is_set(&error)) {
+        pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    if (dbus_bus_request_name(pa_dbus_connection_get(conn), name, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+        pa_log_debug("Got %s!", name);
+        return conn;
+    }
+
+    if (dbus_error_is_set(&error))
+        pa_log_error("Failed to acquire %s: %s: %s", name, error.name, error.message);
+    else
+        pa_log_error("D-Bus name %s already taken.", name);
+
+    /* PA cannot be started twice by the same user and hence we can
+     * ignore mostly the case that a name is already taken. */
+
+fail:
+    if (conn)
+        pa_dbus_connection_unref(conn);
+
+    dbus_error_free(&error);
+    return NULL;
+}
+#endif
+
+int main(int argc, char *argv[]) {
+    pa_core *c = NULL;
+    pa_strbuf *buf = NULL;
+    pa_daemon_conf *conf = NULL;
+    pa_mainloop *mainloop = NULL;
+    char *s;
+    char *configured_address;
+    int r = 0, retval = 1, d = 0;
+    bool valid_pid_file = false;
+    bool ltdl_init = false;
+    int n_fds = 0, *passed_fds = NULL;
+    const char *e;
+#ifdef HAVE_FORK
+    int daemon_pipe[2] = { -1, -1 };
+    int daemon_pipe2[2] = { -1, -1 };
+#endif
+    int autospawn_fd = -1;
+    bool autospawn_locked = false;
+#ifdef HAVE_DBUS
+    pa_dbusobj_server_lookup *server_lookup = NULL; /* /org/pulseaudio/server_lookup */
+    pa_dbus_connection *lookup_service_bus = NULL; /* Always the user bus. */
+    pa_dbus_connection *server_bus = NULL; /* The bus where we reserve org.pulseaudio.Server, either the user or the system bus. */
+    bool start_server;
+#endif
+
+    pa_log_set_ident("pulseaudio");
+    pa_log_set_level(PA_LOG_NOTICE);
+    pa_log_set_flags(PA_LOG_COLORS|PA_LOG_PRINT_FILE|PA_LOG_PRINT_LEVEL, PA_LOG_RESET);
+
+#if defined(__linux__) && defined(__OPTIMIZE__)
+    /*
+       Disable lazy relocations to make usage of external libraries
+       more deterministic for our RT threads. We abuse __OPTIMIZE__ as
+       a check whether we are a debug build or not. This all is
+       admittedly a bit snake-oilish.
+    */
+
+    if (!getenv("LD_BIND_NOW")) {
+        char *rp;
+        char *canonical_rp;
+
+        /* We have to execute ourselves, because the libc caches the
+         * value of $LD_BIND_NOW on initialization. */
+
+        pa_set_env("LD_BIND_NOW", "1");
+
+        if ((canonical_rp = pa_realpath(PA_BINARY))) {
+
+            if ((rp = pa_readlink("/proc/self/exe"))) {
+
+                if (pa_streq(rp, canonical_rp))
+                    pa_assert_se(execv(rp, argv) == 0);
+                else
+                    pa_log_warn("/proc/self/exe does not point to %s, cannot self execute. Are you playing games?", canonical_rp);
+
+                pa_xfree(rp);
+
+            } else
+                pa_log_warn("Couldn't read /proc/self/exe, cannot self execute. Running in a chroot()?");
+
+            pa_xfree(canonical_rp);
+
+        } else
+            pa_log_warn("Couldn't canonicalize binary path, cannot self execute.");
+    }
+#endif
+
+#ifdef HAVE_SYSTEMD_DAEMON
+    n_fds = sd_listen_fds(0);
+    if (n_fds > 0) {
+        int i = n_fds;
+
+        passed_fds = pa_xnew(int, n_fds+2);
+        passed_fds[n_fds] = passed_fds[n_fds+1] = -1;
+        while (i--)
+            passed_fds[i] = SD_LISTEN_FDS_START + i;
+    }
+#endif
+
+    if (!passed_fds) {
+        n_fds = 0;
+        passed_fds = pa_xnew(int, 2);
+        passed_fds[0] = passed_fds[1] = -1;
+    }
+
+    if ((e = getenv("PULSE_PASSED_FD"))) {
+        int passed_fd = atoi(e);
+        if (passed_fd > 2)
+            passed_fds[n_fds] = passed_fd;
+    }
+
+    /* We might be autospawned, in which case have no idea in which
+     * context we have been started. Let's cleanup our execution
+     * context as good as possible */
+
+    pa_reset_personality();
+    pa_drop_root();
+    pa_close_allv(passed_fds);
+    pa_xfree(passed_fds);
+    pa_reset_sigs(-1);
+    pa_unblock_sigs(-1);
+    pa_reset_priority();
+
+    setlocale(LC_ALL, "");
+    pa_init_i18n();
+
+    conf = pa_daemon_conf_new();
+
+    if (pa_daemon_conf_load(conf, NULL) < 0)
+        goto finish;
+
+    if (pa_daemon_conf_env(conf) < 0)
+        goto finish;
+
+    if (pa_cmdline_parse(conf, argc, argv, &d) < 0) {
+        pa_log(_("Failed to parse command line."));
+        goto finish;
+    }
+
+    if (conf->log_target)
+        pa_log_set_target(conf->log_target);
+    else {
+        pa_log_target target = { .type = PA_LOG_STDERR, .file = NULL };
+        pa_log_set_target(&target);
+    }
+
+    pa_log_set_level(conf->log_level);
+    if (conf->log_meta)
+        pa_log_set_flags(PA_LOG_PRINT_META, PA_LOG_SET);
+    if (conf->log_time)
+        pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET);
+    pa_log_set_show_backtrace(conf->log_backtrace);
+
+#ifdef HAVE_DBUS
+    /* conf->system_instance and conf->local_server_type control almost the
+     * same thing; make them agree about what is requested. */
+    switch (conf->local_server_type) {
+        case PA_SERVER_TYPE_UNSET:
+            conf->local_server_type = conf->system_instance ? PA_SERVER_TYPE_SYSTEM : PA_SERVER_TYPE_USER;
+            break;
+        case PA_SERVER_TYPE_USER:
+        case PA_SERVER_TYPE_NONE:
+            conf->system_instance = false;
+            break;
+        case PA_SERVER_TYPE_SYSTEM:
+            conf->system_instance = true;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    start_server = conf->local_server_type == PA_SERVER_TYPE_USER || (getuid() == 0 && conf->local_server_type == PA_SERVER_TYPE_SYSTEM);
+
+    if (!start_server && conf->local_server_type == PA_SERVER_TYPE_SYSTEM) {
+        pa_log_notice(_("System mode refused for non-root user. Only starting the D-Bus server lookup service."));
+        conf->system_instance = false;
+    }
+#endif
+
+    LTDL_SET_PRELOADED_SYMBOLS();
+    pa_ltdl_init();
+    ltdl_init = true;
+
+    if (conf->dl_search_path)
+        lt_dlsetsearchpath(conf->dl_search_path);
+
+#ifdef OS_IS_WIN32
+    {
+        WSADATA data;
+        WSAStartup(MAKEWORD(2, 0), &data);
+    }
+#endif
+
+    pa_random_seed();
+
+    switch (conf->cmd) {
+        case PA_CMD_DUMP_MODULES:
+            pa_dump_modules(conf, argc-d, argv+d);
+            retval = 0;
+            goto finish;
+
+        case PA_CMD_DUMP_CONF: {
+
+            if (d < argc) {
+                pa_log("Too many arguments.");
+                goto finish;
+            }
+
+            s = pa_daemon_conf_dump(conf);
+            fputs(s, stdout);
+            pa_xfree(s);
+            retval = 0;
+            goto finish;
+        }
+
+        case PA_CMD_DUMP_RESAMPLE_METHODS: {
+            int i;
+
+            if (d < argc) {
+                pa_log("Too many arguments.");
+                goto finish;
+            }
+
+            for (i = 0; i < PA_RESAMPLER_MAX; i++)
+                if (pa_resample_method_supported(i))
+                    printf("%s\n", pa_resample_method_to_string(i));
+
+            retval = 0;
+            goto finish;
+        }
+
+        case PA_CMD_HELP :
+            pa_cmdline_help(argv[0]);
+            retval = 0;
+            goto finish;
+
+        case PA_CMD_VERSION :
+
+            if (d < argc) {
+                pa_log("Too many arguments.");
+                goto finish;
+            }
+
+            printf(PACKAGE_NAME" "PACKAGE_VERSION"\n");
+            retval = 0;
+            goto finish;
+
+        case PA_CMD_CHECK: {
+            pid_t pid;
+
+            if (d < argc) {
+                pa_log("Too many arguments.");
+                goto finish;
+            }
+
+            if (pa_pid_file_check_running(&pid, "pulseaudio") < 0)
+                pa_log_info("Daemon not running");
+            else {
+                pa_log_info("Daemon running as PID %u", pid);
+                retval = 0;
+            }
+
+            goto finish;
+
+        }
+        case PA_CMD_KILL:
+
+            if (d < argc) {
+                pa_log("Too many arguments.");
+                goto finish;
+            }
+
+            if (pa_pid_file_kill(SIGINT, NULL, "pulseaudio") < 0)
+                pa_log(_("Failed to kill daemon: %s"), pa_cstrerror(errno));
+            else
+                retval = 0;
+
+            goto finish;
+
+        case PA_CMD_CLEANUP_SHM:
+
+            if (d < argc) {
+                pa_log("Too many arguments.");
+                goto finish;
+            }
+
+            if (pa_shm_cleanup() >= 0)
+                retval = 0;
+
+            goto finish;
+
+        default:
+            pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START);
+    }
+
+    if (d < argc) {
+        pa_log("Too many arguments.");
+        goto finish;
+    }
+
+#ifdef HAVE_GETUID
+    if (getuid() == 0 && !conf->system_instance)
+        pa_log_warn(_("This program is not intended to be run as root (unless --system is specified)."));
+#ifndef HAVE_DBUS /* A similar, only a notice worthy check was done earlier, if D-Bus is enabled. */
+    else if (getuid() != 0 && conf->system_instance) {
+        pa_log(_("Root privileges required."));
+        goto finish;
+    }
+#endif
+#endif  /* HAVE_GETUID */
+
+    if (conf->cmd == PA_CMD_START && conf->system_instance) {
+        pa_log(_("--start not supported for system instances."));
+        goto finish;
+    }
+
+    if (conf->cmd == PA_CMD_START && (configured_address = check_configured_address())) {
+        /* There is an server address in our config, but where did it come from?
+         * By default a standard X11 login will load module-x11-publish which will
+         * inject PULSE_SERVER X11 property. If the PA daemon crashes, we will end
+         * up hitting this code path. So we have to check to see if our configured_address
+         * is the same as the value that would go into this property so that we can
+         * recover (i.e. autospawn) from a crash.
+         */
+        char *ufn;
+        bool start_anyway = false;
+
+        if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
+            char *id;
+
+            if ((id = pa_machine_id())) {
+                pa_strlist *server_list;
+                char formatted_ufn[256];
+
+                pa_snprintf(formatted_ufn, sizeof(formatted_ufn), "{%s}unix:%s", id, ufn);
+                pa_xfree(id);
+
+                if ((server_list = pa_strlist_parse(configured_address))) {
+                    char *u = NULL;
+
+                    /* We only need to check the first server */
+                    server_list = pa_strlist_pop(server_list, &u);
+                    pa_strlist_free(server_list);
+
+                    start_anyway = (u && pa_streq(formatted_ufn, u));
+                    pa_xfree(u);
+                }
+            }
+            pa_xfree(ufn);
+        }
+
+        if (!start_anyway) {
+            pa_log_notice(_("User-configured server at %s, refusing to start/autospawn."), configured_address);
+            pa_xfree(configured_address);
+            retval = 0;
+            goto finish;
+        }
+
+        pa_log_notice(_("User-configured server at %s, which appears to be local. Probing deeper."), configured_address);
+        pa_xfree(configured_address);
+    }
+
+    if (conf->system_instance && !conf->disallow_exit)
+        pa_log_warn(_("Running in system mode, but --disallow-exit not set."));
+
+    if (conf->system_instance && !conf->disallow_module_loading)
+        pa_log_warn(_("Running in system mode, but --disallow-module-loading not set."));
+
+    if (conf->system_instance && !conf->disable_shm) {
+        pa_log_notice(_("Running in system mode, forcibly disabling SHM mode."));
+        conf->disable_shm = true;
+    }
+
+    if (conf->system_instance && conf->exit_idle_time >= 0) {
+        pa_log_notice(_("Running in system mode, forcibly disabling exit idle time."));
+        conf->exit_idle_time = -1;
+    }
+
+    if (conf->cmd == PA_CMD_START) {
+        /* If we shall start PA only when it is not running yet, we
+         * first take the autospawn lock to make things
+         * synchronous. */
+
+        /* This locking and thread synchronisation code doesn't work reliably
+         * on kFreeBSD (Debian bug #705435), or in upstream FreeBSD ports
+         * (bug reference: ports/128947, patched in SVN r231972). */
+#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
+        if ((autospawn_fd = pa_autospawn_lock_init()) < 0) {
+            pa_log("Failed to initialize autospawn lock");
+            goto finish;
+        }
+
+        if ((pa_autospawn_lock_acquire(true) < 0)) {
+            pa_log("Failed to acquire autospawn lock");
+            goto finish;
+        }
+
+        autospawn_locked = true;
+#endif
+    }
+
+    if (conf->daemonize) {
+#ifdef HAVE_FORK
+        pid_t child;
+#endif
+
+        if (pa_stdio_acquire() < 0) {
+            pa_log(_("Failed to acquire stdio."));
+            goto finish;
+        }
+
+#ifdef HAVE_FORK
+        if (pipe(daemon_pipe) < 0) {
+            pa_log(_("pipe() failed: %s"), pa_cstrerror(errno));
+            goto finish;
+        }
+
+        if ((child = fork()) < 0) {
+            pa_log(_("fork() failed: %s"), pa_cstrerror(errno));
+            pa_close_pipe(daemon_pipe);
+            goto finish;
+        }
+
+        if (child != 0) {
+            ssize_t n;
+            /* Father */
+
+            pa_assert_se(pa_close(daemon_pipe[1]) == 0);
+            daemon_pipe[1] = -1;
+
+            if ((n = pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) {
+
+                if (n < 0)
+                    pa_log(_("read() failed: %s"), pa_cstrerror(errno));
+
+                retval = 1;
+            }
+
+            if (retval)
+                pa_log(_("Daemon startup failed."));
+            else
+                pa_log_info("Daemon startup successful.");
+
+            goto finish;
+        }
+
+        if (autospawn_fd >= 0) {
+            /* The lock file is unlocked from the parent, so we need
+             * to close it in the child */
+
+            pa_autospawn_lock_release();
+            pa_autospawn_lock_done(true);
+
+            autospawn_locked = false;
+            autospawn_fd = -1;
+        }
+
+        pa_assert_se(pa_close(daemon_pipe[0]) == 0);
+        daemon_pipe[0] = -1;
+#endif
+
+        if (!conf->log_target) {
+#ifdef HAVE_SYSTEMD_JOURNAL
+            pa_log_target target = { .type = PA_LOG_JOURNAL, .file = NULL };
+#else
+            pa_log_target target = { .type = PA_LOG_SYSLOG, .file = NULL };
+#endif
+            pa_log_set_target(&target);
+        }
+
+#ifdef HAVE_SETSID
+        if (setsid() < 0) {
+            pa_log(_("setsid() failed: %s"), pa_cstrerror(errno));
+            goto finish;
+        }
+#endif
+
+#ifdef HAVE_FORK
+        /* We now are a session and process group leader. Let's fork
+         * again and let the father die, so that we'll become a
+         * process that can never acquire a TTY again, in a session and
+         * process group without leader */
+
+        if (pipe(daemon_pipe2) < 0) {
+            pa_log(_("pipe() failed: %s"), pa_cstrerror(errno));
+            goto finish;
+        }
+
+        if ((child = fork()) < 0) {
+            pa_log(_("fork() failed: %s"), pa_cstrerror(errno));
+            pa_close_pipe(daemon_pipe2);
+            goto finish;
+        }
+
+        if (child != 0) {
+            ssize_t n;
+            /* Father */
+
+            pa_assert_se(pa_close(daemon_pipe2[1]) == 0);
+            daemon_pipe2[1] = -1;
+
+            if ((n = pa_loop_read(daemon_pipe2[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) {
+
+                if (n < 0)
+                    pa_log(_("read() failed: %s"), pa_cstrerror(errno));
+
+                retval = 1;
+            }
+
+            /* We now have to take care of signalling the first fork with
+             * the return value we've received from this fork... */
+            pa_assert(daemon_pipe[1] >= 0);
+
+            pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
+            pa_close(daemon_pipe[1]);
+            daemon_pipe[1] = -1;
+
+            goto finish;
+        }
+
+        pa_assert_se(pa_close(daemon_pipe2[0]) == 0);
+        daemon_pipe2[0] = -1;
+
+        /* We no longer need the (first) daemon_pipe as it's handled in our child above */
+        pa_close_pipe(daemon_pipe);
+#endif
+
+#ifdef SIGTTOU
+        signal(SIGTTOU, SIG_IGN);
+#endif
+#ifdef SIGTTIN
+        signal(SIGTTIN, SIG_IGN);
+#endif
+#ifdef SIGTSTP
+        signal(SIGTSTP, SIG_IGN);
+#endif
+
+        pa_nullify_stdfds();
+    }
+
+    pa_set_env_and_record("PULSE_INTERNAL", "1");
+    pa_assert_se(chdir("/") == 0);
+    umask(0022);
+
+#ifdef HAVE_SYS_RESOURCE_H
+    set_all_rlimits(conf);
+#endif
+    pa_rtclock_hrtimer_enable();
+
+    if (conf->high_priority)
+        pa_raise_priority(conf->nice_level);
+
+    if (conf->system_instance)
+        if (change_user() < 0)
+            goto finish;
+
+    pa_set_env_and_record("PULSE_SYSTEM", conf->system_instance ? "1" : "0");
+
+    pa_log_info("This is PulseAudio %s", PACKAGE_VERSION);
+    pa_log_debug("Compilation host: %s", CANONICAL_HOST);
+    pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS);
+
+#ifdef HAVE_LIBSAMPLERATE
+    pa_log_warn("Compiled with DEPRECATED libsamplerate support!");
+#endif
+
+    s = pa_uname_string();
+    pa_log_debug("Running on host: %s", s);
+    pa_xfree(s);
+
+    pa_log_debug("Found %u CPUs.", pa_ncpus());
+
+    pa_log_info("Page size is %zu bytes", pa_page_size());
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+    pa_log_debug("Compiled with Valgrind support: yes");
+#else
+    pa_log_debug("Compiled with Valgrind support: no");
+#endif
+
+    pa_log_debug("Running in valgrind mode: %s", pa_yes_no(pa_in_valgrind()));
+
+    pa_log_debug("Running in VM: %s", pa_yes_no(pa_running_in_vm()));
+
+#ifdef __OPTIMIZE__
+    pa_log_debug("Optimized build: yes");
+#else
+    pa_log_debug("Optimized build: no");
+#endif
+
+#ifdef NDEBUG
+    pa_log_debug("NDEBUG defined, all asserts disabled.");
+#elif defined(FASTPATH)
+    pa_log_debug("FASTPATH defined, only fast path asserts disabled.");
+#else
+    pa_log_debug("All asserts enabled.");
+#endif
+
+    if (!(s = pa_machine_id())) {
+        pa_log(_("Failed to get machine ID"));
+        goto finish;
+    }
+    pa_log_info("Machine ID is %s.", s);
+    pa_xfree(s);
+
+    if ((s = pa_session_id())) {
+        pa_log_info("Session ID is %s.", s);
+        pa_xfree(s);
+    }
+
+    if (!(s = pa_get_runtime_dir()))
+        goto finish;
+    pa_log_info("Using runtime directory %s.", s);
+    pa_xfree(s);
+
+    if (!(s = pa_get_state_dir()))
+        goto finish;
+    pa_log_info("Using state directory %s.", s);
+    pa_xfree(s);
+
+    pa_log_info("Using modules directory %s.", conf->dl_search_path);
+
+    pa_log_info("Running in system mode: %s", pa_yes_no(pa_in_system_mode()));
+
+    if (pa_in_system_mode())
+        pa_log_warn(_("OK, so you are running PA in system mode. Please make sure that you actually do want to do that.\n"
+                      "Please read http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system mode is usually a bad idea."));
+
+    if (conf->use_pid_file) {
+        int z;
+
+        if ((z = pa_pid_file_create("pulseaudio")) != 0) {
+
+            if (conf->cmd == PA_CMD_START && z > 0) {
+                /* If we are already running and with are run in
+                 * --start mode, then let's return this as success. */
+
+                retval = 0;
+                goto finish;
+            }
+
+            pa_log(_("pa_pid_file_create() failed."));
+            goto finish;
+        }
+
+        valid_pid_file = true;
+    }
+
+    pa_disable_sigpipe();
+
+    if (pa_rtclock_hrtimer())
+        pa_log_info("System supports high resolution timers");
+    else
+        pa_log_info("System appears to not support high resolution timers");
+
+    if (conf->lock_memory) {
+#if defined(HAVE_SYS_MMAN_H) && !defined(__ANDROID__)
+        if (mlockall(MCL_FUTURE) < 0)
+            pa_log_warn("mlockall() failed: %s", pa_cstrerror(errno));
+        else
+            pa_log_info("Successfully locked process into memory.");
+#else
+        pa_log_warn("Memory locking requested but not supported on platform.");
+#endif
+    }
+
+    pa_memtrap_install();
+
+    pa_assert_se(mainloop = pa_mainloop_new());
+
+    if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm,
+                          !conf->disable_shm && !conf->disable_memfd && pa_memfd_is_locally_supported(),
+                          conf->shm_size))) {
+        pa_log(_("pa_core_new() failed."));
+        goto finish;
+    }
+
+    c->default_sample_spec = conf->default_sample_spec;
+    c->alternate_sample_rate = conf->alternate_sample_rate;
+    c->default_channel_map = conf->default_channel_map;
+    c->default_n_fragments = conf->default_n_fragments;
+    c->default_fragment_size_msec = conf->default_fragment_size_msec;
+    c->deferred_volume_safety_margin_usec = conf->deferred_volume_safety_margin_usec;
+    c->deferred_volume_extra_delay_usec = conf->deferred_volume_extra_delay_usec;
+    c->lfe_crossover_freq = conf->lfe_crossover_freq;
+    c->exit_idle_time = conf->exit_idle_time;
+    c->scache_idle_time = conf->scache_idle_time;
+    c->resample_method = conf->resample_method;
+    c->realtime_priority = conf->realtime_priority;
+    c->realtime_scheduling = conf->realtime_scheduling;
+    c->avoid_resampling = conf->avoid_resampling;
+    c->disable_remixing = conf->disable_remixing;
+    c->remixing_use_all_sink_channels = conf->remixing_use_all_sink_channels;
+    c->disable_lfe_remixing = conf->disable_lfe_remixing;
+    c->deferred_volume = conf->deferred_volume;
+    c->running_as_daemon = conf->daemonize;
+    c->disallow_exit = conf->disallow_exit;
+    c->flat_volumes = conf->flat_volumes;
+#ifdef HAVE_DBUS
+    c->server_type = conf->local_server_type;
+#endif
+
+    pa_cpu_init(&c->cpu_info);
+
+    pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
+    pa_signal_new(SIGINT, signal_callback, c);
+    pa_signal_new(SIGTERM, signal_callback, c);
+#ifdef SIGUSR1
+    pa_signal_new(SIGUSR1, signal_callback, c);
+#endif
+#ifdef SIGUSR2
+    pa_signal_new(SIGUSR2, signal_callback, c);
+#endif
+#ifdef SIGHUP
+    pa_signal_new(SIGHUP, signal_callback, c);
+#endif
+
+    if (!conf->no_cpu_limit)
+        pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
+
+    buf = pa_strbuf_new();
+
+#ifdef HAVE_DBUS
+    pa_assert_se(dbus_threads_init_default());
+
+    if (start_server) {
+#endif
+        if (conf->load_default_script_file) {
+            FILE *f;
+
+            if ((f = pa_daemon_conf_open_default_script_file(conf))) {
+                r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
+                fclose(f);
+            }
+        }
+
+        if (r >= 0)
+            r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
+
+        pa_log_error("%s", s = pa_strbuf_to_string_free(buf));
+        pa_xfree(s);
+
+        if (r < 0 && conf->fail) {
+            pa_log(_("Failed to initialize daemon."));
+            goto finish;
+        }
+
+        if (!c->modules || pa_idxset_size(c->modules) == 0) {
+            pa_log(_("Daemon startup without any loaded modules, refusing to work."));
+            goto finish;
+        }
+#ifdef HAVE_DBUS
+    } else {
+        /* When we just provide the D-Bus server lookup service, we don't want
+         * any modules to be loaded. We haven't loaded any so far, so one might
+         * think there's no way to contact the server, but receiving certain
+         * signals could still cause modules to load. */
+        conf->disallow_module_loading = true;
+    }
+#endif
+
+    /* We completed the initial module loading, so let's disable it
+     * from now on, if requested */
+    c->disallow_module_loading = conf->disallow_module_loading;
+
+#ifdef HAVE_DBUS
+    if (!conf->system_instance) {
+        if ((server_lookup = pa_dbusobj_server_lookup_new(c))) {
+            if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.PulseAudio1")))
+                goto finish;
+        }
+    }
+
+    if (start_server)
+        server_bus = register_dbus_name(c, conf->system_instance ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, "org.pulseaudio.Server");
+#endif
+
+#ifdef HAVE_FORK
+    if (daemon_pipe2[1] >= 0) {
+        int ok = 0;
+        pa_loop_write(daemon_pipe2[1], &ok, sizeof(ok), NULL);
+        pa_close(daemon_pipe2[1]);
+        daemon_pipe2[1] = -1;
+    }
+#endif
+
+    pa_log_info("Daemon startup complete.");
+
+#ifdef HAVE_SYSTEMD_DAEMON
+    sd_notify(0, "READY=1");
+#endif
+
+    retval = 0;
+    if (pa_mainloop_run(mainloop, &retval) < 0)
+        goto finish;
+
+    pa_log_info("Daemon shutdown initiated.");
+
+#ifdef HAVE_SYSTEMD_DAEMON
+    sd_notify(0, "STOPPING=1");
+#endif
+
+finish:
+#ifdef HAVE_DBUS
+    if (server_bus)
+        pa_dbus_connection_unref(server_bus);
+    if (lookup_service_bus)
+        pa_dbus_connection_unref(lookup_service_bus);
+    if (server_lookup)
+        pa_dbusobj_server_lookup_free(server_lookup);
+#endif
+
+    if (autospawn_fd >= 0) {
+        if (autospawn_locked)
+            pa_autospawn_lock_release();
+
+        pa_autospawn_lock_done(false);
+    }
+
+    if (c) {
+        /* Ensure all the modules/samples are unloaded when the core is still ref'ed,
+         * as unlink callback hooks in modules may need the core to be ref'ed */
+        pa_module_unload_all(c);
+        pa_scache_free_all(c);
+
+        pa_core_unref(c);
+        pa_log_info("Daemon terminated.");
+    }
+
+    if (!conf->no_cpu_limit)
+        pa_cpu_limit_done();
+
+    pa_signal_done();
+
+#ifdef HAVE_FORK
+    /* If we have daemon_pipe[1] still open, this means we've failed after
+     * the first fork, but before the second. Therefore just write to it. */
+    if (daemon_pipe[1] >= 0)
+        pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
+    else if (daemon_pipe2[1] >= 0)
+        pa_loop_write(daemon_pipe2[1], &retval, sizeof(retval), NULL);
+
+    pa_close_pipe(daemon_pipe2);
+    pa_close_pipe(daemon_pipe);
+#endif
+
+    if (mainloop)
+        pa_mainloop_free(mainloop);
+
+    if (conf)
+        pa_daemon_conf_free(conf);
+
+    if (valid_pid_file)
+        pa_pid_file_remove();
+
+    /* This has no real purpose except making things valgrind-clean */
+    pa_unset_env_recorded();
+
+#ifdef OS_IS_WIN32
+    WSACleanup();
+#endif
+
+    if (ltdl_init)
+        pa_ltdl_done();
+
+#ifdef HAVE_DBUS
+    dbus_shutdown();
+#endif
+
+    return retval;
+}
diff --git a/src/daemon/pulseaudio-system.conf b/src/daemon/pulseaudio-system.conf
new file mode 100644 (file)
index 0000000..2eb342e
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+-->
+
+<busconfig>
+
+  <!-- System-wide PulseAudio runs as 'pulse' user. This fragment is
+       not necessary for user PulseAudio instances. -->
+
+  <policy user="pulse">
+    <allow own="org.pulseaudio.Server"/>
+  </policy>
+
+</busconfig>
diff --git a/src/daemon/pulseaudio.desktop.in b/src/daemon/pulseaudio.desktop.in
new file mode 100644 (file)
index 0000000..06cde27
--- /dev/null
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Version=1.0
+_Name=PulseAudio Sound System
+_Comment=Start the PulseAudio Sound System
+Exec=start-pulseaudio-x11
+Terminal=false
+Type=Application
+Categories=
+GenericName=
+X-GNOME-Autostart-Phase=Initialization
+X-KDE-autostart-phase=1
diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c
new file mode 100644 (file)
index 0000000..ca390c7
--- /dev/null
@@ -0,0 +1,492 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulse/client-conf.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-shared.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "server-lookup.h"
+
+#define OBJECT_PATH "/org/pulseaudio/server_lookup1"
+#define INTERFACE "org.PulseAudio.ServerLookup1"
+
+struct pa_dbusobj_server_lookup {
+    pa_core *core;
+    pa_dbus_connection *conn;
+    bool path_registered;
+};
+
+static const char introspection[] =
+    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+    "<node>"
+    " <!-- If you are looking for documentation make sure to check out\n"
+    "      http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/ -->\n"
+    " <interface name=\"" INTERFACE "\">\n"
+    "  <property name=\"Address\" type=\"s\" access=\"read\"/>\n"
+    " </interface>\n"
+    " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
+    "  <method name=\"Introspect\">\n"
+    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
+    "  </method>\n"
+    " </interface>\n"
+    " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
+    "  <method name=\"Get\">\n"
+    "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
+    "  </method>\n"
+    "  <method name=\"Set\">\n"
+    "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
+    "  </method>\n"
+    "  <method name=\"GetAll\">\n"
+    "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
+    "  </method>\n"
+    " </interface>\n"
+    "</node>\n";
+
+static void unregister_cb(DBusConnection *conn, void *user_data) {
+    pa_dbusobj_server_lookup *sl = user_data;
+
+    pa_assert(sl);
+    pa_assert(sl->path_registered);
+
+    sl->path_registered = false;
+}
+
+static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+    const char *i = introspection;
+    DBusMessage *reply = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    if (!(reply = dbus_message_new_method_return(msg))) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+    if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+    if (!dbus_connection_send(conn, reply, NULL)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+
+finish:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return r;
+}
+
+enum get_address_result_t {
+    SUCCESS,
+    SERVER_FROM_TYPE_FAILED
+};
+
+/* Caller frees the returned address. */
+static enum get_address_result_t get_address(pa_server_type_t server_type, char **address) {
+    enum get_address_result_t r = SUCCESS;
+    pa_client_conf *conf = pa_client_conf_new();
+
+    *address = NULL;
+
+    pa_client_conf_load(conf, false, false);
+
+    if (conf->default_dbus_server)
+        *address = pa_xstrdup(conf->default_dbus_server);
+    else if (!(*address = pa_get_dbus_address_from_server_type(server_type))) {
+        r = SERVER_FROM_TYPE_FAILED;
+        goto finish;
+    }
+
+finish:
+    pa_client_conf_free(conf);
+    return r;
+}
+
+static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+    DBusMessage *reply = NULL;
+    char *address = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter variant_iter;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(sl);
+
+    switch (get_address(sl->core->server_type, &address)) {
+        case SUCCESS:
+            if (!(reply = dbus_message_new_method_return(msg))) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            dbus_message_iter_init_append(reply, &msg_iter);
+            if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_connection_send(conn, reply, NULL)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            r = DBUS_HANDLER_RESULT_HANDLED;
+            goto finish;
+
+        case SERVER_FROM_TYPE_FAILED:
+            if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
+                r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+                goto finish;
+            }
+            if (!dbus_connection_send(conn, reply, NULL)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            r = DBUS_HANDLER_RESULT_HANDLED;
+            goto finish;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+finish:
+    pa_xfree(address);
+    if (reply)
+        dbus_message_unref(reply);
+
+    return r;
+}
+
+static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+    const char* interface;
+    const char* property;
+    DBusMessage *reply = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(sl);
+
+    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
+        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    if (*interface && !pa_streq(interface, INTERFACE)) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+
+    if (!pa_streq(property, "Address")) {
+        if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    r = handle_get_address(conn, msg, sl);
+
+finish:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return r;
+}
+
+static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+    const char* interface;
+    const char* property;
+    DBusMessage *reply = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(sl);
+
+    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
+        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    if (*interface && !pa_streq(interface, INTERFACE)) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+
+    if (!pa_streq(property, "Address")) {
+        if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+    if (!dbus_connection_send(conn, reply, NULL)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    r = DBUS_HANDLER_RESULT_HANDLED;
+
+finish:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return r;
+}
+
+static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+    DBusMessage *reply = NULL;
+    const char *property = "Address";
+    char *interface = NULL;
+    char *address = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    DBusMessageIter variant_iter;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(sl);
+
+    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) {
+        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    switch (get_address(sl->core->server_type, &address)) {
+        case SUCCESS:
+            if (!(reply = dbus_message_new_method_return(msg))) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            dbus_message_iter_init_append(reply, &msg_iter);
+            if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_connection_send(conn, reply, NULL)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            r = DBUS_HANDLER_RESULT_HANDLED;
+            goto finish;
+
+        case SERVER_FROM_TYPE_FAILED:
+            if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
+                r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+                goto finish;
+            }
+            if (!dbus_connection_send(conn, reply, NULL)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            r = DBUS_HANDLER_RESULT_HANDLED;
+            goto finish;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+finish:
+    pa_xfree(address);
+    if (reply)
+        dbus_message_unref(reply);
+
+    return r;
+}
+
+static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) {
+    pa_dbusobj_server_lookup *sl = user_data;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(sl);
+
+    /* pa_log("Got message! type = %s   path = %s   iface = %s   member = %s   dest = %s", dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_destination(msg)); */
+
+    if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") ||
+        (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Introspect")))
+        return handle_introspect(conn, msg, sl);
+
+    if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Get") ||
+        (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Get")))
+        return handle_get(conn, msg, sl);
+
+    if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Set") ||
+        (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Set")))
+        return handle_set(conn, msg, sl);
+
+    if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "GetAll") ||
+        (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "GetAll")))
+        return handle_get_all(conn, msg, sl);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable vtable = {
+    .unregister_function = unregister_cb,
+    .message_function = message_cb,
+    .dbus_internal_pad1 = NULL,
+    .dbus_internal_pad2 = NULL,
+    .dbus_internal_pad3 = NULL,
+    .dbus_internal_pad4 = NULL
+};
+
+pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) {
+    pa_dbusobj_server_lookup *sl;
+    DBusError error;
+
+    dbus_error_init(&error);
+
+    sl = pa_xnew(pa_dbusobj_server_lookup, 1);
+    sl->core = c;
+    sl->path_registered = false;
+
+    if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
+        pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH, &vtable, sl)) {
+        pa_log("dbus_connection_register_object_path() failed for " OBJECT_PATH ".");
+        goto fail;
+    }
+
+    sl->path_registered = true;
+
+    return sl;
+
+fail:
+    dbus_error_free(&error);
+
+    pa_dbusobj_server_lookup_free(sl);
+
+    return NULL;
+}
+
+void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) {
+    pa_assert(sl);
+
+    if (sl->path_registered) {
+        pa_assert(sl->conn);
+        if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH))
+            pa_log_debug("dbus_connection_unregister_object_path() failed for " OBJECT_PATH ".");
+    }
+
+    if (sl->conn)
+        pa_dbus_connection_unref(sl->conn);
+
+    pa_xfree(sl);
+}
diff --git a/src/daemon/server-lookup.h b/src/daemon/server-lookup.h
new file mode 100644 (file)
index 0000000..935baf6
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef fooserverlookuphfoo
+#define fooserverlookuphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus object at path
+ * /org/pulseaudio/server_lookup. Implemented interfaces
+ * are org.pulseaudio.ServerLookup and org.freedesktop.DBus.Introspectable.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/ConnectingToServer/
+ * for the ServerLookup interface documentation.
+ */
+
+#include <pulsecore/core.h>
+
+typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup;
+
+pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c);
+void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl);
+
+#endif
diff --git a/src/daemon/start-pulseaudio-x11.in b/src/daemon/start-pulseaudio-x11.in
new file mode 100755 (executable)
index 0000000..16e46b1
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+set -e
+
+if [ x"$DISPLAY" != x ] ; then
+
+    @PACTL_BINARY@ load-module module-x11-publish "display=$DISPLAY" > /dev/null
+    @PACTL_BINARY@ load-module module-x11-cork-request "display=$DISPLAY" > /dev/null
+
+    if [ x"$KDE_FULL_SESSION" = x"true" ]; then
+       @PACTL_BINARY@ load-module module-device-manager "do_routing=1" > /dev/null
+    fi
+
+    if [ x"$SESSION_MANAGER" != x ] ; then
+       @PACTL_BINARY@ load-module module-x11-xsmp "display=$DISPLAY session_manager=$SESSION_MANAGER" > /dev/null
+    fi
+fi
diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in
new file mode 100755 (executable)
index 0000000..06e3f1e
--- /dev/null
@@ -0,0 +1,69 @@
+#!@PA_BINARY@ -nF
+#
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+# This startup script is used only if PulseAudio is started in system
+# mode.
+changequote(`[', `]')dnl Set up m4 quoting
+
+### Automatically load driver modules depending on the hardware available
+ifelse(@HAVE_UDEV@, 1, [dnl
+.ifexists module-udev-detect@PA_SOEXT@
+load-module module-udev-detect
+.else
+], @HAVE_HAL@, 1, [dnl
+.ifexists module-hal-detect@PA_SOEXT@
+load-module module-hal-detect
+.else
+], @HAVE_COREAUDIO@, 1, [dnl
+.ifexists module-coreaudio-detect@PA_SOEXT@
+load-module module-coreaudio-detect
+.else
+], [dnl
+.ifexists module-detect@PA_SOEXT@
+])dnl
+### Use the static hardware detection module (for systems that lack udev/hal support)
+load-module module-detect
+.endif
+
+### Load several protocols
+.ifexists module-esound-protocol-unix@PA_SOEXT@
+load-module module-esound-protocol-unix
+.endif
+load-module module-native-protocol-unix
+
+### Automatically restore the volume of streams and devices
+load-module module-stream-restore
+load-module module-device-restore
+
+### Automatically restore the default sink/source when changed by the user
+### during runtime
+### NOTE: This should be loaded as early as possible so that subsequent modules
+### that look up the default sink/source get the right value
+load-module module-default-device-restore
+
+### Automatically move streams to the default sink if the sink they are
+### connected to dies, similar for sources
+load-module module-rescue-streams
+
+### Make sure we always have a sink around, even if it is a null sink.
+load-module module-always-sink
+
+### Automatically suspend sinks/sources that become idle for too long
+load-module module-suspend-on-idle
+
+### Enable positioned event sounds
+load-module module-position-event-sounds
diff --git a/src/daemon/systemd/user/pulseaudio.service.in b/src/daemon/systemd/user/pulseaudio.service.in
new file mode 100644 (file)
index 0000000..2bd780f
--- /dev/null
@@ -0,0 +1,26 @@
+[Unit]
+Description=Sound Service
+
+# We require pulseaudio.socket to be active before starting the daemon, because
+# while it is possible to use the service without the socket, it is not clear
+# why it would be desirable.
+#
+# A user installing pulseaudio and doing `systemctl --user start pulseaudio`
+# will not get the socket started, which might be confusing and problematic if
+# the server is to be restarted later on, as the client autospawn feature
+# might kick in. Also, a start of the socket unit will fail, adding to the
+# confusion.
+#
+# After=pulseaudio.socket is not needed, as it is already implicit in the
+# socket-service relationship, see systemd.socket(5).
+Requires=pulseaudio.socket
+
+[Service]
+# Note that notify will only work if --daemonize=no
+Type=notify
+ExecStart=@PA_BINARY@ --daemonize=no
+Restart=on-failure
+
+[Install]
+Also=pulseaudio.socket
+WantedBy=default.target
diff --git a/src/daemon/systemd/user/pulseaudio.socket b/src/daemon/systemd/user/pulseaudio.socket
new file mode 100644 (file)
index 0000000..332ece8
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=Sound System
+
+[Socket]
+Priority=6
+Backlog=5
+ListenStream=%t/pulse/native
+
+[Install]
+WantedBy=sockets.target
diff --git a/src/depmod.py b/src/depmod.py
new file mode 100755 (executable)
index 0000000..39b50ce
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/python
+
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+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/map-file b/src/map-file
new file mode 100644 (file)
index 0000000..93a62b8
--- /dev/null
@@ -0,0 +1,385 @@
+PULSE_0 {
+global:
+pa_ascii_filter;
+pa_ascii_valid;
+pa_bytes_per_second;
+pa_bytes_snprint;
+pa_bytes_to_usec;
+pa_channel_map_can_balance;
+pa_channel_map_can_fade;
+pa_channel_map_can_lfe_balance;
+pa_channel_map_compatible;
+pa_channel_map_equal;
+pa_channel_map_has_position;
+pa_channel_map_init;
+pa_channel_map_init_auto;
+pa_channel_map_init_extend;
+pa_channel_map_init_mono;
+pa_channel_map_init_stereo;
+pa_channel_map_mask;
+pa_channel_map_parse;
+pa_channel_map_snprint;
+pa_channel_map_superset;
+pa_channel_map_to_name;
+pa_channel_map_to_pretty_name;
+pa_channel_map_valid;
+pa_channel_position_from_string;
+pa_channel_position_to_pretty_string;
+pa_channel_position_to_string;
+pa_channels_valid;
+pa_context_add_autoload;
+pa_context_connect;
+pa_context_disconnect;
+pa_context_drain;
+pa_context_errno;
+pa_context_exit_daemon;
+pa_context_get_autoload_info_by_index;
+pa_context_get_autoload_info_by_name;
+pa_context_get_autoload_info_list;
+pa_context_get_card_info_by_index;
+pa_context_get_card_info_by_name;
+pa_context_get_card_info_list;
+pa_context_get_client_info;
+pa_context_get_client_info_list;
+pa_context_get_index;
+pa_context_get_module_info;
+pa_context_get_module_info_list;
+pa_context_get_protocol_version;
+pa_context_get_sample_info_by_index;
+pa_context_get_sample_info_by_name;
+pa_context_get_sample_info_list;
+pa_context_get_server;
+pa_context_get_server_info;
+pa_context_get_server_protocol_version;
+pa_context_get_sink_info_by_index;
+pa_context_get_sink_info_by_name;
+pa_context_get_sink_info_list;
+pa_context_get_sink_input_info;
+pa_context_get_sink_input_info_list;
+pa_context_get_source_info_by_index;
+pa_context_get_source_info_by_name;
+pa_context_get_source_info_list;
+pa_context_get_source_output_info;
+pa_context_get_source_output_info_list;
+pa_context_set_port_latency_offset;
+pa_context_get_state;
+pa_context_get_tile_size;
+pa_context_is_local;
+pa_context_is_pending;
+pa_context_kill_client;
+pa_context_kill_sink_input;
+pa_context_kill_source_output;
+pa_context_load_cookie_from_file;
+pa_context_load_module;
+pa_context_move_sink_input_by_index;
+pa_context_move_sink_input_by_name;
+pa_context_move_source_output_by_index;
+pa_context_move_source_output_by_name;
+pa_context_new;
+pa_context_new_with_proplist;
+pa_context_play_sample;
+pa_context_play_sample_with_proplist;
+pa_context_proplist_remove;
+pa_context_proplist_update;
+pa_context_ref;
+pa_context_remove_autoload_by_index;
+pa_context_remove_autoload_by_name;
+pa_context_remove_sample;
+pa_context_rttime_new;
+pa_context_rttime_restart;
+pa_context_set_card_profile_by_index;
+pa_context_set_card_profile_by_name;
+pa_context_set_default_sink;
+pa_context_set_default_source;
+pa_context_set_event_callback;
+pa_context_set_name;
+pa_context_set_sink_input_mute;
+pa_context_set_sink_input_volume;
+pa_context_set_sink_mute_by_index;
+pa_context_set_sink_mute_by_name;
+pa_context_set_sink_port_by_index;
+pa_context_set_sink_port_by_name;
+pa_context_set_sink_volume_by_index;
+pa_context_set_sink_volume_by_name;
+pa_context_set_source_output_mute;
+pa_context_set_source_output_volume;
+pa_context_set_source_mute_by_index;
+pa_context_set_source_mute_by_name;
+pa_context_set_source_port_by_index;
+pa_context_set_source_port_by_name;
+pa_context_set_source_volume_by_index;
+pa_context_set_source_volume_by_name;
+pa_context_set_state_callback;
+pa_context_set_subscribe_callback;
+pa_context_stat;
+pa_context_subscribe;
+pa_context_suspend_sink_by_index;
+pa_context_suspend_sink_by_name;
+pa_context_suspend_source_by_index;
+pa_context_suspend_source_by_name;
+pa_context_unload_module;
+pa_context_unref;
+pa_cvolume_avg;
+pa_cvolume_avg_mask;
+pa_cvolume_channels_equal_to;
+pa_cvolume_compatible;
+pa_cvolume_compatible_with_channel_map;
+pa_cvolume_dec;
+pa_cvolume_equal;
+pa_cvolume_get_balance;
+pa_cvolume_get_fade;
+pa_cvolume_get_lfe_balance;
+pa_cvolume_get_position;
+pa_cvolume_inc;
+pa_cvolume_inc_clamp;
+pa_cvolume_init;
+pa_cvolume_max;
+pa_cvolume_max_mask;
+pa_cvolume_merge;
+pa_cvolume_min;
+pa_cvolume_min_mask;
+pa_cvolume_remap;
+pa_cvolume_scale;
+pa_cvolume_scale_mask;
+pa_cvolume_set;
+pa_cvolume_set_balance;
+pa_cvolume_set_fade;
+pa_cvolume_set_lfe_balance;
+pa_cvolume_set_position;
+pa_cvolume_snprint;
+pa_cvolume_snprint_verbose;
+pa_cvolume_valid;
+pa_direction_to_string;
+pa_direction_valid;
+pa_encoding_to_string;
+pa_ext_device_manager_delete;
+pa_ext_device_manager_enable_role_device_priority_routing;
+pa_ext_device_manager_read;
+pa_ext_device_manager_reorder_devices_for_role;
+pa_ext_device_manager_set_device_description;
+pa_ext_device_manager_set_subscribe_cb;
+pa_ext_device_manager_subscribe;
+pa_ext_device_manager_test;
+pa_ext_device_restore_read_formats;
+pa_ext_device_restore_read_formats_all;
+pa_ext_device_restore_save_formats;
+pa_ext_device_restore_set_subscribe_cb;
+pa_ext_device_restore_subscribe;
+pa_ext_device_restore_test;
+pa_ext_stream_restore_delete;
+pa_ext_stream_restore_read;
+pa_ext_stream_restore_set_subscribe_cb;
+pa_ext_stream_restore_subscribe;
+pa_ext_stream_restore_test;
+pa_ext_stream_restore_write;
+pa_format_info_copy;
+pa_format_info_free;
+pa_format_info_from_string;
+pa_format_info_from_sample_spec;
+pa_format_info_get_prop_type;
+pa_format_info_get_prop_int;
+pa_format_info_get_prop_int_range;
+pa_format_info_get_prop_int_array;
+pa_format_info_get_prop_string;
+pa_format_info_get_prop_string_array;
+pa_format_info_free_string_array;
+pa_format_info_is_compatible;
+pa_format_info_is_pcm;
+pa_format_info_new;
+pa_format_info_set_channel_map;
+pa_format_info_set_channels;
+pa_format_info_set_prop_int;
+pa_format_info_set_prop_int_array;
+pa_format_info_set_prop_int_range;
+pa_format_info_set_prop_string;
+pa_format_info_set_prop_string_array;
+pa_format_info_set_rate;
+pa_format_info_set_sample_format;
+pa_format_info_snprint;
+pa_format_info_to_sample_spec;
+pa_format_info_valid;
+pa_frame_size;
+pa_get_binary_name;
+pa_get_fqdn;
+pa_get_home_dir;
+pa_get_host_name;
+pa_get_library_version;
+pa_gettimeofday;
+pa_get_user_name;
+pa_glib_mainloop_free;
+pa_glib_mainloop_get_api;
+pa_glib_mainloop_new;
+pa_locale_to_utf8;
+pa_mainloop_api_once;
+pa_mainloop_dispatch;
+pa_mainloop_free;
+pa_mainloop_get_api;
+pa_mainloop_get_retval;
+pa_mainloop_iterate;
+pa_mainloop_new;
+pa_mainloop_poll;
+pa_mainloop_prepare;
+pa_mainloop_quit;
+pa_mainloop_run;
+pa_mainloop_set_poll_func;
+pa_mainloop_wakeup;
+pa_msleep;
+pa_operation_cancel;
+pa_operation_get_state;
+pa_operation_ref;
+pa_operation_set_state_callback;
+pa_operation_unref;
+pa_parse_sample_format;
+pa_path_get_filename;
+pa_proplist_clear;
+pa_proplist_contains;
+pa_proplist_copy;
+pa_proplist_equal;
+pa_proplist_free;
+pa_proplist_from_string;
+pa_proplist_get;
+pa_proplist_gets;
+pa_proplist_isempty;
+pa_proplist_iterate;
+pa_proplist_key_valid;
+pa_proplist_new;
+pa_proplist_set;
+pa_proplist_setf;
+pa_proplist_setp;
+pa_proplist_sets;
+pa_proplist_size;
+pa_proplist_to_string;
+pa_proplist_to_string_sep;
+pa_proplist_unset;
+pa_proplist_unset_many;
+pa_proplist_update;
+pa_rtclock_now;
+pa_sample_format_is_be;
+pa_sample_format_is_le;
+pa_sample_format_to_string;
+pa_sample_format_valid;
+pa_sample_rate_valid;
+pa_sample_size;
+pa_sample_size_of_format;
+pa_sample_spec_equal;
+pa_sample_spec_init;
+pa_sample_spec_snprint;
+pa_sample_spec_valid;
+pa_signal_done;
+pa_signal_free;
+pa_signal_init;
+pa_signal_new;
+pa_signal_set_destroy;
+pa_simple_drain;
+pa_simple_flush;
+pa_simple_free;
+pa_simple_get_latency;
+pa_simple_new;
+pa_simple_read;
+pa_simple_write;
+pa_stream_begin_write;
+pa_stream_cancel_write;
+pa_stream_connect_playback;
+pa_stream_connect_record;
+pa_stream_connect_upload;
+pa_stream_cork;
+pa_stream_disconnect;
+pa_stream_drain;
+pa_stream_drop;
+pa_stream_finish_upload;
+pa_stream_flush;
+pa_stream_get_buffer_attr;
+pa_stream_get_channel_map;
+pa_stream_get_context;
+pa_stream_get_device_index;
+pa_stream_get_device_name;
+pa_stream_get_format_info;
+pa_stream_get_index;
+pa_stream_get_latency;
+pa_stream_get_monitor_stream;
+pa_stream_get_sample_spec;
+pa_stream_get_state;
+pa_stream_get_time;
+pa_stream_get_timing_info;
+pa_stream_get_underflow_index;
+pa_stream_is_corked;
+pa_stream_is_suspended;
+pa_stream_new;
+pa_stream_new_extended;
+pa_stream_new_with_proplist;
+pa_stream_peek;
+pa_stream_prebuf;
+pa_stream_proplist_remove;
+pa_stream_proplist_update;
+pa_stream_readable_size;
+pa_stream_ref;
+pa_stream_set_buffer_attr;
+pa_stream_set_buffer_attr_callback;
+pa_stream_set_event_callback;
+pa_stream_set_latency_update_callback;
+pa_stream_set_monitor_stream;
+pa_stream_set_moved_callback;
+pa_stream_set_name;
+pa_stream_set_overflow_callback;
+pa_stream_set_read_callback;
+pa_stream_set_started_callback;
+pa_stream_set_state_callback;
+pa_stream_set_suspended_callback;
+pa_stream_set_underflow_callback;
+pa_stream_set_write_callback;
+pa_stream_trigger;
+pa_stream_unref;
+pa_stream_update_sample_rate;
+pa_stream_update_timing_info;
+pa_stream_writable_size;
+pa_stream_write;
+pa_stream_write_ext_free;
+pa_strerror;
+pa_sw_cvolume_divide;
+pa_sw_cvolume_divide_scalar;
+pa_sw_cvolume_multiply;
+pa_sw_cvolume_multiply_scalar;
+pa_sw_cvolume_snprint_dB;
+pa_sw_volume_divide;
+pa_sw_volume_from_dB;
+pa_sw_volume_from_linear;
+pa_sw_volume_multiply;
+pa_sw_volume_snprint_dB;
+pa_sw_volume_to_dB;
+pa_sw_volume_to_linear;
+pa_threaded_mainloop_accept;
+pa_threaded_mainloop_free;
+pa_threaded_mainloop_get_api;
+pa_threaded_mainloop_get_retval;
+pa_threaded_mainloop_in_thread;
+pa_threaded_mainloop_lock;
+pa_threaded_mainloop_new;
+pa_threaded_mainloop_set_name;
+pa_threaded_mainloop_signal;
+pa_threaded_mainloop_start;
+pa_threaded_mainloop_stop;
+pa_threaded_mainloop_unlock;
+pa_threaded_mainloop_wait;
+pa_timeval_add;
+pa_timeval_age;
+pa_timeval_cmp;
+pa_timeval_diff;
+pa_timeval_load;
+pa_timeval_store;
+pa_timeval_sub;
+pa_usec_to_bytes;
+pa_utf8_filter;
+pa_utf8_to_locale;
+pa_utf8_valid;
+pa_volume_snprint;
+pa_volume_snprint_verbose;
+pa_xfree;
+pa_xmalloc;
+pa_xmalloc0;
+pa_xmemdup;
+pa_xrealloc;
+pa_xstrdup;
+pa_xstrndup;
+local:
+*;
+};
diff --git a/src/modules/Makefile b/src/modules/Makefile
new file mode 100644 (file)
index 0000000..5332f25
--- /dev/null
@@ -0,0 +1,7 @@
+all:
+       $(MAKE) -C ..
+
+clean:
+       $(MAKE) -C .. clean
+
+.PHONY: all clean
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
new file mode 100644 (file)
index 0000000..986cc7d
--- /dev/null
@@ -0,0 +1,4839 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2009 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <asoundlib.h>
+#include <math.h>
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <pulse/mainloop-api.h>
+#include <pulse/sample.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/conf-parser.h>
+#include <pulsecore/strbuf.h>
+
+#include "alsa-mixer.h"
+#include "alsa-util.h"
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+/* These macros are workarounds for a bug in valgrind, which is not handling the
+ * ALSA TLV syscalls correctly. See
+ * http://valgrind.10908.n7.nabble.com/Missing-ioctl-for-SNDRV-CTL-IOCTL-TLV-READ-td42711.html */
+
+static inline int vgfix_get_capture_dB(snd_mixer_elem_t *a, snd_mixer_selem_channel_id_t b, long *c) {
+    int r = snd_mixer_selem_get_capture_dB(a, b, c);
+    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
+    return r;
+}
+
+static inline int vgfix_get_playback_dB(snd_mixer_elem_t *a, snd_mixer_selem_channel_id_t b, long *c) {
+    int r = snd_mixer_selem_get_playback_dB(a, b, c);
+    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
+    return r;
+}
+
+static inline int vgfix_ask_capture_vol_dB(snd_mixer_elem_t *a, long b, long *c) {
+    int r = snd_mixer_selem_ask_capture_vol_dB(a, b, c);
+    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
+    return r;
+}
+
+static inline int vgfix_ask_playback_vol_dB(snd_mixer_elem_t *a, long b, long *c) {
+    int r = snd_mixer_selem_ask_playback_vol_dB(a, b, c);
+    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
+    return r;
+}
+
+static inline int vgfix_get_capture_dB_range(snd_mixer_elem_t *a, long *b, long *c) {
+    int r = snd_mixer_selem_get_capture_dB_range(a, b, c);
+    VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
+    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
+    return r;
+}
+
+static inline int vgfix_get_playback_dB_range(snd_mixer_elem_t *a, long *b, long *c) {
+    int r = snd_mixer_selem_get_playback_dB_range(a, b, c);
+    VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
+    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
+    return r;
+}
+
+#define snd_mixer_selem_get_capture_dB(a, b, c) vgfix_get_capture_dB(a, b, c)
+#define snd_mixer_selem_get_playback_dB(a, b, c) vgfix_get_playback_dB(a, b, c)
+#define snd_mixer_selem_ask_capture_vol_dB(a, b, c) vgfix_ask_capture_vol_dB(a, b, c)
+#define snd_mixer_selem_ask_playback_vol_dB(a, b, c) vgfix_ask_playback_vol_dB(a, b, c)
+#define snd_mixer_selem_get_capture_dB_range(a, b, c) vgfix_get_capture_dB_range(a, b, c)
+#define snd_mixer_selem_get_playback_dB_range(a, b, c) vgfix_get_playback_dB_range(a, b, c)
+
+#endif
+
+static int setting_select(pa_alsa_setting *s, snd_mixer_t *m);
+
+struct description_map {
+    const char *key;
+    const char *description;
+};
+
+pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *name) {
+    pa_alsa_jack *jack;
+
+    pa_assert(name);
+
+    jack = pa_xnew0(pa_alsa_jack, 1);
+    jack->path = path;
+    jack->name = pa_xstrdup(name);
+    jack->alsa_name = pa_sprintf_malloc("%s Jack", name);
+    jack->state_unplugged = PA_AVAILABLE_NO;
+    jack->state_plugged = PA_AVAILABLE_YES;
+    jack->ucm_devices = pa_dynarray_new(NULL);
+    jack->ucm_hw_mute_devices = pa_dynarray_new(NULL);
+
+    return jack;
+}
+
+void pa_alsa_jack_free(pa_alsa_jack *jack) {
+    pa_assert(jack);
+
+    pa_dynarray_free(jack->ucm_hw_mute_devices);
+    pa_dynarray_free(jack->ucm_devices);
+
+    pa_xfree(jack->alsa_name);
+    pa_xfree(jack->name);
+    pa_xfree(jack);
+}
+
+void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control) {
+    pa_alsa_ucm_device *device;
+    unsigned idx;
+
+    pa_assert(jack);
+
+    if (has_control == jack->has_control)
+        return;
+
+    jack->has_control = has_control;
+
+    PA_DYNARRAY_FOREACH(device, jack->ucm_hw_mute_devices, idx)
+        pa_alsa_ucm_device_update_available(device);
+
+    PA_DYNARRAY_FOREACH(device, jack->ucm_devices, idx)
+        pa_alsa_ucm_device_update_available(device);
+}
+
+void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in) {
+    pa_alsa_ucm_device *device;
+    unsigned idx;
+
+    pa_assert(jack);
+
+    if (plugged_in == jack->plugged_in)
+        return;
+
+    jack->plugged_in = plugged_in;
+
+    /* XXX: If this is a headphone jack that mutes speakers when plugged in,
+     * and the headphones get unplugged, then the headphone device must be set
+     * to unavailable and the speaker device must be set to unknown. So far so
+     * good. But there's an ugly detail: we must first set the availability of
+     * the speakers and then the headphones. We shouldn't need to care about
+     * the order, but we have to, because module-switch-on-port-available gets
+     * separate events for the two devices, and the intermediate state between
+     * the two events is such that the second event doesn't trigger the desired
+     * port switch, if the event order is "wrong".
+     *
+     * These are the transitions when the event order is "right":
+     *
+     *     speakers:   1) unavailable -> 2) unknown   -> 3) unknown
+     *     headphones: 1) available   -> 2) available -> 3) unavailable
+     *
+     * In the 2 -> 3 transition, headphones become unavailable, and
+     * module-switch-on-port-available sees that speakers can be used, so the
+     * port gets changed as it should.
+     *
+     * These are the transitions when the event order is "wrong":
+     *
+     *     speakers:   1) unavailable -> 2) unavailable -> 3) unknown
+     *     headphones: 1) available   -> 2) unavailable -> 3) unavailable
+     *
+     * In the 1 -> 2 transition, headphones become unavailable, and there are
+     * no available ports to use, so no port change happens. In the 2 -> 3
+     * transition, speaker availability becomes unknown, but that's not
+     * a strong enough signal for module-switch-on-port-available, so it still
+     * doesn't do the port switch.
+     *
+     * We should somehow merge the two events so that
+     * module-switch-on-port-available would handle both transitions in one go.
+     * If module-switch-on-port-available used a defer event to delay
+     * the port availability processing, that would probably do the trick. */
+
+    PA_DYNARRAY_FOREACH(device, jack->ucm_hw_mute_devices, idx)
+        pa_alsa_ucm_device_update_available(device);
+
+    PA_DYNARRAY_FOREACH(device, jack->ucm_devices, idx)
+        pa_alsa_ucm_device_update_available(device);
+}
+
+void pa_alsa_jack_add_ucm_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device) {
+    pa_assert(jack);
+    pa_assert(device);
+
+    pa_dynarray_append(jack->ucm_devices, device);
+}
+
+void pa_alsa_jack_add_ucm_hw_mute_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device) {
+    pa_assert(jack);
+    pa_assert(device);
+
+    pa_dynarray_append(jack->ucm_hw_mute_devices, device);
+}
+
+static const char *lookup_description(const char *key, const struct description_map dm[], unsigned n) {
+    unsigned i;
+
+    if (!key)
+        return NULL;
+
+    for (i = 0; i < n; i++)
+        if (pa_streq(dm[i].key, key))
+            return _(dm[i].description);
+
+    return NULL;
+}
+
+struct pa_alsa_fdlist {
+    unsigned num_fds;
+    struct pollfd *fds;
+    /* This is a temporary buffer used to avoid lots of mallocs */
+    struct pollfd *work_fds;
+
+    snd_mixer_t *mixer;
+    snd_hctl_t *hctl;
+
+    pa_mainloop_api *m;
+    pa_defer_event *defer;
+    pa_io_event **ios;
+
+    bool polled;
+
+    void (*cb)(void *userdata);
+    void *userdata;
+};
+
+static void io_cb(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+
+    struct pa_alsa_fdlist *fdl = userdata;
+    int err;
+    unsigned i;
+    unsigned short revents;
+
+    pa_assert(a);
+    pa_assert(fdl);
+    pa_assert(fdl->mixer || fdl->hctl);
+    pa_assert(fdl->fds);
+    pa_assert(fdl->work_fds);
+
+    if (fdl->polled)
+        return;
+
+    fdl->polled = true;
+
+    memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
+
+    for (i = 0; i < fdl->num_fds; i++) {
+        if (e == fdl->ios[i]) {
+            if (events & PA_IO_EVENT_INPUT)
+                fdl->work_fds[i].revents |= POLLIN;
+            if (events & PA_IO_EVENT_OUTPUT)
+                fdl->work_fds[i].revents |= POLLOUT;
+            if (events & PA_IO_EVENT_ERROR)
+                fdl->work_fds[i].revents |= POLLERR;
+            if (events & PA_IO_EVENT_HANGUP)
+                fdl->work_fds[i].revents |= POLLHUP;
+            break;
+        }
+    }
+
+    pa_assert(i != fdl->num_fds);
+
+    if (fdl->hctl)
+        err = snd_hctl_poll_descriptors_revents(fdl->hctl, fdl->work_fds, fdl->num_fds, &revents);
+    else
+        err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents);
+
+    if (err < 0) {
+        pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
+        return;
+    }
+
+    a->defer_enable(fdl->defer, 1);
+
+    if (revents) {
+        if (fdl->hctl)
+            snd_hctl_handle_events(fdl->hctl);
+        else
+            snd_mixer_handle_events(fdl->mixer);
+    }
+}
+
+static void defer_cb(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
+    struct pa_alsa_fdlist *fdl = userdata;
+    unsigned num_fds, i;
+    int err, n;
+    struct pollfd *temp;
+
+    pa_assert(a);
+    pa_assert(fdl);
+    pa_assert(fdl->mixer || fdl->hctl);
+
+    a->defer_enable(fdl->defer, 0);
+
+    if (fdl->hctl)
+        n = snd_hctl_poll_descriptors_count(fdl->hctl);
+    else
+        n = snd_mixer_poll_descriptors_count(fdl->mixer);
+
+    if (n < 0) {
+        pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
+        return;
+    }
+    else if (n == 0) {
+        pa_log_warn("Mixer has no poll descriptors. Please control mixer from PulseAudio only.");
+        return;
+    }
+    num_fds = (unsigned) n;
+
+    if (num_fds != fdl->num_fds) {
+        if (fdl->fds)
+            pa_xfree(fdl->fds);
+        if (fdl->work_fds)
+            pa_xfree(fdl->work_fds);
+        fdl->fds = pa_xnew0(struct pollfd, num_fds);
+        fdl->work_fds = pa_xnew(struct pollfd, num_fds);
+    }
+
+    memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
+
+    if (fdl->hctl)
+        err = snd_hctl_poll_descriptors(fdl->hctl, fdl->work_fds, num_fds);
+    else
+        err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds);
+
+    if (err < 0) {
+        pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
+        return;
+    }
+
+    fdl->polled = false;
+
+    if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
+        return;
+
+    if (fdl->ios) {
+        for (i = 0; i < fdl->num_fds; i++)
+            a->io_free(fdl->ios[i]);
+
+        if (num_fds != fdl->num_fds) {
+            pa_xfree(fdl->ios);
+            fdl->ios = NULL;
+        }
+    }
+
+    if (!fdl->ios)
+        fdl->ios = pa_xnew(pa_io_event*, num_fds);
+
+    /* Swap pointers */
+    temp = fdl->work_fds;
+    fdl->work_fds = fdl->fds;
+    fdl->fds = temp;
+
+    fdl->num_fds = num_fds;
+
+    for (i = 0;i < num_fds;i++)
+        fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
+            ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
+            ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
+            io_cb, fdl);
+}
+
+struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
+    struct pa_alsa_fdlist *fdl;
+
+    fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
+
+    return fdl;
+}
+
+void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
+    pa_assert(fdl);
+
+    if (fdl->defer) {
+        pa_assert(fdl->m);
+        fdl->m->defer_free(fdl->defer);
+    }
+
+    if (fdl->ios) {
+        unsigned i;
+        pa_assert(fdl->m);
+        for (i = 0; i < fdl->num_fds; i++)
+            fdl->m->io_free(fdl->ios[i]);
+        pa_xfree(fdl->ios);
+    }
+
+    if (fdl->fds)
+        pa_xfree(fdl->fds);
+    if (fdl->work_fds)
+        pa_xfree(fdl->work_fds);
+
+    pa_xfree(fdl);
+}
+
+/* We can listen to either a snd_hctl_t or a snd_mixer_t, but not both */
+int pa_alsa_fdlist_set_handle(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, snd_hctl_t *hctl_handle, pa_mainloop_api *m) {
+    pa_assert(fdl);
+    pa_assert(hctl_handle || mixer_handle);
+    pa_assert(!(hctl_handle && mixer_handle));
+    pa_assert(m);
+    pa_assert(!fdl->m);
+
+    fdl->hctl = hctl_handle;
+    fdl->mixer = mixer_handle;
+    fdl->m = m;
+    fdl->defer = m->defer_new(m, defer_cb, fdl);
+
+    return 0;
+}
+
+struct pa_alsa_mixer_pdata {
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *poll_item;
+    snd_mixer_t *mixer;
+};
+
+struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
+    struct pa_alsa_mixer_pdata *pd;
+
+    pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
+
+    return pd;
+}
+
+void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
+    pa_assert(pd);
+
+    if (pd->poll_item) {
+        pa_rtpoll_item_free(pd->poll_item);
+    }
+
+    pa_xfree(pd);
+}
+
+static int rtpoll_work_cb(pa_rtpoll_item *i) {
+    struct pa_alsa_mixer_pdata *pd;
+    struct pollfd *p;
+    unsigned n_fds;
+    unsigned short revents = 0;
+    int err, ret = 0;
+
+    pd = pa_rtpoll_item_get_userdata(i);
+    pa_assert_fp(pd);
+    pa_assert_fp(i == pd->poll_item);
+
+    p = pa_rtpoll_item_get_pollfd(i, &n_fds);
+
+    if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
+        pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
+        ret = -1;
+        goto fail;
+    }
+
+    if (revents) {
+        if (revents & (POLLNVAL | POLLERR)) {
+            pa_log_debug("Device disconnected, stopping poll on mixer");
+            goto fail;
+        } else if (revents & POLLERR) {
+            /* This shouldn't happen. */
+            pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents);
+            goto fail;
+        }
+
+        err = snd_mixer_handle_events(pd->mixer);
+
+        if (PA_LIKELY(err >= 0)) {
+            pa_rtpoll_item_free(i);
+            pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
+        } else {
+            pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err));
+            ret = -1;
+            goto fail;
+        }
+    }
+
+    return ret;
+
+fail:
+    pa_rtpoll_item_free(i);
+
+    pd->poll_item = NULL;
+    pd->rtpoll = NULL;
+    pd->mixer = NULL;
+
+    return ret;
+}
+
+int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
+    pa_rtpoll_item *i;
+    struct pollfd *p;
+    int err, n;
+
+    pa_assert(pd);
+    pa_assert(mixer);
+    pa_assert(rtp);
+
+    if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
+        pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
+        return -1;
+    }
+    else if (n == 0) {
+        pa_log_warn("Mixer has no poll descriptors. Please control mixer from PulseAudio only.");
+        return 0;
+    }
+
+    i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
+
+    p = pa_rtpoll_item_get_pollfd(i, NULL);
+
+    memset(p, 0, sizeof(struct pollfd) * n);
+
+    if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
+        pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
+        pa_rtpoll_item_free(i);
+        return -1;
+    }
+
+    pd->rtpoll = rtp;
+    pd->poll_item = i;
+    pd->mixer = mixer;
+
+    pa_rtpoll_item_set_userdata(i, pd);
+    pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb);
+
+    return 0;
+}
+
+static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
+    [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
+
+    [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
+    [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
+    [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
+
+    [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
+    [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
+    [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
+
+    [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
+
+    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+
+    [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
+    [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
+
+    [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX9] =  SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
+
+    [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+
+    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
+
+    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
+};
+
+static void setting_free(pa_alsa_setting *s) {
+    pa_assert(s);
+
+    if (s->options)
+        pa_idxset_free(s->options, NULL);
+
+    pa_xfree(s->name);
+    pa_xfree(s->description);
+    pa_xfree(s);
+}
+
+static void option_free(pa_alsa_option *o) {
+    pa_assert(o);
+
+    pa_xfree(o->alsa_name);
+    pa_xfree(o->name);
+    pa_xfree(o->description);
+    pa_xfree(o);
+}
+
+static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
+    pa_assert(db_fix);
+
+    pa_xfree(db_fix->name);
+    pa_xfree(db_fix->db_values);
+
+    pa_xfree(db_fix);
+}
+
+static void element_free(pa_alsa_element *e) {
+    pa_alsa_option *o;
+    pa_assert(e);
+
+    while ((o = e->options)) {
+        PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
+        option_free(o);
+    }
+
+    if (e->db_fix)
+        decibel_fix_free(e->db_fix);
+
+    pa_xfree(e->alsa_name);
+    pa_xfree(e);
+}
+
+void pa_alsa_path_free(pa_alsa_path *p) {
+    pa_alsa_jack *j;
+    pa_alsa_element *e;
+    pa_alsa_setting *s;
+
+    pa_assert(p);
+
+    while ((j = p->jacks)) {
+        PA_LLIST_REMOVE(pa_alsa_jack, p->jacks, j);
+        pa_alsa_jack_free(j);
+    }
+
+    while ((e = p->elements)) {
+        PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
+        element_free(e);
+    }
+
+    while ((s = p->settings)) {
+        PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
+        setting_free(s);
+    }
+
+    pa_proplist_free(p->proplist);
+    pa_xfree(p->name);
+    pa_xfree(p->description);
+    pa_xfree(p->description_key);
+    pa_xfree(p);
+}
+
+void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
+    pa_assert(ps);
+
+    if (ps->paths)
+        pa_hashmap_free(ps->paths);
+
+    pa_xfree(ps);
+}
+
+static long to_alsa_dB(pa_volume_t v) {
+    return (long) (pa_sw_volume_to_dB(v) * 100.0);
+}
+
+static pa_volume_t from_alsa_dB(long v) {
+    return pa_sw_volume_from_dB((double) v / 100.0);
+}
+
+static long to_alsa_volume(pa_volume_t v, long min, long max) {
+    long w;
+
+    w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
+    return PA_CLAMP_UNLIKELY(w, min, max);
+}
+
+static pa_volume_t from_alsa_volume(long v, long min, long max) {
+    return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
+}
+
+#define SELEM_INIT(sid, name)                           \
+    do {                                                \
+        snd_mixer_selem_id_alloca(&(sid));              \
+        snd_mixer_selem_id_set_name((sid), (name));     \
+        snd_mixer_selem_id_set_index((sid), 0);         \
+    } while(false)
+
+static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
+    snd_mixer_selem_id_t *sid;
+    snd_mixer_elem_t *me;
+    snd_mixer_selem_channel_id_t c;
+    pa_channel_position_mask_t mask = 0;
+    unsigned k;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(cm);
+    pa_assert(v);
+
+    SELEM_INIT(sid, e->alsa_name);
+    if (!(me = snd_mixer_find_selem(m, sid))) {
+        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+        return -1;
+    }
+
+    pa_cvolume_mute(v, cm->channels);
+
+    /* We take the highest volume of all channels that match */
+
+    for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
+        int r;
+        pa_volume_t f;
+
+        if (e->has_dB) {
+            long value = 0;
+
+            if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
+                if (snd_mixer_selem_has_playback_channel(me, c)) {
+                    if (e->db_fix) {
+                        if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) {
+                            /* If the channel volume is outside the limits set
+                             * by the dB fix, we clamp the hw volume to be
+                             * within the limits. */
+                            if (value < e->db_fix->min_step) {
+                                value = e->db_fix->min_step;
+                                snd_mixer_selem_set_playback_volume(me, c, value);
+                                pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
+                                             "Volume reset to %0.2f dB.", e->alsa_name, c,
+                                             e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
+                            } else if (value > e->db_fix->max_step) {
+                                value = e->db_fix->max_step;
+                                snd_mixer_selem_set_playback_volume(me, c, value);
+                                pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
+                                             "Volume reset to %0.2f dB.", e->alsa_name, c,
+                                             e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
+                            }
+
+                            /* Volume step -> dB value conversion. */
+                            value = e->db_fix->db_values[value - e->db_fix->min_step];
+                        }
+                    } else
+                        r = snd_mixer_selem_get_playback_dB(me, c, &value);
+                } else
+                    r = -1;
+            } else {
+                if (snd_mixer_selem_has_capture_channel(me, c)) {
+                    if (e->db_fix) {
+                        if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) {
+                            /* If the channel volume is outside the limits set
+                             * by the dB fix, we clamp the hw volume to be
+                             * within the limits. */
+                            if (value < e->db_fix->min_step) {
+                                value = e->db_fix->min_step;
+                                snd_mixer_selem_set_capture_volume(me, c, value);
+                                pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
+                                             "Volume reset to %0.2f dB.", e->alsa_name, c,
+                                             e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
+                            } else if (value > e->db_fix->max_step) {
+                                value = e->db_fix->max_step;
+                                snd_mixer_selem_set_capture_volume(me, c, value);
+                                pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
+                                             "Volume reset to %0.2f dB.", e->alsa_name, c,
+                                             e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
+                            }
+
+                            /* Volume step -> dB value conversion. */
+                            value = e->db_fix->db_values[value - e->db_fix->min_step];
+                        }
+                    } else
+                        r = snd_mixer_selem_get_capture_dB(me, c, &value);
+                } else
+                    r = -1;
+            }
+
+            if (r < 0)
+                continue;
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+                VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
+#endif
+
+            f = from_alsa_dB(value);
+
+        } else {
+            long value = 0;
+
+            if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
+                if (snd_mixer_selem_has_playback_channel(me, c))
+                    r = snd_mixer_selem_get_playback_volume(me, c, &value);
+                else
+                    r = -1;
+            } else {
+                if (snd_mixer_selem_has_capture_channel(me, c))
+                    r = snd_mixer_selem_get_capture_volume(me, c, &value);
+                else
+                    r = -1;
+            }
+
+            if (r < 0)
+                continue;
+
+            f = from_alsa_volume(value, e->min_volume, e->max_volume);
+        }
+
+        for (k = 0; k < cm->channels; k++)
+            if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
+                if (v->values[k] < f)
+                    v->values[k] = f;
+
+        mask |= e->masks[c][e->n_channels-1];
+    }
+
+    for (k = 0; k < cm->channels; k++)
+        if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
+            v->values[k] = PA_VOLUME_NORM;
+
+    return 0;
+}
+
+int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
+    pa_alsa_element *e;
+
+    pa_assert(m);
+    pa_assert(p);
+    pa_assert(cm);
+    pa_assert(v);
+
+    if (!p->has_volume)
+        return -1;
+
+    pa_cvolume_reset(v, cm->channels);
+
+    PA_LLIST_FOREACH(e, p->elements) {
+        pa_cvolume ev;
+
+        if (e->volume_use != PA_ALSA_VOLUME_MERGE)
+            continue;
+
+        pa_assert(!p->has_dB || e->has_dB);
+
+        if (element_get_volume(e, m, cm, &ev) < 0)
+            return -1;
+
+        /* If we have no dB information all we can do is take the first element and leave */
+        if (!p->has_dB) {
+            *v = ev;
+            return 0;
+        }
+
+        pa_sw_cvolume_multiply(v, v, &ev);
+    }
+
+    return 0;
+}
+
+static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, bool *b) {
+    snd_mixer_selem_id_t *sid;
+    snd_mixer_elem_t *me;
+    snd_mixer_selem_channel_id_t c;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(b);
+
+    SELEM_INIT(sid, e->alsa_name);
+    if (!(me = snd_mixer_find_selem(m, sid))) {
+        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+        return -1;
+    }
+
+    /* We return muted if at least one channel is muted */
+
+    for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
+        int r;
+        int value = 0;
+
+        if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
+            if (snd_mixer_selem_has_playback_channel(me, c))
+                r = snd_mixer_selem_get_playback_switch(me, c, &value);
+            else
+                r = -1;
+        } else {
+            if (snd_mixer_selem_has_capture_channel(me, c))
+                r = snd_mixer_selem_get_capture_switch(me, c, &value);
+            else
+                r = -1;
+        }
+
+        if (r < 0)
+            continue;
+
+        if (!value) {
+            *b = false;
+            return 0;
+        }
+    }
+
+    *b = true;
+    return 0;
+}
+
+int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, bool *muted) {
+    pa_alsa_element *e;
+
+    pa_assert(m);
+    pa_assert(p);
+    pa_assert(muted);
+
+    if (!p->has_mute)
+        return -1;
+
+    PA_LLIST_FOREACH(e, p->elements) {
+        bool b;
+
+        if (e->switch_use != PA_ALSA_SWITCH_MUTE)
+            continue;
+
+        if (element_get_switch(e, m, &b) < 0)
+            return -1;
+
+        if (!b) {
+            *muted = true;
+            return 0;
+        }
+    }
+
+    *muted = false;
+    return 0;
+}
+
+/* Finds the closest item in db_fix->db_values and returns the corresponding
+ * step. *db_value is replaced with the value from the db_values table.
+ * Rounding is done based on the rounding parameter: -1 means rounding down and
+ * +1 means rounding up. */
+static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, int rounding) {
+    unsigned i = 0;
+    unsigned max_i = 0;
+
+    pa_assert(db_fix);
+    pa_assert(db_value);
+    pa_assert(rounding != 0);
+
+    max_i = db_fix->max_step - db_fix->min_step;
+
+    if (rounding > 0) {
+        for (i = 0; i < max_i; i++) {
+            if (db_fix->db_values[i] >= *db_value)
+                break;
+        }
+    } else {
+        for (i = 0; i < max_i; i++) {
+            if (db_fix->db_values[i + 1] > *db_value)
+                break;
+        }
+    }
+
+    *db_value = db_fix->db_values[i];
+
+    return i + db_fix->min_step;
+}
+
+/* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
+ * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
+ * But even with accurate nearest dB volume step is not selected, so that is why we need
+ * this function. Returns 0 and nearest selectable volume in *value_dB on success or
+ * negative error code if fails. */
+static int element_get_nearest_alsa_dB(snd_mixer_elem_t *me, snd_mixer_selem_channel_id_t c, pa_alsa_direction_t d, long *value_dB) {
+
+    long alsa_val;
+    long value_high;
+    long value_low;
+    int r = -1;
+
+    pa_assert(me);
+    pa_assert(value_dB);
+
+    if (d == PA_ALSA_DIRECTION_OUTPUT) {
+        if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
+            r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_high);
+
+        if (r < 0)
+            return r;
+
+        if (value_high == *value_dB)
+            return r;
+
+        if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
+            r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_low);
+    } else {
+        if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
+            r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_high);
+
+        if (r < 0)
+            return r;
+
+        if (value_high == *value_dB)
+            return r;
+
+        if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
+            r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_low);
+    }
+
+    if (r < 0)
+        return r;
+
+    if (labs(value_high - *value_dB) < labs(value_low - *value_dB))
+        *value_dB = value_high;
+    else
+        *value_dB = value_low;
+
+    return r;
+}
+
+static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, bool deferred_volume, bool write_to_hw) {
+
+    snd_mixer_selem_id_t *sid;
+    pa_cvolume rv;
+    snd_mixer_elem_t *me;
+    snd_mixer_selem_channel_id_t c;
+    pa_channel_position_mask_t mask = 0;
+    unsigned k;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(cm);
+    pa_assert(v);
+    pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
+
+    SELEM_INIT(sid, e->alsa_name);
+    if (!(me = snd_mixer_find_selem(m, sid))) {
+        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+        return -1;
+    }
+
+    pa_cvolume_mute(&rv, cm->channels);
+
+    for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
+        int r;
+        pa_volume_t f = PA_VOLUME_MUTED;
+        bool found = false;
+
+        for (k = 0; k < cm->channels; k++)
+            if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
+                found = true;
+                if (v->values[k] > f)
+                    f = v->values[k];
+            }
+
+        if (!found) {
+            /* Hmm, so this channel does not exist in the volume
+             * struct, so let's bind it to the overall max of the
+             * volume. */
+            f = pa_cvolume_max(v);
+        }
+
+        if (e->has_dB) {
+            long value = to_alsa_dB(f);
+            int rounding;
+
+            if (e->volume_limit >= 0 && value > (e->max_dB * 100))
+                value = e->max_dB * 100;
+
+            if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
+                /* If we call set_playback_volume() without checking first
+                 * if the channel is available, ALSA behaves very
+                 * strangely and doesn't fail the call */
+                if (snd_mixer_selem_has_playback_channel(me, c)) {
+                    rounding = +1;
+                    if (e->db_fix) {
+                        if (write_to_hw)
+                            r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
+                        else {
+                            decibel_fix_get_step(e->db_fix, &value, rounding);
+                            r = 0;
+                        }
+
+                    } else {
+                        if (write_to_hw) {
+                            if (deferred_volume) {
+                                if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_OUTPUT, &value)) >= 0)
+                                    r = snd_mixer_selem_set_playback_dB(me, c, value, 0);
+                            } else {
+                                if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
+                                    r = snd_mixer_selem_get_playback_dB(me, c, &value);
+                           }
+                        } else {
+                            long alsa_val;
+                            if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0)
+                                r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
+                        }
+                    }
+                } else
+                    r = -1;
+            } else {
+                if (snd_mixer_selem_has_capture_channel(me, c)) {
+                    rounding = -1;
+                    if (e->db_fix) {
+                        if (write_to_hw)
+                            r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
+                        else {
+                            decibel_fix_get_step(e->db_fix, &value, rounding);
+                            r = 0;
+                        }
+
+                    } else {
+                        if (write_to_hw) {
+                            if (deferred_volume) {
+                                if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_INPUT, &value)) >= 0)
+                                    r = snd_mixer_selem_set_capture_dB(me, c, value, 0);
+                            } else {
+                                if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
+                                    r = snd_mixer_selem_get_capture_dB(me, c, &value);
+                            }
+                        } else {
+                            long alsa_val;
+                            if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0)
+                                r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
+                        }
+                    }
+                } else
+                    r = -1;
+            }
+
+            if (r < 0)
+                continue;
+
+            f = from_alsa_dB(value);
+
+        } else {
+            long value;
+
+            value = to_alsa_volume(f, e->min_volume, e->max_volume);
+
+            if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
+                if (snd_mixer_selem_has_playback_channel(me, c)) {
+                    if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
+                        r = snd_mixer_selem_get_playback_volume(me, c, &value);
+                } else
+                    r = -1;
+            } else {
+                if (snd_mixer_selem_has_capture_channel(me, c)) {
+                    if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
+                        r = snd_mixer_selem_get_capture_volume(me, c, &value);
+                } else
+                    r = -1;
+            }
+
+            if (r < 0)
+                continue;
+
+            f = from_alsa_volume(value, e->min_volume, e->max_volume);
+        }
+
+        for (k = 0; k < cm->channels; k++)
+            if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
+                if (rv.values[k] < f)
+                    rv.values[k] = f;
+
+        mask |= e->masks[c][e->n_channels-1];
+    }
+
+    for (k = 0; k < cm->channels; k++)
+        if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
+            rv.values[k] = PA_VOLUME_NORM;
+
+    *v = rv;
+    return 0;
+}
+
+int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, bool deferred_volume, bool write_to_hw) {
+
+    pa_alsa_element *e;
+    pa_cvolume rv;
+
+    pa_assert(m);
+    pa_assert(p);
+    pa_assert(cm);
+    pa_assert(v);
+    pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
+
+    if (!p->has_volume)
+        return -1;
+
+    rv = *v; /* Remaining adjustment */
+    pa_cvolume_reset(v, cm->channels); /* Adjustment done */
+
+    PA_LLIST_FOREACH(e, p->elements) {
+        pa_cvolume ev;
+
+        if (e->volume_use != PA_ALSA_VOLUME_MERGE)
+            continue;
+
+        pa_assert(!p->has_dB || e->has_dB);
+
+        ev = rv;
+        if (element_set_volume(e, m, cm, &ev, deferred_volume, write_to_hw) < 0)
+            return -1;
+
+        if (!p->has_dB) {
+            *v = ev;
+            return 0;
+        }
+
+        pa_sw_cvolume_multiply(v, v, &ev);
+        pa_sw_cvolume_divide(&rv, &rv, &ev);
+    }
+
+    return 0;
+}
+
+static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, bool b) {
+    snd_mixer_elem_t *me;
+    snd_mixer_selem_id_t *sid;
+    int r;
+
+    pa_assert(m);
+    pa_assert(e);
+
+    SELEM_INIT(sid, e->alsa_name);
+    if (!(me = snd_mixer_find_selem(m, sid))) {
+        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+        return -1;
+    }
+
+    if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+        r = snd_mixer_selem_set_playback_switch_all(me, b);
+    else
+        r = snd_mixer_selem_set_capture_switch_all(me, b);
+
+    if (r < 0)
+        pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
+
+    return r;
+}
+
+int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, bool muted) {
+    pa_alsa_element *e;
+
+    pa_assert(m);
+    pa_assert(p);
+
+    if (!p->has_mute)
+        return -1;
+
+    PA_LLIST_FOREACH(e, p->elements) {
+
+        if (e->switch_use != PA_ALSA_SWITCH_MUTE)
+            continue;
+
+        if (element_set_switch(e, m, !muted) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+/* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
+ * function sets all channels of the volume element to e->min_volume, 0 dB or
+ * e->constant_volume. */
+static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
+    snd_mixer_elem_t *me = NULL;
+    snd_mixer_selem_id_t *sid = NULL;
+    int r = 0;
+    long volume = -1;
+    bool volume_set = false;
+
+    pa_assert(m);
+    pa_assert(e);
+
+    SELEM_INIT(sid, e->alsa_name);
+    if (!(me = snd_mixer_find_selem(m, sid))) {
+        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+        return -1;
+    }
+
+    switch (e->volume_use) {
+        case PA_ALSA_VOLUME_OFF:
+            volume = e->min_volume;
+            volume_set = true;
+            break;
+
+        case PA_ALSA_VOLUME_ZERO:
+            if (e->db_fix) {
+                long dB = 0;
+
+                volume = decibel_fix_get_step(e->db_fix, &dB, (e->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1));
+                volume_set = true;
+            }
+            break;
+
+        case PA_ALSA_VOLUME_CONSTANT:
+            volume = e->constant_volume;
+            volume_set = true;
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    if (volume_set) {
+        if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+            r = snd_mixer_selem_set_playback_volume_all(me, volume);
+        else
+            r = snd_mixer_selem_set_capture_volume_all(me, volume);
+    } else {
+        pa_assert(e->volume_use == PA_ALSA_VOLUME_ZERO);
+        pa_assert(!e->db_fix);
+
+        if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+            r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
+        else
+            r = snd_mixer_selem_set_capture_dB_all(me, 0, -1);
+    }
+
+    if (r < 0)
+        pa_log_warn("Failed to set volume of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
+
+    return r;
+}
+
+int pa_alsa_path_select(pa_alsa_path *p, pa_alsa_setting *s, snd_mixer_t *m, bool device_is_muted) {
+    pa_alsa_element *e;
+    int r = 0;
+
+    pa_assert(m);
+    pa_assert(p);
+
+    pa_log_debug("Activating path %s", p->name);
+    pa_alsa_path_dump(p);
+
+    /* First turn on hw mute if available, to avoid noise
+     * when setting the mixer controls. */
+    if (p->mute_during_activation) {
+        PA_LLIST_FOREACH(e, p->elements) {
+            if (e->switch_use == PA_ALSA_SWITCH_MUTE)
+                /* If the muting fails here, that's not a critical problem for
+                 * selecting a path, so we ignore the return value.
+                 * element_set_switch() will print a warning anyway, so this
+                 * won't be a silent failure either. */
+                (void) element_set_switch(e, m, false);
+        }
+    }
+
+    PA_LLIST_FOREACH(e, p->elements) {
+
+        switch (e->switch_use) {
+            case PA_ALSA_SWITCH_OFF:
+                r = element_set_switch(e, m, false);
+                break;
+
+            case PA_ALSA_SWITCH_ON:
+                r = element_set_switch(e, m, true);
+                break;
+
+            case PA_ALSA_SWITCH_MUTE:
+            case PA_ALSA_SWITCH_IGNORE:
+            case PA_ALSA_SWITCH_SELECT:
+                r = 0;
+                break;
+        }
+
+        if (r < 0)
+            return -1;
+
+        switch (e->volume_use) {
+            case PA_ALSA_VOLUME_OFF:
+            case PA_ALSA_VOLUME_ZERO:
+            case PA_ALSA_VOLUME_CONSTANT:
+                r = element_set_constant_volume(e, m);
+                break;
+
+            case PA_ALSA_VOLUME_MERGE:
+            case PA_ALSA_VOLUME_IGNORE:
+                r = 0;
+                break;
+        }
+
+        if (r < 0)
+            return -1;
+    }
+
+    if (s)
+        setting_select(s, m);
+
+    /* Finally restore hw mute to the device mute status. */
+    if (p->mute_during_activation) {
+        PA_LLIST_FOREACH(e, p->elements) {
+            if (e->switch_use == PA_ALSA_SWITCH_MUTE) {
+                if (element_set_switch(e, m, !device_is_muted) < 0)
+                    return -1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
+    bool has_switch;
+    bool has_enumeration;
+    bool has_volume;
+
+    pa_assert(e);
+    pa_assert(me);
+
+    if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
+        has_switch =
+            snd_mixer_selem_has_playback_switch(me) ||
+            (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
+    } else {
+        has_switch =
+            snd_mixer_selem_has_capture_switch(me) ||
+            (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
+    }
+
+    if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
+        has_volume =
+            snd_mixer_selem_has_playback_volume(me) ||
+            (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
+    } else {
+        has_volume =
+            snd_mixer_selem_has_capture_volume(me) ||
+            (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
+    }
+
+    has_enumeration = snd_mixer_selem_is_enumerated(me);
+
+    if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
+        (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
+        (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
+        return -1;
+
+    if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
+        return -1;
+
+    if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
+        (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
+        (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
+        return -1;
+
+    if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
+        return -1;
+
+    if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
+        switch (e->required_any) {
+            case PA_ALSA_REQUIRED_VOLUME:
+                e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
+                break;
+            case PA_ALSA_REQUIRED_SWITCH:
+                e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
+                break;
+            case PA_ALSA_REQUIRED_ENUMERATION:
+                e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
+                break;
+            case PA_ALSA_REQUIRED_ANY:
+                e->path->req_any_present |=
+                    (e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
+                    (e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
+                    (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
+                break;
+            default:
+                pa_assert_not_reached();
+        }
+    }
+
+    if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
+        pa_alsa_option *o;
+        PA_LLIST_FOREACH(o, e->options) {
+            e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
+                (o->alsa_idx >= 0);
+            if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
+                return -1;
+            if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
+                return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int element_ask_vol_dB(snd_mixer_elem_t *me, pa_alsa_direction_t dir, long value, long *dBvalue) {
+    if (dir == PA_ALSA_DIRECTION_OUTPUT)
+        return snd_mixer_selem_ask_playback_vol_dB(me, value, dBvalue);
+    else
+        return snd_mixer_selem_ask_capture_vol_dB(me, value, dBvalue);
+}
+
+static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
+
+    long min_dB = 0, max_dB = 0;
+    int r;
+    bool is_mono;
+    pa_channel_position_t p;
+
+    if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
+        if (!snd_mixer_selem_has_playback_volume(me)) {
+            if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
+                e->direction = PA_ALSA_DIRECTION_INPUT;
+            else
+                return false;
+        }
+    } else {
+        if (!snd_mixer_selem_has_capture_volume(me)) {
+            if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
+                e->direction = PA_ALSA_DIRECTION_OUTPUT;
+            else
+                return false;
+        }
+    }
+
+    e->direction_try_other = false;
+
+    if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+        r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
+    else
+        r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
+
+    if (r < 0) {
+        pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
+        return false;
+    }
+
+    if (e->min_volume >= e->max_volume) {
+        pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.",
+                    e->min_volume, e->max_volume);
+        return false;
+    }
+    if (e->volume_use == PA_ALSA_VOLUME_CONSTANT && (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
+        pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
+                    e->constant_volume, e->alsa_name, e->min_volume, e->max_volume);
+        return false;
+    }
+
+
+    if (e->db_fix && ((e->min_volume > e->db_fix->min_step) || (e->max_volume < e->db_fix->max_step))) {
+          pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
+                      "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
+                      e->db_fix->min_step, e->db_fix->max_step, e->min_volume, e->max_volume);
+
+          decibel_fix_free(e->db_fix);
+          e->db_fix = NULL;
+    }
+
+    if (e->db_fix) {
+        e->has_dB = true;
+        e->min_volume = e->db_fix->min_step;
+        e->max_volume = e->db_fix->max_step;
+        min_dB = e->db_fix->db_values[0];
+        max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
+    } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+        e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
+    else
+        e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
+
+    /* Check that the kernel driver returns consistent limits with
+     * both _get_*_dB_range() and _ask_*_vol_dB(). */
+    if (e->has_dB && !e->db_fix) {
+        long min_dB_checked = 0;
+        long max_dB_checked = 0;
+
+        if (element_ask_vol_dB(me, e->direction, e->min_volume, &min_dB_checked) < 0) {
+            pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->min_volume);
+            return false;
+        }
+
+        if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB_checked) < 0) {
+            pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->max_volume);
+            return false;
+        }
+
+        if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
+            pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
+                        "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
+                        "%0.2f dB at level %li.", e->alsa_name, min_dB / 100.0, max_dB / 100.0,
+                        min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
+            return false;
+        }
+    }
+
+    if (e->has_dB) {
+        e->min_dB = ((double) min_dB) / 100.0;
+        e->max_dB = ((double) max_dB) / 100.0;
+
+        if (min_dB >= max_dB) {
+            pa_assert(!e->db_fix);
+            pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.",
+                        e->min_dB, e->max_dB);
+            e->has_dB = false;
+        }
+    }
+
+    if (e->volume_limit >= 0) {
+        if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
+            pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
+                        "%li-%li. The volume limit is ignored.",
+                        e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
+        else {
+            e->max_volume = e->volume_limit;
+
+            if (e->has_dB) {
+                if (e->db_fix) {
+                    e->db_fix->max_step = e->max_volume;
+                    e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
+                } else if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB) < 0) {
+                    pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
+                    e->has_dB = false;
+                } else
+                    e->max_dB = ((double) max_dB) / 100.0;
+            }
+        }
+    }
+
+    if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+        is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
+    else
+        is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
+
+    if (is_mono) {
+        e->n_channels = 1;
+
+        if (!e->override_map) {
+            for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
+                if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
+                    continue;
+                e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
+            }
+            e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
+        }
+        e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
+        return true;
+    }
+
+    e->n_channels = 0;
+    for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
+        if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
+            continue;
+
+        if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+            e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
+        else
+            e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
+    }
+
+    if (e->n_channels <= 0) {
+        pa_log_warn("Volume element %s with no channels?", e->alsa_name);
+        return false;
+    } else if (e->n_channels > 2) {
+        /* FIXME: In some places code like this is used:
+         *
+         *     e->masks[alsa_channel_ids[p]][e->n_channels-1]
+         *
+         * The definition of e->masks is
+         *
+         *     pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
+         *
+         * Since the array size is fixed at 2, we obviously
+         * don't support elements with more than two
+         * channels... */
+        pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e->alsa_name, e->n_channels);
+        return false;
+    }
+
+    if (!e->override_map) {
+        for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
+            bool has_channel;
+
+            if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
+                continue;
+
+            if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+                has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
+            else
+                has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
+
+            e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
+        }
+    }
+
+    e->merged_mask = 0;
+    for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
+        if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
+            continue;
+
+        e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
+    }
+    return true;
+}
+
+static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
+    snd_mixer_selem_id_t *sid;
+    snd_mixer_elem_t *me;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(e->path);
+
+    SELEM_INIT(sid, e->alsa_name);
+
+    if (!(me = snd_mixer_find_selem(m, sid))) {
+
+        if (e->required != PA_ALSA_REQUIRED_IGNORE)
+            return -1;
+
+        e->switch_use = PA_ALSA_SWITCH_IGNORE;
+        e->volume_use = PA_ALSA_VOLUME_IGNORE;
+        e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
+
+        return 0;
+    }
+
+    if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
+        if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
+
+            if (!snd_mixer_selem_has_playback_switch(me)) {
+                if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
+                    e->direction = PA_ALSA_DIRECTION_INPUT;
+                else
+                    e->switch_use = PA_ALSA_SWITCH_IGNORE;
+            }
+
+        } else {
+
+            if (!snd_mixer_selem_has_capture_switch(me)) {
+                if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
+                    e->direction = PA_ALSA_DIRECTION_OUTPUT;
+                else
+                    e->switch_use = PA_ALSA_SWITCH_IGNORE;
+            }
+        }
+
+        if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
+            e->direction_try_other = false;
+    }
+
+    if (!element_probe_volume(e, me))
+        e->volume_use = PA_ALSA_VOLUME_IGNORE;
+
+    if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
+        pa_alsa_option *o;
+
+        PA_LLIST_FOREACH(o, e->options)
+            o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
+    } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
+        int n;
+        pa_alsa_option *o;
+
+        if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
+            pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
+            return -1;
+        }
+
+        PA_LLIST_FOREACH(o, e->options) {
+            int i;
+
+            for (i = 0; i < n; i++) {
+                char buf[128];
+
+                if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
+                    continue;
+
+                if (!pa_streq(buf, o->alsa_name))
+                    continue;
+
+                o->alsa_idx = i;
+            }
+        }
+    }
+
+    if (check_required(e, me) < 0)
+        return -1;
+
+    return 0;
+}
+
+static int jack_probe(pa_alsa_jack *j, snd_mixer_t *m) {
+    bool has_control;
+
+    pa_assert(j);
+    pa_assert(j->path);
+
+    has_control = pa_alsa_mixer_find(m, j->alsa_name, 0) != NULL;
+    pa_alsa_jack_set_has_control(j, has_control);
+
+    if (j->has_control) {
+        if (j->required_absent != PA_ALSA_REQUIRED_IGNORE)
+            return -1;
+        if (j->required_any != PA_ALSA_REQUIRED_IGNORE)
+            j->path->req_any_present = true;
+    } else {
+        if (j->required != PA_ALSA_REQUIRED_IGNORE)
+            return -1;
+    }
+
+    return 0;
+}
+
+static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, bool prefixed) {
+    pa_alsa_element *e;
+
+    pa_assert(p);
+    pa_assert(section);
+
+    if (prefixed) {
+        if (!pa_startswith(section, "Element "))
+            return NULL;
+
+        section += 8;
+    }
+
+    /* This is not an element section, but an enum section? */
+    if (strchr(section, ':'))
+        return NULL;
+
+    if (p->last_element && pa_streq(p->last_element->alsa_name, section))
+        return p->last_element;
+
+    PA_LLIST_FOREACH(e, p->elements)
+        if (pa_streq(e->alsa_name, section))
+            goto finish;
+
+    e = pa_xnew0(pa_alsa_element, 1);
+    e->path = p;
+    e->alsa_name = pa_xstrdup(section);
+    e->direction = p->direction;
+    e->volume_limit = -1;
+
+    PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
+
+finish:
+    p->last_element = e;
+    return e;
+}
+
+static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) {
+    pa_alsa_jack *j;
+
+    if (!pa_startswith(section, "Jack "))
+        return NULL;
+    section += 5;
+
+    if (p->last_jack && pa_streq(p->last_jack->name, section))
+        return p->last_jack;
+
+    PA_LLIST_FOREACH(j, p->jacks)
+        if (pa_streq(j->name, section))
+            goto finish;
+
+    j = pa_alsa_jack_new(p, section);
+    PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
+
+finish:
+    p->last_jack = j;
+    return j;
+}
+
+static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
+    char *en;
+    const char *on;
+    pa_alsa_option *o;
+    pa_alsa_element *e;
+
+    if (!pa_startswith(section, "Option "))
+        return NULL;
+
+    section += 7;
+
+    /* This is not an enum section, but an element section? */
+    if (!(on = strchr(section, ':')))
+        return NULL;
+
+    en = pa_xstrndup(section, on - section);
+    on++;
+
+    if (p->last_option &&
+        pa_streq(p->last_option->element->alsa_name, en) &&
+        pa_streq(p->last_option->alsa_name, on)) {
+        pa_xfree(en);
+        return p->last_option;
+    }
+
+    pa_assert_se(e = element_get(p, en, false));
+    pa_xfree(en);
+
+    PA_LLIST_FOREACH(o, e->options)
+        if (pa_streq(o->alsa_name, on))
+            goto finish;
+
+    o = pa_xnew0(pa_alsa_option, 1);
+    o->element = e;
+    o->alsa_name = pa_xstrdup(on);
+    o->alsa_idx = -1;
+
+    if (p->last_option && p->last_option->element == e)
+        PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
+    else
+        PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
+
+finish:
+    p->last_option = o;
+    return o;
+}
+
+static int element_parse_switch(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_element *e;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    if (!(e = element_get(p, state->section, true))) {
+        pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->rvalue, "ignore"))
+        e->switch_use = PA_ALSA_SWITCH_IGNORE;
+    else if (pa_streq(state->rvalue, "mute"))
+        e->switch_use = PA_ALSA_SWITCH_MUTE;
+    else if (pa_streq(state->rvalue, "off"))
+        e->switch_use = PA_ALSA_SWITCH_OFF;
+    else if (pa_streq(state->rvalue, "on"))
+        e->switch_use = PA_ALSA_SWITCH_ON;
+    else if (pa_streq(state->rvalue, "select"))
+        e->switch_use = PA_ALSA_SWITCH_SELECT;
+    else {
+        pa_log("[%s:%u] Switch invalid of '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int element_parse_volume(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_element *e;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    if (!(e = element_get(p, state->section, true))) {
+        pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->rvalue, "ignore"))
+        e->volume_use = PA_ALSA_VOLUME_IGNORE;
+    else if (pa_streq(state->rvalue, "merge"))
+        e->volume_use = PA_ALSA_VOLUME_MERGE;
+    else if (pa_streq(state->rvalue, "off"))
+        e->volume_use = PA_ALSA_VOLUME_OFF;
+    else if (pa_streq(state->rvalue, "zero"))
+        e->volume_use = PA_ALSA_VOLUME_ZERO;
+    else {
+        uint32_t constant;
+
+        if (pa_atou(state->rvalue, &constant) >= 0) {
+            e->volume_use = PA_ALSA_VOLUME_CONSTANT;
+            e->constant_volume = constant;
+        } else {
+            pa_log("[%s:%u] Volume invalid of '%s'", state->filename, state->lineno, state->section);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int element_parse_enumeration(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_element *e;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    if (!(e = element_get(p, state->section, true))) {
+        pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->rvalue, "ignore"))
+        e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
+    else if (pa_streq(state->rvalue, "select"))
+        e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
+    else {
+        pa_log("[%s:%u] Enumeration invalid of '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int option_parse_priority(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_option *o;
+    uint32_t prio;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    if (!(o = option_get(p, state->section))) {
+        pa_log("[%s:%u] Priority makes no sense in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if (pa_atou(state->rvalue, &prio) < 0) {
+        pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    o->priority = prio;
+    return 0;
+}
+
+static int option_parse_name(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_option *o;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    if (!(o = option_get(p, state->section))) {
+        pa_log("[%s:%u] Name makes no sense in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    pa_xfree(o->name);
+    o->name = pa_xstrdup(state->rvalue);
+
+    return 0;
+}
+
+static int element_parse_required(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_element *e;
+    pa_alsa_option *o;
+    pa_alsa_jack *j;
+    pa_alsa_required_t req;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    e = element_get(p, state->section, true);
+    o = option_get(p, state->section);
+    j = jack_get(p, state->section);
+    if (!e && !o && !j) {
+        pa_log("[%s:%u] Required makes no sense in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->rvalue, "ignore"))
+        req = PA_ALSA_REQUIRED_IGNORE;
+    else if (pa_streq(state->rvalue, "switch") && e)
+        req = PA_ALSA_REQUIRED_SWITCH;
+    else if (pa_streq(state->rvalue, "volume") && e)
+        req = PA_ALSA_REQUIRED_VOLUME;
+    else if (pa_streq(state->rvalue, "enumeration"))
+        req = PA_ALSA_REQUIRED_ENUMERATION;
+    else if (pa_streq(state->rvalue, "any"))
+        req = PA_ALSA_REQUIRED_ANY;
+    else {
+        pa_log("[%s:%u] Required invalid of '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->lvalue, "required-absent")) {
+        if (e)
+            e->required_absent = req;
+        if (o)
+            o->required_absent = req;
+        if (j)
+            j->required_absent = req;
+    }
+    else if (pa_streq(state->lvalue, "required-any")) {
+        if (e) {
+            e->required_any = req;
+            e->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
+        }
+        if (o) {
+            o->required_any = req;
+            o->element->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
+        }
+        if (j) {
+            j->required_any = req;
+            j->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
+        }
+
+    }
+    else {
+        if (e)
+            e->required = req;
+        if (o)
+            o->required = req;
+        if (j)
+            j->required = req;
+    }
+
+    return 0;
+}
+
+static int element_parse_direction(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_element *e;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    if (!(e = element_get(p, state->section, true))) {
+        pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->rvalue, "playback"))
+        e->direction = PA_ALSA_DIRECTION_OUTPUT;
+    else if (pa_streq(state->rvalue, "capture"))
+        e->direction = PA_ALSA_DIRECTION_INPUT;
+    else {
+        pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int element_parse_direction_try_other(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_element *e;
+    int yes;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    if (!(e = element_get(p, state->section, true))) {
+        pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if ((yes = pa_parse_boolean(state->rvalue)) < 0) {
+        pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    e->direction_try_other = !!yes;
+    return 0;
+}
+
+static int element_parse_volume_limit(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_element *e;
+    long volume_limit;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    if (!(e = element_get(p, state->section, true))) {
+        pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if (pa_atol(state->rvalue, &volume_limit) < 0 || volume_limit < 0) {
+        pa_log("[%s:%u] Invalid value for volume-limit", state->filename, state->lineno);
+        return -1;
+    }
+
+    e->volume_limit = volume_limit;
+    return 0;
+}
+
+static pa_channel_position_mask_t parse_mask(const char *m) {
+    pa_channel_position_mask_t v;
+
+    if (pa_streq(m, "all-left"))
+        v = PA_CHANNEL_POSITION_MASK_LEFT;
+    else if (pa_streq(m, "all-right"))
+        v = PA_CHANNEL_POSITION_MASK_RIGHT;
+    else if (pa_streq(m, "all-center"))
+        v = PA_CHANNEL_POSITION_MASK_CENTER;
+    else if (pa_streq(m, "all-front"))
+        v = PA_CHANNEL_POSITION_MASK_FRONT;
+    else if (pa_streq(m, "all-rear"))
+        v = PA_CHANNEL_POSITION_MASK_REAR;
+    else if (pa_streq(m, "all-side"))
+        v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
+    else if (pa_streq(m, "all-top"))
+        v = PA_CHANNEL_POSITION_MASK_TOP;
+    else if (pa_streq(m, "all-no-lfe"))
+        v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
+    else if (pa_streq(m, "all"))
+        v = PA_CHANNEL_POSITION_MASK_ALL;
+    else {
+        pa_channel_position_t p;
+
+        if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
+            return 0;
+
+        v = PA_CHANNEL_POSITION_MASK(p);
+    }
+
+    return v;
+}
+
+static int element_parse_override_map(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_element *e;
+    const char *split_state = NULL;
+    unsigned i = 0;
+    char *n;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    if (!(e = element_get(p, state->section, true))) {
+        pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    while ((n = pa_split(state->rvalue, ",", &split_state))) {
+        pa_channel_position_mask_t m;
+
+        if (!*n)
+            m = 0;
+        else {
+            if ((m = parse_mask(n)) == 0) {
+                pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
+                pa_xfree(n);
+                return -1;
+            }
+        }
+
+        if (pa_streq(state->lvalue, "override-map.1"))
+            e->masks[i++][0] = m;
+        else
+            e->masks[i++][1] = m;
+
+        /* Later on we might add override-map.3 and so on here ... */
+
+        pa_xfree(n);
+    }
+
+    e->override_map = true;
+
+    return 0;
+}
+
+static int jack_parse_state(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_jack *j;
+    pa_available_t pa;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    if (!(j = jack_get(p, state->section))) {
+        pa_log("[%s:%u] state makes no sense in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->rvalue, "yes"))
+        pa = PA_AVAILABLE_YES;
+    else if (pa_streq(state->rvalue, "no"))
+        pa = PA_AVAILABLE_NO;
+    else if (pa_streq(state->rvalue, "unknown"))
+        pa = PA_AVAILABLE_UNKNOWN;
+    else {
+        pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->lvalue, "state.unplugged"))
+        j->state_unplugged = pa;
+    else {
+        j->state_plugged = pa;
+        pa_assert(pa_streq(state->lvalue, "state.plugged"));
+    }
+
+    return 0;
+}
+
+static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
+    snd_mixer_selem_id_t *sid;
+    snd_mixer_elem_t *me;
+    int r;
+
+    pa_assert(e);
+    pa_assert(m);
+
+    SELEM_INIT(sid, e->alsa_name);
+    if (!(me = snd_mixer_find_selem(m, sid))) {
+        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+        return -1;
+    }
+
+    if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
+
+        if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+            r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
+        else
+            r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
+
+        if (r < 0)
+            pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
+
+    } else {
+        pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
+
+        if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
+            pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
+    }
+
+    return r;
+}
+
+static int setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
+    pa_alsa_option *o;
+    uint32_t idx;
+
+    pa_assert(s);
+    pa_assert(m);
+
+    PA_IDXSET_FOREACH(o, s->options, idx)
+        element_set_option(o->element, m, o->alsa_idx);
+
+    return 0;
+}
+
+static int option_verify(pa_alsa_option *o) {
+    static const struct description_map well_known_descriptions[] = {
+        { "input",                     N_("Input") },
+        { "input-docking",             N_("Docking Station Input") },
+        { "input-docking-microphone",  N_("Docking Station Microphone") },
+        { "input-docking-linein",      N_("Docking Station Line In") },
+        { "input-linein",              N_("Line In") },
+        { "input-microphone",          N_("Microphone") },
+        { "input-microphone-front",    N_("Front Microphone") },
+        { "input-microphone-rear",     N_("Rear Microphone") },
+        { "input-microphone-external", N_("External Microphone") },
+        { "input-microphone-internal", N_("Internal Microphone") },
+        { "input-radio",               N_("Radio") },
+        { "input-video",               N_("Video") },
+        { "input-agc-on",              N_("Automatic Gain Control") },
+        { "input-agc-off",             N_("No Automatic Gain Control") },
+        { "input-boost-on",            N_("Boost") },
+        { "input-boost-off",           N_("No Boost") },
+        { "output-amplifier-on",       N_("Amplifier") },
+        { "output-amplifier-off",      N_("No Amplifier") },
+        { "output-bass-boost-on",      N_("Bass Boost") },
+        { "output-bass-boost-off",     N_("No Bass Boost") },
+        { "output-speaker",            N_("Speaker") },
+        { "output-headphones",         N_("Headphones") }
+    };
+
+    pa_assert(o);
+
+    if (!o->name) {
+        pa_log("No name set for option %s", o->alsa_name);
+        return -1;
+    }
+
+    if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
+        o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
+        pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
+        return -1;
+    }
+
+    if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
+        !pa_streq(o->alsa_name, "on") &&
+        !pa_streq(o->alsa_name, "off")) {
+        pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
+        return -1;
+    }
+
+    if (!o->description)
+        o->description = pa_xstrdup(lookup_description(o->name,
+                                                       well_known_descriptions,
+                                                       PA_ELEMENTSOF(well_known_descriptions)));
+    if (!o->description)
+        o->description = pa_xstrdup(o->name);
+
+    return 0;
+}
+
+static int element_verify(pa_alsa_element *e) {
+    pa_alsa_option *o;
+
+    pa_assert(e);
+
+//    pa_log_debug("Element %s, path %s: r=%d, r-any=%d, r-abs=%d", e->alsa_name, e->path->name, e->required, e->required_any, e->required_absent);
+    if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
+        (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
+        (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
+        (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
+        pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
+        return -1;
+    }
+
+    if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
+        pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
+        return -1;
+    }
+
+    PA_LLIST_FOREACH(o, e->options)
+        if (option_verify(o) < 0)
+            return -1;
+
+    return 0;
+}
+
+static int path_verify(pa_alsa_path *p) {
+    static const struct description_map well_known_descriptions[] = {
+        { "analog-input",               N_("Analog Input") },
+        { "analog-input-microphone",    N_("Microphone") },
+        { "analog-input-microphone-front",    N_("Front Microphone") },
+        { "analog-input-microphone-rear",     N_("Rear Microphone") },
+        { "analog-input-microphone-dock",     N_("Dock Microphone") },
+        { "analog-input-microphone-internal", N_("Internal Microphone") },
+        { "analog-input-microphone-headset",  N_("Headset Microphone") },
+        { "analog-input-linein",        N_("Line In") },
+        { "analog-input-radio",         N_("Radio") },
+        { "analog-input-video",         N_("Video") },
+        { "analog-output",              N_("Analog Output") },
+        { "analog-output-headphones",   N_("Headphones") },
+        { "analog-output-lfe-on-mono",  N_("LFE on Separate Mono Output") },
+        { "analog-output-lineout",      N_("Line Out") },
+        { "analog-output-mono",         N_("Analog Mono Output") },
+        { "analog-output-speaker",      N_("Speakers") },
+        { "hdmi-output",                N_("HDMI / DisplayPort") },
+        { "iec958-stereo-output",       N_("Digital Output (S/PDIF)") },
+        { "iec958-stereo-input",        N_("Digital Input (S/PDIF)") },
+        { "iec958-passthrough-output",  N_("Digital Passthrough (S/PDIF)") },
+        { "multichannel-input",         N_("Multichannel Input") },
+        { "multichannel-output",        N_("Multichannel Output") },
+    };
+
+    pa_alsa_element *e;
+
+    pa_assert(p);
+
+    PA_LLIST_FOREACH(e, p->elements)
+        if (element_verify(e) < 0)
+            return -1;
+
+    if (!p->description)
+        p->description = pa_xstrdup(lookup_description(p->description_key ? p->description_key : p->name,
+                                                       well_known_descriptions,
+                                                       PA_ELEMENTSOF(well_known_descriptions)));
+
+    if (!p->description) {
+        if (p->description_key)
+            pa_log_warn("Path %s: Unrecognized description key: %s", p->name, p->description_key);
+
+        p->description = pa_xstrdup(p->name);
+    }
+
+    return 0;
+}
+
+static const char *get_default_paths_dir(void) {
+    if (pa_run_from_build_tree())
+        return PA_SRCDIR "/modules/alsa/mixer/paths/";
+    else
+        return PA_ALSA_PATHS_DIR;
+}
+
+pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
+    pa_alsa_path *p;
+    char *fn;
+    int r;
+    const char *n;
+    bool mute_during_activation = false;
+
+    pa_config_item items[] = {
+        /* [General] */
+        { "priority",            pa_config_parse_unsigned,          NULL, "General" },
+        { "description-key",     pa_config_parse_string,            NULL, "General" },
+        { "description",         pa_config_parse_string,            NULL, "General" },
+        { "mute-during-activation", pa_config_parse_bool,           NULL, "General" },
+        { "eld-device",          pa_config_parse_int,               NULL, "General" },
+
+        /* [Option ...] */
+        { "priority",            option_parse_priority,             NULL, NULL },
+        { "name",                option_parse_name,                 NULL, NULL },
+
+        /* [Jack ...] */
+        { "state.plugged",       jack_parse_state,                  NULL, NULL },
+        { "state.unplugged",     jack_parse_state,                  NULL, NULL },
+
+        /* [Element ...] */
+        { "switch",              element_parse_switch,              NULL, NULL },
+        { "volume",              element_parse_volume,              NULL, NULL },
+        { "enumeration",         element_parse_enumeration,         NULL, NULL },
+        { "override-map.1",      element_parse_override_map,        NULL, NULL },
+        { "override-map.2",      element_parse_override_map,        NULL, NULL },
+        /* ... later on we might add override-map.3 and so on here ... */
+        { "required",            element_parse_required,            NULL, NULL },
+        { "required-any",        element_parse_required,            NULL, NULL },
+        { "required-absent",     element_parse_required,            NULL, NULL },
+        { "direction",           element_parse_direction,           NULL, NULL },
+        { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
+        { "volume-limit",        element_parse_volume_limit,        NULL, NULL },
+        { NULL, NULL, NULL, NULL }
+    };
+
+    pa_assert(fname);
+
+    p = pa_xnew0(pa_alsa_path, 1);
+    n = pa_path_get_filename(fname);
+    p->name = pa_xstrndup(n, strcspn(n, "."));
+    p->proplist = pa_proplist_new();
+    p->direction = direction;
+    p->eld_device = -1;
+
+    items[0].data = &p->priority;
+    items[1].data = &p->description_key;
+    items[2].data = &p->description;
+    items[3].data = &mute_during_activation;
+    items[4].data = &p->eld_device;
+
+    if (!paths_dir)
+        paths_dir = get_default_paths_dir();
+
+    fn = pa_maybe_prefix_path(fname, paths_dir);
+
+    r = pa_config_parse(fn, NULL, items, p->proplist, false, p);
+    pa_xfree(fn);
+
+    if (r < 0)
+        goto fail;
+
+    p->mute_during_activation = mute_during_activation;
+
+    if (path_verify(p) < 0)
+        goto fail;
+
+    return p;
+
+fail:
+    pa_alsa_path_free(p);
+    return NULL;
+}
+
+pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
+    pa_alsa_path *p;
+    pa_alsa_element *e;
+
+    pa_assert(element);
+
+    p = pa_xnew0(pa_alsa_path, 1);
+    p->name = pa_xstrdup(element);
+    p->direction = direction;
+    p->proplist = pa_proplist_new();
+
+    e = pa_xnew0(pa_alsa_element, 1);
+    e->path = p;
+    e->alsa_name = pa_xstrdup(element);
+    e->direction = direction;
+    e->volume_limit = -1;
+
+    e->switch_use = PA_ALSA_SWITCH_MUTE;
+    e->volume_use = PA_ALSA_VOLUME_MERGE;
+
+    PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
+    p->last_element = e;
+    return p;
+}
+
+static bool element_drop_unsupported(pa_alsa_element *e) {
+    pa_alsa_option *o, *n;
+
+    pa_assert(e);
+
+    for (o = e->options; o; o = n) {
+        n = o->next;
+
+        if (o->alsa_idx < 0) {
+            PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
+            option_free(o);
+        }
+    }
+
+    return
+        e->switch_use != PA_ALSA_SWITCH_IGNORE ||
+        e->volume_use != PA_ALSA_VOLUME_IGNORE ||
+        e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
+}
+
+static void path_drop_unsupported(pa_alsa_path *p) {
+    pa_alsa_element *e, *n;
+
+    pa_assert(p);
+
+    for (e = p->elements; e; e = n) {
+        n = e->next;
+
+        if (!element_drop_unsupported(e)) {
+            PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
+            element_free(e);
+        }
+    }
+}
+
+static void path_make_options_unique(pa_alsa_path *p) {
+    pa_alsa_element *e;
+    pa_alsa_option *o, *u;
+
+    PA_LLIST_FOREACH(e, p->elements) {
+        PA_LLIST_FOREACH(o, e->options) {
+            unsigned i;
+            char *m;
+
+            for (u = o->next; u; u = u->next)
+                if (pa_streq(u->name, o->name))
+                    break;
+
+            if (!u)
+                continue;
+
+            m = pa_xstrdup(o->name);
+
+            /* OK, this name is not unique, hence let's rename */
+            for (i = 1, u = o; u; u = u->next) {
+                char *nn, *nd;
+
+                if (!pa_streq(u->name, m))
+                    continue;
+
+                nn = pa_sprintf_malloc("%s-%u", m, i);
+                pa_xfree(u->name);
+                u->name = nn;
+
+                nd = pa_sprintf_malloc("%s %u", u->description, i);
+                pa_xfree(u->description);
+                u->description = nd;
+
+                i++;
+            }
+
+            pa_xfree(m);
+        }
+    }
+}
+
+static bool element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
+    pa_alsa_option *o;
+
+    for (; e; e = e->next)
+        if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
+            e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
+            break;
+
+    if (!e)
+        return false;
+
+    for (o = e->options; o; o = o->next) {
+        pa_alsa_setting *s;
+
+        if (template) {
+            s = pa_xnewdup(pa_alsa_setting, template, 1);
+            s->options = pa_idxset_copy(template->options, NULL);
+            s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
+            s->description =
+                (template->description[0] && o->description[0])
+                ? pa_sprintf_malloc("%s / %s", template->description, o->description)
+                : (template->description[0]
+                   ? pa_xstrdup(template->description)
+                   : pa_xstrdup(o->description));
+
+            s->priority = PA_MAX(template->priority, o->priority);
+        } else {
+            s = pa_xnew0(pa_alsa_setting, 1);
+            s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+            s->name = pa_xstrdup(o->name);
+            s->description = pa_xstrdup(o->description);
+            s->priority = o->priority;
+        }
+
+        pa_idxset_put(s->options, o, NULL);
+
+        if (element_create_settings(e->next, s))
+            /* This is not a leaf, so let's get rid of it */
+            setting_free(s);
+        else {
+            /* This is a leaf, so let's add it */
+            PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
+
+            e->path->last_setting = s;
+        }
+    }
+
+    return true;
+}
+
+static void path_create_settings(pa_alsa_path *p) {
+    pa_assert(p);
+
+    element_create_settings(p->elements, NULL);
+}
+
+int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, bool ignore_dB) {
+    pa_alsa_element *e;
+    pa_alsa_jack *j;
+    double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
+    pa_channel_position_t t;
+    pa_channel_position_mask_t path_volume_channels = 0;
+
+    pa_assert(p);
+    pa_assert(m);
+
+    if (p->probed)
+        return p->supported ? 0 : -1;
+    p->probed = true;
+
+    pa_zero(min_dB);
+    pa_zero(max_dB);
+
+    pa_log_debug("Probing path '%s'", p->name);
+
+    PA_LLIST_FOREACH(j, p->jacks) {
+        if (jack_probe(j, m) < 0) {
+            p->supported = false;
+            pa_log_debug("Probe of jack '%s' failed.", j->alsa_name);
+            return -1;
+        }
+        pa_log_debug("Probe of jack '%s' succeeded (%s)", j->alsa_name, j->has_control ? "found!" : "not found");
+    }
+
+    PA_LLIST_FOREACH(e, p->elements) {
+        if (element_probe(e, m) < 0) {
+            p->supported = false;
+            pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
+            return -1;
+        }
+        pa_log_debug("Probe of element '%s' succeeded (volume=%d, switch=%d, enumeration=%d).", e->alsa_name, e->volume_use, e->switch_use, e->enumeration_use);
+
+        if (ignore_dB)
+            e->has_dB = false;
+
+        if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
+
+            if (!p->has_volume) {
+                p->min_volume = e->min_volume;
+                p->max_volume = e->max_volume;
+            }
+
+            if (e->has_dB) {
+                if (!p->has_volume) {
+                    for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
+                        if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
+                            min_dB[t] = e->min_dB;
+                            max_dB[t] = e->max_dB;
+                            path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
+                        }
+
+                    p->has_dB = true;
+                } else {
+
+                    if (p->has_dB) {
+                        for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
+                            if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
+                                min_dB[t] += e->min_dB;
+                                max_dB[t] += e->max_dB;
+                                path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
+                            }
+                    } else {
+                        /* Hmm, there's another element before us
+                         * which cannot do dB volumes, so we we need
+                         * to 'neutralize' this slider */
+                        e->volume_use = PA_ALSA_VOLUME_ZERO;
+                        pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
+                    }
+                }
+            } else if (p->has_volume) {
+                /* We can't use this volume, so let's ignore it */
+                e->volume_use = PA_ALSA_VOLUME_IGNORE;
+                pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
+            }
+            p->has_volume = true;
+        }
+
+        if (e->switch_use == PA_ALSA_SWITCH_MUTE)
+            p->has_mute = true;
+    }
+
+    if (p->has_req_any && !p->req_any_present) {
+        p->supported = false;
+        pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
+        return -1;
+    }
+
+    path_drop_unsupported(p);
+    path_make_options_unique(p);
+    path_create_settings(p);
+
+    p->supported = true;
+
+    p->min_dB = INFINITY;
+    p->max_dB = -INFINITY;
+
+    for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
+        if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
+            if (p->min_dB > min_dB[t])
+                p->min_dB = min_dB[t];
+
+            if (p->max_dB < max_dB[t])
+                p->max_dB = max_dB[t];
+        }
+    }
+
+    return 0;
+}
+
+void pa_alsa_setting_dump(pa_alsa_setting *s) {
+    pa_assert(s);
+
+    pa_log_debug("Setting %s (%s) priority=%u",
+                 s->name,
+                 pa_strnull(s->description),
+                 s->priority);
+}
+
+void pa_alsa_jack_dump(pa_alsa_jack *j) {
+    pa_assert(j);
+
+    pa_log_debug("Jack %s, alsa_name='%s', detection %s", j->name, j->alsa_name, j->has_control ? "possible" : "unavailable");
+}
+
+void pa_alsa_option_dump(pa_alsa_option *o) {
+    pa_assert(o);
+
+    pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
+                 o->alsa_name,
+                 pa_strnull(o->name),
+                 pa_strnull(o->description),
+                 o->alsa_idx,
+                 o->priority);
+}
+
+void pa_alsa_element_dump(pa_alsa_element *e) {
+    pa_alsa_option *o;
+    pa_assert(e);
+
+    pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
+                 e->alsa_name,
+                 e->direction,
+                 e->switch_use,
+                 e->volume_use,
+                 e->volume_limit,
+                 e->enumeration_use,
+                 e->required,
+                 e->required_any,
+                 e->required_absent,
+                 (long long unsigned) e->merged_mask,
+                 e->n_channels,
+                 pa_yes_no(e->override_map));
+
+    PA_LLIST_FOREACH(o, e->options)
+        pa_alsa_option_dump(o);
+}
+
+void pa_alsa_path_dump(pa_alsa_path *p) {
+    pa_alsa_element *e;
+    pa_alsa_jack *j;
+    pa_alsa_setting *s;
+    pa_assert(p);
+
+    pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
+                 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
+                 p->name,
+                 pa_strnull(p->description),
+                 p->direction,
+                 p->priority,
+                 pa_yes_no(p->probed),
+                 pa_yes_no(p->supported),
+                 pa_yes_no(p->has_mute),
+                 pa_yes_no(p->has_volume),
+                 pa_yes_no(p->has_dB),
+                 p->min_volume, p->max_volume,
+                 p->min_dB, p->max_dB);
+
+    PA_LLIST_FOREACH(e, p->elements)
+        pa_alsa_element_dump(e);
+
+    PA_LLIST_FOREACH(j, p->jacks)
+        pa_alsa_jack_dump(j);
+
+    PA_LLIST_FOREACH(s, p->settings)
+        pa_alsa_setting_dump(s);
+}
+
+static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
+    snd_mixer_selem_id_t *sid;
+    snd_mixer_elem_t *me;
+
+    pa_assert(e);
+    pa_assert(m);
+    pa_assert(cb);
+
+    SELEM_INIT(sid, e->alsa_name);
+    if (!(me = snd_mixer_find_selem(m, sid))) {
+        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+        return;
+    }
+
+    snd_mixer_elem_set_callback(me, cb);
+    snd_mixer_elem_set_callback_private(me, userdata);
+}
+
+void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
+    pa_alsa_element *e;
+
+    pa_assert(p);
+    pa_assert(m);
+    pa_assert(cb);
+
+    PA_LLIST_FOREACH(e, p->elements)
+        element_set_callback(e, m, cb, userdata);
+}
+
+void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
+    pa_alsa_path *p;
+    void *state;
+
+    pa_assert(ps);
+    pa_assert(m);
+    pa_assert(cb);
+
+    PA_HASHMAP_FOREACH(p, ps->paths, state)
+        pa_alsa_path_set_callback(p, m, cb, userdata);
+}
+
+static pa_alsa_path *profile_set_get_path(pa_alsa_profile_set *ps, const char *path_name) {
+    pa_alsa_path *path;
+
+    pa_assert(ps);
+    pa_assert(path_name);
+
+    if ((path = pa_hashmap_get(ps->output_paths, path_name)))
+        return path;
+
+    return pa_hashmap_get(ps->input_paths, path_name);
+}
+
+static void profile_set_add_path(pa_alsa_profile_set *ps, pa_alsa_path *path) {
+    pa_assert(ps);
+    pa_assert(path);
+
+    switch (path->direction) {
+        case PA_ALSA_DIRECTION_OUTPUT:
+            pa_assert_se(pa_hashmap_put(ps->output_paths, path->name, path) >= 0);
+            break;
+
+        case PA_ALSA_DIRECTION_INPUT:
+            pa_assert_se(pa_hashmap_put(ps->input_paths, path->name, path) >= 0);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+}
+
+pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
+    pa_alsa_path_set *ps;
+    char **pn = NULL, **en = NULL, **ie;
+    pa_alsa_decibel_fix *db_fix;
+    void *state, *state2;
+
+    pa_assert(m);
+    pa_assert(m->profile_set);
+    pa_assert(m->profile_set->decibel_fixes);
+    pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
+
+    if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
+        return NULL;
+
+    ps = pa_xnew0(pa_alsa_path_set, 1);
+    ps->direction = direction;
+    ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    if (direction == PA_ALSA_DIRECTION_OUTPUT)
+        pn = m->output_path_names;
+    else
+        pn = m->input_path_names;
+
+    if (pn) {
+        char **in;
+
+        for (in = pn; *in; in++) {
+            pa_alsa_path *p = NULL;
+            bool duplicate = false;
+            char **kn;
+
+            for (kn = pn; kn < in; kn++)
+                if (pa_streq(*kn, *in)) {
+                    duplicate = true;
+                    break;
+                }
+
+            if (duplicate)
+                continue;
+
+            p = profile_set_get_path(m->profile_set, *in);
+
+            if (p && p->direction != direction) {
+                pa_log("Configuration error: Path %s is used both as an input and as an output path.", p->name);
+                goto fail;
+            }
+
+            if (!p) {
+                char *fn = pa_sprintf_malloc("%s.conf", *in);
+                p = pa_alsa_path_new(paths_dir, fn, direction);
+                pa_xfree(fn);
+                if (p)
+                    profile_set_add_path(m->profile_set, p);
+            }
+
+            if (p)
+                pa_hashmap_put(ps->paths, p, p);
+
+        }
+
+        goto finish;
+    }
+
+    if (direction == PA_ALSA_DIRECTION_OUTPUT)
+        en = m->output_element;
+    else
+        en = m->input_element;
+
+    if (!en)
+        goto fail;
+
+    for (ie = en; *ie; ie++) {
+        char **je;
+        pa_alsa_path *p;
+
+        p = pa_alsa_path_synthesize(*ie, direction);
+
+        /* Mark all other passed elements for require-absent */
+        for (je = en; *je; je++) {
+            pa_alsa_element *e;
+
+            if (je == ie)
+                continue;
+
+            e = pa_xnew0(pa_alsa_element, 1);
+            e->path = p;
+            e->alsa_name = pa_xstrdup(*je);
+            e->direction = direction;
+            e->required_absent = PA_ALSA_REQUIRED_ANY;
+            e->volume_limit = -1;
+
+            PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
+            p->last_element = e;
+        }
+
+        pa_hashmap_put(ps->paths, *ie, p);
+    }
+
+finish:
+    /* Assign decibel fixes to elements. */
+    PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
+        pa_alsa_path *p;
+
+        PA_HASHMAP_FOREACH(p, ps->paths, state2) {
+            pa_alsa_element *e;
+
+            PA_LLIST_FOREACH(e, p->elements) {
+                if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
+                    /* The profile set that contains the dB fix may be freed
+                     * before the element, so we have to copy the dB fix
+                     * object. */
+                    e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
+                    e->db_fix->profile_set = NULL;
+                    e->db_fix->name = pa_xstrdup(db_fix->name);
+                    e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
+                }
+            }
+        }
+    }
+
+    return ps;
+
+fail:
+    if (ps)
+        pa_alsa_path_set_free(ps);
+
+    return NULL;
+}
+
+void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
+    pa_alsa_path *p;
+    void *state;
+    pa_assert(ps);
+
+    pa_log_debug("Path Set %p, direction=%i",
+                 (void*) ps,
+                 ps->direction);
+
+    PA_HASHMAP_FOREACH(p, ps->paths, state)
+        pa_alsa_path_dump(p);
+}
+
+static bool options_have_option(pa_alsa_option *options, const char *alsa_name) {
+    pa_alsa_option *o;
+
+    pa_assert(options);
+    pa_assert(alsa_name);
+
+    PA_LLIST_FOREACH(o, options) {
+        if (pa_streq(o->alsa_name, alsa_name))
+            return true;
+    }
+    return false;
+}
+
+static bool enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
+    pa_alsa_option *oa, *ob;
+
+    if (!a_options) return true;
+    if (!b_options) return false;
+
+    /* If there is an option A offers that B does not, then A is not a subset of B. */
+    PA_LLIST_FOREACH(oa, a_options) {
+        bool found = false;
+        PA_LLIST_FOREACH(ob, b_options) {
+            if (pa_streq(oa->alsa_name, ob->alsa_name)) {
+                found = true;
+                break;
+            }
+        }
+        if (!found)
+            return false;
+    }
+    return true;
+}
+
+/**
+ *  Compares two elements to see if a is a subset of b
+ */
+static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
+    pa_assert(a);
+    pa_assert(b);
+    pa_assert(m);
+
+    /* General rules:
+     * Every state is a subset of itself (with caveats for volume_limits and options)
+     * IGNORE is a subset of every other state */
+
+    /* Check the volume_use */
+    if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
+
+        /* "Constant" is subset of "Constant" only when their constant values are equal */
+        if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
+            return false;
+
+        /* Different volume uses when b is not "Merge" means we are definitely not a subset */
+        if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
+            return false;
+
+        /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
+         * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
+         * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
+        if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
+            long a_limit;
+
+            if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
+                a_limit = a->constant_volume;
+            else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
+                long dB = 0;
+
+                if (a->db_fix) {
+                    int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
+                    a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
+                } else {
+                    snd_mixer_selem_id_t *sid;
+                    snd_mixer_elem_t *me;
+
+                    SELEM_INIT(sid, a->alsa_name);
+                    if (!(me = snd_mixer_find_selem(m, sid))) {
+                        pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
+                        return false;
+                    }
+
+                    if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
+                        if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
+                            return false;
+                    } else {
+                        if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
+                            return false;
+                    }
+                }
+            } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
+                a_limit = a->min_volume;
+            else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
+                a_limit = a->volume_limit;
+            else
+                pa_assert_not_reached();
+
+            if (a_limit > b->volume_limit)
+                return false;
+        }
+
+        if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
+            int s;
+            /* If override-maps are different, they're not subsets */
+            if (a->n_channels != b->n_channels)
+                return false;
+            for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
+                if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
+                    pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
+                        a->alsa_name, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
+                    return false;
+               }
+        }
+    }
+
+    if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
+        /* "On" is a subset of "Mute".
+         * "Off" is a subset of "Mute".
+         * "On" is a subset of "Select", if there is an "Option:On" in B.
+         * "Off" is a subset of "Select", if there is an "Option:Off" in B.
+         * "Select" is a subset of "Select", if they have the same options (is this always true?). */
+
+        if (a->switch_use != b->switch_use) {
+
+            if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
+                || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
+                return false;
+
+            if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
+                if (a->switch_use == PA_ALSA_SWITCH_ON) {
+                    if (!options_have_option(b->options, "on"))
+                        return false;
+                } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
+                    if (!options_have_option(b->options, "off"))
+                        return false;
+                }
+            }
+        } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
+            if (!enumeration_is_subset(a->options, b->options))
+                return false;
+        }
+    }
+
+    if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
+        if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
+            return false;
+        if (!enumeration_is_subset(a->options, b->options))
+            return false;
+    }
+
+    return true;
+}
+
+static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
+    pa_alsa_path *p;
+    void *state;
+
+    pa_assert(ps);
+    pa_assert(m);
+
+    /* If we only have one path, then don't bother */
+    if (pa_hashmap_size(ps->paths) < 2)
+        return;
+
+    PA_HASHMAP_FOREACH(p, ps->paths, state) {
+        pa_alsa_path *p2;
+        void *state2;
+
+        PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
+            pa_alsa_element *ea, *eb;
+            pa_alsa_jack *ja, *jb;
+            bool is_subset = true;
+
+            if (p == p2)
+                continue;
+
+            /* If a has a jack that b does not have, a is not a subset */
+            PA_LLIST_FOREACH(ja, p->jacks) {
+                bool exists = false;
+
+                if (!ja->has_control)
+                    continue;
+
+                PA_LLIST_FOREACH(jb, p2->jacks) {
+                    if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) &&
+                       (ja->state_plugged == jb->state_plugged) &&
+                       (ja->state_unplugged == jb->state_unplugged)) {
+                        exists = true;
+                        break;
+                    }
+                }
+
+                if (!exists) {
+                    is_subset = false;
+                    break;
+                }
+            }
+
+            /* Compare the elements of each set... */
+            PA_LLIST_FOREACH(ea, p->elements) {
+                bool found_matching_element = false;
+
+                if (!is_subset)
+                    break;
+
+                PA_LLIST_FOREACH(eb, p2->elements) {
+                    if (pa_streq(ea->alsa_name, eb->alsa_name)) {
+                        found_matching_element = true;
+                        is_subset = element_is_subset(ea, eb, m);
+                        break;
+                    }
+                }
+
+                if (!found_matching_element)
+                    is_subset = false;
+            }
+
+            if (is_subset) {
+                pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
+                pa_hashmap_remove(ps->paths, p);
+                break;
+            }
+        }
+    }
+}
+
+static pa_alsa_path* path_set_find_path_by_description(pa_alsa_path_set *ps, const char* description, pa_alsa_path *ignore) {
+    pa_alsa_path* p;
+    void *state;
+
+    PA_HASHMAP_FOREACH(p, ps->paths, state)
+        if (p != ignore && pa_streq(p->description, description))
+            return p;
+
+    return NULL;
+}
+
+static void path_set_make_path_descriptions_unique(pa_alsa_path_set *ps) {
+    pa_alsa_path *p, *q;
+    void *state, *state2;
+
+    PA_HASHMAP_FOREACH(p, ps->paths, state) {
+        unsigned i;
+        char *old_description;
+
+        q = path_set_find_path_by_description(ps, p->description, p);
+
+        if (!q)
+            continue;
+
+        old_description = pa_xstrdup(p->description);
+
+        /* OK, this description is not unique, hence let's rename */
+        i = 1;
+        PA_HASHMAP_FOREACH(q, ps->paths, state2) {
+            char *new_description;
+
+            if (!pa_streq(q->description, old_description))
+                continue;
+
+            new_description = pa_sprintf_malloc("%s %u", q->description, i);
+            pa_xfree(q->description);
+            q->description = new_description;
+
+            i++;
+        }
+
+        pa_xfree(old_description);
+    }
+}
+
+static void mapping_free(pa_alsa_mapping *m) {
+    pa_assert(m);
+
+    pa_xfree(m->name);
+    pa_xfree(m->description);
+
+    pa_proplist_free(m->proplist);
+
+    pa_xstrfreev(m->device_strings);
+    pa_xstrfreev(m->input_path_names);
+    pa_xstrfreev(m->output_path_names);
+    pa_xstrfreev(m->input_element);
+    pa_xstrfreev(m->output_element);
+    if (m->input_path_set)
+        pa_alsa_path_set_free(m->input_path_set);
+    if (m->output_path_set)
+        pa_alsa_path_set_free(m->output_path_set);
+
+    pa_assert(!m->input_pcm);
+    pa_assert(!m->output_pcm);
+
+    pa_alsa_ucm_mapping_context_free(&m->ucm_context);
+
+    pa_xfree(m);
+}
+
+static void profile_free(pa_alsa_profile *p) {
+    pa_assert(p);
+
+    pa_xfree(p->name);
+    pa_xfree(p->description);
+    pa_xfree(p->input_name);
+    pa_xfree(p->output_name);
+
+    pa_xstrfreev(p->input_mapping_names);
+    pa_xstrfreev(p->output_mapping_names);
+
+    if (p->input_mappings)
+        pa_idxset_free(p->input_mappings, NULL);
+
+    if (p->output_mappings)
+        pa_idxset_free(p->output_mappings, NULL);
+
+    pa_xfree(p);
+}
+
+void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
+    pa_assert(ps);
+
+    if (ps->input_paths)
+        pa_hashmap_free(ps->input_paths);
+
+    if (ps->output_paths)
+        pa_hashmap_free(ps->output_paths);
+
+    if (ps->profiles)
+        pa_hashmap_free(ps->profiles);
+
+    if (ps->mappings)
+        pa_hashmap_free(ps->mappings);
+
+    if (ps->decibel_fixes)
+        pa_hashmap_free(ps->decibel_fixes);
+
+    pa_xfree(ps);
+}
+
+pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
+    pa_alsa_mapping *m;
+
+    if (!pa_startswith(name, "Mapping "))
+        return NULL;
+
+    name += 8;
+
+    if ((m = pa_hashmap_get(ps->mappings, name)))
+        return m;
+
+    m = pa_xnew0(pa_alsa_mapping, 1);
+    m->profile_set = ps;
+    m->exact_channels = true;
+    m->name = pa_xstrdup(name);
+    pa_sample_spec_init(&m->sample_spec);
+    pa_channel_map_init(&m->channel_map);
+    m->proplist = pa_proplist_new();
+
+    pa_hashmap_put(ps->mappings, m->name, m);
+
+    return m;
+}
+
+static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
+    pa_alsa_profile *p;
+
+    if (!pa_startswith(name, "Profile "))
+        return NULL;
+
+    name += 8;
+
+    if ((p = pa_hashmap_get(ps->profiles, name)))
+        return p;
+
+    p = pa_xnew0(pa_alsa_profile, 1);
+    p->profile_set = ps;
+    p->name = pa_xstrdup(name);
+
+    pa_hashmap_put(ps->profiles, p->name, p);
+
+    return p;
+}
+
+static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
+    pa_alsa_decibel_fix *db_fix;
+
+    if (!pa_startswith(name, "DecibelFix "))
+        return NULL;
+
+    name += 11;
+
+    if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
+        return db_fix;
+
+    db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
+    db_fix->profile_set = ps;
+    db_fix->name = pa_xstrdup(name);
+
+    pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
+
+    return db_fix;
+}
+
+static int mapping_parse_device_strings(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_mapping *m;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
+        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
+        return -1;
+    }
+
+    pa_xstrfreev(m->device_strings);
+    if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
+        pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int mapping_parse_channel_map(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_mapping *m;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
+        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
+        return -1;
+    }
+
+    if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
+        pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int mapping_parse_paths(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_mapping *m;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
+        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->lvalue, "paths-input")) {
+        pa_xstrfreev(m->input_path_names);
+        m->input_path_names = pa_split_spaces_strv(state->rvalue);
+    } else {
+        pa_xstrfreev(m->output_path_names);
+        m->output_path_names = pa_split_spaces_strv(state->rvalue);
+    }
+
+    return 0;
+}
+
+static int mapping_parse_exact_channels(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_mapping *m;
+    int b;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
+        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
+        return -1;
+    }
+
+    if ((b = pa_parse_boolean(state->rvalue)) < 0) {
+        pa_log("[%s:%u] %s has invalid value '%s'", state->filename, state->lineno, state->lvalue, state->section);
+        return -1;
+    }
+
+    m->exact_channels = b;
+
+    return 0;
+}
+
+static int mapping_parse_element(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_mapping *m;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
+        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->lvalue, "element-input")) {
+        pa_xstrfreev(m->input_element);
+        m->input_element = pa_split_spaces_strv(state->rvalue);
+    } else {
+        pa_xstrfreev(m->output_element);
+        m->output_element = pa_split_spaces_strv(state->rvalue);
+    }
+
+    return 0;
+}
+
+static int mapping_parse_direction(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_mapping *m;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
+        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->rvalue, "input"))
+        m->direction = PA_ALSA_DIRECTION_INPUT;
+    else if (pa_streq(state->rvalue, "output"))
+        m->direction = PA_ALSA_DIRECTION_OUTPUT;
+    else if (pa_streq(state->rvalue, "any"))
+        m->direction = PA_ALSA_DIRECTION_ANY;
+    else {
+        pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int mapping_parse_description(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_profile *p;
+    pa_alsa_mapping *m;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if ((m = pa_alsa_mapping_get(ps, state->section))) {
+        pa_xfree(m->description);
+        m->description = pa_xstrdup(state->rvalue);
+    } else if ((p = profile_get(ps, state->section))) {
+        pa_xfree(p->description);
+        p->description = pa_xstrdup(state->rvalue);
+    } else {
+        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int mapping_parse_priority(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_profile *p;
+    pa_alsa_mapping *m;
+    uint32_t prio;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if (pa_atou(state->rvalue, &prio) < 0) {
+        pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if ((m = pa_alsa_mapping_get(ps, state->section)))
+        m->priority = prio;
+    else if ((p = profile_get(ps, state->section)))
+        p->priority = prio;
+    else {
+        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int mapping_parse_fallback(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_profile *p;
+    pa_alsa_mapping *m;
+    int k;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if ((k = pa_parse_boolean(state->rvalue)) < 0) {
+        pa_log("[%s:%u] Fallback invalid of '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    if ((m = pa_alsa_mapping_get(ps, state->section)))
+        m->fallback = k;
+    else if ((p = profile_get(ps, state->section)))
+        p->fallback_input = p->fallback_output = k;
+    else {
+        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int profile_parse_mappings(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_profile *p;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if (!(p = profile_get(ps, state->section))) {
+        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
+        return -1;
+    }
+
+    if (pa_streq(state->lvalue, "input-mappings")) {
+        pa_xstrfreev(p->input_mapping_names);
+        p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
+    } else {
+        pa_xstrfreev(p->output_mapping_names);
+        p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
+    }
+
+    return 0;
+}
+
+static int profile_parse_skip_probe(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_profile *p;
+    int b;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if (!(p = profile_get(ps, state->section))) {
+        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
+        return -1;
+    }
+
+    if ((b = pa_parse_boolean(state->rvalue)) < 0) {
+        pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
+        return -1;
+    }
+
+    p->supported = b;
+
+    return 0;
+}
+
+static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_decibel_fix *db_fix;
+    char **items;
+    char *item;
+    long *db_values;
+    unsigned n = 8; /* Current size of the db_values table. */
+    unsigned min_step = 0;
+    unsigned max_step = 0;
+    unsigned i = 0; /* Index to the items table. */
+    unsigned prev_step = 0;
+    double prev_db = 0;
+
+    pa_assert(state);
+
+    ps = state->userdata;
+
+    if (!(db_fix = decibel_fix_get(ps, state->section))) {
+        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
+        return -1;
+    }
+
+    if (!(items = pa_split_spaces_strv(state->rvalue))) {
+        pa_log("[%s:%u] Value missing", state->filename, state->lineno);
+        return -1;
+    }
+
+    db_values = pa_xnew(long, n);
+
+    while ((item = items[i++])) {
+        char *s = item; /* Step value string. */
+        char *d = item; /* dB value string. */
+        uint32_t step;
+        double db;
+
+        /* Move d forward until it points to a colon or to the end of the item. */
+        for (; *d && *d != ':'; ++d);
+
+        if (d == s) {
+            /* item started with colon. */
+            pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
+            goto fail;
+        }
+
+        if (!*d || !*(d + 1)) {
+            /* No colon found, or it was the last character in item. */
+            pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
+            goto fail;
+        }
+
+        /* pa_atou() needs a null-terminating string. Let's replace the colon
+         * with a zero byte. */
+        *d++ = '\0';
+
+        if (pa_atou(s, &step) < 0) {
+            pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
+            goto fail;
+        }
+
+        if (pa_atod(d, &db) < 0) {
+            pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
+            goto fail;
+        }
+
+        if (step <= prev_step && i != 1) {
+            pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
+            goto fail;
+        }
+
+        if (db < prev_db && i != 1) {
+            pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
+            goto fail;
+        }
+
+        if (i == 1) {
+            min_step = step;
+            db_values[0] = (long) (db * 100.0);
+            prev_step = step;
+            prev_db = db;
+        } else {
+            /* Interpolate linearly. */
+            double db_increment = (db - prev_db) / (step - prev_step);
+
+            for (; prev_step < step; ++prev_step, prev_db += db_increment) {
+
+                /* Reallocate the db_values table if it's about to overflow. */
+                if (prev_step + 1 - min_step == n) {
+                    n *= 2;
+                    db_values = pa_xrenew(long, db_values, n);
+                }
+
+                db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
+            }
+        }
+
+        max_step = step;
+    }
+
+    db_fix->min_step = min_step;
+    db_fix->max_step = max_step;
+    pa_xfree(db_fix->db_values);
+    db_fix->db_values = db_values;
+
+    pa_xstrfreev(items);
+
+    return 0;
+
+fail:
+    pa_xstrfreev(items);
+    pa_xfree(db_values);
+
+    return -1;
+}
+
+static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
+                                pa_alsa_direction_t direction, pa_hashmap *used_paths) {
+
+    pa_alsa_path *p;
+    void *state;
+    snd_pcm_t *pcm_handle;
+    pa_alsa_path_set *ps;
+    snd_mixer_t *mixer_handle;
+
+    if (direction == PA_ALSA_DIRECTION_OUTPUT) {
+        if (m->output_path_set)
+            return; /* Already probed */
+        m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
+        pcm_handle = m->output_pcm;
+    } else {
+        if (m->input_path_set)
+            return; /* Already probed */
+        m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
+        pcm_handle = m->input_pcm;
+    }
+
+    if (!ps)
+        return; /* No paths */
+
+    pa_assert(pcm_handle);
+
+    mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
+    if (!mixer_handle) {
+        /* Cannot open mixer, remove all entries */
+        pa_hashmap_remove_all(ps->paths);
+        return;
+    }
+
+    PA_HASHMAP_FOREACH(p, ps->paths, state) {
+        if (pa_alsa_path_probe(p, mixer_handle, m->profile_set->ignore_dB) < 0) {
+            pa_hashmap_remove(ps->paths, p);
+        }
+    }
+
+    path_set_condense(ps, mixer_handle);
+    path_set_make_path_descriptions_unique(ps);
+
+    if (mixer_handle)
+        snd_mixer_close(mixer_handle);
+
+    PA_HASHMAP_FOREACH(p, ps->paths, state)
+        pa_hashmap_put(used_paths, p, p);
+
+    pa_log_debug("Available mixer paths (after tidying):");
+    pa_alsa_path_set_dump(ps);
+}
+
+static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
+
+    static const struct description_map well_known_descriptions[] = {
+        { "analog-mono",            N_("Analog Mono") },
+        { "analog-stereo",          N_("Analog Stereo") },
+        { "stereo-fallback",        N_("Stereo") },
+        /* Note: Not translated to "Analog Stereo Input", because the source
+         * name gets "Input" appended to it automatically, so adding "Input"
+         * here would lead to the source name to become "Analog Stereo Input
+         * Input". The same logic applies to analog-stereo-output,
+         * multichannel-input and multichannel-output. */
+        { "analog-stereo-input",    N_("Analog Stereo") },
+        { "analog-stereo-output",   N_("Analog Stereo") },
+        { "multichannel-input",     N_("Multichannel") },
+        { "multichannel-output",    N_("Multichannel") },
+        { "analog-surround-21",     N_("Analog Surround 2.1") },
+        { "analog-surround-30",     N_("Analog Surround 3.0") },
+        { "analog-surround-31",     N_("Analog Surround 3.1") },
+        { "analog-surround-40",     N_("Analog Surround 4.0") },
+        { "analog-surround-41",     N_("Analog Surround 4.1") },
+        { "analog-surround-50",     N_("Analog Surround 5.0") },
+        { "analog-surround-51",     N_("Analog Surround 5.1") },
+        { "analog-surround-61",     N_("Analog Surround 6.0") },
+        { "analog-surround-61",     N_("Analog Surround 6.1") },
+        { "analog-surround-70",     N_("Analog Surround 7.0") },
+        { "analog-surround-71",     N_("Analog Surround 7.1") },
+        { "iec958-stereo",          N_("Digital Stereo (IEC958)") },
+        { "iec958-passthrough",     N_("Digital Passthrough  (IEC958)") },
+        { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
+        { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
+        { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
+        { "hdmi-stereo",            N_("Digital Stereo (HDMI)") },
+        { "hdmi-surround-51",       N_("Digital Surround 5.1 (HDMI)") },
+    };
+
+    pa_assert(m);
+
+    if (!pa_channel_map_valid(&m->channel_map)) {
+        pa_log("Mapping %s is missing channel map.", m->name);
+        return -1;
+    }
+
+    if (!m->device_strings) {
+        pa_log("Mapping %s is missing device strings.", m->name);
+        return -1;
+    }
+
+    if ((m->input_path_names && m->input_element) ||
+        (m->output_path_names && m->output_element)) {
+        pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
+        return -1;
+    }
+
+    if (!m->description)
+        m->description = pa_xstrdup(lookup_description(m->name,
+                                                       well_known_descriptions,
+                                                       PA_ELEMENTSOF(well_known_descriptions)));
+
+    if (!m->description)
+        m->description = pa_xstrdup(m->name);
+
+    if (bonus) {
+        if (pa_channel_map_equal(&m->channel_map, bonus))
+            m->priority += 50;
+        else if (m->channel_map.channels == bonus->channels)
+            m->priority += 30;
+    }
+
+    return 0;
+}
+
+void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+    pa_assert(m);
+
+    pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
+                 m->name,
+                 pa_strnull(m->description),
+                 m->priority,
+                 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
+                 pa_yes_no(m->supported),
+                 m->direction);
+}
+
+static void profile_set_add_auto_pair(
+        pa_alsa_profile_set *ps,
+        pa_alsa_mapping *m, /* output */
+        pa_alsa_mapping *n  /* input */) {
+
+    char *name;
+    pa_alsa_profile *p;
+
+    pa_assert(ps);
+    pa_assert(m || n);
+
+    if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
+        return;
+
+    if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
+        return;
+
+    if (m && n)
+        name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
+    else if (m)
+        name = pa_sprintf_malloc("output:%s", m->name);
+    else
+        name = pa_sprintf_malloc("input:%s", n->name);
+
+    if (pa_hashmap_get(ps->profiles, name)) {
+        pa_xfree(name);
+        return;
+    }
+
+    p = pa_xnew0(pa_alsa_profile, 1);
+    p->profile_set = ps;
+    p->name = name;
+
+    if (m) {
+        p->output_name = pa_xstrdup(m->name);
+        p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        pa_idxset_put(p->output_mappings, m, NULL);
+        p->priority += m->priority * 100;
+        p->fallback_output = m->fallback;
+    }
+
+    if (n) {
+        p->input_name = pa_xstrdup(n->name);
+        p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        pa_idxset_put(p->input_mappings, n, NULL);
+        p->priority += n->priority;
+        p->fallback_input = n->fallback;
+    }
+
+    pa_hashmap_put(ps->profiles, p->name, p);
+}
+
+static void profile_set_add_auto(pa_alsa_profile_set *ps) {
+    pa_alsa_mapping *m, *n;
+    void *m_state, *n_state;
+
+    pa_assert(ps);
+
+    /* The order is important here:
+       1) try single inputs and outputs before trying their
+          combination, because if the half-duplex test failed, we don't have
+          to try full duplex.
+       2) try the output right before the input combinations with
+          that output, because then the output_pcm is not closed between tests.
+    */
+    PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
+        profile_set_add_auto_pair(ps, NULL, n);
+
+    PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
+        profile_set_add_auto_pair(ps, m, NULL);
+
+        PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
+            profile_set_add_auto_pair(ps, m, n);
+    }
+
+}
+
+static int profile_verify(pa_alsa_profile *p) {
+
+    static const struct description_map well_known_descriptions[] = {
+        { "output:analog-mono+input:analog-mono",     N_("Analog Mono Duplex") },
+        { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
+        { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
+        { "output:multichannel-output+input:multichannel-input", N_("Multichannel Duplex") },
+        { "output:unknown-stereo+input:unknown-stereo", N_("Stereo Duplex") },
+        { "off",                                      N_("Off") }
+    };
+
+    pa_assert(p);
+
+    /* Replace the output mapping names by the actual mappings */
+    if (p->output_mapping_names) {
+        char **name;
+
+        pa_assert(!p->output_mappings);
+        p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+        for (name = p->output_mapping_names; *name; name++) {
+            pa_alsa_mapping *m;
+            char **in;
+            bool duplicate = false;
+
+            for (in = name + 1; *in; in++)
+                if (pa_streq(*name, *in)) {
+                    duplicate = true;
+                    break;
+                }
+
+            if (duplicate)
+                continue;
+
+            if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
+                pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
+                return -1;
+            }
+
+            pa_idxset_put(p->output_mappings, m, NULL);
+
+            if (p->supported)
+                m->supported++;
+        }
+
+        pa_xstrfreev(p->output_mapping_names);
+        p->output_mapping_names = NULL;
+    }
+
+    /* Replace the input mapping names by the actual mappings */
+    if (p->input_mapping_names) {
+        char **name;
+
+        pa_assert(!p->input_mappings);
+        p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+        for (name = p->input_mapping_names; *name; name++) {
+            pa_alsa_mapping *m;
+            char **in;
+            bool duplicate = false;
+
+            for (in = name + 1; *in; in++)
+                if (pa_streq(*name, *in)) {
+                    duplicate = true;
+                    break;
+                }
+
+            if (duplicate)
+                continue;
+
+            if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
+                pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
+                return -1;
+            }
+
+            pa_idxset_put(p->input_mappings, m, NULL);
+
+            if (p->supported)
+                m->supported++;
+        }
+
+        pa_xstrfreev(p->input_mapping_names);
+        p->input_mapping_names = NULL;
+    }
+
+    if (!p->input_mappings && !p->output_mappings) {
+        pa_log("Profile '%s' lacks mappings.", p->name);
+        return -1;
+    }
+
+    if (!p->description)
+        p->description = pa_xstrdup(lookup_description(p->name,
+                                                       well_known_descriptions,
+                                                       PA_ELEMENTSOF(well_known_descriptions)));
+
+    if (!p->description) {
+        pa_strbuf *sb;
+        uint32_t idx;
+        pa_alsa_mapping *m;
+
+        sb = pa_strbuf_new();
+
+        if (p->output_mappings)
+            PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
+                if (!pa_strbuf_isempty(sb))
+                    pa_strbuf_puts(sb, " + ");
+
+                pa_strbuf_printf(sb, _("%s Output"), m->description);
+            }
+
+        if (p->input_mappings)
+            PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
+                if (!pa_strbuf_isempty(sb))
+                    pa_strbuf_puts(sb, " + ");
+
+                pa_strbuf_printf(sb, _("%s Input"), m->description);
+            }
+
+        p->description = pa_strbuf_to_string_free(sb);
+    }
+
+    return 0;
+}
+
+void pa_alsa_profile_dump(pa_alsa_profile *p) {
+    uint32_t idx;
+    pa_alsa_mapping *m;
+    pa_assert(p);
+
+    pa_log_debug("Profile %s (%s), input=%s, output=%s priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
+                 p->name,
+                 pa_strnull(p->description),
+                 pa_strnull(p->input_name),
+                 pa_strnull(p->output_name),
+                 p->priority,
+                 pa_yes_no(p->supported),
+                 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
+                 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
+
+    if (p->input_mappings)
+        PA_IDXSET_FOREACH(m, p->input_mappings, idx)
+            pa_log_debug("Input %s", m->name);
+
+    if (p->output_mappings)
+        PA_IDXSET_FOREACH(m, p->output_mappings, idx)
+            pa_log_debug("Output %s", m->name);
+}
+
+static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
+    pa_assert(db_fix);
+
+    /* Check that the dB mapping has been configured. Since "db-values" is
+     * currently the only option in the DecibelFix section, and decibel fix
+     * objects don't get created if a DecibelFix section is empty, this is
+     * actually a redundant check. Having this may prevent future bugs,
+     * however. */
+    if (!db_fix->db_values) {
+        pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
+        return -1;
+    }
+
+    return 0;
+}
+
+void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
+    char *db_values = NULL;
+
+    pa_assert(db_fix);
+
+    if (db_fix->db_values) {
+        pa_strbuf *buf;
+        unsigned long i, nsteps;
+
+        pa_assert(db_fix->min_step <= db_fix->max_step);
+        nsteps = db_fix->max_step - db_fix->min_step + 1;
+
+        buf = pa_strbuf_new();
+        for (i = 0; i < nsteps; ++i)
+            pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
+
+        db_values = pa_strbuf_to_string_free(buf);
+    }
+
+    pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
+                 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
+
+    pa_xfree(db_values);
+}
+
+pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
+    pa_alsa_profile_set *ps;
+    pa_alsa_profile *p;
+    pa_alsa_mapping *m;
+    pa_alsa_decibel_fix *db_fix;
+    char *fn;
+    int r;
+    void *state;
+
+    static pa_config_item items[] = {
+        /* [General] */
+        { "auto-profiles",          pa_config_parse_bool,         NULL, "General" },
+
+        /* [Mapping ...] */
+        { "device-strings",         mapping_parse_device_strings, NULL, NULL },
+        { "channel-map",            mapping_parse_channel_map,    NULL, NULL },
+        { "paths-input",            mapping_parse_paths,          NULL, NULL },
+        { "paths-output",           mapping_parse_paths,          NULL, NULL },
+        { "element-input",          mapping_parse_element,        NULL, NULL },
+        { "element-output",         mapping_parse_element,        NULL, NULL },
+        { "direction",              mapping_parse_direction,      NULL, NULL },
+        { "exact-channels",         mapping_parse_exact_channels, NULL, NULL },
+
+        /* Shared by [Mapping ...] and [Profile ...] */
+        { "description",            mapping_parse_description,    NULL, NULL },
+        { "priority",               mapping_parse_priority,       NULL, NULL },
+        { "fallback",               mapping_parse_fallback,       NULL, NULL },
+
+        /* [Profile ...] */
+        { "input-mappings",         profile_parse_mappings,       NULL, NULL },
+        { "output-mappings",        profile_parse_mappings,       NULL, NULL },
+        { "skip-probe",             profile_parse_skip_probe,     NULL, NULL },
+
+        /* [DecibelFix ...] */
+        { "db-values",              decibel_fix_parse_db_values,  NULL, NULL },
+        { NULL, NULL, NULL, NULL }
+    };
+
+    ps = pa_xnew0(pa_alsa_profile_set, 1);
+    ps->mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) mapping_free);
+    ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) profile_free);
+    ps->decibel_fixes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) decibel_fix_free);
+    ps->input_paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_alsa_path_free);
+    ps->output_paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_alsa_path_free);
+
+    items[0].data = &ps->auto_profiles;
+
+    if (!fname)
+        fname = "default.conf";
+
+    fn = pa_maybe_prefix_path(fname,
+                              pa_run_from_build_tree() ? PA_SRCDIR "/modules/alsa/mixer/profile-sets/" :
+                              PA_ALSA_PROFILE_SETS_DIR);
+
+    r = pa_config_parse(fn, NULL, items, NULL, false, ps);
+    pa_xfree(fn);
+
+    if (r < 0)
+        goto fail;
+
+    PA_HASHMAP_FOREACH(m, ps->mappings, state)
+        if (mapping_verify(m, bonus) < 0)
+            goto fail;
+
+    if (ps->auto_profiles)
+        profile_set_add_auto(ps);
+
+    PA_HASHMAP_FOREACH(p, ps->profiles, state)
+        if (profile_verify(p) < 0)
+            goto fail;
+
+    PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
+        if (decibel_fix_verify(db_fix) < 0)
+            goto fail;
+
+    return ps;
+
+fail:
+    pa_alsa_profile_set_free(ps);
+    return NULL;
+}
+
+static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
+    pa_alsa_mapping *m;
+    uint32_t idx;
+
+    if (!to_be_finalized)
+        return;
+
+    if (to_be_finalized->output_mappings)
+        PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
+
+            if (!m->output_pcm)
+                continue;
+
+            if (to_be_finalized->supported)
+                m->supported++;
+
+            /* If this mapping is also in the next profile, we won't close the
+             * pcm handle here, because it would get immediately reopened
+             * anyway. */
+            if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
+                continue;
+
+            snd_pcm_close(m->output_pcm);
+            m->output_pcm = NULL;
+        }
+
+    if (to_be_finalized->input_mappings)
+        PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
+
+            if (!m->input_pcm)
+                continue;
+
+            if (to_be_finalized->supported)
+                m->supported++;
+
+            /* If this mapping is also in the next profile, we won't close the
+             * pcm handle here, because it would get immediately reopened
+             * anyway. */
+            if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
+                continue;
+
+            snd_pcm_close(m->input_pcm);
+            m->input_pcm = NULL;
+        }
+}
+
+static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
+                                   const pa_sample_spec *ss,
+                                   const char *dev_id,
+                                   bool exact_channels,
+                                   int mode,
+                                   unsigned default_n_fragments,
+                                   unsigned default_fragment_size_msec) {
+
+    snd_pcm_t* handle;
+    pa_sample_spec try_ss = *ss;
+    pa_channel_map try_map = m->channel_map;
+    snd_pcm_uframes_t try_period_size, try_buffer_size;
+
+    try_ss.channels = try_map.channels;
+
+    try_period_size =
+        pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
+        pa_frame_size(&try_ss);
+    try_buffer_size = default_n_fragments * try_period_size;
+
+    handle = pa_alsa_open_by_template(
+                              m->device_strings, dev_id, NULL, &try_ss,
+                              &try_map, mode, &try_period_size,
+                              &try_buffer_size, 0, NULL, NULL, exact_channels);
+    if (handle && !exact_channels && m->channel_map.channels != try_map.channels) {
+        char buf[PA_CHANNEL_MAP_SNPRINT_MAX];
+        pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,
+                     pa_channel_map_snprint(buf, sizeof(buf), &try_map));
+        m->channel_map = try_map;
+    }
+    return handle;
+}
+
+static void paths_drop_unused(pa_hashmap* h, pa_hashmap *keep) {
+
+    void* state = NULL;
+    const void* key;
+    pa_alsa_path* p;
+
+    pa_assert(h);
+    pa_assert(keep);
+
+    p = pa_hashmap_iterate(h, &state, &key);
+    while (p) {
+        if (pa_hashmap_get(keep, p) == NULL)
+            pa_hashmap_remove_and_free(h, key);
+        p = pa_hashmap_iterate(h, &state, &key);
+    }
+}
+
+static int add_profiles_to_probe(
+        pa_alsa_profile **list,
+        pa_hashmap *profiles,
+        bool fallback_output,
+        bool fallback_input) {
+
+    int i = 0;
+    void *state;
+    pa_alsa_profile *p;
+    PA_HASHMAP_FOREACH(p, profiles, state)
+        if (p->fallback_input == fallback_input && p->fallback_output == fallback_output) {
+            *list = p;
+            list++;
+            i++;
+        }
+    return i;
+}
+
+void pa_alsa_profile_set_probe(
+        pa_alsa_profile_set *ps,
+        const char *dev_id,
+        const pa_sample_spec *ss,
+        unsigned default_n_fragments,
+        unsigned default_fragment_size_msec) {
+
+    bool found_output = false, found_input = false;
+
+    pa_alsa_profile *p, *last = NULL;
+    pa_alsa_profile **pp, **probe_order;
+    pa_alsa_mapping *m;
+    pa_hashmap *broken_inputs, *broken_outputs, *used_paths;
+
+    pa_assert(ps);
+    pa_assert(dev_id);
+    pa_assert(ss);
+
+    if (ps->probed)
+        return;
+
+    broken_inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    broken_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    used_paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    pp = probe_order = pa_xnew0(pa_alsa_profile *, pa_hashmap_size(ps->profiles) + 1);
+
+    pp += add_profiles_to_probe(pp, ps->profiles, false, false);
+    pp += add_profiles_to_probe(pp, ps->profiles, false, true);
+    pp += add_profiles_to_probe(pp, ps->profiles, true, false);
+    pp += add_profiles_to_probe(pp, ps->profiles, true, true);
+
+    for (pp = probe_order; *pp; pp++) {
+        uint32_t idx;
+        p = *pp;
+
+        /* Skip if fallback and already found something */
+        if (found_input && p->fallback_input)
+            continue;
+        if (found_output && p->fallback_output)
+            continue;
+
+        /* Skip if this is already marked that it is supported (i.e. from the config file) */
+        if (!p->supported) {
+
+            profile_finalize_probing(last, p);
+            p->supported = true;
+
+            if (p->output_mappings) {
+                PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
+                    if (pa_hashmap_get(broken_outputs, m) == m) {
+                        pa_log_debug("Skipping profile %s - will not be able to open output:%s", p->name, m->name);
+                        p->supported = false;
+                        break;
+                    }
+                }
+            }
+
+            if (p->input_mappings && p->supported) {
+                PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
+                    if (pa_hashmap_get(broken_inputs, m) == m) {
+                        pa_log_debug("Skipping profile %s - will not be able to open input:%s", p->name, m->name);
+                        p->supported = false;
+                        break;
+                    }
+                }
+            }
+
+            if (p->supported)
+                pa_log_debug("Looking at profile %s", p->name);
+
+            /* Check if we can open all new ones */
+            if (p->output_mappings && p->supported)
+                PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
+
+                    if (m->output_pcm)
+                        continue;
+
+                    pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
+                    if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
+                                                           SND_PCM_STREAM_PLAYBACK,
+                                                           default_n_fragments,
+                                                           default_fragment_size_msec))) {
+                        p->supported = false;
+                        if (pa_idxset_size(p->output_mappings) == 1 &&
+                            ((!p->input_mappings) || pa_idxset_size(p->input_mappings) == 0)) {
+                            pa_log_debug("Caching failure to open output:%s", m->name);
+                            pa_hashmap_put(broken_outputs, m, m);
+                        }
+                        break;
+                    }
+                }
+
+            if (p->input_mappings && p->supported)
+                PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
+
+                    if (m->input_pcm)
+                        continue;
+
+                    pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
+                    if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
+                                                          SND_PCM_STREAM_CAPTURE,
+                                                          default_n_fragments,
+                                                          default_fragment_size_msec))) {
+                        p->supported = false;
+                        if (pa_idxset_size(p->input_mappings) == 1 &&
+                            ((!p->output_mappings) || pa_idxset_size(p->output_mappings) == 0)) {
+                            pa_log_debug("Caching failure to open input:%s", m->name);
+                            pa_hashmap_put(broken_inputs, m, m);
+                        }
+                        break;
+                    }
+                }
+
+            last = p;
+
+            if (!p->supported)
+                continue;
+        }
+
+        pa_log_debug("Profile %s supported.", p->name);
+
+        if (p->output_mappings)
+            PA_IDXSET_FOREACH(m, p->output_mappings, idx)
+                if (m->output_pcm) {
+                    found_output |= !p->fallback_output;
+                    mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths);
+                }
+
+        if (p->input_mappings)
+            PA_IDXSET_FOREACH(m, p->input_mappings, idx)
+                if (m->input_pcm) {
+                    found_input |= !p->fallback_input;
+                    mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths);
+                }
+    }
+
+    /* Clean up */
+    profile_finalize_probing(last, NULL);
+
+    pa_alsa_profile_set_drop_unsupported(ps);
+
+    paths_drop_unused(ps->input_paths, used_paths);
+    paths_drop_unused(ps->output_paths, used_paths);
+    pa_hashmap_free(broken_inputs);
+    pa_hashmap_free(broken_outputs);
+    pa_hashmap_free(used_paths);
+    pa_xfree(probe_order);
+
+    ps->probed = true;
+}
+
+void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
+    pa_alsa_profile *p;
+    pa_alsa_mapping *m;
+    pa_alsa_decibel_fix *db_fix;
+    void *state;
+
+    pa_assert(ps);
+
+    pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
+                 (void*)
+                 ps,
+                 pa_yes_no(ps->auto_profiles),
+                 pa_yes_no(ps->probed),
+                 pa_hashmap_size(ps->mappings),
+                 pa_hashmap_size(ps->profiles),
+                 pa_hashmap_size(ps->decibel_fixes));
+
+    PA_HASHMAP_FOREACH(m, ps->mappings, state)
+        pa_alsa_mapping_dump(m);
+
+    PA_HASHMAP_FOREACH(p, ps->profiles, state)
+        pa_alsa_profile_dump(p);
+
+    PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
+        pa_alsa_decibel_fix_dump(db_fix);
+}
+
+void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
+    pa_alsa_profile *p;
+    pa_alsa_mapping *m;
+    void *state;
+
+    PA_HASHMAP_FOREACH(p, ps->profiles, state) {
+        if (!p->supported)
+            pa_hashmap_remove_and_free(ps->profiles, p->name);
+    }
+
+    PA_HASHMAP_FOREACH(m, ps->mappings, state) {
+        if (m->supported <= 0)
+            pa_hashmap_remove_and_free(ps->mappings, m->name);
+    }
+}
+
+static pa_device_port* device_port_alsa_init(pa_hashmap *ports, /* card ports */
+    const char* name,
+    const char* description,
+    pa_alsa_path *path,
+    pa_alsa_setting *setting,
+    pa_card_profile *cp,
+    pa_hashmap *extra, /* sink/source ports */
+    pa_core *core) {
+
+    pa_device_port *p;
+
+    pa_assert(path);
+
+    p = pa_hashmap_get(ports, name);
+
+    if (!p) {
+        pa_alsa_port_data *data;
+        pa_device_port_new_data port_data;
+
+        pa_device_port_new_data_init(&port_data);
+        pa_device_port_new_data_set_name(&port_data, name);
+        pa_device_port_new_data_set_description(&port_data, description);
+        pa_device_port_new_data_set_direction(&port_data, path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
+
+        p = pa_device_port_new(core, &port_data, sizeof(pa_alsa_port_data));
+        pa_device_port_new_data_done(&port_data);
+        pa_assert(p);
+        pa_hashmap_put(ports, p->name, p);
+        pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
+
+        data = PA_DEVICE_PORT_DATA(p);
+        /* Ownership of the path and setting is not transferred to the port data, so we don't deal with freeing them */
+        data->path = path;
+        data->setting = setting;
+        path->port = p;
+    }
+
+    if (cp)
+        pa_hashmap_put(p->profiles, cp->name, cp);
+
+    if (extra) {
+        pa_hashmap_put(extra, p->name, p);
+        pa_device_port_ref(p);
+    }
+
+    return p;
+}
+
+void pa_alsa_path_set_add_ports(
+        pa_alsa_path_set *ps,
+        pa_card_profile *cp,
+        pa_hashmap *ports, /* card ports */
+        pa_hashmap *extra, /* sink/source ports */
+        pa_core *core) {
+
+    pa_alsa_path *path;
+    void *state;
+
+    pa_assert(ports);
+
+    if (!ps)
+        return;
+
+    PA_HASHMAP_FOREACH(path, ps->paths, state) {
+        if (!path->settings || !path->settings->next) {
+            /* If there is no or just one setting we only need a
+             * single entry */
+            pa_device_port *port = device_port_alsa_init(ports, path->name,
+                path->description, path, path->settings, cp, extra, core);
+            port->priority = path->priority * 100;
+
+        } else {
+            pa_alsa_setting *s;
+            PA_LLIST_FOREACH(s, path->settings) {
+                pa_device_port *port;
+                char *n, *d;
+
+                n = pa_sprintf_malloc("%s;%s", path->name, s->name);
+
+                if (s->description[0])
+                    d = pa_sprintf_malloc("%s / %s", path->description, s->description);
+                else
+                    d = pa_xstrdup(path->description);
+
+                port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
+                port->priority = path->priority * 100 + s->priority;
+
+                pa_xfree(n);
+                pa_xfree(d);
+            }
+        }
+    }
+}
+
+void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card) {
+    pa_hashmap *ports;
+
+    pa_assert(sink_or_source_new_data);
+    pa_assert(ps);
+
+    if (ps->direction == PA_ALSA_DIRECTION_OUTPUT)
+        ports = ((pa_sink_new_data *) sink_or_source_new_data)->ports;
+    else
+        ports = ((pa_source_new_data *) sink_or_source_new_data)->ports;
+
+    if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
+        pa_assert(card);
+        pa_alsa_path_set_add_ports(ps, NULL, card->ports, ports, card->core);
+    }
+
+    pa_log_debug("Added %u ports", pa_hashmap_size(ports));
+}
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
new file mode 100644 (file)
index 0000000..4ebf192
--- /dev/null
@@ -0,0 +1,372 @@
+#ifndef fooalsamixerhfoo
+#define fooalsamixerhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <asoundlib.h>
+
+#include <pulse/sample.h>
+#include <pulse/mainloop-api.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/llist.h>
+#include <pulsecore/rtpoll.h>
+
+typedef struct pa_alsa_fdlist pa_alsa_fdlist;
+typedef struct pa_alsa_mixer_pdata pa_alsa_mixer_pdata;
+typedef struct pa_alsa_setting pa_alsa_setting;
+typedef struct pa_alsa_option pa_alsa_option;
+typedef struct pa_alsa_element pa_alsa_element;
+typedef struct pa_alsa_jack pa_alsa_jack;
+typedef struct pa_alsa_path pa_alsa_path;
+typedef struct pa_alsa_path_set pa_alsa_path_set;
+typedef struct pa_alsa_mapping pa_alsa_mapping;
+typedef struct pa_alsa_profile pa_alsa_profile;
+typedef struct pa_alsa_decibel_fix pa_alsa_decibel_fix;
+typedef struct pa_alsa_profile_set pa_alsa_profile_set;
+typedef struct pa_alsa_port_data pa_alsa_port_data;
+
+#include "alsa-util.h"
+#include "alsa-ucm.h"
+
+typedef enum pa_alsa_switch_use {
+    PA_ALSA_SWITCH_IGNORE,
+    PA_ALSA_SWITCH_MUTE,   /* make this switch follow mute status */
+    PA_ALSA_SWITCH_OFF,    /* set this switch to 'off' unconditionally */
+    PA_ALSA_SWITCH_ON,     /* set this switch to 'on' unconditionally */
+    PA_ALSA_SWITCH_SELECT  /* allow the user to select switch status through a setting */
+} pa_alsa_switch_use_t;
+
+typedef enum pa_alsa_volume_use {
+    PA_ALSA_VOLUME_IGNORE,
+    PA_ALSA_VOLUME_MERGE,   /* merge this volume slider into the global volume slider */
+    PA_ALSA_VOLUME_OFF,     /* set this volume to minimal unconditionally */
+    PA_ALSA_VOLUME_ZERO,    /* set this volume to 0dB unconditionally */
+    PA_ALSA_VOLUME_CONSTANT /* set this volume to a constant value unconditionally */
+} pa_alsa_volume_use_t;
+
+typedef enum pa_alsa_enumeration_use {
+    PA_ALSA_ENUMERATION_IGNORE,
+    PA_ALSA_ENUMERATION_SELECT
+} pa_alsa_enumeration_use_t;
+
+typedef enum pa_alsa_required {
+    PA_ALSA_REQUIRED_IGNORE,
+    PA_ALSA_REQUIRED_SWITCH,
+    PA_ALSA_REQUIRED_VOLUME,
+    PA_ALSA_REQUIRED_ENUMERATION,
+    PA_ALSA_REQUIRED_ANY
+} pa_alsa_required_t;
+
+typedef enum pa_alsa_direction {
+    PA_ALSA_DIRECTION_ANY,
+    PA_ALSA_DIRECTION_OUTPUT,
+    PA_ALSA_DIRECTION_INPUT
+} pa_alsa_direction_t;
+
+/* A setting combines a couple of options into a single entity that
+ * may be selected. Only one setting can be active at the same
+ * time. */
+struct pa_alsa_setting {
+    pa_alsa_path *path;
+    PA_LLIST_FIELDS(pa_alsa_setting);
+
+    pa_idxset *options;
+
+    char *name;
+    char *description;
+    unsigned priority;
+};
+
+/* An option belongs to an element and refers to one enumeration item
+ * of the element is an enumeration item, or a switch status if the
+ * element is a switch item. */
+struct pa_alsa_option {
+    pa_alsa_element *element;
+    PA_LLIST_FIELDS(pa_alsa_option);
+
+    char *alsa_name;
+    int alsa_idx;
+
+    char *name;
+    char *description;
+    unsigned priority;
+
+    pa_alsa_required_t required;
+    pa_alsa_required_t required_any;
+    pa_alsa_required_t required_absent;
+};
+
+/* An element wraps one specific ALSA element. A series of elements
+ * make up a path (see below). If the element is an enumeration or switch
+ * element it may include a list of options. */
+struct pa_alsa_element {
+    pa_alsa_path *path;
+    PA_LLIST_FIELDS(pa_alsa_element);
+
+    char *alsa_name;
+    pa_alsa_direction_t direction;
+
+    pa_alsa_switch_use_t switch_use;
+    pa_alsa_volume_use_t volume_use;
+    pa_alsa_enumeration_use_t enumeration_use;
+
+    pa_alsa_required_t required;
+    pa_alsa_required_t required_any;
+    pa_alsa_required_t required_absent;
+
+    long constant_volume;
+
+    bool override_map:1;
+    bool direction_try_other:1;
+
+    bool has_dB:1;
+    long min_volume, max_volume;
+    long volume_limit; /* -1 for no configured limit */
+    double min_dB, max_dB;
+
+    pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
+    unsigned n_channels;
+
+    pa_channel_position_mask_t merged_mask;
+
+    PA_LLIST_HEAD(pa_alsa_option, options);
+
+    pa_alsa_decibel_fix *db_fix;
+};
+
+struct pa_alsa_jack {
+    pa_alsa_path *path;
+    PA_LLIST_FIELDS(pa_alsa_jack);
+
+    char *name; /* E g "Headphone" */
+    char *alsa_name; /* E g "Headphone Jack" */
+    bool has_control; /* is the jack itself present? */
+    bool plugged_in; /* is this jack currently plugged in? */
+    snd_mixer_elem_t *melem; /* Jack detection handle */
+    pa_available_t state_unplugged, state_plugged;
+
+    pa_alsa_required_t required;
+    pa_alsa_required_t required_any;
+    pa_alsa_required_t required_absent;
+
+    pa_dynarray *ucm_devices; /* pa_alsa_ucm_device */
+    pa_dynarray *ucm_hw_mute_devices; /* pa_alsa_ucm_device */
+};
+
+pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *name);
+void pa_alsa_jack_free(pa_alsa_jack *jack);
+void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control);
+void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in);
+void pa_alsa_jack_add_ucm_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device);
+void pa_alsa_jack_add_ucm_hw_mute_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device);
+
+/* A path wraps a series of elements into a single entity which can be
+ * used to control it as if it had a single volume slider, a single
+ * mute switch and a single list of selectable options. */
+struct pa_alsa_path {
+    pa_alsa_direction_t direction;
+    pa_device_port* port;
+
+    char *name;
+    char *description_key;
+    char *description;
+    unsigned priority;
+    int eld_device;
+    pa_proplist *proplist;
+
+    bool probed:1;
+    bool supported:1;
+    bool has_mute:1;
+    bool has_volume:1;
+    bool has_dB:1;
+    bool mute_during_activation:1;
+    /* These two are used during probing only */
+    bool has_req_any:1;
+    bool req_any_present:1;
+
+    long min_volume, max_volume;
+    double min_dB, max_dB;
+
+    /* This is used during parsing only, as a shortcut so that we
+     * don't have to iterate the list all the time */
+    pa_alsa_element *last_element;
+    pa_alsa_option *last_option;
+    pa_alsa_setting *last_setting;
+    pa_alsa_jack *last_jack;
+
+    PA_LLIST_HEAD(pa_alsa_element, elements);
+    PA_LLIST_HEAD(pa_alsa_setting, settings);
+    PA_LLIST_HEAD(pa_alsa_jack, jacks);
+};
+
+/* A path set is simply a set of paths that are applicable to a
+ * device */
+struct pa_alsa_path_set {
+    pa_hashmap *paths;
+    pa_alsa_direction_t direction;
+};
+
+void pa_alsa_setting_dump(pa_alsa_setting *s);
+
+void pa_alsa_option_dump(pa_alsa_option *o);
+void pa_alsa_jack_dump(pa_alsa_jack *j);
+void pa_alsa_element_dump(pa_alsa_element *e);
+
+pa_alsa_path *pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction);
+pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction);
+int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, bool ignore_dB);
+void pa_alsa_path_dump(pa_alsa_path *p);
+int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v);
+int pa_alsa_path_get_mute(pa_alsa_path *path, snd_mixer_t *m, bool *muted);
+int pa_alsa_path_set_volume(pa_alsa_path *path, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, bool deferred_volume, bool write_to_hw);
+int pa_alsa_path_set_mute(pa_alsa_path *path, snd_mixer_t *m, bool muted);
+int pa_alsa_path_select(pa_alsa_path *p, pa_alsa_setting *s, snd_mixer_t *m, bool device_is_muted);
+void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata);
+void pa_alsa_path_free(pa_alsa_path *p);
+
+pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir);
+void pa_alsa_path_set_dump(pa_alsa_path_set *s);
+void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata);
+void pa_alsa_path_set_free(pa_alsa_path_set *s);
+
+struct pa_alsa_mapping {
+    pa_alsa_profile_set *profile_set;
+
+    char *name;
+    char *description;
+    unsigned priority;
+    pa_alsa_direction_t direction;
+    /* These are copied over to the resultant sink/source */
+    pa_proplist *proplist;
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+
+    char **device_strings;
+
+    char **input_path_names;
+    char **output_path_names;
+    char **input_element; /* list of fallbacks */
+    char **output_element;
+    pa_alsa_path_set *input_path_set;
+    pa_alsa_path_set *output_path_set;
+
+    unsigned supported;
+    bool exact_channels:1;
+    bool fallback:1;
+
+    /* Temporarily used during probing */
+    snd_pcm_t *input_pcm;
+    snd_pcm_t *output_pcm;
+
+    pa_sink *sink;
+    pa_source *source;
+
+    /* ucm device context*/
+    pa_alsa_ucm_mapping_context ucm_context;
+};
+
+struct pa_alsa_profile {
+    pa_alsa_profile_set *profile_set;
+
+    char *name;
+    char *description;
+    unsigned priority;
+
+    char *input_name;
+    char *output_name;
+
+    bool supported:1;
+    bool fallback_input:1;
+    bool fallback_output:1;
+
+    char **input_mapping_names;
+    char **output_mapping_names;
+
+    pa_idxset *input_mappings;
+    pa_idxset *output_mappings;
+};
+
+struct pa_alsa_decibel_fix {
+    pa_alsa_profile_set *profile_set;
+
+    char *name; /* Alsa volume element name. */
+    long min_step;
+    long max_step;
+
+    /* An array that maps alsa volume element steps to decibels. The steps can
+     * be used as indices to this array, after subtracting min_step from the
+     * real value.
+     *
+     * The values are actually stored as integers representing millibels,
+     * because that's the format the alsa API uses. */
+    long *db_values;
+};
+
+struct pa_alsa_profile_set {
+    pa_hashmap *mappings;
+    pa_hashmap *profiles;
+    pa_hashmap *decibel_fixes;
+    pa_hashmap *input_paths;
+    pa_hashmap *output_paths;
+
+    bool auto_profiles;
+    bool ignore_dB:1;
+    bool probed:1;
+};
+
+void pa_alsa_mapping_dump(pa_alsa_mapping *m);
+void pa_alsa_profile_dump(pa_alsa_profile *p);
+void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix);
+pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name);
+
+pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus);
+void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec);
+void pa_alsa_profile_set_free(pa_alsa_profile_set *s);
+void pa_alsa_profile_set_dump(pa_alsa_profile_set *s);
+void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *s);
+
+snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device);
+
+pa_alsa_fdlist *pa_alsa_fdlist_new(void);
+void pa_alsa_fdlist_free(pa_alsa_fdlist *fdl);
+int pa_alsa_fdlist_set_handle(pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, snd_hctl_t *hctl_handle, pa_mainloop_api* m);
+
+/* Alternative for handling alsa mixer events in io-thread. */
+
+pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void);
+void pa_alsa_mixer_pdata_free(pa_alsa_mixer_pdata *pd);
+int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp);
+
+/* Data structure for inclusion in pa_device_port for alsa
+ * sinks/sources. This contains nothing that needs to be freed
+ * individually */
+struct pa_alsa_port_data {
+    pa_alsa_path *path;
+    pa_alsa_setting *setting;
+};
+
+void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card);
+void pa_alsa_path_set_add_ports(pa_alsa_path_set *ps, pa_card_profile *cp, pa_hashmap *ports, pa_hashmap *extra, pa_core *core);
+
+#endif
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
new file mode 100644 (file)
index 0000000..827a650
--- /dev/null
@@ -0,0 +1,2539 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <stdio.h>
+
+#include <asoundlib.h>
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+#include <pulse/internal.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/module.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+
+#include <modules/reserve-wrap.h>
+
+#include "alsa-util.h"
+#include "alsa-sink.h"
+
+/* #define DEBUG_TIMING */
+
+#define DEFAULT_DEVICE "default"
+
+#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)             /* 2s    -- Overall buffer size */
+#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC)        /* 20ms  -- Fill up when only this much is left in the buffer */
+
+#define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC)       /* 10ms  -- On underrun, increase watermark by this */
+#define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC)        /* 5ms   -- When everything's great, decrease watermark by this */
+#define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC)    /* 20s   -- How long after a drop out recheck if things are good now */
+#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (0*PA_USEC_PER_MSEC)   /* 0ms   -- If the buffer level ever below this threshold, increase the watermark */
+#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms -- If the buffer level didn't drop below this threshold in the verification time, decrease the watermark */
+
+/* Note that TSCHED_WATERMARK_INC_THRESHOLD_USEC == 0 means that we
+ * will increase the watermark only if we hit a real underrun. */
+
+#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC)                /* 10ms  -- Sleep at least 10ms on each iteration */
+#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC)                /* 4ms   -- Wakeup at least this long before the buffer runs empty*/
+
+#define SMOOTHER_WINDOW_USEC  (10*PA_USEC_PER_SEC)                 /* 10s   -- smoother windows size */
+#define SMOOTHER_ADJUST_USEC  (1*PA_USEC_PER_SEC)                  /* 1s    -- smoother adjust time */
+
+#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC)                 /* 2ms   -- min smoother update interval */
+#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC)               /* 200ms -- max smoother update interval */
+
+#define VOLUME_ACCURACY (PA_VOLUME_NORM/100)  /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */
+
+#define DEFAULT_REWIND_SAFEGUARD_BYTES (256U) /* 1.33ms @48kHz, we'll never rewind less than this */
+#define DEFAULT_REWIND_SAFEGUARD_USEC (1330) /* 1.33ms, depending on channels/rate/sample we may rewind more than 256 above */
+
+#define DEFAULT_WRITE_ITERATION_THRESHOLD 0.03 /* don't iterate write if < 3% of the buffer is available */
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    snd_pcm_t *pcm_handle;
+
+    char *paths_dir;
+    pa_alsa_fdlist *mixer_fdl;
+    pa_alsa_mixer_pdata *mixer_pd;
+    snd_mixer_t *mixer_handle;
+    pa_alsa_path_set *mixer_path_set;
+    pa_alsa_path *mixer_path;
+
+    pa_cvolume hardware_volume;
+
+    unsigned int *rates;
+
+    size_t
+        frame_size,
+        fragment_size,
+        hwbuf_size,
+        tsched_watermark,
+        tsched_watermark_ref,
+        hwbuf_unused,
+        min_sleep,
+        min_wakeup,
+        watermark_inc_step,
+        watermark_dec_step,
+        watermark_inc_threshold,
+        watermark_dec_threshold,
+        rewind_safeguard;
+
+    snd_pcm_uframes_t frames_per_block;
+
+    pa_usec_t watermark_dec_not_before;
+    pa_usec_t min_latency_ref;
+    pa_usec_t tsched_watermark_usec;
+
+    pa_memchunk memchunk;
+
+    char *device_name;  /* name of the PCM device */
+    char *control_device; /* name of the control device */
+
+    bool use_mmap:1, use_tsched:1, deferred_volume:1, fixed_latency_range:1;
+
+    bool first, after_rewind;
+
+    pa_rtpoll_item *alsa_rtpoll_item;
+
+    pa_smoother *smoother;
+    uint64_t write_count;
+    uint64_t since_start;
+    pa_usec_t smoother_interval;
+    pa_usec_t last_smoother_update;
+
+    pa_idxset *formats;
+
+    pa_reserve_wrapper *reserve;
+    pa_hook_slot *reserve_slot;
+    pa_reserve_monitor_wrapper *monitor;
+    pa_hook_slot *monitor_slot;
+
+    /* ucm context */
+    pa_alsa_ucm_mapping_context *ucm_context;
+};
+
+static void userdata_free(struct userdata *u);
+
+/* FIXME: Is there a better way to do this than device names? */
+static bool is_iec958(struct userdata *u) {
+    return (strncmp("iec958", u->device_name, 6) == 0);
+}
+
+static bool is_hdmi(struct userdata *u) {
+    return (strncmp("hdmi", u->device_name, 4) == 0);
+}
+
+static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct userdata *u) {
+    pa_assert(r);
+    pa_assert(u);
+
+    pa_log_debug("Suspending sink %s, because another application requested us to release the device.", u->sink->name);
+
+    if (pa_sink_suspend(u->sink, true, PA_SUSPEND_APPLICATION) < 0)
+        return PA_HOOK_CANCEL;
+
+    return PA_HOOK_OK;
+}
+
+static void reserve_done(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->reserve_slot) {
+        pa_hook_slot_free(u->reserve_slot);
+        u->reserve_slot = NULL;
+    }
+
+    if (u->reserve) {
+        pa_reserve_wrapper_unref(u->reserve);
+        u->reserve = NULL;
+    }
+}
+
+static void reserve_update(struct userdata *u) {
+    const char *description;
+    pa_assert(u);
+
+    if (!u->sink || !u->reserve)
+        return;
+
+    if ((description = pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+        pa_reserve_wrapper_set_application_device_name(u->reserve, description);
+}
+
+static int reserve_init(struct userdata *u, const char *dname) {
+    char *rname;
+
+    pa_assert(u);
+    pa_assert(dname);
+
+    if (u->reserve)
+        return 0;
+
+    if (pa_in_system_mode())
+        return 0;
+
+    if (!(rname = pa_alsa_get_reserve_name(dname)))
+        return 0;
+
+    /* We are resuming, try to lock the device */
+    u->reserve = pa_reserve_wrapper_get(u->core, rname);
+    pa_xfree(rname);
+
+    if (!(u->reserve))
+        return -1;
+
+    reserve_update(u);
+
+    pa_assert(!u->reserve_slot);
+    u->reserve_slot = pa_hook_connect(pa_reserve_wrapper_hook(u->reserve), PA_HOOK_NORMAL, (pa_hook_cb_t) reserve_cb, u);
+
+    return 0;
+}
+
+static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) {
+    pa_assert(w);
+    pa_assert(u);
+
+    if (PA_PTR_TO_UINT(busy) && !u->reserve) {
+        pa_log_debug("Suspending sink %s, because another application is blocking the access to the device.", u->sink->name);
+        pa_sink_suspend(u->sink, true, PA_SUSPEND_APPLICATION);
+    } else {
+        pa_log_debug("Resuming sink %s, because other applications aren't blocking access to the device any more.", u->sink->name);
+        pa_sink_suspend(u->sink, false, PA_SUSPEND_APPLICATION);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static void monitor_done(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->monitor_slot) {
+        pa_hook_slot_free(u->monitor_slot);
+        u->monitor_slot = NULL;
+    }
+
+    if (u->monitor) {
+        pa_reserve_monitor_wrapper_unref(u->monitor);
+        u->monitor = NULL;
+    }
+}
+
+static int reserve_monitor_init(struct userdata *u, const char *dname) {
+    char *rname;
+
+    pa_assert(u);
+    pa_assert(dname);
+
+    if (pa_in_system_mode())
+        return 0;
+
+    if (!(rname = pa_alsa_get_reserve_name(dname)))
+        return 0;
+
+    /* We are resuming, try to lock the device */
+    u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname);
+    pa_xfree(rname);
+
+    if (!(u->monitor))
+        return -1;
+
+    pa_assert(!u->monitor_slot);
+    u->monitor_slot = pa_hook_connect(pa_reserve_monitor_wrapper_hook(u->monitor), PA_HOOK_NORMAL, (pa_hook_cb_t) monitor_cb, u);
+
+    return 0;
+}
+
+static void fix_min_sleep_wakeup(struct userdata *u) {
+    size_t max_use, max_use_2;
+
+    pa_assert(u);
+    pa_assert(u->use_tsched);
+
+    max_use = u->hwbuf_size - u->hwbuf_unused;
+    max_use_2 = pa_frame_align(max_use/2, &u->sink->sample_spec);
+
+    u->min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->sink->sample_spec);
+    u->min_sleep = PA_CLAMP(u->min_sleep, u->frame_size, max_use_2);
+
+    u->min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->sink->sample_spec);
+    u->min_wakeup = PA_CLAMP(u->min_wakeup, u->frame_size, max_use_2);
+}
+
+static void fix_tsched_watermark(struct userdata *u) {
+    size_t max_use;
+    pa_assert(u);
+    pa_assert(u->use_tsched);
+
+    max_use = u->hwbuf_size - u->hwbuf_unused;
+
+    if (u->tsched_watermark > max_use - u->min_sleep)
+        u->tsched_watermark = max_use - u->min_sleep;
+
+    if (u->tsched_watermark < u->min_wakeup)
+        u->tsched_watermark = u->min_wakeup;
+
+    u->tsched_watermark_usec = pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec);
+}
+
+static void increase_watermark(struct userdata *u) {
+    size_t old_watermark;
+    pa_usec_t old_min_latency, new_min_latency;
+
+    pa_assert(u);
+    pa_assert(u->use_tsched);
+
+    /* First, just try to increase the watermark */
+    old_watermark = u->tsched_watermark;
+    u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_inc_step);
+    fix_tsched_watermark(u);
+
+    if (old_watermark != u->tsched_watermark) {
+        pa_log_info("Increasing wakeup watermark to %0.2f ms",
+                    (double) u->tsched_watermark_usec / PA_USEC_PER_MSEC);
+        return;
+    }
+
+    /* Hmm, we cannot increase the watermark any further, hence let's
+       raise the latency, unless doing so was disabled in
+       configuration */
+    if (u->fixed_latency_range)
+        return;
+
+    old_min_latency = u->sink->thread_info.min_latency;
+    new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC);
+    new_min_latency = PA_MIN(new_min_latency, u->sink->thread_info.max_latency);
+
+    if (old_min_latency != new_min_latency) {
+        pa_log_info("Increasing minimal latency to %0.2f ms",
+                    (double) new_min_latency / PA_USEC_PER_MSEC);
+
+        pa_sink_set_latency_range_within_thread(u->sink, new_min_latency, u->sink->thread_info.max_latency);
+    }
+
+    /* When we reach this we're officially fucked! */
+}
+
+static void decrease_watermark(struct userdata *u) {
+    size_t old_watermark;
+    pa_usec_t now;
+
+    pa_assert(u);
+    pa_assert(u->use_tsched);
+
+    now = pa_rtclock_now();
+
+    if (u->watermark_dec_not_before <= 0)
+        goto restart;
+
+    if (u->watermark_dec_not_before > now)
+        return;
+
+    old_watermark = u->tsched_watermark;
+
+    if (u->tsched_watermark < u->watermark_dec_step)
+        u->tsched_watermark = u->tsched_watermark / 2;
+    else
+        u->tsched_watermark = PA_MAX(u->tsched_watermark / 2, u->tsched_watermark - u->watermark_dec_step);
+
+    fix_tsched_watermark(u);
+
+    if (old_watermark != u->tsched_watermark)
+        pa_log_info("Decreasing wakeup watermark to %0.2f ms",
+                    (double) u->tsched_watermark_usec / PA_USEC_PER_MSEC);
+
+    /* We don't change the latency range*/
+
+restart:
+    u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC;
+}
+
+static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
+    pa_usec_t usec, wm;
+
+    pa_assert(sleep_usec);
+    pa_assert(process_usec);
+
+    pa_assert(u);
+    pa_assert(u->use_tsched);
+
+    usec = pa_sink_get_requested_latency_within_thread(u->sink);
+
+    if (usec == (pa_usec_t) -1)
+        usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec);
+
+    wm = u->tsched_watermark_usec;
+
+    if (wm > usec)
+        wm = usec/2;
+
+    *sleep_usec = usec - wm;
+    *process_usec = wm;
+
+#ifdef DEBUG_TIMING
+    pa_log_debug("Buffer time: %lu ms; Sleep time: %lu ms; Process time: %lu ms",
+                 (unsigned long) (usec / PA_USEC_PER_MSEC),
+                 (unsigned long) (*sleep_usec / PA_USEC_PER_MSEC),
+                 (unsigned long) (*process_usec / PA_USEC_PER_MSEC));
+#endif
+}
+
+static int try_recover(struct userdata *u, const char *call, int err) {
+    pa_assert(u);
+    pa_assert(call);
+    pa_assert(err < 0);
+
+    pa_log_debug("%s: %s", call, pa_alsa_strerror(err));
+
+    pa_assert(err != -EAGAIN);
+
+    if (err == -EPIPE)
+        pa_log_debug("%s: Buffer underrun!", call);
+
+    if (err == -ESTRPIPE)
+        pa_log_debug("%s: System suspended!", call);
+
+    if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) {
+        pa_log("%s: %s", call, pa_alsa_strerror(err));
+        return -1;
+    }
+
+    u->first = true;
+    u->since_start = 0;
+    return 0;
+}
+
+static size_t check_left_to_play(struct userdata *u, size_t n_bytes, bool on_timeout) {
+    size_t left_to_play;
+    bool underrun = false;
+
+    /* We use <= instead of < for this check here because an underrun
+     * only happens after the last sample was processed, not already when
+     * it is removed from the buffer. This is particularly important
+     * when block transfer is used. */
+
+    if (n_bytes <= u->hwbuf_size)
+        left_to_play = u->hwbuf_size - n_bytes;
+    else {
+
+        /* We got a dropout. What a mess! */
+        left_to_play = 0;
+        underrun = true;
+
+#if 0
+        PA_DEBUG_TRAP;
+#endif
+
+        if (!u->first && !u->after_rewind)
+            if (pa_log_ratelimit(PA_LOG_INFO))
+                pa_log_info("Underrun!");
+    }
+
+#ifdef DEBUG_TIMING
+    pa_log_debug("%0.2f ms left to play; inc threshold = %0.2f ms; dec threshold = %0.2f ms",
+                 (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC,
+                 (double) pa_bytes_to_usec(u->watermark_inc_threshold, &u->sink->sample_spec) / PA_USEC_PER_MSEC,
+                 (double) pa_bytes_to_usec(u->watermark_dec_threshold, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
+#endif
+
+    if (u->use_tsched) {
+        bool reset_not_before = true;
+
+        if (!u->first && !u->after_rewind) {
+            if (underrun || left_to_play < u->watermark_inc_threshold)
+                increase_watermark(u);
+            else if (left_to_play > u->watermark_dec_threshold) {
+                reset_not_before = false;
+
+                /* We decrease the watermark only if have actually
+                 * been woken up by a timeout. If something else woke
+                 * us up it's too easy to fulfill the deadlines... */
+
+                if (on_timeout)
+                    decrease_watermark(u);
+            }
+        }
+
+        if (reset_not_before)
+            u->watermark_dec_not_before = 0;
+    }
+
+    return left_to_play;
+}
+
+static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, bool polled, bool on_timeout) {
+    bool work_done = false;
+    pa_usec_t max_sleep_usec = 0, process_usec = 0;
+    size_t left_to_play, input_underrun;
+    unsigned j = 0;
+
+    pa_assert(u);
+    pa_sink_assert_ref(u->sink);
+
+    if (u->use_tsched)
+        hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+    for (;;) {
+        snd_pcm_sframes_t n;
+        size_t n_bytes;
+        int r;
+        bool after_avail = true;
+
+        /* First we determine how many samples are missing to fill the
+         * buffer up to 100% */
+
+        if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
+
+            if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
+                continue;
+
+            return r;
+        }
+
+        n_bytes = (size_t) n * u->frame_size;
+
+#ifdef DEBUG_TIMING
+        pa_log_debug("avail: %lu", (unsigned long) n_bytes);
+#endif
+
+        left_to_play = check_left_to_play(u, n_bytes, on_timeout);
+        on_timeout = false;
+
+        if (u->use_tsched)
+
+            /* We won't fill up the playback buffer before at least
+            * half the sleep time is over because otherwise we might
+            * ask for more data from the clients then they expect. We
+            * need to guarantee that clients only have to keep around
+            * a single hw buffer length. */
+
+            if (!polled &&
+                pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2) {
+#ifdef DEBUG_TIMING
+                pa_log_debug("Not filling up, because too early.");
+#endif
+                break;
+            }
+
+        if (PA_UNLIKELY(n_bytes <= u->hwbuf_unused)) {
+
+            if (polled)
+                PA_ONCE_BEGIN {
+                    char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle);
+                    pa_log(_("ALSA woke us up to write new data to the device, but there was actually nothing to write.\n"
+                             "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+                             "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."),
+                           pa_strnull(dn));
+                    pa_xfree(dn);
+                } PA_ONCE_END;
+
+#ifdef DEBUG_TIMING
+            pa_log_debug("Not filling up, because not necessary.");
+#endif
+            break;
+        }
+
+        j++;
+
+        if (j > 10) {
+#ifdef DEBUG_TIMING
+            pa_log_debug("Not filling up, because already too many iterations.");
+#endif
+
+            break;
+        } else if (j >= 2 && (n_bytes < (DEFAULT_WRITE_ITERATION_THRESHOLD * (u->hwbuf_size - u->hwbuf_unused)))) {
+#ifdef DEBUG_TIMING
+            pa_log_debug("Not filling up, because <%g%% available.", DEFAULT_WRITE_ITERATION_THRESHOLD * 100);
+#endif
+            break;
+        }
+
+        n_bytes -= u->hwbuf_unused;
+        polled = false;
+
+#ifdef DEBUG_TIMING
+        pa_log_debug("Filling up");
+#endif
+
+        for (;;) {
+            pa_memchunk chunk;
+            void *p;
+            int err;
+            const snd_pcm_channel_area_t *areas;
+            snd_pcm_uframes_t offset, frames;
+            snd_pcm_sframes_t sframes;
+            size_t written;
+
+            frames = (snd_pcm_uframes_t) (n_bytes / u->frame_size);
+/*             pa_log_debug("%lu frames to write", (unsigned long) frames); */
+
+            if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
+
+                if (!after_avail && err == -EAGAIN)
+                    break;
+
+                if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            /* Make sure that if these memblocks need to be copied they will fit into one slot */
+            frames = PA_MIN(frames, u->frames_per_block);
+
+            if (!after_avail && frames == 0)
+                break;
+
+            pa_assert(frames > 0);
+            after_avail = false;
+
+            /* Check these are multiples of 8 bit */
+            pa_assert((areas[0].first & 7) == 0);
+            pa_assert((areas[0].step & 7) == 0);
+
+            /* We assume a single interleaved memory buffer */
+            pa_assert((areas[0].first >> 3) == 0);
+            pa_assert((areas[0].step >> 3) == u->frame_size);
+
+            p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
+
+            written = frames * u->frame_size;
+            chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, written, true);
+            chunk.length = pa_memblock_get_length(chunk.memblock);
+            chunk.index = 0;
+
+            pa_sink_render_into_full(u->sink, &chunk);
+            pa_memblock_unref_fixed(chunk.memblock);
+
+            if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
+
+                if ((int) sframes == -EAGAIN)
+                    break;
+
+                if ((r = try_recover(u, "snd_pcm_mmap_commit", (int) sframes)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            work_done = true;
+
+            u->write_count += written;
+            u->since_start += written;
+
+#ifdef DEBUG_TIMING
+            pa_log_debug("Wrote %lu bytes (of possible %lu bytes)", (unsigned long) written, (unsigned long) n_bytes);
+#endif
+
+            if (written >= n_bytes)
+                break;
+
+            n_bytes -= written;
+        }
+    }
+
+    input_underrun = pa_sink_process_input_underruns(u->sink, left_to_play);
+
+    if (u->use_tsched) {
+        pa_usec_t underrun_sleep = pa_bytes_to_usec_round_up(input_underrun, &u->sink->sample_spec);
+
+        *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec);
+        process_usec = u->tsched_watermark_usec;
+
+        if (*sleep_usec > process_usec)
+            *sleep_usec -= process_usec;
+        else
+            *sleep_usec = 0;
+
+        *sleep_usec = PA_MIN(*sleep_usec, underrun_sleep);
+    } else
+        *sleep_usec = 0;
+
+    return work_done ? 1 : 0;
+}
+
+static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, bool polled, bool on_timeout) {
+    bool work_done = false;
+    pa_usec_t max_sleep_usec = 0, process_usec = 0;
+    size_t left_to_play, input_underrun;
+    unsigned j = 0;
+
+    pa_assert(u);
+    pa_sink_assert_ref(u->sink);
+
+    if (u->use_tsched)
+        hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+    for (;;) {
+        snd_pcm_sframes_t n;
+        size_t n_bytes;
+        int r;
+        bool after_avail = true;
+
+        if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
+
+            if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
+                continue;
+
+            return r;
+        }
+
+        n_bytes = (size_t) n * u->frame_size;
+
+#ifdef DEBUG_TIMING
+        pa_log_debug("avail: %lu", (unsigned long) n_bytes);
+#endif
+
+        left_to_play = check_left_to_play(u, n_bytes, on_timeout);
+        on_timeout = false;
+
+        if (u->use_tsched)
+
+            /* We won't fill up the playback buffer before at least
+            * half the sleep time is over because otherwise we might
+            * ask for more data from the clients then they expect. We
+            * need to guarantee that clients only have to keep around
+            * a single hw buffer length. */
+
+            if (!polled &&
+                pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
+                break;
+
+        if (PA_UNLIKELY(n_bytes <= u->hwbuf_unused)) {
+
+            if (polled)
+                PA_ONCE_BEGIN {
+                    char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle);
+                    pa_log(_("ALSA woke us up to write new data to the device, but there was actually nothing to write!\n"
+                             "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+                             "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."),
+                           pa_strnull(dn));
+                    pa_xfree(dn);
+                } PA_ONCE_END;
+
+            break;
+        }
+
+        j++;
+
+        if (j > 10) {
+#ifdef DEBUG_TIMING
+            pa_log_debug("Not filling up, because already too many iterations.");
+#endif
+
+            break;
+        } else if (j >= 2 && (n_bytes < (DEFAULT_WRITE_ITERATION_THRESHOLD * (u->hwbuf_size - u->hwbuf_unused)))) {
+#ifdef DEBUG_TIMING
+            pa_log_debug("Not filling up, because <%g%% available.", DEFAULT_WRITE_ITERATION_THRESHOLD * 100);
+#endif
+            break;
+        }
+
+        n_bytes -= u->hwbuf_unused;
+        polled = false;
+
+        for (;;) {
+            snd_pcm_sframes_t frames;
+            void *p;
+            size_t written;
+
+/*         pa_log_debug("%lu frames to write", (unsigned long) frames); */
+
+            if (u->memchunk.length <= 0)
+                pa_sink_render(u->sink, n_bytes, &u->memchunk);
+
+            pa_assert(u->memchunk.length > 0);
+
+            frames = (snd_pcm_sframes_t) (u->memchunk.length / u->frame_size);
+
+            if (frames > (snd_pcm_sframes_t) (n_bytes/u->frame_size))
+                frames = (snd_pcm_sframes_t) (n_bytes/u->frame_size);
+
+            p = pa_memblock_acquire(u->memchunk.memblock);
+            frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, (snd_pcm_uframes_t) frames);
+            pa_memblock_release(u->memchunk.memblock);
+
+            if (PA_UNLIKELY(frames < 0)) {
+
+                if (!after_avail && (int) frames == -EAGAIN)
+                    break;
+
+                if ((r = try_recover(u, "snd_pcm_writei", (int) frames)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            if (!after_avail && frames == 0)
+                break;
+
+            pa_assert(frames > 0);
+            after_avail = false;
+
+            written = frames * u->frame_size;
+            u->memchunk.index += written;
+            u->memchunk.length -= written;
+
+            if (u->memchunk.length <= 0) {
+                pa_memblock_unref(u->memchunk.memblock);
+                pa_memchunk_reset(&u->memchunk);
+            }
+
+            work_done = true;
+
+            u->write_count += written;
+            u->since_start += written;
+
+/*         pa_log_debug("wrote %lu frames", (unsigned long) frames); */
+
+            if (written >= n_bytes)
+                break;
+
+            n_bytes -= written;
+        }
+    }
+
+    input_underrun = pa_sink_process_input_underruns(u->sink, left_to_play);
+
+    if (u->use_tsched) {
+        pa_usec_t underrun_sleep = pa_bytes_to_usec_round_up(input_underrun, &u->sink->sample_spec);
+
+        *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec);
+        process_usec = u->tsched_watermark_usec;
+
+        if (*sleep_usec > process_usec)
+            *sleep_usec -= process_usec;
+        else
+            *sleep_usec = 0;
+
+        *sleep_usec = PA_MIN(*sleep_usec, underrun_sleep);
+    } else
+        *sleep_usec = 0;
+
+    return work_done ? 1 : 0;
+}
+
+static void update_smoother(struct userdata *u) {
+    snd_pcm_sframes_t delay = 0;
+    int64_t position;
+    int err;
+    pa_usec_t now1 = 0, now2;
+    snd_pcm_status_t *status;
+    snd_htimestamp_t htstamp = { 0, 0 };
+
+    snd_pcm_status_alloca(&status);
+
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    /* Let's update the time smoother */
+
+    if (PA_UNLIKELY((err = pa_alsa_safe_delay(u->pcm_handle, status, &delay, u->hwbuf_size, &u->sink->sample_spec, false)) < 0)) {
+        pa_log_warn("Failed to query DSP status data: %s", pa_alsa_strerror(err));
+        return;
+    }
+
+    snd_pcm_status_get_htstamp(status, &htstamp);
+    now1 = pa_timespec_load(&htstamp);
+
+    /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */
+    if (now1 <= 0)
+        now1 = pa_rtclock_now();
+
+    /* check if the time since the last update is bigger than the interval */
+    if (u->last_smoother_update > 0)
+        if (u->last_smoother_update + u->smoother_interval > now1)
+            return;
+
+    position = (int64_t) u->write_count - ((int64_t) delay * (int64_t) u->frame_size);
+
+    if (PA_UNLIKELY(position < 0))
+        position = 0;
+
+    now2 = pa_bytes_to_usec((uint64_t) position, &u->sink->sample_spec);
+
+    pa_smoother_put(u->smoother, now1, now2);
+
+    u->last_smoother_update = now1;
+    /* exponentially increase the update interval up to the MAX limit */
+    u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL);
+}
+
+static int64_t sink_get_latency(struct userdata *u) {
+    int64_t delay;
+    pa_usec_t now1, now2;
+
+    pa_assert(u);
+
+    now1 = pa_rtclock_now();
+    now2 = pa_smoother_get(u->smoother, now1);
+
+    delay = (int64_t) pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) - (int64_t) now2;
+
+    if (u->memchunk.memblock)
+        delay += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
+
+    return delay;
+}
+
+static int build_pollfd(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    if (u->alsa_rtpoll_item)
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+    if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll)))
+        return -1;
+
+    return 0;
+}
+
+/* Called from IO context */
+static int suspend(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    pa_smoother_pause(u->smoother, pa_rtclock_now());
+
+    /* Let's suspend -- we don't call snd_pcm_drain() here since that might
+     * take awfully long with our long buffer sizes today. */
+    snd_pcm_close(u->pcm_handle);
+    u->pcm_handle = NULL;
+
+    if (u->alsa_rtpoll_item) {
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+        u->alsa_rtpoll_item = NULL;
+    }
+
+    /* We reset max_rewind/max_request here to make sure that while we
+     * are suspended the old max_request/max_rewind values set before
+     * the suspend can influence the per-stream buffer of newly
+     * created streams, without their requirements having any
+     * influence on them. */
+    pa_sink_set_max_rewind_within_thread(u->sink, 0);
+    pa_sink_set_max_request_within_thread(u->sink, 0);
+
+    pa_log_info("Device suspended...");
+
+    return 0;
+}
+
+/* Called from IO context */
+static int update_sw_params(struct userdata *u) {
+    snd_pcm_uframes_t avail_min;
+    int err;
+
+    pa_assert(u);
+
+    /* Use the full buffer if no one asked us for anything specific */
+    u->hwbuf_unused = 0;
+
+    if (u->use_tsched) {
+        pa_usec_t latency;
+
+        if ((latency = pa_sink_get_requested_latency_within_thread(u->sink)) != (pa_usec_t) -1) {
+            size_t b;
+
+            pa_log_debug("Latency set to %0.2fms", (double) latency / PA_USEC_PER_MSEC);
+
+            b = pa_usec_to_bytes(latency, &u->sink->sample_spec);
+
+            /* We need at least one sample in our buffer */
+
+            if (PA_UNLIKELY(b < u->frame_size))
+                b = u->frame_size;
+
+            u->hwbuf_unused = PA_LIKELY(b < u->hwbuf_size) ? (u->hwbuf_size - b) : 0;
+        }
+
+        fix_min_sleep_wakeup(u);
+        fix_tsched_watermark(u);
+    }
+
+    pa_log_debug("hwbuf_unused=%lu", (unsigned long) u->hwbuf_unused);
+
+    /* We need at last one frame in the used part of the buffer */
+    avail_min = (snd_pcm_uframes_t) u->hwbuf_unused / u->frame_size + 1;
+
+    if (u->use_tsched) {
+        pa_usec_t sleep_usec, process_usec;
+
+        hw_sleep_time(u, &sleep_usec, &process_usec);
+        avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec) / u->frame_size;
+    }
+
+    pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
+
+    if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min, !u->use_tsched)) < 0) {
+        pa_log("Failed to set software parameters: %s", pa_alsa_strerror(err));
+        return err;
+    }
+
+    pa_sink_set_max_request_within_thread(u->sink, u->hwbuf_size - u->hwbuf_unused);
+     if (pa_alsa_pcm_is_hw(u->pcm_handle))
+         pa_sink_set_max_rewind_within_thread(u->sink, u->hwbuf_size);
+    else {
+        pa_log_info("Disabling rewind_within_thread for device %s", u->device_name);
+        pa_sink_set_max_rewind_within_thread(u->sink, 0);
+    }
+
+    return 0;
+}
+
+/* Called from IO Context on unsuspend or from main thread when creating sink */
+static void reset_watermark(struct userdata *u, size_t tsched_watermark, pa_sample_spec *ss,
+                            bool in_thread) {
+    u->tsched_watermark = pa_convert_size(tsched_watermark, ss, &u->sink->sample_spec);
+
+    u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->sink->sample_spec);
+    u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->sink->sample_spec);
+
+    u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->sink->sample_spec);
+    u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->sink->sample_spec);
+
+    fix_min_sleep_wakeup(u);
+    fix_tsched_watermark(u);
+
+    if (in_thread)
+        pa_sink_set_latency_range_within_thread(u->sink,
+                                                u->min_latency_ref,
+                                                pa_bytes_to_usec(u->hwbuf_size, ss));
+    else {
+        pa_sink_set_latency_range(u->sink,
+                                  0,
+                                  pa_bytes_to_usec(u->hwbuf_size, ss));
+
+        /* work-around assert in pa_sink_set_latency_within_thead,
+           keep track of min_latency and reuse it when
+           this routine is called from IO context */
+        u->min_latency_ref = u->sink->thread_info.min_latency;
+    }
+
+    pa_log_info("Time scheduling watermark is %0.2fms",
+                (double) u->tsched_watermark_usec / PA_USEC_PER_MSEC);
+}
+
+/* Called from IO context */
+static int unsuspend(struct userdata *u) {
+    pa_sample_spec ss;
+    int err;
+    bool b, d;
+    snd_pcm_uframes_t period_size, buffer_size;
+    char *device_name = NULL;
+
+    pa_assert(u);
+    pa_assert(!u->pcm_handle);
+
+    pa_log_info("Trying resume...");
+
+    if ((is_iec958(u) || is_hdmi(u)) && pa_sink_is_passthrough(u->sink)) {
+        /* Need to open device in NONAUDIO mode */
+        int len = strlen(u->device_name) + 8;
+
+        device_name = pa_xmalloc(len);
+        pa_snprintf(device_name, len, "%s,AES0=6", u->device_name);
+    }
+
+    if ((err = snd_pcm_open(&u->pcm_handle, device_name ? device_name : u->device_name, SND_PCM_STREAM_PLAYBACK,
+                            SND_PCM_NONBLOCK|
+                            SND_PCM_NO_AUTO_RESAMPLE|
+                            SND_PCM_NO_AUTO_CHANNELS|
+                            SND_PCM_NO_AUTO_FORMAT)) < 0) {
+        pa_log("Error opening PCM device %s: %s", u->device_name, pa_alsa_strerror(err));
+        goto fail;
+    }
+
+    ss = u->sink->sample_spec;
+    period_size = u->fragment_size / u->frame_size;
+    buffer_size = u->hwbuf_size / u->frame_size;
+    b = u->use_mmap;
+    d = u->use_tsched;
+
+    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, true)) < 0) {
+        pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err));
+        goto fail;
+    }
+
+    if (b != u->use_mmap || d != u->use_tsched) {
+        pa_log_warn("Resume failed, couldn't get original access mode.");
+        goto fail;
+    }
+
+    if (!pa_sample_spec_equal(&ss, &u->sink->sample_spec)) {
+        pa_log_warn("Resume failed, couldn't restore original sample settings.");
+        goto fail;
+    }
+
+    if (period_size*u->frame_size != u->fragment_size ||
+        buffer_size*u->frame_size != u->hwbuf_size) {
+        pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu/%lu, New %lu/%lu)",
+                    (unsigned long) u->hwbuf_size, (unsigned long) u->fragment_size,
+                    (unsigned long) (buffer_size*u->frame_size), (unsigned long) (period_size*u->frame_size));
+        goto fail;
+    }
+
+    if (update_sw_params(u) < 0)
+        goto fail;
+
+    if (build_pollfd(u) < 0)
+        goto fail;
+
+    u->write_count = 0;
+    pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
+    u->smoother_interval = SMOOTHER_MIN_INTERVAL;
+    u->last_smoother_update = 0;
+
+    u->first = true;
+    u->since_start = 0;
+
+    /* reset the watermark to the value defined when sink was created */
+    if (u->use_tsched)
+        reset_watermark(u, u->tsched_watermark_ref, &u->sink->sample_spec, true);
+
+    pa_log_info("Resumed successfully...");
+
+    pa_xfree(device_name);
+    return 0;
+
+fail:
+    if (u->pcm_handle) {
+        snd_pcm_close(u->pcm_handle);
+        u->pcm_handle = NULL;
+    }
+
+    pa_xfree(device_name);
+
+    return -PA_ERR_IO;
+}
+
+/* Called from IO context */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            int64_t r = 0;
+
+            if (u->pcm_handle)
+                r = sink_get_latency(u);
+
+            *((int64_t*) data) = r;
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SINK_SUSPENDED: {
+                    int r;
+
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+
+                    if ((r = suspend(u)) < 0)
+                        return r;
+
+                    break;
+                }
+
+                case PA_SINK_IDLE:
+                case PA_SINK_RUNNING: {
+                    int r;
+
+                    if (u->sink->thread_info.state == PA_SINK_INIT) {
+                        if (build_pollfd(u) < 0)
+                            return -PA_ERR_IO;
+                    }
+
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+                        if ((r = unsuspend(u)) < 0)
+                            return r;
+                    }
+
+                    break;
+                }
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                case PA_SINK_INVALID_STATE:
+                    ;
+            }
+
+            break;
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state_cb(pa_sink *s, pa_sink_state_t new_state) {
+    pa_sink_state_t old_state;
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    old_state = pa_sink_get_state(u->sink);
+
+    if (PA_SINK_IS_OPENED(old_state) && new_state == PA_SINK_SUSPENDED)
+        reserve_done(u);
+    else if (old_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(new_state))
+        if (reserve_init(u, u->device_name) < 0)
+            return -PA_ERR_BUSY;
+
+    return 0;
+}
+
+static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+
+    pa_assert(u);
+    pa_assert(u->mixer_handle);
+
+    if (mask == SND_CTL_EVENT_MASK_REMOVE)
+        return 0;
+
+    if (!PA_SINK_IS_LINKED(u->sink->state))
+        return 0;
+
+    if (u->sink->suspend_cause & PA_SUSPEND_SESSION) {
+        pa_sink_set_mixer_dirty(u->sink, true);
+        return 0;
+    }
+
+    if (mask & SND_CTL_EVENT_MASK_VALUE) {
+        pa_sink_get_volume(u->sink, true);
+        pa_sink_get_mute(u->sink, true);
+    }
+
+    return 0;
+}
+
+static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+
+    pa_assert(u);
+    pa_assert(u->mixer_handle);
+
+    if (mask == SND_CTL_EVENT_MASK_REMOVE)
+        return 0;
+
+    if (u->sink->suspend_cause & PA_SUSPEND_SESSION) {
+        pa_sink_set_mixer_dirty(u->sink, true);
+        return 0;
+    }
+
+    if (mask & SND_CTL_EVENT_MASK_VALUE)
+        pa_sink_update_volume_and_mute(u->sink);
+
+    return 0;
+}
+
+static void sink_get_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    pa_cvolume r;
+    char volume_buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+
+    if (pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0)
+        return;
+
+    /* Shift down by the base volume, so that 0dB becomes maximum volume */
+    pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
+
+    pa_log_debug("Read hardware volume: %s",
+                 pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &r, &s->channel_map, u->mixer_path->has_dB));
+
+    if (pa_cvolume_equal(&u->hardware_volume, &r))
+        return;
+
+    s->real_volume = u->hardware_volume = r;
+
+    /* Hmm, so the hardware volume changed, let's reset our software volume */
+    if (u->mixer_path->has_dB)
+        pa_sink_set_soft_volume(s, NULL);
+}
+
+static void sink_set_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    pa_cvolume r;
+    char volume_buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    bool deferred_volume = !!(s->flags & PA_SINK_DEFERRED_VOLUME);
+
+    pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+
+    /* Shift up by the base volume */
+    pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume);
+
+    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, deferred_volume, !deferred_volume) < 0)
+        return;
+
+    /* Shift down by the base volume, so that 0dB becomes maximum volume */
+    pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
+
+    u->hardware_volume = r;
+
+    if (u->mixer_path->has_dB) {
+        pa_cvolume new_soft_volume;
+        bool accurate_enough;
+
+        /* Match exactly what the user requested by software */
+        pa_sw_cvolume_divide(&new_soft_volume, &s->real_volume, &u->hardware_volume);
+
+        /* If the adjustment to do in software is only minimal we
+         * can skip it. That saves us CPU at the expense of a bit of
+         * accuracy */
+        accurate_enough =
+            (pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
+            (pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
+
+        pa_log_debug("Requested volume: %s",
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &s->real_volume, &s->channel_map, true));
+        pa_log_debug("Got hardware volume: %s",
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &u->hardware_volume, &s->channel_map, true));
+        pa_log_debug("Calculated software volume: %s (accurate-enough=%s)",
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &new_soft_volume, &s->channel_map, true),
+                     pa_yes_no(accurate_enough));
+
+        if (!accurate_enough)
+            s->soft_volume = new_soft_volume;
+
+    } else {
+        pa_log_debug("Wrote hardware volume: %s",
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &r, &s->channel_map, false));
+
+        /* We can't match exactly what the user requested, hence let's
+         * at least tell the user about it */
+
+        s->real_volume = r;
+    }
+}
+
+static void sink_write_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    pa_cvolume hw_vol = s->thread_info.current_hw_volume;
+
+    pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+    pa_assert(s->flags & PA_SINK_DEFERRED_VOLUME);
+
+    /* Shift up by the base volume */
+    pa_sw_cvolume_divide_scalar(&hw_vol, &hw_vol, s->base_volume);
+
+    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, true, true) < 0)
+        pa_log_error("Writing HW volume failed");
+    else {
+        pa_cvolume tmp_vol;
+        bool accurate_enough;
+
+        /* Shift down by the base volume, so that 0dB becomes maximum volume */
+        pa_sw_cvolume_multiply_scalar(&hw_vol, &hw_vol, s->base_volume);
+
+        pa_sw_cvolume_divide(&tmp_vol, &hw_vol, &s->thread_info.current_hw_volume);
+        accurate_enough =
+            (pa_cvolume_min(&tmp_vol) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
+            (pa_cvolume_max(&tmp_vol) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
+
+        if (!accurate_enough) {
+            char volume_buf[2][PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+            pa_log_debug("Written HW volume did not match with the request: %s (request) != %s",
+                         pa_cvolume_snprint_verbose(volume_buf[0],
+                                                    sizeof(volume_buf[0]),
+                                                    &s->thread_info.current_hw_volume,
+                                                    &s->channel_map,
+                                                    true),
+                         pa_cvolume_snprint_verbose(volume_buf[1], sizeof(volume_buf[1]), &hw_vol, &s->channel_map, true));
+        }
+    }
+}
+
+static int sink_get_mute_cb(pa_sink *s, bool *mute) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+
+    if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, mute) < 0)
+        return -1;
+
+    return 0;
+}
+
+static void sink_set_mute_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+
+    pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted);
+}
+
+static void mixer_volume_init(struct userdata *u) {
+    pa_assert(u);
+
+    if (!u->mixer_path->has_volume) {
+        pa_sink_set_write_volume_callback(u->sink, NULL);
+        pa_sink_set_get_volume_callback(u->sink, NULL);
+        pa_sink_set_set_volume_callback(u->sink, NULL);
+
+        pa_log_info("Driver does not support hardware volume control, falling back to software volume control.");
+    } else {
+        pa_sink_set_get_volume_callback(u->sink, sink_get_volume_cb);
+        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
+
+        if (u->mixer_path->has_dB && u->deferred_volume) {
+            pa_sink_set_write_volume_callback(u->sink, sink_write_volume_cb);
+            pa_log_info("Successfully enabled deferred volume.");
+        } else
+            pa_sink_set_write_volume_callback(u->sink, NULL);
+
+        if (u->mixer_path->has_dB) {
+            pa_sink_enable_decibel_volume(u->sink, true);
+            pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB);
+
+            u->sink->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB);
+            u->sink->n_volume_steps = PA_VOLUME_NORM+1;
+
+            pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->sink->base_volume));
+        } else {
+            pa_sink_enable_decibel_volume(u->sink, false);
+            pa_log_info("Hardware volume ranges from %li to %li.", u->mixer_path->min_volume, u->mixer_path->max_volume);
+
+            u->sink->base_volume = PA_VOLUME_NORM;
+            u->sink->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1;
+        }
+
+        pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
+    }
+
+    if (!u->mixer_path->has_mute) {
+        pa_sink_set_get_mute_callback(u->sink, NULL);
+        pa_sink_set_set_mute_callback(u->sink, NULL);
+        pa_log_info("Driver does not support hardware mute control, falling back to software mute control.");
+    } else {
+        pa_sink_set_get_mute_callback(u->sink, sink_get_mute_cb);
+        pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
+        pa_log_info("Using hardware mute control.");
+    }
+}
+
+static int sink_set_port_ucm_cb(pa_sink *s, pa_device_port *p) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(p);
+    pa_assert(u->ucm_context);
+
+    return pa_alsa_ucm_set_port(u->ucm_context, p, true);
+}
+
+static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
+    struct userdata *u = s->userdata;
+    pa_alsa_port_data *data;
+
+    pa_assert(u);
+    pa_assert(p);
+    pa_assert(u->mixer_handle);
+
+    data = PA_DEVICE_PORT_DATA(p);
+
+    pa_assert_se(u->mixer_path = data->path);
+    pa_alsa_path_select(u->mixer_path, data->setting, u->mixer_handle, s->muted);
+
+    mixer_volume_init(u);
+
+    if (s->set_mute)
+        s->set_mute(s);
+    if (s->flags & PA_SINK_DEFERRED_VOLUME) {
+        if (s->write_volume)
+            s->write_volume(s);
+    } else {
+        if (s->set_volume)
+            s->set_volume(s);
+    }
+
+    return 0;
+}
+
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    size_t before;
+    pa_assert(u);
+    pa_assert(u->use_tsched); /* only when timer scheduling is used
+                               * we can dynamically adjust the
+                               * latency */
+
+    if (!u->pcm_handle)
+        return;
+
+    before = u->hwbuf_unused;
+    update_sw_params(u);
+
+    /* Let's check whether we now use only a smaller part of the
+    buffer then before. If so, we need to make sure that subsequent
+    rewinds are relative to the new maximum fill level and not to the
+    current fill level. Thus, let's do a full rewind once, to clear
+    things up. */
+
+    if (u->hwbuf_unused > before) {
+        pa_log_debug("Requesting rewind due to latency change.");
+        pa_sink_request_rewind(s, (size_t) -1);
+    }
+}
+
+static pa_idxset* sink_get_formats(pa_sink *s) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+
+    return pa_idxset_copy(u->formats, (pa_copy_func_t) pa_format_info_copy);
+}
+
+static bool sink_set_formats(pa_sink *s, pa_idxset *formats) {
+    struct userdata *u = s->userdata;
+    pa_format_info *f, *g;
+    uint32_t idx, n;
+
+    pa_assert(u);
+
+    /* FIXME: also validate sample rates against what the device supports */
+    PA_IDXSET_FOREACH(f, formats, idx) {
+        if (is_iec958(u) && f->encoding == PA_ENCODING_EAC3_IEC61937)
+            /* EAC3 cannot be sent over over S/PDIF */
+            return false;
+    }
+
+    pa_idxset_free(u->formats, (pa_free_cb_t) pa_format_info_free);
+    u->formats = pa_idxset_new(NULL, NULL);
+
+    /* Note: the logic below won't apply if we're using software encoding.
+     * This is fine for now since we don't support that via the passthrough
+     * framework, but this must be changed if we do. */
+
+    /* Count how many sample rates we support */
+    for (idx = 0, n = 0; u->rates[idx]; idx++)
+        n++;
+
+    /* First insert non-PCM formats since we prefer those. */
+    PA_IDXSET_FOREACH(f, formats, idx) {
+        if (!pa_format_info_is_pcm(f)) {
+            g = pa_format_info_copy(f);
+            pa_format_info_set_prop_int_array(g, PA_PROP_FORMAT_RATE, (int *) u->rates, n);
+            pa_idxset_put(u->formats, g, NULL);
+        }
+    }
+
+    /* Now add any PCM formats */
+    PA_IDXSET_FOREACH(f, formats, idx) {
+        if (pa_format_info_is_pcm(f)) {
+            /* We don't set rates here since we'll just tack on a resampler for
+             * unsupported rates */
+            pa_idxset_put(u->formats, pa_format_info_copy(f), NULL);
+        }
+    }
+
+    return true;
+}
+
+static int sink_update_rate_cb(pa_sink *s, uint32_t rate) {
+    struct userdata *u = s->userdata;
+    int i;
+    bool supported = false;
+
+    pa_assert(u);
+
+    for (i = 0; u->rates[i]; i++) {
+        if (u->rates[i] == rate) {
+            supported = true;
+            break;
+        }
+    }
+
+    if (!supported) {
+        pa_log_info("Sink does not support sample rate of %d Hz", rate);
+        return -1;
+    }
+
+    if (!PA_SINK_IS_OPENED(s->state)) {
+        pa_log_info("Updating rate for device %s, new rate is %d",u->device_name, rate);
+        u->sink->sample_spec.rate = rate;
+        return 0;
+    }
+
+    return -1;
+}
+
+static int process_rewind(struct userdata *u) {
+    snd_pcm_sframes_t unused;
+    size_t rewind_nbytes, unused_nbytes, limit_nbytes;
+    pa_assert(u);
+
+    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+        pa_sink_process_rewind(u->sink, 0);
+        return 0;
+    }
+
+    /* Figure out how much we shall rewind and reset the counter */
+    rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+
+    pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+
+    if (PA_UNLIKELY((unused = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
+        if (try_recover(u, "snd_pcm_avail", (int) unused) < 0) {
+            pa_log_warn("Trying to recover from underrun failed during rewind");
+            return -1;
+        }
+    }
+
+    unused_nbytes = (size_t) unused * u->frame_size;
+
+    /* make sure rewind doesn't go too far, can cause issues with DMAs */
+    unused_nbytes += u->rewind_safeguard;
+
+    if (u->hwbuf_size > unused_nbytes)
+        limit_nbytes = u->hwbuf_size - unused_nbytes;
+    else
+        limit_nbytes = 0;
+
+    if (rewind_nbytes > limit_nbytes)
+        rewind_nbytes = limit_nbytes;
+
+    if (rewind_nbytes > 0) {
+        snd_pcm_sframes_t in_frames, out_frames;
+
+        pa_log_debug("Limited to %lu bytes.", (unsigned long) rewind_nbytes);
+
+        in_frames = (snd_pcm_sframes_t) (rewind_nbytes / u->frame_size);
+        pa_log_debug("before: %lu", (unsigned long) in_frames);
+        if ((out_frames = snd_pcm_rewind(u->pcm_handle, (snd_pcm_uframes_t) in_frames)) < 0) {
+            pa_log("snd_pcm_rewind() failed: %s", pa_alsa_strerror((int) out_frames));
+            if (try_recover(u, "process_rewind", out_frames) < 0)
+                return -1;
+            out_frames = 0;
+        }
+
+        pa_log_debug("after: %lu", (unsigned long) out_frames);
+
+        rewind_nbytes = (size_t) out_frames * u->frame_size;
+
+        if (rewind_nbytes <= 0)
+            pa_log_info("Tried rewind, but was apparently not possible.");
+        else {
+            u->write_count -= rewind_nbytes;
+            pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+            pa_sink_process_rewind(u->sink, rewind_nbytes);
+
+            u->after_rewind = true;
+            return 0;
+        }
+    } else
+        pa_log_debug("Mhmm, actually there is nothing to rewind.");
+
+    pa_sink_process_rewind(u->sink, 0);
+    return 0;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    unsigned short revents = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        int ret;
+        pa_usec_t rtpoll_sleep = 0, real_sleep;
+
+#ifdef DEBUG_TIMING
+        pa_log_debug("Loop");
+#endif
+
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) {
+            if (process_rewind(u) < 0)
+                goto fail;
+        }
+
+        /* Render some data and write it to the dsp */
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+            int work_done;
+            pa_usec_t sleep_usec = 0;
+            bool on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll);
+
+            if (u->use_mmap)
+                work_done = mmap_write(u, &sleep_usec, revents & POLLOUT, on_timeout);
+            else
+                work_done = unix_write(u, &sleep_usec, revents & POLLOUT, on_timeout);
+
+            if (work_done < 0)
+                goto fail;
+
+/*             pa_log_debug("work_done = %i", work_done); */
+
+            if (work_done) {
+
+                if (u->first) {
+                    pa_log_info("Starting playback.");
+                    snd_pcm_start(u->pcm_handle);
+
+                    pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
+
+                    u->first = false;
+                }
+
+                update_smoother(u);
+            }
+
+            if (u->use_tsched) {
+                pa_usec_t cusec;
+
+                if (u->since_start <= u->hwbuf_size) {
+
+                    /* USB devices on ALSA seem to hit a buffer
+                     * underrun during the first iterations much
+                     * quicker then we calculate here, probably due to
+                     * the transport latency. To accommodate for that
+                     * we artificially decrease the sleep time until
+                     * we have filled the buffer at least once
+                     * completely.*/
+
+                    if (pa_log_ratelimit(PA_LOG_DEBUG))
+                        pa_log_debug("Cutting sleep time for the initial iterations by half.");
+                    sleep_usec /= 2;
+                }
+
+                /* OK, the playback buffer is now full, let's
+                 * calculate when to wake up next */
+#ifdef DEBUG_TIMING
+                pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC);
+#endif
+
+                /* Convert from the sound card time domain to the
+                 * system time domain */
+                cusec = pa_smoother_translate(u->smoother, pa_rtclock_now(), sleep_usec);
+
+#ifdef DEBUG_TIMING
+                pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC);
+#endif
+
+                /* We don't trust the conversion, so we wake up whatever comes first */
+                rtpoll_sleep = PA_MIN(sleep_usec, cusec);
+            }
+
+            u->after_rewind = false;
+
+        }
+
+        if (u->sink->flags & PA_SINK_DEFERRED_VOLUME) {
+            pa_usec_t volume_sleep;
+            pa_sink_volume_change_apply(u->sink, &volume_sleep);
+            if (volume_sleep > 0) {
+                if (rtpoll_sleep > 0)
+                    rtpoll_sleep = PA_MIN(volume_sleep, rtpoll_sleep);
+                else
+                    rtpoll_sleep = volume_sleep;
+            }
+        }
+
+        if (rtpoll_sleep > 0) {
+            pa_rtpoll_set_timer_relative(u->rtpoll, rtpoll_sleep);
+            real_sleep = pa_rtclock_now();
+        }
+        else
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (rtpoll_sleep > 0) {
+            real_sleep = pa_rtclock_now() - real_sleep;
+#ifdef DEBUG_TIMING
+            pa_log_debug("Expected sleep: %0.2fms, real sleep: %0.2fms (diff %0.2f ms)",
+                (double) rtpoll_sleep / PA_USEC_PER_MSEC, (double) real_sleep / PA_USEC_PER_MSEC,
+                (double) ((int64_t) real_sleep - (int64_t) rtpoll_sleep) / PA_USEC_PER_MSEC);
+#endif
+            if (u->use_tsched && real_sleep > rtpoll_sleep + u->tsched_watermark_usec)
+                pa_log_info("Scheduling delay of %0.2f ms > %0.2f ms, you might want to investigate this to improve latency...",
+                    (double) (real_sleep - rtpoll_sleep) / PA_USEC_PER_MSEC,
+                    (double) (u->tsched_watermark_usec) / PA_USEC_PER_MSEC);
+        }
+
+        if (u->sink->flags & PA_SINK_DEFERRED_VOLUME)
+            pa_sink_volume_change_apply(u->sink, NULL);
+
+        if (ret == 0)
+            goto finish;
+
+        /* Tell ALSA about this and process its response */
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+            struct pollfd *pollfd;
+            int err;
+            unsigned n;
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, &n);
+
+            if ((err = snd_pcm_poll_descriptors_revents(u->pcm_handle, pollfd, n, &revents)) < 0) {
+                pa_log("snd_pcm_poll_descriptors_revents() failed: %s", pa_alsa_strerror(err));
+                goto fail;
+            }
+
+            if (revents & ~POLLOUT) {
+                if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
+                    goto fail;
+
+                u->first = true;
+                u->since_start = 0;
+                revents = 0;
+            } else if (revents && u->use_tsched && pa_log_ratelimit(PA_LOG_DEBUG))
+                pa_log_debug("Wakeup from ALSA!");
+
+        } else
+            revents = 0;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name, pa_alsa_mapping *mapping) {
+    const char *n;
+    char *t;
+
+    pa_assert(data);
+    pa_assert(ma);
+    pa_assert(device_name);
+
+    if ((n = pa_modargs_get_value(ma, "sink_name", NULL))) {
+        pa_sink_new_data_set_name(data, n);
+        data->namereg_fail = true;
+        return;
+    }
+
+    if ((n = pa_modargs_get_value(ma, "name", NULL)))
+        data->namereg_fail = true;
+    else {
+        n = device_id ? device_id : device_name;
+        data->namereg_fail = false;
+    }
+
+    if (mapping)
+        t = pa_sprintf_malloc("alsa_output.%s.%s", n, mapping->name);
+    else
+        t = pa_sprintf_malloc("alsa_output.%s", n);
+
+    pa_sink_new_data_set_name(data, t);
+    pa_xfree(t);
+}
+
+static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char *element, bool ignore_dB) {
+    if (!mapping && !element)
+        return;
+
+    if (!(u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device))) {
+        pa_log_info("Failed to find a working mixer device.");
+        return;
+    }
+
+    if (element) {
+
+        if (!(u->mixer_path = pa_alsa_path_synthesize(element, PA_ALSA_DIRECTION_OUTPUT)))
+            goto fail;
+
+        if (pa_alsa_path_probe(u->mixer_path, u->mixer_handle, ignore_dB) < 0)
+            goto fail;
+
+        pa_log_debug("Probed mixer path %s:", u->mixer_path->name);
+        pa_alsa_path_dump(u->mixer_path);
+    } else if (!(u->mixer_path_set = mapping->output_path_set))
+        goto fail;
+
+    return;
+
+fail:
+
+    if (u->mixer_path) {
+        pa_alsa_path_free(u->mixer_path);
+        u->mixer_path = NULL;
+    }
+
+    if (u->mixer_handle) {
+        snd_mixer_close(u->mixer_handle);
+        u->mixer_handle = NULL;
+    }
+}
+
+static int setup_mixer(struct userdata *u, bool ignore_dB) {
+    bool need_mixer_callback = false;
+
+    pa_assert(u);
+
+    if (!u->mixer_handle)
+        return 0;
+
+    if (u->sink->active_port) {
+        pa_alsa_port_data *data;
+
+        /* We have a list of supported paths, so let's activate the
+         * one that has been chosen as active */
+
+        data = PA_DEVICE_PORT_DATA(u->sink->active_port);
+        u->mixer_path = data->path;
+
+        pa_alsa_path_select(data->path, data->setting, u->mixer_handle, u->sink->muted);
+
+    } else {
+
+        if (!u->mixer_path && u->mixer_path_set)
+            u->mixer_path = pa_hashmap_first(u->mixer_path_set->paths);
+
+        if (u->mixer_path) {
+            /* Hmm, we have only a single path, then let's activate it */
+
+            pa_alsa_path_select(u->mixer_path, u->mixer_path->settings, u->mixer_handle, u->sink->muted);
+
+        } else
+            return 0;
+    }
+
+    mixer_volume_init(u);
+
+    /* Will we need to register callbacks? */
+    if (u->mixer_path_set && u->mixer_path_set->paths) {
+        pa_alsa_path *p;
+        void *state;
+
+        PA_HASHMAP_FOREACH(p, u->mixer_path_set->paths, state) {
+            if (p->has_volume || p->has_mute)
+                need_mixer_callback = true;
+        }
+    }
+    else if (u->mixer_path)
+        need_mixer_callback = u->mixer_path->has_volume || u->mixer_path->has_mute;
+
+    if (need_mixer_callback) {
+        int (*mixer_callback)(snd_mixer_elem_t *, unsigned int);
+        if (u->sink->flags & PA_SINK_DEFERRED_VOLUME) {
+            u->mixer_pd = pa_alsa_mixer_pdata_new();
+            mixer_callback = io_mixer_callback;
+
+            if (pa_alsa_set_mixer_rtpoll(u->mixer_pd, u->mixer_handle, u->rtpoll) < 0) {
+                pa_log("Failed to initialize file descriptor monitoring");
+                return -1;
+            }
+        } else {
+            u->mixer_fdl = pa_alsa_fdlist_new();
+            mixer_callback = ctl_mixer_callback;
+
+            if (pa_alsa_fdlist_set_handle(u->mixer_fdl, u->mixer_handle, NULL, u->core->mainloop) < 0) {
+                pa_log("Failed to initialize file descriptor monitoring");
+                return -1;
+            }
+        }
+
+        if (u->mixer_path_set)
+            pa_alsa_path_set_set_callback(u->mixer_path_set, u->mixer_handle, mixer_callback, u);
+        else
+            pa_alsa_path_set_callback(u->mixer_path, u->mixer_handle, mixer_callback, u);
+    }
+
+    return 0;
+}
+
+pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) {
+
+    struct userdata *u = NULL;
+    const char *dev_id = NULL, *key, *mod_name;
+    pa_sample_spec ss;
+    char *thread_name = NULL;
+    uint32_t alternate_sample_rate;
+    pa_channel_map map;
+    uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark, rewind_safeguard;
+    snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames;
+    size_t frame_size;
+    bool use_mmap = true, b, use_tsched = true, d, ignore_dB = false, namereg_fail = false, deferred_volume = false, set_formats = false, fixed_latency_range = false;
+    pa_sink_new_data data;
+    bool volume_is_set;
+    bool mute_is_set;
+    pa_alsa_profile_set *profile_set = NULL;
+    void *state = NULL;
+
+    pa_assert(m);
+    pa_assert(ma);
+
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
+
+    /* Pick sample spec overrides from the mapping, if any */
+    if (mapping) {
+        if (mapping->sample_spec.format != PA_SAMPLE_INVALID)
+            ss.format = mapping->sample_spec.format;
+        if (mapping->sample_spec.rate != 0)
+            ss.rate = mapping->sample_spec.rate;
+        if (mapping->sample_spec.channels != 0) {
+            ss.channels = mapping->sample_spec.channels;
+            if (pa_channel_map_valid(&mapping->channel_map))
+                pa_assert(pa_channel_map_compatible(&mapping->channel_map, &ss));
+        }
+    }
+
+    /* Override with modargs if provided */
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
+        pa_log("Failed to parse sample specification and channel map");
+        goto fail;
+    }
+
+    alternate_sample_rate = m->core->alternate_sample_rate;
+    if (pa_modargs_get_alternate_sample_rate(ma, &alternate_sample_rate) < 0) {
+        pa_log("Failed to parse alternate sample rate");
+        goto fail;
+    }
+
+    frame_size = pa_frame_size(&ss);
+
+    nfrags = m->core->default_n_fragments;
+    frag_size = (uint32_t) pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);
+    if (frag_size <= 0)
+        frag_size = (uint32_t) frame_size;
+    tsched_size = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss);
+    tsched_watermark = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);
+
+    if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||
+        pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 ||
+        pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 ||
+        pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) {
+        pa_log("Failed to parse buffer metrics");
+        goto fail;
+    }
+
+    buffer_size = nfrags * frag_size;
+
+    period_frames = frag_size/frame_size;
+    buffer_frames = buffer_size/frame_size;
+    tsched_frames = tsched_size/frame_size;
+
+    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+        pa_log("Failed to parse mmap argument.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+        pa_log("Failed to parse tsched argument.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
+        pa_log("Failed to parse ignore_dB argument.");
+        goto fail;
+    }
+
+    rewind_safeguard = PA_MAX(DEFAULT_REWIND_SAFEGUARD_BYTES, pa_usec_to_bytes(DEFAULT_REWIND_SAFEGUARD_USEC, &ss));
+    if (pa_modargs_get_value_u32(ma, "rewind_safeguard", &rewind_safeguard) < 0) {
+        pa_log("Failed to parse rewind_safeguard argument");
+        goto fail;
+    }
+
+    deferred_volume = m->core->deferred_volume;
+    if (pa_modargs_get_value_boolean(ma, "deferred_volume", &deferred_volume) < 0) {
+        pa_log("Failed to parse deferred_volume argument.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "fixed_latency_range", &fixed_latency_range) < 0) {
+        pa_log("Failed to parse fixed_latency_range argument.");
+        goto fail;
+    }
+
+    use_tsched = pa_alsa_may_tsched(use_tsched);
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->use_mmap = use_mmap;
+    u->use_tsched = use_tsched;
+    u->deferred_volume = deferred_volume;
+    u->fixed_latency_range = fixed_latency_range;
+    u->first = true;
+    u->rewind_safeguard = rewind_safeguard;
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->smoother = pa_smoother_new(
+            SMOOTHER_ADJUST_USEC,
+            SMOOTHER_WINDOW_USEC,
+            true,
+            true,
+            5,
+            pa_rtclock_now(),
+            true);
+    u->smoother_interval = SMOOTHER_MIN_INTERVAL;
+
+    /* use ucm */
+    if (mapping && mapping->ucm_context.ucm)
+        u->ucm_context = &mapping->ucm_context;
+
+    dev_id = pa_modargs_get_value(
+            ma, "device_id",
+            pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
+
+    u->paths_dir = pa_xstrdup(pa_modargs_get_value(ma, "paths_dir", NULL));
+
+    if (reserve_init(u, dev_id) < 0)
+        goto fail;
+
+    if (reserve_monitor_init(u, dev_id) < 0)
+        goto fail;
+
+    b = use_mmap;
+    d = use_tsched;
+
+    /* Force ALSA to reread its configuration if module-alsa-card didn't
+     * do it for us. This matters if our device was hot-plugged after ALSA
+     * has already read its configuration - see
+     * https://bugs.freedesktop.org/show_bug.cgi?id=54029
+     */
+
+    if (!card)
+        snd_config_update_free_global();
+
+    if (mapping) {
+
+        if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+            pa_log("device_id= not set");
+            goto fail;
+        }
+
+        if ((mod_name = pa_proplist_gets(mapping->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+            if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
+                pa_log("Failed to enable ucm modifier %s", mod_name);
+            else
+                pa_log_debug("Enabled ucm modifier %s", mod_name);
+        }
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_id_mapping(
+                      dev_id,
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_PLAYBACK,
+                      &period_frames, &buffer_frames, tsched_frames,
+                      &b, &d, mapping)))
+            goto fail;
+
+    } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+
+        if (!(profile_set = pa_alsa_profile_set_new(NULL, &map)))
+            goto fail;
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
+                      dev_id,
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_PLAYBACK,
+                      &period_frames, &buffer_frames, tsched_frames,
+                      &b, &d, profile_set, &mapping)))
+            goto fail;
+
+    } else {
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_string(
+                      pa_modargs_get_value(ma, "device", DEFAULT_DEVICE),
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_PLAYBACK,
+                      &period_frames, &buffer_frames, tsched_frames,
+                      &b, &d, false)))
+            goto fail;
+    }
+
+    pa_assert(u->device_name);
+    pa_log_info("Successfully opened device %s.", u->device_name);
+
+    if (pa_alsa_pcm_is_modem(u->pcm_handle)) {
+        pa_log_notice("Device %s is modem, refusing further initialization.", u->device_name);
+        goto fail;
+    }
+
+    if (mapping)
+        pa_log_info("Selected mapping '%s' (%s).", mapping->description, mapping->name);
+
+    if (use_mmap && !b) {
+        pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
+        u->use_mmap = use_mmap = false;
+    }
+
+    if (use_tsched && (!b || !d)) {
+        pa_log_info("Cannot enable timer-based scheduling, falling back to sound IRQ scheduling.");
+        u->use_tsched = use_tsched = false;
+    }
+
+    if (u->use_mmap)
+        pa_log_info("Successfully enabled mmap() mode.");
+
+    if (u->use_tsched) {
+        pa_log_info("Successfully enabled timer-based scheduling mode.");
+
+        if (u->fixed_latency_range)
+            pa_log_info("Disabling latency range changes on underrun");
+    }
+
+    /* All passthrough formats supported by PulseAudio require
+     * IEC61937 framing with two fake channels. So, passthrough
+     * clients will always send two channels. Multichannel sinks
+     * cannot accept that, because nobody implemented sink channel count
+     * switching so far. So just don't show known non-working settings
+     * to the user. */
+    if ((is_iec958(u) || is_hdmi(u)) && ss.channels == 2)
+        set_formats = true;
+
+    u->rates = pa_alsa_get_supported_rates(u->pcm_handle, ss.rate);
+    if (!u->rates) {
+        pa_log_error("Failed to find any supported sample rates.");
+        goto fail;
+    }
+
+    /* ALSA might tweak the sample spec, so recalculate the frame size */
+    frame_size = pa_frame_size(&ss);
+
+    if (!u->ucm_context)
+        find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
+
+    pa_sink_new_data_init(&data);
+    data.driver = driver;
+    data.module = m;
+    data.card = card;
+    set_sink_name(&data, ma, dev_id, u->device_name, mapping);
+
+    /* We need to give pa_modargs_get_value_boolean() a pointer to a local
+     * variable instead of using &data.namereg_fail directly, because
+     * data.namereg_fail is a bitfield and taking the address of a bitfield
+     * variable is impossible. */
+    namereg_fail = data.namereg_fail;
+    if (pa_modargs_get_value_boolean(ma, "namereg_fail", &namereg_fail) < 0) {
+        pa_log("Failed to parse namereg_fail argument.");
+        pa_sink_new_data_done(&data);
+        goto fail;
+    }
+    data.namereg_fail = namereg_fail;
+
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+    pa_sink_new_data_set_alternate_sample_rate(&data, alternate_sample_rate);
+
+    pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (buffer_frames * frame_size));
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+
+    if (mapping) {
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name);
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description);
+
+        while ((key = pa_proplist_iterate(mapping->proplist, &state)))
+            pa_proplist_sets(data.proplist, key, pa_proplist_gets(mapping->proplist, key));
+    }
+
+    pa_alsa_init_description(data.proplist, card);
+
+    if (u->control_device)
+        pa_alsa_init_proplist_ctl(data.proplist, u->control_device);
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&data);
+        goto fail;
+    }
+
+    if (u->ucm_context)
+        pa_alsa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, true, card);
+    else if (u->mixer_path_set)
+        pa_alsa_add_ports(&data, u->mixer_path_set, card);
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE | PA_SINK_LATENCY | (u->use_tsched ? PA_SINK_DYNAMIC_LATENCY : 0) |
+                          (set_formats ? PA_SINK_SET_FORMATS : 0));
+    volume_is_set = data.volume_is_set;
+    mute_is_set = data.muted_is_set;
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink object");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "deferred_volume_safety_margin",
+                                 &u->sink->thread_info.volume_change_safety_margin) < 0) {
+        pa_log("Failed to parse deferred_volume_safety_margin parameter");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_s32(ma, "deferred_volume_extra_delay",
+                                 &u->sink->thread_info.volume_change_extra_delay) < 0) {
+        pa_log("Failed to parse deferred_volume_extra_delay parameter");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    if (u->use_tsched)
+        u->sink->update_requested_latency = sink_update_requested_latency_cb;
+    u->sink->set_state = sink_set_state_cb;
+    if (u->ucm_context)
+        u->sink->set_port = sink_set_port_ucm_cb;
+    else
+        u->sink->set_port = sink_set_port_cb;
+    if (u->sink->alternate_sample_rate)
+        u->sink->update_rate = sink_update_rate_cb;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    u->frame_size = frame_size;
+    u->frames_per_block = pa_mempool_block_size_max(m->core->mempool) / frame_size;
+    u->fragment_size = frag_size = (size_t) (period_frames * frame_size);
+    u->hwbuf_size = buffer_size = (size_t) (buffer_frames * frame_size);
+    pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels);
+
+    pa_log_info("Using %0.1f fragments of size %lu bytes (%0.2fms), buffer size is %lu bytes (%0.2fms)",
+                (double) u->hwbuf_size / (double) u->fragment_size,
+                (long unsigned) u->fragment_size,
+                (double) pa_bytes_to_usec(u->fragment_size, &ss) / PA_USEC_PER_MSEC,
+                (long unsigned) u->hwbuf_size,
+                (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
+
+    pa_sink_set_max_request(u->sink, u->hwbuf_size);
+    if (pa_alsa_pcm_is_hw(u->pcm_handle))
+        pa_sink_set_max_rewind(u->sink, u->hwbuf_size);
+    else {
+        pa_log_info("Disabling rewind for device %s", u->device_name);
+        pa_sink_set_max_rewind(u->sink, 0);
+    }
+
+    if (u->use_tsched) {
+        u->tsched_watermark_ref = tsched_watermark;
+        reset_watermark(u, u->tsched_watermark_ref, &ss, false);
+    } else
+        pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->hwbuf_size, &ss));
+
+    reserve_update(u);
+
+    if (update_sw_params(u) < 0)
+        goto fail;
+
+    if (u->ucm_context) {
+        if (u->sink->active_port && pa_alsa_ucm_set_port(u->ucm_context, u->sink->active_port, true) < 0)
+            goto fail;
+    } else if (setup_mixer(u, ignore_dB) < 0)
+        goto fail;
+
+    pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
+
+    thread_name = pa_sprintf_malloc("alsa-sink-%s", pa_strnull(pa_proplist_gets(u->sink->proplist, "alsa.id")));
+    if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+    pa_xfree(thread_name);
+    thread_name = NULL;
+
+    /* Get initial mixer settings */
+    if (volume_is_set) {
+        if (u->sink->set_volume)
+            u->sink->set_volume(u->sink);
+    } else {
+        if (u->sink->get_volume)
+            u->sink->get_volume(u->sink);
+    }
+
+    if (mute_is_set) {
+        if (u->sink->set_mute)
+            u->sink->set_mute(u->sink);
+    } else {
+        if (u->sink->get_mute) {
+            bool mute;
+
+            if (u->sink->get_mute(u->sink, &mute) >= 0)
+                pa_sink_set_mute(u->sink, mute, false);
+        }
+    }
+
+    if ((volume_is_set || mute_is_set) && u->sink->write_volume)
+        u->sink->write_volume(u->sink);
+
+    if (set_formats) {
+        /* For S/PDIF and HDMI, allow getting/setting custom formats */
+        pa_format_info *format;
+
+        /* To start with, we only support PCM formats. Other formats may be added
+         * with pa_sink_set_formats().*/
+        format = pa_format_info_new();
+        format->encoding = PA_ENCODING_PCM;
+        u->formats = pa_idxset_new(NULL, NULL);
+        pa_idxset_put(u->formats, format, NULL);
+
+        u->sink->get_formats = sink_get_formats;
+        u->sink->set_formats = sink_set_formats;
+    }
+
+    pa_sink_put(u->sink);
+
+    if (profile_set)
+        pa_alsa_profile_set_free(profile_set);
+
+    return u->sink;
+
+fail:
+    pa_xfree(thread_name);
+
+    if (u)
+        userdata_free(u);
+
+    if (profile_set)
+        pa_alsa_profile_set_free(profile_set);
+
+    return NULL;
+}
+
+static void userdata_free(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->mixer_pd)
+        pa_alsa_mixer_pdata_free(u->mixer_pd);
+
+    if (u->alsa_rtpoll_item)
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->pcm_handle) {
+        snd_pcm_drop(u->pcm_handle);
+        snd_pcm_close(u->pcm_handle);
+    }
+
+    if (u->mixer_fdl)
+        pa_alsa_fdlist_free(u->mixer_fdl);
+
+    if (u->mixer_path && !u->mixer_path_set)
+        pa_alsa_path_free(u->mixer_path);
+
+    if (u->mixer_handle)
+        snd_mixer_close(u->mixer_handle);
+
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+
+    if (u->formats)
+        pa_idxset_free(u->formats, (pa_free_cb_t) pa_format_info_free);
+
+    if (u->rates)
+        pa_xfree(u->rates);
+
+    reserve_done(u);
+    monitor_done(u);
+
+    pa_xfree(u->device_name);
+    pa_xfree(u->control_device);
+    pa_xfree(u->paths_dir);
+    pa_xfree(u);
+}
+
+void pa_alsa_sink_free(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    userdata_free(u);
+}
diff --git a/src/modules/alsa/alsa-sink.h b/src/modules/alsa/alsa-sink.h
new file mode 100644 (file)
index 0000000..78a2cb2
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef fooalsasinkhfoo
+#define fooalsasinkhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/sink.h>
+
+#include "alsa-util.h"
+
+pa_sink* pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping);
+
+void pa_alsa_sink_free(pa_sink *s);
+
+#endif
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
new file mode 100644 (file)
index 0000000..6bec188
--- /dev/null
@@ -0,0 +1,2195 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <stdio.h>
+
+#include <asoundlib.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/module.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+
+#include <modules/reserve-wrap.h>
+
+#include "alsa-util.h"
+#include "alsa-source.h"
+
+/* #define DEBUG_TIMING */
+
+#define DEFAULT_DEVICE "default"
+
+#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)             /* 2s */
+#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC)        /* 20ms */
+
+#define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC)       /* 10ms  */
+#define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC)        /* 5ms */
+#define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC)    /* 20s */
+#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (0*PA_USEC_PER_MSEC)   /* 0ms */
+#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms */
+#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC)           /* 10ms */
+
+#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC)                /* 10ms */
+#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC)                /* 4ms */
+
+#define SMOOTHER_WINDOW_USEC  (10*PA_USEC_PER_SEC)                 /* 10s */
+#define SMOOTHER_ADJUST_USEC  (1*PA_USEC_PER_SEC)                  /* 1s */
+
+#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC)                 /* 2ms */
+#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC)               /* 200ms */
+
+#define VOLUME_ACCURACY (PA_VOLUME_NORM/100)
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_source *source;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    snd_pcm_t *pcm_handle;
+
+    char *paths_dir;
+    pa_alsa_fdlist *mixer_fdl;
+    pa_alsa_mixer_pdata *mixer_pd;
+    snd_mixer_t *mixer_handle;
+    pa_alsa_path_set *mixer_path_set;
+    pa_alsa_path *mixer_path;
+
+    pa_cvolume hardware_volume;
+
+    unsigned int *rates;
+
+    size_t
+        frame_size,
+        fragment_size,
+        hwbuf_size,
+        tsched_watermark,
+        tsched_watermark_ref,
+        hwbuf_unused,
+        min_sleep,
+        min_wakeup,
+        watermark_inc_step,
+        watermark_dec_step,
+        watermark_inc_threshold,
+        watermark_dec_threshold;
+
+    snd_pcm_uframes_t frames_per_block;
+
+    pa_usec_t watermark_dec_not_before;
+    pa_usec_t min_latency_ref;
+    pa_usec_t tsched_watermark_usec;
+
+    char *device_name;  /* name of the PCM device */
+    char *control_device; /* name of the control device */
+
+    bool use_mmap:1, use_tsched:1, deferred_volume:1, fixed_latency_range:1;
+
+    bool first;
+
+    pa_rtpoll_item *alsa_rtpoll_item;
+
+    pa_smoother *smoother;
+    uint64_t read_count;
+    pa_usec_t smoother_interval;
+    pa_usec_t last_smoother_update;
+
+    pa_reserve_wrapper *reserve;
+    pa_hook_slot *reserve_slot;
+    pa_reserve_monitor_wrapper *monitor;
+    pa_hook_slot *monitor_slot;
+
+    /* ucm context */
+    pa_alsa_ucm_mapping_context *ucm_context;
+};
+
+static void userdata_free(struct userdata *u);
+
+static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct userdata *u) {
+    pa_assert(r);
+    pa_assert(u);
+
+    pa_log_debug("Suspending source %s, because another application requested us to release the device.", u->source->name);
+
+    if (pa_source_suspend(u->source, true, PA_SUSPEND_APPLICATION) < 0)
+        return PA_HOOK_CANCEL;
+
+    return PA_HOOK_OK;
+}
+
+static void reserve_done(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->reserve_slot) {
+        pa_hook_slot_free(u->reserve_slot);
+        u->reserve_slot = NULL;
+    }
+
+    if (u->reserve) {
+        pa_reserve_wrapper_unref(u->reserve);
+        u->reserve = NULL;
+    }
+}
+
+static void reserve_update(struct userdata *u) {
+    const char *description;
+    pa_assert(u);
+
+    if (!u->source || !u->reserve)
+        return;
+
+    if ((description = pa_proplist_gets(u->source->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+        pa_reserve_wrapper_set_application_device_name(u->reserve, description);
+}
+
+static int reserve_init(struct userdata *u, const char *dname) {
+    char *rname;
+
+    pa_assert(u);
+    pa_assert(dname);
+
+    if (u->reserve)
+        return 0;
+
+    if (pa_in_system_mode())
+        return 0;
+
+    if (!(rname = pa_alsa_get_reserve_name(dname)))
+        return 0;
+
+    /* We are resuming, try to lock the device */
+    u->reserve = pa_reserve_wrapper_get(u->core, rname);
+    pa_xfree(rname);
+
+    if (!(u->reserve))
+        return -1;
+
+    reserve_update(u);
+
+    pa_assert(!u->reserve_slot);
+    u->reserve_slot = pa_hook_connect(pa_reserve_wrapper_hook(u->reserve), PA_HOOK_NORMAL, (pa_hook_cb_t) reserve_cb, u);
+
+    return 0;
+}
+
+static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) {
+    pa_assert(w);
+    pa_assert(u);
+
+    if (PA_PTR_TO_UINT(busy) && !u->reserve) {
+        pa_log_debug("Suspending source %s, because another application is blocking the access to the device.", u->source->name);
+        pa_source_suspend(u->source, true, PA_SUSPEND_APPLICATION);
+    } else {
+        pa_log_debug("Resuming source %s, because other applications aren't blocking access to the device any more.", u->source->name);
+        pa_source_suspend(u->source, false, PA_SUSPEND_APPLICATION);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static void monitor_done(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->monitor_slot) {
+        pa_hook_slot_free(u->monitor_slot);
+        u->monitor_slot = NULL;
+    }
+
+    if (u->monitor) {
+        pa_reserve_monitor_wrapper_unref(u->monitor);
+        u->monitor = NULL;
+    }
+}
+
+static int reserve_monitor_init(struct userdata *u, const char *dname) {
+    char *rname;
+
+    pa_assert(u);
+    pa_assert(dname);
+
+    if (pa_in_system_mode())
+        return 0;
+
+    if (!(rname = pa_alsa_get_reserve_name(dname)))
+        return 0;
+
+    /* We are resuming, try to lock the device */
+    u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname);
+    pa_xfree(rname);
+
+    if (!(u->monitor))
+        return -1;
+
+    pa_assert(!u->monitor_slot);
+    u->monitor_slot = pa_hook_connect(pa_reserve_monitor_wrapper_hook(u->monitor), PA_HOOK_NORMAL, (pa_hook_cb_t) monitor_cb, u);
+
+    return 0;
+}
+
+static void fix_min_sleep_wakeup(struct userdata *u) {
+    size_t max_use, max_use_2;
+
+    pa_assert(u);
+    pa_assert(u->use_tsched);
+
+    max_use = u->hwbuf_size - u->hwbuf_unused;
+    max_use_2 = pa_frame_align(max_use/2, &u->source->sample_spec);
+
+    u->min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->source->sample_spec);
+    u->min_sleep = PA_CLAMP(u->min_sleep, u->frame_size, max_use_2);
+
+    u->min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->source->sample_spec);
+    u->min_wakeup = PA_CLAMP(u->min_wakeup, u->frame_size, max_use_2);
+}
+
+static void fix_tsched_watermark(struct userdata *u) {
+    size_t max_use;
+    pa_assert(u);
+    pa_assert(u->use_tsched);
+
+    max_use = u->hwbuf_size - u->hwbuf_unused;
+
+    if (u->tsched_watermark > max_use - u->min_sleep)
+        u->tsched_watermark = max_use - u->min_sleep;
+
+    if (u->tsched_watermark < u->min_wakeup)
+        u->tsched_watermark = u->min_wakeup;
+
+   u->tsched_watermark_usec = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec);
+}
+
+static void increase_watermark(struct userdata *u) {
+    size_t old_watermark;
+    pa_usec_t old_min_latency, new_min_latency;
+
+    pa_assert(u);
+    pa_assert(u->use_tsched);
+
+    /* First, just try to increase the watermark */
+    old_watermark = u->tsched_watermark;
+    u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_inc_step);
+    fix_tsched_watermark(u);
+
+    if (old_watermark != u->tsched_watermark) {
+        pa_log_info("Increasing wakeup watermark to %0.2f ms",
+                    (double) u->tsched_watermark_usec / PA_USEC_PER_MSEC);
+        return;
+    }
+
+    /* Hmm, we cannot increase the watermark any further, hence let's
+     raise the latency unless doing so was disabled in
+     configuration */
+    if (u->fixed_latency_range)
+        return;
+
+    old_min_latency = u->source->thread_info.min_latency;
+    new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC);
+    new_min_latency = PA_MIN(new_min_latency, u->source->thread_info.max_latency);
+
+    if (old_min_latency != new_min_latency) {
+        pa_log_info("Increasing minimal latency to %0.2f ms",
+                    (double) new_min_latency / PA_USEC_PER_MSEC);
+
+        pa_source_set_latency_range_within_thread(u->source, new_min_latency, u->source->thread_info.max_latency);
+    }
+
+    /* When we reach this we're officially fucked! */
+}
+
+static void decrease_watermark(struct userdata *u) {
+    size_t old_watermark;
+    pa_usec_t now;
+
+    pa_assert(u);
+    pa_assert(u->use_tsched);
+
+    now = pa_rtclock_now();
+
+    if (u->watermark_dec_not_before <= 0)
+        goto restart;
+
+    if (u->watermark_dec_not_before > now)
+        return;
+
+    old_watermark = u->tsched_watermark;
+
+    if (u->tsched_watermark < u->watermark_dec_step)
+        u->tsched_watermark = u->tsched_watermark / 2;
+    else
+        u->tsched_watermark = PA_MAX(u->tsched_watermark / 2, u->tsched_watermark - u->watermark_dec_step);
+
+    fix_tsched_watermark(u);
+
+    if (old_watermark != u->tsched_watermark)
+        pa_log_info("Decreasing wakeup watermark to %0.2f ms",
+                    (double) u->tsched_watermark_usec / PA_USEC_PER_MSEC);
+
+    /* We don't change the latency range*/
+
+restart:
+    u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC;
+}
+
+static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
+    pa_usec_t wm, usec;
+
+    pa_assert(sleep_usec);
+    pa_assert(process_usec);
+
+    pa_assert(u);
+    pa_assert(u->use_tsched);
+
+    usec = pa_source_get_requested_latency_within_thread(u->source);
+
+    if (usec == (pa_usec_t) -1)
+        usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec);
+
+    wm = u->tsched_watermark_usec;
+
+    if (wm > usec)
+        wm = usec/2;
+
+    *sleep_usec = usec - wm;
+    *process_usec = wm;
+
+#ifdef DEBUG_TIMING
+    pa_log_debug("Buffer time: %lu ms; Sleep time: %lu ms; Process time: %lu ms",
+                 (unsigned long) (usec / PA_USEC_PER_MSEC),
+                 (unsigned long) (*sleep_usec / PA_USEC_PER_MSEC),
+                 (unsigned long) (*process_usec / PA_USEC_PER_MSEC));
+#endif
+}
+
+static int try_recover(struct userdata *u, const char *call, int err) {
+    pa_assert(u);
+    pa_assert(call);
+    pa_assert(err < 0);
+
+    pa_log_debug("%s: %s", call, pa_alsa_strerror(err));
+
+    pa_assert(err != -EAGAIN);
+
+    if (err == -EPIPE)
+        pa_log_debug("%s: Buffer overrun!", call);
+
+    if (err == -ESTRPIPE)
+        pa_log_debug("%s: System suspended!", call);
+
+    if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) {
+        pa_log("%s: %s", call, pa_alsa_strerror(err));
+        return -1;
+    }
+
+    u->first = true;
+    return 0;
+}
+
+static size_t check_left_to_record(struct userdata *u, size_t n_bytes, bool on_timeout) {
+    size_t left_to_record;
+    size_t rec_space = u->hwbuf_size - u->hwbuf_unused;
+    bool overrun = false;
+
+    /* We use <= instead of < for this check here because an overrun
+     * only happens after the last sample was processed, not already when
+     * it is removed from the buffer. This is particularly important
+     * when block transfer is used. */
+
+    if (n_bytes <= rec_space)
+        left_to_record = rec_space - n_bytes;
+    else {
+
+        /* We got a dropout. What a mess! */
+        left_to_record = 0;
+        overrun = true;
+
+#ifdef DEBUG_TIMING
+        PA_DEBUG_TRAP;
+#endif
+
+        if (pa_log_ratelimit(PA_LOG_INFO))
+            pa_log_info("Overrun!");
+    }
+
+#ifdef DEBUG_TIMING
+    pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC);
+#endif
+
+    if (u->use_tsched) {
+        bool reset_not_before = true;
+
+        if (overrun || left_to_record < u->watermark_inc_threshold)
+            increase_watermark(u);
+        else if (left_to_record > u->watermark_dec_threshold) {
+            reset_not_before = false;
+
+            /* We decrease the watermark only if have actually
+             * been woken up by a timeout. If something else woke
+             * us up it's too easy to fulfill the deadlines... */
+
+            if (on_timeout)
+                decrease_watermark(u);
+        }
+
+        if (reset_not_before)
+            u->watermark_dec_not_before = 0;
+    }
+
+    return left_to_record;
+}
+
+static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, bool polled, bool on_timeout) {
+    bool work_done = false;
+    pa_usec_t max_sleep_usec = 0, process_usec = 0;
+    size_t left_to_record;
+    unsigned j = 0;
+
+    pa_assert(u);
+    pa_source_assert_ref(u->source);
+
+    if (u->use_tsched)
+        hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+    for (;;) {
+        snd_pcm_sframes_t n;
+        size_t n_bytes;
+        int r;
+        bool after_avail = true;
+
+        if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
+
+            if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
+                continue;
+
+            return r;
+        }
+
+        n_bytes = (size_t) n * u->frame_size;
+
+#ifdef DEBUG_TIMING
+        pa_log_debug("avail: %lu", (unsigned long) n_bytes);
+#endif
+
+        left_to_record = check_left_to_record(u, n_bytes, on_timeout);
+        on_timeout = false;
+
+        if (u->use_tsched)
+            if (!polled &&
+                pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2) {
+#ifdef DEBUG_TIMING
+                pa_log_debug("Not reading, because too early.");
+#endif
+                break;
+            }
+
+        if (PA_UNLIKELY(n_bytes <= 0)) {
+
+            if (polled)
+                PA_ONCE_BEGIN {
+                    char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle);
+                    pa_log(_("ALSA woke us up to read new data from the device, but there was actually nothing to read.\n"
+                             "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+                             "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."),
+                           pa_strnull(dn));
+                    pa_xfree(dn);
+                } PA_ONCE_END;
+
+#ifdef DEBUG_TIMING
+            pa_log_debug("Not reading, because not necessary.");
+#endif
+            break;
+        }
+
+        if (++j > 10) {
+#ifdef DEBUG_TIMING
+            pa_log_debug("Not filling up, because already too many iterations.");
+#endif
+
+            break;
+        }
+
+        polled = false;
+
+#ifdef DEBUG_TIMING
+        pa_log_debug("Reading");
+#endif
+
+        for (;;) {
+            pa_memchunk chunk;
+            void *p;
+            int err;
+            const snd_pcm_channel_area_t *areas;
+            snd_pcm_uframes_t offset, frames;
+            snd_pcm_sframes_t sframes;
+
+            frames = (snd_pcm_uframes_t) (n_bytes / u->frame_size);
+/*             pa_log_debug("%lu frames to read", (unsigned long) frames); */
+
+            if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
+
+                if (!after_avail && err == -EAGAIN)
+                    break;
+
+                if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            /* Make sure that if these memblocks need to be copied they will fit into one slot */
+            frames = PA_MIN(frames, u->frames_per_block);
+
+            if (!after_avail && frames == 0)
+                break;
+
+            pa_assert(frames > 0);
+            after_avail = false;
+
+            /* Check these are multiples of 8 bit */
+            pa_assert((areas[0].first & 7) == 0);
+            pa_assert((areas[0].step & 7) == 0);
+
+            /* We assume a single interleaved memory buffer */
+            pa_assert((areas[0].first >> 3) == 0);
+            pa_assert((areas[0].step >> 3) == u->frame_size);
+
+            p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
+
+            chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, true);
+            chunk.length = pa_memblock_get_length(chunk.memblock);
+            chunk.index = 0;
+
+            pa_source_post(u->source, &chunk);
+            pa_memblock_unref_fixed(chunk.memblock);
+
+            if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
+
+                if ((r = try_recover(u, "snd_pcm_mmap_commit", (int) sframes)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            work_done = true;
+
+            u->read_count += frames * u->frame_size;
+
+#ifdef DEBUG_TIMING
+            pa_log_debug("Read %lu bytes (of possible %lu bytes)", (unsigned long) (frames * u->frame_size), (unsigned long) n_bytes);
+#endif
+
+            if ((size_t) frames * u->frame_size >= n_bytes)
+                break;
+
+            n_bytes -= (size_t) frames * u->frame_size;
+        }
+    }
+
+    if (u->use_tsched) {
+        *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec);
+        process_usec = u->tsched_watermark_usec;
+
+        if (*sleep_usec > process_usec)
+            *sleep_usec -= process_usec;
+        else
+            *sleep_usec = 0;
+    }
+
+    return work_done ? 1 : 0;
+}
+
+static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, bool polled, bool on_timeout) {
+    int work_done = false;
+    pa_usec_t max_sleep_usec = 0, process_usec = 0;
+    size_t left_to_record;
+    unsigned j = 0;
+
+    pa_assert(u);
+    pa_source_assert_ref(u->source);
+
+    if (u->use_tsched)
+        hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+    for (;;) {
+        snd_pcm_sframes_t n;
+        size_t n_bytes;
+        int r;
+        bool after_avail = true;
+
+        if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
+
+            if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
+                continue;
+
+            return r;
+        }
+
+        n_bytes = (size_t) n * u->frame_size;
+        left_to_record = check_left_to_record(u, n_bytes, on_timeout);
+        on_timeout = false;
+
+        if (u->use_tsched)
+            if (!polled &&
+                pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
+                break;
+
+        if (PA_UNLIKELY(n_bytes <= 0)) {
+
+            if (polled)
+                PA_ONCE_BEGIN {
+                    char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle);
+                    pa_log(_("ALSA woke us up to read new data from the device, but there was actually nothing to read!\n"
+                             "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+                             "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."),
+                           pa_strnull(dn));
+                    pa_xfree(dn);
+                } PA_ONCE_END;
+
+            break;
+        }
+
+        if (++j > 10) {
+#ifdef DEBUG_TIMING
+            pa_log_debug("Not filling up, because already too many iterations.");
+#endif
+
+            break;
+        }
+
+        polled = false;
+
+        for (;;) {
+            void *p;
+            snd_pcm_sframes_t frames;
+            pa_memchunk chunk;
+
+            chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
+
+            frames = (snd_pcm_sframes_t) (pa_memblock_get_length(chunk.memblock) / u->frame_size);
+
+            if (frames > (snd_pcm_sframes_t) (n_bytes/u->frame_size))
+                frames = (snd_pcm_sframes_t) (n_bytes/u->frame_size);
+
+/*             pa_log_debug("%lu frames to read", (unsigned long) n); */
+
+            p = pa_memblock_acquire(chunk.memblock);
+            frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames);
+            pa_memblock_release(chunk.memblock);
+
+            if (PA_UNLIKELY(frames < 0)) {
+                pa_memblock_unref(chunk.memblock);
+
+                if (!after_avail && (int) frames == -EAGAIN)
+                    break;
+
+                if ((r = try_recover(u, "snd_pcm_readi", (int) frames)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            if (!after_avail && frames == 0) {
+                pa_memblock_unref(chunk.memblock);
+                break;
+            }
+
+            pa_assert(frames > 0);
+            after_avail = false;
+
+            chunk.index = 0;
+            chunk.length = (size_t) frames * u->frame_size;
+
+            pa_source_post(u->source, &chunk);
+            pa_memblock_unref(chunk.memblock);
+
+            work_done = true;
+
+            u->read_count += frames * u->frame_size;
+
+/*             pa_log_debug("read %lu frames", (unsigned long) frames); */
+
+            if ((size_t) frames * u->frame_size >= n_bytes)
+                break;
+
+            n_bytes -= (size_t) frames * u->frame_size;
+        }
+    }
+
+    if (u->use_tsched) {
+        *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec);
+        process_usec = u->tsched_watermark_usec;
+
+        if (*sleep_usec > process_usec)
+            *sleep_usec -= process_usec;
+        else
+            *sleep_usec = 0;
+    }
+
+    return work_done ? 1 : 0;
+}
+
+static void update_smoother(struct userdata *u) {
+    snd_pcm_sframes_t delay = 0;
+    uint64_t position;
+    int err;
+    pa_usec_t now1 = 0, now2;
+    snd_pcm_status_t *status;
+    snd_htimestamp_t htstamp = { 0, 0 };
+
+    snd_pcm_status_alloca(&status);
+
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    /* Let's update the time smoother */
+
+    if (PA_UNLIKELY((err = pa_alsa_safe_delay(u->pcm_handle, status, &delay, u->hwbuf_size, &u->source->sample_spec, true)) < 0)) {
+        pa_log_warn("Failed to get delay: %s", pa_alsa_strerror(err));
+        return;
+    }
+
+    snd_pcm_status_get_htstamp(status, &htstamp);
+    now1 = pa_timespec_load(&htstamp);
+
+    /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */
+    if (now1 <= 0)
+        now1 = pa_rtclock_now();
+
+    /* check if the time since the last update is bigger than the interval */
+    if (u->last_smoother_update > 0)
+        if (u->last_smoother_update + u->smoother_interval > now1)
+            return;
+
+    position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size);
+    now2 = pa_bytes_to_usec(position, &u->source->sample_spec);
+
+    pa_smoother_put(u->smoother, now1, now2);
+
+    u->last_smoother_update = now1;
+    /* exponentially increase the update interval up to the MAX limit */
+    u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL);
+}
+
+static int64_t source_get_latency(struct userdata *u) {
+    int64_t delay;
+    pa_usec_t now1, now2;
+
+    pa_assert(u);
+
+    now1 = pa_rtclock_now();
+    now2 = pa_smoother_get(u->smoother, now1);
+
+    delay = (int64_t) now2 - (int64_t) pa_bytes_to_usec(u->read_count, &u->source->sample_spec);
+
+    return delay;
+}
+
+static int build_pollfd(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    if (u->alsa_rtpoll_item)
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+    if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll)))
+        return -1;
+
+    return 0;
+}
+
+/* Called from IO context */
+static int suspend(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    pa_smoother_pause(u->smoother, pa_rtclock_now());
+
+    /* Let's suspend */
+    snd_pcm_close(u->pcm_handle);
+    u->pcm_handle = NULL;
+
+    if (u->alsa_rtpoll_item) {
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+        u->alsa_rtpoll_item = NULL;
+    }
+
+    pa_log_info("Device suspended...");
+
+    return 0;
+}
+
+/* Called from IO context */
+static int update_sw_params(struct userdata *u) {
+    snd_pcm_uframes_t avail_min;
+    int err;
+
+    pa_assert(u);
+
+    /* Use the full buffer if no one asked us for anything specific */
+    u->hwbuf_unused = 0;
+
+    if (u->use_tsched) {
+        pa_usec_t latency;
+
+        if ((latency = pa_source_get_requested_latency_within_thread(u->source)) != (pa_usec_t) -1) {
+            size_t b;
+
+            pa_log_debug("latency set to %0.2fms", (double) latency / PA_USEC_PER_MSEC);
+
+            b = pa_usec_to_bytes(latency, &u->source->sample_spec);
+
+            /* We need at least one sample in our buffer */
+
+            if (PA_UNLIKELY(b < u->frame_size))
+                b = u->frame_size;
+
+            u->hwbuf_unused = PA_LIKELY(b < u->hwbuf_size) ? (u->hwbuf_size - b) : 0;
+        }
+
+        fix_min_sleep_wakeup(u);
+        fix_tsched_watermark(u);
+    }
+
+    pa_log_debug("hwbuf_unused=%lu", (unsigned long) u->hwbuf_unused);
+
+    avail_min = 1;
+
+    if (u->use_tsched) {
+        pa_usec_t sleep_usec, process_usec;
+
+        hw_sleep_time(u, &sleep_usec, &process_usec);
+        avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec) / u->frame_size;
+    }
+
+    pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
+
+    if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min, !u->use_tsched)) < 0) {
+        pa_log("Failed to set software parameters: %s", pa_alsa_strerror(err));
+        return err;
+    }
+
+    return 0;
+}
+
+/* Called from IO Context on unsuspend or from main thread when creating source */
+static void reset_watermark(struct userdata *u, size_t tsched_watermark, pa_sample_spec *ss,
+                            bool in_thread) {
+    u->tsched_watermark = pa_convert_size(tsched_watermark, ss, &u->source->sample_spec);
+
+    u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->source->sample_spec);
+    u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->source->sample_spec);
+
+    u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->source->sample_spec);
+    u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->source->sample_spec);
+
+    fix_min_sleep_wakeup(u);
+    fix_tsched_watermark(u);
+
+    if (in_thread)
+        pa_source_set_latency_range_within_thread(u->source,
+                                                  u->min_latency_ref,
+                                                  pa_bytes_to_usec(u->hwbuf_size, ss));
+    else {
+        pa_source_set_latency_range(u->source,
+                                    0,
+                                    pa_bytes_to_usec(u->hwbuf_size, ss));
+
+        /* work-around assert in pa_source_set_latency_within_thead,
+           keep track of min_latency and reuse it when
+           this routine is called from IO context */
+        u->min_latency_ref = u->source->thread_info.min_latency;
+    }
+
+    pa_log_info("Time scheduling watermark is %0.2fms",
+                (double) u->tsched_watermark_usec / PA_USEC_PER_MSEC);
+}
+
+/* Called from IO context */
+static int unsuspend(struct userdata *u) {
+    pa_sample_spec ss;
+    int err;
+    bool b, d;
+    snd_pcm_uframes_t period_size, buffer_size;
+
+    pa_assert(u);
+    pa_assert(!u->pcm_handle);
+
+    pa_log_info("Trying resume...");
+
+    if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE,
+                            SND_PCM_NONBLOCK|
+                            SND_PCM_NO_AUTO_RESAMPLE|
+                            SND_PCM_NO_AUTO_CHANNELS|
+                            SND_PCM_NO_AUTO_FORMAT)) < 0) {
+        pa_log("Error opening PCM device %s: %s", u->device_name, pa_alsa_strerror(err));
+        goto fail;
+    }
+
+    ss = u->source->sample_spec;
+    period_size = u->fragment_size / u->frame_size;
+    buffer_size = u->hwbuf_size / u->frame_size;
+    b = u->use_mmap;
+    d = u->use_tsched;
+
+    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, true)) < 0) {
+        pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err));
+        goto fail;
+    }
+
+    if (b != u->use_mmap || d != u->use_tsched) {
+        pa_log_warn("Resume failed, couldn't get original access mode.");
+        goto fail;
+    }
+
+    if (!pa_sample_spec_equal(&ss, &u->source->sample_spec)) {
+        pa_log_warn("Resume failed, couldn't restore original sample settings.");
+        goto fail;
+    }
+
+    if (period_size*u->frame_size != u->fragment_size ||
+        buffer_size*u->frame_size != u->hwbuf_size) {
+        pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu/%lu, New %lu/%lu)",
+                    (unsigned long) u->hwbuf_size, (unsigned long) u->fragment_size,
+                    (unsigned long) (buffer_size*u->frame_size), (unsigned long) (period_size*u->frame_size));
+        goto fail;
+    }
+
+    if (update_sw_params(u) < 0)
+        goto fail;
+
+    if (build_pollfd(u) < 0)
+        goto fail;
+
+    /* FIXME: We need to reload the volume somehow */
+
+    u->read_count = 0;
+    pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
+    u->smoother_interval = SMOOTHER_MIN_INTERVAL;
+    u->last_smoother_update = 0;
+
+    u->first = true;
+
+    /* reset the watermark to the value defined when source was created */
+    if (u->use_tsched)
+        reset_watermark(u, u->tsched_watermark_ref, &u->source->sample_spec, true);
+
+    pa_log_info("Resumed successfully...");
+
+    return 0;
+
+fail:
+    if (u->pcm_handle) {
+        snd_pcm_close(u->pcm_handle);
+        u->pcm_handle = NULL;
+    }
+
+    return -PA_ERR_IO;
+}
+
+/* Called from IO context */
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            int64_t r = 0;
+
+            if (u->pcm_handle)
+                r = source_get_latency(u);
+
+            *((int64_t*) data) = r;
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_SET_STATE:
+
+            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SOURCE_SUSPENDED: {
+                    int r;
+
+                    pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
+
+                    if ((r = suspend(u)) < 0)
+                        return r;
+
+                    break;
+                }
+
+                case PA_SOURCE_IDLE:
+                case PA_SOURCE_RUNNING: {
+                    int r;
+
+                    if (u->source->thread_info.state == PA_SOURCE_INIT) {
+                        if (build_pollfd(u) < 0)
+                            return -PA_ERR_IO;
+                    }
+
+                    if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
+                        if ((r = unsuspend(u)) < 0)
+                            return r;
+                    }
+
+                    break;
+                }
+
+                case PA_SOURCE_UNLINKED:
+                case PA_SOURCE_INIT:
+                case PA_SOURCE_INVALID_STATE:
+                    ;
+            }
+
+            break;
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int source_set_state_cb(pa_source *s, pa_source_state_t new_state) {
+    pa_source_state_t old_state;
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    old_state = pa_source_get_state(u->source);
+
+    if (PA_SOURCE_IS_OPENED(old_state) && new_state == PA_SOURCE_SUSPENDED)
+        reserve_done(u);
+    else if (old_state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(new_state))
+        if (reserve_init(u, u->device_name) < 0)
+            return -PA_ERR_BUSY;
+
+    return 0;
+}
+
+static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+
+    pa_assert(u);
+    pa_assert(u->mixer_handle);
+
+    if (mask == SND_CTL_EVENT_MASK_REMOVE)
+        return 0;
+
+    if (!PA_SOURCE_IS_LINKED(u->source->state))
+        return 0;
+
+    if (u->source->suspend_cause & PA_SUSPEND_SESSION) {
+        pa_source_set_mixer_dirty(u->source, true);
+        return 0;
+    }
+
+    if (mask & SND_CTL_EVENT_MASK_VALUE) {
+        pa_source_get_volume(u->source, true);
+        pa_source_get_mute(u->source, true);
+    }
+
+    return 0;
+}
+
+static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+
+    pa_assert(u);
+    pa_assert(u->mixer_handle);
+
+    if (mask == SND_CTL_EVENT_MASK_REMOVE)
+        return 0;
+
+    if (u->source->suspend_cause & PA_SUSPEND_SESSION) {
+        pa_source_set_mixer_dirty(u->source, true);
+        return 0;
+    }
+
+    if (mask & SND_CTL_EVENT_MASK_VALUE)
+        pa_source_update_volume_and_mute(u->source);
+
+    return 0;
+}
+
+static void source_get_volume_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    pa_cvolume r;
+    char volume_buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+
+    if (pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0)
+        return;
+
+    /* Shift down by the base volume, so that 0dB becomes maximum volume */
+    pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
+
+    pa_log_debug("Read hardware volume: %s",
+                 pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &r, &s->channel_map, u->mixer_path->has_dB));
+
+    if (pa_cvolume_equal(&u->hardware_volume, &r))
+        return;
+
+    s->real_volume = u->hardware_volume = r;
+
+    /* Hmm, so the hardware volume changed, let's reset our software volume */
+    if (u->mixer_path->has_dB)
+        pa_source_set_soft_volume(s, NULL);
+}
+
+static void source_set_volume_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    pa_cvolume r;
+    char volume_buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    bool deferred_volume = !!(s->flags & PA_SOURCE_DEFERRED_VOLUME);
+
+    pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+
+    /* Shift up by the base volume */
+    pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume);
+
+    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, deferred_volume, !deferred_volume) < 0)
+        return;
+
+    /* Shift down by the base volume, so that 0dB becomes maximum volume */
+    pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
+
+    u->hardware_volume = r;
+
+    if (u->mixer_path->has_dB) {
+        pa_cvolume new_soft_volume;
+        bool accurate_enough;
+
+        /* Match exactly what the user requested by software */
+        pa_sw_cvolume_divide(&new_soft_volume, &s->real_volume, &u->hardware_volume);
+
+        /* If the adjustment to do in software is only minimal we
+         * can skip it. That saves us CPU at the expense of a bit of
+         * accuracy */
+        accurate_enough =
+            (pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
+            (pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
+
+        pa_log_debug("Requested volume: %s",
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &s->real_volume, &s->channel_map, true));
+        pa_log_debug("Got hardware volume: %s",
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &u->hardware_volume, &s->channel_map, true));
+        pa_log_debug("Calculated software volume: %s (accurate-enough=%s)",
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &new_soft_volume, &s->channel_map, true),
+                     pa_yes_no(accurate_enough));
+
+        if (!accurate_enough)
+            s->soft_volume = new_soft_volume;
+
+    } else {
+        pa_log_debug("Wrote hardware volume: %s",
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &r, &s->channel_map, false));
+
+        /* We can't match exactly what the user requested, hence let's
+         * at least tell the user about it */
+
+        s->real_volume = r;
+    }
+}
+
+static void source_write_volume_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    pa_cvolume hw_vol = s->thread_info.current_hw_volume;
+
+    pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+    pa_assert(s->flags & PA_SOURCE_DEFERRED_VOLUME);
+
+    /* Shift up by the base volume */
+    pa_sw_cvolume_divide_scalar(&hw_vol, &hw_vol, s->base_volume);
+
+    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, true, true) < 0)
+        pa_log_error("Writing HW volume failed");
+    else {
+        pa_cvolume tmp_vol;
+        bool accurate_enough;
+
+        /* Shift down by the base volume, so that 0dB becomes maximum volume */
+        pa_sw_cvolume_multiply_scalar(&hw_vol, &hw_vol, s->base_volume);
+
+        pa_sw_cvolume_divide(&tmp_vol, &hw_vol, &s->thread_info.current_hw_volume);
+        accurate_enough =
+            (pa_cvolume_min(&tmp_vol) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
+            (pa_cvolume_max(&tmp_vol) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
+
+        if (!accurate_enough) {
+            char volume_buf[2][PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+            pa_log_debug("Written HW volume did not match with the request: %s (request) != %s",
+                         pa_cvolume_snprint_verbose(volume_buf[0],
+                                                    sizeof(volume_buf[0]),
+                                                    &s->thread_info.current_hw_volume,
+                                                    &s->channel_map,
+                                                    true),
+                         pa_cvolume_snprint_verbose(volume_buf[1], sizeof(volume_buf[1]), &hw_vol, &s->channel_map, true));
+        }
+    }
+}
+
+static int source_get_mute_cb(pa_source *s, bool *mute) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+
+    if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, mute) < 0)
+        return -1;
+
+    return 0;
+}
+
+static void source_set_mute_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+
+    pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted);
+}
+
+static void mixer_volume_init(struct userdata *u) {
+    pa_assert(u);
+
+    if (!u->mixer_path->has_volume) {
+        pa_source_set_write_volume_callback(u->source, NULL);
+        pa_source_set_get_volume_callback(u->source, NULL);
+        pa_source_set_set_volume_callback(u->source, NULL);
+
+        pa_log_info("Driver does not support hardware volume control, falling back to software volume control.");
+    } else {
+        pa_source_set_get_volume_callback(u->source, source_get_volume_cb);
+        pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
+
+        if (u->mixer_path->has_dB && u->deferred_volume) {
+            pa_source_set_write_volume_callback(u->source, source_write_volume_cb);
+            pa_log_info("Successfully enabled deferred volume.");
+        } else
+            pa_source_set_write_volume_callback(u->source, NULL);
+
+        if (u->mixer_path->has_dB) {
+            pa_source_enable_decibel_volume(u->source, true);
+            pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB);
+
+            u->source->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB);
+            u->source->n_volume_steps = PA_VOLUME_NORM+1;
+
+            pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume));
+        } else {
+            pa_source_enable_decibel_volume(u->source, false);
+            pa_log_info("Hardware volume ranges from %li to %li.", u->mixer_path->min_volume, u->mixer_path->max_volume);
+
+            u->source->base_volume = PA_VOLUME_NORM;
+            u->source->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1;
+        }
+
+        pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
+    }
+
+    if (!u->mixer_path->has_mute) {
+        pa_source_set_get_mute_callback(u->source, NULL);
+        pa_source_set_set_mute_callback(u->source, NULL);
+        pa_log_info("Driver does not support hardware mute control, falling back to software mute control.");
+    } else {
+        pa_source_set_get_mute_callback(u->source, source_get_mute_cb);
+        pa_source_set_set_mute_callback(u->source, source_set_mute_cb);
+        pa_log_info("Using hardware mute control.");
+    }
+}
+
+static int source_set_port_ucm_cb(pa_source *s, pa_device_port *p) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(p);
+    pa_assert(u->ucm_context);
+
+    return pa_alsa_ucm_set_port(u->ucm_context, p, false);
+}
+
+static int source_set_port_cb(pa_source *s, pa_device_port *p) {
+    struct userdata *u = s->userdata;
+    pa_alsa_port_data *data;
+
+    pa_assert(u);
+    pa_assert(p);
+    pa_assert(u->mixer_handle);
+
+    data = PA_DEVICE_PORT_DATA(p);
+
+    pa_assert_se(u->mixer_path = data->path);
+    pa_alsa_path_select(u->mixer_path, data->setting, u->mixer_handle, s->muted);
+
+    mixer_volume_init(u);
+
+    if (s->set_mute)
+        s->set_mute(s);
+    if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
+        if (s->write_volume)
+            s->write_volume(s);
+    } else {
+        if (s->set_volume)
+            s->set_volume(s);
+    }
+
+    return 0;
+}
+
+static void source_update_requested_latency_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    pa_assert(u);
+    pa_assert(u->use_tsched); /* only when timer scheduling is used
+                               * we can dynamically adjust the
+                               * latency */
+
+    if (!u->pcm_handle)
+        return;
+
+    update_sw_params(u);
+}
+
+static int source_update_rate_cb(pa_source *s, uint32_t rate) {
+    struct userdata *u = s->userdata;
+    int i;
+    bool supported = false;
+
+    pa_assert(u);
+
+    for (i = 0; u->rates[i]; i++) {
+        if (u->rates[i] == rate) {
+            supported = true;
+            break;
+        }
+    }
+
+    if (!supported) {
+        pa_log_info("Source does not support sample rate of %d Hz", rate);
+        return -1;
+    }
+
+    if (!PA_SOURCE_IS_OPENED(s->state)) {
+        pa_log_info("Updating rate for device %s, new rate is %d", u->device_name, rate);
+        u->source->sample_spec.rate = rate;
+        return 0;
+    }
+
+    return -1;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    unsigned short revents = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        int ret;
+        pa_usec_t rtpoll_sleep = 0, real_sleep;
+
+#ifdef DEBUG_TIMING
+        pa_log_debug("Loop");
+#endif
+
+        /* Read some data and pass it to the sources */
+        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+            int work_done;
+            pa_usec_t sleep_usec = 0;
+            bool on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll);
+
+            if (u->first) {
+                pa_log_info("Starting capture.");
+                snd_pcm_start(u->pcm_handle);
+
+                pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
+
+                u->first = false;
+            }
+
+            if (u->use_mmap)
+                work_done = mmap_read(u, &sleep_usec, revents & POLLIN, on_timeout);
+            else
+                work_done = unix_read(u, &sleep_usec, revents & POLLIN, on_timeout);
+
+            if (work_done < 0)
+                goto fail;
+
+/*             pa_log_debug("work_done = %i", work_done); */
+
+            if (work_done)
+                update_smoother(u);
+
+            if (u->use_tsched) {
+                pa_usec_t cusec;
+
+                /* OK, the capture buffer is now empty, let's
+                 * calculate when to wake up next */
+
+/*                 pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */
+
+                /* Convert from the sound card time domain to the
+                 * system time domain */
+                cusec = pa_smoother_translate(u->smoother, pa_rtclock_now(), sleep_usec);
+
+/*                 pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
+
+                /* We don't trust the conversion, so we wake up whatever comes first */
+                rtpoll_sleep = PA_MIN(sleep_usec, cusec);
+            }
+        }
+
+        if (u->source->flags & PA_SOURCE_DEFERRED_VOLUME) {
+            pa_usec_t volume_sleep;
+            pa_source_volume_change_apply(u->source, &volume_sleep);
+            if (volume_sleep > 0) {
+                if (rtpoll_sleep > 0)
+                    rtpoll_sleep = PA_MIN(volume_sleep, rtpoll_sleep);
+                else
+                    rtpoll_sleep = volume_sleep;
+            }
+        }
+
+        if (rtpoll_sleep > 0) {
+            pa_rtpoll_set_timer_relative(u->rtpoll, rtpoll_sleep);
+            real_sleep = pa_rtclock_now();
+        }
+        else
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (rtpoll_sleep > 0) {
+            real_sleep = pa_rtclock_now() - real_sleep;
+#ifdef DEBUG_TIMING
+            pa_log_debug("Expected sleep: %0.2fms, real sleep: %0.2fms (diff %0.2f ms)",
+                (double) rtpoll_sleep / PA_USEC_PER_MSEC, (double) real_sleep / PA_USEC_PER_MSEC,
+                (double) ((int64_t) real_sleep - (int64_t) rtpoll_sleep) / PA_USEC_PER_MSEC);
+#endif
+            if (u->use_tsched && real_sleep > rtpoll_sleep + u->tsched_watermark_usec)
+                pa_log_info("Scheduling delay of %0.2f ms > %0.2f ms, you might want to investigate this to improve latency...",
+                    (double) (real_sleep - rtpoll_sleep) / PA_USEC_PER_MSEC,
+                    (double) (u->tsched_watermark_usec) / PA_USEC_PER_MSEC);
+        }
+
+        if (u->source->flags & PA_SOURCE_DEFERRED_VOLUME)
+            pa_source_volume_change_apply(u->source, NULL);
+
+        if (ret == 0)
+            goto finish;
+
+        /* Tell ALSA about this and process its response */
+        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+            struct pollfd *pollfd;
+            int err;
+            unsigned n;
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, &n);
+
+            if ((err = snd_pcm_poll_descriptors_revents(u->pcm_handle, pollfd, n, &revents)) < 0) {
+                pa_log("snd_pcm_poll_descriptors_revents() failed: %s", pa_alsa_strerror(err));
+                goto fail;
+            }
+
+            if (revents & ~POLLIN) {
+                if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
+                    goto fail;
+
+                u->first = true;
+                revents = 0;
+            } else if (revents && u->use_tsched && pa_log_ratelimit(PA_LOG_DEBUG))
+                pa_log_debug("Wakeup from ALSA!");
+
+        } else
+            revents = 0;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name, pa_alsa_mapping *mapping) {
+    const char *n;
+    char *t;
+
+    pa_assert(data);
+    pa_assert(ma);
+    pa_assert(device_name);
+
+    if ((n = pa_modargs_get_value(ma, "source_name", NULL))) {
+        pa_source_new_data_set_name(data, n);
+        data->namereg_fail = true;
+        return;
+    }
+
+    if ((n = pa_modargs_get_value(ma, "name", NULL)))
+        data->namereg_fail = true;
+    else {
+        n = device_id ? device_id : device_name;
+        data->namereg_fail = false;
+    }
+
+    if (mapping)
+        t = pa_sprintf_malloc("alsa_input.%s.%s", n, mapping->name);
+    else
+        t = pa_sprintf_malloc("alsa_input.%s", n);
+
+    pa_source_new_data_set_name(data, t);
+    pa_xfree(t);
+}
+
+static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char *element, bool ignore_dB) {
+    if (!mapping && !element)
+        return;
+
+    if (!(u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device))) {
+        pa_log_info("Failed to find a working mixer device.");
+        return;
+    }
+
+    if (element) {
+
+        if (!(u->mixer_path = pa_alsa_path_synthesize(element, PA_ALSA_DIRECTION_INPUT)))
+            goto fail;
+
+        if (pa_alsa_path_probe(u->mixer_path, u->mixer_handle, ignore_dB) < 0)
+            goto fail;
+
+        pa_log_debug("Probed mixer path %s:", u->mixer_path->name);
+        pa_alsa_path_dump(u->mixer_path);
+    } else if (!(u->mixer_path_set = mapping->input_path_set))
+        goto fail;
+
+    return;
+
+fail:
+
+    if (u->mixer_path) {
+        pa_alsa_path_free(u->mixer_path);
+        u->mixer_path = NULL;
+    }
+
+    if (u->mixer_handle) {
+        snd_mixer_close(u->mixer_handle);
+        u->mixer_handle = NULL;
+    }
+}
+
+static int setup_mixer(struct userdata *u, bool ignore_dB) {
+    bool need_mixer_callback = false;
+
+    pa_assert(u);
+
+    if (!u->mixer_handle)
+        return 0;
+
+    if (u->source->active_port) {
+        pa_alsa_port_data *data;
+
+        /* We have a list of supported paths, so let's activate the
+         * one that has been chosen as active */
+
+        data = PA_DEVICE_PORT_DATA(u->source->active_port);
+        u->mixer_path = data->path;
+
+        pa_alsa_path_select(data->path, data->setting, u->mixer_handle, u->source->muted);
+
+    } else {
+
+        if (!u->mixer_path && u->mixer_path_set)
+            u->mixer_path = pa_hashmap_first(u->mixer_path_set->paths);
+
+        if (u->mixer_path) {
+            /* Hmm, we have only a single path, then let's activate it */
+
+            pa_alsa_path_select(u->mixer_path, u->mixer_path->settings, u->mixer_handle, u->source->muted);
+        } else
+            return 0;
+    }
+
+    mixer_volume_init(u);
+
+    /* Will we need to register callbacks? */
+    if (u->mixer_path_set && u->mixer_path_set->paths) {
+        pa_alsa_path *p;
+        void *state;
+
+        PA_HASHMAP_FOREACH(p, u->mixer_path_set->paths, state) {
+            if (p->has_volume || p->has_mute)
+                need_mixer_callback = true;
+        }
+    }
+    else if (u->mixer_path)
+        need_mixer_callback = u->mixer_path->has_volume || u->mixer_path->has_mute;
+
+    if (need_mixer_callback) {
+        int (*mixer_callback)(snd_mixer_elem_t *, unsigned int);
+        if (u->source->flags & PA_SOURCE_DEFERRED_VOLUME) {
+            u->mixer_pd = pa_alsa_mixer_pdata_new();
+            mixer_callback = io_mixer_callback;
+
+            if (pa_alsa_set_mixer_rtpoll(u->mixer_pd, u->mixer_handle, u->rtpoll) < 0) {
+                pa_log("Failed to initialize file descriptor monitoring");
+                return -1;
+            }
+        } else {
+            u->mixer_fdl = pa_alsa_fdlist_new();
+            mixer_callback = ctl_mixer_callback;
+
+            if (pa_alsa_fdlist_set_handle(u->mixer_fdl, u->mixer_handle, NULL, u->core->mainloop) < 0) {
+                pa_log("Failed to initialize file descriptor monitoring");
+                return -1;
+            }
+        }
+
+        if (u->mixer_path_set)
+            pa_alsa_path_set_set_callback(u->mixer_path_set, u->mixer_handle, mixer_callback, u);
+        else
+            pa_alsa_path_set_callback(u->mixer_path, u->mixer_handle, mixer_callback, u);
+    }
+
+    return 0;
+}
+
+pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) {
+
+    struct userdata *u = NULL;
+    const char *dev_id = NULL, *key, *mod_name;
+    pa_sample_spec ss;
+    char *thread_name = NULL;
+    uint32_t alternate_sample_rate;
+    pa_channel_map map;
+    uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark;
+    snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames;
+    size_t frame_size;
+    bool use_mmap = true, b, use_tsched = true, d, ignore_dB = false, namereg_fail = false, deferred_volume = false, fixed_latency_range = false;
+    pa_source_new_data data;
+    bool volume_is_set;
+    bool mute_is_set;
+    pa_alsa_profile_set *profile_set = NULL;
+    void *state = NULL;
+
+    pa_assert(m);
+    pa_assert(ma);
+
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
+
+    /* Pick sample spec overrides from the mapping, if any */
+    if (mapping) {
+        if (mapping->sample_spec.format != PA_SAMPLE_INVALID)
+            ss.format = mapping->sample_spec.format;
+        if (mapping->sample_spec.rate != 0)
+            ss.rate = mapping->sample_spec.rate;
+        if (mapping->sample_spec.channels != 0) {
+            ss.channels = mapping->sample_spec.channels;
+            if (pa_channel_map_valid(&mapping->channel_map))
+                pa_assert(pa_channel_map_compatible(&mapping->channel_map, &ss));
+        }
+    }
+
+    /* Override with modargs if provided */
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
+        pa_log("Failed to parse sample specification and channel map");
+        goto fail;
+    }
+
+    alternate_sample_rate = m->core->alternate_sample_rate;
+    if (pa_modargs_get_alternate_sample_rate(ma, &alternate_sample_rate) < 0) {
+        pa_log("Failed to parse alternate sample rate");
+        goto fail;
+    }
+
+    frame_size = pa_frame_size(&ss);
+
+    nfrags = m->core->default_n_fragments;
+    frag_size = (uint32_t) pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);
+    if (frag_size <= 0)
+        frag_size = (uint32_t) frame_size;
+    tsched_size = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss);
+    tsched_watermark = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);
+
+    if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||
+        pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 ||
+        pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 ||
+        pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) {
+        pa_log("Failed to parse buffer metrics");
+        goto fail;
+    }
+
+    buffer_size = nfrags * frag_size;
+
+    period_frames = frag_size/frame_size;
+    buffer_frames = buffer_size/frame_size;
+    tsched_frames = tsched_size/frame_size;
+
+    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+        pa_log("Failed to parse mmap argument.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+        pa_log("Failed to parse tsched argument.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
+        pa_log("Failed to parse ignore_dB argument.");
+        goto fail;
+    }
+
+    deferred_volume = m->core->deferred_volume;
+    if (pa_modargs_get_value_boolean(ma, "deferred_volume", &deferred_volume) < 0) {
+        pa_log("Failed to parse deferred_volume argument.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "fixed_latency_range", &fixed_latency_range) < 0) {
+        pa_log("Failed to parse fixed_latency_range argument.");
+        goto fail;
+    }
+
+    use_tsched = pa_alsa_may_tsched(use_tsched);
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->use_mmap = use_mmap;
+    u->use_tsched = use_tsched;
+    u->deferred_volume = deferred_volume;
+    u->fixed_latency_range = fixed_latency_range;
+    u->first = true;
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->smoother = pa_smoother_new(
+            SMOOTHER_ADJUST_USEC,
+            SMOOTHER_WINDOW_USEC,
+            true,
+            true,
+            5,
+            pa_rtclock_now(),
+            true);
+    u->smoother_interval = SMOOTHER_MIN_INTERVAL;
+
+    /* use ucm */
+    if (mapping && mapping->ucm_context.ucm)
+        u->ucm_context = &mapping->ucm_context;
+
+    dev_id = pa_modargs_get_value(
+            ma, "device_id",
+            pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
+
+    u->paths_dir = pa_xstrdup(pa_modargs_get_value(ma, "paths_dir", NULL));
+
+    if (reserve_init(u, dev_id) < 0)
+        goto fail;
+
+    if (reserve_monitor_init(u, dev_id) < 0)
+        goto fail;
+
+    b = use_mmap;
+    d = use_tsched;
+
+    /* Force ALSA to reread its configuration if module-alsa-card didn't
+     * do it for us. This matters if our device was hot-plugged after ALSA
+     * has already read its configuration - see
+     * https://bugs.freedesktop.org/show_bug.cgi?id=54029
+     */
+
+    if (!card)
+        snd_config_update_free_global();
+
+    if (mapping) {
+
+        if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+            pa_log("device_id= not set");
+            goto fail;
+        }
+
+        if ((mod_name = pa_proplist_gets(mapping->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+            if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
+                pa_log("Failed to enable ucm modifier %s", mod_name);
+            else
+                pa_log_debug("Enabled ucm modifier %s", mod_name);
+        }
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_id_mapping(
+                      dev_id,
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_CAPTURE,
+                      &period_frames, &buffer_frames, tsched_frames,
+                      &b, &d, mapping)))
+            goto fail;
+
+    } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+
+        if (!(profile_set = pa_alsa_profile_set_new(NULL, &map)))
+            goto fail;
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
+                      dev_id,
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_CAPTURE,
+                      &period_frames, &buffer_frames, tsched_frames,
+                      &b, &d, profile_set, &mapping)))
+            goto fail;
+
+    } else {
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_string(
+                      pa_modargs_get_value(ma, "device", DEFAULT_DEVICE),
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_CAPTURE,
+                      &period_frames, &buffer_frames, tsched_frames,
+                      &b, &d, false)))
+            goto fail;
+    }
+
+    pa_assert(u->device_name);
+    pa_log_info("Successfully opened device %s.", u->device_name);
+
+    if (pa_alsa_pcm_is_modem(u->pcm_handle)) {
+        pa_log_notice("Device %s is modem, refusing further initialization.", u->device_name);
+        goto fail;
+    }
+
+    if (mapping)
+        pa_log_info("Selected mapping '%s' (%s).", mapping->description, mapping->name);
+
+    if (use_mmap && !b) {
+        pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
+        u->use_mmap = use_mmap = false;
+    }
+
+    if (use_tsched && (!b || !d)) {
+        pa_log_info("Cannot enable timer-based scheduling, falling back to sound IRQ scheduling.");
+        u->use_tsched = use_tsched = false;
+    }
+
+    if (u->use_mmap)
+        pa_log_info("Successfully enabled mmap() mode.");
+
+    if (u->use_tsched) {
+        pa_log_info("Successfully enabled timer-based scheduling mode.");
+        if (u->fixed_latency_range)
+            pa_log_info("Disabling latency range changes on overrun");
+    }
+
+    u->rates = pa_alsa_get_supported_rates(u->pcm_handle, ss.rate);
+    if (!u->rates) {
+        pa_log_error("Failed to find any supported sample rates.");
+        goto fail;
+    }
+
+    /* ALSA might tweak the sample spec, so recalculate the frame size */
+    frame_size = pa_frame_size(&ss);
+
+    if (!u->ucm_context)
+        find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
+
+    pa_source_new_data_init(&data);
+    data.driver = driver;
+    data.module = m;
+    data.card = card;
+    set_source_name(&data, ma, dev_id, u->device_name, mapping);
+
+    /* We need to give pa_modargs_get_value_boolean() a pointer to a local
+     * variable instead of using &data.namereg_fail directly, because
+     * data.namereg_fail is a bitfield and taking the address of a bitfield
+     * variable is impossible. */
+    namereg_fail = data.namereg_fail;
+    if (pa_modargs_get_value_boolean(ma, "namereg_fail", &namereg_fail) < 0) {
+        pa_log("Failed to parse namereg_fail argument.");
+        pa_source_new_data_done(&data);
+        goto fail;
+    }
+    data.namereg_fail = namereg_fail;
+
+    pa_source_new_data_set_sample_spec(&data, &ss);
+    pa_source_new_data_set_channel_map(&data, &map);
+    pa_source_new_data_set_alternate_sample_rate(&data, alternate_sample_rate);
+
+    pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (buffer_frames * frame_size));
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+
+    if (mapping) {
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name);
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description);
+
+        while ((key = pa_proplist_iterate(mapping->proplist, &state)))
+            pa_proplist_sets(data.proplist, key, pa_proplist_gets(mapping->proplist, key));
+    }
+
+    pa_alsa_init_description(data.proplist, card);
+
+    if (u->control_device)
+        pa_alsa_init_proplist_ctl(data.proplist, u->control_device);
+
+    if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_source_new_data_done(&data);
+        goto fail;
+    }
+
+    if (u->ucm_context)
+        pa_alsa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, false, card);
+    else if (u->mixer_path_set)
+        pa_alsa_add_ports(&data, u->mixer_path_set, card);
+
+    u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|(u->use_tsched ? PA_SOURCE_DYNAMIC_LATENCY : 0));
+    volume_is_set = data.volume_is_set;
+    mute_is_set = data.muted_is_set;
+    pa_source_new_data_done(&data);
+
+    if (!u->source) {
+        pa_log("Failed to create source object");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "deferred_volume_safety_margin",
+                                 &u->source->thread_info.volume_change_safety_margin) < 0) {
+        pa_log("Failed to parse deferred_volume_safety_margin parameter");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_s32(ma, "deferred_volume_extra_delay",
+                                 &u->source->thread_info.volume_change_extra_delay) < 0) {
+        pa_log("Failed to parse deferred_volume_extra_delay parameter");
+        goto fail;
+    }
+
+    u->source->parent.process_msg = source_process_msg;
+    if (u->use_tsched)
+        u->source->update_requested_latency = source_update_requested_latency_cb;
+    u->source->set_state = source_set_state_cb;
+    if (u->ucm_context)
+        u->source->set_port = source_set_port_ucm_cb;
+    else
+        u->source->set_port = source_set_port_cb;
+    if (u->source->alternate_sample_rate)
+        u->source->update_rate = source_update_rate_cb;
+    u->source->userdata = u;
+
+    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+
+    u->frame_size = frame_size;
+    u->frames_per_block = pa_mempool_block_size_max(m->core->mempool) / frame_size;
+    u->fragment_size = frag_size = (size_t) (period_frames * frame_size);
+    u->hwbuf_size = buffer_size = (size_t) (buffer_frames * frame_size);
+    pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels);
+
+    pa_log_info("Using %0.1f fragments of size %lu bytes (%0.2fms), buffer size is %lu bytes (%0.2fms)",
+                (double) u->hwbuf_size / (double) u->fragment_size,
+                (long unsigned) u->fragment_size,
+                (double) pa_bytes_to_usec(u->fragment_size, &ss) / PA_USEC_PER_MSEC,
+                (long unsigned) u->hwbuf_size,
+                (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
+
+    if (u->use_tsched) {
+        u->tsched_watermark_ref = tsched_watermark;
+        reset_watermark(u, u->tsched_watermark_ref, &ss, false);
+    }
+    else
+        pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->hwbuf_size, &ss));
+
+    reserve_update(u);
+
+    if (update_sw_params(u) < 0)
+        goto fail;
+
+    if (u->ucm_context) {
+        if (u->source->active_port && pa_alsa_ucm_set_port(u->ucm_context, u->source->active_port, false) < 0)
+            goto fail;
+    } else if (setup_mixer(u, ignore_dB) < 0)
+        goto fail;
+
+    pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
+
+    thread_name = pa_sprintf_malloc("alsa-source-%s", pa_strnull(pa_proplist_gets(u->source->proplist, "alsa.id")));
+    if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+    pa_xfree(thread_name);
+    thread_name = NULL;
+
+    /* Get initial mixer settings */
+    if (volume_is_set) {
+        if (u->source->set_volume)
+            u->source->set_volume(u->source);
+    } else {
+        if (u->source->get_volume)
+            u->source->get_volume(u->source);
+    }
+
+    if (mute_is_set) {
+        if (u->source->set_mute)
+            u->source->set_mute(u->source);
+    } else {
+        if (u->source->get_mute) {
+            bool mute;
+
+            if (u->source->get_mute(u->source, &mute) >= 0)
+                pa_source_set_mute(u->source, mute, false);
+        }
+    }
+
+    if ((volume_is_set || mute_is_set) && u->source->write_volume)
+        u->source->write_volume(u->source);
+
+    pa_source_put(u->source);
+
+    if (profile_set)
+        pa_alsa_profile_set_free(profile_set);
+
+    return u->source;
+
+fail:
+    pa_xfree(thread_name);
+
+    if (u)
+        userdata_free(u);
+
+    if (profile_set)
+        pa_alsa_profile_set_free(profile_set);
+
+    return NULL;
+}
+
+static void userdata_free(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->mixer_pd)
+        pa_alsa_mixer_pdata_free(u->mixer_pd);
+
+    if (u->alsa_rtpoll_item)
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->pcm_handle) {
+        snd_pcm_drop(u->pcm_handle);
+        snd_pcm_close(u->pcm_handle);
+    }
+
+    if (u->mixer_fdl)
+        pa_alsa_fdlist_free(u->mixer_fdl);
+
+    if (u->mixer_path && !u->mixer_path_set)
+        pa_alsa_path_free(u->mixer_path);
+
+    if (u->mixer_handle)
+        snd_mixer_close(u->mixer_handle);
+
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+
+    if (u->rates)
+        pa_xfree(u->rates);
+
+    reserve_done(u);
+    monitor_done(u);
+
+    pa_xfree(u->device_name);
+    pa_xfree(u->control_device);
+    pa_xfree(u->paths_dir);
+    pa_xfree(u);
+}
+
+void pa_alsa_source_free(pa_source *s) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    userdata_free(u);
+}
diff --git a/src/modules/alsa/alsa-source.h b/src/modules/alsa/alsa-source.h
new file mode 100644 (file)
index 0000000..ecbdfcd
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef fooalsasourcehfoo
+#define fooalsasourcehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/source.h>
+
+#include "alsa-util.h"
+
+pa_source* pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping);
+
+void pa_alsa_source_free(pa_source *s);
+
+#endif
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
new file mode 100644 (file)
index 0000000..b42c040
--- /dev/null
@@ -0,0 +1,1940 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2011 Wolfson Microelectronics PLC
+ Author Margarita Olaya <magi@slimlogic.co.uk>
+ Copyright 2012 Feng Wei <wei.feng@freescale.com>, Freescale Ltd.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <asoundlib.h>
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <pulse/sample.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/once.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/conf-parser.h>
+#include <pulsecore/strbuf.h>
+
+#include "alsa-mixer.h"
+#include "alsa-util.h"
+#include "alsa-ucm.h"
+
+#define PA_UCM_PRE_TAG_OUTPUT                       "[Out] "
+#define PA_UCM_PRE_TAG_INPUT                        "[In] "
+
+#define PA_UCM_PLAYBACK_PRIORITY_UNSET(device)      ((device)->playback_channels && !(device)->playback_priority)
+#define PA_UCM_CAPTURE_PRIORITY_UNSET(device)       ((device)->capture_channels && !(device)->capture_priority)
+#define PA_UCM_DEVICE_PRIORITY_SET(device, priority) \
+    do { \
+        if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority);   \
+        if (PA_UCM_CAPTURE_PRIORITY_UNSET(device))  (device)->capture_priority = (priority);    \
+    } while (0)
+#define PA_UCM_IS_MODIFIER_MAPPING(m) ((pa_proplist_gets((m)->proplist, PA_ALSA_PROP_UCM_MODIFIER)) != NULL)
+
+#ifdef HAVE_ALSA_UCM
+
+struct ucm_items {
+    const char *id;
+    const char *property;
+};
+
+struct ucm_info {
+    const char *id;
+    unsigned priority;
+};
+
+static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack);
+static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack);
+
+static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name);
+
+struct ucm_port {
+    pa_alsa_ucm_config *ucm;
+    pa_device_port *core_port;
+
+    /* A single port will be associated with multiple devices if it represents
+     * a combination of devices. */
+    pa_dynarray *devices; /* pa_alsa_ucm_device */
+};
+
+static void ucm_port_init(struct ucm_port *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
+                          pa_alsa_ucm_device **devices, unsigned n_devices);
+static void ucm_port_free(pa_device_port *port);
+static void ucm_port_update_available(struct ucm_port *port);
+
+static struct ucm_items item[] = {
+    {"PlaybackPCM", PA_ALSA_PROP_UCM_SINK},
+    {"CapturePCM", PA_ALSA_PROP_UCM_SOURCE},
+    {"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME},
+    {"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH},
+    {"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY},
+    {"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE},
+    {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS},
+    {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME},
+    {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH},
+    {"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY},
+    {"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE},
+    {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
+    {"TQ", PA_ALSA_PROP_UCM_QOS},
+    {"JackControl", PA_ALSA_PROP_UCM_JACK_CONTROL},
+    {"JackHWMute", PA_ALSA_PROP_UCM_JACK_HW_MUTE},
+    {NULL, NULL},
+};
+
+/* UCM verb info - this should eventually be part of policy manangement */
+static struct ucm_info verb_info[] = {
+    {SND_USE_CASE_VERB_INACTIVE, 0},
+    {SND_USE_CASE_VERB_HIFI, 8000},
+    {SND_USE_CASE_VERB_HIFI_LOW_POWER, 7000},
+    {SND_USE_CASE_VERB_VOICE, 6000},
+    {SND_USE_CASE_VERB_VOICE_LOW_POWER, 5000},
+    {SND_USE_CASE_VERB_VOICECALL, 4000},
+    {SND_USE_CASE_VERB_IP_VOICECALL, 4000},
+    {SND_USE_CASE_VERB_ANALOG_RADIO, 3000},
+    {SND_USE_CASE_VERB_DIGITAL_RADIO, 3000},
+    {NULL, 0}
+};
+
+/* UCM device info - should be overwritten by ucm property */
+static struct ucm_info dev_info[] = {
+    {SND_USE_CASE_DEV_SPEAKER, 100},
+    {SND_USE_CASE_DEV_LINE, 100},
+    {SND_USE_CASE_DEV_HEADPHONES, 100},
+    {SND_USE_CASE_DEV_HEADSET, 300},
+    {SND_USE_CASE_DEV_HANDSET, 200},
+    {SND_USE_CASE_DEV_BLUETOOTH, 400},
+    {SND_USE_CASE_DEV_EARPIECE, 100},
+    {SND_USE_CASE_DEV_SPDIF, 100},
+    {SND_USE_CASE_DEV_HDMI, 100},
+    {SND_USE_CASE_DEV_NONE, 100},
+    {NULL, 0}
+};
+
+/* UCM profile properties - The verb data is store so it can be used to fill
+ * the new profiles properties */
+static int ucm_get_property(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr, const char *verb_name) {
+    const char *value;
+    char *id;
+    int i;
+
+    for (i = 0; item[i].id; i++) {
+        int err;
+
+        id = pa_sprintf_malloc("=%s//%s", item[i].id, verb_name);
+        err = snd_use_case_get(uc_mgr, id, &value);
+        pa_xfree(id);
+        if (err < 0)
+            continue;
+
+        pa_log_debug("Got %s for verb %s: %s", item[i].id, verb_name, value);
+        pa_proplist_sets(verb->proplist, item[i].property, value);
+        free((void*)value);
+    }
+
+    return 0;
+};
+
+static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
+    pa_alsa_ucm_device *d;
+    uint32_t idx;
+
+    PA_IDXSET_FOREACH(d, idxset, idx)
+        if (d == dev)
+            return 1;
+
+    return 0;
+}
+
+static void ucm_add_devices_to_idxset(
+        pa_idxset *idxset,
+        pa_alsa_ucm_device *me,
+        pa_alsa_ucm_device *devices,
+        const char **dev_names,
+        int n) {
+
+    pa_alsa_ucm_device *d;
+
+    PA_LLIST_FOREACH(d, devices) {
+        const char *name;
+        int i;
+
+        if (d == me)
+            continue;
+
+        name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        for (i = 0; i < n; i++)
+            if (pa_streq(dev_names[i], name))
+                pa_idxset_put(idxset, d, NULL);
+    }
+}
+
+/* Create a property list for this ucm device */
+static int ucm_get_device_property(
+        pa_alsa_ucm_device *device,
+        snd_use_case_mgr_t *uc_mgr,
+        pa_alsa_ucm_verb *verb,
+        const char *device_name) {
+
+    const char *value;
+    const char **devices;
+    char *id;
+    int i;
+    int err;
+    uint32_t ui;
+    int n_confdev, n_suppdev;
+
+    for (i = 0; item[i].id; i++) {
+        id = pa_sprintf_malloc("=%s/%s", item[i].id, device_name);
+        err = snd_use_case_get(uc_mgr, id, &value);
+        pa_xfree(id);
+        if (err < 0)
+            continue;
+
+        pa_log_debug("Got %s for device %s: %s", item[i].id, device_name, value);
+        pa_proplist_sets(device->proplist, item[i].property, value);
+        free((void*)value);
+    }
+
+    /* get direction and channels */
+    value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
+    if (value) { /* output */
+        /* get channels */
+        if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
+            device->playback_channels = ui;
+        else
+            pa_log("UCM playback channels %s for device %s out of range", value, device_name);
+
+        /* get pcm */
+        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK);
+        if (!value) { /* take pcm from verb playback default */
+            value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SINK);
+            if (value) {
+                pa_log_debug("UCM playback device %s fetch pcm from verb default %s", device_name, value);
+                pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SINK, value);
+            } else
+                pa_log("UCM playback device %s fetch pcm failed", device_name);
+        }
+    }
+
+    value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
+    if (value) { /* input */
+        /* get channels */
+        if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
+            device->capture_channels = ui;
+        else
+            pa_log("UCM capture channels %s for device %s out of range", value, device_name);
+
+        /* get pcm */
+        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE);
+        if (!value) { /* take pcm from verb capture default */
+            value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SOURCE);
+            if (value) {
+                pa_log_debug("UCM capture device %s fetch pcm from verb default %s", device_name, value);
+                pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SOURCE, value);
+            } else
+                pa_log("UCM capture device %s fetch pcm failed", device_name);
+        }
+    }
+
+    if (device->playback_channels == 0 && device->capture_channels == 0) {
+        pa_log_warn("UCM file does not specify 'PlaybackChannels' or 'CaptureChannels'"
+                    "for device %s, assuming stereo duplex.", device_name);
+        device->playback_channels = 2;
+        device->capture_channels = 2;
+    }
+
+    /* get rate and priority of device */
+    if (device->playback_channels) { /* sink device */
+        /* get rate */
+        if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE)) ||
+            (value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE))) {
+            if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
+                pa_log_debug("UCM playback device %s rate %d", device_name, ui);
+                device->playback_rate = ui;
+            } else
+                pa_log_debug("UCM playback device %s has bad rate %s", device_name, value);
+        }
+
+        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY);
+        if (value) {
+            /* get priority from ucm config */
+            if (pa_atou(value, &ui) == 0)
+                device->playback_priority = ui;
+            else
+                pa_log_debug("UCM playback priority %s for device %s error", value, device_name);
+        }
+    }
+
+    if (device->capture_channels) { /* source device */
+        /* get rate */
+        if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE)) ||
+            (value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE))) {
+            if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
+                pa_log_debug("UCM capture device %s rate %d", device_name, ui);
+                device->capture_rate = ui;
+            } else
+                pa_log_debug("UCM capture device %s has bad rate %s", device_name, value);
+        }
+
+        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY);
+        if (value) {
+            /* get priority from ucm config */
+            if (pa_atou(value, &ui) == 0)
+                device->capture_priority = ui;
+            else
+                pa_log_debug("UCM capture priority %s for device %s error", value, device_name);
+        }
+    }
+
+    if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
+        /* get priority from static table */
+        for (i = 0; dev_info[i].id; i++) {
+            if (strcasecmp(dev_info[i].id, device_name) == 0) {
+                PA_UCM_DEVICE_PRIORITY_SET(device, dev_info[i].priority);
+                break;
+            }
+        }
+    }
+
+    if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) {
+        /* fall through to default priority */
+        device->playback_priority = 100;
+    }
+
+    if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
+        /* fall through to default priority */
+        device->capture_priority = 100;
+    }
+
+    id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name);
+    n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
+    pa_xfree(id);
+
+    if (n_confdev <= 0)
+        pa_log_debug("No %s for device %s", "_conflictingdevs", device_name);
+    else {
+        device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev);
+        snd_use_case_free_list(devices, n_confdev);
+    }
+
+    id = pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name);
+    n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
+    pa_xfree(id);
+
+    if (n_suppdev <= 0)
+        pa_log_debug("No %s for device %s", "_supporteddevs", device_name);
+    else {
+        device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev);
+        snd_use_case_free_list(devices, n_suppdev);
+    }
+
+    return 0;
+};
+
+/* Create a property list for this ucm modifier */
+static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) {
+    const char *value;
+    char *id;
+    int i;
+
+    for (i = 0; item[i].id; i++) {
+        int err;
+
+        id = pa_sprintf_malloc("=%s/%s", item[i].id, modifier_name);
+        err = snd_use_case_get(uc_mgr, id, &value);
+        pa_xfree(id);
+        if (err < 0)
+            continue;
+
+        pa_log_debug("Got %s for modifier %s: %s", item[i].id, modifier_name, value);
+        pa_proplist_sets(modifier->proplist, item[i].property, value);
+        free((void*)value);
+    }
+
+    id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name);
+    modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices);
+    pa_xfree(id);
+    if (modifier->n_confdev < 0)
+        pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name);
+
+    id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name);
+    modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices);
+    pa_xfree(id);
+    if (modifier->n_suppdev < 0)
+        pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name);
+
+    return 0;
+};
+
+/* Create a list of devices for this verb */
+static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
+    const char **dev_list;
+    int num_dev, i;
+
+    num_dev = snd_use_case_get_list(uc_mgr, "_devices", &dev_list);
+    if (num_dev < 0)
+        return num_dev;
+
+    for (i = 0; i < num_dev; i += 2) {
+        pa_alsa_ucm_device *d = pa_xnew0(pa_alsa_ucm_device, 1);
+
+        d->proplist = pa_proplist_new();
+        pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(dev_list[i]));
+        pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(dev_list[i + 1]));
+        d->ucm_ports = pa_dynarray_new(NULL);
+        d->hw_mute_jacks = pa_dynarray_new(NULL);
+        d->available = PA_AVAILABLE_UNKNOWN;
+
+        PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
+    }
+
+    snd_use_case_free_list(dev_list, num_dev);
+
+    return 0;
+};
+
+static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
+    const char **mod_list;
+    int num_mod, i;
+
+    num_mod = snd_use_case_get_list(uc_mgr, "_modifiers", &mod_list);
+    if (num_mod < 0)
+        return num_mod;
+
+    for (i = 0; i < num_mod; i += 2) {
+        pa_alsa_ucm_modifier *m;
+
+        if (!mod_list[i]) {
+            pa_log_warn("Got a modifier with a null name. Skipping.");
+            continue;
+        }
+
+        m = pa_xnew0(pa_alsa_ucm_modifier, 1);
+        m->proplist = pa_proplist_new();
+
+        pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_NAME, mod_list[i]);
+        pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(mod_list[i + 1]));
+
+        PA_LLIST_PREPEND(pa_alsa_ucm_modifier, verb->modifiers, m);
+    }
+
+    snd_use_case_free_list(mod_list, num_mod);
+
+    return 0;
+};
+
+static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) {
+    const char *cur = pa_proplist_gets(dev->proplist, role_name);
+
+    if (!cur)
+        pa_proplist_sets(dev->proplist, role_name, role);
+    else if (!pa_str_in_list_spaces(cur, role)) { /* does not exist */
+        char *value = pa_sprintf_malloc("%s %s", cur, role);
+
+        pa_proplist_sets(dev->proplist, role_name, value);
+        pa_xfree(value);
+    }
+
+    pa_log_info("Add role %s to device %s(%s), result %s", role, dev_name, role_name, pa_proplist_gets(dev->proplist,
+                role_name));
+}
+
+static void add_media_role(const char *name, pa_alsa_ucm_device *list, const char *role_name, const char *role, bool is_sink) {
+    pa_alsa_ucm_device *d;
+
+    PA_LLIST_FOREACH(d, list) {
+        const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        if (pa_streq(dev_name, name)) {
+            const char *sink = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK);
+            const char *source = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE);
+
+            if (is_sink && sink)
+                add_role_to_device(d, dev_name, role_name, role);
+            else if (!is_sink && source)
+                add_role_to_device(d, dev_name, role_name, role);
+            break;
+        }
+    }
+}
+
+static char *modifier_name_to_role(const char *mod_name, bool *is_sink) {
+    char *sub = NULL, *tmp;
+
+    *is_sink = false;
+
+    if (pa_startswith(mod_name, "Play")) {
+        *is_sink = true;
+        sub = pa_xstrdup(mod_name + 4);
+    } else if (pa_startswith(mod_name, "Capture"))
+        sub = pa_xstrdup(mod_name + 7);
+
+    if (!sub || !*sub) {
+        pa_xfree(sub);
+        pa_log_warn("Can't match media roles for modifer %s", mod_name);
+        return NULL;
+    }
+
+    tmp = sub;
+
+    do {
+        *tmp = tolower(*tmp);
+    } while (*(++tmp));
+
+    return sub;
+}
+
+static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, pa_alsa_ucm_device *list, const char *mod_name) {
+    int i;
+    bool is_sink = false;
+    char *sub = NULL;
+    const char *role_name;
+
+    sub = modifier_name_to_role(mod_name, &is_sink);
+    if (!sub)
+        return;
+
+    modifier->action_direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
+    modifier->media_role = sub;
+
+    role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
+    for (i = 0; i < modifier->n_suppdev; i++) {
+        /* if modifier has no specific pcm, we add role intent to its supported devices */
+        if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) &&
+                !pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE))
+            add_media_role(modifier->supported_devices[i], list, role_name, sub, is_sink);
+    }
+}
+
+static void append_lost_relationship(pa_alsa_ucm_device *dev) {
+    uint32_t idx;
+    pa_alsa_ucm_device *d;
+
+    if (dev->conflicting_devices) {
+        PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
+            if (!d->conflicting_devices)
+                d->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+            if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0)
+                pa_log_warn("Add lost conflicting device %s to %s",
+                        pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
+                        pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
+        }
+    }
+
+    if (dev->supported_devices) {
+        PA_IDXSET_FOREACH(d, dev->supported_devices, idx) {
+            if (!d->supported_devices)
+                d->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+            if (pa_idxset_put(d->supported_devices, dev, NULL) == 0)
+                pa_log_warn("Add lost supported device %s to %s",
+                        pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
+                        pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
+        }
+    }
+}
+
+int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
+    char *card_name;
+    const char **verb_list;
+    int num_verbs, i, err = 0;
+
+    /* is UCM available for this card ? */
+    err = snd_card_get_name(card_index, &card_name);
+    if (err < 0) {
+        pa_log("Card can't get card_name from card_index %d", card_index);
+        goto name_fail;
+    }
+
+    err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
+    if (err < 0) {
+        pa_log_info("UCM not available for card %s", card_name);
+        goto ucm_mgr_fail;
+    }
+
+    pa_log_info("UCM available for card %s", card_name);
+
+    /* get a list of all UCM verbs (profiles) for this card */
+    num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list);
+    if (num_verbs < 0) {
+        pa_log("UCM verb list not found for %s", card_name);
+        goto ucm_verb_fail;
+    }
+
+    /* get the properties of each UCM verb */
+    for (i = 0; i < num_verbs; i += 2) {
+        pa_alsa_ucm_verb *verb;
+
+        /* Get devices and modifiers for each verb */
+        err = pa_alsa_ucm_get_verb(ucm->ucm_mgr, verb_list[i], verb_list[i+1], &verb);
+        if (err < 0) {
+            pa_log("Failed to get the verb %s", verb_list[i]);
+            continue;
+        }
+
+        PA_LLIST_PREPEND(pa_alsa_ucm_verb, ucm->verbs, verb);
+    }
+
+    if (!ucm->verbs) {
+        pa_log("No UCM verb is valid for %s", card_name);
+        err = -1;
+    }
+
+    snd_use_case_free_list(verb_list, num_verbs);
+
+ucm_verb_fail:
+    if (err < 0) {
+        snd_use_case_mgr_close(ucm->ucm_mgr);
+        ucm->ucm_mgr = NULL;
+    }
+
+ucm_mgr_fail:
+    free(card_name);
+
+name_fail:
+    return err;
+}
+
+int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb) {
+    pa_alsa_ucm_device *d;
+    pa_alsa_ucm_modifier *mod;
+    pa_alsa_ucm_verb *verb;
+    int err = 0;
+
+    *p_verb = NULL;
+    pa_log_info("Set UCM verb to %s", verb_name);
+    err = snd_use_case_set(uc_mgr, "_verb", verb_name);
+    if (err < 0)
+        return err;
+
+    verb = pa_xnew0(pa_alsa_ucm_verb, 1);
+    verb->proplist = pa_proplist_new();
+
+    pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(verb_name));
+    pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(verb_desc));
+
+    err = ucm_get_devices(verb, uc_mgr);
+    if (err < 0)
+        pa_log("No UCM devices for verb %s", verb_name);
+
+    err = ucm_get_modifiers(verb, uc_mgr);
+    if (err < 0)
+        pa_log("No UCM modifiers for verb %s", verb_name);
+
+    /* Verb properties */
+    ucm_get_property(verb, uc_mgr, verb_name);
+
+    PA_LLIST_FOREACH(d, verb->devices) {
+        const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        /* Devices properties */
+        ucm_get_device_property(d, uc_mgr, verb, dev_name);
+    }
+    /* make conflicting or supported device mutual */
+    PA_LLIST_FOREACH(d, verb->devices)
+        append_lost_relationship(d);
+
+    PA_LLIST_FOREACH(mod, verb->modifiers) {
+        const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        /* Modifier properties */
+        ucm_get_modifier_property(mod, uc_mgr, mod_name);
+
+        /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
+        pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name);
+        ucm_set_media_roles(mod, verb->devices, mod_name);
+    }
+
+    *p_verb = verb;
+    return 0;
+}
+
+static int pa_alsa_ucm_device_cmp(const void *a, const void *b) {
+    const pa_alsa_ucm_device *d1 = *(pa_alsa_ucm_device **)a;
+    const pa_alsa_ucm_device *d2 = *(pa_alsa_ucm_device **)b;
+
+    return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
+}
+
+static void ucm_add_port_combination(
+        pa_hashmap *hash,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_alsa_ucm_device **pdevices,
+        int num,
+        pa_hashmap *ports,
+        pa_card_profile *cp,
+        pa_core *core) {
+
+    pa_device_port *port;
+    int i;
+    unsigned priority;
+    double prio2;
+    char *name, *desc;
+    const char *dev_name;
+    const char *direction;
+    pa_alsa_ucm_device *sorted[num], *dev;
+
+    for (i = 0; i < num; i++)
+        sorted[i] = pdevices[i];
+
+    /* Sort by alphabetical order so as to have a deterministic naming scheme
+     * for combination ports */
+    qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
+
+    dev = sorted[0];
+    dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
+
+    name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name);
+    desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION))
+            : pa_sprintf_malloc("Combination port for %s", dev_name);
+
+    priority = is_sink ? dev->playback_priority : dev->capture_priority;
+    prio2 = (priority == 0 ? 0 : 1.0/priority);
+
+    for (i = 1; i < num; i++) {
+        char *tmp;
+
+        dev = sorted[i];
+        dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        tmp = pa_sprintf_malloc("%s+%s", name, dev_name);
+        pa_xfree(name);
+        name = tmp;
+
+        tmp = pa_sprintf_malloc("%s,%s", desc, dev_name);
+        pa_xfree(desc);
+        desc = tmp;
+
+        priority = is_sink ? dev->playback_priority : dev->capture_priority;
+        if (priority != 0 && prio2 > 0)
+            prio2 += 1.0/priority;
+    }
+
+    /* Make combination ports always have lower priority, and use the formula
+       1/p = 1/p1 + 1/p2 + ... 1/pn.
+       This way, the result will always be less than the individual components,
+       yet higher components will lead to higher result. */
+
+    if (num > 1)
+        priority = prio2 > 0 ? 1.0/prio2 : 0;
+
+    port = pa_hashmap_get(ports, name);
+    if (!port) {
+        struct ucm_port *ucm_port;
+
+        pa_device_port_new_data port_data;
+
+        pa_device_port_new_data_init(&port_data);
+        pa_device_port_new_data_set_name(&port_data, name);
+        pa_device_port_new_data_set_description(&port_data, desc);
+        pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
+
+        port = pa_device_port_new(core, &port_data, sizeof(struct ucm_port));
+        port->impl_free = ucm_port_free;
+        pa_device_port_new_data_done(&port_data);
+
+        ucm_port = PA_DEVICE_PORT_DATA(port);
+        ucm_port_init(ucm_port, context->ucm, port, pdevices, num);
+
+        pa_hashmap_put(ports, port->name, port);
+        pa_log_debug("Add port %s: %s", port->name, port->description);
+    }
+
+    port->priority = priority;
+
+    pa_xfree(name);
+    pa_xfree(desc);
+
+    direction = is_sink ? "output" : "input";
+    pa_log_debug("Port %s direction %s, priority %d", port->name, direction, priority);
+
+    if (cp) {
+        pa_log_debug("Adding profile %s to port %s.", cp->name, port->name);
+        pa_hashmap_put(port->profiles, cp->name, cp);
+    }
+
+    if (hash) {
+        pa_hashmap_put(hash, port->name, port);
+        pa_device_port_ref(port);
+    }
+}
+
+static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
+    int ret = 0;
+    const char *r;
+    const char *state = NULL;
+    int len;
+
+    if (!port_name || !dev_name)
+        return false;
+
+    port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT);
+
+    while ((r = pa_split_in_place(port_name, "+", &len, &state))) {
+        if (!strncmp(r, dev_name, len)) {
+            ret = 1;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+static int ucm_check_conformance(
+        pa_alsa_ucm_mapping_context *context,
+        pa_alsa_ucm_device **pdevices,
+        int dev_num,
+        pa_alsa_ucm_device *dev) {
+
+    uint32_t idx;
+    pa_alsa_ucm_device *d;
+    int i;
+
+    pa_assert(dev);
+
+    pa_log_debug("Check device %s conformance with %d other devices",
+            pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num);
+    if (dev_num == 0) {
+        pa_log_debug("First device in combination, number 1");
+        return 1;
+    }
+
+    if (dev->conflicting_devices) { /* the device defines conflicting devices */
+        PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
+            for (i = 0; i < dev_num; i++) {
+                if (pdevices[i] == d) {
+                    pa_log_debug("Conflicting device found");
+                    return 0;
+                }
+            }
+        }
+    } else if (dev->supported_devices) { /* the device defines supported devices */
+        for (i = 0; i < dev_num; i++) {
+            if (!ucm_device_exists(dev->supported_devices, pdevices[i])) {
+                pa_log_debug("Supported device not found");
+                return 0;
+            }
+        }
+    } else { /* not support any other devices */
+        pa_log_debug("Not support any other devices");
+        return 0;
+    }
+
+    pa_log_debug("Device added to combination, number %d", dev_num + 1);
+    return 1;
+}
+
+static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) {
+    pa_alsa_ucm_device *dev;
+
+    if (*idx == PA_IDXSET_INVALID)
+        dev = pa_idxset_first(idxset, idx);
+    else
+        dev = pa_idxset_next(idxset, idx);
+
+    return dev;
+}
+
+static void ucm_add_ports_combination(
+        pa_hashmap *hash,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_alsa_ucm_device **pdevices,
+        int dev_num,
+        uint32_t map_index,
+        pa_hashmap *ports,
+        pa_card_profile *cp,
+        pa_core *core) {
+
+    pa_alsa_ucm_device *dev;
+    uint32_t idx = map_index;
+
+    if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL)
+        return;
+
+    /* check if device at map_index can combine with existing devices combination */
+    if (ucm_check_conformance(context, pdevices, dev_num, dev)) {
+        /* add device at map_index to devices combination */
+        pdevices[dev_num] = dev;
+        /* add current devices combination as a new port */
+        ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core);
+        /* try more elements combination */
+        ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core);
+    }
+
+    /* try other device with current elements number */
+    ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core);
+}
+
+static char* merge_roles(const char *cur, const char *add) {
+    char *r, *ret;
+    const char *state = NULL;
+
+    if (add == NULL)
+        return pa_xstrdup(cur);
+    else if (cur == NULL)
+        return pa_xstrdup(add);
+
+    ret = pa_xstrdup(cur);
+
+    while ((r = pa_split_spaces(add, &state))) {
+        char *value;
+
+        if (!pa_str_in_list_spaces(ret, r))
+            value = pa_sprintf_malloc("%s %s", ret, r);
+        else {
+            pa_xfree(r);
+            continue;
+        }
+
+        pa_xfree(ret);
+        ret = value;
+        pa_xfree(r);
+    }
+
+    return ret;
+}
+
+void pa_alsa_ucm_add_ports_combination(
+        pa_hashmap *p,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_hashmap *ports,
+        pa_card_profile *cp,
+        pa_core *core) {
+
+    pa_alsa_ucm_device **pdevices;
+
+    pa_assert(context->ucm_devices);
+
+    if (pa_idxset_size(context->ucm_devices) > 0) {
+        pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices));
+        ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core);
+        pa_xfree(pdevices);
+    }
+}
+
+void pa_alsa_ucm_add_ports(
+        pa_hashmap **p,
+        pa_proplist *proplist,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_card *card) {
+
+    uint32_t idx;
+    char *merged_roles;
+    const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
+    pa_alsa_ucm_device *dev;
+    pa_alsa_ucm_modifier *mod;
+    char *tmp;
+
+    pa_assert(p);
+    pa_assert(*p);
+
+    /* add ports first */
+    pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
+
+    /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
+    merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
+    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
+        const char *roles = pa_proplist_gets(dev->proplist, role_name);
+        tmp = merge_roles(merged_roles, roles);
+        pa_xfree(merged_roles);
+        merged_roles = tmp;
+    }
+
+    if (context->ucm_modifiers)
+        PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
+            tmp = merge_roles(merged_roles, mod->media_role);
+            pa_xfree(merged_roles);
+            merged_roles = tmp;
+        }
+
+    if (merged_roles)
+        pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles);
+
+    pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist, PA_PROP_DEVICE_STRING), pa_strnull(merged_roles));
+    pa_xfree(merged_roles);
+}
+
+/* Change UCM verb and device to match selected card profile */
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile) {
+    int ret = 0;
+    const char *profile;
+    pa_alsa_ucm_verb *verb;
+
+    if (new_profile == old_profile)
+        return ret;
+    else if (new_profile == NULL || old_profile == NULL)
+        profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE;
+    else if (!pa_streq(new_profile, old_profile))
+        profile = new_profile;
+    else
+        return ret;
+
+    /* change verb */
+    pa_log_info("Set UCM verb to %s", profile);
+    if ((snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) {
+        pa_log("Failed to set verb %s", profile);
+        ret = -1;
+    }
+
+    /* find active verb */
+    ucm->active_verb = NULL;
+    PA_LLIST_FOREACH(verb, ucm->verbs) {
+        const char *verb_name;
+        verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
+        if (pa_streq(verb_name, profile)) {
+            ucm->active_verb = verb;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
+    int i;
+    int ret = 0;
+    pa_alsa_ucm_config *ucm;
+    const char **enable_devs;
+    int enable_num = 0;
+    uint32_t idx;
+    pa_alsa_ucm_device *dev;
+
+    pa_assert(context && context->ucm);
+
+    ucm = context->ucm;
+    pa_assert(ucm->ucm_mgr);
+
+    enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices));
+
+    /* first disable then enable */
+    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
+        const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        if (ucm_port_contains(port->name, dev_name, is_sink))
+            enable_devs[enable_num++] = dev_name;
+        else {
+            pa_log_debug("Disable ucm device %s", dev_name);
+            if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) {
+                pa_log("Failed to disable ucm device %s", dev_name);
+                ret = -1;
+                break;
+            }
+        }
+    }
+
+    for (i = 0; i < enable_num; i++) {
+        pa_log_debug("Enable ucm device %s", enable_devs[i]);
+        if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devs[i]) < 0) {
+            pa_log("Failed to enable ucm device %s", enable_devs[i]);
+            ret = -1;
+            break;
+        }
+    }
+
+    pa_xfree(enable_devs);
+
+    return ret;
+}
+
+static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
+
+    switch (m->direction) {
+        case PA_ALSA_DIRECTION_ANY:
+            pa_idxset_put(p->output_mappings, m, NULL);
+            pa_idxset_put(p->input_mappings, m, NULL);
+            break;
+        case PA_ALSA_DIRECTION_OUTPUT:
+            pa_idxset_put(p->output_mappings, m, NULL);
+            break;
+        case PA_ALSA_DIRECTION_INPUT:
+            pa_idxset_put(p->input_mappings, m, NULL);
+            break;
+    }
+}
+
+static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *device) {
+    char *cur_desc;
+    const char *new_desc;
+
+    pa_idxset_put(m->ucm_context.ucm_devices, device, NULL);
+
+    new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
+    cur_desc = m->description;
+    if (cur_desc)
+        m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
+    else
+        m->description = pa_xstrdup(new_desc);
+    pa_xfree(cur_desc);
+
+    /* walk around null case */
+    m->description = m->description ? m->description : pa_xstrdup("");
+
+    /* save mapping to ucm device */
+    if (m->direction == PA_ALSA_DIRECTION_OUTPUT)
+        device->playback_mapping = m;
+    else
+        device->capture_mapping = m;
+}
+
+static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifier *modifier) {
+    char *cur_desc;
+    const char *new_desc, *mod_name, *channel_str;
+    uint32_t channels = 0;
+
+    pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL);
+
+    new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
+    cur_desc = m->description;
+    if (cur_desc)
+        m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
+    else
+        m->description = pa_xstrdup(new_desc);
+    pa_xfree(cur_desc);
+
+    m->description = m->description ? m->description : pa_xstrdup("");
+
+    /* Modifier sinks should not be routed to by default */
+    m->priority = 0;
+
+    mod_name = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_NAME);
+    pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_MODIFIER, mod_name);
+
+    /* save mapping to ucm modifier */
+    if (m->direction == PA_ALSA_DIRECTION_OUTPUT) {
+        modifier->playback_mapping = m;
+        channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
+    } else {
+        modifier->capture_mapping = m;
+        channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
+    }
+
+    if (channel_str) {
+        /* FIXME: channel_str is unsanitized input from the UCM configuration,
+         * we should do proper error handling instead of asserting.
+         * https://bugs.freedesktop.org/show_bug.cgi?id=71823 */
+        pa_assert_se(pa_atou(channel_str, &channels) == 0 && pa_channels_valid(channels));
+        pa_log_debug("Got channel count %" PRIu32 " for modifier", channels);
+    }
+
+    if (channels)
+        pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
+    else
+        pa_channel_map_init(&m->channel_map);
+}
+
+static int ucm_create_mapping_direction(
+        pa_alsa_ucm_config *ucm,
+        pa_alsa_profile_set *ps,
+        pa_alsa_profile *p,
+        pa_alsa_ucm_device *device,
+        const char *verb_name,
+        const char *device_name,
+        const char *device_str,
+        bool is_sink) {
+
+    pa_alsa_mapping *m;
+    char *mapping_name;
+    unsigned priority, rate, channels;
+
+    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
+
+    m = pa_alsa_mapping_get(ps, mapping_name);
+    if (!m) {
+        pa_log("No mapping for %s", mapping_name);
+        pa_xfree(mapping_name);
+        return -1;
+    }
+    pa_log_debug("UCM mapping: %s dev %s", mapping_name, device_name);
+    pa_xfree(mapping_name);
+
+    priority = is_sink ? device->playback_priority : device->capture_priority;
+    rate = is_sink ? device->playback_rate : device->capture_rate;
+    channels = is_sink ? device->playback_channels : device->capture_channels;
+
+    if (!m->ucm_context.ucm_devices) {   /* new mapping */
+        m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        m->ucm_context.ucm = ucm;
+        m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
+
+        m->device_strings = pa_xnew0(char*, 2);
+        m->device_strings[0] = pa_xstrdup(device_str);
+        m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
+
+        ucm_add_mapping(p, m);
+        if (rate)
+            m->sample_spec.rate = rate;
+        pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
+    }
+
+    /* mapping priority is the highest one of ucm devices */
+    if (priority > m->priority)
+        m->priority = priority;
+
+    /* mapping channels is the lowest one of ucm devices */
+    if (channels < m->channel_map.channels)
+        pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
+
+    alsa_mapping_add_ucm_device(m, device);
+
+    return 0;
+}
+
+static int ucm_create_mapping_for_modifier(
+        pa_alsa_ucm_config *ucm,
+        pa_alsa_profile_set *ps,
+        pa_alsa_profile *p,
+        pa_alsa_ucm_modifier *modifier,
+        const char *verb_name,
+        const char *mod_name,
+        const char *device_str,
+        bool is_sink) {
+
+    pa_alsa_mapping *m;
+    char *mapping_name;
+
+    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
+
+    m = pa_alsa_mapping_get(ps, mapping_name);
+    if (!m) {
+        pa_log("no mapping for %s", mapping_name);
+        pa_xfree(mapping_name);
+        return -1;
+    }
+    pa_log_info("ucm mapping: %s modifier %s", mapping_name, mod_name);
+    pa_xfree(mapping_name);
+
+    if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) {   /* new mapping */
+        m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        m->ucm_context.ucm = ucm;
+        m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
+
+        m->device_strings = pa_xnew0(char*, 2);
+        m->device_strings[0] = pa_xstrdup(device_str);
+        m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
+        /* Modifier sinks should not be routed to by default */
+        m->priority = 0;
+
+        ucm_add_mapping(p, m);
+    } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */
+        m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    alsa_mapping_add_ucm_modifier(m, modifier);
+
+    return 0;
+}
+
+static int ucm_create_mapping(
+        pa_alsa_ucm_config *ucm,
+        pa_alsa_profile_set *ps,
+        pa_alsa_profile *p,
+        pa_alsa_ucm_device *device,
+        const char *verb_name,
+        const char *device_name,
+        const char *sink,
+        const char *source) {
+
+    int ret = 0;
+
+    if (!sink && !source) {
+        pa_log("No sink and source at %s: %s", verb_name, device_name);
+        return -1;
+    }
+
+    if (sink)
+        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, true);
+    if (ret == 0 && source)
+        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, false);
+
+    return ret;
+}
+
+static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device) {
+    pa_alsa_jack *j;
+    const char *device_name;
+    const char *jack_control;
+    char *name;
+
+    pa_assert(ucm);
+    pa_assert(device);
+
+    device_name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
+
+    jack_control = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_JACK_CONTROL);
+    if (jack_control) {
+        if (!pa_endswith(jack_control, " Jack")) {
+            pa_log("[%s] Invalid JackControl value: \"%s\"", device_name, jack_control);
+            return NULL;
+        }
+
+        /* pa_alsa_jack_new() expects a jack name without " Jack" at the
+         * end, so drop the trailing " Jack". */
+        name = pa_xstrndup(jack_control, strlen(jack_control) - 5);
+    } else {
+        /* The jack control hasn't been explicitly configured - try a jack name
+         * that is the same as the device name. */
+        name = pa_xstrdup(device_name);
+    }
+
+    PA_LLIST_FOREACH(j, ucm->jacks)
+        if (pa_streq(j->name, name))
+            goto finish;
+
+    j = pa_alsa_jack_new(NULL, name);
+    PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
+
+finish:
+    pa_xfree(name);
+
+    return j;
+}
+
+static int ucm_create_profile(
+        pa_alsa_ucm_config *ucm,
+        pa_alsa_profile_set *ps,
+        pa_alsa_ucm_verb *verb,
+        const char *verb_name,
+        const char *verb_desc) {
+
+    pa_alsa_profile *p;
+    pa_alsa_ucm_device *dev;
+    pa_alsa_ucm_modifier *mod;
+    int i = 0;
+    const char *name, *sink, *source;
+    char *verb_cmp, *c;
+
+    pa_assert(ps);
+
+    if (pa_hashmap_get(ps->profiles, verb_name)) {
+        pa_log("Verb %s already exists", verb_name);
+        return -1;
+    }
+
+    p = pa_xnew0(pa_alsa_profile, 1);
+    p->profile_set = ps;
+    p->name = pa_xstrdup(verb_name);
+    p->description = pa_xstrdup(verb_desc);
+
+    p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    p->supported = true;
+    pa_hashmap_put(ps->profiles, p->name, p);
+
+    /* TODO: get profile priority from ucm info or policy management */
+    c = verb_cmp = pa_xstrdup(verb_name);
+    while (*c) {
+        if (*c == '_') *c = ' ';
+        c++;
+    }
+
+    for (i = 0; verb_info[i].id; i++) {
+        if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
+            p->priority = verb_info[i].priority;
+            break;
+        }
+    }
+
+    pa_xfree(verb_cmp);
+
+    if (verb_info[i].id == NULL)
+        p->priority = 1000;
+
+    PA_LLIST_FOREACH(dev, verb->devices) {
+        pa_alsa_jack *jack;
+        const char *jack_hw_mute;
+
+        name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
+        source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
+
+        ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
+
+        jack = ucm_get_jack(ucm, dev);
+        device_set_jack(dev, jack);
+
+        /* JackHWMute contains a list of device names. Each listed device must
+         * be associated with the jack object that we just created. */
+        jack_hw_mute = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_HW_MUTE);
+        if (jack_hw_mute) {
+            char *hw_mute_device_name;
+            const char *state = NULL;
+
+            while ((hw_mute_device_name = pa_split_spaces(jack_hw_mute, &state))) {
+                pa_alsa_ucm_verb *verb2;
+                bool device_found = false;
+
+                /* Search the referenced device from all verbs. If there are
+                 * multiple verbs that have a device with this name, we add the
+                 * hw mute association to each of those devices. */
+                PA_LLIST_FOREACH(verb2, ucm->verbs) {
+                    pa_alsa_ucm_device *hw_mute_device;
+
+                    hw_mute_device = verb_find_device(verb2, hw_mute_device_name);
+                    if (hw_mute_device) {
+                        device_found = true;
+                        device_add_hw_mute_jack(hw_mute_device, jack);
+                    }
+                }
+
+                if (!device_found)
+                    pa_log("[%s] JackHWMute references an unknown device: %s", name, hw_mute_device_name);
+
+                pa_xfree(hw_mute_device_name);
+            }
+        }
+    }
+
+    /* Now find modifiers that have their own PlaybackPCM and create
+     * separate sinks for them. */
+    PA_LLIST_FOREACH(mod, verb->modifiers) {
+        name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        sink = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SINK);
+        source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE);
+
+        if (sink)
+            ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, true);
+        else if (source)
+            ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, false);
+    }
+
+    pa_alsa_profile_dump(p);
+
+    return 0;
+}
+
+static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) {
+    snd_pcm_t* pcm;
+    pa_sample_spec try_ss = ucm->core->default_sample_spec;
+    pa_channel_map try_map;
+    snd_pcm_uframes_t try_period_size, try_buffer_size;
+    bool exact_channels = m->channel_map.channels > 0;
+
+    if (exact_channels) {
+        try_map = m->channel_map;
+        try_ss.channels = try_map.channels;
+    } else
+        pa_channel_map_init_extend(&try_map, try_ss.channels, PA_CHANNEL_MAP_ALSA);
+
+    try_period_size =
+        pa_usec_to_bytes(ucm->core->default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
+        pa_frame_size(&try_ss);
+    try_buffer_size = ucm->core->default_n_fragments * try_period_size;
+
+    pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss,
+            &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels);
+
+    if (pcm && !exact_channels)
+        m->channel_map = try_map;
+
+    return pcm;
+}
+
+static void profile_finalize_probing(pa_alsa_profile *p) {
+    pa_alsa_mapping *m;
+    uint32_t idx;
+
+    PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
+        if (p->supported)
+            m->supported++;
+
+        if (!m->output_pcm)
+            continue;
+
+        snd_pcm_close(m->output_pcm);
+        m->output_pcm = NULL;
+    }
+
+    PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
+        if (p->supported)
+            m->supported++;
+
+        if (!m->input_pcm)
+            continue;
+
+        snd_pcm_close(m->input_pcm);
+        m->input_pcm = NULL;
+    }
+}
+
+static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
+    snd_pcm_t *pcm_handle;
+    snd_mixer_t *mixer_handle;
+    pa_alsa_ucm_mapping_context *context = &m->ucm_context;
+    pa_alsa_ucm_device *dev;
+    uint32_t idx;
+
+    pcm_handle = m->direction == PA_ALSA_DIRECTION_OUTPUT ? m->output_pcm : m->input_pcm;
+    mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
+    if (!mixer_handle)
+        return;
+
+    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
+        bool has_control;
+
+        has_control = pa_alsa_mixer_find(mixer_handle, dev->jack->alsa_name, 0) != NULL;
+        pa_alsa_jack_set_has_control(dev->jack, has_control);
+        pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
+    }
+
+    snd_mixer_close(mixer_handle);
+}
+
+static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
+    void *state;
+    pa_alsa_profile *p;
+    pa_alsa_mapping *m;
+    uint32_t idx;
+
+    PA_HASHMAP_FOREACH(p, ps->profiles, state) {
+        /* change verb */
+        pa_log_info("Set ucm verb to %s", p->name);
+
+        if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
+            pa_log("Failed to set verb %s", p->name);
+            p->supported = false;
+            continue;
+        }
+
+        PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
+            if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
+                /* Skip jack probing on modifier PCMs since we expect this to
+                 * only be controlled on the main device/verb PCM. */
+                continue;
+            }
+
+            m->output_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_PLAYBACK);
+            if (!m->output_pcm) {
+                p->supported = false;
+                break;
+            }
+        }
+
+        if (p->supported) {
+            PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
+                if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
+                    /* Skip jack probing on modifier PCMs since we expect this to
+                     * only be controlled on the main device/verb PCM. */
+                    continue;
+                }
+
+                m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE);
+                if (!m->input_pcm) {
+                    p->supported = false;
+                    break;
+                }
+            }
+        }
+
+        if (!p->supported) {
+            profile_finalize_probing(p);
+            continue;
+        }
+
+        pa_log_debug("Profile %s supported.", p->name);
+
+        PA_IDXSET_FOREACH(m, p->output_mappings, idx)
+            if (!PA_UCM_IS_MODIFIER_MAPPING(m))
+                ucm_mapping_jack_probe(m);
+
+        PA_IDXSET_FOREACH(m, p->input_mappings, idx)
+            if (!PA_UCM_IS_MODIFIER_MAPPING(m))
+                ucm_mapping_jack_probe(m);
+
+        profile_finalize_probing(p);
+    }
+
+    /* restore ucm state */
+    snd_use_case_set(ucm->ucm_mgr, "_verb", SND_USE_CASE_VERB_INACTIVE);
+
+    pa_alsa_profile_set_drop_unsupported(ps);
+}
+
+pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
+    pa_alsa_ucm_verb *verb;
+    pa_alsa_profile_set *ps;
+
+    ps = pa_xnew0(pa_alsa_profile_set, 1);
+    ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    /* create a profile for each verb */
+    PA_LLIST_FOREACH(verb, ucm->verbs) {
+        const char *verb_name;
+        const char *verb_desc;
+
+        verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
+        verb_desc = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
+        if (verb_name == NULL) {
+            pa_log("Verb with no name");
+            continue;
+        }
+
+        ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
+    }
+
+    ucm_probe_profile_set(ucm, ps);
+    ps->probed = true;
+
+    return ps;
+}
+
+static void free_verb(pa_alsa_ucm_verb *verb) {
+    pa_alsa_ucm_device *di, *dn;
+    pa_alsa_ucm_modifier *mi, *mn;
+
+    PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) {
+        PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di);
+
+        if (di->hw_mute_jacks)
+            pa_dynarray_free(di->hw_mute_jacks);
+
+        if (di->ucm_ports)
+            pa_dynarray_free(di->ucm_ports);
+
+        pa_proplist_free(di->proplist);
+        if (di->conflicting_devices)
+            pa_idxset_free(di->conflicting_devices, NULL);
+        if (di->supported_devices)
+            pa_idxset_free(di->supported_devices, NULL);
+        pa_xfree(di);
+    }
+
+    PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) {
+        PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi);
+        pa_proplist_free(mi->proplist);
+        if (mi->n_suppdev > 0)
+            snd_use_case_free_list(mi->supported_devices, mi->n_suppdev);
+        if (mi->n_confdev > 0)
+            snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev);
+        pa_xfree(mi->media_role);
+        pa_xfree(mi);
+    }
+    pa_proplist_free(verb->proplist);
+    pa_xfree(verb);
+}
+
+static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name) {
+    pa_alsa_ucm_device *device;
+
+    pa_assert(verb);
+    pa_assert(device_name);
+
+    PA_LLIST_FOREACH(device, verb->devices) {
+        const char *name;
+
+        name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
+        if (pa_streq(name, device_name))
+            return device;
+    }
+
+    return NULL;
+}
+
+void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
+    pa_alsa_ucm_verb *vi, *vn;
+    pa_alsa_jack *ji, *jn;
+
+    PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
+        PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
+        free_verb(vi);
+    }
+    PA_LLIST_FOREACH_SAFE(ji, jn, ucm->jacks) {
+        PA_LLIST_REMOVE(pa_alsa_jack, ucm->jacks, ji);
+        pa_alsa_jack_free(ji);
+    }
+    if (ucm->ucm_mgr) {
+        snd_use_case_mgr_close(ucm->ucm_mgr);
+        ucm->ucm_mgr = NULL;
+    }
+}
+
+void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
+    pa_alsa_ucm_device *dev;
+    pa_alsa_ucm_modifier *mod;
+    uint32_t idx;
+
+    if (context->ucm_devices) {
+        /* clear ucm device pointer to mapping */
+        PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
+            if (context->direction == PA_DIRECTION_OUTPUT)
+                dev->playback_mapping = NULL;
+            else
+                dev->capture_mapping = NULL;
+        }
+
+        pa_idxset_free(context->ucm_devices, NULL);
+    }
+
+    if (context->ucm_modifiers) {
+        PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
+            if (context->direction == PA_DIRECTION_OUTPUT)
+                mod->playback_mapping = NULL;
+            else
+                mod->capture_mapping = NULL;
+        }
+
+        pa_idxset_free(context->ucm_modifiers, NULL);
+    }
+}
+
+/* Enable the modifier when the first stream with matched role starts */
+void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
+    pa_alsa_ucm_modifier *mod;
+
+    if (!ucm->active_verb)
+        return;
+
+    PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
+        if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
+            if (mod->enabled_counter == 0) {
+                const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
+
+                pa_log_info("Enable ucm modifier %s", mod_name);
+                if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
+                    pa_log("Failed to enable ucm modifier %s", mod_name);
+                }
+            }
+
+            mod->enabled_counter++;
+            break;
+        }
+    }
+}
+
+/* Disable the modifier when the last stream with matched role ends */
+void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
+    pa_alsa_ucm_modifier *mod;
+
+    if (!ucm->active_verb)
+        return;
+
+    PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
+        if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
+
+            mod->enabled_counter--;
+            if (mod->enabled_counter == 0) {
+                const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
+
+                pa_log_info("Disable ucm modifier %s", mod_name);
+                if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
+                    pa_log("Failed to disable ucm modifier %s", mod_name);
+                }
+            }
+
+            break;
+        }
+    }
+}
+
+static void device_add_ucm_port(pa_alsa_ucm_device *device, struct ucm_port *port) {
+    pa_assert(device);
+    pa_assert(port);
+
+    pa_dynarray_append(device->ucm_ports, port);
+}
+
+static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
+    pa_assert(device);
+    pa_assert(jack);
+
+    device->jack = jack;
+    pa_alsa_jack_add_ucm_device(jack, device);
+
+    pa_alsa_ucm_device_update_available(device);
+}
+
+static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
+    pa_assert(device);
+    pa_assert(jack);
+
+    pa_dynarray_append(device->hw_mute_jacks, jack);
+    pa_alsa_jack_add_ucm_hw_mute_device(jack, device);
+
+    pa_alsa_ucm_device_update_available(device);
+}
+
+static void device_set_available(pa_alsa_ucm_device *device, pa_available_t available) {
+    struct ucm_port *port;
+    unsigned idx;
+
+    pa_assert(device);
+
+    if (available == device->available)
+        return;
+
+    device->available = available;
+
+    PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx)
+        ucm_port_update_available(port);
+}
+
+void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) {
+    pa_available_t available = PA_AVAILABLE_UNKNOWN;
+    pa_alsa_jack *jack;
+    unsigned idx;
+
+    pa_assert(device);
+
+    if (device->jack && device->jack->has_control)
+        available = device->jack->plugged_in ? PA_AVAILABLE_YES : PA_AVAILABLE_NO;
+
+    PA_DYNARRAY_FOREACH(jack, device->hw_mute_jacks, idx) {
+        if (jack->plugged_in) {
+            available = PA_AVAILABLE_NO;
+            break;
+        }
+    }
+
+    device_set_available(device, available);
+}
+
+static void ucm_port_init(struct ucm_port *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
+                          pa_alsa_ucm_device **devices, unsigned n_devices) {
+    unsigned i;
+
+    pa_assert(ucm);
+    pa_assert(core_port);
+    pa_assert(devices);
+
+    port->ucm = ucm;
+    port->core_port = core_port;
+    port->devices = pa_dynarray_new(NULL);
+
+    for (i = 0; i < n_devices; i++) {
+        pa_dynarray_append(port->devices, devices[i]);
+        device_add_ucm_port(devices[i], port);
+    }
+
+    ucm_port_update_available(port);
+}
+
+static void ucm_port_free(pa_device_port *port) {
+    struct ucm_port *ucm_port;
+
+    pa_assert(port);
+
+    ucm_port = PA_DEVICE_PORT_DATA(port);
+
+    if (ucm_port->devices)
+        pa_dynarray_free(ucm_port->devices);
+}
+
+static void ucm_port_update_available(struct ucm_port *port) {
+    pa_alsa_ucm_device *device;
+    unsigned idx;
+    pa_available_t available = PA_AVAILABLE_YES;
+
+    pa_assert(port);
+
+    PA_DYNARRAY_FOREACH(device, port->devices, idx) {
+        if (device->available == PA_AVAILABLE_UNKNOWN)
+            available = PA_AVAILABLE_UNKNOWN;
+        else if (device->available == PA_AVAILABLE_NO) {
+            available = PA_AVAILABLE_NO;
+            break;
+        }
+    }
+
+    pa_device_port_set_available(port->core_port, available);
+}
+
+#else /* HAVE_ALSA_UCM */
+
+/* Dummy functions for systems without UCM support */
+
+int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
+        pa_log_info("UCM not available.");
+        return -1;
+}
+
+pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
+    return NULL;
+}
+
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile) {
+    return -1;
+}
+
+int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb) {
+    return -1;
+}
+
+void pa_alsa_ucm_add_ports(
+        pa_hashmap **hash,
+        pa_proplist *proplist,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_card *card) {
+}
+
+void pa_alsa_ucm_add_ports_combination(
+        pa_hashmap *hash,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_hashmap *ports,
+        pa_card_profile *cp,
+        pa_core *core) {
+}
+
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
+    return -1;
+}
+
+void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
+}
+
+void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
+}
+
+void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
+}
+
+void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
+}
+
+#endif
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
new file mode 100644 (file)
index 0000000..53abf3f
--- /dev/null
@@ -0,0 +1,209 @@
+#ifndef fooalsaucmhfoo
+#define fooalsaucmhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2011 Wolfson Microelectronics PLC
+  Author Margarita Olaya <magi@slimlogic.co.uk>
+  Copyright 2012 Feng Wei <wei.feng@freescale.com>, Freescale Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_ALSA_UCM
+#include <use-case.h>
+#else
+typedef void snd_use_case_mgr_t;
+#endif
+
+#include "alsa-mixer.h"
+
+/** For devices: List of verbs, devices or modifiers available */
+#define PA_ALSA_PROP_UCM_NAME                       "alsa.ucm.name"
+
+/** For devices: List of supported devices per verb*/
+#define PA_ALSA_PROP_UCM_DESCRIPTION                "alsa.ucm.description"
+
+/** For devices: Playback device name e.g PlaybackPCM */
+#define PA_ALSA_PROP_UCM_SINK                       "alsa.ucm.sink"
+
+/** For devices: Capture device name e.g CapturePCM*/
+#define PA_ALSA_PROP_UCM_SOURCE                     "alsa.ucm.source"
+
+/** For devices: Playback roles */
+#define PA_ALSA_PROP_UCM_PLAYBACK_ROLES             "alsa.ucm.playback.roles"
+
+/** For devices: Playback control volume ID string. e.g PlaybackVolume */
+#define PA_ALSA_PROP_UCM_PLAYBACK_VOLUME            "alsa.ucm.playback.volume"
+
+/** For devices: Playback switch e.g PlaybackSwitch */
+#define PA_ALSA_PROP_UCM_PLAYBACK_SWITCH            "alsa.ucm.playback.switch"
+
+/** For devices: Playback priority */
+#define PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY          "alsa.ucm.playback.priority"
+
+/** For devices: Playback rate */
+#define PA_ALSA_PROP_UCM_PLAYBACK_RATE              "alsa.ucm.playback.rate"
+
+/** For devices: Playback channels */
+#define PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS          "alsa.ucm.playback.channels"
+
+/** For devices: Capture roles */
+#define PA_ALSA_PROP_UCM_CAPTURE_ROLES              "alsa.ucm.capture.roles"
+
+/** For devices: Capture controls volume ID string. e.g CaptureVolume */
+#define PA_ALSA_PROP_UCM_CAPTURE_VOLUME             "alsa.ucm.capture.volume"
+
+/** For devices: Capture switch e.g CaptureSwitch */
+#define PA_ALSA_PROP_UCM_CAPTURE_SWITCH             "alsa.ucm.capture.switch"
+
+/** For devices: Capture priority */
+#define PA_ALSA_PROP_UCM_CAPTURE_PRIORITY           "alsa.ucm.capture.priority"
+
+/** For devices: Capture rate */
+#define PA_ALSA_PROP_UCM_CAPTURE_RATE               "alsa.ucm.capture.rate"
+
+/** For devices: Capture channels */
+#define PA_ALSA_PROP_UCM_CAPTURE_CHANNELS           "alsa.ucm.capture.channels"
+
+/** For devices: Quality of Service */
+#define PA_ALSA_PROP_UCM_QOS                        "alsa.ucm.qos"
+
+/** For devices: The modifier (if any) that this device corresponds to */
+#define PA_ALSA_PROP_UCM_MODIFIER "alsa.ucm.modifier"
+
+/* Corresponds to the "JackControl" UCM value. */
+#define PA_ALSA_PROP_UCM_JACK_CONTROL               "alsa.ucm.jack_control"
+
+/* Corresponds to the "JackHWMute" UCM value. */
+#define PA_ALSA_PROP_UCM_JACK_HW_MUTE               "alsa.ucm.jack_hw_mute"
+
+typedef struct pa_alsa_ucm_verb pa_alsa_ucm_verb;
+typedef struct pa_alsa_ucm_modifier pa_alsa_ucm_modifier;
+typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
+typedef struct pa_alsa_ucm_config pa_alsa_ucm_config;
+typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context;
+
+int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index);
+pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map);
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile);
+
+int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb);
+
+void pa_alsa_ucm_add_ports(
+        pa_hashmap **hash,
+        pa_proplist *proplist,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_card *card);
+void pa_alsa_ucm_add_ports_combination(
+        pa_hashmap *hash,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_hashmap *ports,
+        pa_card_profile *cp,
+        pa_core *core);
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink);
+
+void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm);
+void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context);
+
+void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir);
+void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir);
+
+/* UCM - Use Case Manager is available on some audio cards */
+
+struct pa_alsa_ucm_device {
+    PA_LLIST_FIELDS(pa_alsa_ucm_device);
+
+    pa_proplist *proplist;
+
+    unsigned playback_priority;
+    unsigned capture_priority;
+
+    unsigned playback_rate;
+    unsigned capture_rate;
+
+    unsigned playback_channels;
+    unsigned capture_channels;
+
+    pa_alsa_mapping *playback_mapping;
+    pa_alsa_mapping *capture_mapping;
+
+    pa_idxset *conflicting_devices;
+    pa_idxset *supported_devices;
+
+    /* One device may be part of multiple ports, since each device has
+     * a dedicated port, and in addition to that we sometimes generate ports
+     * that represent combinations of devices. */
+    pa_dynarray *ucm_ports; /* struct ucm_port */
+
+    pa_alsa_jack *jack;
+    pa_dynarray *hw_mute_jacks; /* pa_alsa_jack */
+    pa_available_t available;
+};
+
+void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device);
+
+struct pa_alsa_ucm_modifier {
+    PA_LLIST_FIELDS(pa_alsa_ucm_modifier);
+
+    pa_proplist *proplist;
+
+    int n_confdev;
+    int n_suppdev;
+
+    const char **conflicting_devices;
+    const char **supported_devices;
+
+    pa_direction_t action_direction;
+
+    char *media_role;
+
+    /* Non-NULL if the modifier has its own PlaybackPCM/CapturePCM */
+    pa_alsa_mapping *playback_mapping;
+    pa_alsa_mapping *capture_mapping;
+
+    /* Count how many role matched streams are running */
+    int enabled_counter;
+};
+
+struct pa_alsa_ucm_verb {
+    PA_LLIST_FIELDS(pa_alsa_ucm_verb);
+
+    pa_proplist *proplist;
+
+    PA_LLIST_HEAD(pa_alsa_ucm_device, devices);
+    PA_LLIST_HEAD(pa_alsa_ucm_modifier, modifiers);
+};
+
+struct pa_alsa_ucm_config {
+    pa_core *core;
+    snd_use_case_mgr_t *ucm_mgr;
+    pa_alsa_ucm_verb *active_verb;
+
+    PA_LLIST_HEAD(pa_alsa_ucm_verb, verbs);
+    PA_LLIST_HEAD(pa_alsa_jack, jacks);
+};
+
+struct pa_alsa_ucm_mapping_context {
+    pa_alsa_ucm_config *ucm;
+    pa_direction_t direction;
+
+    pa_idxset *ucm_devices;
+    pa_idxset *ucm_modifiers;
+};
+
+#endif
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
new file mode 100644 (file)
index 0000000..61fb490
--- /dev/null
@@ -0,0 +1,1723 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2009 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <asoundlib.h>
+
+#include <pulse/sample.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/conf-parser.h>
+#include <pulsecore/core-rtclock.h>
+
+#include "alsa-util.h"
+#include "alsa-mixer.h"
+
+#ifdef HAVE_UDEV
+#include "udev-util.h"
+#endif
+
+static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_sample_format_t *f) {
+
+    static const snd_pcm_format_t format_trans[] = {
+        [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8,
+        [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
+        [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
+        [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
+        [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
+        [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
+        [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
+        [PA_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
+        [PA_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
+        [PA_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE,
+        [PA_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE,
+        [PA_SAMPLE_S24_32LE] = SND_PCM_FORMAT_S24_LE,
+        [PA_SAMPLE_S24_32BE] = SND_PCM_FORMAT_S24_BE,
+    };
+
+    static const pa_sample_format_t try_order[] = {
+        PA_SAMPLE_FLOAT32NE,
+        PA_SAMPLE_FLOAT32RE,
+        PA_SAMPLE_S32NE,
+        PA_SAMPLE_S32RE,
+        PA_SAMPLE_S24_32NE,
+        PA_SAMPLE_S24_32RE,
+        PA_SAMPLE_S24NE,
+        PA_SAMPLE_S24RE,
+        PA_SAMPLE_S16NE,
+        PA_SAMPLE_S16RE,
+        PA_SAMPLE_ALAW,
+        PA_SAMPLE_ULAW,
+        PA_SAMPLE_U8
+    };
+
+    unsigned i;
+    int ret;
+
+    pa_assert(pcm_handle);
+    pa_assert(hwparams);
+    pa_assert(f);
+
+    if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
+        return ret;
+
+    pa_log_debug("snd_pcm_hw_params_set_format(%s) failed: %s",
+                 snd_pcm_format_description(format_trans[*f]),
+                 pa_alsa_strerror(ret));
+
+    if (*f == PA_SAMPLE_FLOAT32BE)
+        *f = PA_SAMPLE_FLOAT32LE;
+    else if (*f == PA_SAMPLE_FLOAT32LE)
+        *f = PA_SAMPLE_FLOAT32BE;
+    else if (*f == PA_SAMPLE_S24BE)
+        *f = PA_SAMPLE_S24LE;
+    else if (*f == PA_SAMPLE_S24LE)
+        *f = PA_SAMPLE_S24BE;
+    else if (*f == PA_SAMPLE_S24_32BE)
+        *f = PA_SAMPLE_S24_32LE;
+    else if (*f == PA_SAMPLE_S24_32LE)
+        *f = PA_SAMPLE_S24_32BE;
+    else if (*f == PA_SAMPLE_S16BE)
+        *f = PA_SAMPLE_S16LE;
+    else if (*f == PA_SAMPLE_S16LE)
+        *f = PA_SAMPLE_S16BE;
+    else if (*f == PA_SAMPLE_S32BE)
+        *f = PA_SAMPLE_S32LE;
+    else if (*f == PA_SAMPLE_S32LE)
+        *f = PA_SAMPLE_S32BE;
+    else
+        goto try_auto;
+
+    if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
+        return ret;
+
+    pa_log_debug("snd_pcm_hw_params_set_format(%s) failed: %s",
+                 snd_pcm_format_description(format_trans[*f]),
+                 pa_alsa_strerror(ret));
+
+try_auto:
+
+    for (i = 0; i < PA_ELEMENTSOF(try_order); i++) {
+        *f = try_order[i];
+
+        if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
+            return ret;
+
+        pa_log_debug("snd_pcm_hw_params_set_format(%s) failed: %s",
+                     snd_pcm_format_description(format_trans[*f]),
+                     pa_alsa_strerror(ret));
+    }
+
+    return -1;
+}
+
+static int set_period_size(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, snd_pcm_uframes_t size) {
+    snd_pcm_uframes_t s;
+    int d, ret;
+
+    pa_assert(pcm_handle);
+    pa_assert(hwparams);
+
+    s = size;
+    d = 0;
+    if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d) < 0) {
+        s = size;
+        d = -1;
+        if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d) < 0) {
+            s = size;
+            d = 1;
+            if ((ret = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d)) < 0) {
+                pa_log_info("snd_pcm_hw_params_set_period_size_near() failed: %s", pa_alsa_strerror(ret));
+                return ret;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int set_buffer_size(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, snd_pcm_uframes_t size) {
+    int ret;
+
+    pa_assert(pcm_handle);
+    pa_assert(hwparams);
+
+    if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &size)) < 0) {
+        pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret));
+        return ret;
+    }
+
+    return 0;
+}
+
+static void check_access(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, bool use_mmap) {
+    if ((use_mmap && !snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) ||
+        !snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED))
+        pa_log_error("Weird, PCM claims to support interleaved access, but snd_pcm_hw_params_set_access() failed.");
+
+    if ((use_mmap && !snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) ||
+        !snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_NONINTERLEAVED))
+        pa_log_debug("PCM seems to support non-interleaved access, but PA doesn't.");
+    else if (use_mmap && !snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_COMPLEX)) {
+        pa_log_debug("PCM seems to support mmapped complex access, but PA doesn't.");
+    }
+}
+
+/* Set the hardware parameters of the given ALSA device. Returns the
+ * selected fragment settings in *buffer_size and *period_size. Determine
+ * whether mmap and tsched mode can be enabled. */
+int pa_alsa_set_hw_params(
+        snd_pcm_t *pcm_handle,
+        pa_sample_spec *ss,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t *buffer_size,
+        snd_pcm_uframes_t tsched_size,
+        bool *use_mmap,
+        bool *use_tsched,
+        bool require_exact_channel_number) {
+
+    int ret = -1;
+    snd_pcm_hw_params_t *hwparams, *hwparams_copy;
+    int dir;
+    snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
+    snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0;
+    bool _use_mmap = use_mmap && *use_mmap;
+    bool _use_tsched = use_tsched && *use_tsched;
+    pa_sample_spec _ss = *ss;
+
+    pa_assert(pcm_handle);
+    pa_assert(ss);
+
+    snd_pcm_hw_params_alloca(&hwparams);
+    snd_pcm_hw_params_alloca(&hwparams_copy);
+
+    if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
+        pa_log_debug("snd_pcm_hw_params_any() failed: %s", pa_alsa_strerror(ret));
+        goto finish;
+    }
+
+    if ((ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
+        pa_log_debug("snd_pcm_hw_params_set_rate_resample() failed: %s", pa_alsa_strerror(ret));
+        goto finish;
+    }
+
+    if (_use_mmap) {
+
+        if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
+
+            /* mmap() didn't work, fall back to interleaved */
+
+            if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+                pa_log_debug("snd_pcm_hw_params_set_access() failed: %s", pa_alsa_strerror(ret));
+                check_access(pcm_handle, hwparams, true);
+                goto finish;
+            }
+
+            _use_mmap = false;
+        }
+
+    } else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+        pa_log_debug("snd_pcm_hw_params_set_access() failed: %s", pa_alsa_strerror(ret));
+        check_access(pcm_handle, hwparams, false);
+        goto finish;
+    }
+
+    if (!_use_mmap)
+        _use_tsched = false;
+
+    if (!pa_alsa_pcm_is_hw(pcm_handle))
+        _use_tsched = false;
+
+    /* The PCM pointer is only updated with period granularity */
+    if (snd_pcm_hw_params_is_batch(hwparams)) {
+        bool is_usb = false;
+        const char *id;
+        snd_pcm_info_t* pcm_info;
+        snd_pcm_info_alloca(&pcm_info);
+
+        if (snd_pcm_info(pcm_handle, pcm_info) == 0 &&
+            (id = snd_pcm_info_get_id(pcm_info))) {
+            /* This horrible hack makes sure we don't disable tsched on USB
+             * devices, which have a low enough transfer size for timer-based
+             * scheduling to work. This can go away when the ALSA API supprots
+             * querying the block transfer size. */
+            if (pa_streq(id, "USB Audio"))
+                is_usb = true;
+        }
+
+        if (!is_usb) {
+            pa_log_info("Disabling tsched mode since BATCH flag is set");
+            _use_tsched = false;
+        }
+    }
+
+#if (SND_LIB_VERSION >= ((1<<16)|(0<<8)|24)) /* API additions in 1.0.24 */
+    if (_use_tsched) {
+
+        /* try to disable period wakeups if hardware can do so */
+        if (snd_pcm_hw_params_can_disable_period_wakeup(hwparams)) {
+
+            if ((ret = snd_pcm_hw_params_set_period_wakeup(pcm_handle, hwparams, false)) < 0)
+                /* don't bail, keep going with default mode with period wakeups */
+                pa_log_debug("snd_pcm_hw_params_set_period_wakeup() failed: %s", pa_alsa_strerror(ret));
+            else
+                pa_log_info("Trying to disable ALSA period wakeups, using timers only");
+        } else
+            pa_log_info("Cannot disable ALSA period wakeups");
+    }
+#endif
+
+    if ((ret = set_format(pcm_handle, hwparams, &_ss.format)) < 0)
+        goto finish;
+
+    if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &_ss.rate, NULL)) < 0) {
+        pa_log_debug("snd_pcm_hw_params_set_rate_near() failed: %s", pa_alsa_strerror(ret));
+        goto finish;
+    }
+
+    /* We ignore very small sampling rate deviations */
+    if (_ss.rate >= ss->rate*.95 && _ss.rate <= ss->rate*1.05)
+        _ss.rate = ss->rate;
+
+    if (require_exact_channel_number) {
+        if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, _ss.channels)) < 0) {
+            pa_log_debug("snd_pcm_hw_params_set_channels(%u) failed: %s", _ss.channels, pa_alsa_strerror(ret));
+            goto finish;
+        }
+    } else {
+        unsigned int c = _ss.channels;
+
+        if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0) {
+            pa_log_debug("snd_pcm_hw_params_set_channels_near(%u) failed: %s", _ss.channels, pa_alsa_strerror(ret));
+            goto finish;
+        }
+
+        _ss.channels = c;
+    }
+
+    if (_use_tsched && tsched_size > 0) {
+        _buffer_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * _ss.rate) / ss->rate);
+        _period_size = _buffer_size;
+    } else {
+        _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * _ss.rate) / ss->rate);
+        _buffer_size = (snd_pcm_uframes_t) (((uint64_t) _buffer_size * _ss.rate) / ss->rate);
+    }
+
+    if (_buffer_size > 0 || _period_size > 0) {
+        snd_pcm_uframes_t max_frames = 0;
+
+        if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &max_frames)) < 0)
+            pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret));
+        else
+            pa_log_debug("Maximum hw buffer size is %lu ms", (long unsigned) (max_frames * PA_MSEC_PER_SEC / _ss.rate));
+
+        /* Some ALSA drivers really don't like if we set the buffer
+         * size first and the number of periods second (which would
+         * make a lot more sense to me). So, try a few combinations
+         * before we give up. */
+
+        if (_buffer_size > 0 && _period_size > 0) {
+            snd_pcm_hw_params_copy(hwparams_copy, hwparams);
+
+            /* First try: set buffer size first, followed by period size */
+            if (set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 &&
+                set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 &&
+                snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) {
+                pa_log_debug("Set buffer size first (to %lu samples), period size second (to %lu samples).", (unsigned long) _buffer_size, (unsigned long) _period_size);
+                goto success;
+            }
+
+            snd_pcm_hw_params_copy(hwparams_copy, hwparams);
+            /* Second try: set period size first, followed by buffer size */
+            if (set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 &&
+                set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 &&
+                snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) {
+                pa_log_debug("Set period size first (to %lu samples), buffer size second (to %lu samples).", (unsigned long) _period_size, (unsigned long) _buffer_size);
+                goto success;
+            }
+        }
+
+        if (_buffer_size > 0) {
+            snd_pcm_hw_params_copy(hwparams_copy, hwparams);
+
+            /* Third try: set only buffer size */
+            if (set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 &&
+                snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) {
+                pa_log_debug("Set only buffer size (to %lu samples).", (unsigned long) _buffer_size);
+                goto success;
+            }
+        }
+
+        if (_period_size > 0) {
+            snd_pcm_hw_params_copy(hwparams_copy, hwparams);
+
+            /* Fourth try: set only period size */
+            if (set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 &&
+                snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) {
+                pa_log_debug("Set only period size (to %lu samples).", (unsigned long) _period_size);
+                goto success;
+            }
+        }
+    }
+
+    pa_log_debug("Set neither period nor buffer size.");
+
+    /* Last chance, set nothing */
+    if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
+        pa_log_info("snd_pcm_hw_params failed: %s", pa_alsa_strerror(ret));
+        goto finish;
+    }
+
+success:
+
+    if (ss->rate != _ss.rate)
+        pa_log_info("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, _ss.rate);
+
+    if (ss->channels != _ss.channels)
+        pa_log_info("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, _ss.channels);
+
+    if (ss->format != _ss.format)
+        pa_log_info("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(_ss.format));
+
+    if ((ret = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
+        pa_log_info("snd_pcm_hw_params_current() failed: %s", pa_alsa_strerror(ret));
+        goto finish;
+    }
+
+    if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
+        (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) {
+        pa_log_info("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", pa_alsa_strerror(ret));
+        goto finish;
+    }
+
+#if (SND_LIB_VERSION >= ((1<<16)|(0<<8)|24)) /* API additions in 1.0.24 */
+    if (_use_tsched) {
+        unsigned int no_wakeup;
+        /* see if period wakeups were disabled */
+        snd_pcm_hw_params_get_period_wakeup(pcm_handle, hwparams, &no_wakeup);
+        if (no_wakeup == 0)
+            pa_log_info("ALSA period wakeups disabled");
+        else
+            pa_log_info("ALSA period wakeups were not disabled");
+    }
+#endif
+
+    ss->rate = _ss.rate;
+    ss->channels = _ss.channels;
+    ss->format = _ss.format;
+
+    pa_assert(_period_size > 0);
+    pa_assert(_buffer_size > 0);
+
+    if (buffer_size)
+        *buffer_size = _buffer_size;
+
+    if (period_size)
+        *period_size = _period_size;
+
+    if (use_mmap)
+        *use_mmap = _use_mmap;
+
+    if (use_tsched)
+        *use_tsched = _use_tsched;
+
+    ret = 0;
+
+finish:
+
+    return ret;
+}
+
+int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, bool period_event) {
+    snd_pcm_sw_params_t *swparams;
+    snd_pcm_uframes_t boundary;
+    int err;
+
+    pa_assert(pcm);
+
+    snd_pcm_sw_params_alloca(&swparams);
+
+    if ((err = snd_pcm_sw_params_current(pcm, swparams)) < 0) {
+        pa_log_warn("Unable to determine current swparams: %s", pa_alsa_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) {
+        pa_log_warn("Unable to disable period event: %s", pa_alsa_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
+        pa_log_warn("Unable to enable time stamping: %s", pa_alsa_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) {
+        pa_log_warn("Unable to get boundary: %s", pa_alsa_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) {
+        pa_log_warn("Unable to set stop threshold: %s", pa_alsa_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) {
+        pa_log_warn("Unable to set start threshold: %s", pa_alsa_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
+        pa_log_error("snd_pcm_sw_params_set_avail_min() failed: %s", pa_alsa_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
+        pa_log_warn("Unable to set sw params: %s", pa_alsa_strerror(err));
+        return err;
+    }
+
+    return 0;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_id_auto(
+        const char *dev_id,
+        char **dev,
+        pa_sample_spec *ss,
+        pa_channel_map* map,
+        int mode,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t *buffer_size,
+        snd_pcm_uframes_t tsched_size,
+        bool *use_mmap,
+        bool *use_tsched,
+        pa_alsa_profile_set *ps,
+        pa_alsa_mapping **mapping) {
+
+    char *d;
+    snd_pcm_t *pcm_handle;
+    void *state;
+    pa_alsa_mapping *m;
+
+    pa_assert(dev_id);
+    pa_assert(dev);
+    pa_assert(ss);
+    pa_assert(map);
+    pa_assert(ps);
+
+    /* First we try to find a device string with a superset of the
+     * requested channel map. We iterate through our device table from
+     * top to bottom and take the first that matches. If we didn't
+     * find a working device that way, we iterate backwards, and check
+     * all devices that do not provide a superset of the requested
+     * channel map.*/
+
+    PA_HASHMAP_FOREACH(m, ps->mappings, state) {
+        if (!pa_channel_map_superset(&m->channel_map, map))
+            continue;
+
+        pa_log_debug("Checking for superset %s (%s)", m->name, m->device_strings[0]);
+
+        pcm_handle = pa_alsa_open_by_device_id_mapping(
+                dev_id,
+                dev,
+                ss,
+                map,
+                mode,
+                period_size,
+                buffer_size,
+                tsched_size,
+                use_mmap,
+                use_tsched,
+                m);
+
+        if (pcm_handle) {
+            if (mapping)
+                *mapping = m;
+
+            return pcm_handle;
+        }
+    }
+
+    PA_HASHMAP_FOREACH_BACKWARDS(m, ps->mappings, state) {
+        if (pa_channel_map_superset(&m->channel_map, map))
+            continue;
+
+        pa_log_debug("Checking for subset %s (%s)", m->name, m->device_strings[0]);
+
+        pcm_handle = pa_alsa_open_by_device_id_mapping(
+                dev_id,
+                dev,
+                ss,
+                map,
+                mode,
+                period_size,
+                buffer_size,
+                tsched_size,
+                use_mmap,
+                use_tsched,
+                m);
+
+        if (pcm_handle) {
+            if (mapping)
+                *mapping = m;
+
+            return pcm_handle;
+        }
+    }
+
+    /* OK, we didn't find any good device, so let's try the raw hw: stuff */
+    d = pa_sprintf_malloc("hw:%s", dev_id);
+    pa_log_debug("Trying %s as last resort...", d);
+    pcm_handle = pa_alsa_open_by_device_string(
+            d,
+            dev,
+            ss,
+            map,
+            mode,
+            period_size,
+            buffer_size,
+            tsched_size,
+            use_mmap,
+            use_tsched,
+            false);
+    pa_xfree(d);
+
+    if (pcm_handle && mapping)
+        *mapping = NULL;
+
+    return pcm_handle;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_id_mapping(
+        const char *dev_id,
+        char **dev,
+        pa_sample_spec *ss,
+        pa_channel_map* map,
+        int mode,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t *buffer_size,
+        snd_pcm_uframes_t tsched_size,
+        bool *use_mmap,
+        bool *use_tsched,
+        pa_alsa_mapping *m) {
+
+    snd_pcm_t *pcm_handle;
+    pa_sample_spec try_ss;
+    pa_channel_map try_map;
+
+    pa_assert(dev_id);
+    pa_assert(dev);
+    pa_assert(ss);
+    pa_assert(map);
+    pa_assert(m);
+
+    try_ss.channels = m->channel_map.channels;
+    try_ss.rate = ss->rate;
+    try_ss.format = ss->format;
+    try_map = m->channel_map;
+
+    pcm_handle = pa_alsa_open_by_template(
+            m->device_strings,
+            dev_id,
+            dev,
+            &try_ss,
+            &try_map,
+            mode,
+            period_size,
+            buffer_size,
+            tsched_size,
+            use_mmap,
+            use_tsched,
+            pa_channel_map_valid(&m->channel_map) /* Query the channel count if we don't know what we want */);
+
+    if (!pcm_handle)
+        return NULL;
+
+    *ss = try_ss;
+    *map = try_map;
+    pa_assert(map->channels == ss->channels);
+
+    return pcm_handle;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_string(
+        const char *device,
+        char **dev,
+        pa_sample_spec *ss,
+        pa_channel_map* map,
+        int mode,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t *buffer_size,
+        snd_pcm_uframes_t tsched_size,
+        bool *use_mmap,
+        bool *use_tsched,
+        bool require_exact_channel_number) {
+
+    int err;
+    char *d;
+    snd_pcm_t *pcm_handle;
+    bool reformat = false;
+
+    pa_assert(device);
+    pa_assert(ss);
+    pa_assert(map);
+
+    d = pa_xstrdup(device);
+
+    for (;;) {
+        pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with");
+
+        if ((err = snd_pcm_open(&pcm_handle, d, mode,
+                                SND_PCM_NONBLOCK|
+                                SND_PCM_NO_AUTO_RESAMPLE|
+                                SND_PCM_NO_AUTO_CHANNELS|
+                                (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
+            pa_log_info("Error opening PCM device %s: %s", d, pa_alsa_strerror(err));
+            goto fail;
+        }
+
+        pa_log_debug("Managed to open %s", d);
+
+        if ((err = pa_alsa_set_hw_params(
+                     pcm_handle,
+                     ss,
+                     period_size,
+                     buffer_size,
+                     tsched_size,
+                     use_mmap,
+                     use_tsched,
+                     require_exact_channel_number)) < 0) {
+
+            if (!reformat) {
+                reformat = true;
+
+                snd_pcm_close(pcm_handle);
+                continue;
+            }
+
+            /* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */
+            if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) {
+                char *t;
+
+                t = pa_sprintf_malloc("plug:%s", d);
+                pa_xfree(d);
+                d = t;
+
+                reformat = false;
+
+                snd_pcm_close(pcm_handle);
+                continue;
+            }
+
+            pa_log_info("Failed to set hardware parameters on %s: %s", d, pa_alsa_strerror(err));
+            snd_pcm_close(pcm_handle);
+
+            goto fail;
+        }
+
+        if (ss->channels > PA_CHANNELS_MAX) {
+            pa_log("Device %s has %u channels, but PulseAudio supports only %u channels. Unable to use the device.",
+                   d, ss->channels, PA_CHANNELS_MAX);
+            snd_pcm_close(pcm_handle);
+            goto fail;
+        }
+
+        if (dev)
+            *dev = d;
+        else
+            pa_xfree(d);
+
+        if (ss->channels != map->channels)
+            pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_ALSA);
+
+        return pcm_handle;
+    }
+
+fail:
+    pa_xfree(d);
+
+    return NULL;
+}
+
+snd_pcm_t *pa_alsa_open_by_template(
+        char **template,
+        const char *dev_id,
+        char **dev,
+        pa_sample_spec *ss,
+        pa_channel_map* map,
+        int mode,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t *buffer_size,
+        snd_pcm_uframes_t tsched_size,
+        bool *use_mmap,
+        bool *use_tsched,
+        bool require_exact_channel_number) {
+
+    snd_pcm_t *pcm_handle;
+    char **i;
+
+    for (i = template; *i; i++) {
+        char *d;
+
+        d = pa_replace(*i, "%f", dev_id);
+
+        pcm_handle = pa_alsa_open_by_device_string(
+                d,
+                dev,
+                ss,
+                map,
+                mode,
+                period_size,
+                buffer_size,
+                tsched_size,
+                use_mmap,
+                use_tsched,
+                require_exact_channel_number);
+
+        pa_xfree(d);
+
+        if (pcm_handle)
+            return pcm_handle;
+    }
+
+    return NULL;
+}
+
+void pa_alsa_dump(pa_log_level_t level, snd_pcm_t *pcm) {
+    int err;
+    snd_output_t *out;
+
+    pa_assert(pcm);
+
+    pa_assert_se(snd_output_buffer_open(&out) == 0);
+
+    if ((err = snd_pcm_dump(pcm, out)) < 0)
+        pa_logl(level, "snd_pcm_dump(): %s", pa_alsa_strerror(err));
+    else {
+        char *s = NULL;
+        snd_output_buffer_string(out, &s);
+        pa_logl(level, "snd_pcm_dump():\n%s", pa_strnull(s));
+    }
+
+    pa_assert_se(snd_output_close(out) == 0);
+}
+
+void pa_alsa_dump_status(snd_pcm_t *pcm) {
+    int err;
+    snd_output_t *out;
+    snd_pcm_status_t *status;
+    char *s = NULL;
+
+    pa_assert(pcm);
+
+    snd_pcm_status_alloca(&status);
+
+    if ((err = snd_output_buffer_open(&out)) < 0) {
+        pa_log_debug("snd_output_buffer_open() failed: %s", pa_cstrerror(err));
+        return;
+    }
+
+    if ((err = snd_pcm_status(pcm, status)) < 0) {
+        pa_log_debug("snd_pcm_status() failed: %s", pa_cstrerror(err));
+        goto finish;
+    }
+
+    if ((err = snd_pcm_status_dump(status, out)) < 0) {
+        pa_log_debug("snd_pcm_status_dump(): %s", pa_alsa_strerror(err));
+        goto finish;
+    }
+
+    snd_output_buffer_string(out, &s);
+    pa_log_debug("snd_pcm_status_dump():\n%s", pa_strnull(s));
+
+finish:
+
+    snd_output_close(out);
+}
+
+static void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt,...) {
+    va_list ap;
+    char *alsa_file;
+
+    alsa_file = pa_sprintf_malloc("(alsa-lib)%s", file);
+
+    va_start(ap, fmt);
+
+    pa_log_levelv_meta(PA_LOG_INFO, alsa_file, line, function, fmt, ap);
+
+    va_end(ap);
+
+    pa_xfree(alsa_file);
+}
+
+static pa_atomic_t n_error_handler_installed = PA_ATOMIC_INIT(0);
+
+void pa_alsa_refcnt_inc(void) {
+    /* This is not really thread safe, but we do our best */
+
+    if (pa_atomic_inc(&n_error_handler_installed) == 0)
+        snd_lib_error_set_handler(alsa_error_handler);
+}
+
+void pa_alsa_refcnt_dec(void) {
+    int r;
+
+    pa_assert_se((r = pa_atomic_dec(&n_error_handler_installed)) >= 1);
+
+    if (r == 1) {
+        snd_lib_error_set_handler(NULL);
+        snd_config_update_free_global();
+    }
+}
+
+bool pa_alsa_init_description(pa_proplist *p, pa_card *card) {
+    const char *d, *k;
+    pa_assert(p);
+
+    if (pa_device_init_description(p, card))
+        return true;
+
+    if (!(d = pa_proplist_gets(p, "alsa.card_name")))
+        d = pa_proplist_gets(p, "alsa.name");
+
+    if (!d)
+        return false;
+
+    k = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_DESCRIPTION);
+
+    if (d && k)
+        pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s %s", d, k);
+    else if (d)
+        pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, d);
+
+    return false;
+}
+
+void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card) {
+    char *cn, *lcn, *dn;
+
+    pa_assert(p);
+    pa_assert(card >= 0);
+
+    pa_proplist_setf(p, "alsa.card", "%i", card);
+
+    if (snd_card_get_name(card, &cn) >= 0) {
+        pa_proplist_sets(p, "alsa.card_name", pa_strip(cn));
+        free(cn);
+    }
+
+    if (snd_card_get_longname(card, &lcn) >= 0) {
+        pa_proplist_sets(p, "alsa.long_card_name", pa_strip(lcn));
+        free(lcn);
+    }
+
+    if ((dn = pa_alsa_get_driver_name(card))) {
+        pa_proplist_sets(p, "alsa.driver_name", dn);
+        pa_xfree(dn);
+    }
+
+#ifdef HAVE_UDEV
+    pa_udev_get_info(card, p);
+#endif
+}
+
+void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info) {
+
+    static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = {
+        [SND_PCM_CLASS_GENERIC] = "generic",
+        [SND_PCM_CLASS_MULTI] = "multi",
+        [SND_PCM_CLASS_MODEM] = "modem",
+        [SND_PCM_CLASS_DIGITIZER] = "digitizer"
+    };
+    static const char * const class_table[SND_PCM_CLASS_LAST+1] = {
+        [SND_PCM_CLASS_GENERIC] = "sound",
+        [SND_PCM_CLASS_MULTI] = NULL,
+        [SND_PCM_CLASS_MODEM] = "modem",
+        [SND_PCM_CLASS_DIGITIZER] = NULL
+    };
+    static const char * const alsa_subclass_table[SND_PCM_SUBCLASS_LAST+1] = {
+        [SND_PCM_SUBCLASS_GENERIC_MIX] = "generic-mix",
+        [SND_PCM_SUBCLASS_MULTI_MIX] = "multi-mix"
+    };
+
+    snd_pcm_class_t class;
+    snd_pcm_subclass_t subclass;
+    const char *n, *id, *sdn;
+    int card;
+
+    pa_assert(p);
+    pa_assert(pcm_info);
+
+    pa_proplist_sets(p, PA_PROP_DEVICE_API, "alsa");
+
+    if ((class = snd_pcm_info_get_class(pcm_info)) <= SND_PCM_CLASS_LAST) {
+        if (class_table[class])
+            pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, class_table[class]);
+        if (alsa_class_table[class])
+            pa_proplist_sets(p, "alsa.class", alsa_class_table[class]);
+    }
+
+    if ((subclass = snd_pcm_info_get_subclass(pcm_info)) <= SND_PCM_SUBCLASS_LAST)
+        if (alsa_subclass_table[subclass])
+            pa_proplist_sets(p, "alsa.subclass", alsa_subclass_table[subclass]);
+
+    if ((n = snd_pcm_info_get_name(pcm_info))) {
+        char *t = pa_xstrdup(n);
+        pa_proplist_sets(p, "alsa.name", pa_strip(t));
+        pa_xfree(t);
+    }
+
+    if ((id = snd_pcm_info_get_id(pcm_info)))
+        pa_proplist_sets(p, "alsa.id", id);
+
+    pa_proplist_setf(p, "alsa.subdevice", "%u", snd_pcm_info_get_subdevice(pcm_info));
+    if ((sdn = snd_pcm_info_get_subdevice_name(pcm_info)))
+        pa_proplist_sets(p, "alsa.subdevice_name", sdn);
+
+    pa_proplist_setf(p, "alsa.device", "%u", snd_pcm_info_get_device(pcm_info));
+
+    if ((card = snd_pcm_info_get_card(pcm_info)) >= 0)
+        pa_alsa_init_proplist_card(c, p, card);
+}
+
+void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm) {
+    snd_pcm_hw_params_t *hwparams;
+    snd_pcm_info_t *info;
+    int bits, err;
+
+    snd_pcm_hw_params_alloca(&hwparams);
+    snd_pcm_info_alloca(&info);
+
+    if ((err = snd_pcm_hw_params_current(pcm, hwparams)) < 0)
+        pa_log_warn("Error fetching hardware parameter info: %s", pa_alsa_strerror(err));
+    else {
+
+        if ((bits = snd_pcm_hw_params_get_sbits(hwparams)) >= 0)
+            pa_proplist_setf(p, "alsa.resolution_bits", "%i", bits);
+    }
+
+    if ((err = snd_pcm_info(pcm, info)) < 0)
+        pa_log_warn("Error fetching PCM info: %s", pa_alsa_strerror(err));
+    else
+        pa_alsa_init_proplist_pcm_info(c, p, info);
+}
+
+void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name) {
+    int err;
+    snd_ctl_t *ctl;
+    snd_ctl_card_info_t *info;
+    const char *t;
+
+    pa_assert(p);
+
+    snd_ctl_card_info_alloca(&info);
+
+    if ((err = snd_ctl_open(&ctl, name, 0)) < 0) {
+        pa_log_warn("Error opening low-level control device '%s': %s", name, snd_strerror(err));
+        return;
+    }
+
+    if ((err = snd_ctl_card_info(ctl, info)) < 0) {
+        pa_log_warn("Control device %s card info: %s", name, snd_strerror(err));
+        snd_ctl_close(ctl);
+        return;
+    }
+
+    if ((t = snd_ctl_card_info_get_mixername(info)) && *t)
+        pa_proplist_sets(p, "alsa.mixer_name", t);
+
+    if ((t = snd_ctl_card_info_get_components(info)) && *t)
+        pa_proplist_sets(p, "alsa.components", t);
+
+    snd_ctl_close(ctl);
+}
+
+int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
+    snd_pcm_state_t state;
+    int err;
+
+    pa_assert(pcm);
+
+    if (revents & POLLERR)
+        pa_log_debug("Got POLLERR from ALSA");
+    if (revents & POLLNVAL)
+        pa_log_warn("Got POLLNVAL from ALSA");
+    if (revents & POLLHUP)
+        pa_log_warn("Got POLLHUP from ALSA");
+    if (revents & POLLPRI)
+        pa_log_warn("Got POLLPRI from ALSA");
+    if (revents & POLLIN)
+        pa_log_debug("Got POLLIN from ALSA");
+    if (revents & POLLOUT)
+        pa_log_debug("Got POLLOUT from ALSA");
+
+    state = snd_pcm_state(pcm);
+    pa_log_debug("PCM state is %s", snd_pcm_state_name(state));
+
+    /* Try to recover from this error */
+
+    switch (state) {
+
+        case SND_PCM_STATE_XRUN:
+            if ((err = snd_pcm_recover(pcm, -EPIPE, 1)) != 0) {
+                pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", pa_alsa_strerror(err));
+                return -1;
+            }
+            break;
+
+        case SND_PCM_STATE_SUSPENDED:
+            if ((err = snd_pcm_recover(pcm, -ESTRPIPE, 1)) != 0) {
+                pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", pa_alsa_strerror(err));
+                return -1;
+            }
+            break;
+
+        default:
+
+            snd_pcm_drop(pcm);
+
+            if ((err = snd_pcm_prepare(pcm)) < 0) {
+                pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", pa_alsa_strerror(err));
+                return -1;
+            }
+            break;
+    }
+
+    return 0;
+}
+
+pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) {
+    int n, err;
+    struct pollfd *pollfd;
+    pa_rtpoll_item *item;
+
+    pa_assert(pcm);
+
+    if ((n = snd_pcm_poll_descriptors_count(pcm)) < 0) {
+        pa_log("snd_pcm_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
+        return NULL;
+    }
+
+    item = pa_rtpoll_item_new(rtpoll, PA_RTPOLL_NEVER, (unsigned) n);
+    pollfd = pa_rtpoll_item_get_pollfd(item, NULL);
+
+    if ((err = snd_pcm_poll_descriptors(pcm, pollfd, (unsigned) n)) < 0) {
+        pa_log("snd_pcm_poll_descriptors() failed: %s", pa_alsa_strerror(err));
+        pa_rtpoll_item_free(item);
+        return NULL;
+    }
+
+    return item;
+}
+
+snd_pcm_sframes_t pa_alsa_safe_avail(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss) {
+    snd_pcm_sframes_t n;
+    size_t k;
+
+    pa_assert(pcm);
+    pa_assert(hwbuf_size > 0);
+    pa_assert(ss);
+
+    /* Some ALSA driver expose weird bugs, let's inform the user about
+     * what is going on */
+
+    n = snd_pcm_avail(pcm);
+
+    if (n <= 0)
+        return n;
+
+    k = (size_t) n * pa_frame_size(ss);
+
+    if (PA_UNLIKELY(k >= hwbuf_size * 5 ||
+                    k >= pa_bytes_per_second(ss)*10)) {
+
+        PA_ONCE_BEGIN {
+            char *dn = pa_alsa_get_driver_name_by_pcm(pcm);
+            pa_log(_("snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
+                     "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."),
+                   (unsigned long) k,
+                   (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC),
+                   pa_strnull(dn));
+            pa_xfree(dn);
+            pa_alsa_dump(PA_LOG_ERROR, pcm);
+        } PA_ONCE_END;
+
+        /* Mhmm, let's try not to fail completely */
+        n = (snd_pcm_sframes_t) (hwbuf_size / pa_frame_size(ss));
+    }
+
+    return n;
+}
+
+int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_status_t *status, snd_pcm_sframes_t *delay, size_t hwbuf_size, const pa_sample_spec *ss,
+                       bool capture) {
+    ssize_t k;
+    size_t abs_k;
+    int err;
+    snd_pcm_sframes_t avail = 0;
+
+    pa_assert(pcm);
+    pa_assert(delay);
+    pa_assert(hwbuf_size > 0);
+    pa_assert(ss);
+
+    /* Some ALSA driver expose weird bugs, let's inform the user about
+     * what is going on. We're going to get both the avail and delay values so
+     * that we can compare and check them for capture.
+     * This is done with snd_pcm_status() which provides
+     * avail, delay and timestamp values in a single kernel call to improve
+     * timer-based scheduling */
+
+    if ((err = snd_pcm_status(pcm, status)) < 0)
+        return err;
+
+    avail = snd_pcm_status_get_avail(status);
+    *delay = snd_pcm_status_get_delay(status);
+
+    k = (ssize_t) *delay * (ssize_t) pa_frame_size(ss);
+
+    abs_k = k >= 0 ? (size_t) k : (size_t) -k;
+
+    if (PA_UNLIKELY(abs_k >= hwbuf_size * 5 ||
+                    abs_k >= pa_bytes_per_second(ss)*10)) {
+
+        PA_ONCE_BEGIN {
+            char *dn = pa_alsa_get_driver_name_by_pcm(pcm);
+            pa_log(_("snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s%lu ms).\n"
+                     "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."),
+                   (signed long) k,
+                   k < 0 ? "-" : "",
+                   (unsigned long) (pa_bytes_to_usec(abs_k, ss) / PA_USEC_PER_MSEC),
+                   pa_strnull(dn));
+            pa_xfree(dn);
+            pa_alsa_dump(PA_LOG_ERROR, pcm);
+        } PA_ONCE_END;
+
+        /* Mhmm, let's try not to fail completely */
+        if (k < 0)
+            *delay = -(snd_pcm_sframes_t) (hwbuf_size / pa_frame_size(ss));
+        else
+            *delay = (snd_pcm_sframes_t) (hwbuf_size / pa_frame_size(ss));
+    }
+
+    if (capture) {
+        abs_k = (size_t) avail * pa_frame_size(ss);
+
+        if (PA_UNLIKELY(abs_k >= hwbuf_size * 5 ||
+                        abs_k >= pa_bytes_per_second(ss)*10)) {
+
+            PA_ONCE_BEGIN {
+                char *dn = pa_alsa_get_driver_name_by_pcm(pcm);
+                pa_log(_("snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
+                         "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."),
+                       (unsigned long) k,
+                       (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC),
+                       pa_strnull(dn));
+                pa_xfree(dn);
+                pa_alsa_dump(PA_LOG_ERROR, pcm);
+            } PA_ONCE_END;
+
+            /* Mhmm, let's try not to fail completely */
+            avail = (snd_pcm_sframes_t) (hwbuf_size / pa_frame_size(ss));
+        }
+
+        if (PA_UNLIKELY(*delay < avail)) {
+            PA_ONCE_BEGIN {
+                char *dn = pa_alsa_get_driver_name_by_pcm(pcm);
+                pa_log(_("snd_pcm_avail_delay() returned strange values: delay %lu is less than avail %lu.\n"
+                         "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."),
+                       (unsigned long) *delay,
+                       (unsigned long) avail,
+                       pa_strnull(dn));
+                pa_xfree(dn);
+                pa_alsa_dump(PA_LOG_ERROR, pcm);
+            } PA_ONCE_END;
+
+            /* try to fixup */
+            *delay = avail;
+        }
+    }
+
+    return 0;
+}
+
+int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss) {
+    int r;
+    snd_pcm_uframes_t before;
+    size_t k;
+
+    pa_assert(pcm);
+    pa_assert(areas);
+    pa_assert(offset);
+    pa_assert(frames);
+    pa_assert(hwbuf_size > 0);
+    pa_assert(ss);
+
+    before = *frames;
+
+    r = snd_pcm_mmap_begin(pcm, areas, offset, frames);
+
+    if (r < 0)
+        return r;
+
+    k = (size_t) *frames * pa_frame_size(ss);
+
+    if (PA_UNLIKELY(*frames > before ||
+                    k >= hwbuf_size * 3 ||
+                    k >= pa_bytes_per_second(ss)*10))
+        PA_ONCE_BEGIN {
+            char *dn = pa_alsa_get_driver_name_by_pcm(pcm);
+            pa_log(_("snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
+                     "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."),
+                   (unsigned long) k,
+                   (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC),
+                   pa_strnull(dn));
+            pa_xfree(dn);
+            pa_alsa_dump(PA_LOG_ERROR, pcm);
+        } PA_ONCE_END;
+
+    return r;
+}
+
+char *pa_alsa_get_driver_name(int card) {
+    char *t, *m, *n;
+
+    pa_assert(card >= 0);
+
+    t = pa_sprintf_malloc("/sys/class/sound/card%i/device/driver/module", card);
+    m = pa_readlink(t);
+    pa_xfree(t);
+
+    if (!m)
+        return NULL;
+
+    n = pa_xstrdup(pa_path_get_filename(m));
+    pa_xfree(m);
+
+    return n;
+}
+
+char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm) {
+    int card;
+    snd_pcm_info_t* info;
+    snd_pcm_info_alloca(&info);
+
+    pa_assert(pcm);
+
+    if (snd_pcm_info(pcm, info) < 0)
+        return NULL;
+
+    if ((card = snd_pcm_info_get_card(info)) < 0)
+        return NULL;
+
+    return pa_alsa_get_driver_name(card);
+}
+
+char *pa_alsa_get_reserve_name(const char *device) {
+    const char *t;
+    int i;
+
+    pa_assert(device);
+
+    if ((t = strchr(device, ':')))
+        device = t+1;
+
+    if ((i = snd_card_get_index(device)) < 0) {
+        int32_t k;
+
+        if (pa_atoi(device, &k) < 0)
+            return NULL;
+
+        i = (int) k;
+    }
+
+    return pa_sprintf_malloc("Audio%i", i);
+}
+
+unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_rate) {
+    static unsigned int all_rates[] = { 8000, 11025, 12000,
+                                        16000, 22050, 24000,
+                                        32000, 44100, 48000,
+                                        64000, 88200, 96000,
+                                        128000, 176400, 192000,
+                                        384000 };
+    bool supported[PA_ELEMENTSOF(all_rates)] = { false, };
+    snd_pcm_hw_params_t *hwparams;
+    unsigned int i, j, n, *rates = NULL;
+    int ret;
+
+    snd_pcm_hw_params_alloca(&hwparams);
+
+    if ((ret = snd_pcm_hw_params_any(pcm, hwparams)) < 0) {
+        pa_log_debug("snd_pcm_hw_params_any() failed: %s", pa_alsa_strerror(ret));
+        return NULL;
+    }
+
+    for (i = 0, n = 0; i < PA_ELEMENTSOF(all_rates); i++) {
+        if (snd_pcm_hw_params_test_rate(pcm, hwparams, all_rates[i], 0) == 0) {
+            supported[i] = true;
+            n++;
+        }
+    }
+
+    if (n > 0) {
+        rates = pa_xnew(unsigned int, n + 1);
+
+        for (i = 0, j = 0; i < PA_ELEMENTSOF(all_rates); i++) {
+            if (supported[i])
+                rates[j++] = all_rates[i];
+        }
+
+        rates[j] = 0;
+    } else {
+        rates = pa_xnew(unsigned int, 2);
+
+        rates[0] = fallback_rate;
+        if ((ret = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rates[0], NULL)) < 0) {
+            pa_log_debug("snd_pcm_hw_params_set_rate_near() failed: %s", pa_alsa_strerror(ret));
+            pa_xfree(rates);
+            return NULL;
+        }
+
+        rates[1] = 0;
+    }
+
+    return rates;
+}
+
+bool pa_alsa_pcm_is_hw(snd_pcm_t *pcm) {
+    snd_pcm_info_t* info;
+    snd_pcm_info_alloca(&info);
+
+    pa_assert(pcm);
+
+    if (snd_pcm_info(pcm, info) < 0)
+        return false;
+
+    return snd_pcm_info_get_card(info) >= 0;
+}
+
+bool pa_alsa_pcm_is_modem(snd_pcm_t *pcm) {
+    snd_pcm_info_t* info;
+    snd_pcm_info_alloca(&info);
+
+    pa_assert(pcm);
+
+    if (snd_pcm_info(pcm, info) < 0)
+        return false;
+
+    return snd_pcm_info_get_class(info) == SND_PCM_CLASS_MODEM;
+}
+
+PA_STATIC_TLS_DECLARE(cstrerror, pa_xfree);
+
+const char* pa_alsa_strerror(int errnum) {
+    const char *original = NULL;
+    char *translated, *t;
+    char errbuf[128];
+
+    if ((t = PA_STATIC_TLS_GET(cstrerror)))
+        pa_xfree(t);
+
+    original = snd_strerror(errnum);
+
+    if (!original) {
+        pa_snprintf(errbuf, sizeof(errbuf), "Unknown error %i", errnum);
+        original = errbuf;
+    }
+
+    if (!(translated = pa_locale_to_utf8(original))) {
+        pa_log_warn("Unable to convert error string to locale, filtering.");
+        translated = pa_utf8_filter(original);
+    }
+
+    PA_STATIC_TLS_SET(cstrerror, translated);
+
+    return translated;
+}
+
+bool pa_alsa_may_tsched(bool want) {
+
+    if (!want)
+        return false;
+
+    if (!pa_rtclock_hrtimer()) {
+        /* We cannot depend on being woken up in time when the timers
+        are inaccurate, so let's fallback to classic IO based playback
+        then. */
+        pa_log_notice("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
+        return false; }
+
+    if (pa_running_in_vm()) {
+        /* We cannot depend on being woken up when we ask for in a VM,
+         * so let's fallback to classic IO based playback then. */
+        pa_log_notice("Disabling timer-based scheduling because running inside a VM.");
+        return false;
+    }
+
+    return true;
+}
+
+#define SND_MIXER_ELEM_PULSEAUDIO (SND_MIXER_ELEM_LAST + 10)
+
+snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, unsigned int device) {
+    snd_mixer_elem_t *elem;
+
+    for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem)) {
+        snd_hctl_elem_t *helem;
+        if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_PULSEAUDIO)
+            continue;
+        helem = snd_mixer_elem_get_private(elem);
+        if (!pa_streq(snd_hctl_elem_get_name(helem), name))
+            continue;
+        if (snd_hctl_elem_get_device(helem) != device)
+            continue;
+        return elem;
+    }
+    return NULL;
+}
+
+static int mixer_class_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2)
+{
+    /* Dummy compare function */
+    return c1 == c2 ? 0 : (c1 > c2 ? 1 : -1);
+}
+
+static int mixer_class_event(snd_mixer_class_t *class, unsigned int mask,
+                       snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
+{
+    int err;
+    const char *name = snd_hctl_elem_get_name(helem);
+    if (mask & SND_CTL_EVENT_MASK_ADD) {
+        snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem);
+        if (iface == SND_CTL_ELEM_IFACE_CARD || iface == SND_CTL_ELEM_IFACE_PCM) {
+            snd_mixer_elem_t *new_melem;
+
+            /* Put the hctl pointer as our private data - it will be useful for callbacks */
+            if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, helem, NULL)) < 0) {
+                pa_log_warn("snd_mixer_elem_new failed: %s", pa_alsa_strerror(err));
+                return 0;
+            }
+
+            if ((err = snd_mixer_elem_attach(new_melem, helem)) < 0) {
+                pa_log_warn("snd_mixer_elem_attach failed: %s", pa_alsa_strerror(err));
+               snd_mixer_elem_free(melem);
+                return 0;
+            }
+
+            if ((err = snd_mixer_elem_add(new_melem, class)) < 0) {
+                pa_log_warn("snd_mixer_elem_add failed: %s", pa_alsa_strerror(err));
+                return 0;
+            }
+        }
+    }
+    else if (mask & SND_CTL_EVENT_MASK_VALUE) {
+        snd_mixer_elem_value(melem); /* Calls the element callback */
+        return 0;
+    }
+    else
+        pa_log_info("Got an unknown mixer class event for %s: mask 0x%x", name, mask);
+
+    return 0;
+}
+
+static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
+    int err;
+    snd_mixer_class_t *class;
+
+    pa_assert(mixer);
+    pa_assert(dev);
+
+    if ((err = snd_mixer_attach(mixer, dev)) < 0) {
+        pa_log_info("Unable to attach to mixer %s: %s", dev, pa_alsa_strerror(err));
+        return -1;
+    }
+
+    if (snd_mixer_class_malloc(&class)) {
+        pa_log_info("Failed to allocate mixer class for %s", dev);
+        return -1;
+    }
+    snd_mixer_class_set_event(class, mixer_class_event);
+    snd_mixer_class_set_compare(class, mixer_class_compare);
+    if ((err = snd_mixer_class_register(class, mixer)) < 0) {
+        pa_log_info("Unable register mixer class for %s: %s", dev, pa_alsa_strerror(err));
+        snd_mixer_class_free(class);
+        return -1;
+    }
+    /* From here on, the mixer class is deallocated by alsa on snd_mixer_close/free. */
+
+    if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
+        pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err));
+        return -1;
+    }
+
+    if ((err = snd_mixer_load(mixer)) < 0) {
+        pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
+        return -1;
+    }
+
+    pa_log_info("Successfully attached to mixer '%s'", dev);
+    return 0;
+}
+
+snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char **ctl_device) {
+    int err;
+    snd_mixer_t *m;
+    char *md;
+    snd_pcm_info_t* info;
+    snd_pcm_info_alloca(&info);
+
+    if ((err = snd_mixer_open(&m, 0)) < 0) {
+        pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
+        return NULL;
+    }
+
+    /* Then, try by card index */
+    md = pa_sprintf_malloc("hw:%i", alsa_card_index);
+    if (prepare_mixer(m, md) >= 0) {
+
+        if (ctl_device)
+            *ctl_device = md;
+        else
+            pa_xfree(md);
+
+        return m;
+    }
+
+    pa_xfree(md);
+
+    snd_mixer_close(m);
+    return NULL;
+}
+
+snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
+    int err;
+    snd_mixer_t *m;
+    const char *dev;
+    snd_pcm_info_t* info;
+    snd_pcm_info_alloca(&info);
+
+    pa_assert(pcm);
+
+    if ((err = snd_mixer_open(&m, 0)) < 0) {
+        pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
+        return NULL;
+    }
+
+    /* First, try by name */
+    if ((dev = snd_pcm_name(pcm)))
+        if (prepare_mixer(m, dev) >= 0) {
+            if (ctl_device)
+                *ctl_device = pa_xstrdup(dev);
+
+            return m;
+        }
+
+    /* Then, try by card index */
+    if (snd_pcm_info(pcm, info) >= 0) {
+        char *md;
+        int card_idx;
+
+        if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
+
+            md = pa_sprintf_malloc("hw:%i", card_idx);
+
+            if (!dev || !pa_streq(dev, md))
+                if (prepare_mixer(m, md) >= 0) {
+
+                    if (ctl_device)
+                        *ctl_device = md;
+                    else
+                        pa_xfree(md);
+
+                    return m;
+                }
+
+            pa_xfree(md);
+        }
+    }
+
+    snd_mixer_close(m);
+    return NULL;
+}
+
+int pa_alsa_get_hdmi_eld(snd_hctl_elem_t *elem, pa_hdmi_eld *eld) {
+
+    /* The ELD format is specific to HDA Intel sound cards and defined in the
+       HDA specification: http://www.intel.com/content/www/us/en/standards/high-definition-audio-specification.html */
+    int err;
+    snd_ctl_elem_info_t *info;
+    snd_ctl_elem_value_t *value;
+    uint8_t *elddata;
+    unsigned int eldsize, mnl;
+    unsigned int device;
+
+    pa_assert(eld != NULL);
+    pa_assert(elem != NULL);
+
+    /* Does it have any contents? */
+    snd_ctl_elem_info_alloca(&info);
+    snd_ctl_elem_value_alloca(&value);
+    if ((err = snd_hctl_elem_info(elem, info)) < 0 ||
+       (err = snd_hctl_elem_read(elem, value)) < 0) {
+        pa_log_warn("Accessing ELD control failed with error %s", snd_strerror(err));
+        return -1;
+    }
+
+    device = snd_hctl_elem_get_device(elem);
+    eldsize = snd_ctl_elem_info_get_count(info);
+    elddata = (unsigned char *) snd_ctl_elem_value_get_bytes(value);
+    if (elddata == NULL || eldsize == 0) {
+        pa_log_debug("ELD info empty (for device=%d)", device);
+        return -1;
+    }
+    if (eldsize < 20 || eldsize > 256) {
+        pa_log_debug("ELD info has wrong size (for device=%d)", device);
+        return -1;
+    }
+
+    /* Try to fetch monitor name */
+    mnl = elddata[4] & 0x1f;
+    if (mnl == 0 || mnl > 16 || 20 + mnl > eldsize) {
+        pa_log_debug("No monitor name in ELD info (for device=%d)", device);
+        mnl = 0;
+    }
+    memcpy(eld->monitor_name, &elddata[20], mnl);
+    eld->monitor_name[mnl] = '\0';
+    if (mnl)
+        pa_log_debug("Monitor name in ELD info is '%s' (for device=%d)", eld->monitor_name, device);
+
+    return 0;
+}
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
new file mode 100644 (file)
index 0000000..8345a0b
--- /dev/null
@@ -0,0 +1,154 @@
+#ifndef fooalsautilhfoo
+#define fooalsautilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <asoundlib.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/proplist.h>
+
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/core.h>
+#include <pulsecore/log.h>
+
+#include "alsa-mixer.h"
+
+int pa_alsa_set_hw_params(
+        snd_pcm_t *pcm_handle,
+        pa_sample_spec *ss,                /* modified at return */
+        snd_pcm_uframes_t *period_size,    /* modified at return */
+        snd_pcm_uframes_t *buffer_size,    /* modified at return */
+        snd_pcm_uframes_t tsched_size,
+        bool *use_mmap,                    /* modified at return */
+        bool *use_tsched,                  /* modified at return */
+        bool require_exact_channel_number);
+
+int pa_alsa_set_sw_params(
+        snd_pcm_t *pcm,
+        snd_pcm_uframes_t avail_min,
+        bool period_event);
+
+/* Picks a working mapping from the profile set based on the specified ss/map */
+snd_pcm_t *pa_alsa_open_by_device_id_auto(
+        const char *dev_id,
+        char **dev,                       /* modified at return */
+        pa_sample_spec *ss,               /* modified at return */
+        pa_channel_map* map,              /* modified at return */
+        int mode,
+        snd_pcm_uframes_t *period_size,   /* modified at return */
+        snd_pcm_uframes_t *buffer_size,   /* modified at return */
+        snd_pcm_uframes_t tsched_size,
+        bool *use_mmap,                   /* modified at return */
+        bool *use_tsched,                 /* modified at return */
+        pa_alsa_profile_set *ps,
+        pa_alsa_mapping **mapping);       /* modified at return */
+
+/* Uses the specified mapping */
+snd_pcm_t *pa_alsa_open_by_device_id_mapping(
+        const char *dev_id,
+        char **dev,                       /* modified at return */
+        pa_sample_spec *ss,               /* modified at return */
+        pa_channel_map* map,              /* modified at return */
+        int mode,
+        snd_pcm_uframes_t *period_size,   /* modified at return */
+        snd_pcm_uframes_t *buffer_size,   /* modified at return */
+        snd_pcm_uframes_t tsched_size,
+        bool *use_mmap,                   /* modified at return */
+        bool *use_tsched,                 /* modified at return */
+        pa_alsa_mapping *mapping);
+
+/* Opens the explicit ALSA device */
+snd_pcm_t *pa_alsa_open_by_device_string(
+        const char *dir,
+        char **dev,                       /* modified at return */
+        pa_sample_spec *ss,               /* modified at return */
+        pa_channel_map* map,              /* modified at return */
+        int mode,
+        snd_pcm_uframes_t *period_size,   /* modified at return */
+        snd_pcm_uframes_t *buffer_size,   /* modified at return */
+        snd_pcm_uframes_t tsched_size,
+        bool *use_mmap,                   /* modified at return */
+        bool *use_tsched,                 /* modified at return */
+        bool require_exact_channel_number);
+
+/* Opens the explicit ALSA device with a fallback list */
+snd_pcm_t *pa_alsa_open_by_template(
+        char **template,
+        const char *dev_id,
+        char **dev,                       /* modified at return */
+        pa_sample_spec *ss,               /* modified at return */
+        pa_channel_map* map,              /* modified at return */
+        int mode,
+        snd_pcm_uframes_t *period_size,   /* modified at return */
+        snd_pcm_uframes_t *buffer_size,   /* modified at return */
+        snd_pcm_uframes_t tsched_size,
+        bool *use_mmap,                   /* modified at return */
+        bool *use_tsched,                 /* modified at return */
+        bool require_exact_channel_number);
+
+void pa_alsa_dump(pa_log_level_t level, snd_pcm_t *pcm);
+void pa_alsa_dump_status(snd_pcm_t *pcm);
+
+void pa_alsa_refcnt_inc(void);
+void pa_alsa_refcnt_dec(void);
+
+void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info);
+void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card);
+void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm);
+void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name);
+bool pa_alsa_init_description(pa_proplist *p, pa_card *card);
+
+int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
+
+pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll);
+
+snd_pcm_sframes_t pa_alsa_safe_avail(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss);
+int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_status_t *status, snd_pcm_sframes_t *delay, size_t hwbuf_size, const pa_sample_spec *ss, bool capture);
+int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss);
+
+char *pa_alsa_get_driver_name(int card);
+char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm);
+
+char *pa_alsa_get_reserve_name(const char *device);
+
+unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_rate);
+
+bool pa_alsa_pcm_is_hw(snd_pcm_t *pcm);
+bool pa_alsa_pcm_is_modem(snd_pcm_t *pcm);
+
+const char* pa_alsa_strerror(int errnum);
+
+bool pa_alsa_may_tsched(bool want);
+
+snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, unsigned int device);
+
+snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char **ctl_device);
+
+typedef struct pa_hdmi_eld pa_hdmi_eld;
+struct pa_hdmi_eld {
+    char monitor_name[17];
+};
+
+int pa_alsa_get_hdmi_eld(snd_hctl_elem_t *elem, pa_hdmi_eld *eld);
+
+#endif
diff --git a/src/modules/alsa/mixer/paths/analog-input-aux.conf b/src/modules/alsa/mixer/paths/analog-input-aux.conf
new file mode 100644 (file)
index 0000000..47e22c5
--- /dev/null
@@ -0,0 +1,65 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where an 'Aux' element exists
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 80
+description-key = analog-input
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Line]
+switch = off
+volume = off
+
+[Element Aux]
+required = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Video]
+switch = off
+volume = off
+
+[Element Mic/Line]
+switch = off
+volume = off
+
+[Element TV Tuner]
+switch = off
+volume = off
+
+[Element FM]
+switch = off
+volume = off
+
+.include analog-input.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf b/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf
new file mode 100644 (file)
index 0000000..96861e7
--- /dev/null
@@ -0,0 +1,104 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where a 'Dock Mic' or 'Dock Mic Boost' element exists
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 78
+description-key = analog-input-microphone-dock
+
+[Jack Dock Mic]
+required-any = any
+
+[Jack Dock Mic Phantom]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Dock Mic Boost]
+required-any = any
+switch = select
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Option Dock Mic Boost:on]
+name = input-boost-on
+
+[Option Dock Mic Boost:off]
+name = input-boost-off
+
+[Element Dock Mic]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Input Source]
+enumeration = select
+
+[Option Input Source:Dock Mic]
+name = analog-input-microphone-dock
+required-any = any
+
+[Element Capture Source]
+enumeration = select
+
+[Option Capture Source:Dock Mic]
+name = analog-input-microphone-dock
+required-any = any
+
+[Element Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Front Mic]
+switch = off
+volume = off
+
+[Element Rear Mic]
+switch = off
+volume = off
+
+[Element Mic Boost]
+switch = off
+volume = off
+
+[Element Internal Mic Boost]
+switch = off
+volume = off
+
+[Element Front Mic Boost]
+switch = off
+volume = off
+
+[Element Rear Mic Boost]
+switch = off
+volume = off
+
+.include analog-input-mic.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-fm.conf b/src/modules/alsa/mixer/paths/analog-input-fm.conf
new file mode 100644 (file)
index 0000000..d3501a8
--- /dev/null
@@ -0,0 +1,65 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where an 'FM' element exists
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 70
+description-key = analog-input-radio
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Line]
+switch = off
+volume = off
+
+[Element Aux]
+switch = off
+volume = off
+
+[Element Video]
+switch = off
+volume = off
+
+[Element Mic/Line]
+switch = off
+volume = off
+
+[Element TV Tuner]
+switch = off
+volume = off
+
+[Element FM]
+required = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+.include analog-input.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-front-mic.conf b/src/modules/alsa/mixer/paths/analog-input-front-mic.conf
new file mode 100644 (file)
index 0000000..6e7775c
--- /dev/null
@@ -0,0 +1,104 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where a 'Front Mic' or 'Front Mic Boost' element exists
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 85
+description-key = analog-input-microphone-front
+
+[Jack Front Mic]
+required-any = any
+
+[Jack Front Mic Phantom]
+required-any = any
+state.plugged = unknown
+state.unplugged = unknown
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Front Mic Boost]
+required-any = any
+switch = select
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Option Front Mic Boost:on]
+name = input-boost-on
+
+[Option Front Mic Boost:off]
+name = input-boost-off
+
+[Element Front Mic]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Input Source]
+enumeration = select
+
+[Option Input Source:Front Mic]
+name = analog-input-microphone-front
+required-any = any
+
+[Element Capture Source]
+enumeration = select
+
+[Option Capture Source:Front Mic]
+name = analog-input-microphone-front
+required-any = any
+
+[Element Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Rear Mic]
+switch = off
+volume = off
+
+[Element Dock Mic]
+switch = off
+volume = off
+
+[Element Mic Boost]
+switch = off
+volume = off
+
+[Element Dock Mic Boost]
+switch = off
+volume = off
+
+[Element Internal Mic Boost]
+switch = off
+volume = off
+
+[Element Rear Mic Boost]
+switch = off
+volume = off
+
+.include analog-input-mic.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-headphone-mic.conf b/src/modules/alsa/mixer/paths/analog-input-headphone-mic.conf
new file mode 100644 (file)
index 0000000..eb5740a
--- /dev/null
@@ -0,0 +1,102 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For some ASUS netbooks that have one jack that can be either a Headphone
+; *or* a mic. This path will be active only when it is used as a mic.
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 87
+description-key = analog-input-microphone
+
+[Jack Headphone Mic]
+required-any = any
+state.plugged = unknown
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Headphone Mic Boost]
+required-any = any
+switch = select
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Headphone Mic]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Input Source]
+enumeration = select
+
+[Option Input Source:Headphone Mic]
+name = analog-input-microphone
+required-any = any
+
+[Element Capture Source]
+enumeration = select
+
+[Option Capture Source:Headphone Mic]
+name = analog-input-microphone
+required-any = any
+
+; Make sure the internal speakers are not auto-muted when you plug a mic in
+[Element Auto-Mute Mode]
+enumeration = select
+
+[Option Auto-Mute Mode:Disabled]
+name = analog-input-microphone
+
+[Element Front Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Rear Mic]
+switch = off
+volume = off
+
+[Element Dock Mic]
+switch = off
+volume = off
+
+[Element Dock Mic Boost]
+switch = off
+volume = off
+
+[Element Internal Mic Boost]
+switch = off
+volume = off
+
+[Element Front Mic Boost]
+switch = off
+volume = off
+
+[Element Rear Mic Boost]
+switch = off
+volume = off
+
+.include analog-input-mic.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-headset-mic.conf b/src/modules/alsa/mixer/paths/analog-input-headset-mic.conf
new file mode 100644 (file)
index 0000000..579db6b
--- /dev/null
@@ -0,0 +1,114 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where a 'Headset Mic' or 'Headset Mic Boost' element exists
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 88
+description-key = analog-input-microphone-headset
+
+[Jack Headset Mic]
+required-any = any
+
+[Jack Headset Mic Phantom]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Jack Headphone]
+state.plugged = unknown
+
+[Jack Front Headphone]
+state.plugged = unknown
+
+[Jack Headphone Mic]
+state.plugged = unknown
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Headset Mic Boost]
+required-any = any
+switch = select
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Headset Mic]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Headset]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Input Source]
+enumeration = select
+
+[Option Input Source:Headset Mic]
+name = Headset Microphone
+required-any = any
+
+[Element Capture Source]
+enumeration = select
+
+[Option Capture Source:Headset Mic]
+name = Headset Microphone
+required-any = any
+
+[Element Front Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Rear Mic]
+switch = off
+volume = off
+
+[Element Dock Mic]
+switch = off
+volume = off
+
+[Element Dock Mic Boost]
+switch = off
+volume = off
+
+[Element Internal Mic Boost]
+switch = off
+volume = off
+
+[Element Front Mic Boost]
+switch = off
+volume = off
+
+[Element Rear Mic Boost]
+switch = off
+volume = off
+
+.include analog-input-mic.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-internal-mic-always.conf b/src/modules/alsa/mixer/paths/analog-input-internal-mic-always.conf
new file mode 100644 (file)
index 0000000..9e22008
--- /dev/null
@@ -0,0 +1,133 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where a 'Internal Mic' or 'Internal Mic Boost' element exists
+; 'Int Mic' and 'Int Mic Boost' are for compatibility with kernels < 2.6.38
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 89
+description-key = analog-input-microphone-internal
+
+[Jack Mic]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Dock Mic]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Front Mic]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Rear Mic]
+state.plugged = no
+state.unplugged = unknown
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Internal Mic Boost]
+switch = select
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Option Internal Mic Boost:on]
+name = input-boost-on
+
+[Option Internal Mic Boost:off]
+name = input-boost-off
+
+[Element Int Mic Boost]
+switch = select
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Option Int Mic Boost:on]
+name = input-boost-on
+
+[Option Int Mic Boost:off]
+name = input-boost-off
+
+[Element Internal Mic]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Int Mic]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Input Source]
+enumeration = select
+
+[Option Input Source:Internal Mic]
+name = analog-input-microphone-internal
+
+[Option Input Source:Int Mic]
+name = analog-input-microphone-internal
+
+[Element Capture Source]
+enumeration = select
+
+[Option Capture Source:Internal Mic]
+name = analog-input-microphone-internal
+
+[Option Capture Source:Int Mic]
+name = analog-input-microphone-internal
+
+[Element Mic]
+switch = off
+volume = off
+
+[Element Dock Mic]
+switch = off
+volume = off
+
+[Element Front Mic]
+switch = off
+volume = off
+
+[Element Rear Mic]
+switch = off
+volume = off
+
+[Element Mic Boost]
+switch = off
+volume = off
+
+[Element Dock Mic Boost]
+switch = off
+volume = off
+
+[Element Front Mic Boost]
+switch = off
+volume = off
+
+[Element Rear Mic Boost]
+switch = off
+volume = off
+
+.include analog-input-mic.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf b/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf
new file mode 100644 (file)
index 0000000..898410a
--- /dev/null
@@ -0,0 +1,154 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where a 'Internal Mic' or 'Internal Mic Boost' element exists
+; 'Int Mic' and 'Int Mic Boost' are for compatibility with kernels < 2.6.38
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 89
+description-key = analog-input-microphone-internal
+
+[Jack Mic]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Dock Mic]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Front Mic]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Rear Mic]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Internal Mic Phantom]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Internal Mic Boost]
+required-any = any
+switch = select
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Option Internal Mic Boost:on]
+name = input-boost-on
+
+[Option Internal Mic Boost:off]
+name = input-boost-off
+
+[Element Int Mic Boost]
+required-any = any
+switch = select
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Option Int Mic Boost:on]
+name = input-boost-on
+
+[Option Int Mic Boost:off]
+name = input-boost-off
+
+[Element Internal Mic]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Int Mic]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Input Source]
+enumeration = select
+
+[Option Input Source:Internal Mic]
+name = analog-input-microphone-internal
+required-any = any
+
+[Option Input Source:Int Mic]
+name = analog-input-microphone-internal
+required-any = any
+
+[Element Capture Source]
+enumeration = select
+
+[Option Capture Source:Internal Mic]
+name = analog-input-microphone-internal
+required-any = any
+
+[Option Capture Source:Int Mic]
+name = analog-input-microphone-internal
+required-any = any
+
+[Element Mic]
+switch = off
+volume = off
+
+[Element Dock Mic]
+switch = off
+volume = off
+
+[Element Front Mic]
+switch = off
+volume = off
+
+[Element Rear Mic]
+switch = off
+volume = off
+
+[Element Headphone Mic]
+switch = off
+volume = off
+
+[Element Headphone Mic Boost]
+switch = off
+volume = off
+
+[Element Mic Boost]
+switch = off
+volume = off
+
+[Element Dock Mic Boost]
+switch = off
+volume = off
+
+[Element Front Mic Boost]
+switch = off
+volume = off
+
+[Element Rear Mic Boost]
+switch = off
+volume = off
+
+.include analog-input-mic.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-linein.conf b/src/modules/alsa/mixer/paths/analog-input-linein.conf
new file mode 100644 (file)
index 0000000..8163ffb
--- /dev/null
@@ -0,0 +1,141 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where a 'Line' element exists
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 81
+
+[Jack Line]
+required-any = any
+
+[Jack Line Phantom]
+required-any = any
+state.plugged = unknown
+state.unplugged = unknown
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Line Boost]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Line]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Input Source]
+enumeration = select
+
+[Option Input Source:Line]
+name = analog-input-linein
+required-any = any
+
+[Element Capture Source]
+enumeration = select
+
+[Option Capture Source:Line]
+name = analog-input-linein
+required-any = any
+
+[Element PCM Capture Source]
+enumeration = select
+
+[Option PCM Capture Source:Line]
+name = analog-input-linein
+required-any = any
+
+[Option PCM Capture Source:Line In]
+name = analog-input-linein
+required-any = any
+
+[Element Mic]
+switch = off
+volume = off
+
+[Element Dock Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Front Mic]
+switch = off
+volume = off
+
+[Element Rear Mic]
+switch = off
+volume = off
+
+[Element Mic Boost]
+switch = off
+volume = off
+
+[Element Dock Mic Boost]
+switch = off
+volume = off
+
+[Element Internal Mic Boost]
+switch = off
+volume = off
+
+[Element Front Mic Boost]
+switch = off
+volume = off
+
+[Element Rear Mic Boost]
+switch = off
+volume = off
+
+[Element Aux]
+switch = off
+volume = off
+
+[Element Video]
+switch = off
+volume = off
+
+[Element Mic/Line]
+switch = off
+volume = off
+
+[Element TV Tuner]
+switch = off
+volume = off
+
+[Element FM]
+switch = off
+volume = off
+
+[Element Mic Jack Mode]
+enumeration = select
+
+[Option Mic Jack Mode:Line In]
+priority = 19
+name = input-linein
diff --git a/src/modules/alsa/mixer/paths/analog-input-mic-line.conf b/src/modules/alsa/mixer/paths/analog-input-mic-line.conf
new file mode 100644 (file)
index 0000000..7147d20
--- /dev/null
@@ -0,0 +1,66 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where a 'Mic/Line' element exists
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 85
+description-key = analog-input
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Line]
+switch = off
+volume = off
+
+[Element Aux]
+switch = off
+volume = off
+
+[Element Video]
+switch = off
+volume = off
+
+[Element Mic/Line]
+required = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element TV Tuner]
+switch = off
+volume = off
+
+[Element FM]
+switch = off
+volume = off
+
+.include analog-input.conf.common
+.include analog-input-mic.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf b/src/modules/alsa/mixer/paths/analog-input-mic.conf
new file mode 100644 (file)
index 0000000..123439b
--- /dev/null
@@ -0,0 +1,138 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where a 'Mic' or 'Mic Boost' element exists
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 87
+description-key = analog-input-microphone
+
+[Jack Mic]
+required-any = any
+
+[Jack Mic Phantom]
+required-any = any
+state.plugged = unknown
+state.unplugged = unknown
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Mic Boost]
+required-any = any
+switch = select
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Option Mic Boost:on]
+name = input-boost-on
+
+[Option Mic Boost:off]
+name = input-boost-off
+
+[Element Mic]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Input Source]
+enumeration = select
+
+[Option Input Source:Mic]
+name = analog-input-microphone
+required-any = any
+
+[Element Capture Source]
+enumeration = select
+
+[Option Capture Source:Mic]
+name = analog-input-microphone
+required-any = any
+
+[Element PCM Capture Source]
+enumeration = select
+
+[Option PCM Capture Source:Mic]
+name = analog-input-microphone
+required-any = any
+
+[Option PCM Capture Source:Mic-In/Mic Array]
+name = analog-input-microphone
+required-any = any
+
+;;; Some AC'97s have "Mic Select" and "Mic Boost (+20dB)"
+
+[Element Mic Select]
+enumeration = select
+
+[Option Mic Select:Mic1]
+name = input-microphone
+priority = 20
+
+[Option Mic Select:Mic2]
+name = input-microphone
+priority = 19
+
+[Element Mic Boost (+20dB)]
+switch = select
+volume = merge
+
+[Option Mic Boost (+20dB):on]
+name = input-boost-on
+
+[Option Mic Boost (+20dB):off]
+name = input-boost-off
+
+[Element Front Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Rear Mic]
+switch = off
+volume = off
+
+[Element Dock Mic]
+switch = off
+volume = off
+
+[Element Dock Mic Boost]
+switch = off
+volume = off
+
+[Element Internal Mic Boost]
+switch = off
+volume = off
+
+[Element Front Mic Boost]
+switch = off
+volume = off
+
+[Element Rear Mic Boost]
+switch = off
+volume = off
+
+.include analog-input-mic.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf.common b/src/modules/alsa/mixer/paths/analog-input-mic.conf.common
new file mode 100644 (file)
index 0000000..e5ced21
--- /dev/null
@@ -0,0 +1,60 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Common element for all microphone inputs
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[Properties]
+device.icon_name = audio-input-microphone
+
+[Element Line]
+switch = off
+volume = off
+
+[Element Line Boost]
+switch = off
+volume = off
+
+[Element Aux]
+switch = off
+volume = off
+
+[Element Video]
+switch = off
+volume = off
+
+[Element Mic/Line]
+switch = off
+volume = off
+
+[Element TV Tuner]
+switch = off
+volume = off
+
+[Element FM]
+switch = off
+volume = off
+
+[Element Inverted Internal Mic]
+switch = off
+volume = off
+
+[Element Mic Jack Mode]
+enumeration = select
+
+[Option Mic Jack Mode:Mic In]
+priority = 19
+name = input-microphone
diff --git a/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf b/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf
new file mode 100644 (file)
index 0000000..7136193
--- /dev/null
@@ -0,0 +1,104 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where a 'Rear Mic' or 'Rear Mic Boost' element exists
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 82
+description-key = analog-input-microphone-rear
+
+[Jack Rear Mic]
+required-any = any
+
+[Jack Rear Mic Phantom]
+required-any = any
+state.plugged = unknown
+state.unplugged = unknown
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Rear Mic Boost]
+required-any = any
+switch = select
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Option Rear Mic Boost:on]
+name = input-boost-on
+
+[Option Rear Mic Boost:off]
+name = input-boost-off
+
+[Element Rear Mic]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Input Source]
+enumeration = select
+
+[Option Input Source:Rear Mic]
+name = analog-input-microphone-rear
+required-any = any
+
+[Element Capture Source]
+enumeration = select
+
+[Option Capture Source:Rear Mic]
+name = analog-input-microphone-rear
+required-any = any
+
+[Element Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Front Mic]
+switch = off
+volume = off
+
+[Element Dock Mic]
+switch = off
+volume = off
+
+[Element Mic Boost]
+switch = off
+volume = off
+
+[Element Dock Mic Boost]
+switch = off
+volume = off
+
+[Element Internal Mic Boost]
+switch = off
+volume = off
+
+[Element Front Mic Boost]
+switch = off
+volume = off
+
+.include analog-input-mic.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf b/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf
new file mode 100644 (file)
index 0000000..99d1d79
--- /dev/null
@@ -0,0 +1,65 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where a 'TV Tuner' element exists
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 70
+description-key = analog-input-video
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Line]
+switch = off
+volume = off
+
+[Element Aux]
+switch = off
+volume = off
+
+[Element Video]
+switch = off
+volume = off
+
+[Element Mic/Line]
+switch = off
+volume = off
+
+[Element TV Tuner]
+required = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element FM]
+switch = off
+volume = off
+
+.include analog-input.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input-video.conf b/src/modules/alsa/mixer/paths/analog-input-video.conf
new file mode 100644 (file)
index 0000000..50c999e
--- /dev/null
@@ -0,0 +1,64 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; For devices where a 'Video' element exists
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 70
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Mic]
+switch = off
+volume = off
+
+[Element Internal Mic]
+switch = off
+volume = off
+
+[Element Line]
+switch = off
+volume = off
+
+[Element Aux]
+switch = off
+volume = off
+
+[Element Video]
+required = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Mic/Line]
+switch = off
+volume = off
+
+[Element TV Tuner]
+switch = off
+volume = off
+
+[Element FM]
+switch = off
+volume = off
+
+.include analog-input.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input.conf b/src/modules/alsa/mixer/paths/analog-input.conf
new file mode 100644 (file)
index 0000000..c9db677
--- /dev/null
@@ -0,0 +1,102 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; A fallback for devices that lack separate Mic/Line/Aux/Video/TV
+; Tuner/FM elements
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 100
+
+[Element Capture]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Mic]
+required-absent = any
+
+[Element Mic Boost]
+required-absent = any
+
+[Element Dock Mic]
+required-absent = any
+
+[Element Dock Mic Boost]
+required-absent = any
+
+[Element Front Mic]
+required-absent = any
+
+[Element Front Mic Boost]
+required-absent = any
+
+[Element Int Mic]
+required-absent = any
+
+[Element Int Mic Boost]
+required-absent = any
+
+[Element Internal Mic]
+required-absent = any
+
+[Element Internal Mic Boost]
+required-absent = any
+
+[Element Rear Mic]
+required-absent = any
+
+[Element Rear Mic Boost]
+required-absent = any
+
+[Element Headset]
+required-absent = any
+
+[Element Headset Mic]
+required-absent = any
+
+[Element Headset Mic Boost]
+required-absent = any
+
+[Element Headphone Mic]
+required-absent = any
+
+[Element Headphone Mic Boost]
+required-absent = any
+
+[Element Line]
+required-absent = any
+
+[Element Line Boost]
+required-absent = any
+
+[Element Aux]
+required-absent = any
+
+[Element Video]
+required-absent = any
+
+[Element Mic/Line]
+required-absent = any
+
+[Element TV Tuner]
+required-absent = any
+
+[Element FM]
+required-absent = any
+
+.include analog-input.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-input.conf.common b/src/modules/alsa/mixer/paths/analog-input.conf.common
new file mode 100644 (file)
index 0000000..17aa581
--- /dev/null
@@ -0,0 +1,289 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Mixer path for PulseAudio's ALSA backend, common elements for all
+; input paths. If multiple options by the same id are discovered they
+; will be suffixed with a number to distuingish them, in the same
+; order they appear here.
+;
+; Source selection should use the following names:
+;
+;       input                       -- If we don't know the exact kind of input
+;       input-microphone
+;       input-microphone-internal
+;       input-microphone-external
+;       input-linein
+;       input-video
+;       input-radio
+;       input-docking-microphone
+;       input-docking-linein
+;       input-docking
+;
+;  We explicitly don't want to wrap the following sources:
+;
+;       CD
+;       Synth/MIDI
+;       Phone
+;       Mix
+;       Digital/SPDIF
+;       Master
+;       PC Speaker
+;
+; See analog-output.conf.common for an explanation on the directives
+
+;;; 'Input Source Select'
+
+[Element Input Source Select]
+enumeration = select
+
+[Option Input Source Select:Input1]
+name = input
+priority = 10
+
+[Option Input Source Select:Input2]
+name = input
+priority = 5
+
+;;; 'Input Source'
+
+[Element Input Source]
+enumeration = select
+
+[Option Input Source:Digital Mic]
+name = input-microphone
+priority = 20
+
+[Option Input Source:Microphone]
+name = input-microphone
+priority = 20
+
+[Option Input Source:Front Microphone]
+name = input-microphone
+priority = 19
+
+[Option Input Source:Internal Mic 1]
+name = input-microphone
+priority = 19
+
+[Option Input Source:Line-In]
+name = input-linein
+priority = 18
+
+[Option Input Source:Line In]
+name = input-linein
+priority = 18
+
+[Option Input Source:Docking-Station]
+name = input-docking
+priority = 17
+
+[Option Input Source:AUX IN]
+name = input
+priority = 10
+
+;;; 'Capture Source'
+
+[Element Capture Source]
+enumeration = select
+
+[Option Capture Source:TV Tuner]
+name = input-video
+
+[Option Capture Source:FM]
+name = input-radio
+
+[Option Capture Source:Mic/Line]
+name = input
+
+[Option Capture Source:Line/Mic]
+name = input
+
+[Option Capture Source:Microphone]
+name = input-microphone
+
+[Option Capture Source:Int DMic]
+name = input-microphone-internal
+
+[Option Capture Source:iMic]
+name = input-microphone-internal
+
+[Option Capture Source:i-Mic]
+name = input-microphone-internal
+
+[Option Capture Source:Internal Microphone]
+name = input-microphone-internal
+
+[Option Capture Source:Front Microphone]
+name = input-microphone
+
+[Option Capture Source:Mic1]
+name = input-microphone
+
+[Option Capture Source:Mic2]
+name = input-microphone
+
+[Option Capture Source:D-Mic]
+name = input-microphone
+
+[Option Capture Source:IntMic]
+name = input-microphone-internal
+
+[Option Capture Source:ExtMic]
+name = input-microphone-external
+
+[Option Capture Source:Ext Mic]
+name = input-microphone-external
+
+[Option Capture Source:E-Mic]
+name = input-microphone-external
+
+[Option Capture Source:e-Mic]
+name = input-microphone-external
+
+[Option Capture Source:LineIn]
+name = input-linein
+
+[Option Capture Source:Analog]
+name = input
+
+[Option Capture Source:Line-In]
+name = input-linein
+
+[Option Capture Source:Line In]
+name = input-linein
+
+[Option Capture Source:Video]
+name = input-video
+
+[Option Capture Source:Aux]
+name = input
+
+[Option Capture Source:Aux0]
+name = input
+
+[Option Capture Source:Aux1]
+name = input
+
+[Option Capture Source:Aux2]
+name = input
+
+[Option Capture Source:Aux3]
+name = input
+
+[Option Capture Source:AUX IN]
+name = input
+
+[Option Capture Source:Aux In]
+name = input
+
+[Option Capture Source:AOUT]
+name = input
+
+[Option Capture Source:AUX]
+name = input
+
+[Option Capture Source:Cam Mic]
+name = input-microphone
+
+[Option Capture Source:Digital Mic]
+name = input-microphone
+
+[Option Capture Source:Digital Mic 1]
+name = input-microphone
+
+[Option Capture Source:Digital Mic 2]
+name = input-microphone
+
+[Option Capture Source:Analog Inputs]
+name = input
+
+[Option Capture Source:Unknown1]
+name = input
+
+[Option Capture Source:Unknown2]
+name = input
+
+[Option Capture Source:Docking-Station]
+name = input-docking
+
+;;; 'Mic Jack Mode'
+
+[Element Mic Jack Mode]
+enumeration = select
+
+[Option Mic Jack Mode:Mic In]
+name = input-microphone
+
+[Option Mic Jack Mode:Line In]
+name = input-linein
+
+;;; 'Digital Input Source'
+
+[Element Digital Input Source]
+enumeration = select
+
+[Option Digital Input Source:Digital Mic 1]
+name = input-microphone
+
+[Option Digital Input Source:Analog Inputs]
+name = input
+
+[Option Digital Input Source:Digital Mic 2]
+name = input-microphone
+
+;;; 'Analog Source'
+
+[Element Analog Source]
+enumeration = select
+
+[Option Analog Source:Mic]
+name = input-microphone
+
+[Option Analog Source:Line in]
+name = input-linein
+
+[Option Analog Source:Aux]
+name = input
+
+;;; 'Shared Mic/Line in'
+
+[Element Shared Mic/Line in]
+enumeration = select
+
+[Option Shared Mic/Line in:Mic in]
+name = input-microphone
+
+[Option Shared Mic/Line in:Line in]
+name = input-linein
+
+;;; Various Boosts
+
+[Element Capture Boost]
+switch = select
+
+[Option Capture Boost:on]
+name = input-boost-on
+
+[Option Capture Boost:off]
+name = input-boost-off
+
+[Element Auto Gain Control]
+switch = select
+
+[Option Auto Gain Control:on]
+name = input-agc-on
+
+[Option Auto Gain Control:off]
+name = input-agc-off
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf b/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
new file mode 100644 (file)
index 0000000..ddabf77
--- /dev/null
@@ -0,0 +1,102 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Path for mixers that have a 'Headphone2' control
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 89
+description-key = analog-output-headphones
+
+[Properties]
+device.icon_name = audio-headphones
+
+[Element Hardware Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master Mono]
+switch = off
+volume = off
+
+; This profile path is intended to control the second headphones, not
+; the first headphones. But it should not hurt if we leave the
+; headphone jack enabled nonetheless.
+[Element Headphone]
+switch = mute
+volume = zero
+
+[Element Headphone+LO]
+switch = mute
+volume = zero
+
+[Element Speaker+LO]
+switch = off
+volume = off
+
+[Element Headphone2]
+required = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Speaker]
+switch = off
+volume = off
+
+[Element Desktop Speaker]
+switch = off
+volume = off
+
+; On some machines Front is actually a part of the Headphone path
+[Element Front]
+switch = mute
+volume = zero
+
+[Element Rear]
+switch = off
+volume = off
+
+[Element Surround]
+switch = off
+volume = off
+
+[Element Side]
+switch = off
+volume = off
+
+[Element Center]
+switch = off
+volume = off
+
+[Element LFE]
+switch = off
+volume = off
+
+[Element Bass Speaker]
+switch = off
+volume = off
+
+.include analog-output.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
new file mode 100644 (file)
index 0000000..b6ee70b
--- /dev/null
@@ -0,0 +1,160 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Path for mixers that have a 'Headphone' control
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 90
+description-key = analog-output-headphones
+
+[Properties]
+device.icon_name = audio-headphones
+
+[Jack Dock Headphone]
+required-any = any
+
+[Jack Dock Headphone Phantom]
+required-any = any
+state.plugged = unknown
+state.unplugged = unknown
+
+[Jack Front Headphone]
+required-any = any
+
+[Jack Front Headphone Phantom]
+required-any = any
+state.plugged = unknown
+state.unplugged = unknown
+
+[Jack Headphone]
+required-any = any
+
+[Jack Headphone Phantom]
+required-any = any
+state.plugged = unknown
+state.unplugged = unknown
+
+# This jack can be either a headphone *or* a mic. Used on some ASUS netbooks.
+[Jack Headphone Mic]
+required-any = any
+
+[Element Hardware Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master Mono]
+switch = off
+volume = off
+
+[Element Speaker+LO]
+switch = off
+volume = off
+
+[Element Headphone+LO]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Headphone]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Headset]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Line HP Swap]
+switch = on
+required-any = any
+
+; This profile path is intended to control the first headphones, not
+; the second headphones. But it should not hurt if we leave the second
+; headphone jack enabled nonetheless.
+[Element Headphone2]
+switch = mute
+volume = zero
+
+[Element Speaker]
+switch = off
+volume = off
+
+[Element Desktop Speaker]
+switch = off
+volume = off
+
+; On some machines Front is actually a part of the Headphone path
+[Element Front]
+switch = mute
+volume = zero
+
+[Element Rear]
+switch = off
+volume = off
+
+[Element Surround]
+switch = off
+volume = off
+
+[Element Side]
+switch = off
+volume = off
+
+[Element Center]
+switch = off
+volume = off
+
+[Element LFE]
+switch = off
+volume = off
+
+[Element Bass Speaker]
+switch = off
+volume = off
+
+[Element Speaker Front]
+switch = off
+volume = off
+
+[Element Speaker Surround]
+switch = off
+volume = off
+
+[Element Speaker Side]
+switch = off
+volume = off
+
+[Element Speaker CLFE]
+switch = off
+volume = off
+
+.include analog-output.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-output-lineout.conf b/src/modules/alsa/mixer/paths/analog-output-lineout.conf
new file mode 100644 (file)
index 0000000..4add78d
--- /dev/null
@@ -0,0 +1,212 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+[General]
+priority = 99
+description-key = analog-output-lineout
+
+[Jack Line Out]
+required-any = any
+
+[Jack Line Out Phantom]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Jack Headphone]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Front Headphone]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Front Line Out]
+required-any = any
+
+[Jack Front Line Out Phantom]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Jack Rear Line Out]
+required-any = any
+
+[Jack Rear Line Out Phantom]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Jack Line Out Front]
+required-any = any
+
+[Jack Line Out Front Phantom]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Jack Line Out CLFE]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Jack Line Out CLFE Phantom]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Jack Line Out Surround]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Jack Line Out Surround Phantom]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Jack Line Out Side]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Jack Line Out Side Phantom]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Jack Dock Line Out]
+required-any = any
+
+[Jack Dock Line Out Phantom]
+state.plugged = unknown
+state.unplugged = unknown
+required-any = any
+
+[Element Hardware Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Speaker+LO]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+required-any = any
+
+[Element Headphone+LO]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+required-any = any
+
+[Element Master Mono]
+switch = off
+volume = off
+
+[Element Line HP Swap]
+switch = off
+required-any = any
+
+; This profile path is intended to control line out, let's mute headphones
+; else there will be a spike when plugging in headphones
+[Element Headphone]
+switch = off
+volume = off
+
+[Element Headphone2]
+switch = off
+volume = off
+
+[Element Speaker]
+switch = off
+volume = off
+
+[Element Desktop Speaker]
+switch = off
+volume = off
+
+[Element Front]
+switch = mute
+volume = merge
+override-map.1 = all-front
+override-map.2 = front-left,front-right
+
+[Element Rear]
+switch = mute
+volume = merge
+override-map.1 = all-rear
+override-map.2 = rear-left,rear-right
+
+[Element Surround]
+switch = mute
+volume = merge
+override-map.1 = all-rear
+override-map.2 = rear-left,rear-right
+
+[Element Side]
+switch = mute
+volume = merge
+override-map.1 = all-side
+override-map.2 = side-left,side-right
+
+[Element Center]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,all-center
+
+[Element LFE]
+switch = mute
+volume = merge
+override-map.1 = lfe
+override-map.2 = lfe,lfe
+
+[Element CLFE]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,lfe
+
+[Element Bass Speaker]
+switch = off
+volume = off
+
+[Element Speaker Front]
+switch = off
+volume = off
+
+[Element Speaker Surround]
+switch = off
+volume = off
+
+[Element Speaker Side]
+switch = off
+volume = off
+
+[Element Speaker CLFE]
+switch = off
+volume = off
+
+.include analog-output.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-output-mono.conf b/src/modules/alsa/mixer/paths/analog-output-mono.conf
new file mode 100644 (file)
index 0000000..9896543
--- /dev/null
@@ -0,0 +1,95 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Intended for usage on boards that have a separate Mono output plug.
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 50
+
+[Element Hardware Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master]
+switch = off
+volume = off
+
+[Element Master Mono]
+required = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+; This profile path is intended to control the speaker, not the
+; headphones. But it should not hurt if we leave the headphone jack
+; enabled nonetheless.
+[Element Headphone]
+switch = mute
+volume = zero
+
+[Element Headphone+LO]
+switch = mute
+volume = zero
+
+[Element Headphone2]
+switch = mute
+volume = zero
+
+[Element Speaker]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Speaker+LO]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Desktop Speaker]
+switch = off
+volume = off
+
+[Element Front]
+switch = off
+volume = off
+
+[Element Rear]
+switch = off
+volume = off
+
+[Element Surround]
+switch = off
+volume = off
+
+[Element Side]
+switch = off
+volume = off
+
+[Element Center]
+switch = off
+volume = off
+
+[Element LFE]
+switch = off
+volume = off
+
+.include analog-output.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf b/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf
new file mode 100644 (file)
index 0000000..71f356d
--- /dev/null
@@ -0,0 +1,177 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Path for mixers that don't have a 'Speaker' control, but where we
+; force enable the speaker paths nonetheless.
+; Needed for some older Dell laptops.
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 100
+description-key = analog-output-speaker
+
+[Properties]
+device.icon_name = audio-speakers
+
+[Jack Headphone]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Front Headphone]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Line Out]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Line Out Front]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Front Line Out]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Rear Line Out]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Dock Line Out]
+state.plugged = no
+state.unplugged = unknown
+
+[Element Hardware Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master Mono]
+switch = off
+volume = off
+
+; This profile path is intended to control the speaker, not the
+; headphones. But it should not hurt if we leave the headphone jack
+; enabled nonetheless.
+[Element Headphone]
+switch = mute
+volume = zero
+
+[Element Headphone2]
+switch = mute
+volume = zero
+
+[Element Headphone+LO]
+switch = off
+volume = off
+
+[Element Speaker+LO]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Speaker]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Desktop Speaker]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Front]
+switch = mute
+volume = merge
+override-map.1 = all-front
+override-map.2 = front-left,front-right
+
+[Element Front Speaker]
+switch = mute
+volume = merge
+override-map.1 = all-front
+override-map.2 = front-left,front-right
+
+[Element Rear]
+switch = mute
+volume = merge
+override-map.1 = all-rear
+override-map.2 = rear-left,rear-right
+
+[Element Surround]
+switch = mute
+volume = merge
+override-map.1 = all-rear
+override-map.2 = rear-left,rear-right
+
+[Element Surround Speaker]
+switch = mute
+volume = merge
+override-map.1 = all-rear
+override-map.2 = rear-left,rear-right
+
+[Element Side]
+switch = mute
+volume = merge
+override-map.1 = all-side
+override-map.2 = side-left,side-right
+
+[Element Center]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,all-center
+
+[Element Center Speaker]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,all-center
+
+[Element LFE]
+switch = mute
+volume = merge
+override-map.1 = lfe
+override-map.2 = lfe,lfe
+
+[Element LFE Speaker]
+switch = mute
+volume = merge
+override-map.1 = lfe
+override-map.2 = lfe,lfe
+
+[Element Bass Speaker]
+switch = mute
+volume = merge
+override-map.1 = lfe
+override-map.2 = lfe,lfe
+
+[Element CLFE]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,lfe
+
+.include analog-output.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-output-speaker.conf b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
new file mode 100644 (file)
index 0000000..9f4dac4
--- /dev/null
@@ -0,0 +1,223 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Path for mixers that have a 'Speaker' control
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 100
+description-key = analog-output-speaker
+
+[Properties]
+device.icon_name = audio-speakers
+
+[Jack Headphone]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Dock Headphone]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Front Headphone]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Line Out]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Line Out Front]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Front Line Out]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Rear Line Out]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Dock Line Out]
+state.plugged = no
+state.unplugged = unknown
+
+[Jack Speaker Phantom]
+required-any = any
+state.plugged = unknown
+state.unplugged = unknown
+
+[Jack Speaker Front Phantom]
+required-any = any
+state.plugged = unknown
+state.unplugged = unknown
+
+[Element Hardware Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master Mono]
+switch = off
+volume = off
+
+; This profile path is intended to control the speaker, let's mute headphones
+; else there will be a spike when plugging in headphones
+[Element Headphone]
+switch = off
+volume = off
+
+[Element Headphone2]
+switch = off
+volume = off
+
+[Element Headphone+LO]
+switch = off
+volume = off
+
+[Element Speaker+LO]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Speaker]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Desktop Speaker]
+required-any = any
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Front]
+switch = mute
+volume = merge
+override-map.1 = all-front
+override-map.2 = front-left,front-right
+
+[Element Front Speaker]
+switch = mute
+volume = merge
+override-map.1 = all-front
+override-map.2 = front-left,front-right
+required-any = any
+
+[Element Speaker Front]
+switch = mute
+volume = merge
+override-map.1 = all-front
+override-map.2 = front-left,front-right
+required-any = any
+
+[Element Rear]
+switch = mute
+volume = merge
+override-map.1 = all-rear
+override-map.2 = rear-left,rear-right
+
+[Element Surround]
+switch = mute
+volume = merge
+override-map.1 = all-rear
+override-map.2 = rear-left,rear-right
+
+[Element Surround Speaker]
+switch = mute
+volume = merge
+override-map.1 = all-rear
+override-map.2 = rear-left,rear-right
+required-any = any
+
+[Element Speaker Surround]
+switch = mute
+volume = merge
+override-map.1 = all-rear
+override-map.2 = rear-left,rear-right
+required-any = any
+
+[Element Side]
+switch = mute
+volume = merge
+override-map.1 = all-side
+override-map.2 = side-left,side-right
+
+[Element Speaker Side]
+switch = mute
+volume = merge
+override-map.1 = all-side
+override-map.2 = side-left,side-right
+
+[Element Center]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,all-center
+
+[Element Center Speaker]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,all-center
+required-any = any
+
+[Element LFE]
+switch = mute
+volume = merge
+override-map.1 = lfe
+override-map.2 = lfe,lfe
+
+[Element LFE Speaker]
+switch = mute
+volume = merge
+override-map.1 = lfe
+override-map.2 = lfe,lfe
+required-any = any
+
+[Element Bass Speaker]
+switch = mute
+volume = merge
+override-map.1 = lfe
+override-map.2 = lfe,lfe
+required-any = any
+
+[Element CLFE]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,lfe
+
+[Element Speaker CLFE]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,lfe
+
+.include analog-output.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-output.conf b/src/modules/alsa/mixer/paths/analog-output.conf
new file mode 100644 (file)
index 0000000..e6ba983
--- /dev/null
@@ -0,0 +1,82 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Intended for the 'default' output. Note that a-o-speaker.conf has a
+; higher priority than this
+;
+; See analog-output.conf.common for an explanation on the directives
+
+[General]
+priority = 99
+
+[Element Hardware Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element Master Mono]
+switch = off
+volume = off
+
+[Element Front]
+switch = mute
+volume = merge
+override-map.1 = all-front
+override-map.2 = front-left,front-right
+
+[Element Rear]
+switch = mute
+volume = merge
+override-map.1 = all-rear
+override-map.2 = rear-left,rear-right
+
+[Element Surround]
+switch = mute
+volume = merge
+override-map.1 = all-rear
+override-map.2 = rear-left,rear-right
+
+[Element Side]
+switch = mute
+volume = merge
+override-map.1 = all-side
+override-map.2 = side-left,side-right
+
+[Element Center]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,all-center
+
+[Element LFE]
+switch = mute
+volume = merge
+override-map.1 = lfe
+override-map.2 = lfe,lfe
+
+[Element CLFE]
+switch = mute
+volume = merge
+override-map.1 = all-center
+override-map.2 = all-center,lfe
+
+.include analog-output.conf.common
diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common
new file mode 100644 (file)
index 0000000..17b4527
--- /dev/null
@@ -0,0 +1,175 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Common part of all paths
+
+; So here's generally how mixer paths are used by PA: PA goes through
+; a mixer path file from top to bottom and checks if a mixer element
+; described therein exists. If so it is added to the list of mixer
+; elements PA will control, keeping the order it read them in. If a
+; mixer element described here has set the required= or
+; required-absent= directives a path might not be accepted as valid
+; and is ignored in its entirety (see below). However usually if a
+; element listed here is missing this one element is ignored but not
+; the entire path.
+;
+; When a device shall be muted/unmuted *all* elements listed in a path
+; file with "switch = mute" will be toggled.
+;
+; When a device shall change its volume, PA will got through the list
+; of all elements with "volume = merge" and set the volume on the
+; first element. If that element does not support dB volumes, this is
+; where the story ends. If it does support dB volumes, PA divides the
+; requested volume by the volume that was set on this element, and
+; then go on to the next element with "volume = merge" and then set
+; that there, and so on.  That way the first volume element in the
+; path will be the one that does the 'biggest' part of the overall
+; volume adjustment, with the remaining elements usually being set to
+; some value next to 0dB. This logic makes sure we get the full range
+; over all volume sliders and a very high granularity of volumes
+; already in hardware.
+;
+; All switches and enumerations set to "select" are exposed via the
+; "port" functionality of sinks/sources. Basically every possible
+; switch setting and every possible enumeration setting will be
+; combined and made into a "port". So make sure you don't list too
+; many switches/enums for exposing, because the number of ports might
+; rise exponentially.
+;
+; Only one path can be selected at a time. All paths that are valid
+; for an audio device will be exposed as "port" for the sink/source.
+
+
+; [General]
+; priority = ...                         # Priority for this path
+; description-key = ...                  # The path description is looked up from a table in path_verify() in
+;                                        # src/modules/alsa/alsa-mixer.c. By default the path name (i.e. the file name
+;                                        # minus the ".conf" suffix) is used as the lookup key, but if this option is
+;                                        # set, then the given string is used as the key instead. In any case the
+;                                        # "description" option can be used to override the path description.
+; description = ...                      # Description for this path. Overrides the normal description lookup logic, as
+;                                        # described in the "description-key" documentation above.
+; mute-during-activation = yes | no      # If this path supports hardware mute, should the hw mute be used while activating this
+;                                        # path? In some cases this can reduce extra noises during port switching, while in other
+;                                        # cases this can increase such noises. Default: no.
+; eld-device = ...                       # If this is an HDMI port, here's where to specify the device number for the ELD mixer
+;                                        # control. The default is to not make use of ELD information.
+;
+; [Properties]                           # Property list for this path. The list is merged into the port property list.
+; <key> = <value>                        # Each property is defined on its own line.
+; ...
+;
+; [Option ...:...]                       # For each option of an enumeration or switch element
+;                                        # that shall be exposed as a sink/source port. Needs to
+;                                        # be named after the Element, followed by a colon, followed
+;                                        # by the option name, resp. on/off if the element is a switch.
+; name = ...                             # Logical name to use in the path identifier
+; priority = ...                         # Priority if this is made into a device port
+; required = ignore | enumeration | any            # In this element, this option must exist or the path will be invalid. ("any" is an alias for "enumeration".)
+; required-any = ignore | enumeration | any        # In this element, either this or another option must exist (or an element)
+; required-absent = ignore | enumeration | any     # In this element, this option must not exist or the path will be invalid
+;
+; [Element ...]                          # For each element that we shall control
+; required = ignore | switch | volume | enumeration | any     # If set, require this element to be of this kind and available,
+;                                                             # otherwise don't consider this path valid for the card
+; required-any = ignore | switch | volume | enumeration | any # If set, at least one of the elements or jacks with required-any in this
+;                                                             # path must be present, otherwise this path is invalid for the card
+; required-absent = ignore | switch | volume                  # If set, require this element to not be of this kind and not
+;                                                             # available, otherwise don't consider this path valid for the card
+;
+; switch = ignore | mute | off | on | select # What to do with this switch: ignore it, make it follow mute status,
+;                                            # always set it to off, always to on, or make it selectable as port.
+;                                            # If set to 'select' you need to define an Option section for on
+;                                            # and off
+; volume = ignore | merge | off | zero | <volume step> # What to do with this volume: ignore it, merge it into the device
+;                                                      # volume slider, always set it to the lowest value possible, or always
+;                                                      # set it to 0 dB (for whatever that means), or always set it to
+;                                                      # <volume step> (this only makes sense in path configurations where
+;                                                      # the exact hardware and driver are known beforehand).
+; volume-limit = <volume step>           # Limit the maximum volume by disabling the volume steps above <volume step>.
+; enumeration = ignore | select          # What to do with this enumeration, ignore it or make it selectable
+;                                        # via device ports. If set to 'select' you need to define an Option section
+;                                        # for each of the items you want to expose
+; direction = playback | capture         # Is this relevant only for playback or capture? If not set this will implicitly be
+;                                        # set the direction of the PCM device is opened as. Generally this doesn't need to be set
+;                                        # unless you have a broken driver that has playback controls marked for capture or vice
+;                                        # versa
+; direction-try-other = no | yes         # If the element does not supported what is requested, try the other direction, too?
+;
+; override-map.1 = ...                   # Override the channel mask of the mixer control if the control only exposes a single channel
+; override-map.2 = ...                   # Override the channel masks of the mixer control if the control only exposes two channels
+;                                        # Override maps should list for each element channel which high-level channels it controls via a
+;                                        # channel mask. A channel mask may either be the name of a single channel, or the words "all-left",
+;                                        # "all-right", "all-center", "all-front", "all-rear", and "all" to encode a specific subset of
+;                                        # channels in a mask
+; [Jack ...]                           # For each jack that we will use for jack detection
+;                                      # The name 'Jack Foo' must match ALSA's 'Foo Jack' control.
+; required = ignore | any              # If not set to ignore, make the path invalid if this jack control is not present.
+; required-absent = ignore | any       # If not set to ignore, make the path invalid if this jack control is present.
+; required-any = ignore | any          # If not set to ignore, make the path invalid if no jack controls and no elements with
+;                                      # the required-any are present.
+; state.plugged = yes | no | unknown   # Normally a plugged jack would mean the port becomes available, and an unplugged means it's
+; state.unplugged = yes | no | unknown # unavailable, but the port status can be overridden by specifying state.plugged and/or state.unplugged.
+
+[Element PCM]
+switch = mute
+volume = merge
+override-map.1 = all
+override-map.2 = all-left,all-right
+
+[Element External Amplifier]
+switch = select
+
+[Option External Amplifier:on]
+name = output-amplifier-on
+priority = 10
+
+[Option External Amplifier:off]
+name = output-amplifier-off
+priority = 0
+
+[Element Bass Boost]
+switch = select
+
+[Option Bass Boost:on]
+name = output-bass-boost-on
+priority = 0
+
+[Option Bass Boost:off]
+name = output-bass-boost-off
+priority = 10
+
+[Element IEC958]
+switch = off
+
+[Element IEC958 Optical Raw]
+switch = off
+
+;;; 'Analog Output'
+
+[Element Analog Output]
+enumeration = select
+
+[Option Analog Output:Speakers]
+name = output-speaker
+priority = 10
+
+[Option Analog Output:Headphones]
+name = output-headphones
+priority = 9
+
+[Option Analog Output:FP Headphones]
+name = output-headphones
+priority = 8
diff --git a/src/modules/alsa/mixer/paths/hdmi-output-0.conf b/src/modules/alsa/mixer/paths/hdmi-output-0.conf
new file mode 100644 (file)
index 0000000..3310147
--- /dev/null
@@ -0,0 +1,10 @@
+[General]
+description = HDMI / DisplayPort
+priority = 59
+eld-device = 3
+
+[Properties]
+device.icon_name = video-display
+
+[Jack HDMI/DP,pcm=3]
+required = ignore
diff --git a/src/modules/alsa/mixer/paths/hdmi-output-1.conf b/src/modules/alsa/mixer/paths/hdmi-output-1.conf
new file mode 100644 (file)
index 0000000..d81ee78
--- /dev/null
@@ -0,0 +1,10 @@
+[General]
+description = HDMI / DisplayPort 2
+priority = 58
+eld-device = 7
+
+[Properties]
+device.icon_name = video-display
+
+[Jack HDMI/DP,pcm=7]
+required = ignore
diff --git a/src/modules/alsa/mixer/paths/hdmi-output-2.conf b/src/modules/alsa/mixer/paths/hdmi-output-2.conf
new file mode 100644 (file)
index 0000000..349812f
--- /dev/null
@@ -0,0 +1,10 @@
+[General]
+description = HDMI / DisplayPort 3
+priority = 57
+eld-device = 8
+
+[Properties]
+device.icon_name = video-display
+
+[Jack HDMI/DP,pcm=8]
+required = ignore
diff --git a/src/modules/alsa/mixer/paths/hdmi-output-3.conf b/src/modules/alsa/mixer/paths/hdmi-output-3.conf
new file mode 100644 (file)
index 0000000..81463c9
--- /dev/null
@@ -0,0 +1,10 @@
+[General]
+description = HDMI / DisplayPort 4
+priority = 56
+eld-device = 9
+
+[Properties]
+device.icon_name = video-display
+
+[Jack HDMI/DP,pcm=9]
+required = ignore
diff --git a/src/modules/alsa/mixer/paths/hdmi-output-4.conf b/src/modules/alsa/mixer/paths/hdmi-output-4.conf
new file mode 100644 (file)
index 0000000..d61ec75
--- /dev/null
@@ -0,0 +1,10 @@
+[General]
+description = HDMI / DisplayPort 5
+priority = 55
+eld-device = 10
+
+[Properties]
+device.icon_name = video-display
+
+[Jack HDMI/DP,pcm=10]
+required = ignore
diff --git a/src/modules/alsa/mixer/paths/hdmi-output-5.conf b/src/modules/alsa/mixer/paths/hdmi-output-5.conf
new file mode 100644 (file)
index 0000000..02c15e8
--- /dev/null
@@ -0,0 +1,10 @@
+[General]
+description = HDMI / DisplayPort 6
+priority = 54
+eld-device = 11
+
+[Properties]
+device.icon_name = video-display
+
+[Jack HDMI/DP,pcm=11]
+required = ignore
diff --git a/src/modules/alsa/mixer/paths/hdmi-output-6.conf b/src/modules/alsa/mixer/paths/hdmi-output-6.conf
new file mode 100644 (file)
index 0000000..188a1ad
--- /dev/null
@@ -0,0 +1,10 @@
+[General]
+description = HDMI / DisplayPort 7
+priority = 53
+eld-device = 12
+
+[Properties]
+device.icon_name = video-display
+
+[Jack HDMI/DP,pcm=12]
+required = ignore
diff --git a/src/modules/alsa/mixer/paths/hdmi-output-7.conf b/src/modules/alsa/mixer/paths/hdmi-output-7.conf
new file mode 100644 (file)
index 0000000..80f4e37
--- /dev/null
@@ -0,0 +1,10 @@
+[General]
+description = HDMI / DisplayPort 8
+priority = 52
+eld-device = 13
+
+[Properties]
+device.icon_name = video-display
+
+[Jack HDMI/DP,pcm=13]
+required = ignore
diff --git a/src/modules/alsa/mixer/paths/iec958-stereo-output.conf b/src/modules/alsa/mixer/paths/iec958-stereo-output.conf
new file mode 100644 (file)
index 0000000..d47e5eb
--- /dev/null
@@ -0,0 +1,18 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+
+[Element IEC958]
+switch = mute
diff --git a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules
new file mode 100644 (file)
index 0000000..70e34e6
--- /dev/null
@@ -0,0 +1,102 @@
+# do not edit this file, it will be overwritten on update
+
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+SUBSYSTEM!="sound", GOTO="pulseaudio_end"
+ACTION!="change", GOTO="pulseaudio_end"
+KERNEL!="card*", GOTO="pulseaudio_end"
+SUBSYSTEMS=="usb", GOTO="pulseaudio_check_usb"
+
+SUBSYSTEMS=="platform", DRIVERS=="thinkpad_acpi", ENV{PULSE_IGNORE}="1"
+
+# Force enable speaker and internal mic for some laptops
+# This should only be necessary for kernels 3.3, 3.4 and 3.5 (as they are lacking the phantom jack kctls).
+# Acer AOA150
+ATTRS{subsystem_vendor}=="0x1025", ATTRS{subsystem_device}=="0x015b", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Acer Aspire 4810TZ
+ATTRS{subsystem_vendor}=="0x1025", ATTRS{subsystem_device}=="0x022a", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Packard bell dot m/a
+ATTRS{subsystem_vendor}=="0x1025", ATTRS{subsystem_device}=="0x028c", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Acer Aspire 1810TZ
+ATTRS{subsystem_vendor}=="0x1025", ATTRS{subsystem_device}=="0x029b", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Acer AOD260 and AO532h
+ATTRS{subsystem_vendor}=="0x1025", ATTRS{subsystem_device}=="0x0349", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Dell MXC051
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x01b5", ENV{PULSE_PROFILE_SET}="force-speaker.conf"
+# Dell Inspiron 6400 and E1505
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x01bd", ENV{PULSE_PROFILE_SET}="force-speaker.conf"
+# Dell Latitude D620
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x01c2", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Dell Latitude D820
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x01cc", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Dell Latitude D520
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x01d4", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Dell Latitude D420
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x01d6", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Dell Inspiron 1525
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x022f", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Dell Inspiron 1011
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x02f4", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Dell XPS 14 (L401X)
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x0468", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Dell XPS 15 (L501X)
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x046e", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Dell XPS 15 (L502X)
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x050e", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Dell Inspiron 3420
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x0553", ENV{PULSE_PROFILE_SET}="force-speaker.conf"
+# Dell Inspiron 3520
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x0555", ENV{PULSE_PROFILE_SET}="force-speaker.conf"
+# Dell Vostro 2420
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x0556", ENV{PULSE_PROFILE_SET}="force-speaker.conf"
+# Dell Vostro 2520
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x0558", ENV{PULSE_PROFILE_SET}="force-speaker.conf"
+# Dell Inspiron One 2020
+ATTRS{subsystem_vendor}=="0x1028", ATTRS{subsystem_device}=="0x0579", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Asus 904HA (1000H)
+ATTRS{subsystem_vendor}=="0x1043", ATTRS{subsystem_device}=="0x831a", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Asus T101MT
+ATTRS{subsystem_vendor}=="0x1043", ATTRS{subsystem_device}=="0x83ce", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Sony Vaio VGN-SR21M
+ATTRS{subsystem_vendor}=="0x104d", ATTRS{subsystem_device}=="0x9033", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Sony Vaio VPC-W115XG
+ATTRS{subsystem_vendor}=="0x104d", ATTRS{subsystem_device}=="0x9064", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Fujitsu Lifebook S7110
+ATTRS{subsystem_vendor}=="0x10cf", ATTRS{subsystem_device}=="0x1397", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Fujitsu Lifebook A530
+ATTRS{subsystem_vendor}=="0x10cf", ATTRS{subsystem_device}=="0x1531", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Toshiba A200
+ATTRS{subsystem_vendor}=="0x1179", ATTRS{subsystem_device}=="0xff00", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# MSI X360
+ATTRS{subsystem_vendor}=="0x1462", ATTRS{subsystem_device}=="0x1053", ENV{PULSE_PROFILE_SET}="force-speaker-and-int-mic.conf"
+# Lenovo 3000 Y410
+ATTRS{subsystem_vendor}=="0x17aa", ATTRS{subsystem_device}=="0x384e", ENV{PULSE_PROFILE_SET}="force-speaker.conf"
+
+GOTO="pulseaudio_end"
+
+LABEL="pulseaudio_check_usb"
+ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1978", ENV{PULSE_PROFILE_SET}="native-instruments-audio8dj.conf"
+ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="0839", ENV{PULSE_PROFILE_SET}="native-instruments-audio4dj.conf"
+ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="baff", ENV{PULSE_PROFILE_SET}="native-instruments-traktorkontrol-s4.conf"
+ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="4711", ENV{PULSE_PROFILE_SET}="native-instruments-korecontroller.conf"
+ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="041d", ENV{PULSE_PROFILE_SET}="native-instruments-traktor-audio2.conf"
+ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1011", ENV{PULSE_PROFILE_SET}="native-instruments-traktor-audio6.conf"
+ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1021", ENV{PULSE_PROFILE_SET}="native-instruments-traktor-audio10.conf"
+ATTRS{idVendor}=="0763", ATTRS{idProduct}=="2012", ENV{PULSE_PROFILE_SET}="maudio-fasttrack-pro.conf"
+ATTRS{idVendor}=="045e", ATTRS{idProduct}=="02bb", ENV{PULSE_PROFILE_SET}="kinect-audio.conf"
+ATTRS{idVendor}=="041e", ATTRS{idProduct}=="322c", ENV{PULSE_PROFILE_SET}="sb-omni-surround-5.1.conf"
+
+LABEL="pulseaudio_end"
diff --git a/src/modules/alsa/mixer/profile-sets/default.conf b/src/modules/alsa/mixer/profile-sets/default.conf
new file mode 100644 (file)
index 0000000..c360e77
--- /dev/null
@@ -0,0 +1,476 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Default profile definitions for the ALSA backend of PulseAudio. This
+; is used as fallback for all cards that have no special mapping
+; assigned (and should be good enough for the vast majority of
+; cards). If you want to assign a different profile set than this one
+; to a device, either set the udev property PULSE_PROFILE_SET for the
+; card, or use the "profile_set" module argument when loading
+; module-alsa-card.
+;
+; So what is this about? Simply, what we do here is map ALSA devices
+; to how they are exposed in PA. We say which ALSA device string to
+; use to open a device, which channel mapping to use then, and which
+; mixer path to use. This is encoded in a 'mapping'. Multiple of these
+; mappings can be bound together in a 'profile' which is then directly
+; exposed in the UI as a card profile. Each mapping assigned to a
+; profile will result in one sink/source to be created if the profile
+; is selected for the card.
+;
+; Additionally, the path set configuration files can describe the
+; decibel values assigned to the steps of the volume elements. This
+; can be used to work around situations when the alsa driver doesn't
+; provide any decibel information, or when the information is
+; incorrect.
+
+
+; [General]
+; auto-profiles = no | yes                  # Instead of defining all profiles manually, autogenerate
+;                                           # them by combining every input mapping with every output mapping.
+;
+; [Mapping id]
+; device-strings = ...                      # ALSA device string. %f will be replaced by the card identifier.
+; channel-map = ...                         # Channel mapping to use for this device
+; description = ...
+; paths-input = ...                         # A list of mixer paths to use. Every path in this list will be probed.
+;                                           # If multiple are found to be working they will be available as device ports
+; paths-output = ...
+; element-input = ...                       # Instead of configuring a full mixer path simply configure a single
+;                                           # mixer element for volume/mute handling
+; element-output = ...
+; priority = ...
+; direction = any | input | output          # Only useful for?
+;
+; exact-channels = yes | no                 # If no, and the exact number of channels is not supported,
+;                                           # allow device to be opened with another channel count
+; fallback = no | yes                       # This mapping will only be considered if all non-fallback mappings fail
+; [Profile id]
+; input-mappings = ...                      # Lists mappings for sources on this profile, those mapping must be
+;                                           # defined in this file too
+; output-mappings = ...                     # Lists mappings for sinks on this profile, those mappings must be
+;                                           # defined in this file too
+; description = ...
+; priority = ...                            # Numeric value to deduce priority for this profile
+; skip-probe = no | yes                     # Skip probing for availability? If this is yes then this profile
+;                                           # will be assumed as working without probing. Makes initialization
+;                                           # a bit faster but only works if the card is really known well.
+;
+; fallback = no | yes                       # This profile will only be considered if all non-fallback profiles fail
+; [DecibelFix element]                      # Decibel fixes can be used to work around missing or incorrect dB
+;                                           # information from alsa. A decibel fix is a table that maps volume steps
+;                                           # to decibel values for one volume element. The "element" part in the
+;                                           # section title is the name of the volume element.
+;                                           #
+;                                           # NOTE: This feature is meant just as a help for figuring out the correct
+;                                           # decibel values. PulseAudio is not the correct place to maintain the
+;                                           # decibel mappings!
+;                                           #
+;                                           # If you need this feature, then you should make sure that when you have
+;                                           # the correct values figured out, the alsa driver developers get informed
+;                                           # too, so that they can fix the driver.
+;
+; db-values = ...                           # The option value consists of pairs of step numbers and decibel values.
+;                                           # The pairs are separated with whitespace, and steps are separated from
+;                                           # the corresponding decibel values with a colon. The values must be in an
+;                                           # increasing order. Here's an example of a valid string:
+;                                           #
+;                                           #     "0:-40.50  1:-38.70  3:-33.00  11:0"
+;                                           #
+;                                           # The lowest step imposes a lower limit for hardware volume and the
+;                                           # highest step correspondingly imposes a higher limit. That means that
+;                                           # that the mixer will never be set outside those values - the rest of the
+;                                           # volume scale is done using software volume.
+;                                           #
+;                                           # As can be seen in the example, you don't need to specify a dB value for
+;                                           # each step. The dB values for skipped steps will be linearly interpolated
+;                                           # using the nearest steps that are given.
+
+[General]
+auto-profiles = yes
+
+[Mapping analog-mono]
+device-strings = hw:%f
+channel-map = mono
+paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2 analog-output-mono
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headset-mic
+priority = 2
+
+[Mapping analog-stereo]
+device-strings = front:%f
+channel-map = left,right
+paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic
+priority = 10
+
+# If everything else fails, try to use hw:0 as a stereo device.
+[Mapping stereo-fallback]
+device-strings = hw:%f
+fallback = yes
+channel-map = front-left,front-right
+paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic
+priority = 1
+
+[Mapping analog-surround-21]
+device-strings = surround21:%f
+channel-map = front-left,front-right,lfe
+paths-output = analog-output analog-output-lineout analog-output-speaker
+priority = 8
+direction = output
+
+[Mapping analog-surround-40]
+device-strings = surround40:%f
+channel-map = front-left,front-right,rear-left,rear-right
+paths-output = analog-output analog-output-lineout analog-output-speaker
+priority = 7
+direction = output
+
+[Mapping analog-surround-41]
+device-strings = surround41:%f
+channel-map = front-left,front-right,rear-left,rear-right,lfe
+paths-output = analog-output analog-output-lineout analog-output-speaker
+priority = 8
+direction = output
+
+[Mapping analog-surround-50]
+device-strings = surround50:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center
+paths-output = analog-output analog-output-lineout analog-output-speaker
+priority = 7
+direction = output
+
+[Mapping analog-surround-51]
+device-strings = surround51:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+paths-output = analog-output analog-output-lineout analog-output-speaker
+priority = 8
+direction = output
+
+[Mapping analog-surround-71]
+device-strings = surround71:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+description = Analog Surround 7.1
+paths-output = analog-output analog-output-lineout analog-output-speaker
+priority = 7
+direction = output
+
+[Mapping iec958-stereo]
+device-strings = iec958:%f
+channel-map = left,right
+paths-input = iec958-stereo-input
+paths-output = iec958-stereo-output
+priority = 5
+
+[Mapping iec958-ac3-surround-40]
+device-strings = a52:%f
+channel-map = front-left,front-right,rear-left,rear-right
+paths-output = iec958-stereo-output
+priority = 2
+direction = output
+
+[Mapping iec958-ac3-surround-51]
+device-strings = a52:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+paths-output = iec958-stereo-output
+priority = 3
+direction = output
+
+[Mapping iec958-dts-surround-51]
+device-strings = dca:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+paths-output = iec958-stereo-output
+priority = 3
+direction = output
+
+[Mapping hdmi-stereo]
+description = Digital Stereo (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = left,right
+priority = 4
+direction = output
+
+[Mapping hdmi-surround]
+description = Digital Surround 5.1 (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 3
+direction = output
+
+[Mapping hdmi-surround71]
+description = Digital Surround 7.1 (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 3
+direction = output
+
+[Mapping hdmi-dts-surround]
+description = Digital Surround 5.1 (HDMI/DTS)
+device-strings = dcahdmi:%f
+paths-output = hdmi-output-0
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-stereo-extra1]
+description = Digital Stereo (HDMI 2)
+device-strings = hdmi:%f,1
+paths-output = hdmi-output-1
+channel-map = left,right
+priority = 2
+direction = output
+
+[Mapping hdmi-surround-extra1]
+description = Digital Surround 5.1 (HDMI 2)
+device-strings = hdmi:%f,1
+paths-output = hdmi-output-1
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-surround71-extra1]
+description = Digital Surround 7.1 (HDMI 2)
+device-strings = hdmi:%f,1
+paths-output = hdmi-output-1
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 1
+direction = output
+
+[Mapping hdmi-dts-surround-extra1]
+description = Digital Surround 5.1 (HDMI 2/DTS)
+device-strings = dcahdmi:%f,1
+paths-output = hdmi-output-1
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-stereo-extra2]
+description = Digital Stereo (HDMI 3)
+device-strings = hdmi:%f,2
+paths-output = hdmi-output-2
+channel-map = left,right
+priority = 2
+direction = output
+
+[Mapping hdmi-surround-extra2]
+description = Digital Surround 5.1 (HDMI 3)
+device-strings = hdmi:%f,2
+paths-output = hdmi-output-2
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-surround71-extra2]
+description = Digital Surround 7.1 (HDMI 3)
+device-strings = hdmi:%f,2
+paths-output = hdmi-output-2
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 1
+direction = output
+
+[Mapping hdmi-dts-surround-extra2]
+description = Digital Surround 5.1 (HDMI 3/DTS)
+device-strings = dcahdmi:%f,2
+paths-output = hdmi-output-2
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-stereo-extra3]
+description = Digital Stereo (HDMI 4)
+device-strings = hdmi:%f,3
+paths-output = hdmi-output-3
+channel-map = left,right
+priority = 2
+direction = output
+
+[Mapping hdmi-surround-extra3]
+description = Digital Surround 5.1 (HDMI 4)
+device-strings = hdmi:%f,3
+paths-output = hdmi-output-3
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-surround71-extra3]
+description = Digital Surround 7.1 (HDMI 4)
+device-strings = hdmi:%f,3
+paths-output = hdmi-output-3
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 1
+direction = output
+
+[Mapping hdmi-dts-surround-extra3]
+description = Digital Surround 5.1 (HDMI 4/DTS)
+device-strings = dcahdmi:%f,3
+paths-output = hdmi-output-3
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-stereo-extra4]
+description = Digital Stereo (HDMI 5)
+device-strings = hdmi:%f,4
+paths-output = hdmi-output-4
+channel-map = left,right
+priority = 2
+direction = output
+
+[Mapping hdmi-surround-extra4]
+description = Digital Surround 5.1 (HDMI 5)
+device-strings = hdmi:%f,4
+paths-output = hdmi-output-4
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-surround71-extra4]
+description = Digital Surround 7.1 (HDMI 5)
+device-strings = hdmi:%f,4
+paths-output = hdmi-output-4
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 1
+direction = output
+
+[Mapping hdmi-dts-surround-extra4]
+description = Digital Surround 5.1 (HDMI 5/DTS)
+device-strings = dcahdmi:%f,4
+paths-output = hdmi-output-4
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-stereo-extra5]
+description = Digital Stereo (HDMI 6)
+device-strings = hdmi:%f,5
+paths-output = hdmi-output-5
+channel-map = left,right
+priority = 2
+direction = output
+
+[Mapping hdmi-surround-extra5]
+description = Digital Surround 5.1 (HDMI 6)
+device-strings = hdmi:%f,5
+paths-output = hdmi-output-5
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-surround71-extra5]
+description = Digital Surround 7.1 (HDMI 6)
+device-strings = hdmi:%f,5
+paths-output = hdmi-output-5
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 1
+direction = output
+
+[Mapping hdmi-dts-surround-extra5]
+description = Digital Surround 5.1 (HDMI 6/DTS)
+device-strings = dcahdmi:%f,5
+paths-output = hdmi-output-5
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-stereo-extra6]
+description = Digital Stereo (HDMI 7)
+device-strings = hdmi:%f,6
+paths-output = hdmi-output-6
+channel-map = left,right
+priority = 2
+direction = output
+
+[Mapping hdmi-surround-extra6]
+description = Digital Surround 5.1 (HDMI 7)
+device-strings = hdmi:%f,6
+paths-output = hdmi-output-6
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-surround71-extra6]
+description = Digital Surround 7.1 (HDMI 7)
+device-strings = hdmi:%f,6
+paths-output = hdmi-output-6
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 1
+direction = output
+
+[Mapping hdmi-dts-surround-extra6]
+description = Digital Surround 5.1 (HDMI 7/DTS)
+device-strings = dcahdmi:%f,6
+paths-output = hdmi-output-6
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-stereo-extra7]
+description = Digital Stereo (HDMI 8)
+device-strings = hdmi:%f,7
+paths-output = hdmi-output-7
+channel-map = left,right
+priority = 2
+direction = output
+
+[Mapping hdmi-surround-extra7]
+description = Digital Surround 5.1 (HDMI 8)
+device-strings = hdmi:%f,7
+paths-output = hdmi-output-7
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping hdmi-surround71-extra7]
+description = Digital Surround 7.1 (HDMI 8)
+device-strings = hdmi:%f,7
+paths-output = hdmi-output-7
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 1
+direction = output
+
+[Mapping hdmi-dts-surround-extra7]
+description = Digital Surround 5.1 (HDMI 8/DTS)
+device-strings = dcahdmi:%f,7
+paths-output = hdmi-output-7
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+[Mapping multichannel-output]
+device-strings = hw:%f
+channel-map = left,right,rear-left,rear-right
+exact-channels = false
+fallback = yes
+priority = 1
+direction = output
+paths-output = multichannel-output
+
+[Mapping multichannel-input]
+device-strings = hw:%f
+channel-map = left,right,rear-left,rear-right
+exact-channels = false
+fallback = yes
+priority = 1
+direction = input
+paths-input = multichannel-input
+
+; An example for defining multiple-sink profiles
+#[Profile output:analog-stereo+output:iec958-stereo+input:analog-stereo]
+#description = Foobar
+#output-mappings = analog-stereo iec958-stereo
+#input-mappings = analog-stereo
diff --git a/src/modules/alsa/mixer/profile-sets/force-speaker-and-int-mic.conf b/src/modules/alsa/mixer/profile-sets/force-speaker-and-int-mic.conf
new file mode 100644 (file)
index 0000000..41924f4
--- /dev/null
@@ -0,0 +1,153 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; This profile forces speaker and internal mic ports even if we have no way
+; of identifying those.
+; See default.conf for explanations.
+
+[General]
+auto-profiles = yes
+
+[Mapping analog-mono]
+device-strings = hw:%f
+channel-map = mono
+paths-output = analog-output analog-output-lineout analog-output-speaker-always analog-output-headphones analog-output-headphones-2 analog-output-mono
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic-always analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line
+priority = 1
+
+[Mapping analog-stereo]
+device-strings = front:%f hw:%f
+channel-map = left,right
+paths-output = analog-output analog-output-lineout analog-output-speaker-always analog-output-headphones analog-output-headphones-2 analog-output-mono
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic-always analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line
+priority = 10
+
+[Mapping analog-surround-21]
+device-strings = surround21:%f
+channel-map = front-left,front-right,lfe
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 8
+direction = output
+
+[Mapping analog-surround-40]
+device-strings = surround40:%f
+channel-map = front-left,front-right,rear-left,rear-right
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 7
+direction = output
+
+[Mapping analog-surround-41]
+device-strings = surround41:%f
+channel-map = front-left,front-right,rear-left,rear-right,lfe
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 8
+direction = output
+
+[Mapping analog-surround-50]
+device-strings = surround50:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 7
+direction = output
+
+[Mapping analog-surround-51]
+device-strings = surround51:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 8
+direction = output
+
+[Mapping analog-surround-71]
+device-strings = surround71:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+description = Analog Surround 7.1
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 7
+direction = output
+
+[Mapping analog-4-channel-input]
+# Alsa doesn't currently provide any better device name than "hw" for 4-channel
+# input. If this causes trouble at some point, then we will need to get a new
+# device name standardized in alsa.
+device-strings = hw:%f
+channel-map = aux0,aux1,aux2,aux3
+priority = 1
+direction = input
+
+[Mapping iec958-stereo]
+device-strings = iec958:%f
+channel-map = left,right
+paths-input = iec958-stereo-input
+paths-output = iec958-stereo-output
+priority = 5
+
+[Mapping iec958-ac3-surround-40]
+device-strings = a52:%f
+channel-map = front-left,front-right,rear-left,rear-right
+paths-output = iec958-stereo-output
+priority = 2
+direction = output
+
+[Mapping iec958-ac3-surround-51]
+device-strings = a52:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+paths-output = iec958-stereo-output
+priority = 3
+direction = output
+
+[Mapping iec958-dts-surround-51]
+device-strings = dca:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+paths-output = iec958-stereo-output
+priority = 3
+direction = output
+
+[Mapping hdmi-stereo]
+description = Digital Stereo (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = left,right
+priority = 4
+direction = output
+
+[Mapping hdmi-surround]
+description = Digital Surround 5.1 (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 3
+direction = output
+
+[Mapping hdmi-surround71]
+description = Digital Surround 7.1 (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 3
+direction = output
+
+[Mapping hdmi-dts-surround]
+description = Digital Surround 5.1 (HDMI/DTS)
+device-strings = dcahdmi:%f
+paths-output = hdmi-output-0
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+; An example for defining multiple-sink profiles
+#[Profile output:analog-stereo+output:iec958-stereo+input:analog-stereo]
+#description = Foobar
+#output-mappings = analog-stereo iec958-stereo
+#input-mappings = analog-stereo
diff --git a/src/modules/alsa/mixer/profile-sets/force-speaker.conf b/src/modules/alsa/mixer/profile-sets/force-speaker.conf
new file mode 100644 (file)
index 0000000..dec57d5
--- /dev/null
@@ -0,0 +1,152 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; This profile forces a speaker port even if we have no way of identifying it.
+; See default.conf for explanations.
+
+[General]
+auto-profiles = yes
+
+[Mapping analog-mono]
+device-strings = hw:%f
+channel-map = mono
+paths-output = analog-output analog-output-lineout analog-output-speaker-always analog-output-headphones analog-output-headphones-2 analog-output-mono
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line
+priority = 1
+
+[Mapping analog-stereo]
+device-strings = front:%f hw:%f
+channel-map = left,right
+paths-output = analog-output analog-output-lineout analog-output-speaker-always analog-output-headphones analog-output-headphones-2 analog-output-mono
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line
+priority = 10
+
+[Mapping analog-surround-21]
+device-strings = surround21:%f
+channel-map = front-left,front-right,lfe
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 8
+direction = output
+
+[Mapping analog-surround-40]
+device-strings = surround40:%f
+channel-map = front-left,front-right,rear-left,rear-right
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 7
+direction = output
+
+[Mapping analog-surround-41]
+device-strings = surround41:%f
+channel-map = front-left,front-right,rear-left,rear-right,lfe
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 8
+direction = output
+
+[Mapping analog-surround-50]
+device-strings = surround50:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 7
+direction = output
+
+[Mapping analog-surround-51]
+device-strings = surround51:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 8
+direction = output
+
+[Mapping analog-surround-71]
+device-strings = surround71:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+description = Analog Surround 7.1
+paths-output = analog-output analog-output-lineout analog-output-speaker-always
+priority = 7
+direction = output
+
+[Mapping analog-4-channel-input]
+# Alsa doesn't currently provide any better device name than "hw" for 4-channel
+# input. If this causes trouble at some point, then we will need to get a new
+# device name standardized in alsa.
+device-strings = hw:%f
+channel-map = aux0,aux1,aux2,aux3
+priority = 1
+direction = input
+
+[Mapping iec958-stereo]
+device-strings = iec958:%f
+channel-map = left,right
+paths-input = iec958-stereo-input
+paths-output = iec958-stereo-output
+priority = 5
+
+[Mapping iec958-ac3-surround-40]
+device-strings = a52:%f
+channel-map = front-left,front-right,rear-left,rear-right
+paths-output = iec958-stereo-output
+priority = 2
+direction = output
+
+[Mapping iec958-ac3-surround-51]
+device-strings = a52:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+paths-output = iec958-stereo-output
+priority = 3
+direction = output
+
+[Mapping iec958-dts-surround-51]
+device-strings = dca:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+paths-output = iec958-stereo-output
+priority = 3
+direction = output
+
+[Mapping hdmi-stereo]
+description = Digital Stereo (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = left,right
+priority = 4
+direction = output
+
+[Mapping hdmi-surround]
+description = Digital Surround 5.1 (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 3
+direction = output
+
+[Mapping hdmi-surround71]
+description = Digital Surround 7.1 (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 3
+direction = output
+
+[Mapping hdmi-dts-surround]
+description = Digital Surround 5.1 (HDMI/DTS)
+device-strings = dcahdmi:%f
+paths-output = hdmi-output-0
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 1
+direction = output
+
+; An example for defining multiple-sink profiles
+#[Profile output:analog-stereo+output:iec958-stereo+input:analog-stereo]
+#description = Foobar
+#output-mappings = analog-stereo iec958-stereo
+#input-mappings = analog-stereo
diff --git a/src/modules/alsa/mixer/profile-sets/kinect-audio.conf b/src/modules/alsa/mixer/profile-sets/kinect-audio.conf
new file mode 100644 (file)
index 0000000..d51fd17
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Audio profile for the Microsoft Kinect Sensor device in UAC mode.
+;
+; Copyright (C) 2011  Antonio Ospite <ospite@studenti.unina.it>
+;
+; This device has an array of four microphones, and no playback capability.
+;
+; See default.conf for an explanation on the directives used here.
+
+[General]
+auto-profiles = no
+
+[Mapping input-4-channels]
+device-strings = hw:%f
+channel-map = front-left,front-right,rear-left,rear-right
+description = 4 Channels Input
+direction = input
+priority = 5
+
+[Profile input:mic-array]
+description = Microphone Array
+input-mappings = input-4-channels
+priority = 2
+skip-probe = yes
diff --git a/src/modules/alsa/mixer/profile-sets/maudio-fasttrack-pro.conf b/src/modules/alsa/mixer/profile-sets/maudio-fasttrack-pro.conf
new file mode 100644 (file)
index 0000000..5122907
--- /dev/null
@@ -0,0 +1,86 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; M-Audio FastTrack Pro
+;
+; This card has one duplex stereo channel called A and an additional
+; stereo output channel called B.
+;
+; We knowingly only define a subset of the theoretically possible
+; mapping combinations as profiles here.
+;
+; See default.conf for an explanation on the directives used here.
+
+[General]
+auto-profiles = no
+
+[Mapping analog-stereo-a-output]
+description = Analog Stereo Channel A
+device-strings = hw:%f,0,0
+channel-map = left,right
+direction = output
+
+; Try both device 0 and device 1 for input, see
+; http://mailman.alsa-project.org/pipermail/alsa-devel/2012-March/050701.html
+[Mapping analog-stereo-a-input]
+description = Analog Stereo Channel A
+device-strings = hw:%f,0,0 hw:%f,1,0
+channel-map = left,right
+direction = input
+
+[Mapping analog-stereo-b-output]
+description = Analog Stereo Channel B
+device-strings = hw:%f,1,0
+channel-map = left,right
+direction = output
+
+[Profile output:analog-stereo-all+input:analog-stereo-all]
+description = Analog Stereo Duplex Channel A, Analog Stereo output Channel B
+output-mappings = analog-stereo-a-output  analog-stereo-b-output
+input-mappings = analog-stereo-a-input
+priority = 100
+skip-probe = yes
+
+[Profile output:analog-stereo-a-output+input:analog-stereo-a-input]
+description = Analog Stereo Duplex Channel A
+output-mappings = analog-stereo-a-output
+input-mappings = analog-stereo-a-input
+priority = 40
+skip-probe = yes
+
+[Profile output:analog-stereo-b+input:analog-stereo-b]
+description = Analog Stereo Output Channel B
+output-mappings = analog-stereo-b-output
+input-mappings =
+priority = 50
+skip-probe = yes
+
+[Profile output:analog-stereo-a]
+description = Analog Stereo Output Channel A
+output-mappings = analog-stereo-a-output
+priority = 5
+skip-probe = yes
+
+[Profile output:analog-stereo-b]
+description = Analog Stereo Output Channel B
+output-mappings = analog-stereo-b-output
+priority = 6
+skip-probe = yes
+
+[Profile input:analog-stereo-a]
+description = Analog Stereo Input Channel A
+input-mappings = analog-stereo-a-input
+priority = 2
+skip-probe = yes
diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf
new file mode 100644 (file)
index 0000000..f7cbc15
--- /dev/null
@@ -0,0 +1,90 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Native Instruments Audio 4 DJ
+;
+; This card has two stereo pairs of input and two stereo pairs of
+; output, named channels A and B. Channel B has an additional
+; Headphone connector.
+;
+; We knowingly only define a subset of the theoretically possible
+; mapping combinations as profiles here.
+;
+; See default.conf for an explanation on the directives used here.
+
+[General]
+auto-profiles = no
+
+[Mapping analog-stereo-a]
+description = Analog Stereo Channel A
+device-strings = hw:%f,0,0
+channel-map = left,right
+
+[Mapping analog-stereo-b-output]
+description = Analog Stereo Channel B (Headphones)
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-b-input]
+description = Analog Stereo Channel B
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = input
+
+[Profile output:analog-stereo-all+input:analog-stereo-all]
+description = Analog Stereo Duplex Channels A, B (Headphones)
+output-mappings = analog-stereo-a analog-stereo-b-output
+input-mappings = analog-stereo-a analog-stereo-b-input
+priority = 100
+skip-probe = yes
+
+[Profile output:analog-stereo-a+input:analog-stereo-a]
+description = Analog Stereo Duplex Channel A
+output-mappings = analog-stereo-a
+input-mappings = analog-stereo-a
+priority = 40
+skip-probe = yes
+
+[Profile output:analog-stereo-b+input:analog-stereo-b]
+description = Analog Stereo Duplex Channel B (Headphones)
+output-mappings = analog-stereo-b-output
+input-mappings = analog-stereo-b-input
+priority = 50
+skip-probe = yes
+
+[Profile output:analog-stereo-a]
+description = Analog Stereo Output Channel A
+output-mappings = analog-stereo-a
+priority = 5
+skip-probe = yes
+
+[Profile output:analog-stereo-b]
+description = Analog Stereo Output Channel B (Headphones)
+output-mappings = analog-stereo-b-output
+priority = 6
+skip-probe = yes
+
+[Profile input:analog-stereo-a]
+description = Analog Stereo Input Channel A
+input-mappings = analog-stereo-a
+priority = 2
+skip-probe = yes
+
+[Profile input:analog-stereo-b]
+description = Analog Stereo Input Channel B
+input-mappings = analog-stereo-b-input
+priority = 1
+skip-probe = yes
diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf
new file mode 100644 (file)
index 0000000..dc1b780
--- /dev/null
@@ -0,0 +1,161 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Native Instruments Audio 8 DJ
+;
+; This card has four stereo pairs of input and four stereo pairs of
+; output, named channels A to D. Channel C has an additional Mic/Line
+; connector, channel D an additional Headphone connector.
+;
+; We knowingly only define a subset of the theoretically possible
+; mapping combinations as profiles here.
+;
+; See default.conf for an explanation on the directives used here.
+
+[General]
+auto-profiles = no
+
+[Mapping analog-stereo-a]
+description = Analog Stereo Channel A
+device-strings = hw:%f,0,0
+channel-map = left,right
+
+[Mapping analog-stereo-b]
+description = Analog Stereo Channel B
+device-strings = hw:%f,0,1
+channel-map = left,right
+
+# Since we want to set a different description for channel C's/D's input
+# and output we define two separate mappings for them
+[Mapping analog-stereo-c-output]
+description = Analog Stereo Channel C
+device-strings = hw:%f,0,2
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-c-input]
+description = Analog Stereo Channel C (Line/Mic)
+device-strings = hw:%f,0,2
+channel-map = left,right
+direction = input
+
+[Mapping analog-stereo-d-output]
+description = Analog Stereo Channel D (Headphones)
+device-strings = hw:%f,0,3
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-d-input]
+description = Analog Stereo Channel D
+device-strings = hw:%f,0,3
+channel-map = left,right
+direction = input
+
+[Profile output:analog-stereo-all+input:analog-stereo-all]
+description = Analog Stereo Duplex Channels A, B, C (Line/Mic), D (Headphones)
+output-mappings = analog-stereo-a analog-stereo-b analog-stereo-c-output analog-stereo-d-output
+input-mappings = analog-stereo-a analog-stereo-b analog-stereo-c-input analog-stereo-d-input
+priority = 100
+skip-probe = yes
+
+[Profile output:analog-stereo-d+input:analog-stereo-c]
+description = Analog Stereo Channel D (Headphones) Output, Channel C (Line/Mic) Input
+output-mappings = analog-stereo-d-output
+input-mappings = analog-stereo-c-input
+priority = 90
+skip-probe = yes
+
+[Profile output:analog-stereo-c-d+input:analog-stereo-c-d]
+description = Analog Stereo Duplex Channels C (Line/Mic), D (Line/Mic)
+output-mappings = analog-stereo-c-output analog-stereo-d-output
+input-mappings = analog-stereo-c-input analog-stereo-d-input
+priority = 80
+skip-probe = yes
+
+[Profile output:analog-stereo-a+input:analog-stereo-a]
+description = Analog Stereo Duplex Channel A
+output-mappings = analog-stereo-a
+input-mappings = analog-stereo-a
+priority = 50
+skip-probe = yes
+
+[Profile output:analog-stereo-b+input:analog-stereo-b]
+description = Analog Stereo Duplex Channel B
+output-mappings = analog-stereo-b
+input-mappings = analog-stereo-b
+priority = 40
+skip-probe = yes
+
+[Profile output:analog-stereo-c+input:analog-stereo-c]
+description = Analog Stereo Duplex Channel C (Line/Mic)
+output-mappings = analog-stereo-c-output
+input-mappings = analog-stereo-c-input
+priority = 60
+skip-probe = yes
+
+[Profile output:analog-stereo-d+input:analog-stereo-d]
+description = Analog Stereo Duplex Channel D (Headphones)
+output-mappings = analog-stereo-d-output
+input-mappings = analog-stereo-d-input
+priority = 70
+skip-probe = yes
+
+[Profile output:analog-stereo-a]
+description = Analog Stereo Output Channel A
+output-mappings = analog-stereo-a
+priority = 6
+skip-probe = yes
+
+[Profile output:analog-stereo-b]
+description = Analog Stereo Output Channel B
+output-mappings = analog-stereo-b
+priority = 5
+skip-probe = yes
+
+[Profile output:analog-stereo-c]
+description = Analog Stereo Output Channel C
+output-mappings = analog-stereo-c-output
+priority = 7
+skip-probe = yes
+
+[Profile output:analog-stereo-d]
+description = Analog Stereo Output Channel D (Headphones)
+output-mappings = analog-stereo-d-output
+priority = 8
+skip-probe = yes
+
+[Profile input:analog-stereo-a]
+description = Analog Stereo Input Channel A
+input-mappings = analog-stereo-a
+priority = 2
+skip-probe = yes
+
+[Profile input:analog-stereo-b]
+description = Analog Stereo Input Channel B
+input-mappings = analog-stereo-b
+priority = 1
+skip-probe = yes
+
+[Profile input:analog-stereo-c]
+description = Analog Stereo Input Channel C (Line/Mic)
+input-mappings = analog-stereo-c-input
+priority = 4
+skip-probe = yes
+
+[Profile input:analog-stereo-d]
+description = Analog Stereo Input Channel D
+input-mappings = analog-stereo-d-input
+priority = 3
+skip-probe = yes
diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-korecontroller.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-korecontroller.conf
new file mode 100644 (file)
index 0000000..35b3d06
--- /dev/null
@@ -0,0 +1,84 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Native Instruments Kore Controller
+;
+; This card has one stereo pairs of input and two stereo pairs of
+; output, named "Master" and "Headphone". The master channel has
+; an additional Coax S/PDIF connector which is always on.
+;
+; We knowingly only define a subset of the theoretically possible
+; mapping combinations as profiles here.
+;
+; See default.conf for an explanation on the directives used here.
+
+[General]
+auto-profiles = no
+
+[Mapping analog-stereo-master-out]
+description = Analog Stereo Master Channel
+device-strings = hw:%f,0,0
+channel-map = left,right
+
+[Mapping analog-stereo-headphone-out]
+description = Analog Stereo Headphone Channel
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-input]
+description = Analog Stereo
+device-strings = hw:%f,0,0
+channel-map = left,right
+direction = input
+
+[Profile output:analog-stereo-all+input:analog-stereo-all]
+description = Analog Stereo Duplex Master Output, Headphones Output
+output-mappings = analog-stereo-master-out analog-stereo-headphone-out
+input-mappings = analog-stereo-input
+priority = 100
+skip-probe = yes
+
+[Profile output:analog-stereo-master+input:analog-stereo-input]
+description = Analog Stereo Duplex Master Output
+output-mappings = analog-stereo-master-out
+input-mappings = analog-stereo-input
+priority = 40
+skip-probe = yes
+
+[Profile output:analog-stereo-headphone-out+input:analog-stereo-input]
+description = Analog Stereo Headphones Output
+output-mappings = analog-stereo-headphone-out
+input-mappings = analog-stereo-input
+priority = 30
+skip-probe = yes
+
+[Profile output:analog-stereo-master]
+description = Analog Stereo Master Output
+output-mappings = analog-stereo-master-out
+priority = 3
+skip-probe = yes
+
+[Profile output:analog-stereo-headphone]
+description = Analog Stereo Headphones Output
+output-mappings = analog-stereo-headphone-out
+priority = 2
+skip-probe = yes
+
+[Profile input:analog-stereo-input]
+description = Analog Stereo Input
+input-mappings = analog-stereo-input
+priority = 1
+skip-probe = yes
diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio10.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio10.conf
new file mode 100644 (file)
index 0000000..c210297
--- /dev/null
@@ -0,0 +1,130 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Native Instruments Audio 10 DJ
+;
+; This card has five stereo pairs of input and five stereo pairs of
+; output
+;
+; We knowingly only define a subset of the theoretically possible
+; mapping combinations as profiles here.
+;
+; See default.conf for an explanation on the directives used here.
+
+[General]
+auto-profiles = no
+
+[Mapping analog-stereo-out-main]
+description = Analog Stereo Main
+device-strings = hw:%f,0,0
+channel-map = left,right
+
+[Mapping analog-stereo-out-a]
+description = Analog Stereo Channel A
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-out-b]
+description = Analog Stereo Channel B
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-out-c]
+description = Analog Stereo Channel C
+device-strings = hw:%f,0,2
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-out-d]
+description = Analog Stereo Channel D
+device-strings = hw:%f,0,3
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-in-main]
+description = Analog Stereo Main
+device-strings = hw:%f,0,0
+channel-map = left,right
+
+[Mapping analog-stereo-in-a]
+description = Analog Stereo Channel A
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = input
+
+[Mapping analog-stereo-in-b]
+description = Analog Stereo Channel B
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = input
+
+[Mapping analog-stereo-in-c]
+description = Analog Stereo Channel C
+device-strings = hw:%f,0,2
+channel-map = left,right
+direction = input
+
+[Mapping analog-stereo-in-d]
+description = Analog Stereo Channel D
+device-strings = hw:%f,0,3
+channel-map = left,right
+direction = input
+
+
+
+
+[Profile output:analog-stereo-all+input:analog-stereo-all]
+description = Analog Stereo Duplex Channels Main, A, B, C, D
+output-mappings = analog-stereo-out-main analog-stereo-out-a analog-stereo-out-b analog-stereo-out-c analog-stereo-out-d
+input-mappings = analog-stereo-in-main analog-stereo-in-a analog-stereo-in-b analog-stereo-in-c analog-stereo-in-d
+priority = 100
+skip-probe = yes
+
+[Profile output:analog-stereo-main+input:analog-stereo-main]
+description = Analog Stereo Duplex Main
+output-mappings = analog-stereo-out-main
+input-mappings = analog-stereo-in-main
+priority = 50
+skip-probe = yes
+
+[Profile output:analog-stereo-a+input:analog-stereo-a]
+description = Analog Stereo Duplex Channel A
+output-mappings = analog-stereo-out-a
+input-mappings = analog-stereo-in-a
+priority = 40
+skip-probe = yes
+
+[Profile output:analog-stereo-b+input:analog-stereo-b]
+description = Analog Stereo Duplex Channel B
+output-mappings = analog-stereo-out-b
+input-mappings = analog-stereo-in-b
+priority = 30
+skip-probe = yes
+
+[Profile output:analog-stereo-a+input:analog-stereo-c]
+description = Analog Stereo Duplex Channel C
+output-mappings = analog-stereo-out-c
+input-mappings = analog-stereo-in-c
+priority = 20
+skip-probe = yes
+
+[Profile output:analog-stereo-a+input:analog-stereo-d]
+description = Analog Stereo Duplex Channel D
+output-mappings = analog-stereo-out-d
+input-mappings = analog-stereo-in-d
+priority = 10
+skip-probe = yes
diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio2.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio2.conf
new file mode 100644 (file)
index 0000000..145dace
--- /dev/null
@@ -0,0 +1,53 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Native Instruments Traktor Audio 2
+;
+; This card has two stereo pairs of output.
+;
+; See default.conf for an explanation on the directives used here.
+
+[General]
+auto-profiles = no
+
+[Mapping analog-stereo-a]
+description = Analog Stereo Channel A
+device-strings = hw:%f,0,0
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-b]
+description = Analog Stereo Channel B
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = output
+
+[Profile output:analog-stereo-a]
+description = Analog Stereo Output Channel A
+output-mappings = analog-stereo-a
+priority = 60
+skip-probe = yes
+
+[Profile output:analog-stereo-b]
+description = Analog Stereo Output Channel B
+output-mappings = analog-stereo-b
+priority = 50
+skip-probe = yes
+
+[Profile analog-stereo-all]
+description = Analog Stereo Output Channels A & B
+output-mappings = analog-stereo-a analog-stereo-b
+priority = 100
+skip-probe = yes
diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio6.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio6.conf
new file mode 100644 (file)
index 0000000..a08e9fc
--- /dev/null
@@ -0,0 +1,91 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Native Instruments Audio 6 DJ
+;
+; This card has three stereo pairs of input and three stereo pairs of
+; output
+;
+; We knowingly only define a subset of the theoretically possible
+; mapping combinations as profiles here.
+;
+; See default.conf for an explanation on the directives used here.
+
+[General]
+auto-profiles = no
+
+[Mapping analog-stereo-out-main]
+description = Analog Stereo Main
+device-strings = hw:%f,0,0
+channel-map = left,right
+
+[Mapping analog-stereo-out-a]
+description = Analog Stereo Channel A
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-out-b]
+description = Analog Stereo Channel B
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-in-main]
+description = Analog Stereo Main
+device-strings = hw:%f,0,0
+channel-map = left,right
+
+[Mapping analog-stereo-in-a]
+description = Analog Stereo Channel A
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = input
+
+[Mapping analog-stereo-in-b]
+description = Analog Stereo Channel B
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = input
+
+
+
+[Profile output:analog-stereo-all+input:analog-stereo-all]
+description = Analog Stereo Duplex Channels A, B (Headphones)
+output-mappings = analog-stereo-out-main analog-stereo-out-a analog-stereo-out-b
+input-mappings = analog-stereo-in-main analog-stereo-in-a analog-stereo-in-b
+priority = 100
+skip-probe = yes
+
+[Profile output:analog-stereo-main+input:analog-stereo-main]
+description = Analog Stereo Duplex Channel Main
+output-mappings = analog-stereo-out-main
+input-mappings = analog-stereo-in-main
+priority = 50
+skip-probe = yes
+
+[Profile output:analog-stereo-a+input:analog-stereo-a]
+description = Analog Stereo Duplex Channel A
+output-mappings = analog-stereo-out-a
+input-mappings = analog-stereo-in-a
+priority = 40
+skip-probe = yes
+
+[Profile output:analog-stereo-b+input:analog-stereo-b]
+description = Analog Stereo Duplex Channel B
+output-mappings = analog-stereo-out-b
+input-mappings = analog-stereo-in-b
+priority = 30
+skip-probe = yes
diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-traktorkontrol-s4.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-traktorkontrol-s4.conf
new file mode 100644 (file)
index 0000000..934965f
--- /dev/null
@@ -0,0 +1,80 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Native Instruments Traktor Kontrol S4
+;
+; This controller has two stereo pairs of input (named "Channel C" and
+; "Channel D") and two stereo pairs of output, one "Main Out" and
+; "Headphone Out".
+;
+; See default.conf for an explanation on the directives used here.
+
+[General]
+auto-profiles = no
+
+[Mapping analog-stereo-output-main]
+description = Analog Stereo Main Out
+device-strings = hw:%f,0,0
+channel-map = left,right
+
+[Mapping analog-stereo-output-headphone]
+description = Analog Stereo Headphones Out
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = output
+
+[Mapping analog-stereo-c-input]
+description = Analog Stereo Channel C
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = input
+
+[Mapping analog-stereo-d-input]
+description = Analog Stereo Channel D
+device-strings = hw:%f,0,1
+channel-map = left,right
+direction = input
+
+[Profile output:analog-stereo-all+input:analog-stereo-all]
+description = Analog Stereo Duplex
+output-mappings = analog-stereo-output-main analog-stereo-output-headphone
+input-mappings = analog-stereo-c-input analog-stereo-d-input
+priority = 100
+skip-probe = yes
+
+[Profile output:analog-stereo-main]
+description = Analog Stereo Main Output
+output-mappings = analog-stereo-output-main
+priority = 4
+skip-probe = yes
+
+[Profile output:analog-stereo-headphone]
+description = Analog Stereo Output Headphones Out
+output-mappings = analog-stereo-output-headphone
+priority = 3
+skip-probe = yes
+
+[Profile input:analog-stereo-c]
+description = Analog Stereo Input Channel C
+input-mappings = analog-stereo-c-input
+priority = 2
+skip-probe = yes
+
+[Profile input:analog-stereo-d]
+description = Analog Stereo Input Channel D
+input-mappings = analog-stereo-d-input
+priority = 1
+skip-probe = yes
+
diff --git a/src/modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf b/src/modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf
new file mode 100644 (file)
index 0000000..d5922da
--- /dev/null
@@ -0,0 +1,58 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+; Creative Sound Blaster Omni Surround 5.1
+;
+; This sound card have Mic/Line in at hw:%f,1,0 on Linux prior to 4.3-rc1,
+; but starting from Linux 4.3-rc1 Mic/Line is at hw:%f,0,0
+; This config supports both cases.
+; Also by default there are some non-existing (physically) inputs
+; and outputs that are not present here.
+; And finally officially supported modes are stereo and 5.1 + stereo S/PDIF,
+; so only these modes included.
+;
+; See default.conf for an explanation on the directives used here.
+
+[General]
+auto-profiles = yes
+
+[Mapping analog-stereo-output]
+device-strings = front:%f
+channel-map = left,right
+paths-output = analog-output
+priority = 10
+direction = output
+
+; Linux 4.2.x- have microphone input as device 1
+; While Linux 4.3-rc1+ have microphone input as device 0
+[Mapping analog-stereo-input]
+device-strings = hw:%f hw:%f,1,0
+paths-input = analog-input-mic analog-input-linein
+channel-map = left,right
+direction = input
+
+[Mapping analog-surround-51]
+device-strings = surround51:%f
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+paths-output = analog-output
+priority = 9
+direction = output
+
+[Mapping iec958-stereo]
+device-strings = iec958:%f
+channel-map = left,right
+paths-output = iec958-stereo-output
+priority = 5
+direction = output
diff --git a/src/modules/alsa/mixer/samples/ATI IXP--Realtek ALC655 rev 0 b/src/modules/alsa/mixer/samples/ATI IXP--Realtek ALC655 rev 0
new file mode 100644 (file)
index 0000000..082c9a1
--- /dev/null
@@ -0,0 +1,150 @@
+Simple mixer control 'Master',0
+  Capabilities: pvolume pswitch pswitch-joined
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 29 [94%] [-3.00dB] [on]
+  Front Right: Playback 29 [94%] [-3.00dB] [on]
+Simple mixer control 'Master Mono',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control 'PCM',0
+  Capabilities: pvolume pswitch pswitch-joined
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 23 [74%] [0.00dB] [on]
+  Front Right: Playback 23 [74%] [0.00dB] [on]
+Simple mixer control 'Surround',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 0 [0%] [-46.50dB] [off]
+  Front Right: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control 'Surround Jack Mode',0
+  Capabilities: enum
+  Items: 'Shared' 'Independent'
+  Item0: 'Shared'
+Simple mixer control 'Center',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control 'LFE',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control 'Line',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'CD',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'Mic',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Mono
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-34.50dB] [off]
+  Front Left: Capture [on]
+  Front Right: Capture [on]
+Simple mixer control 'Mic Boost (+20dB)',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'Mic Select',0
+  Capabilities: enum
+  Items: 'Mic1' 'Mic2'
+  Item0: 'Mic1'
+Simple mixer control 'Video',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Phone',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Mono
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono: Playback 31 [100%] [12.00dB] [off]
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'IEC958',0
+  Capabilities: pswitch pswitch-joined cswitch cswitch-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Mono: Playback [off] Capture [off]
+Simple mixer control 'IEC958 Playback AC97-SPSA',0
+  Capabilities: volume volume-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Limits: 0 - 3
+  Mono: 0 [0%]
+Simple mixer control 'IEC958 Playback Source',0
+  Capabilities: enum
+  Items: 'PCM' 'Analog In' 'IEC958 In'
+  Item0: 'PCM'
+Simple mixer control 'PC Speaker',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 15
+  Mono: Playback 0 [0%] [-45.00dB] [on]
+Simple mixer control 'Aux',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [on] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [on] Capture [off]
+Simple mixer control 'Mono Output Select',0
+  Capabilities: enum
+  Items: 'Mix' 'Mic'
+  Item0: 'Mix'
+Simple mixer control 'Capture',0
+  Capabilities: cvolume cswitch cswitch-joined
+  Capture channels: Front Left - Front Right
+  Limits: Capture 0 - 15
+  Front Left: Capture 12 [80%] [18.00dB] [on]
+  Front Right: Capture 12 [80%] [18.00dB] [on]
+Simple mixer control 'Mix',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Mix Mono',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Channel Mode',0
+  Capabilities: enum
+  Items: '2ch' '4ch' '6ch'
+  Item0: '2ch'
+Simple mixer control 'Duplicate Front',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'External Amplifier',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [on]
diff --git a/src/modules/alsa/mixer/samples/Brooktree Bt878--Bt87x b/src/modules/alsa/mixer/samples/Brooktree Bt878--Bt87x
new file mode 100644 (file)
index 0000000..b8f61fa
--- /dev/null
@@ -0,0 +1,24 @@
+Simple mixer control 'FM',0
+  Capabilities: cswitch cswitch-joined cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Mono
+  Mono: Capture [off]
+Simple mixer control 'Mic/Line',0
+  Capabilities: cswitch cswitch-joined cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Mono
+  Mono: Capture [off]
+Simple mixer control 'Capture',0
+  Capabilities: cvolume cvolume-joined
+  Capture channels: Mono
+  Limits: Capture 0 - 15
+  Mono: Capture 13 [87%]
+Simple mixer control 'Capture Boost',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [on]
+Simple mixer control 'TV Tuner',0
+  Capabilities: cswitch cswitch-joined cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Mono
+  Mono: Capture [on]
diff --git a/src/modules/alsa/mixer/samples/Ensoniq AudioPCI--Cirrus Logic CS4297A rev 3 b/src/modules/alsa/mixer/samples/Ensoniq AudioPCI--Cirrus Logic CS4297A rev 3
new file mode 100644 (file)
index 0000000..a500a81
--- /dev/null
@@ -0,0 +1,135 @@
+Simple mixer control 'Master',0
+  Capabilities: pvolume pswitch pswitch-joined
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 63
+  Mono:
+  Front Left: Playback 63 [100%] [0.00dB] [on]
+  Front Right: Playback 63 [100%] [0.00dB] [on]
+Simple mixer control 'Master Mono',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control 'Headphone',0
+  Capabilities: pvolume pswitch pswitch-joined
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 0 [0%] [-46.50dB] [off]
+  Front Right: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control '3D Control - Center',0
+  Capabilities: volume volume-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Limits: 0 - 15
+  Mono: 0 [0%]
+Simple mixer control '3D Control - Depth',0
+  Capabilities: volume volume-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Limits: 0 - 15
+  Mono: 0 [0%]
+Simple mixer control '3D Control - Switch',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'PCM',0
+  Capabilities: pvolume pswitch pswitch-joined
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 23 [74%] [0.00dB] [on]
+  Front Right: Playback 23 [74%] [0.00dB] [on]
+Simple mixer control 'Line',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [on]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [on]
+Simple mixer control 'CD',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'Mic',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Mono
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono: Playback 23 [74%] [0.00dB] [on]
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Mic Boost (+20dB)',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'Mic Select',0
+  Capabilities: enum
+  Items: 'Mic1' 'Mic2'
+  Item0: 'Mic1'
+Simple mixer control 'Video',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'Phone',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Mono
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-34.50dB] [off]
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'IEC958',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'PC Speaker',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 15
+  Mono: Playback 0 [0%] [-45.00dB] [off]
+Simple mixer control 'Aux',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'Mono Output Select',0
+  Capabilities: enum
+  Items: 'Mix' 'Mic'
+  Item0: 'Mic'
+Simple mixer control 'Capture',0
+  Capabilities: cvolume cswitch cswitch-joined
+  Capture channels: Front Left - Front Right
+  Limits: Capture 0 - 15
+  Front Left: Capture 15 [100%] [22.50dB] [on]
+  Front Right: Capture 15 [100%] [22.50dB] [on]
+Simple mixer control 'Mix',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Mix Mono',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'External Amplifier',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
diff --git a/src/modules/alsa/mixer/samples/HDA ATI HDMI--ATI R6xx HDMI b/src/modules/alsa/mixer/samples/HDA ATI HDMI--ATI R6xx HDMI
new file mode 100644 (file)
index 0000000..244f24a
--- /dev/null
@@ -0,0 +1,4 @@
+Simple mixer control 'IEC958',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [on]
diff --git a/src/modules/alsa/mixer/samples/HDA Intel--Analog Devices AD1981 b/src/modules/alsa/mixer/samples/HDA Intel--Analog Devices AD1981
new file mode 100644 (file)
index 0000000..165522f
--- /dev/null
@@ -0,0 +1,62 @@
+Simple mixer control 'Master',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 63
+  Mono:
+  Front Left: Playback 63 [100%] [3.00dB] [on]
+  Front Right: Playback 63 [100%] [3.00dB] [on]
+Simple mixer control 'PCM',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 23 [74%] [0.00dB] [on]
+  Front Right: Playback 23 [74%] [0.00dB] [on]
+Simple mixer control 'CD',0
+  Capabilities: pvolume pswitch cswitch cswitch-joined cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Capture [off]
+  Front Left: Playback 0 [0%] [-34.50dB] [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off]
+Simple mixer control 'Mic',0
+  Capabilities: pvolume pswitch cswitch cswitch-joined cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Capture [on]
+  Front Left: Playback 0 [0%] [-34.50dB] [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off]
+Simple mixer control 'Mic Boost',0
+  Capabilities: volume
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: 0 - 3
+  Front Left: 0 [0%]
+  Front Right: 0 [0%]
+Simple mixer control 'IEC958',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'IEC958 Default PCM',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'IEC958 Playback Source',0
+  Capabilities: enum
+  Items: 'PCM' 'ADC'
+  Item0: 'PCM'
+Simple mixer control 'Capture',0
+  Capabilities: cvolume cswitch
+  Capture channels: Front Left - Front Right
+  Limits: Capture 0 - 15
+  Front Left: Capture 0 [0%] [0.00dB] [on]
+  Front Right: Capture 0 [0%] [0.00dB] [on]
+Simple mixer control 'Mix',0
+  Capabilities: cswitch cswitch-joined cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Mono
+  Mono: Capture [off]
diff --git a/src/modules/alsa/mixer/samples/HDA Intel--Realtek ALC889A b/src/modules/alsa/mixer/samples/HDA Intel--Realtek ALC889A
new file mode 100644 (file)
index 0000000..28a2e73
--- /dev/null
@@ -0,0 +1,113 @@
+Simple mixer control 'Master',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 64
+  Mono: Playback 64 [100%] [0.00dB] [on]
+Simple mixer control 'Headphone',0
+  Capabilities: pswitch
+  Playback channels: Front Left - Front Right
+  Mono:
+  Front Left: Playback [on]
+  Front Right: Playback [on]
+Simple mixer control 'PCM',0
+  Capabilities: pvolume
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 255
+  Mono:
+  Front Left: Playback 255 [100%] [0.00dB]
+  Front Right: Playback 255 [100%] [0.00dB]
+Simple mixer control 'Front',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 64
+  Mono:
+  Front Left: Playback 44 [69%] [-20.00dB] [on]
+  Front Right: Playback 44 [69%] [-20.00dB] [on]
+Simple mixer control 'Front Mic',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 0 [0%] [-34.50dB] [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off]
+Simple mixer control 'Front Mic Boost',0
+  Capabilities: volume
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: 0 - 3
+  Front Left: 0 [0%]
+  Front Right: 0 [0%]
+Simple mixer control 'Surround',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 64
+  Mono:
+  Front Left: Playback 0 [0%] [-64.00dB] [on]
+  Front Right: Playback 0 [0%] [-64.00dB] [on]
+Simple mixer control 'Center',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 64
+  Mono: Playback 0 [0%] [-64.00dB] [on]
+Simple mixer control 'LFE',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 64
+  Mono: Playback 0 [0%] [-64.00dB] [on]
+Simple mixer control 'Side',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 64
+  Mono:
+  Front Left: Playback 0 [0%] [-64.00dB] [on]
+  Front Right: Playback 0 [0%] [-64.00dB] [on]
+Simple mixer control 'Line',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 0 [0%] [-34.50dB] [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off]
+Simple mixer control 'Mic',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 0 [0%] [-34.50dB] [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off]
+Simple mixer control 'Mic Boost',0
+  Capabilities: volume
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: 0 - 3
+  Front Left: 0 [0%]
+  Front Right: 0 [0%]
+Simple mixer control 'IEC958',0
+  Capabilities: pswitch pswitch-joined cswitch cswitch-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Mono: Playback [on] Capture [on]
+Simple mixer control 'IEC958 Default PCM',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [on]
+Simple mixer control 'Capture',0
+  Capabilities: cvolume cswitch
+  Capture channels: Front Left - Front Right
+  Limits: Capture 0 - 46
+  Front Left: Capture 23 [50%] [7.00dB] [on]
+  Front Right: Capture 23 [50%] [7.00dB] [on]
+Simple mixer control 'Capture',1
+  Capabilities: cvolume cswitch
+  Capture channels: Front Left - Front Right
+  Limits: Capture 0 - 46
+  Front Left: Capture 0 [0%] [-16.00dB] [off]
+  Front Right: Capture 0 [0%] [-16.00dB] [off]
+Simple mixer control 'Input Source',0
+  Capabilities: cenum
+  Items: 'Mic' 'Front Mic' 'Line'
+  Item0: 'Mic'
+Simple mixer control 'Input Source',1
+  Capabilities: cenum
+  Items: 'Mic' 'Front Mic' 'Line'
+  Item0: 'Mic'
diff --git a/src/modules/alsa/mixer/samples/Intel 82801CA-ICH3--Analog Devices AD1881A b/src/modules/alsa/mixer/samples/Intel 82801CA-ICH3--Analog Devices AD1881A
new file mode 100644 (file)
index 0000000..3ddd8af
--- /dev/null
@@ -0,0 +1,128 @@
+Simple mixer control 'Master',0
+  Capabilities: pvolume pswitch pswitch-joined
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 63
+  Mono:
+  Front Left: Playback 44 [70%] [-28.50dB] [on]
+  Front Right: Playback 60 [95%] [-4.50dB] [on]
+Simple mixer control 'Master Mono',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Playback 17 [55%] [-21.00dB] [on]
+Simple mixer control '3D Control - Center',0
+  Capabilities: volume volume-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Limits: 0 - 15
+  Mono: 0 [0%]
+Simple mixer control '3D Control - Depth',0
+  Capabilities: volume volume-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Limits: 0 - 15
+  Mono: 0 [0%]
+Simple mixer control '3D Control - Switch',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'PCM',0
+  Capabilities: pvolume pswitch pswitch-joined
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 9 [29%] [-21.00dB] [on]
+  Front Right: Playback 9 [29%] [-21.00dB] [on]
+Simple mixer control 'PCM Out Path & Mute',0
+  Capabilities: enum
+  Items: 'pre 3D' 'post 3D'
+  Item0: 'pre 3D'
+Simple mixer control 'Line',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'CD',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 9 [29%] [-21.00dB] [on] Capture [off]
+  Front Right: Playback 9 [29%] [-21.00dB] [on] Capture [off]
+Simple mixer control 'Mic',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Mono
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-34.50dB] [off]
+  Front Left: Capture [on]
+  Front Right: Capture [on]
+Simple mixer control 'Mic Boost (+20dB)',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'Mic Select',0
+  Capabilities: enum
+  Items: 'Mic1' 'Mic2'
+  Item0: 'Mic1'
+Simple mixer control 'Video',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'Phone',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Mono
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-34.50dB] [off]
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'PC Speaker',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 15
+  Mono: Playback 8 [53%] [-21.00dB] [on]
+Simple mixer control 'Aux',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'Mono Output Select',0
+  Capabilities: enum
+  Items: 'Mix' 'Mic'
+  Item0: 'Mix'
+Simple mixer control 'Capture',0
+  Capabilities: cvolume cswitch cswitch-joined
+  Capture channels: Front Left - Front Right
+  Limits: Capture 0 - 15
+  Front Left: Capture 13 [87%] [19.50dB] [on]
+  Front Right: Capture 13 [87%] [19.50dB] [on]
+Simple mixer control 'Mix',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Mix Mono',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'External Amplifier',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [on]
diff --git a/src/modules/alsa/mixer/samples/Logitech USB Speaker--USB Mixer b/src/modules/alsa/mixer/samples/Logitech USB Speaker--USB Mixer
new file mode 100644 (file)
index 0000000..38cf677
--- /dev/null
@@ -0,0 +1,27 @@
+Simple mixer control 'Bass',0
+  Capabilities: volume volume-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Limits: 0 - 48
+  Mono: 22 [46%]
+Simple mixer control 'Bass Boost',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'Treble',0
+  Capabilities: volume volume-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Limits: 0 - 48
+  Mono: 25 [52%]
+Simple mixer control 'PCM',0
+  Capabilities: pvolume pswitch pswitch-joined
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 44
+  Mono:
+  Front Left: Playback 10 [23%] [-31.00dB] [on]
+  Front Right: Playback 10 [23%] [-31.00dB] [on]
+Simple mixer control 'Auto Gain Control',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
diff --git a/src/modules/alsa/mixer/samples/USB Audio--USB Mixer b/src/modules/alsa/mixer/samples/USB Audio--USB Mixer
new file mode 100644 (file)
index 0000000..9cb4fa7
--- /dev/null
@@ -0,0 +1,37 @@
+Simple mixer control 'Master',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 255
+  Mono: Playback 105 [41%] [-28.97dB] [on]
+Simple mixer control 'Line',0
+  Capabilities: pvolume cvolume pswitch pswitch-joined cswitch cswitch-joined
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 255 Capture 0 - 128
+  Front Left: Playback 191 [75%] [34.38dB] [off] Capture 0 [0%] [0.18dB] [off]
+  Front Right: Playback 191 [75%] [34.38dB] [off] Capture 0 [0%] [0.18dB] [off]
+Simple mixer control 'Mic',0
+  Capabilities: pvolume pvolume-joined cvolume cvolume-joined pswitch pswitch-joined cswitch cswitch-joined cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Mono
+  Capture channels: Mono
+  Limits: Playback 0 - 255 Capture 0 - 128
+  Mono: Playback 191 [75%] [34.38dB] [off] Capture 0 [0%] [0.18dB] [on]
+Simple mixer control 'Mic Capture',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'IEC958 In',0
+  Capabilities: cswitch cswitch-joined
+  Capture channels: Mono
+  Mono: Capture [off]
+Simple mixer control 'Input 1',0
+  Capabilities: cswitch cswitch-joined cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Mono
+  Mono: Capture [off]
+Simple mixer control 'Input 2',0
+  Capabilities: cswitch cswitch-joined cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Mono
+  Mono: Capture [off]
diff --git a/src/modules/alsa/mixer/samples/USB Device 0x46d:0x9a4--USB Mixer b/src/modules/alsa/mixer/samples/USB Device 0x46d:0x9a4--USB Mixer
new file mode 100644 (file)
index 0000000..783f826
--- /dev/null
@@ -0,0 +1,5 @@
+Simple mixer control 'Mic',0
+  Capabilities: cvolume cvolume-joined cswitch cswitch-joined
+  Capture channels: Mono
+  Limits: Capture 0 - 3072
+  Mono: Capture 1536 [50%] [23.00dB] [on]
diff --git a/src/modules/alsa/mixer/samples/VIA 8237--Analog Devices AD1888 b/src/modules/alsa/mixer/samples/VIA 8237--Analog Devices AD1888
new file mode 100644 (file)
index 0000000..15e7b5a
--- /dev/null
@@ -0,0 +1,211 @@
+Simple mixer control 'Master',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 31 [100%] [0.00dB] [on]
+  Front Right: Playback 31 [100%] [0.00dB] [on]
+Simple mixer control 'Master Mono',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control 'Master Surround',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 0 [0%] [-46.50dB] [off]
+  Front Right: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control 'Headphone Jack Sense',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'PCM',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 23 [74%] [0.00dB] [on]
+  Front Right: Playback 23 [74%] [0.00dB] [on]
+Simple mixer control 'Surround',0
+  Capabilities: pvolume pswitch
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 0 [0%] [-46.50dB] [off]
+  Front Right: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control 'Surround Jack Mode',0
+  Capabilities: enum
+  Items: 'Shared' 'Independent'
+  Item0: 'Shared'
+Simple mixer control 'Center',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Playback 31 [100%] [0.00dB] [off]
+Simple mixer control 'LFE',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control 'Line',0
+  Capabilities: pvolume pswitch cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'Line Jack Sense',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'CD',0
+  Capabilities: pvolume pswitch cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'Mic',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Mono
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-34.50dB] [off]
+  Front Left: Capture [on]
+  Front Right: Capture [on]
+Simple mixer control 'Mic Boost (+20dB)',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'Mic Select',0
+  Capabilities: enum
+  Items: 'Mic1' 'Mic2'
+  Item0: 'Mic1'
+Simple mixer control 'Video',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Phone',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Mono
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-34.50dB] [off]
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'IEC958',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'IEC958 Output',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'IEC958 Playback AC97-SPSA',0
+  Capabilities: volume volume-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Limits: 0 - 3
+  Mono: 3 [100%]
+Simple mixer control 'IEC958 Playback Source',0
+  Capabilities: enum
+  Items: 'AC-Link' 'A/D Converter'
+  Item0: 'AC-Link'
+Simple mixer control 'Aux',0
+  Capabilities: pvolume pswitch cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'Capture',0
+  Capabilities: cvolume cswitch
+  Capture channels: Front Left - Front Right
+  Limits: Capture 0 - 15
+  Front Left: Capture 0 [0%] [0.00dB] [on]
+  Front Right: Capture 0 [0%] [0.00dB] [on]
+Simple mixer control 'Mix',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Mix Mono',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Channel Mode',0
+  Capabilities: enum
+  Items: '2ch' '4ch' '6ch'
+  Item0: '2ch'
+Simple mixer control 'Downmix',0
+  Capabilities: enum
+  Items: 'Off' '6 -> 4' '6 -> 2'
+  Item0: 'Off'
+Simple mixer control 'Exchange Front/Surround',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'External Amplifier',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [on]
+Simple mixer control 'High Pass Filter Enable',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'Input Source Select',0
+  Capabilities: enum
+  Items: 'Input1' 'Input2'
+  Item0: 'Input1'
+Simple mixer control 'Input Source Select',1
+  Capabilities: enum
+  Items: 'Input1' 'Input2'
+  Item0: 'Input1'
+Simple mixer control 'Spread Front to Surround and Center/LFE',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'VIA DXS',0
+  Capabilities: pvolume
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 31 [100%] [-48.00dB]
+  Front Right: Playback 31 [100%] [-48.00dB]
+Simple mixer control 'VIA DXS',1
+  Capabilities: pvolume
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 31 [100%] [-48.00dB]
+  Front Right: Playback 31 [100%] [-48.00dB]
+Simple mixer control 'VIA DXS',2
+  Capabilities: pvolume
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 31 [100%] [-48.00dB]
+  Front Right: Playback 31 [100%] [-48.00dB]
+Simple mixer control 'VIA DXS',3
+  Capabilities: pvolume
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 31 [100%] [-48.00dB]
+  Front Right: Playback 31 [100%] [-48.00dB]
+Simple mixer control 'V_REFOUT Enable',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [on]
diff --git a/src/modules/alsa/mixer/samples/VIA 8237--C-Media Electronics CMI9761A+ b/src/modules/alsa/mixer/samples/VIA 8237--C-Media Electronics CMI9761A+
new file mode 100644 (file)
index 0000000..d4f3db6
--- /dev/null
@@ -0,0 +1,160 @@
+Simple mixer control 'Master',0
+  Capabilities: pvolume pswitch pswitch-joined
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 0 [0%] [-46.50dB] [off]
+  Front Right: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control 'PCM',0
+  Capabilities: pvolume pswitch pswitch-joined
+  Playback channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Mono:
+  Front Left: Playback 31 [100%] [-48.00dB] [off]
+  Front Right: Playback 31 [100%] [-48.00dB] [off]
+Simple mixer control 'Surround',0
+  Capabilities: pswitch
+  Playback channels: Front Left - Front Right
+  Mono:
+  Front Left: Playback [off]
+  Front Right: Playback [off]
+Simple mixer control 'Surround Jack Mode',0
+  Capabilities: enum
+  Items: 'Shared' 'Independent'
+  Item0: 'Shared'
+Simple mixer control 'Center',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Playback 31 [100%] [0.00dB] [off]
+Simple mixer control 'LFE',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 31
+  Mono: Playback 0 [0%] [-46.50dB] [off]
+Simple mixer control 'Line',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'CD',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'Mic',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [on]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [on]
+Simple mixer control 'Mic Boost (+20dB)',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'Mic Select',0
+  Capabilities: enum
+  Items: 'Mic1' 'Mic2'
+  Item0: 'Mic1'
+Simple mixer control 'Video',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Phone',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'IEC958',0
+  Capabilities: pswitch pswitch-joined cswitch cswitch-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Mono: Playback [off] Capture [off]
+Simple mixer control 'IEC958 Capture Monitor',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'IEC958 Capture Valid',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'IEC958 Output',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [off]
+Simple mixer control 'IEC958 Playback AC97-SPSA',0
+  Capabilities: volume volume-joined
+  Playback channels: Mono
+  Capture channels: Mono
+  Limits: 0 - 3
+  Mono: 3 [100%]
+Simple mixer control 'IEC958 Playback Source',0
+  Capabilities: enum
+  Items: 'AC-Link' 'ADC' 'SPDIF-In'
+  Item0: 'AC-Link'
+Simple mixer control 'PC Speaker',0
+  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
+  Playback channels: Mono
+  Limits: Playback 0 - 15
+  Mono: Playback 0 [0%] [-45.00dB] [off]
+Simple mixer control 'Aux',0
+  Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Playback channels: Front Left - Front Right
+  Capture channels: Front Left - Front Right
+  Limits: Playback 0 - 31
+  Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+  Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
+Simple mixer control 'Mono Output Select',0
+  Capabilities: enum
+  Items: 'Mix' 'Mic'
+  Item0: 'Mix'
+Simple mixer control 'Capture',0
+  Capabilities: cvolume cswitch cswitch-joined
+  Capture channels: Front Left - Front Right
+  Limits: Capture 0 - 15
+  Front Left: Capture 0 [0%] [0.00dB] [on]
+  Front Right: Capture 0 [0%] [0.00dB] [on]
+Simple mixer control 'Mix',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Mix Mono',0
+  Capabilities: cswitch cswitch-exclusive
+  Capture exclusive group: 0
+  Capture channels: Front Left - Front Right
+  Front Left: Capture [off]
+  Front Right: Capture [off]
+Simple mixer control 'Channel Mode',0
+  Capabilities: enum
+  Items: '2ch' '4ch' '6ch'
+  Item0: '2ch'
+Simple mixer control 'DAC Clock Source',0
+  Capabilities: enum
+  Items: 'AC-Link' 'SPDIF-In' 'Both'
+  Item0: 'AC-Link'
+Simple mixer control 'External Amplifier',0
+  Capabilities: pswitch pswitch-joined
+  Playback channels: Mono
+  Mono: Playback [on]
+Simple mixer control 'Input Source Select',0
+  Capabilities: enum
+  Items: 'Input1' 'Input2'
+  Item0: 'Input1'
+Simple mixer control 'Input Source Select',1
+  Capabilities: enum
+  Items: 'Input1' 'Input2'
+  Item0: 'Input1'
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
new file mode 100644 (file)
index 0000000..4f1236e
--- /dev/null
@@ -0,0 +1,955 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/queue.h>
+
+#include <modules/reserve-wrap.h>
+
+#ifdef HAVE_UDEV
+#include <modules/udev-util.h>
+#endif
+
+#include "alsa-util.h"
+#include "alsa-ucm.h"
+#include "alsa-sink.h"
+#include "alsa-source.h"
+#include "module-alsa-card-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Card");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "name=<name for the card/sink/source, to be prefixed> "
+        "card_name=<name for the card> "
+        "card_properties=<properties for the card> "
+        "sink_name=<name for the sink> "
+        "sink_properties=<properties for the sink> "
+        "source_name=<name for the source> "
+        "source_properties=<properties for the source> "
+        "namereg_fail=<when false attempt to synthesise new names if they are already taken> "
+        "device_id=<ALSA card index> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "fragments=<number of fragments> "
+        "fragment_size=<fragment size> "
+        "mmap=<enable memory mapping?> "
+        "tsched=<enable system timer based scheduling mode?> "
+        "tsched_buffer_size=<buffer size when using timer based scheduling> "
+        "tsched_buffer_watermark=<lower fill watermark> "
+        "profile=<profile name> "
+        "fixed_latency_range=<disable latency range changes on underrun?> "
+        "ignore_dB=<ignore dB information from the device?> "
+        "deferred_volume=<Synchronize software and hardware volume changes to avoid momentary jumps?> "
+        "profile_set=<profile set configuration file> "
+        "paths_dir=<directory containing the path configuration files> "
+        "use_ucm=<load use case manager> "
+);
+
+static const char* const valid_modargs[] = {
+    "name",
+    "card_name",
+    "card_properties",
+    "sink_name",
+    "sink_properties",
+    "source_name",
+    "source_properties",
+    "namereg_fail",
+    "device_id",
+    "format",
+    "rate",
+    "fragments",
+    "fragment_size",
+    "mmap",
+    "tsched",
+    "tsched_buffer_size",
+    "tsched_buffer_watermark",
+    "fixed_latency_range",
+    "profile",
+    "ignore_dB",
+    "deferred_volume",
+    "profile_set",
+    "paths_dir",
+    "use_ucm",
+    NULL
+};
+
+#define DEFAULT_DEVICE_ID "0"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    char *device_id;
+    int alsa_card_index;
+
+    snd_mixer_t *mixer_handle;
+    pa_hashmap *jacks;
+    pa_alsa_fdlist *mixer_fdl;
+
+    pa_card *card;
+
+    pa_modargs *modargs;
+
+    pa_alsa_profile_set *profile_set;
+
+    /* ucm stuffs */
+    bool use_ucm;
+    pa_alsa_ucm_config ucm;
+
+};
+
+struct profile_data {
+    pa_alsa_profile *profile;
+};
+
+static void add_profiles(struct userdata *u, pa_hashmap *h, pa_hashmap *ports) {
+    pa_alsa_profile *ap;
+    void *state;
+
+    pa_assert(u);
+    pa_assert(h);
+
+    PA_HASHMAP_FOREACH(ap, u->profile_set->profiles, state) {
+        struct profile_data *d;
+        pa_card_profile *cp;
+        pa_alsa_mapping *m;
+        uint32_t idx;
+
+        cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data));
+        cp->priority = ap->priority;
+        cp->input_name = pa_xstrdup(ap->input_name);
+        cp->output_name = pa_xstrdup(ap->output_name);
+
+        if (ap->output_mappings) {
+            cp->n_sinks = pa_idxset_size(ap->output_mappings);
+
+            PA_IDXSET_FOREACH(m, ap->output_mappings, idx) {
+                if (u->use_ucm)
+                    pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context, true, ports, cp, u->core);
+                else
+                    pa_alsa_path_set_add_ports(m->output_path_set, cp, ports, NULL, u->core);
+                if (m->channel_map.channels > cp->max_sink_channels)
+                    cp->max_sink_channels = m->channel_map.channels;
+            }
+        }
+
+        if (ap->input_mappings) {
+            cp->n_sources = pa_idxset_size(ap->input_mappings);
+
+            PA_IDXSET_FOREACH(m, ap->input_mappings, idx) {
+                if (u->use_ucm)
+                    pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context, false, ports, cp, u->core);
+                else
+                    pa_alsa_path_set_add_ports(m->input_path_set, cp, ports, NULL, u->core);
+                if (m->channel_map.channels > cp->max_source_channels)
+                    cp->max_source_channels = m->channel_map.channels;
+            }
+        }
+
+        d = PA_CARD_PROFILE_DATA(cp);
+        d->profile = ap;
+
+        pa_hashmap_put(h, cp->name, cp);
+    }
+}
+
+static void add_disabled_profile(pa_hashmap *profiles) {
+    pa_card_profile *p;
+    struct profile_data *d;
+
+    p = pa_card_profile_new("off", _("Off"), sizeof(struct profile_data));
+
+    d = PA_CARD_PROFILE_DATA(p);
+    d->profile = NULL;
+
+    pa_hashmap_put(profiles, p->name, p);
+}
+
+static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
+    struct userdata *u;
+    struct profile_data *nd, *od;
+    uint32_t idx;
+    pa_alsa_mapping *am;
+    pa_queue *sink_inputs = NULL, *source_outputs = NULL;
+    int ret = 0;
+
+    pa_assert(c);
+    pa_assert(new_profile);
+    pa_assert_se(u = c->userdata);
+
+    nd = PA_CARD_PROFILE_DATA(new_profile);
+    od = PA_CARD_PROFILE_DATA(c->active_profile);
+
+    if (od->profile && od->profile->output_mappings)
+        PA_IDXSET_FOREACH(am, od->profile->output_mappings, idx) {
+            if (!am->sink)
+                continue;
+
+            if (nd->profile &&
+                nd->profile->output_mappings &&
+                pa_idxset_get_by_data(nd->profile->output_mappings, am, NULL))
+                continue;
+
+            sink_inputs = pa_sink_move_all_start(am->sink, sink_inputs);
+            pa_alsa_sink_free(am->sink);
+            am->sink = NULL;
+        }
+
+    if (od->profile && od->profile->input_mappings)
+        PA_IDXSET_FOREACH(am, od->profile->input_mappings, idx) {
+            if (!am->source)
+                continue;
+
+            if (nd->profile &&
+                nd->profile->input_mappings &&
+                pa_idxset_get_by_data(nd->profile->input_mappings, am, NULL))
+                continue;
+
+            source_outputs = pa_source_move_all_start(am->source, source_outputs);
+            pa_alsa_source_free(am->source);
+            am->source = NULL;
+        }
+
+    /* if UCM is available for this card then update the verb */
+    if (u->use_ucm) {
+        if (pa_alsa_ucm_set_profile(&u->ucm, nd->profile ? nd->profile->name : NULL,
+                    od->profile ? od->profile->name : NULL) < 0) {
+            ret = -1;
+            goto finish;
+        }
+    }
+
+    if (nd->profile && nd->profile->output_mappings)
+        PA_IDXSET_FOREACH(am, nd->profile->output_mappings, idx) {
+
+            if (!am->sink)
+                am->sink = pa_alsa_sink_new(c->module, u->modargs, __FILE__, c, am);
+
+            if (sink_inputs && am->sink) {
+                pa_sink_move_all_finish(am->sink, sink_inputs, false);
+                sink_inputs = NULL;
+            }
+        }
+
+    if (nd->profile && nd->profile->input_mappings)
+        PA_IDXSET_FOREACH(am, nd->profile->input_mappings, idx) {
+
+            if (!am->source)
+                am->source = pa_alsa_source_new(c->module, u->modargs, __FILE__, c, am);
+
+            if (source_outputs && am->source) {
+                pa_source_move_all_finish(am->source, source_outputs, false);
+                source_outputs = NULL;
+            }
+        }
+
+finish:
+    if (sink_inputs)
+        pa_sink_move_all_fail(sink_inputs);
+
+    if (source_outputs)
+        pa_source_move_all_fail(source_outputs);
+
+    return ret;
+}
+
+static void init_profile(struct userdata *u) {
+    uint32_t idx;
+    pa_alsa_mapping *am;
+    struct profile_data *d;
+    pa_alsa_ucm_config *ucm = &u->ucm;
+
+    pa_assert(u);
+
+    d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+
+    if (d->profile && u->use_ucm) {
+        /* Set initial verb */
+        if (pa_alsa_ucm_set_profile(ucm, d->profile->name, NULL) < 0) {
+            pa_log("Failed to set ucm profile %s", d->profile->name);
+            return;
+        }
+    }
+
+    if (d->profile && d->profile->output_mappings)
+        PA_IDXSET_FOREACH(am, d->profile->output_mappings, idx)
+            am->sink = pa_alsa_sink_new(u->module, u->modargs, __FILE__, u->card, am);
+
+    if (d->profile && d->profile->input_mappings)
+        PA_IDXSET_FOREACH(am, d->profile->input_mappings, idx)
+            am->source = pa_alsa_source_new(u->module, u->modargs, __FILE__, u->card, am);
+}
+
+static pa_available_t calc_port_state(pa_device_port *p, struct userdata *u) {
+    void *state;
+    pa_alsa_jack *jack;
+    pa_available_t pa = PA_AVAILABLE_UNKNOWN;
+    pa_device_port *port;
+
+    PA_HASHMAP_FOREACH(jack, u->jacks, state) {
+        pa_available_t cpa;
+
+        if (u->use_ucm)
+            port = pa_hashmap_get(u->card->ports, jack->name);
+        else {
+            if (jack->path)
+                port = jack->path->port;
+            else
+                continue;
+        }
+
+        if (p != port)
+            continue;
+
+        cpa = jack->plugged_in ? jack->state_plugged : jack->state_unplugged;
+
+        if (cpa == PA_AVAILABLE_NO) {
+          /* If a plugged-in jack causes the availability to go to NO, it
+           * should override all other availability information (like a
+           * blacklist) so set and bail */
+          if (jack->plugged_in) {
+            pa = cpa;
+            break;
+          }
+
+          /* If the current availablility is unknown go the more precise no,
+           * but otherwise don't change state */
+          if (pa == PA_AVAILABLE_UNKNOWN)
+            pa = cpa;
+        } else if (cpa == PA_AVAILABLE_YES) {
+          /* Output is available through at least one jack, so go to that
+           * level of availability. We still need to continue iterating through
+           * the jacks in case a jack is plugged in that forces the state to no
+           */
+          pa = cpa;
+        }
+    }
+    return pa;
+}
+
+struct temp_port_avail {
+    pa_device_port *port;
+    pa_available_t avail;
+};
+
+static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) {
+    struct userdata *u = snd_mixer_elem_get_callback_private(melem);
+    snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem);
+    snd_ctl_elem_value_t *elem_value;
+    bool plugged_in;
+    void *state;
+    pa_alsa_jack *jack;
+    struct temp_port_avail *tp, *tports;
+    pa_card_profile *profile;
+
+    pa_assert(u);
+
+    /* Changing the jack state may cause a port change, and a port change will
+     * make the sink or source change the mixer settings. If there are multiple
+     * users having pulseaudio running, the mixer changes done by inactive
+     * users may mess up the volume settings for the active users, because when
+     * the inactive users change the mixer settings, those changes are picked
+     * up by the active user's pulseaudio instance and the changes are
+     * interpreted as if the active user changed the settings manually e.g.
+     * with alsamixer. Even single-user systems suffer from this, because gdm
+     * runs its own pulseaudio instance.
+     *
+     * We rerun this function when being unsuspended to catch up on jack state
+     * changes */
+    if (u->card->suspend_cause & PA_SUSPEND_SESSION)
+        return 0;
+
+    if (mask == SND_CTL_EVENT_MASK_REMOVE)
+        return 0;
+
+    snd_ctl_elem_value_alloca(&elem_value);
+    if (snd_hctl_elem_read(elem, elem_value) < 0) {
+        pa_log_warn("Failed to read jack detection from '%s'", pa_strnull(snd_hctl_elem_get_name(elem)));
+        return 0;
+    }
+
+    plugged_in = !!snd_ctl_elem_value_get_boolean(elem_value, 0);
+
+    pa_log_debug("Jack '%s' is now %s", pa_strnull(snd_hctl_elem_get_name(elem)), plugged_in ? "plugged in" : "unplugged");
+
+    tports = tp = pa_xnew0(struct temp_port_avail, pa_hashmap_size(u->jacks)+1);
+
+    PA_HASHMAP_FOREACH(jack, u->jacks, state)
+        if (jack->melem == melem) {
+            pa_alsa_jack_set_plugged_in(jack, plugged_in);
+
+            if (u->use_ucm) {
+                /* When using UCM, pa_alsa_jack_set_plugged_in() maps the jack
+                 * state to port availability. */
+                continue;
+            }
+
+            /* When not using UCM, we have to do the jack state -> port
+             * availability mapping ourselves. */
+            pa_assert_se(tp->port = jack->path->port);
+            tp->avail = calc_port_state(tp->port, u);
+            tp++;
+        }
+
+    /* Report available ports before unavailable ones: in case port 1 becomes available when port 2 becomes unavailable,
+       this prevents an unnecessary switch port 1 -> port 3 -> port 2 */
+
+    for (tp = tports; tp->port; tp++)
+        if (tp->avail != PA_AVAILABLE_NO)
+           pa_device_port_set_available(tp->port, tp->avail);
+    for (tp = tports; tp->port; tp++)
+        if (tp->avail == PA_AVAILABLE_NO)
+           pa_device_port_set_available(tp->port, tp->avail);
+
+    /* Update profile availabilities. The logic could be improved; for now we
+     * only set obviously unavailable profiles (those that contain only
+     * unavailable ports) to PA_AVAILABLE_NO and all others to
+     * PA_AVAILABLE_UNKNOWN. */
+    PA_HASHMAP_FOREACH(profile, u->card->profiles, state) {
+        pa_device_port *port;
+        void *state2;
+        pa_available_t available = PA_AVAILABLE_NO;
+
+        /* Don't touch the "off" profile. */
+        if (profile->n_sources == 0 && profile->n_sinks == 0)
+            continue;
+
+        PA_HASHMAP_FOREACH(port, u->card->ports, state2) {
+            if (!pa_hashmap_get(port->profiles, profile->name))
+                continue;
+
+            if (port->available != PA_AVAILABLE_NO) {
+                available = PA_AVAILABLE_UNKNOWN;
+                break;
+            }
+        }
+
+        pa_card_profile_set_available(profile, available);
+    }
+
+    pa_xfree(tports);
+    return 0;
+}
+
+static pa_device_port* find_port_with_eld_device(pa_hashmap *ports, int device) {
+    void *state;
+    pa_device_port *p;
+
+    PA_HASHMAP_FOREACH(p, ports, state) {
+        pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(p);
+        pa_assert(data->path);
+        if (device == data->path->eld_device)
+            return p;
+    }
+    return NULL;
+}
+
+static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask) {
+    struct userdata *u = snd_mixer_elem_get_callback_private(melem);
+    snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem);
+    int device = snd_hctl_elem_get_device(elem);
+    const char *old_monitor_name;
+    pa_device_port *p;
+    pa_hdmi_eld eld;
+    bool changed = false;
+
+    if (mask == SND_CTL_EVENT_MASK_REMOVE)
+        return 0;
+
+    p = find_port_with_eld_device(u->card->ports, device);
+    if (p == NULL) {
+        pa_log_error("Invalid device changed in ALSA: %d", device);
+        return 0;
+    }
+
+    if (pa_alsa_get_hdmi_eld(elem, &eld) < 0)
+        memset(&eld, 0, sizeof(eld));
+
+    old_monitor_name = pa_proplist_gets(p->proplist, PA_PROP_DEVICE_PRODUCT_NAME);
+    if (eld.monitor_name[0] == '\0') {
+        changed |= old_monitor_name != NULL;
+        pa_proplist_unset(p->proplist, PA_PROP_DEVICE_PRODUCT_NAME);
+    } else {
+        changed |= (old_monitor_name == NULL) || (strcmp(old_monitor_name, eld.monitor_name) != 0);
+        pa_proplist_sets(p->proplist, PA_PROP_DEVICE_PRODUCT_NAME, eld.monitor_name);
+    }
+
+    if (changed && mask != 0)
+        pa_subscription_post(u->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, u->card->index);
+
+    return 0;
+}
+
+static void init_eld_ctls(struct userdata *u) {
+    void *state;
+    pa_device_port *port;
+
+    if (!u->mixer_handle)
+        return;
+
+    /* The code in this function expects ports to have a pa_alsa_port_data
+     * struct as their data, but in UCM mode ports don't have any data. Hence,
+     * the ELD controls can't currently be used in UCM mode. */
+    if (u->use_ucm)
+        return;
+
+    PA_HASHMAP_FOREACH(port, u->card->ports, state) {
+        pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(port);
+        snd_mixer_elem_t* melem;
+        int device;
+
+        pa_assert(data->path);
+        device = data->path->eld_device;
+        if (device < 0)
+            continue;
+
+        melem = pa_alsa_mixer_find(u->mixer_handle, "ELD", device);
+        if (melem) {
+            snd_mixer_elem_set_callback(melem, hdmi_eld_changed);
+            snd_mixer_elem_set_callback_private(melem, u);
+            hdmi_eld_changed(melem, 0);
+        }
+        else
+            pa_log_debug("No ELD device found for port %s.", port->name);
+    }
+}
+
+static void init_jacks(struct userdata *u) {
+    void *state;
+    pa_alsa_path* path;
+    pa_alsa_jack* jack;
+
+    u->jacks = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    if (u->use_ucm) {
+        PA_LLIST_FOREACH(jack, u->ucm.jacks)
+            if (jack->has_control)
+                pa_hashmap_put(u->jacks, jack, jack);
+    } else {
+        /* See if we have any jacks */
+        if (u->profile_set->output_paths)
+            PA_HASHMAP_FOREACH(path, u->profile_set->output_paths, state)
+                PA_LLIST_FOREACH(jack, path->jacks)
+                    if (jack->has_control)
+                        pa_hashmap_put(u->jacks, jack, jack);
+
+        if (u->profile_set->input_paths)
+            PA_HASHMAP_FOREACH(path, u->profile_set->input_paths, state)
+                PA_LLIST_FOREACH(jack, path->jacks)
+                    if (jack->has_control)
+                        pa_hashmap_put(u->jacks, jack, jack);
+    }
+
+    pa_log_debug("Found %d jacks.", pa_hashmap_size(u->jacks));
+
+    if (pa_hashmap_size(u->jacks) == 0)
+        return;
+
+    u->mixer_fdl = pa_alsa_fdlist_new();
+
+    u->mixer_handle = pa_alsa_open_mixer(u->alsa_card_index, NULL);
+    if (u->mixer_handle && pa_alsa_fdlist_set_handle(u->mixer_fdl, u->mixer_handle, NULL, u->core->mainloop) >= 0) {
+        PA_HASHMAP_FOREACH(jack, u->jacks, state) {
+            jack->melem = pa_alsa_mixer_find(u->mixer_handle, jack->alsa_name, 0);
+            if (!jack->melem) {
+                pa_log_warn("Jack '%s' seems to have disappeared.", jack->alsa_name);
+                pa_alsa_jack_set_has_control(jack, false);
+                continue;
+            }
+            snd_mixer_elem_set_callback(jack->melem, report_jack_state);
+            snd_mixer_elem_set_callback_private(jack->melem, u);
+            report_jack_state(jack->melem, 0);
+        }
+
+    } else
+        pa_log("Failed to open mixer for jack detection");
+
+}
+
+static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *device_id) {
+    char *t;
+    const char *n;
+
+    pa_assert(data);
+    pa_assert(ma);
+    pa_assert(device_id);
+
+    if ((n = pa_modargs_get_value(ma, "card_name", NULL))) {
+        pa_card_new_data_set_name(data, n);
+        data->namereg_fail = true;
+        return;
+    }
+
+    if ((n = pa_modargs_get_value(ma, "name", NULL)))
+        data->namereg_fail = true;
+    else {
+        n = device_id;
+        data->namereg_fail = false;
+    }
+
+    t = pa_sprintf_malloc("alsa_card.%s", n);
+    pa_card_new_data_set_name(data, t);
+    pa_xfree(t);
+}
+
+static pa_hook_result_t card_suspend_changed(pa_core *c, pa_card *card, struct userdata *u) {
+    void *state;
+    pa_alsa_jack *jack;
+
+    if (card->suspend_cause == 0) {
+        /* We were unsuspended, update jack state in case it changed while we were suspended */
+        PA_HASHMAP_FOREACH(jack, u->jacks, state) {
+            report_jack_state(jack->melem, 0);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_put_hook_callback(pa_core *c, pa_sink_input *sink_input, struct userdata *u) {
+    const char *role;
+    pa_sink *sink = sink_input->sink;
+
+    pa_assert(sink);
+
+    role = pa_proplist_gets(sink_input->proplist, PA_PROP_MEDIA_ROLE);
+
+    /* new sink input linked to sink of this card */
+    if (role && sink->card == u->card)
+        pa_alsa_ucm_roled_stream_begin(&u->ucm, role, PA_DIRECTION_OUTPUT);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_put_hook_callback(pa_core *c, pa_source_output *source_output, struct userdata *u) {
+    const char *role;
+    pa_source *source = source_output->source;
+
+    pa_assert(source);
+
+    role = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE);
+
+    /* new source output linked to source of this card */
+    if (role && source->card == u->card)
+        pa_alsa_ucm_roled_stream_begin(&u->ucm, role, PA_DIRECTION_INPUT);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_unlink_hook_callback(pa_core *c, pa_sink_input *sink_input, struct userdata *u) {
+    const char *role;
+    pa_sink *sink = sink_input->sink;
+
+    pa_assert(sink);
+
+    role = pa_proplist_gets(sink_input->proplist, PA_PROP_MEDIA_ROLE);
+
+    /* new sink input unlinked from sink of this card */
+    if (role && sink->card == u->card)
+        pa_alsa_ucm_roled_stream_end(&u->ucm, role, PA_DIRECTION_OUTPUT);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_unlink_hook_callback(pa_core *c, pa_source_output *source_output, struct userdata *u) {
+    const char *role;
+    pa_source *source = source_output->source;
+
+    pa_assert(source);
+
+    role = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE);
+
+    /* new source output unlinked from source of this card */
+    if (role && source->card == u->card)
+        pa_alsa_ucm_roled_stream_end(&u->ucm, role, PA_DIRECTION_INPUT);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module *m) {
+    pa_card_new_data data;
+    bool ignore_dB = false;
+    struct userdata *u;
+    pa_reserve_wrapper *reserve = NULL;
+    const char *description;
+    const char *profile_str = NULL;
+    char *fn = NULL;
+    bool namereg_fail = false;
+
+    pa_alsa_refcnt_inc();
+
+    pa_assert(m);
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->use_ucm = true;
+    u->ucm.core = m->core;
+
+    if (!(u->modargs = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    u->device_id = pa_xstrdup(pa_modargs_get_value(u->modargs, "device_id", DEFAULT_DEVICE_ID));
+
+    if ((u->alsa_card_index = snd_card_get_index(u->device_id)) < 0) {
+        pa_log("Card '%s' doesn't exist: %s", u->device_id, pa_alsa_strerror(u->alsa_card_index));
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(u->modargs, "ignore_dB", &ignore_dB) < 0) {
+        pa_log("Failed to parse ignore_dB argument.");
+        goto fail;
+    }
+
+    if (!pa_in_system_mode()) {
+        char *rname;
+
+        if ((rname = pa_alsa_get_reserve_name(u->device_id))) {
+            reserve = pa_reserve_wrapper_get(m->core, rname);
+            pa_xfree(rname);
+
+            if (!reserve)
+                goto fail;
+        }
+    }
+
+    if (pa_modargs_get_value_boolean(u->modargs, "use_ucm", &u->use_ucm) < 0) {
+        pa_log("Failed to parse use_ucm argument.");
+        goto fail;
+    }
+
+    /* Force ALSA to reread its configuration. This matters if our device
+     * was hot-plugged after ALSA has already read its configuration - see
+     * https://bugs.freedesktop.org/show_bug.cgi?id=54029
+     */
+
+    snd_config_update_free_global();
+
+    if (u->use_ucm && !pa_alsa_ucm_query_profiles(&u->ucm, u->alsa_card_index)) {
+        pa_log_info("Found UCM profiles");
+
+        u->profile_set = pa_alsa_ucm_add_profile_set(&u->ucm, &u->core->default_channel_map);
+
+        /* hook start of sink input/source output to enable modifiers */
+        /* A little bit later than module-role-cork */
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE+10,
+                (pa_hook_cb_t) sink_input_put_hook_callback, u);
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_LATE+10,
+                (pa_hook_cb_t) source_output_put_hook_callback, u);
+
+        /* hook end of sink input/source output to disable modifiers */
+        /* A little bit later than module-role-cork */
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE+10,
+                (pa_hook_cb_t) sink_input_unlink_hook_callback, u);
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_LATE+10,
+                (pa_hook_cb_t) source_output_unlink_hook_callback, u);
+    }
+    else {
+        u->use_ucm = false;
+#ifdef HAVE_UDEV
+        fn = pa_udev_get_property(u->alsa_card_index, "PULSE_PROFILE_SET");
+#endif
+
+        if (pa_modargs_get_value(u->modargs, "profile_set", NULL)) {
+            pa_xfree(fn);
+            fn = pa_xstrdup(pa_modargs_get_value(u->modargs, "profile_set", NULL));
+        }
+
+        u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map);
+        pa_xfree(fn);
+    }
+
+    if (!u->profile_set)
+        goto fail;
+
+    u->profile_set->ignore_dB = ignore_dB;
+
+    pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec, m->core->default_n_fragments, m->core->default_fragment_size_msec);
+    pa_alsa_profile_set_dump(u->profile_set);
+
+    pa_card_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+
+    pa_alsa_init_proplist_card(m->core, data.proplist, u->alsa_card_index);
+
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_id);
+    pa_alsa_init_description(data.proplist, NULL);
+    set_card_name(&data, u->modargs, u->device_id);
+
+    /* We need to give pa_modargs_get_value_boolean() a pointer to a local
+     * variable instead of using &data.namereg_fail directly, because
+     * data.namereg_fail is a bitfield and taking the address of a bitfield
+     * variable is impossible. */
+    namereg_fail = data.namereg_fail;
+    if (pa_modargs_get_value_boolean(u->modargs, "namereg_fail", &namereg_fail) < 0) {
+        pa_log("Failed to parse namereg_fail argument.");
+        pa_card_new_data_done(&data);
+        goto fail;
+    }
+    data.namereg_fail = namereg_fail;
+
+    if (reserve)
+        if ((description = pa_proplist_gets(data.proplist, PA_PROP_DEVICE_DESCRIPTION)))
+            pa_reserve_wrapper_set_application_device_name(reserve, description);
+
+    add_profiles(u, data.profiles, data.ports);
+
+    if (pa_hashmap_isempty(data.profiles)) {
+        pa_log("Failed to find a working profile.");
+        pa_card_new_data_done(&data);
+        goto fail;
+    }
+
+    add_disabled_profile(data.profiles);
+
+    if (pa_modargs_get_proplist(u->modargs, "card_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_card_new_data_done(&data);
+        goto fail;
+    }
+
+    u->card = pa_card_new(m->core, &data);
+    pa_card_new_data_done(&data);
+
+    if (!u->card)
+        goto fail;
+
+    u->card->userdata = u;
+    u->card->set_profile = card_set_profile;
+
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_SUSPEND_CHANGED], PA_HOOK_NORMAL,
+            (pa_hook_cb_t) card_suspend_changed, u);
+
+    init_jacks(u);
+
+    pa_card_choose_initial_profile(u->card);
+
+    /* If the "profile" modarg is given, we have to override whatever the usual
+     * policy chose in pa_card_choose_initial_profile(). */
+    profile_str = pa_modargs_get_value(u->modargs, "profile", NULL);
+    if (profile_str) {
+        pa_card_profile *profile;
+
+        profile = pa_hashmap_get(u->card->profiles, profile_str);
+        if (!profile) {
+            pa_log("No such profile: %s", profile_str);
+            goto fail;
+        }
+
+        pa_card_set_profile(u->card, profile, false);
+    }
+
+    pa_card_put(u->card);
+
+    init_profile(u);
+    init_eld_ctls(u);
+
+    if (reserve)
+        pa_reserve_wrapper_unref(reserve);
+
+    if (!pa_hashmap_isempty(u->profile_set->decibel_fixes))
+        pa_log_warn("Card %s uses decibel fixes (i.e. overrides the decibel information for some alsa volume elements). "
+                    "Please note that this feature is meant just as a help for figuring out the correct decibel values. "
+                    "PulseAudio is not the correct place to maintain the decibel mappings! The fixed decibel values "
+                    "should be sent to ALSA developers so that they can fix the driver. If it turns out that this feature "
+                    "is abused (i.e. fixes are not pushed to ALSA), the decibel fix feature may be removed in some future "
+                    "PulseAudio version.", u->card->name);
+
+    return 0;
+
+fail:
+    if (reserve)
+        pa_reserve_wrapper_unref(reserve);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+    int n = 0;
+    uint32_t idx;
+    pa_sink *sink;
+    pa_source *source;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+    pa_assert(u->card);
+
+    PA_IDXSET_FOREACH(sink, u->card->sinks, idx)
+        n += pa_sink_linked_by(sink);
+
+    PA_IDXSET_FOREACH(source, u->card->sources, idx)
+        n += pa_source_linked_by(source);
+
+    return n;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        goto finish;
+
+    if (u->mixer_fdl)
+        pa_alsa_fdlist_free(u->mixer_fdl);
+    if (u->mixer_handle)
+        snd_mixer_close(u->mixer_handle);
+    if (u->jacks)
+        pa_hashmap_free(u->jacks);
+
+    if (u->card && u->card->sinks)
+        pa_idxset_remove_all(u->card->sinks, (pa_free_cb_t) pa_alsa_sink_free);
+
+    if (u->card && u->card->sources)
+        pa_idxset_remove_all(u->card->sources, (pa_free_cb_t) pa_alsa_source_free);
+
+    if (u->card)
+        pa_card_free(u->card);
+
+    if (u->modargs)
+        pa_modargs_free(u->modargs);
+
+    if (u->profile_set)
+        pa_alsa_profile_set_free(u->profile_set);
+
+    pa_alsa_ucm_free(&u->ucm);
+
+    pa_xfree(u->device_id);
+    pa_xfree(u);
+
+finish:
+    pa_alsa_refcnt_dec();
+}
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
new file mode 100644 (file)
index 0000000..854d080
--- /dev/null
@@ -0,0 +1,138 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/module.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+
+#include "alsa-util.h"
+#include "alsa-sink.h"
+#include "module-alsa-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "name=<name of the sink, to be prefixed> "
+        "sink_name=<name for the sink> "
+        "sink_properties=<properties for the sink> "
+        "namereg_fail=<when false attempt to synthesise new sink_name if it is already taken> "
+        "device=<ALSA device> "
+        "device_id=<ALSA card index> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "alternate_rate=<alternate sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map> "
+        "fragments=<number of fragments> "
+        "fragment_size=<fragment size> "
+        "mmap=<enable memory mapping?> "
+        "tsched=<enable system timer based scheduling mode?> "
+        "tsched_buffer_size=<buffer size when using timer based scheduling> "
+        "tsched_buffer_watermark=<lower fill watermark> "
+        "ignore_dB=<ignore dB information from the device?> "
+        "control=<name of mixer control> "
+        "rewind_safeguard=<number of bytes that cannot be rewound> "
+        "deferred_volume=<Synchronize software and hardware volume changes to avoid momentary jumps?> "
+        "deferred_volume_safety_margin=<usec adjustment depending on volume direction> "
+        "deferred_volume_extra_delay=<usec adjustment to HW volume changes> "
+        "fixed_latency_range=<disable latency range changes on underrun?>");
+
+static const char* const valid_modargs[] = {
+    "name",
+    "sink_name",
+    "sink_properties",
+    "namereg_fail",
+    "device",
+    "device_id",
+    "format",
+    "rate",
+    "alternate_rate",
+    "channels",
+    "channel_map",
+    "fragments",
+    "fragment_size",
+    "mmap",
+    "tsched",
+    "tsched_buffer_size",
+    "tsched_buffer_watermark",
+    "ignore_dB",
+    "control",
+    "rewind_safeguard",
+    "deferred_volume",
+    "deferred_volume_safety_margin",
+    "deferred_volume_extra_delay",
+    "fixed_latency_range",
+    NULL
+};
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+
+    pa_assert(m);
+
+    pa_alsa_refcnt_inc();
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (!(m->userdata = pa_alsa_sink_new(m, ma, __FILE__, NULL, NULL)))
+        goto fail;
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    pa_sink *sink;
+
+    pa_assert(m);
+    pa_assert_se(sink = m->userdata);
+
+    return pa_sink_linked_by(sink);
+}
+
+void pa__done(pa_module*m) {
+    pa_sink *sink;
+
+    pa_assert(m);
+
+    if ((sink = m->userdata))
+        pa_alsa_sink_free(sink);
+
+    pa_alsa_refcnt_dec();
+}
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
new file mode 100644 (file)
index 0000000..45fb1ac
--- /dev/null
@@ -0,0 +1,145 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <asoundlib.h>
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "alsa-util.h"
+#include "alsa-source.h"
+#include "module-alsa-source-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "name=<name for the source, to be prefixed> "
+        "source_name=<name for the source> "
+        "source_properties=<properties for the source> "
+        "namereg_fail=<when false attempt to synthesise new source_name if it is already taken> "
+        "device=<ALSA device> "
+        "device_id=<ALSA card index> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "alternate_rate=<alternate sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map> "
+        "fragments=<number of fragments> "
+        "fragment_size=<fragment size> "
+        "mmap=<enable memory mapping?> "
+        "tsched=<enable system timer based scheduling mode?> "
+        "tsched_buffer_size=<buffer size when using timer based scheduling> "
+        "tsched_buffer_watermark=<upper fill watermark> "
+        "ignore_dB=<ignore dB information from the device?> "
+        "control=<name of mixer control>"
+        "deferred_volume=<Synchronize software and hardware volume changes to avoid momentary jumps?> "
+        "deferred_volume_safety_margin=<usec adjustment depending on volume direction> "
+        "deferred_volume_extra_delay=<usec adjustment to HW volume changes> "
+        "fixed_latency_range=<disable latency range changes on overrun?>");
+
+static const char* const valid_modargs[] = {
+    "name",
+    "source_name",
+    "source_properties",
+    "namereg_fail",
+    "device",
+    "device_id",
+    "format",
+    "rate",
+    "alternate_rate",
+    "channels",
+    "channel_map",
+    "fragments",
+    "fragment_size",
+    "mmap",
+    "tsched",
+    "tsched_buffer_size",
+    "tsched_buffer_watermark",
+    "ignore_dB",
+    "control",
+    "deferred_volume",
+    "deferred_volume_safety_margin",
+    "deferred_volume_extra_delay",
+    "fixed_latency_range",
+    NULL
+};
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+
+    pa_assert(m);
+
+    pa_alsa_refcnt_inc();
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (!(m->userdata = pa_alsa_source_new(m, ma, __FILE__, NULL, NULL)))
+        goto fail;
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    pa_source *source;
+
+    pa_assert(m);
+    pa_assert_se(source = m->userdata);
+
+    return pa_source_linked_by(source);
+}
+
+void pa__done(pa_module*m) {
+    pa_source *source;
+
+    pa_assert(m);
+
+    if ((source = m->userdata))
+        pa_alsa_source_free(source);
+
+    pa_alsa_refcnt_dec();
+}
diff --git a/src/modules/bluetooth/a2dp-codecs.h b/src/modules/bluetooth/a2dp-codecs.h
new file mode 100644 (file)
index 0000000..8afcfcb
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define A2DP_CODEC_SBC                 0x00
+#define A2DP_CODEC_MPEG12              0x01
+#define A2DP_CODEC_MPEG24              0x02
+#define A2DP_CODEC_ATRAC               0x03
+
+#define SBC_SAMPLING_FREQ_16000                (1 << 3)
+#define SBC_SAMPLING_FREQ_32000                (1 << 2)
+#define SBC_SAMPLING_FREQ_44100                (1 << 1)
+#define SBC_SAMPLING_FREQ_48000                1
+
+#define SBC_CHANNEL_MODE_MONO          (1 << 3)
+#define SBC_CHANNEL_MODE_DUAL_CHANNEL  (1 << 2)
+#define SBC_CHANNEL_MODE_STEREO                (1 << 1)
+#define SBC_CHANNEL_MODE_JOINT_STEREO  1
+
+#define SBC_BLOCK_LENGTH_4             (1 << 3)
+#define SBC_BLOCK_LENGTH_8             (1 << 2)
+#define SBC_BLOCK_LENGTH_12            (1 << 1)
+#define SBC_BLOCK_LENGTH_16            1
+
+#define SBC_SUBBANDS_4                 (1 << 1)
+#define SBC_SUBBANDS_8                 1
+
+#define SBC_ALLOCATION_SNR             (1 << 1)
+#define SBC_ALLOCATION_LOUDNESS                1
+
+#define MPEG_CHANNEL_MODE_MONO         (1 << 3)
+#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define MPEG_CHANNEL_MODE_STEREO       (1 << 1)
+#define MPEG_CHANNEL_MODE_JOINT_STEREO 1
+
+#define MPEG_LAYER_MP1                 (1 << 2)
+#define MPEG_LAYER_MP2                 (1 << 1)
+#define MPEG_LAYER_MP3                 1
+
+#define MPEG_SAMPLING_FREQ_16000       (1 << 5)
+#define MPEG_SAMPLING_FREQ_22050       (1 << 4)
+#define MPEG_SAMPLING_FREQ_24000       (1 << 3)
+#define MPEG_SAMPLING_FREQ_32000       (1 << 2)
+#define MPEG_SAMPLING_FREQ_44100       (1 << 1)
+#define MPEG_SAMPLING_FREQ_48000       1
+
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+typedef struct {
+       uint8_t channel_mode:4;
+       uint8_t frequency:4;
+       uint8_t allocation_method:2;
+       uint8_t subbands:2;
+       uint8_t block_length:4;
+       uint8_t min_bitpool;
+       uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+       uint8_t channel_mode:4;
+       uint8_t crc:1;
+       uint8_t layer:3;
+       uint8_t frequency:6;
+       uint8_t mpf:1;
+       uint8_t rfa:1;
+       uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+typedef struct {
+       uint8_t frequency:4;
+       uint8_t channel_mode:4;
+       uint8_t block_length:4;
+       uint8_t subbands:2;
+       uint8_t allocation_method:2;
+       uint8_t min_bitpool;
+       uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+       uint8_t layer:3;
+       uint8_t crc:1;
+       uint8_t channel_mode:4;
+       uint8_t rfa:1;
+       uint8_t mpf:1;
+       uint8_t frequency:6;
+       uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
new file mode 100644 (file)
index 0000000..0f0104d
--- /dev/null
@@ -0,0 +1,714 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 Wim Taymans <wim.taymans at gmail.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/shared.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-shared.h>
+#include <pulsecore/log.h>
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sco.h>
+
+#include "bluez5-util.h"
+
+struct pa_bluetooth_backend {
+  pa_core *core;
+  pa_dbus_connection *connection;
+  pa_bluetooth_discovery *discovery;
+  bool enable_hs_role;
+
+  PA_LLIST_HEAD(pa_dbus_pending, pending);
+};
+
+struct transport_data {
+    int rfcomm_fd;
+    pa_io_event *rfcomm_io;
+    int sco_fd;
+    pa_io_event *sco_io;
+    pa_mainloop_api *mainloop;
+};
+
+#define BLUEZ_SERVICE "org.bluez"
+#define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
+
+#define BLUEZ_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported"
+
+#define BLUEZ_PROFILE_MANAGER_INTERFACE BLUEZ_SERVICE ".ProfileManager1"
+#define BLUEZ_PROFILE_INTERFACE BLUEZ_SERVICE ".Profile1"
+
+#define HSP_AG_PROFILE "/Profile/HSPAGProfile"
+#define HSP_HS_PROFILE "/Profile/HSPHSProfile"
+
+/* RFCOMM channel for HSP headset role
+ * The choice seems to be a bit arbitrary -- it looks like at least channels 2, 4 and 5 also work*/
+#define HSP_HS_DEFAULT_CHANNEL  3
+
+#define PROFILE_INTROSPECT_XML                                          \
+    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
+    "<node>"                                                            \
+    " <interface name=\"" BLUEZ_PROFILE_INTERFACE "\">"                 \
+    "  <method name=\"Release\">"                                       \
+    "  </method>"                                                       \
+    "  <method name=\"RequestDisconnection\">"                          \
+    "   <arg name=\"device\" direction=\"in\" type=\"o\"/>"             \
+    "  </method>"                                                       \
+    "  <method name=\"NewConnection\">"                                 \
+    "   <arg name=\"device\" direction=\"in\" type=\"o\"/>"             \
+    "   <arg name=\"fd\" direction=\"in\" type=\"h\"/>"                 \
+    "   <arg name=\"opts\" direction=\"in\" type=\"a{sv}\"/>"           \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
+    "  <method name=\"Introspect\">"                                    \
+    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    "</node>"
+
+static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m,
+        DBusPendingCallNotifyFunction func, void *call_data) {
+
+    pa_dbus_pending *p;
+    DBusPendingCall *call;
+
+    pa_assert(backend);
+    pa_assert(m);
+
+    pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(backend->connection), m, &call, -1));
+
+    p = pa_dbus_pending_new(pa_dbus_connection_get(backend->connection), m, call, backend, call_data);
+    PA_LLIST_PREPEND(pa_dbus_pending, backend->pending, p);
+    dbus_pending_call_set_notify(call, func, p, NULL);
+
+    return p;
+}
+
+static int sco_do_connect(pa_bluetooth_transport *t) {
+    pa_bluetooth_device *d = t->device;
+    struct sockaddr_sco addr;
+    socklen_t len;
+    int err, i;
+    int sock;
+    bdaddr_t src;
+    bdaddr_t dst;
+    const char *src_addr, *dst_addr;
+
+    src_addr = d->adapter->address;
+    dst_addr = d->address;
+
+    /* don't use ba2str to avoid -lbluetooth */
+    for (i = 5; i >= 0; i--, src_addr += 3)
+        src.b[i] = strtol(src_addr, NULL, 16);
+    for (i = 5; i >= 0; i--, dst_addr += 3)
+        dst.b[i] = strtol(dst_addr, NULL, 16);
+
+    sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+    if (sock < 0) {
+        pa_log_error("socket(SEQPACKET, SCO) %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    len = sizeof(addr);
+    memset(&addr, 0, len);
+    addr.sco_family = AF_BLUETOOTH;
+    bacpy(&addr.sco_bdaddr, &src);
+
+    if (bind(sock, (struct sockaddr *) &addr, len) < 0) {
+        pa_log_error("bind(): %s", pa_cstrerror(errno));
+        goto fail_close;
+    }
+
+    memset(&addr, 0, len);
+    addr.sco_family = AF_BLUETOOTH;
+    bacpy(&addr.sco_bdaddr, &dst);
+
+    pa_log_info("doing connect");
+    err = connect(sock, (struct sockaddr *) &addr, len);
+    if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) {
+        pa_log_error("connect(): %s", pa_cstrerror(errno));
+        goto fail_close;
+    }
+    return sock;
+
+fail_close:
+    close(sock);
+    return -1;
+}
+
+static int sco_do_accept(pa_bluetooth_transport *t) {
+    struct transport_data *trd = t->userdata;
+    struct sockaddr_sco addr;
+    socklen_t optlen;
+    int sock;
+
+    memset(&addr, 0, sizeof(addr));
+    optlen = sizeof(addr);
+
+    pa_log_info ("doing accept");
+    sock = accept(trd->sco_fd, (struct sockaddr *) &addr, &optlen);
+    if (sock < 0) {
+        if (errno != EAGAIN)
+            pa_log_error("accept(): %s", pa_cstrerror(errno));
+        goto fail;
+    }
+    return sock;
+
+fail:
+    return -1;
+}
+
+static int sco_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
+    int sock;
+    socklen_t len;
+
+    if (optional)
+        sock = sco_do_accept(t);
+    else
+        sock = sco_do_connect(t);
+
+    if (sock < 0)
+        goto fail;
+
+    if (imtu) *imtu = 48;
+    if (omtu) *omtu = 48;
+
+    if (t->device->autodetect_mtu) {
+        struct sco_options sco_opt;
+
+        len = sizeof(sco_opt);
+        memset(&sco_opt, 0, len);
+
+        if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0)
+            pa_log_warn("getsockopt(SCO_OPTIONS) failed, loading defaults");
+        else {
+            pa_log_debug("autodetected imtu = omtu = %u", sco_opt.mtu);
+            if (imtu) *imtu = sco_opt.mtu;
+            if (omtu) *omtu = sco_opt.mtu;
+        }
+    }
+
+    return sock;
+
+fail:
+    return -1;
+}
+
+static void sco_release_cb(pa_bluetooth_transport *t) {
+    pa_log_info("Transport %s released", t->path);
+    /* device will close the SCO socket for us */
+}
+
+static void sco_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    pa_bluetooth_transport *t = userdata;
+
+    pa_assert(io);
+    pa_assert(t);
+
+    if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
+        pa_log_error("error listening SCO connection: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (t->state != PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) {
+        pa_log_info("SCO incoming connection: changing state to PLAYING");
+        pa_bluetooth_transport_set_state (t, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING);
+    }
+
+fail:
+    return;
+}
+
+static int sco_listen(pa_bluetooth_transport *t) {
+    struct transport_data *trd = t->userdata;
+    struct sockaddr_sco addr;
+    int sock, i;
+    bdaddr_t src;
+    const char *src_addr;
+
+    sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, BTPROTO_SCO);
+    if (sock < 0) {
+        pa_log_error("socket(SEQPACKET, SCO) %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    src_addr = t->device->adapter->address;
+
+    /* don't use ba2str to avoid -lbluetooth */
+    for (i = 5; i >= 0; i--, src_addr += 3)
+        src.b[i] = strtol(src_addr, NULL, 16);
+
+    /* Bind to local address */
+    memset(&addr, 0, sizeof(addr));
+    addr.sco_family = AF_BLUETOOTH;
+    bacpy(&addr.sco_bdaddr, &src);
+
+    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        pa_log_error("bind(): %s", pa_cstrerror(errno));
+        goto fail_close;
+    }
+
+    pa_log_info ("doing listen");
+    if (listen(sock, 1) < 0) {
+        pa_log_error("listen(): %s", pa_cstrerror(errno));
+        goto fail_close;
+    }
+
+    trd->sco_fd = sock;
+    trd->sco_io = trd->mainloop->io_new(trd->mainloop, sock, PA_IO_EVENT_INPUT,
+        sco_io_callback, t);
+
+    return sock;
+
+fail_close:
+    close(sock);
+    return -1;
+}
+
+static void register_profile_reply(DBusPendingCall *pending, void *userdata) {
+    DBusMessage *r;
+    pa_dbus_pending *p;
+    pa_bluetooth_backend *b;
+    char *profile;
+
+    pa_assert(pending);
+    pa_assert_se(p = userdata);
+    pa_assert_se(b = p->context_data);
+    pa_assert_se(profile = p->call_data);
+    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+    if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
+        pa_log_info("Couldn't register profile %s because it is disabled in BlueZ", profile);
+        goto finish;
+    }
+
+    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+        pa_log_error(BLUEZ_PROFILE_MANAGER_INTERFACE ".RegisterProfile() failed: %s: %s", dbus_message_get_error_name(r),
+                     pa_dbus_get_error_message(r));
+        goto finish;
+    }
+
+finish:
+    dbus_message_unref(r);
+
+    PA_LLIST_REMOVE(pa_dbus_pending, b->pending, p);
+    pa_dbus_pending_free(p);
+
+    pa_xfree(profile);
+}
+
+static void register_profile(pa_bluetooth_backend *b, const char *profile, const char *uuid) {
+    DBusMessage *m;
+    DBusMessageIter i, d;
+    dbus_bool_t autoconnect;
+    dbus_uint16_t version, chan;
+
+    pa_log_debug("Registering Profile %s %s", profile, uuid);
+
+    pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/org/bluez", BLUEZ_PROFILE_MANAGER_INTERFACE, "RegisterProfile"));
+
+    dbus_message_iter_init_append(m, &i);
+    pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &profile));
+    pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &uuid));
+    dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
+            DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d);
+    if (pa_streq (uuid, PA_BLUETOOTH_UUID_HSP_HS)) {
+        /* In the headset role, the connection will only be initiated from the remote side */
+        autoconnect = 0;
+        pa_dbus_append_basic_variant_dict_entry(&d, "AutoConnect", DBUS_TYPE_BOOLEAN, &autoconnect);
+        chan = HSP_HS_DEFAULT_CHANNEL;
+        pa_dbus_append_basic_variant_dict_entry(&d, "Channel", DBUS_TYPE_UINT16, &chan);
+        /* HSP version 1.2 */
+        version = 0x0102;
+        pa_dbus_append_basic_variant_dict_entry(&d, "Version", DBUS_TYPE_UINT16, &version);
+    }
+    dbus_message_iter_close_container(&i, &d);
+
+    send_and_add_to_pending(b, m, register_profile_reply, pa_xstrdup(profile));
+}
+
+static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    pa_bluetooth_transport *t = userdata;
+
+    pa_assert(io);
+    pa_assert(t);
+
+    if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
+        pa_log_info("Lost RFCOMM connection.");
+        goto fail;
+    }
+
+    if (events & PA_IO_EVENT_INPUT) {
+        char buf[512];
+        ssize_t len;
+        int gain, dummy;
+        bool  do_reply = false;
+
+        len = pa_read(fd, buf, 511, NULL);
+        if (len < 0) {
+            pa_log_error("RFCOMM read error: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+        buf[len] = 0;
+        pa_log_debug("RFCOMM << %s", buf);
+
+        /* There are only four HSP AT commands:
+         * AT+VGS=value: value between 0 and 15, sent by the HS to AG to set the speaker gain.
+         * +VGS=value is sent by AG to HS as a response to an AT+VGS command or when the gain
+         * is changed on the AG side.
+         * AT+VGM=value: value between 0 and 15, sent by the HS to AG to set the microphone gain.
+         * +VGM=value is sent by AG to HS as a response to an AT+VGM command or when the gain
+         * is changed on the AG side.
+         * AT+CKPD=200: Sent by HS when headset button is pressed.
+         * RING: Sent by AG to HS to notify of an incoming call. It can safely be ignored because
+         * it does not expect a reply. */
+        if (sscanf(buf, "AT+VGS=%d", &gain) == 1 || sscanf(buf, "\r\n+VGM=%d\r\n", &gain) == 1) {
+            t->speaker_gain = gain;
+            pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED), t);
+            do_reply = true;
+
+        } else if (sscanf(buf, "AT+VGM=%d", &gain) == 1 || sscanf(buf, "\r\n+VGS=%d\r\n", &gain) == 1) {
+            t->microphone_gain = gain;
+            pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED), t);
+            do_reply = true;
+        } else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) {
+            do_reply = true;
+        } else {
+            do_reply = false;
+        }
+
+        if (do_reply) {
+            pa_log_debug("RFCOMM >> OK");
+
+            len = write(fd, "\r\nOK\r\n", 6);
+
+            /* we ignore any errors, it's not critical and real errors should
+             * be caught with the HANGUP and ERROR events handled above */
+            if (len < 0)
+                pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
+        }
+    }
+
+    return;
+
+fail:
+    pa_bluetooth_transport_unlink(t);
+    pa_bluetooth_transport_free(t);
+}
+
+static void transport_destroy(pa_bluetooth_transport *t) {
+    struct transport_data *trd = t->userdata;
+
+    if (trd->sco_io) {
+        trd->mainloop->io_free(trd->sco_io);
+        shutdown(trd->sco_fd, SHUT_RDWR);
+        close (trd->sco_fd);
+    }
+
+    trd->mainloop->io_free(trd->rfcomm_io);
+    shutdown(trd->rfcomm_fd, SHUT_RDWR);
+    close (trd->rfcomm_fd);
+
+    pa_xfree(trd);
+}
+
+static void set_speaker_gain(pa_bluetooth_transport *t, uint16_t gain) {
+    struct transport_data *trd = t->userdata;
+    char buf[512];
+    ssize_t len, written;
+
+    if (t->speaker_gain == gain)
+      return;
+
+    t->speaker_gain = gain;
+
+    /* If we are in the AG role, we send a command to the head set to change
+     * the speaker gain. In the HS role, source and sink are swapped, so
+     * in this case we notify the AG that the microphone gain has changed */
+    if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) {
+        len = sprintf(buf, "\r\n+VGS=%d\r\n", gain);
+        pa_log_debug("RFCOMM >> +VGS=%d", gain);
+    } else {
+        len = sprintf(buf, "\r\nAT+VGM=%d\r\n", gain);
+        pa_log_debug("RFCOMM >> AT+VGM=%d", gain);
+    }
+
+    written = write(trd->rfcomm_fd, buf, len);
+
+    if (written != len)
+        pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
+}
+
+static void set_microphone_gain(pa_bluetooth_transport *t, uint16_t gain) {
+    struct transport_data *trd = t->userdata;
+    char buf[512];
+    ssize_t len, written;
+
+    if (t->microphone_gain == gain)
+      return;
+
+    t->microphone_gain = gain;
+
+    /* If we are in the AG role, we send a command to the head set to change
+     * the microphone gain. In the HS role, source and sink are swapped, so
+     * in this case we notify the AG that the speaker gain has changed */
+    if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) {
+        len = sprintf(buf, "\r\n+VGM=%d\r\n", gain);
+        pa_log_debug("RFCOMM >> +VGM=%d", gain);
+    } else {
+        len = sprintf(buf, "\r\nAT+VGS=%d\r\n", gain);
+        pa_log_debug("RFCOMM >> AT+VGS=%d", gain);
+    }
+
+    written = write (trd->rfcomm_fd, buf, len);
+
+    if (written != len)
+        pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
+}
+
+static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m, void *userdata) {
+    pa_bluetooth_backend *b = userdata;
+    pa_bluetooth_device *d;
+    pa_bluetooth_transport *t;
+    pa_bluetooth_profile_t p;
+    DBusMessage *r;
+    int fd;
+    const char *sender, *path, PA_UNUSED *handler;
+    DBusMessageIter arg_i;
+    char *pathfd;
+    struct transport_data *trd;
+
+    if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oha{sv}")) {
+        pa_log_error("Invalid signature found in NewConnection");
+        goto fail;
+    }
+
+    handler = dbus_message_get_path(m);
+    if (pa_streq(handler, HSP_AG_PROFILE)) {
+        p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;
+    } else if (pa_streq(handler, HSP_HS_PROFILE)) {
+        p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;
+    } else {
+        pa_log_error("Invalid handler");
+        goto fail;
+    }
+
+    pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_OBJECT_PATH);
+    dbus_message_iter_get_basic(&arg_i, &path);
+
+    d = pa_bluetooth_discovery_get_device_by_path(b->discovery, path);
+    if (d == NULL) {
+        pa_log_error("Device doesnt exist for %s", path);
+        goto fail;
+    }
+
+    pa_assert_se(dbus_message_iter_next(&arg_i));
+
+    pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_UNIX_FD);
+    dbus_message_iter_get_basic(&arg_i, &fd);
+
+    pa_log_debug("dbus: NewConnection path=%s, fd=%d, profile %s", path, fd,
+        pa_bluetooth_profile_to_string(p));
+
+    sender = dbus_message_get_sender(m);
+
+    pathfd = pa_sprintf_malloc ("%s/fd%d", path, fd);
+    t = pa_bluetooth_transport_new(d, sender, pathfd, p, NULL, 0);
+    pa_xfree(pathfd);
+
+    t->acquire = sco_acquire_cb;
+    t->release = sco_release_cb;
+    t->destroy = transport_destroy;
+    t->set_speaker_gain = set_speaker_gain;
+    t->set_microphone_gain = set_microphone_gain;
+
+    trd = pa_xnew0(struct transport_data, 1);
+    trd->rfcomm_fd = fd;
+    trd->mainloop = b->core->mainloop;
+    trd->rfcomm_io = trd->mainloop->io_new(b->core->mainloop, fd, PA_IO_EVENT_INPUT,
+        rfcomm_io_callback, t);
+    t->userdata =  trd;
+
+    sco_listen(t);
+
+    pa_bluetooth_transport_put(t);
+
+    pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
+
+    pa_assert_se(r = dbus_message_new_method_return(m));
+
+    return r;
+
+fail:
+    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to handle new connection"));
+    return r;
+}
+
+static DBusMessage *profile_request_disconnection(DBusConnection *conn, DBusMessage *m, void *userdata) {
+    DBusMessage *r;
+
+    pa_assert_se(r = dbus_message_new_method_return(m));
+
+    return r;
+}
+
+static DBusHandlerResult profile_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
+    pa_bluetooth_backend *b = userdata;
+    DBusMessage *r = NULL;
+    const char *path, *interface, *member;
+
+    pa_assert(b);
+
+    path = dbus_message_get_path(m);
+    interface = dbus_message_get_interface(m);
+    member = dbus_message_get_member(m);
+
+    pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
+
+    if (!pa_streq(path, HSP_AG_PROFILE) && !pa_streq(path, HSP_HS_PROFILE))
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+        const char *xml = PROFILE_INTROSPECT_XML;
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
+
+    } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "Release")) {
+        pa_log_debug("Release not handled");
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "RequestDisconnection")) {
+        r = profile_request_disconnection(c, m, userdata);
+    } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "NewConnection"))
+        r = profile_new_connection(c, m, userdata);
+    else
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (r) {
+        pa_assert_se(dbus_connection_send(pa_dbus_connection_get(b->connection), r, NULL));
+        dbus_message_unref(r);
+    }
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void profile_init(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile) {
+    static const DBusObjectPathVTable vtable_profile = {
+        .message_function = profile_handler,
+    };
+    const char *object_name;
+    const char *uuid;
+
+    pa_assert(b);
+
+    switch (profile) {
+        case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+            object_name = HSP_AG_PROFILE;
+            uuid = PA_BLUETOOTH_UUID_HSP_AG;
+            break;
+        case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+            object_name = HSP_HS_PROFILE;
+            uuid = PA_BLUETOOTH_UUID_HSP_HS;
+            break;
+        default:
+            pa_assert_not_reached();
+            break;
+    }
+
+    pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(b->connection), object_name, &vtable_profile, b));
+    register_profile(b, object_name, uuid);
+}
+
+static void profile_done(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile) {
+    pa_assert(b);
+
+    switch (profile) {
+        case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+            dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_AG_PROFILE);
+            break;
+        case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+            dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_HS_PROFILE);
+            break;
+        default:
+            pa_assert_not_reached();
+            break;
+    }
+}
+
+void pa_bluetooth_native_backend_enable_hs_role(pa_bluetooth_backend *native_backend, bool enable_hs_role) {
+
+   if (enable_hs_role == native_backend->enable_hs_role)
+       return;
+
+   if (enable_hs_role)
+       profile_init(native_backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
+   else
+       profile_done(native_backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
+
+   native_backend->enable_hs_role = enable_hs_role;
+}
+
+pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_hs_role) {
+    pa_bluetooth_backend *backend;
+    DBusError err;
+
+    pa_log_debug("Bluetooth Headset Backend API support using the native backend");
+
+    backend = pa_xnew0(pa_bluetooth_backend, 1);
+    backend->core = c;
+
+    dbus_error_init(&err);
+    if (!(backend->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) {
+        pa_log("Failed to get D-Bus connection: %s", err.message);
+        dbus_error_free(&err);
+        pa_xfree(backend);
+        return NULL;
+    }
+
+    backend->discovery = y;
+    backend->enable_hs_role = enable_hs_role;
+
+    if (enable_hs_role)
+       profile_init(backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
+    profile_init(backend, PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT);
+
+    return backend;
+}
+
+void pa_bluetooth_native_backend_free(pa_bluetooth_backend *backend) {
+    pa_assert(backend);
+
+    pa_dbus_free_pending_list(&backend->pending);
+
+    if (backend->enable_hs_role)
+       profile_done(backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
+    profile_done(backend, PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT);
+
+    pa_dbus_connection_unref(backend->connection);
+
+    pa_xfree(backend);
+}
diff --git a/src/modules/bluetooth/backend-ofono.c b/src/modules/bluetooth/backend-ofono.c
new file mode 100644 (file)
index 0000000..2c51497
--- /dev/null
@@ -0,0 +1,685 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 João Paulo Rechi Vita
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <poll.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-shared.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/core-error.h>
+
+#include "bluez5-util.h"
+
+#define HFP_AUDIO_CODEC_CVSD    0x01
+#define HFP_AUDIO_CODEC_MSBC    0x02
+
+#define OFONO_SERVICE "org.ofono"
+#define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent"
+#define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager"
+
+#define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent"
+
+#define HF_AUDIO_AGENT_XML                                          \
+    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
+    "<node>"                                                        \
+    "  <interface name=\"org.freedesktop.DBus.Introspectable\">"    \
+    "    <method name=\"Introspect\">"                              \
+    "      <arg direction=\"out\" type=\"s\" />"                    \
+    "    </method>"                                                 \
+    "  </interface>"                                                \
+    "  <interface name=\"org.ofono.HandsfreeAudioAgent\">"          \
+    "    <method name=\"Release\">"                                 \
+    "    </method>"                                                 \
+    "    <method name=\"NewConnection\">"                           \
+    "      <arg direction=\"in\"  type=\"o\" name=\"card_path\" />" \
+    "      <arg direction=\"in\"  type=\"h\" name=\"sco_fd\" />"    \
+    "      <arg direction=\"in\"  type=\"y\" name=\"codec\" />"     \
+    "    </method>"                                                 \
+    "  </interface>"                                                \
+    "</node>"
+
+struct hf_audio_card {
+    pa_bluetooth_backend *backend;
+    char *path;
+    char *remote_address;
+    char *local_address;
+
+    bool connecting;
+    int fd;
+    uint8_t codec;
+
+    pa_bluetooth_transport *transport;
+};
+
+struct pa_bluetooth_backend {
+    pa_core *core;
+    pa_bluetooth_discovery *discovery;
+    pa_dbus_connection *connection;
+    pa_hashmap *cards;
+    char *ofono_bus_id;
+
+    PA_LLIST_HEAD(pa_dbus_pending, pending);
+};
+
+static pa_dbus_pending* hf_dbus_send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m,
+                                                    DBusPendingCallNotifyFunction func, void *call_data) {
+    pa_dbus_pending *p;
+    DBusPendingCall *call;
+
+    pa_assert(backend);
+    pa_assert(m);
+
+    pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(backend->connection), m, &call, -1));
+
+    p = pa_dbus_pending_new(pa_dbus_connection_get(backend->connection), m, call, backend, call_data);
+    PA_LLIST_PREPEND(pa_dbus_pending, backend->pending, p);
+    dbus_pending_call_set_notify(call, func, p, NULL);
+
+    return p;
+}
+
+static struct hf_audio_card *hf_audio_card_new(pa_bluetooth_backend *backend, const char *path) {
+    struct hf_audio_card *card = pa_xnew0(struct hf_audio_card, 1);
+
+    card->path = pa_xstrdup(path);
+    card->backend = backend;
+    card->fd = -1;
+
+    return card;
+}
+
+static void hf_audio_card_free(struct hf_audio_card *card) {
+    pa_assert(card);
+
+    if (card->transport)
+        pa_bluetooth_transport_free(card->transport);
+
+    pa_xfree(card->path);
+    pa_xfree(card->remote_address);
+    pa_xfree(card->local_address);
+    pa_xfree(card);
+}
+
+static int socket_accept(int sock)
+{
+    char c;
+    struct pollfd pfd;
+
+    if (sock < 0)
+        return -ENOTCONN;
+
+    memset(&pfd, 0, sizeof(pfd));
+    pfd.fd = sock;
+    pfd.events = POLLOUT;
+
+    if (poll(&pfd, 1, 0) < 0)
+        return -errno;
+
+    /*
+     * If socket already writable then it is not in defer setup state,
+     * otherwise it needs to be read to authorize the connection.
+     */
+    if ((pfd.revents & POLLOUT))
+        return 0;
+
+    /* Enable socket by reading 1 byte */
+    if (read(sock, &c, 1) < 0)
+        return -errno;
+
+    return 0;
+}
+
+static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
+    struct hf_audio_card *card = t->userdata;
+    int err;
+
+    pa_assert(card);
+
+    if (!optional && card->fd < 0) {
+        DBusMessage *m, *r;
+        DBusError derr;
+
+        if (card->connecting)
+            return -EAGAIN;
+
+        card->connecting = true;
+
+        dbus_error_init(&derr);
+        pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.ofono.HandsfreeAudioCard", "Connect"));
+        r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(card->backend->connection), m, -1, &derr);
+        dbus_message_unref(m);
+        m = NULL;
+
+        if (!r)
+            return -1;
+
+        dbus_message_unref(r);
+        r = NULL;
+
+        if (card->connecting)
+            return -EAGAIN;
+    }
+
+    /* The correct block size should take into account the SCO MTU from
+     * the Bluetooth adapter and (for adapters in the USB bus) the MxPS
+     * value from the Isoc USB endpoint in use by btusb and should be
+     * made available to userspace by the Bluetooth kernel subsystem.
+     * Meanwhile the empiric value 48 will be used. */
+    if (imtu)
+        *imtu = 48;
+    if (omtu)
+        *omtu = 48;
+
+    t->codec = card->codec;
+
+    err = socket_accept(card->fd);
+    if (err < 0) {
+        pa_log_error("Deferred setup failed on fd %d: %s", card->fd, pa_cstrerror(-err));
+        return -1;
+    }
+
+    return card->fd;
+}
+
+static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) {
+    struct hf_audio_card *card = t->userdata;
+
+    pa_assert(card);
+
+    if (card->fd < 0) {
+        pa_log_info("Transport %s already released", t->path);
+        return;
+    }
+
+    /* shutdown to make sure connection is dropped immediately */
+    shutdown(card->fd, SHUT_RDWR);
+    close(card->fd);
+    card->fd = -1;
+}
+
+static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char *path, DBusMessageIter *props_i) {
+    DBusMessageIter i, value_i;
+    const char *key, *value;
+    struct hf_audio_card *card;
+    pa_bluetooth_device *d;
+    pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;
+
+    pa_assert(backend);
+    pa_assert(path);
+    pa_assert(props_i);
+
+    pa_log_debug("New HF card found: %s", path);
+
+    card = hf_audio_card_new(backend, path);
+
+    while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) {
+        char c;
+
+        dbus_message_iter_recurse(props_i, &i);
+
+        dbus_message_iter_get_basic(&i, &key);
+        dbus_message_iter_next(&i);
+        dbus_message_iter_recurse(&i, &value_i);
+
+        if ((c = dbus_message_iter_get_arg_type(&value_i)) != DBUS_TYPE_STRING) {
+            pa_log_error("Invalid properties for %s: expected 's', received '%c'", path, c);
+            goto fail;
+        }
+
+        dbus_message_iter_get_basic(&value_i, &value);
+
+        if (pa_streq(key, "RemoteAddress")) {
+            pa_xfree(card->remote_address);
+            card->remote_address = pa_xstrdup(value);
+        } else if (pa_streq(key, "LocalAddress")) {
+            pa_xfree(card->local_address);
+            card->local_address = pa_xstrdup(value);
+        } else if (pa_streq(key, "Type")) {
+            if (pa_streq(value, "gateway"))
+                p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;
+        }
+
+        pa_log_debug("%s: %s", key, value);
+
+        dbus_message_iter_next(props_i);
+    }
+
+    d = pa_bluetooth_discovery_get_device_by_address(backend->discovery, card->remote_address, card->local_address);
+    if (!d) {
+        pa_log_error("Device doesnt exist for %s", path);
+        goto fail;
+    }
+
+    card->transport = pa_bluetooth_transport_new(d, backend->ofono_bus_id, path, p, NULL, 0);
+    card->transport->acquire = hf_audio_agent_transport_acquire;
+    card->transport->release = hf_audio_agent_transport_release;
+    card->transport->userdata = card;
+
+    pa_bluetooth_transport_put(card->transport);
+    pa_hashmap_put(backend->cards, card->path, card);
+
+    return;
+
+fail:
+    hf_audio_card_free(card);
+}
+
+static void hf_audio_agent_card_removed(pa_bluetooth_backend *backend, const char *path) {
+    struct hf_audio_card *card;
+
+    pa_assert(backend);
+    pa_assert(path);
+
+    pa_log_debug("HF card removed: %s", path);
+
+    card = pa_hashmap_remove(backend->cards, path);
+    if (!card)
+        return;
+
+    hf_audio_card_free(card);
+}
+
+static void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userdata) {
+    DBusMessage *r;
+    pa_dbus_pending *p;
+    pa_bluetooth_backend *backend;
+    DBusMessageIter i, array_i, struct_i, props_i;
+
+    pa_assert_se(p = userdata);
+    pa_assert_se(backend = p->context_data);
+    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+        pa_log_error("Failed to get a list of handsfree audio cards from ofono: %s: %s",
+                     dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
+        goto finish;
+    }
+
+    if (!dbus_message_iter_init(r, &i) || !pa_streq(dbus_message_get_signature(r), "a(oa{sv})")) {
+        pa_log_error("Invalid arguments in GetCards() reply");
+        goto finish;
+    }
+
+    dbus_message_iter_recurse(&i, &array_i);
+    while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) {
+        const char *path;
+
+        dbus_message_iter_recurse(&array_i, &struct_i);
+        dbus_message_iter_get_basic(&struct_i, &path);
+        dbus_message_iter_next(&struct_i);
+
+        dbus_message_iter_recurse(&struct_i, &props_i);
+
+        hf_audio_agent_card_found(backend, path, &props_i);
+
+        dbus_message_iter_next(&array_i);
+    }
+
+finish:
+    dbus_message_unref(r);
+
+    PA_LLIST_REMOVE(pa_dbus_pending, backend->pending, p);
+    pa_dbus_pending_free(p);
+}
+
+static void hf_audio_agent_get_cards(pa_bluetooth_backend *hf) {
+    DBusMessage *m;
+
+    pa_assert(hf);
+
+    pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "GetCards"));
+    hf_dbus_send_and_add_to_pending(hf, m, hf_audio_agent_get_cards_reply, NULL);
+}
+
+static void ofono_bus_id_destroy(pa_bluetooth_backend *backend) {
+    pa_hashmap_remove_all(backend->cards);
+
+    if (backend->ofono_bus_id) {
+        pa_xfree(backend->ofono_bus_id);
+        backend->ofono_bus_id = NULL;
+        pa_bluetooth_discovery_set_ofono_running(backend->discovery, false);
+    }
+}
+
+static void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userdata) {
+    DBusMessage *r;
+    pa_dbus_pending *p;
+    pa_bluetooth_backend *backend;
+
+    pa_assert_se(p = userdata);
+    pa_assert_se(backend = p->context_data);
+    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+        pa_log_error("Failed to register as a handsfree audio agent with ofono: %s: %s",
+                     dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
+        goto finish;
+    }
+
+    backend->ofono_bus_id = pa_xstrdup(dbus_message_get_sender(r));
+
+    hf_audio_agent_get_cards(backend);
+
+finish:
+    dbus_message_unref(r);
+
+    PA_LLIST_REMOVE(pa_dbus_pending, backend->pending, p);
+    pa_dbus_pending_free(p);
+
+    pa_bluetooth_discovery_set_ofono_running(backend->discovery, backend->ofono_bus_id != NULL);
+}
+
+static void hf_audio_agent_register(pa_bluetooth_backend *hf) {
+    DBusMessage *m;
+    uint8_t codecs[2];
+    const uint8_t *pcodecs = codecs;
+    int ncodecs = 0;
+    const char *path = HF_AUDIO_AGENT_PATH;
+
+    pa_assert(hf);
+
+    pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "Register"));
+
+    codecs[ncodecs++] = HFP_AUDIO_CODEC_CVSD;
+
+    pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pcodecs, ncodecs,
+                                          DBUS_TYPE_INVALID));
+
+    hf_dbus_send_and_add_to_pending(hf, m, hf_audio_agent_register_reply, NULL);
+}
+
+static void hf_audio_agent_unregister(pa_bluetooth_backend *backend) {
+    DBusMessage *m;
+    const char *path = HF_AUDIO_AGENT_PATH;
+
+    pa_assert(backend);
+    pa_assert(backend->connection);
+
+    if (backend->ofono_bus_id) {
+        pa_assert_se(m = dbus_message_new_method_call(backend->ofono_bus_id, "/", HF_AUDIO_MANAGER_INTERFACE, "Unregister"));
+        pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID));
+        pa_assert_se(dbus_connection_send(pa_dbus_connection_get(backend->connection), m, NULL));
+
+        ofono_bus_id_destroy(backend);
+    }
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) {
+    const char *sender;
+    DBusError err;
+    pa_bluetooth_backend *backend = data;
+
+    pa_assert(bus);
+    pa_assert(m);
+    pa_assert(backend);
+
+    sender = dbus_message_get_sender(m);
+    if (!pa_safe_streq(backend->ofono_bus_id, sender) && !pa_streq("org.freedesktop.DBus", sender))
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    dbus_error_init(&err);
+
+    if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
+        const char *name, *old_owner, *new_owner;
+
+        if (!dbus_message_get_args(m, &err,
+                                   DBUS_TYPE_STRING, &name,
+                                   DBUS_TYPE_STRING, &old_owner,
+                                   DBUS_TYPE_STRING, &new_owner,
+                                   DBUS_TYPE_INVALID)) {
+            pa_log_error("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
+            goto fail;
+        }
+
+        if (pa_streq(name, OFONO_SERVICE)) {
+
+            if (old_owner && *old_owner) {
+                pa_log_debug("oFono disappeared");
+                ofono_bus_id_destroy(backend);
+            }
+
+            if (new_owner && *new_owner) {
+                pa_log_debug("oFono appeared");
+                hf_audio_agent_register(backend);
+            }
+        }
+
+    } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardAdded")) {
+        const char *p;
+        DBusMessageIter arg_i, props_i;
+
+        if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
+            pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardAdded");
+            goto fail;
+        }
+
+        dbus_message_iter_get_basic(&arg_i, &p);
+
+        pa_assert_se(dbus_message_iter_next(&arg_i));
+        pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
+
+        dbus_message_iter_recurse(&arg_i, &props_i);
+
+        hf_audio_agent_card_found(backend, p, &props_i);
+    } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardRemoved")) {
+        const char *p;
+
+        if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID)) {
+            pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardRemoved: %s", err.message);
+            goto fail;
+        }
+
+        hf_audio_agent_card_removed(backend, p);
+    }
+
+fail:
+    dbus_error_free(&err);
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) {
+    DBusMessage *r;
+    const char *sender;
+    pa_bluetooth_backend *backend = data;
+
+    pa_assert(backend);
+
+    sender = dbus_message_get_sender(m);
+    if (!pa_safe_streq(backend->ofono_bus_id, sender)) {
+        pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender"));
+        return r;
+    }
+
+    pa_log_debug("HF audio agent has been unregistered by oFono (%s)", backend->ofono_bus_id);
+
+    ofono_bus_id_destroy(backend);
+
+    pa_assert_se(r = dbus_message_new_method_return(m));
+
+    return r;
+}
+
+static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) {
+    DBusMessage *r;
+    const char *sender, *path;
+    int fd;
+    uint8_t codec;
+    struct hf_audio_card *card;
+    pa_bluetooth_backend *backend = data;
+
+    pa_assert(backend);
+
+    sender = dbus_message_get_sender(m);
+    if (!pa_safe_streq(backend->ofono_bus_id, sender)) {
+        pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender"));
+        return r;
+    }
+
+    if (dbus_message_get_args(m, NULL,
+                              DBUS_TYPE_OBJECT_PATH, &path,
+                              DBUS_TYPE_UNIX_FD, &fd,
+                              DBUS_TYPE_BYTE, &codec,
+                              DBUS_TYPE_INVALID) == FALSE) {
+        pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call"));
+        return r;
+    }
+
+    card = pa_hashmap_get(backend->cards, path);
+
+    card->connecting = false;
+
+    if (!card || codec != HFP_AUDIO_CODEC_CVSD || card->fd >= 0) {
+        pa_log_warn("New audio connection invalid arguments (path=%s fd=%d, codec=%d)", path, fd, codec);
+        pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call"));
+        shutdown(fd, SHUT_RDWR);
+        close(fd);
+        return r;
+    }
+
+    pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", path, fd, codec);
+
+    card->fd = fd;
+    card->transport->codec = codec;
+
+    pa_bluetooth_transport_set_state(card->transport, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING);
+
+    pa_assert_se(r = dbus_message_new_method_return(m));
+
+    return r;
+}
+
+static DBusHandlerResult hf_audio_agent_handler(DBusConnection *c, DBusMessage *m, void *data) {
+    pa_bluetooth_backend *backend = data;
+    DBusMessage *r = NULL;
+    const char *path, *interface, *member;
+
+    pa_assert(backend);
+
+    path = dbus_message_get_path(m);
+    interface = dbus_message_get_interface(m);
+    member = dbus_message_get_member(m);
+
+    if (!pa_streq(path, HF_AUDIO_AGENT_PATH))
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
+
+    if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+        const char *xml = HF_AUDIO_AGENT_XML;
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
+
+    } else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "NewConnection"))
+        r = hf_audio_agent_new_connection(c, m, data);
+    else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "Release"))
+        r = hf_audio_agent_release(c, m, data);
+    else
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (r) {
+        pa_assert_se(dbus_connection_send(pa_dbus_connection_get(backend->connection), r, NULL));
+        dbus_message_unref(r);
+    }
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+pa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y) {
+    pa_bluetooth_backend *backend;
+    DBusError err;
+    static const DBusObjectPathVTable vtable_hf_audio_agent = {
+        .message_function = hf_audio_agent_handler,
+    };
+
+    pa_assert(c);
+
+    backend = pa_xnew0(pa_bluetooth_backend, 1);
+    backend->core = c;
+    backend->discovery = y;
+    backend->cards = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
+                                         (pa_free_cb_t) hf_audio_card_free);
+
+    dbus_error_init(&err);
+
+    if (!(backend->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) {
+        pa_log("Failed to get D-Bus connection: %s", err.message);
+        dbus_error_free(&err);
+        pa_xfree(backend);
+        return NULL;
+    }
+
+    /* dynamic detection of handsfree audio cards */
+    if (!dbus_connection_add_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend, NULL)) {
+        pa_log_error("Failed to add filter function");
+        pa_dbus_connection_unref(backend->connection);
+        pa_xfree(backend);
+        return NULL;
+    }
+
+    if (pa_dbus_add_matches(pa_dbus_connection_get(backend->connection), &err,
+            "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
+            "arg0='" OFONO_SERVICE "'",
+            "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'",
+            "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'",
+            NULL) < 0) {
+        pa_log("Failed to add oFono D-Bus matches: %s", err.message);
+        dbus_connection_remove_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend);
+        pa_dbus_connection_unref(backend->connection);
+        pa_xfree(backend);
+        return NULL;
+    }
+
+    pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(backend->connection), HF_AUDIO_AGENT_PATH,
+                                                      &vtable_hf_audio_agent, backend));
+
+    hf_audio_agent_register(backend);
+
+    return backend;
+}
+
+void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *backend) {
+    pa_assert(backend);
+
+    pa_dbus_free_pending_list(&backend->pending);
+
+    hf_audio_agent_unregister(backend);
+
+    dbus_connection_unregister_object_path(pa_dbus_connection_get(backend->connection), HF_AUDIO_AGENT_PATH);
+
+    pa_dbus_remove_matches(pa_dbus_connection_get(backend->connection),
+            "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
+            "arg0='" OFONO_SERVICE "'",
+            "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'",
+            "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'",
+            NULL);
+
+    dbus_connection_remove_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend);
+
+    pa_dbus_connection_unref(backend->connection);
+
+    pa_hashmap_free(backend->cards);
+
+    pa_xfree(backend);
+}
diff --git a/src/modules/bluetooth/bluez4-util.c b/src/modules/bluetooth/bluez4-util.c
new file mode 100644 (file)
index 0000000..ca60619
--- /dev/null
@@ -0,0 +1,1851 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008-2013 João Paulo Rechi Vita
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/dbus-shared.h>
+
+#include "bluez4-util.h"
+#include "a2dp-codecs.h"
+
+#define ENDPOINT_PATH_HFP_AG "/MediaEndpoint/BlueZ4/HFPAG"
+#define ENDPOINT_PATH_HFP_HS "/MediaEndpoint/BlueZ4/HFPHS"
+#define ENDPOINT_PATH_A2DP_SOURCE "/MediaEndpoint/BlueZ4/A2DPSource"
+#define ENDPOINT_PATH_A2DP_SINK "/MediaEndpoint/BlueZ4/A2DPSink"
+
+#define ENDPOINT_INTROSPECT_XML                                         \
+    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
+    "<node>"                                                            \
+    " <interface name=\"org.bluez.MediaEndpoint\">"                     \
+    "  <method name=\"SetConfiguration\">"                              \
+    "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
+    "   <arg name=\"configuration\" direction=\"in\" type=\"ay\"/>"     \
+    "  </method>"                                                       \
+    "  <method name=\"SelectConfiguration\">"                           \
+    "   <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>"      \
+    "   <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>"    \
+    "  </method>"                                                       \
+    "  <method name=\"ClearConfiguration\">"                            \
+    "  </method>"                                                       \
+    "  <method name=\"Release\">"                                       \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
+    "  <method name=\"Introspect\">"                                    \
+    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    "</node>"
+
+struct pa_bluez4_discovery {
+    PA_REFCNT_DECLARE;
+
+    pa_core *core;
+    pa_dbus_connection *connection;
+    PA_LLIST_HEAD(pa_dbus_pending, pending);
+    bool adapters_listed;
+    pa_hashmap *devices;
+    pa_hashmap *transports;
+    pa_hook hooks[PA_BLUEZ4_HOOK_MAX];
+    bool filter_added;
+};
+
+static void get_properties_reply(DBusPendingCall *pending, void *userdata);
+static pa_dbus_pending* send_and_add_to_pending(pa_bluez4_discovery *y, DBusMessage *m, DBusPendingCallNotifyFunction func,
+                                                void *call_data);
+static void found_adapter(pa_bluez4_discovery *y, const char *path);
+static pa_bluez4_device *found_device(pa_bluez4_discovery *y, const char* path);
+static void transport_set_state(pa_bluez4_transport *transport, pa_bluez4_transport_state_t state);
+
+static pa_bluez4_audio_state_t audio_state_from_string(const char* value) {
+    pa_assert(value);
+
+    if (pa_streq(value, "disconnected"))
+        return PA_BLUEZ4_AUDIO_STATE_DISCONNECTED;
+    else if (pa_streq(value, "connecting"))
+        return PA_BLUEZ4_AUDIO_STATE_CONNECTING;
+    else if (pa_streq(value, "connected"))
+        return PA_BLUEZ4_AUDIO_STATE_CONNECTED;
+    else if (pa_streq(value, "playing"))
+        return PA_BLUEZ4_AUDIO_STATE_PLAYING;
+
+    return PA_BLUEZ4_AUDIO_STATE_INVALID;
+}
+
+const char *pa_bluez4_profile_to_string(pa_bluez4_profile_t profile) {
+    switch(profile) {
+        case PA_BLUEZ4_PROFILE_A2DP_SINK:
+            return "a2dp";
+        case PA_BLUEZ4_PROFILE_A2DP_SOURCE:
+            return "a2dp_source";
+        case PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT:
+            return "hsp";
+        case PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY:
+            return "hfgw";
+        case PA_BLUEZ4_PROFILE_OFF:
+            pa_assert_not_reached();
+    }
+
+    pa_assert_not_reached();
+}
+
+static int profile_from_interface(const char *interface, pa_bluez4_profile_t *p) {
+    pa_assert(interface);
+    pa_assert(p);
+
+    if (pa_streq(interface, "org.bluez.AudioSink")) {
+        *p = PA_BLUEZ4_PROFILE_A2DP_SINK;
+        return 0;
+    } else if (pa_streq(interface, "org.bluez.AudioSource")) {
+        *p = PA_BLUEZ4_PROFILE_A2DP_SOURCE;
+        return 0;
+    } else if (pa_streq(interface, "org.bluez.Headset")) {
+        *p = PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT;
+        return 0;
+    } else if (pa_streq(interface, "org.bluez.HandsfreeGateway")) {
+        *p = PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY;
+        return 0;
+    }
+
+    return -1;
+}
+
+static pa_bluez4_transport_state_t audio_state_to_transport_state(pa_bluez4_audio_state_t state) {
+    switch (state) {
+        case PA_BLUEZ4_AUDIO_STATE_INVALID: /* Typically if state hasn't been received yet */
+        case PA_BLUEZ4_AUDIO_STATE_DISCONNECTED:
+        case PA_BLUEZ4_AUDIO_STATE_CONNECTING:
+            return PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED;
+        case PA_BLUEZ4_AUDIO_STATE_CONNECTED:
+            return PA_BLUEZ4_TRANSPORT_STATE_IDLE;
+        case PA_BLUEZ4_AUDIO_STATE_PLAYING:
+            return PA_BLUEZ4_TRANSPORT_STATE_PLAYING;
+    }
+
+    pa_assert_not_reached();
+}
+
+static pa_bluez4_device* device_new(pa_bluez4_discovery *discovery, const char *path) {
+    pa_bluez4_device *d;
+    unsigned i;
+
+    pa_assert(discovery);
+    pa_assert(path);
+
+    d = pa_xnew0(pa_bluez4_device, 1);
+
+    d->discovery = discovery;
+    d->dead = false;
+
+    d->device_info_valid = 0;
+
+    d->name = NULL;
+    d->path = pa_xstrdup(path);
+    d->paired = -1;
+    d->alias = NULL;
+    d->uuids = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
+    d->address = NULL;
+    d->class = -1;
+    d->trusted = -1;
+
+    d->audio_state = PA_BLUEZ4_AUDIO_STATE_INVALID;
+
+    for (i = 0; i < PA_BLUEZ4_PROFILE_COUNT; i++)
+        d->profile_state[i] = PA_BLUEZ4_AUDIO_STATE_INVALID;
+
+    return d;
+}
+
+static void transport_free(pa_bluez4_transport *t) {
+    pa_assert(t);
+
+    pa_xfree(t->owner);
+    pa_xfree(t->path);
+    pa_xfree(t->config);
+    pa_xfree(t);
+}
+
+static void device_free(pa_bluez4_device *d) {
+    pa_bluez4_transport *t;
+    unsigned i;
+
+    pa_assert(d);
+
+    for (i = 0; i < PA_BLUEZ4_PROFILE_COUNT; i++) {
+        if (!(t = d->transports[i]))
+            continue;
+
+        d->transports[i] = NULL;
+        pa_hashmap_remove(d->discovery->transports, t->path);
+        transport_set_state(t, PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED);
+        transport_free(t);
+    }
+
+    if (d->uuids)
+        pa_hashmap_free(d->uuids);
+
+    pa_xfree(d->name);
+    pa_xfree(d->path);
+    pa_xfree(d->alias);
+    pa_xfree(d->address);
+    pa_xfree(d);
+}
+
+static const char *check_variant_property(DBusMessageIter *i) {
+    const char *key;
+
+    pa_assert(i);
+
+    if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+        pa_log("Property name not a string.");
+        return NULL;
+    }
+
+    dbus_message_iter_get_basic(i, &key);
+
+    if (!dbus_message_iter_next(i)) {
+        pa_log("Property value missing");
+        return NULL;
+    }
+
+    if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+        pa_log("Property value not a variant.");
+        return NULL;
+    }
+
+    return key;
+}
+
+static int parse_manager_property(pa_bluez4_discovery *y, DBusMessageIter *i, bool is_property_change) {
+    const char *key;
+    DBusMessageIter variant_i;
+
+    pa_assert(y);
+
+    key = check_variant_property(i);
+    if (key == NULL)
+        return -1;
+
+    dbus_message_iter_recurse(i, &variant_i);
+
+    switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+        case DBUS_TYPE_ARRAY: {
+
+            DBusMessageIter ai;
+            dbus_message_iter_recurse(&variant_i, &ai);
+
+            if (pa_streq(key, "Adapters")) {
+                y->adapters_listed = true;
+
+                if (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_OBJECT_PATH)
+                    break;
+
+                while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
+                    const char *value;
+
+                    dbus_message_iter_get_basic(&ai, &value);
+
+                    found_adapter(y, value);
+
+                    dbus_message_iter_next(&ai);
+                }
+            }
+
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static int parse_adapter_property(pa_bluez4_discovery *y, DBusMessageIter *i, bool is_property_change) {
+    const char *key;
+    DBusMessageIter variant_i;
+
+    pa_assert(y);
+
+    key = check_variant_property(i);
+    if (key == NULL)
+        return -1;
+
+    dbus_message_iter_recurse(i, &variant_i);
+
+    switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+        case DBUS_TYPE_ARRAY: {
+
+            DBusMessageIter ai;
+            dbus_message_iter_recurse(&variant_i, &ai);
+
+            if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_OBJECT_PATH &&
+                pa_streq(key, "Devices")) {
+
+                while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
+                    const char *value;
+
+                    dbus_message_iter_get_basic(&ai, &value);
+
+                    found_device(y, value);
+
+                    dbus_message_iter_next(&ai);
+                }
+            }
+
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static int parse_device_property(pa_bluez4_device *d, DBusMessageIter *i, bool is_property_change) {
+    const char *key;
+    DBusMessageIter variant_i;
+
+    pa_assert(d);
+
+    key = check_variant_property(i);
+    if (key == NULL)
+        return -1;
+
+    dbus_message_iter_recurse(i, &variant_i);
+
+/*     pa_log_debug("Parsing property org.bluez.Device.%s", key); */
+
+    switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+        case DBUS_TYPE_STRING: {
+
+            const char *value;
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "Name")) {
+                pa_xfree(d->name);
+                d->name = pa_xstrdup(value);
+            } else if (pa_streq(key, "Alias")) {
+                pa_xfree(d->alias);
+                d->alias = pa_xstrdup(value);
+            } else if (pa_streq(key, "Address")) {
+                if (is_property_change) {
+                    pa_log("Device property 'Address' expected to be constant but changed for %s", d->path);
+                    return -1;
+                }
+
+                if (d->address) {
+                    pa_log("Device %s: Received a duplicate Address property.", d->path);
+                    return -1;
+                }
+
+                d->address = pa_xstrdup(value);
+            }
+
+/*             pa_log_debug("Value %s", value); */
+
+            break;
+        }
+
+        case DBUS_TYPE_BOOLEAN: {
+
+            dbus_bool_t value;
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "Paired"))
+                d->paired = !!value;
+            else if (pa_streq(key, "Trusted"))
+                d->trusted = !!value;
+
+/*             pa_log_debug("Value %s", pa_yes_no(value)); */
+
+            break;
+        }
+
+        case DBUS_TYPE_UINT32: {
+
+            uint32_t value;
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "Class"))
+                d->class = (int) value;
+
+/*             pa_log_debug("Value %u", (unsigned) value); */
+
+            break;
+        }
+
+        case DBUS_TYPE_ARRAY: {
+            DBusMessageIter ai;
+            dbus_message_iter_recurse(&variant_i, &ai);
+
+            if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
+                DBusMessage *m;
+                bool has_audio = false;
+
+                /* bluetoothd never removes UUIDs from a device object so we
+                 * don't need to check for disappeared UUIDs here. */
+                while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
+                    const char *value;
+                    char *uuid;
+                    struct pa_bluez4_hook_uuid_data uuiddata;
+
+                    dbus_message_iter_get_basic(&ai, &value);
+
+                    if (pa_hashmap_get(d->uuids, value)) {
+                        dbus_message_iter_next(&ai);
+                        continue;
+                    }
+
+                    uuid = pa_xstrdup(value);
+                    pa_hashmap_put(d->uuids, uuid, uuid);
+
+                    pa_log_debug("%s: %s", key, value);
+
+                    uuiddata.device = d;
+                    uuiddata.uuid = value;
+                    pa_hook_fire(&d->discovery->hooks[PA_BLUEZ4_HOOK_DEVICE_UUID_ADDED], &uuiddata);
+
+                    /* Vudentz said the interfaces are here when the UUIDs are announced */
+                    if (pa_streq(PA_BLUEZ4_UUID_HSP_AG, value) || pa_streq(PA_BLUEZ4_UUID_HFP_AG, value)) {
+                        pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.HandsfreeGateway",
+                                                                      "GetProperties"));
+                        send_and_add_to_pending(d->discovery, m, get_properties_reply, d);
+                        has_audio = true;
+                    } else if (pa_streq(PA_BLUEZ4_UUID_HSP_HS, value) || pa_streq(PA_BLUEZ4_UUID_HFP_HF, value)) {
+                        pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Headset",
+                                                                      "GetProperties"));
+                        send_and_add_to_pending(d->discovery, m, get_properties_reply, d);
+                        has_audio = true;
+                    } else if (pa_streq(PA_BLUEZ4_UUID_A2DP_SINK, value)) {
+                        pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSink",
+                                                                      "GetProperties"));
+                        send_and_add_to_pending(d->discovery, m, get_properties_reply, d);
+                        has_audio = true;
+                    } else if (pa_streq(PA_BLUEZ4_UUID_A2DP_SOURCE, value)) {
+                        pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSource",
+                                                                      "GetProperties"));
+                        send_and_add_to_pending(d->discovery, m, get_properties_reply, d);
+                        has_audio = true;
+                    }
+
+                    dbus_message_iter_next(&ai);
+                }
+
+                /* this might eventually be racy if .Audio is not there yet, but
+                   the State change will come anyway later, so this call is for
+                   cold-detection mostly */
+                if (has_audio) {
+                    pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties"));
+                    send_and_add_to_pending(d->discovery, m, get_properties_reply, d);
+                }
+            }
+
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static const char *transport_state_to_string(pa_bluez4_transport_state_t state) {
+    switch (state) {
+        case PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED:
+            return "disconnected";
+        case PA_BLUEZ4_TRANSPORT_STATE_IDLE:
+            return "idle";
+        case PA_BLUEZ4_TRANSPORT_STATE_PLAYING:
+            return "playing";
+    }
+
+    pa_assert_not_reached();
+}
+
+static int parse_audio_property(pa_bluez4_device *d, const char *interface, DBusMessageIter *i, bool is_property_change) {
+    pa_bluez4_transport *transport;
+    const char *key;
+    DBusMessageIter variant_i;
+    bool is_audio_interface;
+    pa_bluez4_profile_t p = PA_BLUEZ4_PROFILE_OFF;
+
+    pa_assert(d);
+    pa_assert(interface);
+    pa_assert(i);
+
+    if (!(is_audio_interface = pa_streq(interface, "org.bluez.Audio")))
+        if (profile_from_interface(interface, &p) < 0)
+            return 0; /* Interface not known so silently ignore property */
+
+    key = check_variant_property(i);
+    if (key == NULL)
+        return -1;
+
+    transport = p == PA_BLUEZ4_PROFILE_OFF ? NULL : d->transports[p];
+
+    dbus_message_iter_recurse(i, &variant_i);
+
+/*     pa_log_debug("Parsing property org.bluez.{Audio|AudioSink|AudioSource|Headset}.%s", key); */
+
+    switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+        case DBUS_TYPE_STRING: {
+
+            const char *value;
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "State")) {
+                pa_bluez4_audio_state_t state = audio_state_from_string(value);
+
+                pa_log_debug("Device %s interface %s property 'State' changed to value '%s'", d->path, interface, value);
+
+                if (state == PA_BLUEZ4_AUDIO_STATE_INVALID)
+                    return -1;
+
+                if (is_audio_interface) {
+                    d->audio_state = state;
+                    break;
+                }
+
+                pa_assert(p != PA_BLUEZ4_PROFILE_OFF);
+
+                d->profile_state[p] = state;
+
+                if (!transport)
+                    break;
+
+                transport_set_state(transport, audio_state_to_transport_state(state));
+            }
+
+            break;
+        }
+
+        case DBUS_TYPE_UINT16: {
+            uint16_t value;
+
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "MicrophoneGain")) {
+                uint16_t gain;
+
+                pa_log_debug("dbus: property '%s' changed to value '%u'", key, value);
+
+                if (!transport) {
+                    pa_log("Volume change does not have an associated transport");
+                    return -1;
+                }
+
+                if ((gain = PA_MIN(value, HSP_MAX_GAIN)) == transport->microphone_gain)
+                    break;
+
+                transport->microphone_gain = gain;
+                pa_hook_fire(&d->discovery->hooks[PA_BLUEZ4_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED], transport);
+            } else if (pa_streq(key, "SpeakerGain")) {
+                uint16_t gain;
+
+                pa_log_debug("dbus: property '%s' changed to value '%u'", key, value);
+
+                if (!transport) {
+                    pa_log("Volume change does not have an associated transport");
+                    return -1;
+                }
+
+                if ((gain = PA_MIN(value, HSP_MAX_GAIN)) == transport->speaker_gain)
+                    break;
+
+                transport->speaker_gain = gain;
+                pa_hook_fire(&d->discovery->hooks[PA_BLUEZ4_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED], transport);
+            }
+
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static void run_callback(pa_bluez4_device *d, bool dead) {
+    pa_assert(d);
+
+    if (d->device_info_valid != 1)
+        return;
+
+    d->dead = dead;
+    pa_hook_fire(&d->discovery->hooks[PA_BLUEZ4_HOOK_DEVICE_CONNECTION_CHANGED], d);
+}
+
+static void remove_all_devices(pa_bluez4_discovery *y) {
+    pa_bluez4_device *d;
+
+    pa_assert(y);
+
+    while ((d = pa_hashmap_steal_first(y->devices))) {
+        run_callback(d, true);
+        device_free(d);
+    }
+}
+
+static pa_bluez4_device *found_device(pa_bluez4_discovery *y, const char* path) {
+    DBusMessage *m;
+    pa_bluez4_device *d;
+
+    pa_assert(y);
+    pa_assert(path);
+
+    d = pa_hashmap_get(y->devices, path);
+    if (d)
+        return d;
+
+    d = device_new(y, path);
+
+    pa_hashmap_put(y->devices, d->path, d);
+
+    pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties"));
+    send_and_add_to_pending(y, m, get_properties_reply, d);
+
+    /* Before we read the other properties (Audio, AudioSink, AudioSource,
+     * Headset) we wait that the UUID is read */
+    return d;
+}
+
+static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
+    DBusMessage *r;
+    DBusMessageIter arg_i, element_i;
+    pa_dbus_pending *p;
+    pa_bluez4_device *d;
+    pa_bluez4_discovery *y;
+    int valid;
+    bool old_any_connected;
+
+    pa_assert_se(p = userdata);
+    pa_assert_se(y = p->context_data);
+    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+/*     pa_log_debug("Got %s.GetProperties response for %s", */
+/*                  dbus_message_get_interface(p->message), */
+/*                  dbus_message_get_path(p->message)); */
+
+    /* We don't use p->call_data here right-away since the device
+     * might already be invalidated at this point */
+
+    if (dbus_message_has_interface(p->message, "org.bluez.Manager") ||
+        dbus_message_has_interface(p->message, "org.bluez.Adapter"))
+        d = NULL;
+    else if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(p->message)))) {
+        pa_log_warn("Received GetProperties() reply from unknown device: %s (device removed?)", dbus_message_get_path(p->message));
+        goto finish2;
+    }
+
+    pa_assert(p->call_data == d);
+
+    if (d != NULL) {
+        old_any_connected = pa_bluez4_device_any_audio_connected(d);
+        valid = dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR ? -1 : 1;
+
+        if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
+            d->device_info_valid = valid;
+    }
+
+    if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
+        pa_log_debug("Bluetooth daemon is apparently not available.");
+        remove_all_devices(y);
+        goto finish2;
+    }
+
+    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+        pa_log("%s.GetProperties() failed: %s: %s", dbus_message_get_interface(p->message), dbus_message_get_error_name(r),
+               pa_dbus_get_error_message(r));
+        goto finish;
+    }
+
+    if (!dbus_message_iter_init(r, &arg_i)) {
+        pa_log("GetProperties reply has no arguments.");
+        goto finish;
+    }
+
+    if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+        pa_log("GetProperties argument is not an array.");
+        goto finish;
+    }
+
+    dbus_message_iter_recurse(&arg_i, &element_i);
+    while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+
+        if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+            DBusMessageIter dict_i;
+
+            dbus_message_iter_recurse(&element_i, &dict_i);
+
+            if (dbus_message_has_interface(p->message, "org.bluez.Manager")) {
+                if (parse_manager_property(y, &dict_i, false) < 0)
+                    goto finish;
+
+            } else if (dbus_message_has_interface(p->message, "org.bluez.Adapter")) {
+                if (parse_adapter_property(y, &dict_i, false) < 0)
+                    goto finish;
+
+            } else if (dbus_message_has_interface(p->message, "org.bluez.Device")) {
+                if (parse_device_property(d, &dict_i, false) < 0)
+                    goto finish;
+
+            } else if (parse_audio_property(d, dbus_message_get_interface(p->message), &dict_i, false) < 0)
+                goto finish;
+
+        }
+
+        dbus_message_iter_next(&element_i);
+    }
+
+finish:
+    if (d != NULL && old_any_connected != pa_bluez4_device_any_audio_connected(d))
+        run_callback(d, false);
+
+finish2:
+    dbus_message_unref(r);
+
+    PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+    pa_dbus_pending_free(p);
+}
+
+static pa_dbus_pending* send_and_add_to_pending(pa_bluez4_discovery *y, DBusMessage *m, DBusPendingCallNotifyFunction func,
+                                                void *call_data) {
+    pa_dbus_pending *p;
+    DBusPendingCall *call;
+
+    pa_assert(y);
+    pa_assert(m);
+
+    pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(y->connection), m, &call, -1));
+
+    p = pa_dbus_pending_new(pa_dbus_connection_get(y->connection), m, call, y, call_data);
+    PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
+    dbus_pending_call_set_notify(call, func, p, NULL);
+
+    return p;
+}
+
+static void register_endpoint_reply(DBusPendingCall *pending, void *userdata) {
+    DBusMessage *r;
+    pa_dbus_pending *p;
+    pa_bluez4_discovery *y;
+    char *endpoint;
+
+    pa_assert(pending);
+    pa_assert_se(p = userdata);
+    pa_assert_se(y = p->context_data);
+    pa_assert_se(endpoint = p->call_data);
+    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+    if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
+        pa_log_debug("Bluetooth daemon is apparently not available.");
+        remove_all_devices(y);
+        goto finish;
+    }
+
+    if (dbus_message_is_error(r, PA_BLUEZ4_ERROR_NOT_SUPPORTED)) {
+        pa_log_info("Couldn't register endpoint %s, because BlueZ is configured to disable the endpoint type.", endpoint);
+        goto finish;
+    }
+
+    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+        pa_log("org.bluez.Media.RegisterEndpoint() failed: %s: %s", dbus_message_get_error_name(r),
+               pa_dbus_get_error_message(r));
+        goto finish;
+    }
+
+finish:
+    dbus_message_unref(r);
+
+    PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+    pa_dbus_pending_free(p);
+
+    pa_xfree(endpoint);
+}
+
+static void register_endpoint(pa_bluez4_discovery *y, const char *path, const char *endpoint, const char *uuid) {
+    DBusMessage *m;
+    DBusMessageIter i, d;
+    uint8_t codec = 0;
+
+    pa_log_debug("Registering %s on adapter %s.", endpoint, path);
+
+    pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Media", "RegisterEndpoint"));
+
+    dbus_message_iter_init_append(m, &i);
+
+    pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint));
+
+    dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                    DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+                                    &d);
+
+    pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
+
+    pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec);
+
+    if (pa_streq(uuid, PA_BLUEZ4_UUID_HFP_AG) || pa_streq(uuid, PA_BLUEZ4_UUID_HFP_HF)) {
+        uint8_t capability = 0;
+        pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capability, 1);
+    } else {
+        a2dp_sbc_t capabilities;
+
+        capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL |
+                                    SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO;
+        capabilities.frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 |
+                                 SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000;
+        capabilities.allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS;
+        capabilities.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8;
+        capabilities.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
+                                    SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16;
+        capabilities.min_bitpool = MIN_BITPOOL;
+        capabilities.max_bitpool = MAX_BITPOOL;
+
+        pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
+    }
+
+    dbus_message_iter_close_container(&i, &d);
+
+    send_and_add_to_pending(y, m, register_endpoint_reply, pa_xstrdup(endpoint));
+}
+
+static void found_adapter(pa_bluez4_discovery *y, const char *path) {
+    DBusMessage *m;
+
+    pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "GetProperties"));
+    send_and_add_to_pending(y, m, get_properties_reply, NULL);
+
+    register_endpoint(y, path, ENDPOINT_PATH_HFP_AG, PA_BLUEZ4_UUID_HFP_AG);
+    register_endpoint(y, path, ENDPOINT_PATH_HFP_HS, PA_BLUEZ4_UUID_HFP_HF);
+    register_endpoint(y, path, ENDPOINT_PATH_A2DP_SOURCE, PA_BLUEZ4_UUID_A2DP_SOURCE);
+    register_endpoint(y, path, ENDPOINT_PATH_A2DP_SINK, PA_BLUEZ4_UUID_A2DP_SINK);
+}
+
+static void list_adapters(pa_bluez4_discovery *y) {
+    DBusMessage *m;
+    pa_assert(y);
+
+    pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "GetProperties"));
+    send_and_add_to_pending(y, m, get_properties_reply, NULL);
+}
+
+static int transport_parse_property(pa_bluez4_transport *t, DBusMessageIter *i) {
+    const char *key;
+    DBusMessageIter variant_i;
+
+    key = check_variant_property(i);
+    if (key == NULL)
+        return -1;
+
+    dbus_message_iter_recurse(i, &variant_i);
+
+    switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+        case DBUS_TYPE_BOOLEAN: {
+
+            dbus_bool_t value;
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "NREC") && t->nrec != value) {
+                t->nrec = value;
+                pa_log_debug("Transport %s: Property 'NREC' changed to %s.", t->path, t->nrec ? "True" : "False");
+                pa_hook_fire(&t->device->discovery->hooks[PA_BLUEZ4_HOOK_TRANSPORT_NREC_CHANGED], t);
+            }
+
+            break;
+         }
+    }
+
+    return 0;
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
+    DBusError err;
+    pa_bluez4_discovery *y;
+
+    pa_assert(bus);
+    pa_assert(m);
+
+    pa_assert_se(y = userdata);
+
+    dbus_error_init(&err);
+
+    pa_log_debug("dbus: interface=%s, path=%s, member=%s",
+            dbus_message_get_interface(m),
+            dbus_message_get_path(m),
+            dbus_message_get_member(m));
+
+    if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceRemoved")) {
+        const char *path;
+        pa_bluez4_device *d;
+
+        if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+            pa_log("Failed to parse org.bluez.Adapter.DeviceRemoved: %s", err.message);
+            goto fail;
+        }
+
+        pa_log_debug("Device %s removed", path);
+
+        if ((d = pa_hashmap_remove(y->devices, path))) {
+            run_callback(d, true);
+            device_free(d);
+        }
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceCreated")) {
+        const char *path;
+
+        if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+            pa_log("Failed to parse org.bluez.Adapter.DeviceCreated: %s", err.message);
+            goto fail;
+        }
+
+        pa_log_debug("Device %s created", path);
+
+        found_device(y, path);
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    } else if (dbus_message_is_signal(m, "org.bluez.Manager", "AdapterAdded")) {
+        const char *path;
+
+        if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+            pa_log("Failed to parse org.bluez.Manager.AdapterAdded: %s", err.message);
+            goto fail;
+        }
+
+        if (!y->adapters_listed) {
+            pa_log_debug("Ignoring 'AdapterAdded' because initial adapter list has not been received yet.");
+            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        }
+
+        pa_log_debug("Adapter %s created", path);
+
+        found_adapter(y, path);
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    } else if (dbus_message_is_signal(m, "org.bluez.Audio", "PropertyChanged") ||
+               dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") ||
+               dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged") ||
+               dbus_message_is_signal(m, "org.bluez.AudioSource", "PropertyChanged") ||
+               dbus_message_is_signal(m, "org.bluez.HandsfreeGateway", "PropertyChanged") ||
+               dbus_message_is_signal(m, "org.bluez.Device", "PropertyChanged")) {
+
+        pa_bluez4_device *d;
+
+        if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
+            DBusMessageIter arg_i;
+            bool old_any_connected = pa_bluez4_device_any_audio_connected(d);
+
+            if (!dbus_message_iter_init(m, &arg_i)) {
+                pa_log("Failed to parse PropertyChanged for device %s", d->path);
+                goto fail;
+            }
+
+            if (dbus_message_has_interface(m, "org.bluez.Device")) {
+                if (parse_device_property(d, &arg_i, true) < 0)
+                    goto fail;
+
+            } else if (parse_audio_property(d, dbus_message_get_interface(m), &arg_i, true) < 0)
+                goto fail;
+
+            if (old_any_connected != pa_bluez4_device_any_audio_connected(d))
+                run_callback(d, false);
+        }
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    } else if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
+        const char *name, *old_owner, *new_owner;
+
+        if (!dbus_message_get_args(m, &err,
+                                   DBUS_TYPE_STRING, &name,
+                                   DBUS_TYPE_STRING, &old_owner,
+                                   DBUS_TYPE_STRING, &new_owner,
+                                   DBUS_TYPE_INVALID)) {
+            pa_log("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
+            goto fail;
+        }
+
+        if (pa_streq(name, "org.bluez")) {
+            if (old_owner && *old_owner) {
+                pa_log_debug("Bluetooth daemon disappeared.");
+                remove_all_devices(y);
+                y->adapters_listed = false;
+            }
+
+            if (new_owner && *new_owner) {
+                pa_log_debug("Bluetooth daemon appeared.");
+                list_adapters(y);
+            }
+        }
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    } else if (dbus_message_is_signal(m, "org.bluez.MediaTransport", "PropertyChanged")) {
+        pa_bluez4_transport *t;
+        DBusMessageIter arg_i;
+
+        if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
+            goto fail;
+
+        if (!dbus_message_iter_init(m, &arg_i)) {
+            pa_log("Failed to parse PropertyChanged for transport %s", t->path);
+            goto fail;
+        }
+
+        if (transport_parse_property(t, &arg_i) < 0)
+            goto fail;
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+
+fail:
+    dbus_error_free(&err);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+pa_bluez4_device* pa_bluez4_discovery_get_by_address(pa_bluez4_discovery *y, const char* address) {
+    pa_bluez4_device *d;
+    void *state = NULL;
+
+    pa_assert(y);
+    pa_assert(PA_REFCNT_VALUE(y) > 0);
+    pa_assert(address);
+
+    while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
+        if (pa_streq(d->address, address))
+            return d->device_info_valid == 1 ? d : NULL;
+
+    return NULL;
+}
+
+pa_bluez4_device* pa_bluez4_discovery_get_by_path(pa_bluez4_discovery *y, const char* path) {
+    pa_bluez4_device *d;
+
+    pa_assert(y);
+    pa_assert(PA_REFCNT_VALUE(y) > 0);
+    pa_assert(path);
+
+    if ((d = pa_hashmap_get(y->devices, path)))
+        if (d->device_info_valid == 1)
+            return d;
+
+    return NULL;
+}
+
+bool pa_bluez4_device_any_audio_connected(const pa_bluez4_device *d) {
+    unsigned i;
+
+    pa_assert(d);
+
+    if (d->dead || d->device_info_valid != 1)
+        return false;
+
+    if (d->audio_state == PA_BLUEZ4_AUDIO_STATE_INVALID)
+        return false;
+
+    /* Make sure audio_state is *not* in CONNECTING state before we fire the
+     * hook to report the new device state. This is actually very important in
+     * order to make module-card-restore work well with headsets: if the headset
+     * supports both HSP and A2DP, one of those profiles is connected first and
+     * then the other, and lastly the Audio interface becomes connected.
+     * Checking only audio_state means that this function will return false at
+     * the time when only the first connection has been made. This is good,
+     * because otherwise, if the first connection is for HSP and we would
+     * already load a new device module instance, and module-card-restore tries
+     * to restore the A2DP profile, that would fail because A2DP is not yet
+     * connected. Waiting until the Audio interface gets connected means that
+     * both headset profiles will be connected when the device module is
+     * loaded. */
+    if (d->audio_state == PA_BLUEZ4_AUDIO_STATE_CONNECTING)
+        return false;
+
+    for (i = 0; i < PA_BLUEZ4_PROFILE_COUNT; i++)
+        if (d->transports[i] && d->transports[i]->state != PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED)
+            return true;
+
+    return false;
+}
+
+int pa_bluez4_transport_acquire(pa_bluez4_transport *t, bool optional, size_t *imtu, size_t *omtu) {
+    const char *accesstype = "rw";
+    DBusMessage *m, *r;
+    DBusError err;
+    int ret;
+    uint16_t i, o;
+
+    pa_assert(t);
+    pa_assert(t->device);
+    pa_assert(t->device->discovery);
+
+    if (optional) {
+        /* FIXME: we are trying to acquire the transport only if the stream is
+           playing, without actually initiating the stream request from our side
+           (which is typically undesireable specially for hfgw use-cases.
+           However this approach is racy, since the stream could have been
+           suspended in the meantime, so we can't really guarantee that the
+           stream will not be requested until BlueZ's API supports this
+           atomically. */
+        if (t->state < PA_BLUEZ4_TRANSPORT_STATE_PLAYING) {
+            pa_log_info("Failed optional acquire of transport %s", t->path);
+            return -1;
+        }
+    }
+
+    dbus_error_init(&err);
+
+    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", "Acquire"));
+    pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
+    r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
+    dbus_message_unref(m);
+    m = NULL;
+
+    if (!r) {
+        dbus_error_free(&err);
+        return -1;
+    }
+
+    if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_UINT16, &i, DBUS_TYPE_UINT16, &o,
+                               DBUS_TYPE_INVALID)) {
+        pa_log("Failed to parse org.bluez.MediaTransport.Acquire(): %s", err.message);
+        ret = -1;
+        dbus_error_free(&err);
+        goto fail;
+    }
+
+    if (imtu)
+        *imtu = i;
+
+    if (omtu)
+        *omtu = o;
+
+fail:
+    dbus_message_unref(r);
+    return ret;
+}
+
+void pa_bluez4_transport_release(pa_bluez4_transport *t) {
+    const char *accesstype = "rw";
+    DBusMessage *m, *r;
+    DBusError err;
+
+    pa_assert(t);
+    pa_assert(t->device);
+    pa_assert(t->device->discovery);
+
+    dbus_error_init(&err);
+
+    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", "Release"));
+    pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
+    r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
+    dbus_message_unref(m);
+    m = NULL;
+    if (r) {
+        dbus_message_unref(r);
+        r = NULL;
+    }
+
+    if (dbus_error_is_set(&err)) {
+        pa_log("Failed to release transport %s: %s", t->path, err.message);
+        dbus_error_free(&err);
+    } else
+        pa_log_info("Transport %s released", t->path);
+}
+
+static void set_property(pa_bluez4_discovery *y, const char *bus, const char *path, const char *interface,
+                         const char *prop_name, int prop_type, void *prop_value) {
+    DBusMessage *m;
+    DBusMessageIter i;
+
+    pa_assert(y);
+    pa_assert(path);
+    pa_assert(interface);
+    pa_assert(prop_name);
+
+    pa_assert_se(m = dbus_message_new_method_call(bus, path, interface, "SetProperty"));
+    dbus_message_iter_init_append(m, &i);
+    pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &prop_name));
+    pa_dbus_append_basic_variant(&i, prop_type, prop_value);
+
+    dbus_message_set_no_reply(m, true);
+    pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), m, NULL));
+    dbus_message_unref(m);
+}
+
+void pa_bluez4_transport_set_microphone_gain(pa_bluez4_transport *t, uint16_t value) {
+    dbus_uint16_t gain = PA_MIN(value, HSP_MAX_GAIN);
+
+    pa_assert(t);
+    pa_assert(t->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT);
+
+    set_property(t->device->discovery, "org.bluez", t->device->path, "org.bluez.Headset",
+                 "MicrophoneGain", DBUS_TYPE_UINT16, &gain);
+}
+
+void pa_bluez4_transport_set_speaker_gain(pa_bluez4_transport *t, uint16_t value) {
+    dbus_uint16_t gain = PA_MIN(value, HSP_MAX_GAIN);
+
+    pa_assert(t);
+    pa_assert(t->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT);
+
+    set_property(t->device->discovery, "org.bluez", t->device->path, "org.bluez.Headset",
+                 "SpeakerGain", DBUS_TYPE_UINT16, &gain);
+}
+
+static int setup_dbus(pa_bluez4_discovery *y) {
+    DBusError err;
+
+    dbus_error_init(&err);
+
+    if (!(y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err))) {
+        pa_log("Failed to get D-Bus connection: %s", err.message);
+        dbus_error_free(&err);
+        return -1;
+    }
+
+    return 0;
+}
+
+static pa_bluez4_transport *transport_new(pa_bluez4_device *d, const char *owner, const char *path, pa_bluez4_profile_t p,
+                                             const uint8_t *config, int size) {
+    pa_bluez4_transport *t;
+
+    t = pa_xnew0(pa_bluez4_transport, 1);
+    t->device = d;
+    t->owner = pa_xstrdup(owner);
+    t->path = pa_xstrdup(path);
+    t->profile = p;
+    t->config_size = size;
+
+    if (size > 0) {
+        t->config = pa_xnew(uint8_t, size);
+        memcpy(t->config, config, size);
+    }
+
+    t->state = audio_state_to_transport_state(d->profile_state[p]);
+
+    return t;
+}
+
+static void transport_set_state(pa_bluez4_transport *transport, pa_bluez4_transport_state_t state) {
+    if (transport->state == state)
+        return;
+
+    pa_log_debug("Transport %s state: %s -> %s",
+                 transport->path, transport_state_to_string(transport->state), transport_state_to_string(state));
+
+    transport->state = state;
+
+    pa_hook_fire(&transport->device->discovery->hooks[PA_BLUEZ4_HOOK_TRANSPORT_STATE_CHANGED], transport);
+}
+
+static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
+    pa_bluez4_discovery *y = userdata;
+    pa_bluez4_device *d;
+    pa_bluez4_transport *t;
+    const char *sender, *path, *dev_path = NULL, *uuid = NULL;
+    uint8_t *config = NULL;
+    int size = 0;
+    bool nrec = false;
+    pa_bluez4_profile_t p;
+    DBusMessageIter args, props;
+    DBusMessage *r;
+    bool old_any_connected;
+
+    if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
+        pa_log("Invalid signature for method SetConfiguration");
+        goto fail2;
+    }
+
+    dbus_message_iter_get_basic(&args, &path);
+
+    if (pa_hashmap_get(y->transports, path)) {
+        pa_log("org.bluez.MediaEndpoint.SetConfiguration: Transport %s is already configured.", path);
+        goto fail2;
+    }
+
+    pa_assert_se(dbus_message_iter_next(&args));
+
+    dbus_message_iter_recurse(&args, &props);
+    if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+        goto fail;
+
+    /* Read transport properties */
+    while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
+        const char *key;
+        DBusMessageIter value, entry;
+        int var;
+
+        dbus_message_iter_recurse(&props, &entry);
+        dbus_message_iter_get_basic(&entry, &key);
+
+        dbus_message_iter_next(&entry);
+        dbus_message_iter_recurse(&entry, &value);
+
+        var = dbus_message_iter_get_arg_type(&value);
+
+        if (strcasecmp(key, "UUID") == 0) {
+            if (var != DBUS_TYPE_STRING)
+                goto fail;
+
+            dbus_message_iter_get_basic(&value, &uuid);
+        } else if (strcasecmp(key, "Device") == 0) {
+            if (var != DBUS_TYPE_OBJECT_PATH)
+                goto fail;
+
+            dbus_message_iter_get_basic(&value, &dev_path);
+        } else if (strcasecmp(key, "NREC") == 0) {
+            dbus_bool_t tmp_boolean;
+            if (var != DBUS_TYPE_BOOLEAN)
+                goto fail;
+
+            dbus_message_iter_get_basic(&value, &tmp_boolean);
+            nrec = tmp_boolean;
+        } else if (strcasecmp(key, "Configuration") == 0) {
+            DBusMessageIter array;
+            if (var != DBUS_TYPE_ARRAY)
+                goto fail;
+
+            dbus_message_iter_recurse(&value, &array);
+            dbus_message_iter_get_fixed_array(&array, &config, &size);
+        }
+
+        dbus_message_iter_next(&props);
+    }
+
+    d = found_device(y, dev_path);
+    if (!d)
+        goto fail;
+
+    if (dbus_message_has_path(m, ENDPOINT_PATH_HFP_AG))
+        p = PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT;
+    else if (dbus_message_has_path(m, ENDPOINT_PATH_HFP_HS))
+        p = PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY;
+    else if (dbus_message_has_path(m, ENDPOINT_PATH_A2DP_SOURCE))
+        p = PA_BLUEZ4_PROFILE_A2DP_SINK;
+    else
+        p = PA_BLUEZ4_PROFILE_A2DP_SOURCE;
+
+    if (d->transports[p] != NULL) {
+        pa_log("Cannot configure transport %s because profile %d is already used", path, p);
+        goto fail2;
+    }
+
+    old_any_connected = pa_bluez4_device_any_audio_connected(d);
+
+    sender = dbus_message_get_sender(m);
+
+    t = transport_new(d, sender, path, p, config, size);
+    if (nrec)
+        t->nrec = nrec;
+
+    d->transports[p] = t;
+    pa_assert_se(pa_hashmap_put(y->transports, t->path, t) >= 0);
+
+    pa_log_debug("Transport %s profile %d available", t->path, t->profile);
+
+    pa_assert_se(r = dbus_message_new_method_return(m));
+    pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
+    dbus_message_unref(r);
+
+    if (old_any_connected != pa_bluez4_device_any_audio_connected(d))
+        run_callback(d, false);
+
+    return NULL;
+
+fail:
+    pa_log("org.bluez.MediaEndpoint.SetConfiguration: invalid arguments");
+
+fail2:
+    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments",
+                                            "Unable to set configuration"));
+    return r;
+}
+
+static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage *m, void *userdata) {
+    pa_bluez4_discovery *y = userdata;
+    pa_bluez4_transport *t;
+    DBusMessage *r;
+    DBusError e;
+    const char *path;
+
+    dbus_error_init(&e);
+
+    if (!dbus_message_get_args(m, &e, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+        pa_log("org.bluez.MediaEndpoint.ClearConfiguration: %s", e.message);
+        dbus_error_free(&e);
+        goto fail;
+    }
+
+    if ((t = pa_hashmap_get(y->transports, path))) {
+        bool old_any_connected = pa_bluez4_device_any_audio_connected(t->device);
+
+        pa_log_debug("Clearing transport %s profile %d", t->path, t->profile);
+        t->device->transports[t->profile] = NULL;
+        pa_hashmap_remove(y->transports, t->path);
+        transport_set_state(t, PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED);
+
+        if (old_any_connected != pa_bluez4_device_any_audio_connected(t->device))
+            run_callback(t->device, false);
+
+        transport_free(t);
+    }
+
+    pa_assert_se(r = dbus_message_new_method_return(m));
+
+    return r;
+
+fail:
+    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments",
+                                            "Unable to clear configuration"));
+    return r;
+}
+
+static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
+
+    switch (freq) {
+        case SBC_SAMPLING_FREQ_16000:
+        case SBC_SAMPLING_FREQ_32000:
+            return 53;
+
+        case SBC_SAMPLING_FREQ_44100:
+
+            switch (mode) {
+                case SBC_CHANNEL_MODE_MONO:
+                case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+                    return 31;
+
+                case SBC_CHANNEL_MODE_STEREO:
+                case SBC_CHANNEL_MODE_JOINT_STEREO:
+                    return 53;
+
+                default:
+                    pa_log_warn("Invalid channel mode %u", mode);
+                    return 53;
+            }
+
+        case SBC_SAMPLING_FREQ_48000:
+
+            switch (mode) {
+                case SBC_CHANNEL_MODE_MONO:
+                case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+                    return 29;
+
+                case SBC_CHANNEL_MODE_STEREO:
+                case SBC_CHANNEL_MODE_JOINT_STEREO:
+                    return 51;
+
+                default:
+                    pa_log_warn("Invalid channel mode %u", mode);
+                    return 51;
+            }
+
+        default:
+            pa_log_warn("Invalid sampling freq %u", freq);
+            return 53;
+    }
+}
+
+static DBusMessage *endpoint_select_configuration(DBusConnection *c, DBusMessage *m, void *userdata) {
+    pa_bluez4_discovery *y = userdata;
+    a2dp_sbc_t *cap, config;
+    uint8_t *pconf = (uint8_t *) &config;
+    int i, size;
+    DBusMessage *r;
+    DBusError e;
+
+    static const struct {
+        uint32_t rate;
+        uint8_t cap;
+    } freq_table[] = {
+        { 16000U, SBC_SAMPLING_FREQ_16000 },
+        { 32000U, SBC_SAMPLING_FREQ_32000 },
+        { 44100U, SBC_SAMPLING_FREQ_44100 },
+        { 48000U, SBC_SAMPLING_FREQ_48000 }
+    };
+
+    dbus_error_init(&e);
+
+    if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
+        pa_log("org.bluez.MediaEndpoint.SelectConfiguration: %s", e.message);
+        dbus_error_free(&e);
+        goto fail;
+    }
+
+    if (dbus_message_has_path(m, ENDPOINT_PATH_HFP_AG) || dbus_message_has_path(m, ENDPOINT_PATH_HFP_HS))
+        goto done;
+
+    pa_assert(size == sizeof(config));
+
+    memset(&config, 0, sizeof(config));
+
+    /* Find the lowest freq that is at least as high as the requested
+     * sampling rate */
+    for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)
+        if (freq_table[i].rate >= y->core->default_sample_spec.rate && (cap->frequency & freq_table[i].cap)) {
+            config.frequency = freq_table[i].cap;
+            break;
+        }
+
+    if ((unsigned) i == PA_ELEMENTSOF(freq_table)) {
+        for (--i; i >= 0; i--) {
+            if (cap->frequency & freq_table[i].cap) {
+                config.frequency = freq_table[i].cap;
+                break;
+            }
+        }
+
+        if (i < 0) {
+            pa_log("Not suitable sample rate");
+            goto fail;
+        }
+    }
+
+    pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table));
+
+    if (y->core->default_sample_spec.channels <= 1) {
+        if (cap->channel_mode & SBC_CHANNEL_MODE_MONO)
+            config.channel_mode = SBC_CHANNEL_MODE_MONO;
+    }
+
+    if (y->core->default_sample_spec.channels >= 2) {
+        if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+            config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
+        else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO)
+            config.channel_mode = SBC_CHANNEL_MODE_STEREO;
+        else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+            config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
+        else if (cap->channel_mode & SBC_CHANNEL_MODE_MONO) {
+            config.channel_mode = SBC_CHANNEL_MODE_MONO;
+        } else {
+            pa_log("No supported channel modes");
+            goto fail;
+        }
+    }
+
+    if (cap->block_length & SBC_BLOCK_LENGTH_16)
+        config.block_length = SBC_BLOCK_LENGTH_16;
+    else if (cap->block_length & SBC_BLOCK_LENGTH_12)
+        config.block_length = SBC_BLOCK_LENGTH_12;
+    else if (cap->block_length & SBC_BLOCK_LENGTH_8)
+        config.block_length = SBC_BLOCK_LENGTH_8;
+    else if (cap->block_length & SBC_BLOCK_LENGTH_4)
+        config.block_length = SBC_BLOCK_LENGTH_4;
+    else {
+        pa_log_error("No supported block lengths");
+        goto fail;
+    }
+
+    if (cap->subbands & SBC_SUBBANDS_8)
+        config.subbands = SBC_SUBBANDS_8;
+    else if (cap->subbands & SBC_SUBBANDS_4)
+        config.subbands = SBC_SUBBANDS_4;
+    else {
+        pa_log_error("No supported subbands");
+        goto fail;
+    }
+
+    if (cap->allocation_method & SBC_ALLOCATION_LOUDNESS)
+        config.allocation_method = SBC_ALLOCATION_LOUDNESS;
+    else if (cap->allocation_method & SBC_ALLOCATION_SNR)
+        config.allocation_method = SBC_ALLOCATION_SNR;
+
+    config.min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool);
+    config.max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(config.frequency, config.channel_mode), cap->max_bitpool);
+
+done:
+    pa_assert_se(r = dbus_message_new_method_return(m));
+
+    pa_assert_se(dbus_message_append_args(
+                                     r,
+                                     DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size,
+                                     DBUS_TYPE_INVALID));
+
+    return r;
+
+fail:
+    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments",
+                                            "Unable to select configuration"));
+    return r;
+}
+
+static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
+    struct pa_bluez4_discovery *y = userdata;
+    DBusMessage *r = NULL;
+    DBusError e;
+    const char *path, *interface, *member;
+
+    pa_assert(y);
+
+    path = dbus_message_get_path(m);
+    interface = dbus_message_get_interface(m);
+    member = dbus_message_get_member(m);
+
+    pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
+
+    dbus_error_init(&e);
+
+    if (!pa_streq(path, ENDPOINT_PATH_A2DP_SOURCE) && !pa_streq(path, ENDPOINT_PATH_A2DP_SINK)
+            && !pa_streq(path, ENDPOINT_PATH_HFP_AG) && !pa_streq(path, ENDPOINT_PATH_HFP_HS))
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+        const char *xml = ENDPOINT_INTROSPECT_XML;
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
+
+    } else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SetConfiguration"))
+        r = endpoint_set_configuration(c, m, userdata);
+    else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SelectConfiguration"))
+        r = endpoint_select_configuration(c, m, userdata);
+    else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "ClearConfiguration"))
+        r = endpoint_clear_configuration(c, m, userdata);
+    else
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (r) {
+        pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
+        dbus_message_unref(r);
+    }
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+pa_bluez4_discovery* pa_bluez4_discovery_get(pa_core *c) {
+    DBusError err;
+    pa_bluez4_discovery *y;
+    DBusConnection *conn;
+    unsigned i;
+    static const DBusObjectPathVTable vtable_endpoint = {
+        .message_function = endpoint_handler,
+    };
+
+    pa_assert(c);
+
+    dbus_error_init(&err);
+
+    if ((y = pa_shared_get(c, "bluez4-discovery")))
+        return pa_bluez4_discovery_ref(y);
+
+    y = pa_xnew0(pa_bluez4_discovery, 1);
+    PA_REFCNT_INIT(y);
+    y->core = c;
+    y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
+
+    for (i = 0; i < PA_BLUEZ4_HOOK_MAX; i++)
+        pa_hook_init(&y->hooks[i], y);
+
+    pa_shared_set(c, "bluez4-discovery", y);
+
+    if (setup_dbus(y) < 0)
+        goto fail;
+
+    conn = pa_dbus_connection_get(y->connection);
+
+    /* dynamic detection of bluetooth audio devices */
+    if (!dbus_connection_add_filter(conn, filter_cb, y, NULL)) {
+        pa_log_error("Failed to add filter function");
+        goto fail;
+    }
+
+    y->filter_added = true;
+
+    if (pa_dbus_add_matches(
+                conn, &err,
+                "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
+                ",arg0='org.bluez'",
+                "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
+                "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
+                "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
+                "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
+                NULL) < 0) {
+        pa_log("Failed to add D-Bus matches: %s", err.message);
+        goto fail;
+    }
+
+    pa_assert_se(dbus_connection_register_object_path(conn, ENDPOINT_PATH_HFP_AG, &vtable_endpoint, y));
+    pa_assert_se(dbus_connection_register_object_path(conn, ENDPOINT_PATH_HFP_HS, &vtable_endpoint, y));
+    pa_assert_se(dbus_connection_register_object_path(conn, ENDPOINT_PATH_A2DP_SOURCE, &vtable_endpoint, y));
+    pa_assert_se(dbus_connection_register_object_path(conn, ENDPOINT_PATH_A2DP_SINK, &vtable_endpoint, y));
+
+    list_adapters(y);
+
+    return y;
+
+fail:
+    if (y)
+        pa_bluez4_discovery_unref(y);
+
+    dbus_error_free(&err);
+
+    return NULL;
+}
+
+pa_bluez4_discovery* pa_bluez4_discovery_ref(pa_bluez4_discovery *y) {
+    pa_assert(y);
+    pa_assert(PA_REFCNT_VALUE(y) > 0);
+
+    PA_REFCNT_INC(y);
+
+    return y;
+}
+
+void pa_bluez4_discovery_unref(pa_bluez4_discovery *y) {
+    unsigned i;
+
+    pa_assert(y);
+    pa_assert(PA_REFCNT_VALUE(y) > 0);
+
+    if (PA_REFCNT_DEC(y) > 0)
+        return;
+
+    pa_dbus_free_pending_list(&y->pending);
+
+    if (y->devices) {
+        remove_all_devices(y);
+        pa_hashmap_free(y->devices);
+    }
+
+    if (y->transports) {
+        pa_assert(pa_hashmap_isempty(y->transports));
+        pa_hashmap_free(y->transports);
+    }
+
+    if (y->connection) {
+        dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_HFP_AG);
+        dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_HFP_HS);
+        dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_A2DP_SOURCE);
+        dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_A2DP_SINK);
+        pa_dbus_remove_matches(
+            pa_dbus_connection_get(y->connection),
+            "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
+            ",arg0='org.bluez'",
+            "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
+            "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
+            "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
+            "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
+            "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
+            "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
+            "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
+            "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
+            "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
+            "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
+            "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
+            NULL);
+
+        if (y->filter_added)
+            dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
+
+        pa_dbus_connection_unref(y->connection);
+    }
+
+    for (i = 0; i < PA_BLUEZ4_HOOK_MAX; i++)
+        pa_hook_done(&y->hooks[i]);
+
+    if (y->core)
+        pa_shared_remove(y->core, "bluez4-discovery");
+
+    pa_xfree(y);
+}
+
+pa_hook* pa_bluez4_discovery_hook(pa_bluez4_discovery *y, pa_bluez4_hook_t hook) {
+    pa_assert(y);
+    pa_assert(PA_REFCNT_VALUE(y) > 0);
+
+    return &y->hooks[hook];
+}
+
+pa_bluez4_form_factor_t pa_bluez4_get_form_factor(uint32_t class) {
+    unsigned major, minor;
+    pa_bluez4_form_factor_t r;
+
+    static const pa_bluez4_form_factor_t table[] = {
+        [1] = PA_BLUEZ4_FORM_FACTOR_HEADSET,
+        [2] = PA_BLUEZ4_FORM_FACTOR_HANDSFREE,
+        [4] = PA_BLUEZ4_FORM_FACTOR_MICROPHONE,
+        [5] = PA_BLUEZ4_FORM_FACTOR_SPEAKER,
+        [6] = PA_BLUEZ4_FORM_FACTOR_HEADPHONE,
+        [7] = PA_BLUEZ4_FORM_FACTOR_PORTABLE,
+        [8] = PA_BLUEZ4_FORM_FACTOR_CAR,
+        [10] = PA_BLUEZ4_FORM_FACTOR_HIFI
+    };
+
+    /*
+     * See Bluetooth Assigned Numbers:
+     * https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
+     */
+    major = (class >> 8) & 0x1F;
+    minor = (class >> 2) & 0x3F;
+
+    switch (major) {
+        case 2:
+            return PA_BLUEZ4_FORM_FACTOR_PHONE;
+        case 4:
+            break;
+        default:
+            pa_log_debug("Unknown Bluetooth major device class %u", major);
+            return PA_BLUEZ4_FORM_FACTOR_UNKNOWN;
+    }
+
+    r = minor < PA_ELEMENTSOF(table) ? table[minor] : PA_BLUEZ4_FORM_FACTOR_UNKNOWN;
+
+    if (!r)
+        pa_log_debug("Unknown Bluetooth minor device class %u", minor);
+
+    return r;
+}
+
+const char *pa_bluez4_form_factor_to_string(pa_bluez4_form_factor_t ff) {
+    switch (ff) {
+        case PA_BLUEZ4_FORM_FACTOR_UNKNOWN:
+            return "unknown";
+        case PA_BLUEZ4_FORM_FACTOR_HEADSET:
+            return "headset";
+        case PA_BLUEZ4_FORM_FACTOR_HANDSFREE:
+            return "hands-free";
+        case PA_BLUEZ4_FORM_FACTOR_MICROPHONE:
+            return "microphone";
+        case PA_BLUEZ4_FORM_FACTOR_SPEAKER:
+            return "speaker";
+        case PA_BLUEZ4_FORM_FACTOR_HEADPHONE:
+            return "headphone";
+        case PA_BLUEZ4_FORM_FACTOR_PORTABLE:
+            return "portable";
+        case PA_BLUEZ4_FORM_FACTOR_CAR:
+            return "car";
+        case PA_BLUEZ4_FORM_FACTOR_HIFI:
+            return "hifi";
+        case PA_BLUEZ4_FORM_FACTOR_PHONE:
+            return "phone";
+    }
+
+    pa_assert_not_reached();
+}
diff --git a/src/modules/bluetooth/bluez4-util.h b/src/modules/bluetooth/bluez4-util.h
new file mode 100644 (file)
index 0000000..f4b5ca3
--- /dev/null
@@ -0,0 +1,160 @@
+#ifndef foobluez4utilhfoo
+#define foobluez4utilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008-2013 João Paulo Rechi Vita
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/llist.h>
+#include <pulsecore/macro.h>
+
+#define PA_BLUEZ4_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported"
+
+#define PA_BLUEZ4_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb"
+#define PA_BLUEZ4_UUID_A2DP_SINK   "0000110b-0000-1000-8000-00805f9b34fb"
+#define PA_BLUEZ4_UUID_HSP_HS      "00001108-0000-1000-8000-00805f9b34fb"
+#define PA_BLUEZ4_UUID_HSP_AG      "00001112-0000-1000-8000-00805f9b34fb"
+#define PA_BLUEZ4_UUID_HFP_HF      "0000111e-0000-1000-8000-00805f9b34fb"
+#define PA_BLUEZ4_UUID_HFP_AG      "0000111f-0000-1000-8000-00805f9b34fb"
+
+#define HSP_MAX_GAIN 15
+
+typedef struct pa_bluez4_device pa_bluez4_device;
+typedef struct pa_bluez4_discovery pa_bluez4_discovery;
+typedef struct pa_bluez4_transport pa_bluez4_transport;
+
+struct userdata;
+
+typedef enum pa_bluez4_profile {
+    PA_BLUEZ4_PROFILE_A2DP_SINK,
+    PA_BLUEZ4_PROFILE_A2DP_SOURCE,
+    PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT,
+    PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY,
+    PA_BLUEZ4_PROFILE_OFF
+} pa_bluez4_profile_t;
+
+#define PA_BLUEZ4_PROFILE_COUNT PA_BLUEZ4_PROFILE_OFF
+
+struct pa_bluez4_hook_uuid_data {
+    pa_bluez4_device *device;
+    const char *uuid;
+};
+
+/* Hook data: pa_bluez4_discovery pointer. */
+typedef enum pa_bluez4_hook {
+    PA_BLUEZ4_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluez4_device */
+    PA_BLUEZ4_HOOK_DEVICE_UUID_ADDED, /* Call data: pa_bluez4_hook_uuid_data */
+    PA_BLUEZ4_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluez4_transport */
+    PA_BLUEZ4_HOOK_TRANSPORT_NREC_CHANGED, /* Call data: pa_bluez4_transport */
+    PA_BLUEZ4_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED, /* Call data: pa_bluez4_transport */
+    PA_BLUEZ4_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED, /* Call data: pa_bluez4_transport */
+    PA_BLUEZ4_HOOK_MAX
+} pa_bluez4_hook_t;
+
+typedef enum pa_bluez4_transport_state {
+    PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED,
+    PA_BLUEZ4_TRANSPORT_STATE_IDLE, /* Connected but not playing */
+    PA_BLUEZ4_TRANSPORT_STATE_PLAYING
+} pa_bluez4_transport_state_t;
+
+struct pa_bluez4_transport {
+    pa_bluez4_device *device;
+    char *owner;
+    char *path;
+    pa_bluez4_profile_t profile;
+    uint8_t codec;
+    uint8_t *config;
+    int config_size;
+
+    pa_bluez4_transport_state_t state;
+    bool nrec;
+    uint16_t microphone_gain; /* Used for HSP/HFP */
+    uint16_t speaker_gain; /* Used for HSP/HFP */
+};
+
+/* This enum is shared among Audio, Headset, AudioSink, and AudioSource, although not all values are acceptable in all profiles */
+typedef enum pa_bluez4_audio_state {
+    PA_BLUEZ4_AUDIO_STATE_INVALID = -1,
+    PA_BLUEZ4_AUDIO_STATE_DISCONNECTED,
+    PA_BLUEZ4_AUDIO_STATE_CONNECTING,
+    PA_BLUEZ4_AUDIO_STATE_CONNECTED,
+    PA_BLUEZ4_AUDIO_STATE_PLAYING
+} pa_bluez4_audio_state_t;
+
+struct pa_bluez4_device {
+    pa_bluez4_discovery *discovery;
+    bool dead;
+
+    int device_info_valid;      /* 0: no results yet; 1: good results; -1: bad results ... */
+
+    /* Device information */
+    char *name;
+    char *path;
+    pa_bluez4_transport *transports[PA_BLUEZ4_PROFILE_COUNT];
+    int paired;
+    char *alias;
+    char *address;
+    int class;
+    pa_hashmap *uuids; /* char* -> char* (hashmap-as-a-set) */
+    int trusted;
+
+    /* Audio state */
+    pa_bluez4_audio_state_t audio_state;
+
+    /* AudioSink, AudioSource, Headset and HandsfreeGateway states */
+    pa_bluez4_audio_state_t profile_state[PA_BLUEZ4_PROFILE_COUNT];
+};
+
+pa_bluez4_discovery* pa_bluez4_discovery_get(pa_core *core);
+pa_bluez4_discovery* pa_bluez4_discovery_ref(pa_bluez4_discovery *y);
+void pa_bluez4_discovery_unref(pa_bluez4_discovery *d);
+
+pa_bluez4_device* pa_bluez4_discovery_get_by_path(pa_bluez4_discovery *d, const char* path);
+pa_bluez4_device* pa_bluez4_discovery_get_by_address(pa_bluez4_discovery *d, const char* address);
+
+bool pa_bluez4_device_any_audio_connected(const pa_bluez4_device *d);
+
+int pa_bluez4_transport_acquire(pa_bluez4_transport *t, bool optional, size_t *imtu, size_t *omtu);
+void pa_bluez4_transport_release(pa_bluez4_transport *t);
+
+void pa_bluez4_transport_set_microphone_gain(pa_bluez4_transport *t, uint16_t value);
+void pa_bluez4_transport_set_speaker_gain(pa_bluez4_transport *t, uint16_t value);
+
+pa_hook* pa_bluez4_discovery_hook(pa_bluez4_discovery *y, pa_bluez4_hook_t hook);
+
+typedef enum pa_bluez4_form_factor {
+    PA_BLUEZ4_FORM_FACTOR_UNKNOWN,
+    PA_BLUEZ4_FORM_FACTOR_HEADSET,
+    PA_BLUEZ4_FORM_FACTOR_HANDSFREE,
+    PA_BLUEZ4_FORM_FACTOR_MICROPHONE,
+    PA_BLUEZ4_FORM_FACTOR_SPEAKER,
+    PA_BLUEZ4_FORM_FACTOR_HEADPHONE,
+    PA_BLUEZ4_FORM_FACTOR_PORTABLE,
+    PA_BLUEZ4_FORM_FACTOR_CAR,
+    PA_BLUEZ4_FORM_FACTOR_HIFI,
+    PA_BLUEZ4_FORM_FACTOR_PHONE,
+} pa_bluez4_form_factor_t;
+
+pa_bluez4_form_factor_t pa_bluez4_get_form_factor(uint32_t class);
+const char *pa_bluez4_form_factor_to_string(pa_bluez4_form_factor_t ff);
+
+const char *pa_bluez4_profile_to_string(pa_bluez4_profile_t profile);
+
+#endif
diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c
new file mode 100644 (file)
index 0000000..c928323
--- /dev/null
@@ -0,0 +1,1863 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008-2013 João Paulo Rechi Vita
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-shared.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/shared.h>
+
+#include "a2dp-codecs.h"
+
+#include "bluez5-util.h"
+
+#define WAIT_FOR_PROFILES_TIMEOUT_USEC (3 * PA_USEC_PER_SEC)
+
+#define BLUEZ_SERVICE "org.bluez"
+#define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter1"
+#define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device1"
+#define BLUEZ_MEDIA_INTERFACE BLUEZ_SERVICE ".Media1"
+#define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1"
+#define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
+
+#define BLUEZ_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported"
+
+#define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource"
+#define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink"
+
+#define ENDPOINT_INTROSPECT_XML                                         \
+    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
+    "<node>"                                                            \
+    " <interface name=\"" BLUEZ_MEDIA_ENDPOINT_INTERFACE "\">"          \
+    "  <method name=\"SetConfiguration\">"                              \
+    "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
+    "   <arg name=\"properties\" direction=\"in\" type=\"ay\"/>"        \
+    "  </method>"                                                       \
+    "  <method name=\"SelectConfiguration\">"                           \
+    "   <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>"      \
+    "   <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>"    \
+    "  </method>"                                                       \
+    "  <method name=\"ClearConfiguration\">"                            \
+    "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
+    "  </method>"                                                       \
+    "  <method name=\"Release\">"                                       \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
+    "  <method name=\"Introspect\">"                                    \
+    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    "</node>"
+
+struct pa_bluetooth_discovery {
+    PA_REFCNT_DECLARE;
+
+    pa_core *core;
+    pa_dbus_connection *connection;
+    bool filter_added;
+    bool matches_added;
+    bool objects_listed;
+    pa_hook hooks[PA_BLUETOOTH_HOOK_MAX];
+    pa_hashmap *adapters;
+    pa_hashmap *devices;
+    pa_hashmap *transports;
+
+    int headset_backend;
+    pa_bluetooth_backend *ofono_backend, *native_backend;
+    PA_LLIST_HEAD(pa_dbus_pending, pending);
+};
+
+static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m,
+                                                                  DBusPendingCallNotifyFunction func, void *call_data) {
+    pa_dbus_pending *p;
+    DBusPendingCall *call;
+
+    pa_assert(y);
+    pa_assert(m);
+
+    pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(y->connection), m, &call, -1));
+
+    p = pa_dbus_pending_new(pa_dbus_connection_get(y->connection), m, call, y, call_data);
+    PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
+    dbus_pending_call_set_notify(call, func, p, NULL);
+
+    return p;
+}
+
+static const char *check_variant_property(DBusMessageIter *i) {
+    const char *key;
+
+    pa_assert(i);
+
+    if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+        pa_log_error("Property name not a string.");
+        return NULL;
+    }
+
+    dbus_message_iter_get_basic(i, &key);
+
+    if (!dbus_message_iter_next(i)) {
+        pa_log_error("Property value missing");
+        return NULL;
+    }
+
+    if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+        pa_log_error("Property value not a variant.");
+        return NULL;
+    }
+
+    return key;
+}
+
+pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const char *owner, const char *path,
+                                                   pa_bluetooth_profile_t p, const uint8_t *config, size_t size) {
+    pa_bluetooth_transport *t;
+
+    t = pa_xnew0(pa_bluetooth_transport, 1);
+    t->device = d;
+    t->owner = pa_xstrdup(owner);
+    t->path = pa_xstrdup(path);
+    t->profile = p;
+    t->config_size = size;
+
+    if (size > 0) {
+        t->config = pa_xnew(uint8_t, size);
+        memcpy(t->config, config, size);
+    }
+
+    return t;
+}
+
+static const char *transport_state_to_string(pa_bluetooth_transport_state_t state) {
+    switch(state) {
+        case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED:
+            return "disconnected";
+        case PA_BLUETOOTH_TRANSPORT_STATE_IDLE:
+            return "idle";
+        case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING:
+            return "playing";
+    }
+
+    return "invalid";
+}
+
+static bool device_supports_profile(pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
+    switch (profile) {
+        case PA_BLUETOOTH_PROFILE_A2DP_SINK:
+            return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SINK);
+        case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
+            return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SOURCE);
+        case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+            return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS)
+                || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF);
+        case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+            return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_AG)
+                || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_AG);
+        case PA_BLUETOOTH_PROFILE_OFF:
+            pa_assert_not_reached();
+    }
+
+    pa_assert_not_reached();
+}
+
+static bool device_is_profile_connected(pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
+    if (device->transports[profile] && device->transports[profile]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
+        return true;
+    else
+        return false;
+}
+
+static unsigned device_count_disconnected_profiles(pa_bluetooth_device *device) {
+    pa_bluetooth_profile_t profile;
+    unsigned count = 0;
+
+    for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
+        if (!device_supports_profile(device, profile))
+            continue;
+
+        if (!device_is_profile_connected(device, profile))
+            count++;
+    }
+
+    return count;
+}
+
+static void device_stop_waiting_for_profiles(pa_bluetooth_device *device) {
+    if (!device->wait_for_profiles_timer)
+        return;
+
+    device->discovery->core->mainloop->time_free(device->wait_for_profiles_timer);
+    device->wait_for_profiles_timer = NULL;
+}
+
+static void wait_for_profiles_cb(pa_mainloop_api *api, pa_time_event* event, const struct timeval *tv, void *userdata) {
+    pa_bluetooth_device *device = userdata;
+    pa_strbuf *buf;
+    pa_bluetooth_profile_t profile;
+    bool first = true;
+    char *profiles_str;
+
+    device_stop_waiting_for_profiles(device);
+
+    buf = pa_strbuf_new();
+
+    for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
+        if (device_is_profile_connected(device, profile))
+            continue;
+
+        if (!device_supports_profile(device, profile))
+            continue;
+
+        if (first)
+            first = false;
+        else
+            pa_strbuf_puts(buf, ", ");
+
+        pa_strbuf_puts(buf, pa_bluetooth_profile_to_string(profile));
+    }
+
+    profiles_str = pa_strbuf_to_string_free(buf);
+    pa_log_debug("Timeout expired, and device %s still has disconnected profiles: %s",
+                 device->path, profiles_str);
+    pa_xfree(profiles_str);
+    pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
+}
+
+static void device_start_waiting_for_profiles(pa_bluetooth_device *device) {
+    pa_assert(!device->wait_for_profiles_timer);
+    device->wait_for_profiles_timer = pa_core_rttime_new(device->discovery->core,
+                                                         pa_rtclock_now() + WAIT_FOR_PROFILES_TIMEOUT_USEC,
+                                                         wait_for_profiles_cb, device);
+}
+
+void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t, pa_bluetooth_transport_state_t state) {
+    bool old_any_connected;
+    unsigned n_disconnected_profiles;
+    bool new_device_appeared;
+    bool device_disconnected;
+
+    pa_assert(t);
+
+    if (t->state == state)
+        return;
+
+    old_any_connected = pa_bluetooth_device_any_transport_connected(t->device);
+
+    pa_log_debug("Transport %s state: %s -> %s",
+                 t->path, transport_state_to_string(t->state), transport_state_to_string(state));
+
+    t->state = state;
+
+    pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
+
+    /* If there are profiles that are expected to get connected soon (based
+     * on the UUID list), we wait for a bit before announcing the new
+     * device, so that all profiles have time to get connected before the
+     * card object is created. If we didn't wait, the card would always
+     * have only one profile marked as available in the initial state,
+     * which would prevent module-card-restore from restoring the initial
+     * profile properly. */
+
+    n_disconnected_profiles = device_count_disconnected_profiles(t->device);
+
+    new_device_appeared = !old_any_connected && pa_bluetooth_device_any_transport_connected(t->device);
+    device_disconnected = old_any_connected && !pa_bluetooth_device_any_transport_connected(t->device);
+
+    if (new_device_appeared) {
+        if (n_disconnected_profiles > 0)
+            device_start_waiting_for_profiles(t->device);
+        else
+            pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
+        return;
+    }
+
+    if (device_disconnected) {
+        if (t->device->wait_for_profiles_timer) {
+            /* If the timer is still running when the device disconnects, we
+             * never sent the notification of the device getting connected, so
+             * we don't need to send a notification about the disconnection
+             * either. Let's just stop the timer. */
+            device_stop_waiting_for_profiles(t->device);
+        } else
+            pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
+        return;
+    }
+
+    if (n_disconnected_profiles == 0 && t->device->wait_for_profiles_timer) {
+        /* All profiles are now connected, so we can stop the wait timer and
+         * send a notification of the new device. */
+        device_stop_waiting_for_profiles(t->device);
+        pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
+    }
+}
+
+void pa_bluetooth_transport_put(pa_bluetooth_transport *t) {
+    pa_assert(t);
+
+    t->device->transports[t->profile] = t;
+    pa_assert_se(pa_hashmap_put(t->device->discovery->transports, t->path, t) >= 0);
+    pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE);
+}
+
+void pa_bluetooth_transport_unlink(pa_bluetooth_transport *t) {
+    pa_assert(t);
+
+    pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED);
+    pa_hashmap_remove(t->device->discovery->transports, t->path);
+    t->device->transports[t->profile] = NULL;
+}
+
+void pa_bluetooth_transport_free(pa_bluetooth_transport *t) {
+    pa_assert(t);
+
+    if (t->destroy)
+        t->destroy(t);
+    pa_bluetooth_transport_unlink(t);
+
+    pa_xfree(t->owner);
+    pa_xfree(t->path);
+    pa_xfree(t->config);
+    pa_xfree(t);
+}
+
+static int bluez5_transport_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
+    DBusMessage *m, *r;
+    DBusError err;
+    int ret;
+    uint16_t i, o;
+    const char *method = optional ? "TryAcquire" : "Acquire";
+
+    pa_assert(t);
+    pa_assert(t->device);
+    pa_assert(t->device->discovery);
+
+    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, method));
+
+    dbus_error_init(&err);
+
+    r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
+    dbus_message_unref(m);
+    m = NULL;
+    if (!r) {
+        if (optional && pa_streq(err.name, "org.bluez.Error.NotAvailable"))
+            pa_log_info("Failed optional acquire of unavailable transport %s", t->path);
+        else
+            pa_log_error("Transport %s() failed for transport %s (%s)", method, t->path, err.message);
+
+        dbus_error_free(&err);
+        return -1;
+    }
+
+    if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_UINT16, &i, DBUS_TYPE_UINT16, &o,
+                               DBUS_TYPE_INVALID)) {
+        pa_log_error("Failed to parse %s() reply: %s", method, err.message);
+        dbus_error_free(&err);
+        ret = -1;
+        goto finish;
+    }
+
+    if (imtu)
+        *imtu = i;
+
+    if (omtu)
+        *omtu = o;
+
+finish:
+    dbus_message_unref(r);
+    return ret;
+}
+
+static void bluez5_transport_release_cb(pa_bluetooth_transport *t) {
+    DBusMessage *m, *r;
+    DBusError err;
+
+    pa_assert(t);
+    pa_assert(t->device);
+    pa_assert(t->device->discovery);
+
+    dbus_error_init(&err);
+
+    if (t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) {
+        pa_log_info("Transport %s auto-released by BlueZ or already released", t->path);
+        return;
+    }
+
+    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, "Release"));
+    r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
+    dbus_message_unref(m);
+    m = NULL;
+    if (r) {
+        dbus_message_unref(r);
+        r = NULL;
+    }
+
+    if (dbus_error_is_set(&err)) {
+        pa_log_error("Failed to release transport %s: %s", t->path, err.message);
+        dbus_error_free(&err);
+    } else
+        pa_log_info("Transport %s released", t->path);
+}
+
+bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) {
+    unsigned i;
+
+    pa_assert(d);
+
+    if (!d->valid)
+        return false;
+
+    for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
+        if (d->transports[i] && d->transports[i]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
+            return true;
+
+    return false;
+}
+
+static int transport_state_from_string(const char* value, pa_bluetooth_transport_state_t *state) {
+    pa_assert(value);
+    pa_assert(state);
+
+    if (pa_streq(value, "idle"))
+        *state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
+    else if (pa_streq(value, "pending") || pa_streq(value, "active"))
+        *state = PA_BLUETOOTH_TRANSPORT_STATE_PLAYING;
+    else
+        return -1;
+
+    return 0;
+}
+
+static void parse_transport_property(pa_bluetooth_transport *t, DBusMessageIter *i) {
+    const char *key;
+    DBusMessageIter variant_i;
+
+    key = check_variant_property(i);
+    if (key == NULL)
+        return;
+
+    dbus_message_iter_recurse(i, &variant_i);
+
+    switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+        case DBUS_TYPE_STRING: {
+
+            const char *value;
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "State")) {
+                pa_bluetooth_transport_state_t state;
+
+                if (transport_state_from_string(value, &state) < 0) {
+                    pa_log_error("Invalid state received: %s", value);
+                    return;
+                }
+
+                pa_bluetooth_transport_set_state(t, state);
+            }
+
+            break;
+        }
+    }
+
+    return;
+}
+
+static int parse_transport_properties(pa_bluetooth_transport *t, DBusMessageIter *i) {
+    DBusMessageIter element_i;
+
+    dbus_message_iter_recurse(i, &element_i);
+
+    while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+        DBusMessageIter dict_i;
+
+        dbus_message_iter_recurse(&element_i, &dict_i);
+
+        parse_transport_property(t, &dict_i);
+
+        dbus_message_iter_next(&element_i);
+    }
+
+    return 0;
+}
+
+static pa_bluetooth_device* device_create(pa_bluetooth_discovery *y, const char *path) {
+    pa_bluetooth_device *d;
+
+    pa_assert(y);
+    pa_assert(path);
+
+    d = pa_xnew0(pa_bluetooth_device, 1);
+    d->discovery = y;
+    d->path = pa_xstrdup(path);
+    d->uuids = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
+
+    pa_hashmap_put(y->devices, d->path, d);
+
+    return d;
+}
+
+pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery *y, const char *path) {
+    pa_bluetooth_device *d;
+
+    pa_assert(y);
+    pa_assert(PA_REFCNT_VALUE(y) > 0);
+    pa_assert(path);
+
+    if ((d = pa_hashmap_get(y->devices, path)) && d->valid)
+        return d;
+
+    return NULL;
+}
+
+pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local) {
+    pa_bluetooth_device *d;
+    void *state = NULL;
+
+    pa_assert(y);
+    pa_assert(PA_REFCNT_VALUE(y) > 0);
+    pa_assert(remote);
+    pa_assert(local);
+
+    while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
+        if (d->valid && pa_streq(d->address, remote) && pa_streq(d->adapter->address, local))
+            return d;
+
+    return NULL;
+}
+
+static void device_free(pa_bluetooth_device *d) {
+    unsigned i;
+
+    pa_assert(d);
+
+    device_stop_waiting_for_profiles(d);
+
+    for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
+        pa_bluetooth_transport *t;
+
+        if (!(t = d->transports[i]))
+            continue;
+
+        pa_bluetooth_transport_free(t);
+    }
+
+    if (d->uuids)
+        pa_hashmap_free(d->uuids);
+
+    pa_xfree(d->path);
+    pa_xfree(d->alias);
+    pa_xfree(d->address);
+    pa_xfree(d->adapter_path);
+    pa_xfree(d);
+}
+
+static void device_remove(pa_bluetooth_discovery *y, const char *path) {
+    pa_bluetooth_device *d;
+
+    if (!(d = pa_hashmap_remove(y->devices, path)))
+        pa_log_warn("Unknown device removed %s", path);
+    else {
+        pa_log_debug("Device %s removed", path);
+        device_free(d);
+    }
+}
+
+static void device_set_valid(pa_bluetooth_device *device, bool valid) {
+    bool old_any_connected;
+
+    pa_assert(device);
+
+    if (valid == device->valid)
+        return;
+
+    old_any_connected = pa_bluetooth_device_any_transport_connected(device);
+    device->valid = valid;
+
+    if (pa_bluetooth_device_any_transport_connected(device) != old_any_connected)
+        pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
+}
+
+static void device_update_valid(pa_bluetooth_device *d) {
+    pa_assert(d);
+
+    if (!d->properties_received) {
+        pa_assert(!d->valid);
+        return;
+    }
+
+    /* Check if mandatory properties are set. */
+    if (!d->address || !d->adapter_path || !d->alias) {
+        device_set_valid(d, false);
+        return;
+    }
+
+    if (!d->adapter || !d->adapter->valid) {
+        device_set_valid(d, false);
+        return;
+    }
+
+    device_set_valid(d, true);
+}
+
+static void device_set_adapter(pa_bluetooth_device *device, pa_bluetooth_adapter *adapter) {
+    pa_assert(device);
+
+    if (adapter == device->adapter)
+        return;
+
+    device->adapter = adapter;
+
+    device_update_valid(device);
+}
+
+static pa_bluetooth_adapter* adapter_create(pa_bluetooth_discovery *y, const char *path) {
+    pa_bluetooth_adapter *a;
+
+    pa_assert(y);
+    pa_assert(path);
+
+    a = pa_xnew0(pa_bluetooth_adapter, 1);
+    a->discovery = y;
+    a->path = pa_xstrdup(path);
+
+    pa_hashmap_put(y->adapters, a->path, a);
+
+    return a;
+}
+
+static void adapter_free(pa_bluetooth_adapter *a) {
+    pa_bluetooth_device *d;
+    void *state;
+
+    pa_assert(a);
+    pa_assert(a->discovery);
+
+    PA_HASHMAP_FOREACH(d, a->discovery->devices, state)
+        if (d->adapter == a)
+            device_set_adapter(d, NULL);
+
+    pa_xfree(a->path);
+    pa_xfree(a->address);
+    pa_xfree(a);
+}
+
+static void adapter_remove(pa_bluetooth_discovery *y, const char *path) {
+    pa_bluetooth_adapter *a;
+
+    if (!(a = pa_hashmap_remove(y->adapters, path)))
+        pa_log_warn("Unknown adapter removed %s", path);
+    else {
+        pa_log_debug("Adapter %s removed", path);
+        adapter_free(a);
+    }
+}
+
+static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
+    const char *key;
+    DBusMessageIter variant_i;
+
+    pa_assert(d);
+
+    key = check_variant_property(i);
+    if (key == NULL) {
+        pa_log_error("Received invalid property for device %s", d->path);
+        return;
+    }
+
+    dbus_message_iter_recurse(i, &variant_i);
+
+    switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+        case DBUS_TYPE_STRING: {
+            const char *value;
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "Alias")) {
+                pa_xfree(d->alias);
+                d->alias = pa_xstrdup(value);
+                pa_log_debug("%s: %s", key, value);
+            } else if (pa_streq(key, "Address")) {
+                if (d->properties_received) {
+                    pa_log_warn("Device property 'Address' expected to be constant but changed for %s, ignoring", d->path);
+                    return;
+                }
+
+                if (d->address) {
+                    pa_log_warn("Device %s: Received a duplicate 'Address' property, ignoring", d->path);
+                    return;
+                }
+
+                d->address = pa_xstrdup(value);
+                pa_log_debug("%s: %s", key, value);
+            }
+
+            break;
+        }
+
+        case DBUS_TYPE_OBJECT_PATH: {
+            const char *value;
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "Adapter")) {
+
+                if (d->properties_received) {
+                    pa_log_warn("Device property 'Adapter' expected to be constant but changed for %s, ignoring", d->path);
+                    return;
+                }
+
+                if (d->adapter_path) {
+                    pa_log_warn("Device %s: Received a duplicate 'Adapter' property, ignoring", d->path);
+                    return;
+                }
+
+                d->adapter_path = pa_xstrdup(value);
+                pa_log_debug("%s: %s", key, value);
+            }
+
+            break;
+        }
+
+        case DBUS_TYPE_UINT32: {
+            uint32_t value;
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "Class")) {
+                d->class_of_device = value;
+                pa_log_debug("%s: %d", key, value);
+            }
+
+            break;
+        }
+
+        case DBUS_TYPE_ARRAY: {
+            DBusMessageIter ai;
+            dbus_message_iter_recurse(&variant_i, &ai);
+
+            if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
+                /* bluetoothd never removes UUIDs from a device object so we
+                 * don't need to check for disappeared UUIDs here. */
+                while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
+                    const char *value;
+                    char *uuid;
+
+                    dbus_message_iter_get_basic(&ai, &value);
+
+                    if (pa_hashmap_get(d->uuids, value)) {
+                        dbus_message_iter_next(&ai);
+                        continue;
+                    }
+
+                    uuid = pa_xstrdup(value);
+                    pa_hashmap_put(d->uuids, uuid, uuid);
+
+                    pa_log_debug("%s: %s", key, value);
+                    dbus_message_iter_next(&ai);
+                }
+            }
+
+            break;
+        }
+    }
+}
+
+static void parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i) {
+    DBusMessageIter element_i;
+
+    dbus_message_iter_recurse(i, &element_i);
+
+    while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+        DBusMessageIter dict_i;
+
+        dbus_message_iter_recurse(&element_i, &dict_i);
+        parse_device_property(d, &dict_i);
+        dbus_message_iter_next(&element_i);
+    }
+
+    if (!d->properties_received) {
+        d->properties_received = true;
+        device_update_valid(d);
+
+        if (!d->address || !d->adapter_path || !d->alias)
+            pa_log_error("Non-optional information missing for device %s", d->path);
+    }
+}
+
+static void parse_adapter_properties(pa_bluetooth_adapter *a, DBusMessageIter *i, bool is_property_change) {
+    DBusMessageIter element_i;
+
+    pa_assert(a);
+
+    dbus_message_iter_recurse(i, &element_i);
+
+    while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+        DBusMessageIter dict_i, variant_i;
+        const char *key;
+
+        dbus_message_iter_recurse(&element_i, &dict_i);
+
+        key = check_variant_property(&dict_i);
+        if (key == NULL) {
+            pa_log_error("Received invalid property for adapter %s", a->path);
+            return;
+        }
+
+        dbus_message_iter_recurse(&dict_i, &variant_i);
+
+        if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_STRING && pa_streq(key, "Address")) {
+            const char *value;
+
+            if (is_property_change) {
+                pa_log_warn("Adapter property 'Address' expected to be constant but changed for %s, ignoring", a->path);
+                return;
+            }
+
+            if (a->address) {
+                pa_log_warn("Adapter %s received a duplicate 'Address' property, ignoring", a->path);
+                return;
+            }
+
+            dbus_message_iter_get_basic(&variant_i, &value);
+            a->address = pa_xstrdup(value);
+            a->valid = true;
+        }
+
+        dbus_message_iter_next(&element_i);
+    }
+}
+
+static void register_endpoint_reply(DBusPendingCall *pending, void *userdata) {
+    DBusMessage *r;
+    pa_dbus_pending *p;
+    pa_bluetooth_discovery *y;
+    char *endpoint;
+
+    pa_assert(pending);
+    pa_assert_se(p = userdata);
+    pa_assert_se(y = p->context_data);
+    pa_assert_se(endpoint = p->call_data);
+    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+    if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
+        pa_log_info("Couldn't register endpoint %s because it is disabled in BlueZ", endpoint);
+        goto finish;
+    }
+
+    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+        pa_log_error(BLUEZ_MEDIA_INTERFACE ".RegisterEndpoint() failed: %s: %s", dbus_message_get_error_name(r),
+                     pa_dbus_get_error_message(r));
+        goto finish;
+    }
+
+finish:
+    dbus_message_unref(r);
+
+    PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+    pa_dbus_pending_free(p);
+
+    pa_xfree(endpoint);
+}
+
+static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const char *endpoint, const char *uuid) {
+    DBusMessage *m;
+    DBusMessageIter i, d;
+    uint8_t codec = 0;
+
+    pa_log_debug("Registering %s on adapter %s", endpoint, path);
+
+    pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint"));
+
+    dbus_message_iter_init_append(m, &i);
+    pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint));
+    dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
+                                         DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d);
+    pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
+    pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec);
+
+    if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) || pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) {
+        a2dp_sbc_t capabilities;
+
+        capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO |
+                                    SBC_CHANNEL_MODE_JOINT_STEREO;
+        capabilities.frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 |
+                                 SBC_SAMPLING_FREQ_48000;
+        capabilities.allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS;
+        capabilities.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8;
+        capabilities.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16;
+        capabilities.min_bitpool = MIN_BITPOOL;
+        capabilities.max_bitpool = MAX_BITPOOL;
+
+        pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
+    }
+
+    dbus_message_iter_close_container(&i, &d);
+
+    send_and_add_to_pending(y, m, register_endpoint_reply, pa_xstrdup(endpoint));
+}
+
+static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessageIter *dict_i) {
+    DBusMessageIter element_i;
+    const char *path;
+    void *state;
+    pa_bluetooth_device *d;
+
+    pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_OBJECT_PATH);
+    dbus_message_iter_get_basic(dict_i, &path);
+
+    pa_assert_se(dbus_message_iter_next(dict_i));
+    pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_ARRAY);
+
+    dbus_message_iter_recurse(dict_i, &element_i);
+
+    while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+        DBusMessageIter iface_i;
+        const char *interface;
+
+        dbus_message_iter_recurse(&element_i, &iface_i);
+
+        pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_STRING);
+        dbus_message_iter_get_basic(&iface_i, &interface);
+
+        pa_assert_se(dbus_message_iter_next(&iface_i));
+        pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY);
+
+        if (pa_streq(interface, BLUEZ_ADAPTER_INTERFACE)) {
+            pa_bluetooth_adapter *a;
+
+            if ((a = pa_hashmap_get(y->adapters, path))) {
+                pa_log_error("Found duplicated D-Bus path for adapter %s", path);
+                return;
+            } else
+                a = adapter_create(y, path);
+
+            pa_log_debug("Adapter %s found", path);
+
+            parse_adapter_properties(a, &iface_i, false);
+
+            if (!a->valid)
+                return;
+
+            register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE);
+            register_endpoint(y, path, A2DP_SINK_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SINK);
+
+        } else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
+
+            if ((d = pa_hashmap_get(y->devices, path))) {
+                if (d->properties_received) {
+                    pa_log_error("Found duplicated D-Bus path for device %s", path);
+                    return;
+                }
+            } else
+                d = device_create(y, path);
+
+            pa_log_debug("Device %s found", d->path);
+
+            parse_device_properties(d, &iface_i);
+
+        } else
+            pa_log_debug("Unknown interface %s found, skipping", interface);
+
+        dbus_message_iter_next(&element_i);
+    }
+
+    PA_HASHMAP_FOREACH(d, y->devices, state) {
+        if (d->properties_received && !d->tried_to_link_with_adapter) {
+            if (d->adapter_path) {
+                device_set_adapter(d, pa_hashmap_get(d->discovery->adapters, d->adapter_path));
+
+                if (!d->adapter)
+                    pa_log("Device %s points to a nonexistent adapter %s.", d->path, d->adapter_path);
+                else if (!d->adapter->valid)
+                    pa_log("Device %s points to an invalid adapter %s.", d->path, d->adapter_path);
+            }
+
+            d->tried_to_link_with_adapter = true;
+        }
+    }
+
+    return;
+}
+
+void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running) {
+    pa_assert(y);
+
+    pa_log_debug("oFono is running: %s", pa_yes_no(is_running));
+    if (y->headset_backend != HEADSET_BACKEND_AUTO)
+        return;
+
+    /* If ofono starts running, all devices that might be connected to the HS role
+     * need to be disconnected, so that the devices can be handled by ofono */
+    if (is_running) {
+        void *state;
+        pa_bluetooth_device *d;
+
+        PA_HASHMAP_FOREACH(d, y->devices, state) {
+            if (device_supports_profile(d, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)) {
+                DBusMessage *m;
+
+                pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, d->path, "org.bluez.Device1", "Disconnect"));
+                dbus_message_set_no_reply(m, true);
+                dbus_connection_send(pa_dbus_connection_get(y->connection), m, NULL);
+                dbus_message_unref(m);
+            }
+        }
+    }
+
+    pa_bluetooth_native_backend_enable_hs_role(y->native_backend, !is_running);
+}
+
+static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) {
+    pa_dbus_pending *p;
+    pa_bluetooth_discovery *y;
+    DBusMessage *r;
+    DBusMessageIter arg_i, element_i;
+
+    pa_assert_se(p = userdata);
+    pa_assert_se(y = p->context_data);
+    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+    if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
+        pa_log_warn("BlueZ D-Bus ObjectManager not available");
+        goto finish;
+    }
+
+    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+        pa_log_error("GetManagedObjects() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
+        goto finish;
+    }
+
+    if (!dbus_message_iter_init(r, &arg_i) || !pa_streq(dbus_message_get_signature(r), "a{oa{sa{sv}}}")) {
+        pa_log_error("Invalid reply signature for GetManagedObjects()");
+        goto finish;
+    }
+
+    dbus_message_iter_recurse(&arg_i, &element_i);
+    while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+        DBusMessageIter dict_i;
+
+        dbus_message_iter_recurse(&element_i, &dict_i);
+
+        parse_interfaces_and_properties(y, &dict_i);
+
+        dbus_message_iter_next(&element_i);
+    }
+
+    y->objects_listed = true;
+
+    if (!y->native_backend && y->headset_backend != HEADSET_BACKEND_OFONO)
+        y->native_backend = pa_bluetooth_native_backend_new(y->core, y, (y->headset_backend == HEADSET_BACKEND_NATIVE));
+    if (!y->ofono_backend && y->headset_backend != HEADSET_BACKEND_NATIVE)
+        y->ofono_backend = pa_bluetooth_ofono_backend_new(y->core, y);
+
+finish:
+    dbus_message_unref(r);
+
+    PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+    pa_dbus_pending_free(p);
+}
+
+static void get_managed_objects(pa_bluetooth_discovery *y) {
+    DBusMessage *m;
+
+    pa_assert(y);
+
+    pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/", "org.freedesktop.DBus.ObjectManager",
+                                                  "GetManagedObjects"));
+    send_and_add_to_pending(y, m, get_managed_objects_reply, NULL);
+}
+
+pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook) {
+    pa_assert(y);
+    pa_assert(PA_REFCNT_VALUE(y) > 0);
+
+    return &y->hooks[hook];
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
+    pa_bluetooth_discovery *y;
+    DBusError err;
+
+    pa_assert(bus);
+    pa_assert(m);
+    pa_assert_se(y = userdata);
+
+    dbus_error_init(&err);
+
+    if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
+        const char *name, *old_owner, *new_owner;
+
+        if (!dbus_message_get_args(m, &err,
+                                   DBUS_TYPE_STRING, &name,
+                                   DBUS_TYPE_STRING, &old_owner,
+                                   DBUS_TYPE_STRING, &new_owner,
+                                   DBUS_TYPE_INVALID)) {
+            pa_log_error("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
+            goto fail;
+        }
+
+        if (pa_streq(name, BLUEZ_SERVICE)) {
+            if (old_owner && *old_owner) {
+                pa_log_debug("Bluetooth daemon disappeared");
+                pa_hashmap_remove_all(y->devices);
+                pa_hashmap_remove_all(y->adapters);
+                y->objects_listed = false;
+                if (y->ofono_backend) {
+                    pa_bluetooth_ofono_backend_free(y->ofono_backend);
+                    y->ofono_backend = NULL;
+                }
+                if (y->native_backend) {
+                    pa_bluetooth_native_backend_free(y->native_backend);
+                    y->native_backend = NULL;
+                }
+            }
+
+            if (new_owner && *new_owner) {
+                pa_log_debug("Bluetooth daemon appeared");
+                get_managed_objects(y);
+            }
+        }
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")) {
+        DBusMessageIter arg_i;
+
+        if (!y->objects_listed)
+            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
+
+        if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) {
+            pa_log_error("Invalid signature found in InterfacesAdded");
+            goto fail;
+        }
+
+        parse_interfaces_and_properties(y, &arg_i);
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")) {
+        const char *p;
+        DBusMessageIter arg_i;
+        DBusMessageIter element_i;
+
+        if (!y->objects_listed)
+            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
+
+        if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oas")) {
+            pa_log_error("Invalid signature found in InterfacesRemoved");
+            goto fail;
+        }
+
+        dbus_message_iter_get_basic(&arg_i, &p);
+
+        pa_assert_se(dbus_message_iter_next(&arg_i));
+        pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
+
+        dbus_message_iter_recurse(&arg_i, &element_i);
+
+        while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
+            const char *iface;
+
+            dbus_message_iter_get_basic(&element_i, &iface);
+
+            if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE))
+                device_remove(y, p);
+            else if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE))
+                adapter_remove(y, p);
+
+            dbus_message_iter_next(&element_i);
+        }
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
+        DBusMessageIter arg_i;
+        const char *iface;
+
+        if (!y->objects_listed)
+            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
+
+        if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "sa{sv}as")) {
+            pa_log_error("Invalid signature found in PropertiesChanged");
+            goto fail;
+        }
+
+        dbus_message_iter_get_basic(&arg_i, &iface);
+
+        pa_assert_se(dbus_message_iter_next(&arg_i));
+        pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
+
+        if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE)) {
+            pa_bluetooth_adapter *a;
+
+            pa_log_debug("Properties changed in adapter %s", dbus_message_get_path(m));
+
+            if (!(a = pa_hashmap_get(y->adapters, dbus_message_get_path(m)))) {
+                pa_log_warn("Properties changed in unknown adapter");
+                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            }
+
+            parse_adapter_properties(a, &arg_i, true);
+
+        } else if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE)) {
+            pa_bluetooth_device *d;
+
+            pa_log_debug("Properties changed in device %s", dbus_message_get_path(m));
+
+            if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
+                pa_log_warn("Properties changed in unknown device");
+                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            }
+
+            if (!d->properties_received)
+                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+            parse_device_properties(d, &arg_i);
+        } else if (pa_streq(iface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
+            pa_bluetooth_transport *t;
+
+            pa_log_debug("Properties changed in transport %s", dbus_message_get_path(m));
+
+            if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
+                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+            parse_transport_properties(t, &arg_i);
+        }
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+
+fail:
+    dbus_error_free(&err);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
+    /* These bitpool values were chosen based on the A2DP spec recommendation */
+    switch (freq) {
+        case SBC_SAMPLING_FREQ_16000:
+        case SBC_SAMPLING_FREQ_32000:
+            return 53;
+
+        case SBC_SAMPLING_FREQ_44100:
+
+            switch (mode) {
+                case SBC_CHANNEL_MODE_MONO:
+                case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+                    return 31;
+
+                case SBC_CHANNEL_MODE_STEREO:
+                case SBC_CHANNEL_MODE_JOINT_STEREO:
+                    return 53;
+            }
+
+            pa_log_warn("Invalid channel mode %u", mode);
+            return 53;
+
+        case SBC_SAMPLING_FREQ_48000:
+
+            switch (mode) {
+                case SBC_CHANNEL_MODE_MONO:
+                case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+                    return 29;
+
+                case SBC_CHANNEL_MODE_STEREO:
+                case SBC_CHANNEL_MODE_JOINT_STEREO:
+                    return 51;
+            }
+
+            pa_log_warn("Invalid channel mode %u", mode);
+            return 51;
+    }
+
+    pa_log_warn("Invalid sampling freq %u", freq);
+    return 53;
+}
+
+const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
+    switch(profile) {
+        case PA_BLUETOOTH_PROFILE_A2DP_SINK:
+            return "a2dp_sink";
+        case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
+            return "a2dp_source";
+        case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+            return "headset_head_unit";
+        case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+            return "headset_audio_gateway";
+        case PA_BLUETOOTH_PROFILE_OFF:
+            return "off";
+    }
+
+    return NULL;
+}
+
+static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
+    pa_bluetooth_discovery *y = userdata;
+    pa_bluetooth_device *d;
+    pa_bluetooth_transport *t;
+    const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL;
+    const uint8_t *config = NULL;
+    int size = 0;
+    pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_OFF;
+    DBusMessageIter args, props;
+    DBusMessage *r;
+
+    if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
+        pa_log_error("Invalid signature for method SetConfiguration()");
+        goto fail2;
+    }
+
+    dbus_message_iter_get_basic(&args, &path);
+
+    if (pa_hashmap_get(y->transports, path)) {
+        pa_log_error("Endpoint SetConfiguration(): Transport %s is already configured.", path);
+        goto fail2;
+    }
+
+    pa_assert_se(dbus_message_iter_next(&args));
+
+    dbus_message_iter_recurse(&args, &props);
+    if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+        goto fail;
+
+    /* Read transport properties */
+    while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
+        const char *key;
+        DBusMessageIter value, entry;
+        int var;
+
+        dbus_message_iter_recurse(&props, &entry);
+        dbus_message_iter_get_basic(&entry, &key);
+
+        dbus_message_iter_next(&entry);
+        dbus_message_iter_recurse(&entry, &value);
+
+        var = dbus_message_iter_get_arg_type(&value);
+
+        if (pa_streq(key, "UUID")) {
+            if (var != DBUS_TYPE_STRING) {
+                pa_log_error("Property %s of wrong type %c", key, (char)var);
+                goto fail;
+            }
+
+            dbus_message_iter_get_basic(&value, &uuid);
+
+            endpoint_path = dbus_message_get_path(m);
+            if (pa_streq(endpoint_path, A2DP_SOURCE_ENDPOINT)) {
+                if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE))
+                    p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
+            } else if (pa_streq(endpoint_path, A2DP_SINK_ENDPOINT)) {
+                if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))
+                    p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
+            }
+
+            if (p == PA_BLUETOOTH_PROFILE_OFF) {
+                pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path);
+                goto fail;
+            }
+        } else if (pa_streq(key, "Device")) {
+            if (var != DBUS_TYPE_OBJECT_PATH) {
+                pa_log_error("Property %s of wrong type %c", key, (char)var);
+                goto fail;
+            }
+
+            dbus_message_iter_get_basic(&value, &dev_path);
+        } else if (pa_streq(key, "Configuration")) {
+            DBusMessageIter array;
+            a2dp_sbc_t *c;
+
+            if (var != DBUS_TYPE_ARRAY) {
+                pa_log_error("Property %s of wrong type %c", key, (char)var);
+                goto fail;
+            }
+
+            dbus_message_iter_recurse(&value, &array);
+            var = dbus_message_iter_get_arg_type(&array);
+            if (var != DBUS_TYPE_BYTE) {
+                pa_log_error("%s is an array of wrong type %c", key, (char)var);
+                goto fail;
+            }
+
+            dbus_message_iter_get_fixed_array(&array, &config, &size);
+            if (size != sizeof(a2dp_sbc_t)) {
+                pa_log_error("Configuration array of invalid size");
+                goto fail;
+            }
+
+            c = (a2dp_sbc_t *) config;
+
+            if (c->frequency != SBC_SAMPLING_FREQ_16000 && c->frequency != SBC_SAMPLING_FREQ_32000 &&
+                c->frequency != SBC_SAMPLING_FREQ_44100 && c->frequency != SBC_SAMPLING_FREQ_48000) {
+                pa_log_error("Invalid sampling frequency in configuration");
+                goto fail;
+            }
+
+            if (c->channel_mode != SBC_CHANNEL_MODE_MONO && c->channel_mode != SBC_CHANNEL_MODE_DUAL_CHANNEL &&
+                c->channel_mode != SBC_CHANNEL_MODE_STEREO && c->channel_mode != SBC_CHANNEL_MODE_JOINT_STEREO) {
+                pa_log_error("Invalid channel mode in configuration");
+                goto fail;
+            }
+
+            if (c->allocation_method != SBC_ALLOCATION_SNR && c->allocation_method != SBC_ALLOCATION_LOUDNESS) {
+                pa_log_error("Invalid allocation method in configuration");
+                goto fail;
+            }
+
+            if (c->subbands != SBC_SUBBANDS_4 && c->subbands != SBC_SUBBANDS_8) {
+                pa_log_error("Invalid SBC subbands in configuration");
+                goto fail;
+            }
+
+            if (c->block_length != SBC_BLOCK_LENGTH_4 && c->block_length != SBC_BLOCK_LENGTH_8 &&
+                c->block_length != SBC_BLOCK_LENGTH_12 && c->block_length != SBC_BLOCK_LENGTH_16) {
+                pa_log_error("Invalid block length in configuration");
+                goto fail;
+            }
+        }
+
+        dbus_message_iter_next(&props);
+    }
+
+    if ((d = pa_hashmap_get(y->devices, dev_path))) {
+        if (!d->valid) {
+            pa_log_error("Information about device %s is invalid", dev_path);
+            goto fail2;
+        }
+    } else {
+        /* InterfacesAdded signal is probably on its way, device_info_valid is kept as 0. */
+        pa_log_warn("SetConfiguration() received for unknown device %s", dev_path);
+        d = device_create(y, dev_path);
+    }
+
+    if (d->transports[p] != NULL) {
+        pa_log_error("Cannot configure transport %s because profile %s is already used", path, pa_bluetooth_profile_to_string(p));
+        goto fail2;
+    }
+
+    sender = dbus_message_get_sender(m);
+
+    pa_assert_se(r = dbus_message_new_method_return(m));
+    pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
+    dbus_message_unref(r);
+
+    t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
+    t->acquire = bluez5_transport_acquire_cb;
+    t->release = bluez5_transport_release_cb;
+    pa_bluetooth_transport_put(t);
+
+    pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
+
+    return NULL;
+
+fail:
+    pa_log_error("Endpoint SetConfiguration(): invalid arguments");
+
+fail2:
+    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to set configuration"));
+    return r;
+}
+
+static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
+    pa_bluetooth_discovery *y = userdata;
+    a2dp_sbc_t *cap, config;
+    uint8_t *pconf = (uint8_t *) &config;
+    int i, size;
+    DBusMessage *r;
+    DBusError err;
+
+    static const struct {
+        uint32_t rate;
+        uint8_t cap;
+    } freq_table[] = {
+        { 16000U, SBC_SAMPLING_FREQ_16000 },
+        { 32000U, SBC_SAMPLING_FREQ_32000 },
+        { 44100U, SBC_SAMPLING_FREQ_44100 },
+        { 48000U, SBC_SAMPLING_FREQ_48000 }
+    };
+
+    dbus_error_init(&err);
+
+    if (!dbus_message_get_args(m, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
+        pa_log_error("Endpoint SelectConfiguration(): %s", err.message);
+        dbus_error_free(&err);
+        goto fail;
+    }
+
+    if (size != sizeof(config)) {
+        pa_log_error("Capabilities array has invalid size");
+        goto fail;
+    }
+
+    pa_zero(config);
+
+    /* Find the lowest freq that is at least as high as the requested sampling rate */
+    for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)
+        if (freq_table[i].rate >= y->core->default_sample_spec.rate && (cap->frequency & freq_table[i].cap)) {
+            config.frequency = freq_table[i].cap;
+            break;
+        }
+
+    if ((unsigned) i == PA_ELEMENTSOF(freq_table)) {
+        for (--i; i >= 0; i--) {
+            if (cap->frequency & freq_table[i].cap) {
+                config.frequency = freq_table[i].cap;
+                break;
+            }
+        }
+
+        if (i < 0) {
+            pa_log_error("Not suitable sample rate");
+            goto fail;
+        }
+    }
+
+    pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table));
+
+    if (y->core->default_sample_spec.channels <= 1) {
+        if (cap->channel_mode & SBC_CHANNEL_MODE_MONO)
+            config.channel_mode = SBC_CHANNEL_MODE_MONO;
+        else if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+            config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
+        else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO)
+            config.channel_mode = SBC_CHANNEL_MODE_STEREO;
+        else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+            config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
+        else {
+            pa_log_error("No supported channel modes");
+            goto fail;
+        }
+    }
+
+    if (y->core->default_sample_spec.channels >= 2) {
+        if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+            config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
+        else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO)
+            config.channel_mode = SBC_CHANNEL_MODE_STEREO;
+        else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+            config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
+        else if (cap->channel_mode & SBC_CHANNEL_MODE_MONO)
+            config.channel_mode = SBC_CHANNEL_MODE_MONO;
+        else {
+            pa_log_error("No supported channel modes");
+            goto fail;
+        }
+    }
+
+    if (cap->block_length & SBC_BLOCK_LENGTH_16)
+        config.block_length = SBC_BLOCK_LENGTH_16;
+    else if (cap->block_length & SBC_BLOCK_LENGTH_12)
+        config.block_length = SBC_BLOCK_LENGTH_12;
+    else if (cap->block_length & SBC_BLOCK_LENGTH_8)
+        config.block_length = SBC_BLOCK_LENGTH_8;
+    else if (cap->block_length & SBC_BLOCK_LENGTH_4)
+        config.block_length = SBC_BLOCK_LENGTH_4;
+    else {
+        pa_log_error("No supported block lengths");
+        goto fail;
+    }
+
+    if (cap->subbands & SBC_SUBBANDS_8)
+        config.subbands = SBC_SUBBANDS_8;
+    else if (cap->subbands & SBC_SUBBANDS_4)
+        config.subbands = SBC_SUBBANDS_4;
+    else {
+        pa_log_error("No supported subbands");
+        goto fail;
+    }
+
+    if (cap->allocation_method & SBC_ALLOCATION_LOUDNESS)
+        config.allocation_method = SBC_ALLOCATION_LOUDNESS;
+    else if (cap->allocation_method & SBC_ALLOCATION_SNR)
+        config.allocation_method = SBC_ALLOCATION_SNR;
+
+    config.min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool);
+    config.max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(config.frequency, config.channel_mode), cap->max_bitpool);
+
+    if (config.min_bitpool > config.max_bitpool)
+        goto fail;
+
+    pa_assert_se(r = dbus_message_new_method_return(m));
+    pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size, DBUS_TYPE_INVALID));
+
+    return r;
+
+fail:
+    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to select configuration"));
+    return r;
+}
+
+static DBusMessage *endpoint_clear_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
+    pa_bluetooth_discovery *y = userdata;
+    pa_bluetooth_transport *t;
+    DBusMessage *r;
+    DBusError err;
+    const char *path;
+
+    dbus_error_init(&err);
+
+    if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+        pa_log_error("Endpoint ClearConfiguration(): %s", err.message);
+        dbus_error_free(&err);
+        goto fail;
+    }
+
+    if ((t = pa_hashmap_get(y->transports, path))) {
+        pa_log_debug("Clearing transport %s profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
+        pa_bluetooth_transport_free(t);
+    }
+
+    pa_assert_se(r = dbus_message_new_method_return(m));
+
+    return r;
+
+fail:
+    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to clear configuration"));
+    return r;
+}
+
+static DBusMessage *endpoint_release(DBusConnection *conn, DBusMessage *m, void *userdata) {
+    DBusMessage *r;
+
+    pa_assert_se(r = dbus_message_new_error(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE ".Error.NotImplemented",
+                                            "Method not implemented"));
+
+    return r;
+}
+
+static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
+    struct pa_bluetooth_discovery *y = userdata;
+    DBusMessage *r = NULL;
+    const char *path, *interface, *member;
+
+    pa_assert(y);
+
+    path = dbus_message_get_path(m);
+    interface = dbus_message_get_interface(m);
+    member = dbus_message_get_member(m);
+
+    pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
+
+    if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT))
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+        const char *xml = ENDPOINT_INTROSPECT_XML;
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
+
+    } else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"))
+        r = endpoint_set_configuration(c, m, userdata);
+    else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SelectConfiguration"))
+        r = endpoint_select_configuration(c, m, userdata);
+    else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "ClearConfiguration"))
+        r = endpoint_clear_configuration(c, m, userdata);
+    else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "Release"))
+        r = endpoint_release(c, m, userdata);
+    else
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (r) {
+        pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
+        dbus_message_unref(r);
+    }
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void endpoint_init(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
+    static const DBusObjectPathVTable vtable_endpoint = {
+        .message_function = endpoint_handler,
+    };
+
+    pa_assert(y);
+
+    switch(profile) {
+        case PA_BLUETOOTH_PROFILE_A2DP_SINK:
+            pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT,
+                                                              &vtable_endpoint, y));
+            break;
+        case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
+            pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT,
+                                                              &vtable_endpoint, y));
+            break;
+        default:
+            pa_assert_not_reached();
+            break;
+    }
+}
+
+static void endpoint_done(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
+    pa_assert(y);
+
+    switch(profile) {
+        case PA_BLUETOOTH_PROFILE_A2DP_SINK:
+            dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT);
+            break;
+        case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
+            dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT);
+            break;
+        default:
+            pa_assert_not_reached();
+            break;
+    }
+}
+
+pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backend) {
+    pa_bluetooth_discovery *y;
+    DBusError err;
+    DBusConnection *conn;
+    unsigned i;
+
+    y = pa_xnew0(pa_bluetooth_discovery, 1);
+    PA_REFCNT_INIT(y);
+    y->core = c;
+    y->headset_backend = headset_backend;
+    y->adapters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
+                                      (pa_free_cb_t) adapter_free);
+    y->devices = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
+                                     (pa_free_cb_t) device_free);
+    y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
+
+    for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
+        pa_hook_init(&y->hooks[i], y);
+
+    pa_shared_set(c, "bluetooth-discovery", y);
+
+    dbus_error_init(&err);
+
+    if (!(y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err))) {
+        pa_log_error("Failed to get D-Bus connection: %s", err.message);
+        goto fail;
+    }
+
+    conn = pa_dbus_connection_get(y->connection);
+
+    /* dynamic detection of bluetooth audio devices */
+    if (!dbus_connection_add_filter(conn, filter_cb, y, NULL)) {
+        pa_log_error("Failed to add filter function");
+        goto fail;
+    }
+    y->filter_added = true;
+
+    if (pa_dbus_add_matches(conn, &err,
+            "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
+            ",arg0='" BLUEZ_SERVICE "'",
+            "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
+            "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',"
+            "member='InterfacesRemoved'",
+            "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
+            ",arg0='" BLUEZ_ADAPTER_INTERFACE "'",
+            "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
+            ",arg0='" BLUEZ_DEVICE_INTERFACE "'",
+            "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
+            ",arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
+            NULL) < 0) {
+        pa_log_error("Failed to add D-Bus matches: %s", err.message);
+        goto fail;
+    }
+    y->matches_added = true;
+
+    endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SINK);
+    endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
+
+    get_managed_objects(y);
+
+    return y;
+
+fail:
+    pa_bluetooth_discovery_unref(y);
+    dbus_error_free(&err);
+
+    return NULL;
+}
+
+pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
+    pa_assert(y);
+    pa_assert(PA_REFCNT_VALUE(y) > 0);
+
+    PA_REFCNT_INC(y);
+
+    return y;
+}
+
+void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
+    pa_assert(y);
+    pa_assert(PA_REFCNT_VALUE(y) > 0);
+
+    if (PA_REFCNT_DEC(y) > 0)
+        return;
+
+    pa_dbus_free_pending_list(&y->pending);
+
+    if (y->ofono_backend)
+        pa_bluetooth_ofono_backend_free(y->ofono_backend);
+    if (y->native_backend)
+        pa_bluetooth_native_backend_free(y->native_backend);
+
+    if (y->adapters)
+        pa_hashmap_free(y->adapters);
+
+    if (y->devices)
+        pa_hashmap_free(y->devices);
+
+    if (y->transports) {
+        pa_assert(pa_hashmap_isempty(y->transports));
+        pa_hashmap_free(y->transports);
+    }
+
+    if (y->connection) {
+
+        if (y->matches_added)
+            pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
+                "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
+                "arg0='" BLUEZ_SERVICE "'",
+                "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',"
+                "member='InterfacesAdded'",
+                "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',"
+                "member='InterfacesRemoved'",
+                "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',"
+                "member='PropertiesChanged',arg0='" BLUEZ_ADAPTER_INTERFACE "'",
+                "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',"
+                "member='PropertiesChanged',arg0='" BLUEZ_DEVICE_INTERFACE "'",
+                "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',"
+                "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
+                NULL);
+
+        if (y->filter_added)
+            dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
+
+        endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SINK);
+        endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
+
+        pa_dbus_connection_unref(y->connection);
+    }
+
+    pa_shared_remove(y->core, "bluetooth-discovery");
+    pa_xfree(y);
+}
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
new file mode 100644 (file)
index 0000000..a3e7bf3
--- /dev/null
@@ -0,0 +1,168 @@
+#ifndef foobluez5utilhfoo
+#define foobluez5utilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008-2013 João Paulo Rechi Vita
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+
+#define PA_BLUETOOTH_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb"
+#define PA_BLUETOOTH_UUID_A2DP_SINK   "0000110b-0000-1000-8000-00805f9b34fb"
+#define PA_BLUETOOTH_UUID_HSP_HS      "00001108-0000-1000-8000-00805f9b34fb"
+#define PA_BLUETOOTH_UUID_HSP_AG      "00001112-0000-1000-8000-00805f9b34fb"
+#define PA_BLUETOOTH_UUID_HFP_HF      "0000111e-0000-1000-8000-00805f9b34fb"
+#define PA_BLUETOOTH_UUID_HFP_AG      "0000111f-0000-1000-8000-00805f9b34fb"
+
+typedef struct pa_bluetooth_transport pa_bluetooth_transport;
+typedef struct pa_bluetooth_device pa_bluetooth_device;
+typedef struct pa_bluetooth_adapter pa_bluetooth_adapter;
+typedef struct pa_bluetooth_discovery pa_bluetooth_discovery;
+typedef struct pa_bluetooth_backend pa_bluetooth_backend;
+
+typedef enum pa_bluetooth_hook {
+    PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED,          /* Call data: pa_bluetooth_device */
+    PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED,            /* Call data: pa_bluetooth_transport */
+    PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED,  /* Call data: pa_bluetooth_transport */
+    PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED,     /* Call data: pa_bluetooth_transport */
+    PA_BLUETOOTH_HOOK_MAX
+} pa_bluetooth_hook_t;
+
+typedef enum profile {
+    PA_BLUETOOTH_PROFILE_A2DP_SINK,
+    PA_BLUETOOTH_PROFILE_A2DP_SOURCE,
+    PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT,
+    PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY,
+    PA_BLUETOOTH_PROFILE_OFF
+} pa_bluetooth_profile_t;
+#define PA_BLUETOOTH_PROFILE_COUNT PA_BLUETOOTH_PROFILE_OFF
+
+typedef enum pa_bluetooth_transport_state {
+    PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED,
+    PA_BLUETOOTH_TRANSPORT_STATE_IDLE,
+    PA_BLUETOOTH_TRANSPORT_STATE_PLAYING
+} pa_bluetooth_transport_state_t;
+
+typedef int (*pa_bluetooth_transport_acquire_cb)(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu);
+typedef void (*pa_bluetooth_transport_release_cb)(pa_bluetooth_transport *t);
+typedef void (*pa_bluetooth_transport_destroy_cb)(pa_bluetooth_transport *t);
+typedef void (*pa_bluetooth_transport_set_speaker_gain_cb)(pa_bluetooth_transport *t, uint16_t gain);
+typedef void (*pa_bluetooth_transport_set_microphone_gain_cb)(pa_bluetooth_transport *t, uint16_t gain);
+
+struct pa_bluetooth_transport {
+    pa_bluetooth_device *device;
+
+    char *owner;
+    char *path;
+    pa_bluetooth_profile_t profile;
+
+    uint8_t codec;
+    uint8_t *config;
+    size_t config_size;
+
+    uint16_t microphone_gain;
+    uint16_t speaker_gain;
+
+    pa_bluetooth_transport_state_t state;
+
+    pa_bluetooth_transport_acquire_cb acquire;
+    pa_bluetooth_transport_release_cb release;
+    pa_bluetooth_transport_destroy_cb destroy;
+    pa_bluetooth_transport_set_speaker_gain_cb set_speaker_gain;
+    pa_bluetooth_transport_set_microphone_gain_cb set_microphone_gain;
+    void *userdata;
+};
+
+struct pa_bluetooth_device {
+    pa_bluetooth_discovery *discovery;
+    pa_bluetooth_adapter *adapter;
+
+    bool properties_received;
+    bool tried_to_link_with_adapter;
+    bool valid;
+    bool autodetect_mtu;
+
+    /* Device information */
+    char *path;
+    char *adapter_path;
+    char *alias;
+    char *address;
+    uint32_t class_of_device;
+    pa_hashmap *uuids; /* char* -> char* (hashmap-as-a-set) */
+
+    pa_bluetooth_transport *transports[PA_BLUETOOTH_PROFILE_COUNT];
+
+    pa_time_event *wait_for_profiles_timer;
+};
+
+struct pa_bluetooth_adapter {
+    pa_bluetooth_discovery *discovery;
+    char *path;
+    char *address;
+
+    bool valid;
+};
+
+#ifdef HAVE_BLUEZ_5_OFONO_HEADSET
+pa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y);
+void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *b);
+#else
+static inline pa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y) {
+    return NULL;
+}
+static inline void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *b) {}
+#endif
+
+#ifdef HAVE_BLUEZ_5_NATIVE_HEADSET
+pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_hs_role);
+void pa_bluetooth_native_backend_free(pa_bluetooth_backend *b);
+void pa_bluetooth_native_backend_enable_hs_role(pa_bluetooth_backend *b, bool enable_hs_role);
+#else
+static inline pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_hs_role) {
+    return NULL;
+}
+static inline void pa_bluetooth_native_backend_free(pa_bluetooth_backend *b) {}
+static inline void pa_bluetooth_native_backend_enable_hs_role(pa_bluetooth_backend *b, bool enable_hs_role) {}
+#endif
+
+pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const char *owner, const char *path,
+                                                   pa_bluetooth_profile_t p, const uint8_t *config, size_t size);
+
+void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t, pa_bluetooth_transport_state_t state);
+void pa_bluetooth_transport_put(pa_bluetooth_transport *t);
+void pa_bluetooth_transport_unlink(pa_bluetooth_transport *t);
+void pa_bluetooth_transport_free(pa_bluetooth_transport *t);
+
+bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d);
+
+pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery *y, const char *path);
+pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local);
+
+pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook);
+
+const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile);
+
+#define HEADSET_BACKEND_OFONO 0
+#define HEADSET_BACKEND_NATIVE 1
+#define HEADSET_BACKEND_AUTO 2
+
+pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core, int headset_backend);
+pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y);
+void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y);
+void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running);
+#endif
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
new file mode 100644 (file)
index 0000000..d69a77f
--- /dev/null
@@ -0,0 +1,89 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 João Paulo Rechi Vita
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/module.h>
+
+#include "module-bluetooth-discover-symdef.h"
+
+PA_MODULE_AUTHOR("João Paulo Rechi Vita");
+PA_MODULE_DESCRIPTION("Detect available Bluetooth daemon and load the corresponding discovery module");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+    "headset=ofono|native|auto (bluez 5 only)"
+    "autodetect_mtu=<boolean> (bluez 5 only)"
+);
+
+struct userdata {
+    uint32_t bluez5_module_idx;
+    uint32_t bluez4_module_idx;
+};
+
+int pa__init(pa_module* m) {
+    struct userdata *u;
+    pa_module *mm;
+
+    pa_assert(m);
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->bluez5_module_idx = PA_INVALID_INDEX;
+    u->bluez4_module_idx = PA_INVALID_INDEX;
+
+    if (pa_module_exists("module-bluez5-discover")) {
+        mm = pa_module_load(m->core, "module-bluez5-discover", m->argument);
+        if (mm)
+            u->bluez5_module_idx = mm->index;
+    }
+
+    if (pa_module_exists("module-bluez4-discover")) {
+        mm = pa_module_load(m->core, "module-bluez4-discover",  NULL);
+        if (mm)
+            u->bluez4_module_idx = mm->index;
+    }
+
+    if (u->bluez5_module_idx == PA_INVALID_INDEX && u->bluez4_module_idx == PA_INVALID_INDEX) {
+        pa_xfree(u);
+        return -1;
+    }
+
+    return 0;
+}
+
+void pa__done(pa_module* m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->bluez5_module_idx != PA_INVALID_INDEX)
+        pa_module_unload_by_index(m->core, u->bluez5_module_idx, true);
+
+    if (u->bluez4_module_idx != PA_INVALID_INDEX)
+        pa_module_unload_by_index(m->core, u->bluez4_module_idx, true);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c
new file mode 100644 (file)
index 0000000..316b9a8
--- /dev/null
@@ -0,0 +1,528 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2009 Canonical Ltd
+  Copyright (C) 2012 Intel Corporation
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/source.h>
+#include <pulsecore/core-util.h>
+
+#include "module-bluetooth-policy-symdef.h"
+
+PA_MODULE_AUTHOR("Frédéric Dalleau, Pali Rohár");
+PA_MODULE_DESCRIPTION("Policy module to make using bluetooth devices out-of-the-box easier");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+        "auto_switch=<Switch between hsp and a2dp profile? (0 - never, 1 - media.role=phone, 2 - heuristic> "
+        "a2dp_source=<Handle a2dp_source card profile (sink role)?> "
+        "ag=<Handle headset_audio_gateway card profile (headset role)?> "
+        "hfgw=<Handle hfgw card profile (headset role)?> DEPRECATED");
+
+static const char* const valid_modargs[] = {
+    "auto_switch",
+    "a2dp_source",
+    "ag",
+    "hfgw",
+    NULL
+};
+
+struct userdata {
+    uint32_t auto_switch;
+    bool enable_a2dp_source;
+    bool enable_ag;
+    pa_hook_slot *source_put_slot;
+    pa_hook_slot *sink_put_slot;
+    pa_hook_slot *source_output_put_slot;
+    pa_hook_slot *source_output_unlink_slot;
+    pa_hook_slot *card_init_profile_slot;
+    pa_hook_slot *card_unlink_slot;
+    pa_hook_slot *profile_available_changed_slot;
+    pa_hashmap *will_need_revert_card_map;
+};
+
+/* When a source is created, loopback it to default sink */
+static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, void *userdata) {
+    struct userdata *u = userdata;
+    const char *s;
+    const char *role;
+    char *args;
+
+    pa_assert(c);
+    pa_assert(source);
+
+    /* Only consider bluetooth sinks and sources */
+    s = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_BUS);
+    if (!s)
+        return PA_HOOK_OK;
+
+    if (!pa_streq(s, "bluetooth"))
+        return PA_HOOK_OK;
+
+    s = pa_proplist_gets(source->proplist, "bluetooth.protocol");
+    if (!s)
+        return PA_HOOK_OK;
+
+    if (u->enable_a2dp_source && pa_streq(s, "a2dp_source"))
+        role = "music";
+    /* TODO: remove hfgw when we remove BlueZ 4 support */
+    else if (u->enable_ag && (pa_streq(s, "hfgw") || pa_streq(s, "headset_audio_gateway")))
+        role = "phone";
+    else {
+        pa_log_debug("Profile %s cannot be selected for loopback", s);
+        return PA_HOOK_OK;
+    }
+
+    /* Load module-loopback */
+    args = pa_sprintf_malloc("source=\"%s\" source_dont_move=\"true\" sink_input_properties=\"media.role=%s\"", source->name,
+                             role);
+    (void) pa_module_load(c, "module-loopback", args);
+    pa_xfree(args);
+
+    return PA_HOOK_OK;
+}
+
+/* When a sink is created, loopback it to default source */
+static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void *userdata) {
+    struct userdata *u = userdata;
+    const char *s;
+    const char *role;
+    char *args;
+
+    pa_assert(c);
+    pa_assert(sink);
+
+    /* Only consider bluetooth sinks and sources */
+    s = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_BUS);
+    if (!s)
+        return PA_HOOK_OK;
+
+    if (!pa_streq(s, "bluetooth"))
+        return PA_HOOK_OK;
+
+    s = pa_proplist_gets(sink->proplist, "bluetooth.protocol");
+    if (!s)
+        return PA_HOOK_OK;
+
+    /* TODO: remove hfgw when we remove BlueZ 4 support */
+    if (u->enable_ag && (pa_streq(s, "hfgw") || pa_streq(s, "headset_audio_gateway")))
+        role = "phone";
+    else {
+        pa_log_debug("Profile %s cannot be selected for loopback", s);
+        return PA_HOOK_OK;
+    }
+
+    /* Load module-loopback */
+    args = pa_sprintf_malloc("sink=\"%s\" sink_dont_move=\"true\" source_output_properties=\"media.role=%s\"", sink->name,
+                             role);
+    (void) pa_module_load(c, "module-loopback", args);
+    pa_xfree(args);
+
+    return PA_HOOK_OK;
+}
+
+static void card_set_profile(struct userdata *u, pa_card *card, bool revert_to_a2dp)
+{
+    pa_card_profile *profile;
+    void *state;
+
+    /* Find available profile and activate it */
+    PA_HASHMAP_FOREACH(profile, card->profiles, state) {
+        if (profile->available == PA_AVAILABLE_NO)
+            continue;
+
+        /* Check for correct profile based on revert_to_a2dp */
+        if (revert_to_a2dp) {
+            if (!pa_streq(profile->name, "a2dp") && !pa_streq(profile->name, "a2dp_sink"))
+                continue;
+        } else {
+            if (!pa_streq(profile->name, "hsp") && !pa_streq(profile->name, "headset_head_unit"))
+                continue;
+        }
+
+        pa_log_debug("Setting card '%s' to profile '%s'", card->name, profile->name);
+
+        if (pa_card_set_profile(card, profile, false) != 0) {
+            pa_log_warn("Could not set profile '%s'", profile->name);
+            continue;
+        }
+
+        /* When we are not in revert_to_a2dp phase flag this card for will_need_revert */
+        if (!revert_to_a2dp)
+            pa_hashmap_put(u->will_need_revert_card_map, card, PA_INT_TO_PTR(1));
+
+        break;
+    }
+}
+
+/* Switch profile for one card */
+static void switch_profile(pa_card *card, bool revert_to_a2dp, void *userdata) {
+    struct userdata *u = userdata;
+    const char *s;
+
+    /* Only consider bluetooth cards */
+    s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS);
+    if (!s || !pa_streq(s, "bluetooth"))
+        return;
+
+    if (revert_to_a2dp) {
+        /* In revert_to_a2dp phase only consider cards with will_need_revert flag and remove it */
+        if (!pa_hashmap_remove(u->will_need_revert_card_map, card))
+            return;
+
+        /* Skip card if does not have active hsp profile */
+        if (!pa_streq(card->active_profile->name, "hsp") && !pa_streq(card->active_profile->name, "headset_head_unit"))
+            return;
+
+        /* Skip card if already has active a2dp profile */
+        if (pa_streq(card->active_profile->name, "a2dp") || pa_streq(card->active_profile->name, "a2dp_sink"))
+            return;
+    } else {
+        /* Skip card if does not have active a2dp profile */
+        if (!pa_streq(card->active_profile->name, "a2dp") && !pa_streq(card->active_profile->name, "a2dp_sink"))
+            return;
+
+        /* Skip card if already has active hsp profile */
+        if (pa_streq(card->active_profile->name, "hsp") || pa_streq(card->active_profile->name, "headset_head_unit"))
+            return;
+    }
+
+    card_set_profile(u, card, revert_to_a2dp);
+}
+
+/* Return true if we should ignore this source output */
+static bool ignore_output(pa_source_output *source_output, void *userdata) {
+    struct userdata *u = userdata;
+    const char *s;
+
+    /* New applications could set media.role for identifying streams */
+    /* We are interested only in media.role=phone */
+    s = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE);
+    if (s)
+        return !pa_streq(s, "phone");
+
+    /* If media.role is not set use some heuristic (if enabled) */
+    if (u->auto_switch != 2)
+        return true;
+
+    /* Ignore if resample method is peaks (used by desktop volume programs) */
+    if (pa_source_output_get_resample_method(source_output) == PA_RESAMPLER_PEAKS)
+        return true;
+
+    /* Ignore if there is no client/application assigned (used by virtual stream) */
+    if (!source_output->client)
+        return true;
+
+    /* Ignore if recording from monitor of sink */
+    if (source_output->direct_on_input)
+        return true;
+
+    return false;
+}
+
+static unsigned source_output_count(pa_core *c, void *userdata) {
+    pa_source_output *source_output;
+    uint32_t idx;
+    unsigned count = 0;
+
+    PA_IDXSET_FOREACH(source_output, c->source_outputs, idx)
+        if (!ignore_output(source_output, userdata))
+            ++count;
+
+    return count;
+}
+
+/* Switch profile for all cards */
+static void switch_profile_all(pa_idxset *cards, bool revert_to_a2dp, void *userdata) {
+    pa_card *card;
+    uint32_t idx;
+
+    PA_IDXSET_FOREACH(card, cards, idx)
+        switch_profile(card, revert_to_a2dp, userdata);
+}
+
+/* When a source output is created, switch profile a2dp to profile hsp */
+static pa_hook_result_t source_output_put_hook_callback(pa_core *c, pa_source_output *source_output, void *userdata) {
+    pa_assert(c);
+    pa_assert(source_output);
+
+    if (ignore_output(source_output, userdata))
+        return PA_HOOK_OK;
+
+    switch_profile_all(c->cards, false, userdata);
+    return PA_HOOK_OK;
+}
+
+/* When all source outputs are unlinked, switch profile hsp back back to profile a2dp */
+static pa_hook_result_t source_output_unlink_hook_callback(pa_core *c, pa_source_output *source_output, void *userdata) {
+    pa_assert(c);
+    pa_assert(source_output);
+
+    if (ignore_output(source_output, userdata))
+        return PA_HOOK_OK;
+
+    /* If there are still some source outputs do nothing. */
+    if (source_output_count(c, userdata) > 0)
+        return PA_HOOK_OK;
+
+    switch_profile_all(c->cards, true, userdata);
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_init_profile_hook_callback(pa_core *c, pa_card *card, void *userdata) {
+    struct userdata *u = userdata;
+    const char *s;
+
+    pa_assert(c);
+    pa_assert(card);
+
+    if (source_output_count(c, userdata) == 0)
+        return PA_HOOK_OK;
+
+    /* Only consider bluetooth cards */
+    s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS);
+    if (!s || !pa_streq(s, "bluetooth"))
+        return PA_HOOK_OK;
+
+    /* Ignore card if has already set other initial profile than a2dp */
+    if (card->active_profile &&
+        !pa_streq(card->active_profile->name, "a2dp") &&
+        !pa_streq(card->active_profile->name, "a2dp_sink"))
+        return PA_HOOK_OK;
+
+    /* Set initial profile to hsp */
+    card_set_profile(u, card, false);
+
+    /* Flag this card for will_need_revert */
+    pa_hashmap_put(u->will_need_revert_card_map, card, PA_INT_TO_PTR(1));
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_unlink_hook_callback(pa_core *c, pa_card *card, void *userdata) {
+    pa_assert(c);
+    pa_assert(card);
+    switch_profile(card, true, userdata);
+    return PA_HOOK_OK;
+}
+
+static pa_card_profile *find_best_profile(pa_card *card) {
+    void *state;
+    pa_card_profile *profile;
+    pa_card_profile *result = card->active_profile;
+
+    PA_HASHMAP_FOREACH(profile, card->profiles, state) {
+        if (profile->available == PA_AVAILABLE_NO)
+            continue;
+
+        if (result == NULL ||
+            (profile->available == PA_AVAILABLE_YES && result->available == PA_AVAILABLE_UNKNOWN) ||
+            (profile->available == result->available && profile->priority > result->priority))
+            result = profile;
+    }
+
+    return result;
+}
+
+static pa_hook_result_t profile_available_hook_callback(pa_core *c, pa_card_profile *profile, void *userdata) {
+    pa_card *card;
+    const char *s;
+    bool is_active_profile;
+    pa_card_profile *selected_profile;
+
+    pa_assert(c);
+    pa_assert(profile);
+    pa_assert_se((card = profile->card));
+
+    /* Only consider bluetooth cards */
+    s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS);
+    if (!s || !pa_streq(s, "bluetooth"))
+        return PA_HOOK_OK;
+
+    /* Do not automatically switch profiles for headsets, just in case */
+    /* TODO: remove a2dp and hsp when we remove BlueZ 4 support */
+    if (pa_streq(profile->name, "hsp") || pa_streq(profile->name, "a2dp") || pa_streq(profile->name, "a2dp_sink") ||
+        pa_streq(profile->name, "headset_head_unit"))
+        return PA_HOOK_OK;
+
+    is_active_profile = card->active_profile == profile;
+
+    if (profile->available == PA_AVAILABLE_YES) {
+        if (is_active_profile)
+            return PA_HOOK_OK;
+
+        if (card->active_profile->available == PA_AVAILABLE_YES && card->active_profile->priority >= profile->priority)
+            return PA_HOOK_OK;
+
+        selected_profile = profile;
+    } else {
+        if (!is_active_profile)
+            return PA_HOOK_OK;
+
+        pa_assert_se((selected_profile = find_best_profile(card)));
+
+        if (selected_profile == card->active_profile)
+            return PA_HOOK_OK;
+    }
+
+    pa_log_debug("Setting card '%s' to profile '%s'", card->name, selected_profile->name);
+
+    if (pa_card_set_profile(card, selected_profile, false) != 0)
+        pa_log_warn("Could not set profile '%s'", selected_profile->name);
+
+    return PA_HOOK_OK;
+}
+
+static void handle_all_profiles(pa_core *core) {
+    pa_card *card;
+    uint32_t state;
+
+    PA_IDXSET_FOREACH(card, core->cards, state) {
+        pa_card_profile *profile;
+        void *state2;
+
+        PA_HASHMAP_FOREACH(profile, card->profiles, state2)
+            profile_available_hook_callback(core, profile, NULL);
+    }
+}
+
+int pa__init(pa_module *m) {
+    pa_modargs *ma;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log_error("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+
+    u->auto_switch = 1;
+
+    if (pa_modargs_get_value(ma, "auto_switch", NULL)) {
+        bool auto_switch_bool;
+
+        /* auto_switch originally took a boolean value, let's keep
+         * compatibility with configuration files that still pass a boolean. */
+        if (pa_modargs_get_value_boolean(ma, "auto_switch", &auto_switch_bool) >= 0) {
+            if (auto_switch_bool)
+                u->auto_switch = 1;
+            else
+                u->auto_switch = 0;
+
+        } else if (pa_modargs_get_value_u32(ma, "auto_switch", &u->auto_switch) < 0) {
+            pa_log("Failed to parse auto_switch argument.");
+            goto fail;
+        }
+    }
+
+    u->enable_a2dp_source = true;
+    if (pa_modargs_get_value_boolean(ma, "a2dp_source", &u->enable_a2dp_source) < 0) {
+        pa_log("Failed to parse a2dp_source argument.");
+        goto fail;
+    }
+
+    u->enable_ag = true;
+    if (pa_modargs_get_value_boolean(ma, "hfgw", &u->enable_ag) < 0) {
+        pa_log("Failed to parse hfgw argument.");
+        goto fail;
+    }
+    if (pa_modargs_get_value_boolean(ma, "ag", &u->enable_ag) < 0) {
+        pa_log("Failed to parse ag argument.");
+        goto fail;
+    }
+
+    u->will_need_revert_card_map = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->source_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL,
+                                         (pa_hook_cb_t) source_put_hook_callback, u);
+
+    u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL,
+                                       (pa_hook_cb_t) sink_put_hook_callback, u);
+
+    if (u->auto_switch) {
+        u->source_output_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_NORMAL,
+                                                    (pa_hook_cb_t) source_output_put_hook_callback, u);
+
+        u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL,
+                                                       (pa_hook_cb_t) source_output_unlink_hook_callback, u);
+
+        u->card_init_profile_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_CHOOSE_INITIAL_PROFILE], PA_HOOK_NORMAL,
+                                           (pa_hook_cb_t) card_init_profile_hook_callback, u);
+
+        u->card_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_UNLINK], PA_HOOK_NORMAL,
+                                           (pa_hook_cb_t) card_unlink_hook_callback, u);
+    }
+
+    u->profile_available_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED],
+                                                        PA_HOOK_NORMAL, (pa_hook_cb_t) profile_available_hook_callback, u);
+
+    handle_all_profiles(m->core);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->source_put_slot)
+        pa_hook_slot_free(u->source_put_slot);
+
+    if (u->sink_put_slot)
+        pa_hook_slot_free(u->sink_put_slot);
+
+    if (u->source_output_put_slot)
+        pa_hook_slot_free(u->source_output_put_slot);
+
+    if (u->source_output_unlink_slot)
+        pa_hook_slot_free(u->source_output_unlink_slot);
+
+    if (u->card_init_profile_slot)
+        pa_hook_slot_free(u->card_init_profile_slot);
+
+    if (u->card_unlink_slot)
+        pa_hook_slot_free(u->card_unlink_slot);
+
+    if (u->profile_available_changed_slot)
+        pa_hook_slot_free(u->profile_available_changed_slot);
+
+    pa_hashmap_free(u->will_need_revert_card_map);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c
new file mode 100644 (file)
index 0000000..b3f7790
--- /dev/null
@@ -0,0 +1,2657 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008-2013 João Paulo Rechi Vita
+  Copyright 2011-2013 BMW Car IT GmbH.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <linux/sockios.h>
+#include <arpa/inet.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/sample.h>
+#include <pulse/timeval.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/poll.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/namereg.h>
+
+#include <sbc/sbc.h>
+
+#include "module-bluez4-device-symdef.h"
+#include "a2dp-codecs.h"
+#include "rtp.h"
+#include "bluez4-util.h"
+
+#define BITPOOL_DEC_LIMIT 32
+#define BITPOOL_DEC_STEP 5
+
+PA_MODULE_AUTHOR("João Paulo Rechi Vita");
+PA_MODULE_DESCRIPTION("BlueZ 4 Bluetooth audio sink and source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "name=<name for the card/sink/source, to be prefixed> "
+        "card_name=<name for the card> "
+        "card_properties=<properties for the card> "
+        "sink_name=<name for the sink> "
+        "sink_properties=<properties for the sink> "
+        "source_name=<name for the source> "
+        "source_properties=<properties for the source> "
+        "address=<address of the device> "
+        "profile=<a2dp|hsp|hfgw> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "path=<device object path> "
+        "sco_sink=<SCO over PCM sink name> "
+        "sco_source=<SCO over PCM source name>");
+
+/* TODO: not close fd when entering suspend mode in a2dp */
+
+static const char* const valid_modargs[] = {
+    "name",
+    "card_name",
+    "card_properties",
+    "sink_name",
+    "sink_properties",
+    "source_name",
+    "source_properties",
+    "address",
+    "profile",
+    "rate",
+    "channels",
+    "path",
+    "sco_sink",
+    "sco_source",
+    NULL
+};
+
+struct a2dp_info {
+    sbc_t sbc;                           /* Codec data */
+    bool sbc_initialized;                /* Keep track if the encoder is initialized */
+    size_t codesize, frame_length;       /* SBC Codesize, frame_length. We simply cache those values here */
+
+    void* buffer;                        /* Codec transfer buffer */
+    size_t buffer_size;                  /* Size of the buffer */
+
+    uint16_t seq_num;                    /* Cumulative packet sequence */
+    uint8_t min_bitpool;
+    uint8_t max_bitpool;
+};
+
+struct hsp_info {
+    pa_sink *sco_sink;
+    void (*sco_sink_set_volume)(pa_sink *s);
+    pa_source *sco_source;
+    void (*sco_source_set_volume)(pa_source *s);
+};
+
+struct bluetooth_msg {
+    pa_msgobject parent;
+    pa_card *card;
+};
+
+typedef struct bluetooth_msg bluetooth_msg;
+PA_DEFINE_PRIVATE_CLASS(bluetooth_msg, pa_msgobject);
+#define BLUETOOTH_MSG(o) (bluetooth_msg_cast(o))
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    pa_bluez4_device *device;
+    pa_hook_slot *uuid_added_slot;
+    char *address;
+    char *path;
+    pa_bluez4_transport *transport;
+    bool transport_acquired;
+    pa_hook_slot *discovery_slot;
+    pa_hook_slot *sink_state_changed_slot;
+    pa_hook_slot *source_state_changed_slot;
+    pa_hook_slot *transport_state_changed_slot;
+    pa_hook_slot *transport_nrec_changed_slot;
+    pa_hook_slot *transport_microphone_changed_slot;
+    pa_hook_slot *transport_speaker_changed_slot;
+
+    pa_bluez4_discovery *discovery;
+
+    char *output_port_name;
+    char *input_port_name;
+
+    pa_card *card;
+    pa_sink *sink;
+    pa_source *source;
+
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+    pa_thread *thread;
+    bluetooth_msg *msg;
+
+    uint64_t read_index, write_index;
+    pa_usec_t started_at;
+    pa_smoother *read_smoother;
+
+    pa_memchunk write_memchunk;
+
+    pa_sample_spec sample_spec, requested_sample_spec;
+
+    int stream_fd;
+
+    size_t read_link_mtu;
+    size_t read_block_size;
+
+    size_t write_link_mtu;
+    size_t write_block_size;
+
+    struct a2dp_info a2dp;
+    struct hsp_info hsp;
+
+    pa_bluez4_profile_t profile;
+
+    pa_modargs *modargs;
+
+    int stream_write_type;
+};
+
+enum {
+    BLUETOOTH_MESSAGE_IO_THREAD_FAILED,
+    BLUETOOTH_MESSAGE_MAX
+};
+
+#define FIXED_LATENCY_PLAYBACK_A2DP (25*PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_RECORD_A2DP (25*PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_PLAYBACK_HSP (125*PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_RECORD_HSP (25*PA_USEC_PER_MSEC)
+
+#define MAX_PLAYBACK_CATCH_UP_USEC (100*PA_USEC_PER_MSEC)
+
+#define USE_SCO_OVER_PCM(u) (u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT && (u->hsp.sco_sink && u->hsp.sco_source))
+
+static int init_profile(struct userdata *u);
+
+/* from IO thread */
+static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool) {
+    struct a2dp_info *a2dp;
+
+    pa_assert(u);
+
+    a2dp = &u->a2dp;
+
+    if (a2dp->sbc.bitpool == bitpool)
+        return;
+
+    if (bitpool > a2dp->max_bitpool)
+        bitpool = a2dp->max_bitpool;
+    else if (bitpool < a2dp->min_bitpool)
+        bitpool = a2dp->min_bitpool;
+
+    a2dp->sbc.bitpool = bitpool;
+
+    a2dp->codesize = sbc_get_codesize(&a2dp->sbc);
+    a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc);
+
+    pa_log_debug("Bitpool has changed to %u", a2dp->sbc.bitpool);
+
+    u->read_block_size =
+        (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+        / a2dp->frame_length * a2dp->codesize;
+
+    u->write_block_size =
+        (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+        / a2dp->frame_length * a2dp->codesize;
+
+    pa_sink_set_max_request_within_thread(u->sink, u->write_block_size);
+    pa_sink_set_fixed_latency_within_thread(u->sink,
+            FIXED_LATENCY_PLAYBACK_A2DP + pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
+}
+
+/* from IO thread, except in SCO over PCM */
+static void bt_transport_config_mtu(struct userdata *u) {
+    /* Calculate block sizes */
+    if (u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY) {
+        u->read_block_size = u->read_link_mtu;
+        u->write_block_size = u->write_link_mtu;
+    } else {
+        u->read_block_size =
+            (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+            / u->a2dp.frame_length * u->a2dp.codesize;
+
+        u->write_block_size =
+            (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+            / u->a2dp.frame_length * u->a2dp.codesize;
+    }
+
+    if (USE_SCO_OVER_PCM(u))
+        return;
+
+    if (u->sink) {
+        pa_sink_set_max_request_within_thread(u->sink, u->write_block_size);
+        pa_sink_set_fixed_latency_within_thread(u->sink,
+                                                (u->profile == PA_BLUEZ4_PROFILE_A2DP_SINK ?
+                                                 FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_HSP) +
+                                                pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
+    }
+
+    if (u->source)
+        pa_source_set_fixed_latency_within_thread(u->source,
+                                                  (u->profile == PA_BLUEZ4_PROFILE_A2DP_SOURCE ?
+                                                   FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_HSP) +
+                                                  pa_bytes_to_usec(u->read_block_size, &u->sample_spec));
+}
+
+/* from IO thread, except in SCO over PCM */
+
+static void setup_stream(struct userdata *u) {
+    struct pollfd *pollfd;
+    int one;
+
+    pa_log_info("Transport %s resuming", u->transport->path);
+
+    bt_transport_config_mtu(u);
+
+    pa_make_fd_nonblock(u->stream_fd);
+    pa_make_socket_low_delay(u->stream_fd);
+
+    one = 1;
+    if (setsockopt(u->stream_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0)
+        pa_log_warn("Failed to enable SO_TIMESTAMP: %s", pa_cstrerror(errno));
+
+    pa_log_debug("Stream properly set up, we're ready to roll!");
+
+    if (u->profile == PA_BLUEZ4_PROFILE_A2DP_SINK)
+        a2dp_set_bitpool(u, u->a2dp.max_bitpool);
+
+    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+    pollfd->fd = u->stream_fd;
+    pollfd->events = pollfd->revents = 0;
+
+    u->read_index = u->write_index = 0;
+    u->started_at = 0;
+
+    if (u->source)
+        u->read_smoother = pa_smoother_new(
+                PA_USEC_PER_SEC,
+                PA_USEC_PER_SEC*2,
+                true,
+                true,
+                10,
+                pa_rtclock_now(),
+                true);
+}
+
+static void teardown_stream(struct userdata *u) {
+    if (u->rtpoll_item) {
+        pa_rtpoll_item_free(u->rtpoll_item);
+        u->rtpoll_item = NULL;
+    }
+
+    if (u->stream_fd >= 0) {
+        pa_close(u->stream_fd);
+        u->stream_fd = -1;
+    }
+
+    if (u->read_smoother) {
+        pa_smoother_free(u->read_smoother);
+        u->read_smoother = NULL;
+    }
+
+    if (u->write_memchunk.memblock) {
+        pa_memblock_unref(u->write_memchunk.memblock);
+        pa_memchunk_reset(&u->write_memchunk);
+    }
+
+    pa_log_debug("Audio stream torn down");
+}
+
+static void bt_transport_release(struct userdata *u) {
+    pa_assert(u->transport);
+
+    /* Ignore if already released */
+    if (!u->transport_acquired)
+        return;
+
+    pa_log_debug("Releasing transport %s", u->transport->path);
+
+    pa_bluez4_transport_release(u->transport);
+
+    u->transport_acquired = false;
+
+    teardown_stream(u);
+}
+
+static int bt_transport_acquire(struct userdata *u, bool optional) {
+    pa_assert(u->transport);
+
+    if (u->transport_acquired)
+        return 0;
+
+    pa_log_debug("Acquiring transport %s", u->transport->path);
+
+    u->stream_fd = pa_bluez4_transport_acquire(u->transport, optional, &u->read_link_mtu, &u->write_link_mtu);
+    if (u->stream_fd < 0) {
+        if (!optional)
+            pa_log("Failed to acquire transport %s", u->transport->path);
+        else
+            pa_log_info("Failed optional acquire of transport %s", u->transport->path);
+
+        return -1;
+    }
+
+    u->transport_acquired = true;
+    pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd);
+
+    return 0;
+}
+
+/* Run from IO thread */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+    bool failed = false;
+    int r;
+
+    pa_assert(u->sink == PA_SINK(o));
+    pa_assert(u->transport);
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SINK_SUSPENDED:
+                    /* Ignore if transition is PA_SINK_INIT->PA_SINK_SUSPENDED */
+                    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state))
+                        break;
+
+                    /* Stop the device if the source is suspended as well */
+                    if (!u->source || u->source->state == PA_SOURCE_SUSPENDED)
+                        /* We deliberately ignore whether stopping
+                         * actually worked. Since the stream_fd is
+                         * closed it doesn't really matter */
+                        bt_transport_release(u);
+
+                    break;
+
+                case PA_SINK_IDLE:
+                case PA_SINK_RUNNING:
+                    if (u->sink->thread_info.state != PA_SINK_SUSPENDED)
+                        break;
+
+                    /* Resume the device if the source was suspended as well */
+                    if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+                        if (bt_transport_acquire(u, false) < 0)
+                            failed = true;
+                        else
+                            setup_stream(u);
+                    }
+                    break;
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                case PA_SINK_INVALID_STATE:
+                    ;
+            }
+            break;
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+
+            if (u->read_smoother) {
+                int64_t wi, ri;
+
+                ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
+                wi = pa_bytes_to_usec(u->write_index + u->write_block_size, &u->sample_spec);
+
+                *((int64_t*) data) = wi - ri;
+            } else {
+                int64_t ri, wi;
+
+                ri = pa_rtclock_now() - u->started_at;
+                wi = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+
+                *((int64_t*) data) = wi - ri;
+            }
+
+            *((int64_t*) data) += u->sink->thread_info.fixed_latency;
+            return 0;
+        }
+    }
+
+    r = pa_sink_process_msg(o, code, data, offset, chunk);
+
+    return (r < 0 || !failed) ? r : -1;
+}
+
+/* Run from IO thread */
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+    bool failed = false;
+    int r;
+
+    pa_assert(u->source == PA_SOURCE(o));
+    pa_assert(u->transport);
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_SET_STATE:
+
+            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SOURCE_SUSPENDED:
+                    /* Ignore if transition is PA_SOURCE_INIT->PA_SOURCE_SUSPENDED */
+                    if (!PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+                        break;
+
+                    /* Stop the device if the sink is suspended as well */
+                    if (!u->sink || u->sink->state == PA_SINK_SUSPENDED)
+                        bt_transport_release(u);
+
+                    if (u->read_smoother)
+                        pa_smoother_pause(u->read_smoother, pa_rtclock_now());
+                    break;
+
+                case PA_SOURCE_IDLE:
+                case PA_SOURCE_RUNNING:
+                    if (u->source->thread_info.state != PA_SOURCE_SUSPENDED)
+                        break;
+
+                    /* Resume the device if the sink was suspended as well */
+                    if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+                        if (bt_transport_acquire(u, false) < 0)
+                            failed = true;
+                        else
+                            setup_stream(u);
+                    }
+                    /* We don't resume the smoother here. Instead we
+                     * wait until the first packet arrives */
+                    break;
+
+                case PA_SOURCE_UNLINKED:
+                case PA_SOURCE_INIT:
+                case PA_SOURCE_INVALID_STATE:
+                    ;
+            }
+            break;
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            int64_t wi, ri;
+
+            if (u->read_smoother) {
+                wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
+                ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
+
+                *((int64_t*) data) = wi - ri + u->source->thread_info.fixed_latency;
+            } else
+                *((int64_t*) data) = 0;
+
+            return 0;
+        }
+
+    }
+
+    r = pa_source_process_msg(o, code, data, offset, chunk);
+
+    return (r < 0 || !failed) ? r : -1;
+}
+
+/* Called from main thread context */
+static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct bluetooth_msg *u = BLUETOOTH_MSG(obj);
+
+    switch (code) {
+        case BLUETOOTH_MESSAGE_IO_THREAD_FAILED: {
+            if (u->card->module->unload_requested)
+                break;
+
+            pa_log_debug("Switching the profile to off due to IO thread failure.");
+
+            pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
+            break;
+        }
+    }
+    return 0;
+}
+
+/* Run from IO thread */
+static int hsp_process_render(struct userdata *u) {
+    int ret = 0;
+
+    pa_assert(u);
+    pa_assert(u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY);
+    pa_assert(u->sink);
+
+    /* First, render some data */
+    if (!u->write_memchunk.memblock)
+        pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk);
+
+    pa_assert(u->write_memchunk.length == u->write_block_size);
+
+    for (;;) {
+        ssize_t l;
+        const void *p;
+
+        /* Now write that data to the socket. The socket is of type
+         * SEQPACKET, and we generated the data of the MTU size, so this
+         * should just work. */
+
+        p = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk);
+        l = pa_write(u->stream_fd, p, u->write_memchunk.length, &u->stream_write_type);
+        pa_memblock_release(u->write_memchunk.memblock);
+
+        pa_assert(l != 0);
+
+        if (l < 0) {
+
+            if (errno == EINTR)
+                /* Retry right away if we got interrupted */
+                continue;
+
+            else if (errno == EAGAIN)
+                /* Hmm, apparently the socket was not writable, give up for now */
+                break;
+
+            pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno));
+            ret = -1;
+            break;
+        }
+
+        pa_assert((size_t) l <= u->write_memchunk.length);
+
+        if ((size_t) l != u->write_memchunk.length) {
+            pa_log_error("Wrote memory block to socket only partially! %llu written, wanted to write %llu.",
+                        (unsigned long long) l,
+                        (unsigned long long) u->write_memchunk.length);
+            ret = -1;
+            break;
+        }
+
+        u->write_index += (uint64_t) u->write_memchunk.length;
+        pa_memblock_unref(u->write_memchunk.memblock);
+        pa_memchunk_reset(&u->write_memchunk);
+
+        ret = 1;
+        break;
+    }
+
+    return ret;
+}
+
+/* Run from IO thread */
+static int hsp_process_push(struct userdata *u) {
+    int ret = 0;
+    pa_memchunk memchunk;
+
+    pa_assert(u);
+    pa_assert(u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY);
+    pa_assert(u->source);
+    pa_assert(u->read_smoother);
+
+    memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
+    memchunk.index = memchunk.length = 0;
+
+    for (;;) {
+        ssize_t l;
+        void *p;
+        struct msghdr m;
+        struct cmsghdr *cm;
+        uint8_t aux[1024];
+        struct iovec iov;
+        bool found_tstamp = false;
+        pa_usec_t tstamp;
+
+        memset(&m, 0, sizeof(m));
+        memset(&aux, 0, sizeof(aux));
+        memset(&iov, 0, sizeof(iov));
+
+        m.msg_iov = &iov;
+        m.msg_iovlen = 1;
+        m.msg_control = aux;
+        m.msg_controllen = sizeof(aux);
+
+        p = pa_memblock_acquire(memchunk.memblock);
+        iov.iov_base = p;
+        iov.iov_len = pa_memblock_get_length(memchunk.memblock);
+        l = recvmsg(u->stream_fd, &m, 0);
+        pa_memblock_release(memchunk.memblock);
+
+        if (l <= 0) {
+
+            if (l < 0 && errno == EINTR)
+                /* Retry right away if we got interrupted */
+                continue;
+
+            else if (l < 0 && errno == EAGAIN)
+                /* Hmm, apparently the socket was not readable, give up for now. */
+                break;
+
+            pa_log_error("Failed to read data from SCO socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF");
+            ret = -1;
+            break;
+        }
+
+        pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock));
+
+        /* In some rare occasions, we might receive packets of a very strange
+         * size. This could potentially be possible if the SCO packet was
+         * received partially over-the-air, or more probably due to hardware
+         * issues in our Bluetooth adapter. In these cases, in order to avoid
+         * an assertion failure due to unaligned data, just discard the whole
+         * packet */
+        if (!pa_frame_aligned(l, &u->sample_spec)) {
+            pa_log_warn("SCO packet received of unaligned size: %zu", l);
+            break;
+        }
+
+        memchunk.length = (size_t) l;
+        u->read_index += (uint64_t) l;
+
+        for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm))
+            if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) {
+                struct timeval *tv = (struct timeval*) CMSG_DATA(cm);
+                pa_rtclock_from_wallclock(tv);
+                tstamp = pa_timeval_load(tv);
+                found_tstamp = true;
+                break;
+            }
+
+        if (!found_tstamp) {
+            pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!");
+            tstamp = pa_rtclock_now();
+        }
+
+        pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
+        pa_smoother_resume(u->read_smoother, tstamp, true);
+
+        pa_source_post(u->source, &memchunk);
+
+        ret = l;
+        break;
+    }
+
+    pa_memblock_unref(memchunk.memblock);
+
+    return ret;
+}
+
+/* Run from IO thread */
+static void a2dp_prepare_buffer(struct userdata *u) {
+    size_t min_buffer_size = PA_MAX(u->read_link_mtu, u->write_link_mtu);
+
+    pa_assert(u);
+
+    if (u->a2dp.buffer_size >= min_buffer_size)
+        return;
+
+    u->a2dp.buffer_size = 2 * min_buffer_size;
+    pa_xfree(u->a2dp.buffer);
+    u->a2dp.buffer = pa_xmalloc(u->a2dp.buffer_size);
+}
+
+/* Run from IO thread */
+static int a2dp_process_render(struct userdata *u) {
+    struct a2dp_info *a2dp;
+    struct rtp_header *header;
+    struct rtp_payload *payload;
+    size_t nbytes;
+    void *d;
+    const void *p;
+    size_t to_write, to_encode;
+    unsigned frame_count;
+    int ret = 0;
+
+    pa_assert(u);
+    pa_assert(u->profile == PA_BLUEZ4_PROFILE_A2DP_SINK);
+    pa_assert(u->sink);
+
+    /* First, render some data */
+    if (!u->write_memchunk.memblock)
+        pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk);
+
+    pa_assert(u->write_memchunk.length == u->write_block_size);
+
+    a2dp_prepare_buffer(u);
+
+    a2dp = &u->a2dp;
+    header = a2dp->buffer;
+    payload = (struct rtp_payload*) ((uint8_t*) a2dp->buffer + sizeof(*header));
+
+    frame_count = 0;
+
+    /* Try to create a packet of the full MTU */
+
+    p = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk);
+    to_encode = u->write_memchunk.length;
+
+    d = (uint8_t*) a2dp->buffer + sizeof(*header) + sizeof(*payload);
+    to_write = a2dp->buffer_size - sizeof(*header) - sizeof(*payload);
+
+    while (PA_LIKELY(to_encode > 0 && to_write > 0)) {
+        ssize_t written;
+        ssize_t encoded;
+
+        encoded = sbc_encode(&a2dp->sbc,
+                             p, to_encode,
+                             d, to_write,
+                             &written);
+
+        if (PA_UNLIKELY(encoded <= 0)) {
+            pa_log_error("SBC encoding error (%li)", (long) encoded);
+            pa_memblock_release(u->write_memchunk.memblock);
+            return -1;
+        }
+
+/*         pa_log_debug("SBC: encoded: %lu; written: %lu", (unsigned long) encoded, (unsigned long) written); */
+/*         pa_log_debug("SBC: codesize: %lu; frame_length: %lu", (unsigned long) a2dp->codesize, (unsigned long) a2dp->frame_length); */
+
+        pa_assert_fp((size_t) encoded <= to_encode);
+        pa_assert_fp((size_t) encoded == a2dp->codesize);
+
+        pa_assert_fp((size_t) written <= to_write);
+        pa_assert_fp((size_t) written == a2dp->frame_length);
+
+        p = (const uint8_t*) p + encoded;
+        to_encode -= encoded;
+
+        d = (uint8_t*) d + written;
+        to_write -= written;
+
+        frame_count++;
+    }
+
+    pa_memblock_release(u->write_memchunk.memblock);
+
+    pa_assert(to_encode == 0);
+
+    PA_ONCE_BEGIN {
+        pa_log_debug("Using SBC encoder implementation: %s", pa_strnull(sbc_get_implementation_info(&a2dp->sbc)));
+    } PA_ONCE_END;
+
+    /* write it to the fifo */
+    memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
+    header->v = 2;
+    header->pt = 1;
+    header->sequence_number = htons(a2dp->seq_num++);
+    header->timestamp = htonl(u->write_index / pa_frame_size(&u->sample_spec));
+    header->ssrc = htonl(1);
+    payload->frame_count = frame_count;
+
+    nbytes = (uint8_t*) d - (uint8_t*) a2dp->buffer;
+
+    for (;;) {
+        ssize_t l;
+
+        l = pa_write(u->stream_fd, a2dp->buffer, nbytes, &u->stream_write_type);
+
+        pa_assert(l != 0);
+
+        if (l < 0) {
+
+            if (errno == EINTR)
+                /* Retry right away if we got interrupted */
+                continue;
+
+            else if (errno == EAGAIN)
+                /* Hmm, apparently the socket was not writable, give up for now */
+                break;
+
+            pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno));
+            ret = -1;
+            break;
+        }
+
+        pa_assert((size_t) l <= nbytes);
+
+        if ((size_t) l != nbytes) {
+            pa_log_warn("Wrote memory block to socket only partially! %llu written, wanted to write %llu.",
+                        (unsigned long long) l,
+                        (unsigned long long) nbytes);
+            ret = -1;
+            break;
+        }
+
+        u->write_index += (uint64_t) u->write_memchunk.length;
+        pa_memblock_unref(u->write_memchunk.memblock);
+        pa_memchunk_reset(&u->write_memchunk);
+
+        ret = 1;
+
+        break;
+    }
+
+    return ret;
+}
+
+static int a2dp_process_push(struct userdata *u) {
+    int ret = 0;
+    pa_memchunk memchunk;
+
+    pa_assert(u);
+    pa_assert(u->profile == PA_BLUEZ4_PROFILE_A2DP_SOURCE);
+    pa_assert(u->source);
+    pa_assert(u->read_smoother);
+
+    memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
+    memchunk.index = memchunk.length = 0;
+
+    for (;;) {
+        bool found_tstamp = false;
+        pa_usec_t tstamp;
+        struct a2dp_info *a2dp;
+        struct rtp_header *header;
+        struct rtp_payload *payload;
+        const void *p;
+        void *d;
+        ssize_t l;
+        size_t to_write, to_decode;
+        size_t total_written = 0;
+
+        a2dp_prepare_buffer(u);
+
+        a2dp = &u->a2dp;
+        header = a2dp->buffer;
+        payload = (struct rtp_payload*) ((uint8_t*) a2dp->buffer + sizeof(*header));
+
+        l = pa_read(u->stream_fd, a2dp->buffer, a2dp->buffer_size, &u->stream_write_type);
+
+        if (l <= 0) {
+
+            if (l < 0 && errno == EINTR)
+                /* Retry right away if we got interrupted */
+                continue;
+
+            else if (l < 0 && errno == EAGAIN)
+                /* Hmm, apparently the socket was not readable, give up for now. */
+                break;
+
+            pa_log_error("Failed to read data from socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF");
+            ret = -1;
+            break;
+        }
+
+        pa_assert((size_t) l <= a2dp->buffer_size);
+
+        /* TODO: get timestamp from rtp */
+        if (!found_tstamp) {
+            /* pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); */
+            tstamp = pa_rtclock_now();
+        }
+
+        p = (uint8_t*) a2dp->buffer + sizeof(*header) + sizeof(*payload);
+        to_decode = l - sizeof(*header) - sizeof(*payload);
+
+        d = pa_memblock_acquire(memchunk.memblock);
+        to_write = memchunk.length = pa_memblock_get_length(memchunk.memblock);
+
+        while (PA_LIKELY(to_decode > 0)) {
+            size_t written;
+            ssize_t decoded;
+
+            decoded = sbc_decode(&a2dp->sbc,
+                                 p, to_decode,
+                                 d, to_write,
+                                 &written);
+
+            if (PA_UNLIKELY(decoded <= 0)) {
+                pa_log_error("SBC decoding error (%li)", (long) decoded);
+                pa_memblock_release(memchunk.memblock);
+                pa_memblock_unref(memchunk.memblock);
+                return 0;
+            }
+
+/*             pa_log_debug("SBC: decoded: %lu; written: %lu", (unsigned long) decoded, (unsigned long) written); */
+/*             pa_log_debug("SBC: frame_length: %lu; codesize: %lu", (unsigned long) a2dp->frame_length, (unsigned long) a2dp->codesize); */
+
+            total_written += written;
+
+            /* Reset frame length, it can be changed due to bitpool change */
+            a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc);
+
+            pa_assert_fp((size_t) decoded <= to_decode);
+            pa_assert_fp((size_t) decoded == a2dp->frame_length);
+
+            pa_assert_fp((size_t) written == a2dp->codesize);
+
+            p = (const uint8_t*) p + decoded;
+            to_decode -= decoded;
+
+            d = (uint8_t*) d + written;
+            to_write -= written;
+        }
+
+        u->read_index += (uint64_t) total_written;
+        pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
+        pa_smoother_resume(u->read_smoother, tstamp, true);
+
+        memchunk.length -= to_write;
+
+        pa_memblock_release(memchunk.memblock);
+
+        pa_source_post(u->source, &memchunk);
+
+        ret = l;
+        break;
+    }
+
+    pa_memblock_unref(memchunk.memblock);
+
+    return ret;
+}
+
+static void a2dp_reduce_bitpool(struct userdata *u) {
+    struct a2dp_info *a2dp;
+    uint8_t bitpool;
+
+    pa_assert(u);
+
+    a2dp = &u->a2dp;
+
+    /* Check if bitpool is already at its limit */
+    if (a2dp->sbc.bitpool <= BITPOOL_DEC_LIMIT)
+        return;
+
+    bitpool = a2dp->sbc.bitpool - BITPOOL_DEC_STEP;
+
+    if (bitpool < BITPOOL_DEC_LIMIT)
+        bitpool = BITPOOL_DEC_LIMIT;
+
+    a2dp_set_bitpool(u, bitpool);
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    unsigned do_write = 0;
+    unsigned pending_read_bytes = 0;
+    bool writable = false;
+
+    pa_assert(u);
+    pa_assert(u->transport);
+
+    pa_log_debug("IO Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    /* Setup the stream only if the transport was already acquired */
+    if (u->transport_acquired)
+        setup_stream(u);
+
+    for (;;) {
+        struct pollfd *pollfd;
+        int ret;
+        bool disable_timer = true;
+
+        pollfd = u->rtpoll_item ? pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL) : NULL;
+
+        if (u->source && PA_SOURCE_IS_LINKED(u->source->thread_info.state)) {
+
+            /* We should send two blocks to the device before we expect
+             * a response. */
+
+            if (u->write_index == 0 && u->read_index <= 0)
+                do_write = 2;
+
+            if (pollfd && (pollfd->revents & POLLIN)) {
+                int n_read;
+
+                if (u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY)
+                    n_read = hsp_process_push(u);
+                else
+                    n_read = a2dp_process_push(u);
+
+                if (n_read < 0)
+                    goto io_fail;
+
+                if (n_read > 0) {
+                    /* We just read something, so we are supposed to write something, too */
+                    pending_read_bytes += n_read;
+                    do_write += pending_read_bytes / u->write_block_size;
+                    pending_read_bytes = pending_read_bytes % u->write_block_size;
+                }
+            }
+        }
+
+        if (u->sink && PA_SINK_IS_LINKED(u->sink->thread_info.state)) {
+
+            if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+                pa_sink_process_rewind(u->sink, 0);
+
+            if (pollfd) {
+                if (pollfd->revents & POLLOUT)
+                    writable = true;
+
+                if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0 && writable) {
+                    pa_usec_t time_passed;
+                    pa_usec_t audio_sent;
+
+                    /* Hmm, there is no input stream we could synchronize
+                     * to. So let's do things by time */
+
+                    time_passed = pa_rtclock_now() - u->started_at;
+                    audio_sent = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+
+                    if (audio_sent <= time_passed) {
+                        pa_usec_t audio_to_send = time_passed - audio_sent;
+
+                        /* Never try to catch up for more than 100ms */
+                        if (u->write_index > 0 && audio_to_send > MAX_PLAYBACK_CATCH_UP_USEC) {
+                            pa_usec_t skip_usec;
+                            uint64_t skip_bytes;
+
+                            skip_usec = audio_to_send - MAX_PLAYBACK_CATCH_UP_USEC;
+                            skip_bytes = pa_usec_to_bytes(skip_usec, &u->sample_spec);
+
+                            if (skip_bytes > 0) {
+                                pa_memchunk tmp;
+
+                                pa_log_warn("Skipping %llu us (= %llu bytes) in audio stream",
+                                            (unsigned long long) skip_usec,
+                                            (unsigned long long) skip_bytes);
+
+                                pa_sink_render_full(u->sink, skip_bytes, &tmp);
+                                pa_memblock_unref(tmp.memblock);
+                                u->write_index += skip_bytes;
+
+                                if (u->profile == PA_BLUEZ4_PROFILE_A2DP_SINK)
+                                    a2dp_reduce_bitpool(u);
+                            }
+                        }
+
+                        do_write = 1;
+                        pending_read_bytes = 0;
+                    }
+                }
+
+                if (writable && do_write > 0) {
+                    int n_written;
+
+                    if (u->write_index <= 0)
+                        u->started_at = pa_rtclock_now();
+
+                    if (u->profile == PA_BLUEZ4_PROFILE_A2DP_SINK) {
+                        if ((n_written = a2dp_process_render(u)) < 0)
+                            goto io_fail;
+                    } else {
+                        if ((n_written = hsp_process_render(u)) < 0)
+                            goto io_fail;
+                    }
+
+                    if (n_written == 0)
+                        pa_log("Broken kernel: we got EAGAIN on write() after POLLOUT!");
+
+                    do_write -= n_written;
+                    writable = false;
+                }
+
+                if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0) {
+                    pa_usec_t sleep_for;
+                    pa_usec_t time_passed, next_write_at;
+
+                    if (writable) {
+                        /* Hmm, there is no input stream we could synchronize
+                         * to. So let's estimate when we need to wake up the latest */
+                        time_passed = pa_rtclock_now() - u->started_at;
+                        next_write_at = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+                        sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0;
+                        /* pa_log("Sleeping for %lu; time passed %lu, next write at %lu", (unsigned long) sleep_for, (unsigned long) time_passed, (unsigned long)next_write_at); */
+                    } else
+                        /* drop stream every 500 ms */
+                        sleep_for = PA_USEC_PER_MSEC * 500;
+
+                    pa_rtpoll_set_timer_relative(u->rtpoll, sleep_for);
+                    disable_timer = false;
+                }
+            }
+        }
+
+        if (disable_timer)
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Hmm, nothing to do. Let's sleep */
+        if (pollfd)
+            pollfd->events = (short) (((u->sink && PA_SINK_IS_LINKED(u->sink->thread_info.state) && !writable) ? POLLOUT : 0) |
+                                      (u->source && PA_SOURCE_IS_LINKED(u->source->thread_info.state) ? POLLIN : 0));
+
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) {
+            pa_log_debug("pa_rtpoll_run failed with: %d", ret);
+            goto fail;
+        }
+        if (ret == 0) {
+            pa_log_debug("IO thread shutdown requested, stopping cleanly");
+            bt_transport_release(u);
+            goto finish;
+        }
+
+        pollfd = u->rtpoll_item ? pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL) : NULL;
+
+        if (pollfd && (pollfd->revents & ~(POLLOUT|POLLIN))) {
+            pa_log_info("FD error: %s%s%s%s",
+                        pollfd->revents & POLLERR ? "POLLERR " :"",
+                        pollfd->revents & POLLHUP ? "POLLHUP " :"",
+                        pollfd->revents & POLLPRI ? "POLLPRI " :"",
+                        pollfd->revents & POLLNVAL ? "POLLNVAL " :"");
+            goto io_fail;
+        }
+
+        continue;
+
+io_fail:
+        /* In case of HUP, just tear down the streams */
+        if (!pollfd || (pollfd->revents & POLLHUP) == 0)
+            goto fail;
+
+        do_write = 0;
+        pending_read_bytes = 0;
+        writable = false;
+
+        teardown_stream(u);
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
+    pa_log_debug("IO thread failed");
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_IO_THREAD_FAILED, NULL, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("IO thread shutting down");
+}
+
+static pa_available_t transport_state_to_availability(pa_bluez4_transport_state_t state) {
+    if (state == PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED)
+        return PA_AVAILABLE_NO;
+    else if (state >= PA_BLUEZ4_TRANSPORT_STATE_PLAYING)
+        return PA_AVAILABLE_YES;
+    else
+        return PA_AVAILABLE_UNKNOWN;
+}
+
+static pa_direction_t get_profile_direction(pa_bluez4_profile_t p) {
+    static const pa_direction_t profile_direction[] = {
+        [PA_BLUEZ4_PROFILE_A2DP_SINK] = PA_DIRECTION_OUTPUT,
+        [PA_BLUEZ4_PROFILE_A2DP_SOURCE] = PA_DIRECTION_INPUT,
+        [PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
+        [PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
+        [PA_BLUEZ4_PROFILE_OFF] = 0
+    };
+
+    return profile_direction[p];
+}
+
+/* Run from main thread */
+static pa_available_t get_port_availability(struct userdata *u, pa_direction_t direction) {
+    pa_available_t result = PA_AVAILABLE_NO;
+    unsigned i;
+
+    pa_assert(u);
+    pa_assert(u->device);
+
+    for (i = 0; i < PA_BLUEZ4_PROFILE_COUNT; i++) {
+        pa_bluez4_transport *transport;
+
+        if (!(get_profile_direction(i) & direction))
+            continue;
+
+        if (!(transport = u->device->transports[i]))
+            continue;
+
+        switch(transport->state) {
+            case PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED:
+                continue;
+
+            case PA_BLUEZ4_TRANSPORT_STATE_IDLE:
+                if (result == PA_AVAILABLE_NO)
+                    result = PA_AVAILABLE_UNKNOWN;
+
+                break;
+
+            case PA_BLUEZ4_TRANSPORT_STATE_PLAYING:
+                return PA_AVAILABLE_YES;
+        }
+    }
+
+    return result;
+}
+
+/* Run from main thread */
+static void handle_transport_state_change(struct userdata *u, struct pa_bluez4_transport *transport) {
+    bool acquire = false;
+    bool release = false;
+    pa_bluez4_profile_t profile;
+    pa_card_profile *cp;
+    pa_bluez4_transport_state_t state;
+    pa_device_port *port;
+
+    pa_assert(u);
+    pa_assert(transport);
+
+    profile = transport->profile;
+    state = transport->state;
+
+    /* Update profile availability */
+    if (!(cp = pa_hashmap_get(u->card->profiles, pa_bluez4_profile_to_string(profile))))
+        return;
+
+    pa_card_profile_set_available(cp, transport_state_to_availability(state));
+
+    /* Update port availability */
+    pa_assert_se(port = pa_hashmap_get(u->card->ports, u->output_port_name));
+    pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_OUTPUT));
+
+    pa_assert_se(port = pa_hashmap_get(u->card->ports, u->input_port_name));
+    pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_INPUT));
+
+    /* Acquire or release transport as needed */
+    acquire = (state == PA_BLUEZ4_TRANSPORT_STATE_PLAYING && u->profile == profile);
+    release = (state != PA_BLUEZ4_TRANSPORT_STATE_PLAYING && u->profile == profile);
+
+    if (acquire)
+        if (bt_transport_acquire(u, true) >= 0) {
+            if (u->source) {
+                pa_log_debug("Resuming source %s, because the bluetooth audio state changed to 'playing'.", u->source->name);
+                pa_source_suspend(u->source, false, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
+            }
+
+            if (u->sink) {
+                pa_log_debug("Resuming sink %s, because the bluetooth audio state changed to 'playing'.", u->sink->name);
+                pa_sink_suspend(u->sink, false, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
+            }
+        }
+
+    if (release && u->transport_acquired) {
+        /* FIXME: this release is racy, since the audio stream might have
+           been set up again in the meantime (but not processed yet by PA).
+           BlueZ should probably release the transport automatically, and
+           in that case we would just mark the transport as released */
+
+        /* Remote side closed the stream so we consider it PA_SUSPEND_USER */
+        if (u->source) {
+            pa_log_debug("Suspending source %s, because the remote end closed the stream.", u->source->name);
+            pa_source_suspend(u->source, true, PA_SUSPEND_USER);
+        }
+
+        if (u->sink) {
+            pa_log_debug("Suspending sink %s, because the remote end closed the stream.", u->sink->name);
+            pa_sink_suspend(u->sink, true, PA_SUSPEND_USER);
+        }
+    }
+}
+
+/* Run from main thread */
+static void sink_set_volume_cb(pa_sink *s) {
+    uint16_t gain;
+    pa_volume_t volume;
+    struct userdata *u;
+    char *k;
+
+    pa_assert(s);
+    pa_assert(s->core);
+
+    k = pa_sprintf_malloc("bluetooth-device@%p", (void*) s);
+    u = pa_shared_get(s->core, k);
+    pa_xfree(k);
+
+    pa_assert(u);
+    pa_assert(u->sink == s);
+    pa_assert(u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT);
+    pa_assert(u->transport);
+
+    gain = (dbus_uint16_t) round((double) pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN / PA_VOLUME_NORM);
+    volume = (pa_volume_t) round((double) gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
+
+    pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
+
+    pa_bluez4_transport_set_speaker_gain(u->transport, gain);
+}
+
+/* Run from main thread */
+static void source_set_volume_cb(pa_source *s) {
+    uint16_t gain;
+    pa_volume_t volume;
+    struct userdata *u;
+    char *k;
+
+    pa_assert(s);
+    pa_assert(s->core);
+
+    k = pa_sprintf_malloc("bluetooth-device@%p", (void*) s);
+    u = pa_shared_get(s->core, k);
+    pa_xfree(k);
+
+    pa_assert(u);
+    pa_assert(u->source == s);
+    pa_assert(u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT);
+    pa_assert(u->transport);
+
+    gain = (dbus_uint16_t) round((double) pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN / PA_VOLUME_NORM);
+    volume = (pa_volume_t) round((double) gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
+
+    pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
+
+    pa_bluez4_transport_set_microphone_gain(u->transport, gain);
+}
+
+/* Run from main thread */
+static char *get_name(const char *type, pa_modargs *ma, const char *device_id, const char *profile, bool *namereg_fail) {
+    char *t;
+    const char *n;
+
+    pa_assert(type);
+    pa_assert(ma);
+    pa_assert(device_id);
+    pa_assert(namereg_fail);
+
+    t = pa_sprintf_malloc("%s_name", type);
+    n = pa_modargs_get_value(ma, t, NULL);
+    pa_xfree(t);
+
+    if (n) {
+        *namereg_fail = true;
+        return pa_xstrdup(n);
+    }
+
+    if ((n = pa_modargs_get_value(ma, "name", NULL)))
+        *namereg_fail = true;
+    else {
+        n = device_id;
+        *namereg_fail = false;
+    }
+
+    if (profile)
+        return pa_sprintf_malloc("bluez_%s.%s.%s", type, n, profile);
+    else
+        return pa_sprintf_malloc("bluez_%s.%s", type, n);
+}
+
+static int sco_over_pcm_state_update(struct userdata *u, bool changed) {
+    pa_assert(u);
+    pa_assert(USE_SCO_OVER_PCM(u));
+
+    if (PA_SINK_IS_OPENED(pa_sink_get_state(u->hsp.sco_sink)) ||
+        PA_SOURCE_IS_OPENED(pa_source_get_state(u->hsp.sco_source))) {
+
+        if (u->stream_fd >= 0)
+            return 0;
+
+        pa_log_debug("Resuming SCO over PCM");
+        if (init_profile(u) < 0) {
+            pa_log("Can't resume SCO over PCM");
+            return -1;
+        }
+
+        if (bt_transport_acquire(u, false) < 0)
+            return -1;
+
+        setup_stream(u);
+
+        return 0;
+    }
+
+    if (changed) {
+        if (u->stream_fd < 0)
+            return 0;
+
+        pa_log_debug("Closing SCO over PCM");
+
+        bt_transport_release(u);
+    }
+
+    return 0;
+}
+
+static pa_hook_result_t sink_state_changed_cb(pa_core *c, pa_sink *s, struct userdata *u) {
+    pa_assert(c);
+    pa_sink_assert_ref(s);
+    pa_assert(u);
+
+    if (!USE_SCO_OVER_PCM(u) || s != u->hsp.sco_sink)
+        return PA_HOOK_OK;
+
+    sco_over_pcm_state_update(u, true);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_state_changed_cb(pa_core *c, pa_source *s, struct userdata *u) {
+    pa_assert(c);
+    pa_source_assert_ref(s);
+    pa_assert(u);
+
+    if (!USE_SCO_OVER_PCM(u) || s != u->hsp.sco_source)
+        return PA_HOOK_OK;
+
+    sco_over_pcm_state_update(u, true);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t transport_nrec_changed_cb(pa_bluez4_discovery *y, pa_bluez4_transport *t, struct userdata *u) {
+    pa_proplist *p;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t != u->transport)
+        return PA_HOOK_OK;
+
+    p = pa_proplist_new();
+    pa_proplist_sets(p, "bluetooth.nrec", t->nrec ? "1" : "0");
+    pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, p);
+    pa_proplist_free(p);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t transport_microphone_gain_changed_cb(pa_bluez4_discovery *y, pa_bluez4_transport *t,
+                                                             struct userdata *u) {
+    pa_cvolume v;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t != u->transport)
+        return PA_HOOK_OK;
+
+    pa_assert(u->source);
+
+    pa_cvolume_set(&v, u->sample_spec.channels,
+                   (pa_volume_t) round((double) t->microphone_gain * PA_VOLUME_NORM / HSP_MAX_GAIN));
+    pa_source_volume_changed(u->source, &v);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t transport_speaker_gain_changed_cb(pa_bluez4_discovery *y, pa_bluez4_transport *t,
+                                                          struct userdata *u) {
+    pa_cvolume v;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t != u->transport)
+        return PA_HOOK_OK;
+
+    pa_assert(u->sink);
+
+    pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) round((double) t->speaker_gain * PA_VOLUME_NORM / HSP_MAX_GAIN));
+    pa_sink_volume_changed(u->sink, &v);
+
+    return PA_HOOK_OK;
+}
+
+static void connect_ports(struct userdata *u, void *sink_or_source_new_data, pa_direction_t direction) {
+    pa_device_port *port;
+
+    if (direction == PA_DIRECTION_OUTPUT) {
+        pa_sink_new_data *sink_new_data = sink_or_source_new_data;
+
+        pa_assert_se(port = pa_hashmap_get(u->card->ports, u->output_port_name));
+        pa_assert_se(pa_hashmap_put(sink_new_data->ports, port->name, port) >= 0);
+        pa_device_port_ref(port);
+    } else {
+        pa_source_new_data *source_new_data = sink_or_source_new_data;
+
+        pa_assert_se(port = pa_hashmap_get(u->card->ports, u->input_port_name));
+        pa_assert_se(pa_hashmap_put(source_new_data->ports, port->name, port) >= 0);
+        pa_device_port_ref(port);
+    }
+}
+
+static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
+    return 0;
+}
+
+static int source_set_port_cb(pa_source *s, pa_device_port *p) {
+    return 0;
+}
+
+/* Run from main thread */
+static int add_sink(struct userdata *u) {
+    char *k;
+
+    pa_assert(u->transport);
+
+    if (USE_SCO_OVER_PCM(u)) {
+        pa_proplist *p;
+
+        u->sink = u->hsp.sco_sink;
+        p = pa_proplist_new();
+        pa_proplist_sets(p, "bluetooth.protocol", pa_bluez4_profile_to_string(u->profile));
+        pa_proplist_update(u->sink->proplist, PA_UPDATE_MERGE, p);
+        pa_proplist_free(p);
+    } else {
+        pa_sink_new_data data;
+        bool b;
+
+        pa_sink_new_data_init(&data);
+        data.driver = __FILE__;
+        data.module = u->module;
+        pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
+        pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluez4_profile_to_string(u->profile));
+        if (u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT)
+            pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
+        data.card = u->card;
+        data.name = get_name("sink", u->modargs, u->address, pa_bluez4_profile_to_string(u->profile), &b);
+        data.namereg_fail = b;
+
+        if (pa_modargs_get_proplist(u->modargs, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+            pa_log("Invalid properties");
+            pa_sink_new_data_done(&data);
+            return -1;
+        }
+        connect_ports(u, &data, PA_DIRECTION_OUTPUT);
+
+        if (!u->transport_acquired)
+            switch (u->profile) {
+                case PA_BLUEZ4_PROFILE_A2DP_SINK:
+                case PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT:
+                    pa_assert_not_reached(); /* Profile switch should have failed */
+                    break;
+                case PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY:
+                    data.suspend_cause = PA_SUSPEND_USER;
+                    break;
+                case PA_BLUEZ4_PROFILE_A2DP_SOURCE:
+                case PA_BLUEZ4_PROFILE_OFF:
+                    pa_assert_not_reached();
+            }
+
+        u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+        pa_sink_new_data_done(&data);
+
+        if (!u->sink) {
+            pa_log_error("Failed to create sink");
+            return -1;
+        }
+
+        u->sink->userdata = u;
+        u->sink->parent.process_msg = sink_process_msg;
+        u->sink->set_port = sink_set_port_cb;
+    }
+
+    if (u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT) {
+        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
+        u->sink->n_volume_steps = 16;
+
+        k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->sink);
+        pa_shared_set(u->core, k, u);
+        pa_xfree(k);
+    }
+
+    return 0;
+}
+
+/* Run from main thread */
+static int add_source(struct userdata *u) {
+    char *k;
+
+    pa_assert(u->transport);
+
+    if (USE_SCO_OVER_PCM(u)) {
+        u->source = u->hsp.sco_source;
+        pa_proplist_sets(u->source->proplist, "bluetooth.protocol", pa_bluez4_profile_to_string(u->profile));
+    } else {
+        pa_source_new_data data;
+        bool b;
+
+        pa_source_new_data_init(&data);
+        data.driver = __FILE__;
+        data.module = u->module;
+        pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
+        pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluez4_profile_to_string(u->profile));
+        if (u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT)
+            pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
+
+        data.card = u->card;
+        data.name = get_name("source", u->modargs, u->address, pa_bluez4_profile_to_string(u->profile), &b);
+        data.namereg_fail = b;
+
+        if (pa_modargs_get_proplist(u->modargs, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+            pa_log("Invalid properties");
+            pa_source_new_data_done(&data);
+            return -1;
+        }
+
+        connect_ports(u, &data, PA_DIRECTION_INPUT);
+
+        if (!u->transport_acquired)
+            switch (u->profile) {
+                case PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT:
+                    pa_assert_not_reached(); /* Profile switch should have failed */
+                    break;
+                case PA_BLUEZ4_PROFILE_A2DP_SOURCE:
+                case PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY:
+                    data.suspend_cause = PA_SUSPEND_USER;
+                    break;
+                case PA_BLUEZ4_PROFILE_A2DP_SINK:
+                case PA_BLUEZ4_PROFILE_OFF:
+                    pa_assert_not_reached();
+            }
+
+        u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+        pa_source_new_data_done(&data);
+
+        if (!u->source) {
+            pa_log_error("Failed to create source");
+            return -1;
+        }
+
+        u->source->userdata = u;
+        u->source->parent.process_msg = source_process_msg;
+        u->source->set_port = source_set_port_cb;
+    }
+
+    if ((u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT) || (u->profile == PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY)) {
+        pa_bluez4_transport *t = u->transport;
+        pa_proplist_sets(u->source->proplist, "bluetooth.nrec", t->nrec ? "1" : "0");
+    }
+
+    if (u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT) {
+        pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
+        u->source->n_volume_steps = 16;
+
+        k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->source);
+        pa_shared_set(u->core, k, u);
+        pa_xfree(k);
+    }
+
+    return 0;
+}
+
+static void bt_transport_config_a2dp(struct userdata *u) {
+    const pa_bluez4_transport *t;
+    struct a2dp_info *a2dp = &u->a2dp;
+    a2dp_sbc_t *config;
+
+    t = u->transport;
+    pa_assert(t);
+
+    config = (a2dp_sbc_t *) t->config;
+
+    u->sample_spec.format = PA_SAMPLE_S16LE;
+
+    if (a2dp->sbc_initialized)
+        sbc_reinit(&a2dp->sbc, 0);
+    else
+        sbc_init(&a2dp->sbc, 0);
+    a2dp->sbc_initialized = true;
+
+    switch (config->frequency) {
+        case SBC_SAMPLING_FREQ_16000:
+            a2dp->sbc.frequency = SBC_FREQ_16000;
+            u->sample_spec.rate = 16000U;
+            break;
+        case SBC_SAMPLING_FREQ_32000:
+            a2dp->sbc.frequency = SBC_FREQ_32000;
+            u->sample_spec.rate = 32000U;
+            break;
+        case SBC_SAMPLING_FREQ_44100:
+            a2dp->sbc.frequency = SBC_FREQ_44100;
+            u->sample_spec.rate = 44100U;
+            break;
+        case SBC_SAMPLING_FREQ_48000:
+            a2dp->sbc.frequency = SBC_FREQ_48000;
+            u->sample_spec.rate = 48000U;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    switch (config->channel_mode) {
+        case SBC_CHANNEL_MODE_MONO:
+            a2dp->sbc.mode = SBC_MODE_MONO;
+            u->sample_spec.channels = 1;
+            break;
+        case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+            a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
+            u->sample_spec.channels = 2;
+            break;
+        case SBC_CHANNEL_MODE_STEREO:
+            a2dp->sbc.mode = SBC_MODE_STEREO;
+            u->sample_spec.channels = 2;
+            break;
+        case SBC_CHANNEL_MODE_JOINT_STEREO:
+            a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
+            u->sample_spec.channels = 2;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    switch (config->allocation_method) {
+        case SBC_ALLOCATION_SNR:
+            a2dp->sbc.allocation = SBC_AM_SNR;
+            break;
+        case SBC_ALLOCATION_LOUDNESS:
+            a2dp->sbc.allocation = SBC_AM_LOUDNESS;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    switch (config->subbands) {
+        case SBC_SUBBANDS_4:
+            a2dp->sbc.subbands = SBC_SB_4;
+            break;
+        case SBC_SUBBANDS_8:
+            a2dp->sbc.subbands = SBC_SB_8;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    switch (config->block_length) {
+        case SBC_BLOCK_LENGTH_4:
+            a2dp->sbc.blocks = SBC_BLK_4;
+            break;
+        case SBC_BLOCK_LENGTH_8:
+            a2dp->sbc.blocks = SBC_BLK_8;
+            break;
+        case SBC_BLOCK_LENGTH_12:
+            a2dp->sbc.blocks = SBC_BLK_12;
+            break;
+        case SBC_BLOCK_LENGTH_16:
+            a2dp->sbc.blocks = SBC_BLK_16;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    a2dp->min_bitpool = config->min_bitpool;
+    a2dp->max_bitpool = config->max_bitpool;
+
+    /* Set minimum bitpool for source to get the maximum possible block_size */
+    a2dp->sbc.bitpool = u->profile == PA_BLUEZ4_PROFILE_A2DP_SINK ? a2dp->max_bitpool : a2dp->min_bitpool;
+    a2dp->codesize = sbc_get_codesize(&a2dp->sbc);
+    a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc);
+
+    pa_log_info("SBC parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u",
+                a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, a2dp->sbc.bitpool);
+}
+
+static void bt_transport_config(struct userdata *u) {
+    if (u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY) {
+        u->sample_spec.format = PA_SAMPLE_S16LE;
+        u->sample_spec.channels = 1;
+        u->sample_spec.rate = 8000;
+    } else
+        bt_transport_config_a2dp(u);
+}
+
+/* Run from main thread */
+static pa_hook_result_t transport_state_changed_cb(pa_bluez4_discovery *y, pa_bluez4_transport *t, struct userdata *u) {
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t == u->transport && t->state == PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED)
+        pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
+
+    if (t->device == u->device)
+        handle_transport_state_change(u, t);
+
+    return PA_HOOK_OK;
+}
+
+/* Run from main thread */
+static int setup_transport(struct userdata *u) {
+    pa_bluez4_transport *t;
+
+    pa_assert(u);
+    pa_assert(!u->transport);
+    pa_assert(u->profile != PA_BLUEZ4_PROFILE_OFF);
+
+    /* check if profile has a transport */
+    t = u->device->transports[u->profile];
+    if (!t || t->state == PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED) {
+        pa_log_warn("Profile has no transport");
+        return -1;
+    }
+
+    u->transport = t;
+
+    if (u->profile == PA_BLUEZ4_PROFILE_A2DP_SOURCE || u->profile == PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY)
+        bt_transport_acquire(u, true); /* In case of error, the sink/sources will be created suspended */
+    else if (bt_transport_acquire(u, false) < 0)
+        return -1; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */
+
+    bt_transport_config(u);
+
+    return 0;
+}
+
+/* Run from main thread */
+static int init_profile(struct userdata *u) {
+    int r = 0;
+    pa_assert(u);
+    pa_assert(u->profile != PA_BLUEZ4_PROFILE_OFF);
+
+    if (setup_transport(u) < 0)
+        return -1;
+
+    pa_assert(u->transport);
+
+    if (u->profile == PA_BLUEZ4_PROFILE_A2DP_SINK ||
+        u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT ||
+        u->profile == PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY)
+        if (add_sink(u) < 0)
+            r = -1;
+
+    if (u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT ||
+        u->profile == PA_BLUEZ4_PROFILE_A2DP_SOURCE ||
+        u->profile == PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY)
+        if (add_source(u) < 0)
+            r = -1;
+
+    return r;
+}
+
+/* Run from main thread */
+static void stop_thread(struct userdata *u) {
+    char *k;
+
+    pa_assert(u);
+
+    if (u->sink && !USE_SCO_OVER_PCM(u))
+        pa_sink_unlink(u->sink);
+
+    if (u->source && !USE_SCO_OVER_PCM(u))
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+        u->thread = NULL;
+    }
+
+    if (u->rtpoll_item) {
+        pa_rtpoll_item_free(u->rtpoll_item);
+        u->rtpoll_item = NULL;
+    }
+
+    if (u->rtpoll) {
+        pa_thread_mq_done(&u->thread_mq);
+
+        pa_rtpoll_free(u->rtpoll);
+        u->rtpoll = NULL;
+    }
+
+    if (u->transport) {
+        bt_transport_release(u);
+        u->transport = NULL;
+    }
+
+    if (u->sink) {
+        if (u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT) {
+            k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->sink);
+            pa_shared_remove(u->core, k);
+            pa_xfree(k);
+        }
+
+        pa_sink_unref(u->sink);
+        u->sink = NULL;
+    }
+
+    if (u->source) {
+        if (u->profile == PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT) {
+            k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->source);
+            pa_shared_remove(u->core, k);
+            pa_xfree(k);
+        }
+
+        pa_source_unref(u->source);
+        u->source = NULL;
+    }
+
+    if (u->read_smoother) {
+        pa_smoother_free(u->read_smoother);
+        u->read_smoother = NULL;
+    }
+}
+
+/* Run from main thread */
+static int start_thread(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(!u->thread);
+    pa_assert(!u->rtpoll);
+    pa_assert(!u->rtpoll_item);
+
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        return -1;
+    }
+
+    if (USE_SCO_OVER_PCM(u)) {
+        if (sco_over_pcm_state_update(u, false) < 0) {
+            char *k;
+
+            if (u->sink) {
+                k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->sink);
+                pa_shared_remove(u->core, k);
+                pa_xfree(k);
+                u->sink = NULL;
+            }
+            if (u->source) {
+                k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->source);
+                pa_shared_remove(u->core, k);
+                pa_xfree(k);
+                u->source = NULL;
+            }
+            return -1;
+        }
+
+        pa_sink_ref(u->sink);
+        pa_source_ref(u->source);
+        /* FIXME: monitor stream_fd error */
+        return 0;
+    }
+
+    if (!(u->thread = pa_thread_new("bluetooth", thread_func, u))) {
+        pa_log_error("Failed to create IO thread");
+        return -1;
+    }
+
+    if (u->sink) {
+        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+        pa_sink_set_rtpoll(u->sink, u->rtpoll);
+        pa_sink_put(u->sink);
+
+        if (u->sink->set_volume)
+            u->sink->set_volume(u->sink);
+    }
+
+    if (u->source) {
+        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+        pa_source_set_rtpoll(u->source, u->rtpoll);
+        pa_source_put(u->source);
+
+        if (u->source->set_volume)
+            u->source->set_volume(u->source);
+    }
+
+    return 0;
+}
+
+static void save_sco_volume_callbacks(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(USE_SCO_OVER_PCM(u));
+
+    u->hsp.sco_sink_set_volume = u->hsp.sco_sink->set_volume;
+    u->hsp.sco_source_set_volume = u->hsp.sco_source->set_volume;
+}
+
+static void restore_sco_volume_callbacks(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(USE_SCO_OVER_PCM(u));
+
+    pa_sink_set_set_volume_callback(u->hsp.sco_sink, u->hsp.sco_sink_set_volume);
+    pa_source_set_set_volume_callback(u->hsp.sco_source, u->hsp.sco_source_set_volume);
+}
+
+/* Run from main thread */
+static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
+    struct userdata *u;
+    pa_bluez4_profile_t *d;
+
+    pa_assert(c);
+    pa_assert(new_profile);
+    pa_assert_se(u = c->userdata);
+
+    d = PA_CARD_PROFILE_DATA(new_profile);
+
+    if (*d != PA_BLUEZ4_PROFILE_OFF) {
+        const pa_bluez4_device *device = u->device;
+
+        if (!device->transports[*d] || device->transports[*d]->state == PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED) {
+            pa_log_warn("Profile not connected, refused to switch profile to %s", new_profile->name);
+            return -PA_ERR_IO;
+        }
+    }
+
+    stop_thread(u);
+
+    if (USE_SCO_OVER_PCM(u))
+        restore_sco_volume_callbacks(u);
+
+    u->profile = *d;
+    u->sample_spec = u->requested_sample_spec;
+
+    if (USE_SCO_OVER_PCM(u))
+        save_sco_volume_callbacks(u);
+
+    if (u->profile != PA_BLUEZ4_PROFILE_OFF)
+        if (init_profile(u) < 0)
+            goto off;
+
+    if (u->sink || u->source)
+        if (start_thread(u) < 0)
+            goto off;
+
+    return 0;
+
+off:
+    stop_thread(u);
+
+    pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
+
+    return -PA_ERR_IO;
+}
+
+/* Run from main thread */
+static void create_card_ports(struct userdata *u, pa_hashmap *ports) {
+    pa_device_port *port;
+    pa_device_port_new_data port_data;
+
+    const char *name_prefix = NULL;
+    const char *input_description = NULL;
+    const char *output_description = NULL;
+
+    pa_assert(u);
+    pa_assert(ports);
+    pa_assert(u->device);
+
+    switch (pa_bluez4_get_form_factor(u->device->class)) {
+        case PA_BLUEZ4_FORM_FACTOR_UNKNOWN:
+            break;
+
+        case PA_BLUEZ4_FORM_FACTOR_HEADSET:
+            name_prefix = "headset";
+            input_description = output_description = _("Headset");
+            break;
+
+        case PA_BLUEZ4_FORM_FACTOR_HANDSFREE:
+            name_prefix = "handsfree";
+            input_description = output_description = _("Handsfree");
+            break;
+
+        case PA_BLUEZ4_FORM_FACTOR_MICROPHONE:
+            name_prefix = "microphone";
+            input_description = _("Microphone");
+            break;
+
+        case PA_BLUEZ4_FORM_FACTOR_SPEAKER:
+            name_prefix = "speaker";
+            output_description = _("Speaker");
+            break;
+
+        case PA_BLUEZ4_FORM_FACTOR_HEADPHONE:
+            name_prefix = "headphone";
+            output_description = _("Headphone");
+            break;
+
+        case PA_BLUEZ4_FORM_FACTOR_PORTABLE:
+            name_prefix = "portable";
+            input_description = output_description = _("Portable");
+            break;
+
+        case PA_BLUEZ4_FORM_FACTOR_CAR:
+            name_prefix = "car";
+            input_description = output_description = _("Car");
+            break;
+
+        case PA_BLUEZ4_FORM_FACTOR_HIFI:
+            name_prefix = "hifi";
+            input_description = output_description = _("HiFi");
+            break;
+
+        case PA_BLUEZ4_FORM_FACTOR_PHONE:
+            name_prefix = "phone";
+            input_description = output_description = _("Phone");
+            break;
+    }
+
+    if (!name_prefix)
+        name_prefix = "unknown";
+
+    if (!output_description)
+        output_description = _("Bluetooth Output");
+
+    if (!input_description)
+        input_description = _("Bluetooth Input");
+
+    u->output_port_name = pa_sprintf_malloc("%s-output", name_prefix);
+    u->input_port_name = pa_sprintf_malloc("%s-input", name_prefix);
+
+    pa_device_port_new_data_init(&port_data);
+    pa_device_port_new_data_set_name(&port_data, u->output_port_name);
+    pa_device_port_new_data_set_description(&port_data, output_description);
+    pa_device_port_new_data_set_direction(&port_data, PA_DIRECTION_OUTPUT);
+    pa_device_port_new_data_set_available(&port_data, get_port_availability(u, PA_DIRECTION_OUTPUT));
+    pa_assert_se(port = pa_device_port_new(u->core, &port_data, 0));
+    pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0);
+    pa_device_port_new_data_done(&port_data);
+
+    pa_device_port_new_data_init(&port_data);
+    pa_device_port_new_data_set_name(&port_data, u->input_port_name);
+    pa_device_port_new_data_set_description(&port_data, input_description);
+    pa_device_port_new_data_set_direction(&port_data, PA_DIRECTION_INPUT);
+    pa_device_port_new_data_set_available(&port_data, get_port_availability(u, PA_DIRECTION_INPUT));
+    pa_assert_se(port = pa_device_port_new(u->core, &port_data, 0));
+    pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0);
+    pa_device_port_new_data_done(&port_data);
+}
+
+/* Run from main thread */
+static pa_card_profile *create_card_profile(struct userdata *u, pa_bluez4_profile_t profile, pa_hashmap *ports) {
+    pa_device_port *input_port, *output_port;
+    const char *name;
+    pa_card_profile *p = NULL;
+    pa_bluez4_profile_t *d = NULL;
+    pa_bluez4_transport *t;
+
+    pa_assert(u->input_port_name);
+    pa_assert(u->output_port_name);
+    pa_assert_se(input_port = pa_hashmap_get(ports, u->input_port_name));
+    pa_assert_se(output_port = pa_hashmap_get(ports, u->output_port_name));
+
+    name = pa_bluez4_profile_to_string(profile);
+
+    switch (profile) {
+    case PA_BLUEZ4_PROFILE_A2DP_SINK:
+        p = pa_card_profile_new(name, _("High Fidelity Playback (A2DP)"), sizeof(pa_bluez4_profile_t));
+        p->priority = 10;
+        p->n_sinks = 1;
+        p->n_sources = 0;
+        p->max_sink_channels = 2;
+        p->max_source_channels = 0;
+        pa_hashmap_put(output_port->profiles, p->name, p);
+
+        d = PA_CARD_PROFILE_DATA(p);
+        break;
+
+    case PA_BLUEZ4_PROFILE_A2DP_SOURCE:
+        p = pa_card_profile_new(name, _("High Fidelity Capture (A2DP)"), sizeof(pa_bluez4_profile_t));
+        p->priority = 10;
+        p->n_sinks = 0;
+        p->n_sources = 1;
+        p->max_sink_channels = 0;
+        p->max_source_channels = 2;
+        pa_hashmap_put(input_port->profiles, p->name, p);
+
+        d = PA_CARD_PROFILE_DATA(p);
+        break;
+
+    case PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT:
+        p = pa_card_profile_new(name, _("Telephony Duplex (HSP/HFP)"), sizeof(pa_bluez4_profile_t));
+        p->priority = 20;
+        p->n_sinks = 1;
+        p->n_sources = 1;
+        p->max_sink_channels = 1;
+        p->max_source_channels = 1;
+        pa_hashmap_put(input_port->profiles, p->name, p);
+        pa_hashmap_put(output_port->profiles, p->name, p);
+
+        d = PA_CARD_PROFILE_DATA(p);
+        break;
+
+    case PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY:
+        p = pa_card_profile_new(name, _("Handsfree Gateway"), sizeof(pa_bluez4_profile_t));
+        p->priority = 20;
+        p->n_sinks = 1;
+        p->n_sources = 1;
+        p->max_sink_channels = 1;
+        p->max_source_channels = 1;
+        pa_hashmap_put(input_port->profiles, p->name, p);
+        pa_hashmap_put(output_port->profiles, p->name, p);
+
+        d = PA_CARD_PROFILE_DATA(p);
+        break;
+
+    case PA_BLUEZ4_PROFILE_OFF:
+        pa_assert_not_reached();
+    }
+
+    *d = profile;
+
+    if ((t = u->device->transports[*d]))
+        p->available = transport_state_to_availability(t->state);
+
+    return p;
+}
+
+static int uuid_to_profile(const char *uuid, pa_bluez4_profile_t *_r) {
+    if (pa_streq(uuid, PA_BLUEZ4_UUID_A2DP_SINK))
+        *_r = PA_BLUEZ4_PROFILE_A2DP_SINK;
+    else if (pa_streq(uuid, PA_BLUEZ4_UUID_A2DP_SOURCE))
+        *_r = PA_BLUEZ4_PROFILE_A2DP_SOURCE;
+    else if (pa_streq(uuid, PA_BLUEZ4_UUID_HSP_HS) || pa_streq(uuid, PA_BLUEZ4_UUID_HFP_HF))
+        *_r = PA_BLUEZ4_PROFILE_HEADSET_HEAD_UNIT;
+    else if (pa_streq(uuid, PA_BLUEZ4_UUID_HSP_AG) || pa_streq(uuid, PA_BLUEZ4_UUID_HFP_AG))
+        *_r = PA_BLUEZ4_PROFILE_HEADSET_AUDIO_GATEWAY;
+    else
+        return -PA_ERR_INVALID;
+
+    return 0;
+}
+
+/* Run from main thread */
+static int add_card(struct userdata *u) {
+    pa_card_new_data data;
+    bool b;
+    pa_card_profile *p;
+    pa_bluez4_profile_t *d;
+    pa_bluez4_form_factor_t ff;
+    char *n;
+    const char *profile_str;
+    const pa_bluez4_device *device;
+    const char *uuid;
+    void *state;
+
+    pa_assert(u);
+    pa_assert(u->device);
+
+    device = u->device;
+
+    pa_card_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = u->module;
+
+    n = pa_utf8_filter(device->alias);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, n);
+    pa_xfree(n);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, device->address);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "bluez");
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_BUS, "bluetooth");
+
+    if ((ff = pa_bluez4_get_form_factor(device->class)) != PA_BLUEZ4_FORM_FACTOR_UNKNOWN)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, pa_bluez4_form_factor_to_string(ff));
+
+    pa_proplist_sets(data.proplist, "bluez.path", device->path);
+    pa_proplist_setf(data.proplist, "bluez.class", "0x%06x", (unsigned) device->class);
+    pa_proplist_sets(data.proplist, "bluez.alias", device->alias);
+    data.name = get_name("card", u->modargs, device->address, NULL, &b);
+    data.namereg_fail = b;
+
+    if (pa_modargs_get_proplist(u->modargs, "card_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_card_new_data_done(&data);
+        return -1;
+    }
+
+    create_card_ports(u, data.ports);
+
+    PA_HASHMAP_FOREACH(uuid, device->uuids, state) {
+        pa_bluez4_profile_t profile;
+
+        if (uuid_to_profile(uuid, &profile) < 0)
+            continue;
+
+        if (pa_hashmap_get(data.profiles, pa_bluez4_profile_to_string(profile)))
+            continue;
+
+        p = create_card_profile(u, profile, data.ports);
+        pa_hashmap_put(data.profiles, p->name, p);
+    }
+
+    pa_assert(!pa_hashmap_isempty(data.profiles));
+
+    p = pa_card_profile_new("off", _("Off"), sizeof(pa_bluez4_profile_t));
+    p->available = PA_AVAILABLE_YES;
+    d = PA_CARD_PROFILE_DATA(p);
+    *d = PA_BLUEZ4_PROFILE_OFF;
+    pa_hashmap_put(data.profiles, p->name, p);
+
+    u->card = pa_card_new(u->core, &data);
+    pa_card_new_data_done(&data);
+
+    if (!u->card) {
+        pa_log("Failed to allocate card.");
+        return -1;
+    }
+
+    u->card->userdata = u;
+    u->card->set_profile = card_set_profile;
+
+    pa_card_choose_initial_profile(u->card);
+
+    /* If the "profile" modarg is given, we have to override whatever the usual
+     * policy chose in pa_card_choose_initial_profile(). */
+    profile_str = pa_modargs_get_value(u->modargs, "profile", NULL);
+    if (profile_str) {
+        pa_card_profile *profile;
+
+        profile = pa_hashmap_get(u->card->profiles, profile_str);
+        if (!profile) {
+            pa_log("No such profile: %s", profile_str);
+            return -1;
+        }
+
+        pa_card_set_profile(u->card, profile, false);
+    }
+
+    pa_card_put(u->card);
+
+    d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+
+    if (*d != PA_BLUEZ4_PROFILE_OFF && (!device->transports[*d] ||
+                              device->transports[*d]->state == PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED)) {
+        pa_log_warn("Default profile not connected, selecting off profile");
+        u->card->active_profile = pa_hashmap_get(u->card->profiles, "off");
+        u->card->save_profile = false;
+    }
+
+    d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+    u->profile = *d;
+
+    if (USE_SCO_OVER_PCM(u))
+        save_sco_volume_callbacks(u);
+
+    return 0;
+}
+
+/* Run from main thread */
+static pa_bluez4_device* find_device(struct userdata *u, const char *address, const char *path) {
+    pa_bluez4_device *d = NULL;
+
+    pa_assert(u);
+
+    if (!address && !path) {
+        pa_log_error("Failed to get device address/path from module arguments.");
+        return NULL;
+    }
+
+    if (path) {
+        if (!(d = pa_bluez4_discovery_get_by_path(u->discovery, path))) {
+            pa_log_error("%s is not a valid BlueZ audio device.", path);
+            return NULL;
+        }
+
+        if (address && !(pa_streq(d->address, address))) {
+            pa_log_error("Passed path %s address %s != %s don't match.", path, d->address, address);
+            return NULL;
+        }
+
+    } else {
+        if (!(d = pa_bluez4_discovery_get_by_address(u->discovery, address))) {
+            pa_log_error("%s is not known.", address);
+            return NULL;
+        }
+    }
+
+    if (d) {
+        u->address = pa_xstrdup(d->address);
+        u->path = pa_xstrdup(d->path);
+    }
+
+    return d;
+}
+
+/* Run from main thread */
+static pa_hook_result_t uuid_added_cb(pa_bluez4_discovery *y, const struct pa_bluez4_hook_uuid_data *data,
+                                      struct userdata *u) {
+    pa_bluez4_profile_t profile;
+    pa_card_profile *p;
+
+    pa_assert(data);
+    pa_assert(data->device);
+    pa_assert(data->uuid);
+    pa_assert(u);
+
+    if (data->device != u->device)
+        return PA_HOOK_OK;
+
+    if (uuid_to_profile(data->uuid, &profile) < 0)
+        return PA_HOOK_OK;
+
+    if (pa_hashmap_get(u->card->profiles, pa_bluez4_profile_to_string(profile)))
+        return PA_HOOK_OK;
+
+    p = create_card_profile(u, profile, u->card->ports);
+    pa_card_add_profile(u->card, p);
+
+    return PA_HOOK_OK;
+}
+
+/* Run from main thread */
+static pa_hook_result_t discovery_hook_cb(pa_bluez4_discovery *y, const pa_bluez4_device *d, struct userdata *u) {
+    pa_assert(u);
+    pa_assert(d);
+
+    if (d != u->device)
+        return PA_HOOK_OK;
+
+    if (d->dead)
+        pa_log_debug("Device %s removed: unloading module", d->path);
+    else if (!pa_bluez4_device_any_audio_connected(d))
+        pa_log_debug("Unloading module, because device %s doesn't have any audio profiles connected anymore.", d->path);
+    else
+        return PA_HOOK_OK;
+
+    pa_module_unload(u->module, true);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module *m) {
+    pa_modargs *ma;
+    uint32_t channels;
+    struct userdata *u;
+    const char *address, *path;
+    pa_bluez4_device *device;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log_error("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    u->core = m->core;
+    u->stream_fd = -1;
+    u->sample_spec = m->core->default_sample_spec;
+    u->modargs = ma;
+
+    if (pa_modargs_get_value(ma, "sco_sink", NULL) &&
+        !(u->hsp.sco_sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_sink", NULL), PA_NAMEREG_SINK))) {
+        pa_log("SCO sink not found");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value(ma, "sco_source", NULL) &&
+        !(u->hsp.sco_source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_source", NULL), PA_NAMEREG_SOURCE))) {
+        pa_log("SCO source not found");
+        goto fail;
+    }
+
+    if (pa_modargs_get_sample_rate(ma, &u->sample_spec.rate) < 0) {
+        pa_log_error("Failed to get rate from module arguments");
+        goto fail;
+    }
+
+    channels = u->sample_spec.channels;
+    if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 ||
+        !pa_channels_valid(channels)) {
+        pa_log_error("Failed to get channels from module arguments");
+        goto fail;
+    }
+    u->sample_spec.channels = (uint8_t) channels;
+    u->requested_sample_spec = u->sample_spec;
+
+    address = pa_modargs_get_value(ma, "address", NULL);
+    path = pa_modargs_get_value(ma, "path", NULL);
+
+    if (!(u->discovery = pa_bluez4_discovery_get(m->core)))
+        goto fail;
+
+    if (!(device = find_device(u, address, path)))
+        goto fail;
+
+    u->device = device;
+
+    u->discovery_slot =
+        pa_hook_connect(pa_bluez4_discovery_hook(u->discovery, PA_BLUEZ4_HOOK_DEVICE_CONNECTION_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) discovery_hook_cb, u);
+
+    u->uuid_added_slot =
+        pa_hook_connect(pa_bluez4_discovery_hook(u->discovery, PA_BLUEZ4_HOOK_DEVICE_UUID_ADDED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) uuid_added_cb, u);
+
+    u->sink_state_changed_slot =
+        pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED],
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_cb, u);
+
+    u->source_state_changed_slot =
+        pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED],
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) source_state_changed_cb, u);
+
+    u->transport_state_changed_slot =
+        pa_hook_connect(pa_bluez4_discovery_hook(u->discovery, PA_BLUEZ4_HOOK_TRANSPORT_STATE_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) transport_state_changed_cb, u);
+
+    u->transport_nrec_changed_slot =
+        pa_hook_connect(pa_bluez4_discovery_hook(u->discovery, PA_BLUEZ4_HOOK_TRANSPORT_NREC_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) transport_nrec_changed_cb, u);
+
+    u->transport_microphone_changed_slot =
+        pa_hook_connect(pa_bluez4_discovery_hook(u->discovery, PA_BLUEZ4_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) transport_microphone_gain_changed_cb, u);
+
+    u->transport_speaker_changed_slot =
+        pa_hook_connect(pa_bluez4_discovery_hook(u->discovery, PA_BLUEZ4_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) transport_speaker_gain_changed_cb, u);
+
+    /* Add the card structure. This will also initialize the default profile */
+    if (add_card(u) < 0)
+        goto fail;
+
+    if (!(u->msg = pa_msgobject_new(bluetooth_msg)))
+        goto fail;
+
+    u->msg->parent.process_msg = device_process_msg;
+    u->msg->card = u->card;
+
+    if (u->profile != PA_BLUEZ4_PROFILE_OFF)
+        if (init_profile(u) < 0)
+            goto off;
+
+    if (u->sink || u->source)
+        if (start_thread(u) < 0)
+            goto off;
+
+    return 0;
+
+off:
+    stop_thread(u);
+
+    pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
+
+    return 0;
+
+fail:
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return
+        (u->sink ? pa_sink_linked_by(u->sink) : 0) +
+        (u->source ? pa_source_linked_by(u->source) : 0);
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    stop_thread(u);
+
+    if (u->discovery_slot)
+        pa_hook_slot_free(u->discovery_slot);
+
+    if (u->uuid_added_slot)
+        pa_hook_slot_free(u->uuid_added_slot);
+
+    if (u->sink_state_changed_slot)
+        pa_hook_slot_free(u->sink_state_changed_slot);
+
+    if (u->source_state_changed_slot)
+        pa_hook_slot_free(u->source_state_changed_slot);
+
+    if (u->transport_state_changed_slot)
+        pa_hook_slot_free(u->transport_state_changed_slot);
+
+    if (u->transport_nrec_changed_slot)
+        pa_hook_slot_free(u->transport_nrec_changed_slot);
+
+    if (u->transport_microphone_changed_slot)
+        pa_hook_slot_free(u->transport_microphone_changed_slot);
+
+    if (u->transport_speaker_changed_slot)
+        pa_hook_slot_free(u->transport_speaker_changed_slot);
+
+    if (USE_SCO_OVER_PCM(u))
+        restore_sco_volume_callbacks(u);
+
+    if (u->msg)
+        pa_xfree(u->msg);
+
+    if (u->card)
+        pa_card_free(u->card);
+
+    if (u->a2dp.buffer)
+        pa_xfree(u->a2dp.buffer);
+
+    sbc_finish(&u->a2dp.sbc);
+
+    if (u->modargs)
+        pa_modargs_free(u->modargs);
+
+    pa_xfree(u->output_port_name);
+    pa_xfree(u->input_port_name);
+
+    pa_xfree(u->address);
+    pa_xfree(u->path);
+
+    if (u->discovery)
+        pa_bluez4_discovery_unref(u->discovery);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/bluetooth/module-bluez4-discover.c b/src/modules/bluetooth/module-bluez4-discover.c
new file mode 100644 (file)
index 0000000..cac7510
--- /dev/null
@@ -0,0 +1,188 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008-2013 João Paulo Rechi Vita
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-shared.h>
+
+#include "module-bluez4-discover-symdef.h"
+#include "bluez4-util.h"
+
+PA_MODULE_AUTHOR("João Paulo Rechi Vita");
+PA_MODULE_DESCRIPTION("Detect available BlueZ 4 Bluetooth audio devices and load BlueZ 4 Bluetooth audio drivers");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_USAGE("sco_sink=<name of sink> "
+                "sco_source=<name of source> ");
+PA_MODULE_LOAD_ONCE(true);
+
+static const char* const valid_modargs[] = {
+    "sco_sink",
+    "sco_source",
+    "async", /* deprecated */
+    NULL
+};
+
+struct userdata {
+    pa_module *module;
+    pa_modargs *modargs;
+    pa_core *core;
+    pa_bluez4_discovery *discovery;
+    pa_hook_slot *slot;
+    pa_hashmap *hashmap;
+};
+
+struct pa_module_info {
+    char *path;
+    uint32_t module;
+};
+
+static pa_hook_result_t load_module_for_device(pa_bluez4_discovery *y, const pa_bluez4_device *d, struct userdata *u) {
+    struct pa_module_info *mi;
+
+    pa_assert(u);
+    pa_assert(d);
+
+    mi = pa_hashmap_get(u->hashmap, d->path);
+
+    if (pa_bluez4_device_any_audio_connected(d)) {
+
+        if (!mi) {
+            pa_module *m = NULL;
+            char *args;
+
+            /* Oh, awesome, a new device has shown up and been connected! */
+
+            args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path);
+
+            if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) &&
+                pa_modargs_get_value(u->modargs, "sco_source", NULL)) {
+                char *tmp;
+
+                tmp = pa_sprintf_malloc("%s sco_sink=\"%s\" sco_source=\"%s\"", args,
+                                        pa_modargs_get_value(u->modargs, "sco_sink", NULL),
+                                        pa_modargs_get_value(u->modargs, "sco_source", NULL));
+                pa_xfree(args);
+                args = tmp;
+            }
+
+            pa_log_debug("Loading module-bluez4-device %s", args);
+            m = pa_module_load(u->module->core, "module-bluez4-device", args);
+            pa_xfree(args);
+
+            if (m) {
+                mi = pa_xnew(struct pa_module_info, 1);
+                mi->module = m->index;
+                mi->path = pa_xstrdup(d->path);
+
+                pa_hashmap_put(u->hashmap, mi->path, mi);
+            } else
+                pa_log_debug("Failed to load module for device %s", d->path);
+        }
+
+    } else {
+
+        if (mi) {
+
+            /* Hmm, disconnection? Then the module unloads itself */
+
+            pa_log_debug("Unregistering module for %s", d->path);
+            pa_hashmap_remove(u->hashmap, mi->path);
+            pa_xfree(mi->path);
+            pa_xfree(mi);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module* m) {
+    struct userdata *u;
+    pa_modargs *ma;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value(ma, "async", NULL))
+        pa_log_warn("The 'async' argument is deprecated and does nothing.");
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    u->core = m->core;
+    u->modargs = ma;
+    u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    if (!(u->discovery = pa_bluez4_discovery_get(u->core)))
+        goto fail;
+
+    u->slot = pa_hook_connect(pa_bluez4_discovery_hook(u->discovery, PA_BLUEZ4_HOOK_DEVICE_CONNECTION_CHANGED),
+                              PA_HOOK_NORMAL, (pa_hook_cb_t) load_module_for_device, u);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module* m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->slot)
+        pa_hook_slot_free(u->slot);
+
+    if (u->discovery)
+        pa_bluez4_discovery_unref(u->discovery);
+
+    if (u->hashmap) {
+        struct pa_module_info *mi;
+
+        while ((mi = pa_hashmap_steal_first(u->hashmap))) {
+            pa_xfree(mi->path);
+            pa_xfree(mi);
+        }
+
+        pa_hashmap_free(u->hashmap);
+    }
+
+    if (u->modargs)
+        pa_modargs_free(u->modargs);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
new file mode 100644 (file)
index 0000000..530207a
--- /dev/null
@@ -0,0 +1,2382 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008-2013 João Paulo Rechi Vita
+  Copyright 2011-2013 BMW Car IT GmbH.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <arpa/inet.h>
+#include <sbc/sbc.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/poll.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/time-smoother.h>
+
+#include "a2dp-codecs.h"
+#include "bluez5-util.h"
+#include "rtp.h"
+
+#include "module-bluez5-device-symdef.h"
+
+PA_MODULE_AUTHOR("João Paulo Rechi Vita");
+PA_MODULE_DESCRIPTION("BlueZ 5 Bluetooth audio sink and source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE("path=<device object path>"
+                "autodetect_mtu=<boolean>");
+
+#define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_PLAYBACK_SCO (125 * PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_RECORD_A2DP   (25 * PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_RECORD_SCO    (25 * PA_USEC_PER_MSEC)
+
+#define BITPOOL_DEC_LIMIT 32
+#define BITPOOL_DEC_STEP 5
+#define HSP_MAX_GAIN 15
+
+static const char* const valid_modargs[] = {
+    "path",
+    "autodetect_mtu",
+    NULL
+};
+
+enum {
+    BLUETOOTH_MESSAGE_IO_THREAD_FAILED,
+    BLUETOOTH_MESSAGE_STREAM_FD_HUP,
+    BLUETOOTH_MESSAGE_MAX
+};
+
+enum {
+    PA_SOURCE_MESSAGE_SETUP_STREAM = PA_SOURCE_MESSAGE_MAX,
+};
+
+enum {
+    PA_SINK_MESSAGE_SETUP_STREAM = PA_SINK_MESSAGE_MAX,
+};
+
+typedef struct bluetooth_msg {
+    pa_msgobject parent;
+    pa_card *card;
+} bluetooth_msg;
+PA_DEFINE_PRIVATE_CLASS(bluetooth_msg, pa_msgobject);
+#define BLUETOOTH_MSG(o) (bluetooth_msg_cast(o))
+
+typedef struct sbc_info {
+    sbc_t sbc;                           /* Codec data */
+    bool sbc_initialized;                /* Keep track if the encoder is initialized */
+    size_t codesize, frame_length;       /* SBC Codesize, frame_length. We simply cache those values here */
+    uint16_t seq_num;                    /* Cumulative packet sequence */
+    uint8_t min_bitpool;
+    uint8_t max_bitpool;
+
+    void* buffer;                        /* Codec transfer buffer */
+    size_t buffer_size;                  /* Size of the buffer */
+} sbc_info_t;
+
+struct userdata {
+    pa_module *module;
+    pa_core *core;
+
+    pa_hook_slot *device_connection_changed_slot;
+    pa_hook_slot *transport_state_changed_slot;
+    pa_hook_slot *transport_speaker_gain_changed_slot;
+    pa_hook_slot *transport_microphone_gain_changed_slot;
+
+    pa_bluetooth_discovery *discovery;
+    pa_bluetooth_device *device;
+    pa_bluetooth_transport *transport;
+    bool transport_acquired;
+    bool stream_setup_done;
+
+    pa_card *card;
+    pa_sink *sink;
+    pa_source *source;
+    pa_bluetooth_profile_t profile;
+    char *output_port_name;
+    char *input_port_name;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+    bluetooth_msg *msg;
+
+    int stream_fd;
+    int stream_write_type;
+    size_t read_link_mtu;
+    size_t write_link_mtu;
+    size_t read_block_size;
+    size_t write_block_size;
+    uint64_t read_index;
+    uint64_t write_index;
+    pa_usec_t started_at;
+    pa_smoother *read_smoother;
+    pa_memchunk write_memchunk;
+    pa_sample_spec sample_spec;
+    struct sbc_info sbc_info;
+};
+
+typedef enum pa_bluetooth_form_factor {
+    PA_BLUETOOTH_FORM_FACTOR_UNKNOWN,
+    PA_BLUETOOTH_FORM_FACTOR_HEADSET,
+    PA_BLUETOOTH_FORM_FACTOR_HANDSFREE,
+    PA_BLUETOOTH_FORM_FACTOR_MICROPHONE,
+    PA_BLUETOOTH_FORM_FACTOR_SPEAKER,
+    PA_BLUETOOTH_FORM_FACTOR_HEADPHONE,
+    PA_BLUETOOTH_FORM_FACTOR_PORTABLE,
+    PA_BLUETOOTH_FORM_FACTOR_CAR,
+    PA_BLUETOOTH_FORM_FACTOR_HIFI,
+    PA_BLUETOOTH_FORM_FACTOR_PHONE,
+} pa_bluetooth_form_factor_t;
+
+/* Run from main thread */
+static pa_bluetooth_form_factor_t form_factor_from_class(uint32_t class_of_device) {
+    unsigned major, minor;
+    pa_bluetooth_form_factor_t r;
+
+    static const pa_bluetooth_form_factor_t table[] = {
+        [1] = PA_BLUETOOTH_FORM_FACTOR_HEADSET,
+        [2] = PA_BLUETOOTH_FORM_FACTOR_HANDSFREE,
+        [4] = PA_BLUETOOTH_FORM_FACTOR_MICROPHONE,
+        [5] = PA_BLUETOOTH_FORM_FACTOR_SPEAKER,
+        [6] = PA_BLUETOOTH_FORM_FACTOR_HEADPHONE,
+        [7] = PA_BLUETOOTH_FORM_FACTOR_PORTABLE,
+        [8] = PA_BLUETOOTH_FORM_FACTOR_CAR,
+        [10] = PA_BLUETOOTH_FORM_FACTOR_HIFI
+    };
+
+    /*
+     * See Bluetooth Assigned Numbers:
+     * https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
+     */
+    major = (class_of_device >> 8) & 0x1F;
+    minor = (class_of_device >> 2) & 0x3F;
+
+    switch (major) {
+        case 2:
+            return PA_BLUETOOTH_FORM_FACTOR_PHONE;
+        case 4:
+            break;
+        default:
+            pa_log_debug("Unknown Bluetooth major device class %u", major);
+            return PA_BLUETOOTH_FORM_FACTOR_UNKNOWN;
+    }
+
+    r = minor < PA_ELEMENTSOF(table) ? table[minor] : PA_BLUETOOTH_FORM_FACTOR_UNKNOWN;
+
+    if (!r)
+        pa_log_debug("Unknown Bluetooth minor device class %u", minor);
+
+    return r;
+}
+
+/* Run from main thread */
+static const char *form_factor_to_string(pa_bluetooth_form_factor_t ff) {
+    switch (ff) {
+        case PA_BLUETOOTH_FORM_FACTOR_UNKNOWN:
+            return "unknown";
+        case PA_BLUETOOTH_FORM_FACTOR_HEADSET:
+            return "headset";
+        case PA_BLUETOOTH_FORM_FACTOR_HANDSFREE:
+            return "hands-free";
+        case PA_BLUETOOTH_FORM_FACTOR_MICROPHONE:
+            return "microphone";
+        case PA_BLUETOOTH_FORM_FACTOR_SPEAKER:
+            return "speaker";
+        case PA_BLUETOOTH_FORM_FACTOR_HEADPHONE:
+            return "headphone";
+        case PA_BLUETOOTH_FORM_FACTOR_PORTABLE:
+            return "portable";
+        case PA_BLUETOOTH_FORM_FACTOR_CAR:
+            return "car";
+        case PA_BLUETOOTH_FORM_FACTOR_HIFI:
+            return "hifi";
+        case PA_BLUETOOTH_FORM_FACTOR_PHONE:
+            return "phone";
+    }
+
+    pa_assert_not_reached();
+}
+
+/* Run from main thread */
+static void connect_ports(struct userdata *u, void *new_data, pa_direction_t direction) {
+    pa_device_port *port;
+
+    if (direction == PA_DIRECTION_OUTPUT) {
+        pa_sink_new_data *sink_new_data = new_data;
+
+        pa_assert_se(port = pa_hashmap_get(u->card->ports, u->output_port_name));
+        pa_assert_se(pa_hashmap_put(sink_new_data->ports, port->name, port) >= 0);
+        pa_device_port_ref(port);
+    } else {
+        pa_source_new_data *source_new_data = new_data;
+
+        pa_assert_se(port = pa_hashmap_get(u->card->ports, u->input_port_name));
+        pa_assert_se(pa_hashmap_put(source_new_data->ports, port->name, port) >= 0);
+        pa_device_port_ref(port);
+    }
+}
+
+/* Run from IO thread */
+static int sco_process_render(struct userdata *u) {
+    ssize_t l;
+    pa_memchunk memchunk;
+
+    pa_assert(u);
+    pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
+                u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
+    pa_assert(u->sink);
+
+    pa_sink_render_full(u->sink, u->write_block_size, &memchunk);
+
+    pa_assert(memchunk.length == u->write_block_size);
+
+    for (;;) {
+        const void *p;
+
+        /* Now write that data to the socket. The socket is of type
+         * SEQPACKET, and we generated the data of the MTU size, so this
+         * should just work. */
+
+        p = (const uint8_t *) pa_memblock_acquire_chunk(&memchunk);
+        l = pa_write(u->stream_fd, p, memchunk.length, &u->stream_write_type);
+        pa_memblock_release(memchunk.memblock);
+
+        pa_assert(l != 0);
+
+        if (l > 0)
+            break;
+
+        if (errno == EINTR)
+            /* Retry right away if we got interrupted */
+            continue;
+        else if (errno == EAGAIN)
+            /* Hmm, apparently the socket was not writable, give up for now */
+            return 0;
+
+        pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_assert((size_t) l <= memchunk.length);
+
+    if ((size_t) l != memchunk.length) {
+        pa_log_error("Wrote memory block to socket only partially! %llu written, wanted to write %llu.",
+                    (unsigned long long) l,
+                    (unsigned long long) memchunk.length);
+        return -1;
+    }
+
+    u->write_index += (uint64_t) memchunk.length;
+    pa_memblock_unref(memchunk.memblock);
+
+    return 1;
+}
+
+/* Run from IO thread */
+static int sco_process_push(struct userdata *u) {
+    ssize_t l;
+    pa_memchunk memchunk;
+    struct cmsghdr *cm;
+    struct msghdr m;
+    bool found_tstamp = false;
+    pa_usec_t tstamp = 0;
+
+    pa_assert(u);
+    pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
+                u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
+    pa_assert(u->source);
+    pa_assert(u->read_smoother);
+
+    memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
+    memchunk.index = memchunk.length = 0;
+
+    for (;;) {
+        void *p;
+        uint8_t aux[1024];
+        struct iovec iov;
+
+        pa_zero(m);
+        pa_zero(aux);
+        pa_zero(iov);
+
+        m.msg_iov = &iov;
+        m.msg_iovlen = 1;
+        m.msg_control = aux;
+        m.msg_controllen = sizeof(aux);
+
+        p = pa_memblock_acquire(memchunk.memblock);
+        iov.iov_base = p;
+        iov.iov_len = pa_memblock_get_length(memchunk.memblock);
+        l = recvmsg(u->stream_fd, &m, 0);
+        pa_memblock_release(memchunk.memblock);
+
+        if (l > 0)
+            break;
+
+        if (l < 0 && errno == EINTR)
+            /* Retry right away if we got interrupted */
+            continue;
+
+        pa_memblock_unref(memchunk.memblock);
+
+        if (l < 0 && errno == EAGAIN)
+            /* Hmm, apparently the socket was not readable, give up for now. */
+            return 0;
+
+        pa_log_error("Failed to read data from SCO socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF");
+        return -1;
+    }
+
+    pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock));
+
+    /* In some rare occasions, we might receive packets of a very strange
+     * size. This could potentially be possible if the SCO packet was
+     * received partially over-the-air, or more probably due to hardware
+     * issues in our Bluetooth adapter. In these cases, in order to avoid
+     * an assertion failure due to unaligned data, just discard the whole
+     * packet */
+    if (!pa_frame_aligned(l, &u->sample_spec)) {
+        pa_log_warn("SCO packet received of unaligned size: %zu", l);
+        pa_memblock_unref(memchunk.memblock);
+        return -1;
+    }
+
+    memchunk.length = (size_t) l;
+    u->read_index += (uint64_t) l;
+
+    for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm))
+        if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) {
+            struct timeval *tv = (struct timeval*) CMSG_DATA(cm);
+            pa_rtclock_from_wallclock(tv);
+            tstamp = pa_timeval_load(tv);
+            found_tstamp = true;
+            break;
+        }
+
+    if (!found_tstamp) {
+        pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!");
+        tstamp = pa_rtclock_now();
+    }
+
+    pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
+    pa_smoother_resume(u->read_smoother, tstamp, true);
+
+    pa_source_post(u->source, &memchunk);
+    pa_memblock_unref(memchunk.memblock);
+
+    return l;
+}
+
+/* Run from IO thread */
+static void a2dp_prepare_buffer(struct userdata *u) {
+    size_t min_buffer_size = PA_MAX(u->read_link_mtu, u->write_link_mtu);
+
+    pa_assert(u);
+
+    if (u->sbc_info.buffer_size >= min_buffer_size)
+        return;
+
+    u->sbc_info.buffer_size = 2 * min_buffer_size;
+    pa_xfree(u->sbc_info.buffer);
+    u->sbc_info.buffer = pa_xmalloc(u->sbc_info.buffer_size);
+}
+
+/* Run from IO thread */
+static int a2dp_process_render(struct userdata *u) {
+    struct sbc_info *sbc_info;
+    struct rtp_header *header;
+    struct rtp_payload *payload;
+    size_t nbytes;
+    void *d;
+    const void *p;
+    size_t to_write, to_encode;
+    unsigned frame_count;
+    int ret = 0;
+
+    pa_assert(u);
+    pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
+    pa_assert(u->sink);
+
+    /* First, render some data */
+    if (!u->write_memchunk.memblock)
+        pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk);
+
+    pa_assert(u->write_memchunk.length == u->write_block_size);
+
+    a2dp_prepare_buffer(u);
+
+    sbc_info = &u->sbc_info;
+    header = sbc_info->buffer;
+    payload = (struct rtp_payload*) ((uint8_t*) sbc_info->buffer + sizeof(*header));
+
+    frame_count = 0;
+
+    /* Try to create a packet of the full MTU */
+
+    p = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk);
+    to_encode = u->write_memchunk.length;
+
+    d = (uint8_t*) sbc_info->buffer + sizeof(*header) + sizeof(*payload);
+    to_write = sbc_info->buffer_size - sizeof(*header) - sizeof(*payload);
+
+    while (PA_LIKELY(to_encode > 0 && to_write > 0)) {
+        ssize_t written;
+        ssize_t encoded;
+
+        encoded = sbc_encode(&sbc_info->sbc,
+                             p, to_encode,
+                             d, to_write,
+                             &written);
+
+        if (PA_UNLIKELY(encoded <= 0)) {
+            pa_log_error("SBC encoding error (%li)", (long) encoded);
+            pa_memblock_release(u->write_memchunk.memblock);
+            return -1;
+        }
+
+        pa_assert_fp((size_t) encoded <= to_encode);
+        pa_assert_fp((size_t) encoded == sbc_info->codesize);
+
+        pa_assert_fp((size_t) written <= to_write);
+        pa_assert_fp((size_t) written == sbc_info->frame_length);
+
+        p = (const uint8_t*) p + encoded;
+        to_encode -= encoded;
+
+        d = (uint8_t*) d + written;
+        to_write -= written;
+
+        frame_count++;
+    }
+
+    pa_memblock_release(u->write_memchunk.memblock);
+
+    pa_assert(to_encode == 0);
+
+    PA_ONCE_BEGIN {
+        pa_log_debug("Using SBC encoder implementation: %s", pa_strnull(sbc_get_implementation_info(&sbc_info->sbc)));
+    } PA_ONCE_END;
+
+    /* write it to the fifo */
+    memset(sbc_info->buffer, 0, sizeof(*header) + sizeof(*payload));
+    header->v = 2;
+    header->pt = 1;
+    header->sequence_number = htons(sbc_info->seq_num++);
+    header->timestamp = htonl(u->write_index / pa_frame_size(&u->sample_spec));
+    header->ssrc = htonl(1);
+    payload->frame_count = frame_count;
+
+    nbytes = (uint8_t*) d - (uint8_t*) sbc_info->buffer;
+
+    for (;;) {
+        ssize_t l;
+
+        l = pa_write(u->stream_fd, sbc_info->buffer, nbytes, &u->stream_write_type);
+
+        pa_assert(l != 0);
+
+        if (l < 0) {
+
+            if (errno == EINTR)
+                /* Retry right away if we got interrupted */
+                continue;
+
+            else if (errno == EAGAIN)
+                /* Hmm, apparently the socket was not writable, give up for now */
+                break;
+
+            pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno));
+            ret = -1;
+            break;
+        }
+
+        pa_assert((size_t) l <= nbytes);
+
+        if ((size_t) l != nbytes) {
+            pa_log_warn("Wrote memory block to socket only partially! %llu written, wanted to write %llu.",
+                        (unsigned long long) l,
+                        (unsigned long long) nbytes);
+            ret = -1;
+            break;
+        }
+
+        u->write_index += (uint64_t) u->write_memchunk.length;
+        pa_memblock_unref(u->write_memchunk.memblock);
+        pa_memchunk_reset(&u->write_memchunk);
+
+        ret = 1;
+
+        break;
+    }
+
+    return ret;
+}
+
+/* Run from IO thread */
+static int a2dp_process_push(struct userdata *u) {
+    int ret = 0;
+    pa_memchunk memchunk;
+
+    pa_assert(u);
+    pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
+    pa_assert(u->source);
+    pa_assert(u->read_smoother);
+
+    memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
+    memchunk.index = memchunk.length = 0;
+
+    for (;;) {
+        bool found_tstamp = false;
+        pa_usec_t tstamp;
+        struct sbc_info *sbc_info;
+        struct rtp_header *header;
+        struct rtp_payload *payload;
+        const void *p;
+        void *d;
+        ssize_t l;
+        size_t to_write, to_decode;
+        size_t total_written = 0;
+
+        a2dp_prepare_buffer(u);
+
+        sbc_info = &u->sbc_info;
+        header = sbc_info->buffer;
+        payload = (struct rtp_payload*) ((uint8_t*) sbc_info->buffer + sizeof(*header));
+
+        l = pa_read(u->stream_fd, sbc_info->buffer, sbc_info->buffer_size, &u->stream_write_type);
+
+        if (l <= 0) {
+
+            if (l < 0 && errno == EINTR)
+                /* Retry right away if we got interrupted */
+                continue;
+
+            else if (l < 0 && errno == EAGAIN)
+                /* Hmm, apparently the socket was not readable, give up for now. */
+                break;
+
+            pa_log_error("Failed to read data from socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF");
+            ret = -1;
+            break;
+        }
+
+        pa_assert((size_t) l <= sbc_info->buffer_size);
+
+        /* TODO: get timestamp from rtp */
+        if (!found_tstamp) {
+            /* pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); */
+            tstamp = pa_rtclock_now();
+        }
+
+        p = (uint8_t*) sbc_info->buffer + sizeof(*header) + sizeof(*payload);
+        to_decode = l - sizeof(*header) - sizeof(*payload);
+
+        d = pa_memblock_acquire(memchunk.memblock);
+        to_write = memchunk.length = pa_memblock_get_length(memchunk.memblock);
+
+        while (PA_LIKELY(to_decode > 0)) {
+            size_t written;
+            ssize_t decoded;
+
+            decoded = sbc_decode(&sbc_info->sbc,
+                                 p, to_decode,
+                                 d, to_write,
+                                 &written);
+
+            if (PA_UNLIKELY(decoded <= 0)) {
+                pa_log_error("SBC decoding error (%li)", (long) decoded);
+                pa_memblock_release(memchunk.memblock);
+                pa_memblock_unref(memchunk.memblock);
+                return 0;
+            }
+
+            total_written += written;
+
+            /* Reset frame length, it can be changed due to bitpool change */
+            sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);
+
+            pa_assert_fp((size_t) decoded <= to_decode);
+            pa_assert_fp((size_t) decoded == sbc_info->frame_length);
+
+            pa_assert_fp((size_t) written == sbc_info->codesize);
+
+            p = (const uint8_t*) p + decoded;
+            to_decode -= decoded;
+
+            d = (uint8_t*) d + written;
+            to_write -= written;
+        }
+
+        u->read_index += (uint64_t) total_written;
+        pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
+        pa_smoother_resume(u->read_smoother, tstamp, true);
+
+        memchunk.length -= to_write;
+
+        pa_memblock_release(memchunk.memblock);
+
+        pa_source_post(u->source, &memchunk);
+
+        ret = l;
+        break;
+    }
+
+    pa_memblock_unref(memchunk.memblock);
+
+    return ret;
+}
+
+/* Run from I/O thread */
+static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool) {
+    struct sbc_info *sbc_info;
+
+    pa_assert(u);
+
+    sbc_info = &u->sbc_info;
+
+    if (sbc_info->sbc.bitpool == bitpool)
+        return;
+
+    if (bitpool > sbc_info->max_bitpool)
+        bitpool = sbc_info->max_bitpool;
+    else if (bitpool < sbc_info->min_bitpool)
+        bitpool = sbc_info->min_bitpool;
+
+    sbc_info->sbc.bitpool = bitpool;
+
+    sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc);
+    sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);
+
+    pa_log_debug("Bitpool has changed to %u", sbc_info->sbc.bitpool);
+
+    u->read_block_size =
+        (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+        / sbc_info->frame_length * sbc_info->codesize;
+
+    u->write_block_size =
+        (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+        / sbc_info->frame_length * sbc_info->codesize;
+
+    pa_sink_set_max_request_within_thread(u->sink, u->write_block_size);
+    pa_sink_set_fixed_latency_within_thread(u->sink,
+            FIXED_LATENCY_PLAYBACK_A2DP + pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
+}
+
+/* Run from I/O thread */
+static void a2dp_reduce_bitpool(struct userdata *u) {
+    struct sbc_info *sbc_info;
+    uint8_t bitpool;
+
+    pa_assert(u);
+
+    sbc_info = &u->sbc_info;
+
+    /* Check if bitpool is already at its limit */
+    if (sbc_info->sbc.bitpool <= BITPOOL_DEC_LIMIT)
+        return;
+
+    bitpool = sbc_info->sbc.bitpool - BITPOOL_DEC_STEP;
+
+    if (bitpool < BITPOOL_DEC_LIMIT)
+        bitpool = BITPOOL_DEC_LIMIT;
+
+    a2dp_set_bitpool(u, bitpool);
+}
+
+static void teardown_stream(struct userdata *u) {
+    if (u->rtpoll_item) {
+        pa_rtpoll_item_free(u->rtpoll_item);
+        u->rtpoll_item = NULL;
+    }
+
+    if (u->stream_fd >= 0) {
+        pa_close(u->stream_fd);
+        u->stream_fd = -1;
+    }
+
+    if (u->read_smoother) {
+        pa_smoother_free(u->read_smoother);
+        u->read_smoother = NULL;
+    }
+
+    if (u->write_memchunk.memblock) {
+        pa_memblock_unref(u->write_memchunk.memblock);
+        pa_memchunk_reset(&u->write_memchunk);
+    }
+
+    pa_log_debug("Audio stream torn down");
+    u->stream_setup_done = false;
+}
+
+static int transport_acquire(struct userdata *u, bool optional) {
+    pa_assert(u->transport);
+
+    if (u->transport_acquired)
+        return 0;
+
+    pa_log_debug("Acquiring transport %s", u->transport->path);
+
+    u->stream_fd = u->transport->acquire(u->transport, optional, &u->read_link_mtu, &u->write_link_mtu);
+    if (u->stream_fd < 0)
+        return u->stream_fd;
+
+    u->transport_acquired = true;
+    pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd);
+
+    return 0;
+}
+
+static void transport_release(struct userdata *u) {
+    pa_assert(u->transport);
+
+    /* Ignore if already released */
+    if (!u->transport_acquired)
+        return;
+
+    pa_log_debug("Releasing transport %s", u->transport->path);
+
+    u->transport->release(u->transport);
+
+    u->transport_acquired = false;
+
+    teardown_stream(u);
+
+    /* Set transport state to idle if this was not already done by the remote end closing
+     * the file descriptor. Only do this when called from the I/O thread */
+    if (pa_thread_mq_get() != NULL && u->transport->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING)
+        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_STREAM_FD_HUP, NULL, 0, NULL, NULL);
+}
+
+/* Run from I/O thread */
+static void transport_config_mtu(struct userdata *u) {
+    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
+        u->read_block_size = u->read_link_mtu;
+        u->write_block_size = u->write_link_mtu;
+
+        if (!pa_frame_aligned(u->read_block_size, &u->source->sample_spec)) {
+            pa_log_debug("Got invalid read MTU: %lu, rounding down", u->read_block_size);
+            u->read_block_size = pa_frame_align(u->read_block_size, &u->source->sample_spec);
+        }
+
+        if (!pa_frame_aligned(u->write_block_size, &u->sink->sample_spec)) {
+            pa_log_debug("Got invalid write MTU: %lu, rounding down", u->write_block_size);
+            u->write_block_size = pa_frame_align(u->write_block_size, &u->sink->sample_spec);
+        }
+    } else {
+        u->read_block_size =
+            (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+            / u->sbc_info.frame_length * u->sbc_info.codesize;
+
+        u->write_block_size =
+            (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+            / u->sbc_info.frame_length * u->sbc_info.codesize;
+    }
+
+    if (u->sink) {
+        pa_sink_set_max_request_within_thread(u->sink, u->write_block_size);
+        pa_sink_set_fixed_latency_within_thread(u->sink,
+                                                (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ?
+                                                 FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_SCO) +
+                                                pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
+    }
+
+    if (u->source)
+        pa_source_set_fixed_latency_within_thread(u->source,
+                                                  (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE ?
+                                                   FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_SCO) +
+                                                  pa_bytes_to_usec(u->read_block_size, &u->sample_spec));
+}
+
+/* Run from I/O thread */
+static void setup_stream(struct userdata *u) {
+    struct pollfd *pollfd;
+    int one;
+
+    /* return if stream is already set up */
+    if (u->stream_setup_done)
+        return;
+
+    pa_log_info("Transport %s resuming", u->transport->path);
+
+    transport_config_mtu(u);
+
+    pa_make_fd_nonblock(u->stream_fd);
+    pa_make_socket_low_delay(u->stream_fd);
+
+    one = 1;
+    if (setsockopt(u->stream_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0)
+        pa_log_warn("Failed to enable SO_TIMESTAMP: %s", pa_cstrerror(errno));
+
+    pa_log_debug("Stream properly set up, we're ready to roll!");
+
+    if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
+        a2dp_set_bitpool(u, u->sbc_info.max_bitpool);
+
+    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+    pollfd->fd = u->stream_fd;
+    pollfd->events = pollfd->revents = 0;
+
+    u->read_index = u->write_index = 0;
+    u->started_at = 0;
+    u->stream_setup_done = true;
+
+    if (u->source)
+        u->read_smoother = pa_smoother_new(PA_USEC_PER_SEC, 2*PA_USEC_PER_SEC, true, true, 10, pa_rtclock_now(), true);
+}
+
+/* Called from I/O thread, returns true if the transport was acquired or
+ * a connection was requested successfully. */
+static bool setup_transport_and_stream(struct userdata *u) {
+    int transport_error;
+
+    transport_error = transport_acquire(u, false);
+    if (transport_error < 0) {
+        if (transport_error != -EAGAIN)
+            return false;
+    } else
+        setup_stream(u);
+    return true;
+}
+
+/* Run from IO thread */
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+    bool failed = false;
+    int r;
+
+    pa_assert(u->source == PA_SOURCE(o));
+    pa_assert(u->transport);
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_SET_STATE:
+
+            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SOURCE_SUSPENDED:
+                    /* Ignore if transition is PA_SOURCE_INIT->PA_SOURCE_SUSPENDED */
+                    if (!PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+                        break;
+
+                    /* Stop the device if the sink is suspended as well */
+                    if (!u->sink || u->sink->state == PA_SINK_SUSPENDED)
+                        transport_release(u);
+
+                    if (u->read_smoother)
+                        pa_smoother_pause(u->read_smoother, pa_rtclock_now());
+
+                    break;
+
+                case PA_SOURCE_IDLE:
+                case PA_SOURCE_RUNNING:
+                    if (u->source->thread_info.state != PA_SOURCE_SUSPENDED)
+                        break;
+
+                    /* Resume the device if the sink was suspended as well */
+                    if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+                        failed = !setup_transport_and_stream(u);
+
+                    /* We don't resume the smoother here. Instead we
+                     * wait until the first packet arrives */
+
+                    break;
+
+                case PA_SOURCE_UNLINKED:
+                case PA_SOURCE_INIT:
+                case PA_SOURCE_INVALID_STATE:
+                    break;
+            }
+
+            break;
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            int64_t wi, ri;
+
+            if (u->read_smoother) {
+                wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
+                ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
+
+                *((int64_t*) data) = u->source->thread_info.fixed_latency + wi - ri;
+            } else
+                *((int64_t*) data) = 0;
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_SETUP_STREAM:
+            setup_stream(u);
+            return 0;
+
+    }
+
+    r = pa_source_process_msg(o, code, data, offset, chunk);
+
+    return (r < 0 || !failed) ? r : -1;
+}
+
+/* Run from main thread */
+static void source_set_volume_cb(pa_source *s) {
+    uint16_t gain;
+    pa_volume_t volume;
+    struct userdata *u;
+
+    pa_assert(s);
+    pa_assert(s->core);
+
+    u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(u->source == s);
+
+    if (u->transport->set_microphone_gain == NULL)
+      return;
+
+    gain = (pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN) / PA_VOLUME_NORM;
+
+    if (gain > HSP_MAX_GAIN)
+        gain = HSP_MAX_GAIN;
+
+    volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
+
+    /* increment volume by one to correct rounding errors */
+    if (volume < PA_VOLUME_NORM)
+        volume++;
+
+    pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
+
+    /* Set soft volume when in headset role */
+    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
+        pa_cvolume_set(&s->soft_volume, u->sample_spec.channels, volume);
+
+    /* If we are in the AG role, we send a command to the head set to change
+     * the microphone gain. In the HS role, source and sink are swapped, so
+     * in this case we notify the AG that the speaker gain has changed */
+    u->transport->set_microphone_gain(u->transport, gain);
+}
+
+/* Run from main thread */
+static int add_source(struct userdata *u) {
+    pa_source_new_data data;
+
+    pa_assert(u->transport);
+
+    pa_source_new_data_init(&data);
+    data.module = u->module;
+    data.card = u->card;
+    data.driver = __FILE__;
+    data.name = pa_sprintf_malloc("bluez_source.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile));
+    data.namereg_fail = false;
+    pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
+    pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
+    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
+
+    connect_ports(u, &data, PA_DIRECTION_INPUT);
+
+    if (!u->transport_acquired)
+        switch (u->profile) {
+            case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
+            case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+                data.suspend_cause = PA_SUSPEND_USER;
+                break;
+            case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+                /* u->stream_fd contains the error returned by the last transport_acquire()
+                 * EAGAIN means we are waiting for a NewConnection signal */
+                if (u->stream_fd == -EAGAIN)
+                    data.suspend_cause = PA_SUSPEND_USER;
+                else
+                    pa_assert_not_reached();
+                break;
+            case PA_BLUETOOTH_PROFILE_A2DP_SINK:
+            case PA_BLUETOOTH_PROFILE_OFF:
+                pa_assert_not_reached();
+                break;
+        }
+
+    u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+    pa_source_new_data_done(&data);
+    if (!u->source) {
+        pa_log_error("Failed to create source");
+        return -1;
+    }
+
+    u->source->userdata = u;
+    u->source->parent.process_msg = source_process_msg;
+
+    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
+        pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
+        u->source->n_volume_steps = 16;
+    }
+    return 0;
+}
+
+/* Run from IO thread */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+    bool failed = false;
+    int r;
+
+    pa_assert(u->sink == PA_SINK(o));
+    pa_assert(u->transport);
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SINK_SUSPENDED:
+                    /* Ignore if transition is PA_SINK_INIT->PA_SINK_SUSPENDED */
+                    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state))
+                        break;
+
+                    /* Stop the device if the source is suspended as well */
+                    if (!u->source || u->source->state == PA_SOURCE_SUSPENDED)
+                        /* We deliberately ignore whether stopping
+                         * actually worked. Since the stream_fd is
+                         * closed it doesn't really matter */
+                        transport_release(u);
+
+                    break;
+
+                case PA_SINK_IDLE:
+                case PA_SINK_RUNNING:
+                    if (u->sink->thread_info.state != PA_SINK_SUSPENDED)
+                        break;
+
+                    /* Resume the device if the source was suspended as well */
+                    if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+                        failed = !setup_transport_and_stream(u);
+
+                    break;
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                case PA_SINK_INVALID_STATE:
+                    break;
+            }
+
+            break;
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            int64_t wi, ri;
+
+            if (u->read_smoother) {
+                ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
+                wi = pa_bytes_to_usec(u->write_index + u->write_block_size, &u->sample_spec);
+            } else {
+                ri = pa_rtclock_now() - u->started_at;
+                wi = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+            }
+
+            *((int64_t*) data) = u->sink->thread_info.fixed_latency + wi - ri;
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SETUP_STREAM:
+            setup_stream(u);
+            return 0;
+    }
+
+    r = pa_sink_process_msg(o, code, data, offset, chunk);
+
+    return (r < 0 || !failed) ? r : -1;
+}
+
+/* Run from main thread */
+static void sink_set_volume_cb(pa_sink *s) {
+    uint16_t gain;
+    pa_volume_t volume;
+    struct userdata *u;
+
+    pa_assert(s);
+    pa_assert(s->core);
+
+    u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(u->sink == s);
+
+    if (u->transport->set_speaker_gain == NULL)
+      return;
+
+    gain = (pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN) / PA_VOLUME_NORM;
+
+    if (gain > HSP_MAX_GAIN)
+        gain = HSP_MAX_GAIN;
+
+    volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
+
+    /* increment volume by one to correct rounding errors */
+    if (volume < PA_VOLUME_NORM)
+        volume++;
+
+    pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
+
+    /* Set soft volume when in headset role */
+    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
+        pa_cvolume_set(&s->soft_volume, u->sample_spec.channels, volume);
+
+    /* If we are in the AG role, we send a command to the head set to change
+     * the speaker gain. In the HS role, source and sink are swapped, so
+     * in this case we notify the AG that the microphone gain has changed */
+    u->transport->set_speaker_gain(u->transport, gain);
+}
+
+/* Run from main thread */
+static int add_sink(struct userdata *u) {
+    pa_sink_new_data data;
+
+    pa_assert(u->transport);
+
+    pa_sink_new_data_init(&data);
+    data.module = u->module;
+    data.card = u->card;
+    data.driver = __FILE__;
+    data.name = pa_sprintf_malloc("bluez_sink.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile));
+    data.namereg_fail = false;
+    pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
+    pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
+    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
+
+    connect_ports(u, &data, PA_DIRECTION_OUTPUT);
+
+    if (!u->transport_acquired)
+        switch (u->profile) {
+            case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+                data.suspend_cause = PA_SUSPEND_USER;
+                break;
+            case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+                /* u->stream_fd contains the error returned by the last transport_acquire()
+                 * EAGAIN means we are waiting for a NewConnection signal */
+                if (u->stream_fd == -EAGAIN)
+                    data.suspend_cause = PA_SUSPEND_USER;
+                else
+                    pa_assert_not_reached();
+                break;
+            case PA_BLUETOOTH_PROFILE_A2DP_SINK:
+                /* Profile switch should have failed */
+            case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
+            case PA_BLUETOOTH_PROFILE_OFF:
+                pa_assert_not_reached();
+                break;
+        }
+
+    u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+    pa_sink_new_data_done(&data);
+    if (!u->sink) {
+        pa_log_error("Failed to create sink");
+        return -1;
+    }
+
+    u->sink->userdata = u;
+    u->sink->parent.process_msg = sink_process_msg;
+
+    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
+        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
+        u->sink->n_volume_steps = 16;
+    }
+    return 0;
+}
+
+/* Run from main thread */
+static void transport_config(struct userdata *u) {
+    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
+        u->sample_spec.format = PA_SAMPLE_S16LE;
+        u->sample_spec.channels = 1;
+        u->sample_spec.rate = 8000;
+    } else {
+        sbc_info_t *sbc_info = &u->sbc_info;
+        a2dp_sbc_t *config;
+
+        pa_assert(u->transport);
+
+        u->sample_spec.format = PA_SAMPLE_S16LE;
+        config = (a2dp_sbc_t *) u->transport->config;
+
+        if (sbc_info->sbc_initialized)
+            sbc_reinit(&sbc_info->sbc, 0);
+        else
+            sbc_init(&sbc_info->sbc, 0);
+        sbc_info->sbc_initialized = true;
+
+        switch (config->frequency) {
+            case SBC_SAMPLING_FREQ_16000:
+                sbc_info->sbc.frequency = SBC_FREQ_16000;
+                u->sample_spec.rate = 16000U;
+                break;
+            case SBC_SAMPLING_FREQ_32000:
+                sbc_info->sbc.frequency = SBC_FREQ_32000;
+                u->sample_spec.rate = 32000U;
+                break;
+            case SBC_SAMPLING_FREQ_44100:
+                sbc_info->sbc.frequency = SBC_FREQ_44100;
+                u->sample_spec.rate = 44100U;
+                break;
+            case SBC_SAMPLING_FREQ_48000:
+                sbc_info->sbc.frequency = SBC_FREQ_48000;
+                u->sample_spec.rate = 48000U;
+                break;
+            default:
+                pa_assert_not_reached();
+        }
+
+        switch (config->channel_mode) {
+            case SBC_CHANNEL_MODE_MONO:
+                sbc_info->sbc.mode = SBC_MODE_MONO;
+                u->sample_spec.channels = 1;
+                break;
+            case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+                sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL;
+                u->sample_spec.channels = 2;
+                break;
+            case SBC_CHANNEL_MODE_STEREO:
+                sbc_info->sbc.mode = SBC_MODE_STEREO;
+                u->sample_spec.channels = 2;
+                break;
+            case SBC_CHANNEL_MODE_JOINT_STEREO:
+                sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO;
+                u->sample_spec.channels = 2;
+                break;
+            default:
+                pa_assert_not_reached();
+        }
+
+        switch (config->allocation_method) {
+            case SBC_ALLOCATION_SNR:
+                sbc_info->sbc.allocation = SBC_AM_SNR;
+                break;
+            case SBC_ALLOCATION_LOUDNESS:
+                sbc_info->sbc.allocation = SBC_AM_LOUDNESS;
+                break;
+            default:
+                pa_assert_not_reached();
+        }
+
+        switch (config->subbands) {
+            case SBC_SUBBANDS_4:
+                sbc_info->sbc.subbands = SBC_SB_4;
+                break;
+            case SBC_SUBBANDS_8:
+                sbc_info->sbc.subbands = SBC_SB_8;
+                break;
+            default:
+                pa_assert_not_reached();
+        }
+
+        switch (config->block_length) {
+            case SBC_BLOCK_LENGTH_4:
+                sbc_info->sbc.blocks = SBC_BLK_4;
+                break;
+            case SBC_BLOCK_LENGTH_8:
+                sbc_info->sbc.blocks = SBC_BLK_8;
+                break;
+            case SBC_BLOCK_LENGTH_12:
+                sbc_info->sbc.blocks = SBC_BLK_12;
+                break;
+            case SBC_BLOCK_LENGTH_16:
+                sbc_info->sbc.blocks = SBC_BLK_16;
+                break;
+            default:
+                pa_assert_not_reached();
+        }
+
+        sbc_info->min_bitpool = config->min_bitpool;
+        sbc_info->max_bitpool = config->max_bitpool;
+
+        /* Set minimum bitpool for source to get the maximum possible block_size */
+        sbc_info->sbc.bitpool = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? sbc_info->max_bitpool : sbc_info->min_bitpool;
+        sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc);
+        sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);
+
+        pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u, bitpool=%u",
+                    sbc_info->sbc.allocation, sbc_info->sbc.subbands ? 8 : 4, sbc_info->sbc.blocks, sbc_info->sbc.bitpool);
+    }
+}
+
+/* Run from main thread */
+static int setup_transport(struct userdata *u) {
+    pa_bluetooth_transport *t;
+
+    pa_assert(u);
+    pa_assert(!u->transport);
+    pa_assert(u->profile != PA_BLUETOOTH_PROFILE_OFF);
+
+    /* check if profile has a transport */
+    t = u->device->transports[u->profile];
+    if (!t || t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) {
+        pa_log_warn("Profile %s has no transport", pa_bluetooth_profile_to_string(u->profile));
+        return -1;
+    }
+
+    u->transport = t;
+
+    if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
+        transport_acquire(u, true); /* In case of error, the sink/sources will be created suspended */
+    else {
+        int transport_error;
+
+        transport_error = transport_acquire(u, false);
+        if (transport_error < 0 && transport_error != -EAGAIN)
+            return -1; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */
+    }
+
+    transport_config(u);
+
+    return 0;
+}
+
+/* Run from main thread */
+static pa_direction_t get_profile_direction(pa_bluetooth_profile_t p) {
+    static const pa_direction_t profile_direction[] = {
+        [PA_BLUETOOTH_PROFILE_A2DP_SINK] = PA_DIRECTION_OUTPUT,
+        [PA_BLUETOOTH_PROFILE_A2DP_SOURCE] = PA_DIRECTION_INPUT,
+        [PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
+        [PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
+        [PA_BLUETOOTH_PROFILE_OFF] = 0
+    };
+
+    return profile_direction[p];
+}
+
+/* Run from main thread */
+static int init_profile(struct userdata *u) {
+    int r = 0;
+    pa_assert(u);
+    pa_assert(u->profile != PA_BLUETOOTH_PROFILE_OFF);
+
+    if (setup_transport(u) < 0)
+        return -1;
+
+    pa_assert(u->transport);
+
+    if (get_profile_direction (u->profile) & PA_DIRECTION_OUTPUT)
+        if (add_sink(u) < 0)
+            r = -1;
+
+    if (get_profile_direction (u->profile) & PA_DIRECTION_INPUT)
+        if (add_source(u) < 0)
+            r = -1;
+
+    return r;
+}
+
+/* I/O thread function */
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    unsigned do_write = 0;
+    unsigned pending_read_bytes = 0;
+    bool writable = false;
+
+    pa_assert(u);
+    pa_assert(u->transport);
+
+    pa_log_debug("IO Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    /* Setup the stream only if the transport was already acquired */
+    if (u->transport_acquired)
+        setup_stream(u);
+
+    for (;;) {
+        struct pollfd *pollfd;
+        int ret;
+        bool disable_timer = true;
+
+        pollfd = u->rtpoll_item ? pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL) : NULL;
+
+        if (pollfd && (pollfd->revents & ~(POLLOUT|POLLIN))) {
+            pa_log_info("FD error: %s%s%s%s",
+                        pollfd->revents & POLLERR ? "POLLERR " :"",
+                        pollfd->revents & POLLHUP ? "POLLHUP " :"",
+                        pollfd->revents & POLLPRI ? "POLLPRI " :"",
+                        pollfd->revents & POLLNVAL ? "POLLNVAL " :"");
+
+            if (pollfd->revents & POLLHUP) {
+                pollfd = NULL;
+                teardown_stream(u);
+                do_write = 0;
+                pending_read_bytes = 0;
+                writable = false;
+                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_STREAM_FD_HUP, NULL, 0, NULL, NULL);
+            } else
+                goto fail;
+        }
+
+        if (u->source && PA_SOURCE_IS_LINKED(u->source->thread_info.state)) {
+
+            /* We should send two blocks to the device before we expect
+             * a response. */
+
+            if (u->write_index == 0 && u->read_index <= 0)
+                do_write = 2;
+
+            if (pollfd && (pollfd->revents & POLLIN)) {
+                int n_read;
+
+                if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
+                    n_read = a2dp_process_push(u);
+                else
+                    n_read = sco_process_push(u);
+
+                if (n_read < 0)
+                    goto fail;
+
+                if (n_read > 0) {
+                    /* We just read something, so we are supposed to write something, too */
+                    pending_read_bytes += n_read;
+                    do_write += pending_read_bytes / u->write_block_size;
+                    pending_read_bytes = pending_read_bytes % u->write_block_size;
+                }
+            }
+        }
+
+        if (u->sink && PA_SINK_IS_LINKED(u->sink->thread_info.state)) {
+
+            if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+                pa_sink_process_rewind(u->sink, 0);
+
+            if (pollfd) {
+                if (pollfd->revents & POLLOUT)
+                    writable = true;
+
+                if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0 && writable) {
+                    pa_usec_t time_passed;
+                    pa_usec_t audio_sent;
+
+                    /* Hmm, there is no input stream we could synchronize
+                     * to. So let's do things by time */
+
+                    time_passed = pa_rtclock_now() - u->started_at;
+                    audio_sent = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+
+                    if (audio_sent <= time_passed) {
+                        pa_usec_t audio_to_send = time_passed - audio_sent;
+
+                        /* Never try to catch up for more than 100ms */
+                        if (u->write_index > 0 && audio_to_send > MAX_PLAYBACK_CATCH_UP_USEC) {
+                            pa_usec_t skip_usec;
+                            uint64_t skip_bytes;
+
+                            skip_usec = audio_to_send - MAX_PLAYBACK_CATCH_UP_USEC;
+                            skip_bytes = pa_usec_to_bytes(skip_usec, &u->sample_spec);
+
+                            if (skip_bytes > 0) {
+                                pa_memchunk tmp;
+
+                                pa_log_warn("Skipping %llu us (= %llu bytes) in audio stream",
+                                            (unsigned long long) skip_usec,
+                                            (unsigned long long) skip_bytes);
+
+                                pa_sink_render_full(u->sink, skip_bytes, &tmp);
+                                pa_memblock_unref(tmp.memblock);
+                                u->write_index += skip_bytes;
+
+                                if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
+                                    a2dp_reduce_bitpool(u);
+                            }
+                        }
+
+                        do_write = 1;
+                        pending_read_bytes = 0;
+                    }
+                }
+
+                if (writable && do_write > 0) {
+                    int n_written;
+
+                    if (u->write_index <= 0)
+                        u->started_at = pa_rtclock_now();
+
+                    if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
+                        if ((n_written = a2dp_process_render(u)) < 0)
+                            goto fail;
+                    } else {
+                        if ((n_written = sco_process_render(u)) < 0)
+                            goto fail;
+                    }
+
+                    if (n_written == 0)
+                        pa_log("Broken kernel: we got EAGAIN on write() after POLLOUT!");
+
+                    do_write -= n_written;
+                    writable = false;
+                }
+
+                if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0) {
+                    pa_usec_t sleep_for;
+                    pa_usec_t time_passed, next_write_at;
+
+                    if (writable) {
+                        /* Hmm, there is no input stream we could synchronize
+                         * to. So let's estimate when we need to wake up the latest */
+                        time_passed = pa_rtclock_now() - u->started_at;
+                        next_write_at = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+                        sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0;
+                        /* pa_log("Sleeping for %lu; time passed %lu, next write at %lu", (unsigned long) sleep_for, (unsigned long) time_passed, (unsigned long)next_write_at); */
+                    } else
+                        /* drop stream every 500 ms */
+                        sleep_for = PA_USEC_PER_MSEC * 500;
+
+                    pa_rtpoll_set_timer_relative(u->rtpoll, sleep_for);
+                    disable_timer = false;
+                }
+            }
+        }
+
+        if (disable_timer)
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Hmm, nothing to do. Let's sleep */
+        if (pollfd)
+            pollfd->events = (short) (((u->sink && PA_SINK_IS_LINKED(u->sink->thread_info.state) && !writable) ? POLLOUT : 0) |
+                                      (u->source && PA_SOURCE_IS_LINKED(u->source->thread_info.state) ? POLLIN : 0));
+
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) {
+            pa_log_debug("pa_rtpoll_run failed with: %d", ret);
+            goto fail;
+        }
+        if (ret == 0) {
+            pa_log_debug("IO thread shutdown requested, stopping cleanly");
+            transport_release(u);
+            goto finish;
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
+    pa_log_debug("IO thread failed");
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_IO_THREAD_FAILED, NULL, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("IO thread shutting down");
+}
+
+/* Run from main thread */
+static int start_thread(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(!u->thread);
+    pa_assert(!u->rtpoll);
+    pa_assert(!u->rtpoll_item);
+
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        return -1;
+    }
+
+    if (!(u->thread = pa_thread_new("bluetooth", thread_func, u))) {
+        pa_log_error("Failed to create IO thread");
+        return -1;
+    }
+
+    if (u->sink) {
+        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+        pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+        /* If we are in the headset role, the sink should not become default
+         * unless there is no other sound device available. */
+        if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
+            u->sink->priority = 1500;
+
+        pa_sink_put(u->sink);
+
+        if (u->sink->set_volume)
+            u->sink->set_volume(u->sink);
+    }
+
+    if (u->source) {
+        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+        pa_source_set_rtpoll(u->source, u->rtpoll);
+
+        /* If we are in the headset role or the device is an a2dp source,
+         * the source should not become default unless there is no other
+         * sound device available. */
+        if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY || u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
+            u->source->priority = 1500;
+
+        pa_source_put(u->source);
+
+        if (u->source->set_volume)
+            u->source->set_volume(u->source);
+    }
+
+    return 0;
+}
+
+/* Run from main thread */
+static void stop_thread(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+        u->thread = NULL;
+    }
+
+    if (u->rtpoll_item) {
+        pa_rtpoll_item_free(u->rtpoll_item);
+        u->rtpoll_item = NULL;
+    }
+
+    if (u->rtpoll) {
+        pa_rtpoll_free(u->rtpoll);
+        u->rtpoll = NULL;
+        pa_thread_mq_done(&u->thread_mq);
+    }
+
+    if (u->transport) {
+        transport_release(u);
+        u->transport = NULL;
+    }
+
+    if (u->sink) {
+        pa_sink_unref(u->sink);
+        u->sink = NULL;
+    }
+
+    if (u->source) {
+        pa_source_unref(u->source);
+        u->source = NULL;
+    }
+
+    if (u->read_smoother) {
+        pa_smoother_free(u->read_smoother);
+        u->read_smoother = NULL;
+    }
+}
+
+/* Run from main thread */
+static pa_available_t get_port_availability(struct userdata *u, pa_direction_t direction) {
+    pa_available_t result = PA_AVAILABLE_NO;
+    unsigned i;
+
+    pa_assert(u);
+    pa_assert(u->device);
+
+    for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
+        pa_bluetooth_transport *transport;
+
+        if (!(get_profile_direction(i) & direction))
+            continue;
+
+        if (!(transport = u->device->transports[i]))
+            continue;
+
+        switch(transport->state) {
+            case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED:
+                continue;
+
+            case PA_BLUETOOTH_TRANSPORT_STATE_IDLE:
+                if (result == PA_AVAILABLE_NO)
+                    result = PA_AVAILABLE_UNKNOWN;
+
+                break;
+
+            case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING:
+                return PA_AVAILABLE_YES;
+        }
+    }
+
+    return result;
+}
+
+/* Run from main thread */
+static pa_available_t transport_state_to_availability(pa_bluetooth_transport_state_t state) {
+    switch (state) {
+        case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED:
+            return PA_AVAILABLE_NO;
+        case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING:
+            return PA_AVAILABLE_YES;
+        default:
+            return PA_AVAILABLE_UNKNOWN;
+    }
+}
+
+/* Run from main thread */
+static void create_card_ports(struct userdata *u, pa_hashmap *ports) {
+    pa_device_port *port;
+    pa_device_port_new_data port_data;
+    const char *name_prefix, *input_description, *output_description;
+
+    pa_assert(u);
+    pa_assert(ports);
+    pa_assert(u->device);
+
+    name_prefix = "unknown";
+    input_description = _("Bluetooth Input");
+    output_description = _("Bluetooth Output");
+
+    switch (form_factor_from_class(u->device->class_of_device)) {
+        case PA_BLUETOOTH_FORM_FACTOR_HEADSET:
+            name_prefix = "headset";
+            input_description = output_description = _("Headset");
+            break;
+
+        case PA_BLUETOOTH_FORM_FACTOR_HANDSFREE:
+            name_prefix = "handsfree";
+            input_description = output_description = _("Handsfree");
+            break;
+
+        case PA_BLUETOOTH_FORM_FACTOR_MICROPHONE:
+            name_prefix = "microphone";
+            input_description = _("Microphone");
+            output_description = _("Bluetooth Output");
+            break;
+
+        case PA_BLUETOOTH_FORM_FACTOR_SPEAKER:
+            name_prefix = "speaker";
+            input_description = _("Bluetooth Input");
+            output_description = _("Speaker");
+            break;
+
+        case PA_BLUETOOTH_FORM_FACTOR_HEADPHONE:
+            name_prefix = "headphone";
+            input_description = _("Bluetooth Input");
+            output_description = _("Headphone");
+            break;
+
+        case PA_BLUETOOTH_FORM_FACTOR_PORTABLE:
+            name_prefix = "portable";
+            input_description = output_description = _("Portable");
+            break;
+
+        case PA_BLUETOOTH_FORM_FACTOR_CAR:
+            name_prefix = "car";
+            input_description = output_description = _("Car");
+            break;
+
+        case PA_BLUETOOTH_FORM_FACTOR_HIFI:
+            name_prefix = "hifi";
+            input_description = output_description = _("HiFi");
+            break;
+
+        case PA_BLUETOOTH_FORM_FACTOR_PHONE:
+            name_prefix = "phone";
+            input_description = output_description = _("Phone");
+            break;
+
+        case PA_BLUETOOTH_FORM_FACTOR_UNKNOWN:
+            name_prefix = "unknown";
+            input_description = _("Bluetooth Input");
+            output_description = _("Bluetooth Output");
+            break;
+    }
+
+    u->output_port_name = pa_sprintf_malloc("%s-output", name_prefix);
+    pa_device_port_new_data_init(&port_data);
+    pa_device_port_new_data_set_name(&port_data, u->output_port_name);
+    pa_device_port_new_data_set_description(&port_data, output_description);
+    pa_device_port_new_data_set_direction(&port_data, PA_DIRECTION_OUTPUT);
+    pa_device_port_new_data_set_available(&port_data, get_port_availability(u, PA_DIRECTION_OUTPUT));
+    pa_assert_se(port = pa_device_port_new(u->core, &port_data, 0));
+    pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0);
+    pa_device_port_new_data_done(&port_data);
+
+    u->input_port_name = pa_sprintf_malloc("%s-input", name_prefix);
+    pa_device_port_new_data_init(&port_data);
+    pa_device_port_new_data_set_name(&port_data, u->input_port_name);
+    pa_device_port_new_data_set_description(&port_data, input_description);
+    pa_device_port_new_data_set_direction(&port_data, PA_DIRECTION_INPUT);
+    pa_device_port_new_data_set_available(&port_data, get_port_availability(u, PA_DIRECTION_INPUT));
+    pa_assert_se(port = pa_device_port_new(u->core, &port_data, 0));
+    pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0);
+    pa_device_port_new_data_done(&port_data);
+}
+
+/* Run from main thread */
+static pa_card_profile *create_card_profile(struct userdata *u, pa_bluetooth_profile_t profile, pa_hashmap *ports) {
+    pa_device_port *input_port, *output_port;
+    const char *name;
+    pa_card_profile *cp = NULL;
+    pa_bluetooth_profile_t *p;
+
+    pa_assert(u->input_port_name);
+    pa_assert(u->output_port_name);
+    pa_assert_se(input_port = pa_hashmap_get(ports, u->input_port_name));
+    pa_assert_se(output_port = pa_hashmap_get(ports, u->output_port_name));
+
+    name = pa_bluetooth_profile_to_string(profile);
+
+    switch (profile) {
+    case PA_BLUETOOTH_PROFILE_A2DP_SINK:
+        cp = pa_card_profile_new(name, _("High Fidelity Playback (A2DP Sink)"), sizeof(pa_bluetooth_profile_t));
+        cp->priority = 10;
+        cp->n_sinks = 1;
+        cp->n_sources = 0;
+        cp->max_sink_channels = 2;
+        cp->max_source_channels = 0;
+        pa_hashmap_put(output_port->profiles, cp->name, cp);
+
+        p = PA_CARD_PROFILE_DATA(cp);
+        break;
+
+    case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
+        cp = pa_card_profile_new(name, _("High Fidelity Capture (A2DP Source)"), sizeof(pa_bluetooth_profile_t));
+        cp->priority = 10;
+        cp->n_sinks = 0;
+        cp->n_sources = 1;
+        cp->max_sink_channels = 0;
+        cp->max_source_channels = 2;
+        pa_hashmap_put(input_port->profiles, cp->name, cp);
+
+        p = PA_CARD_PROFILE_DATA(cp);
+        break;
+
+    case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+        cp = pa_card_profile_new(name, _("Headset Head Unit (HSP/HFP)"), sizeof(pa_bluetooth_profile_t));
+        cp->priority = 20;
+        cp->n_sinks = 1;
+        cp->n_sources = 1;
+        cp->max_sink_channels = 1;
+        cp->max_source_channels = 1;
+        pa_hashmap_put(input_port->profiles, cp->name, cp);
+        pa_hashmap_put(output_port->profiles, cp->name, cp);
+
+        p = PA_CARD_PROFILE_DATA(cp);
+        break;
+
+    case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+        cp = pa_card_profile_new(name, _("Headset Audio Gateway (HSP/HFP)"), sizeof(pa_bluetooth_profile_t));
+        cp->priority = 20;
+        cp->n_sinks = 1;
+        cp->n_sources = 1;
+        cp->max_sink_channels = 1;
+        cp->max_source_channels = 1;
+        pa_hashmap_put(input_port->profiles, cp->name, cp);
+        pa_hashmap_put(output_port->profiles, cp->name, cp);
+
+        p = PA_CARD_PROFILE_DATA(cp);
+        break;
+
+    case PA_BLUETOOTH_PROFILE_OFF:
+        pa_assert_not_reached();
+    }
+
+    *p = profile;
+
+    if (u->device->transports[*p])
+        cp->available = transport_state_to_availability(u->device->transports[*p]->state);
+    else
+        cp->available = PA_AVAILABLE_NO;
+
+    return cp;
+}
+
+/* Run from main thread */
+static int set_profile_cb(pa_card *c, pa_card_profile *new_profile) {
+    struct userdata *u;
+    pa_bluetooth_profile_t *p;
+
+    pa_assert(c);
+    pa_assert(new_profile);
+    pa_assert_se(u = c->userdata);
+
+    p = PA_CARD_PROFILE_DATA(new_profile);
+
+    if (*p != PA_BLUETOOTH_PROFILE_OFF) {
+        const pa_bluetooth_device *d = u->device;
+
+        if (!d->transports[*p] || d->transports[*p]->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) {
+            pa_log_warn("Refused to switch profile to %s: Not connected", new_profile->name);
+            return -PA_ERR_IO;
+        }
+    }
+
+    stop_thread(u);
+
+    u->profile = *p;
+
+    if (u->profile != PA_BLUETOOTH_PROFILE_OFF)
+        if (init_profile(u) < 0)
+            goto off;
+
+    if (u->sink || u->source)
+        if (start_thread(u) < 0)
+            goto off;
+
+    return 0;
+
+off:
+    stop_thread(u);
+
+    pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
+
+    return -PA_ERR_IO;
+}
+
+static int uuid_to_profile(const char *uuid, pa_bluetooth_profile_t *_r) {
+    if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))
+        *_r = PA_BLUETOOTH_PROFILE_A2DP_SINK;
+    else if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE))
+        *_r = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
+    else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF))
+        *_r = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;
+    else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_AG) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_AG))
+        *_r = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;
+    else
+        return -PA_ERR_INVALID;
+
+    return 0;
+}
+
+/* Run from main thread */
+static int add_card(struct userdata *u) {
+    const pa_bluetooth_device *d;
+    pa_card_new_data data;
+    char *alias;
+    pa_bluetooth_form_factor_t ff;
+    pa_card_profile *cp;
+    pa_bluetooth_profile_t *p;
+    const char *uuid;
+    void *state;
+
+    pa_assert(u);
+    pa_assert(u->device);
+
+    d = u->device;
+
+    pa_card_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = u->module;
+
+    alias = pa_utf8_filter(d->alias);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, alias);
+    pa_xfree(alias);
+
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, d->address);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "bluez");
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_BUS, "bluetooth");
+
+    if ((ff = form_factor_from_class(d->class_of_device)) != PA_BLUETOOTH_FORM_FACTOR_UNKNOWN)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, form_factor_to_string(ff));
+
+    pa_proplist_sets(data.proplist, "bluez.path", d->path);
+    pa_proplist_setf(data.proplist, "bluez.class", "0x%06x", d->class_of_device);
+    pa_proplist_sets(data.proplist, "bluez.alias", d->alias);
+    data.name = pa_sprintf_malloc("bluez_card.%s", d->address);
+    data.namereg_fail = false;
+
+    create_card_ports(u, data.ports);
+
+    PA_HASHMAP_FOREACH(uuid, d->uuids, state) {
+        pa_bluetooth_profile_t profile;
+
+        if (uuid_to_profile(uuid, &profile) < 0)
+            continue;
+
+        if (pa_hashmap_get(data.profiles, pa_bluetooth_profile_to_string(profile)))
+            continue;
+
+        cp = create_card_profile(u, profile, data.ports);
+        pa_hashmap_put(data.profiles, cp->name, cp);
+    }
+
+    pa_assert(!pa_hashmap_isempty(data.profiles));
+
+    cp = pa_card_profile_new("off", _("Off"), sizeof(pa_bluetooth_profile_t));
+    cp->available = PA_AVAILABLE_YES;
+    p = PA_CARD_PROFILE_DATA(cp);
+    *p = PA_BLUETOOTH_PROFILE_OFF;
+    pa_hashmap_put(data.profiles, cp->name, cp);
+
+    u->card = pa_card_new(u->core, &data);
+    pa_card_new_data_done(&data);
+    if (!u->card) {
+        pa_log("Failed to allocate card.");
+        return -1;
+    }
+
+    u->card->userdata = u;
+    u->card->set_profile = set_profile_cb;
+    pa_card_choose_initial_profile(u->card);
+    pa_card_put(u->card);
+
+    p = PA_CARD_PROFILE_DATA(u->card->active_profile);
+    u->profile = *p;
+
+    return 0;
+}
+
+/* Run from main thread */
+static void handle_transport_state_change(struct userdata *u, struct pa_bluetooth_transport *t) {
+    bool acquire = false;
+    bool release = false;
+    pa_card_profile *cp;
+    pa_device_port *port;
+    pa_available_t oldavail;
+
+    pa_assert(u);
+    pa_assert(t);
+    pa_assert_se(cp = pa_hashmap_get(u->card->profiles, pa_bluetooth_profile_to_string(t->profile)));
+
+    oldavail = cp->available;
+    pa_card_profile_set_available(cp, transport_state_to_availability(t->state));
+
+    /* Update port availability */
+    pa_assert_se(port = pa_hashmap_get(u->card->ports, u->output_port_name));
+    pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_OUTPUT));
+    pa_assert_se(port = pa_hashmap_get(u->card->ports, u->input_port_name));
+    pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_INPUT));
+
+    /* Acquire or release transport as needed */
+    acquire = (t->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == t->profile);
+    release = (oldavail != PA_AVAILABLE_NO && t->state != PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == t->profile);
+
+    if (acquire && transport_acquire(u, true) >= 0) {
+        if (u->source) {
+            pa_log_debug("Resuming source %s because its transport state changed to playing", u->source->name);
+
+            /* When the ofono backend resumes source or sink when in the audio gateway role, the
+             * state of source or sink may already be RUNNING before the transport is acquired via
+             * hf_audio_agent_new_connection(), so the pa_source_suspend() call will not lead to a
+             * state change message. In this case we explicitely need to signal the I/O thread to
+             * set up the stream. */
+            if (PA_SOURCE_IS_OPENED(u->source->state))
+                pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_SETUP_STREAM, NULL, 0, NULL);
+
+            /* We remove the IDLE suspend cause, because otherwise
+             * module-loopback doesn't uncork its streams. FIXME: Messing with
+             * the IDLE suspend cause here is wrong, the correct way to handle
+             * this would probably be to uncork the loopback streams not only
+             * when the other end is unsuspended, but also when the other end's
+             * suspend cause changes to IDLE only (currently there's no
+             * notification mechanism for suspend cause changes, though). */
+            pa_source_suspend(u->source, false, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
+        }
+
+        if (u->sink) {
+            pa_log_debug("Resuming sink %s because its transport state changed to playing", u->sink->name);
+
+            /* Same comment as above */
+            if (PA_SINK_IS_OPENED(u->sink->state))
+                pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_SETUP_STREAM, NULL, 0, NULL);
+
+            /* FIXME: See the previous comment. */
+            pa_sink_suspend(u->sink, false, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
+        }
+    }
+
+    if (release && u->transport_acquired) {
+        /* FIXME: this release is racy, since the audio stream might have
+         * been set up again in the meantime (but not processed yet by PA).
+         * BlueZ should probably release the transport automatically, and in
+         * that case we would just mark the transport as released */
+
+        /* Remote side closed the stream so we consider it PA_SUSPEND_USER */
+        if (u->source) {
+            pa_log_debug("Suspending source %s because the remote end closed the stream", u->source->name);
+            pa_source_suspend(u->source, true, PA_SUSPEND_USER);
+        }
+
+        if (u->sink) {
+            pa_log_debug("Suspending sink %s because the remote end closed the stream", u->sink->name);
+            pa_sink_suspend(u->sink, true, PA_SUSPEND_USER);
+        }
+    }
+}
+
+/* Run from main thread */
+static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) {
+    pa_assert(d);
+    pa_assert(u);
+
+    if (d != u->device || pa_bluetooth_device_any_transport_connected(d))
+        return PA_HOOK_OK;
+
+    pa_log_debug("Unloading module for device %s", d->path);
+    pa_module_unload(u->module, true);
+
+    return PA_HOOK_OK;
+}
+
+/* Run from main thread */
+static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) {
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t == u->transport && t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
+        pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
+
+    if (t->device == u->device)
+        handle_transport_state_change(u, t);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t transport_speaker_gain_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) {
+    pa_volume_t volume;
+    pa_cvolume v;
+    uint16_t gain;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t != u->transport)
+      return PA_HOOK_OK;
+
+    gain = t->speaker_gain;
+    volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
+
+    /* increment volume by one to correct rounding errors */
+    if (volume < PA_VOLUME_NORM)
+        volume++;
+
+    pa_cvolume_set(&v, u->sample_spec.channels, volume);
+    if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
+        pa_sink_volume_changed(u->sink, &v);
+    else
+        pa_sink_set_volume(u->sink, &v, true, true);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t transport_microphone_gain_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) {
+    pa_volume_t volume;
+    pa_cvolume v;
+    uint16_t gain;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t != u->transport)
+      return PA_HOOK_OK;
+
+    gain = t->microphone_gain;
+    volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
+
+    /* increment volume by one to correct rounding errors */
+    if (volume < PA_VOLUME_NORM)
+        volume++;
+
+    pa_cvolume_set(&v, u->sample_spec.channels, volume);
+
+    if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
+        pa_source_volume_changed(u->source, &v);
+    else
+        pa_source_set_volume(u->source, &v, true, true);
+
+    return PA_HOOK_OK;
+}
+
+/* Run from main thread context */
+static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct bluetooth_msg *m = BLUETOOTH_MSG(obj);
+    struct userdata *u = m->card->userdata;
+
+    switch (code) {
+        case BLUETOOTH_MESSAGE_IO_THREAD_FAILED:
+            if (m->card->module->unload_requested)
+                break;
+
+            pa_log_debug("Switching the profile to off due to IO thread failure.");
+            pa_assert_se(pa_card_set_profile(m->card, pa_hashmap_get(m->card->profiles, "off"), false) >= 0);
+            break;
+        case BLUETOOTH_MESSAGE_STREAM_FD_HUP:
+            if (u->transport->state > PA_BLUETOOTH_TRANSPORT_STATE_IDLE)
+                pa_bluetooth_transport_set_state(u->transport, PA_BLUETOOTH_TRANSPORT_STATE_IDLE);
+            break;
+    }
+
+    return 0;
+}
+
+int pa__init(pa_module* m) {
+    struct userdata *u;
+    const char *path;
+    pa_modargs *ma;
+    bool autodetect_mtu;
+
+    pa_assert(m);
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    u->core = m->core;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log_error("Failed to parse module arguments");
+        goto fail_free_modargs;
+    }
+
+    if (!(path = pa_modargs_get_value(ma, "path", NULL))) {
+        pa_log_error("Failed to get device path from module arguments");
+        goto fail_free_modargs;
+    }
+
+    if ((u->discovery = pa_shared_get(u->core, "bluetooth-discovery")))
+        pa_bluetooth_discovery_ref(u->discovery);
+    else {
+        pa_log_error("module-bluez5-discover doesn't seem to be loaded, refusing to load module-bluez5-device");
+        goto fail_free_modargs;
+    }
+
+    if (!(u->device = pa_bluetooth_discovery_get_device_by_path(u->discovery, path))) {
+        pa_log_error("%s is unknown", path);
+        goto fail_free_modargs;
+    }
+
+    autodetect_mtu = false;
+    if (pa_modargs_get_value_boolean(ma, "autodetect_mtu", &autodetect_mtu) < 0) {
+        pa_log("Invalid boolean value for autodetect_mtu parameter");
+        goto fail_free_modargs;
+    }
+
+    u->device->autodetect_mtu = autodetect_mtu;
+
+    pa_modargs_free(ma);
+
+    u->device_connection_changed_slot =
+        pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) device_connection_changed_cb, u);
+
+    u->transport_state_changed_slot =
+        pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) transport_state_changed_cb, u);
+
+    u->transport_speaker_gain_changed_slot =
+        pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_speaker_gain_changed_cb, u);
+
+    u->transport_microphone_gain_changed_slot =
+        pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_microphone_gain_changed_cb, u);
+
+    if (add_card(u) < 0)
+        goto fail;
+
+    if (!(u->msg = pa_msgobject_new(bluetooth_msg)))
+        goto fail;
+
+    u->msg->parent.process_msg = device_process_msg;
+    u->msg->card = u->card;
+    u->stream_setup_done = false;
+
+    if (u->profile != PA_BLUETOOTH_PROFILE_OFF)
+        if (init_profile(u) < 0)
+            goto off;
+
+    if (u->sink || u->source)
+        if (start_thread(u) < 0)
+            goto off;
+
+    return 0;
+
+off:
+    stop_thread(u);
+
+    pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
+
+    return 0;
+
+fail_free_modargs:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+fail:
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    stop_thread(u);
+
+    if (u->device_connection_changed_slot)
+        pa_hook_slot_free(u->device_connection_changed_slot);
+
+    if (u->transport_state_changed_slot)
+        pa_hook_slot_free(u->transport_state_changed_slot);
+
+    if (u->transport_speaker_gain_changed_slot)
+        pa_hook_slot_free(u->transport_speaker_gain_changed_slot);
+
+    if (u->transport_microphone_gain_changed_slot)
+        pa_hook_slot_free(u->transport_microphone_gain_changed_slot);
+
+    if (u->sbc_info.buffer)
+        pa_xfree(u->sbc_info.buffer);
+
+    if (u->sbc_info.sbc_initialized)
+        sbc_finish(&u->sbc_info.sbc);
+
+    if (u->msg)
+        pa_xfree(u->msg);
+
+    if (u->card)
+        pa_card_free(u->card);
+
+    if (u->discovery)
+        pa_bluetooth_discovery_unref(u->discovery);
+
+    pa_xfree(u->output_port_name);
+    pa_xfree(u->input_port_name);
+
+    pa_xfree(u);
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return (u->sink ? pa_sink_linked_by(u->sink) : 0) + (u->source ? pa_source_linked_by(u->source) : 0);
+}
diff --git a/src/modules/bluetooth/module-bluez5-discover.c b/src/modules/bluetooth/module-bluez5-discover.c
new file mode 100644 (file)
index 0000000..c535ead
--- /dev/null
@@ -0,0 +1,174 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008-2013 João Paulo Rechi Vita
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/shared.h>
+
+#include "bluez5-util.h"
+
+#include "module-bluez5-discover-symdef.h"
+
+PA_MODULE_AUTHOR("João Paulo Rechi Vita");
+PA_MODULE_DESCRIPTION("Detect available BlueZ 5 Bluetooth audio devices and load BlueZ 5 Bluetooth audio drivers");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+    "headset=ofono|native|auto"
+);
+
+static const char* const valid_modargs[] = {
+    "headset",
+    "autodetect_mtu",
+    NULL
+};
+
+struct userdata {
+    pa_module *module;
+    pa_core *core;
+    pa_hashmap *loaded_device_paths;
+    pa_hook_slot *device_connection_changed_slot;
+    pa_bluetooth_discovery *discovery;
+    bool autodetect_mtu;
+};
+
+static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) {
+    bool module_loaded;
+
+    pa_assert(d);
+    pa_assert(u);
+
+    module_loaded = pa_hashmap_get(u->loaded_device_paths, d->path) ? true : false;
+
+    if (module_loaded && !pa_bluetooth_device_any_transport_connected(d)) {
+        /* disconnection, the module unloads itself */
+        pa_log_debug("Unregistering module for %s", d->path);
+        pa_hashmap_remove(u->loaded_device_paths, d->path);
+        return PA_HOOK_OK;
+    }
+
+    if (!module_loaded && pa_bluetooth_device_any_transport_connected(d)) {
+        /* a new device has been connected */
+        pa_module *m;
+        char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i", d->path, (int)u->autodetect_mtu);
+
+        pa_log_debug("Loading module-bluez5-device %s", args);
+        m = pa_module_load(u->module->core, "module-bluez5-device", args);
+        pa_xfree(args);
+
+        if (m)
+            /* No need to duplicate the path here since the device object will
+             * exist for the whole hashmap entry lifespan */
+            pa_hashmap_put(u->loaded_device_paths, d->path, d->path);
+        else
+            pa_log_warn("Failed to load module for device %s", d->path);
+
+        return PA_HOOK_OK;
+    }
+
+    return PA_HOOK_OK;
+}
+
+#ifdef HAVE_BLUEZ_5_NATIVE_HEADSET
+const char *default_headset_backend = "auto";
+#else
+const char *default_headset_backend = "ofono";
+#endif
+
+int pa__init(pa_module *m) {
+    struct userdata *u;
+    pa_modargs *ma;
+    const char *headset_str;
+    int headset_backend;
+    bool autodetect_mtu;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments.");
+        goto fail;
+    }
+
+    pa_assert_se(headset_str = pa_modargs_get_value(ma, "headset", default_headset_backend));
+    if (pa_streq(headset_str, "ofono"))
+        headset_backend = HEADSET_BACKEND_OFONO;
+    else if (pa_streq(headset_str, "native"))
+        headset_backend = HEADSET_BACKEND_NATIVE;
+    else if (pa_streq(headset_str, "auto"))
+        headset_backend = HEADSET_BACKEND_AUTO;
+    else {
+        pa_log("headset parameter must be either ofono, native or auto (found %s)", headset_str);
+        goto fail;
+    }
+
+    autodetect_mtu = false;
+    if (pa_modargs_get_value_boolean(ma, "autodetect_mtu", &autodetect_mtu) < 0) {
+        pa_log("Invalid boolean value for autodetect_mtu parameter");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    u->core = m->core;
+    u->autodetect_mtu = autodetect_mtu;
+    u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    if (!(u->discovery = pa_bluetooth_discovery_get(u->core, headset_backend)))
+        goto fail;
+
+    u->device_connection_changed_slot =
+        pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) device_connection_changed_cb, u);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+    pa__done(m);
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->device_connection_changed_slot)
+        pa_hook_slot_free(u->device_connection_changed_slot);
+
+    if (u->discovery)
+        pa_bluetooth_discovery_unref(u->discovery);
+
+    if (u->loaded_device_paths)
+        pa_hashmap_free(u->loaded_device_paths);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/bluetooth/rtp.h b/src/modules/bluetooth/rtp.h
new file mode 100644 (file)
index 0000000..20694c1
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+       unsigned cc:4;
+       unsigned x:1;
+       unsigned p:1;
+       unsigned v:2;
+
+       unsigned pt:7;
+       unsigned m:1;
+
+       uint16_t sequence_number;
+       uint32_t timestamp;
+       uint32_t ssrc;
+       uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+       unsigned frame_count:4;
+       unsigned rfa0:1;
+       unsigned is_last_fragment:1;
+       unsigned is_first_fragment:1;
+       unsigned is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+       unsigned v:2;
+       unsigned p:1;
+       unsigned x:1;
+       unsigned cc:4;
+
+       unsigned m:1;
+       unsigned pt:7;
+
+       uint16_t sequence_number;
+       uint32_t timestamp;
+       uint32_t ssrc;
+       uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+       unsigned is_fragmented:1;
+       unsigned is_first_fragment:1;
+       unsigned is_last_fragment:1;
+       unsigned rfa0:1;
+       unsigned frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c
new file mode 100644 (file)
index 0000000..bb8b642
--- /dev/null
@@ -0,0 +1,251 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+
+#include "iface-card-profile.h"
+
+#define OBJECT_NAME "profile"
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_available(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_card_profile {
+    uint32_t index;
+    pa_card_profile *profile;
+    char *path;
+    pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_DESCRIPTION,
+    PROPERTY_HANDLER_SINKS,
+    PROPERTY_HANDLER_SOURCES,
+    PROPERTY_HANDLER_PRIORITY,
+    PROPERTY_HANDLER_AVAILABLE,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]       = { .property_name = "Index",       .type = "u", .get_cb = handle_get_index,       .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]        = { .property_name = "Name",        .type = "s", .get_cb = handle_get_name,        .set_cb = NULL },
+    [PROPERTY_HANDLER_DESCRIPTION] = { .property_name = "Description", .type = "s", .get_cb = handle_get_description, .set_cb = NULL },
+    [PROPERTY_HANDLER_SINKS]       = { .property_name = "Sinks",       .type = "u", .get_cb = handle_get_sinks,       .set_cb = NULL },
+    [PROPERTY_HANDLER_SOURCES]     = { .property_name = "Sources",     .type = "u", .get_cb = handle_get_sources,     .set_cb = NULL },
+    [PROPERTY_HANDLER_PRIORITY]    = { .property_name = "Priority",    .type = "u", .get_cb = handle_get_priority,    .set_cb = NULL },
+    [PROPERTY_HANDLER_AVAILABLE]   = { .property_name = "Available",   .type = "b", .get_cb = handle_get_available,   .set_cb = NULL },
+};
+
+static pa_dbus_interface_info profile_interface_info = {
+    .name = PA_DBUSIFACE_CARD_PROFILE_INTERFACE,
+    .method_handlers = NULL,
+    .n_method_handlers = 0,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = NULL,
+    .n_signals = 0
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &p->index);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->profile->name);
+}
+
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->profile->description);
+}
+
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+    dbus_uint32_t sinks = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    sinks = p->profile->n_sinks;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sinks);
+}
+
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+    dbus_uint32_t sources = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    sources = p->profile->n_sources;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sources);
+}
+
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+    dbus_uint32_t priority = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    priority = p->profile->priority;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &priority);
+}
+
+static void handle_get_available(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+    dbus_bool_t available;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    available = p->profile->available != PA_AVAILABLE_NO;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &available);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t sinks = 0;
+    dbus_uint32_t sources = 0;
+    dbus_uint32_t priority = 0;
+    dbus_bool_t available;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    sinks = p->profile->n_sinks;
+    sources = p->profile->n_sources;
+    priority = p->profile->priority;
+    available = p->profile->available != PA_AVAILABLE_NO;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &p->index);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &p->profile->name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DESCRIPTION].property_name, DBUS_TYPE_STRING, &p->profile->description);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_UINT32, &sinks);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_UINT32, &sources);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PRIORITY].property_name, DBUS_TYPE_UINT32, &priority);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_AVAILABLE].property_name, DBUS_TYPE_BOOLEAN, &available);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(
+        pa_dbusiface_card *card,
+        pa_core *core,
+        pa_card_profile *profile,
+        uint32_t idx) {
+    pa_dbusiface_card_profile *p = NULL;
+
+    pa_assert(card);
+    pa_assert(core);
+    pa_assert(profile);
+
+    p = pa_xnew(pa_dbusiface_card_profile, 1);
+    p->index = idx;
+    p->profile = profile;
+    p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_card_get_path(card), OBJECT_NAME, idx);
+    p->dbus_protocol = pa_dbus_protocol_get(core);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(p->dbus_protocol, p->path, &profile_interface_info, p) >= 0);
+
+    return p;
+}
+
+void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p) {
+    pa_assert(p);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(p->dbus_protocol, p->path, profile_interface_info.name) >= 0);
+
+    pa_dbus_protocol_unref(p->dbus_protocol);
+
+    pa_xfree(p->path);
+    pa_xfree(p);
+}
+
+const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p) {
+    pa_assert(p);
+
+    return p->path;
+}
+
+const char *pa_dbusiface_card_profile_get_name(pa_dbusiface_card_profile *p) {
+    pa_assert(p);
+
+    return p->profile->name;
+}
+
+pa_card_profile *pa_dbusiface_card_profile_get_profile(pa_dbusiface_card_profile *p) {
+    pa_assert(p);
+
+    return p->profile;
+}
diff --git a/src/modules/dbus/iface-card-profile.h b/src/modules/dbus/iface-card-profile.h
new file mode 100644 (file)
index 0000000..443d790
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef foodbusifacecardprofilehfoo
+#define foodbusifacecardprofilehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.CardProfile.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/CardProfile/
+ * for the CardProfile interface documentation.
+ */
+
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-card.h"
+
+#define PA_DBUSIFACE_CARD_PROFILE_INTERFACE PA_DBUS_CORE_INTERFACE ".CardProfile"
+
+typedef struct pa_dbusiface_card_profile pa_dbusiface_card_profile;
+
+pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(
+        pa_dbusiface_card *card,
+        pa_core *core,
+        pa_card_profile *profile,
+        uint32_t idx);
+void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p);
+
+const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p);
+const char *pa_dbusiface_card_profile_get_name(pa_dbusiface_card_profile *p);
+pa_card_profile *pa_dbusiface_card_profile_get_profile(pa_dbusiface_card_profile *p);
+
+#endif
diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
new file mode 100644 (file)
index 0000000..5203250
--- /dev/null
@@ -0,0 +1,603 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-card-profile.h"
+
+#include "iface-card.h"
+
+#define OBJECT_NAME "card"
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_card {
+    pa_dbusiface_core *core;
+
+    pa_card *card;
+    char *path;
+    pa_hashmap *profiles;
+    uint32_t next_profile_index;
+    pa_card_profile *active_profile;
+    pa_proplist *proplist;
+
+    pa_hook_slot *card_profile_added_slot;
+    pa_hook_slot *card_profile_changed_slot;
+    pa_hook_slot *card_profile_available_slot;
+
+    pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_DRIVER,
+    PROPERTY_HANDLER_OWNER_MODULE,
+    PROPERTY_HANDLER_SINKS,
+    PROPERTY_HANDLER_SOURCES,
+    PROPERTY_HANDLER_PROFILES,
+    PROPERTY_HANDLER_ACTIVE_PROFILE,
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]          = { .property_name = "Index",         .type = "u",      .get_cb = handle_get_index,          .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]           = { .property_name = "Name",          .type = "s",      .get_cb = handle_get_name,           .set_cb = NULL },
+    [PROPERTY_HANDLER_DRIVER]         = { .property_name = "Driver",        .type = "s",      .get_cb = handle_get_driver,         .set_cb = NULL },
+    [PROPERTY_HANDLER_OWNER_MODULE]   = { .property_name = "OwnerModule",   .type = "o",      .get_cb = handle_get_owner_module,   .set_cb = NULL },
+    [PROPERTY_HANDLER_SINKS]          = { .property_name = "Sinks",         .type = "ao",     .get_cb = handle_get_sinks,          .set_cb = NULL },
+    [PROPERTY_HANDLER_SOURCES]        = { .property_name = "Sources",       .type = "ao",     .get_cb = handle_get_sources,        .set_cb = NULL },
+    [PROPERTY_HANDLER_PROFILES]       = { .property_name = "Profiles",      .type = "ao",     .get_cb = handle_get_profiles,       .set_cb = NULL },
+    [PROPERTY_HANDLER_ACTIVE_PROFILE] = { .property_name = "ActiveProfile", .type = "o",      .get_cb = handle_get_active_profile, .set_cb = handle_set_active_profile },
+    [PROPERTY_HANDLER_PROPERTY_LIST]  = { .property_name = "PropertyList",  .type = "a{say}", .get_cb = handle_get_property_list,  .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_GET_PROFILE_BY_NAME,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info get_profile_by_name_args[] = { { "name", "s", "in" }, { "profile", "o", "out" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_GET_PROFILE_BY_NAME] = {
+        .method_name = "GetProfileByName",
+        .arguments = get_profile_by_name_args,
+        .n_arguments = sizeof(get_profile_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_profile_by_name }
+};
+
+enum signal_index {
+    SIGNAL_ACTIVE_PROFILE_UPDATED,
+    SIGNAL_NEW_PROFILE,
+    SIGNAL_PROFILE_AVAILABLE_CHANGED,
+    SIGNAL_PROPERTY_LIST_UPDATED,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info active_profile_updated_args[]    = { { "profile",       "o",      NULL } };
+static pa_dbus_arg_info new_profile_args[]               = { { "profile",       "o",      NULL } };
+static pa_dbus_arg_info profile_available_changed_args[] = { { "profile",       "o",      NULL },
+                                                             { "available",     "b",      NULL } };
+static pa_dbus_arg_info property_list_updated_args[]     = { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_ACTIVE_PROFILE_UPDATED]     = { .name = "ActiveProfileUpdated",     .arguments = active_profile_updated_args,    .n_arguments = 1 },
+    [SIGNAL_NEW_PROFILE]                = { .name = "NewProfile",               .arguments = new_profile_args,               .n_arguments = 1 },
+    [SIGNAL_PROFILE_AVAILABLE_CHANGED]  = { .name = "ProfileAvailableChanged",  .arguments = profile_available_changed_args, .n_arguments = 2 },
+    [SIGNAL_PROPERTY_LIST_UPDATED]      = { .name = "PropertyListUpdated",      .arguments = property_list_updated_args,     .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info card_interface_info = {
+    .name = PA_DBUSIFACE_CARD_INTERFACE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    dbus_uint32_t idx;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    idx = c->card->index;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->card->name);
+}
+
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->card->driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char *owner_module;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!c->card->module) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Card %s doesn't have an owner module.", c->card->name);
+        return;
+    }
+
+    owner_module = pa_dbusiface_core_get_module_path(c->core, c->card->module);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &owner_module);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sinks(pa_dbusiface_card *c, unsigned *n) {
+    const char **sinks = NULL;
+    unsigned i = 0;
+    uint32_t idx = 0;
+    pa_sink *sink = NULL;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_idxset_size(c->card->sinks);
+
+    if (*n == 0)
+        return NULL;
+
+    sinks = pa_xnew(const char *, *n);
+
+    PA_IDXSET_FOREACH(sink, c->card->sinks, idx) {
+        sinks[i] = pa_dbusiface_core_get_sink_path(c->core, sink);
+        ++i;
+    }
+
+    return sinks;
+}
+
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char **sinks;
+    unsigned n_sinks;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    sinks = get_sinks(c, &n_sinks);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks);
+
+    pa_xfree(sinks);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sources(pa_dbusiface_card *c, unsigned *n) {
+    const char **sources = NULL;
+    unsigned i = 0;
+    uint32_t idx = 0;
+    pa_source *source = NULL;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_idxset_size(c->card->sources);
+
+    if (*n == 0)
+        return NULL;
+
+    sources = pa_xnew(const char *, *n);
+
+    PA_IDXSET_FOREACH(source, c->card->sources, idx) {
+        sources[i] = pa_dbusiface_core_get_source_path(c->core, source);
+        ++i;
+    }
+
+    return sources;
+}
+
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char **sources;
+    unsigned n_sources;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    sources = get_sources(c, &n_sources);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sources, n_sources);
+
+    pa_xfree(sources);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_profiles(pa_dbusiface_card *c, unsigned *n) {
+    const char **profiles;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_card_profile *profile;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->profiles);
+
+    if (*n == 0)
+        return NULL;
+
+    profiles = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(profile, c->profiles, state)
+        profiles[i++] = pa_dbusiface_card_profile_get_path(profile);
+
+    return profiles;
+}
+
+static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char **profiles;
+    unsigned n_profiles;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    profiles = get_profiles(c, &n_profiles);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, profiles, n_profiles);
+
+    pa_xfree(profiles);
+}
+
+static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char *active_profile;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    active_profile = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name));
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_profile);
+}
+
+static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char *new_active_path;
+    pa_dbusiface_card_profile *new_active;
+    int r;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(c);
+
+    dbus_message_iter_get_basic(iter, &new_active_path);
+
+    if (!(new_active = pa_hashmap_get(c->profiles, new_active_path))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile.", new_active_path);
+        return;
+    }
+
+    if ((r = pa_card_set_profile(c->card, pa_dbusiface_card_profile_get_profile(new_active), true)) < 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                           "Internal error in PulseAudio: pa_card_set_profile() failed with error code %i.", r);
+        return;
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, c->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t idx;
+    const char *owner_module = NULL;
+    const char **sinks = NULL;
+    unsigned n_sinks = 0;
+    const char **sources = NULL;
+    unsigned n_sources = 0;
+    const char **profiles = NULL;
+    unsigned n_profiles = 0;
+    const char *active_profile = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    idx = c->card->index;
+    if (c->card->module)
+        owner_module = pa_dbusiface_core_get_module_path(c->core, c->card->module);
+    sinks = get_sinks(c, &n_sinks);
+    sources = get_sources(c, &n_sources);
+    profiles = get_profiles(c, &n_profiles);
+    active_profile = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name));
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &c->card->name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->card->driver);
+
+    if (owner_module)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module);
+
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROFILES].property_name, DBUS_TYPE_OBJECT_PATH, profiles, n_profiles);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACTIVE_PROFILE].property_name, DBUS_TYPE_OBJECT_PATH, &active_profile);
+
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, c->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(sinks);
+    pa_xfree(sources);
+    pa_xfree(profiles);
+}
+
+static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char *profile_name = NULL;
+    pa_dbusiface_card_profile *profile = NULL;
+    const char *profile_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &profile_name, DBUS_TYPE_INVALID));
+
+    if (!(profile = pa_hashmap_get(c->profiles, profile_name))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile on card %s.", profile_name, c->card->name);
+        return;
+    }
+
+    profile_path = pa_dbusiface_card_profile_get_path(profile);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &profile_path);
+}
+
+static void check_card_proplist(pa_dbusiface_card *c) {
+    DBusMessage *signal_msg;
+
+    if (!pa_proplist_equal(c->proplist, c->card->proplist)) {
+        DBusMessageIter msg_iter;
+
+        pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist);
+
+        pa_assert_se(signal_msg = dbus_message_new_signal(c->path,
+                                                          PA_DBUSIFACE_CARD_INTERFACE,
+                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal_msg, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, c->proplist);
+
+        pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+}
+
+static pa_hook_result_t card_profile_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_card *dbus_card = slot_data;
+    pa_card *core_card = call_data;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    if (dbus_card->card != core_card)
+        return PA_HOOK_OK;
+
+    dbus_card->active_profile = dbus_card->card->active_profile;
+
+    object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(dbus_card->profiles, dbus_card->active_profile->name));
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(dbus_card->path,
+                                                      PA_DBUSIFACE_CARD_INTERFACE,
+                                                      signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(dbus_card->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    check_card_proplist(dbus_card);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_profile_added_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_core *core = hook_data;
+    pa_dbusiface_card *c = slot_data;
+    pa_card_profile *profile = call_data;
+    pa_dbusiface_card_profile *p;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    if (profile->card != c->card)
+        return PA_HOOK_OK;
+
+    p = pa_dbusiface_card_profile_new(c, core, profile, c->next_profile_index++);
+    pa_assert_se(pa_hashmap_put(c->profiles, (char *) pa_dbusiface_card_profile_get_name(p), p) >= 0);
+
+    /* Send D-Bus signal */
+    object_path = pa_dbusiface_card_profile_get_path(p);
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(c->path,
+                                                      PA_DBUSIFACE_CARD_INTERFACE,
+                                                      signals[SIGNAL_NEW_PROFILE].name));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    check_card_proplist(c);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_profile_available_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_card *c = slot_data;
+    pa_card_profile *profile = call_data;
+    pa_dbusiface_card_profile *p;
+    const char *object_path;
+    dbus_bool_t available;
+    DBusMessage *signal_msg;
+
+    if (profile->card != c->card)
+        return PA_HOOK_OK;
+
+    pa_assert_se((p = pa_hashmap_get(c->profiles, profile->name)));
+
+    object_path = pa_dbusiface_card_profile_get_path(p);
+    available = profile->available != PA_AVAILABLE_NO;
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(c->path,
+                                                      PA_DBUSIFACE_CARD_INTERFACE,
+                                                      signals[SIGNAL_PROFILE_AVAILABLE_CHANGED].name));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path,
+                                                      DBUS_TYPE_BOOLEAN, &available,
+                                                      DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    check_card_proplist(c);
+
+    return PA_HOOK_OK;
+}
+
+pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) {
+    pa_dbusiface_card *c = NULL;
+    pa_card_profile *profile;
+    void *state;
+
+    pa_assert(core);
+    pa_assert(card);
+
+    c = pa_xnew0(pa_dbusiface_card, 1);
+    c->core = core;
+    c->card = card;
+    c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, card->index);
+    c->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
+                                      (pa_free_cb_t) pa_dbusiface_card_profile_free);
+    c->next_profile_index = 0;
+    c->active_profile = card->active_profile;
+    c->proplist = pa_proplist_copy(card->proplist);
+    c->dbus_protocol = pa_dbus_protocol_get(card->core);
+
+    PA_HASHMAP_FOREACH(profile, card->profiles, state) {
+        pa_dbusiface_card_profile *p = pa_dbusiface_card_profile_new(c, card->core, profile, c->next_profile_index++);
+        pa_hashmap_put(c->profiles, (char *) pa_dbusiface_card_profile_get_name(p), p);
+    }
+
+    pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &card_interface_info, c) >= 0);
+
+    c->card_profile_changed_slot = pa_hook_connect(&card->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], PA_HOOK_NORMAL,
+                                                   card_profile_changed_cb, c);
+    c->card_profile_added_slot = pa_hook_connect(&card->core->hooks[PA_CORE_HOOK_CARD_PROFILE_ADDED], PA_HOOK_NORMAL,
+                                                 card_profile_added_cb, c);
+    c->card_profile_available_slot = pa_hook_connect(&card->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED], PA_HOOK_NORMAL,
+                                                     card_profile_available_changed_cb, c);
+
+    return c;
+}
+
+void pa_dbusiface_card_free(pa_dbusiface_card *c) {
+    pa_assert(c);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, card_interface_info.name) >= 0);
+
+    pa_hook_slot_free(c->card_profile_added_slot);
+    pa_hook_slot_free(c->card_profile_changed_slot);
+    pa_hook_slot_free(c->card_profile_available_slot);
+
+    pa_hashmap_free(c->profiles);
+    pa_proplist_free(c->proplist);
+    pa_dbus_protocol_unref(c->dbus_protocol);
+
+    pa_xfree(c->path);
+    pa_xfree(c);
+}
+
+const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c) {
+    pa_assert(c);
+
+    return c->path;
+}
diff --git a/src/modules/dbus/iface-card.h b/src/modules/dbus/iface-card.h
new file mode 100644 (file)
index 0000000..5896724
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef foodbusifacecardhfoo
+#define foodbusifacecardhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Card.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Card/
+ * for the Card interface documentation.
+ */
+
+#include <pulsecore/card.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_CARD_INTERFACE PA_DBUS_CORE_INTERFACE ".Card"
+
+typedef struct pa_dbusiface_card pa_dbusiface_card;
+
+pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card);
+void pa_dbusiface_card_free(pa_dbusiface_card *c);
+
+const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c);
+
+#endif
diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c
new file mode 100644 (file)
index 0000000..455ea45
--- /dev/null
@@ -0,0 +1,459 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+  Copyright 2009 Vincent Filali-Ansary <filali.v@azurdigitalnetworks.net>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-client.h"
+
+#define OBJECT_NAME "client"
+
+struct pa_dbusiface_client {
+    pa_dbusiface_core *core;
+
+    pa_client *client;
+    char *path;
+    pa_proplist *proplist;
+
+    pa_hook_slot *client_proplist_changed_slot;
+
+    pa_dbus_protocol *dbus_protocol;
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_DRIVER,
+    PROPERTY_HANDLER_OWNER_MODULE,
+    PROPERTY_HANDLER_PLAYBACK_STREAMS,
+    PROPERTY_HANDLER_RECORD_STREAMS,
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]            = { .property_name = "Index",           .type = "u",      .get_cb = handle_get_index,            .set_cb = NULL },
+    [PROPERTY_HANDLER_DRIVER]           = { .property_name = "Driver",          .type = "s",      .get_cb = handle_get_driver,           .set_cb = NULL },
+    [PROPERTY_HANDLER_OWNER_MODULE]     = { .property_name = "OwnerModule",     .type = "o",      .get_cb = handle_get_owner_module,     .set_cb = NULL },
+    [PROPERTY_HANDLER_PLAYBACK_STREAMS] = { .property_name = "PlaybackStreams", .type = "ao",     .get_cb = handle_get_playback_streams, .set_cb = NULL },
+    [PROPERTY_HANDLER_RECORD_STREAMS]   = { .property_name = "RecordStreams",   .type = "ao",     .get_cb = handle_get_record_streams,   .set_cb = NULL },
+    [PROPERTY_HANDLER_PROPERTY_LIST]    = { .property_name = "PropertyList",    .type = "a{say}", .get_cb = handle_get_property_list,    .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_KILL,
+    METHOD_HANDLER_UPDATE_PROPERTIES,
+    METHOD_HANDLER_REMOVE_PROPERTIES,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info update_properties_args[] = { { "property_list", "a{say}", "in" }, { "update_mode", "u", "in" } };
+static pa_dbus_arg_info remove_properties_args[] = { { "keys", "as", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_KILL] = {
+        .method_name = "Kill",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_kill },
+    [METHOD_HANDLER_UPDATE_PROPERTIES] = {
+        .method_name = "UpdateProperties",
+        .arguments = update_properties_args,
+        .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_update_properties },
+    [METHOD_HANDLER_REMOVE_PROPERTIES] = {
+        .method_name = "RemoveProperties",
+        .arguments = remove_properties_args,
+        .n_arguments = sizeof(remove_properties_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_remove_properties }
+};
+
+enum signal_index {
+    SIGNAL_PROPERTY_LIST_UPDATED,
+    SIGNAL_CLIENT_EVENT,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+static pa_dbus_arg_info client_event_args[]          = { { "name",          "s",      NULL },
+                                                         { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 },
+    /* ClientEvent is sent from module-dbus-protocol.c. */
+    [SIGNAL_CLIENT_EVENT]          = { .name = "ClientEvent",         .arguments = client_event_args,          .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info client_interface_info = {
+    .name = PA_DBUSIFACE_CLIENT_INTERFACE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    dbus_uint32_t idx = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    idx = c->client->index;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->client->driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    const char *owner_module = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!c->client->module) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Client %d doesn't have an owner module.", c->client->index);
+        return;
+    }
+
+    owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &owner_module);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_playback_streams(pa_dbusiface_client *c, unsigned *n) {
+    const char **playback_streams = NULL;
+    unsigned i = 0;
+    uint32_t idx = 0;
+    pa_sink_input *sink_input = NULL;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_idxset_size(c->client->sink_inputs);
+
+    if (*n == 0)
+        return NULL;
+
+    playback_streams = pa_xnew(const char *, *n);
+
+    PA_IDXSET_FOREACH(sink_input, c->client->sink_inputs, idx)
+        playback_streams[i++] = pa_dbusiface_core_get_playback_stream_path(c->core, sink_input);
+
+    return playback_streams;
+}
+
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    const char **playback_streams = NULL;
+    unsigned n_playback_streams = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    playback_streams = get_playback_streams(c, &n_playback_streams);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams);
+
+    pa_xfree(playback_streams);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_record_streams(pa_dbusiface_client *c, unsigned *n) {
+    const char **record_streams = NULL;
+    unsigned i = 0;
+    uint32_t idx = 0;
+    pa_source_output *source_output = NULL;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_idxset_size(c->client->source_outputs);
+
+    if (*n == 0)
+        return NULL;
+
+    record_streams = pa_xnew(const char *, *n);
+
+    PA_IDXSET_FOREACH(source_output, c->client->source_outputs, idx)
+        record_streams[i++] = pa_dbusiface_core_get_record_stream_path(c->core, source_output);
+
+    return record_streams;
+}
+
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    const char **record_streams = NULL;
+    unsigned n_record_streams = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    record_streams = get_record_streams(c, &n_record_streams);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams);
+
+    pa_xfree(record_streams);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, c->client->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t idx = 0;
+    const char *owner_module = NULL;
+    const char **playback_streams = NULL;
+    unsigned n_playback_streams = 0;
+    const char **record_streams = NULL;
+    unsigned n_record_streams = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    idx = c->client->index;
+    if (c->client->module)
+        owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module);
+    playback_streams = get_playback_streams(c, &n_playback_streams);
+    record_streams = get_record_streams(c, &n_record_streams);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->client->driver);
+
+    if (owner_module)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module);
+
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PLAYBACK_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RECORD_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams);
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, c->client->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(playback_streams);
+    pa_xfree(record_streams);
+}
+
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    dbus_connection_ref(conn);
+
+    pa_client_kill(c->client);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    dbus_connection_unref(conn);
+}
+
+static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    DBusMessageIter msg_iter;
+    pa_proplist *property_list = NULL;
+    dbus_uint32_t update_mode = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client.");
+        return;
+    }
+
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+
+    if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+        return;
+
+    dbus_message_iter_get_basic(&msg_iter, &update_mode);
+
+    if (!(update_mode == PA_UPDATE_SET || update_mode == PA_UPDATE_MERGE || update_mode == PA_UPDATE_REPLACE)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid update mode: %u", update_mode);
+        goto finish;
+    }
+
+    pa_client_update_proplist(c->client, update_mode, property_list);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+finish:
+    if (property_list)
+        pa_proplist_free(property_list);
+}
+
+static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    char **keys = NULL;
+    int n_keys = 0;
+    bool changed = false;
+    int i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client.");
+        return;
+    }
+
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID));
+
+    for (i = 0; i < n_keys; ++i)
+        changed |= pa_proplist_unset(c->client->proplist, keys[i]) >= 0;
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    if (changed) {
+        pa_hook_fire(&c->client->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c->client);
+        pa_subscription_post(c->client->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+    }
+
+    dbus_free_string_array(keys);
+}
+
+static pa_hook_result_t client_proplist_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_client *c = slot_data;
+    pa_client *client = call_data;
+    DBusMessage *signal_msg;
+
+    pa_assert(c);
+    pa_assert(client);
+
+    if (c->client != client)
+        return PA_HOOK_OK;
+
+    if (!pa_proplist_equal(c->proplist, c->client->proplist)) {
+        DBusMessageIter msg_iter;
+
+        pa_proplist_update(c->proplist, PA_UPDATE_SET, c->client->proplist);
+
+        pa_assert_se(signal_msg = dbus_message_new_signal(c->path,
+                                                          PA_DBUSIFACE_CLIENT_INTERFACE,
+                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal_msg, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, c->proplist);
+
+        pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+
+    return PA_HOOK_OK;
+}
+
+pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) {
+    pa_dbusiface_client *c = NULL;
+
+    pa_assert(core);
+    pa_assert(client);
+
+    c = pa_xnew(pa_dbusiface_client, 1);
+    c->core = core;
+    c->client = client;
+    c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index);
+    c->proplist = pa_proplist_copy(client->proplist);
+    c->dbus_protocol = pa_dbus_protocol_get(client->core);
+    c->client_proplist_changed_slot = pa_hook_connect(&client->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED],
+                                                      PA_HOOK_NORMAL, client_proplist_changed_cb, c);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &client_interface_info, c) >= 0);
+
+    return c;
+}
+
+void pa_dbusiface_client_free(pa_dbusiface_client *c) {
+    pa_assert(c);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, client_interface_info.name) >= 0);
+
+    pa_hook_slot_free(c->client_proplist_changed_slot);
+    pa_proplist_free(c->proplist);
+    pa_dbus_protocol_unref(c->dbus_protocol);
+
+    pa_xfree(c->path);
+    pa_xfree(c);
+}
+
+const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c) {
+    pa_assert(c);
+
+    return c->path;
+}
diff --git a/src/modules/dbus/iface-client.h b/src/modules/dbus/iface-client.h
new file mode 100644 (file)
index 0000000..7fef3f5
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef foodbusifaceclienthfoo
+#define foodbusifaceclienthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Client.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Client/
+ * for the Client interface documentation.
+ */
+
+#include <pulsecore/client.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_CLIENT_INTERFACE PA_DBUS_CORE_INTERFACE ".Client"
+
+typedef struct pa_dbusiface_client pa_dbusiface_client;
+
+pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client);
+void pa_dbusiface_client_free(pa_dbusiface_client *c);
+
+const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c);
+
+#endif
diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
new file mode 100644 (file)
index 0000000..7177455
--- /dev/null
@@ -0,0 +1,2413 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+
+#include <dbus/dbus.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/strbuf.h>
+
+#include "iface-card.h"
+#include "iface-client.h"
+#include "iface-device.h"
+#include "iface-memstats.h"
+#include "iface-module.h"
+#include "iface-sample.h"
+#include "iface-stream.h"
+
+#include "iface-core.h"
+
+#define INTERFACE_REVISION 0
+
+static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_alternate_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_alternate_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_clients(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_my_client(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_extensions(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_core {
+    pa_core *core;
+
+    pa_dbus_protocol *dbus_protocol;
+
+    pa_hashmap *cards;
+    pa_hashmap *sinks_by_index;
+    pa_hashmap *sinks_by_path;
+    pa_hashmap *sources_by_index;
+    pa_hashmap *sources_by_path;
+    pa_hashmap *playback_streams;
+    pa_hashmap *record_streams;
+    pa_hashmap *samples;
+    pa_hashmap *modules;
+    pa_hashmap *clients;
+
+    pa_sink *fallback_sink;
+    pa_source *fallback_source;
+
+    pa_hook_slot *module_new_slot;
+    pa_hook_slot *module_removed_slot;
+    pa_hook_slot *default_sink_changed_slot;
+    pa_hook_slot *default_source_changed_slot;
+    pa_hook_slot *sample_cache_new_slot;
+    pa_hook_slot *sample_cache_removed_slot;
+    pa_hook_slot *card_put_slot;
+    pa_hook_slot *card_unlink_slot;
+    pa_hook_slot *sink_input_put_slot;
+    pa_hook_slot *sink_input_unlink_slot;
+    pa_hook_slot *source_output_put_slot;
+    pa_hook_slot *source_output_unlink_slot;
+    pa_hook_slot *client_put_slot;
+    pa_hook_slot *client_unlink_slot;
+    pa_hook_slot *sink_put_slot;
+    pa_hook_slot *sink_unlink_slot;
+    pa_hook_slot *source_put_slot;
+    pa_hook_slot *source_unlink_slot;
+    pa_hook_slot *extension_registered_slot;
+    pa_hook_slot *extension_unregistered_slot;
+
+    pa_dbusiface_memstats *memstats;
+};
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INTERFACE_REVISION,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_VERSION,
+    PROPERTY_HANDLER_IS_LOCAL,
+    PROPERTY_HANDLER_USERNAME,
+    PROPERTY_HANDLER_HOSTNAME,
+    PROPERTY_HANDLER_DEFAULT_CHANNELS,
+    PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT,
+    PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE,
+    PROPERTY_HANDLER_ALTERNATE_SAMPLE_RATE,
+    PROPERTY_HANDLER_CARDS,
+    PROPERTY_HANDLER_SINKS,
+    PROPERTY_HANDLER_FALLBACK_SINK,
+    PROPERTY_HANDLER_SOURCES,
+    PROPERTY_HANDLER_FALLBACK_SOURCE,
+    PROPERTY_HANDLER_PLAYBACK_STREAMS,
+    PROPERTY_HANDLER_RECORD_STREAMS,
+    PROPERTY_HANDLER_SAMPLES,
+    PROPERTY_HANDLER_MODULES,
+    PROPERTY_HANDLER_CLIENTS,
+    PROPERTY_HANDLER_MY_CLIENT,
+    PROPERTY_HANDLER_EXTENSIONS,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INTERFACE_REVISION]    = { .property_name = "InterfaceRevision",   .type = "u",  .get_cb = handle_get_interface_revision,    .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]                  = { .property_name = "Name",                .type = "s",  .get_cb = handle_get_name,                  .set_cb = NULL },
+    [PROPERTY_HANDLER_VERSION]               = { .property_name = "Version",             .type = "s",  .get_cb = handle_get_version,               .set_cb = NULL },
+    [PROPERTY_HANDLER_IS_LOCAL]              = { .property_name = "IsLocal",             .type = "b",  .get_cb = handle_get_is_local,              .set_cb = NULL },
+    [PROPERTY_HANDLER_USERNAME]              = { .property_name = "Username",            .type = "s",  .get_cb = handle_get_username,              .set_cb = NULL },
+    [PROPERTY_HANDLER_HOSTNAME]              = { .property_name = "Hostname",            .type = "s",  .get_cb = handle_get_hostname,              .set_cb = NULL },
+    [PROPERTY_HANDLER_DEFAULT_CHANNELS]      = { .property_name = "DefaultChannels",     .type = "au", .get_cb = handle_get_default_channels,      .set_cb = handle_set_default_channels },
+    [PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT] = { .property_name = "DefaultSampleFormat", .type = "u",  .get_cb = handle_get_default_sample_format, .set_cb = handle_set_default_sample_format },
+    [PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE]   = { .property_name = "DefaultSampleRate",   .type = "u",  .get_cb = handle_get_default_sample_rate,   .set_cb = handle_set_default_sample_rate },
+    [PROPERTY_HANDLER_ALTERNATE_SAMPLE_RATE]   = { .property_name = "AlternateSampleRate",   .type = "u",  .get_cb = handle_get_alternate_sample_rate,   .set_cb = handle_set_alternate_sample_rate },
+    [PROPERTY_HANDLER_CARDS]                 = { .property_name = "Cards",               .type = "ao", .get_cb = handle_get_cards,                 .set_cb = NULL },
+    [PROPERTY_HANDLER_SINKS]                 = { .property_name = "Sinks",               .type = "ao", .get_cb = handle_get_sinks,                 .set_cb = NULL },
+    [PROPERTY_HANDLER_FALLBACK_SINK]         = { .property_name = "FallbackSink",        .type = "o",  .get_cb = handle_get_fallback_sink,         .set_cb = handle_set_fallback_sink },
+    [PROPERTY_HANDLER_SOURCES]               = { .property_name = "Sources",             .type = "ao", .get_cb = handle_get_sources,               .set_cb = NULL },
+    [PROPERTY_HANDLER_FALLBACK_SOURCE]       = { .property_name = "FallbackSource",      .type = "o",  .get_cb = handle_get_fallback_source,       .set_cb = handle_set_fallback_source },
+    [PROPERTY_HANDLER_PLAYBACK_STREAMS]      = { .property_name = "PlaybackStreams",     .type = "ao", .get_cb = handle_get_playback_streams,      .set_cb = NULL },
+    [PROPERTY_HANDLER_RECORD_STREAMS]        = { .property_name = "RecordStreams",       .type = "ao", .get_cb = handle_get_record_streams,        .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLES]               = { .property_name = "Samples",             .type = "ao", .get_cb = handle_get_samples,               .set_cb = NULL },
+    [PROPERTY_HANDLER_MODULES]               = { .property_name = "Modules",             .type = "ao", .get_cb = handle_get_modules,               .set_cb = NULL },
+    [PROPERTY_HANDLER_CLIENTS]               = { .property_name = "Clients",             .type = "ao", .get_cb = handle_get_clients,               .set_cb = NULL },
+    [PROPERTY_HANDLER_MY_CLIENT]             = { .property_name = "MyClient",            .type = "o",  .get_cb = handle_get_my_client,             .set_cb = NULL },
+    [PROPERTY_HANDLER_EXTENSIONS]            = { .property_name = "Extensions",          .type = "as", .get_cb = handle_get_extensions,            .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_GET_CARD_BY_NAME,
+    METHOD_HANDLER_GET_SINK_BY_NAME,
+    METHOD_HANDLER_GET_SOURCE_BY_NAME,
+    METHOD_HANDLER_GET_SAMPLE_BY_NAME,
+    METHOD_HANDLER_UPLOAD_SAMPLE,
+    METHOD_HANDLER_LOAD_MODULE,
+    METHOD_HANDLER_EXIT,
+    METHOD_HANDLER_LISTEN_FOR_SIGNAL,
+    METHOD_HANDLER_STOP_LISTENING_FOR_SIGNAL,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info get_card_by_name_args[] = { { "name", "s", "in" }, { "card", "o", "out" } };
+static pa_dbus_arg_info get_sink_by_name_args[] = { { "name", "s", "in" }, { "sink", "o", "out" } };
+static pa_dbus_arg_info get_source_by_name_args[] = { { "name", "s", "in" }, { "source", "o", "out" } };
+static pa_dbus_arg_info get_sample_by_name_args[] = { { "name", "s", "in" }, { "sample", "o", "out" } };
+static pa_dbus_arg_info upload_sample_args[] = { { "name",           "s",      "in" },
+                                                 { "sample_format",  "u",      "in" },
+                                                 { "sample_rate",    "u",      "in" },
+                                                 { "channels",       "au",     "in" },
+                                                 { "default_volume", "au",     "in" },
+                                                 { "property_list",  "a{say}", "in" },
+                                                 { "data",           "ay",     "in" },
+                                                 { "sample",         "o",      "out" } };
+static pa_dbus_arg_info load_module_args[] = { { "name", "s", "in" }, { "arguments", "a{ss}", "in" }, { "module", "o", "out" } };
+static pa_dbus_arg_info listen_for_signal_args[] = { { "signal", "s", "in" }, { "objects", "ao", "in" } };
+static pa_dbus_arg_info stop_listening_for_signal_args[] = { { "signal", "s", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_GET_CARD_BY_NAME] = {
+        .method_name = "GetCardByName",
+        .arguments = get_card_by_name_args,
+        .n_arguments = sizeof(get_card_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_card_by_name },
+    [METHOD_HANDLER_GET_SINK_BY_NAME] = {
+        .method_name = "GetSinkByName",
+        .arguments = get_sink_by_name_args,
+        .n_arguments = sizeof(get_sink_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_sink_by_name },
+    [METHOD_HANDLER_GET_SOURCE_BY_NAME] = {
+        .method_name = "GetSourceByName",
+        .arguments = get_source_by_name_args,
+        .n_arguments = sizeof(get_source_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_source_by_name },
+    [METHOD_HANDLER_GET_SAMPLE_BY_NAME] = {
+        .method_name = "GetSampleByName",
+        .arguments = get_sample_by_name_args,
+        .n_arguments = sizeof(get_sample_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_sample_by_name },
+    [METHOD_HANDLER_UPLOAD_SAMPLE] = {
+        .method_name = "UploadSample",
+        .arguments = upload_sample_args,
+        .n_arguments = sizeof(upload_sample_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_upload_sample },
+    [METHOD_HANDLER_LOAD_MODULE] = {
+        .method_name = "LoadModule",
+        .arguments = load_module_args,
+        .n_arguments = sizeof(load_module_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_load_module },
+    [METHOD_HANDLER_EXIT] = {
+        .method_name = "Exit",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_exit },
+    [METHOD_HANDLER_LISTEN_FOR_SIGNAL] = {
+        .method_name = "ListenForSignal",
+        .arguments = listen_for_signal_args,
+        .n_arguments = sizeof(listen_for_signal_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_listen_for_signal },
+    [METHOD_HANDLER_STOP_LISTENING_FOR_SIGNAL] = {
+        .method_name = "StopListeningForSignal",
+        .arguments = stop_listening_for_signal_args,
+        .n_arguments = sizeof(stop_listening_for_signal_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_stop_listening_for_signal }
+};
+
+enum signal_index {
+    SIGNAL_NEW_CARD,
+    SIGNAL_CARD_REMOVED,
+    SIGNAL_NEW_SINK,
+    SIGNAL_SINK_REMOVED,
+    SIGNAL_FALLBACK_SINK_UPDATED,
+    SIGNAL_FALLBACK_SINK_UNSET,
+    SIGNAL_NEW_SOURCE,
+    SIGNAL_SOURCE_REMOVED,
+    SIGNAL_FALLBACK_SOURCE_UPDATED,
+    SIGNAL_FALLBACK_SOURCE_UNSET,
+    SIGNAL_NEW_PLAYBACK_STREAM,
+    SIGNAL_PLAYBACK_STREAM_REMOVED,
+    SIGNAL_NEW_RECORD_STREAM,
+    SIGNAL_RECORD_STREAM_REMOVED,
+    SIGNAL_NEW_SAMPLE,
+    SIGNAL_SAMPLE_REMOVED,
+    SIGNAL_NEW_MODULE,
+    SIGNAL_MODULE_REMOVED,
+    SIGNAL_NEW_CLIENT,
+    SIGNAL_CLIENT_REMOVED,
+    SIGNAL_NEW_EXTENSION,
+    SIGNAL_EXTENSION_REMOVED,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info new_card_args[] =                { { "card",            "o", NULL } };
+static pa_dbus_arg_info card_removed_args[] =            { { "card",            "o", NULL } };
+static pa_dbus_arg_info new_sink_args[] =                { { "sink",            "o", NULL } };
+static pa_dbus_arg_info sink_removed_args[] =            { { "sink",            "o", NULL } };
+static pa_dbus_arg_info fallback_sink_updated_args[] =   { { "sink",            "o", NULL } };
+static pa_dbus_arg_info new_source_args[] =              { { "source",          "o", NULL } };
+static pa_dbus_arg_info source_removed_args[] =          { { "source",          "o", NULL } };
+static pa_dbus_arg_info fallback_source_updated_args[] = { { "source",          "o", NULL } };
+static pa_dbus_arg_info new_playback_stream_args[] =     { { "playback_stream", "o", NULL } };
+static pa_dbus_arg_info playback_stream_removed_args[] = { { "playback_stream", "o", NULL } };
+static pa_dbus_arg_info new_record_stream_args[] =       { { "record_stream",   "o", NULL } };
+static pa_dbus_arg_info record_stream_removed_args[] =   { { "record_stream",   "o", NULL } };
+static pa_dbus_arg_info new_sample_args[] =              { { "sample",          "o", NULL } };
+static pa_dbus_arg_info sample_removed_args[] =          { { "sample",          "o", NULL } };
+static pa_dbus_arg_info new_module_args[] =              { { "module",          "o", NULL } };
+static pa_dbus_arg_info module_removed_args[] =          { { "module",          "o", NULL } };
+static pa_dbus_arg_info new_client_args[] =              { { "client",          "o", NULL } };
+static pa_dbus_arg_info client_removed_args[] =          { { "client",          "o", NULL } };
+static pa_dbus_arg_info new_extension_args[] =           { { "extension",       "s", NULL } };
+static pa_dbus_arg_info extension_removed_args[] =       { { "extension",       "s", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_NEW_CARD]                = { .name = "NewCard",               .arguments = new_card_args,                .n_arguments = 1 },
+    [SIGNAL_CARD_REMOVED]            = { .name = "CardRemoved",           .arguments = card_removed_args,            .n_arguments = 1 },
+    [SIGNAL_NEW_SINK]                = { .name = "NewSink",               .arguments = new_sink_args,                .n_arguments = 1 },
+    [SIGNAL_SINK_REMOVED]            = { .name = "SinkRemoved",           .arguments = sink_removed_args,            .n_arguments = 1 },
+    [SIGNAL_FALLBACK_SINK_UPDATED]   = { .name = "FallbackSinkUpdated",   .arguments = fallback_sink_updated_args,   .n_arguments = 1 },
+    [SIGNAL_FALLBACK_SINK_UNSET]     = { .name = "FallbackSinkUnset",     .arguments = NULL,                         .n_arguments = 0 },
+    [SIGNAL_NEW_SOURCE]              = { .name = "NewSource",             .arguments = new_source_args,              .n_arguments = 1 },
+    [SIGNAL_SOURCE_REMOVED]          = { .name = "SourceRemoved",         .arguments = source_removed_args,          .n_arguments = 1 },
+    [SIGNAL_FALLBACK_SOURCE_UPDATED] = { .name = "FallbackSourceUpdated", .arguments = fallback_source_updated_args, .n_arguments = 1 },
+    [SIGNAL_FALLBACK_SOURCE_UNSET]   = { .name = "FallbackSourceUnset",   .arguments = NULL,                         .n_arguments = 0 },
+    [SIGNAL_NEW_PLAYBACK_STREAM]     = { .name = "NewPlaybackStream",     .arguments = new_playback_stream_args,     .n_arguments = 1 },
+    [SIGNAL_PLAYBACK_STREAM_REMOVED] = { .name = "PlaybackStreamRemoved", .arguments = playback_stream_removed_args, .n_arguments = 1 },
+    [SIGNAL_NEW_RECORD_STREAM]       = { .name = "NewRecordStream",       .arguments = new_record_stream_args,       .n_arguments = 1 },
+    [SIGNAL_RECORD_STREAM_REMOVED]   = { .name = "RecordStreamRemoved",   .arguments = record_stream_removed_args,   .n_arguments = 1 },
+    [SIGNAL_NEW_SAMPLE]              = { .name = "NewSample",             .arguments = new_sample_args,              .n_arguments = 1 },
+    [SIGNAL_SAMPLE_REMOVED]          = { .name = "SampleRemoved",         .arguments = sample_removed_args,          .n_arguments = 1 },
+    [SIGNAL_NEW_MODULE]              = { .name = "NewModule",             .arguments = new_module_args,              .n_arguments = 1 },
+    [SIGNAL_MODULE_REMOVED]          = { .name = "ModuleRemoved",         .arguments = module_removed_args,          .n_arguments = 1 },
+    [SIGNAL_NEW_CLIENT]              = { .name = "NewClient",             .arguments = new_client_args,              .n_arguments = 1 },
+    [SIGNAL_CLIENT_REMOVED]          = { .name = "ClientRemoved",         .arguments = client_removed_args,          .n_arguments = 1 },
+    [SIGNAL_NEW_EXTENSION]           = { .name = "NewExtension",          .arguments = new_extension_args,           .n_arguments = 1 },
+    [SIGNAL_EXTENSION_REMOVED]       = { .name = "ExtensionRemoved",      .arguments = extension_removed_args,       .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info core_interface_info = {
+    .name = PA_DBUS_CORE_INTERFACE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    dbus_uint32_t interface_revision = INTERFACE_REVISION;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &interface_revision);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    const char *server_name = PACKAGE_NAME;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &server_name);
+}
+
+static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    const char *version = PACKAGE_VERSION;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &version);
+}
+
+static dbus_bool_t get_is_local(DBusConnection *conn) {
+    int conn_fd;
+
+    pa_assert(conn);
+
+    if (!dbus_connection_get_socket(conn, &conn_fd))
+        return FALSE;
+
+    return pa_socket_is_local(conn_fd);
+}
+
+static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    dbus_bool_t is_local;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    is_local = get_is_local(conn);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_local);
+}
+
+static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    char *username = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    username = pa_get_user_name_malloc();
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &username);
+
+    pa_xfree(username);
+}
+
+static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    char *hostname = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    hostname = pa_get_host_name_malloc();
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &hostname);
+
+    pa_xfree(hostname);
+}
+
+/* Caller frees the returned array. */
+static dbus_uint32_t *get_default_channels(pa_dbusiface_core *c, unsigned *n) {
+    dbus_uint32_t *default_channels = NULL;
+    unsigned i;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = c->core->default_channel_map.channels;
+    default_channels = pa_xnew(dbus_uint32_t, *n);
+
+    for (i = 0; i < *n; ++i)
+        default_channels[i] = c->core->default_channel_map.map[i];
+
+    return default_channels;
+}
+
+static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t *default_channels = NULL;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    default_channels = get_default_channels(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, default_channels, n);
+
+    pa_xfree(default_channels);
+}
+
+static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    DBusMessageIter array_iter;
+    pa_channel_map new_channel_map;
+    const dbus_uint32_t *default_channels;
+    int n_channels;
+    unsigned i;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(c);
+
+    pa_channel_map_init(&new_channel_map);
+
+    dbus_message_iter_recurse(iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &default_channels, &n_channels);
+
+    if (n_channels <= 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array.");
+        return;
+    }
+
+    if (n_channels > (int) PA_CHANNELS_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Too many channels: %i. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX);
+        return;
+    }
+
+    new_channel_map.channels = n_channels;
+
+    for (i = 0; i < new_channel_map.channels; ++i) {
+        if (default_channels[i] >= PA_CHANNEL_POSITION_MAX) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u.", default_channels[i]);
+            return;
+        }
+
+        new_channel_map.map[i] = default_channels[i];
+    }
+
+    c->core->default_channel_map = new_channel_map;
+    c->core->default_sample_spec.channels = n_channels;
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t default_sample_format;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    default_sample_format = c->core->default_sample_spec.format;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_format);
+}
+
+static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t default_sample_format;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(c);
+
+    dbus_message_iter_get_basic(iter, &default_sample_format);
+
+    if (!pa_sample_format_valid(default_sample_format)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format.");
+        return;
+    }
+
+    c->core->default_sample_spec.format = default_sample_format;
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t default_sample_rate;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    default_sample_rate = c->core->default_sample_spec.rate;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate);
+}
+
+static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t default_sample_rate;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(c);
+
+    dbus_message_iter_get_basic(iter, &default_sample_rate);
+
+    if (!pa_sample_rate_valid(default_sample_rate)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate.");
+        return;
+    }
+
+    c->core->default_sample_spec.rate = default_sample_rate;
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_alternate_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t alternate_sample_rate;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    alternate_sample_rate = c->core->alternate_sample_rate;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &alternate_sample_rate);
+}
+
+static void handle_set_alternate_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t alternate_sample_rate;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(c);
+
+    dbus_message_iter_get_basic(iter, &alternate_sample_rate);
+
+    if (!pa_sample_rate_valid(alternate_sample_rate)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate.");
+        return;
+    }
+
+    c->core->alternate_sample_rate = alternate_sample_rate;
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_cards(pa_dbusiface_core *c, unsigned *n) {
+    const char **cards;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_card *card;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->cards);
+
+    if (*n == 0)
+        return NULL;
+
+    cards = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(card, c->cards, state)
+        cards[i++] = pa_dbusiface_card_get_path(card);
+
+    return cards;
+}
+
+static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **cards;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    cards = get_cards(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, cards, n);
+
+    pa_xfree(cards);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) {
+    const char **sinks;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_device *sink;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->sinks_by_index);
+
+    if (*n == 0)
+        return NULL;
+
+    sinks = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(sink, c->sinks_by_index, state)
+        sinks[i++] = pa_dbusiface_device_get_path(sink);
+
+    return sinks;
+}
+
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **sinks;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    sinks = get_sinks(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sinks, n);
+
+    pa_xfree(sinks);
+}
+
+static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    pa_dbusiface_device *fallback_sink;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!c->fallback_sink) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sinks, and therefore no fallback sink either.");
+        return;
+    }
+
+    pa_assert_se((fallback_sink = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))));
+    object_path = pa_dbusiface_device_get_path(fallback_sink);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    pa_dbusiface_device *fallback_sink;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(c);
+
+    if (!c->fallback_sink) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sinks, and therefore no fallback sink either.");
+        return;
+    }
+
+    dbus_message_iter_get_basic(iter, &object_path);
+
+    if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", object_path);
+        return;
+    }
+
+    pa_core_set_configured_default_sink(c->core, pa_dbusiface_device_get_sink(fallback_sink)->name);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sources(pa_dbusiface_core *c, unsigned *n) {
+    const char **sources;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_device *source;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->sources_by_index);
+
+    if (*n == 0)
+        return NULL;
+
+    sources = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(source, c->sources_by_index, state)
+        sources[i++] = pa_dbusiface_device_get_path(source);
+
+    return sources;
+}
+
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **sources;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    sources = get_sources(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sources, n);
+
+    pa_xfree(sources);
+}
+
+static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    pa_dbusiface_device *fallback_source;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!c->fallback_source) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sources, and therefore no fallback source either.");
+        return;
+    }
+
+    pa_assert_se((fallback_source = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index))));
+    object_path = pa_dbusiface_device_get_path(fallback_source);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    pa_dbusiface_device *fallback_source;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(c);
+
+    if (!c->fallback_source) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sources, and therefore no fallback source either.");
+        return;
+    }
+
+    dbus_message_iter_get_basic(iter, &object_path);
+
+    if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", object_path);
+        return;
+    }
+
+    pa_core_set_configured_default_source(c->core, pa_dbusiface_device_get_source(fallback_source)->name);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) {
+    const char **streams;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_stream *stream;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->playback_streams);
+
+    if (*n == 0)
+        return NULL;
+
+    streams = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(stream, c->playback_streams, state)
+        streams[i++] = pa_dbusiface_stream_get_path(stream);
+
+    return streams;
+}
+
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **playback_streams;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    playback_streams = get_playback_streams(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, playback_streams, n);
+
+    pa_xfree(playback_streams);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) {
+    const char **streams;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_stream *stream;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->record_streams);
+
+    if (*n == 0)
+        return NULL;
+
+    streams = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(stream, c->record_streams, state)
+        streams[i++] = pa_dbusiface_stream_get_path(stream);
+
+    return streams;
+}
+
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **record_streams;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    record_streams = get_record_streams(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, record_streams, n);
+
+    pa_xfree(record_streams);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_samples(pa_dbusiface_core *c, unsigned *n) {
+    const char **samples;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_sample *sample;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->samples);
+
+    if (*n == 0)
+        return NULL;
+
+    samples = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(sample, c->samples, state)
+        samples[i++] = pa_dbusiface_sample_get_path(sample);
+
+    return samples;
+}
+
+static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **samples;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    samples = get_samples(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, samples, n);
+
+    pa_xfree(samples);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_modules(pa_dbusiface_core *c, unsigned *n) {
+    const char **modules;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_module *module;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->modules);
+
+    if (*n == 0)
+        return NULL;
+
+    modules = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(module, c->modules, state)
+        modules[i++] = pa_dbusiface_module_get_path(module);
+
+    return modules;
+}
+
+static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **modules;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    modules = get_modules(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, modules, n);
+
+    pa_xfree(modules);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_clients(pa_dbusiface_core *c, unsigned *n) {
+    const char **clients;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_client *client;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->clients);
+
+    if (*n == 0)
+        return NULL;
+
+    clients = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(client, c->clients, state)
+        clients[i++] = pa_dbusiface_client_get_path(client);
+
+    return clients;
+}
+
+static void handle_get_clients(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **clients;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    clients = get_clients(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, clients, n);
+
+    pa_xfree(clients);
+}
+
+static const char *get_my_client(pa_dbusiface_core *c, DBusConnection *conn) {
+    pa_client *my_client;
+
+    pa_assert(c);
+    pa_assert(conn);
+
+    pa_assert_se((my_client = pa_dbus_protocol_get_client(c->dbus_protocol, conn)));
+
+    return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(my_client->index)));
+}
+
+static void handle_get_my_client(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char *my_client;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    my_client = get_my_client(c, conn);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &my_client);
+}
+
+static void handle_get_extensions(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **extensions;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    extensions = pa_dbus_protocol_get_extensions(c->dbus_protocol, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, extensions, n);
+
+    pa_xfree(extensions);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t interface_revision;
+    const char *server_name;
+    const char *version;
+    dbus_bool_t is_local;
+    char *username;
+    char *hostname;
+    dbus_uint32_t *default_channels;
+    unsigned n_default_channels;
+    dbus_uint32_t default_sample_format;
+    dbus_uint32_t default_sample_rate;
+    dbus_uint32_t alternate_sample_rate;
+    const char **cards;
+    unsigned n_cards;
+    const char **sinks;
+    unsigned n_sinks;
+    const char *fallback_sink;
+    const char **sources;
+    unsigned n_sources;
+    const char *fallback_source;
+    const char **playback_streams;
+    unsigned n_playback_streams;
+    const char **record_streams;
+    unsigned n_record_streams;
+    const char **samples;
+    unsigned n_samples;
+    const char **modules;
+    unsigned n_modules;
+    const char **clients;
+    unsigned n_clients;
+    const char *my_client;
+    const char **extensions;
+    unsigned n_extensions;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    interface_revision = INTERFACE_REVISION;
+    server_name = PACKAGE_NAME;
+    version = PACKAGE_VERSION;
+    is_local = get_is_local(conn);
+    username = pa_get_user_name_malloc();
+    hostname = pa_get_host_name_malloc();
+    default_channels = get_default_channels(c, &n_default_channels);
+    default_sample_format = c->core->default_sample_spec.format;
+    default_sample_rate = c->core->default_sample_spec.rate;
+    alternate_sample_rate = c->core->alternate_sample_rate;
+    cards = get_cards(c, &n_cards);
+    sinks = get_sinks(c, &n_sinks);
+    fallback_sink = c->fallback_sink
+                    ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index)))
+                    : NULL;
+    sources = get_sources(c, &n_sources);
+    fallback_source = c->fallback_source
+                      ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index,
+                                                                    PA_UINT32_TO_PTR(c->fallback_source->index)))
+                      : NULL;
+    playback_streams = get_playback_streams(c, &n_playback_streams);
+    record_streams = get_record_streams(c, &n_record_streams);
+    samples = get_samples(c, &n_samples);
+    modules = get_modules(c, &n_modules);
+    clients = get_clients(c, &n_clients);
+    my_client = get_my_client(c, conn);
+    extensions = pa_dbus_protocol_get_extensions(c->dbus_protocol, &n_extensions);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INTERFACE_REVISION].property_name, DBUS_TYPE_UINT32, &interface_revision);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &server_name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VERSION].property_name, DBUS_TYPE_STRING, &version);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_LOCAL].property_name, DBUS_TYPE_BOOLEAN, &is_local);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_USERNAME].property_name, DBUS_TYPE_STRING, &username);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HOSTNAME].property_name, DBUS_TYPE_STRING, &hostname);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_CHANNELS].property_name, DBUS_TYPE_UINT32, default_channels, n_default_channels);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &default_sample_format);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &default_sample_rate);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ALTERNATE_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &alternate_sample_rate);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CARDS].property_name, DBUS_TYPE_OBJECT_PATH, cards, n_cards);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks);
+
+    if (fallback_sink)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FALLBACK_SINK].property_name, DBUS_TYPE_OBJECT_PATH, &fallback_sink);
+
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources);
+
+    if (fallback_source)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FALLBACK_SOURCE].property_name, DBUS_TYPE_OBJECT_PATH, &fallback_source);
+
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PLAYBACK_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RECORD_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLES].property_name, DBUS_TYPE_OBJECT_PATH, samples, n_samples);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MODULES].property_name, DBUS_TYPE_OBJECT_PATH, modules, n_modules);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENTS].property_name, DBUS_TYPE_OBJECT_PATH, clients, n_clients);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MY_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &my_client);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_EXTENSIONS].property_name, DBUS_TYPE_STRING, extensions, n_extensions);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(username);
+    pa_xfree(hostname);
+    pa_xfree(default_channels);
+    pa_xfree(cards);
+    pa_xfree(sinks);
+    pa_xfree(sources);
+    pa_xfree(playback_streams);
+    pa_xfree(record_streams);
+    pa_xfree(samples);
+    pa_xfree(modules);
+    pa_xfree(clients);
+    pa_xfree(extensions);
+}
+
+static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    char *card_name;
+    pa_card *card;
+    pa_dbusiface_card *dbus_card;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID));
+
+    if (!(card = pa_namereg_get(c->core, card_name, PA_NAMEREG_CARD))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such card.");
+        return;
+    }
+
+    pa_assert_se((dbus_card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(card->index))));
+
+    object_path = pa_dbusiface_card_get_path(dbus_card);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    char *sink_name;
+    pa_sink *sink;
+    pa_dbusiface_device *dbus_sink;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID));
+
+    if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_name);
+        return;
+    }
+
+    pa_assert_se((dbus_sink = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(sink->index))));
+
+    object_path = pa_dbusiface_device_get_path(dbus_sink);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    char *source_name;
+    pa_source *source;
+    pa_dbusiface_device *dbus_source;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID));
+
+    if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", source_name);
+        return;
+    }
+
+    pa_assert_se((dbus_source = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index))));
+
+    object_path = pa_dbusiface_device_get_path(dbus_source);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    char *sample_name;
+    pa_scache_entry *sample;
+    pa_dbusiface_sample *dbus_sample;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID));
+
+    if (!(sample = pa_namereg_get(c->core, sample_name, PA_NAMEREG_SAMPLE))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sample.");
+        return;
+    }
+
+    pa_assert_se((dbus_sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(sample->index))));
+
+    object_path = pa_dbusiface_sample_get_path(dbus_sample);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    DBusMessageIter msg_iter;
+    DBusMessageIter array_iter;
+    const char *name;
+    dbus_uint32_t sample_format;
+    dbus_uint32_t sample_rate;
+    const dbus_uint32_t *channels;
+    int n_channels;
+    const dbus_uint32_t *default_volume;
+    int n_volume_entries;
+    pa_proplist *property_list;
+    const uint8_t *data;
+    int data_length;
+    int i;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_memchunk chunk;
+    uint32_t idx;
+    pa_dbusiface_sample *dbus_sample = NULL;
+    pa_scache_entry *sample = NULL;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    chunk.memblock = NULL;
+
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &name);
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &sample_format);
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &sample_rate);
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_recurse(&msg_iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &channels, &n_channels);
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_recurse(&msg_iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &default_volume, &n_volume_entries);
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+        return;
+
+    dbus_message_iter_recurse(&msg_iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &data, &data_length);
+
+    if (!pa_sample_format_valid(sample_format)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format.");
+        goto finish;
+    }
+
+    if (!pa_sample_rate_valid(sample_rate)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate.");
+        goto finish;
+    }
+
+    if (n_channels <= 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel map.");
+        goto finish;
+    }
+
+    if (n_channels > (int) PA_CHANNELS_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Too many channels: %i. The maximum is %u.", n_channels, PA_CHANNELS_MAX);
+        goto finish;
+    }
+
+    for (i = 0; i < n_channels; ++i) {
+        if (channels[i] >= PA_CHANNEL_POSITION_MAX) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position.");
+            goto finish;
+        }
+    }
+
+    if (n_volume_entries != 0 && n_volume_entries != n_channels) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "The channels and default_volume arguments have different number of elements (%i and %i, resp).",
+                           n_channels, n_volume_entries);
+        goto finish;
+    }
+
+    for (i = 0; i < n_volume_entries; ++i) {
+        if (!PA_VOLUME_IS_VALID(default_volume[i])) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u.", default_volume[i]);
+            goto finish;
+        }
+    }
+
+    if (data_length == 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty data.");
+        goto finish;
+    }
+
+    if (data_length > PA_SCACHE_ENTRY_SIZE_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Too big sample: %i bytes. The maximum sample length is %u bytes.",
+                           data_length, PA_SCACHE_ENTRY_SIZE_MAX);
+        goto finish;
+    }
+
+    ss.format = sample_format;
+    ss.rate = sample_rate;
+    ss.channels = n_channels;
+
+    pa_assert(pa_sample_spec_valid(&ss));
+
+    if (!pa_frame_aligned(data_length, &ss)) {
+        char buf[PA_SAMPLE_SPEC_SNPRINT_MAX];
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "The sample length (%i bytes) doesn't align with the sample format and channels (%s).",
+                           data_length, pa_sample_spec_snprint(buf, sizeof(buf), &ss));
+        goto finish;
+    }
+
+    map.channels = n_channels;
+    for (i = 0; i < n_channels; ++i)
+        map.map[i] = channels[i];
+
+    chunk.memblock = pa_memblock_new(c->core->mempool, data_length);
+    chunk.index = 0;
+    chunk.length = data_length;
+
+    memcpy(pa_memblock_acquire(chunk.memblock), data, data_length);
+    pa_memblock_release(chunk.memblock);
+
+    if (pa_scache_add_item(c->core, name, &ss, &map, &chunk, property_list, &idx) < 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Adding the sample failed.");
+        goto finish;
+    }
+
+    pa_assert_se(sample = pa_idxset_get_by_index(c->core->scache, idx));
+
+    if (n_volume_entries > 0) {
+        sample->volume.channels = n_channels;
+        for (i = 0; i < n_volume_entries; ++i)
+            sample->volume.values[i] = default_volume[i];
+        sample->volume_is_set = true;
+    } else {
+        sample->volume_is_set = false;
+    }
+
+    dbus_sample = pa_dbusiface_sample_new(c, sample);
+    pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), dbus_sample);
+
+    object_path = pa_dbusiface_sample_get_path(dbus_sample);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+
+finish:
+    if (property_list)
+        pa_proplist_free(property_list);
+
+    if (chunk.memblock)
+        pa_memblock_unref(chunk.memblock);
+}
+
+static bool contains_space(const char *string) {
+    const char *p;
+
+    pa_assert(string);
+
+    for (p = string; *p; ++p) {
+        if (isspace((unsigned char)*p))
+            return true;
+    }
+
+    return false;
+}
+
+static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    char *name = NULL;
+    const char *key = NULL;
+    const char *value = NULL;
+    char *escaped_value = NULL;
+    pa_strbuf *arg_buffer = NULL;
+    char *arg_string = NULL;
+    pa_module *module = NULL;
+    pa_dbusiface_module *dbus_module = NULL;
+    const char *object_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (c->core->disallow_module_loading) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow module loading.");
+        return;
+    }
+
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &name);
+
+    arg_buffer = pa_strbuf_new();
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_recurse(&msg_iter, &dict_iter);
+
+    while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
+        if (!pa_strbuf_isempty(arg_buffer))
+            pa_strbuf_putc(arg_buffer, ' ');
+
+        dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
+
+        dbus_message_iter_get_basic(&dict_entry_iter, &key);
+
+        if (strlen(key) <= 0 || !pa_ascii_valid(key) || contains_space(key)) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name: %s", key);
+            goto finish;
+        }
+
+        pa_assert_se(dbus_message_iter_next(&dict_entry_iter));
+        dbus_message_iter_get_basic(&dict_entry_iter, &value);
+
+        escaped_value = pa_escape(value, "\"");
+        pa_strbuf_printf(arg_buffer, "%s=\"%s\"", key, escaped_value);
+        pa_xfree(escaped_value);
+
+        dbus_message_iter_next(&dict_iter);
+    }
+
+    arg_string = pa_strbuf_to_string(arg_buffer);
+
+    if (!(module = pa_module_load(c->core, name, arg_string))) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Failed to load module.");
+        goto finish;
+    }
+
+    /* This is created during module loading in module_new_cb() */
+    dbus_module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(module->index));
+    object_path = pa_dbusiface_module_get_path(dbus_module);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+
+finish:
+    if (arg_buffer)
+        pa_strbuf_free(arg_buffer);
+
+    pa_xfree(arg_string);
+}
+
+static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (c->core->disallow_exit) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow exiting.");
+        return;
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_core_exit(c->core, false, 0);
+}
+
+static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char *signal_str;
+    char **objects = NULL;
+    int n_objects;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_assert_se(dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_STRING, &signal_str,
+                                       DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects,
+                                       DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_add_signal_listener(c->dbus_protocol, conn, *signal_str ? signal_str : NULL, objects, n_objects);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    dbus_free_string_array(objects);
+}
+
+static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char *signal_str;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &signal_str, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_remove_signal_listener(c->dbus_protocol, conn, *signal_str ? signal_str : NULL);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static pa_hook_result_t module_new_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_module * module = call_data;
+    pa_dbusiface_module *module_iface;
+    const char *object_path;
+    DBusMessage *signal_msg = NULL;
+
+    pa_assert(c);
+    pa_assert(module);
+
+    if (pa_streq(module->name, "module-dbus-protocol")) {
+        /* module-dbus-protocol can only be loaded once, and will be accounted
+         * for while iterating core->modules in pa_dbusiface_core_new(). As it
+         * happens, we will also see it here when the hook is called after the
+         * module is initialised, so we ignore it. */
+        return PA_HOOK_OK;
+    }
+
+    module_iface = pa_dbusiface_module_new(module);
+    pa_assert_se(pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), module_iface) >= 0);
+
+    object_path = pa_dbusiface_module_get_path(module_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_NEW_MODULE].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t module_removed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_module * module = call_data;
+    pa_dbusiface_module *module_iface;
+    const char *object_path;
+    DBusMessage *signal_msg = NULL;
+
+    pa_assert(c);
+    pa_assert(module);
+
+    pa_assert_se((module_iface = pa_hashmap_remove(c->modules, PA_UINT32_TO_PTR(module->index))));
+
+    object_path = pa_dbusiface_module_get_path(module_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_MODULE_REMOVED].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbusiface_module_free(module_iface);
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sample_cache_new_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_scache_entry *sample = call_data;
+    pa_dbusiface_sample *sample_iface;
+    const char *object_path;
+    DBusMessage *signal_msg = NULL;
+
+    pa_assert(c);
+    pa_assert(sample);
+
+    sample_iface = pa_dbusiface_sample_new(c, sample);
+    pa_assert_se(pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(sample->index), sample_iface) >= 0);
+
+    object_path = pa_dbusiface_sample_get_path(sample_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_NEW_SAMPLE].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sample_cache_removed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_scache_entry *sample = call_data;
+    pa_dbusiface_sample *sample_iface;
+    const char *object_path;
+    DBusMessage *signal_msg = NULL;
+
+    pa_assert(c);
+    pa_assert(sample);
+
+    pa_assert_se((sample_iface = pa_hashmap_remove(c->samples, PA_UINT32_TO_PTR(sample->index))));
+
+    object_path = pa_dbusiface_sample_get_path(sample_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_SAMPLE_REMOVED].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbusiface_sample_free(sample_iface);
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_dbusiface_device *create_dbus_object_for_sink(pa_dbusiface_core *c, pa_sink *s) {
+    pa_dbusiface_device *d;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    d = pa_dbusiface_device_new_sink(c, s);
+    object_path = pa_dbusiface_device_get_path(d);
+
+    pa_assert_se(pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(s->index), d) >= 0);
+    pa_assert_se(pa_hashmap_put(c->sinks_by_path, (char *) object_path, d) >= 0);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_NEW_SINK].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return d;
+}
+
+static pa_hook_result_t default_sink_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_sink *new_fallback_sink = call_data;
+    pa_dbusiface_device *device_iface;
+    const char *object_path;
+    DBusMessage *signal_msg = NULL;
+
+    pa_assert(c);
+
+    if (c->fallback_sink != new_fallback_sink) {
+        if (c->fallback_sink)
+            pa_sink_unref(c->fallback_sink);
+        c->fallback_sink = new_fallback_sink ? pa_sink_ref(new_fallback_sink) : NULL;
+
+        if (c->fallback_sink) {
+            device_iface = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index));
+
+            /* It's possible that we haven't created a dbus object for the
+             * source yet, because if a new source immediately becomes the
+             * default source, the default source change hook is fired before
+             * the put hook. */
+            if (!device_iface)
+                device_iface = create_dbus_object_for_sink(c, c->fallback_sink);
+
+            object_path = pa_dbusiface_device_get_path(device_iface);
+
+            pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+            pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+        } else {
+            pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_FALLBACK_SINK_UNSET].name)));
+        }
+    }
+
+    if (signal_msg) {
+        pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_dbusiface_device *create_dbus_object_for_source(pa_dbusiface_core *c, pa_source *s) {
+    pa_dbusiface_device *d;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    d = pa_dbusiface_device_new_source(c, s);
+    object_path = pa_dbusiface_device_get_path(d);
+
+    pa_assert_se(pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(s->index), d) >= 0);
+    pa_assert_se(pa_hashmap_put(c->sources_by_path, (char *) object_path, d) >= 0);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_NEW_SOURCE].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return d;
+}
+
+static pa_hook_result_t default_source_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_source *new_fallback_source = call_data;
+    pa_dbusiface_device *device_iface;
+    const char *object_path;
+    DBusMessage *signal_msg = NULL;
+
+    pa_assert(c);
+
+    if (c->fallback_source != new_fallback_source) {
+        if (c->fallback_source)
+            pa_source_unref(c->fallback_source);
+        c->fallback_source = new_fallback_source ? pa_source_ref(new_fallback_source) : NULL;
+
+        if (c->fallback_source) {
+            device_iface = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index));
+
+            /* It's possible that we haven't created a dbus object for the
+             * source yet, because if a new source immediately becomes the
+             * default source, the default source change hook is fired before
+             * the put hook. */
+            if (!device_iface)
+                device_iface = create_dbus_object_for_source(c, c->fallback_source);
+
+            object_path = pa_dbusiface_device_get_path(device_iface);
+
+            pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+            pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+        } else {
+            pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_FALLBACK_SOURCE_UNSET].name)));
+        }
+    }
+
+    if (signal_msg) {
+        pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_put_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_card *card = call_data;
+    pa_dbusiface_card *card_iface = NULL;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    pa_assert(c);
+    pa_assert(card);
+
+    card_iface = pa_dbusiface_card_new(c, card);
+    pa_assert_se(pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(card->index), card_iface) >= 0);
+
+    object_path = pa_dbusiface_card_get_path(card_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_NEW_CARD].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_card *card = call_data;
+    pa_dbusiface_card *card_iface;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    pa_assert(c);
+    pa_assert(card);
+
+    pa_assert_se((card_iface = pa_hashmap_remove(c->cards, PA_UINT32_TO_PTR(card->index))));
+
+    object_path = pa_dbusiface_card_get_path(card_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_CARD_REMOVED].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbusiface_card_free(card_iface);
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_put_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_sink_input *sink_input = call_data;
+    pa_dbusiface_stream *stream_iface;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    pa_assert(c);
+    pa_assert(sink_input);
+
+    stream_iface = pa_dbusiface_stream_new_playback(c, sink_input);
+    pa_assert_se(pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(sink_input->index), stream_iface) >= 0);
+
+    object_path = pa_dbusiface_stream_get_path(stream_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_NEW_PLAYBACK_STREAM].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_sink_input *sink_input = call_data;
+    pa_dbusiface_stream *stream_iface;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    pa_assert(c);
+    pa_assert(sink_input);
+
+    pa_assert_se((stream_iface = pa_hashmap_remove(c->playback_streams, PA_UINT32_TO_PTR(sink_input->index))));
+
+    object_path = pa_dbusiface_stream_get_path(stream_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbusiface_stream_free(stream_iface);
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_put_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_source_output *source_output = call_data;
+    pa_dbusiface_stream *stream_iface;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    pa_assert(c);
+    pa_assert(source_output);
+
+    stream_iface = pa_dbusiface_stream_new_record(c, source_output);
+    pa_assert_se(pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(source_output->index), stream_iface) >= 0);
+
+    object_path = pa_dbusiface_stream_get_path(stream_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_NEW_RECORD_STREAM].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_source_output *source_output = call_data;
+    pa_dbusiface_stream *stream_iface;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    pa_assert(c);
+    pa_assert(source_output);
+
+    pa_assert_se((stream_iface = pa_hashmap_remove(c->record_streams, PA_UINT32_TO_PTR(source_output->index))));
+
+    object_path = pa_dbusiface_stream_get_path(stream_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbusiface_stream_free(stream_iface);
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t client_put_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_client *client = call_data;
+    pa_dbusiface_client *client_iface;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    pa_assert(c);
+    pa_assert(client);
+
+    client_iface = pa_dbusiface_client_new(c, client);
+    pa_assert_se(pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(client->index), client_iface) >= 0);
+
+    object_path = pa_dbusiface_client_get_path(client_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_NEW_CLIENT].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t client_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_client *client = call_data;
+    pa_dbusiface_client *client_iface;
+    const char *object_path;
+    DBusMessage *signal_msg;
+
+    pa_assert(c);
+    pa_assert(client);
+
+    pa_assert_se((client_iface = pa_hashmap_remove(c->clients, PA_UINT32_TO_PTR(client->index))));
+
+    object_path = pa_dbusiface_client_get_path(client_iface);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_CLIENT_REMOVED].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbusiface_client_free(client_iface);
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_put_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_sink *s = call_data;
+
+    pa_assert(c);
+    pa_assert(s);
+
+    /* We may have alredy encountered this sink, because if the new sink was
+     * chosen as the default sink, the default sink change hook was fired
+     * first, and we saw the sink in default_sink_changed_cb(). */
+    if (pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(s->index)))
+        return PA_HOOK_OK;
+
+    create_dbus_object_for_sink(c, s);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_sink *s = call_data;
+    pa_dbusiface_device *d = NULL;
+    const char *object_path = NULL;
+    DBusMessage *signal_msg = NULL;
+
+    pa_assert(c);
+    pa_assert(s);
+
+    pa_assert_se(d = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(s->index)));
+    object_path = pa_dbusiface_device_get_path(d);
+    pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path));
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                      PA_DBUS_CORE_INTERFACE,
+                                                      signals[SIGNAL_SINK_REMOVED].name));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    pa_dbusiface_device_free(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_put_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_source *s = call_data;
+
+    pa_assert(c);
+    pa_assert(s);
+
+    /* We may have alredy encountered this source, because if the new source
+     * was chosen as the default source, the default source change hook was
+     * fired first, and we saw the source in default_source_changed_cb(). */
+    if (pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(s->index)))
+        return PA_HOOK_OK;
+
+    create_dbus_object_for_source(c, s);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_unlink_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    pa_source *s = call_data;
+    pa_dbusiface_device *d = NULL;
+    const char *object_path = NULL;
+    DBusMessage *signal_msg = NULL;
+
+    pa_assert(c);
+    pa_assert(s);
+
+    pa_assert_se(d = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(s->index)));
+    object_path = pa_dbusiface_device_get_path(d);
+    pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path));
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                      PA_DBUS_CORE_INTERFACE,
+                                                      signals[SIGNAL_SOURCE_REMOVED].name));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    pa_dbusiface_device_free(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    const char *ext_name = call_data;
+    DBusMessage *signal_msg = NULL;
+
+    pa_assert(c);
+    pa_assert(ext_name);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_NEW_EXTENSION].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    const char *ext_name = call_data;
+    DBusMessage *signal_msg = NULL;
+
+    pa_assert(c);
+    pa_assert(ext_name);
+
+    pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                       PA_DBUS_CORE_INTERFACE,
+                                                       signals[SIGNAL_EXTENSION_REMOVED].name)));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
+    pa_dbusiface_core *c;
+    pa_card *card;
+    pa_sink *sink;
+    pa_source *source;
+    pa_dbusiface_device *device;
+    pa_sink_input *sink_input;
+    pa_source_output *source_output;
+    pa_scache_entry *sample;
+    pa_module *module;
+    pa_client *client;
+    uint32_t idx;
+
+    pa_assert(core);
+
+    c = pa_xnew(pa_dbusiface_core, 1);
+    c->core = core;
+    c->dbus_protocol = pa_dbus_protocol_get(core);
+    c->cards = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_card_free);
+    c->sinks_by_index = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+                                            (pa_free_cb_t) pa_dbusiface_device_free);
+    c->sinks_by_path = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    c->sources_by_index = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+                                              (pa_free_cb_t) pa_dbusiface_device_free);
+    c->sources_by_path = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    c->playback_streams = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+                                              (pa_free_cb_t) pa_dbusiface_stream_free);
+    c->record_streams = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+                                            (pa_free_cb_t) pa_dbusiface_stream_free);
+    c->samples = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_sample_free);
+    c->modules = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_module_free);
+    c->clients = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_client_free);
+    c->fallback_sink = core->default_sink;
+    c->fallback_source = core->default_source;
+    c->default_sink_changed_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED],
+                                                   PA_HOOK_NORMAL, default_sink_changed_cb, c);
+    c->default_source_changed_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED],
+                                                     PA_HOOK_NORMAL, default_source_changed_cb, c);
+    c->module_new_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_MODULE_NEW],
+                                         PA_HOOK_NORMAL, module_new_cb, c);
+    c->module_removed_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_MODULE_UNLINK],
+                                             PA_HOOK_NORMAL, module_removed_cb, c);
+    c->sample_cache_new_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SAMPLE_CACHE_NEW],
+                                               PA_HOOK_NORMAL, sample_cache_new_cb, c);
+    c->sample_cache_removed_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SAMPLE_CACHE_UNLINK],
+                                                   PA_HOOK_NORMAL, sample_cache_removed_cb, c);
+    c->card_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_CARD_PUT],
+                                       PA_HOOK_NORMAL, card_put_cb, c);
+    c->card_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_CARD_UNLINK],
+                                          PA_HOOK_NORMAL, card_unlink_cb, c);
+    c->sink_input_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT],
+                                             PA_HOOK_NORMAL, sink_input_put_cb, c);
+    c->sink_input_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK],
+                                                PA_HOOK_NORMAL, sink_input_unlink_cb, c);
+    c->source_output_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT],
+                                                PA_HOOK_NORMAL, source_output_put_cb, c);
+    c->source_output_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK],
+                                                   PA_HOOK_NORMAL, source_output_unlink_cb, c);
+    c->client_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_CLIENT_PUT],
+                                         PA_HOOK_NORMAL, client_put_cb, c);
+    c->client_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_CLIENT_UNLINK],
+                                            PA_HOOK_NORMAL, client_unlink_cb, c);
+    c->sink_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, sink_put_cb, c);
+    c->sink_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_NORMAL, sink_unlink_cb, c);
+    c->source_put_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, source_put_cb, c);
+    c->source_unlink_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_NORMAL, source_unlink_cb, c);
+    c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol,
+                                                                 PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED,
+                                                                 PA_HOOK_NORMAL,
+                                                                 extension_registered_cb,
+                                                                 c);
+    c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol,
+                                                                   PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED,
+                                                                   PA_HOOK_NORMAL,
+                                                                   extension_unregistered_cb,
+                                                                   c);
+    c->memstats = pa_dbusiface_memstats_new(c, core);
+
+    if (c->fallback_sink)
+        pa_sink_ref(c->fallback_sink);
+    if (c->fallback_source)
+        pa_source_ref(c->fallback_source);
+
+    PA_IDXSET_FOREACH(card, core->cards, idx)
+        pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card));
+
+    PA_IDXSET_FOREACH(sink, core->sinks, idx) {
+        device = pa_dbusiface_device_new_sink(c, sink);
+        pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
+        pa_hashmap_put(c->sinks_by_path, (char *) pa_dbusiface_device_get_path(device), device);
+    }
+
+    PA_IDXSET_FOREACH(source, core->sources, idx) {
+        device = pa_dbusiface_device_new_source(c, source);
+        pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
+        pa_hashmap_put(c->sources_by_path, (char *) pa_dbusiface_device_get_path(device), device);
+    }
+
+    PA_IDXSET_FOREACH(sink_input, core->sink_inputs, idx)
+        pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(c, sink_input));
+
+    PA_IDXSET_FOREACH(source_output, core->source_outputs, idx)
+        pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(c, source_output));
+
+    PA_IDXSET_FOREACH(sample, core->scache, idx)
+        pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample));
+
+    PA_IDXSET_FOREACH(module, core->modules, idx)
+        pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module));
+
+    PA_IDXSET_FOREACH(client, core->clients, idx)
+        pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client));
+
+    pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c) >= 0);
+
+    return c;
+}
+
+void pa_dbusiface_core_free(pa_dbusiface_core *c) {
+    pa_assert(c);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name) >= 0);
+
+    /* Note that the order of freeing is important below.
+     * Do not change it for the sake of tidiness without checking! */
+    pa_hashmap_free(c->cards);
+    pa_hashmap_free(c->sinks_by_path);
+    pa_hashmap_free(c->sinks_by_index);
+    pa_hashmap_free(c->sources_by_path);
+    pa_hashmap_free(c->sources_by_index);
+    pa_hashmap_free(c->playback_streams);
+    pa_hashmap_free(c->record_streams);
+    pa_hashmap_free(c->samples);
+    pa_hashmap_free(c->modules);
+    pa_hashmap_free(c->clients);
+    pa_hook_slot_free(c->module_new_slot);
+    pa_hook_slot_free(c->module_removed_slot);
+    pa_hook_slot_free(c->default_sink_changed_slot);
+    pa_hook_slot_free(c->default_source_changed_slot);
+    pa_hook_slot_free(c->sample_cache_new_slot);
+    pa_hook_slot_free(c->sample_cache_removed_slot);
+    pa_hook_slot_free(c->card_put_slot);
+    pa_hook_slot_free(c->card_unlink_slot);
+    pa_hook_slot_free(c->sink_input_put_slot);
+    pa_hook_slot_free(c->sink_input_unlink_slot);
+    pa_hook_slot_free(c->source_output_put_slot);
+    pa_hook_slot_free(c->source_output_unlink_slot);
+    pa_hook_slot_free(c->client_put_slot);
+    pa_hook_slot_free(c->client_unlink_slot);
+    pa_hook_slot_free(c->sink_put_slot);
+    pa_hook_slot_free(c->sink_unlink_slot);
+    pa_hook_slot_free(c->source_put_slot);
+    pa_hook_slot_free(c->source_unlink_slot);
+    pa_hook_slot_free(c->extension_registered_slot);
+    pa_hook_slot_free(c->extension_unregistered_slot);
+    pa_dbusiface_memstats_free(c->memstats);
+
+    if (c->fallback_sink)
+        pa_sink_unref(c->fallback_sink);
+    if (c->fallback_source)
+        pa_source_unref(c->fallback_source);
+
+    pa_dbus_protocol_unref(c->dbus_protocol);
+
+    pa_xfree(c);
+}
+
+const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card) {
+    pa_assert(c);
+    pa_assert(card);
+
+    return pa_dbusiface_card_get_path(pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(card->index)));
+}
+
+const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink) {
+    pa_assert(c);
+    pa_assert(sink);
+
+    return pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(sink->index)));
+}
+
+const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source) {
+    pa_assert(c);
+    pa_assert(source);
+
+    return pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index)));
+}
+
+const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input) {
+    pa_assert(c);
+    pa_assert(sink_input);
+
+    return pa_dbusiface_stream_get_path(pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(sink_input->index)));
+}
+
+const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output) {
+    pa_assert(c);
+    pa_assert(source_output);
+
+    return pa_dbusiface_stream_get_path(pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(source_output->index)));
+}
+
+const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module) {
+    pa_assert(c);
+    pa_assert(module);
+
+    return pa_dbusiface_module_get_path(pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(module->index)));
+}
+
+const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client) {
+    pa_assert(c);
+    pa_assert(client);
+
+    return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(client->index)));
+}
+
+pa_sink *pa_dbusiface_core_get_sink(pa_dbusiface_core *c, const char *object_path) {
+    pa_dbusiface_device *device = NULL;
+
+    pa_assert(c);
+    pa_assert(object_path);
+
+    device = pa_hashmap_get(c->sinks_by_path, object_path);
+
+    if (device)
+        return pa_dbusiface_device_get_sink(device);
+    else
+        return NULL;
+}
+
+pa_source *pa_dbusiface_core_get_source(pa_dbusiface_core *c, const char *object_path) {
+    pa_dbusiface_device *device = NULL;
+
+    pa_assert(c);
+    pa_assert(object_path);
+
+    device = pa_hashmap_get(c->sources_by_path, object_path);
+
+    if (device)
+        return pa_dbusiface_device_get_source(device);
+    else
+        return NULL;
+}
diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h
new file mode 100644 (file)
index 0000000..2959fc8
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef foodbusifacecorehfoo
+#define foodbusifacecorehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Core/
+ * for the Core interface documentation.
+ */
+
+#include <pulsecore/core.h>
+
+typedef struct pa_dbusiface_core pa_dbusiface_core;
+
+pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core);
+void pa_dbusiface_core_free(pa_dbusiface_core *c);
+
+const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card);
+const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink);
+const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source);
+const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input);
+const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output);
+const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module);
+const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client);
+
+/* Returns NULL if there's no sink with the given path. */
+pa_sink *pa_dbusiface_core_get_sink(pa_dbusiface_core *c, const char *object_path);
+
+/* Returns NULL if there's no source with the given path. */
+pa_source *pa_dbusiface_core_get_source(pa_dbusiface_core *c, const char *object_path);
+
+#endif
diff --git a/src/modules/dbus/iface-device-port.c b/src/modules/dbus/iface-device-port.c
new file mode 100644 (file)
index 0000000..4892443
--- /dev/null
@@ -0,0 +1,249 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+
+#include "iface-device-port.h"
+
+#define OBJECT_NAME "port"
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_available(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_device_port {
+    uint32_t index;
+    pa_device_port *port;
+    char *path;
+
+    pa_hook_slot *available_changed_slot;
+
+    pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_DESCRIPTION,
+    PROPERTY_HANDLER_PRIORITY,
+    PROPERTY_HANDLER_AVAILABLE,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]       = { .property_name = "Index",       .type = "u", .get_cb = handle_get_index,       .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]        = { .property_name = "Name",        .type = "s", .get_cb = handle_get_name,        .set_cb = NULL },
+    [PROPERTY_HANDLER_DESCRIPTION] = { .property_name = "Description", .type = "s", .get_cb = handle_get_description, .set_cb = NULL },
+    [PROPERTY_HANDLER_PRIORITY]    = { .property_name = "Priority",    .type = "u", .get_cb = handle_get_priority,    .set_cb = NULL },
+    [PROPERTY_HANDLER_AVAILABLE]   = { .property_name = "Available",   .type = "u", .get_cb = handle_get_available,   .set_cb = NULL }
+};
+
+enum signal_index {
+    SIGNAL_AVAILABLE_CHANGED,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info available_changed_args[] = { { "available", "u", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_AVAILABLE_CHANGED] = { .name = "AvailableChanged", .arguments = available_changed_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info port_interface_info = {
+    .name = PA_DBUSIFACE_DEVICE_PORT_INTERFACE,
+    .method_handlers = NULL,
+    .n_method_handlers = 0,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device_port *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &p->index);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device_port *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->name);
+}
+
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device_port *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->description);
+}
+
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device_port *p = userdata;
+    dbus_uint32_t priority = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    priority = p->port->priority;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &priority);
+}
+
+static void handle_get_available(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device_port *p = userdata;
+    dbus_uint32_t available = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    available = p->port->available;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &available);
+}
+
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device_port *p = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t priority = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    priority = p->port->priority;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &p->index);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &p->port->name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DESCRIPTION].property_name, DBUS_TYPE_STRING, &p->port->description);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PRIORITY].property_name, DBUS_TYPE_UINT32, &priority);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_AVAILABLE].property_name, DBUS_TYPE_UINT32, &p->port->available);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+static pa_hook_result_t available_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_device_port *p = slot_data;
+    pa_device_port *port = call_data;
+    DBusMessage *signal_msg;
+    uint32_t available;
+
+    pa_assert(p);
+    pa_assert(port);
+
+    if(p->port != port)
+        return PA_HOOK_OK;
+
+    available = port->available;
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(p->path,
+                                                      PA_DBUSIFACE_DEVICE_PORT_INTERFACE,
+                                                      signals[SIGNAL_AVAILABLE_CHANGED].name));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_UINT32, &available, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(p->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+
+pa_dbusiface_device_port *pa_dbusiface_device_port_new(
+        pa_dbusiface_device *device,
+        pa_core *core,
+        pa_device_port *port,
+        uint32_t idx) {
+    pa_dbusiface_device_port *p = NULL;
+
+    pa_assert(device);
+    pa_assert(core);
+    pa_assert(port);
+
+    p = pa_xnew(pa_dbusiface_device_port, 1);
+    p->index = idx;
+    p->port = port;
+    p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_device_get_path(device), OBJECT_NAME, idx);
+    p->dbus_protocol = pa_dbus_protocol_get(core);
+    p->available_changed_slot = pa_hook_connect(&port->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED],
+                                                PA_HOOK_NORMAL, available_changed_cb, p);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(p->dbus_protocol, p->path, &port_interface_info, p) >= 0);
+
+    return p;
+}
+
+void pa_dbusiface_device_port_free(pa_dbusiface_device_port *p) {
+    pa_assert(p);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(p->dbus_protocol, p->path, port_interface_info.name) >= 0);
+
+    pa_hook_slot_free(p->available_changed_slot);
+    pa_dbus_protocol_unref(p->dbus_protocol);
+
+    pa_xfree(p->path);
+    pa_xfree(p);
+}
+
+const char *pa_dbusiface_device_port_get_path(pa_dbusiface_device_port *p) {
+    pa_assert(p);
+
+    return p->path;
+}
+
+const char *pa_dbusiface_device_port_get_name(pa_dbusiface_device_port *p) {
+    pa_assert(p);
+
+    return p->port->name;
+}
diff --git a/src/modules/dbus/iface-device-port.h b/src/modules/dbus/iface-device-port.h
new file mode 100644 (file)
index 0000000..f362d47
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef foodbusifacedeviceporthfoo
+#define foodbusifacedeviceporthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.DevicePort.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/DevicePort/
+ * for the DevicePort interface documentation.
+ */
+
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/sink.h>
+
+#include "iface-device.h"
+
+#define PA_DBUSIFACE_DEVICE_PORT_INTERFACE PA_DBUS_CORE_INTERFACE ".DevicePort"
+
+typedef struct pa_dbusiface_device_port pa_dbusiface_device_port;
+
+pa_dbusiface_device_port *pa_dbusiface_device_port_new(
+        pa_dbusiface_device *device,
+        pa_core *core,
+        pa_device_port *port,
+        uint32_t idx);
+void pa_dbusiface_device_port_free(pa_dbusiface_device_port *p);
+
+const char *pa_dbusiface_device_port_get_path(pa_dbusiface_device_port *p);
+const char *pa_dbusiface_device_port_get_name(pa_dbusiface_device_port *p);
+
+#endif
diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
new file mode 100644 (file)
index 0000000..2c370a8
--- /dev/null
@@ -0,0 +1,1377 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-device-port.h"
+
+#include "iface-device.h"
+
+#define SINK_OBJECT_NAME "sink"
+#define SOURCE_OBJECT_NAME "source"
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_has_dynamic_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_is_hardware_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_sink_get_monitor_source(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_sink_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_source_get_monitor_of_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_device {
+    pa_dbusiface_core *core;
+
+    union {
+        pa_sink *sink;
+        pa_source *source;
+    };
+    pa_device_type_t type;
+    char *path;
+    pa_cvolume volume;
+    dbus_bool_t mute;
+    union {
+        pa_sink_state_t sink_state;
+        pa_source_state_t source_state;
+    };
+    pa_hashmap *ports;
+    uint32_t next_port_index;
+    pa_device_port *active_port;
+    pa_proplist *proplist;
+
+    pa_hook_slot *volume_changed_slot;
+    pa_hook_slot *mute_changed_slot;
+    pa_hook_slot *state_changed_slot;
+    pa_hook_slot *port_changed_slot;
+    pa_hook_slot *proplist_changed_slot;
+
+    pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_DRIVER,
+    PROPERTY_HANDLER_OWNER_MODULE,
+    PROPERTY_HANDLER_CARD,
+    PROPERTY_HANDLER_SAMPLE_FORMAT,
+    PROPERTY_HANDLER_SAMPLE_RATE,
+    PROPERTY_HANDLER_CHANNELS,
+    PROPERTY_HANDLER_VOLUME,
+    PROPERTY_HANDLER_HAS_FLAT_VOLUME,
+    PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME,
+    PROPERTY_HANDLER_BASE_VOLUME,
+    PROPERTY_HANDLER_VOLUME_STEPS,
+    PROPERTY_HANDLER_MUTE,
+    PROPERTY_HANDLER_HAS_HARDWARE_VOLUME,
+    PROPERTY_HANDLER_HAS_HARDWARE_MUTE,
+    PROPERTY_HANDLER_CONFIGURED_LATENCY,
+    PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY,
+    PROPERTY_HANDLER_LATENCY,
+    PROPERTY_HANDLER_IS_HARDWARE_DEVICE,
+    PROPERTY_HANDLER_IS_NETWORK_DEVICE,
+    PROPERTY_HANDLER_STATE,
+    PROPERTY_HANDLER_PORTS,
+    PROPERTY_HANDLER_ACTIVE_PORT,
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+enum sink_property_handler_index {
+    SINK_PROPERTY_HANDLER_MONITOR_SOURCE,
+    SINK_PROPERTY_HANDLER_MAX
+};
+
+enum source_property_handler_index {
+    SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK,
+    SOURCE_PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]                             = { .property_name = "Index",                         .type = "u",      .get_cb = handle_get_index,                             .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]                              = { .property_name = "Name",                          .type = "s",      .get_cb = handle_get_name,                              .set_cb = NULL },
+    [PROPERTY_HANDLER_DRIVER]                            = { .property_name = "Driver",                        .type = "s",      .get_cb = handle_get_driver,                            .set_cb = NULL },
+    [PROPERTY_HANDLER_OWNER_MODULE]                      = { .property_name = "OwnerModule",                   .type = "o",      .get_cb = handle_get_owner_module,                      .set_cb = NULL },
+    [PROPERTY_HANDLER_CARD]                              = { .property_name = "Card",                          .type = "o",      .get_cb = handle_get_card,                              .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_FORMAT]                     = { .property_name = "SampleFormat",                  .type = "u",      .get_cb = handle_get_sample_format,                     .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_RATE]                       = { .property_name = "SampleRate",                    .type = "u",      .get_cb = handle_get_sample_rate,                       .set_cb = NULL },
+    [PROPERTY_HANDLER_CHANNELS]                          = { .property_name = "Channels",                      .type = "au",     .get_cb = handle_get_channels,                          .set_cb = NULL },
+    [PROPERTY_HANDLER_VOLUME]                            = { .property_name = "Volume",                        .type = "au",     .get_cb = handle_get_volume,                            .set_cb = handle_set_volume },
+    [PROPERTY_HANDLER_HAS_FLAT_VOLUME]                   = { .property_name = "HasFlatVolume",                 .type = "b",      .get_cb = handle_get_has_flat_volume,                   .set_cb = NULL },
+    [PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME] = { .property_name = "HasConvertibleToDecibelVolume", .type = "b",      .get_cb = handle_get_has_convertible_to_decibel_volume, .set_cb = NULL },
+    [PROPERTY_HANDLER_BASE_VOLUME]                       = { .property_name = "BaseVolume",                    .type = "u",      .get_cb = handle_get_base_volume,                       .set_cb = NULL },
+    [PROPERTY_HANDLER_VOLUME_STEPS]                      = { .property_name = "VolumeSteps",                   .type = "u",      .get_cb = handle_get_volume_steps,                      .set_cb = NULL },
+    [PROPERTY_HANDLER_MUTE]                              = { .property_name = "Mute",                          .type = "b",      .get_cb = handle_get_mute,                              .set_cb = handle_set_mute },
+    [PROPERTY_HANDLER_HAS_HARDWARE_VOLUME]               = { .property_name = "HasHardwareVolume",             .type = "b",      .get_cb = handle_get_has_hardware_volume,               .set_cb = NULL },
+    [PROPERTY_HANDLER_HAS_HARDWARE_MUTE]                 = { .property_name = "HasHardwareMute",               .type = "b",      .get_cb = handle_get_has_hardware_mute,                 .set_cb = NULL },
+    [PROPERTY_HANDLER_CONFIGURED_LATENCY]                = { .property_name = "ConfiguredLatency",             .type = "t",      .get_cb = handle_get_configured_latency,                .set_cb = NULL },
+    [PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY]               = { .property_name = "HasDynamicLatency",             .type = "b",      .get_cb = handle_get_has_dynamic_latency,               .set_cb = NULL },
+    [PROPERTY_HANDLER_LATENCY]                           = { .property_name = "Latency",                       .type = "t",      .get_cb = handle_get_latency,                           .set_cb = NULL },
+    [PROPERTY_HANDLER_IS_HARDWARE_DEVICE]                = { .property_name = "IsHardwareDevice",              .type = "b",      .get_cb = handle_get_is_hardware_device,                .set_cb = NULL },
+    [PROPERTY_HANDLER_IS_NETWORK_DEVICE]                 = { .property_name = "IsNetworkDevice",               .type = "b",      .get_cb = handle_get_is_network_device,                 .set_cb = NULL },
+    [PROPERTY_HANDLER_STATE]                             = { .property_name = "State",                         .type = "u",      .get_cb = handle_get_state,                             .set_cb = NULL },
+    [PROPERTY_HANDLER_PORTS]                             = { .property_name = "Ports",                         .type = "ao",     .get_cb = handle_get_ports,                             .set_cb = NULL },
+    [PROPERTY_HANDLER_ACTIVE_PORT]                       = { .property_name = "ActivePort",                    .type = "o",      .get_cb = handle_get_active_port,                       .set_cb = handle_set_active_port },
+    [PROPERTY_HANDLER_PROPERTY_LIST]                     = { .property_name = "PropertyList",                  .type = "a{say}", .get_cb = handle_get_property_list,                     .set_cb = NULL }
+};
+
+static pa_dbus_property_handler sink_property_handlers[SINK_PROPERTY_HANDLER_MAX] = {
+    [SINK_PROPERTY_HANDLER_MONITOR_SOURCE] = { .property_name = "MonitorSource", .type = "o", .get_cb = handle_sink_get_monitor_source, .set_cb = NULL }
+};
+
+static pa_dbus_property_handler source_property_handlers[SOURCE_PROPERTY_HANDLER_MAX] = {
+    [SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK] = { .property_name = "MonitorOfSink", .type = "o", .get_cb = handle_source_get_monitor_of_sink, .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_SUSPEND,
+    METHOD_HANDLER_GET_PORT_BY_NAME,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info suspend_args[] = { { "suspend", "b", "in" } };
+static pa_dbus_arg_info get_port_by_name_args[] = { { "name", "s", "in" }, { "port", "o", "out" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_SUSPEND] = {
+        .method_name = "Suspend",
+        .arguments = suspend_args,
+        .n_arguments = sizeof(suspend_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_suspend },
+    [METHOD_HANDLER_GET_PORT_BY_NAME] = {
+        .method_name = "GetPortByName",
+        .arguments = get_port_by_name_args,
+        .n_arguments = sizeof(get_port_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_port_by_name }
+};
+
+enum signal_index {
+    SIGNAL_VOLUME_UPDATED,
+    SIGNAL_MUTE_UPDATED,
+    SIGNAL_STATE_UPDATED,
+    SIGNAL_ACTIVE_PORT_UPDATED,
+    SIGNAL_PROPERTY_LIST_UPDATED,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info volume_updated_args[]        = { { "volume",        "au",     NULL } };
+static pa_dbus_arg_info mute_updated_args[]          = { { "muted",         "b",      NULL } };
+static pa_dbus_arg_info state_updated_args[]         = { { "state",         "u",      NULL } };
+static pa_dbus_arg_info active_port_updated_args[]   = { { "port",          "o",      NULL } };
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_VOLUME_UPDATED]        = { .name = "VolumeUpdated",       .arguments = volume_updated_args,        .n_arguments = 1 },
+    [SIGNAL_MUTE_UPDATED]          = { .name = "MuteUpdated",         .arguments = mute_updated_args,          .n_arguments = 1 },
+    [SIGNAL_STATE_UPDATED]         = { .name = "StateUpdated",        .arguments = state_updated_args,         .n_arguments = 1 },
+    [SIGNAL_ACTIVE_PORT_UPDATED]   = { .name = "ActivePortUpdated",   .arguments = active_port_updated_args,   .n_arguments = 1 },
+    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info device_interface_info = {
+    .name = PA_DBUSIFACE_DEVICE_INTERFACE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static pa_dbus_interface_info sink_interface_info = {
+    .name = PA_DBUSIFACE_SINK_INTERFACE,
+    .method_handlers = NULL,
+    .n_method_handlers = 0,
+    .property_handlers = sink_property_handlers,
+    .n_property_handlers = SINK_PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_sink_get_all,
+    .signals = NULL,
+    .n_signals = 0
+};
+
+static pa_dbus_interface_info source_interface_info = {
+    .name = PA_DBUSIFACE_SOURCE_INTERFACE,
+    .method_handlers = NULL,
+    .n_method_handlers = 0,
+    .property_handlers = source_property_handlers,
+    .n_property_handlers = SOURCE_PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_source_get_all,
+    .signals = NULL,
+    .n_signals = 0
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t idx = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    idx = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->index : d->source->index;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *name = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    name = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->name : d->source->name;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &name);
+}
+
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *driver = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    driver = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->driver : d->source->driver;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    pa_module *owner_module = NULL;
+    const char *object_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    owner_module = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->module : d->source->module;
+
+    if (!owner_module) {
+        if (d->type == PA_DEVICE_TYPE_SINK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Sink %s doesn't have an owner module.", d->sink->name);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Source %s doesn't have an owner module.", d->source->name);
+        return;
+    }
+
+    object_path = pa_dbusiface_core_get_module_path(d->core, owner_module);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    pa_card *card = NULL;
+    const char *object_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    card = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->card : d->source->card;
+
+    if (!card) {
+        if (d->type == PA_DEVICE_TYPE_SINK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Sink %s doesn't belong to any card.", d->sink->name);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Source %s doesn't belong to any card.", d->source->name);
+        return;
+    }
+
+    object_path = pa_dbusiface_core_get_card_path(d->core, card);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t sample_format = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    sample_format = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->sample_spec.format : d->source->sample_spec.format;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format);
+}
+
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t sample_rate = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    sample_rate = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->sample_spec.rate : d->source->sample_spec.rate;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_rate);
+}
+
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    pa_channel_map *channel_map = NULL;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    channel_map = (d->type == PA_DEVICE_TYPE_SINK) ? &d->sink->channel_map : &d->source->channel_map;
+
+    for (i = 0; i < channel_map->channels; ++i)
+        channels[i] = channel_map->map[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels);
+}
+
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t volume[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    for (i = 0; i < d->volume.channels; ++i)
+        volume[i] = d->volume.values[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, d->volume.channels);
+}
+
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    DBusMessageIter array_iter;
+    int device_channels = 0;
+    dbus_uint32_t *volume = NULL;
+    int n_volume_entries = 0;
+    pa_cvolume new_vol;
+    int i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(d);
+
+    device_channels = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->channel_map.channels : d->source->channel_map.channels;
+
+    dbus_message_iter_recurse(iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries);
+
+    if (n_volume_entries != device_channels && n_volume_entries != 1) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Expected %u volume entries, got %i.", device_channels, n_volume_entries);
+        return;
+    }
+
+    pa_cvolume_init(&new_vol);
+    new_vol.channels = n_volume_entries;
+
+    for (i = 0; i < n_volume_entries; ++i) {
+        if (!PA_VOLUME_IS_VALID(volume[i])) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %u", volume[i]);
+            return;
+        }
+        new_vol.values[i] = volume[i];
+    }
+
+    if (d->type == PA_DEVICE_TYPE_SINK)
+        pa_sink_set_volume(d->sink, &new_vol, true, true);
+    else
+        pa_source_set_volume(d->source, &new_vol, true, true);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t has_flat_volume = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    has_flat_volume = (d->type == PA_DEVICE_TYPE_SINK) ? !!(d->sink->flags & PA_SINK_FLAT_VOLUME) : FALSE;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_flat_volume);
+}
+
+static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t has_convertible_to_decibel_volume = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    has_convertible_to_decibel_volume = (d->type == PA_DEVICE_TYPE_SINK)
+                                        ? !!(d->sink->flags & PA_SINK_DECIBEL_VOLUME)
+                                        : !!(d->source->flags & PA_SOURCE_DECIBEL_VOLUME);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume);
+}
+
+static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t base_volume;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    base_volume = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->base_volume : d->source->base_volume;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &base_volume);
+}
+
+static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t volume_steps;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    volume_steps = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->n_volume_steps : d->source->n_volume_steps;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &volume_steps);
+}
+
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->mute);
+}
+
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t mute = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(d);
+
+    dbus_message_iter_get_basic(iter, &mute);
+
+    if (d->type == PA_DEVICE_TYPE_SINK)
+        pa_sink_set_mute(d->sink, mute, true);
+    else
+        pa_source_set_mute(d->source, mute, true);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t has_hardware_volume = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    has_hardware_volume = (d->type == PA_DEVICE_TYPE_SINK)
+                          ? !!(d->sink->flags & PA_SINK_HW_VOLUME_CTRL)
+                          : !!(d->source->flags & PA_SOURCE_HW_VOLUME_CTRL);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_hardware_volume);
+}
+
+static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t has_hardware_mute = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    has_hardware_mute = (d->type == PA_DEVICE_TYPE_SINK)
+                        ? !!(d->sink->flags & PA_SINK_HW_MUTE_CTRL)
+                        : !!(d->source->flags & PA_SOURCE_HW_MUTE_CTRL);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_hardware_mute);
+}
+
+static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint64_t configured_latency = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    configured_latency = (d->type == PA_DEVICE_TYPE_SINK)
+                         ? pa_sink_get_requested_latency(d->sink)
+                         : pa_source_get_requested_latency(d->source);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &configured_latency);
+}
+
+static void handle_get_has_dynamic_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t has_dynamic_latency = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    has_dynamic_latency = (d->type == PA_DEVICE_TYPE_SINK)
+                          ? !!(d->sink->flags & PA_SINK_DYNAMIC_LATENCY)
+                          : !!(d->source->flags & PA_SOURCE_DYNAMIC_LATENCY);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_dynamic_latency);
+}
+
+static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint64_t latency = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    if (d->type == PA_DEVICE_TYPE_SINK && !(d->sink->flags & PA_SINK_LATENCY)) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sink %s doesn't support latency querying.", d->sink->name);
+        return;
+    }
+
+    if (d->type == PA_DEVICE_TYPE_SOURCE && !(d->source->flags & PA_SOURCE_LATENCY)) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Source %s doesn't support latency querying.", d->source->name);
+        return;
+    }
+
+    latency = (d->type == PA_DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &latency);
+}
+
+static void handle_get_is_hardware_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t is_hardware_device = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    is_hardware_device = (d->type == PA_DEVICE_TYPE_SINK)
+                         ? !!(d->sink->flags & PA_SINK_HARDWARE)
+                         : !!(d->source->flags & PA_SOURCE_HARDWARE);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_hardware_device);
+}
+
+static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t is_network_device = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    is_network_device = (d->type == PA_DEVICE_TYPE_SINK)
+                        ? !!(d->sink->flags & PA_SINK_NETWORK)
+                        : !!(d->source->flags & PA_SOURCE_NETWORK);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_network_device);
+}
+
+static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t state;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    state = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink_state : d->source_state;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &state);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_ports(pa_dbusiface_device *d, unsigned *n) {
+    const char **ports;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_device_port *port = NULL;
+
+    pa_assert(d);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(d->ports);
+
+    if (*n == 0)
+        return NULL;
+
+    ports = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(port, d->ports, state)
+        ports[i++] = pa_dbusiface_device_port_get_path(port);
+
+    return ports;
+}
+
+static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char **ports = NULL;
+    unsigned n_ports = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    ports = get_ports(d, &n_ports);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, ports, n_ports);
+
+    pa_xfree(ports);
+}
+
+static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *active_port;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    if (!d->active_port) {
+        pa_assert(pa_hashmap_isempty(d->ports));
+
+        if (d->type == PA_DEVICE_TYPE_SINK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "The sink %s has no ports, and therefore there's no active port either.", d->sink->name);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "The source %s has no ports, and therefore there's no active port either.", d->source->name);
+        return;
+    }
+
+    active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_port);
+}
+
+static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *new_active_path;
+    pa_dbusiface_device_port *port;
+    void *state;
+    pa_dbusiface_device_port *new_active = NULL;
+    int r;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(d);
+
+    if (!d->active_port) {
+        pa_assert(pa_hashmap_isempty(d->ports));
+
+        if (d->type == PA_DEVICE_TYPE_SINK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "The sink %s has no ports, and therefore there's no active port either.", d->sink->name);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "The source %s has no ports, and therefore there's no active port either.", d->source->name);
+        return;
+    }
+
+    dbus_message_iter_get_basic(iter, &new_active_path);
+
+    PA_HASHMAP_FOREACH(port, d->ports, state) {
+        if (pa_streq(pa_dbusiface_device_port_get_path(port), new_active_path)) {
+            new_active = port;
+            break;
+        }
+    }
+
+    if (!new_active) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such port: %s", new_active_path);
+        return;
+    }
+
+    if (d->type == PA_DEVICE_TYPE_SINK) {
+        if ((r = pa_sink_set_port(d->sink, pa_dbusiface_device_port_get_name(new_active), true)) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                               "Internal error in PulseAudio: pa_sink_set_port() failed with error code %i.", r);
+            return;
+        }
+    } else {
+        if ((r = pa_source_set_port(d->source, pa_dbusiface_device_port_get_name(new_active), true)) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                               "Internal error in PulseAudio: pa_source_set_port() failed with error code %i.", r);
+            return;
+        }
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, d->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t idx = 0;
+    const char *name = NULL;
+    const char *driver = NULL;
+    pa_module *owner_module = NULL;
+    const char *owner_module_path = NULL;
+    pa_card *card = NULL;
+    const char *card_path = NULL;
+    dbus_uint32_t sample_format = 0;
+    dbus_uint32_t sample_rate = 0;
+    pa_channel_map *channel_map = NULL;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
+    dbus_uint32_t volume[PA_CHANNELS_MAX];
+    dbus_bool_t has_flat_volume = FALSE;
+    dbus_bool_t has_convertible_to_decibel_volume = FALSE;
+    dbus_uint32_t base_volume = 0;
+    dbus_uint32_t volume_steps = 0;
+    dbus_bool_t has_hardware_volume = FALSE;
+    dbus_bool_t has_hardware_mute = FALSE;
+    dbus_uint64_t configured_latency = 0;
+    dbus_bool_t has_dynamic_latency = FALSE;
+    dbus_uint64_t latency = 0;
+    dbus_bool_t is_hardware_device = FALSE;
+    dbus_bool_t is_network_device = FALSE;
+    dbus_uint32_t state = 0;
+    const char **ports = NULL;
+    unsigned n_ports = 0;
+    const char *active_port = NULL;
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    if (d->type == PA_DEVICE_TYPE_SINK) {
+        idx = d->sink->index;
+        name = d->sink->name;
+        driver = d->sink->driver;
+        owner_module = d->sink->module;
+        card = d->sink->card;
+        sample_format = d->sink->sample_spec.format;
+        sample_rate = d->sink->sample_spec.rate;
+        channel_map = &d->sink->channel_map;
+        has_flat_volume = !!(d->sink->flags & PA_SINK_FLAT_VOLUME);
+        has_convertible_to_decibel_volume = !!(d->sink->flags & PA_SINK_DECIBEL_VOLUME);
+        base_volume = d->sink->base_volume;
+        volume_steps = d->sink->n_volume_steps;
+        has_hardware_volume = !!(d->sink->flags & PA_SINK_HW_VOLUME_CTRL);
+        has_hardware_mute = !!(d->sink->flags & PA_SINK_HW_MUTE_CTRL);
+        configured_latency = pa_sink_get_requested_latency(d->sink);
+        has_dynamic_latency = !!(d->sink->flags & PA_SINK_DYNAMIC_LATENCY);
+        latency = pa_sink_get_latency(d->sink);
+        is_hardware_device = !!(d->sink->flags & PA_SINK_HARDWARE);
+        is_network_device = !!(d->sink->flags & PA_SINK_NETWORK);
+        state = pa_sink_get_state(d->sink);
+    } else {
+        idx = d->source->index;
+        name = d->source->name;
+        driver = d->source->driver;
+        owner_module = d->source->module;
+        card = d->source->card;
+        sample_format = d->source->sample_spec.format;
+        sample_rate = d->source->sample_spec.rate;
+        channel_map = &d->source->channel_map;
+        has_flat_volume = FALSE;
+        has_convertible_to_decibel_volume = !!(d->source->flags & PA_SOURCE_DECIBEL_VOLUME);
+        base_volume = d->source->base_volume;
+        volume_steps = d->source->n_volume_steps;
+        has_hardware_volume = !!(d->source->flags & PA_SOURCE_HW_VOLUME_CTRL);
+        has_hardware_mute = !!(d->source->flags & PA_SOURCE_HW_MUTE_CTRL);
+        configured_latency = pa_source_get_requested_latency(d->source);
+        has_dynamic_latency = !!(d->source->flags & PA_SOURCE_DYNAMIC_LATENCY);
+        latency = pa_source_get_latency(d->source);
+        is_hardware_device = !!(d->source->flags & PA_SOURCE_HARDWARE);
+        is_network_device = !!(d->source->flags & PA_SOURCE_NETWORK);
+        state = pa_source_get_state(d->source);
+    }
+    if (owner_module)
+        owner_module_path = pa_dbusiface_core_get_module_path(d->core, owner_module);
+    if (card)
+        card_path = pa_dbusiface_core_get_card_path(d->core, card);
+    for (i = 0; i < channel_map->channels; ++i)
+        channels[i] = channel_map->map[i];
+    for (i = 0; i < d->volume.channels; ++i)
+        volume[i] = d->volume.values[i];
+    ports = get_ports(d, &n_ports);
+    if (d->active_port)
+        active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver);
+
+    if (owner_module)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path);
+
+    if (card)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CARD].property_name, DBUS_TYPE_OBJECT_PATH, &card_path);
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &sample_rate);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, d->volume.channels);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_FLAT_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_flat_volume);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BASE_VOLUME].property_name, DBUS_TYPE_UINT32, &base_volume);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME_STEPS].property_name, DBUS_TYPE_UINT32, &volume_steps);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &d->mute);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_volume);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_MUTE].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_mute);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CONFIGURED_LATENCY].property_name, DBUS_TYPE_UINT64, &configured_latency);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY].property_name, DBUS_TYPE_BOOLEAN, &has_dynamic_latency);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_LATENCY].property_name, DBUS_TYPE_UINT64, &latency);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_HARDWARE_DEVICE].property_name, DBUS_TYPE_BOOLEAN, &is_hardware_device);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_NETWORK_DEVICE].property_name, DBUS_TYPE_BOOLEAN, &is_network_device);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_STATE].property_name, DBUS_TYPE_UINT32, &state);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PORTS].property_name, DBUS_TYPE_OBJECT_PATH, ports, n_ports);
+
+    if (active_port)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACTIVE_PORT].property_name, DBUS_TYPE_OBJECT_PATH, &active_port);
+
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, d->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(ports);
+}
+
+static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t suspend = FALSE;
+    pa_client *client;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID));
+    pa_assert_se(client = pa_dbus_protocol_get_client(d->dbus_protocol, conn));
+
+    if (d->type == PA_DEVICE_TYPE_SINK) {
+        pa_log_debug("%s sink %s requested by client %" PRIu32 ".", suspend ? "Suspending" : "Resuming", d->sink->name, client->index);
+
+        if (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed.");
+            return;
+        }
+
+    } else {
+        pa_log_debug("%s source %s requested by client %" PRIu32 ".", suspend ? "Suspending" : "Resuming", d->source->name, client->index);
+
+        if (pa_source_suspend(d->source, suspend, PA_SUSPEND_USER) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_source_suspend() failed.");
+            return;
+        }
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *port_name = NULL;
+    pa_dbusiface_device_port *port = NULL;
+    const char *port_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID));
+
+    if (!(port = pa_hashmap_get(d->ports, port_name))) {
+        if (d->type == PA_DEVICE_TYPE_SINK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND,
+                               "%s: No such port on sink %s.", port_name, d->sink->name);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND,
+                               "%s: No such port on source %s.", port_name, d->source->name);
+        return;
+    }
+
+    port_path = pa_dbusiface_device_port_get_path(port);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &port_path);
+}
+
+static void handle_sink_get_monitor_source(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *monitor_source = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+    pa_assert(d->type == PA_DEVICE_TYPE_SINK);
+
+    monitor_source = pa_dbusiface_core_get_source_path(d->core, d->sink->monitor_source);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &monitor_source);
+}
+
+static void handle_sink_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    const char *monitor_source = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+    pa_assert(d->type == PA_DEVICE_TYPE_SINK);
+
+    monitor_source = pa_dbusiface_core_get_source_path(d->core, d->sink->monitor_source);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[SINK_PROPERTY_HANDLER_MONITOR_SOURCE].property_name, DBUS_TYPE_OBJECT_PATH, &monitor_source);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+}
+
+static void handle_source_get_monitor_of_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *monitor_of_sink = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+    pa_assert(d->type == PA_DEVICE_TYPE_SOURCE);
+
+    if (!d->source->monitor_of) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s is not a monitor source.", d->source->name);
+        return;
+    }
+
+    monitor_of_sink = pa_dbusiface_core_get_sink_path(d->core, d->source->monitor_of);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &monitor_of_sink);
+}
+
+static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    const char *monitor_of_sink = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+    pa_assert(d->type == PA_DEVICE_TYPE_SOURCE);
+
+    if (d->source->monitor_of)
+        monitor_of_sink = pa_dbusiface_core_get_sink_path(d->core, d->source->monitor_of);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    if (monitor_of_sink)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK].property_name, DBUS_TYPE_OBJECT_PATH, &monitor_of_sink);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+}
+
+static pa_hook_result_t volume_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_device *d = slot_data;
+    DBusMessage *signal_msg = NULL;
+    const pa_cvolume *new_volume = NULL;
+    unsigned i = 0;
+
+    if ((d->type == PA_DEVICE_TYPE_SINK && d->sink != call_data) ||
+        (d->type == PA_DEVICE_TYPE_SOURCE && d->source != call_data))
+        return PA_HOOK_OK;
+
+    new_volume = (d->type == PA_DEVICE_TYPE_SINK)
+                 ? pa_sink_get_volume(d->sink, false)
+                 : pa_source_get_volume(d->source, false);
+
+    if (!pa_cvolume_equal(&d->volume, new_volume)) {
+        dbus_uint32_t volume[PA_CHANNELS_MAX];
+        dbus_uint32_t *volume_ptr = volume;
+
+        d->volume = *new_volume;
+
+        for (i = 0; i < d->volume.channels; ++i)
+            volume[i] = d->volume.values[i];
+
+        pa_assert_se(signal_msg = dbus_message_new_signal(d->path,
+                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                          signals[SIGNAL_VOLUME_UPDATED].name));
+        pa_assert_se(dbus_message_append_args(signal_msg,
+                                              DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, d->volume.channels,
+                                              DBUS_TYPE_INVALID));
+
+        pa_dbus_protocol_send_signal(d->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t mute_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_device *d = slot_data;
+    DBusMessage *signal_msg = NULL;
+    bool new_mute = false;
+
+    if ((d->type == PA_DEVICE_TYPE_SINK && d->sink != call_data) ||
+        (d->type == PA_DEVICE_TYPE_SOURCE && d->source != call_data))
+        return PA_HOOK_OK;
+
+    new_mute = (d->type == PA_DEVICE_TYPE_SINK)
+               ? pa_sink_get_mute(d->sink, false)
+               : pa_source_get_mute(d->source, false);
+
+    if (d->mute != new_mute) {
+        d->mute = new_mute;
+
+        pa_assert_se(signal_msg = dbus_message_new_signal(d->path,
+                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                          signals[SIGNAL_MUTE_UPDATED].name));
+        pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_BOOLEAN, &d->mute, DBUS_TYPE_INVALID));
+
+        pa_dbus_protocol_send_signal(d->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t state_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_device *d = slot_data;
+    DBusMessage *signal_msg = NULL;
+    pa_sink_state_t new_sink_state = 0;
+    pa_source_state_t new_source_state = 0;
+
+    if ((d->type == PA_DEVICE_TYPE_SINK && d->sink != call_data) ||
+        (d->type == PA_DEVICE_TYPE_SOURCE && d->source != call_data))
+        return PA_HOOK_OK;
+
+    if (d->type == PA_DEVICE_TYPE_SINK)
+        new_sink_state = pa_sink_get_state(d->sink);
+    else
+        new_source_state = pa_source_get_state(d->source);
+
+    if ((d->type == PA_DEVICE_TYPE_SINK && d->sink_state != new_sink_state)
+        || (d->type == PA_DEVICE_TYPE_SOURCE && d->source_state != new_source_state)) {
+        dbus_uint32_t state = 0;
+
+        if (d->type == PA_DEVICE_TYPE_SINK)
+            d->sink_state = new_sink_state;
+        else
+            d->source_state = new_source_state;
+
+        state = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink_state : d->source_state;
+
+        pa_assert_se(signal_msg = dbus_message_new_signal(d->path,
+                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                          signals[SIGNAL_STATE_UPDATED].name));
+        pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID));
+
+        pa_dbus_protocol_send_signal(d->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t port_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_device *d = slot_data;
+    DBusMessage *signal_msg = NULL;
+    pa_device_port *new_active_port = NULL;
+
+    if ((d->type == PA_DEVICE_TYPE_SINK && d->sink != call_data) ||
+        (d->type == PA_DEVICE_TYPE_SOURCE && d->source != call_data))
+        return PA_HOOK_OK;
+
+    new_active_port = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->active_port : d->source->active_port;
+
+    if (d->active_port != new_active_port) {
+        const char *object_path = NULL;
+
+        d->active_port = new_active_port;
+        object_path = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));
+
+        pa_assert_se(signal_msg = dbus_message_new_signal(d->path,
+                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                          signals[SIGNAL_ACTIVE_PORT_UPDATED].name));
+        pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+        pa_dbus_protocol_send_signal(d->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t proplist_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_device *d = slot_data;
+    DBusMessage *signal_msg = NULL;
+    pa_proplist *new_proplist = NULL;
+
+    if ((d->type == PA_DEVICE_TYPE_SINK && d->sink != call_data) ||
+        (d->type == PA_DEVICE_TYPE_SOURCE && d->source != call_data))
+        return PA_HOOK_OK;
+
+    new_proplist = (d->type == PA_DEVICE_TYPE_SINK) ? d->sink->proplist : d->source->proplist;
+
+    if (!pa_proplist_equal(d->proplist, new_proplist)) {
+        DBusMessageIter msg_iter;
+
+        pa_proplist_update(d->proplist, PA_UPDATE_SET, new_proplist);
+
+        pa_assert_se(signal_msg = dbus_message_new_signal(d->path,
+                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal_msg, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, d->proplist);
+
+        pa_dbus_protocol_send_signal(d->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+
+    return PA_HOOK_OK;
+}
+
+pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink) {
+    pa_dbusiface_device *d = NULL;
+    pa_device_port *port;
+    void *state;
+
+    pa_assert(core);
+    pa_assert(sink);
+
+    d = pa_xnew0(pa_dbusiface_device, 1);
+    d->core = core;
+    d->sink = pa_sink_ref(sink);
+    d->type = PA_DEVICE_TYPE_SINK;
+    d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index);
+    d->volume = *pa_sink_get_volume(sink, false);
+    d->mute = pa_sink_get_mute(sink, false);
+    d->sink_state = pa_sink_get_state(sink);
+    d->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_device_port_free);
+    d->next_port_index = 0;
+    d->active_port = sink->active_port;
+    d->proplist = pa_proplist_copy(sink->proplist);
+    d->dbus_protocol = pa_dbus_protocol_get(sink->core);
+    d->volume_changed_slot = pa_hook_connect(&sink->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
+                                             PA_HOOK_NORMAL, volume_changed_cb, d);
+    d->mute_changed_slot = pa_hook_connect(&sink->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED],
+                                           PA_HOOK_NORMAL, mute_changed_cb, d);
+    d->state_changed_slot = pa_hook_connect(&sink->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED],
+                                            PA_HOOK_NORMAL, state_changed_cb, d);
+    d->port_changed_slot = pa_hook_connect(&sink->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED],
+                                           PA_HOOK_NORMAL, port_changed_cb, d);
+    d->proplist_changed_slot = pa_hook_connect(&sink->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED],
+                                               PA_HOOK_NORMAL, proplist_changed_cb, d);
+
+    PA_HASHMAP_FOREACH(port, sink->ports, state) {
+        pa_dbusiface_device_port *p = pa_dbusiface_device_port_new(d, sink->core, port, d->next_port_index++);
+        pa_hashmap_put(d->ports, (char *) pa_dbusiface_device_port_get_name(p), p);
+    }
+
+    pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &device_interface_info, d) >= 0);
+    pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &sink_interface_info, d) >= 0);
+
+    return d;
+}
+
+pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source) {
+    pa_dbusiface_device *d = NULL;
+    pa_device_port *port;
+    void *state;
+
+    pa_assert(core);
+    pa_assert(source);
+
+    d = pa_xnew0(pa_dbusiface_device, 1);
+    d->core = core;
+    d->source = pa_source_ref(source);
+    d->type = PA_DEVICE_TYPE_SOURCE;
+    d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index);
+    d->volume = *pa_source_get_volume(source, false);
+    d->mute = pa_source_get_mute(source, false);
+    d->source_state = pa_source_get_state(source);
+    d->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_device_port_free);
+    d->next_port_index = 0;
+    d->active_port = source->active_port;
+    d->proplist = pa_proplist_copy(source->proplist);
+    d->dbus_protocol = pa_dbus_protocol_get(source->core);
+    d->volume_changed_slot = pa_hook_connect(&source->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
+                                             PA_HOOK_NORMAL, volume_changed_cb, d);
+    d->mute_changed_slot = pa_hook_connect(&source->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
+                                           PA_HOOK_NORMAL, mute_changed_cb, d);
+    d->state_changed_slot = pa_hook_connect(&source->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED],
+                                            PA_HOOK_NORMAL, state_changed_cb, d);
+    d->port_changed_slot = pa_hook_connect(&source->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED],
+                                           PA_HOOK_NORMAL, port_changed_cb, d);
+    d->proplist_changed_slot = pa_hook_connect(&source->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED],
+                                               PA_HOOK_NORMAL, proplist_changed_cb, d);
+
+    PA_HASHMAP_FOREACH(port, source->ports, state) {
+        pa_dbusiface_device_port *p = pa_dbusiface_device_port_new(d, source->core, port, d->next_port_index++);
+        pa_hashmap_put(d->ports, (char *) pa_dbusiface_device_port_get_name(p), p);
+    }
+
+    pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &device_interface_info, d) >= 0);
+    pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &source_interface_info, d) >= 0);
+
+    return d;
+}
+
+void pa_dbusiface_device_free(pa_dbusiface_device *d) {
+    pa_assert(d);
+
+    pa_hook_slot_free(d->volume_changed_slot);
+    pa_hook_slot_free(d->mute_changed_slot);
+    pa_hook_slot_free(d->state_changed_slot);
+    pa_hook_slot_free(d->port_changed_slot);
+    pa_hook_slot_free(d->proplist_changed_slot);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, device_interface_info.name) >= 0);
+
+    if (d->type == PA_DEVICE_TYPE_SINK) {
+        pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, sink_interface_info.name) >= 0);
+        pa_sink_unref(d->sink);
+
+    } else {
+        pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, source_interface_info.name) >= 0);
+        pa_source_unref(d->source);
+    }
+    pa_hashmap_free(d->ports);
+    pa_proplist_free(d->proplist);
+    pa_dbus_protocol_unref(d->dbus_protocol);
+
+    pa_xfree(d->path);
+    pa_xfree(d);
+}
+
+const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d) {
+    pa_assert(d);
+
+    return d->path;
+}
+
+pa_sink *pa_dbusiface_device_get_sink(pa_dbusiface_device *d) {
+    pa_assert(d);
+    pa_assert(d->type == PA_DEVICE_TYPE_SINK);
+
+    return d->sink;
+}
+
+pa_source *pa_dbusiface_device_get_source(pa_dbusiface_device *d) {
+    pa_assert(d);
+    pa_assert(d->type == PA_DEVICE_TYPE_SOURCE);
+
+    return d->source;
+}
diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device.h
new file mode 100644 (file)
index 0000000..97ecd7a
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef foodbusifacedevicehfoo
+#define foodbusifacedevicehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interfaces org.PulseAudio.Core1.Device,
+ * org.PulseAudio.Core1.Sink and org.PulseAudio.Core1.Source.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Device/
+ * for the interface documentation.
+ */
+
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_DEVICE_INTERFACE PA_DBUS_CORE_INTERFACE ".Device"
+#define PA_DBUSIFACE_SINK_INTERFACE PA_DBUS_CORE_INTERFACE ".Sink"
+#define PA_DBUSIFACE_SOURCE_INTERFACE PA_DBUS_CORE_INTERFACE ".Source"
+
+typedef struct pa_dbusiface_device pa_dbusiface_device;
+
+pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink);
+pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source);
+void pa_dbusiface_device_free(pa_dbusiface_device *d);
+
+const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d);
+
+pa_sink *pa_dbusiface_device_get_sink(pa_dbusiface_device *d);
+pa_source *pa_dbusiface_device_get_source(pa_dbusiface_device *d);
+
+#endif
diff --git a/src/modules/dbus/iface-memstats.c b/src/modules/dbus/iface-memstats.c
new file mode 100644 (file)
index 0000000..cd1d7ea
--- /dev/null
@@ -0,0 +1,228 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-scache.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-memstats.h"
+
+#define OBJECT_NAME "memstats"
+
+static void handle_get_current_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_current_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_accumulated_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_accumulated_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_cache_size(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_memstats {
+    pa_core *core;
+    char *path;
+    pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+    PROPERTY_HANDLER_CURRENT_MEMBLOCKS,
+    PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE,
+    PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS,
+    PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE,
+    PROPERTY_HANDLER_SAMPLE_CACHE_SIZE,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_CURRENT_MEMBLOCKS]          = { .property_name = "CurrentMemblocks",         .type = "u", .get_cb = handle_get_current_memblocks,          .set_cb = NULL },
+    [PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE]     = { .property_name = "CurrentMemblocksSize",     .type = "u", .get_cb = handle_get_current_memblocks_size,     .set_cb = NULL },
+    [PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS]      = { .property_name = "AccumulatedMemblocks",     .type = "u", .get_cb = handle_get_accumulated_memblocks,      .set_cb = NULL },
+    [PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE] = { .property_name = "AccumulatedMemblocksSize", .type = "u", .get_cb = handle_get_accumulated_memblocks_size, .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_CACHE_SIZE]          = { .property_name = "SampleCacheSize",          .type = "u", .get_cb = handle_get_sample_cache_size,          .set_cb = NULL }
+};
+
+static pa_dbus_interface_info memstats_interface_info = {
+    .name = PA_DBUSIFACE_MEMSTATS_INTERFACE,
+    .method_handlers = NULL,
+    .n_method_handlers = 0,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = NULL,
+    .n_signals = 0
+};
+
+static void handle_get_current_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    const pa_mempool_stat *stat;
+    dbus_uint32_t current_memblocks;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    stat = pa_mempool_get_stat(m->core->mempool);
+
+    current_memblocks = pa_atomic_load(&stat->n_allocated);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &current_memblocks);
+}
+
+static void handle_get_current_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    const pa_mempool_stat *stat;
+    dbus_uint32_t current_memblocks_size;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    stat = pa_mempool_get_stat(m->core->mempool);
+
+    current_memblocks_size = pa_atomic_load(&stat->allocated_size);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &current_memblocks_size);
+}
+
+static void handle_get_accumulated_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    const pa_mempool_stat *stat;
+    dbus_uint32_t accumulated_memblocks;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    stat = pa_mempool_get_stat(m->core->mempool);
+
+    accumulated_memblocks = pa_atomic_load(&stat->n_accumulated);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &accumulated_memblocks);
+}
+
+static void handle_get_accumulated_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    const pa_mempool_stat *stat;
+    dbus_uint32_t accumulated_memblocks_size;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    stat = pa_mempool_get_stat(m->core->mempool);
+
+    accumulated_memblocks_size = pa_atomic_load(&stat->accumulated_size);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &accumulated_memblocks_size);
+}
+
+static void handle_get_sample_cache_size(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    dbus_uint32_t sample_cache_size;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    sample_cache_size = pa_scache_total_size(m->core);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_cache_size);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    const pa_mempool_stat *stat;
+    dbus_uint32_t current_memblocks;
+    dbus_uint32_t current_memblocks_size;
+    dbus_uint32_t accumulated_memblocks;
+    dbus_uint32_t accumulated_memblocks_size;
+    dbus_uint32_t sample_cache_size;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    stat = pa_mempool_get_stat(m->core->mempool);
+
+    current_memblocks = pa_atomic_load(&stat->n_allocated);
+    current_memblocks_size = pa_atomic_load(&stat->allocated_size);
+    accumulated_memblocks = pa_atomic_load(&stat->n_accumulated);
+    accumulated_memblocks_size = pa_atomic_load(&stat->accumulated_size);
+    sample_cache_size = pa_scache_total_size(m->core);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CURRENT_MEMBLOCKS].property_name, DBUS_TYPE_UINT32, &current_memblocks);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE].property_name, DBUS_TYPE_UINT32, &current_memblocks_size);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS].property_name, DBUS_TYPE_UINT32, &accumulated_memblocks);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE].property_name, DBUS_TYPE_UINT32, &accumulated_memblocks_size);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_CACHE_SIZE].property_name, DBUS_TYPE_UINT32, &sample_cache_size);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+}
+
+pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_dbusiface_core *dbus_core, pa_core *core) {
+    pa_dbusiface_memstats *m;
+
+    pa_assert(dbus_core);
+    pa_assert(core);
+
+    m = pa_xnew(pa_dbusiface_memstats, 1);
+    m->core = core;
+    m->path = pa_sprintf_malloc("%s/%s", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME);
+    m->dbus_protocol = pa_dbus_protocol_get(core);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &memstats_interface_info, m) >= 0);
+
+    return m;
+}
+
+void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m) {
+    pa_assert(m);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, m->path, memstats_interface_info.name) >= 0);
+
+    pa_xfree(m->path);
+
+    pa_dbus_protocol_unref(m->dbus_protocol);
+
+    pa_xfree(m);
+}
+
+const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m) {
+    pa_assert(m);
+
+    return m->path;
+}
diff --git a/src/modules/dbus/iface-memstats.h b/src/modules/dbus/iface-memstats.h
new file mode 100644 (file)
index 0000000..482d6be
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef foodbusifacememstatshfoo
+#define foodbusifacememstatshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Memstats.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Memstats/
+ * for the Memstats interface documentation.
+ */
+
+#include <pulsecore/core.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_MEMSTATS_INTERFACE PA_DBUS_CORE_INTERFACE ".Memstats"
+
+typedef struct pa_dbusiface_memstats pa_dbusiface_memstats;
+
+pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_dbusiface_core *dbus_core, pa_core *core);
+void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m);
+
+const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m);
+
+#endif
diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c
new file mode 100644 (file)
index 0000000..222cd73
--- /dev/null
@@ -0,0 +1,331 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-module.h"
+
+#define OBJECT_NAME "module"
+
+struct pa_dbusiface_module {
+    pa_module *module;
+    char *path;
+    pa_proplist *proplist;
+
+    pa_dbus_protocol *dbus_protocol;
+    pa_hook_slot *module_proplist_changed_slot;
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_ARGUMENTS,
+    PROPERTY_HANDLER_USAGE_COUNTER,
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]         = { .property_name = "Index",        .type = "u",      .get_cb = handle_get_index,         .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]          = { .property_name = "Name",         .type = "s",      .get_cb = handle_get_name,          .set_cb = NULL },
+    [PROPERTY_HANDLER_ARGUMENTS]     = { .property_name = "Arguments",    .type = "a{ss}",  .get_cb = handle_get_arguments,     .set_cb = NULL },
+    [PROPERTY_HANDLER_USAGE_COUNTER] = { .property_name = "UsageCounter", .type = "u",      .get_cb = handle_get_usage_counter, .set_cb = NULL },
+    [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_UNLOAD,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_UNLOAD] = {
+        .method_name = "Unload",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_unload }
+};
+
+enum signal_index {
+    SIGNAL_PROPERTY_LIST_UPDATED,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info property_list_updated_args[] =  { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info module_interface_info = {
+    .name = PA_DBUSIFACE_MODULE_INTERFACE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+    dbus_uint32_t idx = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    idx = m->module->index;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &m->module->name);
+}
+
+static void append_modargs_variant(DBusMessageIter *iter, pa_dbusiface_module *m) {
+    pa_modargs *ma = NULL;
+    DBusMessageIter variant_iter;
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    void *state = NULL;
+    const char *key = NULL;
+    const char *value = NULL;
+
+    pa_assert(iter);
+    pa_assert(m);
+
+    pa_assert_se(ma = pa_modargs_new(m->module->argument, NULL));
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{ss}", &variant_iter));
+    pa_assert_se(dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, "{ss}", &dict_iter));
+
+    for (state = NULL, key = pa_modargs_iterate(ma, &state); key; key = pa_modargs_iterate(ma, &state)) {
+        pa_assert_se(value = pa_modargs_get_value(ma, key, NULL));
+
+        pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+
+        pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+        pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &value));
+
+        pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+    }
+
+    pa_assert_se(dbus_message_iter_close_container(&variant_iter, &dict_iter));
+    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+
+    pa_modargs_free(ma);
+}
+
+static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    pa_assert_se(reply = dbus_message_new_method_return(msg));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    append_modargs_variant(&msg_iter, m);
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+    int real_counter_value = -1;
+    dbus_uint32_t usage_counter = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    if (!m->module->get_n_used || (real_counter_value = m->module->get_n_used(m->module)) < 0) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Module %u (%s) doesn't have a usage counter.", m->module->index, m->module->name);
+        return;
+    }
+
+    usage_counter = real_counter_value;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &usage_counter);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, m->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    dbus_uint32_t idx = 0;
+    int real_counter_value = -1;
+    dbus_uint32_t usage_counter = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    idx = m->module->index;
+    if (m->module->get_n_used && (real_counter_value = m->module->get_n_used(m->module)) >= 0)
+        usage_counter = real_counter_value;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &m->module->name);
+
+    pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name));
+    append_modargs_variant(&dict_entry_iter, m);
+    pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+
+    if (real_counter_value >= 0)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name, DBUS_TYPE_UINT32, &usage_counter);
+
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, m->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+}
+
+static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    if (m->module->core->disallow_module_loading) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow module unloading.");
+        return;
+    }
+
+    pa_module_unload_request(m->module, false);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static pa_hook_result_t module_proplist_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_module *module_iface = slot_data;
+    pa_module * module = call_data;
+    DBusMessage *signal_msg;
+
+    pa_assert(module_iface);
+    pa_assert(module);
+
+    if (module_iface->module != module)
+        return PA_HOOK_OK;
+
+    if (!pa_proplist_equal(module_iface->proplist, module->proplist)) {
+        DBusMessageIter msg_iter;
+
+        pa_proplist_update(module_iface->proplist, PA_UPDATE_SET, module->proplist);
+
+        pa_assert_se(signal_msg = dbus_message_new_signal(module_iface->path,
+                                                          PA_DBUSIFACE_MODULE_INTERFACE,
+                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal_msg, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, module_iface->proplist);
+
+        pa_dbus_protocol_send_signal(module_iface->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+
+    return PA_HOOK_OK;
+}
+
+pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module) {
+    pa_dbusiface_module *m;
+
+    pa_assert(module);
+
+    m = pa_xnew0(pa_dbusiface_module, 1);
+    m->module = module;
+    m->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, module->index);
+    m->proplist = pa_proplist_copy(module->proplist);
+    m->dbus_protocol = pa_dbus_protocol_get(module->core);
+    m->module_proplist_changed_slot = pa_hook_connect(&module->core->hooks[PA_CORE_HOOK_MODULE_PROPLIST_CHANGED],
+                                                      PA_HOOK_NORMAL, module_proplist_changed_cb, m);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &module_interface_info, m) >= 0);
+
+    return m;
+}
+
+void pa_dbusiface_module_free(pa_dbusiface_module *m) {
+    pa_assert(m);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, m->path, module_interface_info.name) >= 0);
+
+    pa_proplist_free(m->proplist);
+    pa_dbus_protocol_unref(m->dbus_protocol);
+    pa_hook_slot_free(m->module_proplist_changed_slot);
+
+    pa_xfree(m->path);
+    pa_xfree(m);
+}
+
+const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m) {
+    pa_assert(m);
+
+    return m->path;
+}
diff --git a/src/modules/dbus/iface-module.h b/src/modules/dbus/iface-module.h
new file mode 100644 (file)
index 0000000..f4192d0
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef foodbusifacemodulehfoo
+#define foodbusifacemodulehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Module.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Module/
+ * for the Module interface documentation.
+ */
+
+#include <pulsecore/module.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_MODULE_INTERFACE PA_DBUS_CORE_INTERFACE ".Module"
+
+typedef struct pa_dbusiface_module pa_dbusiface_module;
+
+pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module);
+void pa_dbusiface_module_free(pa_dbusiface_module *m);
+
+const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m);
+
+#endif
diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c
new file mode 100644 (file)
index 0000000..5118919
--- /dev/null
@@ -0,0 +1,520 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-sample.h"
+
+#define OBJECT_NAME "sample"
+
+struct pa_dbusiface_sample {
+    pa_dbusiface_core *core;
+
+    pa_scache_entry *sample;
+    char *path;
+    pa_proplist *proplist;
+
+    pa_hook_slot *sample_cache_changed_slot;
+
+    pa_dbus_protocol *dbus_protocol;
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_SAMPLE_FORMAT,
+    PROPERTY_HANDLER_SAMPLE_RATE,
+    PROPERTY_HANDLER_CHANNELS,
+    PROPERTY_HANDLER_DEFAULT_VOLUME,
+    PROPERTY_HANDLER_DURATION,
+    PROPERTY_HANDLER_BYTES,
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]          = { .property_name = "Index",         .type = "u",      .get_cb = handle_get_index,          .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]           = { .property_name = "Name",          .type = "s",      .get_cb = handle_get_name,           .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_FORMAT]  = { .property_name = "SampleFormat",  .type = "u",      .get_cb = handle_get_sample_format,  .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_RATE]    = { .property_name = "SampleRate",    .type = "u",      .get_cb = handle_get_sample_rate,    .set_cb = NULL },
+    [PROPERTY_HANDLER_CHANNELS]       = { .property_name = "Channels",      .type = "au",     .get_cb = handle_get_channels,       .set_cb = NULL },
+    [PROPERTY_HANDLER_DEFAULT_VOLUME] = { .property_name = "DefaultVolume", .type = "au",     .get_cb = handle_get_default_volume, .set_cb = NULL },
+    [PROPERTY_HANDLER_DURATION]       = { .property_name = "Duration",      .type = "t",      .get_cb = handle_get_duration,       .set_cb = NULL },
+    [PROPERTY_HANDLER_BYTES]          = { .property_name = "Bytes",         .type = "u",      .get_cb = handle_get_bytes,          .set_cb = NULL },
+    [PROPERTY_HANDLER_PROPERTY_LIST]  = { .property_name = "PropertyList",  .type = "a{say}", .get_cb = handle_get_property_list,  .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_PLAY,
+    METHOD_HANDLER_PLAY_TO_SINK,
+    METHOD_HANDLER_REMOVE,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info play_args[] = { { "volume", "u", "in" }, { "property_list", "a{say}", "in" } };
+static pa_dbus_arg_info play_to_sink_args[] = { { "sink",          "o",      "in" },
+                                                { "volume",        "u",      "in" },
+                                                { "property_list", "a{say}", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_PLAY] = {
+        .method_name = "Play",
+        .arguments = play_args,
+        .n_arguments = sizeof(play_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_play },
+    [METHOD_HANDLER_PLAY_TO_SINK] = {
+        .method_name = "PlayToSink",
+        .arguments = play_to_sink_args,
+        .n_arguments = sizeof(play_to_sink_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_play_to_sink },
+    [METHOD_HANDLER_REMOVE] = {
+        .method_name = "Remove",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_remove }
+};
+
+enum signal_index {
+    SIGNAL_PROPERTY_LIST_UPDATED,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info sample_interface_info = {
+    .name = PA_DBUSIFACE_SAMPLE_INTERFACE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t idx = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    idx = s->sample->index;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &s->sample->name);
+}
+
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t sample_format = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->memchunk.memblock) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s isn't loaded into memory yet, so its sample format is unknown.", s->sample->name);
+        return;
+    }
+
+    sample_format = s->sample->sample_spec.format;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format);
+}
+
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t sample_rate = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->memchunk.memblock) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s isn't loaded into memory yet, so its sample rate is unknown.", s->sample->name);
+        return;
+    }
+
+    sample_rate = s->sample->sample_spec.rate;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_rate);
+}
+
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->memchunk.memblock) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s isn't loaded into memory yet, so its channel map is unknown.", s->sample->name);
+        return;
+    }
+
+    for (i = 0; i < s->sample->channel_map.channels; ++i)
+        channels[i] = s->sample->channel_map.map[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, s->sample->channel_map.channels);
+}
+
+static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t default_volume[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->volume_is_set) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s doesn't have default volume stored.", s->sample->name);
+        return;
+    }
+
+    for (i = 0; i < s->sample->volume.channels; ++i)
+        default_volume[i] = s->sample->volume.values[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, default_volume, s->sample->volume.channels);
+}
+
+static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint64_t duration = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->memchunk.memblock) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s isn't loaded into memory yet, so its duration is unknown.", s->sample->name);
+        return;
+    }
+
+    duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &duration);
+}
+
+static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t bytes = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->memchunk.memblock) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s isn't loaded into memory yet, so its size is unknown.", s->sample->name);
+        return;
+    }
+
+    bytes = s->sample->memchunk.length;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &bytes);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t idx = 0;
+    dbus_uint32_t sample_format = 0;
+    dbus_uint32_t sample_rate = 0;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
+    dbus_uint32_t default_volume[PA_CHANNELS_MAX];
+    dbus_uint64_t duration = 0;
+    dbus_uint32_t bytes = 0;
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    idx = s->sample->index;
+    if (s->sample->memchunk.memblock) {
+        sample_format = s->sample->sample_spec.format;
+        sample_rate = s->sample->sample_spec.rate;
+        for (i = 0; i < s->sample->channel_map.channels; ++i)
+            channels[i] = s->sample->channel_map.map[i];
+        duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec);
+        bytes = s->sample->memchunk.length;
+    }
+    if (s->sample->volume_is_set) {
+        for (i = 0; i < s->sample->volume.channels; ++i)
+            default_volume[i] = s->sample->volume.values[i];
+    }
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &s->sample->name);
+
+    if (s->sample->memchunk.memblock) {
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format);
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &sample_rate);
+        pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, s->sample->channel_map.channels);
+    }
+
+    if (s->sample->volume_is_set)
+        pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_VOLUME].property_name, DBUS_TYPE_UINT32, default_volume, s->sample->volume.channels);
+
+    if (s->sample->memchunk.memblock) {
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DURATION].property_name, DBUS_TYPE_UINT64, &duration);
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BYTES].property_name, DBUS_TYPE_UINT32, &bytes);
+    }
+
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    DBusMessageIter msg_iter;
+    dbus_uint32_t volume = 0;
+    pa_proplist *property_list = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &volume);
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+        return;
+
+    if (!PA_VOLUME_IS_VALID(volume)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume.");
+        goto finish;
+    }
+
+    if (!s->sample->core->default_sink) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                           "Can't play sample %s, because there are no sinks available.", s->sample->name);
+        goto finish;
+    }
+
+    if (pa_scache_play_item(s->sample->core,
+                            s->sample->name,
+                            s->sample->core->default_sink,
+                            volume,
+                            property_list,
+                            NULL) < 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name);
+        goto finish;
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+finish:
+    if (property_list)
+        pa_proplist_free(property_list);
+}
+
+static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    DBusMessageIter msg_iter;
+    const char *sink_path = NULL;
+    dbus_uint32_t volume = 0;
+    pa_proplist *property_list = NULL;
+    pa_sink *sink = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &sink_path);
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &volume);
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+        return;
+
+    if (!(sink = pa_dbusiface_core_get_sink(s->core, sink_path))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_path);
+        goto finish;
+    }
+
+    if (!PA_VOLUME_IS_VALID(volume)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume.");
+        goto finish;
+    }
+
+    if (pa_scache_play_item(s->sample->core, s->sample->name, sink, volume, property_list, NULL) < 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name);
+        goto finish;
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+finish:
+    if (property_list)
+        pa_proplist_free(property_list);
+}
+
+static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (pa_scache_remove_item(s->sample->core, s->sample->name) < 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Removing sample %s failed.", s->sample->name);
+        return;
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static pa_hook_result_t sample_cache_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_sample *sample_iface = slot_data;
+    pa_scache_entry *sample = call_data;
+    DBusMessage *signal_msg;
+
+    pa_assert(sample);
+    pa_assert(sample_iface);
+
+    if (sample_iface->sample != sample)
+        return PA_HOOK_OK;
+
+    if (!pa_proplist_equal(sample_iface->proplist, sample_iface->sample->proplist)) {
+        DBusMessageIter msg_iter;
+
+        pa_proplist_update(sample_iface->proplist, PA_UPDATE_SET, sample_iface->sample->proplist);
+
+        pa_assert_se(signal_msg = dbus_message_new_signal(sample_iface->path,
+                                                          PA_DBUSIFACE_SAMPLE_INTERFACE,
+                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal_msg, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, sample_iface->proplist);
+
+        pa_dbus_protocol_send_signal(sample_iface->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+
+    return PA_HOOK_OK;
+}
+
+pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample) {
+    pa_dbusiface_sample *s = NULL;
+
+    pa_assert(core);
+    pa_assert(sample);
+
+    s = pa_xnew0(pa_dbusiface_sample, 1);
+    s->core = core;
+    s->sample = sample;
+    s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, sample->index);
+    s->proplist = pa_proplist_copy(sample->proplist);
+    s->dbus_protocol = pa_dbus_protocol_get(sample->core);
+    s->sample_cache_changed_slot = pa_hook_connect(&sample->core->hooks[PA_CORE_HOOK_SAMPLE_CACHE_CHANGED],
+                                                   PA_HOOK_NORMAL, sample_cache_changed_cb, s);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &sample_interface_info, s) >= 0);
+
+    return s;
+}
+
+void pa_dbusiface_sample_free(pa_dbusiface_sample *s) {
+    pa_assert(s);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, sample_interface_info.name) >= 0);
+
+    pa_hook_slot_free(s->sample_cache_changed_slot);
+    pa_proplist_free(s->proplist);
+    pa_dbus_protocol_unref(s->dbus_protocol);
+
+    pa_xfree(s->path);
+    pa_xfree(s);
+}
+
+const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *s) {
+    pa_assert(s);
+
+    return s->path;
+}
diff --git a/src/modules/dbus/iface-sample.h b/src/modules/dbus/iface-sample.h
new file mode 100644 (file)
index 0000000..443b764
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef foodbusifacesamplehfoo
+#define foodbusifacesamplehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Sample.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Sample/
+ * for the Sample interface documentation.
+ */
+
+#include <pulsecore/core-scache.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_SAMPLE_INTERFACE PA_DBUS_CORE_INTERFACE ".Sample"
+
+typedef struct pa_dbusiface_sample pa_dbusiface_sample;
+
+pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample);
+void pa_dbusiface_sample_free(pa_dbusiface_sample *c);
+
+const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *c);
+
+#endif
diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c
new file mode 100644 (file)
index 0000000..ade62ca
--- /dev/null
@@ -0,0 +1,1000 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+  Copyright 2009 Vincent Filali-Ansary <filali.v@azurdigitalnetworks.net>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-stream.h"
+
+#define PLAYBACK_OBJECT_NAME "playback_stream"
+#define RECORD_OBJECT_NAME "record_stream"
+
+enum stream_type {
+    STREAM_TYPE_PLAYBACK,
+    STREAM_TYPE_RECORD
+};
+
+struct pa_dbusiface_stream {
+    pa_dbusiface_core *core;
+
+    union {
+        pa_sink_input *sink_input;
+        pa_source_output *source_output;
+    };
+    enum stream_type type;
+    char *path;
+    union {
+        pa_sink *sink;
+        pa_source *source;
+    };
+    uint32_t sample_rate;
+    pa_cvolume volume;
+    dbus_bool_t mute;
+    pa_proplist *proplist;
+
+    bool has_volume;
+
+    pa_dbus_protocol *dbus_protocol;
+    pa_hook_slot *send_event_slot;
+    pa_hook_slot *move_finish_slot;
+    pa_hook_slot *volume_changed_slot;
+    pa_hook_slot *mute_changed_slot;
+    pa_hook_slot *proplist_changed_slot;
+    pa_hook_slot *state_changed_slot;
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_DRIVER,
+    PROPERTY_HANDLER_OWNER_MODULE,
+    PROPERTY_HANDLER_CLIENT,
+    PROPERTY_HANDLER_DEVICE,
+    PROPERTY_HANDLER_SAMPLE_FORMAT,
+    PROPERTY_HANDLER_SAMPLE_RATE,
+    PROPERTY_HANDLER_CHANNELS,
+    PROPERTY_HANDLER_VOLUME,
+    PROPERTY_HANDLER_MUTE,
+    PROPERTY_HANDLER_BUFFER_LATENCY,
+    PROPERTY_HANDLER_DEVICE_LATENCY,
+    PROPERTY_HANDLER_RESAMPLE_METHOD,
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]           = { .property_name = "Index",          .type = "u",      .get_cb = handle_get_index,           .set_cb = NULL },
+    [PROPERTY_HANDLER_DRIVER]          = { .property_name = "Driver",         .type = "s",      .get_cb = handle_get_driver,          .set_cb = NULL },
+    [PROPERTY_HANDLER_OWNER_MODULE]    = { .property_name = "OwnerModule",    .type = "o",      .get_cb = handle_get_owner_module,    .set_cb = NULL },
+    [PROPERTY_HANDLER_CLIENT]          = { .property_name = "Client",         .type = "o",      .get_cb = handle_get_client,          .set_cb = NULL },
+    [PROPERTY_HANDLER_DEVICE]          = { .property_name = "Device",         .type = "o",      .get_cb = handle_get_device,          .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_FORMAT]   = { .property_name = "SampleFormat",   .type = "u",      .get_cb = handle_get_sample_format,   .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_RATE]     = { .property_name = "SampleRate",     .type = "u",      .get_cb = handle_get_sample_rate,     .set_cb = NULL },
+    [PROPERTY_HANDLER_CHANNELS]        = { .property_name = "Channels",       .type = "au",     .get_cb = handle_get_channels,        .set_cb = NULL },
+    [PROPERTY_HANDLER_VOLUME]          = { .property_name = "Volume",         .type = "au",     .get_cb = handle_get_volume,          .set_cb = handle_set_volume },
+    [PROPERTY_HANDLER_MUTE]            = { .property_name = "Mute",           .type = "b",      .get_cb = handle_get_mute,            .set_cb = handle_set_mute },
+    [PROPERTY_HANDLER_BUFFER_LATENCY]  = { .property_name = "BufferLatency",  .type = "t",      .get_cb = handle_get_buffer_latency,  .set_cb = NULL },
+    [PROPERTY_HANDLER_DEVICE_LATENCY]  = { .property_name = "DeviceLatency",  .type = "t",      .get_cb = handle_get_device_latency,  .set_cb = NULL },
+    [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s",      .get_cb = handle_get_resample_method, .set_cb = NULL },
+    [PROPERTY_HANDLER_PROPERTY_LIST]   = { .property_name = "PropertyList",   .type = "a{say}", .get_cb = handle_get_property_list,   .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_MOVE,
+    METHOD_HANDLER_KILL,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info move_args[] = { { "device", "o", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_MOVE] = {
+        .method_name = "Move",
+        .arguments = move_args,
+        .n_arguments = sizeof(move_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_move },
+    [METHOD_HANDLER_KILL] = {
+        .method_name = "Kill",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_kill }
+};
+
+enum signal_index {
+    SIGNAL_DEVICE_UPDATED,
+    SIGNAL_SAMPLE_RATE_UPDATED,
+    SIGNAL_VOLUME_UPDATED,
+    SIGNAL_MUTE_UPDATED,
+    SIGNAL_PROPERTY_LIST_UPDATED,
+    SIGNAL_STREAM_EVENT,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info device_updated_args[]        = { { "device",        "o",      NULL } };
+static pa_dbus_arg_info sample_rate_updated_args[]   = { { "sample_rate",   "u",      NULL } };
+static pa_dbus_arg_info volume_updated_args[]        = { { "volume",        "au",     NULL } };
+static pa_dbus_arg_info mute_updated_args[]          = { { "muted",         "b",      NULL } };
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+static pa_dbus_arg_info stream_event_args[]          = { { "name",          "s",      NULL }, { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_DEVICE_UPDATED]        = { .name = "DeviceUpdated",       .arguments = device_updated_args,        .n_arguments = 1 },
+    [SIGNAL_SAMPLE_RATE_UPDATED]   = { .name = "SampleRateUpdated",   .arguments = sample_rate_updated_args,   .n_arguments = 1 },
+    [SIGNAL_VOLUME_UPDATED]        = { .name = "VolumeUpdated",       .arguments = volume_updated_args,        .n_arguments = 1 },
+    [SIGNAL_MUTE_UPDATED]          = { .name = "MuteUpdated",         .arguments = mute_updated_args,          .n_arguments = 1 },
+    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 },
+    [SIGNAL_STREAM_EVENT]          = { .name = "StreamEvent",         .arguments = stream_event_args,          .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }
+};
+
+static pa_dbus_interface_info stream_interface_info = {
+    .name = PA_DBUSIFACE_STREAM_INTERFACE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_uint32_t idx;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+/* The returned string has to be freed with pa_xfree() by the caller. */
+static char *stream_to_string(pa_dbusiface_stream *s) {
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        return pa_sprintf_malloc("Playback stream %u", (unsigned) s->sink_input->index);
+    else
+        return pa_sprintf_malloc("Record stream %u", (unsigned) s->source_output->index);
+}
+
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    const char *driver = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    driver = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->driver : s->source_output->driver;
+
+    if (!driver) {
+        char *str = stream_to_string(s);
+
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have a driver.", str);
+        pa_xfree(str);
+
+        return;
+    }
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    pa_module *owner_module = NULL;
+    const char *object_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    owner_module = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->module : s->source_output->module;
+
+    if (!owner_module) {
+        char *str = stream_to_string(s);
+
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have an owner module.", str);
+        pa_xfree(str);
+
+        return;
+    }
+
+    object_path = pa_dbusiface_core_get_module_path(s->core, owner_module);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    pa_client *client = NULL;
+    const char *object_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    client = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->client : s->source_output->client;
+
+    if (!client) {
+        char *str = stream_to_string(s);
+
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s isn't associated to any client.", str);
+        pa_xfree(str);
+
+        return;
+    }
+
+    object_path = pa_dbusiface_core_get_client_path(s->core, client);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    const char *device = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        device = pa_dbusiface_core_get_sink_path(s->core, s->sink);
+    else
+        device = pa_dbusiface_core_get_source_path(s->core, s->source);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &device);
+}
+
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_uint32_t sample_format = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    sample_format = (s->type == STREAM_TYPE_PLAYBACK)
+                    ? s->sink_input->sample_spec.format
+                    : s->source_output->sample_spec.format;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format);
+}
+
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &s->sample_rate);
+}
+
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    pa_channel_map *channel_map = NULL;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    channel_map = (s->type == STREAM_TYPE_PLAYBACK) ? &s->sink_input->channel_map : &s->source_output->channel_map;
+
+    for (i = 0; i < channel_map->channels; ++i)
+        channels[i] = channel_map->map[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels);
+}
+
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_uint32_t volume[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->has_volume) {
+        char *str = stream_to_string(s);
+
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have volume.", str);
+        pa_xfree(str);
+
+        return;
+    }
+
+    for (i = 0; i < s->volume.channels; ++i)
+        volume[i] = s->volume.values[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, s->volume.channels);
+}
+
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    bool volume_writable = true;
+    DBusMessageIter array_iter;
+    int stream_channels = 0;
+    dbus_uint32_t *volume = NULL;
+    int n_volume_entries = 0;
+    pa_cvolume new_vol;
+    int i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(s);
+
+    volume_writable = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->volume_writable : false;
+
+    if (!s->has_volume || !volume_writable) {
+        char *str = stream_to_string(s);
+
+        if (!s->has_volume)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have volume.", str);
+        else if (!volume_writable)
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "%s has read-only volume.", str);
+        pa_xfree(str);
+
+        return;
+    }
+
+    stream_channels = s->sink_input->channel_map.channels;
+
+    dbus_message_iter_recurse(iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries);
+
+    if (n_volume_entries != stream_channels && n_volume_entries != 1) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Expected %u volume entries, got %u.", stream_channels, n_volume_entries);
+        return;
+    }
+
+    pa_cvolume_init(&new_vol);
+    new_vol.channels = n_volume_entries;
+
+    for (i = 0; i < n_volume_entries; ++i) {
+        if (!PA_VOLUME_IS_VALID(volume[i])) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u", volume[i]);
+            return;
+        }
+        new_vol.values[i] = volume[i];
+    }
+
+    pa_sink_input_set_volume(s->sink_input, &new_vol, true, true);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_RECORD) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
+        return;
+    }
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->mute);
+}
+
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_bool_t mute = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(s);
+
+    dbus_message_iter_get_basic(iter, &mute);
+
+    if (s->type == STREAM_TYPE_RECORD) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
+        return;
+    }
+
+    pa_sink_input_set_mute(s->sink_input, mute, true);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_uint64_t buffer_latency = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        buffer_latency = pa_sink_input_get_latency(s->sink_input, NULL);
+    else
+        buffer_latency = pa_source_output_get_latency(s->source_output, NULL);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &buffer_latency);
+}
+
+static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_uint64_t device_latency = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        pa_sink_input_get_latency(s->sink_input, &device_latency);
+    else
+        pa_source_output_get_latency(s->source_output, &device_latency);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &device_latency);
+}
+
+static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    const char *resample_method = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method);
+    else
+        resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method);
+
+    if (!resample_method)
+        resample_method = "";
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &resample_method);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t idx = 0;
+    const char *driver = NULL;
+    pa_module *owner_module = NULL;
+    const char *owner_module_path = NULL;
+    pa_client *client = NULL;
+    const char *client_path = NULL;
+    const char *device = NULL;
+    dbus_uint32_t sample_format = 0;
+    pa_channel_map *channel_map = NULL;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
+    dbus_uint32_t volume[PA_CHANNELS_MAX];
+    dbus_uint64_t buffer_latency = 0;
+    dbus_uint64_t device_latency = 0;
+    const char *resample_method = NULL;
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->has_volume) {
+        for (i = 0; i < s->volume.channels; ++i)
+            volume[i] = s->volume.values[i];
+    }
+
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        idx = s->sink_input->index;
+        driver = s->sink_input->driver;
+        owner_module = s->sink_input->module;
+        client = s->sink_input->client;
+        device = pa_dbusiface_core_get_sink_path(s->core, s->sink);
+        sample_format = s->sink_input->sample_spec.format;
+        channel_map = &s->sink_input->channel_map;
+        buffer_latency = pa_sink_input_get_latency(s->sink_input, &device_latency);
+        resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method);
+    } else {
+        idx = s->source_output->index;
+        driver = s->source_output->driver;
+        owner_module = s->source_output->module;
+        client = s->source_output->client;
+        device = pa_dbusiface_core_get_source_path(s->core, s->source);
+        sample_format = s->source_output->sample_spec.format;
+        channel_map = &s->source_output->channel_map;
+        buffer_latency = pa_source_output_get_latency(s->source_output, &device_latency);
+        resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method);
+    }
+    if (owner_module)
+        owner_module_path = pa_dbusiface_core_get_module_path(s->core, owner_module);
+    if (client)
+        client_path = pa_dbusiface_core_get_client_path(s->core, client);
+    for (i = 0; i < channel_map->channels; ++i)
+        channels[i] = channel_map->map[i];
+    if (!resample_method)
+        resample_method = "";
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+
+    if (driver)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver);
+
+    if (owner_module)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path);
+
+    if (client)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &client_path);
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE].property_name, DBUS_TYPE_OBJECT_PATH, &device);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &s->sample_rate);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels);
+
+    if (s->has_volume) {
+        pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels);
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &s->mute);
+    }
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BUFFER_LATENCY].property_name, DBUS_TYPE_UINT64, &buffer_latency);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE_LATENCY].property_name, DBUS_TYPE_UINT64, &device_latency);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RESAMPLE_METHOD].property_name, DBUS_TYPE_STRING, &resample_method);
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    const char *device = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_INVALID));
+
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        pa_sink *sink = pa_dbusiface_core_get_sink(s->core, device);
+
+        if (!sink) {
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", device);
+            return;
+        }
+
+        if (pa_sink_input_move_to(s->sink_input, sink, true) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                               "Moving playback stream %u to sink %s failed.", s->sink_input->index, sink->name);
+            return;
+        }
+    } else {
+        pa_source *source = pa_dbusiface_core_get_source(s->core, device);
+
+        if (!source) {
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", device);
+            return;
+        }
+
+        if (pa_source_output_move_to(s->source_output, source, true) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                               "Moving record stream %u to source %s failed.", s->source_output->index, source->name);
+            return;
+        }
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        pa_sink_input_kill(s->sink_input);
+    else
+        pa_source_output_kill(s->source_output);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void check_and_signal_rate(pa_dbusiface_stream *s) {
+    DBusMessage *signal_msg = NULL;
+    uint32_t new_sample_rate = 0;
+
+    pa_assert(s);
+
+    new_sample_rate = (s->type == STREAM_TYPE_PLAYBACK)
+                      ? s->sink_input->sample_spec.rate
+                      : s->source_output->sample_spec.rate;
+
+    if (s->sample_rate != new_sample_rate) {
+        s->sample_rate = new_sample_rate;
+
+        pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+                                                          PA_DBUSIFACE_STREAM_INTERFACE,
+                                                          signals[SIGNAL_SAMPLE_RATE_UPDATED].name));
+        pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_UINT32, &s->sample_rate, DBUS_TYPE_INVALID));
+
+        pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+}
+
+static pa_hook_result_t move_finish_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_stream *s = slot_data;
+    const char *new_device_path = NULL;
+    DBusMessage *signal_msg = NULL;
+
+    if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
+        (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
+        return PA_HOOK_OK;
+
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        pa_sink *new_sink = s->sink_input->sink;
+
+        if (s->sink != new_sink) {
+            pa_sink_unref(s->sink);
+            s->sink = pa_sink_ref(new_sink);
+
+            new_device_path = pa_dbusiface_core_get_sink_path(s->core, new_sink);
+
+            pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+                                                              PA_DBUSIFACE_STREAM_INTERFACE,
+                                                              signals[SIGNAL_DEVICE_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID));
+
+            pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+            dbus_message_unref(signal_msg);
+        }
+    } else {
+        pa_source *new_source = s->source_output->source;
+
+        if (s->source != new_source) {
+            pa_source_unref(s->source);
+            s->source = pa_source_ref(new_source);
+
+            new_device_path = pa_dbusiface_core_get_source_path(s->core, new_source);
+
+            pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+                                                              PA_DBUSIFACE_STREAM_INTERFACE,
+                                                              signals[SIGNAL_DEVICE_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID));
+
+            pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+            dbus_message_unref(signal_msg);
+        }
+    }
+
+    check_and_signal_rate(s);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t volume_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_stream *s = slot_data;
+    DBusMessage *signal_msg = NULL;
+    unsigned i = 0;
+
+    if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
+        (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
+        return PA_HOOK_OK;
+
+    if (s->type == STREAM_TYPE_PLAYBACK && s->has_volume) {
+        pa_cvolume new_volume;
+
+        pa_sink_input_get_volume(s->sink_input, &new_volume, true);
+
+        if (!pa_cvolume_equal(&s->volume, &new_volume)) {
+            dbus_uint32_t volume[PA_CHANNELS_MAX];
+            dbus_uint32_t *volume_ptr = volume;
+
+            s->volume = new_volume;
+
+            for (i = 0; i < s->volume.channels; ++i)
+                volume[i] = s->volume.values[i];
+
+            pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+                                                              PA_DBUSIFACE_STREAM_INTERFACE,
+                                                              signals[SIGNAL_VOLUME_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal_msg,
+                                                  DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels,
+                                                  DBUS_TYPE_INVALID));
+
+            pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+            dbus_message_unref(signal_msg);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t mute_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_stream *s = slot_data;
+    DBusMessage *signal_msg = NULL;
+
+    if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
+        (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
+        return PA_HOOK_OK;
+
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        bool new_mute = false;
+
+        new_mute = s->sink_input->muted;
+
+        if (s->mute != new_mute) {
+            s->mute = new_mute;
+
+            pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+                                                              PA_DBUSIFACE_STREAM_INTERFACE,
+                                                              signals[SIGNAL_MUTE_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_BOOLEAN, &s->mute, DBUS_TYPE_INVALID));
+
+            pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+            dbus_message_unref(signal_msg);
+            signal_msg = NULL;
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t proplist_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_stream *s = slot_data;
+    DBusMessage *signal_msg = NULL;
+    pa_proplist *new_proplist = NULL;
+
+    if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
+        (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
+        return PA_HOOK_OK;
+
+    new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist;
+
+    if (!pa_proplist_equal(s->proplist, new_proplist)) {
+        DBusMessageIter msg_iter;
+
+        pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist);
+
+        pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+                                                          PA_DBUSIFACE_STREAM_INTERFACE,
+                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal_msg, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, s->proplist);
+
+        pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+        dbus_message_unref(signal_msg);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t state_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_stream *s = slot_data;
+
+    pa_assert(s);
+
+    if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
+        (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
+        return PA_HOOK_OK;
+
+    check_and_signal_rate(s);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t send_event_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_stream *s = slot_data;
+    DBusMessage *signal_msg = NULL;
+    DBusMessageIter msg_iter;
+    const char *name = NULL;
+    pa_proplist *property_list = NULL;
+
+    pa_assert(call_data);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        pa_sink_input_send_event_hook_data *data = call_data;
+
+        if (data->sink_input != s->sink_input)
+            return PA_HOOK_OK;
+
+        name = data->event;
+        property_list = data->data;
+    } else {
+        pa_source_output_send_event_hook_data *data = call_data;
+
+        if (data->source_output != s->source_output)
+            return PA_HOOK_OK;
+
+        name = data->event;
+        property_list = data->data;
+    }
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
+                                                      PA_DBUSIFACE_STREAM_INTERFACE,
+                                                      signals[SIGNAL_STREAM_EVENT].name));
+    dbus_message_iter_init_append(signal_msg, &msg_iter);
+    pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name));
+    pa_dbus_append_proplist(&msg_iter, property_list);
+
+    pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
+    return PA_HOOK_OK;
+}
+
+pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) {
+    pa_dbusiface_stream *s;
+
+    pa_assert(core);
+    pa_assert(sink_input);
+
+    s = pa_xnew(pa_dbusiface_stream, 1);
+    s->core = core;
+    s->sink_input = pa_sink_input_ref(sink_input);
+    s->type = STREAM_TYPE_PLAYBACK;
+    s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index);
+    s->sink = pa_sink_ref(sink_input->sink);
+    s->sample_rate = sink_input->sample_spec.rate;
+    s->has_volume = pa_sink_input_is_volume_readable(sink_input);
+
+    if (s->has_volume)
+        pa_sink_input_get_volume(sink_input, &s->volume, true);
+    else
+        pa_cvolume_init(&s->volume);
+
+    s->mute = sink_input->muted;
+    s->proplist = pa_proplist_copy(sink_input->proplist);
+    s->dbus_protocol = pa_dbus_protocol_get(sink_input->core);
+    s->send_event_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT],
+                                         PA_HOOK_NORMAL,
+                                         send_event_cb,
+                                         s);
+    s->move_finish_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH],
+                                          PA_HOOK_NORMAL, move_finish_cb, s);
+    s->volume_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED],
+                                             PA_HOOK_NORMAL, volume_changed_cb, s);
+    s->mute_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED],
+                                           PA_HOOK_NORMAL, mute_changed_cb, s);
+    s->proplist_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED],
+                                               PA_HOOK_NORMAL, proplist_changed_cb, s);
+    s->state_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED],
+                                            PA_HOOK_NORMAL, state_changed_cb, s);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
+
+    return s;
+}
+
+pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output) {
+    pa_dbusiface_stream *s;
+
+    pa_assert(core);
+    pa_assert(source_output);
+
+    s = pa_xnew(pa_dbusiface_stream, 1);
+    s->core = core;
+    s->source_output = pa_source_output_ref(source_output);
+    s->type = STREAM_TYPE_RECORD;
+    s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index);
+    s->source = pa_source_ref(source_output->source);
+    s->sample_rate = source_output->sample_spec.rate;
+    pa_cvolume_init(&s->volume);
+    s->mute = false;
+    s->proplist = pa_proplist_copy(source_output->proplist);
+    s->has_volume = false;
+    s->dbus_protocol = pa_dbus_protocol_get(source_output->core);
+    s->send_event_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT],
+                                         PA_HOOK_NORMAL,
+                                         send_event_cb,
+                                         s);
+    s->move_finish_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH],
+                                          PA_HOOK_NORMAL, move_finish_cb, s);
+    s->volume_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED],
+                                             PA_HOOK_NORMAL, volume_changed_cb, s);
+    s->mute_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED],
+                                           PA_HOOK_NORMAL, mute_changed_cb, s);
+    s->proplist_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED],
+                                               PA_HOOK_NORMAL, proplist_changed_cb, s);
+    s->state_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED],
+                                            PA_HOOK_NORMAL, state_changed_cb, s);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
+
+    return s;
+}
+
+void pa_dbusiface_stream_free(pa_dbusiface_stream *s) {
+    pa_assert(s);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, stream_interface_info.name) >= 0);
+
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        pa_sink_input_unref(s->sink_input);
+        pa_sink_unref(s->sink);
+    } else {
+        pa_source_output_unref(s->source_output);
+        pa_source_unref(s->source);
+    }
+
+    pa_proplist_free(s->proplist);
+    pa_dbus_protocol_unref(s->dbus_protocol);
+    pa_hook_slot_free(s->send_event_slot);
+    pa_hook_slot_free(s->move_finish_slot);
+    pa_hook_slot_free(s->volume_changed_slot);
+    pa_hook_slot_free(s->mute_changed_slot);
+    pa_hook_slot_free(s->proplist_changed_slot);
+    pa_hook_slot_free(s->state_changed_slot);
+
+    pa_xfree(s->path);
+    pa_xfree(s);
+}
+
+const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s) {
+    pa_assert(s);
+
+    return s->path;
+}
diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h
new file mode 100644 (file)
index 0000000..155cd7d
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef foodbusifacestreamhfoo
+#define foodbusifacestreamhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Stream.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/Stream/
+ * for the Stream interface documentation.
+ */
+
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_STREAM_INTERFACE PA_DBUS_CORE_INTERFACE ".Stream"
+
+typedef struct pa_dbusiface_stream pa_dbusiface_stream;
+
+pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input);
+pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output);
+void pa_dbusiface_stream_free(pa_dbusiface_stream *s);
+
+const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s);
+
+#endif
diff --git a/src/modules/dbus/module-dbus-protocol.c b/src/modules/dbus/module-dbus-protocol.c
new file mode 100644 (file)
index 0000000..2cd206e
--- /dev/null
@@ -0,0 +1,619 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Shams E. King
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/client.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/module.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-client.h"
+#include "iface-core.h"
+
+#include "module-dbus-protocol-symdef.h"
+
+PA_MODULE_DESCRIPTION("D-Bus interface");
+PA_MODULE_USAGE(
+        "access=local|remote|local,remote "
+        "tcp_port=<port number> "
+        "tcp_listen=<hostname>");
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_AUTHOR("Tanu Kaskinen");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+
+enum server_type {
+    SERVER_TYPE_LOCAL,
+    SERVER_TYPE_TCP
+};
+
+struct server;
+struct connection;
+
+struct userdata {
+    pa_module *module;
+    bool local_access;
+    bool remote_access;
+    uint32_t tcp_port;
+    char *tcp_listen;
+
+    struct server *local_server;
+    struct server *tcp_server;
+
+    pa_idxset *connections;
+
+    pa_defer_event *cleanup_event;
+
+    pa_dbus_protocol *dbus_protocol;
+    pa_dbusiface_core *core_iface;
+};
+
+struct server {
+    struct userdata *userdata;
+    enum server_type type;
+    DBusServer *dbus_server;
+};
+
+struct connection {
+    struct server *server;
+    pa_dbus_wrap_connection *wrap_conn;
+    pa_client *client;
+};
+
+static const char* const valid_modargs[] = {
+    "access",
+    "tcp_port",
+    "tcp_listen",
+    NULL
+};
+
+static void connection_free(struct connection *c) {
+    pa_assert(c);
+
+    pa_assert_se(pa_dbus_protocol_unregister_connection(c->server->userdata->dbus_protocol, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0);
+
+    pa_client_free(c->client);
+    pa_dbus_wrap_connection_free(c->wrap_conn);
+    pa_xfree(c);
+}
+
+/* Called from pa_client_kill(). */
+static void client_kill_cb(pa_client *c) {
+    struct connection *conn;
+
+    pa_assert(c);
+    pa_assert(c->userdata);
+
+    conn = c->userdata;
+    pa_idxset_remove_by_data(conn->server->userdata->connections, conn, NULL);
+    connection_free(conn);
+    c->userdata = NULL;
+
+    pa_log_info("Connection killed.");
+}
+
+/* Called from pa_client_send_event(). */
+static void client_send_event_cb(pa_client *c, const char *name, pa_proplist *data) {
+    struct connection *conn = NULL;
+    DBusMessage *signal_msg = NULL;
+    DBusMessageIter msg_iter;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(data);
+    pa_assert(c->userdata);
+
+    conn = c->userdata;
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(pa_dbusiface_core_get_client_path(conn->server->userdata->core_iface, c),
+                                                      PA_DBUSIFACE_CLIENT_INTERFACE,
+                                                      "ClientEvent"));
+    dbus_message_iter_init_append(signal_msg, &msg_iter);
+    pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name));
+    pa_dbus_append_proplist(&msg_iter, data);
+
+    pa_assert_se(dbus_connection_send(pa_dbus_wrap_connection_get(conn->wrap_conn), signal_msg, NULL));
+    dbus_message_unref(signal_msg);
+}
+
+/* Called by D-Bus at the authentication phase. */
+static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) {
+    pa_log_debug("Allowing connection by user %lu.", uid);
+
+    return TRUE;
+}
+
+static DBusHandlerResult disconnection_filter_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
+    struct connection *c = user_data;
+
+    pa_assert(connection);
+    pa_assert(message);
+    pa_assert(c);
+
+    if (dbus_message_is_signal(message, "org.freedesktop.DBus.Local", "Disconnected")) {
+        /* The connection died. Now we want to free the connection object, but
+         * let's wait until this message is fully processed, in case someone
+         * else is interested in this signal too. */
+        c->server->userdata->module->core->mainloop->defer_enable(c->server->userdata->cleanup_event, 1);
+    }
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+/* Called by D-Bus when a new client connection is received. */
+static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) {
+    struct server *s = data;
+    struct connection *c;
+    pa_client_new_data new_data;
+    pa_client *client;
+
+    pa_assert(new_connection);
+    pa_assert(s);
+
+    pa_client_new_data_init(&new_data);
+    new_data.module = s->userdata->module;
+    new_data.driver = __FILE__;
+    pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client");
+    client = pa_client_new(s->userdata->module->core, &new_data);
+    pa_client_new_data_done(&new_data);
+
+    if (!client) {
+        dbus_connection_close(new_connection);
+        return;
+    }
+
+    if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) {
+        /* FIXME: Here we allow anyone from anywhere to access the server,
+         * anonymously. Access control should be configurable. */
+        dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL);
+        dbus_connection_set_allow_anonymous(new_connection, TRUE);
+    }
+
+    c = pa_xnew(struct connection, 1);
+    c->server = s;
+    c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, true, new_connection);
+    c->client = client;
+
+    c->client->kill = client_kill_cb;
+    c->client->send_event = client_send_event_cb;
+    c->client->userdata = c;
+
+    pa_assert_se(dbus_connection_add_filter(new_connection, disconnection_filter_cb, c, NULL));
+
+    pa_idxset_put(s->userdata->connections, c, NULL);
+
+    pa_assert_se(pa_dbus_protocol_register_connection(s->userdata->dbus_protocol, new_connection, c->client) >= 0);
+}
+
+/* Called by PA mainloop when a D-Bus fd watch event needs handling. */
+static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    unsigned int flags = 0;
+    DBusWatch *watch = userdata;
+
+#ifdef HAVE_DBUS_WATCH_GET_UNIX_FD
+    pa_assert(fd == dbus_watch_get_unix_fd(watch));
+#else
+    pa_assert(fd == dbus_watch_get_fd(watch));
+#endif
+
+    if (!dbus_watch_get_enabled(watch)) {
+        pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
+        return;
+    }
+
+    if (events & PA_IO_EVENT_INPUT)
+        flags |= DBUS_WATCH_READABLE;
+    if (events & PA_IO_EVENT_OUTPUT)
+        flags |= DBUS_WATCH_WRITABLE;
+    if (events & PA_IO_EVENT_HANGUP)
+        flags |= DBUS_WATCH_HANGUP;
+    if (events & PA_IO_EVENT_ERROR)
+        flags |= DBUS_WATCH_ERROR;
+
+    dbus_watch_handle(watch, flags);
+}
+
+/* Called by PA mainloop when a D-Bus timer event needs handling. */
+static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) {
+    DBusTimeout *timeout = userdata;
+
+    if (dbus_timeout_get_enabled(timeout)) {
+        struct timeval next = *tv;
+        dbus_timeout_handle(timeout);
+
+        /* restart it for the next scheduled time */
+        pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+        mainloop->time_restart(e, &next);
+    }
+}
+
+/* Translates D-Bus fd watch event flags to PA IO event flags. */
+static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
+    unsigned int flags;
+    pa_io_event_flags_t events = 0;
+
+    pa_assert(watch);
+
+    flags = dbus_watch_get_flags(watch);
+
+    /* no watch flags for disabled watches */
+    if (!dbus_watch_get_enabled(watch))
+        return PA_IO_EVENT_NULL;
+
+    if (flags & DBUS_WATCH_READABLE)
+        events |= PA_IO_EVENT_INPUT;
+    if (flags & DBUS_WATCH_WRITABLE)
+        events |= PA_IO_EVENT_OUTPUT;
+
+    return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is added. */
+static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) {
+    struct server *s = data;
+    pa_mainloop_api *mainloop;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(s);
+
+    mainloop = s->userdata->module->core->mainloop;
+
+    ev = mainloop->io_new(
+            mainloop,
+#ifdef HAVE_DBUS_WATCH_GET_UNIX_FD
+            dbus_watch_get_unix_fd(watch),
+#else
+            dbus_watch_get_fd(watch),
+#endif
+            get_watch_flags(watch), io_event_cb, watch);
+
+    dbus_watch_set_data(watch, ev, NULL);
+
+    return TRUE;
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is removed. */
+static void watch_remove_cb(DBusWatch *watch, void *data) {
+    struct server *s = data;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(s);
+
+    if ((ev = dbus_watch_get_data(watch)))
+        s->userdata->module->core->mainloop->io_free(ev);
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is toggled. */
+static void watch_toggled_cb(DBusWatch *watch, void *data) {
+    struct server *s = data;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(s);
+
+    pa_assert_se(ev = dbus_watch_get_data(watch));
+
+    /* get_watch_flags() checks if the watch is enabled */
+    s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch));
+}
+
+/* Called by D-Bus when a D-Bus timer event is added. */
+static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) {
+    struct server *s = data;
+    pa_mainloop_api *mainloop;
+    pa_time_event *ev;
+    struct timeval tv;
+
+    pa_assert(timeout);
+    pa_assert(s);
+
+    if (!dbus_timeout_get_enabled(timeout))
+        return FALSE;
+
+    mainloop = s->userdata->module->core->mainloop;
+
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+
+    ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout);
+
+    dbus_timeout_set_data(timeout, ev, NULL);
+
+    return TRUE;
+}
+
+/* Called by D-Bus when a D-Bus timer event is removed. */
+static void timeout_remove_cb(DBusTimeout *timeout, void *data) {
+    struct server *s = data;
+    pa_time_event *ev;
+
+    pa_assert(timeout);
+    pa_assert(s);
+
+    if ((ev = dbus_timeout_get_data(timeout)))
+        s->userdata->module->core->mainloop->time_free(ev);
+}
+
+/* Called by D-Bus when a D-Bus timer event is toggled. */
+static void timeout_toggled_cb(DBusTimeout *timeout, void *data) {
+    struct server *s = data;
+    pa_mainloop_api *mainloop;
+    pa_time_event *ev;
+
+    pa_assert(timeout);
+    pa_assert(s);
+
+    mainloop = s->userdata->module->core->mainloop;
+
+    pa_assert_se(ev = dbus_timeout_get_data(timeout));
+
+    if (dbus_timeout_get_enabled(timeout)) {
+        struct timeval tv;
+
+        pa_gettimeofday(&tv);
+        pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+
+        mainloop->time_restart(ev, &tv);
+    } else
+        mainloop->time_restart(ev, NULL);
+}
+
+static void server_free(struct server *s) {
+    pa_assert(s);
+
+    if (s->dbus_server) {
+        dbus_server_disconnect(s->dbus_server);
+        dbus_server_unref(s->dbus_server);
+    }
+
+    pa_xfree(s);
+}
+
+static struct server *start_server(struct userdata *u, const char *address, enum server_type type) {
+    /* XXX: We assume that when we unref the DBusServer instance at module
+     * shutdown, nobody else holds any references to it. If we stop assuming
+     * that someday, dbus_server_set_new_connection_function,
+     * dbus_server_set_watch_functions and dbus_server_set_timeout_functions
+     * calls should probably register free callbacks, instead of providing NULL
+     * as they do now. */
+
+    struct server *s = NULL;
+    DBusError error;
+
+    pa_assert(u);
+    pa_assert(address);
+
+    dbus_error_init(&error);
+
+    s = pa_xnew0(struct server, 1);
+    s->userdata = u;
+    s->type = type;
+    s->dbus_server = dbus_server_listen(address, &error);
+
+    if (dbus_error_is_set(&error)) {
+        pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL);
+
+    if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) {
+        pa_log("dbus_server_set_watch_functions() ran out of memory.");
+        goto fail;
+    }
+
+    if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) {
+        pa_log("dbus_server_set_timeout_functions() ran out of memory.");
+        goto fail;
+    }
+
+    return s;
+
+fail:
+    if (s)
+        server_free(s);
+
+    dbus_error_free(&error);
+
+    return NULL;
+}
+
+static struct server *start_local_server(struct userdata *u) {
+    struct server *s = NULL;
+    char *address = NULL;
+
+    pa_assert(u);
+
+    address = pa_get_dbus_address_from_server_type(u->module->core->server_type);
+
+    s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */
+
+    pa_xfree(address);
+
+    return s;
+}
+
+static struct server *start_tcp_server(struct userdata *u) {
+    struct server *s = NULL;
+    char *address = NULL;
+
+    pa_assert(u);
+
+    address = pa_sprintf_malloc("tcp:host=%s,port=%u", u->tcp_listen, u->tcp_port);
+
+    s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */
+
+    pa_xfree(address);
+
+    return s;
+}
+
+static int get_access_arg(pa_modargs *ma, bool *local_access, bool *remote_access) {
+    const char *value = NULL;
+
+    pa_assert(ma);
+    pa_assert(local_access);
+    pa_assert(remote_access);
+
+    if (!(value = pa_modargs_get_value(ma, "access", NULL)))
+        return 0;
+
+    if (pa_streq(value, "local")) {
+        *local_access = true;
+        *remote_access = false;
+    } else if (pa_streq(value, "remote")) {
+        *local_access = false;
+        *remote_access = true;
+    } else if (pa_streq(value, "local,remote")) {
+        *local_access = true;
+        *remote_access = true;
+    } else
+        return -1;
+
+    return 0;
+}
+
+/* Frees dead client connections. */
+static void cleanup_cb(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
+    struct userdata *u = userdata;
+    struct connection *conn = NULL;
+    uint32_t idx;
+
+    PA_IDXSET_FOREACH(conn, u->connections, idx) {
+        if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) {
+            pa_idxset_remove_by_data(u->connections, conn, NULL);
+            connection_free(conn);
+        }
+    }
+
+    u->module->core->mainloop->defer_enable(e, 0);
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u = NULL;
+    pa_modargs *ma = NULL;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    u->local_access = true;
+    u->remote_access = false;
+    u->tcp_port = PA_DBUS_DEFAULT_PORT;
+
+    if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) {
+        pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL));
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) {
+        pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL));
+        goto fail;
+    }
+
+    u->tcp_listen = pa_xstrdup(pa_modargs_get_value(ma, "tcp_listen", "0.0.0.0"));
+
+    if (u->local_access && !(u->local_server = start_local_server(u))) {
+        pa_log("Starting the local D-Bus server failed.");
+        goto fail;
+    }
+
+    if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) {
+        pa_log("Starting the D-Bus server for remote connections failed.");
+        goto fail;
+    }
+
+    u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->cleanup_event = m->core->mainloop->defer_new(m->core->mainloop, cleanup_cb, u);
+    m->core->mainloop->defer_enable(u->cleanup_event, 0);
+
+    u->dbus_protocol = pa_dbus_protocol_get(m->core);
+    u->core_iface = pa_dbusiface_core_new(m->core);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->core_iface)
+        pa_dbusiface_core_free(u->core_iface);
+
+    if (u->connections)
+        pa_idxset_free(u->connections, (pa_free_cb_t) connection_free);
+
+    /* This must not be called before the connections are freed, because if
+     * there are any connections left, they will emit the
+     * org.freedesktop.DBus.Local.Disconnected signal, and
+     * disconnection_filter_cb() will be called. disconnection_filter_cb() then
+     * tries to enable the defer event, and if it's already freed, an assertion
+     * will be hit in mainloop.c. */
+    if (u->cleanup_event)
+        m->core->mainloop->defer_free(u->cleanup_event);
+
+    if (u->tcp_server)
+        server_free(u->tcp_server);
+
+    if (u->local_server)
+        server_free(u->local_server);
+
+    if (u->dbus_protocol)
+        pa_dbus_protocol_unref(u->dbus_protocol);
+
+    pa_xfree(u->tcp_listen);
+    pa_xfree(u);
+    m->userdata = NULL;
+}
diff --git a/src/modules/echo-cancel/adrian-aec.c b/src/modules/echo-cancel/adrian-aec.c
new file mode 100644 (file)
index 0000000..71461e4
--- /dev/null
@@ -0,0 +1,287 @@
+/* aec.cpp
+ *
+ * Copyright (C) DFS Deutsche Flugsicherung (2004, 2005).
+ * All Rights Reserved.
+ *
+ * Acoustic Echo Cancellation NLMS-pw algorithm
+ *
+ * Version 0.3 filter created with www.dsptutor.freeuk.com
+ * Version 0.3.1 Allow change of stability parameter delta
+ * Version 0.4 Leaky Normalized LMS - pre whitening algorithm
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <pulse/xmalloc.h>
+
+#include "adrian-aec.h"
+
+#ifndef DISABLE_ORC
+#include "adrian-aec-orc-gen.h"
+#endif
+
+#ifdef __SSE__
+#include <xmmintrin.h>
+#endif
+
+/* Vector Dot Product */
+static REAL dotp(REAL a[], REAL b[])
+{
+  REAL sum0 = 0.0f, sum1 = 0.0f;
+  int j;
+
+  for (j = 0; j < NLMS_LEN; j += 2) {
+    // optimize: partial loop unrolling
+    sum0 += a[j] * b[j];
+    sum1 += a[j + 1] * b[j + 1];
+  }
+  return sum0 + sum1;
+}
+
+static REAL dotp_sse(REAL a[], REAL b[])
+{
+#ifdef __SSE__
+  /* This is taken from speex's inner product implementation */
+  int j;
+  REAL sum;
+  __m128 acc = _mm_setzero_ps();
+
+  for (j=0;j<NLMS_LEN;j+=8)
+  {
+    acc = _mm_add_ps(acc, _mm_mul_ps(_mm_load_ps(a+j), _mm_loadu_ps(b+j)));
+    acc = _mm_add_ps(acc, _mm_mul_ps(_mm_load_ps(a+j+4), _mm_loadu_ps(b+j+4)));
+  }
+  acc = _mm_add_ps(acc, _mm_movehl_ps(acc, acc));
+  acc = _mm_add_ss(acc, _mm_shuffle_ps(acc, acc, 0x55));
+  _mm_store_ss(&sum, acc);
+
+  return sum;
+#else
+  return dotp(a, b);
+#endif
+}
+
+
+AEC* AEC_init(int RATE, int have_vector)
+{
+  AEC *a = pa_xnew0(AEC, 1);
+  a->j = NLMS_EXT;
+  AEC_setambient(a, NoiseFloor);
+  a->dfast = a->dslow = M75dB_PCM;
+  a->xfast = a->xslow = M80dB_PCM;
+  a->gain = 1.0f;
+  a->Fx = IIR1_init(2000.0f/RATE);
+  a->Fe = IIR1_init(2000.0f/RATE);
+  a->cutoff = FIR_HP_300Hz_init();
+  a->acMic = IIR_HP_init();
+  a->acSpk = IIR_HP_init();
+
+  a->aes_y2 = M0dB;
+
+  a->fdwdisplay = -1;
+
+  if (have_vector) {
+      /* Get a 16-byte aligned location */
+      a->w = (REAL *) (((uintptr_t) a->w_arr) - (((uintptr_t) a->w_arr) % 16) + 16);
+      a->dotp = dotp_sse;
+  } else {
+      /* We don't care about alignment, just use the array as-is */
+      a->w = a->w_arr;
+      a->dotp = dotp;
+  }
+
+  return a;
+}
+
+void AEC_done(AEC *a) {
+    pa_assert(a);
+
+    pa_xfree(a->Fx);
+    pa_xfree(a->Fe);
+    pa_xfree(a->acMic);
+    pa_xfree(a->acSpk);
+    pa_xfree(a->cutoff);
+    pa_xfree(a);
+}
+
+// Adrian soft decision DTD
+// (Dual Average Near-End to Far-End signal Ratio DTD)
+// This algorithm uses exponential smoothing with differnt
+// ageing parameters to get fast and slow near-end and far-end
+// signal averages. The ratio of NFRs term
+// (dfast / xfast) / (dslow / xslow) is used to compute the stepsize
+// A ratio value of 2.5 is mapped to stepsize 0, a ratio of 0 is
+// mapped to 1.0 with a limited linear function.
+static float AEC_dtd(AEC *a, REAL d, REAL x)
+{
+  float ratio, stepsize;
+
+  // fast near-end and far-end average
+  a->dfast += ALPHAFAST * (fabsf(d) - a->dfast);
+  a->xfast += ALPHAFAST * (fabsf(x) - a->xfast);
+
+  // slow near-end and far-end average
+  a->dslow += ALPHASLOW * (fabsf(d) - a->dslow);
+  a->xslow += ALPHASLOW * (fabsf(x) - a->xslow);
+
+  if (a->xfast < M70dB_PCM) {
+    return 0.0f;   // no Spk signal
+  }
+
+  if (a->dfast < M70dB_PCM) {
+    return 0.0f;   // no Mic signal
+  }
+
+  // ratio of NFRs
+  ratio = (a->dfast * a->xslow) / (a->dslow * a->xfast);
+
+  // Linear interpolation with clamping at the limits
+  if (ratio < STEPX1)
+    stepsize = STEPY1;
+  else if (ratio > STEPX2)
+    stepsize = STEPY2;
+  else
+    stepsize = STEPY1 + (STEPY2 - STEPY1) * (ratio - STEPX1) / (STEPX2 - STEPX1);
+
+  return stepsize;
+}
+
+
+static void AEC_leaky(AEC *a)
+// The xfast signal is used to charge the hangover timer to Thold.
+// When hangover expires (no Spk signal for some time) the vector w
+// is erased. This is my implementation of Leaky NLMS.
+{
+  if (a->xfast >= M70dB_PCM) {
+    // vector w is valid for hangover Thold time
+    a->hangover = Thold;
+  } else {
+    if (a->hangover > 1) {
+      --(a->hangover);
+    } else if (1 == a->hangover) {
+      --(a->hangover);
+      // My Leaky NLMS is to erase vector w when hangover expires
+      memset(a->w_arr, 0, sizeof(a->w_arr));
+    }
+  }
+}
+
+
+#if 0
+void AEC::openwdisplay() {
+  // open TCP connection to program wdisplay.tcl
+  fdwdisplay = socket_async("127.0.0.1", 50999);
+};
+#endif
+
+
+static REAL AEC_nlms_pw(AEC *a, REAL d, REAL x_, float stepsize)
+{
+  REAL e;
+  REAL ef;
+  a->x[a->j] = x_;
+  a->xf[a->j] = IIR1_highpass(a->Fx, x_);     // pre-whitening of x
+
+  // calculate error value
+  // (mic signal - estimated mic signal from spk signal)
+  e = d;
+  if (a->hangover > 0) {
+    e -= a->dotp(a->w, a->x + a->j);
+  }
+  ef = IIR1_highpass(a->Fe, e);     // pre-whitening of e
+
+  // optimize: iterative dotp(xf, xf)
+  a->dotp_xf_xf += (a->xf[a->j] * a->xf[a->j] - a->xf[a->j + NLMS_LEN - 1] * a->xf[a->j + NLMS_LEN - 1]);
+
+  if (stepsize > 0.0f) {
+    // calculate variable step size
+    REAL mikro_ef = stepsize * ef / a->dotp_xf_xf;
+
+#ifdef DISABLE_ORC
+    // update tap weights (filter learning)
+    int i;
+    for (i = 0; i < NLMS_LEN; i += 2) {
+      // optimize: partial loop unrolling
+      a->w[i] += mikro_ef * a->xf[i + a->j];
+      a->w[i + 1] += mikro_ef * a->xf[i + a->j + 1];
+    }
+#else
+    update_tap_weights(a->w, &a->xf[a->j], mikro_ef, NLMS_LEN);
+#endif
+  }
+
+  if (--(a->j) < 0) {
+    // optimize: decrease number of memory copies
+    a->j = NLMS_EXT;
+    memmove(a->x + a->j + 1, a->x, (NLMS_LEN - 1) * sizeof(REAL));
+    memmove(a->xf + a->j + 1, a->xf, (NLMS_LEN - 1) * sizeof(REAL));
+  }
+
+  // Saturation
+  if (e > MAXPCM) {
+    return MAXPCM;
+  } else if (e < -MAXPCM) {
+    return -MAXPCM;
+  } else {
+    return e;
+  }
+}
+
+
+int AEC_doAEC(AEC *a, int d_, int x_)
+{
+  REAL d = (REAL) d_;
+  REAL x = (REAL) x_;
+
+  // Mic Highpass Filter - to remove DC
+  d = IIR_HP_highpass(a->acMic, d);
+
+  // Mic Highpass Filter - cut-off below 300Hz
+  d = FIR_HP_300Hz_highpass(a->cutoff, d);
+
+  // Amplify, for e.g. Soundcards with -6dB max. volume
+  d *= a->gain;
+
+  // Spk Highpass Filter - to remove DC
+  x = IIR_HP_highpass(a->acSpk, x);
+
+  // Double Talk Detector
+  a->stepsize = AEC_dtd(a, d, x);
+
+  // Leaky (ageing of vector w)
+  AEC_leaky(a);
+
+  // Acoustic Echo Cancellation
+  d = AEC_nlms_pw(a, d, x, a->stepsize);
+
+#if 0
+  if (fdwdisplay >= 0) {
+    if (++dumpcnt >= (WIDEB*RATE/10)) {
+      // wdisplay creates 10 dumps per seconds = large CPU load!
+      dumpcnt = 0;
+      write(fdwdisplay, ws, DUMP_LEN*sizeof(float));
+      // we don't check return value. This is not production quality!!!
+      memset(ws, 0, sizeof(ws));
+    } else {
+      int i;
+      for (i = 0; i < DUMP_LEN; i += 2) {
+        // optimize: partial loop unrolling
+        ws[i] += w[i];
+        ws[i + 1] += w[i + 1];
+      }
+    }
+  }
+#endif
+
+  return (int) d;
+}
diff --git a/src/modules/echo-cancel/adrian-aec.h b/src/modules/echo-cancel/adrian-aec.h
new file mode 100644 (file)
index 0000000..6271774
--- /dev/null
@@ -0,0 +1,383 @@
+/* aec.h
+ *
+ * Copyright (C) DFS Deutsche Flugsicherung (2004, 2005).
+ * All Rights Reserved.
+ * Author: Andre Adrian
+ *
+ * Acoustic Echo Cancellation Leaky NLMS-pw algorithm
+ *
+ * Version 0.3 filter created with www.dsptutor.freeuk.com
+ * Version 0.3.1 Allow change of stability parameter delta
+ * Version 0.4 Leaky Normalized LMS - pre whitening algorithm
+ */
+
+#ifndef _AEC_H                  /* include only once */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+
+#define WIDEB 2
+
+// use double if your CPU does software-emulation of float
+#define REAL float
+
+/* dB Values */
+#define M0dB 1.0f
+#define M3dB 0.71f
+#define M6dB 0.50f
+#define M9dB 0.35f
+#define M12dB 0.25f
+#define M18dB 0.125f
+#define M24dB 0.063f
+
+/* dB values for 16bit PCM */
+/* MxdB_PCM = 32767 * 10 ^(x / 20) */
+#define M10dB_PCM 10362.0f
+#define M20dB_PCM 3277.0f
+#define M25dB_PCM 1843.0f
+#define M30dB_PCM 1026.0f
+#define M35dB_PCM 583.0f
+#define M40dB_PCM 328.0f
+#define M45dB_PCM 184.0f
+#define M50dB_PCM 104.0f
+#define M55dB_PCM 58.0f
+#define M60dB_PCM 33.0f
+#define M65dB_PCM 18.0f
+#define M70dB_PCM 10.0f
+#define M75dB_PCM 6.0f
+#define M80dB_PCM 3.0f
+#define M85dB_PCM 2.0f
+#define M90dB_PCM 1.0f
+
+#define MAXPCM 32767.0f
+
+/* Design constants (Change to fine tune the algorithms */
+
+/* The following values are for hardware AEC and studio quality
+ * microphone */
+
+/* NLMS filter length in taps (samples). A longer filter length gives
+ * better Echo Cancellation, but maybe slower convergence speed and
+ * needs more CPU power (Order of NLMS is linear) */
+#define NLMS_LEN  (100*WIDEB*8)
+
+/* Vector w visualization length in taps (samples).
+ * Must match argv value for wdisplay.tcl */
+#define DUMP_LEN  (40*WIDEB*8)
+
+/* minimum energy in xf. Range: M70dB_PCM to M50dB_PCM. Should be equal
+ * to microphone ambient Noise level */
+#define NoiseFloor M55dB_PCM
+
+/* Leaky hangover in taps.
+ */
+#define Thold (60 * WIDEB * 8)
+
+// Adrian soft decision DTD
+// left point. X is ratio, Y is stepsize
+#define STEPX1 1.0
+#define STEPY1 1.0
+// right point. STEPX2=2.0 is good double talk, 3.0 is good single talk.
+#define STEPX2 2.5
+#define STEPY2 0
+#define ALPHAFAST (1.0f / 100.0f)
+#define ALPHASLOW (1.0f / 20000.0f)
+
+
+
+/* Ageing multiplier for LMS memory vector w */
+#define Leaky 0.9999f
+
+/* Double Talk Detector Speaker/Microphone Threshold. Range <=1
+ * Large value (M0dB) is good for Single-Talk Echo cancellation,
+ * small value (M12dB) is good for Doulbe-Talk AEC */
+#define GeigelThreshold M6dB
+
+/* for Non Linear Processor. Range >0 to 1. Large value (M0dB) is good
+ * for Double-Talk, small value (M12dB) is good for Single-Talk */
+#define NLPAttenuation M12dB
+
+/* Below this line there are no more design constants */
+
+typedef struct IIR_HP IIR_HP;
+
+/* Exponential Smoothing or IIR Infinite Impulse Response Filter */
+struct IIR_HP {
+  REAL x;
+};
+
+static  IIR_HP* IIR_HP_init(void) {
+    IIR_HP *i = pa_xnew(IIR_HP, 1);
+    i->x = 0.0f;
+    return i;
+  }
+
+static  REAL IIR_HP_highpass(IIR_HP *i, REAL in) {
+    const REAL a0 = 0.01f;      /* controls Transfer Frequency */
+    /* Highpass = Signal - Lowpass. Lowpass = Exponential Smoothing */
+    i->x += a0 * (in - i->x);
+    return in - i->x;
+  }
+
+typedef struct FIR_HP_300Hz FIR_HP_300Hz;
+
+#if WIDEB==1
+/* 17 taps FIR Finite Impulse Response filter
+ * Coefficients calculated with
+ * www.dsptutor.freeuk.com/KaiserFilterDesign/KaiserFilterDesign.html
+ */
+class FIR_HP_300Hz {
+  REAL z[18];
+
+public:
+   FIR_HP_300Hz() {
+    memset(this, 0, sizeof(FIR_HP_300Hz));
+  }
+
+  REAL highpass(REAL in) {
+    const REAL a[18] = {
+    // Kaiser Window FIR Filter, Filter type: High pass
+    // Passband: 300.0 - 4000.0 Hz, Order: 16
+    // Transition band: 75.0 Hz, Stopband attenuation: 10.0 dB
+    -0.034870606, -0.039650206, -0.044063766, -0.04800318,
+    -0.051370874, -0.054082647, -0.056070227, -0.057283327,
+    0.8214126, -0.057283327, -0.056070227, -0.054082647,
+    -0.051370874, -0.04800318, -0.044063766, -0.039650206,
+    -0.034870606, 0.0
+    };
+    memmove(z + 1, z, 17 * sizeof(REAL));
+    z[0] = in;
+    REAL sum0 = 0.0, sum1 = 0.0;
+    int j;
+
+    for (j = 0; j < 18; j += 2) {
+      // optimize: partial loop unrolling
+      sum0 += a[j] * z[j];
+      sum1 += a[j + 1] * z[j + 1];
+    }
+    return sum0 + sum1;
+  }
+};
+
+#else
+
+/* 35 taps FIR Finite Impulse Response filter
+ * Passband 150Hz to 4kHz for 8kHz sample rate, 300Hz to 8kHz for 16kHz
+ * sample rate.
+ * Coefficients calculated with
+ * www.dsptutor.freeuk.com/KaiserFilterDesign/KaiserFilterDesign.html
+ */
+struct FIR_HP_300Hz {
+  REAL z[36];
+};
+
+static  FIR_HP_300Hz* FIR_HP_300Hz_init(void) {
+    FIR_HP_300Hz *ret = pa_xnew(FIR_HP_300Hz, 1);
+    memset(ret, 0, sizeof(FIR_HP_300Hz));
+    return ret;
+  }
+
+static  REAL FIR_HP_300Hz_highpass(FIR_HP_300Hz *f, REAL in) {
+    REAL sum0 = 0.0, sum1 = 0.0;
+    int j;
+    const REAL a[36] = {
+      // Kaiser Window FIR Filter, Filter type: High pass
+      // Passband: 150.0 - 4000.0 Hz, Order: 34
+      // Transition band: 34.0 Hz, Stopband attenuation: 10.0 dB
+      -0.016165324, -0.017454365, -0.01871232, -0.019931411,
+      -0.021104068, -0.022222936, -0.02328091, -0.024271343,
+      -0.025187887, -0.02602462, -0.026776174, -0.027437767,
+      -0.028004972, -0.028474221, -0.028842418, -0.029107114,
+      -0.02926664, 0.8524841, -0.02926664, -0.029107114,
+      -0.028842418, -0.028474221, -0.028004972, -0.027437767,
+      -0.026776174, -0.02602462, -0.025187887, -0.024271343,
+      -0.02328091, -0.022222936, -0.021104068, -0.019931411,
+      -0.01871232, -0.017454365, -0.016165324, 0.0
+    };
+    memmove(f->z + 1, f->z, 35 * sizeof(REAL));
+    f->z[0] = in;
+
+    for (j = 0; j < 36; j += 2) {
+      // optimize: partial loop unrolling
+      sum0 += a[j] * f->z[j];
+      sum1 += a[j + 1] * f->z[j + 1];
+    }
+    return sum0 + sum1;
+  }
+#endif
+
+typedef struct IIR1 IIR1;
+
+/* Recursive single pole IIR Infinite Impulse response High-pass filter
+ *
+ * Reference: The Scientist and Engineer's Guide to Digital Processing
+ *
+ *     output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1]
+ *
+ *      X  = exp(-2.0 * pi * Fc)
+ *      A0 = (1 + X) / 2
+ *      A1 = -(1 + X) / 2
+ *      B1 = X
+ *      Fc = cutoff freq / sample rate
+ */
+struct IIR1 {
+  REAL in0, out0;
+  REAL a0, a1, b1;
+};
+
+#if 0
+  IIR1() {
+    memset(this, 0, sizeof(IIR1));
+  }
+#endif
+
+static  IIR1* IIR1_init(REAL Fc) {
+    IIR1 *i = pa_xnew(IIR1, 1);
+    i->b1 = expf(-2.0f * M_PI * Fc);
+    i->a0 = (1.0f + i->b1) / 2.0f;
+    i->a1 = -(i->a0);
+    i->in0 = 0.0f;
+    i->out0 = 0.0f;
+    return i;
+  }
+
+static  REAL IIR1_highpass(IIR1 *i, REAL in) {
+    REAL out = i->a0 * in + i->a1 * i->in0 + i->b1 * i->out0;
+    i->in0 = in;
+    i->out0 = out;
+    return out;
+  }
+
+
+#if 0
+/* Recursive two pole IIR Infinite Impulse Response filter
+ * Coefficients calculated with
+ * http://www.dsptutor.freeuk.com/IIRFilterDesign/IIRFiltDes102.html
+ */
+class IIR2 {
+  REAL x[2], y[2];
+
+public:
+   IIR2() {
+    memset(this, 0, sizeof(IIR2));
+  }
+
+  REAL highpass(REAL in) {
+    // Butterworth IIR filter, Filter type: HP
+    // Passband: 2000 - 4000.0 Hz, Order: 2
+    const REAL a[] = { 0.29289323f, -0.58578646f, 0.29289323f };
+    const REAL b[] = { 1.3007072E-16f, 0.17157288f };
+    REAL out =
+      a[0] * in + a[1] * x[0] + a[2] * x[1] - b[0] * y[0] - b[1] * y[1];
+
+    x[1] = x[0];
+    x[0] = in;
+    y[1] = y[0];
+    y[0] = out;
+    return out;
+  }
+};
+#endif
+
+
+// Extention in taps to reduce mem copies
+#define NLMS_EXT  (10*8)
+
+// block size in taps to optimize DTD calculation
+#define DTD_LEN   16
+
+typedef struct AEC AEC;
+
+struct AEC {
+  // Time domain Filters
+  IIR_HP *acMic, *acSpk;        // DC-level remove Highpass)
+  FIR_HP_300Hz *cutoff;         // 150Hz cut-off Highpass
+  REAL gain;                    // Mic signal amplify
+  IIR1 *Fx, *Fe;                // pre-whitening Highpass for x, e
+
+  // Adrian soft decision DTD (Double Talk Detector)
+  REAL dfast, xfast;
+  REAL dslow, xslow;
+
+  // NLMS-pw
+  REAL x[NLMS_LEN + NLMS_EXT];  // tap delayed loudspeaker signal
+  REAL xf[NLMS_LEN + NLMS_EXT]; // pre-whitening tap delayed signal
+  REAL w_arr[NLMS_LEN + (16 / sizeof(REAL))]; // tap weights
+  REAL *w;                      // this will be a 16-byte aligned pointer into w_arr
+  int j;                        // optimize: less memory copies
+  double dotp_xf_xf;            // double to avoid loss of precision
+  float delta;                  // noise floor to stabilize NLMS
+
+  // AES
+  float aes_y2;                 // not in use!
+
+  // w vector visualization
+  REAL ws[DUMP_LEN];            // tap weights sums
+  int fdwdisplay;               // TCP file descriptor
+  int dumpcnt;                  // wdisplay output counter
+
+  // variables are public for visualization
+  int hangover;
+  float stepsize;
+
+  // vfuncs that are picked based on processor features available
+  REAL (*dotp) (REAL[], REAL[]);
+};
+
+/* Double-Talk Detector
+ *
+ * in d: microphone sample (PCM as REALing point value)
+ * in x: loudspeaker sample (PCM as REALing point value)
+ * return: from 0 for doubletalk to 1.0 for single talk
+ */
+static  float AEC_dtd(AEC *a, REAL d, REAL x);
+
+static  void AEC_leaky(AEC *a);
+
+/* Normalized Least Mean Square Algorithm pre-whitening (NLMS-pw)
+ * The LMS algorithm was developed by Bernard Widrow
+ * book: Haykin, Adaptive Filter Theory, 4. edition, Prentice Hall, 2002
+ *
+ * in d: microphone sample (16bit PCM value)
+ * in x_: loudspeaker sample (16bit PCM value)
+ * in stepsize: NLMS adaptation variable
+ * return: echo cancelled microphone sample
+ */
+static  REAL AEC_nlms_pw(AEC *a, REAL d, REAL x_, float stepsize);
+
+AEC* AEC_init(int RATE, int have_vector);
+void AEC_done(AEC *a);
+
+/* Acoustic Echo Cancellation and Suppression of one sample
+ * in   d:  microphone signal with echo
+ * in   x:  loudspeaker signal
+ * return:  echo cancelled microphone signal
+ */
+  int AEC_doAEC(AEC *a, int d_, int x_);
+
+PA_GCC_UNUSED static  float AEC_getambient(AEC *a) {
+    return a->dfast;
+  }
+static  void AEC_setambient(AEC *a, float Min_xf) {
+    a->dotp_xf_xf -= a->delta;  // subtract old delta
+    a->delta = (NLMS_LEN-1) * Min_xf * Min_xf;
+    a->dotp_xf_xf += a->delta;  // add new delta
+  }
+PA_GCC_UNUSED static  void AEC_setgain(AEC *a, float gain_) {
+    a->gain = gain_;
+  }
+#if 0
+  void AEC_openwdisplay(AEC *a);
+#endif
+PA_GCC_UNUSED static  void AEC_setaes(AEC *a, float aes_y2_) {
+    a->aes_y2 = aes_y2_;
+  }
+
+#define _AEC_H
+#endif
diff --git a/src/modules/echo-cancel/adrian-aec.orc b/src/modules/echo-cancel/adrian-aec.orc
new file mode 100644 (file)
index 0000000..8054772
--- /dev/null
@@ -0,0 +1,8 @@
+.function update_tap_weights
+.dest 4 w float
+.source 4 xf float
+.floatparam 4 mikro_ef
+.temp 4 tmp float
+
+mulf tmp, mikro_ef, xf
+addf w, w, tmp
diff --git a/src/modules/echo-cancel/adrian-license.txt b/src/modules/echo-cancel/adrian-license.txt
new file mode 100644 (file)
index 0000000..7c06efd
--- /dev/null
@@ -0,0 +1,17 @@
+  Copyright (C) DFS Deutsche Flugsicherung (2004). All Rights Reserved.
+
+  You are allowed to use this source code in any open source or closed
+  source software you want. You are allowed to use the algorithms for a
+  hardware solution. You are allowed to modify the source code.
+  You are not allowed to remove the name of the author from this memo or
+  from the source code files. You are not allowed to monopolize the
+  source code or the algorithms behind the source code as your
+  intellectual property. This source code is free of royalty and comes
+  with no warranty.
+
+--- The following does not apply to the PulseAudio module ---
+
+  Please see g711/gen-lic.txt for the ITU-T G.711 codec copyright.
+  Please see gsm/gen-lic.txt for the ITU-T GSM codec copyright.
+  Please see ilbc/COPYRIGHT and ilbc/NOTICE for the IETF iLBC codec
+  copyright.
diff --git a/src/modules/echo-cancel/adrian.c b/src/modules/echo-cancel/adrian.c
new file mode 100644 (file)
index 0000000..3c47fae
--- /dev/null
@@ -0,0 +1,118 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2010 Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+    Contributor: Wim Taymans <wim.taymans@gmail.com>
+
+    The actual implementation is taken from the sources at
+    http://andreadrian.de/intercom/ - for the license, look for
+    adrian-license.txt in the same directory as this file.
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/modargs.h>
+
+#include "echo-cancel.h"
+
+/* should be between 10-20 ms */
+#define DEFAULT_FRAME_SIZE_MS 20
+
+static const char* const valid_modargs[] = {
+    "frame_size_ms",
+    NULL
+};
+
+static void pa_adrian_ec_fixate_spec(pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+                                     pa_sample_spec *play_ss, pa_channel_map *play_map,
+                                     pa_sample_spec *out_ss, pa_channel_map *out_map) {
+    out_ss->format = PA_SAMPLE_S16NE;
+    out_ss->channels = 1;
+    pa_channel_map_init_mono(out_map);
+
+    *play_ss = *out_ss;
+    *play_map = *out_map;
+    *rec_ss = *out_ss;
+    *rec_map = *out_map;
+}
+
+bool pa_adrian_ec_init(pa_core *c, pa_echo_canceller *ec,
+                       pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+                       pa_sample_spec *play_ss, pa_channel_map *play_map,
+                       pa_sample_spec *out_ss, pa_channel_map *out_map,
+                       uint32_t *nframes, const char *args) {
+    int rate, have_vector = 0;
+    uint32_t frame_size_ms;
+    pa_modargs *ma;
+
+    if (!(ma = pa_modargs_new(args, valid_modargs))) {
+        pa_log("Failed to parse submodule arguments.");
+        goto fail;
+    }
+
+    frame_size_ms = DEFAULT_FRAME_SIZE_MS;
+    if (pa_modargs_get_value_u32(ma, "frame_size_ms", &frame_size_ms) < 0 || frame_size_ms < 1 || frame_size_ms > 200) {
+        pa_log("Invalid frame_size_ms specification");
+        goto fail;
+    }
+
+    pa_adrian_ec_fixate_spec(rec_ss, rec_map, play_ss, play_map, out_ss, out_map);
+
+    rate = out_ss->rate;
+    *nframes = (rate * frame_size_ms) / 1000;
+    ec->params.adrian.blocksize = (*nframes) * pa_frame_size(out_ss);
+
+    pa_log_debug ("Using nframes %d, blocksize %u, channels %d, rate %d", *nframes, ec->params.adrian.blocksize, out_ss->channels, out_ss->rate);
+
+    /* For now we only support SSE */
+    if (c->cpu_info.cpu_type == PA_CPU_X86 && (c->cpu_info.flags.x86 & PA_CPU_X86_SSE))
+        have_vector = 1;
+
+    ec->params.adrian.aec = AEC_init(rate, have_vector);
+    if (!ec->params.adrian.aec)
+        goto fail;
+
+    pa_modargs_free(ma);
+    return true;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+    return false;
+}
+
+void pa_adrian_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
+    unsigned int i;
+
+    for (i = 0; i < ec->params.adrian.blocksize; i += 2) {
+        /* We know it's S16NE mono data */
+        int r = *(int16_t *)(rec + i);
+        int p = *(int16_t *)(play + i);
+        *(int16_t *)(out + i) = (int16_t) AEC_doAEC(ec->params.adrian.aec, r, p);
+    }
+}
+
+void pa_adrian_ec_done(pa_echo_canceller *ec) {
+    if (ec->params.adrian.aec) {
+        AEC_done(ec->params.adrian.aec);
+        ec->params.adrian.aec = NULL;
+    }
+}
diff --git a/src/modules/echo-cancel/adrian.h b/src/modules/echo-cancel/adrian.h
new file mode 100644 (file)
index 0000000..a5e0444
--- /dev/null
@@ -0,0 +1,30 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2010 Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+    The actual implementation is taken from the sources at
+    http://andreadrian.de/intercom/ - for the license, look for
+    adrian-license.txt in the same directory as this file.
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* Forward declarations */
+
+typedef struct AEC AEC;
+
+AEC* AEC_init(int RATE, int have_vector);
+void AEC_done(AEC *a);
+int AEC_doAEC(AEC *a, int d_, int x_);
diff --git a/src/modules/echo-cancel/echo-cancel.h b/src/modules/echo-cancel/echo-cancel.h
new file mode 100644 (file)
index 0000000..ee67949
--- /dev/null
@@ -0,0 +1,189 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2010 Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifndef fooechocancelhfoo
+#define fooechocancelhfoo
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
+
+#ifdef HAVE_SPEEX
+#include <speex/speex_echo.h>
+#include <speex/speex_preprocess.h>
+#endif
+
+#include "adrian.h"
+
+/* Common data structures */
+
+typedef struct pa_echo_canceller_msg pa_echo_canceller_msg;
+
+typedef struct pa_echo_canceller_params pa_echo_canceller_params;
+
+struct pa_echo_canceller_params {
+    union {
+        struct {
+            pa_sample_spec out_ss;
+        } null;
+#ifdef HAVE_SPEEX
+        struct {
+            SpeexEchoState *state;
+            SpeexPreprocessState *pp_state;
+        } speex;
+#endif
+#ifdef HAVE_ADRIAN_EC
+        struct {
+            uint32_t blocksize;
+            AEC *aec;
+        } adrian;
+#endif
+#ifdef HAVE_WEBRTC
+        struct {
+            /* This is a void* so that we don't have to convert this whole file
+             * to C++ linkage. apm is a pointer to an AudioProcessing object */
+            void *apm;
+            unsigned int blocksize; /* in frames */
+            pa_sample_spec rec_ss, play_ss, out_ss;
+            float *rec_buffer[PA_CHANNELS_MAX], *play_buffer[PA_CHANNELS_MAX]; /* for deinterleaved buffers */
+            void *trace_callback;
+            bool agc;
+            bool first;
+            unsigned int agc_start_volume;
+        } webrtc;
+#endif
+        /* each canceller-specific structure goes here */
+    };
+
+    /* Set this if canceller can do drift compensation. Also see set_drift()
+     * below */
+    bool drift_compensation;
+};
+
+typedef struct pa_echo_canceller pa_echo_canceller;
+
+struct pa_echo_canceller {
+    /* Initialise canceller engine. */
+    bool   (*init)                      (pa_core *c,
+                                         pa_echo_canceller *ec,
+                                         pa_sample_spec *rec_ss,
+                                         pa_channel_map *rec_map,
+                                         pa_sample_spec *play_ss,
+                                         pa_channel_map *play_map,
+                                         pa_sample_spec *out_ss,
+                                         pa_channel_map *out_map,
+                                         uint32_t *nframes,
+                                         const char *args);
+
+    /* You should have only one of play()+record() or run() set. The first
+     * works under the assumption that you'll handle buffering and matching up
+     * samples yourself. If you set run(), module-echo-cancel will handle
+     * synchronising the playback and record streams. */
+
+    /* Feed the engine 'nframes' playback frames. */
+    void        (*play)                 (pa_echo_canceller *ec, const uint8_t *play);
+    /* Feed the engine 'nframes' record frames. nframes processed frames are
+     * returned in out. */
+    void        (*record)               (pa_echo_canceller *ec, const uint8_t *rec, uint8_t *out);
+    /* Feed the engine nframes playback and record frames, with a reasonable
+     * effort at keeping the two in sync. nframes processed frames are
+     * returned in out. */
+    void        (*run)                  (pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out);
+
+    /* Optional callback to set the drift, expressed as the ratio of the
+     * difference in number of playback and capture samples to the number of
+     * capture samples, for some instant of time. This is used only if the
+     * canceller signals that it supports drift compensation, and is called
+     * before record(). The actual implementation needs to derive drift based
+     * on point samples -- the individual values are not accurate enough to use
+     * as-is. */
+    /* NOTE: the semantics of this function might change in the future. */
+    void        (*set_drift)            (pa_echo_canceller *ec, float drift);
+
+    /* Free up resources. */
+    void        (*done)                 (pa_echo_canceller *ec);
+
+    /* Structure with common and engine-specific canceller parameters. */
+    pa_echo_canceller_params params;
+
+    /* msgobject that can be used to send messages back to the main thread */
+    pa_echo_canceller_msg *msg;
+};
+
+/* Functions to be used by the canceller analog gain control routines */
+pa_volume_t pa_echo_canceller_get_capture_volume(pa_echo_canceller *ec);
+void pa_echo_canceller_set_capture_volume(pa_echo_canceller *ec, pa_volume_t volume);
+
+/* Computes EC block size in frames (rounded down to nearest power-of-2) based
+ * on sample rate and milliseconds. */
+uint32_t pa_echo_canceller_blocksize_power2(unsigned rate, unsigned ms);
+
+/* Null canceller functions */
+bool pa_null_ec_init(pa_core *c, pa_echo_canceller *ec,
+                     pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+                     pa_sample_spec *play_ss, pa_channel_map *play_map,
+                     pa_sample_spec *out_ss, pa_channel_map *out_map,
+                     uint32_t *nframes, const char *args);
+void pa_null_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out);
+void pa_null_ec_done(pa_echo_canceller *ec);
+
+#ifdef HAVE_SPEEX
+/* Speex canceller functions */
+bool pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec,
+                      pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+                      pa_sample_spec *play_ss, pa_channel_map *play_map,
+                      pa_sample_spec *out_ss, pa_channel_map *out_map,
+                      uint32_t *nframes, const char *args);
+void pa_speex_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out);
+void pa_speex_ec_done(pa_echo_canceller *ec);
+#endif
+
+#ifdef HAVE_ADRIAN_EC
+/* Adrian Andre's echo canceller */
+bool pa_adrian_ec_init(pa_core *c, pa_echo_canceller *ec,
+                       pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+                       pa_sample_spec *play_ss, pa_channel_map *play_map,
+                       pa_sample_spec *out_ss, pa_channel_map *out_map,
+                       uint32_t *nframes, const char *args);
+void pa_adrian_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out);
+void pa_adrian_ec_done(pa_echo_canceller *ec);
+#endif
+
+#ifdef HAVE_WEBRTC
+/* WebRTC canceller functions */
+PA_C_DECL_BEGIN
+bool pa_webrtc_ec_init(pa_core *c, pa_echo_canceller *ec,
+                       pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+                       pa_sample_spec *play_ss, pa_channel_map *play_map,
+                       pa_sample_spec *out_ss, pa_channel_map *out_map,
+                       uint32_t *nframes, const char *args);
+void pa_webrtc_ec_play(pa_echo_canceller *ec, const uint8_t *play);
+void pa_webrtc_ec_record(pa_echo_canceller *ec, const uint8_t *rec, uint8_t *out);
+void pa_webrtc_ec_set_drift(pa_echo_canceller *ec, float drift);
+void pa_webrtc_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out);
+void pa_webrtc_ec_done(pa_echo_canceller *ec);
+PA_C_DECL_END
+#endif
+
+#endif /* fooechocancelhfoo */
diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
new file mode 100644 (file)
index 0000000..b28d60a
--- /dev/null
@@ -0,0 +1,2360 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2010 Wim Taymans <wim.taymans@gmail.com>
+
+    Based on module-virtual-sink.c
+             module-virtual-source.c
+             module-loopback.c
+
+        Copyright 2010 Intel Corporation
+        Contributor: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+
+#include "echo-cancel.h"
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/ltdl-helper.h>
+
+#include "module-echo-cancel-symdef.h"
+
+PA_MODULE_AUTHOR("Wim Taymans");
+PA_MODULE_DESCRIPTION("Echo Cancellation");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        _("source_name=<name for the source> "
+          "source_properties=<properties for the source> "
+          "source_master=<name of source to filter> "
+          "sink_name=<name for the sink> "
+          "sink_properties=<properties for the sink> "
+          "sink_master=<name of sink to filter> "
+          "adjust_time=<how often to readjust rates in s> "
+          "adjust_threshold=<how much drift to readjust after in ms> "
+          "format=<sample format> "
+          "rate=<sample rate> "
+          "channels=<number of channels> "
+          "channel_map=<channel map> "
+          "aec_method=<implementation to use> "
+          "aec_args=<parameters for the AEC engine> "
+          "save_aec=<save AEC data in /tmp> "
+          "autoloaded=<set if this module is being loaded automatically> "
+          "use_volume_sharing=<yes or no> "
+          "use_master_format=<yes or no> "
+        ));
+
+/* NOTE: Make sure the enum and ec_table are maintained in the correct order */
+typedef enum {
+    PA_ECHO_CANCELLER_INVALID = -1,
+    PA_ECHO_CANCELLER_NULL,
+#ifdef HAVE_SPEEX
+    PA_ECHO_CANCELLER_SPEEX,
+#endif
+#ifdef HAVE_ADRIAN_EC
+    PA_ECHO_CANCELLER_ADRIAN,
+#endif
+#ifdef HAVE_WEBRTC
+    PA_ECHO_CANCELLER_WEBRTC,
+#endif
+} pa_echo_canceller_method_t;
+
+#ifdef HAVE_WEBRTC
+#define DEFAULT_ECHO_CANCELLER "webrtc"
+#else
+#define DEFAULT_ECHO_CANCELLER "speex"
+#endif
+
+static const pa_echo_canceller ec_table[] = {
+    {
+        /* Null, Dummy echo canceller (just copies data) */
+        .init                   = pa_null_ec_init,
+        .run                    = pa_null_ec_run,
+        .done                   = pa_null_ec_done,
+    },
+#ifdef HAVE_SPEEX
+    {
+        /* Speex */
+        .init                   = pa_speex_ec_init,
+        .run                    = pa_speex_ec_run,
+        .done                   = pa_speex_ec_done,
+    },
+#endif
+#ifdef HAVE_ADRIAN_EC
+    {
+        /* Adrian Andre's NLMS implementation */
+        .init                   = pa_adrian_ec_init,
+        .run                    = pa_adrian_ec_run,
+        .done                   = pa_adrian_ec_done,
+    },
+#endif
+#ifdef HAVE_WEBRTC
+    {
+        /* WebRTC's audio processing engine */
+        .init                   = pa_webrtc_ec_init,
+        .play                   = pa_webrtc_ec_play,
+        .record                 = pa_webrtc_ec_record,
+        .set_drift              = pa_webrtc_ec_set_drift,
+        .run                    = pa_webrtc_ec_run,
+        .done                   = pa_webrtc_ec_done,
+    },
+#endif
+};
+
+#define DEFAULT_RATE 32000
+#define DEFAULT_CHANNELS 1
+#define DEFAULT_ADJUST_TIME_USEC (1*PA_USEC_PER_SEC)
+#define DEFAULT_ADJUST_TOLERANCE (5*PA_USEC_PER_MSEC)
+#define DEFAULT_SAVE_AEC false
+#define DEFAULT_AUTOLOADED false
+#define DEFAULT_USE_MASTER_FORMAT false
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+
+#define MAX_LATENCY_BLOCKS 10
+
+/* Can only be used in main context */
+#define IS_ACTIVE(u) ((pa_source_get_state((u)->source) == PA_SOURCE_RUNNING) && \
+                      (pa_sink_get_state((u)->sink) == PA_SINK_RUNNING))
+
+/* This module creates a new (virtual) source and sink.
+ *
+ * The data sent to the new sink is kept in a memblockq before being
+ * forwarded to the real sink_master.
+ *
+ * Data read from source_master is matched against the saved sink data and
+ * echo canceled data is then pushed onto the new source.
+ *
+ * Both source and sink masters have their own threads to push/pull data
+ * respectively. We however perform all our actions in the source IO thread.
+ * To do this we send all played samples to the source IO thread where they
+ * are then pushed into the memblockq.
+ *
+ * Alignment is performed in two steps:
+ *
+ * 1) when something happens that requires quick adjustment of the alignment of
+ *    capture and playback samples, we perform a resync. This adjusts the
+ *    position in the playback memblock to the requested sample. Quick
+ *    adjustments include moving the playback samples before the capture
+ *    samples (because else the echo canceller does not work) or when the
+ *    playback pointer drifts too far away.
+ *
+ * 2) periodically check the difference between capture and playback. We use a
+ *    low and high watermark for adjusting the alignment. Playback should always
+ *    be before capture and the difference should not be bigger than one frame
+ *    size. We would ideally like to resample the sink_input but most driver
+ *    don't give enough accuracy to be able to do that right now.
+ */
+
+struct userdata;
+
+struct pa_echo_canceller_msg {
+    pa_msgobject parent;
+    struct userdata *userdata;
+};
+
+PA_DEFINE_PRIVATE_CLASS(pa_echo_canceller_msg, pa_msgobject);
+#define PA_ECHO_CANCELLER_MSG(o) (pa_echo_canceller_msg_cast(o))
+
+struct snapshot {
+    pa_usec_t sink_now;
+    pa_usec_t sink_latency;
+    size_t sink_delay;
+    int64_t send_counter;
+
+    pa_usec_t source_now;
+    pa_usec_t source_latency;
+    size_t source_delay;
+    int64_t recv_counter;
+    size_t rlen;
+    size_t plen;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    bool dead;
+    bool save_aec;
+
+    pa_echo_canceller *ec;
+    uint32_t source_output_blocksize;
+    uint32_t source_blocksize;
+    uint32_t sink_blocksize;
+
+    bool need_realign;
+
+    /* to wakeup the source I/O thread */
+    pa_asyncmsgq *asyncmsgq;
+    pa_rtpoll_item *rtpoll_item_read, *rtpoll_item_write;
+
+    pa_source *source;
+    bool source_auto_desc;
+    pa_source_output *source_output;
+    pa_memblockq *source_memblockq; /* echo canceller needs fixed sized chunks */
+    size_t source_skip;
+
+    pa_sink *sink;
+    bool sink_auto_desc;
+    pa_sink_input *sink_input;
+    pa_memblockq *sink_memblockq;
+    int64_t send_counter;          /* updated in sink IO thread */
+    int64_t recv_counter;
+    size_t sink_skip;
+
+    /* Bytes left over from previous iteration */
+    size_t sink_rem;
+    size_t source_rem;
+
+    pa_atomic_t request_resync;
+
+    pa_time_event *time_event;
+    pa_usec_t adjust_time;
+    int adjust_threshold;
+
+    FILE *captured_file;
+    FILE *played_file;
+    FILE *canceled_file;
+    FILE *drift_file;
+
+    bool use_volume_sharing;
+
+    struct {
+        pa_cvolume current_volume;
+    } thread_info;
+};
+
+static void source_output_snapshot_within_thread(struct userdata *u, struct snapshot *snapshot);
+
+static const char* const valid_modargs[] = {
+    "source_name",
+    "source_properties",
+    "source_master",
+    "sink_name",
+    "sink_properties",
+    "sink_master",
+    "adjust_time",
+    "adjust_threshold",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "aec_method",
+    "aec_args",
+    "save_aec",
+    "autoloaded",
+    "use_volume_sharing",
+    "use_master_format",
+    NULL
+};
+
+enum {
+    SOURCE_OUTPUT_MESSAGE_POST = PA_SOURCE_OUTPUT_MESSAGE_MAX,
+    SOURCE_OUTPUT_MESSAGE_REWIND,
+    SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT,
+    SOURCE_OUTPUT_MESSAGE_APPLY_DIFF_TIME
+};
+
+enum {
+    SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT
+};
+
+enum {
+    ECHO_CANCELLER_MESSAGE_SET_VOLUME,
+};
+
+static int64_t calc_diff(struct userdata *u, struct snapshot *snapshot) {
+    int64_t diff_time, buffer_latency;
+    pa_usec_t plen, rlen, source_delay, sink_delay, recv_counter, send_counter;
+
+    /* get latency difference between playback and record */
+    plen = pa_bytes_to_usec(snapshot->plen, &u->sink_input->sample_spec);
+    rlen = pa_bytes_to_usec(snapshot->rlen, &u->source_output->sample_spec);
+    if (plen > rlen)
+        buffer_latency = plen - rlen;
+    else
+        buffer_latency = 0;
+
+    source_delay = pa_bytes_to_usec(snapshot->source_delay, &u->source_output->sample_spec);
+    sink_delay = pa_bytes_to_usec(snapshot->sink_delay, &u->sink_input->sample_spec);
+    buffer_latency += source_delay + sink_delay;
+
+    /* add the latency difference due to samples not yet transferred */
+    send_counter = pa_bytes_to_usec(snapshot->send_counter, &u->sink->sample_spec);
+    recv_counter = pa_bytes_to_usec(snapshot->recv_counter, &u->sink->sample_spec);
+    if (recv_counter <= send_counter)
+        buffer_latency += (int64_t) (send_counter - recv_counter);
+    else
+        buffer_latency = PA_CLIP_SUB(buffer_latency, (int64_t) (recv_counter - send_counter));
+
+    /* capture and playback are perfectly aligned when diff_time is 0 */
+    diff_time = (snapshot->sink_now + snapshot->sink_latency - buffer_latency) -
+          (snapshot->source_now - snapshot->source_latency);
+
+    pa_log_debug("Diff %lld (%lld - %lld + %lld) %lld %lld %lld %lld", (long long) diff_time,
+        (long long) snapshot->sink_latency,
+        (long long) buffer_latency, (long long) snapshot->source_latency,
+        (long long) source_delay, (long long) sink_delay,
+        (long long) (send_counter - recv_counter),
+        (long long) (snapshot->sink_now - snapshot->source_now));
+
+    return diff_time;
+}
+
+/* Called from main context */
+static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t old_rate, base_rate, new_rate;
+    int64_t diff_time;
+    /*size_t fs*/
+    struct snapshot latency_snapshot;
+
+    pa_assert(u);
+    pa_assert(a);
+    pa_assert(u->time_event == e);
+    pa_assert_ctl_context();
+
+    if (!IS_ACTIVE(u))
+        return;
+
+    /* update our snapshots */
+    pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, &latency_snapshot, 0, NULL);
+    pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, &latency_snapshot, 0, NULL);
+
+    /* calculate drift between capture and playback */
+    diff_time = calc_diff(u, &latency_snapshot);
+
+    /*fs = pa_frame_size(&u->source_output->sample_spec);*/
+    old_rate = u->sink_input->sample_spec.rate;
+    base_rate = u->source_output->sample_spec.rate;
+
+    if (diff_time < 0) {
+        /* recording before playback, we need to adjust quickly. The echo
+         * canceller does not work in this case. */
+        pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_APPLY_DIFF_TIME,
+            NULL, diff_time, NULL, NULL);
+        /*new_rate = base_rate - ((pa_usec_to_bytes(-diff_time, &u->source_output->sample_spec) / fs) * PA_USEC_PER_SEC) / u->adjust_time;*/
+        new_rate = base_rate;
+    }
+    else {
+        if (diff_time > u->adjust_threshold) {
+            /* diff too big, quickly adjust */
+            pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_APPLY_DIFF_TIME,
+                NULL, diff_time, NULL, NULL);
+        }
+
+        /* recording behind playback, we need to slowly adjust the rate to match */
+        /*new_rate = base_rate + ((pa_usec_to_bytes(diff_time, &u->source_output->sample_spec) / fs) * PA_USEC_PER_SEC) / u->adjust_time;*/
+
+        /* assume equal samplerates for now */
+        new_rate = base_rate;
+    }
+
+    /* make sure we don't make too big adjustments because that sounds horrible */
+    if (new_rate > base_rate * 1.1 || new_rate < base_rate * 0.9)
+        new_rate = base_rate;
+
+    if (new_rate != old_rate) {
+        pa_log_info("Old rate %lu Hz, new rate %lu Hz", (unsigned long) old_rate, (unsigned long) new_rate);
+
+        pa_sink_input_set_rate(u->sink_input, new_rate);
+    }
+
+    pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time);
+}
+
+/* Called from source I/O thread context */
+static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY:
+
+            /* The source is _put() before the source output is, so let's
+             * make sure we don't access it in that time. Also, the
+             * source output is first shut down, the source second. */
+            if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
+                !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            *((int64_t*) data) =
+
+                /* Get the latency of the master source */
+                pa_source_get_latency_within_thread(u->source_output->source, true) +
+                /* Add the latency internal to our source output on top */
+                pa_bytes_to_usec(pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq), &u->source_output->source->sample_spec) +
+                /* and the buffering we do on the source */
+                pa_bytes_to_usec(u->source_output_blocksize, &u->source_output->source->sample_spec);
+
+            return 0;
+
+        case PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED:
+            u->thread_info.current_volume = u->source->reference_volume;
+            break;
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from sink I/O thread context */
+static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY:
+
+            /* The sink is _put() before the sink input is, so let's
+             * make sure we don't access it in that time. Also, the
+             * sink input is first shut down, the sink second. */
+            if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+                !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            *((int64_t*) data) =
+
+                /* Get the latency of the master sink */
+                pa_sink_get_latency_within_thread(u->sink_input->sink, true) +
+
+                /* Add the latency internal to our sink input on top */
+                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
+
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
+
+            /* When set to running or idle for the first time, request a rewind
+             * of the master sink to make sure we are heard immediately */
+            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
+                pa_log_debug("Requesting rewind due to state change.");
+                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
+            }
+            break;
+        }
+
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int source_set_state_cb(pa_source *s, pa_source_state_t state) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(state) ||
+        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
+        return 0;
+
+    if (state == PA_SOURCE_RUNNING) {
+        /* restart timer when both sink and source are active */
+        if ((pa_sink_get_state(u->sink) == PA_SINK_RUNNING) && u->adjust_time)
+            pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time);
+
+        pa_atomic_store(&u->request_resync, 1);
+        pa_source_output_cork(u->source_output, false);
+    } else if (state == PA_SOURCE_SUSPENDED) {
+        pa_source_output_cork(u->source_output, true);
+    }
+
+    return 0;
+}
+
+/* Called from main context */
+static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(state) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return 0;
+
+    if (state == PA_SINK_RUNNING) {
+        /* restart timer when both sink and source are active */
+        if ((pa_source_get_state(u->source) == PA_SOURCE_RUNNING) && u->adjust_time)
+            pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time);
+
+        pa_atomic_store(&u->request_resync, 1);
+        pa_sink_input_cork(u->sink_input, false);
+    } else if (state == PA_SINK_SUSPENDED) {
+        pa_sink_input_cork(u->sink_input, true);
+    }
+
+    return 0;
+}
+
+/* Called from source I/O thread context */
+static void source_update_requested_latency_cb(pa_source *s) {
+    struct userdata *u;
+    pa_usec_t latency;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
+        !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state))
+        return;
+
+    pa_log_debug("Source update requested latency");
+
+    /* Cap the maximum latency so we don't have to process too large chunks */
+    latency = PA_MIN(pa_source_get_requested_latency_within_thread(s),
+                     pa_bytes_to_usec(u->source_blocksize, &s->sample_spec) * MAX_LATENCY_BLOCKS);
+
+    pa_source_output_set_requested_latency_within_thread(u->source_output, latency);
+}
+
+/* Called from sink I/O thread context */
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u;
+    pa_usec_t latency;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    pa_log_debug("Sink update requested latency");
+
+    /* Cap the maximum latency so we don't have to process too large chunks */
+    latency = PA_MIN(pa_sink_get_requested_latency_within_thread(s),
+                     pa_bytes_to_usec(u->sink_blocksize, &s->sample_spec) * MAX_LATENCY_BLOCKS);
+
+    pa_sink_input_set_requested_latency_within_thread(u->sink_input, latency);
+}
+
+/* Called from sink I/O thread context */
+static void sink_request_rewind_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    pa_log_debug("Sink request rewind %lld", (long long) s->thread_info.rewind_nbytes);
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_request_rewind(u->sink_input,
+                                 s->thread_info.rewind_nbytes, true, false, false);
+}
+
+/* Called from main context */
+static void source_set_volume_cb(pa_source *s) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
+        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
+        return;
+
+    pa_source_output_set_volume(u->source_output, &s->real_volume, s->save_volume, true);
+}
+
+/* Called from main context */
+static void sink_set_volume_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return;
+
+    pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, true);
+}
+
+/* Called from main context. */
+static void source_get_volume_cb(pa_source *s) {
+    struct userdata *u;
+    pa_cvolume v;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
+        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
+        return;
+
+    pa_source_output_get_volume(u->source_output, &v, true);
+
+    if (pa_cvolume_equal(&s->real_volume, &v))
+        /* no change */
+        return;
+
+    s->real_volume = v;
+    pa_source_set_soft_volume(s, NULL);
+}
+
+/* Called from main context */
+static void source_set_mute_cb(pa_source *s) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
+        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
+        return;
+
+    pa_source_output_set_mute(u->source_output, s->muted, s->save_muted);
+}
+
+/* Called from main context */
+static void sink_set_mute_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return;
+
+    pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted);
+}
+
+/* Called from source I/O thread context. */
+static void apply_diff_time(struct userdata *u, int64_t diff_time) {
+    int64_t diff;
+
+    if (diff_time < 0) {
+        diff = pa_usec_to_bytes(-diff_time, &u->sink_input->sample_spec);
+
+        if (diff > 0) {
+            /* add some extra safety samples to compensate for jitter in the
+             * timings */
+            diff += 10 * pa_frame_size (&u->sink_input->sample_spec);
+
+            pa_log("Playback after capture (%lld), drop sink %lld", (long long) diff_time, (long long) diff);
+
+            u->sink_skip = diff;
+            u->source_skip = 0;
+        }
+    } else if (diff_time > 0) {
+        diff = pa_usec_to_bytes(diff_time, &u->source_output->sample_spec);
+
+        if (diff > 0) {
+            pa_log("Playback too far ahead (%lld), drop source %lld", (long long) diff_time, (long long) diff);
+
+            u->source_skip = diff;
+            u->sink_skip = 0;
+        }
+    }
+}
+
+/* Called from source I/O thread context. */
+static void do_resync(struct userdata *u) {
+    int64_t diff_time;
+    struct snapshot latency_snapshot;
+
+    pa_log("Doing resync");
+
+    /* update our snapshot */
+    /* 1. Get sink input latency snapshot, might cause buffers to be sent to source thread */
+    pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, &latency_snapshot, 0, NULL);
+    /* 2. Pick up any in-flight buffers (and discard if needed) */
+    while (pa_asyncmsgq_process_one(u->asyncmsgq))
+        ;
+    /* 3. Now get the source output latency snapshot */
+    source_output_snapshot_within_thread(u, &latency_snapshot);
+
+    /* calculate drift between capture and playback */
+    diff_time = calc_diff(u, &latency_snapshot);
+
+    /* and adjust for the drift */
+    apply_diff_time(u, diff_time);
+}
+
+/* 1. Calculate drift at this point, pass to canceller
+ * 2. Push out playback samples in blocksize chunks
+ * 3. Push out capture samples in blocksize chunks
+ * 4. ???
+ * 5. Profit
+ *
+ * Called from source I/O thread context.
+ */
+static void do_push_drift_comp(struct userdata *u) {
+    size_t rlen, plen;
+    pa_memchunk rchunk, pchunk, cchunk;
+    uint8_t *rdata, *pdata, *cdata;
+    float drift;
+    int unused PA_GCC_UNUSED;
+
+    rlen = pa_memblockq_get_length(u->source_memblockq);
+    plen = pa_memblockq_get_length(u->sink_memblockq);
+
+    /* Estimate snapshot drift as follows:
+     *   pd: amount of data consumed since last time
+     *   rd: amount of data consumed since last time
+     *
+     *   drift = (pd - rd) / rd;
+     *
+     * We calculate pd and rd as the memblockq length less the number of
+     * samples left from the last iteration (to avoid double counting
+     * those remainder samples.
+     */
+    drift = ((float)(plen - u->sink_rem) - (rlen - u->source_rem)) / ((float)(rlen - u->source_rem));
+    u->sink_rem = plen % u->sink_blocksize;
+    u->source_rem = rlen % u->source_output_blocksize;
+
+    if (u->save_aec) {
+        if (u->drift_file)
+            fprintf(u->drift_file, "d %a\n", drift);
+    }
+
+    /* Send in the playback samples first */
+    while (plen >= u->sink_blocksize) {
+        pa_memblockq_peek_fixed_size(u->sink_memblockq, u->sink_blocksize, &pchunk);
+        pdata = pa_memblock_acquire(pchunk.memblock);
+        pdata += pchunk.index;
+
+        u->ec->play(u->ec, pdata);
+
+        if (u->save_aec) {
+            if (u->drift_file)
+                fprintf(u->drift_file, "p %d\n", u->sink_blocksize);
+            if (u->played_file)
+                unused = fwrite(pdata, 1, u->sink_blocksize, u->played_file);
+        }
+
+        pa_memblock_release(pchunk.memblock);
+        pa_memblockq_drop(u->sink_memblockq, u->sink_blocksize);
+        pa_memblock_unref(pchunk.memblock);
+
+        plen -= u->sink_blocksize;
+    }
+
+    /* And now the capture samples */
+    while (rlen >= u->source_output_blocksize) {
+        pa_memblockq_peek_fixed_size(u->source_memblockq, u->source_output_blocksize, &rchunk);
+
+        rdata = pa_memblock_acquire(rchunk.memblock);
+        rdata += rchunk.index;
+
+        cchunk.index = 0;
+        cchunk.length = u->source_output_blocksize;
+        cchunk.memblock = pa_memblock_new(u->source->core->mempool, cchunk.length);
+        cdata = pa_memblock_acquire(cchunk.memblock);
+
+        u->ec->set_drift(u->ec, drift);
+        u->ec->record(u->ec, rdata, cdata);
+
+        if (u->save_aec) {
+            if (u->drift_file)
+                fprintf(u->drift_file, "c %d\n", u->source_output_blocksize);
+            if (u->captured_file)
+                unused = fwrite(rdata, 1, u->source_output_blocksize, u->captured_file);
+            if (u->canceled_file)
+                unused = fwrite(cdata, 1, u->source_output_blocksize, u->canceled_file);
+        }
+
+        pa_memblock_release(cchunk.memblock);
+        pa_memblock_release(rchunk.memblock);
+
+        pa_memblock_unref(rchunk.memblock);
+
+        pa_source_post(u->source, &cchunk);
+        pa_memblock_unref(cchunk.memblock);
+
+        pa_memblockq_drop(u->source_memblockq, u->source_output_blocksize);
+        rlen -= u->source_output_blocksize;
+    }
+}
+
+/* This one's simpler than the drift compensation case -- we just iterate over
+ * the capture buffer, and pass the canceller blocksize bytes of playback and
+ * capture data. If playback is currently inactive, we just push silence.
+ *
+ * Called from source I/O thread context. */
+static void do_push(struct userdata *u) {
+    size_t rlen, plen;
+    pa_memchunk rchunk, pchunk, cchunk;
+    uint8_t *rdata, *pdata, *cdata;
+    int unused PA_GCC_UNUSED;
+
+    rlen = pa_memblockq_get_length(u->source_memblockq);
+    plen = pa_memblockq_get_length(u->sink_memblockq);
+
+    while (rlen >= u->source_output_blocksize) {
+
+        /* take fixed blocks from recorded and played samples */
+        pa_memblockq_peek_fixed_size(u->source_memblockq, u->source_output_blocksize, &rchunk);
+        pa_memblockq_peek_fixed_size(u->sink_memblockq, u->sink_blocksize, &pchunk);
+
+        /* we ran out of played data and pchunk has been filled with silence bytes */
+        if (plen < u->sink_blocksize)
+            pa_memblockq_seek(u->sink_memblockq, u->sink_blocksize - plen, PA_SEEK_RELATIVE, true);
+
+        rdata = pa_memblock_acquire(rchunk.memblock);
+        rdata += rchunk.index;
+        pdata = pa_memblock_acquire(pchunk.memblock);
+        pdata += pchunk.index;
+
+        cchunk.index = 0;
+        cchunk.length = u->source_blocksize;
+        cchunk.memblock = pa_memblock_new(u->source->core->mempool, cchunk.length);
+        cdata = pa_memblock_acquire(cchunk.memblock);
+
+        if (u->save_aec) {
+            if (u->captured_file)
+                unused = fwrite(rdata, 1, u->source_output_blocksize, u->captured_file);
+            if (u->played_file)
+                unused = fwrite(pdata, 1, u->sink_blocksize, u->played_file);
+        }
+
+        /* perform echo cancellation */
+        u->ec->run(u->ec, rdata, pdata, cdata);
+
+        if (u->save_aec) {
+            if (u->canceled_file)
+                unused = fwrite(cdata, 1, u->source_blocksize, u->canceled_file);
+        }
+
+        pa_memblock_release(cchunk.memblock);
+        pa_memblock_release(pchunk.memblock);
+        pa_memblock_release(rchunk.memblock);
+
+        /* drop consumed source samples */
+        pa_memblockq_drop(u->source_memblockq, u->source_output_blocksize);
+        pa_memblock_unref(rchunk.memblock);
+        rlen -= u->source_output_blocksize;
+
+        /* drop consumed sink samples */
+        pa_memblockq_drop(u->sink_memblockq, u->sink_blocksize);
+        pa_memblock_unref(pchunk.memblock);
+
+        if (plen >= u->sink_blocksize)
+            plen -= u->sink_blocksize;
+        else
+            plen = 0;
+
+        /* forward the (echo-canceled) data to the virtual source */
+        pa_source_post(u->source, &cchunk);
+        pa_memblock_unref(cchunk.memblock);
+    }
+}
+
+/* Called from source I/O thread context. */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    struct userdata *u;
+    size_t rlen, plen, to_skip;
+    pa_memchunk rchunk;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        return;
+
+    if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) {
+        pa_log("Push when no link?");
+        return;
+    }
+
+    /* handle queued messages, do any message sending of our own */
+    while (pa_asyncmsgq_process_one(u->asyncmsgq) > 0)
+        ;
+
+    pa_memblockq_push_align(u->source_memblockq, chunk);
+
+    rlen = pa_memblockq_get_length(u->source_memblockq);
+    plen = pa_memblockq_get_length(u->sink_memblockq);
+
+    /* Let's not do anything else till we have enough data to process */
+    if (rlen < u->source_output_blocksize)
+        return;
+
+    /* See if we need to drop samples in order to sync */
+    if (pa_atomic_cmpxchg (&u->request_resync, 1, 0)) {
+        do_resync(u);
+    }
+
+    /* Okay, skip cancellation for skipped source samples if needed. */
+    if (PA_UNLIKELY(u->source_skip)) {
+        /* The slightly tricky bit here is that we drop all but modulo
+         * blocksize bytes and then adjust for that last bit on the sink side.
+         * We do this because the source data is coming at a fixed rate, which
+         * means the only way to try to catch up is drop sink samples and let
+         * the canceller cope up with this. */
+        to_skip = rlen >= u->source_skip ? u->source_skip : rlen;
+        to_skip -= to_skip % u->source_output_blocksize;
+
+        if (to_skip) {
+            pa_memblockq_peek_fixed_size(u->source_memblockq, to_skip, &rchunk);
+            pa_source_post(u->source, &rchunk);
+
+            pa_memblock_unref(rchunk.memblock);
+            pa_memblockq_drop(u->source_memblockq, to_skip);
+
+            rlen -= to_skip;
+            u->source_skip -= to_skip;
+        }
+
+        if (rlen && u->source_skip % u->source_output_blocksize) {
+            u->sink_skip += (uint64_t) (u->source_output_blocksize - (u->source_skip % u->source_output_blocksize)) * u->sink_blocksize / u->source_output_blocksize;
+            u->source_skip -= (u->source_skip % u->source_output_blocksize);
+        }
+    }
+
+    /* And for the sink, these samples have been played back already, so we can
+     * just drop them and get on with it. */
+    if (PA_UNLIKELY(u->sink_skip)) {
+        to_skip = plen >= u->sink_skip ? u->sink_skip : plen;
+
+        pa_memblockq_drop(u->sink_memblockq, to_skip);
+
+        plen -= to_skip;
+        u->sink_skip -= to_skip;
+    }
+
+    /* process and push out samples */
+    if (u->ec->params.drift_compensation)
+        do_push_drift_comp(u);
+    else
+        do_push(u);
+}
+
+/* Called from sink I/O thread context. */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    pa_assert_se(u = i->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return -1;
+
+    if (u->sink->thread_info.rewind_requested)
+        pa_sink_process_rewind(u->sink, 0);
+
+    pa_sink_render_full(u->sink, nbytes, chunk);
+
+    if (i->thread_info.underrun_for > 0) {
+        pa_log_debug("Handling end of underrun.");
+        pa_atomic_store(&u->request_resync, 1);
+    }
+
+    /* let source thread handle the chunk. pass the sample count as well so that
+     * the source IO thread can update the right variables. */
+    pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_POST,
+        NULL, 0, chunk, NULL);
+    u->send_counter += chunk->length;
+
+    return 0;
+}
+
+/* Called from source I/O thread context. */
+static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    /* If the source is not yet linked, there is nothing to rewind */
+    if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        return;
+
+    pa_source_process_rewind(u->source, nbytes);
+
+    /* go back on read side, we need to use older sink data for this */
+    pa_memblockq_rewind(u->sink_memblockq, nbytes);
+
+    /* manipulate write index */
+    pa_memblockq_seek(u->source_memblockq, -nbytes, PA_SEEK_RELATIVE, true);
+
+    pa_log_debug("Source rewind (%lld) %lld", (long long) nbytes,
+        (long long) pa_memblockq_get_length (u->source_memblockq));
+}
+
+/* Called from sink I/O thread context. */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If the sink is not yet linked, there is nothing to rewind */
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_log_debug("Sink process rewind %lld", (long long) nbytes);
+
+    pa_sink_process_rewind(u->sink, nbytes);
+
+    pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL);
+    u->send_counter -= nbytes;
+}
+
+/* Called from source I/O thread context. */
+static void source_output_snapshot_within_thread(struct userdata *u, struct snapshot *snapshot) {
+    size_t delay, rlen, plen;
+    pa_usec_t now, latency;
+
+    now = pa_rtclock_now();
+    latency = pa_source_get_latency_within_thread(u->source_output->source, false);
+    delay = pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq);
+
+    delay = (u->source_output->thread_info.resampler ? pa_resampler_request(u->source_output->thread_info.resampler, delay) : delay);
+    rlen = pa_memblockq_get_length(u->source_memblockq);
+    plen = pa_memblockq_get_length(u->sink_memblockq);
+
+    snapshot->source_now = now;
+    snapshot->source_latency = latency;
+    snapshot->source_delay = delay;
+    snapshot->recv_counter = u->recv_counter;
+    snapshot->rlen = rlen + u->sink_skip;
+    snapshot->plen = plen + u->source_skip;
+}
+
+/* Called from source I/O thread context. */
+static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE_OUTPUT(obj)->userdata;
+
+    switch (code) {
+
+        case SOURCE_OUTPUT_MESSAGE_POST:
+
+            pa_source_output_assert_io_context(u->source_output);
+
+            if (u->source_output->source->thread_info.state == PA_SOURCE_RUNNING)
+                pa_memblockq_push_align(u->sink_memblockq, chunk);
+            else
+                pa_memblockq_flush_write(u->sink_memblockq, true);
+
+            u->recv_counter += (int64_t) chunk->length;
+
+            return 0;
+
+        case SOURCE_OUTPUT_MESSAGE_REWIND:
+            pa_source_output_assert_io_context(u->source_output);
+
+            /* manipulate write index, never go past what we have */
+            if (PA_SOURCE_IS_OPENED(u->source_output->source->thread_info.state))
+                pa_memblockq_seek(u->sink_memblockq, -offset, PA_SEEK_RELATIVE, true);
+            else
+                pa_memblockq_flush_write(u->sink_memblockq, true);
+
+            pa_log_debug("Sink rewind (%lld)", (long long) offset);
+
+            u->recv_counter -= offset;
+
+            return 0;
+
+        case SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT: {
+            struct snapshot *snapshot = (struct snapshot *) data;
+
+            source_output_snapshot_within_thread(u, snapshot);
+            return 0;
+        }
+
+        case SOURCE_OUTPUT_MESSAGE_APPLY_DIFF_TIME:
+            apply_diff_time(u, offset);
+            return 0;
+
+    }
+
+    return pa_source_output_process_msg(obj, code, data, offset, chunk);
+}
+
+/* Called from sink I/O thread context. */
+static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK_INPUT(obj)->userdata;
+
+    switch (code) {
+
+        case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: {
+            size_t delay;
+            pa_usec_t now, latency;
+            struct snapshot *snapshot = (struct snapshot *) data;
+
+            pa_sink_input_assert_io_context(u->sink_input);
+
+            now = pa_rtclock_now();
+            latency = pa_sink_get_latency_within_thread(u->sink_input->sink, false);
+            delay = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq);
+
+            delay = (u->sink_input->thread_info.resampler ? pa_resampler_request(u->sink_input->thread_info.resampler, delay) : delay);
+
+            snapshot->sink_now = now;
+            snapshot->sink_latency = latency;
+            snapshot->sink_delay = delay;
+            snapshot->send_counter = u->send_counter;
+            return 0;
+        }
+    }
+
+    return pa_sink_input_process_msg(obj, code, data, offset, chunk);
+}
+
+/* Called from sink I/O thread context. */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_log_debug("Sink input update max rewind %lld", (long long) nbytes);
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_memblockq_set_maxrewind(u->sink_memblockq, nbytes);
+    pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
+}
+
+/* Called from source I/O thread context. */
+static void source_output_update_max_rewind_cb(pa_source_output *o, size_t nbytes) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_se(u = o->userdata);
+
+    pa_log_debug("Source output update max rewind %lld", (long long) nbytes);
+
+    pa_source_set_max_rewind_within_thread(u->source, nbytes);
+}
+
+/* Called from sink I/O thread context. */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_log_debug("Sink input update max request %lld", (long long) nbytes);
+
+    pa_sink_set_max_request_within_thread(u->sink, nbytes);
+}
+
+/* Called from sink I/O thread context. */
+static void sink_input_update_sink_requested_latency_cb(pa_sink_input *i) {
+    struct userdata *u;
+    pa_usec_t latency;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    latency = pa_sink_get_requested_latency_within_thread(i->sink);
+
+    pa_log_debug("Sink input update requested latency %lld", (long long) latency);
+}
+
+/* Called from source I/O thread context. */
+static void source_output_update_source_requested_latency_cb(pa_source_output *o) {
+    struct userdata *u;
+    pa_usec_t latency;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_se(u = o->userdata);
+
+    latency = pa_source_get_requested_latency_within_thread(o->source);
+
+    pa_log_debug("Source output update requested latency %lld", (long long) latency);
+}
+
+/* Called from sink I/O thread context. */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_log_debug("Sink input update latency range %lld %lld",
+        (long long) i->sink->thread_info.min_latency,
+        (long long) i->sink->thread_info.max_latency);
+
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+}
+
+/* Called from source I/O thread context. */
+static void source_output_update_source_latency_range_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_se(u = o->userdata);
+
+    pa_log_debug("Source output update latency range %lld %lld",
+        (long long) o->source->thread_info.min_latency,
+        (long long) o->source->thread_info.max_latency);
+
+    pa_source_set_latency_range_within_thread(u->source, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
+}
+
+/* Called from sink I/O thread context. */
+static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_log_debug("Sink input update fixed latency %lld",
+        (long long) i->sink->thread_info.fixed_latency);
+
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+}
+
+/* Called from source I/O thread context. */
+static void source_output_update_source_fixed_latency_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_se(u = o->userdata);
+
+    pa_log_debug("Source output update fixed latency %lld",
+        (long long) o->source->thread_info.fixed_latency);
+
+    pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency);
+}
+
+/* Called from source I/O thread context. */
+static void source_output_attach_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    pa_source_set_rtpoll(u->source, o->source->thread_info.rtpoll);
+    pa_source_set_latency_range_within_thread(u->source, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
+    pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency);
+    pa_source_set_max_rewind_within_thread(u->source, pa_source_output_get_max_rewind(o));
+
+    pa_log_debug("Source output %d attach", o->index);
+
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_attach_within_thread(u->source);
+
+    u->rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
+            o->source->thread_info.rtpoll,
+            PA_RTPOLL_LATE,
+            u->asyncmsgq);
+}
+
+/* Called from sink I/O thread context. */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll);
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+
+    /* (8.1) IF YOU NEED A FIXED BLOCK SIZE ADD THE LATENCY FOR ONE
+     * BLOCK MINUS ONE SAMPLE HERE. SEE (7) */
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+
+    /* (8.2) IF YOU NEED A FIXED BLOCK SIZE ROUND
+     * pa_sink_input_get_max_request(i) UP TO MULTIPLES OF IT
+     * HERE. SEE (6) */
+    pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i));
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
+
+    pa_log_debug("Sink input %d attach", i->index);
+
+    u->rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+            i->sink->thread_info.rtpoll,
+            PA_RTPOLL_LATE,
+            u->asyncmsgq);
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_attach_within_thread(u->sink);
+}
+
+/* Called from source I/O thread context. */
+static void source_output_detach_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_detach_within_thread(u->source);
+    pa_source_set_rtpoll(u->source, NULL);
+
+    pa_log_debug("Source output %d detach", o->index);
+
+    if (u->rtpoll_item_read) {
+        pa_rtpoll_item_free(u->rtpoll_item_read);
+        u->rtpoll_item_read = NULL;
+    }
+}
+
+/* Called from sink I/O thread context. */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_detach_within_thread(u->sink);
+
+    pa_sink_set_rtpoll(u->sink, NULL);
+
+    pa_log_debug("Sink input %d detach", i->index);
+
+    if (u->rtpoll_item_write) {
+        pa_rtpoll_item_free(u->rtpoll_item_write);
+        u->rtpoll_item_write = NULL;
+    }
+}
+
+/* Called from source I/O thread context except when cork() is called without valid source. */
+static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_se(u = o->userdata);
+
+    pa_log_debug("Source output %d state %d", o->index, state);
+}
+
+/* Called from sink I/O thread context. */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_log_debug("Sink input %d state %d", i->index, state);
+}
+
+/* Called from main context. */
+static void source_output_kill_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert_se(u = o->userdata);
+
+    u->dead = true;
+
+    /* The order here matters! We first kill the source so that streams can
+     * properly be moved away while the source output is still connected to
+     * the master. */
+    pa_source_output_cork(u->source_output, true);
+    pa_source_unlink(u->source);
+    pa_source_output_unlink(u->source_output);
+
+    pa_source_output_unref(u->source_output);
+    u->source_output = NULL;
+
+    pa_source_unref(u->source);
+    u->source = NULL;
+
+    pa_log_debug("Source output kill %d", o->index);
+
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    u->dead = true;
+
+    /* The order here matters! We first kill the sink so that streams
+     * can properly be moved away while the sink input is still connected
+     * to the master. */
+    pa_sink_input_cork(u->sink_input, true);
+    pa_sink_unlink(u->sink);
+    pa_sink_input_unlink(u->sink_input);
+
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    pa_sink_unref(u->sink);
+    u->sink = NULL;
+
+    pa_log_debug("Sink input kill %d", i->index);
+
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main context. */
+static bool source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert_se(u = o->userdata);
+
+    if (u->dead)
+        return false;
+
+    return (u->source != dest) && (u->sink != dest->monitor_of);
+}
+
+/* Called from main context */
+static bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (u->dead)
+        return false;
+
+    return u->sink != dest;
+}
+
+/* Called from main context. */
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert_se(u = o->userdata);
+
+    if (dest) {
+        pa_source_set_asyncmsgq(u->source, dest->asyncmsgq);
+        pa_source_update_flags(u->source, PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY, dest->flags);
+    } else
+        pa_source_set_asyncmsgq(u->source, NULL);
+
+    if (u->source_auto_desc && dest) {
+        const char *y, *z;
+        pa_proplist *pl;
+
+        pl = pa_proplist_new();
+        if (u->sink_input->sink) {
+            pa_proplist_sets(pl, PA_PROP_DEVICE_MASTER_DEVICE, u->sink_input->sink->name);
+            y = pa_proplist_gets(u->sink_input->sink->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        } else
+            y = "<unknown>"; /* Probably in the middle of a move */
+        z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "%s (echo cancelled with %s)", z ? z : dest->name,
+                y ? y : u->sink_input->sink->name);
+
+        pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, pl);
+        pa_proplist_free(pl);
+    }
+}
+
+/* Called from main context */
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (dest) {
+        pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
+        pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
+    } else
+        pa_sink_set_asyncmsgq(u->sink, NULL);
+
+    if (u->sink_auto_desc && dest) {
+        const char *y, *z;
+        pa_proplist *pl;
+
+        pl = pa_proplist_new();
+        if (u->source_output->source) {
+            pa_proplist_sets(pl, PA_PROP_DEVICE_MASTER_DEVICE, u->source_output->source->name);
+            y = pa_proplist_gets(u->source_output->source->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        } else
+            y = "<unknown>"; /* Probably in the middle of a move */
+        z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "%s (echo cancelled with %s)", z ? z : dest->name,
+                         y ? y : u->source_output->source->name);
+
+        pa_sink_update_proplist(u->sink, PA_UPDATE_REPLACE, pl);
+        pa_proplist_free(pl);
+    }
+}
+
+/* Called from main context */
+static void sink_input_volume_changed_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_volume_changed(u->sink, &i->volume);
+}
+
+/* Called from main context */
+static void sink_input_mute_changed_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_mute_changed(u->sink, i->muted);
+}
+
+/* Called from main context */
+static int canceller_process_msg_cb(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    struct pa_echo_canceller_msg *msg;
+    struct userdata *u;
+
+    pa_assert(o);
+
+    msg = PA_ECHO_CANCELLER_MSG(o);
+    u = msg->userdata;
+
+    switch (code) {
+        case ECHO_CANCELLER_MESSAGE_SET_VOLUME: {
+            pa_volume_t v = PA_PTR_TO_UINT(userdata);
+            pa_cvolume vol;
+
+            if (u->use_volume_sharing) {
+                pa_cvolume_set(&vol, u->source->sample_spec.channels, v);
+                pa_source_set_volume(u->source, &vol, true, false);
+            } else {
+                pa_cvolume_set(&vol, u->source_output->sample_spec.channels, v);
+                pa_source_output_set_volume(u->source_output, &vol, false, true);
+            }
+
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+            break;
+    }
+
+    return 0;
+}
+
+/* Called by the canceller, so source I/O thread context. */
+pa_volume_t pa_echo_canceller_get_capture_volume(pa_echo_canceller *ec) {
+#ifndef ECHO_CANCEL_TEST
+    return pa_cvolume_avg(&ec->msg->userdata->thread_info.current_volume);
+#else
+    return PA_VOLUME_NORM;
+#endif
+}
+
+/* Called by the canceller, so source I/O thread context. */
+void pa_echo_canceller_set_capture_volume(pa_echo_canceller *ec, pa_volume_t v) {
+#ifndef ECHO_CANCEL_TEST
+    if (pa_cvolume_avg(&ec->msg->userdata->thread_info.current_volume) != v) {
+        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(ec->msg), ECHO_CANCELLER_MESSAGE_SET_VOLUME, PA_UINT_TO_PTR(v),
+                0, NULL, NULL);
+    }
+#endif
+}
+
+uint32_t pa_echo_canceller_blocksize_power2(unsigned rate, unsigned ms) {
+    unsigned nframes = (rate * ms) / 1000;
+    uint32_t y = 1 << ((8 * sizeof(uint32_t)) - 2);
+
+    pa_assert(rate >= 4000);
+    pa_assert(ms >= 1);
+
+    /* nframes should be a power of 2, round down to nearest power of two */
+    while (y > nframes)
+        y >>= 1;
+
+    pa_assert(y >= 1);
+    return y;
+}
+
+static pa_echo_canceller_method_t get_ec_method_from_string(const char *method) {
+    if (pa_streq(method, "null"))
+        return PA_ECHO_CANCELLER_NULL;
+#ifdef HAVE_SPEEX
+    if (pa_streq(method, "speex"))
+        return PA_ECHO_CANCELLER_SPEEX;
+#endif
+#ifdef HAVE_ADRIAN_EC
+    if (pa_streq(method, "adrian"))
+        return PA_ECHO_CANCELLER_ADRIAN;
+#endif
+#ifdef HAVE_WEBRTC
+    if (pa_streq(method, "webrtc"))
+        return PA_ECHO_CANCELLER_WEBRTC;
+#endif
+    return PA_ECHO_CANCELLER_INVALID;
+}
+
+/* Common initialisation bits between module-echo-cancel and the standalone
+ * test program.
+ *
+ * Called from main context. */
+static int init_common(pa_modargs *ma, struct userdata *u, pa_sample_spec *source_ss, pa_channel_map *source_map) {
+    const char *ec_string;
+    pa_echo_canceller_method_t ec_method;
+
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, source_ss, source_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    u->ec = pa_xnew0(pa_echo_canceller, 1);
+    if (!u->ec) {
+        pa_log("Failed to alloc echo canceller");
+        goto fail;
+    }
+
+    ec_string = pa_modargs_get_value(ma, "aec_method", DEFAULT_ECHO_CANCELLER);
+    if ((ec_method = get_ec_method_from_string(ec_string)) < 0) {
+        pa_log("Invalid echo canceller implementation '%s'", ec_string);
+        goto fail;
+    }
+
+    pa_log_info("Using AEC engine: %s", ec_string);
+
+    u->ec->init = ec_table[ec_method].init;
+    u->ec->play = ec_table[ec_method].play;
+    u->ec->record = ec_table[ec_method].record;
+    u->ec->set_drift = ec_table[ec_method].set_drift;
+    u->ec->run = ec_table[ec_method].run;
+    u->ec->done = ec_table[ec_method].done;
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+/* Called from main context. */
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_sample_spec source_output_ss, source_ss, sink_ss;
+    pa_channel_map source_output_map, source_map, sink_map;
+    pa_modargs *ma;
+    pa_source *source_master=NULL;
+    pa_sink *sink_master=NULL;
+    bool autoloaded;
+    pa_source_output_new_data source_output_data;
+    pa_sink_input_new_data sink_input_data;
+    pa_source_new_data source_data;
+    pa_sink_new_data sink_data;
+    pa_memchunk silence;
+    uint32_t temp;
+    uint32_t nframes = 0;
+    bool use_master_format;
+    pa_usec_t blocksize_usec;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (!(source_master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source_master", NULL), PA_NAMEREG_SOURCE))) {
+        pa_log("Master source not found");
+        goto fail;
+    }
+    pa_assert(source_master);
+
+    if (!(sink_master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink_master", NULL), PA_NAMEREG_SINK))) {
+        pa_log("Master sink not found");
+        goto fail;
+    }
+    pa_assert(sink_master);
+
+    if (source_master->monitor_of == sink_master) {
+        pa_log("Can't cancel echo between a sink and its monitor");
+        goto fail;
+    }
+
+    /* Set to true if we just want to inherit sample spec and channel map from the sink and source master */
+    use_master_format = DEFAULT_USE_MASTER_FORMAT;
+    if (pa_modargs_get_value_boolean(ma, "use_master_format", &use_master_format) < 0) {
+        pa_log("use_master_format= expects a boolean argument");
+        goto fail;
+    }
+
+    source_ss = source_master->sample_spec;
+    sink_ss = sink_master->sample_spec;
+
+    if (use_master_format) {
+        source_map = source_master->channel_map;
+        sink_map = sink_master->channel_map;
+    } else {
+        source_ss = source_master->sample_spec;
+        source_ss.rate = DEFAULT_RATE;
+        source_ss.channels = DEFAULT_CHANNELS;
+        pa_channel_map_init_auto(&source_map, source_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+        sink_ss = sink_master->sample_spec;
+        sink_ss.rate = DEFAULT_RATE;
+        sink_ss.channels = DEFAULT_CHANNELS;
+        pa_channel_map_init_auto(&sink_map, sink_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    if (!u) {
+        pa_log("Failed to alloc userdata");
+        goto fail;
+    }
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->dead = false;
+
+    u->use_volume_sharing = true;
+    if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &u->use_volume_sharing) < 0) {
+        pa_log("use_volume_sharing= expects a boolean argument");
+        goto fail;
+    }
+
+    temp = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
+    if (pa_modargs_get_value_u32(ma, "adjust_time", &temp) < 0) {
+        pa_log("Failed to parse adjust_time value");
+        goto fail;
+    }
+
+    if (temp != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC)
+        u->adjust_time = temp * PA_USEC_PER_SEC;
+    else
+        u->adjust_time = DEFAULT_ADJUST_TIME_USEC;
+
+    temp = DEFAULT_ADJUST_TOLERANCE / PA_USEC_PER_MSEC;
+    if (pa_modargs_get_value_u32(ma, "adjust_threshold", &temp) < 0) {
+        pa_log("Failed to parse adjust_threshold value");
+        goto fail;
+    }
+
+    if (temp != DEFAULT_ADJUST_TOLERANCE / PA_USEC_PER_MSEC)
+        u->adjust_threshold = temp * PA_USEC_PER_MSEC;
+    else
+        u->adjust_threshold = DEFAULT_ADJUST_TOLERANCE;
+
+    u->save_aec = DEFAULT_SAVE_AEC;
+    if (pa_modargs_get_value_boolean(ma, "save_aec", &u->save_aec) < 0) {
+        pa_log("Failed to parse save_aec value");
+        goto fail;
+    }
+
+    autoloaded = DEFAULT_AUTOLOADED;
+    if (pa_modargs_get_value_boolean(ma, "autoloaded", &autoloaded) < 0) {
+        pa_log("Failed to parse autoloaded value");
+        goto fail;
+    }
+
+    if (init_common(ma, u, &source_ss, &source_map) < 0)
+        goto fail;
+
+    u->asyncmsgq = pa_asyncmsgq_new(0);
+    if (!u->asyncmsgq) {
+        pa_log("pa_asyncmsgq_new() failed.");
+        goto fail;
+    }
+
+    u->need_realign = true;
+
+    source_output_ss = source_ss;
+    source_output_map = source_map;
+
+    if (sink_ss.rate != source_ss.rate) {
+        pa_log_info("Sample rates of play and out stream differ. Adjusting rate of play stream.");
+        sink_ss.rate = source_ss.rate;
+    }
+
+    pa_assert(u->ec->init);
+    if (!u->ec->init(u->core, u->ec, &source_output_ss, &source_output_map, &sink_ss, &sink_map, &source_ss, &source_map, &nframes, pa_modargs_get_value(ma, "aec_args", NULL))) {
+        pa_log("Failed to init AEC engine");
+        goto fail;
+    }
+
+    pa_assert(source_output_ss.rate == source_ss.rate);
+    pa_assert(sink_ss.rate == source_ss.rate);
+
+    u->source_output_blocksize = nframes * pa_frame_size(&source_output_ss);
+    u->source_blocksize = nframes * pa_frame_size(&source_ss);
+    u->sink_blocksize = nframes * pa_frame_size(&sink_ss);
+
+    if (u->ec->params.drift_compensation)
+        pa_assert(u->ec->set_drift);
+
+    /* Create source */
+    pa_source_new_data_init(&source_data);
+    source_data.driver = __FILE__;
+    source_data.module = m;
+    if (!(source_data.name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
+        source_data.name = pa_sprintf_malloc("%s.echo-cancel", source_master->name);
+    pa_source_new_data_set_sample_spec(&source_data, &source_ss);
+    pa_source_new_data_set_channel_map(&source_data, &source_map);
+    pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, source_master->name);
+    pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+    if (!autoloaded)
+        pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
+
+    if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_source_new_data_done(&source_data);
+        goto fail;
+    }
+
+    if ((u->source_auto_desc = !pa_proplist_contains(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+        const char *y, *z;
+
+        y = pa_proplist_gets(sink_master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        z = pa_proplist_gets(source_master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s (echo cancelled with %s)",
+                z ? z : source_master->name, y ? y : sink_master->name);
+    }
+
+    u->source = pa_source_new(m->core, &source_data, (source_master->flags & (PA_SOURCE_LATENCY | PA_SOURCE_DYNAMIC_LATENCY))
+                                                     | (u->use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0));
+    pa_source_new_data_done(&source_data);
+
+    if (!u->source) {
+        pa_log("Failed to create source.");
+        goto fail;
+    }
+
+    u->source->parent.process_msg = source_process_msg_cb;
+    u->source->set_state = source_set_state_cb;
+    u->source->update_requested_latency = source_update_requested_latency_cb;
+    pa_source_set_set_mute_callback(u->source, source_set_mute_cb);
+    if (!u->use_volume_sharing) {
+        pa_source_set_get_volume_callback(u->source, source_get_volume_cb);
+        pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
+        pa_source_enable_decibel_volume(u->source, true);
+    }
+    u->source->userdata = u;
+
+    pa_source_set_asyncmsgq(u->source, source_master->asyncmsgq);
+
+    /* Create sink */
+    pa_sink_new_data_init(&sink_data);
+    sink_data.driver = __FILE__;
+    sink_data.module = m;
+    if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+        sink_data.name = pa_sprintf_malloc("%s.echo-cancel", sink_master->name);
+    pa_sink_new_data_set_sample_spec(&sink_data, &sink_ss);
+    pa_sink_new_data_set_channel_map(&sink_data, &sink_map);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, sink_master->name);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+    if (!autoloaded)
+        pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&sink_data);
+        goto fail;
+    }
+
+    if ((u->sink_auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+        const char *y, *z;
+
+        y = pa_proplist_gets(source_master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        z = pa_proplist_gets(sink_master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s (echo cancelled with %s)",
+                z ? z : sink_master->name, y ? y : source_master->name);
+    }
+
+    u->sink = pa_sink_new(m->core, &sink_data, (sink_master->flags & (PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY))
+                                               | (u->use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0));
+    pa_sink_new_data_done(&sink_data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg_cb;
+    u->sink->set_state = sink_set_state_cb;
+    u->sink->update_requested_latency = sink_update_requested_latency_cb;
+    u->sink->request_rewind = sink_request_rewind_cb;
+    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
+    if (!u->use_volume_sharing) {
+        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
+        pa_sink_enable_decibel_volume(u->sink, true);
+    }
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, sink_master->asyncmsgq);
+
+    /* Create source output */
+    pa_source_output_new_data_init(&source_output_data);
+    source_output_data.driver = __FILE__;
+    source_output_data.module = m;
+    pa_source_output_new_data_set_source(&source_output_data, source_master, false);
+    source_output_data.destination_source = u->source;
+
+    pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Source Stream");
+    pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_source_output_new_data_set_sample_spec(&source_output_data, &source_output_ss);
+    pa_source_output_new_data_set_channel_map(&source_output_data, &source_output_map);
+    source_output_data.flags |= PA_SOURCE_OUTPUT_START_CORKED;
+
+    if (autoloaded)
+        source_output_data.flags |= PA_SOURCE_OUTPUT_DONT_MOVE;
+
+    pa_source_output_new(&u->source_output, m->core, &source_output_data);
+    pa_source_output_new_data_done(&source_output_data);
+
+    if (!u->source_output)
+        goto fail;
+
+    u->source_output->parent.process_msg = source_output_process_msg_cb;
+    u->source_output->push = source_output_push_cb;
+    u->source_output->process_rewind = source_output_process_rewind_cb;
+    u->source_output->update_max_rewind = source_output_update_max_rewind_cb;
+    u->source_output->update_source_requested_latency = source_output_update_source_requested_latency_cb;
+    u->source_output->update_source_latency_range = source_output_update_source_latency_range_cb;
+    u->source_output->update_source_fixed_latency = source_output_update_source_fixed_latency_cb;
+    u->source_output->kill = source_output_kill_cb;
+    u->source_output->attach = source_output_attach_cb;
+    u->source_output->detach = source_output_detach_cb;
+    u->source_output->state_change = source_output_state_change_cb;
+    u->source_output->may_move_to = source_output_may_move_to_cb;
+    u->source_output->moving = source_output_moving_cb;
+    u->source_output->userdata = u;
+
+    u->source->output_from_master = u->source_output;
+
+    /* Create sink input */
+    pa_sink_input_new_data_init(&sink_input_data);
+    sink_input_data.driver = __FILE__;
+    sink_input_data.module = m;
+    pa_sink_input_new_data_set_sink(&sink_input_data, sink_master, false);
+    sink_input_data.origin_sink = u->sink;
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Sink Stream");
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &sink_ss);
+    pa_sink_input_new_data_set_channel_map(&sink_input_data, &sink_map);
+    sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE | PA_SINK_INPUT_START_CORKED;
+
+    if (autoloaded)
+        sink_input_data.flags |= PA_SINK_INPUT_DONT_MOVE;
+
+    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
+    pa_sink_input_new_data_done(&sink_input_data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->parent.process_msg = sink_input_process_msg_cb;
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->update_max_request = sink_input_update_max_request_cb;
+    u->sink_input->update_sink_requested_latency = sink_input_update_sink_requested_latency_cb;
+    u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+    u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->attach = sink_input_attach_cb;
+    u->sink_input->detach = sink_input_detach_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
+    u->sink_input->may_move_to = sink_input_may_move_to_cb;
+    u->sink_input->moving = sink_input_moving_cb;
+    if (!u->use_volume_sharing)
+        u->sink_input->volume_changed = sink_input_volume_changed_cb;
+    u->sink_input->mute_changed = sink_input_mute_changed_cb;
+    u->sink_input->userdata = u;
+
+    u->sink->input_to_master = u->sink_input;
+
+    pa_sink_input_get_silence(u->sink_input, &silence);
+
+    u->source_memblockq = pa_memblockq_new("module-echo-cancel source_memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0,
+        &source_output_ss, 1, 1, 0, &silence);
+    u->sink_memblockq = pa_memblockq_new("module-echo-cancel sink_memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0,
+        &sink_ss, 0, 1, 0, &silence);
+
+    pa_memblock_unref(silence.memblock);
+
+    if (!u->source_memblockq || !u->sink_memblockq) {
+        pa_log("Failed to create memblockq.");
+        goto fail;
+    }
+
+    if (u->adjust_time > 0 && !u->ec->params.drift_compensation)
+        u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time, time_callback, u);
+    else if (u->ec->params.drift_compensation) {
+        pa_log_info("Canceller does drift compensation -- built-in compensation will be disabled");
+        u->adjust_time = 0;
+        /* Perform resync just once to give the canceller a leg up */
+        pa_atomic_store(&u->request_resync, 1);
+    }
+
+    if (u->save_aec) {
+        pa_log("Creating AEC files in /tmp");
+        u->captured_file = fopen("/tmp/aec_rec.sw", "wb");
+        if (u->captured_file == NULL)
+            perror ("fopen failed");
+        u->played_file = fopen("/tmp/aec_play.sw", "wb");
+        if (u->played_file == NULL)
+            perror ("fopen failed");
+        u->canceled_file = fopen("/tmp/aec_out.sw", "wb");
+        if (u->canceled_file == NULL)
+            perror ("fopen failed");
+        if (u->ec->params.drift_compensation) {
+            u->drift_file = fopen("/tmp/aec_drift.txt", "w");
+            if (u->drift_file == NULL)
+                perror ("fopen failed");
+        }
+    }
+
+    u->ec->msg = pa_msgobject_new(pa_echo_canceller_msg);
+    u->ec->msg->parent.process_msg = canceller_process_msg_cb;
+    u->ec->msg->userdata = u;
+
+    u->thread_info.current_volume = u->source->reference_volume;
+
+    /* We don't want to deal with too many chunks at a time */
+    blocksize_usec = pa_bytes_to_usec(u->source_blocksize, &u->source->sample_spec);
+    if (u->source->flags & PA_SOURCE_DYNAMIC_LATENCY)
+        pa_source_set_latency_range(u->source, blocksize_usec, blocksize_usec * MAX_LATENCY_BLOCKS);
+    pa_source_output_set_requested_latency(u->source_output, blocksize_usec * MAX_LATENCY_BLOCKS);
+
+    blocksize_usec = pa_bytes_to_usec(u->sink_blocksize, &u->sink->sample_spec);
+    if (u->sink->flags & PA_SINK_DYNAMIC_LATENCY)
+        pa_sink_set_latency_range(u->sink, blocksize_usec, blocksize_usec * MAX_LATENCY_BLOCKS);
+    pa_sink_input_set_requested_latency(u->sink_input, blocksize_usec * MAX_LATENCY_BLOCKS);
+
+    /* The order here is important. The input/output must be put first,
+     * otherwise streams might attach to the sink/source before the
+     * sink input or source output is attached to the master. */
+    pa_sink_input_put(u->sink_input);
+    pa_source_output_put(u->source_output);
+
+    pa_sink_put(u->sink);
+    pa_source_put(u->source);
+
+    pa_source_output_cork(u->source_output, false);
+    pa_sink_input_cork(u->sink_input, false);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+/* Called from main context. */
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_sink_linked_by(u->sink) + pa_source_linked_by(u->source);
+}
+
+/* Called from main context. */
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    u->dead = true;
+
+    /* See comments in source_output_kill_cb() above regarding
+     * destruction order! */
+
+    if (u->time_event)
+        u->core->mainloop->time_free(u->time_event);
+
+    if (u->source_output)
+        pa_source_output_cork(u->source_output, true);
+    if (u->sink_input)
+        pa_sink_input_cork(u->sink_input, true);
+
+    if (u->source)
+        pa_source_unlink(u->source);
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->source_output) {
+        pa_source_output_unlink(u->source_output);
+        pa_source_output_unref(u->source_output);
+    }
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+    }
+
+    if (u->source)
+        pa_source_unref(u->source);
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->source_memblockq)
+        pa_memblockq_free(u->source_memblockq);
+    if (u->sink_memblockq)
+        pa_memblockq_free(u->sink_memblockq);
+
+    if (u->ec) {
+        if (u->ec->done)
+            u->ec->done(u->ec);
+
+        pa_xfree(u->ec);
+    }
+
+    if (u->asyncmsgq)
+        pa_asyncmsgq_unref(u->asyncmsgq);
+
+    if (u->save_aec) {
+        if (u->played_file)
+            fclose(u->played_file);
+        if (u->captured_file)
+            fclose(u->captured_file);
+        if (u->canceled_file)
+            fclose(u->canceled_file);
+        if (u->drift_file)
+            fclose(u->drift_file);
+    }
+
+    pa_xfree(u);
+}
+
+#ifdef ECHO_CANCEL_TEST
+/*
+ * Stand-alone test program for running in the canceller on pre-recorded files.
+ */
+int main(int argc, char* argv[]) {
+    struct userdata u;
+    pa_sample_spec source_output_ss, source_ss, sink_ss;
+    pa_channel_map source_output_map, source_map, sink_map;
+    pa_modargs *ma = NULL;
+    uint8_t *rdata = NULL, *pdata = NULL, *cdata = NULL;
+    int unused PA_GCC_UNUSED;
+    int ret = 0, i;
+    char c;
+    float drift;
+    uint32_t nframes;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    pa_memzero(&u, sizeof(u));
+
+    if (argc < 4 || argc > 7) {
+        goto usage;
+    }
+
+    u.captured_file = fopen(argv[2], "rb");
+    if (u.captured_file == NULL) {
+        perror ("Could not open capture file");
+        goto fail;
+    }
+    u.played_file = fopen(argv[1], "rb");
+    if (u.played_file == NULL) {
+        perror ("Could not open play file");
+        goto fail;
+    }
+    u.canceled_file = fopen(argv[3], "wb");
+    if (u.canceled_file == NULL) {
+        perror ("Could not open canceled file");
+        goto fail;
+    }
+
+    u.core = pa_xnew0(pa_core, 1);
+    u.core->cpu_info.cpu_type = PA_CPU_X86;
+    u.core->cpu_info.flags.x86 |= PA_CPU_X86_SSE;
+
+    if (!(ma = pa_modargs_new(argc > 4 ? argv[4] : NULL, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    source_ss.format = PA_SAMPLE_FLOAT32LE;
+    source_ss.rate = DEFAULT_RATE;
+    source_ss.channels = DEFAULT_CHANNELS;
+    pa_channel_map_init_auto(&source_map, source_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+    sink_ss.format = PA_SAMPLE_FLOAT32LE;
+    sink_ss.rate = DEFAULT_RATE;
+    sink_ss.channels = DEFAULT_CHANNELS;
+    pa_channel_map_init_auto(&sink_map, sink_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+    if (init_common(ma, &u, &source_ss, &source_map) < 0)
+        goto fail;
+
+    source_output_ss = source_ss;
+    source_output_map = source_map;
+
+    if (!u.ec->init(u.core, u.ec, &source_output_ss, &source_output_map, &sink_ss, &sink_map, &source_ss, &source_map, &nframes,
+                     pa_modargs_get_value(ma, "aec_args", NULL))) {
+        pa_log("Failed to init AEC engine");
+        goto fail;
+    }
+    u.source_output_blocksize = nframes * pa_frame_size(&source_output_ss);
+    u.source_blocksize = nframes * pa_frame_size(&source_ss);
+    u.sink_blocksize = nframes * pa_frame_size(&sink_ss);
+
+    if (u.ec->params.drift_compensation) {
+        if (argc < 6) {
+            pa_log("Drift compensation enabled but drift file not specified");
+            goto fail;
+        }
+
+        u.drift_file = fopen(argv[5], "rt");
+
+        if (u.drift_file == NULL) {
+            perror ("Could not open drift file");
+            goto fail;
+        }
+    }
+
+    rdata = pa_xmalloc(u.source_output_blocksize);
+    pdata = pa_xmalloc(u.sink_blocksize);
+    cdata = pa_xmalloc(u.source_blocksize);
+
+    if (!u.ec->params.drift_compensation) {
+        while (fread(rdata, u.source_output_blocksize, 1, u.captured_file) > 0) {
+            if (fread(pdata, u.sink_blocksize, 1, u.played_file) == 0) {
+                perror("Played file ended before captured file");
+                goto fail;
+            }
+
+            u.ec->run(u.ec, rdata, pdata, cdata);
+
+            unused = fwrite(cdata, u.source_blocksize, 1, u.canceled_file);
+        }
+    } else {
+        while (fscanf(u.drift_file, "%c", &c) > 0) {
+            switch (c) {
+                case 'd':
+                    if (!fscanf(u.drift_file, "%a", &drift)) {
+                        perror("Drift file incomplete");
+                        goto fail;
+                    }
+
+                    u.ec->set_drift(u.ec, drift);
+
+                    break;
+
+                case 'c':
+                    if (!fscanf(u.drift_file, "%d", &i)) {
+                        perror("Drift file incomplete");
+                        goto fail;
+                    }
+
+                    if (fread(rdata, i, 1, u.captured_file) <= 0) {
+                        perror("Captured file ended prematurely");
+                        goto fail;
+                    }
+
+                    u.ec->record(u.ec, rdata, cdata);
+
+                    unused = fwrite(cdata, i, 1, u.canceled_file);
+
+                    break;
+
+                case 'p':
+                    if (!fscanf(u.drift_file, "%d", &i)) {
+                        perror("Drift file incomplete");
+                        goto fail;
+                    }
+
+                    if (fread(pdata, i, 1, u.played_file) <= 0) {
+                        perror("Played file ended prematurely");
+                        goto fail;
+                    }
+
+                    u.ec->play(u.ec, pdata);
+
+                    break;
+            }
+        }
+
+        if (fread(rdata, i, 1, u.captured_file) > 0)
+            pa_log("All capture data was not consumed");
+        if (fread(pdata, i, 1, u.played_file) > 0)
+            pa_log("All playback data was not consumed");
+    }
+
+    u.ec->done(u.ec);
+
+out:
+    if (u.captured_file)
+        fclose(u.captured_file);
+    if (u.played_file)
+        fclose(u.played_file);
+    if (u.canceled_file)
+        fclose(u.canceled_file);
+    if (u.drift_file)
+        fclose(u.drift_file);
+
+    pa_xfree(rdata);
+    pa_xfree(pdata);
+    pa_xfree(cdata);
+
+    pa_xfree(u.ec);
+    pa_xfree(u.core);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return ret;
+
+usage:
+    pa_log("Usage: %s play_file rec_file out_file [module args] [drift_file]", argv[0]);
+
+fail:
+    ret = -1;
+    goto out;
+}
+#endif /* ECHO_CANCEL_TEST */
diff --git a/src/modules/echo-cancel/null.c b/src/modules/echo-cancel/null.c
new file mode 100644 (file)
index 0000000..c8ecf27
--- /dev/null
@@ -0,0 +1,56 @@
+/***
+    Copyright 2012 Peter Meerwald <p.meerwald@bct-electronic.com>
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/cdecl.h>
+
+PA_C_DECL_BEGIN
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include "echo-cancel.h"
+PA_C_DECL_END
+
+bool pa_null_ec_init(pa_core *c, pa_echo_canceller *ec,
+                     pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+                     pa_sample_spec *play_ss, pa_channel_map *play_map,
+                     pa_sample_spec *out_ss, pa_channel_map *out_map,
+                     uint32_t *nframes, const char *args) {
+    char strss_source[PA_SAMPLE_SPEC_SNPRINT_MAX];
+    char strss_sink[PA_SAMPLE_SPEC_SNPRINT_MAX];
+
+    *nframes = 256;
+    ec->params.null.out_ss = *out_ss;
+
+    *rec_ss = *out_ss;
+    *rec_map = *out_map;
+
+    pa_log_debug("null AEC: nframes=%u, sample spec source=%s, sample spec sink=%s", *nframes,
+                 pa_sample_spec_snprint(strss_source, sizeof(strss_source), out_ss),
+                 pa_sample_spec_snprint(strss_sink, sizeof(strss_sink), play_ss));
+
+    return true;
+}
+
+void pa_null_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
+    /* The null implementation simply copies the recorded buffer to the output
+       buffer and ignores the play buffer. */
+    memcpy(out, rec, 256 * pa_frame_size(&ec->params.null.out_ss));
+}
+
+void pa_null_ec_done(pa_echo_canceller *ec) {
+}
diff --git a/src/modules/echo-cancel/speex.c b/src/modules/echo-cancel/speex.c
new file mode 100644 (file)
index 0000000..a3ae646
--- /dev/null
@@ -0,0 +1,225 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2010 Wim Taymans <wim.taymans@gmail.com>
+
+    Contributor: Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include "echo-cancel.h"
+
+/* should be between 10-20 ms */
+#define DEFAULT_FRAME_SIZE_MS 20
+/* should be between 100-500 ms */
+#define DEFAULT_FILTER_SIZE_MS 200
+#define DEFAULT_AGC_ENABLED true
+#define DEFAULT_DENOISE_ENABLED true
+#define DEFAULT_ECHO_SUPPRESS_ENABLED true
+#define DEFAULT_ECHO_SUPPRESS_ATTENUATION 0
+
+static const char* const valid_modargs[] = {
+    "frame_size_ms",
+    "filter_size_ms",
+    "agc",
+    "denoise",
+    "echo_suppress",
+    "echo_suppress_attenuation",
+    "echo_suppress_attenuation_active",
+    NULL
+};
+
+static void speex_ec_fixate_spec(pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+                                 pa_sample_spec *play_ss, pa_channel_map *play_map,
+                                 pa_sample_spec *out_ss, pa_channel_map *out_map) {
+    out_ss->format = PA_SAMPLE_S16NE;
+
+    *play_ss = *out_ss;
+    *play_map = *out_map;
+    *rec_ss = *out_ss;
+    *rec_map = *out_map;
+}
+
+static bool pa_speex_ec_preprocessor_init(pa_echo_canceller *ec, pa_sample_spec *out_ss, uint32_t nframes, pa_modargs *ma) {
+    bool agc;
+    bool denoise;
+    bool echo_suppress;
+    int32_t echo_suppress_attenuation;
+    int32_t echo_suppress_attenuation_active;
+
+    agc = DEFAULT_AGC_ENABLED;
+    if (pa_modargs_get_value_boolean(ma, "agc", &agc) < 0) {
+        pa_log("Failed to parse agc value");
+        goto fail;
+    }
+
+    denoise = DEFAULT_DENOISE_ENABLED;
+    if (pa_modargs_get_value_boolean(ma, "denoise", &denoise) < 0) {
+        pa_log("Failed to parse denoise value");
+        goto fail;
+    }
+
+    echo_suppress = DEFAULT_ECHO_SUPPRESS_ENABLED;
+    if (pa_modargs_get_value_boolean(ma, "echo_suppress", &echo_suppress) < 0) {
+        pa_log("Failed to parse echo_suppress value");
+        goto fail;
+    }
+
+    echo_suppress_attenuation = DEFAULT_ECHO_SUPPRESS_ATTENUATION;
+    if (pa_modargs_get_value_s32(ma, "echo_suppress_attenuation", &echo_suppress_attenuation) < 0) {
+        pa_log("Failed to parse echo_suppress_attenuation value");
+        goto fail;
+    }
+    if (echo_suppress_attenuation > 0) {
+        pa_log("echo_suppress_attenuation should be a negative dB value");
+        goto fail;
+    }
+
+    echo_suppress_attenuation_active = DEFAULT_ECHO_SUPPRESS_ATTENUATION;
+    if (pa_modargs_get_value_s32(ma, "echo_suppress_attenuation_active", &echo_suppress_attenuation_active) < 0) {
+        pa_log("Failed to parse echo_suppress_attenuation_active value");
+        goto fail;
+    }
+    if (echo_suppress_attenuation_active > 0) {
+        pa_log("echo_suppress_attenuation_active should be a negative dB value");
+        goto fail;
+    }
+
+    if (agc || denoise || echo_suppress) {
+        spx_int32_t tmp;
+
+        if (out_ss->channels != 1) {
+            pa_log("AGC, denoising and echo suppression only work with channels=1");
+            goto fail;
+        }
+
+        ec->params.speex.pp_state = speex_preprocess_state_init(nframes, out_ss->rate);
+
+        tmp = agc;
+        speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_AGC, &tmp);
+
+        tmp = denoise;
+        speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_DENOISE, &tmp);
+
+        if (echo_suppress) {
+            if (echo_suppress_attenuation)
+                speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS,
+                                     &echo_suppress_attenuation);
+
+            if (echo_suppress_attenuation_active) {
+                speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE,
+                                     &echo_suppress_attenuation_active);
+            }
+
+            speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_STATE,
+                                 ec->params.speex.state);
+        }
+
+        pa_log_info("Loaded speex preprocessor with params: agc=%s, denoise=%s, echo_suppress=%s", pa_yes_no(agc),
+                    pa_yes_no(denoise), pa_yes_no(echo_suppress));
+    } else
+        pa_log_info("All preprocessing options are disabled");
+
+    return true;
+
+fail:
+    return false;
+}
+
+bool pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec,
+                      pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+                      pa_sample_spec *play_ss, pa_channel_map *play_map,
+                      pa_sample_spec *out_ss, pa_channel_map *out_map,
+                      uint32_t *nframes, const char *args) {
+    int rate;
+    uint32_t frame_size_ms, filter_size_ms;
+    pa_modargs *ma;
+
+    if (!(ma = pa_modargs_new(args, valid_modargs))) {
+        pa_log("Failed to parse submodule arguments.");
+        goto fail;
+    }
+
+    filter_size_ms = DEFAULT_FILTER_SIZE_MS;
+    if (pa_modargs_get_value_u32(ma, "filter_size_ms", &filter_size_ms) < 0 || filter_size_ms < 1 || filter_size_ms > 2000) {
+        pa_log("Invalid filter_size_ms specification");
+        goto fail;
+    }
+
+    frame_size_ms = DEFAULT_FRAME_SIZE_MS;
+    if (pa_modargs_get_value_u32(ma, "frame_size_ms", &frame_size_ms) < 0 || frame_size_ms < 1 || frame_size_ms > 200) {
+        pa_log("Invalid frame_size_ms specification");
+        goto fail;
+    }
+
+    speex_ec_fixate_spec(rec_ss, rec_map, play_ss, play_map, out_ss, out_map);
+
+    rate = out_ss->rate;
+    *nframes = pa_echo_canceller_blocksize_power2(rate, frame_size_ms);
+
+    pa_log_debug ("Using nframes %d, channels %d, rate %d", *nframes, out_ss->channels, out_ss->rate);
+    ec->params.speex.state = speex_echo_state_init_mc(*nframes, (rate * filter_size_ms) / 1000, out_ss->channels, out_ss->channels);
+
+    if (!ec->params.speex.state)
+        goto fail;
+
+    speex_echo_ctl(ec->params.speex.state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate);
+
+    if (!pa_speex_ec_preprocessor_init(ec, out_ss, *nframes, ma))
+        goto fail;
+
+    pa_modargs_free(ma);
+    return true;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+    if (ec->params.speex.pp_state) {
+        speex_preprocess_state_destroy(ec->params.speex.pp_state);
+        ec->params.speex.pp_state = NULL;
+    }
+    if (ec->params.speex.state) {
+        speex_echo_state_destroy(ec->params.speex.state);
+        ec->params.speex.state = NULL;
+    }
+    return false;
+}
+
+void pa_speex_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
+    speex_echo_cancellation(ec->params.speex.state, (const spx_int16_t *) rec, (const spx_int16_t *) play,
+                            (spx_int16_t *) out);
+
+    /* preprecessor is run after AEC. This is not a mistake! */
+    if (ec->params.speex.pp_state)
+        speex_preprocess_run(ec->params.speex.pp_state, (spx_int16_t *) out);
+}
+
+void pa_speex_ec_done(pa_echo_canceller *ec) {
+    if (ec->params.speex.pp_state) {
+        speex_preprocess_state_destroy(ec->params.speex.pp_state);
+        ec->params.speex.pp_state = NULL;
+    }
+
+    if (ec->params.speex.state) {
+        speex_echo_state_destroy(ec->params.speex.state);
+        ec->params.speex.state = NULL;
+    }
+}
diff --git a/src/modules/echo-cancel/webrtc.cc b/src/modules/echo-cancel/webrtc.cc
new file mode 100644 (file)
index 0000000..aadb1af
--- /dev/null
@@ -0,0 +1,594 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2011 Collabora Ltd.
+              2015 Aldebaran SoftBank Group
+
+    Contributor: Arun Raghavan <mail@arunraghavan.net>
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/cdecl.h>
+
+PA_C_DECL_BEGIN
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+
+#include <pulse/timeval.h>
+#include "echo-cancel.h"
+PA_C_DECL_END
+
+#include <webrtc/modules/audio_processing/include/audio_processing.h>
+#include <webrtc/modules/interface/module_common_types.h>
+#include <webrtc/system_wrappers/include/trace.h>
+
+#define BLOCK_SIZE_US 10000
+
+#define DEFAULT_HIGH_PASS_FILTER true
+#define DEFAULT_NOISE_SUPPRESSION true
+#define DEFAULT_ANALOG_GAIN_CONTROL true
+#define DEFAULT_DIGITAL_GAIN_CONTROL false
+#define DEFAULT_MOBILE false
+#define DEFAULT_ROUTING_MODE "speakerphone"
+#define DEFAULT_COMFORT_NOISE true
+#define DEFAULT_DRIFT_COMPENSATION false
+#define DEFAULT_VAD true
+#define DEFAULT_EXTENDED_FILTER false
+#define DEFAULT_INTELLIGIBILITY_ENHANCER false
+#define DEFAULT_EXPERIMENTAL_AGC false
+#define DEFAULT_AGC_START_VOLUME 85
+#define DEFAULT_BEAMFORMING false
+#define DEFAULT_TRACE false
+
+#define WEBRTC_AGC_MAX_VOLUME 255
+
+static const char* const valid_modargs[] = {
+    "high_pass_filter",
+    "noise_suppression",
+    "analog_gain_control",
+    "digital_gain_control",
+    "mobile",
+    "routing_mode",
+    "comfort_noise",
+    "drift_compensation",
+    "voice_detection",
+    "extended_filter",
+    "intelligibility_enhancer",
+    "experimental_agc",
+    "agc_start_volume",
+    "beamforming",
+    "mic_geometry", /* documented in parse_mic_geometry() */
+    "target_direction", /* documented in parse_mic_geometry() */
+    "trace",
+    NULL
+};
+
+static int routing_mode_from_string(const char *rmode) {
+    if (pa_streq(rmode, "quiet-earpiece-or-headset"))
+        return webrtc::EchoControlMobile::kQuietEarpieceOrHeadset;
+    else if (pa_streq(rmode, "earpiece"))
+        return webrtc::EchoControlMobile::kEarpiece;
+    else if (pa_streq(rmode, "loud-earpiece"))
+        return webrtc::EchoControlMobile::kLoudEarpiece;
+    else if (pa_streq(rmode, "speakerphone"))
+        return webrtc::EchoControlMobile::kSpeakerphone;
+    else if (pa_streq(rmode, "loud-speakerphone"))
+        return webrtc::EchoControlMobile::kLoudSpeakerphone;
+    else
+        return -1;
+}
+
+class PaWebrtcTraceCallback : public webrtc::TraceCallback {
+    void Print(webrtc::TraceLevel level, const char *message, int length)
+    {
+        if (level & webrtc::kTraceError || level & webrtc::kTraceCritical)
+            pa_log(message);
+        else if (level & webrtc::kTraceWarning)
+            pa_log_warn(message);
+        else if (level & webrtc::kTraceInfo)
+            pa_log_info(message);
+        else
+            pa_log_debug(message);
+    }
+};
+
+static int webrtc_volume_from_pa(pa_volume_t v)
+{
+    return (v * WEBRTC_AGC_MAX_VOLUME) / PA_VOLUME_NORM;
+}
+
+static pa_volume_t webrtc_volume_to_pa(int v)
+{
+    return (v * PA_VOLUME_NORM) / WEBRTC_AGC_MAX_VOLUME;
+}
+
+static void webrtc_ec_fixate_spec(pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+                                  pa_sample_spec *play_ss, pa_channel_map *play_map,
+                                  pa_sample_spec *out_ss, pa_channel_map *out_map,
+                                  bool beamforming)
+{
+    rec_ss->format = PA_SAMPLE_FLOAT32NE;
+    play_ss->format = PA_SAMPLE_FLOAT32NE;
+
+    /* AudioProcessing expects one of the following rates */
+    if (rec_ss->rate >= 48000)
+        rec_ss->rate = 48000;
+    else if (rec_ss->rate >= 32000)
+        rec_ss->rate = 32000;
+    else if (rec_ss->rate >= 16000)
+        rec_ss->rate = 16000;
+    else
+        rec_ss->rate = 8000;
+
+    *out_ss = *rec_ss;
+    *out_map = *rec_map;
+
+    if (beamforming) {
+        /* The beamformer gives us a single channel */
+        out_ss->channels = 1;
+        pa_channel_map_init_mono(out_map);
+    }
+
+    /* Playback stream rate needs to be the same as capture */
+    play_ss->rate = rec_ss->rate;
+}
+
+static bool parse_point(const char **point, float (&f)[3]) {
+    int ret, length;
+
+    ret = sscanf(*point, "%g,%g,%g%n", &f[0], &f[1], &f[2], &length);
+    if (ret != 3)
+        return false;
+
+    /* Consume the bytes we've read so far */
+    *point += length;
+
+    return true;
+}
+
+static bool parse_mic_geometry(const char **mic_geometry, std::vector<webrtc::Point>& geometry) {
+    /* The microphone geometry is expressed as cartesian point form:
+     *   x1,y1,z1,x2,y2,z2,...
+     *
+     * Where x1,y1,z1 is the position of the first microphone with regards to
+     * the array's "center", x2,y2,z2 the position of the second, and so on.
+     *
+     * 'x' is the horizontal coordinate, with positive values being to the
+     * right from the mic array's perspective.
+     *
+     * 'y' is the depth coordinate, with positive values being in front of the
+     * array.
+     *
+     * 'z' is the vertical coordinate, with positive values being above the
+     * array.
+     *
+     * All distances are in meters.
+     */
+
+    /* The target direction is expected to be in spherical point form:
+     *   a,e,r
+     *
+     * Where 'a' is the azimuth of the target point relative to the center of
+     * the array, 'e' its elevation, and 'r' the radius.
+     *
+     * 0 radians azimuth is to the right of the array, and positive angles
+     * move in a counter-clockwise direction.
+     *
+     * 0 radians elevation is horizontal w.r.t. the array, and positive
+     * angles go upwards.
+     *
+     * radius is distance from the array center in meters.
+     */
+
+    int i;
+    float f[3];
+
+    for (i = 0; i < geometry.size(); i++) {
+        if (!parse_point(mic_geometry, f)) {
+            pa_log("Failed to parse channel %d in mic_geometry", i);
+            return false;
+        }
+
+        /* Except for the last point, we should have a trailing comma */
+        if (i != geometry.size() - 1) {
+            if (**mic_geometry != ',') {
+                pa_log("Failed to parse channel %d in mic_geometry", i);
+                return false;
+            }
+
+            (*mic_geometry)++;
+        }
+
+        pa_log_debug("Got mic #%d position: (%g, %g, %g)", i, f[0], f[1], f[2]);
+
+        geometry[i].c[0] = f[0];
+        geometry[i].c[1] = f[1];
+        geometry[i].c[2] = f[2];
+    }
+
+    if (**mic_geometry != '\0') {
+        pa_log("Failed to parse mic_geometry value: more parameters than expected");
+        return false;
+    }
+
+    return true;
+}
+
+bool pa_webrtc_ec_init(pa_core *c, pa_echo_canceller *ec,
+                       pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+                       pa_sample_spec *play_ss, pa_channel_map *play_map,
+                       pa_sample_spec *out_ss, pa_channel_map *out_map,
+                       uint32_t *nframes, const char *args) {
+    webrtc::AudioProcessing *apm = NULL;
+    webrtc::ProcessingConfig pconfig;
+    webrtc::Config config;
+    bool hpf, ns, agc, dgc, mobile, cn, vad, ext_filter, intelligibility, experimental_agc, beamforming;
+    int rm = -1, i;
+    uint32_t agc_start_volume;
+    pa_modargs *ma;
+    bool trace = false;
+
+    if (!(ma = pa_modargs_new(args, valid_modargs))) {
+        pa_log("Failed to parse submodule arguments.");
+        goto fail;
+    }
+
+    hpf = DEFAULT_HIGH_PASS_FILTER;
+    if (pa_modargs_get_value_boolean(ma, "high_pass_filter", &hpf) < 0) {
+        pa_log("Failed to parse high_pass_filter value");
+        goto fail;
+    }
+
+    ns = DEFAULT_NOISE_SUPPRESSION;
+    if (pa_modargs_get_value_boolean(ma, "noise_suppression", &ns) < 0) {
+        pa_log("Failed to parse noise_suppression value");
+        goto fail;
+    }
+
+    agc = DEFAULT_ANALOG_GAIN_CONTROL;
+    if (pa_modargs_get_value_boolean(ma, "analog_gain_control", &agc) < 0) {
+        pa_log("Failed to parse analog_gain_control value");
+        goto fail;
+    }
+
+    dgc = agc ? false : DEFAULT_DIGITAL_GAIN_CONTROL;
+    if (pa_modargs_get_value_boolean(ma, "digital_gain_control", &dgc) < 0) {
+        pa_log("Failed to parse digital_gain_control value");
+        goto fail;
+    }
+
+    if (agc && dgc) {
+        pa_log("You must pick only one between analog and digital gain control");
+        goto fail;
+    }
+
+    mobile = DEFAULT_MOBILE;
+    if (pa_modargs_get_value_boolean(ma, "mobile", &mobile) < 0) {
+        pa_log("Failed to parse mobile value");
+        goto fail;
+    }
+
+    ec->params.drift_compensation = DEFAULT_DRIFT_COMPENSATION;
+    if (pa_modargs_get_value_boolean(ma, "drift_compensation", &ec->params.drift_compensation) < 0) {
+        pa_log("Failed to parse drift_compensation value");
+        goto fail;
+    }
+
+    if (mobile) {
+        if (ec->params.drift_compensation) {
+            pa_log("Can't use drift_compensation in mobile mode");
+            goto fail;
+        }
+
+        if ((rm = routing_mode_from_string(pa_modargs_get_value(ma, "routing_mode", DEFAULT_ROUTING_MODE))) < 0) {
+            pa_log("Failed to parse routing_mode value");
+            goto fail;
+        }
+
+        cn = DEFAULT_COMFORT_NOISE;
+        if (pa_modargs_get_value_boolean(ma, "comfort_noise", &cn) < 0) {
+            pa_log("Failed to parse cn value");
+            goto fail;
+        }
+    } else {
+        if (pa_modargs_get_value(ma, "comfort_noise", NULL) || pa_modargs_get_value(ma, "routing_mode", NULL)) {
+            pa_log("The routing_mode and comfort_noise options are only valid with mobile=true");
+            goto fail;
+        }
+    }
+
+    vad = DEFAULT_VAD;
+    if (pa_modargs_get_value_boolean(ma, "voice_detection", &vad) < 0) {
+        pa_log("Failed to parse voice_detection value");
+        goto fail;
+    }
+
+    ext_filter = DEFAULT_EXTENDED_FILTER;
+    if (pa_modargs_get_value_boolean(ma, "extended_filter", &ext_filter) < 0) {
+        pa_log("Failed to parse extended_filter value");
+        goto fail;
+    }
+
+    intelligibility = DEFAULT_INTELLIGIBILITY_ENHANCER;
+    if (pa_modargs_get_value_boolean(ma, "intelligibility_enhancer", &intelligibility) < 0) {
+        pa_log("Failed to parse intelligibility_enhancer value");
+        goto fail;
+    }
+
+    experimental_agc = DEFAULT_EXPERIMENTAL_AGC;
+    if (pa_modargs_get_value_boolean(ma, "experimental_agc", &experimental_agc) < 0) {
+        pa_log("Failed to parse experimental_agc value");
+        goto fail;
+    }
+
+    agc_start_volume = DEFAULT_AGC_START_VOLUME;
+    if (pa_modargs_get_value_u32(ma, "agc_start_volume", &agc_start_volume) < 0) {
+        pa_log("Failed to parse agc_start_volume value");
+        goto fail;
+    }
+    if (agc_start_volume > WEBRTC_AGC_MAX_VOLUME) {
+        pa_log("AGC start volume must not exceed %u", WEBRTC_AGC_MAX_VOLUME);
+        goto fail;
+    }
+    ec->params.webrtc.agc_start_volume = agc_start_volume;
+
+    beamforming = DEFAULT_BEAMFORMING;
+    if (pa_modargs_get_value_boolean(ma, "beamforming", &beamforming) < 0) {
+        pa_log("Failed to parse beamforming value");
+        goto fail;
+    }
+
+    if (ext_filter)
+        config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(true));
+    if (intelligibility)
+        pa_log_warn("The intelligibility enhancer is not currently supported");
+    if (experimental_agc)
+        config.Set<webrtc::ExperimentalAgc>(new webrtc::ExperimentalAgc(true, ec->params.webrtc.agc_start_volume));
+
+    trace = DEFAULT_TRACE;
+    if (pa_modargs_get_value_boolean(ma, "trace", &trace) < 0) {
+        pa_log("Failed to parse trace value");
+        goto fail;
+    }
+
+    if (trace) {
+        webrtc::Trace::CreateTrace();
+        webrtc::Trace::set_level_filter(webrtc::kTraceAll);
+        ec->params.webrtc.trace_callback = new PaWebrtcTraceCallback();
+        webrtc::Trace::SetTraceCallback((PaWebrtcTraceCallback *) ec->params.webrtc.trace_callback);
+    }
+
+    webrtc_ec_fixate_spec(rec_ss, rec_map, play_ss, play_map, out_ss, out_map, beamforming);
+
+    /* We do this after fixate because we need the capture channel count */
+    if (beamforming) {
+        std::vector<webrtc::Point> geometry(rec_ss->channels);
+        webrtc::SphericalPointf direction(0.0f, 0.0f, 0.0f);
+        const char *mic_geometry, *target_direction;
+
+        if (!(mic_geometry = pa_modargs_get_value(ma, "mic_geometry", NULL))) {
+            pa_log("mic_geometry must be set if beamforming is enabled");
+            goto fail;
+        }
+
+        if (!parse_mic_geometry(&mic_geometry, geometry)) {
+            pa_log("Failed to parse mic_geometry value");
+            goto fail;
+        }
+
+        if ((target_direction = pa_modargs_get_value(ma, "target_direction", NULL))) {
+            float f[3];
+
+            if (!parse_point(&target_direction, f)) {
+                pa_log("Failed to parse target_direction value");
+                goto fail;
+            }
+
+            if (*target_direction != '\0') {
+                pa_log("Failed to parse target_direction value: more parameters than expected");
+                goto fail;
+            }
+
+#define IS_ZERO(f) ((f) < 0.000001 && (f) > -0.000001)
+
+            if (!IS_ZERO(f[1]) || !IS_ZERO(f[2])) {
+                pa_log("The beamformer currently only supports targeting along the azimuth");
+                goto fail;
+            }
+
+            direction.s[0] = f[0];
+            direction.s[1] = f[1];
+            direction.s[2] = f[2];
+        }
+
+        if (!target_direction)
+            config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry));
+        else
+            config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry, direction));
+    }
+
+    apm = webrtc::AudioProcessing::Create(config);
+
+    pconfig = {
+        webrtc::StreamConfig(rec_ss->rate, rec_ss->channels, false), /* input stream */
+        webrtc::StreamConfig(out_ss->rate, out_ss->channels, false), /* output stream */
+        webrtc::StreamConfig(play_ss->rate, play_ss->channels, false), /* reverse input stream */
+        webrtc::StreamConfig(play_ss->rate, play_ss->channels, false), /* reverse output stream */
+    };
+    if (apm->Initialize(pconfig) != webrtc::AudioProcessing::kNoError) {
+        pa_log("Error initialising audio processing module");
+        goto fail;
+    }
+
+    if (hpf)
+        apm->high_pass_filter()->Enable(true);
+
+    if (!mobile) {
+        apm->echo_cancellation()->enable_drift_compensation(ec->params.drift_compensation);
+        apm->echo_cancellation()->Enable(true);
+    } else {
+        apm->echo_control_mobile()->set_routing_mode(static_cast<webrtc::EchoControlMobile::RoutingMode>(rm));
+        apm->echo_control_mobile()->enable_comfort_noise(cn);
+        apm->echo_control_mobile()->Enable(true);
+    }
+
+    if (ns) {
+        apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh);
+        apm->noise_suppression()->Enable(true);
+    }
+
+    if (agc || dgc) {
+        if (mobile && rm <= webrtc::EchoControlMobile::kEarpiece) {
+            /* Maybe this should be a knob, but we've got a lot of knobs already */
+            apm->gain_control()->set_mode(webrtc::GainControl::kFixedDigital);
+            ec->params.webrtc.agc = false;
+        } else if (dgc) {
+            apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital);
+            ec->params.webrtc.agc = false;
+        } else {
+            apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveAnalog);
+            if (apm->gain_control()->set_analog_level_limits(0, WEBRTC_AGC_MAX_VOLUME) !=
+                    webrtc::AudioProcessing::kNoError) {
+                pa_log("Failed to initialise AGC");
+                goto fail;
+            }
+            ec->params.webrtc.agc = true;
+        }
+
+        apm->gain_control()->Enable(true);
+    }
+
+    if (vad)
+        apm->voice_detection()->Enable(true);
+
+    ec->params.webrtc.apm = apm;
+    ec->params.webrtc.rec_ss = *rec_ss;
+    ec->params.webrtc.play_ss = *play_ss;
+    ec->params.webrtc.out_ss = *out_ss;
+    ec->params.webrtc.blocksize = (uint64_t) out_ss->rate * BLOCK_SIZE_US / PA_USEC_PER_SEC;
+    *nframes = ec->params.webrtc.blocksize;
+    ec->params.webrtc.first = true;
+
+    for (i = 0; i < rec_ss->channels; i++)
+        ec->params.webrtc.rec_buffer[i] = pa_xnew(float, *nframes);
+    for (i = 0; i < play_ss->channels; i++)
+        ec->params.webrtc.play_buffer[i] = pa_xnew(float, *nframes);
+
+    pa_modargs_free(ma);
+    return true;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+    if (ec->params.webrtc.trace_callback) {
+        webrtc::Trace::ReturnTrace();
+        delete ((PaWebrtcTraceCallback *) ec->params.webrtc.trace_callback);
+    } if (apm)
+        delete apm;
+
+    return false;
+}
+
+void pa_webrtc_ec_play(pa_echo_canceller *ec, const uint8_t *play) {
+    webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.webrtc.apm;
+    const pa_sample_spec *ss = &ec->params.webrtc.play_ss;
+    int n = ec->params.webrtc.blocksize;
+    float **buf = ec->params.webrtc.play_buffer;
+    webrtc::StreamConfig config(ss->rate, ss->channels, false);
+
+    pa_deinterleave(play, (void **) buf, ss->channels, pa_sample_size(ss), n);
+
+    pa_assert_se(apm->ProcessReverseStream(buf, config, config, buf) == webrtc::AudioProcessing::kNoError);
+
+    /* FIXME: If ProcessReverseStream() makes any changes to the audio, such as
+     * applying intelligibility enhancement, those changes don't have any
+     * effect. This function is called at the source side, but the processing
+     * would have to be done in the sink to be able to feed the processed audio
+     * to speakers. */
+}
+
+void pa_webrtc_ec_record(pa_echo_canceller *ec, const uint8_t *rec, uint8_t *out) {
+    webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.webrtc.apm;
+    const pa_sample_spec *rec_ss = &ec->params.webrtc.rec_ss;
+    const pa_sample_spec *out_ss = &ec->params.webrtc.out_ss;
+    float **buf = ec->params.webrtc.rec_buffer;
+    int n = ec->params.webrtc.blocksize;
+    int old_volume, new_volume;
+    webrtc::StreamConfig rec_config(rec_ss->rate, rec_ss->channels, false);
+    webrtc::StreamConfig out_config(out_ss->rate, out_ss->channels, false);
+
+    pa_deinterleave(rec, (void **) buf, rec_ss->channels, pa_sample_size(rec_ss), n);
+
+    if (ec->params.webrtc.agc) {
+        pa_volume_t v = pa_echo_canceller_get_capture_volume(ec);
+        old_volume = webrtc_volume_from_pa(v);
+        apm->gain_control()->set_stream_analog_level(old_volume);
+    }
+
+    apm->set_stream_delay_ms(0);
+    pa_assert_se(apm->ProcessStream(buf, rec_config, out_config, buf) == webrtc::AudioProcessing::kNoError);
+
+    if (ec->params.webrtc.agc) {
+        if (PA_UNLIKELY(ec->params.webrtc.first)) {
+            /* We start at a sane default volume (taken from the Chromium
+             * condition on the experimental AGC in audio_processing.h). This is
+             * needed to make sure that there's enough energy in the capture
+             * signal for the AGC to work */
+            ec->params.webrtc.first = false;
+            new_volume = ec->params.webrtc.agc_start_volume;
+        } else {
+            new_volume = apm->gain_control()->stream_analog_level();
+        }
+
+        if (old_volume != new_volume)
+            pa_echo_canceller_set_capture_volume(ec, webrtc_volume_to_pa(new_volume));
+    }
+
+    pa_interleave((const void **) buf, out_ss->channels, out, pa_sample_size(out_ss), n);
+}
+
+void pa_webrtc_ec_set_drift(pa_echo_canceller *ec, float drift) {
+    webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.webrtc.apm;
+
+    apm->echo_cancellation()->set_stream_drift_samples(drift * ec->params.webrtc.blocksize);
+}
+
+void pa_webrtc_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
+    pa_webrtc_ec_play(ec, play);
+    pa_webrtc_ec_record(ec, rec, out);
+}
+
+void pa_webrtc_ec_done(pa_echo_canceller *ec) {
+    int i;
+
+    if (ec->params.webrtc.trace_callback) {
+        webrtc::Trace::ReturnTrace();
+        delete ((PaWebrtcTraceCallback *) ec->params.webrtc.trace_callback);
+    }
+
+    if (ec->params.webrtc.apm) {
+        delete (webrtc::AudioProcessing*)ec->params.webrtc.apm;
+        ec->params.webrtc.apm = NULL;
+    }
+
+    for (i = 0; i < ec->params.webrtc.rec_ss.channels; i++)
+        pa_xfree(ec->params.webrtc.rec_buffer[i]);
+    for (i = 0; i < ec->params.webrtc.play_ss.channels; i++)
+        pa_xfree(ec->params.webrtc.play_buffer[i]);
+}
diff --git a/src/modules/gconf/gconf-helper.c b/src/modules/gconf/gconf-helper.c
new file mode 100644 (file)
index 0000000..eccd073
--- /dev/null
@@ -0,0 +1,133 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gconf/gconf-client.h>
+#include <glib.h>
+
+#include <pulsecore/core-util.h>
+
+#define PA_GCONF_ROOT "/system/pulseaudio"
+#define PA_GCONF_PATH_MODULES PA_GCONF_ROOT"/modules"
+
+static void handle_module(GConfClient *client, const char *name) {
+    gchar p[1024];
+    gboolean enabled, locked;
+    int i;
+
+    pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/locked", name);
+    locked = gconf_client_get_bool(client, p, FALSE);
+
+    if (locked)
+        return;
+
+    pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/enabled", name);
+    enabled = gconf_client_get_bool(client, p, FALSE);
+
+    printf("%c%s%c", enabled ? '+' : '-', name, 0);
+
+    if (enabled) {
+
+        for (i = 0; i < 10; i++) {
+            gchar *n, *a;
+
+            pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/name%i", name, i);
+            if (!(n = gconf_client_get_string(client, p, NULL)) || !*n)
+                break;
+
+            pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/args%i", name, i);
+            a = gconf_client_get_string(client, p, NULL);
+
+            printf("%s%c%s%c", n, 0, a ? a : "", 0);
+
+            g_free(n);
+            g_free(a);
+        }
+
+        printf("%c", 0);
+    }
+
+    fflush(stdout);
+}
+
+static void modules_callback(
+        GConfClient* client,
+        guint cnxn_id,
+        GConfEntry *entry,
+        gpointer user_data) {
+
+    const char *n;
+    char buf[128];
+
+    g_assert(strncmp(entry->key, PA_GCONF_PATH_MODULES"/", sizeof(PA_GCONF_PATH_MODULES)) == 0);
+
+    n = entry->key + sizeof(PA_GCONF_PATH_MODULES);
+
+    g_strlcpy(buf, n, sizeof(buf));
+    buf[strcspn(buf, "/")] = 0;
+
+    handle_module(client, buf);
+}
+
+int main(int argc, char *argv[]) {
+    GMainLoop *g;
+    GConfClient *client;
+    GSList *modules, *m;
+
+#if !GLIB_CHECK_VERSION(2,36,0)
+    g_type_init();
+#endif
+
+    if (!(client = gconf_client_get_default()))
+        goto fail;
+
+    gconf_client_add_dir(client, PA_GCONF_ROOT, GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);
+    gconf_client_notify_add(client, PA_GCONF_PATH_MODULES, modules_callback, NULL, NULL, NULL);
+
+    modules = gconf_client_all_dirs(client, PA_GCONF_PATH_MODULES, NULL);
+
+    for (m = modules; m; m = m->next) {
+        char *e = strrchr(m->data, '/');
+        handle_module(client, e ? e+1 : m->data);
+    }
+
+    g_slist_free(modules);
+
+    /* Signal the parent that we are now initialized */
+    printf("!");
+    fflush(stdout);
+
+    g = g_main_loop_new(NULL, FALSE);
+    g_main_loop_run(g);
+    g_main_loop_unref(g);
+
+    g_object_unref(G_OBJECT(client));
+
+    return 0;
+
+fail:
+    return 1;
+}
diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c
new file mode 100644 (file)
index 0000000..1e1b855
--- /dev/null
@@ -0,0 +1,400 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulse/mainloop-api.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/start-child.h>
+
+#include "module-gconf-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("GConf Adapter");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+#define MAX_MODULES 10
+#define BUF_MAX 2048
+
+struct userdata;
+
+struct module_item {
+    char *name;
+    char *args;
+    uint32_t index;
+};
+
+struct pa_module_info {
+    struct userdata *userdata;
+    char *name;
+
+    struct module_item items[MAX_MODULES];
+    unsigned n_items;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    pa_hashmap *module_infos;
+
+    pid_t pid;
+
+    int fd;
+    int fd_type;
+    pa_io_event *io_event;
+
+    char buf[BUF_MAX];
+    size_t buf_fill;
+};
+
+static int fill_buf(struct userdata *u) {
+    ssize_t r;
+    pa_assert(u);
+
+    if (u->buf_fill >= BUF_MAX) {
+        pa_log("read buffer overflow");
+        return -1;
+    }
+
+    if ((r = pa_read(u->fd, u->buf + u->buf_fill, BUF_MAX - u->buf_fill, &u->fd_type)) <= 0)
+        return -1;
+
+    u->buf_fill += (size_t) r;
+    return 0;
+}
+
+static int read_byte(struct userdata *u) {
+    int ret;
+    pa_assert(u);
+
+    if (u->buf_fill < 1)
+        if (fill_buf(u) < 0)
+            return -1;
+
+    ret = u->buf[0];
+    pa_assert(u->buf_fill > 0);
+    u->buf_fill--;
+    memmove(u->buf, u->buf+1, u->buf_fill);
+    return ret;
+}
+
+static char *read_string(struct userdata *u) {
+    pa_assert(u);
+
+    for (;;) {
+        char *e;
+
+        if ((e = memchr(u->buf, 0, u->buf_fill))) {
+            char *ret = pa_xstrdup(u->buf);
+            u->buf_fill -= (size_t) (e - u->buf +1);
+            memmove(u->buf, e+1, u->buf_fill);
+            return ret;
+        }
+
+        if (fill_buf(u) < 0)
+            return NULL;
+    }
+}
+
+static void unload_one_module(struct pa_module_info *m, unsigned i) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert(i < m->n_items);
+
+    u = m->userdata;
+
+    if (m->items[i].index == PA_INVALID_INDEX)
+        return;
+
+    pa_log_debug("Unloading module #%i", m->items[i].index);
+    pa_module_unload_by_index(u->core, m->items[i].index, true);
+    m->items[i].index = PA_INVALID_INDEX;
+    pa_xfree(m->items[i].name);
+    pa_xfree(m->items[i].args);
+    m->items[i].name = m->items[i].args = NULL;
+}
+
+static void unload_all_modules(struct pa_module_info *m) {
+    unsigned i;
+
+    pa_assert(m);
+
+    for (i = 0; i < m->n_items; i++)
+        unload_one_module(m, i);
+
+    m->n_items = 0;
+}
+
+static void load_module(
+        struct pa_module_info *m,
+        unsigned i,
+        const char *name,
+        const char *args,
+        bool is_new) {
+
+    struct userdata *u;
+    pa_module *mod;
+
+    pa_assert(m);
+    pa_assert(name);
+    pa_assert(args);
+
+    u = m->userdata;
+
+    if (!is_new) {
+        if (m->items[i].index != PA_INVALID_INDEX &&
+            pa_streq(m->items[i].name, name) &&
+            pa_streq(m->items[i].args, args))
+            return;
+
+        unload_one_module(m, i);
+    }
+
+    pa_log_debug("Loading module '%s' with args '%s' due to GConf configuration.", name, args);
+
+    m->items[i].name = pa_xstrdup(name);
+    m->items[i].args = pa_xstrdup(args);
+    m->items[i].index = PA_INVALID_INDEX;
+
+    if (!(mod = pa_module_load(u->core, name, args))) {
+        pa_log("pa_module_load() failed");
+        return;
+    }
+
+    m->items[i].index = mod->index;
+}
+
+static void module_info_free(void *p) {
+    struct pa_module_info *m = p;
+
+    pa_assert(m);
+
+    unload_all_modules(m);
+    pa_xfree(m->name);
+    pa_xfree(m);
+}
+
+static int handle_event(struct userdata *u) {
+    int opcode;
+    int ret = 0;
+
+    do {
+        if ((opcode = read_byte(u)) < 0) {
+            if (errno == EINTR || errno == EAGAIN)
+                break;
+            goto fail;
+        }
+
+        switch (opcode) {
+            case '!':
+                /* The helper tool is now initialized */
+                ret = 1;
+                break;
+
+            case '+': {
+                char *name;
+                struct pa_module_info *m;
+                unsigned i, j;
+
+                if (!(name = read_string(u)))
+                    goto fail;
+
+                if (!(m = pa_hashmap_get(u->module_infos, name))) {
+                    m = pa_xnew(struct pa_module_info, 1);
+                    m->userdata = u;
+                    m->name = name;
+                    m->n_items = 0;
+                    pa_hashmap_put(u->module_infos, m->name, m);
+                } else
+                    pa_xfree(name);
+
+                i = 0;
+                while (i < MAX_MODULES) {
+                    char *module, *args;
+
+                    if (!(module = read_string(u))) {
+                        if (i > m->n_items) m->n_items = i;
+                        goto fail;
+                    }
+
+                    if (!*module) {
+                        pa_xfree(module);
+                        break;
+                    }
+
+                    if (!(args = read_string(u))) {
+                        pa_xfree(module);
+
+                        if (i > m->n_items) m->n_items = i;
+                        goto fail;
+                    }
+
+                    load_module(m, i, module, args, i >= m->n_items);
+
+                    i++;
+
+                    pa_xfree(module);
+                    pa_xfree(args);
+                }
+
+                /* Unload all removed modules */
+                for (j = i; j < m->n_items; j++)
+                    unload_one_module(m, j);
+
+                m->n_items = i;
+
+                break;
+            }
+
+            case '-': {
+                char *name;
+
+                if (!(name = read_string(u)))
+                    goto fail;
+
+                pa_hashmap_remove_and_free(u->module_infos, name);
+                pa_xfree(name);
+
+                break;
+            }
+        }
+    } while (u->buf_fill > 0 && ret == 0);
+
+    return ret;
+
+fail:
+    pa_log("Unable to read or parse data from client.");
+    return -1;
+}
+
+static void io_event_cb(
+        pa_mainloop_api*a,
+        pa_io_event* e,
+        int fd,
+        pa_io_event_flags_t events,
+        void *userdata) {
+
+    struct userdata *u = userdata;
+
+    if (handle_event(u) < 0) {
+
+        if (u->io_event) {
+            u->core->mainloop->io_free(u->io_event);
+            u->io_event = NULL;
+        }
+
+        pa_module_unload_request(u->module, true);
+    }
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    int r;
+
+    u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->module_infos = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) module_info_free);
+    u->pid = (pid_t) -1;
+    u->fd = -1;
+    u->fd_type = 0;
+    u->io_event = NULL;
+    u->buf_fill = 0;
+
+    if ((u->fd = pa_start_child_for_read(
+#if defined(__linux__) && !defined(__OPTIMIZE__)
+                              pa_run_from_build_tree() ? PA_BUILDDIR "/gconf-helper" :
+#endif
+                 PA_GCONF_HELPER, NULL, &u->pid)) < 0)
+        goto fail;
+
+    u->io_event = m->core->mainloop->io_new(
+            m->core->mainloop,
+            u->fd,
+            PA_IO_EVENT_INPUT,
+            io_event_cb,
+            u);
+
+    do {
+        if ((r = handle_event(u)) < 0)
+            goto fail;
+
+        /* Read until the client signalled us that it is ready with
+         * initialization */
+    } while (r != 1);
+
+    return 0;
+
+fail:
+    pa__done(m);
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->pid != (pid_t) -1) {
+        kill(u->pid, SIGTERM);
+
+        for (;;) {
+            if (waitpid(u->pid, NULL, 0) >= 0)
+                break;
+
+            if (errno != EINTR) {
+                pa_log("waitpid() failed: %s", pa_cstrerror(errno));
+                break;
+            }
+        }
+    }
+
+    if (u->io_event)
+        m->core->mainloop->io_free(u->io_event);
+
+    if (u->fd >= 0)
+        pa_close(u->fd);
+
+    if (u->module_infos)
+        pa_hashmap_free(u->module_infos);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/jack/module-jack-sink.c b/src/modules/jack/module-jack-sink.c
new file mode 100644 (file)
index 0000000..82bfccd
--- /dev/null
@@ -0,0 +1,526 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <jack/jack.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+
+#include "module-jack-sink-symdef.h"
+
+/* General overview:
+ *
+ * Because JACK has a very inflexible event loop management which
+ * doesn't allow us to add our own event sources to the event thread
+ * we cannot use the JACK real-time thread for dispatching our PA
+ * work. Instead, we run an additional RT thread which does most of
+ * the PA handling, and have the JACK RT thread request data from it
+ * via pa_asyncmsgq. The cost is an additional context switch which
+ * should hopefully not be that expensive if RT scheduling is
+ * enabled. A better fix would only be possible with additional event
+ * source support in JACK.
+ */
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("JACK Sink");
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "sink_properties=<properties for the card> "
+        "server_name=<jack server name> "
+        "client_name=<jack client name> "
+        "channels=<number of channels> "
+        "channel_map=<channel map> "
+        "connect=<connect ports?>");
+
+#define DEFAULT_SINK_NAME "jack_out"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    unsigned channels;
+
+    jack_port_t* port[PA_CHANNELS_MAX];
+    jack_client_t *client;
+
+    void *buffer[PA_CHANNELS_MAX];
+
+    pa_thread_mq thread_mq;
+    pa_asyncmsgq *jack_msgq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+
+    pa_thread *thread;
+
+    jack_nframes_t frames_in_buffer;
+    jack_nframes_t saved_frame_time;
+    bool saved_frame_time_valid;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "server_name",
+    "client_name",
+    "channels",
+    "channel_map",
+    "connect",
+    NULL
+};
+
+enum {
+    SINK_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX,
+    SINK_MESSAGE_BUFFER_SIZE,
+    SINK_MESSAGE_ON_SHUTDOWN
+};
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *memchunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case SINK_MESSAGE_RENDER:
+
+            /* Handle the request from the JACK thread */
+
+            if (u->sink->thread_info.state == PA_SINK_RUNNING) {
+                pa_memchunk chunk;
+                size_t nbytes;
+                void *p;
+
+                pa_assert(offset > 0);
+                nbytes = (size_t) offset * pa_frame_size(&u->sink->sample_spec);
+
+                pa_sink_render_full(u->sink, nbytes, &chunk);
+
+                p = pa_memblock_acquire_chunk(&chunk);
+                pa_deinterleave(p, u->buffer, u->channels, sizeof(float), (unsigned) offset);
+                pa_memblock_release(chunk.memblock);
+
+                pa_memblock_unref(chunk.memblock);
+            } else {
+                unsigned c;
+                pa_sample_spec ss;
+
+                /* Humm, we're not RUNNING, hence let's write some silence */
+                /* This can happen if we're paused, or during shutdown (when we're unlinked but jack is still running). */
+
+                ss = u->sink->sample_spec;
+                ss.channels = 1;
+
+                for (c = 0; c < u->channels; c++)
+                    pa_silence_memory(u->buffer[c], (size_t) offset * pa_sample_size(&ss), &ss);
+            }
+
+            u->frames_in_buffer = (jack_nframes_t) offset;
+            u->saved_frame_time = * (jack_nframes_t*) data;
+            u->saved_frame_time_valid = true;
+
+            return 0;
+
+        case SINK_MESSAGE_BUFFER_SIZE:
+            pa_sink_set_max_request_within_thread(u->sink, (size_t) offset * pa_frame_size(&u->sink->sample_spec));
+            return 0;
+
+        case SINK_MESSAGE_ON_SHUTDOWN:
+            pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            jack_nframes_t ft, d;
+            jack_latency_range_t r;
+            size_t n;
+            int32_t number_of_frames;
+
+            /* This is the "worst-case" latency */
+            jack_port_get_latency_range(u->port[0], JackPlaybackLatency, &r);
+            number_of_frames = r.max + u->frames_in_buffer;
+
+            if (u->saved_frame_time_valid) {
+                /* Adjust the worst case latency by the time that
+                 * passed since we last handed data to JACK */
+
+                ft = jack_frame_time(u->client);
+                d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0;
+                number_of_frames -= d;
+            }
+
+            /* Convert it to usec */
+            if (number_of_frames > 0) {
+                n = number_of_frames * pa_frame_size(&u->sink->sample_spec);
+                *((int64_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
+            } else {
+                n = - number_of_frames * pa_frame_size(&u->sink->sample_spec);
+                *((int64_t*) data) = - (int64_t)pa_bytes_to_usec(n, &u->sink->sample_spec);
+            }
+
+            return 0;
+        }
+
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, memchunk);
+}
+
+/* JACK Callback: This is called when JACK needs some data */
+static int jack_process(jack_nframes_t nframes, void *arg) {
+    struct userdata *u = arg;
+    unsigned c;
+    jack_nframes_t frame_time;
+    pa_assert(u);
+
+    /* We just forward the request to our other RT thread */
+
+    for (c = 0; c < u->channels; c++)
+        pa_assert_se(u->buffer[c] = jack_port_get_buffer(u->port[c], nframes));
+
+    frame_time = jack_frame_time(u->client);
+
+    pa_assert_se(pa_asyncmsgq_send(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RENDER, &frame_time, nframes, NULL) == 0);
+    return 0;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        int ret;
+
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+            pa_sink_process_rewind(u->sink, 0);
+
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+/* JACK Callback: This is called when JACK triggers an error */
+static void jack_error_func(const char*t) {
+    char *s;
+
+    s = pa_xstrndup(t, strcspn(t, "\n\r"));
+    pa_log_warn("JACK error >%s<", s);
+    pa_xfree(s);
+}
+
+/* JACK Callback: This is called when JACK is set up */
+static void jack_init(void *arg) {
+    struct userdata *u = arg;
+
+    pa_log_info("JACK thread starting up.");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority+4);
+}
+
+/* JACK Callback: This is called when JACK kicks us */
+static void jack_shutdown(void* arg) {
+    struct userdata *u = arg;
+
+    pa_log_info("JACK thread shutting down.");
+    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL);
+}
+
+/* JACK Callback: This is called when JACK changes the buffer size */
+static int jack_buffer_size(jack_nframes_t nframes, void *arg) {
+    struct userdata *u = arg;
+
+    pa_log_info("JACK buffer size changed.");
+    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_BUFFER_SIZE, NULL, nframes, NULL, NULL);
+    return 0;
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    jack_status_t status;
+    const char *server_name, *client_name;
+    uint32_t channels = 0;
+    bool do_connect = true;
+    unsigned i;
+    const char **ports = NULL, **p;
+    pa_sink_new_data data;
+    jack_latency_range_t r;
+    size_t n;
+
+    pa_assert(m);
+
+    jack_set_error_function(jack_error_func);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) {
+        pa_log("Failed to parse connect= argument.");
+        goto fail;
+    }
+
+    server_name = pa_modargs_get_value(ma, "server_name", NULL);
+    client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Sink");
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->saved_frame_time_valid = false;
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    /* The queue linking the JACK thread and our RT thread */
+    u->jack_msgq = pa_asyncmsgq_new(0);
+    if (!u->jack_msgq) {
+        pa_log("pa_asyncmsgq_new() failed.");
+        goto fail;
+    }
+
+    /* The msgq from the JACK RT thread should have an even higher
+     * priority than the normal message queues, to match the guarantee
+     * all other drivers make: supplying the audio device with data is
+     * the top priority -- and as long as that is possible we don't do
+     * anything else */
+    u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
+
+    if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
+        pa_log("jack_client_open() failed.");
+        goto fail;
+    }
+
+    ports = jack_get_ports(u->client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical|JackPortIsInput);
+
+    channels = 0;
+    if (ports)
+        for (p = ports; *p; p++)
+            channels++;
+
+    if (!channels)
+        channels = m->core->default_sample_spec.channels;
+
+    if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 ||
+        !pa_channels_valid(channels)) {
+        pa_log("Failed to parse channels= argument.");
+        goto fail;
+    }
+
+    if (channels == m->core->default_channel_map.channels)
+        map = m->core->default_channel_map;
+    else
+        pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA);
+
+    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) {
+        pa_log("Failed to parse channel_map= argument.");
+        goto fail;
+    }
+
+    pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client));
+
+    u->channels = ss.channels = (uint8_t) channels;
+    ss.rate = jack_get_sample_rate(u->client);
+    ss.format = PA_SAMPLE_FLOAT32NE;
+
+    pa_assert(pa_sample_spec_valid(&ss));
+
+    for (i = 0; i < ss.channels; i++) {
+        if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) {
+            pa_log("jack_port_register() failed.");
+            goto fail;
+        }
+    }
+
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
+    if (server_name)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client));
+    pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&data);
+        goto fail;
+    }
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+    pa_sink_set_max_request(u->sink, jack_get_buffer_size(u->client) * pa_frame_size(&u->sink->sample_spec));
+
+    jack_set_process_callback(u->client, jack_process, u);
+    jack_on_shutdown(u->client, jack_shutdown, u);
+    jack_set_thread_init_callback(u->client, jack_init, u);
+    jack_set_buffer_size_callback(u->client, jack_buffer_size, u);
+
+    if (!(u->thread = pa_thread_new("jack-sink", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    if (jack_activate(u->client)) {
+        pa_log("jack_activate() failed");
+        goto fail;
+    }
+
+    if (do_connect) {
+        for (i = 0, p = ports; i < ss.channels; i++, p++) {
+
+            if (!p || !*p) {
+                pa_log("Not enough physical output ports, leaving unconnected.");
+                break;
+            }
+
+            pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p);
+
+            if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) {
+                pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p);
+                break;
+            }
+        }
+    }
+
+    jack_port_get_latency_range(u->port[0], JackPlaybackLatency, &r);
+    n = r.max * pa_frame_size(&u->sink->sample_spec);
+    pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(n, &u->sink->sample_spec));
+    pa_sink_put(u->sink);
+
+    if (ports)
+        jack_free(ports);
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    if (ports)
+        jack_free(ports);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_sink_linked_by(u->sink);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->client)
+        jack_client_close(u->client);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->jack_msgq)
+        pa_asyncmsgq_unref(u->jack_msgq);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/jack/module-jack-source.c b/src/modules/jack/module-jack-source.c
new file mode 100644 (file)
index 0000000..1f020c0
--- /dev/null
@@ -0,0 +1,466 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <jack/jack.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+
+#include "module-jack-source-symdef.h"
+
+/* See module-jack-sink for a few comments how this module basically
+ * works */
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("JACK Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "source_name=<name for the source> "
+        "source_properties=<properties for the source> "
+        "server_name=<jack server name> "
+        "client_name=<jack client name> "
+        "channels=<number of channels> "
+        "channel_map=<channel map> "
+        "connect=<connect ports?>");
+
+#define DEFAULT_SOURCE_NAME "jack_in"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_source *source;
+
+    unsigned channels;
+
+    jack_port_t* port[PA_CHANNELS_MAX];
+    jack_client_t *client;
+
+    pa_thread_mq thread_mq;
+    pa_asyncmsgq *jack_msgq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+
+    pa_thread *thread;
+
+    jack_nframes_t saved_frame_time;
+    bool saved_frame_time_valid;
+};
+
+static const char* const valid_modargs[] = {
+    "source_name",
+    "source_properties",
+    "server_name",
+    "client_name",
+    "channels",
+    "channel_map",
+    "connect",
+    NULL
+};
+
+enum {
+    SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX,
+    SOURCE_MESSAGE_ON_SHUTDOWN
+};
+
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case SOURCE_MESSAGE_POST:
+
+            /* Handle the new block from the JACK thread */
+            pa_assert(chunk);
+            pa_assert(chunk->length > 0);
+
+            if (u->source->thread_info.state == PA_SOURCE_RUNNING)
+                pa_source_post(u->source, chunk);
+
+            u->saved_frame_time = (jack_nframes_t) offset;
+            u->saved_frame_time_valid = true;
+
+            return 0;
+
+        case SOURCE_MESSAGE_ON_SHUTDOWN:
+            pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            jack_latency_range_t r;
+            jack_nframes_t l, ft, d;
+            size_t n;
+
+            /* This is the "worst-case" latency */
+            jack_port_get_latency_range(u->port[0], JackCaptureLatency, &r);
+            l = r.max;
+
+            if (u->saved_frame_time_valid) {
+                /* Adjust the worst case latency by the time that
+                 * passed since we last handed data to JACK */
+
+                ft = jack_frame_time(u->client);
+                d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0;
+                l += d;
+            }
+
+            /* Convert it to usec */
+            n = l * pa_frame_size(&u->source->sample_spec);
+            *((int64_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec);
+
+            return 0;
+        }
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static int jack_process(jack_nframes_t nframes, void *arg) {
+    unsigned c;
+    struct userdata *u = arg;
+    const void *buffer[PA_CHANNELS_MAX];
+    void *p;
+    jack_nframes_t frame_time;
+    pa_memchunk chunk;
+
+    pa_assert(u);
+
+    for (c = 0; c < u->channels; c++)
+        pa_assert_se(buffer[c] = jack_port_get_buffer(u->port[c], nframes));
+
+    /* We interleave the data and pass it on to the other RT thread */
+
+    pa_memchunk_reset(&chunk);
+    chunk.length = nframes * pa_frame_size(&u->source->sample_spec);
+    chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length);
+    p = pa_memblock_acquire(chunk.memblock);
+    pa_interleave(buffer, u->channels, p, sizeof(float), nframes);
+    pa_memblock_release(chunk.memblock);
+
+    frame_time = jack_frame_time(u->client);
+
+    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, NULL, frame_time, &chunk, NULL);
+
+    pa_memblock_unref(chunk.memblock);
+
+    return 0;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        int ret;
+
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+static void jack_error_func(const char*t) {
+    char *s;
+
+    s = pa_xstrndup(t, strcspn(t, "\n\r"));
+    pa_log_warn("JACK error >%s<", s);
+    pa_xfree(s);
+}
+
+static void jack_init(void *arg) {
+    struct userdata *u = arg;
+
+    pa_log_info("JACK thread starting up.");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority+4);
+}
+
+static void jack_shutdown(void* arg) {
+    struct userdata *u = arg;
+
+    pa_log_info("JACK thread shutting down..");
+    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL);
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    jack_status_t status;
+    const char *server_name, *client_name;
+    uint32_t channels = 0;
+    bool do_connect = true;
+    unsigned i;
+    const char **ports = NULL, **p;
+    pa_source_new_data data;
+    jack_latency_range_t r;
+    size_t n;
+
+    pa_assert(m);
+
+    jack_set_error_function(jack_error_func);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) {
+        pa_log("Failed to parse connect= argument.");
+        goto fail;
+    }
+
+    server_name = pa_modargs_get_value(ma, "server_name", NULL);
+    client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Source");
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->saved_frame_time_valid = false;
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->jack_msgq = pa_asyncmsgq_new(0);
+    if (!u->jack_msgq) {
+        pa_log("pa_asyncmsgq_new() failed.");
+        goto fail;
+    }
+
+    u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
+
+    if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
+        pa_log("jack_client_open() failed.");
+        goto fail;
+    }
+
+    ports = jack_get_ports(u->client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical|JackPortIsOutput);
+
+    channels = 0;
+    if (ports)
+        for (p = ports; *p; p++)
+            channels++;
+
+    if (!channels)
+        channels = m->core->default_sample_spec.channels;
+
+    if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 ||
+        !pa_channels_valid(channels)) {
+        pa_log("failed to parse channels= argument.");
+        goto fail;
+    }
+
+    if (channels == m->core->default_channel_map.channels)
+        map = m->core->default_channel_map;
+    else
+        pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA);
+
+    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) {
+        pa_log("failed to parse channel_map= argument.");
+        goto fail;
+    }
+
+    pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client));
+
+    u->channels = ss.channels = (uint8_t) channels;
+    ss.rate = jack_get_sample_rate(u->client);
+    ss.format = PA_SAMPLE_FLOAT32NE;
+
+    pa_assert(pa_sample_spec_valid(&ss));
+
+    for (i = 0; i < ss.channels; i++) {
+        if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput|JackPortIsTerminal, 0))) {
+            pa_log("jack_port_register() failed.");
+            goto fail;
+        }
+    }
+
+    pa_source_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+    pa_source_new_data_set_sample_spec(&data, &ss);
+    pa_source_new_data_set_channel_map(&data, &map);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
+    if (server_name)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack source (%s)", jack_get_client_name(u->client));
+    pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
+
+    if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_source_new_data_done(&data);
+        goto fail;
+    }
+
+    u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY);
+    pa_source_new_data_done(&data);
+
+    if (!u->source) {
+        pa_log("Failed to create source.");
+        goto fail;
+    }
+
+    u->source->parent.process_msg = source_process_msg;
+    u->source->userdata = u;
+
+    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+
+    jack_set_process_callback(u->client, jack_process, u);
+    jack_on_shutdown(u->client, jack_shutdown, u);
+    jack_set_thread_init_callback(u->client, jack_init, u);
+
+    if (!(u->thread = pa_thread_new("jack-source", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    if (jack_activate(u->client)) {
+        pa_log("jack_activate() failed");
+        goto fail;
+    }
+
+    if (do_connect) {
+        for (i = 0, p = ports; i < ss.channels; i++, p++) {
+
+            if (!p || !*p) {
+                pa_log("Not enough physical output ports, leaving unconnected.");
+                break;
+            }
+
+            pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p);
+
+            if (jack_connect(u->client, *p, jack_port_name(u->port[i]))) {
+                pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p);
+                break;
+            }
+        }
+
+    }
+
+    jack_port_get_latency_range(u->port[0], JackCaptureLatency, &r);
+    n = r.max * pa_frame_size(&u->source->sample_spec);
+    pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(n, &u->source->sample_spec));
+    pa_source_put(u->source);
+
+    if (ports)
+        jack_free(ports);
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    if (ports)
+        jack_free(ports);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_source_linked_by(u->source);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->client)
+        jack_client_close(u->client);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->jack_msgq)
+        pa_asyncmsgq_unref(u->jack_msgq);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/jack/module-jackdbus-detect.c b/src/modules/jack/module-jackdbus-detect.c
new file mode 100644 (file)
index 0000000..26910b4
--- /dev/null
@@ -0,0 +1,308 @@
+/***
+  This file is part of PulseAudio.
+
+  Written by David Henningsson <david.henningsson@canonical.com>
+  Copyright 2010 Canonical Ltd.
+
+  Some code taken from other parts of PulseAudio, these are
+  Copyright 2006-2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-shared.h>
+
+#include "module-jackdbus-detect-symdef.h"
+
+PA_MODULE_AUTHOR("David Henningsson");
+PA_MODULE_DESCRIPTION("Adds JACK sink/source ports when JACK is started");
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_USAGE(
+    "channels=<number of channels> "
+    "connect=<connect ports?>");
+
+#define JACK_SERVICE_NAME "org.jackaudio.service"
+#define JACK_INTERFACE_NAME "org.jackaudio.JackControl"
+#define JACK_INTERFACE_PATH "/org/jackaudio/Controller"
+
+#define SERVICE_FILTER                          \
+        "type='signal',"                        \
+        "sender='" DBUS_SERVICE_DBUS "',"       \
+        "interface='" DBUS_INTERFACE_DBUS "',"  \
+        "member='NameOwnerChanged',"            \
+        "arg0='" JACK_SERVICE_NAME "'"
+
+#define RUNNING_FILTER(_a)                      \
+        "type='signal',"                        \
+        "sender='" JACK_SERVICE_NAME "',"       \
+        "interface='" JACK_INTERFACE_NAME "',"  \
+        "member='" _a "'"
+
+static const char* const valid_modargs[] = {
+    "channels",
+    "connect",
+    NULL
+};
+
+#define JACK_SS_SINK 0
+#define JACK_SS_SOURCE 1
+#define JACK_SS_COUNT 2
+
+static const char* const modnames[JACK_SS_COUNT] = {
+    "module-jack-sink",
+    "module-jack-source"
+};
+
+struct userdata {
+    pa_module *module;
+    pa_core *core;
+    pa_dbus_connection *connection;
+    bool filter_added, match_added;
+    bool is_service_started;
+    bool autoconnect_ports;
+    uint32_t channels;
+    /* Using index here protects us from module unloading without us knowing */
+    int jack_module_index[JACK_SS_COUNT];
+};
+
+static void ensure_ports_stopped(struct userdata* u) {
+    int i;
+    pa_assert(u);
+
+    for (i = 0; i < JACK_SS_COUNT; i++)
+        if (u->jack_module_index[i]) {
+            pa_module_unload_request_by_index(u->core, u->jack_module_index[i], true);
+            u->jack_module_index[i] = 0;
+            pa_log_info("Stopped %s.", modnames[i]);
+        }
+}
+
+static void ensure_ports_started(struct userdata* u) {
+    int i;
+    pa_assert(u);
+
+    for (i = 0; i < JACK_SS_COUNT; i++)
+        if (!u->jack_module_index[i]) {
+            char* args;
+            pa_module* m;
+            if (u->channels > 0) {
+                args = pa_sprintf_malloc("connect=%s channels=%" PRIu32, pa_yes_no(u->autoconnect_ports), u->channels);
+            } else {
+                args = pa_sprintf_malloc("connect=%s", pa_yes_no(u->autoconnect_ports));
+            }
+            m = pa_module_load(u->core, modnames[i], args);
+            pa_xfree(args);
+
+            if (m) {
+                pa_log_info("Successfully started %s.", modnames[i]);
+                u->jack_module_index[i] = m->index;
+            }
+            else
+                pa_log_info("Failed to start %s.", modnames[i]);
+        }
+}
+
+static bool check_service_started(struct userdata* u) {
+    DBusError error;
+    DBusMessage *m = NULL, *reply = NULL;
+    bool new_status = false;
+    dbus_bool_t call_result;
+    pa_assert(u);
+
+    dbus_error_init(&error);
+
+    /* Just a safety check; it isn't such a big deal if the name disappears just after the call. */
+    if (!dbus_bus_name_has_owner(pa_dbus_connection_get(u->connection),
+            JACK_SERVICE_NAME, &error)) {
+        pa_log_debug("jackdbus isn't running.");
+        goto finish;
+    }
+
+    if (!(m = dbus_message_new_method_call(JACK_SERVICE_NAME, JACK_INTERFACE_PATH, JACK_INTERFACE_NAME, "IsStarted"))) {
+        pa_log("Failed to allocate IsStarted() method call.");
+        goto finish;
+    }
+
+    if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
+        pa_log("IsStarted() call failed: %s: %s", error.name, error.message);
+        goto finish;
+    }
+
+    if (!dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &call_result, DBUS_TYPE_INVALID)) {
+        pa_log("IsStarted() call return failed: %s: %s", error.name, error.message);
+        goto finish;
+    }
+
+    new_status = call_result;
+
+finish:
+    if (m)
+        dbus_message_unref(m);
+    if (reply)
+        dbus_message_unref(reply);
+
+    dbus_error_free(&error);
+    if (new_status)
+        ensure_ports_started(u);
+    else
+        ensure_ports_stopped(u);
+    u->is_service_started = new_status;
+    return new_status;
+}
+
+static DBusHandlerResult dbus_filter_handler(DBusConnection *c, DBusMessage *s, void *userdata) {
+    struct userdata *u = NULL;
+    DBusError error;
+
+    pa_assert(userdata);
+    u = ((pa_module*) userdata)->userdata;
+    pa_assert(u);
+
+    dbus_error_init(&error);
+
+    if (dbus_message_is_signal(s, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
+        const char *name, *old, *new;
+        if (!dbus_message_get_args(s, &error,
+                                   DBUS_TYPE_STRING, &name,
+                                   DBUS_TYPE_STRING, &old,
+                                   DBUS_TYPE_STRING, &new,
+                                   DBUS_TYPE_INVALID))
+            goto finish;
+        if (!pa_streq(name, JACK_SERVICE_NAME))
+            goto finish;
+
+        ensure_ports_stopped(u);
+        check_service_started(u);
+    }
+
+    else if (dbus_message_is_signal(s, JACK_INTERFACE_NAME, "ServerStarted")) {
+        ensure_ports_stopped(u);
+        check_service_started(u);
+    }
+
+    else if (dbus_message_is_signal(s, JACK_INTERFACE_NAME, "ServerStopped")) {
+        ensure_ports_stopped(u);
+    }
+
+finish:
+    dbus_error_free(&error);
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int pa__init(pa_module *m) {
+    DBusError error;
+    pa_dbus_connection *connection = NULL;
+    struct userdata *u = NULL;
+    pa_modargs *ma;
+
+    pa_assert(m);
+
+    dbus_error_init(&error);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->autoconnect_ports = true;
+    u->channels = 0;
+
+    if (pa_modargs_get_value_boolean(ma, "connect", &u->autoconnect_ports) < 0) {
+        pa_log("Failed to parse connect= argument.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "channels", &u->channels) < 0 || !pa_channels_valid(u->channels)) {
+        pa_log("Failed to parse channels= argument.");
+        goto fail;
+    }
+
+    if (!(connection = pa_dbus_bus_get(m->core, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
+
+        if (connection)
+            pa_dbus_connection_unref(connection);
+
+        pa_log_error("Unable to contact D-Bus session bus: %s: %s", error.name, error.message);
+        goto fail;
+    }
+    u->connection = connection;
+
+    if (!dbus_connection_add_filter(pa_dbus_connection_get(connection), dbus_filter_handler, m, NULL)) {
+        pa_log_error("Unable to add D-Bus filter");
+        goto fail;
+    }
+    u->filter_added = 1;
+
+    if (pa_dbus_add_matches(
+                pa_dbus_connection_get(connection), &error, SERVICE_FILTER,
+                RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL) < 0) {
+        pa_log_error("Unable to subscribe to signals: %s: %s", error.name, error.message);
+        goto fail;
+    }
+    u->match_added = 1;
+
+    check_service_started(u);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    dbus_error_free(&error);
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    ensure_ports_stopped(u);
+
+    if (u->match_added) {
+        pa_dbus_remove_matches(
+                pa_dbus_connection_get(u->connection), SERVICE_FILTER,
+                RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL);
+    }
+
+    if (u->filter_added) {
+        dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), dbus_filter_handler, m);
+    }
+
+    if (u->connection) {
+        pa_dbus_connection_unref(u->connection);
+    }
+
+    pa_xfree(u);
+}
diff --git a/src/modules/ladspa.h b/src/modules/ladspa.h
new file mode 100644 (file)
index 0000000..a5fd464
--- /dev/null
@@ -0,0 +1,602 @@
+/* ladspa.h
+
+   Linux Audio Developer's Simple Plugin API Version 1.1[LGPL].
+   Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis,
+   Stefan Westerfeld.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef LADSPA_INCLUDED
+#define LADSPA_INCLUDED
+
+#define LADSPA_VERSION "1.1"
+#define LADSPA_VERSION_MAJOR 1
+#define LADSPA_VERSION_MINOR 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************/
+
+/* Overview:
+
+   There is a large number of synthesis packages in use or development
+   on the Linux platform at this time. This API (`The Linux Audio
+   Developer's Simple Plugin API') attempts to give programmers the
+   ability to write simple `plugin' audio processors in C/C++ and link
+   them dynamically (`plug') into a range of these packages (`hosts').
+   It should be possible for any host and any plugin to communicate
+   completely through this interface.
+
+   This API is deliberately short and simple. To achieve compatibility
+   with a range of promising Linux sound synthesis packages it
+   attempts to find the `greatest common divisor' in their logical
+   behaviour. Having said this, certain limiting decisions are
+   implicit, notably the use of a fixed type (LADSPA_Data) for all
+   data transfer and absence of a parameterised `initialisation'
+   phase. See below for the LADSPA_Data typedef.
+
+   Plugins are expected to distinguish between control and audio
+   data. Plugins have `ports' that are inputs or outputs for audio or
+   control data and each plugin is `run' for a `block' corresponding
+   to a short time interval measured in samples. Audio data is
+   communicated using arrays of LADSPA_Data, allowing a block of audio
+   to be processed by the plugin in a single pass. Control data is
+   communicated using single LADSPA_Data values. Control data has a
+   single value at the start of a call to the `run()' or `run_adding()'
+   function, and may be considered to remain this value for its
+   duration. The plugin may assume that all its input and output ports
+   have been connected to the relevant data location (see the
+   `connect_port()' function below) before it is asked to run.
+
+   Plugins will reside in shared object files suitable for dynamic
+   linking by dlopen() and family. The file will provide a number of
+   `plugin types' that can be used to instantiate actual plugins
+   (sometimes known as `plugin instances') that can be connected
+   together to perform tasks.
+
+   This API contains very limited error-handling. */
+
+/*****************************************************************************/
+
+/* Fundamental data type passed in and out of plugin. This data type
+   is used to communicate audio samples and control values. It is
+   assumed that the plugin will work sensibly given any numeric input
+   value although it may have a preferred range (see hints below).
+
+   For audio it is generally assumed that 1.0f is the `0dB' reference
+   amplitude and is a `normal' signal level. */
+
+typedef float LADSPA_Data;
+
+/*****************************************************************************/
+
+/* Special Plugin Properties:
+
+   Optional features of the plugin type are encapsulated in the
+   LADSPA_Properties type. This is assembled by ORing individual
+   properties together. */
+
+typedef int LADSPA_Properties;
+
+/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a
+   real-time dependency (e.g. listens to a MIDI device) and so its
+   output must not be cached or subject to significant latency. */
+#define LADSPA_PROPERTY_REALTIME        0x1
+
+/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin
+   may cease to work correctly if the host elects to use the same data
+   location for both input and output (see connect_port()). This
+   should be avoided as enabling this flag makes it impossible for
+   hosts to use the plugin to process audio `in-place.' */
+#define LADSPA_PROPERTY_INPLACE_BROKEN  0x2
+
+/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin
+   is capable of running not only in a conventional host but also in a
+   `hard real-time' environment. To qualify for this the plugin must
+   satisfy all of the following:
+
+   (1) The plugin must not use malloc(), free() or other heap memory
+   management within its run() or run_adding() functions. All new
+   memory used in run() must be managed via the stack. These
+   restrictions only apply to the run() function.
+
+   (2) The plugin will not attempt to make use of any library
+   functions with the exceptions of functions in the ANSI standard C
+   and C maths libraries, which the host is expected to provide.
+
+   (3) The plugin will not access files, devices, pipes, sockets, IPC
+   or any other mechanism that might result in process or thread
+   blocking.
+
+   (4) The plugin will take an amount of time to execute a run() or
+   run_adding() call approximately of form (A+B*SampleCount) where A
+   and B depend on the machine and host in use. This amount of time
+   may not depend on input signals or plugin state. The host is left
+   the responsibility to perform timings to estimate upper bounds for
+   A and B. */
+#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4
+
+#define LADSPA_IS_REALTIME(x)        ((x) & LADSPA_PROPERTY_REALTIME)
+#define LADSPA_IS_INPLACE_BROKEN(x)  ((x) & LADSPA_PROPERTY_INPLACE_BROKEN)
+#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE)
+
+/*****************************************************************************/
+
+/* Plugin Ports:
+
+   Plugins have `ports' that are inputs or outputs for audio or
+   data. Ports can communicate arrays of LADSPA_Data (for audio
+   inputs/outputs) or single LADSPA_Data values (for control
+   input/outputs). This information is encapsulated in the
+   LADSPA_PortDescriptor type which is assembled by ORing individual
+   properties together.
+
+   Note that a port must be an input or an output port but not both
+   and that a port must be a control or audio port but not both. */
+
+typedef int LADSPA_PortDescriptor;
+
+/* Property LADSPA_PORT_INPUT indicates that the port is an input. */
+#define LADSPA_PORT_INPUT   0x1
+
+/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */
+#define LADSPA_PORT_OUTPUT  0x2
+
+/* Property LADSPA_PORT_CONTROL indicates that the port is a control
+   port. */
+#define LADSPA_PORT_CONTROL 0x4
+
+/* Property LADSPA_PORT_AUDIO indicates that the port is a audio
+   port. */
+#define LADSPA_PORT_AUDIO   0x8
+
+#define LADSPA_IS_PORT_INPUT(x)   ((x) & LADSPA_PORT_INPUT)
+#define LADSPA_IS_PORT_OUTPUT(x)  ((x) & LADSPA_PORT_OUTPUT)
+#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL)
+#define LADSPA_IS_PORT_AUDIO(x)   ((x) & LADSPA_PORT_AUDIO)
+
+/*****************************************************************************/
+
+/* Plugin Port Range Hints:
+
+   The host may wish to provide a representation of data entering or
+   leaving a plugin (e.g. to generate a GUI automatically). To make
+   this more meaningful, the plugin should provide `hints' to the host
+   describing the usual values taken by the data.
+
+   Note that these are only hints. The host may ignore them and the
+   plugin must not assume that data supplied to it is meaningful. If
+   the plugin receives invalid input data it is expected to continue
+   to run without failure and, where possible, produce a sensible
+   output (e.g. a high-pass filter given a negative cutoff frequency
+   might switch to an all-pass mode).
+
+   Hints are meaningful for all input and output ports but hints for
+   input control ports are expected to be particularly useful.
+
+   More hint information is encapsulated in the
+   LADSPA_PortRangeHintDescriptor type which is assembled by ORing
+   individual hint types together. Hints may require further
+   LowerBound and UpperBound information.
+
+   All the hint information for a particular port is aggregated in the
+   LADSPA_PortRangeHint structure. */
+
+typedef int LADSPA_PortRangeHintDescriptor;
+
+/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field
+   of the LADSPA_PortRangeHint should be considered meaningful. The
+   value in this field should be considered the (inclusive) lower
+   bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+   specified then the value of LowerBound should be multiplied by the
+   sample rate. */
+#define LADSPA_HINT_BOUNDED_BELOW   0x1
+
+/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field
+   of the LADSPA_PortRangeHint should be considered meaningful. The
+   value in this field should be considered the (inclusive) upper
+   bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+   specified then the value of UpperBound should be multiplied by the
+   sample rate. */
+#define LADSPA_HINT_BOUNDED_ABOVE   0x2
+
+/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be
+   considered a Boolean toggle. Data less than or equal to zero should
+   be considered `off' or `false,' and data above zero should be
+   considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in
+   conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or
+   LADSPA_HINT_DEFAULT_1. */
+#define LADSPA_HINT_TOGGLED         0x4
+
+/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified
+   should be interpreted as multiples of the sample rate. For
+   instance, a frequency range from 0Hz to the Nyquist frequency (half
+   the sample rate) could be requested by this hint in conjunction
+   with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
+   at all must support this hint to retain meaning. */
+#define LADSPA_HINT_SAMPLE_RATE     0x8
+
+/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the
+   user will find it more intuitive to view values using a logarithmic
+   scale. This is particularly useful for frequencies and gains. */
+#define LADSPA_HINT_LOGARITHMIC     0x10
+
+/* Hint LADSPA_HINT_INTEGER indicates that a user interface would
+   probably wish to provide a stepped control taking only integer
+   values. Any bounds set should be slightly wider than the actual
+   integer range required to avoid floating point rounding errors. For
+   instance, the integer set {0,1,2,3} might be described as [-0.1,
+   3.1]. */
+#define LADSPA_HINT_INTEGER         0x20
+
+/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal'
+   value for the port that is sensible as a default. For instance,
+   this value is suitable for use as an initial value in a user
+   interface or as a value the host might assign to a control port
+   when the user has not provided one. Defaults are encoded using a
+   mask so only one default may be specified for a port. Some of the
+   hints make use of lower and upper bounds, in which case the
+   relevant bound or bounds must be available and
+   LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting
+   default must be rounded if LADSPA_HINT_INTEGER is present. Default
+   values were introduced in LADSPA v1.1. */
+#define LADSPA_HINT_DEFAULT_MASK    0x3C0
+
+/* This default values indicates that no default is provided. */
+#define LADSPA_HINT_DEFAULT_NONE    0x0
+
+/* This default hint indicates that the suggested lower bound for the
+   port should be used. */
+#define LADSPA_HINT_DEFAULT_MINIMUM 0x40
+
+/* This default hint indicates that a low value between the suggested
+   lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 +
+   log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper
+   * 0.25). */
+#define LADSPA_HINT_DEFAULT_LOW     0x80
+
+/* This default hint indicates that a middle value between the
+   suggested lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 +
+   log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper *
+   0.5). */
+#define LADSPA_HINT_DEFAULT_MIDDLE  0xC0
+
+/* This default hint indicates that a high value between the suggested
+   lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 +
+   log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper
+   * 0.75). */
+#define LADSPA_HINT_DEFAULT_HIGH    0x100
+
+/* This default hint indicates that the suggested upper bound for the
+   port should be used. */
+#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140
+
+/* This default hint indicates that the number 0 should be used. Note
+   that this default may be used in conjunction with
+   LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_0       0x200
+
+/* This default hint indicates that the number 1 should be used. Note
+   that this default may be used in conjunction with
+   LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_1       0x240
+
+/* This default hint indicates that the number 100 should be used. */
+#define LADSPA_HINT_DEFAULT_100     0x280
+
+/* This default hint indicates that the Hz frequency of `concert A'
+   should be used. This will be 440 unless the host uses an unusual
+   tuning convention, in which case it may be within a few Hz. */
+#define LADSPA_HINT_DEFAULT_440     0x2C0
+
+#define LADSPA_IS_HINT_BOUNDED_BELOW(x)   ((x) & LADSPA_HINT_BOUNDED_BELOW)
+#define LADSPA_IS_HINT_BOUNDED_ABOVE(x)   ((x) & LADSPA_HINT_BOUNDED_ABOVE)
+#define LADSPA_IS_HINT_TOGGLED(x)         ((x) & LADSPA_HINT_TOGGLED)
+#define LADSPA_IS_HINT_SAMPLE_RATE(x)     ((x) & LADSPA_HINT_SAMPLE_RATE)
+#define LADSPA_IS_HINT_LOGARITHMIC(x)     ((x) & LADSPA_HINT_LOGARITHMIC)
+#define LADSPA_IS_HINT_INTEGER(x)         ((x) & LADSPA_HINT_INTEGER)
+
+#define LADSPA_IS_HINT_HAS_DEFAULT(x)     ((x) & LADSPA_HINT_DEFAULT_MASK)
+#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MINIMUM)
+#define LADSPA_IS_HINT_DEFAULT_LOW(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_LOW)
+#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x)  (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MIDDLE)
+#define LADSPA_IS_HINT_DEFAULT_HIGH(x)    (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_HIGH)
+#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MAXIMUM)
+#define LADSPA_IS_HINT_DEFAULT_0(x)       (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_0)
+#define LADSPA_IS_HINT_DEFAULT_1(x)       (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_1)
+#define LADSPA_IS_HINT_DEFAULT_100(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_100)
+#define LADSPA_IS_HINT_DEFAULT_440(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                            == LADSPA_HINT_DEFAULT_440)
+
+typedef struct _LADSPA_PortRangeHint {
+
+  /* Hints about the port. */
+  LADSPA_PortRangeHintDescriptor HintDescriptor;
+
+  /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When
+     LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+     multiplied by the relevant sample rate. */
+  LADSPA_Data LowerBound;
+
+  /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When
+     LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+     multiplied by the relevant sample rate. */
+  LADSPA_Data UpperBound;
+
+} LADSPA_PortRangeHint;
+
+/*****************************************************************************/
+
+/* Plugin Handles:
+
+   This plugin handle indicates a particular instance of the plugin
+   concerned. It is valid to compare this to NULL (0 for C++) but
+   otherwise the host should not attempt to interpret it. The plugin
+   may use it to reference internal instance data. */
+
+typedef void * LADSPA_Handle;
+
+/*****************************************************************************/
+
+/* Descriptor for a Type of Plugin:
+
+   This structure is used to describe a plugin type. It provides a
+   number of functions to examine the type, instantiate it, link it to
+   buffers and workspaces and to run it. */
+
+typedef struct _LADSPA_Descriptor {
+
+  /* This numeric identifier indicates the plugin type
+     uniquely. Plugin programmers may reserve ranges of IDs from a
+     central body to avoid clashes. Hosts may assume that IDs are
+     below 0x1000000. */
+  unsigned long UniqueID;
+
+  /* This identifier can be used as a unique, case-sensitive
+     identifier for the plugin type within the plugin file. Plugin
+     types should be identified by file and label rather than by index
+     or plugin name, which may be changed in new plugin
+     versions. Labels must not contain white-space characters. */
+  const char * Label;
+
+  /* This indicates a number of properties of the plugin. */
+  LADSPA_Properties Properties;
+
+  /* This member points to the null-terminated name of the plugin
+     (e.g. "Sine Oscillator"). */
+  const char * Name;
+
+  /* This member points to the null-terminated string indicating the
+     maker of the plugin. This can be an empty string but not NULL. */
+  const char * Maker;
+
+  /* This member points to the null-terminated string indicating any
+     copyright applying to the plugin. If no Copyright applies the
+     string "None" should be used. */
+  const char * Copyright;
+
+  /* This indicates the number of ports (input AND output) present on
+     the plugin. */
+  unsigned long PortCount;
+
+  /* This member indicates an array of port descriptors. Valid indices
+     vary from 0 to PortCount-1. */
+  const LADSPA_PortDescriptor * PortDescriptors;
+
+  /* This member indicates an array of null-terminated strings
+     describing ports (e.g. "Frequency (Hz)"). Valid indices vary from
+     0 to PortCount-1. */
+  const char * const * PortNames;
+
+  /* This member indicates an array of range hints for each port (see
+     above). Valid indices vary from 0 to PortCount-1. */
+  const LADSPA_PortRangeHint * PortRangeHints;
+
+  /* This may be used by the plugin developer to pass any custom
+     implementation data into an instantiate call. It must not be used
+     or interpreted by the host. It is expected that most plugin
+     writers will not use this facility as LADSPA_Handle should be
+     used to hold instance data. */
+  void * ImplementationData;
+
+  /* This member is a function pointer that instantiates a plugin. A
+     handle is returned indicating the new plugin instance. The
+     instantiation function accepts a sample rate as a parameter. The
+     plugin descriptor from which this instantiate function was found
+     must also be passed. This function must return NULL if
+     instantiation fails.
+
+     Note that instance initialisation should generally occur in
+     activate() rather than here. */
+  LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor,
+                               unsigned long                     SampleRate);
+
+  /* This member is a function pointer that connects a port on an
+     instantiated plugin to a memory location at which a block of data
+     for the port will be read/written. The data location is expected
+     to be an array of LADSPA_Data for audio ports or a single
+     LADSPA_Data value for control ports. Memory issues will be
+     managed by the host. The plugin must read/write the data at these
+     locations every time run() or run_adding() is called and the data
+     present at the time of this connection call should not be
+     considered meaningful.
+
+     connect_port() may be called more than once for a plugin instance
+     to allow the host to change the buffers that the plugin is
+     reading or writing. These calls may be made before or after
+     activate() or deactivate() calls.
+
+     connect_port() must be called at least once for each port before
+     run() or run_adding() is called. When working with blocks of
+     LADSPA_Data the plugin should pay careful attention to the block
+     size passed to the run function as the block allocated may only
+     just be large enough to contain the block of samples.
+
+     Plugin writers should be aware that the host may elect to use the
+     same buffer for more than one port and even use the same buffer
+     for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN).
+     However, overlapped buffers or use of a single buffer for both
+     audio and control data may result in unexpected behaviour. */
+   void (*connect_port)(LADSPA_Handle Instance,
+                        unsigned long Port,
+                        LADSPA_Data * DataLocation);
+
+  /* This member is a function pointer that initialises a plugin
+     instance and activates it for use. This is separated from
+     instantiate() to aid real-time support and so that hosts can
+     reinitialise a plugin instance by calling deactivate() and then
+     activate(). In this case the plugin instance must reset all state
+     information dependent on the history of the plugin instance
+     except for any data locations provided by connect_port() and any
+     gain set by set_run_adding_gain(). If there is nothing for
+     activate() to do then the plugin writer may provide a NULL rather
+     than an empty function.
+
+     When present, hosts must call this function once before run() (or
+     run_adding()) is called for the first time. This call should be
+     made as close to the run() call as possible and indicates to
+     real-time plugins that they are now live. Plugins should not rely
+     on a prompt call to run() after activate(). activate() may not be
+     called again unless deactivate() is called first. Note that
+     connect_port() may be called before or after a call to
+     activate(). */
+  void (*activate)(LADSPA_Handle Instance);
+
+  /* This method is a function pointer that runs an instance of a
+     plugin for a block. Two parameters are required: the first is a
+     handle to the particular instance to be run and the second
+     indicates the block size (in samples) for which the plugin
+     instance may run.
+
+     Note that if an activate() function exists then it must be called
+     before run() or run_adding(). If deactivate() is called for a
+     plugin instance then the plugin instance may not be reused until
+     activate() has been called again.
+
+     If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE
+     then there are various things that the plugin should not do
+     within the run() or run_adding() functions (see above). */
+  void (*run)(LADSPA_Handle Instance,
+              unsigned long SampleCount);
+
+  /* This method is a function pointer that runs an instance of a
+     plugin for a block. This has identical behaviour to run() except
+     in the way data is output from the plugin. When run() is used,
+     values are written directly to the memory areas associated with
+     the output ports. However when run_adding() is called, values
+     must be added to the values already present in the memory
+     areas. Furthermore, output values written must be scaled by the
+     current gain set by set_run_adding_gain() (see below) before
+     addition.
+
+     run_adding() is optional. When it is not provided by a plugin,
+     this function pointer must be set to NULL. When it is provided,
+     the function set_run_adding_gain() must be provided also. */
+  void (*run_adding)(LADSPA_Handle Instance,
+                     unsigned long SampleCount);
+
+  /* This method is a function pointer that sets the output gain for
+     use when run_adding() is called (see above). If this function is
+     never called the gain is assumed to default to 1. Gain
+     information should be retained when activate() or deactivate()
+     are called.
+
+     This function should be provided by the plugin if and only if the
+     run_adding() function is provided. When it is absent this
+     function pointer must be set to NULL. */
+  void (*set_run_adding_gain)(LADSPA_Handle Instance,
+                              LADSPA_Data   Gain);
+
+  /* This is the counterpart to activate() (see above). If there is
+     nothing for deactivate() to do then the plugin writer may provide
+     a NULL rather than an empty function.
+
+     Hosts must deactivate all activated units after they have been
+     run() (or run_adding()) for the last time. This call should be
+     made as close to the last run() call as possible and indicates to
+     real-time plugins that they are no longer live. Plugins should
+     not rely on prompt deactivation. Note that connect_port() may be
+     called before or after a call to deactivate().
+
+     Deactivation is not similar to pausing as the plugin instance
+     will be reinitialised when activate() is called to reuse it. */
+  void (*deactivate)(LADSPA_Handle Instance);
+
+  /* Once an instance of a plugin has been finished with it can be
+     deleted using the following function. The instance handle passed
+     ceases to be valid after this call.
+
+     If activate() was called for a plugin instance then a
+     corresponding call to deactivate() must be made before cleanup()
+     is called. */
+  void (*cleanup)(LADSPA_Handle Instance);
+
+} LADSPA_Descriptor;
+
+/**********************************************************************/
+
+/* Accessing a Plugin: */
+
+/* The exact mechanism by which plugins are loaded is host-dependent,
+   however all most hosts will need to know is the name of shared
+   object file containing the plugin types. To allow multiple hosts to
+   share plugin types, hosts may wish to check for environment
+   variable LADSPA_PATH. If present, this should contain a
+   colon-separated path indicating directories that should be searched
+   (in order) when loading plugin types.
+
+   A plugin programmer must include a function called
+   "ladspa_descriptor" with the following function prototype within
+   the shared object file. This function will have C-style linkage (if
+   you are using C++ this is taken care of by the `extern "C"' clause
+   at the top of the file).
+
+   A host will find the plugin shared object file by one means or
+   another, find the ladspa_descriptor() function, call it, and
+   proceed from there.
+
+   Plugin types are accessed by index (not ID) using values from 0
+   upwards. Out of range indexes must result in this function
+   returning NULL, so the plugin count can be determined by checking
+   for the least index that results in NULL being returned. */
+
+const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index);
+
+/* Datatype corresponding to the ladspa_descriptor() function. */
+typedef const LADSPA_Descriptor *
+(*LADSPA_Descriptor_Function)(unsigned long Index);
+
+/**********************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LADSPA_INCLUDED */
+
+/* EOF */
diff --git a/src/modules/macosx/module-bonjour-publish.c b/src/modules/macosx/module-bonjour-publish.c
new file mode 100644 (file)
index 0000000..b28d74e
--- /dev/null
@@ -0,0 +1,511 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Daniel Mack
+  based on module-zeroconf-publish.c
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dns_sd.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/parseaddr.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/dynarray.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/protocol-native.h>
+
+#include "module-bonjour-publish-symdef.h"
+
+PA_MODULE_AUTHOR("Daniel Mack");
+PA_MODULE_DESCRIPTION("Mac OS X Bonjour Service Publisher");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+#define SERVICE_TYPE_SINK "_pulse-sink._tcp"
+#define SERVICE_TYPE_SOURCE "_pulse-source._tcp"
+#define SERVICE_TYPE_SERVER "_pulse-server._tcp"
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+enum service_subtype {
+    SUBTYPE_HARDWARE,
+    SUBTYPE_VIRTUAL,
+    SUBTYPE_MONITOR
+};
+
+struct service {
+    struct userdata *userdata;
+    DNSServiceRef service;
+    DNSRecordRef rec, rec2;
+    char *service_name;
+    pa_object *device;
+    enum service_subtype subtype;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    pa_hashmap *services;
+    char *service_name;
+
+    pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot;
+
+    pa_native_protocol *native;
+    DNSServiceRef main_service;
+};
+
+static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_channel_map *ret_map, const char **ret_name, pa_proplist **ret_proplist, enum service_subtype *ret_subtype) {
+    pa_assert(s);
+    pa_assert(ret_ss);
+    pa_assert(ret_proplist);
+    pa_assert(ret_subtype);
+
+    if (pa_sink_isinstance(s->device)) {
+        pa_sink *sink = PA_SINK(s->device);
+
+        *ret_ss = sink->sample_spec;
+        *ret_map = sink->channel_map;
+        *ret_name = sink->name;
+        *ret_proplist = sink->proplist;
+        *ret_subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
+
+    } else if (pa_source_isinstance(s->device)) {
+        pa_source *source = PA_SOURCE(s->device);
+
+        *ret_ss = source->sample_spec;
+        *ret_map = source->channel_map;
+        *ret_name = source->name;
+        *ret_proplist = source->proplist;
+        *ret_subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL);
+
+    } else
+        pa_assert_not_reached();
+}
+
+static void txt_record_server_data(pa_core *c, TXTRecordRef *txt) {
+    char s[128];
+    char *t;
+
+    pa_assert(c);
+
+    TXTRecordSetValue(txt, "server-version", strlen(PACKAGE_NAME" "PACKAGE_VERSION), PACKAGE_NAME" "PACKAGE_VERSION);
+
+    t = pa_get_user_name_malloc();
+    TXTRecordSetValue(txt, "user-name", strlen(t), t);
+    pa_xfree(t);
+
+    t = pa_machine_id();
+    TXTRecordSetValue(txt, "machine-id", strlen(t), t);
+    pa_xfree(t);
+
+    t = pa_uname_string();
+    TXTRecordSetValue(txt, "uname", strlen(t), t);
+    pa_xfree(t);
+
+    t = pa_get_fqdn(s, sizeof(s));
+    TXTRecordSetValue(txt, "fqdn", strlen(t), t);
+
+    snprintf(s, sizeof(s), "0x%08x", c->cookie);
+    TXTRecordSetValue(txt, "cookie", strlen(s), s);
+}
+
+static void service_free(struct service *s);
+
+static void dns_service_register_reply(DNSServiceRef sdRef,
+                                       DNSServiceFlags flags,
+                                       DNSServiceErrorType errorCode,
+                                       const char *name,
+                                       const char *regtype,
+                                       const char *domain,
+                                       void *context) {
+    struct service *s = context;
+
+    pa_assert(s);
+
+    switch (errorCode) {
+    case kDNSServiceErr_NameConflict:
+        pa_log("DNS service reported kDNSServiceErr_NameConflict\n");
+        service_free(s);
+        break;
+
+    case kDNSServiceErr_NoError:
+    default:
+        break;
+    }
+}
+
+static uint16_t compute_port(struct userdata *u) {
+    pa_strlist *i;
+
+    pa_assert(u);
+
+    for (i = pa_native_protocol_servers(u->native); i; i = pa_strlist_next(i)) {
+        pa_parsed_address a;
+
+        if (pa_parse_address(pa_strlist_data(i), &a) >= 0 &&
+            (a.type == PA_PARSED_ADDRESS_TCP4 ||
+             a.type == PA_PARSED_ADDRESS_TCP6 ||
+             a.type == PA_PARSED_ADDRESS_TCP_AUTO) &&
+            a.port > 0) {
+
+            pa_xfree(a.path_or_host);
+            return htons(a.port);
+        }
+
+        pa_xfree(a.path_or_host);
+    }
+
+    return htons(PA_NATIVE_DEFAULT_PORT);
+}
+
+static int publish_service(struct service *s) {
+    int r = -1;
+    TXTRecordRef txt;
+    DNSServiceErrorType err;
+    const char *name = NULL, *t;
+    pa_proplist *proplist = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX], tmp[64];
+    enum service_subtype subtype;
+
+    const char * const subtype_text[] = {
+        [SUBTYPE_HARDWARE] = "hardware",
+        [SUBTYPE_VIRTUAL] = "virtual",
+        [SUBTYPE_MONITOR] = "monitor"
+    };
+
+    pa_assert(s);
+
+    if (s->service) {
+        DNSServiceRefDeallocate(s->service);
+        s->service = NULL;
+    }
+
+    TXTRecordCreate(&txt, 0, NULL);
+
+    txt_record_server_data(s->userdata->core, &txt);
+
+    get_service_data(s, &ss, &map, &name, &proplist, &subtype);
+    TXTRecordSetValue(&txt, "device", strlen(name), name);
+
+    snprintf(tmp, sizeof(tmp), "%u", ss.rate);
+    TXTRecordSetValue(&txt, "rate", strlen(tmp), tmp);
+
+    snprintf(tmp, sizeof(tmp), "%u", ss.channels);
+    TXTRecordSetValue(&txt, "channels", strlen(tmp), tmp);
+
+    t = pa_sample_format_to_string(ss.format);
+    TXTRecordSetValue(&txt, "format", strlen(t), t);
+
+    t = pa_channel_map_snprint(cm, sizeof(cm), &map);
+    TXTRecordSetValue(&txt, "channel_map", strlen(t), t);
+
+    t = subtype_text[subtype];
+    TXTRecordSetValue(&txt, "subtype", strlen(t), t);
+
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_DESCRIPTION)))
+        TXTRecordSetValue(&txt, "description", strlen(t), t);
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_ICON_NAME)))
+        TXTRecordSetValue(&txt, "icon-name", strlen(t), t);
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_VENDOR_NAME)))
+        TXTRecordSetValue(&txt, "vendor-name", strlen(t), t);
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_PRODUCT_NAME)))
+        TXTRecordSetValue(&txt, "product-name", strlen(t), t);
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_CLASS)))
+        TXTRecordSetValue(&txt, "class", strlen(t), t);
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_FORM_FACTOR)))
+        TXTRecordSetValue(&txt, "form-factor", strlen(t), t);
+
+    err = DNSServiceRegister(&s->service,
+                             0,         /* flags */
+                             kDNSServiceInterfaceIndexAny,
+                             s->service_name,
+                             pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
+                             NULL,      /* domain */
+                             NULL,      /* host */
+                             compute_port(s->userdata),
+                             TXTRecordGetLength(&txt),
+                             TXTRecordGetBytesPtr(&txt),
+                             dns_service_register_reply, s);
+
+    if (err != kDNSServiceErr_NoError) {
+        pa_log("DNSServiceRegister() returned err %d", err);
+        goto finish;
+    }
+
+    pa_log_debug("Successfully registered Bonjour services for >%s<.", s->service_name);
+    return 0;
+
+finish:
+
+    /* Remove this service */
+    if (r < 0)
+        service_free(s);
+
+    TXTRecordDeallocate(&txt);
+
+    return r;
+}
+
+static struct service *get_service(struct userdata *u, pa_object *device) {
+    struct service *s;
+    char *hn, *un;
+    const char *n;
+
+    pa_assert(u);
+    pa_object_assert_ref(device);
+
+    if ((s = pa_hashmap_get(u->services, device)))
+        return s;
+
+    s = pa_xnew0(struct service, 1);
+    s->userdata = u;
+    s->device = device;
+
+    if (pa_sink_isinstance(device)) {
+        if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+            n = PA_SINK(device)->name;
+    } else {
+        if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+            n = PA_SOURCE(device)->name;
+    }
+
+    hn = pa_get_host_name_malloc();
+    un = pa_get_user_name_malloc();
+
+    s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s", un, hn, n), kDNSServiceMaxDomainName-1);
+
+    pa_xfree(un);
+    pa_xfree(hn);
+
+    pa_hashmap_put(u->services, s->device, s);
+
+    return s;
+}
+
+static void service_free(struct service *s) {
+    pa_assert(s);
+
+    pa_hashmap_remove(s->userdata->services, s->device);
+
+    if (s->service)
+        DNSServiceRefDeallocate(s->service);
+
+    pa_xfree(s->service_name);
+    pa_xfree(s);
+}
+
+static bool shall_ignore(pa_object *o) {
+    pa_object_assert_ref(o);
+
+    if (pa_sink_isinstance(o))
+        return !!(PA_SINK(o)->flags & PA_SINK_NETWORK);
+
+    if (pa_source_isinstance(o))
+        return PA_SOURCE(o)->monitor_of || (PA_SOURCE(o)->flags & PA_SOURCE_NETWORK);
+
+    pa_assert_not_reached();
+}
+
+static pa_hook_result_t device_new_or_changed_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    pa_assert(c);
+    pa_object_assert_ref(o);
+
+    if (!shall_ignore(o))
+        publish_service(get_service(u, o));
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    struct service *s;
+
+    pa_assert(c);
+    pa_object_assert_ref(o);
+
+    if ((s = pa_hashmap_get(u->services, o)))
+        service_free(s);
+
+    return PA_HOOK_OK;
+}
+
+static int publish_main_service(struct userdata *u) {
+    DNSServiceErrorType err;
+    TXTRecordRef txt;
+
+    pa_assert(u);
+
+    if (u->main_service) {
+        DNSServiceRefDeallocate(u->main_service);
+        u->main_service = NULL;
+    }
+
+    TXTRecordCreate(&txt, 0, NULL);
+    txt_record_server_data(u->core, &txt);
+
+    err = DNSServiceRegister(&u->main_service,
+                             0, /* flags */
+                             kDNSServiceInterfaceIndexAny,
+                             u->service_name,
+                             SERVICE_TYPE_SERVER,
+                             NULL, /* domain */
+                             NULL, /* host */
+                             compute_port(u),
+                             TXTRecordGetLength(&txt),
+                             TXTRecordGetBytesPtr(&txt),
+                             NULL, NULL);
+
+    if (err != kDNSServiceErr_NoError) {
+        pa_log("%s(): DNSServiceRegister() returned err %d", __func__, err);
+        return err;
+    }
+
+    TXTRecordDeallocate(&txt);
+
+    return 0;
+}
+
+static int publish_all_services(struct userdata *u) {
+    pa_sink *sink;
+    pa_source *source;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    pa_log_debug("Publishing services in Bonjour");
+
+    for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx)))
+        if (!shall_ignore(PA_OBJECT(sink)))
+            publish_service(get_service(u, PA_OBJECT(sink)));
+
+    for (source = PA_SOURCE(pa_idxset_first(u->core->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(u->core->sources, &idx)))
+        if (!shall_ignore(PA_OBJECT(source)))
+            publish_service(get_service(u, PA_OBJECT(source)));
+
+    return publish_main_service(u);
+}
+
+static void unpublish_all_services(struct userdata *u) {
+    void *state = NULL;
+    struct service *s;
+
+    pa_assert(u);
+
+    pa_log_debug("Unpublishing services in Bonjour");
+
+    while ((s = pa_hashmap_iterate(u->services, &state, NULL)))
+        service_free(s);
+
+    if (u->main_service)
+        DNSServiceRefDeallocate(u->main_service);
+}
+
+int pa__init(pa_module*m) {
+
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    char *hn, *un;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->native = pa_native_protocol_get(u->core);
+
+    u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
+    u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
+
+    un = pa_get_user_name_malloc();
+    hn = pa_get_host_name_malloc();
+    u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", un, hn), kDNSServiceMaxDomainName-1);
+    pa_xfree(un);
+    pa_xfree(hn);
+
+    publish_all_services(u);
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata*u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    unpublish_all_services(u);
+
+    if (u->services)
+        pa_hashmap_free(u->services);
+
+    if (u->sink_new_slot)
+        pa_hook_slot_free(u->sink_new_slot);
+    if (u->source_new_slot)
+        pa_hook_slot_free(u->source_new_slot);
+    if (u->sink_changed_slot)
+        pa_hook_slot_free(u->sink_changed_slot);
+    if (u->source_changed_slot)
+        pa_hook_slot_free(u->source_changed_slot);
+    if (u->sink_unlink_slot)
+        pa_hook_slot_free(u->sink_unlink_slot);
+    if (u->source_unlink_slot)
+        pa_hook_slot_free(u->source_unlink_slot);
+
+    if (u->native)
+        pa_native_protocol_unref(u->native);
+
+    pa_xfree(u->service_name);
+    pa_xfree(u);
+}
diff --git a/src/modules/macosx/module-coreaudio-detect.c b/src/modules/macosx/module-coreaudio-detect.c
new file mode 100644 (file)
index 0000000..d9c09da
--- /dev/null
@@ -0,0 +1,282 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009,2010 Daniel Mack <daniel@caiaq.de>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/llist.h>
+
+#include <CoreAudio/CoreAudio.h>
+
+#include "module-coreaudio-detect-symdef.h"
+
+#define DEVICE_MODULE_NAME "module-coreaudio-device"
+
+PA_MODULE_AUTHOR("Daniel Mack");
+PA_MODULE_DESCRIPTION("CoreAudio device detection");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE("ioproc_frames=<passed on to module-coreaudio-device> ");
+
+static const char* const valid_modargs[] = {
+    "ioproc_frames",
+    NULL
+};
+
+typedef struct ca_device ca_device;
+
+struct ca_device {
+    AudioObjectID id;
+    unsigned int module_index;
+    PA_LLIST_FIELDS(ca_device);
+};
+
+struct userdata {
+    int detect_fds[2];
+    pa_io_event *detect_io;
+    unsigned int ioproc_frames;
+    PA_LLIST_HEAD(ca_device, devices);
+};
+
+static int ca_device_added(struct pa_module *m, AudioObjectID id) {
+    AudioObjectPropertyAddress property_address;
+    OSStatus err;
+    pa_module *mod;
+    struct userdata *u;
+    struct ca_device *dev;
+    char *args, tmp[64];
+    UInt32 size;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    /* To prevent generating a black hole that will suck us in,
+       don't create sources/sinks for PulseAudio virtual devices */
+
+    property_address.mSelector = kAudioDevicePropertyDeviceManufacturer;
+    property_address.mScope = kAudioObjectPropertyScopeGlobal;
+    property_address.mElement = kAudioObjectPropertyElementMaster;
+
+    size = sizeof(tmp);
+    err = AudioObjectGetPropertyData(id, &property_address, 0, NULL, &size, tmp);
+
+    if (!err && pa_streq(tmp, "pulseaudio.org"))
+        return 0;
+
+    if (u->ioproc_frames)
+        args = pa_sprintf_malloc("object_id=%d ioproc_frames=%d", (int) id, u->ioproc_frames);
+    else
+        args = pa_sprintf_malloc("object_id=%d", (int) id);
+
+    pa_log_debug("Loading %s with arguments '%s'", DEVICE_MODULE_NAME, args);
+    mod = pa_module_load(m->core, DEVICE_MODULE_NAME, args);
+    pa_xfree(args);
+
+    if (!mod) {
+        pa_log_info("Failed to load module %s with arguments '%s'", DEVICE_MODULE_NAME, args);
+        return -1;
+    }
+
+    dev = pa_xnew0(ca_device, 1);
+    dev->module_index = mod->index;
+    dev->id = id;
+
+    PA_LLIST_INIT(ca_device, dev);
+    PA_LLIST_PREPEND(ca_device, u->devices, dev);
+
+    return 0;
+}
+
+static int ca_update_device_list(struct pa_module *m) {
+    AudioObjectPropertyAddress property_address;
+    OSStatus err;
+    UInt32 i, size, num_devices;
+    AudioDeviceID *device_id;
+    struct ca_device *dev;
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    property_address.mSelector = kAudioHardwarePropertyDevices;
+    property_address.mScope = kAudioObjectPropertyScopeGlobal;
+    property_address.mElement = kAudioObjectPropertyElementMaster;
+
+    /* get the number of currently available audio devices */
+    err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &property_address, 0, NULL, &size);
+    if (err) {
+        pa_log("Unable to get data size for kAudioHardwarePropertyDevices.");
+        return -1;
+    }
+
+    num_devices = size / sizeof(AudioDeviceID);
+    device_id = pa_xnew(AudioDeviceID, num_devices);
+
+    err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_address, 0, NULL, &size, device_id);
+    if (err) {
+        pa_log("Unable to get kAudioHardwarePropertyDevices.");
+        pa_xfree(device_id);
+        return -1;
+    }
+
+    /* scan for devices which are reported but not in our cached list */
+    for (i = 0; i < num_devices; i++) {
+        bool found = false;
+
+        PA_LLIST_FOREACH(dev, u->devices)
+            if (dev->id == device_id[i]) {
+                found = true;
+                break;
+            }
+
+        if (!found)
+            ca_device_added(m, device_id[i]);
+    }
+
+    /* scan for devices which are in our cached list but are not reported */
+scan_removed:
+
+    PA_LLIST_FOREACH(dev, u->devices) {
+        bool found = false;
+
+        for (i = 0; i < num_devices; i++)
+            if (dev->id == device_id[i]) {
+                found = true;
+                break;
+            }
+
+        if (!found) {
+            pa_log_debug("object id %d has been removed (module index %d) %p", (unsigned int) dev->id, dev->module_index, dev);
+            pa_module_unload_request_by_index(m->core, dev->module_index, true);
+            PA_LLIST_REMOVE(ca_device, u->devices, dev);
+            pa_xfree(dev);
+            /* the current list item pointer is not valid anymore, so start over. */
+            goto scan_removed;
+        }
+    }
+
+    pa_xfree(device_id);
+    return 0;
+}
+
+static OSStatus property_listener_proc(AudioObjectID objectID, UInt32 numberAddresses,
+                                       const AudioObjectPropertyAddress inAddresses[],
+                                       void *clientData) {
+    struct userdata *u = clientData;
+    char dummy = 1;
+
+    pa_assert(u);
+
+    /* dispatch module load/unload operations in main thread */
+    write(u->detect_fds[1], &dummy, 1);
+
+    return 0;
+}
+
+static void detect_handle(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    pa_module *m = userdata;
+    char dummy;
+
+    pa_assert(m);
+
+    read(fd, &dummy, 1);
+    ca_update_device_list(m);
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u = pa_xnew0(struct userdata, 1);
+    AudioObjectPropertyAddress property_address;
+    pa_modargs *ma;
+
+    pa_assert(m);
+
+    m->userdata = u;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    pa_modargs_get_value_u32(ma, "ioproc_frames", &u->ioproc_frames);
+
+    property_address.mSelector = kAudioHardwarePropertyDevices;
+    property_address.mScope = kAudioObjectPropertyScopeGlobal;
+    property_address.mElement = kAudioObjectPropertyElementMaster;
+
+    if (AudioObjectAddPropertyListener(kAudioObjectSystemObject, &property_address, property_listener_proc, u)) {
+        pa_log("AudioObjectAddPropertyListener() failed.");
+        goto fail;
+    }
+
+    if (ca_update_device_list(m))
+       goto fail;
+
+    pa_assert_se(pipe(u->detect_fds) == 0);
+    pa_assert_se(u->detect_io = m->core->mainloop->io_new(m->core->mainloop, u->detect_fds[0], PA_IO_EVENT_INPUT, detect_handle, m));
+
+    return 0;
+
+fail:
+    pa_xfree(u);
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+    struct ca_device *dev;
+    AudioObjectPropertyAddress property_address;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    dev = u->devices;
+
+    property_address.mSelector = kAudioHardwarePropertyDevices;
+    property_address.mScope = kAudioObjectPropertyScopeGlobal;
+    property_address.mElement = kAudioObjectPropertyElementMaster;
+
+    AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &property_address, property_listener_proc, u);
+
+    while (dev) {
+        struct ca_device *next = dev->next;
+
+        pa_module_unload_request_by_index(m->core, dev->module_index, true);
+        pa_xfree(dev);
+
+        dev = next;
+    }
+
+    if (u->detect_fds[0] >= 0)
+        close(u->detect_fds[0]);
+
+    if (u->detect_fds[1] >= 0)
+        close(u->detect_fds[1]);
+
+    if (u->detect_io)
+        m->core->mainloop->io_free(u->detect_io);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/macosx/module-coreaudio-device.c b/src/modules/macosx/module-coreaudio-device.c
new file mode 100644 (file)
index 0000000..48faf08
--- /dev/null
@@ -0,0 +1,969 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009,2010 Daniel Mack <daniel@caiaq.de>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* TODO:
+    - implement hardware volume controls
+    - handle audio device stream format changes (will require changes to the core)
+    - add an "off" mode that removes all sinks and sources
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/card.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/i18n.h>
+
+#include <CoreAudio/CoreAudio.h>
+#include <CoreAudio/CoreAudioTypes.h>
+#include <CoreAudio/AudioHardware.h>
+
+#include "module-coreaudio-device-symdef.h"
+
+#define DEFAULT_FRAMES_PER_IOPROC 512
+
+PA_MODULE_AUTHOR("Daniel Mack");
+PA_MODULE_DESCRIPTION("CoreAudio device");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE("object_id=<the CoreAudio device id> "
+                "ioproc_frames=<audio frames per IOProc call> ");
+
+static const char* const valid_modargs[] = {
+    "object_id",
+    "ioproc_frames",
+    NULL
+};
+
+enum {
+    CA_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX,
+};
+
+typedef struct coreaudio_sink coreaudio_sink;
+typedef struct coreaudio_source coreaudio_source;
+
+struct userdata {
+    AudioObjectID object_id;
+    AudioDeviceIOProcID proc_id;
+
+    pa_thread_mq thread_mq;
+    pa_asyncmsgq *async_msgq;
+
+    pa_rtpoll *rtpoll;
+    pa_thread *thread;
+
+    pa_module *module;
+    pa_card *card;
+    bool running;
+
+    char *device_name, *vendor_name;
+
+    const AudioBufferList *render_input_data;
+    AudioBufferList       *render_output_data;
+
+    AudioStreamBasicDescription stream_description;
+
+    PA_LLIST_HEAD(coreaudio_sink, sinks);
+    PA_LLIST_HEAD(coreaudio_source, sources);
+};
+
+struct coreaudio_sink {
+    pa_sink *pa_sink;
+    struct userdata *userdata;
+
+    char *name;
+    unsigned int channel_idx;
+    bool active;
+
+    pa_channel_map map;
+    pa_sample_spec ss;
+
+    PA_LLIST_FIELDS(coreaudio_sink);
+};
+
+struct coreaudio_source {
+    pa_source *pa_source;
+    struct userdata *userdata;
+
+    char *name;
+    unsigned int channel_idx;
+    bool active;
+
+    pa_channel_map map;
+    pa_sample_spec ss;
+
+    PA_LLIST_FIELDS(coreaudio_source);
+};
+
+static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
+    return 0;
+}
+
+static OSStatus io_render_proc (AudioDeviceID          device,
+                                const AudioTimeStamp  *now,
+                                const AudioBufferList *inputData,
+                                const AudioTimeStamp  *inputTime,
+                                AudioBufferList       *outputData,
+                                const AudioTimeStamp  *outputTime,
+                                void                  *clientData) {
+    struct userdata *u = clientData;
+
+    pa_assert(u);
+    pa_assert(device == u->object_id);
+
+    u->render_input_data = inputData;
+    u->render_output_data = outputData;
+
+    if (u->sinks)
+        pa_assert_se(pa_asyncmsgq_send(u->async_msgq, PA_MSGOBJECT(u->sinks->pa_sink),
+                                        CA_MESSAGE_RENDER, NULL, 0, NULL) == 0);
+
+    if (u->sources)
+        pa_assert_se(pa_asyncmsgq_send(u->async_msgq, PA_MSGOBJECT(u->sources->pa_source),
+                                        CA_MESSAGE_RENDER, NULL, 0, NULL) == 0);
+
+    return 0;
+}
+
+static OSStatus ca_stream_format_changed(AudioObjectID objectID,
+                                         UInt32 numberAddresses,
+                                         const AudioObjectPropertyAddress addresses[],
+                                         void *clientData) {
+    struct userdata *u = clientData;
+    UInt32 i;
+
+    pa_assert(u);
+
+    /* REVISIT: PA can't currently handle external format change requests.
+     * Hence, we set the original format back in this callback to avoid horrible audio artefacts.
+     * The device settings will appear to be 'locked' for any application as long as the PA daemon is running.
+     * Once we're able to propagate such events up in the core, this needs to be changed. */
+
+    for (i = 0; i < numberAddresses; i++)
+        AudioObjectSetPropertyData(objectID, addresses + i, 0, NULL, sizeof(u->stream_description), &u->stream_description);
+
+    return 0;
+}
+
+static pa_usec_t get_latency_us(pa_object *o) {
+    struct userdata *u;
+    pa_sample_spec *ss;
+    bool is_source;
+    UInt32 v = 0, total = 0;
+    OSStatus err;
+    UInt32 size = sizeof(v);
+    AudioObjectPropertyAddress property_address;
+    AudioObjectID stream_id;
+
+    if (pa_sink_isinstance(o)) {
+        coreaudio_sink *sink = PA_SINK(o)->userdata;
+
+        u = sink->userdata;
+        ss = &sink->ss;
+        is_source = false;
+    } else if (pa_source_isinstance(o)) {
+        coreaudio_source *source = PA_SOURCE(o)->userdata;
+
+        u = source->userdata;
+        ss = &source->ss;
+        is_source = true;
+    } else
+        pa_assert_not_reached();
+
+    pa_assert(u);
+
+    property_address.mScope = is_source ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
+    property_address.mElement = kAudioObjectPropertyElementMaster;
+
+    /* get the device latency */
+    property_address.mSelector = kAudioDevicePropertyLatency;
+    size = sizeof(v);
+    err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &v);
+    if (!err)
+        total += v;
+    else
+        pa_log_warn("Failed to get device latency: %d", err);
+
+    /* the IOProc buffer size */
+    property_address.mSelector = kAudioDevicePropertyBufferFrameSize;
+    size = sizeof(v);
+    err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &v);
+    if (!err)
+        total += v;
+    else
+        pa_log_warn("Failed to get buffer frame size: %d", err);
+
+    /* IOProc safety offset - this value is the same for both directions, hence we divide it by 2 */
+    property_address.mSelector = kAudioDevicePropertySafetyOffset;
+    size = sizeof(v);
+    err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &v);
+    if (!err)
+        total += v / 2;
+    else
+        pa_log_warn("Failed to get safety offset: %d", err);
+
+    /* get the stream latency.
+     * FIXME: this assumes the stream latency is the same for all streams */
+    property_address.mSelector = kAudioDevicePropertyStreams;
+    size = sizeof(stream_id);
+    err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &stream_id);
+    if (!err) {
+        property_address.mSelector = kAudioStreamPropertyLatency;
+        property_address.mScope = kAudioObjectPropertyScopeGlobal;
+        size = sizeof(v);
+        err = AudioObjectGetPropertyData(stream_id, &property_address, 0, NULL, &size, &v);
+        if (!err)
+            total += v;
+        else
+            pa_log_warn("Failed to get stream latency: %d", err);
+    } else
+        pa_log_warn("Failed to get streams: %d", err);
+
+    return pa_bytes_to_usec(total * pa_frame_size(ss), ss);
+}
+
+static void ca_device_check_device_state(struct userdata *u) {
+    coreaudio_sink *ca_sink;
+    coreaudio_source *ca_source;
+    bool active = false;
+
+    pa_assert(u);
+
+    for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next)
+        if (ca_sink->active)
+            active = true;
+
+    for (ca_source = u->sources; ca_source; ca_source = ca_source->next)
+        if (ca_source->active)
+            active = true;
+
+    if (active && !u->running)
+        AudioDeviceStart(u->object_id, u->proc_id);
+    else if (!active && u->running)
+        AudioDeviceStop(u->object_id, u->proc_id);
+
+    u->running = active;
+}
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    coreaudio_sink *sink = PA_SINK(o)->userdata;
+    struct userdata *u = sink->userdata;
+    unsigned int i;
+    pa_memchunk audio_chunk;
+
+    switch (code) {
+        case CA_MESSAGE_RENDER: {
+            /* audio out */
+            for (i = 0; i < u->render_output_data->mNumberBuffers; i++) {
+                AudioBuffer *buf = u->render_output_data->mBuffers + i;
+
+                pa_assert(sink);
+
+                if (PA_SINK_IS_OPENED(sink->pa_sink->thread_info.state)) {
+                    audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, false);
+                    audio_chunk.length = buf->mDataByteSize;
+                    audio_chunk.index = 0;
+
+                    pa_sink_render_into_full(sink->pa_sink, &audio_chunk);
+                    pa_memblock_unref_fixed(audio_chunk.memblock);
+                }
+
+                sink = sink->next;
+            }
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            *((int64_t *) data) = get_latency_us(PA_OBJECT(o));
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    coreaudio_source *source = PA_SOURCE(o)->userdata;
+    struct userdata *u = source->userdata;
+    unsigned int i;
+    pa_memchunk audio_chunk;
+
+    switch (code) {
+        case CA_MESSAGE_RENDER: {
+            /* audio in */
+            for (i = 0; i < u->render_input_data->mNumberBuffers; i++) {
+                const AudioBuffer *buf = u->render_input_data->mBuffers + i;
+
+                pa_assert(source);
+
+                if (PA_SOURCE_IS_OPENED(source->pa_source->thread_info.state)) {
+                    audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, true);
+                    audio_chunk.length = buf->mDataByteSize;
+                    audio_chunk.index = 0;
+
+                    pa_source_post(source->pa_source, &audio_chunk);
+                    pa_memblock_unref_fixed(audio_chunk.memblock);
+                }
+
+                source = source->next;
+            }
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            *((int64_t *) data) = get_latency_us(PA_OBJECT(o));
+            return 0;
+        }
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);;
+}
+
+static int ca_sink_set_state(pa_sink *s, pa_sink_state_t state) {
+    coreaudio_sink *sink = s->userdata;
+
+    switch (state) {
+        case PA_SINK_SUSPENDED:
+        case PA_SINK_IDLE:
+            sink->active = false;
+            break;
+
+        case PA_SINK_RUNNING:
+            sink->active = true;
+            break;
+
+        case PA_SINK_UNLINKED:
+        case PA_SINK_INIT:
+        case PA_SINK_INVALID_STATE:
+            ;
+    }
+
+    ca_device_check_device_state(sink->userdata);
+
+    return 0;
+}
+
+/* Caveat: The caller is responsible to get rid of the CFString(Ref). */
+static char * CFString_to_cstr(CFStringRef cfstr) {
+    char *ret = NULL;
+
+    if (cfstr != NULL) {
+        const char *tmp = CFStringGetCStringPtr(cfstr, kCFStringEncodingUTF8);
+        CFIndex n = CFStringGetLength(cfstr) + 1 /* for the terminating NULL */;
+
+        ret = pa_xmalloc(n);
+
+        if (tmp == NULL) {
+            if (!CFStringGetCString(cfstr, ret, n, kCFStringEncodingUTF8)) {
+                pa_xfree(ret);
+                ret = NULL;
+            }
+        } else {
+            strncpy(ret, tmp, n - 1);
+            ret[n - 1] = '\0';
+        }
+    }
+
+    return ret;
+}
+
+static int ca_device_create_sink(pa_module *m, AudioBuffer *buf, int channel_idx) {
+    OSStatus err;
+    UInt32 size;
+    struct userdata *u = m->userdata;
+    pa_sink_new_data new_data;
+    pa_sink_flags_t flags = PA_SINK_LATENCY | PA_SINK_HARDWARE;
+    coreaudio_sink *ca_sink;
+    pa_sink *sink;
+    unsigned int i;
+    char *tmp = NULL;
+    pa_strbuf *strbuf;
+    AudioObjectPropertyAddress property_address;
+    CFStringRef tmp_cfstr = NULL;
+
+    if (buf->mNumberChannels > PA_CHANNELS_MAX) {
+        pa_log("Skipping device with more channels than we support (%u)", (unsigned int) buf->mNumberChannels);
+        return -1;
+    }
+
+    ca_sink = pa_xnew0(coreaudio_sink, 1);
+    ca_sink->map.channels = buf->mNumberChannels;
+    ca_sink->ss.channels = buf->mNumberChannels;
+    ca_sink->channel_idx = channel_idx;
+
+    /* build a name for this stream */
+    strbuf = pa_strbuf_new();
+
+    for (i = 0; i < buf->mNumberChannels; i++) {
+        property_address.mSelector = kAudioObjectPropertyElementName;
+        property_address.mScope = kAudioDevicePropertyScopeOutput;
+        property_address.mElement = channel_idx + i + 1;
+        size = sizeof(tmp_cfstr);
+        err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &tmp_cfstr);
+        if (err == 0) {
+            tmp = CFString_to_cstr(tmp_cfstr);
+
+            if (tmp_cfstr)
+                CFRelease(tmp_cfstr);
+        }
+
+        if (i > 0)
+            pa_strbuf_puts(strbuf, ", ");
+
+        if (err || !tmp || !strlen(tmp))
+            pa_strbuf_printf(strbuf, "Channel %d", (int) property_address.mElement);
+        else
+            pa_strbuf_puts(strbuf, tmp);
+
+        pa_xfree(tmp);
+        tmp = NULL;
+    }
+
+    ca_sink->name = pa_strbuf_to_string_free(strbuf);
+
+    pa_log_debug("Stream name is >%s<", ca_sink->name);
+
+    /* default to mono streams */
+    for (i = 0; i < ca_sink->map.channels; i++)
+        ca_sink->map.map[i] = PA_CHANNEL_POSITION_MONO;
+
+    if (buf->mNumberChannels == 2) {
+        ca_sink->map.map[0] = PA_CHANNEL_POSITION_LEFT;
+        ca_sink->map.map[1] = PA_CHANNEL_POSITION_RIGHT;
+    }
+
+    ca_sink->ss.rate = u->stream_description.mSampleRate;
+    ca_sink->ss.format = PA_SAMPLE_FLOAT32LE;
+
+    pa_sink_new_data_init(&new_data);
+    new_data.card = u->card;
+    new_data.driver = __FILE__;
+    new_data.module = u->module;
+    new_data.namereg_fail = false;
+    pa_sink_new_data_set_name(&new_data, ca_sink->name);
+    pa_sink_new_data_set_channel_map(&new_data, &ca_sink->map);
+    pa_sink_new_data_set_sample_spec(&new_data, &ca_sink->ss);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_PRODUCT_NAME, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "mmap");
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_API, "CoreAudio");
+    pa_proplist_setf(new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) buf->mDataByteSize);
+
+    if (u->vendor_name)
+        pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_VENDOR_NAME, u->vendor_name);
+
+    sink = pa_sink_new(m->core, &new_data, flags);
+    pa_sink_new_data_done(&new_data);
+
+    if (!sink) {
+        pa_log("unable to create sink.");
+        return -1;
+    }
+
+    sink->parent.process_msg = sink_process_msg;
+    sink->userdata = ca_sink;
+    sink->set_state = ca_sink_set_state;
+
+    pa_sink_set_asyncmsgq(sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(sink, u->rtpoll);
+
+    ca_sink->pa_sink = sink;
+    ca_sink->userdata = u;
+
+    PA_LLIST_PREPEND(coreaudio_sink, u->sinks, ca_sink);
+
+    return 0;
+}
+
+static int ca_source_set_state(pa_source *s, pa_source_state_t state) {
+    coreaudio_source *source = s->userdata;
+
+    switch (state) {
+        case PA_SOURCE_SUSPENDED:
+        case PA_SOURCE_IDLE:
+            source->active = false;
+            break;
+
+        case PA_SOURCE_RUNNING:
+            source->active = true;
+            break;
+
+        case PA_SOURCE_UNLINKED:
+        case PA_SOURCE_INIT:
+        case PA_SOURCE_INVALID_STATE:
+            ;
+    }
+
+    ca_device_check_device_state(source->userdata);
+
+    return 0;
+}
+
+static int ca_device_create_source(pa_module *m, AudioBuffer *buf, int channel_idx) {
+    OSStatus err;
+    UInt32 size;
+    struct userdata *u = m->userdata;
+    pa_source_new_data new_data;
+    pa_source_flags_t flags = PA_SOURCE_LATENCY | PA_SOURCE_HARDWARE;
+    coreaudio_source *ca_source;
+    pa_source *source;
+    unsigned int i;
+    char *tmp = NULL;
+    pa_strbuf *strbuf;
+    AudioObjectPropertyAddress property_address;
+    CFStringRef tmp_cfstr = NULL;
+
+    if (buf->mNumberChannels > PA_CHANNELS_MAX) {
+        pa_log("Skipping device with more channels than we support (%u)", (unsigned int) buf->mNumberChannels);
+        return -1;
+    }
+
+    ca_source = pa_xnew0(coreaudio_source, 1);
+    ca_source->map.channels = buf->mNumberChannels;
+    ca_source->ss.channels = buf->mNumberChannels;
+    ca_source->channel_idx = channel_idx;
+
+    /* build a name for this stream */
+    strbuf = pa_strbuf_new();
+
+    for (i = 0; i < buf->mNumberChannels; i++) {
+        property_address.mSelector = kAudioObjectPropertyElementName;
+        property_address.mScope = kAudioDevicePropertyScopeInput;
+        property_address.mElement = channel_idx + i + 1;
+        size = sizeof(tmp_cfstr);
+        err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &tmp_cfstr);
+        if (err == 0) {
+            tmp = CFString_to_cstr(tmp_cfstr);
+
+            if (tmp_cfstr)
+                CFRelease(tmp_cfstr);
+        }
+
+        if (i > 0)
+            pa_strbuf_puts(strbuf, ", ");
+
+        if (err || !tmp || !strlen(tmp))
+            pa_strbuf_printf(strbuf, "Channel %d", (int) property_address.mElement);
+        else
+            pa_strbuf_puts(strbuf, tmp);
+
+        pa_xfree(tmp);
+        tmp = NULL;
+    }
+
+    ca_source->name = pa_strbuf_to_string_free(strbuf);
+
+    pa_log_debug("Stream name is >%s<", ca_source->name);
+
+    /* default to mono streams */
+    for (i = 0; i < ca_source->map.channels; i++)
+        ca_source->map.map[i] = PA_CHANNEL_POSITION_MONO;
+
+    if (buf->mNumberChannels == 2) {
+        ca_source->map.map[0] = PA_CHANNEL_POSITION_LEFT;
+        ca_source->map.map[1] = PA_CHANNEL_POSITION_RIGHT;
+    }
+
+    ca_source->ss.rate = u->stream_description.mSampleRate;
+    ca_source->ss.format = PA_SAMPLE_FLOAT32LE;
+
+    pa_source_new_data_init(&new_data);
+    new_data.card = u->card;
+    new_data.driver = __FILE__;
+    new_data.module = u->module;
+    new_data.namereg_fail = false;
+    pa_source_new_data_set_name(&new_data, ca_source->name);
+    pa_source_new_data_set_channel_map(&new_data, &ca_source->map);
+    pa_source_new_data_set_sample_spec(&new_data, &ca_source->ss);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_PRODUCT_NAME, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "mmap");
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_API, "CoreAudio");
+    pa_proplist_setf(new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) buf->mDataByteSize);
+
+    if (u->vendor_name)
+        pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_VENDOR_NAME, u->vendor_name);
+
+    source = pa_source_new(m->core, &new_data, flags);
+    pa_source_new_data_done(&new_data);
+
+    if (!source) {
+        pa_log("unable to create source.");
+        return -1;
+    }
+
+    source->parent.process_msg = source_process_msg;
+    source->userdata = ca_source;
+    source->set_state = ca_source_set_state;
+
+    pa_source_set_asyncmsgq(source, u->thread_mq.inq);
+    pa_source_set_rtpoll(source, u->rtpoll);
+
+    ca_source->pa_source = source;
+    ca_source->userdata = u;
+
+    PA_LLIST_PREPEND(coreaudio_source, u->sources, ca_source);
+
+    return 0;
+}
+
+static int ca_device_create_streams(pa_module *m, bool direction_in) {
+    OSStatus err;
+    UInt32 size, i, channel_idx;
+    struct userdata *u = m->userdata;
+    AudioBufferList *buffer_list;
+    AudioObjectPropertyAddress property_address;
+
+    property_address.mScope = direction_in ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
+    property_address.mElement = kAudioObjectPropertyElementMaster;
+
+    /* get current stream format */
+    size = sizeof(AudioStreamBasicDescription);
+    property_address.mSelector = kAudioDevicePropertyStreamFormat;
+    err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &u->stream_description);
+    if (err) {
+        /* no appropriate streams found - silently bail. */
+        return -1;
+    }
+
+    if (u->stream_description.mFormatID != kAudioFormatLinearPCM) {
+        pa_log("Unsupported audio format '%c%c%c%c'",
+            (char) (u->stream_description.mFormatID >> 24),
+            (char) (u->stream_description.mFormatID >> 16) & 0xff,
+            (char) (u->stream_description.mFormatID >> 8) & 0xff,
+            (char) (u->stream_description.mFormatID & 0xff));
+        return -1;
+    }
+
+    /* get stream configuration */
+    size = 0;
+    property_address.mSelector = kAudioDevicePropertyStreamConfiguration;
+    err = AudioObjectGetPropertyDataSize(u->object_id, &property_address, 0, NULL, &size);
+    if (err) {
+        pa_log("Failed to get kAudioDevicePropertyStreamConfiguration (%s).", direction_in ? "input" : "output");
+        return -1;
+    }
+
+    if (!size)
+        return 0;
+
+    buffer_list = (AudioBufferList *) pa_xmalloc(size);
+    err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, buffer_list);
+
+    if (!err) {
+        pa_log_debug("Sample rate: %f", u->stream_description.mSampleRate);
+        pa_log_debug("%d bytes per packet",   (unsigned int) u->stream_description.mBytesPerPacket);
+        pa_log_debug("%d frames per packet",  (unsigned int) u->stream_description.mFramesPerPacket);
+        pa_log_debug("%d bytes per frame",    (unsigned int) u->stream_description.mBytesPerFrame);
+        pa_log_debug("%d channels per frame", (unsigned int) u->stream_description.mChannelsPerFrame);
+        pa_log_debug("%d bits per channel",   (unsigned int) u->stream_description.mBitsPerChannel);
+
+        for (channel_idx = 0, i = 0; i < buffer_list->mNumberBuffers; i++) {
+            AudioBuffer *buf = buffer_list->mBuffers + i;
+
+            if (direction_in)
+                ca_device_create_source(m, buf, channel_idx);
+            else
+                ca_device_create_sink(m, buf, channel_idx);
+
+            channel_idx += buf->mNumberChannels;
+        }
+    }
+
+    pa_xfree(buffer_list);
+    return 0;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+    pa_assert(u->module);
+    pa_assert(u->module->core);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->module->core->realtime_scheduling)
+        pa_make_realtime(u->module->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        coreaudio_sink *ca_sink;
+        int ret;
+
+        PA_LLIST_FOREACH(ca_sink, u->sinks) {
+            if (PA_UNLIKELY(ca_sink->pa_sink->thread_info.rewind_requested))
+                pa_sink_process_rewind(ca_sink->pa_sink, 0);
+        }
+
+        ret = pa_rtpoll_run(u->rtpoll);
+
+        if (ret < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->module->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module *m) {
+    OSStatus err;
+    UInt32 size, frames;
+    struct userdata *u = NULL;
+    pa_modargs *ma = NULL;
+    char tmp[64];
+    pa_card_new_data card_new_data;
+    pa_card_profile *p;
+    coreaudio_sink *ca_sink;
+    coreaudio_source *ca_source;
+    AudioObjectPropertyAddress property_address;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    m->userdata = u;
+
+    if (pa_modargs_get_value_u32(ma, "object_id", (unsigned int *) &u->object_id) != 0) {
+        pa_log("Failed to parse object_id argument.");
+        goto fail;
+    }
+
+    property_address.mScope = kAudioObjectPropertyScopeGlobal;
+    property_address.mElement = kAudioObjectPropertyElementMaster;
+
+    /* get device product name */
+    property_address.mSelector = kAudioDevicePropertyDeviceName;
+    size = sizeof(tmp);
+    err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, tmp);
+    if (err) {
+        pa_log("Failed to get kAudioDevicePropertyDeviceName (err = %08x).", (int) err);
+        goto fail;
+    }
+
+    u->device_name = pa_xstrdup(tmp);
+
+    pa_card_new_data_init(&card_new_data);
+    pa_proplist_sets(card_new_data.proplist, PA_PROP_DEVICE_STRING, tmp);
+    card_new_data.driver = __FILE__;
+    pa_card_new_data_set_name(&card_new_data, tmp);
+    pa_log_info("Initializing module for CoreAudio device '%s' (id %d)", tmp, (unsigned int) u->object_id);
+
+    /* get device vendor name (may fail) */
+    property_address.mSelector = kAudioDevicePropertyDeviceManufacturer;
+    size = sizeof(tmp);
+    err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, tmp);
+    if (!err)
+        u->vendor_name = pa_xstrdup(tmp);
+
+    /* add on profile */
+    p = pa_card_profile_new("on", _("On"), 0);
+    pa_hashmap_put(card_new_data.profiles, p->name, p);
+
+    /* create the card object */
+    u->card = pa_card_new(m->core, &card_new_data);
+    if (!u->card) {
+        pa_log("Unable to create card.\n");
+        goto fail;
+    }
+
+    pa_card_new_data_done(&card_new_data);
+    u->card->userdata = u;
+    u->card->set_profile = card_set_profile;
+    pa_card_choose_initial_profile(u->card);
+    pa_card_put(u->card);
+
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->async_msgq = pa_asyncmsgq_new(0);
+    if (!u->async_msgq) {
+        pa_log("pa_asyncmsgq_new() failed.");
+        goto fail;
+    }
+
+    pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->async_msgq);
+
+    PA_LLIST_HEAD_INIT(coreaudio_sink, u->sinks);
+
+    /* create sinks */
+    ca_device_create_streams(m, false);
+
+    /* create sources */
+    ca_device_create_streams(m, true);
+
+    /* create the message thread */
+    if (!(u->thread = pa_thread_new(u->device_name, thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    /* register notification callback for stream format changes */
+    property_address.mSelector = kAudioDevicePropertyStreamFormat;
+    property_address.mScope = kAudioObjectPropertyScopeGlobal;
+    property_address.mElement = kAudioObjectPropertyElementMaster;
+
+    AudioObjectAddPropertyListener(u->object_id, &property_address, ca_stream_format_changed, u);
+
+    /* set number of frames in IOProc */
+    frames = DEFAULT_FRAMES_PER_IOPROC;
+    pa_modargs_get_value_u32(ma, "ioproc_frames", (unsigned int *) &frames);
+
+    property_address.mSelector = kAudioDevicePropertyBufferFrameSize;
+    AudioObjectSetPropertyData(u->object_id, &property_address, 0, NULL, sizeof(frames), &frames);
+    pa_log_debug("%u frames per IOProc\n", (unsigned int) frames);
+
+    /* create one ioproc for both directions */
+    err = AudioDeviceCreateIOProcID(u->object_id, io_render_proc, u, &u->proc_id);
+    if (err) {
+        pa_log("AudioDeviceCreateIOProcID() failed (err = %08x\n).", (int) err);
+        goto fail;
+    }
+
+    for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next)
+        pa_sink_put(ca_sink->pa_sink);
+
+    for (ca_source = u->sources; ca_source; ca_source = ca_source->next)
+        pa_source_put(ca_source->pa_source);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (u)
+        pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+    coreaudio_sink *ca_sink;
+    coreaudio_source *ca_source;
+    AudioObjectPropertyAddress property_address;
+
+    pa_assert(m);
+
+    u = m->userdata;
+    pa_assert(u);
+
+    /* unlink sinks */
+    for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next)
+        if (ca_sink->pa_sink)
+            pa_sink_unlink(ca_sink->pa_sink);
+
+    /* unlink sources */
+    for (ca_source = u->sources; ca_source; ca_source = ca_source->next)
+        if (ca_source->pa_source)
+            pa_source_unlink(ca_source->pa_source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+        pa_thread_mq_done(&u->thread_mq);
+    }
+
+    if (u->async_msgq)
+        pa_asyncmsgq_unref(u->async_msgq);
+
+    /* free sinks */
+    for (ca_sink = u->sinks; ca_sink;) {
+        coreaudio_sink *next = ca_sink->next;
+
+        if (ca_sink->pa_sink)
+            pa_sink_unref(ca_sink->pa_sink);
+
+        pa_xfree(ca_sink->name);
+        pa_xfree(ca_sink);
+        ca_sink = next;
+    }
+
+    /* free sources */
+    for (ca_source = u->sources; ca_source;) {
+        coreaudio_source *next = ca_source->next;
+
+        if (ca_source->pa_source)
+            pa_source_unref(ca_source->pa_source);
+
+        pa_xfree(ca_source->name);
+        pa_xfree(ca_source);
+        ca_source = next;
+    }
+
+    if (u->proc_id) {
+        AudioDeviceStop(u->object_id, u->proc_id);
+        AudioDeviceDestroyIOProcID(u->object_id, u->proc_id);
+    }
+
+    property_address.mSelector = kAudioDevicePropertyStreamFormat;
+    property_address.mScope = kAudioObjectPropertyScopeGlobal;
+    property_address.mElement = kAudioObjectPropertyElementMaster;
+
+    AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &property_address, ca_stream_format_changed, u);
+
+    pa_xfree(u->device_name);
+    pa_xfree(u->vendor_name);
+    pa_rtpoll_free(u->rtpoll);
+    pa_card_free(u->card);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-allow-passthrough.c b/src/modules/module-allow-passthrough.c
new file mode 100644 (file)
index 0000000..31ff270
--- /dev/null
@@ -0,0 +1,312 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright (C) 2014 Collabora Ltd. <http://www.collabora.co.uk/>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+
+#include "module-allow-passthrough-symdef.h"
+
+PA_MODULE_AUTHOR("Guillaume Desmottes");
+PA_MODULE_DESCRIPTION("When a passthrough stream is requested, route all the other streams to a dummy device");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+static const char* const valid_modargs[] = {
+    NULL,
+};
+
+struct userdata {
+    /* (pa_sink *) -> (pa_sink *)
+     * Map the 'real' muted sink to the null-sink currently being used to play
+     * its streams. */
+    pa_hashmap *null_sinks;
+
+    bool moving;
+};
+
+static pa_sink *ensure_null_sink_for_sink(struct userdata *u, pa_sink *s, pa_core *c) {
+    char *t;
+    pa_module *m;
+    pa_sink *sink;
+    uint32_t idx;
+    const char *name;
+
+    sink = pa_hashmap_get(u->null_sinks, s);
+    if (sink != NULL) {
+        /* We already have a null-sink for this sink */
+        return sink;
+    }
+
+    name = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME);
+
+    t = pa_sprintf_malloc("sink_name=allow_passthrough_null_%s sink_properties='device.description=\"%s\"'",
+                          name ? name : "", _("Dummy Output"));
+    m = pa_module_load(c, "module-null-sink", t);
+    pa_xfree(t);
+
+    if (m == NULL)
+        return NULL;
+
+    PA_IDXSET_FOREACH(sink, c->sinks, idx) {
+        if (sink->module->index == m->index) {
+            pa_hashmap_put(u->null_sinks, s, sink);
+            return sink;
+        }
+    }
+
+   return NULL;
+}
+
+static void unload_null_sink_module_for_sink(struct userdata *u, pa_sink *s, pa_core *c) {
+    pa_sink *null_sink;
+
+    null_sink = pa_hashmap_get(u->null_sinks, s);
+    if (null_sink == NULL)
+        return;
+
+    pa_module_unload_request_by_index(c, null_sink->module->index, true);
+
+    pa_hashmap_remove(u->null_sinks, s);
+}
+
+static void move_stream(struct userdata *u, pa_sink_input *i, pa_sink *target) {
+    u->moving = true;
+    if (pa_sink_input_move_to(i, target, false) < 0)
+        pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
+                    pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+    else
+        pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
+                    pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+    u->moving = false;
+}
+
+/* Check if @sink has any passthrough stream, ignoring @ignore */
+static bool sink_has_passthrough_stream(pa_sink *sink, pa_sink_input *ignore)
+{
+    pa_sink_input *stream;
+    uint32_t idx;
+
+    PA_IDXSET_FOREACH(stream, sink->inputs, idx) {
+        if (stream == ignore)
+            continue;
+
+        if (pa_sink_input_is_passthrough(stream))
+            return true;
+    }
+
+    return false;
+}
+
+static pa_hook_result_t new_passthrough_stream(struct userdata *u, pa_core *c, pa_sink *sink, pa_sink_input *i) {
+    uint32_t idx;
+    pa_sink_input *stream;
+    pa_sink *null_sink;
+
+    if (sink_has_passthrough_stream(sink, i)) {
+        pa_log_info("Dropping playing a passthrough stream; ignoring");
+        /* PulseAudio will reject the stream itself */
+        return PA_HOOK_OK;
+    }
+
+    pa_log_info("Just received a passthrough stream; pause all the others streams so it can play");
+
+    null_sink = ensure_null_sink_for_sink(u, sink, c);
+    if (null_sink == NULL)
+        return PA_HOOK_OK;
+
+    PA_IDXSET_FOREACH(stream, sink->inputs, idx) {
+        /* We don't want to move the stream which just moved to the sink and trigger this re-routing */
+        if (stream != i)
+            move_stream(u, stream, null_sink);
+    }
+
+    return PA_HOOK_OK;
+}
+
+/* return a null sink for the new stream if it needs to be re-routed */
+static pa_sink * new_normal_stream(struct userdata *u, pa_core *c, pa_sink *sink) {
+    if (!sink_has_passthrough_stream(sink, NULL))
+        return NULL;
+
+    /* A passthrough stream is already playing on this sink, re-route to a null sink */
+    return ensure_null_sink_for_sink(u, sink, c);
+}
+
+static pa_hook_result_t sink_input_new_cb(pa_core *core, pa_sink_input_new_data *new_data, struct userdata *u) {
+    pa_sink *null_sink;
+
+    pa_core_assert_ref(core);
+    /* This is a bit of a hack, to determine whether the input stream will use
+     * a passthrough stream, the sink should have been selected and a format
+     * renegotiated. This can either happen by an earlier module (e.g. one
+     * doing routing or other policies) and if not pulseaudio core will setup
+     * the defaults after all hooks for this event have been processed.
+     *
+     * Unfortunately if no other module decides on sink/format before this hook
+     * runs, pulse core doing it is too late, so if a sink and/or stream format
+     * haven't been setup & configured just yet do so now using the same code
+     * as pulsecore would use (default sink and higher priority negotiated
+     * format). */
+    if (!new_data->sink) {
+        pa_sink *sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK);
+        pa_return_val_if_fail(sink, -PA_ERR_NOENTITY);
+        pa_sink_input_new_data_set_sink(new_data, sink, false);
+    }
+
+    if (!new_data->format && new_data->nego_formats && !pa_idxset_isempty(new_data->nego_formats))
+        new_data->format = pa_format_info_copy(pa_idxset_first(new_data->nego_formats, NULL));
+
+    if (pa_sink_input_new_data_is_passthrough(new_data))
+        return new_passthrough_stream(u, core, new_data->sink, NULL);
+
+    null_sink = new_normal_stream(u, core, new_data->sink);
+
+    if (null_sink) {
+        pa_log_info("Already playing a passthrough stream; re-routing new stream to the null sink");
+        pa_sink_input_new_data_set_sink(new_data, null_sink, false);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t passthrough_stream_removed(struct userdata *u, pa_core *c, pa_sink_input *i) {
+    uint32_t idx;
+    pa_sink_input *stream;
+    pa_sink *null_sink;
+
+    pa_assert(i->sink);
+
+    null_sink = pa_hashmap_get(u->null_sinks, i->sink);
+    if (null_sink == NULL)
+        return PA_HOOK_OK;
+
+    pa_log_info("Passthrough stream removed; restore all streams");
+
+    PA_IDXSET_FOREACH(stream, null_sink->inputs, idx) {
+        move_stream(u, stream, i->sink);
+    }
+
+    unload_null_sink_module_for_sink(u, i->sink, c);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_removed(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_sink_input_assert_ref(i);
+
+    if (pa_sink_input_is_passthrough(i))
+        return passthrough_stream_removed(u, core, i);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    return sink_input_removed(core, i, u);
+}
+
+static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    if (u->moving)
+        return PA_HOOK_OK;
+
+    return sink_input_removed(core, i, u);
+}
+
+static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_sink *null_sink;
+
+    if (u->moving)
+        return PA_HOOK_OK;
+
+    if (pa_sink_input_is_passthrough(i))
+        /* Passthrough stream has been moved to a new sink */
+        return new_passthrough_stream(u, core, i->sink, i);
+
+    null_sink = new_normal_stream(u, core, i->sink);
+    if (null_sink) {
+        pa_log_info("Already playing a passthrough stream; re-routing moved stream to the null sink");
+        move_stream(u, i, null_sink);
+    }
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        return -1;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+
+    u->null_sinks = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_new_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u);
+
+    u->moving = false;
+
+    pa_modargs_free(ma);
+    return 0;
+}
+
+static void unload_all_null_sink_modules(struct userdata *u, pa_core *c) {
+    void *state = NULL;
+    pa_sink *null_sink;
+
+    PA_HASHMAP_FOREACH(null_sink, u->null_sinks, state)
+        pa_module_unload_request_by_index(c, null_sink->module->index, true);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (m->core->state != PA_CORE_SHUTDOWN)
+        unload_all_null_sink_modules(u, m->core);
+
+    if (u->null_sinks)
+        pa_hashmap_free(u->null_sinks);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c
new file mode 100644 (file)
index 0000000..12f63f7
--- /dev/null
@@ -0,0 +1,189 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2008 Colin Guthrie
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+
+#include "module-always-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION(_("Always keeps at least one sink loaded even if it's a null one"));
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+        "sink_name=<name of sink>");
+
+#define DEFAULT_SINK_NAME "auto_null"
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    NULL,
+};
+
+struct userdata {
+    uint32_t null_module;
+    bool ignore;
+    char *sink_name;
+};
+
+static void load_null_sink_if_needed(pa_core *c, pa_sink *sink, struct userdata* u) {
+    pa_sink *target;
+    uint32_t idx;
+    char *t;
+    pa_module *m;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    if (u->null_module != PA_INVALID_INDEX)
+        return; /* We've already got a null-sink loaded */
+
+    /* Loop through all sinks and check to see if we have *any*
+     * sinks. Ignore the sink passed in (if it's not null), and
+     * don't count filter sinks. */
+    PA_IDXSET_FOREACH(target, c->sinks, idx)
+        if (!sink || ((target != sink) && !pa_sink_is_filter(target)))
+            break;
+
+    if (target)
+        return;
+
+    pa_log_debug("Autoloading null-sink as no other sinks detected.");
+
+    u->ignore = true;
+
+    t = pa_sprintf_malloc("sink_name=%s sink_properties='device.description=\"%s\"'", u->sink_name,
+                          _("Dummy Output"));
+    m = pa_module_load(c, "module-null-sink", t);
+    u->null_module = m ? m->index : PA_INVALID_INDEX;
+    pa_xfree(t);
+
+    u->ignore = false;
+
+    if (!m)
+        pa_log_warn("Unable to load module-null-sink");
+}
+
+static pa_hook_result_t put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+
+    /* This is us detecting ourselves on load... just ignore this. */
+    if (u->ignore)
+        return PA_HOOK_OK;
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    /* Auto-loaded null-sink not active, so ignoring newly detected sink. */
+    if (u->null_module == PA_INVALID_INDEX)
+        return PA_HOOK_OK;
+
+    /* This is us detecting ourselves on load in a different way... just ignore this too. */
+    if (sink->module && sink->module->index == u->null_module)
+        return PA_HOOK_OK;
+
+    /* We don't count filter sinks since they need a real sink */
+    if (pa_sink_is_filter(sink))
+        return PA_HOOK_OK;
+
+    pa_log_info("A new sink has been discovered. Unloading null-sink.");
+
+    pa_module_unload_request_by_index(c, u->null_module, true);
+    u->null_module = PA_INVALID_INDEX;
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+
+    /* First check to see if it's our own null-sink that's been removed... */
+    if (u->null_module != PA_INVALID_INDEX && sink->module && sink->module->index == u->null_module) {
+        pa_log_debug("Autoloaded null-sink removed");
+        u->null_module = PA_INVALID_INDEX;
+        return PA_HOOK_OK;
+    }
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    load_null_sink_if_needed(c, sink, u);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        return -1;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) put_hook_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) unlink_hook_callback, u);
+    u->null_module = PA_INVALID_INDEX;
+    u->ignore = false;
+
+    pa_modargs_free(ma);
+
+    load_null_sink_if_needed(m->core, NULL, u);
+
+    return 0;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->null_module != PA_INVALID_INDEX && m->core->state != PA_CORE_SHUTDOWN)
+        pa_module_unload_request_by_index(m->core, u->null_module, true);
+
+    pa_xfree(u->sink_name);
+    pa_xfree(u);
+}
diff --git a/src/modules/module-augment-properties.c b/src/modules/module-augment-properties.c
new file mode 100644 (file)
index 0000000..541f0e7
--- /dev/null
@@ -0,0 +1,352 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <time.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/client.h>
+#include <pulsecore/conf-parser.h>
+
+#include "module-augment-properties-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Augment the property sets of streams with additional static information");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+#define STAT_INTERVAL 30
+#define MAX_CACHE_SIZE 50
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct rule {
+    time_t timestamp;
+    bool good;
+    time_t mtime;
+    char *process_name;
+    char *application_name;
+    char *icon_name;
+    char *role;
+    pa_proplist *proplist;
+};
+
+struct userdata {
+    pa_hashmap *cache;
+    pa_hook_slot *client_new_slot, *client_proplist_changed_slot;
+};
+
+static void rule_free(struct rule *r) {
+    pa_assert(r);
+
+    pa_xfree(r->process_name);
+    pa_xfree(r->application_name);
+    pa_xfree(r->icon_name);
+    pa_xfree(r->role);
+    if (r->proplist)
+        pa_proplist_free(r->proplist);
+    pa_xfree(r);
+}
+
+static int parse_properties(pa_config_parser_state *state) {
+    struct rule *r;
+    pa_proplist *n;
+
+    pa_assert(state);
+
+    r = state->userdata;
+
+    if (!(n = pa_proplist_from_string(state->rvalue)))
+        return -1;
+
+    if (r->proplist) {
+        pa_proplist_update(r->proplist, PA_UPDATE_MERGE, n);
+        pa_proplist_free(n);
+    } else
+        r->proplist = n;
+
+    return 0;
+}
+
+static int parse_categories(pa_config_parser_state *state) {
+    struct rule *r;
+    const char *split_state = NULL;
+    char *c;
+
+    pa_assert(state);
+
+    r = state->userdata;
+
+    while ((c = pa_split(state->rvalue, ";", &split_state))) {
+
+        if (pa_streq(c, "Game")) {
+            pa_xfree(r->role);
+            r->role = pa_xstrdup("game");
+        } else if (pa_streq(c, "Telephony")) {
+            pa_xfree(r->role);
+            r->role = pa_xstrdup("phone");
+        }
+
+        pa_xfree(c);
+    }
+
+    return 0;
+}
+
+static int check_type(pa_config_parser_state *state) {
+    pa_assert(state);
+
+    return pa_streq(state->rvalue, "Application") ? 0 : -1;
+}
+
+static int catch_all(pa_config_parser_state *state) {
+    return 0;
+}
+
+static void update_rule(struct rule *r) {
+    char *fn;
+    struct stat st;
+    static pa_config_item table[] = {
+        { "Name", pa_config_parse_string,              NULL, "Desktop Entry" },
+        { "Icon", pa_config_parse_string,              NULL, "Desktop Entry" },
+        { "Type", check_type,                          NULL, "Desktop Entry" },
+        { "X-PulseAudio-Properties", parse_properties, NULL, "Desktop Entry" },
+        { "Categories", parse_categories,              NULL, "Desktop Entry" },
+        { NULL,  catch_all, NULL, NULL },
+        { NULL, NULL, NULL, NULL },
+    };
+    bool found = false;
+
+    pa_assert(r);
+    fn = pa_sprintf_malloc(DESKTOPFILEDIR PA_PATH_SEP "%s.desktop", r->process_name);
+
+    if (stat(fn, &st) == 0)
+        found = true;
+    else {
+#ifdef DT_DIR
+        DIR *desktopfiles_dir;
+        struct dirent *dir;
+
+        /* Let's try a more aggressive search, but only one level */
+        if ((desktopfiles_dir = opendir(DESKTOPFILEDIR))) {
+            while ((dir = readdir(desktopfiles_dir))) {
+                if (dir->d_type != DT_DIR
+                    || pa_streq(dir->d_name, ".")
+                    || pa_streq(dir->d_name, ".."))
+                    continue;
+
+                pa_xfree(fn);
+                fn = pa_sprintf_malloc(DESKTOPFILEDIR PA_PATH_SEP "%s" PA_PATH_SEP "%s.desktop", dir->d_name, r->process_name);
+
+                if (stat(fn, &st) == 0) {
+                    found = true;
+                    break;
+                }
+            }
+            closedir(desktopfiles_dir);
+        }
+#endif
+    }
+    if (!found) {
+        r->good = false;
+        pa_xfree(fn);
+        return;
+    }
+
+    if (r->good) {
+        if (st.st_mtime == r->mtime) {
+            /* Theoretically the filename could have changed, but if so
+               having the same mtime is very unlikely so not worth tracking it in r */
+            pa_xfree(fn);
+            return;
+        }
+        pa_log_debug("Found %s (which has been updated since we last checked).", fn);
+    } else
+        pa_log_debug("Found %s.", fn);
+
+    r->good = true;
+    r->mtime = st.st_mtime;
+    pa_xfree(r->application_name);
+    pa_xfree(r->icon_name);
+    pa_xfree(r->role);
+    r->application_name = r->icon_name = r->role = NULL;
+    if (r->proplist)
+        pa_proplist_clear(r->proplist);
+
+    table[0].data = &r->application_name;
+    table[1].data = &r->icon_name;
+
+    if (pa_config_parse(fn, NULL, table, NULL, false, r) < 0)
+        pa_log_warn("Failed to parse .desktop file %s.", fn);
+
+    pa_xfree(fn);
+}
+
+static void apply_rule(struct rule *r, pa_proplist *p) {
+    pa_assert(r);
+    pa_assert(p);
+
+    if (!r->good)
+        return;
+
+    if (r->proplist)
+        pa_proplist_update(p, PA_UPDATE_MERGE, r->proplist);
+
+    if (r->icon_name)
+        if (!pa_proplist_contains(p, PA_PROP_APPLICATION_ICON_NAME))
+            pa_proplist_sets(p, PA_PROP_APPLICATION_ICON_NAME, r->icon_name);
+
+    if (r->application_name) {
+        const char *t;
+
+        t = pa_proplist_gets(p, PA_PROP_APPLICATION_NAME);
+
+        if (!t || pa_streq(t, r->process_name))
+            pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, r->application_name);
+    }
+
+    if (r->role)
+        if (!pa_proplist_contains(p, PA_PROP_MEDIA_ROLE))
+            pa_proplist_sets(p, PA_PROP_MEDIA_ROLE, r->role);
+}
+
+static void make_room(pa_hashmap *cache) {
+    pa_assert(cache);
+
+    while (pa_hashmap_size(cache) >= MAX_CACHE_SIZE) {
+        struct rule *r;
+
+        pa_assert_se(r = pa_hashmap_steal_first(cache));
+        rule_free(r);
+    }
+}
+
+static pa_hook_result_t process(struct userdata *u, pa_proplist *p) {
+    struct rule *r;
+    time_t now;
+    const char *pn;
+
+    pa_assert(u);
+    pa_assert(p);
+
+    if (!(pn = pa_proplist_gets(p, PA_PROP_APPLICATION_PROCESS_BINARY)))
+        return PA_HOOK_OK;
+
+    if (*pn == '.' || strchr(pn, '/'))
+        return PA_HOOK_OK;
+
+    time(&now);
+
+    pa_log_debug("Looking for .desktop file for %s", pn);
+
+    if ((r = pa_hashmap_get(u->cache, pn))) {
+        if (now-r->timestamp > STAT_INTERVAL) {
+            r->timestamp = now;
+            update_rule(r);
+        }
+    } else {
+        make_room(u->cache);
+
+        r = pa_xnew0(struct rule, 1);
+        r->process_name = pa_xstrdup(pn);
+        r->timestamp = now;
+        pa_hashmap_put(u->cache, r->process_name, r);
+        update_rule(r);
+    }
+
+    apply_rule(r, p);
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t client_new_cb(pa_core *core, pa_client_new_data *data, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_assert(data);
+    pa_assert(u);
+
+    return process(u, data->proplist);
+}
+
+static pa_hook_result_t client_proplist_changed_cb(pa_core *core, pa_client *client, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_assert(client);
+    pa_assert(u);
+
+    return process(u, client->proplist);
+}
+
+int pa__init(pa_module *m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+
+    u->cache = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) rule_free);
+    u->client_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CLIENT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) client_new_cb, u);
+    u->client_proplist_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) client_proplist_changed_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->client_new_slot)
+        pa_hook_slot_free(u->client_new_slot);
+    if (u->client_proplist_changed_slot)
+        pa_hook_slot_free(u->client_proplist_changed_slot);
+
+    if (u->cache)
+        pa_hashmap_free(u->cache);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
new file mode 100644 (file)
index 0000000..3c0307b
--- /dev/null
@@ -0,0 +1,670 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/card.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/database.h>
+#include <pulsecore/tagstruct.h>
+
+#include "module-card-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore profile of cards");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_time_event *save_time_event;
+    pa_database *database;
+};
+
+#define ENTRY_VERSION 4
+
+struct port_info {
+    char *name;
+    int64_t offset;
+    char *profile;
+};
+
+struct entry {
+    char *profile;
+    pa_hashmap *ports; /* Port name -> struct port_info */
+    char *preferred_input_port;
+    char *preferred_output_port;
+};
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(u);
+
+    pa_assert(e == u->save_time_event);
+    u->core->mainloop->time_free(u->save_time_event);
+    u->save_time_event = NULL;
+
+    pa_database_sync(u->database);
+    pa_log_info("Synced.");
+}
+
+static void trigger_save(struct userdata *u) {
+    if (u->save_time_event)
+        return;
+
+    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
+}
+
+static void port_info_free(struct port_info *p_info) {
+    pa_assert(p_info);
+
+    pa_xfree(p_info->profile);
+    pa_xfree(p_info->name);
+    pa_xfree(p_info);
+}
+
+static struct entry* entry_new(void) {
+    struct entry *r = pa_xnew0(struct entry, 1);
+    r->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) port_info_free);
+    return r;
+}
+
+static struct port_info *port_info_new(pa_device_port *port) {
+    struct port_info *p_info;
+
+    if (port) {
+        p_info = pa_xnew0(struct port_info, 1);
+        p_info->name = pa_xstrdup(port->name);
+        p_info->offset = port->latency_offset;
+        if (port->preferred_profile)
+            p_info->profile = pa_xstrdup(port->preferred_profile);
+    } else
+        p_info = pa_xnew0(struct port_info, 1);
+
+    return p_info;
+}
+
+static void entry_free(struct entry* e) {
+    pa_assert(e);
+
+    pa_xfree(e->preferred_output_port);
+    pa_xfree(e->preferred_input_port);
+    pa_xfree(e->profile);
+    pa_hashmap_free(e->ports);
+
+    pa_xfree(e);
+}
+
+static struct entry *entry_from_card(pa_card *card) {
+    struct port_info *p_info;
+    struct entry *entry;
+    pa_device_port *port;
+    void *state;
+
+    pa_assert(card);
+
+    entry = entry_new();
+    if (card->save_profile)
+        entry->profile = pa_xstrdup(card->active_profile->name);
+
+    PA_HASHMAP_FOREACH(port, card->ports, state) {
+        p_info = port_info_new(port);
+        pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
+    }
+
+    return entry;
+}
+
+static bool entrys_equal(struct entry *a, struct entry *b) {
+    struct port_info *Ap_info, *Bp_info;
+    void *state;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    if (!pa_streq(a->profile, b->profile) ||
+            pa_hashmap_size(a->ports) != pa_hashmap_size(b->ports))
+        return false;
+
+    PA_HASHMAP_FOREACH(Ap_info, a->ports, state) {
+        if ((Bp_info = pa_hashmap_get(b->ports, Ap_info->name))) {
+            if (Ap_info->offset != Bp_info->offset)
+                return false;
+        } else
+            return false;
+    }
+
+    if (!pa_safe_streq(a->preferred_input_port, b->preferred_input_port))
+        return false;
+
+    if (!pa_safe_streq(a->preferred_output_port, b->preferred_output_port))
+        return false;
+
+    return true;
+}
+
+static bool entry_write(struct userdata *u, const char *name, const struct entry *e) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    bool r;
+    void *state;
+    struct port_info *p_info;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu8(t, ENTRY_VERSION);
+    pa_tagstruct_puts(t, e->profile);
+    pa_tagstruct_putu32(t, pa_hashmap_size(e->ports));
+
+    PA_HASHMAP_FOREACH(p_info, e->ports, state) {
+        pa_tagstruct_puts(t, p_info->name);
+        pa_tagstruct_puts64(t, p_info->offset);
+        pa_tagstruct_puts(t, p_info->profile);
+    }
+
+    pa_tagstruct_puts(t, e->preferred_input_port);
+    pa_tagstruct_puts(t, e->preferred_output_port);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, true) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+
+#define LEGACY_ENTRY_VERSION 1
+static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
+    struct legacy_entry {
+        uint8_t version;
+        char profile[PA_NAME_MAX];
+    } PA_GCC_PACKED ;
+    struct legacy_entry *le;
+    struct entry *e;
+
+    pa_assert(u);
+    pa_assert(data);
+
+    if (data->size != sizeof(struct legacy_entry)) {
+        pa_log_debug("Size does not match.");
+        return NULL;
+    }
+
+    le = (struct legacy_entry*)data->data;
+
+    if (le->version != LEGACY_ENTRY_VERSION) {
+        pa_log_debug("Version mismatch.");
+        return NULL;
+    }
+
+    if (!memchr(le->profile, 0, sizeof(le->profile))) {
+        pa_log_warn("Profile has missing NUL byte.");
+        return NULL;
+    }
+
+    e = entry_new();
+    e->profile = pa_xstrdup(le->profile);
+    return e;
+}
+#endif
+
+static struct entry* entry_read(struct userdata *u, const char *name) {
+    pa_datum key, data;
+    struct entry *e = NULL;
+    pa_tagstruct *t = NULL;
+    const char* profile;
+    uint8_t version;
+
+    pa_assert(u);
+    pa_assert(name);
+
+    key.data = (char*) name;
+    key.size = strlen(name);
+
+    pa_zero(data);
+
+    if (!pa_database_get(u->database, &key, &data)) {
+        pa_log_debug("Database contains no data for key: %s", name);
+        return NULL;
+    }
+
+    t = pa_tagstruct_new_fixed(data.data, data.size);
+    e = entry_new();
+
+    if (pa_tagstruct_getu8(t, &version) < 0 ||
+        version > ENTRY_VERSION ||
+        pa_tagstruct_gets(t, &profile) < 0) {
+
+        goto fail;
+    }
+
+    if (!profile)
+        profile = "";
+
+    e->profile = pa_xstrdup(profile);
+
+    if (version >= 2) {
+        uint32_t port_count = 0;
+        const char *port_name = NULL, *profile_name = NULL;
+        int64_t port_offset = 0;
+        struct port_info *p_info;
+        unsigned i;
+
+        if (pa_tagstruct_getu32(t, &port_count) < 0)
+            goto fail;
+
+        for (i = 0; i < port_count; i++) {
+            if (pa_tagstruct_gets(t, &port_name) < 0 ||
+                !port_name ||
+                pa_hashmap_get(e->ports, port_name) ||
+                pa_tagstruct_gets64(t, &port_offset) < 0)
+                goto fail;
+            if (version >= 3 && pa_tagstruct_gets(t, &profile_name) < 0)
+                goto fail;
+
+            p_info = port_info_new(NULL);
+            p_info->name = pa_xstrdup(port_name);
+            p_info->offset = port_offset;
+            if (profile_name)
+                p_info->profile = pa_xstrdup(profile_name);
+
+            pa_assert_se(pa_hashmap_put(e->ports, p_info->name, p_info) >= 0);
+        }
+    }
+
+    if (version >= 4) {
+        const char *preferred_input_port;
+        const char *preferred_output_port;
+
+        if (pa_tagstruct_gets(t, &preferred_input_port) < 0
+                || pa_tagstruct_gets(t, &preferred_output_port) < 0)
+            goto fail;
+
+        e->preferred_input_port = pa_xstrdup(preferred_input_port);
+        e->preferred_output_port = pa_xstrdup(preferred_output_port);
+    }
+
+    if (!pa_tagstruct_eof(t))
+        goto fail;
+
+    pa_tagstruct_free(t);
+    pa_datum_free(&data);
+
+    return e;
+
+fail:
+
+    pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
+
+    if (e)
+        entry_free(e);
+    if (t)
+        pa_tagstruct_free(t);
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+    pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
+    if ((e = legacy_entry_read(u, &data))) {
+        pa_log_debug("Success. Saving new format for key: %s", name);
+        if (entry_write(u, name, e))
+            trigger_save(u);
+        pa_datum_free(&data);
+        return e;
+    } else
+        pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
+#endif
+
+    pa_datum_free(&data);
+    return NULL;
+}
+
+static void show_full_info(pa_card *card) {
+    pa_assert(card);
+
+    if (card->save_profile)
+        pa_log_info("Storing profile and port latency offsets for card %s.", card->name);
+    else
+        pa_log_info("Storing port latency offsets for card %s.", card->name);
+}
+
+static pa_hook_result_t card_put_hook_callback(pa_core *c, pa_card *card, struct userdata *u) {
+    struct entry *entry, *old;
+
+    pa_assert(card);
+
+    entry = entry_from_card(card);
+
+    if ((old = entry_read(u, card->name))) {
+        if (!card->save_profile)
+            entry->profile = pa_xstrdup(old->profile);
+        if (entrys_equal(entry, old))
+            goto finish;
+    }
+
+    show_full_info(card);
+
+    if (entry_write(u, card->name, entry))
+        trigger_save(u);
+
+finish:
+    entry_free(entry);
+    if (old)
+        entry_free(old);
+
+    return PA_HOOK_OK;
+}
+
+static void update_profile_for_port(struct entry *entry, pa_card *card, pa_device_port *p) {
+    struct port_info *p_info;
+
+    if (p == NULL)
+        return;
+
+    if (!(p_info = pa_hashmap_get(entry->ports, p->name))) {
+        p_info = port_info_new(p);
+        pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
+    }
+
+    if (!pa_safe_streq(p_info->profile, p->preferred_profile)) {
+        pa_xfree(p_info->profile);
+        p_info->profile = pa_xstrdup(p->preferred_profile);
+        pa_log_info("Storing profile %s for port %s on card %s.", p_info->profile, p->name, card->name);
+    }
+}
+
+static pa_hook_result_t card_profile_changed_callback(pa_core *c, pa_card *card, struct userdata *u) {
+    struct entry *entry;
+    pa_sink *sink;
+    pa_source *source;
+    uint32_t state;
+
+    pa_assert(card);
+
+    if (!card->save_profile)
+        return PA_HOOK_OK;
+
+    if ((entry = entry_read(u, card->name))) {
+        pa_xfree(entry->profile);
+        entry->profile = pa_xstrdup(card->active_profile->name);
+        pa_log_info("Storing card profile for card %s.", card->name);
+    } else {
+        entry = entry_from_card(card);
+        show_full_info(card);
+    }
+
+    PA_IDXSET_FOREACH(sink, card->sinks, state)
+        update_profile_for_port(entry, card, sink->active_port);
+    PA_IDXSET_FOREACH(source, card->sources, state)
+        update_profile_for_port(entry, card, source->active_port);
+
+    if (entry_write(u, card->name, entry))
+        trigger_save(u);
+
+    entry_free(entry);
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_profile_added_callback(pa_core *c, pa_card_profile *profile, struct userdata *u) {
+    struct entry *entry;
+
+    pa_assert(profile);
+
+    if (profile->available == PA_AVAILABLE_NO)
+        return PA_HOOK_OK;
+
+    if (!(entry = entry_read(u, profile->card->name)))
+        return PA_HOOK_OK;
+
+    if (pa_safe_streq(entry->profile, profile->name)) {
+        if (pa_card_set_profile(profile->card, profile, true) >= 0)
+            pa_log_info("Restored profile '%s' for card %s.", profile->name, profile->card->name);
+    }
+
+    entry_free(entry);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t port_offset_change_callback(pa_core *c, pa_device_port *port, struct userdata *u) {
+    struct entry *entry;
+    pa_card *card;
+
+    pa_assert(port);
+    card = port->card;
+
+    if ((entry = entry_read(u, card->name))) {
+        struct port_info *p_info;
+
+        if ((p_info = pa_hashmap_get(entry->ports, port->name)))
+            p_info->offset = port->latency_offset;
+        else {
+            p_info = port_info_new(port);
+            pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
+        }
+
+        pa_log_info("Storing latency offset for port %s on card %s.", port->name, card->name);
+
+    } else {
+        entry = entry_from_card(card);
+        show_full_info(card);
+    }
+
+    if (entry_write(u, card->name, entry))
+        trigger_save(u);
+
+    entry_free(entry);
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
+    struct entry *e;
+    void *state;
+    pa_device_port *p;
+    struct port_info *p_info;
+
+    pa_assert(new_data);
+
+    if (!(e = entry_read(u, new_data->name)))
+        return PA_HOOK_OK;
+
+    /* Always restore the latency offsets because their
+     * initial value is always 0 */
+
+    pa_log_info("Restoring port latency offsets for card %s.", new_data->name);
+
+    PA_HASHMAP_FOREACH(p_info, e->ports, state)
+        if ((p = pa_hashmap_get(new_data->ports, p_info->name))) {
+            p->latency_offset = p_info->offset;
+            if (!p->preferred_profile && p_info->profile)
+                pa_device_port_set_preferred_profile(p, p_info->profile);
+        }
+
+    if (e->preferred_input_port) {
+        p = pa_hashmap_get(new_data->ports, e->preferred_input_port);
+        if (p)
+            pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_INPUT, p);
+    }
+
+    if (e->preferred_output_port) {
+        p = pa_hashmap_get(new_data->ports, e->preferred_output_port);
+        if (p)
+            pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_OUTPUT, p);
+    }
+
+    entry_free(e);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_choose_initial_profile_callback(pa_core *core, pa_card *card, struct userdata *u) {
+    struct entry *e;
+
+    if (!(e = entry_read(u, card->name)))
+        return PA_HOOK_OK;
+
+    if (e->profile[0]) {
+        pa_card_profile *profile;
+
+        profile = pa_hashmap_get(card->profiles, e->profile);
+        if (profile) {
+            pa_log_info("Restoring profile '%s' for card %s.", card->active_profile->name, card->name);
+            pa_card_set_profile(card, profile, true);
+        } else {
+            pa_log_debug("Tried to restore profile %s for card %s, but the card doesn't have such profile.",
+                         e->profile, card->name);
+        }
+    }
+
+    entry_free(e);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_preferred_port_changed_callback(pa_core *core, pa_card_preferred_port_changed_hook_data *data,
+                                                             struct userdata *u) {
+    struct entry *e;
+    pa_card *card;
+
+    card = data->card;
+
+    e = entry_read(u, card->name);
+    if (!e)
+        e = entry_from_card(card);
+
+    if (data->direction == PA_DIRECTION_INPUT) {
+        pa_xfree(e->preferred_input_port);
+        e->preferred_input_port = pa_xstrdup(card->preferred_input_port ? card->preferred_input_port->name : NULL);
+    } else {
+        pa_xfree(e->preferred_output_port);
+        e->preferred_output_port = pa_xstrdup(card->preferred_output_port ? card->preferred_output_port->name : NULL);
+    }
+
+    if (entry_write(u, card->name, e))
+        trigger_save(u);
+
+    entry_free(e);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    char *fname;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) card_new_hook_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_CHOOSE_INITIAL_PROFILE], PA_HOOK_NORMAL,
+                           (pa_hook_cb_t) card_choose_initial_profile_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) card_put_hook_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_preferred_port_changed_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_changed_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_ADDED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_added_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) port_offset_change_callback, u);
+
+    if (!(fname = pa_state_path("card-database", true)))
+        goto fail;
+
+    if (!(u->database = pa_database_open(fname, true))) {
+        pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
+        pa_xfree(fname);
+        goto fail;
+    }
+
+    pa_log_info("Successfully opened database file '%s'.", fname);
+    pa_xfree(fname);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->save_time_event) {
+        u->core->mainloop->time_free(u->save_time_event);
+        pa_database_sync(u->database);
+    }
+
+    if (u->database)
+        pa_database_close(u->database);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-cli.c b/src/modules/module-cli.c
new file mode 100644 (file)
index 0000000..a69ad4a
--- /dev/null
@@ -0,0 +1,142 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/cli.h>
+#include <pulsecore/sioman.h>
+#include <pulsecore/log.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "module-cli-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Command line interface");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE("exit_on_eof=<exit daemon after EOF?>");
+
+static const char* const valid_modargs[] = {
+    "exit_on_eof",
+    NULL
+};
+
+static void eof_and_unload_cb(pa_cli*c, void *userdata) {
+    pa_module *m = userdata;
+
+    pa_assert(c);
+    pa_assert(m);
+
+    pa_module_unload_request(m, true);
+}
+
+static void eof_and_exit_cb(pa_cli*c, void *userdata) {
+    pa_module *m = userdata;
+
+    pa_assert(c);
+    pa_assert(m);
+
+    pa_core_exit(m->core, false, 0);
+}
+
+int pa__init(pa_module*m) {
+    pa_iochannel *io;
+    pa_modargs *ma;
+    bool exit_on_eof = false;
+#ifndef OS_IS_WIN32
+    int fd;
+#endif
+
+    pa_assert(m);
+
+    if (m->core->running_as_daemon) {
+        pa_log_info("Running as daemon, refusing to load this module.");
+        return 0;
+    }
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "exit_on_eof", &exit_on_eof) < 0) {
+        pa_log("exit_on_eof= expects boolean argument.");
+        goto fail;
+    }
+
+    if (pa_stdio_acquire() < 0) {
+        pa_log("STDIN/STDOUT already in use.");
+        goto fail;
+    }
+
+    /* We try to open the controlling tty anew here. This has the
+     * benefit of giving us a new fd that doesn't share the O_NDELAY
+     * flag with fds 0, 1, or 2. Since pa_iochannel_xxx needs O_NDELAY
+     * on its fd using those fds directly could set O_NDELAY which
+     * fprintf() doesn't really like, resulting in truncated output
+     * of log messages, particularly because if stdout and stderr are
+     * dup'ed they share the same O_NDELAY, too. */
+
+#ifndef OS_IS_WIN32
+    if ((fd = pa_open_cloexec("/dev/tty", O_RDWR|O_NONBLOCK, 0)) >= 0) {
+        io = pa_iochannel_new(m->core->mainloop, fd, fd);
+        pa_log_debug("Managed to open /dev/tty.");
+    }
+    else
+#endif
+    {
+        io = pa_iochannel_new(m->core->mainloop, STDIN_FILENO, STDOUT_FILENO);
+        pa_iochannel_set_noclose(io, true);
+        pa_log_debug("Failed to open /dev/tty, using stdin/stdout fds instead.");
+    }
+
+    m->userdata = pa_cli_new(m->core, io, m);
+    pa_cli_set_eof_callback(m->userdata, exit_on_eof ? eof_and_exit_cb : eof_and_unload_cb, m);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    pa_assert(m);
+
+    if (m->userdata) {
+        pa_cli_free(m->userdata);
+        pa_stdio_release();
+    }
+}
diff --git a/src/modules/module-combine-sink.c b/src/modules/module-combine-sink.c
new file mode 100644 (file)
index 0000000..4aa7de9
--- /dev/null
@@ -0,0 +1,1565 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/module.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/strlist.h>
+
+#include "module-combine-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Combine multiple sinks to one");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "sink_properties=<properties for the sink> "
+        "slaves=<slave sinks> "
+        "adjust_time=<how often to readjust rates in s> "
+        "resample_method=<method> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map>");
+
+#define DEFAULT_SINK_NAME "combined"
+
+#define MEMBLOCKQ_MAXLENGTH (1024*1024*16)
+
+#define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC)
+
+#define BLOCK_USEC (PA_USEC_PER_MSEC * 200)
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "slaves",
+    "adjust_time",
+    "resample_method",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    NULL
+};
+
+struct output {
+    struct userdata *userdata;
+
+    pa_sink *sink;
+    pa_sink_input *sink_input;
+    bool ignore_state_change;
+
+    /* This message queue is only for POST messages, i.e. the messages that
+     * carry audio data from the sink thread to the output thread. The POST
+     * messages need to be handled in a separate queue, because the queue is
+     * processed not only in the output thread mainloop, but also inside the
+     * sink input pop() callback. Processing other messages (such as
+     * SET_REQUESTED_LATENCY) is not safe inside the pop() callback; at least
+     * one reason why it's not safe is that messages that generate rewind
+     * requests (such as SET_REQUESTED_LATENCY) cause crashes when processed
+     * in the pop() callback. */
+    pa_asyncmsgq *audio_inq;
+
+    /* This message queue is for all other messages than POST from the sink
+     * thread to the output thread (currently "all other messages" means just
+     * the SET_REQUESTED_LATENCY message). */
+    pa_asyncmsgq *control_inq;
+
+    /* Message queue from the output thread to the sink thread. */
+    pa_asyncmsgq *outq;
+
+    pa_rtpoll_item *audio_inq_rtpoll_item_read, *audio_inq_rtpoll_item_write;
+    pa_rtpoll_item *control_inq_rtpoll_item_read, *control_inq_rtpoll_item_write;
+    pa_rtpoll_item *outq_rtpoll_item_read, *outq_rtpoll_item_write;
+
+    pa_memblockq *memblockq;
+
+    /* For communication of the stream latencies to the main thread */
+    pa_usec_t total_latency;
+
+    /* For communication of the stream parameters to the sink thread */
+    pa_atomic_t max_request;
+    pa_atomic_t max_latency;
+    pa_atomic_t min_latency;
+
+    PA_LLIST_FIELDS(struct output);
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    pa_time_event *time_event;
+    pa_usec_t adjust_time;
+
+    bool automatic;
+    bool auto_desc;
+
+    pa_strlist *unlinked_slaves;
+
+    pa_hook_slot *sink_put_slot, *sink_unlink_slot, *sink_state_changed_slot;
+
+    pa_resample_method_t resample_method;
+
+    pa_usec_t block_usec;
+    pa_usec_t default_min_latency;
+    pa_usec_t default_max_latency;
+
+    pa_idxset* outputs; /* managed in main context */
+
+    struct {
+        PA_LLIST_HEAD(struct output, active_outputs); /* managed in IO thread context */
+        pa_atomic_t running;  /* we cache that value here, so that every thread can query it cheaply */
+        pa_usec_t timestamp;
+        bool in_null_mode;
+        pa_smoother *smoother;
+        uint64_t counter;
+    } thread_info;
+};
+
+enum {
+    SINK_MESSAGE_ADD_OUTPUT = PA_SINK_MESSAGE_MAX,
+    SINK_MESSAGE_REMOVE_OUTPUT,
+    SINK_MESSAGE_NEED,
+    SINK_MESSAGE_UPDATE_LATENCY,
+    SINK_MESSAGE_UPDATE_MAX_REQUEST,
+    SINK_MESSAGE_UPDATE_LATENCY_RANGE
+};
+
+enum {
+    SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX,
+    SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY
+};
+
+static void output_disable(struct output *o);
+static void output_enable(struct output *o);
+static void output_free(struct output *o);
+static int output_create_sink_input(struct output *o);
+
+static void adjust_rates(struct userdata *u) {
+    struct output *o;
+    pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency, avg_total_latency = 0;
+    uint32_t base_rate;
+    uint32_t idx;
+    unsigned n = 0;
+
+    pa_assert(u);
+    pa_sink_assert_ref(u->sink);
+
+    if (pa_idxset_size(u->outputs) <= 0)
+        return;
+
+    if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)))
+        return;
+
+    PA_IDXSET_FOREACH(o, u->outputs, idx) {
+        pa_usec_t sink_latency;
+
+        if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
+            continue;
+
+        o->total_latency = pa_sink_input_get_latency(o->sink_input, &sink_latency);
+        o->total_latency += sink_latency;
+
+        if (sink_latency > max_sink_latency)
+            max_sink_latency = sink_latency;
+
+        if (min_total_latency == (pa_usec_t) -1 || o->total_latency < min_total_latency)
+            min_total_latency = o->total_latency;
+
+        avg_total_latency += o->total_latency;
+        n++;
+
+        pa_log_debug("[%s] total=%0.2fms sink=%0.2fms ", o->sink->name, (double) o->total_latency / PA_USEC_PER_MSEC, (double) sink_latency / PA_USEC_PER_MSEC);
+
+        if (o->total_latency > 10*PA_USEC_PER_SEC)
+            pa_log_warn("[%s] Total latency of output is very high (%0.2fms), most likely the audio timing in one of your drivers is broken.", o->sink->name, (double) o->total_latency / PA_USEC_PER_MSEC);
+    }
+
+    if (min_total_latency == (pa_usec_t) -1)
+        return;
+
+    avg_total_latency /= n;
+
+    target_latency = PA_MAX(max_sink_latency, min_total_latency);
+
+    pa_log_info("[%s] avg total latency is %0.2f msec.", u->sink->name, (double) avg_total_latency / PA_USEC_PER_MSEC);
+    pa_log_info("[%s] target latency is %0.2f msec.", u->sink->name, (double) target_latency / PA_USEC_PER_MSEC);
+
+    base_rate = u->sink->sample_spec.rate;
+
+    PA_IDXSET_FOREACH(o, u->outputs, idx) {
+        uint32_t new_rate = base_rate;
+        uint32_t current_rate;
+
+        if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
+            continue;
+
+        current_rate = o->sink_input->sample_spec.rate;
+
+        if (o->total_latency != target_latency)
+            new_rate += (uint32_t) (((double) o->total_latency - (double) target_latency) / (double) u->adjust_time * (double) new_rate);
+
+        if (new_rate < (uint32_t) (base_rate*0.8) || new_rate > (uint32_t) (base_rate*1.25)) {
+            pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", o->sink_input->sink->name, base_rate, new_rate);
+            new_rate = base_rate;
+        } else {
+            if (base_rate < new_rate + 20 && new_rate < base_rate + 20)
+              new_rate = base_rate;
+            /* Do the adjustment in small steps; 2‰ can be considered inaudible */
+            if (new_rate < (uint32_t) (current_rate*0.998) || new_rate > (uint32_t) (current_rate*1.002)) {
+                pa_log_info("[%s] new rate of %u Hz not within 2‰ of %u Hz, forcing smaller adjustment", o->sink_input->sink->name, new_rate, current_rate);
+                new_rate = PA_CLAMP(new_rate, (uint32_t) (current_rate*0.998), (uint32_t) (current_rate*1.002));
+            }
+            pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.2f msec.", o->sink_input->sink->name, new_rate, (double) new_rate / base_rate, (double) o->total_latency / PA_USEC_PER_MSEC);
+        }
+        pa_sink_input_set_rate(o->sink_input, new_rate);
+    }
+
+    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, NULL, (int64_t) avg_total_latency, NULL);
+}
+
+static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+    pa_assert(a);
+    pa_assert(u->time_event == e);
+
+    adjust_rates(u);
+
+    if (pa_sink_get_state(u->sink) == PA_SINK_SUSPENDED) {
+        u->core->mainloop->time_free(e);
+        u->time_event = NULL;
+    } else
+        pa_core_rttime_restart(u->core, e, pa_rtclock_now() + u->adjust_time);
+}
+
+static void process_render_null(struct userdata *u, pa_usec_t now) {
+    size_t ate = 0;
+
+    pa_assert(u);
+    pa_assert(u->sink->thread_info.state == PA_SINK_RUNNING);
+
+    if (u->thread_info.in_null_mode)
+        u->thread_info.timestamp = now;
+
+    while (u->thread_info.timestamp < now + u->block_usec) {
+        pa_memchunk chunk;
+
+        pa_sink_render(u->sink, u->sink->thread_info.max_request, &chunk);
+        pa_memblock_unref(chunk.memblock);
+
+        u->thread_info.counter += chunk.length;
+
+/*         pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */
+        u->thread_info.timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
+
+        ate += chunk.length;
+
+        if (ate >= u->sink->thread_info.max_request)
+            break;
+    }
+
+/*     pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */
+
+    pa_smoother_put(u->thread_info.smoother, now,
+                    pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec) - (u->thread_info.timestamp - now));
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority+1);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    u->thread_info.timestamp = pa_rtclock_now();
+    u->thread_info.in_null_mode = false;
+
+    for (;;) {
+        int ret;
+
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+            pa_sink_process_rewind(u->sink, 0);
+
+        /* If no outputs are connected, render some data and drop it immediately. */
+        if (u->sink->thread_info.state == PA_SINK_RUNNING && !u->thread_info.active_outputs) {
+            pa_usec_t now;
+
+            now = pa_rtclock_now();
+
+            if (!u->thread_info.in_null_mode || u->thread_info.timestamp <= now)
+                process_render_null(u, now);
+
+            pa_rtpoll_set_timer_absolute(u->rtpoll, u->thread_info.timestamp);
+            u->thread_info.in_null_mode = true;
+        } else {
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+            u->thread_info.in_null_mode = false;
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) {
+            pa_log_info("pa_rtpoll_run() = %i", ret);
+            goto fail;
+        }
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+/* Called from combine sink I/O thread context */
+static void render_memblock(struct userdata *u, struct output *o, size_t length) {
+    pa_assert(u);
+    pa_assert(o);
+
+    /* We are run by the sink thread, on behalf of an output (o). The
+     * output is waiting for us, hence it is safe to access its
+     * mainblockq and asyncmsgq directly. */
+
+    /* If we are not running, we cannot produce any data */
+    if (!pa_atomic_load(&u->thread_info.running))
+        return;
+
+    /* Maybe there's some data in the requesting output's queue
+     * now? */
+    while (pa_asyncmsgq_process_one(o->audio_inq) > 0)
+        ;
+
+    /* Ok, now let's prepare some data if we really have to */
+    while (!pa_memblockq_is_readable(o->memblockq)) {
+        struct output *j;
+        pa_memchunk chunk;
+
+        /* Render data! */
+        pa_sink_render(u->sink, length, &chunk);
+
+        u->thread_info.counter += chunk.length;
+
+        /* OK, let's send this data to the other threads */
+        PA_LLIST_FOREACH(j, u->thread_info.active_outputs) {
+            if (j == o)
+                continue;
+
+            pa_asyncmsgq_post(j->audio_inq, PA_MSGOBJECT(j->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, &chunk, NULL);
+        }
+
+        /* And place it directly into the requesting output's queue */
+        pa_memblockq_push_align(o->memblockq, &chunk);
+        pa_memblock_unref(chunk.memblock);
+    }
+}
+
+/* Called from I/O thread context */
+static void request_memblock(struct output *o, size_t length) {
+    pa_assert(o);
+    pa_sink_input_assert_ref(o->sink_input);
+    pa_sink_assert_ref(o->userdata->sink);
+
+    /* If another thread already prepared some data we received
+     * the data over the asyncmsgq, hence let's first process
+     * it. */
+    while (pa_asyncmsgq_process_one(o->audio_inq) > 0)
+        ;
+
+    /* Check whether we're now readable */
+    if (pa_memblockq_is_readable(o->memblockq))
+        return;
+
+    /* OK, we need to prepare new data, but only if the sink is actually running */
+    if (pa_atomic_load(&o->userdata->thread_info.running))
+        pa_asyncmsgq_send(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_NEED, o, (int64_t) length, NULL);
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    /* If necessary, get some new data */
+    request_memblock(o, nbytes);
+
+    /* pa_log("%s q size is %u + %u (%u/%u)", */
+    /*        i->sink->name, */
+    /*        pa_memblockq_get_nblocks(o->memblockq), */
+    /*        pa_memblockq_get_nblocks(i->thread_info.render_memblockq), */
+    /*        pa_memblockq_get_maxrewind(o->memblockq), */
+    /*        pa_memblockq_get_maxrewind(i->thread_info.render_memblockq)); */
+
+    if (pa_memblockq_peek(o->memblockq, chunk) < 0)
+        return -1;
+
+    pa_memblockq_drop(o->memblockq, chunk->length);
+
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    pa_memblockq_rewind(o->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    pa_memblockq_set_maxrewind(o->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    if (pa_atomic_load(&o->max_request) == (int) nbytes)
+        return;
+
+    pa_atomic_store(&o->max_request, (int) nbytes);
+    pa_log_debug("Sink input update max request %lu", (unsigned long) nbytes);
+    pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL);
+}
+
+/* Called from thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+    struct output *o;
+    pa_usec_t min, max, fix;
+
+    pa_assert(i);
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    fix = i->sink->thread_info.fixed_latency;
+    if (fix > 0) {
+        min = fix;
+        max = fix;
+    } else {
+        min = i->sink->thread_info.min_latency;
+        max = i->sink->thread_info.max_latency;
+    }
+
+    if ((pa_atomic_load(&o->min_latency) == (int) min) &&
+        (pa_atomic_load(&o->max_latency) == (int) max))
+        return;
+
+    pa_atomic_store(&o->min_latency, (int) min);
+    pa_atomic_store(&o->max_latency, (int) max);
+    pa_log_debug("Sink input update latency range %lu %lu", (unsigned long) min, (unsigned long) max);
+    pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_LATENCY_RANGE, NULL, 0, NULL, NULL);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct output *o;
+    pa_usec_t fix, min, max;
+    size_t nbytes;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    /* Set up the queue from the sink thread to us */
+    pa_assert(!o->audio_inq_rtpoll_item_read);
+    pa_assert(!o->control_inq_rtpoll_item_read);
+    pa_assert(!o->outq_rtpoll_item_write);
+
+    o->audio_inq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
+            i->sink->thread_info.rtpoll,
+            PA_RTPOLL_LATE,  /* This one is not that important, since we check for data in _peek() anyway. */
+            o->audio_inq);
+
+    o->control_inq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
+            i->sink->thread_info.rtpoll,
+            PA_RTPOLL_NORMAL,
+            o->control_inq);
+
+    o->outq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+            i->sink->thread_info.rtpoll,
+            PA_RTPOLL_EARLY,
+            o->outq);
+
+    pa_sink_input_request_rewind(i, 0, false, true, true);
+
+    nbytes = pa_sink_input_get_max_request(i);
+    pa_atomic_store(&o->max_request, (int) nbytes);
+    pa_log_debug("attach max request %lu", (unsigned long) nbytes);
+
+    fix = i->sink->thread_info.fixed_latency;
+    if (fix > 0) {
+        min = max = fix;
+    } else {
+        min = i->sink->thread_info.min_latency;
+        max = i->sink->thread_info.max_latency;
+    }
+    pa_atomic_store(&o->min_latency, (int) min);
+    pa_atomic_store(&o->max_latency, (int) max);
+    pa_log_debug("attach latency range %lu %lu", (unsigned long) min, (unsigned long) max);
+
+    /* We register the output. That means that the sink will start to pass data to
+     * this output. */
+    pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    /* We unregister the output. That means that the sink doesn't
+     * pass any further data to this output */
+    pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
+
+    if (o->audio_inq_rtpoll_item_read) {
+        pa_rtpoll_item_free(o->audio_inq_rtpoll_item_read);
+        o->audio_inq_rtpoll_item_read = NULL;
+    }
+
+    if (o->control_inq_rtpoll_item_read) {
+        pa_rtpoll_item_free(o->control_inq_rtpoll_item_read);
+        o->control_inq_rtpoll_item_read = NULL;
+    }
+
+    if (o->outq_rtpoll_item_write) {
+        pa_rtpoll_item_free(o->outq_rtpoll_item_write);
+        o->outq_rtpoll_item_write = NULL;
+    }
+
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    pa_module_unload_request(o->userdata->module, true);
+    pa_idxset_remove_by_data(o->userdata->outputs, o, NULL);
+    output_free(o);
+}
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct output *o = PA_SINK_INPUT(obj)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = data;
+
+            *r = pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &o->sink_input->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+            break;
+        }
+
+        case SINK_INPUT_MESSAGE_POST:
+
+            if (PA_SINK_IS_OPENED(o->sink_input->sink->thread_info.state))
+                pa_memblockq_push_align(o->memblockq, chunk);
+            else
+                pa_memblockq_flush_write(o->memblockq, true);
+
+            return 0;
+
+        case SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: {
+            pa_usec_t latency = (pa_usec_t) offset;
+
+            pa_sink_input_set_requested_latency_within_thread(o->sink_input, latency);
+
+            return 0;
+        }
+    }
+
+    return pa_sink_input_process_msg(obj, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static void suspend(struct userdata *u) {
+    struct output *o;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    /* Let's suspend by unlinking all streams */
+    PA_IDXSET_FOREACH(o, u->outputs, idx)
+        output_disable(o);
+
+    pa_log_info("Device suspended...");
+}
+
+/* Called from main context */
+static void unsuspend(struct userdata *u) {
+    struct output *o;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    /* Let's resume */
+    PA_IDXSET_FOREACH(o, u->outputs, idx)
+        output_enable(o);
+
+    if (!u->time_event && u->adjust_time > 0)
+        u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + u->adjust_time, time_callback, u);
+
+    pa_log_info("Resumed successfully...");
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(sink);
+    pa_assert_se(u = sink->userdata);
+
+    /* Please note that in contrast to the ALSA modules we call
+     * suspend/unsuspend from main context here! */
+
+    switch (state) {
+        case PA_SINK_SUSPENDED:
+            pa_assert(PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
+
+            suspend(u);
+            break;
+
+        case PA_SINK_IDLE:
+        case PA_SINK_RUNNING:
+
+            if (pa_sink_get_state(u->sink) == PA_SINK_SUSPENDED)
+                unsuspend(u);
+
+            break;
+
+        case PA_SINK_UNLINKED:
+        case PA_SINK_INIT:
+        case PA_SINK_INVALID_STATE:
+            ;
+    }
+
+    return 0;
+}
+
+/* Called from IO context */
+static void update_max_request(struct userdata *u) {
+    size_t max_request = 0;
+    struct output *o;
+
+    pa_assert(u);
+    pa_sink_assert_io_context(u->sink);
+
+    /* Collects the max_request values of all streams and sets the
+     * largest one locally */
+
+    PA_LLIST_FOREACH(o, u->thread_info.active_outputs) {
+        size_t mr = (size_t) pa_atomic_load(&o->max_request);
+
+        if (mr > max_request)
+            max_request = mr;
+    }
+
+    if (max_request <= 0)
+        max_request = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
+    pa_log_debug("Sink update max request %lu", (unsigned long) max_request);
+    pa_sink_set_max_request_within_thread(u->sink, max_request);
+}
+
+/* Called from IO context */
+static void update_latency_range(struct userdata *u) {
+    pa_usec_t min_latency = 0, max_latency = (pa_usec_t) -1;
+    struct output *o;
+
+    pa_assert(u);
+    pa_sink_assert_io_context(u->sink);
+
+    /* Collects the latency_range values of all streams and sets
+     * the max of min and min of max locally */
+    PA_LLIST_FOREACH(o, u->thread_info.active_outputs) {
+        pa_usec_t min = (size_t) pa_atomic_load(&o->min_latency);
+        pa_usec_t max = (size_t) pa_atomic_load(&o->max_latency);
+
+        if (min > min_latency)
+            min_latency = min;
+        if (max_latency == (pa_usec_t) -1 || max < max_latency)
+            max_latency = max;
+    }
+    if (max_latency == (pa_usec_t) -1) {
+        /* No outputs, use default limits. */
+        min_latency = u->default_min_latency;
+        max_latency = u->default_max_latency;
+    }
+
+    /* As long as we don't support rewinding, we should limit the max latency
+     * to a conservative value. */
+    if (max_latency > u->default_max_latency)
+        max_latency = u->default_max_latency;
+
+    /* Never ever try to set lower max latency than min latency, it just
+     * doesn't make sense. */
+    if (max_latency < min_latency)
+        max_latency = min_latency;
+
+    pa_log_debug("Sink update latency range %" PRIu64 " %" PRIu64, min_latency, max_latency);
+    pa_sink_set_latency_range_within_thread(u->sink, min_latency, max_latency);
+}
+
+/* Called from thread context of the io thread */
+static void output_add_within_thread(struct output *o) {
+    pa_assert(o);
+    pa_sink_assert_io_context(o->sink);
+
+    PA_LLIST_PREPEND(struct output, o->userdata->thread_info.active_outputs, o);
+
+    pa_assert(!o->outq_rtpoll_item_read);
+    pa_assert(!o->audio_inq_rtpoll_item_write);
+    pa_assert(!o->control_inq_rtpoll_item_write);
+
+    o->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
+            o->userdata->rtpoll,
+            PA_RTPOLL_EARLY-1,  /* This item is very important */
+            o->outq);
+    o->audio_inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+            o->userdata->rtpoll,
+            PA_RTPOLL_EARLY,
+            o->audio_inq);
+    o->control_inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+            o->userdata->rtpoll,
+            PA_RTPOLL_NORMAL,
+            o->control_inq);
+}
+
+/* Called from thread context of the io thread */
+static void output_remove_within_thread(struct output *o) {
+    pa_assert(o);
+    pa_sink_assert_io_context(o->sink);
+
+    PA_LLIST_REMOVE(struct output, o->userdata->thread_info.active_outputs, o);
+
+    if (o->outq_rtpoll_item_read) {
+        pa_rtpoll_item_free(o->outq_rtpoll_item_read);
+        o->outq_rtpoll_item_read = NULL;
+    }
+
+    if (o->audio_inq_rtpoll_item_write) {
+        pa_rtpoll_item_free(o->audio_inq_rtpoll_item_write);
+        o->audio_inq_rtpoll_item_write = NULL;
+    }
+
+    if (o->control_inq_rtpoll_item_write) {
+        pa_rtpoll_item_free(o->control_inq_rtpoll_item_write);
+        o->control_inq_rtpoll_item_write = NULL;
+    }
+}
+
+/* Called from sink I/O thread context */
+static void sink_update_requested_latency(pa_sink *s) {
+    struct userdata *u;
+    struct output *o;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    u->block_usec = pa_sink_get_requested_latency_within_thread(s);
+
+    if (u->block_usec == (pa_usec_t) -1)
+        u->block_usec = s->thread_info.max_latency;
+
+    pa_log_debug("Sink update requested latency %0.2f", (double) u->block_usec / PA_USEC_PER_MSEC);
+
+    /* Just hand this one over to all sink_inputs */
+    PA_LLIST_FOREACH(o, u->thread_info.active_outputs) {
+        pa_asyncmsgq_post(o->control_inq, PA_MSGOBJECT(o->sink_input), SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL,
+                          u->block_usec, NULL, NULL);
+    }
+}
+
+
+/* Called from thread context of the io thread */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            bool running = (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING);
+
+            pa_atomic_store(&u->thread_info.running, running);
+
+            if (running)
+                pa_smoother_resume(u->thread_info.smoother, pa_rtclock_now(), true);
+            else
+                pa_smoother_pause(u->thread_info.smoother, pa_rtclock_now());
+
+            break;
+        }
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t x, y, c;
+            int64_t *delay = data;
+
+            x = pa_rtclock_now();
+            y = pa_smoother_get(u->thread_info.smoother, x);
+
+            c = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec);
+
+            *delay = (int64_t)c - y;
+
+            return 0;
+        }
+
+        case SINK_MESSAGE_ADD_OUTPUT:
+            output_add_within_thread(data);
+            update_max_request(u);
+            update_latency_range(u);
+            return 0;
+
+        case SINK_MESSAGE_REMOVE_OUTPUT:
+            output_remove_within_thread(data);
+            update_max_request(u);
+            update_latency_range(u);
+            return 0;
+
+        case SINK_MESSAGE_NEED:
+            render_memblock(u, (struct output*) data, (size_t) offset);
+            return 0;
+
+        case SINK_MESSAGE_UPDATE_LATENCY: {
+            pa_usec_t x, y, latency = (pa_usec_t) offset;
+
+            x = pa_rtclock_now();
+            y = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec);
+
+            if (y > latency)
+                y -= latency;
+            else
+                y = 0;
+
+            pa_smoother_put(u->thread_info.smoother, x, y);
+            return 0;
+        }
+
+        case SINK_MESSAGE_UPDATE_MAX_REQUEST:
+            update_max_request(u);
+            break;
+
+        case SINK_MESSAGE_UPDATE_LATENCY_RANGE:
+            update_latency_range(u);
+            break;
+
+}
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void update_description(struct userdata *u) {
+    bool first = true;
+    char *t;
+    struct output *o;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    if (!u->auto_desc)
+        return;
+
+    if (pa_idxset_isempty(u->outputs)) {
+        pa_sink_set_description(u->sink, "Simultaneous output");
+        return;
+    }
+
+    t = pa_xstrdup("Simultaneous output to");
+
+    PA_IDXSET_FOREACH(o, u->outputs, idx) {
+        char *e;
+
+        if (first) {
+            e = pa_sprintf_malloc("%s %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+            first = false;
+        } else
+            e = pa_sprintf_malloc("%s, %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+
+        pa_xfree(t);
+        t = e;
+    }
+
+    pa_sink_set_description(u->sink, t);
+    pa_xfree(t);
+}
+
+static int output_create_sink_input(struct output *o) {
+    struct userdata *u;
+    pa_sink_input_new_data data;
+
+    pa_assert(o);
+
+    if (o->sink_input)
+        return 0;
+
+    u = o->userdata;
+
+    pa_sink_input_new_data_init(&data);
+    pa_sink_input_new_data_set_sink(&data, o->sink, false);
+    data.driver = __FILE__;
+    pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_sink_input_new_data_set_sample_spec(&data, &u->sink->sample_spec);
+    pa_sink_input_new_data_set_channel_map(&data, &u->sink->channel_map);
+    data.module = u->module;
+    data.resample_method = u->resample_method;
+    data.flags = PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE|PA_SINK_INPUT_NO_CREATE_ON_SUSPEND;
+
+    pa_sink_input_new(&o->sink_input, u->core, &data);
+
+    pa_sink_input_new_data_done(&data);
+
+    if (!o->sink_input)
+        return -1;
+
+    o->sink_input->parent.process_msg = sink_input_process_msg;
+    o->sink_input->pop = sink_input_pop_cb;
+    o->sink_input->process_rewind = sink_input_process_rewind_cb;
+    o->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    o->sink_input->update_max_request = sink_input_update_max_request_cb;
+    o->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+    o->sink_input->attach = sink_input_attach_cb;
+    o->sink_input->detach = sink_input_detach_cb;
+    o->sink_input->kill = sink_input_kill_cb;
+    o->sink_input->userdata = o;
+
+    pa_sink_input_set_requested_latency(o->sink_input, pa_sink_get_requested_latency(u->sink));
+
+    return 0;
+}
+
+/* Called from main context */
+static struct output *output_new(struct userdata *u, pa_sink *sink) {
+    struct output *o;
+
+    pa_assert(u);
+    pa_assert(sink);
+    pa_assert(u->sink);
+
+    o = pa_xnew0(struct output, 1);
+    o->userdata = u;
+
+    o->audio_inq = pa_asyncmsgq_new(0);
+    if (!o->audio_inq) {
+        pa_log("pa_asyncmsgq_new() failed.");
+        goto fail;
+    }
+
+    o->control_inq = pa_asyncmsgq_new(0);
+    if (!o->control_inq) {
+        pa_log("pa_asyncmsgq_new() failed.");
+        goto fail;
+    }
+
+    o->outq = pa_asyncmsgq_new(0);
+    if (!o->outq) {
+        pa_log("pa_asyncmsgq_new() failed.");
+        goto fail;
+    }
+
+    o->sink = sink;
+    o->memblockq = pa_memblockq_new(
+            "module-combine-sink output memblockq",
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            MEMBLOCKQ_MAXLENGTH,
+            &u->sink->sample_spec,
+            1,
+            0,
+            0,
+            &u->sink->silence);
+
+    pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0);
+    update_description(u);
+
+    return o;
+
+fail:
+    output_free(o);
+
+    return NULL;
+}
+
+/* Called from main context */
+static void output_free(struct output *o) {
+    pa_assert(o);
+
+    output_disable(o);
+    update_description(o->userdata);
+
+    if (o->audio_inq_rtpoll_item_read)
+        pa_rtpoll_item_free(o->audio_inq_rtpoll_item_read);
+    if (o->audio_inq_rtpoll_item_write)
+        pa_rtpoll_item_free(o->audio_inq_rtpoll_item_write);
+
+    if (o->control_inq_rtpoll_item_read)
+        pa_rtpoll_item_free(o->control_inq_rtpoll_item_read);
+    if (o->control_inq_rtpoll_item_write)
+        pa_rtpoll_item_free(o->control_inq_rtpoll_item_write);
+
+    if (o->outq_rtpoll_item_read)
+        pa_rtpoll_item_free(o->outq_rtpoll_item_read);
+    if (o->outq_rtpoll_item_write)
+        pa_rtpoll_item_free(o->outq_rtpoll_item_write);
+
+    if (o->audio_inq)
+        pa_asyncmsgq_unref(o->audio_inq);
+
+    if (o->control_inq)
+        pa_asyncmsgq_unref(o->control_inq);
+
+    if (o->outq)
+        pa_asyncmsgq_unref(o->outq);
+
+    if (o->memblockq)
+        pa_memblockq_free(o->memblockq);
+
+    pa_xfree(o);
+}
+
+/* Called from main context */
+static void output_enable(struct output *o) {
+    pa_assert(o);
+
+    if (o->sink_input)
+        return;
+
+    /* This might cause the sink to be resumed. The state change hook
+     * of the sink might hence be called from here, which might then
+     * cause us to be called in a loop. Make sure that state changes
+     * for this output don't cause this loop by setting a flag here */
+    o->ignore_state_change = true;
+
+    if (output_create_sink_input(o) >= 0) {
+
+        if (pa_sink_get_state(o->sink) != PA_SINK_INIT) {
+            /* Enable the sink input. That means that the sink
+             * is now asked for new data. */
+            pa_sink_input_put(o->sink_input);
+        }
+    }
+
+    o->ignore_state_change = false;
+}
+
+/* Called from main context */
+static void output_disable(struct output *o) {
+    pa_assert(o);
+
+    if (!o->sink_input)
+        return;
+
+    /* We disable the sink input. That means that the sink is
+     * not asked for new data anymore  */
+    pa_sink_input_unlink(o->sink_input);
+
+    /* Now deallocate the stream */
+    pa_sink_input_unref(o->sink_input);
+    o->sink_input = NULL;
+
+    /* Finally, drop all queued data */
+    pa_memblockq_flush_write(o->memblockq, true);
+    pa_asyncmsgq_flush(o->audio_inq, false);
+    pa_asyncmsgq_flush(o->control_inq, false);
+    pa_asyncmsgq_flush(o->outq, false);
+}
+
+/* Called from main context */
+static void output_verify(struct output *o) {
+    pa_assert(o);
+
+    if (PA_SINK_IS_OPENED(pa_sink_get_state(o->userdata->sink)))
+        output_enable(o);
+    else
+        output_disable(o);
+}
+
+/* Called from main context */
+static bool is_suitable_sink(struct userdata *u, pa_sink *s) {
+    const char *t;
+
+    pa_sink_assert_ref(s);
+
+    if (s == u->sink)
+        return false;
+
+    if (!(s->flags & PA_SINK_HARDWARE))
+        return false;
+
+    if (!(s->flags & PA_SINK_LATENCY))
+        return false;
+
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS)))
+        if (!pa_streq(t, "sound"))
+            return false;
+
+    return true;
+}
+
+/* Called from main context */
+static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+    struct output *o;
+
+    pa_core_assert_ref(c);
+    pa_sink_assert_ref(s);
+    pa_assert(u);
+
+    if (u->automatic) {
+        if (!is_suitable_sink(u, s))
+            return PA_HOOK_OK;
+    } else {
+        /* Check if the sink is a previously unlinked slave (non-automatic mode) */
+        pa_strlist *l = u->unlinked_slaves;
+
+        while (l && !pa_streq(pa_strlist_data(l), s->name))
+            l = pa_strlist_next(l);
+
+        if (!l)
+            return PA_HOOK_OK;
+
+        u->unlinked_slaves = pa_strlist_remove(u->unlinked_slaves, s->name);
+    }
+
+    pa_log_info("Configuring new sink: %s", s->name);
+    if (!(o = output_new(u, s))) {
+        pa_log("Failed to create sink input on sink '%s'.", s->name);
+        return PA_HOOK_OK;
+    }
+
+    output_verify(o);
+
+    return PA_HOOK_OK;
+}
+
+/* Called from main context */
+static struct output* find_output(struct userdata *u, pa_sink *s) {
+    struct output *o;
+    uint32_t idx;
+
+    pa_assert(u);
+    pa_assert(s);
+
+    if (u->sink == s)
+        return NULL;
+
+    PA_IDXSET_FOREACH(o, u->outputs, idx)
+        if (o->sink == s)
+            return o;
+
+    return NULL;
+}
+
+/* Called from main context */
+static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+    struct output *o;
+
+    pa_assert(c);
+    pa_sink_assert_ref(s);
+    pa_assert(u);
+
+    if (!(o = find_output(u, s)))
+        return PA_HOOK_OK;
+
+    pa_log_info("Unconfiguring sink: %s", s->name);
+
+    if (!u->automatic)
+        u->unlinked_slaves = pa_strlist_prepend(u->unlinked_slaves, s->name);
+
+    pa_idxset_remove_by_data(u->outputs, o, NULL);
+    output_free(o);
+
+    return PA_HOOK_OK;
+}
+
+/* Called from main context */
+static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+    struct output *o;
+
+    if (!(o = find_output(u, s)))
+        return PA_HOOK_OK;
+
+    /* This state change might be triggered because we are creating a
+     * stream here, in that case we don't want to create it a second
+     * time here and enter a loop */
+    if (o->ignore_state_change)
+        return PA_HOOK_OK;
+
+    output_verify(o);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    const char *slaves, *rm;
+    int resample_method = PA_RESAMPLER_TRIVIAL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    struct output *o;
+    uint32_t idx;
+    pa_sink_new_data data;
+    uint32_t adjust_time_sec;
+    size_t nbytes;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments");
+        goto fail;
+    }
+
+    if ((rm = pa_modargs_get_value(ma, "resample_method", NULL))) {
+        if ((resample_method = pa_parse_resample_method(rm)) < 0) {
+            pa_log("invalid resample method '%s'", rm);
+            goto fail;
+        }
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->resample_method = resample_method;
+    u->outputs = pa_idxset_new(NULL, NULL);
+    u->thread_info.smoother = pa_smoother_new(
+            PA_USEC_PER_SEC,
+            PA_USEC_PER_SEC*2,
+            true,
+            true,
+            10,
+            pa_rtclock_now(),
+            true);
+
+    adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
+    if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
+        pa_log("Failed to parse adjust_time value");
+        goto fail;
+    }
+
+    if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC)
+        u->adjust_time = adjust_time_sec * PA_USEC_PER_SEC;
+    else
+        u->adjust_time = DEFAULT_ADJUST_TIME_USEC;
+
+    slaves = pa_modargs_get_value(ma, "slaves", NULL);
+    u->automatic = !slaves;
+
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
+
+    /* Check the specified slave sinks for sample_spec and channel_map to use for the combined sink */
+    if (!u->automatic) {
+        const char*split_state = NULL;
+        char *n = NULL;
+        pa_sample_spec slaves_spec;
+        pa_channel_map slaves_map;
+        bool is_first_slave = true;
+
+        pa_sample_spec_init(&slaves_spec);
+
+        while ((n = pa_split(slaves, ",", &split_state))) {
+            pa_sink *slave_sink;
+
+            if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK))) {
+                pa_log("Invalid slave sink '%s'", n);
+                pa_xfree(n);
+                goto fail;
+            }
+
+            pa_xfree(n);
+
+            if (is_first_slave) {
+                slaves_spec = slave_sink->sample_spec;
+                slaves_map = slave_sink->channel_map;
+                is_first_slave = false;
+            } else {
+                if (slaves_spec.format != slave_sink->sample_spec.format)
+                    slaves_spec.format = PA_SAMPLE_INVALID;
+
+                if (slaves_spec.rate < slave_sink->sample_spec.rate)
+                    slaves_spec.rate = slave_sink->sample_spec.rate;
+
+                if (!pa_channel_map_equal(&slaves_map, &slave_sink->channel_map))
+                    slaves_spec.channels = 0;
+            }
+        }
+
+        if (!is_first_slave) {
+            if (slaves_spec.format != PA_SAMPLE_INVALID)
+                ss.format = slaves_spec.format;
+
+            ss.rate = slaves_spec.rate;
+
+            if (slaves_spec.channels > 0) {
+                map = slaves_map;
+                ss.channels = slaves_map.channels;
+            }
+        }
+    }
+
+    if ((pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0)) {
+        pa_log("Invalid sample specification.");
+        goto fail;
+    }
+
+    pa_sink_new_data_init(&data);
+    data.namereg_fail = false;
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+
+    if (slaves)
+        pa_proplist_sets(data.proplist, "combine.slaves", slaves);
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&data);
+        goto fail;
+    }
+
+    /* Check proplist for a description & fill in a default value if not */
+    u->auto_desc = false;
+    if (NULL == pa_proplist_gets(data.proplist, PA_PROP_DEVICE_DESCRIPTION)) {
+        u->auto_desc = true;
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output");
+    }
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->set_state = sink_set_state;
+    u->sink->update_requested_latency = sink_update_requested_latency;
+    u->sink->userdata = u;
+
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+
+    nbytes = pa_usec_to_bytes(BLOCK_USEC, &u->sink->sample_spec);
+    pa_sink_set_max_request(u->sink, nbytes);
+    pa_sink_set_latency_range(u->sink, 0, BLOCK_USEC);
+    /* pulse clamps the range, get the real values */
+    u->default_min_latency = u->sink->thread_info.min_latency;
+    u->default_max_latency = u->sink->thread_info.max_latency;
+    u->block_usec = u->sink->thread_info.max_latency;
+
+
+    if (!u->automatic) {
+        const char*split_state;
+        char *n = NULL;
+        pa_assert(slaves);
+
+        /* The slaves have been specified manually */
+
+        split_state = NULL;
+        while ((n = pa_split(slaves, ",", &split_state))) {
+            pa_sink *slave_sink;
+
+            if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK)) || slave_sink == u->sink) {
+                pa_log("Invalid slave sink '%s'", n);
+                pa_xfree(n);
+                goto fail;
+            }
+
+            pa_xfree(n);
+
+            if (!output_new(u, slave_sink)) {
+                pa_log("Failed to create slave sink input on sink '%s'.", slave_sink->name);
+                goto fail;
+            }
+        }
+
+        if (pa_idxset_size(u->outputs) <= 1)
+            pa_log_warn("No slave sinks specified.");
+
+        u->sink_put_slot = NULL;
+
+    } else {
+        pa_sink *s;
+
+        /* We're in automatic mode, we add every sink that matches our needs  */
+
+        PA_IDXSET_FOREACH(s, m->core->sinks, idx) {
+
+            if (!is_suitable_sink(u, s))
+                continue;
+
+            if (!output_new(u, s)) {
+                pa_log("Failed to create sink input on sink '%s'.", s->name);
+                goto fail;
+            }
+        }
+    }
+
+    u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_hook_cb, u);
+    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_hook_cb, u);
+    u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_hook_cb, u);
+
+    if (!(u->thread = pa_thread_new("combine", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    /* Activate the sink and the sink inputs */
+    pa_sink_put(u->sink);
+
+    PA_IDXSET_FOREACH(o, u->outputs, idx)
+        output_verify(o);
+
+    if (u->adjust_time > 0)
+        u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time, time_callback, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    pa_strlist_free(u->unlinked_slaves);
+
+    if (u->sink_put_slot)
+        pa_hook_slot_free(u->sink_put_slot);
+
+    if (u->sink_unlink_slot)
+        pa_hook_slot_free(u->sink_unlink_slot);
+
+    if (u->sink_state_changed_slot)
+        pa_hook_slot_free(u->sink_state_changed_slot);
+
+    if (u->outputs)
+        pa_idxset_free(u->outputs, (pa_free_cb_t) output_free);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->time_event)
+        u->core->mainloop->time_free(u->time_event);
+
+    if (u->thread_info.smoother)
+        pa_smoother_free(u->thread_info.smoother);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
new file mode 100644 (file)
index 0000000..5a12a13
--- /dev/null
@@ -0,0 +1,69 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+  Copyright 2011 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+
+#include "module-combine-symdef.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("Compatibility module (module-combine rename)");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_DEPRECATED("Please use module-combine-sink instead of module-combine!");
+
+struct userdata {
+    uint32_t module_index;
+};
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_module *module;
+
+    pa_assert(m);
+    pa_assert_se(m->userdata = u = pa_xnew0(struct userdata, 1));
+
+    pa_log_warn("We will now load module-combine-sink. Please make sure to remove module-combine from your configuration.");
+
+    module = pa_module_load(m->core, "module-combine-sink", m->argument);
+    u->module_index = module ? module->index : PA_INVALID_INDEX;
+
+    return module ? 0 : -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert(m->userdata);
+
+    u = m->userdata;
+
+    if (u && PA_INVALID_INDEX != u->module_index)
+        pa_module_unload_by_index(m->core, u->module_index, true);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c
new file mode 100644 (file)
index 0000000..7997c4f
--- /dev/null
@@ -0,0 +1,355 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2008 Lennart Poettering
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/dbus-shared.h>
+
+#include "module-console-kit-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Create a client for each ConsoleKit session of this user");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct session {
+    char *id;
+    pa_client *client;
+};
+
+struct userdata {
+    pa_module *module;
+    pa_core *core;
+    pa_dbus_connection *connection;
+    pa_hashmap *sessions;
+    bool filter_added;
+};
+
+static void add_session(struct userdata *u, const char *id) {
+    DBusError error;
+    DBusMessage *m = NULL, *reply = NULL;
+    uint32_t uid;
+    struct session *session;
+    pa_client_new_data data;
+
+    dbus_error_init(&error);
+
+    if (pa_hashmap_get(u->sessions, id)) {
+        pa_log_warn("Duplicate session %s, ignoring.", id);
+        return;
+    }
+
+    if (!(m = dbus_message_new_method_call("org.freedesktop.ConsoleKit", id, "org.freedesktop.ConsoleKit.Session", "GetUnixUser"))) {
+        pa_log("Failed to allocate GetUnixUser() method call.");
+        goto fail;
+    }
+
+    if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
+        pa_log("GetUnixUser() call failed: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    /* CK 0.3 this changed from int32 to uint32 */
+    if (!dbus_message_get_args(reply, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID)) {
+        dbus_error_free(&error);
+
+        if (!dbus_message_get_args(reply, &error, DBUS_TYPE_INT32, &uid, DBUS_TYPE_INVALID)) {
+            pa_log("Failed to parse GetUnixUser() result: %s: %s", error.name, error.message);
+            goto fail;
+        }
+    }
+
+    /* We only care about our own sessions */
+    if ((uid_t) uid != getuid())
+        goto fail;
+
+    session = pa_xnew(struct session, 1);
+    session->id = pa_xstrdup(id);
+
+    pa_client_new_data_init(&data);
+    data.module = u->module;
+    data.driver = __FILE__;
+    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "ConsoleKit Session %s", id);
+    pa_proplist_sets(data.proplist, "console-kit.session", id);
+    session->client = pa_client_new(u->core, &data);
+    pa_client_new_data_done(&data);
+
+    if (!session->client) {
+        pa_xfree(session->id);
+        pa_xfree(session);
+        goto fail;
+    }
+
+    pa_hashmap_put(u->sessions, session->id, session);
+
+    pa_log_debug("Added new session %s", id);
+
+fail:
+
+    if (m)
+        dbus_message_unref(m);
+
+    if (reply)
+        dbus_message_unref(reply);
+
+    dbus_error_free(&error);
+}
+
+static void free_session(struct session *session) {
+    pa_assert(session);
+
+    pa_log_debug("Removing session %s", session->id);
+
+    pa_client_free(session->client);
+    pa_xfree(session->id);
+    pa_xfree(session);
+}
+
+static void remove_session(struct userdata *u, const char *id) {
+    pa_assert(u);
+    pa_assert(id);
+
+    pa_hashmap_remove_and_free(u->sessions, id);
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
+    struct userdata *u = userdata;
+    DBusError error;
+    const char *path;
+
+    pa_assert(bus);
+    pa_assert(message);
+    pa_assert(u);
+
+    dbus_error_init(&error);
+
+    if (dbus_message_is_signal(message, "org.freedesktop.ConsoleKit.Seat", "SessionAdded")) {
+
+        /* CK API changed to match spec in 0.3 */
+        if (!dbus_message_get_args(message, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+            dbus_error_free(&error);
+
+            if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) {
+                pa_log_error("Failed to parse SessionAdded message: %s: %s", error.name, error.message);
+                goto finish;
+            }
+        }
+
+        add_session(u, path);
+
+    } else if (dbus_message_is_signal(message, "org.freedesktop.ConsoleKit.Seat", "SessionRemoved")) {
+
+        /* CK API changed to match spec in 0.3 */
+        if (!dbus_message_get_args(message, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+            dbus_error_free(&error);
+
+            if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) {
+                pa_log_error("Failed to parse SessionRemoved message: %s: %s", error.name, error.message);
+                goto finish;
+            }
+        }
+
+        remove_session(u, path);
+    }
+
+finish:
+    dbus_error_free(&error);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int get_session_list(struct userdata *u) {
+    DBusError error;
+    DBusMessage *m = NULL, *reply = NULL;
+    uint32_t uid;
+    DBusMessageIter iter, sub;
+    int ret = -1;
+
+    pa_assert(u);
+
+    dbus_error_init(&error);
+
+    if (!(m = dbus_message_new_method_call("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "GetSessionsForUnixUser"))) {
+        pa_log("Failed to allocate GetSessionsForUnixUser() method call.");
+        goto fail;
+    }
+
+    uid = (uint32_t) getuid();
+    if (!(dbus_message_append_args(m, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID))) {
+        pa_log("Failed to append arguments to GetSessionsForUnixUser() method call.");
+        goto fail;
+    }
+
+    if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
+        pa_log("GetSessionsForUnixUser() call failed: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    dbus_message_iter_init(reply, &iter);
+
+    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+        dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_OBJECT_PATH) {
+        pa_log("Failed to parse GetSessionsForUnixUser() result.");
+        goto fail;
+    }
+
+    dbus_message_iter_recurse(&iter, &sub);
+
+    for (;;) {
+        int at;
+        const char *id;
+
+        if ((at = dbus_message_iter_get_arg_type(&sub)) == DBUS_TYPE_INVALID)
+            break;
+
+        pa_assert(at == DBUS_TYPE_OBJECT_PATH);
+        dbus_message_iter_get_basic(&sub, &id);
+
+        add_session(u, id);
+
+        dbus_message_iter_next(&sub);
+    }
+
+    ret = 0;
+
+fail:
+
+    if (m)
+        dbus_message_unref(m);
+
+    if (reply)
+        dbus_message_unref(reply);
+
+    dbus_error_free(&error);
+
+    return ret;
+}
+
+int pa__init(pa_module*m) {
+    DBusError error;
+    pa_dbus_connection *connection;
+    struct userdata *u = NULL;
+    pa_modargs *ma;
+
+    pa_assert(m);
+
+    dbus_error_init(&error);
+
+    /* If systemd's logind service is running, we shouldn't watch ConsoleKit
+     * but login */
+    if (access("/run/systemd/seats/", F_OK) >= 0)
+        return 0;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (!(connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
+
+        if (connection)
+            pa_dbus_connection_unref(connection);
+
+        pa_log_error("Unable to contact D-Bus system bus: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->connection = connection;
+    u->sessions = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) free_session);
+
+    if (!dbus_connection_add_filter(pa_dbus_connection_get(connection), filter_cb, u, NULL)) {
+        pa_log_error("Failed to add filter function");
+        goto fail;
+    }
+
+    u->filter_added = true;
+
+    if (pa_dbus_add_matches(
+                pa_dbus_connection_get(connection), &error,
+                "type='signal',sender='org.freedesktop.ConsoleKit',interface='org.freedesktop.ConsoleKit.Seat',member='SessionAdded'",
+                "type='signal',sender='org.freedesktop.ConsoleKit',interface='org.freedesktop.ConsoleKit.Seat',member='SessionRemoved'", NULL) < 0) {
+        pa_log_error("Unable to subscribe to ConsoleKit signals: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    if (get_session_list(u) < 0)
+        goto fail;
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    dbus_error_free(&error);
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sessions)
+        pa_hashmap_free(u->sessions);
+
+    if (u->connection) {
+        pa_dbus_remove_matches(
+                pa_dbus_connection_get(u->connection),
+                "type='signal',sender='org.freedesktop.ConsoleKit',interface='org.freedesktop.ConsoleKit.Seat',member='SessionAdded'",
+                "type='signal',sender='org.freedesktop.ConsoleKit',interface='org.freedesktop.ConsoleKit.Seat',member='SessionRemoved'", NULL);
+
+        if (u->filter_added)
+            dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), filter_cb, u);
+
+        pa_dbus_connection_unref(u->connection);
+    }
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
new file mode 100644 (file)
index 0000000..adf19ff
--- /dev/null
@@ -0,0 +1,195 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-error.h>
+
+#include "module-default-device-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore the default sink and source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+#define SAVE_INTERVAL (5 * PA_USEC_PER_SEC)
+
+struct userdata {
+    pa_core *core;
+    pa_subscription *subscription;
+    pa_time_event *time_event;
+    char *sink_filename, *source_filename;
+    bool modified;
+};
+
+static void load(struct userdata *u) {
+    FILE *f;
+
+    /* We never overwrite manually configured settings */
+
+    if (u->core->configured_default_sink)
+        pa_log_info("Manually configured default sink, not overwriting.");
+    else if ((f = pa_fopen_cloexec(u->sink_filename, "r"))) {
+        char ln[256] = "";
+
+        if (fgets(ln, sizeof(ln)-1, f))
+            pa_strip_nl(ln);
+        fclose(f);
+
+        if (!ln[0])
+            pa_log_info("No previous default sink setting, ignoring.");
+        else if (!pa_namereg_is_valid_name(ln))
+            pa_log_warn("Invalid sink name: %s", ln);
+        else {
+            pa_log_info("Restoring default sink '%s'.", ln);
+            pa_core_set_configured_default_sink(u->core, ln);
+        }
+
+    } else if (errno != ENOENT)
+        pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
+
+    if (u->core->configured_default_source)
+        pa_log_info("Manually configured default source, not overwriting.");
+    else if ((f = pa_fopen_cloexec(u->source_filename, "r"))) {
+        char ln[256] = "";
+
+        if (fgets(ln, sizeof(ln)-1, f))
+            pa_strip_nl(ln);
+        fclose(f);
+
+        if (!ln[0])
+            pa_log_info("No previous default source setting, ignoring.");
+        else if (!pa_namereg_is_valid_name(ln))
+            pa_log_warn("Invalid source name: %s", ln);
+        else {
+            pa_log_info("Restoring default source '%s'.", ln);
+            pa_core_set_configured_default_source(u->core, ln);
+        }
+
+    } else if (errno != ENOENT)
+        pa_log("Failed to load default source: %s", pa_cstrerror(errno));
+}
+
+static void save(struct userdata *u) {
+    FILE *f;
+
+    if (!u->modified)
+        return;
+
+    if (u->sink_filename) {
+        if ((f = pa_fopen_cloexec(u->sink_filename, "w"))) {
+            fprintf(f, "%s\n", u->core->configured_default_sink ? u->core->configured_default_sink : "");
+            fclose(f);
+        } else
+            pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
+    }
+
+    if (u->source_filename) {
+        if ((f = pa_fopen_cloexec(u->source_filename, "w"))) {
+            fprintf(f, "%s\n", u->core->configured_default_source ? u->core->configured_default_source : "");
+            fclose(f);
+        } else
+            pa_log("Failed to save default source: %s", pa_cstrerror(errno));
+    }
+
+    u->modified = false;
+}
+
+static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+    save(u);
+
+    if (u->time_event) {
+        u->core->mainloop->time_free(u->time_event);
+        u->time_event = NULL;
+    }
+}
+
+static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    u->modified = true;
+
+    if (!u->time_event)
+        u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, time_cb, u);
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+
+    if (!(u->sink_filename = pa_state_path("default-sink", true)))
+        goto fail;
+
+    if (!(u->source_filename = pa_state_path("default-source", true)))
+        goto fail;
+
+    load(u);
+
+    u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    save(u);
+
+    if (u->subscription)
+        pa_subscription_free(u->subscription);
+
+    if (u->time_event)
+        m->core->mainloop->time_free(u->time_event);
+
+    pa_xfree(u->sink_filename);
+    pa_xfree(u->source_filename);
+    pa_xfree(u);
+}
diff --git a/src/modules/module-defs.h.m4 b/src/modules/module-defs.h.m4
new file mode 100644 (file)
index 0000000..838b8e8
--- /dev/null
@@ -0,0 +1,35 @@
+changecom(`/*', `*/')dnl
+define(`module_name', patsubst(patsubst(patsubst(fname, `-symdef.h$'), `^.*/'), `[^0-9a-zA-Z]', `_'))dnl
+define(`c_symbol', patsubst(module_name, `[^0-9a-zA-Z]', `_'))dnl
+define(`c_macro', patsubst(module_name, `[^0-9a-zA-Z]', `'))dnl
+define(`incmacro', `foo'c_macro`symdeffoo')dnl
+define(`gen_symbol', `#define $1 'module_name`_LTX_$1')dnl
+#ifndef incmacro
+#define incmacro
+
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/macro.h>
+
+gen_symbol(pa__init)
+gen_symbol(pa__done)
+gen_symbol(pa__get_author)
+gen_symbol(pa__get_description)
+gen_symbol(pa__get_usage)
+gen_symbol(pa__get_version)
+gen_symbol(pa__get_deprecated)
+gen_symbol(pa__load_once)
+gen_symbol(pa__get_n_used)
+
+int pa__init(pa_module*m);
+void pa__done(pa_module*m);
+int pa__get_n_used(pa_module*m);
+
+const char* pa__get_author(void);
+const char* pa__get_description(void);
+const char* pa__get_usage(void);
+const char* pa__get_version(void);
+const char* pa__get_deprecated(void);
+bool pa__load_once(void);
+
+#endif
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
new file mode 100644 (file)
index 0000000..d6c6b76
--- /dev/null
@@ -0,0 +1,270 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+  Copyright 2006 Diego Pettenò
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "module-detect-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE("just-one=<boolean>");
+
+#ifdef __linux__
+PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-detect!");
+#endif
+
+static const char* const valid_modargs[] = {
+    "just-one",
+    NULL
+};
+
+#ifdef HAVE_ALSA
+
+static int detect_alsa(pa_core *c, int just_one) {
+    FILE *f;
+    int n = 0, n_sink = 0, n_source = 0;
+
+    if (!(f = pa_fopen_cloexec("/proc/asound/devices", "r"))) {
+
+        if (errno != ENOENT)
+            pa_log_error("open(\"/proc/asound/devices\") failed: %s", pa_cstrerror(errno));
+
+        return -1;
+    }
+
+    while (!feof(f)) {
+        char line[64], args[64];
+        unsigned device, subdevice;
+        int is_sink;
+
+        if (!fgets(line, sizeof(line), f))
+            break;
+
+        line[strcspn(line, "\r\n")] = 0;
+
+        if (pa_endswith(line, "digital audio playback"))
+            is_sink = 1;
+        else if (pa_endswith(line, "digital audio capture"))
+            is_sink = 0;
+        else
+            continue;
+
+        if (just_one && is_sink && n_sink >= 1)
+            continue;
+
+        if (just_one && !is_sink && n_source >= 1)
+            continue;
+
+        if (sscanf(line, " %*i: [%u- %u]: ", &device, &subdevice) != 2)
+            continue;
+
+        /* Only one sink per device */
+        if (subdevice != 0)
+            continue;
+
+        pa_snprintf(args, sizeof(args), "device_id=%u", device);
+        if (!pa_module_load(c, is_sink ? "module-alsa-sink" : "module-alsa-source", args))
+            continue;
+
+        n++;
+
+        if (is_sink)
+            n_sink++;
+        else
+            n_source++;
+    }
+
+    fclose(f);
+
+    return n;
+}
+#endif
+
+#ifdef HAVE_OSS_OUTPUT
+static int detect_oss(pa_core *c, int just_one) {
+    FILE *f;
+    int n = 0, b = 0;
+
+    if (!(f = pa_fopen_cloexec("/dev/sndstat", "r")) &&
+        !(f = pa_fopen_cloexec("/proc/sndstat", "r")) &&
+        !(f = pa_fopen_cloexec("/proc/asound/oss/sndstat", "r"))) {
+
+        if (errno != ENOENT)
+            pa_log_error("failed to open OSS sndstat device: %s", pa_cstrerror(errno));
+
+        return -1;
+    }
+
+    while (!feof(f)) {
+        char line[256], args[64];
+        unsigned device;
+
+        if (!fgets(line, sizeof(line), f))
+            break;
+
+        line[strcspn(line, "\r\n")] = 0;
+
+        if (!b) {
+            b = pa_streq(line, "Audio devices:") || pa_streq(line, "Installed devices:");
+            continue;
+        }
+
+        if (line[0] == 0)
+            break;
+
+        if (sscanf(line, "%u: ", &device) == 1) {
+            if (device == 0)
+                pa_snprintf(args, sizeof(args), "device=/dev/dsp");
+            else
+                pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", device);
+
+            if (!pa_module_load(c, "module-oss", args))
+                continue;
+
+        } else if (sscanf(line, "pcm%u: ", &device) == 1) {
+            /* FreeBSD support, the devices are named /dev/dsp0.0, dsp0.1 and so on */
+            pa_snprintf(args, sizeof(args), "device=/dev/dsp%u.0", device);
+
+            if (!pa_module_load(c, "module-oss", args))
+                continue;
+        }
+
+        n++;
+
+        if (just_one)
+            break;
+    }
+
+    fclose(f);
+    return n;
+}
+#endif
+
+#ifdef HAVE_SOLARIS
+static int detect_solaris(pa_core *c, int just_one) {
+    struct stat s;
+    const char *dev;
+    char args[64];
+
+    dev = getenv("AUDIODEV");
+    if (!dev)
+        dev = "/dev/audio";
+
+    if (stat(dev, &s) < 0) {
+        if (errno != ENOENT)
+            pa_log_error("failed to open device %s: %s", dev, pa_cstrerror(errno));
+        return -1;
+    }
+
+    if (!S_ISCHR(s.st_mode))
+        return 0;
+
+    pa_snprintf(args, sizeof(args), "device=%s", dev);
+
+    if (!pa_module_load(c, "module-solaris", args))
+        return 0;
+
+    return 1;
+}
+#endif
+
+#ifdef OS_IS_WIN32
+static int detect_waveout(pa_core *c, int just_one) {
+    /*
+     * FIXME: No point in enumerating devices until the plugin supports
+     * selecting anything but the first.
+     */
+    if (!pa_module_load(c, "module-waveout", ""))
+        return 0;
+
+    return 1;
+}
+#endif
+
+int pa__init(pa_module*m) {
+    bool just_one = false;
+    int n = 0;
+    pa_modargs *ma;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "just-one", &just_one) < 0) {
+        pa_log("just_one= expects a boolean argument.");
+        goto fail;
+    }
+
+#ifdef HAVE_ALSA
+    if ((n = detect_alsa(m->core, just_one)) <= 0)
+#endif
+#ifdef HAVE_OSS_OUTPUT
+    if ((n = detect_oss(m->core, just_one)) <= 0)
+#endif
+#ifdef HAVE_SOLARIS
+    if ((n = detect_solaris(m->core, just_one)) <= 0)
+#endif
+#ifdef OS_IS_WIN32
+    if ((n = detect_waveout(m->core, just_one)) <= 0)
+#endif
+    {
+        pa_log_warn("failed to detect any sound hardware.");
+        goto fail;
+    }
+
+    pa_log_info("loaded %i modules.", n);
+
+    /* We were successful and can unload ourselves now. */
+    pa_module_unload_request(m, true);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
new file mode 100644 (file)
index 0000000..2a0a67f
--- /dev/null
@@ -0,0 +1,1743 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+  Copyright 2009 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/protocol-native.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/database.h>
+#include <pulsecore/tagstruct.h>
+
+#include "module-device-manager-symdef.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+    "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
+    "on_hotplug=<When new device becomes available, recheck streams?> "
+    "on_rescue=<When device becomes unavailable, recheck streams?>");
+
+#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
+#define DUMP_DATABASE
+
+static const char* const valid_modargs[] = {
+    "do_routing",
+    "on_hotplug",
+    "on_rescue",
+    NULL
+};
+
+#define NUM_ROLES 9
+enum {
+    ROLE_NONE,
+    ROLE_VIDEO,
+    ROLE_MUSIC,
+    ROLE_GAME,
+    ROLE_EVENT,
+    ROLE_PHONE,
+    ROLE_ANIMATION,
+    ROLE_PRODUCTION,
+    ROLE_A11Y,
+    ROLE_MAX
+};
+
+typedef uint32_t role_indexes_t[NUM_ROLES];
+
+static const char* role_names[NUM_ROLES] = {
+    "none",
+    "video",
+    "music",
+    "game",
+    "event",
+    "phone",
+    "animation",
+    "production",
+    "a11y",
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_subscription *subscription;
+    pa_hook_slot
+        *sink_new_hook_slot,
+        *source_new_hook_slot,
+        *sink_input_new_hook_slot,
+        *source_output_new_hook_slot,
+        *sink_put_hook_slot,
+        *source_put_hook_slot,
+        *sink_unlink_hook_slot,
+        *source_unlink_hook_slot,
+        *connection_unlink_hook_slot;
+    pa_time_event *save_time_event;
+    pa_database *database;
+
+    pa_native_protocol *protocol;
+    pa_idxset *subscribed;
+
+    bool on_hotplug;
+    bool on_rescue;
+    bool do_routing;
+
+    role_indexes_t preferred_sinks;
+    role_indexes_t preferred_sources;
+};
+
+#define ENTRY_VERSION 1
+
+struct entry {
+    uint8_t version;
+    char *description;
+    bool user_set_description;
+    char *icon;
+    role_indexes_t priority;
+};
+
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_READ,
+    SUBCOMMAND_RENAME,
+    SUBCOMMAND_DELETE,
+    SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
+    SUBCOMMAND_REORDER,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT
+};
+
+/* Forward declarations */
+#ifdef DUMP_DATABASE
+static void dump_database(struct userdata *);
+#endif
+static void notify_subscribers(struct userdata *);
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(u);
+
+    pa_assert(e == u->save_time_event);
+    u->core->mainloop->time_free(u->save_time_event);
+    u->save_time_event = NULL;
+
+    pa_database_sync(u->database);
+    pa_log_info("Synced.");
+
+#ifdef DUMP_DATABASE
+    dump_database(u);
+#endif
+}
+
+static void trigger_save(struct userdata *u) {
+
+    pa_assert(u);
+
+    notify_subscribers(u);
+
+    if (u->save_time_event)
+        return;
+
+    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
+}
+
+static struct entry* entry_new(void) {
+    struct entry *r = pa_xnew0(struct entry, 1);
+    r->version = ENTRY_VERSION;
+    return r;
+}
+
+static void entry_free(struct entry* e) {
+    pa_assert(e);
+
+    pa_xfree(e->description);
+    pa_xfree(e->icon);
+    pa_xfree(e);
+}
+
+static bool entry_write(struct userdata *u, const char *name, const struct entry *e) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    bool r;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_puts(t, e->description);
+    pa_tagstruct_put_boolean(t, e->user_set_description);
+    pa_tagstruct_puts(t, e->icon);
+    for (uint8_t i=0; i<ROLE_MAX; ++i)
+        pa_tagstruct_putu32(t, e->priority[i]);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, true) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+
+#define LEGACY_ENTRY_VERSION 1
+static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
+    struct legacy_entry {
+        uint8_t version;
+        char description[PA_NAME_MAX];
+        bool user_set_description;
+        char icon[PA_NAME_MAX];
+        role_indexes_t priority;
+    } PA_GCC_PACKED;
+    struct legacy_entry *le;
+    struct entry *e;
+
+    pa_assert(u);
+    pa_assert(data);
+
+    if (data->size != sizeof(struct legacy_entry)) {
+        pa_log_debug("Size does not match.");
+        return NULL;
+    }
+
+    le = (struct legacy_entry*)data->data;
+
+    if (le->version != LEGACY_ENTRY_VERSION) {
+        pa_log_debug("Version mismatch.");
+        return NULL;
+    }
+
+    if (!memchr(le->description, 0, sizeof(le->description))) {
+        pa_log_warn("Description has missing NUL byte.");
+        return NULL;
+    }
+
+    if (!le->description[0]) {
+        pa_log_warn("Description is empty.");
+        return NULL;
+    }
+
+    if (!memchr(le->icon, 0, sizeof(le->icon))) {
+        pa_log_warn("Icon has missing NUL byte.");
+        return NULL;
+    }
+
+    e = entry_new();
+    e->description = pa_xstrdup(le->description);
+    e->icon = pa_xstrdup(le->icon);
+    return e;
+}
+#endif
+
+static struct entry* entry_read(struct userdata *u, const char *name) {
+    pa_datum key, data;
+    struct entry *e = NULL;
+    pa_tagstruct *t = NULL;
+    const char *description, *icon;
+
+    pa_assert(u);
+    pa_assert(name);
+
+    key.data = (char*) name;
+    key.size = strlen(name);
+
+    pa_zero(data);
+
+    if (!pa_database_get(u->database, &key, &data)) {
+        pa_log_debug("Database contains no data for key: %s", name);
+        return NULL;
+    }
+
+    t = pa_tagstruct_new_fixed(data.data, data.size);
+    e = entry_new();
+
+    if (pa_tagstruct_getu8(t, &e->version) < 0 ||
+        e->version > ENTRY_VERSION ||
+        pa_tagstruct_gets(t, &description) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->user_set_description) < 0 ||
+        pa_tagstruct_gets(t, &icon) < 0) {
+
+        goto fail;
+    }
+
+    if (e->user_set_description && !description) {
+        pa_log("Entry has user_set_description set, but the description is NULL.");
+        goto fail;
+    }
+
+    if (e->user_set_description && !*description) {
+        pa_log("Entry has user_set_description set, but the description is empty.");
+        goto fail;
+    }
+
+    e->description = pa_xstrdup(description);
+    e->icon = pa_xstrdup(icon);
+
+    for (uint8_t i=0; i<ROLE_MAX; ++i) {
+        if (pa_tagstruct_getu32(t, &e->priority[i]) < 0)
+            goto fail;
+    }
+
+    if (!pa_tagstruct_eof(t))
+        goto fail;
+
+    pa_tagstruct_free(t);
+    pa_datum_free(&data);
+
+    return e;
+
+fail:
+    pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
+
+    if (e)
+        entry_free(e);
+    if (t)
+        pa_tagstruct_free(t);
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+    pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
+    if ((e = legacy_entry_read(u, &data))) {
+        pa_log_debug("Success. Saving new format for key: %s", name);
+        if (entry_write(u, name, e))
+            trigger_save(u);
+        pa_datum_free(&data);
+        return e;
+    } else
+        pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
+#endif
+
+    pa_datum_free(&data);
+    return NULL;
+}
+
+#ifdef DUMP_DATABASE
+static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, bool sink_mode) {
+    pa_assert(u);
+    pa_assert(human);
+
+    if (sink_mode) {
+        pa_sink *s;
+        if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
+            pa_log_debug("   %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
+        else
+            pa_log_debug("   %s No sink specified", human);
+    } else {
+        pa_source *s;
+        if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
+            pa_log_debug("   %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
+        else
+            pa_log_debug("   %s No source specified", human);
+    }
+}
+
+static void dump_database(struct userdata *u) {
+    pa_datum key;
+    bool done;
+
+    pa_assert(u);
+
+    done = !pa_database_first(u->database, &key, NULL);
+
+    pa_log_debug("Dumping database");
+    while (!done) {
+        char *name;
+        struct entry *e;
+        pa_datum next_key;
+
+        done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+        name = pa_xstrndup(key.data, key.size);
+
+        if ((e = entry_read(u, name))) {
+            pa_log_debug(" Got entry: %s", name);
+            pa_log_debug("  Description: %s", e->description);
+            pa_log_debug("  Priorities: None:   %3u, Video: %3u, Music:  %3u, Game: %3u, Event: %3u",
+                         e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
+            pa_log_debug("              Phone:  %3u, Anim:  %3u, Prodtn: %3u, A11y: %3u",
+                         e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
+            entry_free(e);
+        }
+
+        pa_xfree(name);
+
+        pa_datum_free(&key);
+        key = next_key;
+    }
+
+    if (u->do_routing) {
+        pa_log_debug(" Highest priority devices per-role:");
+
+        pa_log_debug("  Sinks:");
+        for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
+            char name[13];
+            uint32_t len = PA_MIN(12u, strlen(role_names[role]));
+            strncpy(name, role_names[role], len);
+            for (int i = len+1; i < 12; ++i) name[i] = ' ';
+            name[len] = ':'; name[0] -= 32; name[12] = '\0';
+            dump_database_helper(u, role, name, true);
+        }
+
+        pa_log_debug("  Sources:");
+        for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
+            char name[13];
+            uint32_t len = PA_MIN(12u, strlen(role_names[role]));
+            strncpy(name, role_names[role], len);
+            for (int i = len+1; i < 12; ++i) name[i] = ' ';
+            name[len] = ':'; name[0] -= 32; name[12] = '\0';
+            dump_database_helper(u, role, name, false);
+        }
+    }
+
+    pa_log_debug("Completed database dump");
+}
+#endif
+
+static void notify_subscribers(struct userdata *u) {
+
+    pa_native_connection *c;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    PA_IDXSET_FOREACH(c, u->subscribed, idx) {
+        pa_tagstruct *t;
+
+        t = pa_tagstruct_new();
+        pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
+        pa_tagstruct_putu32(t, 0);
+        pa_tagstruct_putu32(t, u->module->index);
+        pa_tagstruct_puts(t, u->module->name);
+        pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
+
+        pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
+    }
+}
+
+static bool entries_equal(const struct entry *a, const struct entry *b) {
+
+    pa_assert(a);
+    pa_assert(b);
+
+    if (!pa_streq(a->description, b->description)
+        || a->user_set_description != b->user_set_description
+        || !pa_streq(a->icon, b->icon))
+        return false;
+
+    for (int i=0; i < NUM_ROLES; ++i)
+        if (a->priority[i] != b->priority[i])
+            return false;
+
+    return true;
+}
+
+static char *get_name(const char *key, const char *prefix) {
+    char *t;
+
+    if (strncmp(key, prefix, strlen(prefix)))
+        return NULL;
+
+    t = pa_xstrdup(key + strlen(prefix));
+    return t;
+}
+
+static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
+    struct entry *old;
+
+    pa_assert(u);
+    pa_assert(entry);
+    pa_assert(name);
+    pa_assert(prefix);
+
+    if ((old = entry_read(u, name))) {
+        *entry = *old;
+        entry->description = pa_xstrdup(old->description);
+        entry->icon = pa_xstrdup(old->icon);
+    } else {
+        /* This is a new device, so make sure we write its priority list correctly */
+        role_indexes_t max_priority;
+        pa_datum key;
+        bool done;
+
+        pa_zero(max_priority);
+        done = !pa_database_first(u->database, &key, NULL);
+
+        /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
+        while (!done) {
+            pa_datum next_key;
+
+            done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+            if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
+                char *name2;
+                struct entry *e;
+
+                name2 = pa_xstrndup(key.data, key.size);
+
+                if ((e = entry_read(u, name2))) {
+                    for (uint32_t i = 0; i < NUM_ROLES; ++i) {
+                        max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
+                    }
+
+                    entry_free(e);
+                }
+
+                pa_xfree(name2);
+            }
+            pa_datum_free(&key);
+            key = next_key;
+        }
+
+        /* Actually initialise our entry now we've calculated it */
+        for (uint32_t i = 0; i < NUM_ROLES; ++i) {
+            entry->priority[i] = max_priority[i] + 1;
+        }
+        entry->user_set_description = false;
+    }
+
+    return old;
+}
+
+static uint32_t get_role_index(const char* role) {
+    pa_assert(role);
+
+    for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
+        if (pa_streq(role, role_names[i]))
+            return i;
+
+    return PA_INVALID_INDEX;
+}
+
+static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
+    role_indexes_t *indexes, highest_priority_available;
+    pa_datum key;
+    bool done, sink_mode;
+
+    pa_assert(u);
+    pa_assert(prefix);
+
+    sink_mode = pa_streq(prefix, "sink:");
+
+    if (sink_mode)
+        indexes = &u->preferred_sinks;
+    else
+        indexes = &u->preferred_sources;
+
+    for (uint32_t i = 0; i < NUM_ROLES; ++i) {
+        (*indexes)[i] = PA_INVALID_INDEX;
+    }
+    pa_zero(highest_priority_available);
+
+    done = !pa_database_first(u->database, &key, NULL);
+
+    /* Find all existing devices with the same prefix so we find the highest priority device for each role */
+    while (!done) {
+        pa_datum next_key;
+
+        done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+        if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
+            char *name, *device_name;
+            struct entry *e;
+
+            name = pa_xstrndup(key.data, key.size);
+            pa_assert_se(device_name = get_name(name, prefix));
+
+            if ((e = entry_read(u, name))) {
+                for (uint32_t i = 0; i < NUM_ROLES; ++i) {
+                    if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
+                        /* We've found a device with a higher priority than that we've currently got,
+                           so see if it is currently available or not and update our list */
+                        uint32_t idx;
+                        bool found = false;
+
+                        if (sink_mode) {
+                            pa_sink *sink;
+
+                            PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
+                                if ((pa_sink*) ignore_device == sink)
+                                    continue;
+                                if (!PA_SINK_IS_LINKED(sink->state))
+                                    continue;
+                                if (pa_streq(sink->name, device_name)) {
+                                    found = true;
+                                    idx = sink->index; /* Is this needed? */
+                                    break;
+                                }
+                            }
+                        } else {
+                            pa_source *source;
+
+                            PA_IDXSET_FOREACH(source, u->core->sources, idx) {
+                                if ((pa_source*) ignore_device == source)
+                                    continue;
+                                if (!PA_SOURCE_IS_LINKED(source->state))
+                                    continue;
+                                if (pa_streq(source->name, device_name)) {
+                                    found = true;
+                                    idx = source->index; /* Is this needed? */
+                                    break;
+                                }
+                            }
+                        }
+                        if (found) {
+                            highest_priority_available[i] = e->priority[i];
+                            (*indexes)[i] = idx;
+                        }
+
+                    }
+                }
+
+                entry_free(e);
+            }
+
+            pa_xfree(name);
+            pa_xfree(device_name);
+        }
+
+        pa_datum_free(&key);
+        key = next_key;
+    }
+}
+
+static void route_sink_input(struct userdata *u, pa_sink_input *si) {
+    const char *auto_filtered_prop;
+    const char *role;
+    uint32_t role_index, device_index;
+    bool auto_filtered = false;
+    pa_sink *sink;
+
+    pa_assert(u);
+    pa_assert(u->do_routing);
+
+    if (si->save_sink)
+        return;
+
+    /* Skip this if it is already in the process of being moved anyway */
+    if (!si->sink)
+        return;
+
+    auto_filtered_prop = pa_proplist_gets(si->proplist, "module-device-manager.auto_filtered");
+    if (auto_filtered_prop)
+        auto_filtered = (pa_parse_boolean(auto_filtered_prop) == 1);
+
+    /* It might happen that a stream and a sink are set up at the
+    same time, in which case we want to make sure we don't
+    interfere with that */
+    if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
+        return;
+
+    if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
+        role_index = get_role_index("none");
+    else
+        role_index = get_role_index(role);
+
+    if (PA_INVALID_INDEX == role_index)
+        return;
+
+    device_index = u->preferred_sinks[role_index];
+    if (PA_INVALID_INDEX == device_index)
+        return;
+
+    if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
+        return;
+
+    if (auto_filtered) {
+        /* For streams for which a filter has been loaded by another module, we
+         * do not try to execute moves within the same filter hierarchy */
+        if (pa_sink_get_master(si->sink) == pa_sink_get_master(sink))
+            return;
+    }
+
+    if (si->sink != sink)
+        pa_sink_input_move_to(si, sink, false);
+}
+
+static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
+    pa_sink_input *si;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    if (!u->do_routing)
+        return PA_HOOK_OK;
+
+    update_highest_priority_device_indexes(u, "sink:", ignore_sink);
+
+    PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
+        route_sink_input(u, si);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static void route_source_output(struct userdata *u, pa_source_output *so) {
+    const char *auto_filtered_prop;
+    const char *role;
+    uint32_t role_index, device_index;
+    bool auto_filtered = false;
+    pa_source *source;
+
+    pa_assert(u);
+    pa_assert(u->do_routing);
+
+    if (so->save_source)
+        return;
+
+    if (so->direct_on_input)
+        return;
+
+    /* Skip this if it is already in the process of being moved anyway */
+    if (!so->source)
+        return;
+
+    auto_filtered_prop = pa_proplist_gets(so->proplist, "module-device-manager.auto_filtered");
+    if (auto_filtered_prop)
+        auto_filtered = (pa_parse_boolean(auto_filtered_prop) == 1);
+
+    /* It might happen that a stream and a source are set up at the
+    same time, in which case we want to make sure we don't
+    interfere with that */
+    if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
+        return;
+
+    if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
+        role_index = get_role_index("none");
+    else
+        role_index = get_role_index(role);
+
+    if (PA_INVALID_INDEX == role_index)
+        return;
+
+    device_index = u->preferred_sources[role_index];
+    if (PA_INVALID_INDEX == device_index)
+        return;
+
+    if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
+        return;
+
+    if (auto_filtered) {
+        /* For streams for which a filter has been loaded by another module, we
+         * do not try to execute moves within the same filter hierarchy */
+        if (pa_source_get_master(so->source) == pa_source_get_master(source))
+            return;
+    }
+
+    if (so->source != source)
+        pa_source_output_move_to(so, source, false);
+}
+
+static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
+    pa_source_output *so;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    if (!u->do_routing)
+        return PA_HOOK_OK;
+
+    update_highest_priority_device_indexes(u, "source:", ignore_source);
+
+    PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
+        route_source_output(u, so);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    struct userdata *u = userdata;
+    struct entry *entry, *old = NULL;
+    char *name = NULL;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+
+        /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
+        t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
+        return;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
+        pa_sink_input *si;
+
+        if (!u->do_routing)
+            return;
+        if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
+            return;
+
+        /* The role may change mid-stream, so we reroute */
+        route_sink_input(u, si);
+
+        return;
+    } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
+        pa_source_output *so;
+
+        if (!u->do_routing)
+            return;
+        if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
+            return;
+
+        /* The role may change mid-stream, so we reroute */
+        route_source_output(u, so);
+
+        return;
+    } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
+        pa_sink *sink;
+
+        if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
+            return;
+
+        entry = entry_new();
+        name = pa_sprintf_malloc("sink:%s", sink->name);
+
+        old = load_or_initialize_entry(u, entry, name, "sink:");
+
+        if (!entry->user_set_description) {
+            pa_xfree(entry->description);
+            entry->description = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
+        } else if (!pa_streq(entry->description, pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+            /* Warning: If two modules fight over the description, this could cause an infinite loop.
+               by changing the description here, we retrigger this subscription callback. The only thing stopping us from
+               looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
+               the description, this will fail... */
+            pa_sink_set_description(sink, entry->description);
+        }
+
+        pa_xfree(entry->icon);
+        entry->icon = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME));
+
+    } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
+        pa_source *source;
+
+        pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
+
+        if (!(source = pa_idxset_get_by_index(c->sources, idx)))
+            return;
+
+        if (source->monitor_of)
+            return;
+
+        entry = entry_new();
+        name = pa_sprintf_malloc("source:%s", source->name);
+
+        old = load_or_initialize_entry(u, entry, name, "source:");
+
+        if (!entry->user_set_description) {
+            pa_xfree(entry->description);
+            entry->description = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
+        } else if (!pa_streq(entry->description, pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+            /* Warning: If two modules fight over the description, this could cause an infinite loop.
+               by changing the description here, we retrigger this subscription callback. The only thing stopping us from
+               looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
+               the description, this will fail... */
+            pa_source_set_description(source, entry->description);
+        }
+
+        pa_xfree(entry->icon);
+        entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME));
+    } else {
+        pa_assert_not_reached();
+    }
+
+    pa_assert(name);
+
+    if (old) {
+
+        if (entries_equal(old, entry)) {
+            entry_free(old);
+            entry_free(entry);
+            pa_xfree(name);
+
+            return;
+        }
+
+        entry_free(old);
+    }
+
+    pa_log_info("Storing device %s.", name);
+
+    if (entry_write(u, name, entry))
+        trigger_save(u);
+    else
+        pa_log_warn("Could not save device");;
+
+    entry_free(entry);
+    pa_xfree(name);
+}
+
+static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    name = pa_sprintf_malloc("sink:%s", new_data->name);
+
+    if ((e = entry_read(u, name))) {
+        if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+            pa_log_info("Restoring description for sink %s.", new_data->name);
+            pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
+        }
+
+        entry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    name = pa_sprintf_malloc("source:%s", new_data->name);
+
+    if ((e = entry_read(u, name))) {
+        if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+            /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
+            pa_log_info("Restoring description for source %s.", new_data->name);
+            pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
+        }
+
+        entry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
+    const char *role;
+    uint32_t role_index;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    if (!u->do_routing)
+        return PA_HOOK_OK;
+
+    if (new_data->sink)
+        pa_log_debug("Not restoring device for stream because already set.");
+    else {
+        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
+            role_index = get_role_index("none");
+        else
+            role_index = get_role_index(role);
+
+        if (PA_INVALID_INDEX != role_index) {
+            uint32_t device_index;
+
+            device_index = u->preferred_sinks[role_index];
+            if (PA_INVALID_INDEX != device_index) {
+                pa_sink *sink;
+
+                if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
+                    if (!pa_sink_input_new_data_set_sink(new_data, sink, false))
+                        pa_log_debug("Not restoring device for stream because no supported format was found");
+                }
+            }
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
+    const char *role;
+    uint32_t role_index;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    if (!u->do_routing)
+        return PA_HOOK_OK;
+
+    if (new_data->direct_on_input)
+        return PA_HOOK_OK;
+
+    if (new_data->source)
+        pa_log_debug("Not restoring device for stream because already set.");
+    else {
+        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
+            role_index = get_role_index("none");
+        else
+            role_index = get_role_index(role);
+
+        if (PA_INVALID_INDEX != role_index) {
+            uint32_t device_index;
+
+            device_index = u->preferred_sources[role_index];
+            if (PA_INVALID_INDEX != device_index) {
+                pa_source *source;
+
+                if ((source = pa_idxset_get_by_index(u->core->sources, device_index)))
+                    if (!pa_source_output_new_data_set_source(new_data, source, false))
+                        pa_log_debug("Not restoring device for stream because no supported format was found");
+            }
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
+    pa_assert(c);
+    pa_assert(u);
+    pa_assert(u->core == c);
+    pa_assert(u->on_hotplug);
+
+    notify_subscribers(u);
+
+    return route_sink_inputs(u, NULL);
+}
+
+static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
+    pa_assert(c);
+    pa_assert(u);
+    pa_assert(u->core == c);
+    pa_assert(u->on_hotplug);
+
+    notify_subscribers(u);
+
+    return route_source_outputs(u, NULL);
+}
+
+static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+    pa_assert(u->core == c);
+    pa_assert(u->on_rescue);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    notify_subscribers(u);
+
+    return route_sink_inputs(u, sink);
+}
+
+static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
+    pa_assert(c);
+    pa_assert(source);
+    pa_assert(u);
+    pa_assert(u->core == c);
+    pa_assert(u->on_rescue);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    notify_subscribers(u);
+
+    return route_source_outputs(u, source);
+}
+
+static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
+    uint32_t idx;
+    char *n;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    if (!e->user_set_description)
+        return;
+
+    if ((n = get_name(name, "sink:"))) {
+        pa_sink *s;
+        PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
+            if (!pa_streq(s->name, n)) {
+                continue;
+            }
+
+            pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
+            pa_sink_set_description(s, e->description);
+        }
+        pa_xfree(n);
+    }
+    else if ((n = get_name(name, "source:"))) {
+        pa_source *s;
+        PA_IDXSET_FOREACH(s, u->core->sources, idx) {
+            if (!pa_streq(s->name, n)) {
+                continue;
+            }
+
+            if (s->monitor_of) {
+                pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
+                continue;
+            }
+
+            pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
+            pa_source_set_description(s, e->description);
+        }
+        pa_xfree(n);
+    }
+}
+
+#define EXT_VERSION 1
+
+static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
+  struct userdata *u;
+  uint32_t command;
+  pa_tagstruct *reply = NULL;
+
+  pa_assert(p);
+  pa_assert(m);
+  pa_assert(c);
+  pa_assert(t);
+
+  u = m->userdata;
+
+  if (pa_tagstruct_getu32(t, &command) < 0)
+    goto fail;
+
+  reply = pa_tagstruct_new();
+  pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+  pa_tagstruct_putu32(reply, tag);
+
+  switch (command) {
+    case SUBCOMMAND_TEST: {
+      if (!pa_tagstruct_eof(t))
+        goto fail;
+
+      pa_tagstruct_putu32(reply, EXT_VERSION);
+      break;
+    }
+
+    case SUBCOMMAND_READ: {
+      pa_datum key;
+      bool done;
+
+      if (!pa_tagstruct_eof(t))
+        goto fail;
+
+      done = !pa_database_first(u->database, &key, NULL);
+
+      while (!done) {
+        pa_datum next_key;
+        struct entry *e;
+        char *name;
+
+        done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+        name = pa_xstrndup(key.data, key.size);
+        pa_datum_free(&key);
+
+        if ((e = entry_read(u, name))) {
+            uint32_t idx;
+            char *device_name;
+            uint32_t found_index = PA_INVALID_INDEX;
+
+            if ((device_name = get_name(name, "sink:"))) {
+                pa_sink* s;
+                PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
+                    if (pa_streq(s->name, device_name)) {
+                        found_index = s->index;
+                        break;
+                    }
+                }
+                pa_xfree(device_name);
+            } else if ((device_name = get_name(name, "source:"))) {
+                pa_source* s;
+                PA_IDXSET_FOREACH(s, u->core->sources, idx) {
+                    if (pa_streq(s->name, device_name)) {
+                        found_index = s->index;
+                        break;
+                    }
+                }
+                pa_xfree(device_name);
+            }
+
+            pa_tagstruct_puts(reply, name);
+            pa_tagstruct_puts(reply, e->description);
+            pa_tagstruct_puts(reply, e->icon);
+            pa_tagstruct_putu32(reply, found_index);
+            pa_tagstruct_putu32(reply, NUM_ROLES);
+
+            for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
+                pa_tagstruct_puts(reply, role_names[i]);
+                pa_tagstruct_putu32(reply, e->priority[i]);
+            }
+
+            entry_free(e);
+        }
+
+        pa_xfree(name);
+
+        key = next_key;
+      }
+
+      break;
+    }
+
+    case SUBCOMMAND_RENAME: {
+
+        struct entry *e;
+        const char *device, *description;
+
+        if (pa_tagstruct_gets(t, &device) < 0 ||
+          pa_tagstruct_gets(t, &description) < 0)
+          goto fail;
+
+        if (!device || !*device || !description || !*description)
+          goto fail;
+
+        if ((e = entry_read(u, device))) {
+            pa_xfree(e->description);
+            e->description = pa_xstrdup(description);
+            e->user_set_description = true;
+
+            if (entry_write(u, (char *)device, e)) {
+                apply_entry(u, device, e);
+
+                trigger_save(u);
+            }
+            else
+                pa_log_warn("Could not save device");
+
+            entry_free(e);
+        }
+        else
+            pa_log_warn("Could not rename device %s, no entry in database", device);
+
+      break;
+    }
+
+    case SUBCOMMAND_DELETE:
+
+      while (!pa_tagstruct_eof(t)) {
+        const char *name;
+        pa_datum key;
+
+        if (pa_tagstruct_gets(t, &name) < 0)
+          goto fail;
+
+        key.data = (char*) name;
+        key.size = strlen(name);
+
+        /** @todo: Reindex the priorities */
+        pa_database_unset(u->database, &key);
+      }
+
+      trigger_save(u);
+
+      break;
+
+    case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
+
+        bool enable;
+
+        if (pa_tagstruct_get_boolean(t, &enable) < 0)
+            goto fail;
+
+        if ((u->do_routing = enable)) {
+            /* Update our caches */
+            update_highest_priority_device_indexes(u, "sink:", NULL);
+            update_highest_priority_device_indexes(u, "source:", NULL);
+        }
+
+        break;
+    }
+
+    case SUBCOMMAND_REORDER: {
+
+        const char *role;
+        struct entry *e;
+        uint32_t role_index, n_devices;
+        pa_datum key;
+        bool done, sink_mode = true;
+        struct device_t { uint32_t prio; char *device; };
+        struct device_t *device;
+        struct device_t **devices;
+        uint32_t i, idx, offset;
+        pa_hashmap *h;
+        /*void *state;*/
+        bool first;
+
+        if (pa_tagstruct_gets(t, &role) < 0 ||
+            pa_tagstruct_getu32(t, &n_devices) < 0 ||
+            n_devices < 1)
+            goto fail;
+
+        if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
+            goto fail;
+
+        /* Cycle through the devices given and make sure they exist */
+        h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+        first = true;
+        idx = 0;
+        for (i = 0; i < n_devices; ++i) {
+            const char *s;
+            if (pa_tagstruct_gets(t, &s) < 0) {
+                while ((device = pa_hashmap_steal_first(h))) {
+                    pa_xfree(device->device);
+                    pa_xfree(device);
+                }
+
+                pa_hashmap_free(h);
+                pa_log_error("Protocol error on reorder");
+                goto fail;
+            }
+
+            /* Ensure this is a valid entry */
+            if (!(e = entry_read(u, s))) {
+                while ((device = pa_hashmap_steal_first(h))) {
+                    pa_xfree(device->device);
+                    pa_xfree(device);
+                }
+
+                pa_hashmap_free(h);
+                pa_log_error("Client specified an unknown device in its reorder list.");
+                goto fail;
+            }
+            entry_free(e);
+
+            if (first) {
+                first = false;
+                sink_mode = (0 == strncmp("sink:", s, 5));
+            } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) {
+                while ((device = pa_hashmap_steal_first(h))) {
+                    pa_xfree(device->device);
+                    pa_xfree(device);
+                }
+
+                pa_hashmap_free(h);
+                pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
+                goto fail;
+            }
+
+            /* Add the device to our hashmap. If it's already in it, free it now and carry on */
+            device = pa_xnew(struct device_t, 1);
+            device->device = pa_xstrdup(s);
+            if (pa_hashmap_put(h, device->device, device) == 0) {
+                device->prio = idx;
+                idx++;
+            } else {
+                pa_xfree(device->device);
+                pa_xfree(device);
+            }
+        }
+
+        /*pa_log_debug("Hashmap contents (received from client)");
+        PA_HASHMAP_FOREACH(device, h, state) {
+            pa_log_debug("  - %s (%d)", device->device, device->prio);
+        }*/
+
+        /* Now cycle through our list and add all the devices.
+           This has the effect of adding in any in our DB,
+           not specified in the device list (and thus will be
+           tacked on at the end) */
+        offset = idx;
+        done = !pa_database_first(u->database, &key, NULL);
+
+        while (!done && idx < 256) {
+            pa_datum next_key;
+
+            done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+            device = pa_xnew(struct device_t, 1);
+            device->device = pa_xstrndup(key.data, key.size);
+            if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
+                || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
+
+                /* Add the device to our hashmap. If it's already in it, free it now and carry on */
+                if (pa_hashmap_put(h, device->device, device) == 0
+                    && (e = entry_read(u, device->device))) {
+                    /* We add offset on to the existing priority so that when we order, the
+                       existing entries are always lower priority than the new ones. */
+                    device->prio = (offset + e->priority[role_index]);
+                    pa_xfree(e);
+                }
+                else {
+                    pa_xfree(device->device);
+                    pa_xfree(device);
+                }
+            } else {
+                pa_xfree(device->device);
+                pa_xfree(device);
+            }
+
+            pa_datum_free(&key);
+
+            key = next_key;
+        }
+
+        /*pa_log_debug("Hashmap contents (combined with database)");
+        PA_HASHMAP_FOREACH(device, h, state) {
+            pa_log_debug("  - %s (%d)", device->device, device->prio);
+        }*/
+
+        /* Now we put all the entries in a simple list for sorting it. */
+        n_devices = pa_hashmap_size(h);
+        devices = pa_xnew(struct device_t *,  n_devices);
+        idx = 0;
+        while ((device = pa_hashmap_steal_first(h))) {
+            devices[idx++] = device;
+        }
+        pa_hashmap_free(h);
+
+        /* Simple bubble sort */
+        for (i = 0; i < n_devices; ++i) {
+            for (uint32_t j = i; j < n_devices; ++j) {
+                if (devices[i]->prio > devices[j]->prio) {
+                    struct device_t *tmp;
+                    tmp = devices[i];
+                    devices[i] = devices[j];
+                    devices[j] = tmp;
+                }
+            }
+        }
+
+        /*pa_log_debug("Sorted device list");
+        for (i = 0; i < n_devices; ++i) {
+            pa_log_debug("  - %s (%d)", devices[i]->device, devices[i]->prio);
+        }*/
+
+        /* Go through in order and write the new entry and cleanup our own list */
+        idx = 1;
+        first = true;
+        for (i = 0; i < n_devices; ++i) {
+            if ((e = entry_read(u, devices[i]->device))) {
+                if (e->priority[role_index] == idx)
+                    idx++;
+                else {
+                    e->priority[role_index] = idx;
+
+                    if (entry_write(u, (char *) devices[i]->device, e)) {
+                        first = false;
+                        idx++;
+                    }
+                }
+
+                pa_xfree(e);
+            }
+            pa_xfree(devices[i]->device);
+            pa_xfree(devices[i]);
+        }
+
+        pa_xfree(devices);
+
+        if (!first) {
+            trigger_save(u);
+
+            if (sink_mode)
+                route_sink_inputs(u, NULL);
+            else
+                route_source_outputs(u, NULL);
+        }
+
+        break;
+    }
+
+    case SUBCOMMAND_SUBSCRIBE: {
+
+      bool enabled;
+
+      if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
+        !pa_tagstruct_eof(t))
+        goto fail;
+
+      if (enabled)
+        pa_idxset_put(u->subscribed, c, NULL);
+      else
+        pa_idxset_remove_by_data(u->subscribed, c, NULL);
+
+      break;
+    }
+
+    default:
+      goto fail;
+  }
+
+  pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
+  return 0;
+
+  fail:
+
+  if (reply)
+    pa_tagstruct_free(reply);
+
+  return -1;
+}
+
+static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
+    pa_assert(p);
+    pa_assert(c);
+    pa_assert(u);
+
+    pa_idxset_remove_by_data(u->subscribed, c, NULL);
+    return PA_HOOK_OK;
+}
+
+struct prioritised_indexes {
+    uint32_t index;
+    int32_t priority;
+};
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    char *fname;
+    pa_sink *sink;
+    pa_source *source;
+    uint32_t idx;
+    bool do_routing = false, on_hotplug = true, on_rescue = true;
+    uint32_t total_devices;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
+        pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
+        pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
+        pa_log("on_hotplug= and on_rescue= expect boolean arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->do_routing = do_routing;
+    u->on_hotplug = on_hotplug;
+    u->on_rescue = on_rescue;
+    u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->protocol = pa_native_protocol_get(m->core);
+    pa_native_protocol_install_ext(u->protocol, m, extension_cb);
+
+    u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
+
+    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
+
+    /* Used to handle device description management */
+    u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
+    u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u);
+
+    /* The following slots are used to deal with routing */
+    /* A little bit later than module-stream-restore, but before module-intended-roles */
+    u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+5, (pa_hook_cb_t) sink_input_new_hook_callback, u);
+    u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+5, (pa_hook_cb_t) source_output_new_hook_callback, u);
+
+    if (on_hotplug) {
+        /* A little bit later than module-stream-restore, but before module-intended-roles */
+        u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+5, (pa_hook_cb_t) sink_put_hook_callback, u);
+        u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+5, (pa_hook_cb_t) source_put_hook_callback, u);
+    }
+
+    if (on_rescue) {
+        /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
+        u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+5, (pa_hook_cb_t) sink_unlink_hook_callback, u);
+        u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+5, (pa_hook_cb_t) source_unlink_hook_callback, u);
+    }
+
+    if (!(fname = pa_state_path("device-manager", true)))
+        goto fail;
+
+    if (!(u->database = pa_database_open(fname, true))) {
+        pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
+        pa_xfree(fname);
+        goto fail;
+    }
+
+    pa_log_info("Successfully opened database file '%s'.", fname);
+    pa_xfree(fname);
+
+    /* Attempt to inject the devices into the list in priority order */
+    total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources));
+    if (total_devices > 0 && total_devices < 128) {
+        uint32_t i;
+        struct prioritised_indexes p_i[128];
+
+        /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
+        i = 0;
+        PA_IDXSET_FOREACH(sink, m->core->sinks, idx) {
+            pa_log_debug("Found sink index %u", sink->index);
+            p_i[i  ].index = sink->index;
+            p_i[i++].priority = sink->priority;
+        }
+        /* Bubble sort it (only really useful for first time creation) */
+        if (i > 1)
+          for (uint32_t j = 0; j < i; ++j)
+              for (uint32_t k = 0; k < i; ++k)
+                  if (p_i[j].priority > p_i[k].priority) {
+                      struct prioritised_indexes tmp_pi = p_i[k];
+                      p_i[k] = p_i[j];
+                      p_i[j] = tmp_pi;
+                  }
+        /* Register it */
+        for (uint32_t j = 0; j < i; ++j)
+            subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
+
+        /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
+        i = 0;
+        PA_IDXSET_FOREACH(source, m->core->sources, idx) {
+            p_i[i  ].index = source->index;
+            p_i[i++].priority = source->priority;
+        }
+        /* Bubble sort it (only really useful for first time creation) */
+        if (i > 1)
+          for (uint32_t j = 0; j < i; ++j)
+              for (uint32_t k = 0; k < i; ++k)
+                  if (p_i[j].priority > p_i[k].priority) {
+                      struct prioritised_indexes tmp_pi = p_i[k];
+                      p_i[k] = p_i[j];
+                      p_i[j] = tmp_pi;
+                  }
+        /* Register it */
+        for (uint32_t j = 0; j < i; ++j)
+            subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
+    }
+    else if (total_devices > 0) {
+        /* This user has a *lot* of devices... */
+        PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
+            subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
+
+        PA_IDXSET_FOREACH(source, m->core->sources, idx)
+            subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
+    }
+
+    /* Perform the routing (if it's enabled) which will update our priority list cache too */
+    for (uint32_t i = 0; i < NUM_ROLES; ++i) {
+        u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
+    }
+
+    route_sink_inputs(u, NULL);
+    route_source_outputs(u, NULL);
+
+#ifdef DUMP_DATABASE
+    dump_database(u);
+#endif
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->subscription)
+        pa_subscription_free(u->subscription);
+
+    if (u->sink_new_hook_slot)
+        pa_hook_slot_free(u->sink_new_hook_slot);
+    if (u->source_new_hook_slot)
+        pa_hook_slot_free(u->source_new_hook_slot);
+
+    if (u->sink_input_new_hook_slot)
+        pa_hook_slot_free(u->sink_input_new_hook_slot);
+    if (u->source_output_new_hook_slot)
+        pa_hook_slot_free(u->source_output_new_hook_slot);
+
+    if (u->sink_put_hook_slot)
+        pa_hook_slot_free(u->sink_put_hook_slot);
+    if (u->source_put_hook_slot)
+        pa_hook_slot_free(u->source_put_hook_slot);
+
+    if (u->sink_unlink_hook_slot)
+        pa_hook_slot_free(u->sink_unlink_hook_slot);
+    if (u->source_unlink_hook_slot)
+        pa_hook_slot_free(u->source_unlink_hook_slot);
+
+    if (u->connection_unlink_hook_slot)
+        pa_hook_slot_free(u->connection_unlink_hook_slot);
+
+    if (u->save_time_event)
+        u->core->mainloop->time_free(u->save_time_event);
+
+    if (u->database)
+        pa_database_close(u->database);
+
+    if (u->protocol) {
+        pa_native_protocol_remove_ext(u->protocol, m);
+        pa_native_protocol_unref(u->protocol);
+    }
+
+    if (u->subscribed)
+        pa_idxset_free(u->subscribed, NULL);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
new file mode 100644 (file)
index 0000000..8d7b34b
--- /dev/null
@@ -0,0 +1,1315 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+  Copyright 2011 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
+#include <pulse/format.h>
+#include <pulse/internal.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/protocol-native.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/database.h>
+#include <pulsecore/tagstruct.h>
+
+#include "module-device-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+        "restore_port=<Save/restore port?> "
+        "restore_volume=<Save/restore volumes?> "
+        "restore_muted=<Save/restore muted states?> "
+        "restore_formats=<Save/restore saved formats?>");
+
+#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
+
+static const char* const valid_modargs[] = {
+    "restore_volume",
+    "restore_muted",
+    "restore_port",
+    "restore_formats",
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_subscription *subscription;
+    pa_time_event *save_time_event;
+    pa_database *database;
+
+    pa_native_protocol *protocol;
+    pa_idxset *subscribed;
+
+    bool restore_volume:1;
+    bool restore_muted:1;
+    bool restore_port:1;
+    bool restore_formats:1;
+};
+
+/* Protocol extension commands */
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT,
+    SUBCOMMAND_READ_FORMATS_ALL,
+    SUBCOMMAND_READ_FORMATS,
+    SUBCOMMAND_SAVE_FORMATS
+};
+
+#define ENTRY_VERSION 1
+
+struct entry {
+    uint8_t version;
+    bool port_valid;
+    char *port;
+};
+
+#define PERPORTENTRY_VERSION 1
+
+struct perportentry {
+    uint8_t version;
+    bool muted_valid, volume_valid;
+    bool muted;
+    pa_channel_map channel_map;
+    pa_cvolume volume;
+    pa_idxset *formats;
+};
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(u);
+
+    pa_assert(e == u->save_time_event);
+    u->core->mainloop->time_free(u->save_time_event);
+    u->save_time_event = NULL;
+
+    pa_database_sync(u->database);
+    pa_log_info("Synced.");
+}
+
+static void trigger_save(struct userdata *u, pa_device_type_t type, uint32_t sink_idx) {
+    pa_native_connection *c;
+    uint32_t idx;
+
+    if (sink_idx != PA_INVALID_INDEX) {
+        PA_IDXSET_FOREACH(c, u->subscribed, idx) {
+            pa_tagstruct *t;
+
+            t = pa_tagstruct_new();
+            pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
+            pa_tagstruct_putu32(t, 0);
+            pa_tagstruct_putu32(t, u->module->index);
+            pa_tagstruct_puts(t, u->module->name);
+            pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
+            pa_tagstruct_putu32(t, type);
+            pa_tagstruct_putu32(t, sink_idx);
+
+            pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
+        }
+    }
+
+    if (u->save_time_event)
+        return;
+
+    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
+}
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+/* Some forward declarations */
+static bool legacy_entry_read(struct userdata *u, pa_datum *data, struct entry **entry, struct perportentry **perportentry);
+static struct perportentry* perportentry_read(struct userdata *u, const char *basekeyname, const char *port);
+static bool perportentry_write(struct userdata *u, const char *basekeyname, const char *port, const struct perportentry *e);
+static void perportentry_free(struct perportentry* e);
+#endif
+
+static struct entry* entry_new(void) {
+    struct entry *r = pa_xnew0(struct entry, 1);
+    r->version = ENTRY_VERSION;
+    return r;
+}
+
+static void entry_free(struct entry* e) {
+    pa_assert(e);
+
+    pa_xfree(e->port);
+    pa_xfree(e);
+}
+
+static bool entry_write(struct userdata *u, const char *name, const struct entry *e) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    bool r;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_put_boolean(t, e->port_valid);
+    pa_tagstruct_puts(t, e->port);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, true) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
+static struct entry* entry_read(struct userdata *u, const char *name) {
+    pa_datum key, data;
+    struct entry *e = NULL;
+    pa_tagstruct *t = NULL;
+    const char* port;
+
+    pa_assert(u);
+    pa_assert(name);
+
+    key.data = (char*) name;
+    key.size = strlen(name);
+
+    pa_zero(data);
+
+    if (!pa_database_get(u->database, &key, &data)) {
+        pa_log_debug("Database contains no data for key: %s", name);
+        return NULL;
+    }
+
+    t = pa_tagstruct_new_fixed(data.data, data.size);
+    e = entry_new();
+
+    if (pa_tagstruct_getu8(t, &e->version) < 0 ||
+        e->version > ENTRY_VERSION ||
+        pa_tagstruct_get_boolean(t, &e->port_valid) < 0 ||
+        pa_tagstruct_gets(t, &port) < 0) {
+
+        goto fail;
+    }
+
+    if (!pa_tagstruct_eof(t))
+        goto fail;
+
+    e->port = pa_xstrdup(port);
+
+    pa_tagstruct_free(t);
+    pa_datum_free(&data);
+
+    return e;
+
+fail:
+
+    pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
+
+    if (e)
+        entry_free(e);
+    if (t)
+        pa_tagstruct_free(t);
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+{
+    struct perportentry *ppe;
+    pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
+    if (legacy_entry_read(u, &data, &e, &ppe)) {
+        bool written = false;
+
+        pa_log_debug("Success. Saving new format for key: %s", name);
+        written = entry_write(u, name, e);
+
+        /* Now convert the legacy entry into per-port entries */
+        if (0 == strncmp("sink:", name, 5)) {
+            pa_sink *sink;
+
+            if ((sink = pa_namereg_get(u->core, name+5, PA_NAMEREG_SINK))) {
+                /* Write a "null" port entry. The read code will automatically try this
+                 * if it cannot find a specific port-named entry. */
+                written = perportentry_write(u, name, NULL, ppe) || written;
+            }
+        } else if (0 == strncmp("source:", name, 7)) {
+            pa_source *source;
+
+            if ((source = pa_namereg_get(u->core, name+7, PA_NAMEREG_SOURCE))) {
+                /* Write a "null" port entry. The read code will automatically try this
+                 * if it cannot find a specific port-named entry. */
+                written = perportentry_write(u, name, NULL, ppe) || written;
+            }
+        }
+        perportentry_free(ppe);
+
+        if (written)
+            /* NB The device type doesn't matter when we pass in an invalid index. */
+            trigger_save(u, PA_DEVICE_TYPE_SINK, PA_INVALID_INDEX);
+
+        pa_datum_free(&data);
+        return e;
+    }
+    pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
+}
+#endif
+
+    pa_datum_free(&data);
+    return NULL;
+}
+
+static struct entry* entry_copy(const struct entry *e) {
+    struct entry* r;
+
+    pa_assert(e);
+    r = entry_new();
+    r->version = e->version;
+    r->port_valid = e->port_valid;
+    r->port = pa_xstrdup(e->port);
+
+    return r;
+}
+
+static bool entries_equal(const struct entry *a, const struct entry *b) {
+
+    pa_assert(a && b);
+
+    if (a->port_valid != b->port_valid ||
+        (a->port_valid && !pa_streq(a->port, b->port)))
+        return false;
+
+    return true;
+}
+
+static struct perportentry* perportentry_new(bool add_pcm_format) {
+    struct perportentry *r = pa_xnew0(struct perportentry, 1);
+    r->version = PERPORTENTRY_VERSION;
+    r->formats = pa_idxset_new(NULL, NULL);
+    if (add_pcm_format) {
+        pa_format_info *f = pa_format_info_new();
+        f->encoding = PA_ENCODING_PCM;
+        pa_idxset_put(r->formats, f, NULL);
+    }
+    return r;
+}
+
+static void perportentry_free(struct perportentry* e) {
+    pa_assert(e);
+
+    pa_idxset_free(e->formats, (pa_free_cb_t) pa_format_info_free);
+    pa_xfree(e);
+}
+
+static bool perportentry_write(struct userdata *u, const char *basekeyname, const char *port, const struct perportentry *e) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    bool r;
+    uint32_t i;
+    pa_format_info *f;
+    uint8_t n_formats;
+    char *name;
+
+    pa_assert(u);
+    pa_assert(basekeyname);
+    pa_assert(e);
+
+    name = pa_sprintf_malloc("%s:%s", basekeyname, (port ? port : "null"));
+
+    n_formats = pa_idxset_size(e->formats);
+    pa_assert(n_formats > 0);
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_put_boolean(t, e->volume_valid);
+    pa_tagstruct_put_channel_map(t, &e->channel_map);
+    pa_tagstruct_put_cvolume(t, &e->volume);
+    pa_tagstruct_put_boolean(t, e->muted_valid);
+    pa_tagstruct_put_boolean(t, e->muted);
+    pa_tagstruct_putu8(t, n_formats);
+
+    PA_IDXSET_FOREACH(f, e->formats, i) {
+        pa_tagstruct_put_format_info(t, f);
+    }
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, true) == 0);
+
+    pa_tagstruct_free(t);
+    pa_xfree(name);
+
+    return r;
+}
+
+static struct perportentry* perportentry_read(struct userdata *u, const char *basekeyname, const char *port) {
+    pa_datum key, data;
+    struct perportentry *e = NULL;
+    pa_tagstruct *t = NULL;
+    uint8_t i, n_formats;
+    char *name;
+
+    pa_assert(u);
+    pa_assert(basekeyname);
+
+    name = pa_sprintf_malloc("%s:%s", basekeyname, (port ? port : "null"));
+
+    key.data = name;
+    key.size = strlen(name);
+
+    pa_zero(data);
+
+    if (!pa_database_get(u->database, &key, &data))
+        goto fail;
+
+    t = pa_tagstruct_new_fixed(data.data, data.size);
+    e = perportentry_new(false);
+
+    if (pa_tagstruct_getu8(t, &e->version) < 0 ||
+        e->version > PERPORTENTRY_VERSION ||
+        pa_tagstruct_get_boolean(t, &e->volume_valid) < 0 ||
+        pa_tagstruct_get_channel_map(t, &e->channel_map) < 0 ||
+        pa_tagstruct_get_cvolume(t, &e->volume) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
+        pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) {
+
+        goto fail;
+    }
+
+    for (i = 0; i < n_formats; ++i) {
+        pa_format_info *f = pa_format_info_new();
+        if (pa_tagstruct_get_format_info(t, f) < 0) {
+            pa_format_info_free(f);
+            goto fail;
+        }
+        pa_idxset_put(e->formats, f, NULL);
+    }
+
+    if (!pa_tagstruct_eof(t))
+        goto fail;
+
+    if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
+        pa_log_warn("Invalid channel map stored in database for device %s", name);
+        goto fail;
+    }
+
+    if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) {
+        pa_log_warn("Volume and channel map don't match in database entry for device %s", name);
+        goto fail;
+    }
+
+    pa_tagstruct_free(t);
+    pa_datum_free(&data);
+    pa_xfree(name);
+
+    return e;
+
+fail:
+
+    if (e)
+        perportentry_free(e);
+    if (t)
+        pa_tagstruct_free(t);
+
+    pa_datum_free(&data);
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+    /* Try again with a null port. This is used when dealing with migration from older versions */
+    if (port) {
+        pa_xfree(name);
+        return perportentry_read(u, basekeyname, NULL);
+    }
+#endif
+
+    pa_log_debug("Database contains no (or invalid) data for key: %s", name);
+
+    pa_xfree(name);
+
+    return NULL;
+}
+
+static struct perportentry* perportentry_copy(const struct perportentry *e) {
+    struct perportentry* r;
+    uint32_t idx;
+    pa_format_info *f;
+
+    pa_assert(e);
+    r = perportentry_new(false);
+    r->version = e->version;
+    r->muted_valid = e->muted_valid;
+    r->volume_valid = e->volume_valid;
+    r->muted = e->muted;
+    r->channel_map = e->channel_map;
+    r->volume = e->volume;
+
+    PA_IDXSET_FOREACH(f, e->formats, idx) {
+        pa_idxset_put(r->formats, pa_format_info_copy(f), NULL);
+    }
+    return r;
+}
+
+static bool perportentries_equal(const struct perportentry *a, const struct perportentry *b) {
+    pa_cvolume t;
+
+    pa_assert(a && b);
+
+    if (a->muted_valid != b->muted_valid ||
+        (a->muted_valid && (a->muted != b->muted)))
+        return false;
+
+    t = b->volume;
+    if (a->volume_valid != b->volume_valid ||
+        (a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume)))
+        return false;
+
+    if (pa_idxset_size(a->formats) != pa_idxset_size(b->formats))
+        return false;
+
+    /** TODO: Compare a bit better */
+
+    return true;
+}
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+
+#define LEGACY_ENTRY_VERSION 2
+static bool legacy_entry_read(struct userdata *u, pa_datum *data, struct entry **entry, struct perportentry **perportentry) {
+    struct legacy_entry {
+        uint8_t version;
+        bool muted_valid:1, volume_valid:1, port_valid:1;
+        bool muted:1;
+        pa_channel_map channel_map;
+        pa_cvolume volume;
+        char port[PA_NAME_MAX];
+    } PA_GCC_PACKED;
+    struct legacy_entry *le;
+
+    pa_assert(u);
+    pa_assert(data);
+    pa_assert(entry);
+    pa_assert(perportentry);
+
+    if (data->size != sizeof(struct legacy_entry)) {
+        pa_log_debug("Size does not match.");
+        return false;
+    }
+
+    le = (struct legacy_entry*)data->data;
+
+    if (le->version != LEGACY_ENTRY_VERSION) {
+        pa_log_debug("Version mismatch.");
+        return false;
+    }
+
+    if (!memchr(le->port, 0, sizeof(le->port))) {
+        pa_log_warn("Port has missing NUL byte.");
+        return false;
+    }
+
+    if (le->volume_valid && !pa_channel_map_valid(&le->channel_map)) {
+        pa_log_warn("Invalid channel map.");
+        return false;
+    }
+
+    if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
+        pa_log_warn("Volume and channel map don't match.");
+        return false;
+    }
+
+    *entry = entry_new();
+    (*entry)->port_valid = le->port_valid;
+    (*entry)->port = pa_xstrdup(le->port);
+
+    *perportentry = perportentry_new(true);
+    (*perportentry)->muted_valid = le->muted_valid;
+    (*perportentry)->volume_valid = le->volume_valid;
+    (*perportentry)->muted = le->muted;
+    (*perportentry)->channel_map = le->channel_map;
+    (*perportentry)->volume = le->volume;
+
+    return true;
+}
+#endif
+
+static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    struct userdata *u = userdata;
+    struct entry *e, *olde;
+    struct perportentry *ppe, *oldppe;
+    char *name;
+    const char *port = NULL;
+    pa_device_type_t type;
+    bool written = false;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
+        return;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
+        pa_sink *sink;
+
+        if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
+            return;
+
+        type = PA_DEVICE_TYPE_SINK;
+        name = pa_sprintf_malloc("sink:%s", sink->name);
+        if (sink->active_port)
+            port = sink->active_port->name;
+
+        if ((olde = entry_read(u, name)))
+            e = entry_copy(olde);
+        else
+            e = entry_new();
+
+        if (sink->save_port) {
+            pa_xfree(e->port);
+            e->port = pa_xstrdup(port ? port : "");
+            e->port_valid = true;
+        }
+
+        if ((oldppe = perportentry_read(u, name, port)))
+            ppe = perportentry_copy(oldppe);
+        else
+            ppe = perportentry_new(true);
+
+        if (sink->save_volume) {
+            ppe->channel_map = sink->channel_map;
+            ppe->volume = *pa_sink_get_volume(sink, false);
+            ppe->volume_valid = true;
+        }
+
+        if (sink->save_muted) {
+            ppe->muted = pa_sink_get_mute(sink, false);
+            ppe->muted_valid = true;
+        }
+    } else {
+        pa_source *source;
+
+        pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
+
+        if (!(source = pa_idxset_get_by_index(c->sources, idx)))
+            return;
+
+        type = PA_DEVICE_TYPE_SOURCE;
+        name = pa_sprintf_malloc("source:%s", source->name);
+        if (source->active_port)
+            port = source->active_port->name;
+
+        if ((olde = entry_read(u, name)))
+            e = entry_copy(olde);
+        else
+            e = entry_new();
+
+        if (source->save_port) {
+            pa_xfree(e->port);
+            e->port = pa_xstrdup(port ? port : "");
+            e->port_valid = true;
+        }
+
+        if ((oldppe = perportentry_read(u, name, port)))
+            ppe = perportentry_copy(oldppe);
+        else
+            ppe = perportentry_new(true);
+
+        if (source->save_volume) {
+            ppe->channel_map = source->channel_map;
+            ppe->volume = *pa_source_get_volume(source, false);
+            ppe->volume_valid = true;
+        }
+
+        if (source->save_muted) {
+            ppe->muted = pa_source_get_mute(source, false);
+            ppe->muted_valid = true;
+        }
+    }
+
+    pa_assert(e);
+
+    if (olde) {
+
+        if (entries_equal(olde, e)) {
+            entry_free(olde);
+            entry_free(e);
+            e = NULL;
+        } else
+            entry_free(olde);
+    }
+
+    if (e) {
+        pa_log_info("Storing port for device %s.", name);
+
+        written = entry_write(u, name, e);
+
+        entry_free(e);
+    }
+
+    pa_assert(ppe);
+
+    if (oldppe) {
+
+        if (perportentries_equal(oldppe, ppe)) {
+            perportentry_free(oldppe);
+            perportentry_free(ppe);
+            ppe = NULL;
+        } else
+            perportentry_free(oldppe);
+    }
+
+    if (ppe) {
+        pa_log_info("Storing volume/mute for device+port %s:%s.", name, (port ? port : "null"));
+
+        written = perportentry_write(u, name, port, ppe) || written;
+
+        perportentry_free(ppe);
+    }
+    pa_xfree(name);
+
+    if (written)
+        trigger_save(u, type, idx);
+}
+
+static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+    pa_assert(u->restore_port);
+
+    name = pa_sprintf_malloc("sink:%s", new_data->name);
+
+    if ((e = entry_read(u, name))) {
+
+        if (e->port_valid) {
+            if (!new_data->active_port) {
+                pa_log_info("Restoring port for sink %s.", name);
+                pa_sink_new_data_set_port(new_data, e->port);
+                new_data->save_port = true;
+            } else
+                pa_log_debug("Not restoring port for sink %s, because already set.", name);
+        }
+
+        entry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct perportentry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+    pa_assert(u->restore_volume || u->restore_muted);
+
+    name = pa_sprintf_malloc("sink:%s", new_data->name);
+
+    if ((e = perportentry_read(u, name, new_data->active_port))) {
+
+        if (u->restore_volume && e->volume_valid) {
+
+            if (!new_data->volume_is_set) {
+                pa_cvolume v;
+                char buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+                v = e->volume;
+                pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
+                pa_sink_new_data_set_volume(new_data, &v);
+                pa_log_info("Restoring volume for sink %s: %s", new_data->name,
+                            pa_cvolume_snprint_verbose(buf, sizeof(buf), &new_data->volume, &new_data->channel_map, false));
+
+                new_data->save_volume = true;
+            } else
+                pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name);
+        }
+
+        if (u->restore_muted && e->muted_valid) {
+
+            if (!new_data->muted_is_set) {
+                pa_sink_new_data_set_muted(new_data, e->muted);
+                new_data->save_muted = true;
+                pa_log_info("Restoring mute state for sink %s: %smuted", new_data->name,
+                            new_data->muted ? "" : "un");
+            } else
+                pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name);
+        }
+
+        perportentry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_port_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+    char *name;
+    struct perportentry *e;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+    pa_assert(u->restore_volume || u->restore_muted);
+
+    name = pa_sprintf_malloc("sink:%s", sink->name);
+
+    if ((e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) {
+
+        if (u->restore_volume && e->volume_valid) {
+            pa_cvolume v;
+
+            pa_log_info("Restoring volume for sink %s.", sink->name);
+            v = e->volume;
+            pa_cvolume_remap(&v, &e->channel_map, &sink->channel_map);
+            pa_sink_set_volume(sink, &v, true, false);
+
+            sink->save_volume = true;
+        }
+
+        if (u->restore_muted && e->muted_valid) {
+
+            pa_log_info("Restoring mute state for sink %s.", sink->name);
+            pa_sink_set_mute(sink, e->muted, false);
+            sink->save_muted = true;
+        }
+
+        perportentry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+    char *name;
+    struct perportentry *e;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+    pa_assert(u->restore_formats);
+
+    name = pa_sprintf_malloc("sink:%s", sink->name);
+
+    if ((e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) {
+
+        if (!pa_sink_set_formats(sink, e->formats))
+            pa_log_debug("Could not set format on sink %s", sink->name);
+
+        perportentry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+    pa_assert(u->restore_port);
+
+    name = pa_sprintf_malloc("source:%s", new_data->name);
+
+    if ((e = entry_read(u, name))) {
+
+        if (e->port_valid) {
+            if (!new_data->active_port) {
+                pa_log_info("Restoring port for source %s.", name);
+                pa_source_new_data_set_port(new_data, e->port);
+                new_data->save_port = true;
+            } else
+                pa_log_debug("Not restoring port for source %s, because already set.", name);
+        }
+
+        entry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct perportentry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+    pa_assert(u->restore_volume || u->restore_muted);
+
+    name = pa_sprintf_malloc("source:%s", new_data->name);
+
+    if ((e = perportentry_read(u, name, new_data->active_port))) {
+
+        if (u->restore_volume && e->volume_valid) {
+
+            if (!new_data->volume_is_set) {
+                pa_cvolume v;
+                char buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+                v = e->volume;
+                pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
+                pa_source_new_data_set_volume(new_data, &v);
+                pa_log_info("Restoring volume for source %s: %s", new_data->name,
+                            pa_cvolume_snprint_verbose(buf, sizeof(buf), &new_data->volume, &new_data->channel_map, false));
+
+                new_data->save_volume = true;
+            } else
+                pa_log_debug("Not restoring volume for source %s, because already set.", new_data->name);
+        }
+
+        if (u->restore_muted && e->muted_valid) {
+
+            if (!new_data->muted_is_set) {
+                pa_source_new_data_set_muted(new_data, e->muted);
+                new_data->save_muted = true;
+                pa_log_info("Restoring mute state for source %s: %smuted", new_data->name,
+                            new_data->muted ? "" : "un");
+            } else
+                pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name);
+        }
+
+        perportentry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_port_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
+    char *name;
+    struct perportentry *e;
+
+    pa_assert(c);
+    pa_assert(source);
+    pa_assert(u);
+    pa_assert(u->restore_volume || u->restore_muted);
+
+    name = pa_sprintf_malloc("source:%s", source->name);
+
+    if ((e = perportentry_read(u, name, (source->active_port ? source->active_port->name : NULL)))) {
+
+        if (u->restore_volume && e->volume_valid) {
+            pa_cvolume v;
+
+            pa_log_info("Restoring volume for source %s.", source->name);
+            v = e->volume;
+            pa_cvolume_remap(&v, &e->channel_map, &source->channel_map);
+            pa_source_set_volume(source, &v, true, false);
+
+            source->save_volume = true;
+        }
+
+        if (u->restore_muted && e->muted_valid) {
+
+            pa_log_info("Restoring mute state for source %s.", source->name);
+            pa_source_set_mute(source, e->muted, false);
+            source->save_muted = true;
+        }
+
+        perportentry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+#define EXT_VERSION 1
+
+static void read_sink_format_reply(struct userdata *u, pa_tagstruct *reply, pa_sink *sink) {
+    struct perportentry *e;
+    char *name;
+
+    pa_assert(u);
+    pa_assert(reply);
+    pa_assert(sink);
+
+    pa_tagstruct_putu32(reply, PA_DEVICE_TYPE_SINK);
+    pa_tagstruct_putu32(reply, sink->index);
+
+    /* Read or create an entry */
+    name = pa_sprintf_malloc("sink:%s", sink->name);
+    if (!(e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) {
+        /* Fake a reply with PCM encoding supported */
+        pa_format_info *f = pa_format_info_new();
+
+        pa_tagstruct_putu8(reply, 1);
+        f->encoding = PA_ENCODING_PCM;
+        pa_tagstruct_put_format_info(reply, f);
+
+        pa_format_info_free(f);
+    } else {
+        uint32_t idx;
+        pa_format_info *f;
+
+        /* Write all the formats from the entry to the reply */
+        pa_tagstruct_putu8(reply, pa_idxset_size(e->formats));
+        PA_IDXSET_FOREACH(f, e->formats, idx) {
+            pa_tagstruct_put_format_info(reply, f);
+        }
+        perportentry_free(e);
+    }
+    pa_xfree(name);
+}
+
+static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
+    struct userdata *u;
+    uint32_t command;
+    pa_tagstruct *reply = NULL;
+
+    pa_assert(p);
+    pa_assert(m);
+    pa_assert(c);
+    pa_assert(t);
+
+    u = m->userdata;
+
+    if (pa_tagstruct_getu32(t, &command) < 0)
+        goto fail;
+
+    reply = pa_tagstruct_new();
+    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+    pa_tagstruct_putu32(reply, tag);
+
+    switch (command) {
+        case SUBCOMMAND_TEST: {
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            pa_tagstruct_putu32(reply, EXT_VERSION);
+            break;
+        }
+
+        case SUBCOMMAND_SUBSCRIBE: {
+
+            bool enabled;
+
+            if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
+                !pa_tagstruct_eof(t))
+                goto fail;
+
+            if (enabled)
+                pa_idxset_put(u->subscribed, c, NULL);
+            else
+                pa_idxset_remove_by_data(u->subscribed, c, NULL);
+
+            break;
+        }
+
+        case SUBCOMMAND_READ_FORMATS_ALL: {
+            pa_sink *sink;
+            uint32_t idx;
+
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
+                read_sink_format_reply(u, reply, sink);
+            }
+
+            break;
+        }
+        case SUBCOMMAND_READ_FORMATS: {
+            pa_device_type_t type;
+            uint32_t sink_index;
+            pa_sink *sink;
+
+            pa_assert(reply);
+
+            /* Get the sink index and the number of formats from the tagstruct */
+            if (pa_tagstruct_getu32(t, &type) < 0 ||
+                pa_tagstruct_getu32(t, &sink_index) < 0)
+                goto fail;
+
+            if (type != PA_DEVICE_TYPE_SINK) {
+                pa_log("Device format reading is only supported on sinks");
+                goto fail;
+            }
+
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            /* Now find our sink */
+            if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index)))
+                goto fail;
+
+            read_sink_format_reply(u, reply, sink);
+
+            break;
+        }
+
+        case SUBCOMMAND_SAVE_FORMATS: {
+
+            struct perportentry *e;
+            pa_device_type_t type;
+            uint32_t sink_index;
+            char *name;
+            pa_sink *sink;
+            uint8_t i, n_formats;
+
+            /* Get the sink index and the number of formats from the tagstruct */
+            if (pa_tagstruct_getu32(t, &type) < 0 ||
+                pa_tagstruct_getu32(t, &sink_index) < 0 ||
+                pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) {
+
+                goto fail;
+            }
+
+            if (type != PA_DEVICE_TYPE_SINK) {
+                pa_log("Device format saving is only supported on sinks");
+                goto fail;
+            }
+
+            /* Now find our sink */
+            if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index))) {
+                pa_log("Could not find sink #%d", sink_index);
+                goto fail;
+            }
+
+            /* Read or create an entry */
+            name = pa_sprintf_malloc("sink:%s", sink->name);
+            if (!(e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL))))
+                e = perportentry_new(false);
+            else {
+                /* Clean out any saved formats */
+                pa_idxset_free(e->formats, (pa_free_cb_t) pa_format_info_free);
+                e->formats = pa_idxset_new(NULL, NULL);
+            }
+
+            /* Read all the formats from our tagstruct */
+            for (i = 0; i < n_formats; ++i) {
+                pa_format_info *f = pa_format_info_new();
+                if (pa_tagstruct_get_format_info(t, f) < 0) {
+                    pa_format_info_free(f);
+                    perportentry_free(e);
+                    pa_xfree(name);
+                    goto fail;
+                }
+                pa_idxset_put(e->formats, f, NULL);
+            }
+
+            if (!pa_tagstruct_eof(t)) {
+                perportentry_free(e);
+                pa_xfree(name);
+                goto fail;
+            }
+
+            if (pa_sink_set_formats(sink, e->formats) && perportentry_write(u, name, (sink->active_port ? sink->active_port->name : NULL), e))
+                trigger_save(u, type, sink_index);
+            else
+                pa_log_warn("Could not save format info for sink %s", sink->name);
+
+            pa_xfree(name);
+            perportentry_free(e);
+
+            break;
+        }
+
+        default:
+            goto fail;
+    }
+
+    pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
+    return 0;
+
+fail:
+
+    if (reply)
+        pa_tagstruct_free(reply);
+
+    return -1;
+}
+
+static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
+    pa_assert(p);
+    pa_assert(c);
+    pa_assert(u);
+
+    pa_idxset_remove_by_data(u->subscribed, c, NULL);
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    char *fname;
+    pa_sink *sink;
+    pa_source *source;
+    uint32_t idx;
+    bool restore_volume = true, restore_muted = true, restore_port = true, restore_formats = true;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 ||
+        pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 ||
+        pa_modargs_get_value_boolean(ma, "restore_port", &restore_port) < 0 ||
+        pa_modargs_get_value_boolean(ma, "restore_formats", &restore_formats) < 0) {
+        pa_log("restore_port, restore_volume, restore_muted and restore_formats expect boolean arguments");
+        goto fail;
+    }
+
+    if (!restore_muted && !restore_volume && !restore_port && !restore_formats)
+        pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->restore_volume = restore_volume;
+    u->restore_muted = restore_muted;
+    u->restore_port = restore_port;
+    u->restore_formats = restore_formats;
+
+    u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->protocol = pa_native_protocol_get(m->core);
+    pa_native_protocol_install_ext(u->protocol, m, extension_cb);
+
+    pa_module_hook_connect(m, &pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
+
+    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
+
+    if (restore_port) {
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u);
+    }
+
+    if (restore_muted || restore_volume) {
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_fixate_hook_callback, u);
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) source_fixate_hook_callback, u);
+
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) sink_port_hook_callback, u);
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) source_port_hook_callback, u);
+    }
+
+    if (restore_formats)
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) sink_put_hook_callback, u);
+
+    if (!(fname = pa_state_path("device-volumes", true)))
+        goto fail;
+
+    if (!(u->database = pa_database_open(fname, true))) {
+        pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
+        pa_xfree(fname);
+        goto fail;
+    }
+
+    pa_log_info("Successfully opened database file '%s'.", fname);
+    pa_xfree(fname);
+
+    PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
+        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
+
+    PA_IDXSET_FOREACH(source, m->core->sources, idx)
+        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->subscription)
+        pa_subscription_free(u->subscription);
+
+    if (u->save_time_event) {
+        u->core->mainloop->time_free(u->save_time_event);
+        pa_database_sync(u->database);
+    }
+
+    if (u->database)
+        pa_database_close(u->database);
+
+    if (u->protocol) {
+        pa_native_protocol_remove_ext(u->protocol, m);
+        pa_native_protocol_unref(u->protocol);
+    }
+
+    if (u->subscribed)
+        pa_idxset_free(u->subscribed, NULL);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
new file mode 100644 (file)
index 0000000..280ca25
--- /dev/null
@@ -0,0 +1,2255 @@
+/***
+  This file is part of PulseAudio.
+
+  This module is based off Lennart Poettering's LADSPA sink and swaps out
+  LADSPA functionality for a dbus-aware STFT OLA based digital equalizer.
+  All new work is published under PulseAudio's original license.
+
+  Copyright 2009 Jason Newton <nevion@gmail.com>
+
+  Original Author:
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+#include <string.h>
+#include <stdint.h>
+
+//#undef __SSE2__
+#ifdef __SSE2__
+#include <xmmintrin.h>
+#include <emmintrin.h>
+#endif
+
+#include <fftw3.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/aupdate.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/database.h>
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/dbus-util.h>
+
+#include "module-equalizer-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Jason Newton");
+PA_MODULE_DESCRIPTION(_("General Purpose Equalizer"));
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        _("sink_name=<name of the sink> "
+          "sink_properties=<properties for the sink> "
+          "sink_master=<sink to connect to> "
+          "format=<sample format> "
+          "rate=<sample rate> "
+          "channels=<number of channels> "
+          "channel_map=<channel map> "
+          "autoloaded=<set if this module is being loaded automatically> "
+          "use_volume_sharing=<yes or no> "
+         ));
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+#define DEFAULT_AUTOLOADED false
+
+struct userdata {
+    pa_module *module;
+    pa_sink *sink;
+    pa_sink_input *sink_input;
+    bool autoloaded;
+
+    size_t channels;
+    size_t fft_size;//length (res) of fft
+    size_t window_size;/*
+                        *sliding window size
+                        *effectively chooses R
+                        */
+    size_t R;/* the hop size between overlapping windows
+              * the latency of the filter, calculated from window_size
+              * based on constraints of COLA and window function
+              */
+    //for twiddling with pulseaudio
+    size_t overlap_size;//window_size-R
+    size_t samples_gathered;
+    size_t input_buffer_max;
+    //message
+    float *W;//windowing function (time domain)
+    float *work_buffer, **input, **overlap_accum;
+    fftwf_complex *output_window;
+    fftwf_plan forward_plan, inverse_plan;
+    //size_t samplings;
+
+    float **Xs;
+    float ***Hs;//thread updatable copies of the freq response filters (magnitude based)
+    pa_aupdate **a_H;
+    pa_memblockq *input_q;
+    char *output_buffer;
+    size_t output_buffer_length;
+    size_t output_buffer_max_length;
+    pa_memblockq *output_q;
+    bool first_iteration;
+
+    pa_dbus_protocol *dbus_protocol;
+    char *dbus_path;
+
+    pa_database *database;
+    char **base_profiles;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "sink_master",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "autoloaded",
+    "use_volume_sharing",
+    NULL
+};
+
+#define v_size 4
+#define SINKLIST "equalized_sinklist"
+#define EQDB "equalizer_db"
+#define EQ_STATE_DB "equalizer-state"
+#define FILTER_SIZE(u) ((u)->fft_size / 2 + 1)
+#define CHANNEL_PROFILE_SIZE(u) (FILTER_SIZE(u) + 1)
+#define FILTER_STATE_SIZE(u) (CHANNEL_PROFILE_SIZE(u) * (u)->channels)
+
+static void dbus_init(struct userdata *u);
+static void dbus_done(struct userdata *u);
+
+static void hanning_window(float *W, size_t window_size) {
+    /* h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2 */
+    for (size_t i = 0; i < window_size; ++i)
+        W[i] = (float).5 * (1 - cos(2*M_PI*i / (window_size+1)));
+}
+
+static void fix_filter(float *H, size_t fft_size) {
+    /* divide out the fft gain */
+    for (size_t i = 0; i < fft_size / 2 + 1; ++i)
+        H[i] /= fft_size;
+}
+
+static void interpolate(float *samples, size_t length, uint32_t *xs, float *ys, size_t n_points) {
+    /* Note that xs must be monotonically increasing! */
+    float x_range_lower, x_range_upper, c0;
+
+    pa_assert(n_points >= 2);
+    pa_assert(xs[0] == 0);
+    pa_assert(xs[n_points - 1] == length - 1);
+
+    for (size_t x = 0, x_range_lower_i = 0; x < length-1; ++x) {
+        pa_assert(x_range_lower_i < n_points-1);
+
+        x_range_lower = (float) xs[x_range_lower_i];
+        x_range_upper = (float) xs[x_range_lower_i+1];
+
+        pa_assert_se(x_range_lower < x_range_upper);
+        pa_assert_se(x >= x_range_lower);
+        pa_assert_se(x <= x_range_upper);
+
+        /* bilinear-interpolation of coefficients specified */
+        c0 = (x-x_range_lower) / (x_range_upper-x_range_lower);
+        pa_assert(c0 >= 0 && c0 <= 1.0);
+
+        samples[x] = ((1.0f - c0) * ys[x_range_lower_i] + c0 * ys[x_range_lower_i + 1]);
+        while(x >= xs[x_range_lower_i + 1])
+            x_range_lower_i++;
+    }
+
+    samples[length-1] = ys[n_points-1];
+}
+
+static bool is_monotonic(const uint32_t *xs, size_t length) {
+    pa_assert(xs);
+
+    if (length < 2)
+        return true;
+
+    for(size_t i = 1; i < length; ++i)
+        if (xs[i] <= xs[i-1])
+            return false;
+
+    return true;
+}
+
+/* ensures memory allocated is a multiple of v_size and aligned */
+static void * alloc(size_t x, size_t s) {
+    size_t f;
+    float *t;
+
+    f = PA_ROUND_UP(x*s, sizeof(float)*v_size);
+    pa_assert_se(t = fftwf_malloc(f));
+    pa_memzero(t, f);
+
+    return t;
+}
+
+static void alloc_input_buffers(struct userdata *u, size_t min_buffer_length) {
+    if (min_buffer_length <= u->input_buffer_max)
+        return;
+
+    pa_assert(min_buffer_length >= u->window_size);
+    for (size_t c = 0; c < u->channels; ++c) {
+        float *tmp = alloc(min_buffer_length, sizeof(float));
+        if (u->input[c]) {
+            if (!u->first_iteration)
+                memcpy(tmp, u->input[c], u->overlap_size * sizeof(float));
+            fftwf_free(u->input[c]);
+        }
+        u->input[c] = tmp;
+    }
+    u->input_buffer_max = min_buffer_length;
+}
+
+/* Called from I/O thread context */
+static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            //size_t fs=pa_frame_size(&u->sink->sample_spec);
+
+            /* The sink is _put() before the sink input is, so let's
+             * make sure we don't access it in that time. Also, the
+             * sink input is first shut down, the sink second. */
+            if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+                !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            *((int64_t*) data) =
+                /* Get the latency of the master sink */
+                pa_sink_get_latency_within_thread(u->sink_input->sink, true) +
+
+                /* Add the latency internal to our sink input on top */
+                pa_bytes_to_usec(pa_memblockq_get_length(u->output_q) +
+                                 pa_memblockq_get_length(u->input_q), &u->sink_input->sink->sample_spec) +
+                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
+            //    pa_bytes_to_usec(u->samples_gathered * fs, &u->sink->sample_spec);
+            //+ pa_bytes_to_usec(u->latency * fs, ss)
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
+
+            /* When set to running or idle for the first time, request a rewind
+             * of the master sink to make sure we are heard immediately */
+            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
+                pa_log_debug("Requesting rewind due to state change.");
+                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
+            }
+            break;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(state) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return 0;
+
+    pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_request_rewind_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes+pa_memblockq_get_length(u->input_q), true, false, false);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_set_requested_latency_within_thread(
+            u->sink_input,
+            pa_sink_get_requested_latency_within_thread(s));
+}
+
+/* Called from main context */
+static void sink_set_volume_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return;
+
+    pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, true);
+}
+
+/* Called from main context */
+static void sink_set_mute_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return;
+
+    pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted);
+}
+
+#if 1
+//reference implementation
+static void dsp_logic(
+    float * restrict dst,//used as a temp array too, needs to be fft_length!
+    float * restrict src,/*input data w/ overlap at start,
+                               *automatically cycled in routine
+                               */
+    float * restrict overlap,
+    const float X,//multiplier
+    const float * restrict H,//The freq. magnitude scalers filter
+    const float * restrict W,//The windowing function
+    fftwf_complex * restrict output_window,//The transformed windowed src
+    struct userdata *u) {
+
+    //use a linear-phase sliding STFT and overlap-add method (for each channel)
+    //window the data
+    for(size_t j = 0; j < u->window_size; ++j) {
+        dst[j] = X * W[j] * src[j];
+    }
+    //zero pad the remaining fft window
+    memset(dst + u->window_size, 0, (u->fft_size - u->window_size) * sizeof(float));
+    //Processing is done here!
+    //do fft
+    fftwf_execute_dft_r2c(u->forward_plan, dst, output_window);
+    //perform filtering
+    for(size_t j = 0; j < FILTER_SIZE(u); ++j) {
+        u->output_window[j][0] *= H[j];
+        u->output_window[j][1] *= H[j];
+    }
+    //inverse fft
+    fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst);
+    ////debug: tests overlapping add
+    ////and negates ALL PREVIOUS processing
+    ////yields a perfect reconstruction if COLA is held
+    //for(size_t j = 0; j < u->window_size; ++j) {
+    //    u->work_buffer[j] = u->W[j] * u->input[c][j];
+    //}
+
+    //overlap add and preserve overlap component from this window (linear phase)
+    for(size_t j = 0; j < u->overlap_size; ++j) {
+        u->work_buffer[j] += overlap[j];
+        overlap[j] = dst[u->R + j];
+    }
+    ////debug: tests if basic buffering works
+    ////shouldn't modify the signal AT ALL (beyond roundoff)
+    //for(size_t j = 0; j < u->window_size;++j) {
+    //    u->work_buffer[j] = u->input[c][j];
+    //}
+
+    //preserve the needed input for the next window's overlap
+    memmove(src, src + u->R,
+        (u->samples_gathered - u->R) * sizeof(float)
+    );
+}
+#else
+typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float))));
+typedef union float_vector {
+    float f[v_size];
+    v4sf v;
+    __m128 m;
+} float_vector_t;
+
+//regardless of sse enabled, the loops in here assume
+//16 byte aligned addresses and memory allocations divisible by v_size
+static void dsp_logic(
+    float * restrict dst,//used as a temp array too, needs to be fft_length!
+    float * restrict src,/*input data w/ overlap at start,
+                               *automatically cycled in routine
+                               */
+    float * restrict overlap,//The size of the overlap
+    const float X,//multiplier
+    const float * restrict H,//The freq. magnitude scalers filter
+    const float * restrict W,//The windowing function
+    fftwf_complex * restrict output_window,//The transformed windowed src
+    struct userdata *u) {//Collection of constants
+    const size_t overlap_size = PA_ROUND_UP(u->overlap_size, v_size);
+    float_vector_t x;
+    x.f[0] = x.f[1] = x.f[2] = x.f[3] = X;
+
+    //assert(u->samples_gathered >= u->R);
+    //use a linear-phase sliding STFT and overlap-add method
+    for(size_t j = 0; j < u->window_size; j += v_size) {
+        //dst[j] = W[j] * src[j];
+        float_vector_t *d = (float_vector_t*) (dst + j);
+        float_vector_t *w = (float_vector_t*) (W + j);
+        float_vector_t *s = (float_vector_t*) (src + j);
+//#if __SSE2__
+        d->m = _mm_mul_ps(x.m, _mm_mul_ps(w->m, s->m));
+//        d->v = x->v * w->v * s->v;
+//#endif
+    }
+    //zero pad the remaining fft window
+    memset(dst + u->window_size, 0, (u->fft_size - u->window_size) * sizeof(float));
+
+    //Processing is done here!
+    //do fft
+    fftwf_execute_dft_r2c(u->forward_plan, dst, output_window);
+    //perform filtering - purely magnitude based
+    for(size_t j = 0; j < FILTER_SIZE; j += v_size / 2) {
+        //output_window[j][0]*=H[j];
+        //output_window[j][1]*=H[j];
+        float_vector_t *d = (float_vector_t*)( ((float *) output_window) + 2 * j);
+        float_vector_t h;
+        h.f[0] = h.f[1] = H[j];
+        h.f[2] = h.f[3] = H[j + 1];
+//#if __SSE2__
+        d->m = _mm_mul_ps(d->m, h.m);
+//#else
+//        d->v = d->v * h.v;
+//#endif
+    }
+
+    //inverse fft
+    fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst);
+
+    ////debug: tests overlapping add
+    ////and negates ALL PREVIOUS processing
+    ////yields a perfect reconstruction if COLA is held
+    //for(size_t j = 0; j < u->window_size; ++j) {
+    //    dst[j] = W[j] * src[j];
+    //}
+
+    //overlap add and preserve overlap component from this window (linear phase)
+    for(size_t j = 0; j < overlap_size; j += v_size) {
+        //dst[j]+=overlap[j];
+        //overlap[j]+=dst[j+R];
+        float_vector_t *d = (float_vector_t*)(dst + j);
+        float_vector_t *o = (float_vector_t*)(overlap + j);
+//#if __SSE2__
+        d->m = _mm_add_ps(d->m, o->m);
+        o->m = ((float_vector_t*)(dst + u->R + j))->m;
+//#else
+//        d->v = d->v + o->v;
+//        o->v = ((float_vector_t*)(dst + u->R + j))->v;
+//#endif
+    }
+    //memcpy(overlap, dst+u->R, u->overlap_size * sizeof(float)); //overlap preserve (debug)
+    //zero out the bit beyond the real overlap so we don't add garbage next iteration
+    memset(overlap + u->overlap_size, 0, overlap_size - u->overlap_size);
+
+    ////debug: tests if basic buffering works
+    ////shouldn't modify the signal AT ALL (beyond roundoff)
+    //for(size_t j = 0; j < u->window_size; ++j) {
+    //    dst[j] = src[j];
+    //}
+
+    //preserve the needed input for the next window's overlap
+    memmove(src, src + u->R,
+        (u->samples_gathered - u->R) * sizeof(float)
+    );
+}
+#endif
+
+static void flatten_to_memblockq(struct userdata *u) {
+    size_t mbs = pa_mempool_block_size_max(u->sink->core->mempool);
+    pa_memchunk tchunk;
+    char *dst;
+    size_t i = 0;
+    while(i < u->output_buffer_length) {
+        tchunk.index = 0;
+        tchunk.length = PA_MIN((u->output_buffer_length - i), mbs);
+        tchunk.memblock = pa_memblock_new(u->sink->core->mempool, tchunk.length);
+        //pa_log_debug("pushing %ld into the q", tchunk.length);
+        dst = pa_memblock_acquire(tchunk.memblock);
+        memcpy(dst, u->output_buffer + i, tchunk.length);
+        pa_memblock_release(tchunk.memblock);
+        pa_memblockq_push(u->output_q, &tchunk);
+        pa_memblock_unref(tchunk.memblock);
+        i += tchunk.length;
+    }
+}
+
+static void process_samples(struct userdata *u) {
+    size_t fs = pa_frame_size(&(u->sink->sample_spec));
+    unsigned a_i;
+    float *H, X;
+    size_t iterations, offset;
+    pa_assert(u->samples_gathered >= u->window_size);
+    iterations = (u->samples_gathered - u->overlap_size) / u->R;
+    //make sure there is enough buffer memory allocated
+    if (iterations * u->R * fs > u->output_buffer_max_length) {
+        u->output_buffer_max_length = iterations * u->R * fs;
+        pa_xfree(u->output_buffer);
+        u->output_buffer = pa_xmalloc(u->output_buffer_max_length);
+    }
+    u->output_buffer_length = iterations * u->R * fs;
+
+    for(size_t iter = 0; iter < iterations; ++iter) {
+        offset = iter * u->R * fs;
+        for(size_t c = 0;c < u->channels; c++) {
+            a_i = pa_aupdate_read_begin(u->a_H[c]);
+            X = u->Xs[c][a_i];
+            H = u->Hs[c][a_i];
+            dsp_logic(
+                u->work_buffer,
+                u->input[c],
+                u->overlap_accum[c],
+                X,
+                H,
+                u->W,
+                u->output_window,
+                u
+            );
+            pa_aupdate_read_end(u->a_H[c]);
+            if (u->first_iteration) {
+                /* The windowing function will make the audio ramped in, as a cheap fix we can
+                 * undo the windowing (for non-zero window values)
+                 */
+                for(size_t i = 0; i < u->overlap_size; ++i) {
+                    u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i];
+                }
+            }
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, (uint8_t *) (((float *)u->output_buffer) + c) + offset, fs, u->work_buffer, sizeof(float), u->R);
+        }
+        if (u->first_iteration) {
+            u->first_iteration = false;
+        }
+        u->samples_gathered -= u->R;
+    }
+    flatten_to_memblockq(u);
+}
+
+static void input_buffer(struct userdata *u, pa_memchunk *in) {
+    size_t fs = pa_frame_size(&(u->sink->sample_spec));
+    size_t samples = in->length/fs;
+    float *src = pa_memblock_acquire_chunk(in);
+    pa_assert(u->samples_gathered + samples <= u->input_buffer_max);
+    for(size_t c = 0; c < u->channels; c++) {
+        //buffer with an offset after the overlap from previous
+        //iterations
+        pa_assert_se(
+            u->input[c] + u->samples_gathered + samples <= u->input[c] + u->input_buffer_max
+        );
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples);
+    }
+    u->samples_gathered += samples;
+    pa_memblock_release(in->memblock);
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+    size_t fs, target_samples;
+    size_t mbs;
+    //struct timeval start, end;
+    pa_memchunk tchunk;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+    pa_assert(chunk);
+    pa_assert(u->sink);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return -1;
+
+    /* FIXME: Please clean this up. I see more commented code lines
+     * than uncommented code lines. I am sorry, but I am too dumb to
+     * understand this. */
+
+    fs = pa_frame_size(&(u->sink->sample_spec));
+    mbs = pa_mempool_block_size_max(u->sink->core->mempool);
+    if (pa_memblockq_get_length(u->output_q) > 0) {
+        //pa_log_debug("qsize is %ld", pa_memblockq_get_length(u->output_q));
+        goto END;
+    }
+    //nbytes = PA_MIN(nbytes, pa_mempool_block_size_max(u->sink->core->mempool));
+    target_samples = PA_ROUND_UP(nbytes / fs, u->R);
+    ////pa_log_debug("vanilla mbs = %ld",mbs);
+    //mbs = PA_ROUND_DOWN(mbs / fs, u->R);
+    //mbs = PA_MAX(mbs, u->R);
+    //target_samples = PA_MAX(target_samples, mbs);
+    //pa_log_debug("target samples: %ld", target_samples);
+    if (u->first_iteration) {
+        //allocate request_size
+        target_samples = PA_MAX(target_samples, u->window_size);
+    }else{
+        //allocate request_size + overlap
+        target_samples += u->overlap_size;
+    }
+    alloc_input_buffers(u, target_samples);
+    //pa_log_debug("post target samples: %ld", target_samples);
+    chunk->memblock = NULL;
+
+    /* Hmm, process any rewind request that might be queued up */
+    pa_sink_process_rewind(u->sink, 0);
+
+    //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
+    //pa_rtclock_get(&start);
+    do{
+        size_t input_remaining = target_samples - u->samples_gathered;
+       // pa_log_debug("input remaining %ld samples", input_remaining);
+        pa_assert(input_remaining > 0);
+        while (pa_memblockq_peek(u->input_q, &tchunk) < 0) {
+            //pa_sink_render(u->sink, input_remaining * fs, &tchunk);
+            pa_sink_render_full(u->sink, PA_MIN(input_remaining * fs, mbs), &tchunk);
+            pa_memblockq_push(u->input_q, &tchunk);
+            pa_memblock_unref(tchunk.memblock);
+        }
+        pa_assert(tchunk.memblock);
+
+        tchunk.length = PA_MIN(input_remaining * fs, tchunk.length);
+
+        pa_memblockq_drop(u->input_q, tchunk.length);
+        //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
+        /* copy new input */
+        //pa_rtclock_get(start);
+       // pa_log_debug("buffering %ld bytes", tchunk.length);
+        input_buffer(u, &tchunk);
+        //pa_rtclock_get(&end);
+        //pa_log_debug("Took %0.5f seconds to setup", pa_timeval_diff(end, start) / (double) PA_USEC_PER_SEC);
+        pa_memblock_unref(tchunk.memblock);
+    } while(u->samples_gathered < target_samples);
+
+    //pa_rtclock_get(&end);
+    //pa_log_debug("Took %0.6f seconds to get data", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC);
+
+    pa_assert(u->fft_size >= u->window_size);
+    pa_assert(u->R < u->window_size);
+    //pa_rtclock_get(&start);
+    /* process a block */
+    process_samples(u);
+    //pa_rtclock_get(&end);
+    //pa_log_debug("Took %0.6f seconds to process", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC);
+END:
+    pa_assert_se(pa_memblockq_peek(u->output_q, chunk) >= 0);
+    pa_assert(chunk->memblock);
+    pa_memblockq_drop(u->output_q, chunk->length);
+
+    //pa_log_debug("gave %ld", chunk->length/fs);
+    //pa_log_debug("end pop");
+    return 0;
+}
+
+/* Called from main context */
+static void sink_input_volume_changed_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_volume_changed(u->sink, &i->volume);
+}
+
+/* Called from main context */
+static void sink_input_mute_changed_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_mute_changed(u->sink, i->muted);
+}
+
+#if 0
+static void reset_filter(struct userdata *u) {
+    size_t fs = pa_frame_size(&u->sink->sample_spec);
+    size_t max_request;
+
+    u->samples_gathered = 0;
+
+    for(size_t i = 0; i < u->channels; ++i)
+        pa_memzero(u->overlap_accum[i], u->overlap_size * sizeof(float));
+
+    u->first_iteration = true;
+    //set buffer size to max request, no overlap copy
+    max_request = PA_ROUND_UP(pa_sink_input_get_max_request(u->sink_input) / fs , u->R);
+    max_request = PA_MAX(max_request, u->window_size);
+    pa_sink_set_max_request_within_thread(u->sink, max_request * fs);
+}
+#endif
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+    size_t amount = 0;
+
+    pa_log_debug("Rewind callback!");
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If the sink is not yet linked, there is nothing to rewind */
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    if (u->sink->thread_info.rewind_nbytes > 0) {
+        size_t max_rewrite;
+
+        //max_rewrite = nbytes;
+        max_rewrite = nbytes + pa_memblockq_get_length(u->input_q);
+        //PA_MIN(pa_memblockq_get_length(u->input_q), nbytes);
+        amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
+        u->sink->thread_info.rewind_nbytes = 0;
+
+        if (amount > 0) {
+            //invalidate the output q
+            pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, true);
+            pa_log("Resetting filter");
+            //reset_filter(u); //this is the "proper" thing to do...
+        }
+    }
+
+    pa_sink_process_rewind(u->sink, amount);
+    pa_memblockq_rewind(u->input_q, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_memblockq_set_maxrewind(u->input_q, nbytes);
+    pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+    size_t fs;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    fs = pa_frame_size(&u->sink_input->sample_spec);
+    pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(nbytes / fs, u->R) * fs);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_detach_within_thread(u->sink);
+
+    pa_sink_set_rtpoll(u->sink, NULL);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct userdata *u;
+    size_t fs, max_request;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll);
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+
+    fs = pa_frame_size(&u->sink_input->sample_spec);
+    /* set buffer size to max request, no overlap copy */
+    max_request = PA_ROUND_UP(pa_sink_input_get_max_request(u->sink_input) / fs, u->R);
+    max_request = PA_MAX(max_request, u->window_size);
+
+    pa_sink_set_max_request_within_thread(u->sink, max_request * fs);
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_attach_within_thread(u->sink);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* The order here matters! We first kill the sink so that streams
+     * can properly be moved away while the sink input is still connected
+     * to the master. */
+    pa_sink_input_cork(u->sink_input, true);
+    pa_sink_unlink(u->sink);
+    pa_sink_input_unlink(u->sink_input);
+
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    /* Leave u->sink alone for now, it will be cleaned up on module
+     * unload (and it is needed during unload as well). */
+
+    pa_module_unload_request(u->module, true);
+}
+
+static void pack(char **strs, size_t len, char **packed, size_t *length) {
+    size_t t_len = 0;
+    size_t headers = (1+len) * sizeof(uint16_t);
+    char *p;
+    for(size_t i = 0; i < len; ++i) {
+        t_len += strlen(strs[i]);
+    }
+    *length = headers + t_len;
+    p = *packed = pa_xmalloc0(*length);
+    *((uint16_t *) p) = (uint16_t) len;
+    p += sizeof(uint16_t);
+    for(size_t i = 0; i < len; ++i) {
+        uint16_t l = strlen(strs[i]);
+        *((uint16_t *) p) = (uint16_t) l;
+        p += sizeof(uint16_t);
+        memcpy(p, strs[i], l);
+        p += l;
+    }
+}
+static void unpack(char *str, size_t length, char ***strs, size_t *len) {
+    char *p = str;
+    *len = *((uint16_t *) p);
+    p += sizeof(uint16_t);
+    *strs = pa_xnew(char *, *len);
+
+    for(size_t i = 0; i < *len; ++i) {
+        size_t l = *((uint16_t *) p);
+        p += sizeof(uint16_t);
+        (*strs)[i] = pa_xnew(char, l + 1);
+        memcpy((*strs)[i], p, l);
+        (*strs)[i][l] = '\0';
+        p += l;
+    }
+}
+static void save_profile(struct userdata *u, size_t channel, char *name) {
+    unsigned a_i;
+    const size_t profile_size = CHANNEL_PROFILE_SIZE(u) * sizeof(float);
+    float *H_n, *profile;
+    const float *H;
+    pa_datum key, data;
+    profile = pa_xnew0(float, profile_size);
+    a_i = pa_aupdate_read_begin(u->a_H[channel]);
+    profile[0] = u->Xs[a_i][channel];
+    H = u->Hs[channel][a_i];
+    H_n = profile + 1;
+    for(size_t i = 0 ; i < FILTER_SIZE(u); ++i) {
+        H_n[i] = H[i] * u->fft_size;
+        //H_n[i] = H[i];
+    }
+    pa_aupdate_read_end(u->a_H[channel]);
+    key.data=name;
+    key.size = strlen(key.data);
+    data.data = profile;
+    data.size = profile_size;
+    pa_database_set(u->database, &key, &data, true);
+    pa_database_sync(u->database);
+    if (u->base_profiles[channel]) {
+        pa_xfree(u->base_profiles[channel]);
+    }
+    u->base_profiles[channel] = pa_xstrdup(name);
+}
+
+static void save_state(struct userdata *u) {
+    unsigned a_i;
+    const size_t filter_state_size = FILTER_STATE_SIZE(u) * sizeof(float);
+    float *H_n, *state;
+    float *H;
+    pa_datum key, data;
+    pa_database *database;
+    char *dbname;
+    char *packed;
+    size_t packed_length;
+
+    pack(u->base_profiles, u->channels, &packed, &packed_length);
+    state = (float *) pa_xmalloc0(filter_state_size + packed_length);
+    memcpy(state + FILTER_STATE_SIZE(u), packed, packed_length);
+    pa_xfree(packed);
+
+    for(size_t c = 0; c < u->channels; ++c) {
+        a_i = pa_aupdate_read_begin(u->a_H[c]);
+        state[c * CHANNEL_PROFILE_SIZE(u)] = u->Xs[c][a_i];
+        H = u->Hs[c][a_i];
+        H_n = &state[c * CHANNEL_PROFILE_SIZE(u) + 1];
+        memcpy(H_n, H, FILTER_SIZE(u) * sizeof(float));
+        pa_aupdate_read_end(u->a_H[c]);
+    }
+
+    key.data = u->sink->name;
+    key.size = strlen(key.data);
+    data.data = state;
+    data.size = filter_state_size + packed_length;
+    //thread safety for 0.9.17?
+    pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, false));
+    pa_assert_se(database = pa_database_open(dbname, true));
+    pa_xfree(dbname);
+
+    pa_database_set(database, &key, &data, true);
+    pa_database_sync(database);
+    pa_database_close(database);
+    pa_xfree(state);
+}
+
+static void remove_profile(pa_core *c, char *name) {
+    pa_datum key;
+    pa_database *database;
+    key.data = name;
+    key.size = strlen(key.data);
+    pa_assert_se(database = pa_shared_get(c, EQDB));
+    pa_database_unset(database, &key);
+    pa_database_sync(database);
+}
+
+static const char* load_profile(struct userdata *u, size_t channel, char *name) {
+    unsigned a_i;
+    pa_datum key, value;
+    const size_t profile_size = CHANNEL_PROFILE_SIZE(u) * sizeof(float);
+    key.data = name;
+    key.size = strlen(key.data);
+    if (pa_database_get(u->database, &key, &value) != NULL) {
+        if (value.size == profile_size) {
+            float *profile = (float *) value.data;
+            a_i = pa_aupdate_write_begin(u->a_H[channel]);
+            u->Xs[channel][a_i] = profile[0];
+            memcpy(u->Hs[channel][a_i], profile + 1, FILTER_SIZE(u) * sizeof(float));
+            fix_filter(u->Hs[channel][a_i], u->fft_size);
+            pa_aupdate_write_end(u->a_H[channel]);
+            pa_xfree(u->base_profiles[channel]);
+            u->base_profiles[channel] = pa_xstrdup(name);
+        }else{
+            return "incompatible size";
+        }
+        pa_datum_free(&value);
+    }else{
+        return "profile doesn't exist";
+    }
+    return NULL;
+}
+
+static void load_state(struct userdata *u) {
+    unsigned a_i;
+    float *H;
+    pa_datum key, value;
+    pa_database *database;
+    char *dbname;
+    pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, false));
+    database = pa_database_open(dbname, false);
+    pa_xfree(dbname);
+    if (!database) {
+        pa_log("No resume state");
+        return;
+    }
+
+    key.data = u->sink->name;
+    key.size = strlen(key.data);
+
+    if (pa_database_get(database, &key, &value) != NULL) {
+        if (value.size > FILTER_STATE_SIZE(u) * sizeof(float) + sizeof(uint16_t)) {
+            float *state = (float *) value.data;
+            size_t n_profs;
+            char **names;
+            for(size_t c = 0; c < u->channels; ++c) {
+                a_i = pa_aupdate_write_begin(u->a_H[c]);
+                H = state + c * CHANNEL_PROFILE_SIZE(u) + 1;
+                u->Xs[c][a_i] = state[c * CHANNEL_PROFILE_SIZE(u)];
+                memcpy(u->Hs[c][a_i], H, FILTER_SIZE(u) * sizeof(float));
+                pa_aupdate_write_end(u->a_H[c]);
+            }
+            unpack(((char *)value.data) + FILTER_STATE_SIZE(u) * sizeof(float), value.size - FILTER_STATE_SIZE(u) * sizeof(float), &names, &n_profs);
+            n_profs = PA_MIN(n_profs, u->channels);
+            for(size_t c = 0; c < n_profs; ++c) {
+                pa_xfree(u->base_profiles[c]);
+                u->base_profiles[c] = names[c];
+            }
+            pa_xfree(names);
+        }
+        pa_datum_free(&value);
+    }else{
+        pa_log("resume state exists but is wrong size!");
+    }
+    pa_database_close(database);
+}
+
+/* Called from main context */
+static bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    return u->sink != dest;
+}
+
+/* Called from main context */
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (u->autoloaded) {
+        /* We were autoloaded, and don't support moving. Let's unload ourselves. */
+        pa_log_debug("Can't move autoloaded stream, unloading");
+        pa_module_unload_request(u->module, true);
+    }
+
+    if (dest) {
+        pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
+        pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
+    } else
+        pa_sink_set_asyncmsgq(u->sink, NULL);
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma;
+    const char *z;
+    pa_sink *master;
+    pa_sink_input_new_data sink_input_data;
+    pa_sink_new_data sink_data;
+    size_t i;
+    unsigned c;
+    float *H;
+    unsigned a_i;
+    bool use_volume_sharing = true;
+
+    pa_assert(m);
+
+    pa_log_warn("module-equalizer-sink is currently unsupported, and can sometimes cause "
+                "PulseAudio crashes, increased latency or audible artifacts.");
+    pa_log_warn("If you're facing audio problems, try unloading this module as a potential workaround.");
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink_master", NULL), PA_NAMEREG_SINK))) {
+        pa_log("Master sink not found");
+        goto fail;
+    }
+
+    ss = master->sample_spec;
+    ss.format = PA_SAMPLE_FLOAT32;
+    map = master->channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    //fs = pa_frame_size(&ss);
+
+    if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) {
+        pa_log("use_volume_sharing= expects a boolean argument");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    m->userdata = u;
+
+    u->channels = ss.channels;
+    u->fft_size = pow(2, ceil(log(ss.rate) / log(2)));//probably unstable near corner cases of powers of 2
+    pa_log_debug("fft size: %zd", u->fft_size);
+    u->window_size = 15999;
+    if (u->window_size % 2 == 0)
+        u->window_size--;
+    u->R = (u->window_size + 1) / 2;
+    u->overlap_size = u->window_size - u->R;
+    u->samples_gathered = 0;
+    u->input_buffer_max = 0;
+
+    u->a_H = pa_xnew0(pa_aupdate *, u->channels);
+    u->Xs = pa_xnew0(float *, u->channels);
+    u->Hs = pa_xnew0(float **, u->channels);
+
+    for (c = 0; c < u->channels; ++c) {
+        u->Xs[c] = pa_xnew0(float, 2);
+        u->Hs[c] = pa_xnew0(float *, 2);
+        for (i = 0; i < 2; ++i)
+            u->Hs[c][i] = alloc(FILTER_SIZE(u), sizeof(float));
+    }
+
+    u->W = alloc(u->window_size, sizeof(float));
+    u->work_buffer = alloc(u->fft_size, sizeof(float));
+    u->input = pa_xnew0(float *, u->channels);
+    u->overlap_accum = pa_xnew0(float *, u->channels);
+    for (c = 0; c < u->channels; ++c) {
+        u->a_H[c] = pa_aupdate_new();
+        u->input[c] = NULL;
+        u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float));
+    }
+    u->output_window = alloc(FILTER_SIZE(u), sizeof(fftwf_complex));
+    u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE);
+    u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE);
+
+    hanning_window(u->W, u->window_size);
+    u->first_iteration = true;
+
+    u->base_profiles = pa_xnew0(char *, u->channels);
+    for (c = 0; c < u->channels; ++c)
+        u->base_profiles[c] = pa_xstrdup("default");
+
+    /* Create sink */
+    pa_sink_new_data_init(&sink_data);
+    sink_data.driver = __FILE__;
+    sink_data.module = m;
+    if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+        sink_data.name = pa_sprintf_malloc("%s.equalizer", master->name);
+    pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+    pa_sink_new_data_set_channel_map(&sink_data, &map);
+
+    z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+    pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "FFT based equalizer on %s", z ? z : master->name);
+
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&sink_data);
+        goto fail;
+    }
+
+    u->autoloaded = DEFAULT_AUTOLOADED;
+    if (pa_modargs_get_value_boolean(ma, "autoloaded", &u->autoloaded) < 0) {
+        pa_log("Failed to parse autoloaded value");
+        goto fail;
+    }
+
+    u->sink = pa_sink_new(m->core, &sink_data, (master->flags & (PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY))
+                                               | (use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0));
+    pa_sink_new_data_done(&sink_data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg_cb;
+    u->sink->set_state = sink_set_state_cb;
+    u->sink->update_requested_latency = sink_update_requested_latency_cb;
+    u->sink->request_rewind = sink_request_rewind_cb;
+    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
+    if (!use_volume_sharing) {
+        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
+        pa_sink_enable_decibel_volume(u->sink, true);
+    }
+    u->sink->userdata = u;
+
+    u->input_q = pa_memblockq_new("module-equalizer-sink input_q", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, &u->sink->silence);
+    u->output_q = pa_memblockq_new("module-equalizer-sink output_q", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, NULL);
+    u->output_buffer = NULL;
+    u->output_buffer_length = 0;
+    u->output_buffer_max_length = 0;
+
+    pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
+    //pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->R*fs, &ss));
+
+    /* Create sink input */
+    pa_sink_input_new_data_init(&sink_input_data);
+    sink_input_data.driver = __FILE__;
+    sink_input_data.module = m;
+    pa_sink_input_new_data_set_sink(&sink_input_data, master, false);
+    sink_input_data.origin_sink = u->sink;
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream");
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+    pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+    sink_input_data.flags |= PA_SINK_INPUT_START_CORKED;
+
+    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
+    pa_sink_input_new_data_done(&sink_input_data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->update_max_request = sink_input_update_max_request_cb;
+    u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+    u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->attach = sink_input_attach_cb;
+    u->sink_input->detach = sink_input_detach_cb;
+    u->sink_input->may_move_to = sink_input_may_move_to_cb;
+    u->sink_input->moving = sink_input_moving_cb;
+    if (!use_volume_sharing)
+        u->sink_input->volume_changed = sink_input_volume_changed_cb;
+    u->sink_input->mute_changed = sink_input_mute_changed_cb;
+    u->sink_input->userdata = u;
+
+    u->sink->input_to_master = u->sink_input;
+
+    dbus_init(u);
+
+    /* default filter to these */
+    for (c = 0; c< u->channels; ++c) {
+        a_i = pa_aupdate_write_begin(u->a_H[c]);
+        H = u->Hs[c][a_i];
+        u->Xs[c][a_i] = 1.0f;
+
+        for(i = 0; i < FILTER_SIZE(u); ++i)
+            H[i] = 1.0 / sqrtf(2.0f);
+
+        fix_filter(H, u->fft_size);
+        pa_aupdate_write_end(u->a_H[c]);
+    }
+
+    /* load old parameters */
+    load_state(u);
+
+    /* The order here is important. The input must be put first,
+     * otherwise streams might attach to the sink before the sink
+     * input is attached to the master. */
+    pa_sink_input_put(u->sink_input);
+    pa_sink_put(u->sink);
+    pa_sink_input_cork(u->sink_input, false);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_sink_linked_by(u->sink);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    unsigned c;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    save_state(u);
+
+    dbus_done(u);
+
+    for(c = 0; c < u->channels; ++c)
+        pa_xfree(u->base_profiles[c]);
+    pa_xfree(u->base_profiles);
+
+    /* See comments in sink_input_kill_cb() above regarding
+     * destruction order! */
+
+    if (u->sink_input)
+        pa_sink_input_cork(u->sink_input, true);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+}
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    pa_xfree(u->output_buffer);
+    pa_memblockq_free(u->output_q);
+    pa_memblockq_free(u->input_q);
+
+    fftwf_destroy_plan(u->inverse_plan);
+    fftwf_destroy_plan(u->forward_plan);
+    fftwf_free(u->output_window);
+    for (c = 0; c < u->channels; ++c) {
+        pa_aupdate_free(u->a_H[c]);
+        fftwf_free(u->overlap_accum[c]);
+        fftwf_free(u->input[c]);
+    }
+    pa_xfree(u->a_H);
+    pa_xfree(u->overlap_accum);
+    pa_xfree(u->input);
+    fftwf_free(u->work_buffer);
+    fftwf_free(u->W);
+    for (c = 0; c < u->channels; ++c) {
+        pa_xfree(u->Xs[c]);
+        for (size_t i = 0; i < 2; ++i)
+            fftwf_free(u->Hs[c][i]);
+        fftwf_free(u->Hs[c]);
+    }
+    pa_xfree(u->Xs);
+    pa_xfree(u->Hs);
+
+    pa_xfree(u);
+}
+
+/*
+ * DBus Routines and Callbacks
+ */
+#define EXTNAME "org.PulseAudio.Ext.Equalizing1"
+#define MANAGER_PATH "/org/pulseaudio/equalizing1"
+#define MANAGER_IFACE EXTNAME ".Manager"
+#define EQUALIZER_IFACE EXTNAME ".Equalizer"
+static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_n_channels(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_save_state(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u);
+enum manager_method_index {
+    MANAGER_METHOD_REMOVE_PROFILE,
+    MANAGER_METHOD_MAX
+};
+
+pa_dbus_arg_info remove_profile_args[]={
+    {"name", "s","in"},
+};
+
+static pa_dbus_method_handler manager_methods[MANAGER_METHOD_MAX]={
+    [MANAGER_METHOD_REMOVE_PROFILE]={
+        .method_name="RemoveProfile",
+        .arguments=remove_profile_args,
+        .n_arguments=sizeof(remove_profile_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=manager_handle_remove_profile}
+};
+
+enum manager_handler_index {
+    MANAGER_HANDLER_REVISION,
+    MANAGER_HANDLER_EQUALIZED_SINKS,
+    MANAGER_HANDLER_PROFILES,
+    MANAGER_HANDLER_MAX
+};
+
+static pa_dbus_property_handler manager_handlers[MANAGER_HANDLER_MAX]={
+    [MANAGER_HANDLER_REVISION]={.property_name="InterfaceRevision",.type="u",.get_cb=manager_get_revision,.set_cb=NULL},
+    [MANAGER_HANDLER_EQUALIZED_SINKS]={.property_name="EqualizedSinks",.type="ao",.get_cb=manager_get_sinks,.set_cb=NULL},
+    [MANAGER_HANDLER_PROFILES]={.property_name="Profiles",.type="as",.get_cb=manager_get_profiles,.set_cb=NULL}
+};
+
+pa_dbus_arg_info sink_args[]={
+    {"sink", "o", NULL}
+};
+
+enum manager_signal_index{
+    MANAGER_SIGNAL_SINK_ADDED,
+    MANAGER_SIGNAL_SINK_REMOVED,
+    MANAGER_SIGNAL_PROFILES_CHANGED,
+    MANAGER_SIGNAL_MAX
+};
+
+static pa_dbus_signal_info manager_signals[MANAGER_SIGNAL_MAX]={
+    [MANAGER_SIGNAL_SINK_ADDED]={.name="SinkAdded", .arguments=sink_args, .n_arguments=sizeof(sink_args)/sizeof(pa_dbus_arg_info)},
+    [MANAGER_SIGNAL_SINK_REMOVED]={.name="SinkRemoved", .arguments=sink_args, .n_arguments=sizeof(sink_args)/sizeof(pa_dbus_arg_info)},
+    [MANAGER_SIGNAL_PROFILES_CHANGED]={.name="ProfilesChanged", .arguments=NULL, .n_arguments=0}
+};
+
+static pa_dbus_interface_info manager_info={
+    .name=MANAGER_IFACE,
+    .method_handlers=manager_methods,
+    .n_method_handlers=MANAGER_METHOD_MAX,
+    .property_handlers=manager_handlers,
+    .n_property_handlers=MANAGER_HANDLER_MAX,
+    .get_all_properties_cb=manager_get_all,
+    .signals=manager_signals,
+    .n_signals=MANAGER_SIGNAL_MAX
+};
+
+enum equalizer_method_index {
+    EQUALIZER_METHOD_FILTER_POINTS,
+    EQUALIZER_METHOD_SEED_FILTER,
+    EQUALIZER_METHOD_SAVE_PROFILE,
+    EQUALIZER_METHOD_LOAD_PROFILE,
+    EQUALIZER_METHOD_SET_FILTER,
+    EQUALIZER_METHOD_GET_FILTER,
+    EQUALIZER_METHOD_SAVE_STATE,
+    EQUALIZER_METHOD_GET_PROFILE_NAME,
+    EQUALIZER_METHOD_MAX
+};
+
+enum equalizer_handler_index {
+    EQUALIZER_HANDLER_REVISION,
+    EQUALIZER_HANDLER_SAMPLERATE,
+    EQUALIZER_HANDLER_FILTERSAMPLERATE,
+    EQUALIZER_HANDLER_N_COEFS,
+    EQUALIZER_HANDLER_N_CHANNELS,
+    EQUALIZER_HANDLER_MAX
+};
+
+pa_dbus_arg_info filter_points_args[]={
+    {"channel", "u","in"},
+    {"xs", "au","in"},
+    {"ys", "ad","out"},
+    {"preamp", "d","out"}
+};
+pa_dbus_arg_info seed_filter_args[]={
+    {"channel", "u","in"},
+    {"xs", "au","in"},
+    {"ys", "ad","in"},
+    {"preamp", "d","in"}
+};
+
+pa_dbus_arg_info set_filter_args[]={
+    {"channel", "u","in"},
+    {"ys", "ad","in"},
+    {"preamp", "d","in"}
+};
+pa_dbus_arg_info get_filter_args[]={
+    {"channel", "u","in"},
+    {"ys", "ad","out"},
+    {"preamp", "d","out"}
+};
+
+pa_dbus_arg_info save_profile_args[]={
+    {"channel", "u","in"},
+    {"name", "s","in"}
+};
+pa_dbus_arg_info load_profile_args[]={
+    {"channel", "u","in"},
+    {"name", "s","in"}
+};
+pa_dbus_arg_info base_profile_name_args[]={
+    {"channel", "u","in"},
+    {"name", "s","out"}
+};
+
+static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={
+    [EQUALIZER_METHOD_SEED_FILTER]={
+        .method_name="SeedFilter",
+        .arguments=seed_filter_args,
+        .n_arguments=sizeof(seed_filter_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_seed_filter},
+    [EQUALIZER_METHOD_FILTER_POINTS]={
+        .method_name="FilterAtPoints",
+        .arguments=filter_points_args,
+        .n_arguments=sizeof(filter_points_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_get_filter_points},
+    [EQUALIZER_METHOD_SET_FILTER]={
+        .method_name="SetFilter",
+        .arguments=set_filter_args,
+        .n_arguments=sizeof(set_filter_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_set_filter},
+    [EQUALIZER_METHOD_GET_FILTER]={
+        .method_name="GetFilter",
+        .arguments=get_filter_args,
+        .n_arguments=sizeof(get_filter_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_get_filter},
+    [EQUALIZER_METHOD_SAVE_PROFILE]={
+        .method_name="SaveProfile",
+        .arguments=save_profile_args,
+        .n_arguments=sizeof(save_profile_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_save_profile},
+    [EQUALIZER_METHOD_LOAD_PROFILE]={
+        .method_name="LoadProfile",
+        .arguments=load_profile_args,
+        .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_load_profile},
+    [EQUALIZER_METHOD_SAVE_STATE]={
+        .method_name="SaveState",
+        .arguments=NULL,
+        .n_arguments=0,
+        .receive_cb=equalizer_handle_save_state},
+    [EQUALIZER_METHOD_GET_PROFILE_NAME]={
+        .method_name="BaseProfile",
+        .arguments=base_profile_name_args,
+        .n_arguments=sizeof(base_profile_name_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_get_profile_name}
+};
+
+static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={
+    [EQUALIZER_HANDLER_REVISION]={.property_name="InterfaceRevision",.type="u",.get_cb=equalizer_get_revision,.set_cb=NULL},
+    [EQUALIZER_HANDLER_SAMPLERATE]={.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL},
+    [EQUALIZER_HANDLER_FILTERSAMPLERATE]={.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL},
+    [EQUALIZER_HANDLER_N_COEFS]={.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL},
+    [EQUALIZER_HANDLER_N_CHANNELS]={.property_name="NChannels",.type="u",.get_cb=equalizer_get_n_channels,.set_cb=NULL},
+};
+
+enum equalizer_signal_index{
+    EQUALIZER_SIGNAL_FILTER_CHANGED,
+    EQUALIZER_SIGNAL_SINK_RECONFIGURED,
+    EQUALIZER_SIGNAL_MAX
+};
+
+static pa_dbus_signal_info equalizer_signals[EQUALIZER_SIGNAL_MAX]={
+    [EQUALIZER_SIGNAL_FILTER_CHANGED]={.name="FilterChanged", .arguments=NULL, .n_arguments=0},
+    [EQUALIZER_SIGNAL_SINK_RECONFIGURED]={.name="SinkReconfigured", .arguments=NULL, .n_arguments=0},
+};
+
+static pa_dbus_interface_info equalizer_info={
+    .name=EQUALIZER_IFACE,
+    .method_handlers=equalizer_methods,
+    .n_method_handlers=EQUALIZER_METHOD_MAX,
+    .property_handlers=equalizer_handlers,
+    .n_property_handlers=EQUALIZER_HANDLER_MAX,
+    .get_all_properties_cb=equalizer_get_all,
+    .signals=equalizer_signals,
+    .n_signals=EQUALIZER_SIGNAL_MAX
+};
+
+void dbus_init(struct userdata *u) {
+    uint32_t dummy;
+    DBusMessage *message = NULL;
+    pa_idxset *sink_list = NULL;
+    u->dbus_protocol=pa_dbus_protocol_get(u->sink->core);
+    u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u) >= 0);
+    sink_list = pa_shared_get(u->sink->core, SINKLIST);
+    u->database = pa_shared_get(u->sink->core, EQDB);
+    if (sink_list == NULL) {
+        char *dbname;
+        sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func);
+        pa_shared_set(u->sink->core, SINKLIST, sink_list);
+        pa_assert_se(dbname = pa_state_path("equalizer-presets", false));
+        pa_assert_se(u->database = pa_database_open(dbname, true));
+        pa_xfree(dbname);
+        pa_shared_set(u->sink->core, EQDB, u->database);
+        pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->sink->core);
+        pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME);
+    }
+    pa_idxset_put(sink_list, u, &dummy);
+
+    pa_assert_se((message = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_SINK_ADDED].name)));
+    dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &u->dbus_path, DBUS_TYPE_INVALID);
+    pa_dbus_protocol_send_signal(u->dbus_protocol, message);
+    dbus_message_unref(message);
+}
+
+void dbus_done(struct userdata *u) {
+    pa_idxset *sink_list;
+    uint32_t dummy;
+
+    DBusMessage *message = NULL;
+    pa_assert_se((message = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_SINK_REMOVED].name)));
+    dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &u->dbus_path, DBUS_TYPE_INVALID);
+    pa_dbus_protocol_send_signal(u->dbus_protocol, message);
+    dbus_message_unref(message);
+
+    pa_assert_se(sink_list=pa_shared_get(u->sink->core,SINKLIST));
+    pa_idxset_remove_by_data(sink_list,u,&dummy);
+    if (pa_idxset_size(sink_list) == 0) {
+        pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME);
+        pa_dbus_protocol_remove_interface(u->dbus_protocol, MANAGER_PATH, manager_info.name);
+        pa_shared_remove(u->sink->core, EQDB);
+        pa_database_close(u->database);
+        pa_shared_remove(u->sink->core, SINKLIST);
+        pa_xfree(sink_list);
+    }
+    pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, equalizer_info.name);
+    pa_xfree(u->dbus_path);
+    pa_dbus_protocol_unref(u->dbus_protocol);
+}
+
+void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    DBusError error;
+    pa_core *c = (pa_core *)_u;
+    DBusMessage *message = NULL;
+    pa_dbus_protocol *dbus_protocol;
+    char *name;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+    dbus_error_init(&error);
+    if (!dbus_message_get_args(msg, &error,
+                 DBUS_TYPE_STRING, &name,
+                DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    remove_profile(c,name);
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_assert_se((message = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name)));
+    dbus_protocol = pa_dbus_protocol_get(c);
+    pa_dbus_protocol_send_signal(dbus_protocol, message);
+    pa_dbus_protocol_unref(dbus_protocol);
+    dbus_message_unref(message);
+}
+
+void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    uint32_t rev=1;
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev);
+}
+
+static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks) {
+    void *iter = NULL;
+    struct userdata *sink_u = NULL;
+    uint32_t dummy;
+    pa_idxset *sink_list;
+    pa_assert(u);
+    pa_assert(names);
+    pa_assert(n_sinks);
+
+    pa_assert_se(sink_list = pa_shared_get(u, SINKLIST));
+    *n_sinks = (unsigned) pa_idxset_size(sink_list);
+    *names = *n_sinks > 0 ? pa_xnew0(char *,*n_sinks) : NULL;
+    for(uint32_t i = 0; i < *n_sinks; ++i) {
+        sink_u = (struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy);
+        (*names)[i] = pa_xstrdup(sink_u->dbus_path);
+    }
+}
+
+void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    unsigned n;
+    char **names = NULL;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    get_sinks((pa_core *) _u, &names, &n);
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n);
+    for(unsigned i = 0; i < n; ++i) {
+        pa_xfree(names[i]);
+    }
+    pa_xfree(names);
+}
+
+static void get_profiles(pa_core *c, char ***names, unsigned *n) {
+    char *name;
+    pa_database *database;
+    pa_datum key, next_key;
+    pa_strlist *head=NULL, *iter;
+    bool done;
+    pa_assert_se(database = pa_shared_get(c, EQDB));
+
+    pa_assert(c);
+    pa_assert(names);
+    pa_assert(n);
+    done = !pa_database_first(database, &key, NULL);
+    *n = 0;
+    while(!done) {
+        done = !pa_database_next(database, &key, &next_key, NULL);
+        name=pa_xmalloc(key.size + 1);
+        memcpy(name, key.data, key.size);
+        name[key.size] = '\0';
+        pa_datum_free(&key);
+        head = pa_strlist_prepend(head, name);
+        pa_xfree(name);
+        key = next_key;
+        (*n)++;
+    }
+    (*names) = *n > 0 ? pa_xnew0(char *, *n) : NULL;
+    iter=head;
+    for(unsigned i = 0; i < *n; ++i) {
+        (*names)[*n - 1 - i] = pa_xstrdup(pa_strlist_data(iter));
+        iter = pa_strlist_next(iter);
+    }
+    pa_strlist_free(head);
+}
+
+void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    char **names;
+    unsigned n;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    get_profiles((pa_core *)_u, &names, &n);
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, names, n);
+    for(unsigned i = 0; i < n; ++i) {
+        pa_xfree(names[i]);
+    }
+    pa_xfree(names);
+}
+
+void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    pa_core *c;
+    char **names = NULL;
+    unsigned n;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter, dict_iter;
+    uint32_t rev;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert_se(c = _u);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    rev = 1;
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev);
+
+    get_sinks(c, &names, &n);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter,manager_handlers[MANAGER_HANDLER_EQUALIZED_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, names, n);
+    for(unsigned i = 0; i < n; ++i) {
+        pa_xfree(names[i]);
+    }
+    pa_xfree(names);
+
+    get_profiles(c, &names, &n);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_PROFILES].property_name, DBUS_TYPE_STRING, names, n);
+    for(unsigned i = 0; i < n; ++i) {
+        pa_xfree(names[i]);
+    }
+    pa_xfree(names);
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u = _u;
+    DBusError error;
+    DBusMessage *message = NULL;
+    float *ys;
+    uint32_t *xs, channel, r_channel;
+    double *_ys, preamp;
+    unsigned x_npoints, y_npoints, a_i;
+    float *H;
+    bool points_good = true;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints,
+                DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &_ys, &y_npoints,
+                DBUS_TYPE_DOUBLE, &preamp,
+                DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    if (channel > u->channels) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+    for(size_t i = 0; i < x_npoints; ++i) {
+        if (xs[i] >= FILTER_SIZE(u)) {
+            points_good = false;
+            break;
+        }
+    }
+    if (!is_monotonic(xs, x_npoints) || !points_good) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<=x<=%zd", u->fft_size / 2);
+        dbus_error_free(&error);
+        return;
+    }else if (x_npoints != y_npoints || x_npoints < 2 || x_npoints > FILTER_SIZE(u)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%zd!", FILTER_SIZE(u));
+        dbus_error_free(&error);
+        return;
+    }else if (xs[0] != 0 || xs[x_npoints - 1] != u->fft_size / 2) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs[0] must be 0 and xs[-1]=fft_size/2");
+        dbus_error_free(&error);
+        return;
+    }
+
+    ys = pa_xmalloc(x_npoints * sizeof(float));
+    for(uint32_t i = 0; i < x_npoints; ++i) {
+        ys[i] = (float) _ys[i];
+    }
+    r_channel = channel == u->channels ? 0 : channel;
+    a_i = pa_aupdate_write_begin(u->a_H[r_channel]);
+    H = u->Hs[r_channel][a_i];
+    u->Xs[r_channel][a_i] = preamp;
+    interpolate(H, FILTER_SIZE(u), xs, ys, x_npoints);
+    fix_filter(H, u->fft_size);
+    if (channel == u->channels) {
+        for(size_t c = 1; c < u->channels; ++c) {
+            unsigned b_i = pa_aupdate_write_begin(u->a_H[c]);
+            float *H_p = u->Hs[c][b_i];
+            u->Xs[c][b_i] = preamp;
+            memcpy(H_p, H, FILTER_SIZE(u) * sizeof(float));
+            pa_aupdate_write_end(u->a_H[c]);
+        }
+    }
+    pa_aupdate_write_end(u->a_H[r_channel]);
+    pa_xfree(ys);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_assert_se((message = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
+    pa_dbus_protocol_send_signal(u->dbus_protocol, message);
+    dbus_message_unref(message);
+}
+
+void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u = (struct userdata *) _u;
+    uint32_t *xs, channel, r_channel;
+    double *ys, preamp;
+    unsigned x_npoints, a_i;
+    float *H;
+    bool points_good=true;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    dbus_error_init(&error);
+    if (!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints,
+                DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    if (channel > u->channels) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+
+    for(size_t i = 0; i < x_npoints; ++i) {
+        if (xs[i] >= FILTER_SIZE(u)) {
+            points_good=false;
+            break;
+        }
+    }
+
+    if (x_npoints > FILTER_SIZE(u) || !points_good) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs indices/length must be <= %zd!", FILTER_SIZE(u));
+        dbus_error_free(&error);
+        return;
+    }
+
+    r_channel = channel == u->channels ? 0 : channel;
+    ys = pa_xmalloc(x_npoints * sizeof(double));
+    a_i = pa_aupdate_read_begin(u->a_H[r_channel]);
+    H = u->Hs[r_channel][a_i];
+    preamp = u->Xs[r_channel][a_i];
+    for(uint32_t i = 0; i < x_npoints; ++i) {
+        ys[i] = H[xs[i]] * u->fft_size;
+    }
+    pa_aupdate_read_end(u->a_H[r_channel]);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+
+    pa_dbus_append_basic_array(&msg_iter, DBUS_TYPE_DOUBLE, ys, x_npoints);
+    pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_DOUBLE, &preamp);
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+    pa_xfree(ys);
+}
+
+static void get_filter(struct userdata *u, size_t channel, double **H_, double *preamp) {
+    float *H;
+    unsigned a_i;
+    size_t r_channel = channel == u->channels ? 0 : channel;
+    *H_ = pa_xnew0(double, FILTER_SIZE(u));
+    a_i = pa_aupdate_read_begin(u->a_H[r_channel]);
+    H = u->Hs[r_channel][a_i];
+    for(size_t i = 0;i < FILTER_SIZE(u); ++i) {
+        (*H_)[i] = H[i] * u->fft_size;
+    }
+    *preamp = u->Xs[r_channel][a_i];
+
+    pa_aupdate_read_end(u->a_H[r_channel]);
+}
+
+void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u;
+    unsigned n_coefs;
+    uint32_t channel;
+    double *H_, preamp;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusError error;
+    pa_assert_se(u = (struct userdata *) _u);
+    pa_assert(conn);
+    pa_assert(msg);
+
+    dbus_error_init(&error);
+    if (!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    if (channel > u->channels) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+
+    n_coefs = CHANNEL_PROFILE_SIZE(u);
+    pa_assert(conn);
+    pa_assert(msg);
+    get_filter(u, channel, &H_, &preamp);
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+
+    pa_dbus_append_basic_array(&msg_iter, DBUS_TYPE_DOUBLE, H_, n_coefs);
+    pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_DOUBLE, &preamp);
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+    pa_xfree(H_);
+}
+
+static void set_filter(struct userdata *u, size_t channel, double *H_, double preamp) {
+    unsigned a_i;
+    size_t r_channel = channel == u->channels ? 0 : channel;
+    float *H;
+    //all channels
+    a_i = pa_aupdate_write_begin(u->a_H[r_channel]);
+    u->Xs[r_channel][a_i] = (float) preamp;
+    H = u->Hs[r_channel][a_i];
+    for(size_t i = 0; i < FILTER_SIZE(u); ++i) {
+        H[i] = (float) H_[i];
+    }
+    fix_filter(H, u->fft_size);
+    if (channel == u->channels) {
+        for(size_t c = 1; c < u->channels; ++c) {
+            unsigned b_i = pa_aupdate_write_begin(u->a_H[c]);
+            u->Xs[c][b_i] = u->Xs[r_channel][a_i];
+            memcpy(u->Hs[c][b_i], u->Hs[r_channel][a_i], FILTER_SIZE(u) * sizeof(float));
+            pa_aupdate_write_end(u->a_H[c]);
+        }
+    }
+    pa_aupdate_write_end(u->a_H[r_channel]);
+}
+
+void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u;
+    double *H, preamp;
+    uint32_t channel;
+    unsigned _n_coefs;
+    DBusMessage *message = NULL;
+    DBusError error;
+    pa_assert_se(u = (struct userdata *) _u);
+    pa_assert(conn);
+    pa_assert(msg);
+
+    dbus_error_init(&error);
+    if (!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &H, &_n_coefs,
+                DBUS_TYPE_DOUBLE, &preamp,
+                DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    if (channel > u->channels) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+    if (_n_coefs != FILTER_SIZE(u)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %zd coefficients, you gave %d", FILTER_SIZE(u), _n_coefs);
+        return;
+    }
+    set_filter(u, channel, H, preamp);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_assert_se((message = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
+    pa_dbus_protocol_send_signal(u->dbus_protocol, message);
+    dbus_message_unref(message);
+}
+
+void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u = (struct userdata *) _u;
+    char *name;
+    uint32_t channel, r_channel;
+    DBusMessage *message = NULL;
+    DBusError error;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_STRING, &name,
+                DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    if (channel > u->channels) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+    r_channel = channel == u->channels ? 0 : channel;
+    save_profile(u, r_channel, name);
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_assert_se((message = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name)));
+    pa_dbus_protocol_send_signal(u->dbus_protocol, message);
+    dbus_message_unref(message);
+}
+
+void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u = (struct userdata *) _u;
+    char *name;
+    DBusError error;
+    uint32_t channel, r_channel;
+    const char *err_msg = NULL;
+    DBusMessage *message = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_STRING, &name,
+                DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    if (channel > u->channels) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+    r_channel = channel == u->channels ? 0 : channel;
+
+    err_msg = load_profile(u, r_channel, name);
+    if (err_msg != NULL) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name, err_msg);
+        dbus_error_free(&error);
+        return;
+    }
+    if (channel == u->channels) {
+        for(uint32_t c = 1; c < u->channels; ++c) {
+            load_profile(u, c, name);
+        }
+    }
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_assert_se((message = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
+    pa_dbus_protocol_send_signal(u->dbus_protocol, message);
+    dbus_message_unref(message);
+}
+
+void equalizer_handle_save_state(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u = (struct userdata *) _u;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    save_state(u);
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u = (struct userdata *) _u;
+    DBusError error;
+    uint32_t channel, r_channel;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    if (channel > u->channels) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+    r_channel = channel == u->channels ? 0 : channel;
+    pa_assert(u->base_profiles[r_channel]);
+    pa_dbus_send_basic_value_reply(conn,msg, DBUS_TYPE_STRING, &u->base_profiles[r_channel]);
+}
+
+void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    uint32_t rev=1;
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev);
+}
+
+void equalizer_get_n_channels(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u;
+    uint32_t channels;
+    pa_assert_se(u = (struct userdata *) _u);
+    pa_assert(conn);
+    pa_assert(msg);
+
+    channels = (uint32_t) u->channels;
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &channels);
+}
+
+void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u;
+    uint32_t n_coefs;
+    pa_assert_se(u = (struct userdata *) _u);
+    pa_assert(conn);
+    pa_assert(msg);
+
+    n_coefs = (uint32_t) CHANNEL_PROFILE_SIZE(u);
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs);
+}
+
+void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u;
+    uint32_t rate;
+    pa_assert_se(u = (struct userdata *) _u);
+    pa_assert(conn);
+    pa_assert(msg);
+
+    rate = (uint32_t) u->sink->sample_spec.rate;
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate);
+}
+
+void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u;
+    uint32_t fft_size;
+    pa_assert_se(u = (struct userdata *) _u);
+    pa_assert(conn);
+    pa_assert(msg);
+
+    fft_size = (uint32_t) u->fft_size;
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size);
+}
+
+void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter, dict_iter;
+    uint32_t rev, n_coefs, rate, fft_size, channels;
+
+    pa_assert_se(u = _u);
+    pa_assert(msg);
+
+    rev = 1;
+    n_coefs = (uint32_t) CHANNEL_PROFILE_SIZE(u);
+    rate = (uint32_t) u->sink->sample_spec.rate;
+    fft_size = (uint32_t) u->fft_size;
+    channels = (uint32_t) u->channels;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_CHANNELS].property_name, DBUS_TYPE_UINT32, &channels);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
diff --git a/src/modules/module-esound-compat-spawnfd.c b/src/modules/module-esound-compat-spawnfd.c
new file mode 100644 (file)
index 0000000..61c91a2
--- /dev/null
@@ -0,0 +1,76 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "module-esound-compat-spawnfd-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnfd emulation");
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_USAGE("fd=<file descriptor>");
+
+static const char* const valid_modargs[] = {
+    "fd",
+    NULL,
+};
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    int ret = -1;
+    int32_t fd = -1;
+    char x = 1;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||
+        pa_modargs_get_value_s32(ma, "fd", &fd) < 0 ||
+        fd < 0) {
+
+        pa_log("Failed to parse module arguments");
+        goto finish;
+    }
+
+    if (pa_loop_write(fd, &x, sizeof(x), NULL) != sizeof(x))
+        pa_log_warn("write(%u, 1, 1) failed: %s", fd, pa_cstrerror(errno));
+
+    pa_assert_se(pa_close(fd) == 0);
+
+    pa_module_unload_request(m, true);
+
+    ret = 0;
+
+finish:
+    if (ma)
+        pa_modargs_free(ma);
+
+    return ret;
+}
diff --git a/src/modules/module-esound-compat-spawnpid.c b/src/modules/module-esound-compat-spawnpid.c
new file mode 100644 (file)
index 0000000..9aad2d0
--- /dev/null
@@ -0,0 +1,73 @@
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+
+#include "module-esound-compat-spawnpid-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnpid emulation");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE("pid=<process id>");
+
+static const char* const valid_modargs[] = {
+    "pid",
+    NULL,
+};
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    int ret = -1;
+    uint32_t pid = 0;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||
+        pa_modargs_get_value_u32(ma, "pid", &pid) < 0 ||
+        !pid) {
+        pa_log("Failed to parse module arguments");
+        goto finish;
+    }
+
+    if (kill((pid_t) pid, SIGUSR1) < 0)
+        pa_log_warn("kill(%u) failed: %s", pid, pa_cstrerror(errno));
+
+    pa_module_unload_request(m, true);
+
+    ret = 0;
+
+finish:
+    if (ma)
+        pa_modargs_free(ma);
+
+    return ret;
+}
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
new file mode 100644 (file)
index 0000000..59bef48
--- /dev/null
@@ -0,0 +1,724 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_LINUX_SOCKIOS_H
+#include <linux/sockios.h>
+#endif
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/socket.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/socket-client.h>
+#include <pulsecore/esound.h>
+#include <pulsecore/authkey.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/poll.h>
+
+#include "module-esound-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND Sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "sink_properties=<properties for the sink> "
+        "server=<address> cookie=<filename>  "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels>");
+
+#define DEFAULT_SINK_NAME "esound_out"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+    pa_thread *thread;
+
+    pa_memchunk memchunk;
+
+    void *write_data;
+    size_t write_length, write_index;
+
+    void *read_data;
+    size_t read_length, read_index;
+
+    enum {
+        STATE_AUTH,
+        STATE_LATENCY,
+        STATE_PREPARE,
+        STATE_RUNNING,
+        STATE_DEAD
+    } state;
+
+    pa_usec_t latency;
+
+    esd_format_t format;
+    int32_t rate;
+
+    pa_smoother *smoother;
+    int fd;
+
+    int64_t offset;
+
+    pa_iochannel *io;
+    pa_socket_client *client;
+
+    size_t block_size;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "server",
+    "cookie",
+    "format",
+    "rate",
+    "channels",
+    NULL
+};
+
+enum {
+    SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX
+};
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SINK_SUSPENDED:
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+
+                    pa_smoother_pause(u->smoother, pa_rtclock_now());
+                    break;
+
+                case PA_SINK_IDLE:
+                case PA_SINK_RUNNING:
+
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
+                        pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
+
+                    break;
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                case PA_SINK_INVALID_STATE:
+                    ;
+            }
+
+            break;
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t w, r;
+
+            r = pa_smoother_get(u->smoother, pa_rtclock_now());
+            w = pa_bytes_to_usec((uint64_t) u->offset + u->memchunk.length, &u->sink->sample_spec);
+
+            *((int64_t*) data) = (int64_t)w - r;
+            return 0;
+        }
+
+        case SINK_MESSAGE_PASS_SOCKET: {
+            struct pollfd *pollfd;
+
+            pa_assert(!u->rtpoll_item);
+
+            u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+            pollfd->fd = u->fd;
+            pollfd->events = pollfd->revents = 0;
+
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    int write_type = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
+
+    for (;;) {
+        int ret;
+
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+            pa_sink_process_rewind(u->sink, 0);
+
+        if (u->rtpoll_item) {
+            struct pollfd *pollfd;
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+            /* Render some data and write it to the fifo */
+            if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+                pa_usec_t usec;
+                int64_t n;
+
+                for (;;) {
+                    ssize_t l;
+                    void *p;
+
+                    if (u->memchunk.length <= 0)
+                        pa_sink_render(u->sink, u->block_size, &u->memchunk);
+
+                    pa_assert(u->memchunk.length > 0);
+
+                    p = pa_memblock_acquire(u->memchunk.memblock);
+                    l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
+                    pa_memblock_release(u->memchunk.memblock);
+
+                    pa_assert(l != 0);
+
+                    if (l < 0) {
+
+                        if (errno == EINTR)
+                            continue;
+                        else if (errno == EAGAIN) {
+
+                            /* OK, we filled all socket buffers up
+                             * now. */
+                            goto filled_up;
+
+                        } else {
+                            pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+                            goto fail;
+                        }
+
+                    } else {
+                        u->offset += l;
+
+                        u->memchunk.index += (size_t) l;
+                        u->memchunk.length -= (size_t) l;
+
+                        if (u->memchunk.length <= 0) {
+                            pa_memblock_unref(u->memchunk.memblock);
+                            pa_memchunk_reset(&u->memchunk);
+                        }
+
+                        pollfd->revents = 0;
+
+                        if (u->memchunk.length > 0)
+
+                            /* OK, we wrote less that we asked for,
+                             * hence we can assume that the socket
+                             * buffers are full now */
+                            goto filled_up;
+                    }
+                }
+
+            filled_up:
+
+                /* At this spot we know that the socket buffers are
+                 * fully filled up. This is the best time to estimate
+                 * the playback position of the server */
+
+                n = u->offset;
+
+#ifdef SIOCOUTQ
+                {
+                    int l;
+                    if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
+                        n -= l;
+                }
+#endif
+
+                usec = pa_bytes_to_usec((uint64_t) n, &u->sink->sample_spec);
+
+                if (usec > u->latency)
+                    usec -= u->latency;
+                else
+                    usec = 0;
+
+                pa_smoother_put(u->smoother, pa_rtclock_now(), usec);
+            }
+
+            /* Hmm, nothing to do. Let's sleep */
+            pollfd->events = (short) (PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0);
+        }
+
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        if (u->rtpoll_item) {
+            struct pollfd* pollfd;
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+            if (pollfd->revents & ~POLLOUT) {
+                pa_log("FIFO shutdown.");
+                goto fail;
+            }
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+static int do_write(struct userdata *u) {
+    ssize_t r;
+    pa_assert(u);
+
+    if (!pa_iochannel_is_writable(u->io))
+        return 0;
+
+    if (u->write_data) {
+        pa_assert(u->write_index < u->write_length);
+
+        if ((r = pa_iochannel_write(u->io, (uint8_t*) u->write_data + u->write_index, u->write_length - u->write_index)) < 0) {
+            pa_log("write() failed: %s", pa_cstrerror(errno));
+            return -1;
+        }
+
+        u->write_index += (size_t) r;
+        pa_assert(u->write_index <= u->write_length);
+
+        if (u->write_index == u->write_length) {
+            pa_xfree(u->write_data);
+            u->write_data = NULL;
+            u->write_index = u->write_length = 0;
+        }
+    }
+
+    if (!u->write_data && u->state == STATE_PREPARE) {
+        int so_sndbuf = 0;
+        socklen_t sl = sizeof(int);
+
+        /* OK, we're done with sending all control data we need to, so
+         * let's hand the socket over to the IO thread now */
+
+        pa_assert(u->fd < 0);
+        u->fd = pa_iochannel_get_send_fd(u->io);
+
+        pa_iochannel_set_noclose(u->io, true);
+        pa_iochannel_free(u->io);
+        u->io = NULL;
+
+        pa_make_tcp_socket_low_delay(u->fd);
+
+        if (getsockopt(u->fd, SOL_SOCKET, SO_SNDBUF, (void *) &so_sndbuf, &sl) < 0)
+            pa_log_warn("getsockopt(SO_SNDBUF) failed: %s", pa_cstrerror(errno));
+        else {
+            pa_log_debug("SO_SNDBUF is %zu.", (size_t) so_sndbuf);
+            pa_sink_set_max_request(u->sink, PA_MAX((size_t) so_sndbuf, u->block_size));
+        }
+
+        pa_log_debug("Connection authenticated, handing fd to IO thread...");
+
+        pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL);
+        u->state = STATE_RUNNING;
+    }
+
+    return 0;
+}
+
+static int handle_response(struct userdata *u) {
+    pa_assert(u);
+
+    switch (u->state) {
+
+        case STATE_AUTH:
+            pa_assert(u->read_length == sizeof(int32_t));
+
+            /* Process auth data */
+            if (!*(int32_t*) u->read_data) {
+                pa_log("Authentication failed: %s", pa_cstrerror(errno));
+                return -1;
+            }
+
+            /* Request latency data */
+            pa_assert(!u->write_data);
+            *(int32_t*) (u->write_data = pa_xmalloc(u->write_length = sizeof(int32_t))) = ESD_PROTO_LATENCY;
+
+            u->write_index = 0;
+            u->state = STATE_LATENCY;
+
+            /* Space for next response */
+            pa_assert(u->read_length >= sizeof(int32_t));
+            u->read_index = 0;
+            u->read_length = sizeof(int32_t);
+
+            break;
+
+        case STATE_LATENCY: {
+            int32_t *p;
+            pa_assert(u->read_length == sizeof(int32_t));
+
+            /* Process latency info */
+            u->latency = (pa_usec_t) ((double) (*(int32_t*) u->read_data) * 1000000 / 44100);
+            if (u->latency > 10000000) {
+                pa_log_warn("Invalid latency information received from server");
+                u->latency = 0;
+            }
+
+            /* Create stream */
+            pa_assert(!u->write_data);
+            p = u->write_data = pa_xmalloc0(u->write_length = sizeof(int32_t)*3+ESD_NAME_MAX);
+            *(p++) = ESD_PROTO_STREAM_PLAY;
+            *(p++) = u->format;
+            *(p++) = u->rate;
+            pa_strlcpy((char*) p, "PulseAudio Tunnel", ESD_NAME_MAX);
+
+            u->write_index = 0;
+            u->state = STATE_PREPARE;
+
+            /* Don't read any further */
+            pa_xfree(u->read_data);
+            u->read_data = NULL;
+            u->read_index = u->read_length = 0;
+
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    return 0;
+}
+
+static int do_read(struct userdata *u) {
+    pa_assert(u);
+
+    if (!pa_iochannel_is_readable(u->io))
+        return 0;
+
+    if (u->state == STATE_AUTH || u->state == STATE_LATENCY) {
+        ssize_t r;
+
+        if (!u->read_data)
+            return 0;
+
+        pa_assert(u->read_index < u->read_length);
+
+        if ((r = pa_iochannel_read(u->io, (uint8_t*) u->read_data + u->read_index, u->read_length - u->read_index)) <= 0) {
+            pa_log("read() failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            return -1;
+        }
+
+        u->read_index += (size_t) r;
+        pa_assert(u->read_index <= u->read_length);
+
+        if (u->read_index == u->read_length)
+            return handle_response(u);
+    }
+
+    return 0;
+}
+
+static void io_callback(pa_iochannel *io, void*userdata) {
+    struct userdata *u = userdata;
+    pa_assert(u);
+
+    if (do_read(u) < 0 || do_write(u) < 0) {
+
+        if (u->io) {
+            pa_iochannel_free(u->io);
+            u->io = NULL;
+        }
+
+        pa_module_unload_request(u->module, true);
+    }
+}
+
+static void on_connection(pa_socket_client *c, pa_iochannel*io, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_socket_client_unref(u->client);
+    u->client = NULL;
+
+    if (!io) {
+        pa_log("Connection failed: %s", pa_cstrerror(errno));
+        pa_module_unload_request(u->module, true);
+        return;
+    }
+
+    pa_assert(!u->io);
+    u->io = io;
+    pa_iochannel_set_callback(u->io, io_callback, u);
+
+    pa_log_debug("Connection established, authenticating ...");
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    pa_modargs *ma = NULL;
+    const char *espeaker;
+    uint32_t key;
+    pa_sink_new_data data;
+    char *cookie_path;
+    int r;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+        pa_log("invalid sample format specification");
+        goto fail;
+    }
+
+    if ((ss.format != PA_SAMPLE_U8 && ss.format != PA_SAMPLE_S16NE) ||
+        (ss.channels > 2)) {
+        pa_log("esound sample type support is limited to mono/stereo and U8 or S16NE sample data");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->fd = -1;
+    u->smoother = pa_smoother_new(
+            PA_USEC_PER_SEC,
+            PA_USEC_PER_SEC*2,
+            true,
+            true,
+            10,
+            0,
+            false);
+    pa_memchunk_reset(&u->memchunk);
+    u->offset = 0;
+
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->rtpoll_item = NULL;
+
+    u->format =
+        (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) |
+        (ss.channels == 2 ? ESD_STEREO : ESD_MONO);
+    u->rate = (int32_t) ss.rate;
+    u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss);
+
+    u->read_data = u->write_data = NULL;
+    u->read_index = u->write_index = u->read_length = u->write_length = 0;
+
+    u->state = STATE_AUTH;
+    u->latency = 0;
+
+    if (!(espeaker = getenv("ESPEAKER")))
+        espeaker = ESD_UNIX_SOCKET_NAME;
+
+    espeaker = pa_modargs_get_value(ma, "server", espeaker);
+
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, espeaker);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "esd");
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "EsounD Output on %s", espeaker);
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&data);
+        goto fail;
+    }
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    if (!(u->client = pa_socket_client_new_string(u->core->mainloop, true, espeaker, ESD_DEFAULT_PORT))) {
+        pa_log("Failed to connect to server.");
+        goto fail;
+    }
+
+    pa_socket_client_set_callback(u->client, on_connection, u);
+
+    cookie_path = pa_xstrdup(pa_modargs_get_value(ma, "cookie", NULL));
+    if (!cookie_path) {
+        if (pa_append_to_home_dir(".esd_auth", &cookie_path) < 0)
+            goto fail;
+    }
+
+    /* Prepare the initial request */
+    u->write_data = pa_xmalloc(u->write_length = ESD_KEY_LEN + sizeof(int32_t));
+
+    r = pa_authkey_load(cookie_path, true, u->write_data, ESD_KEY_LEN);
+    pa_xfree(cookie_path);
+    if (r < 0) {
+        pa_log("Failed to load cookie");
+        goto fail;
+    }
+
+    key = ESD_ENDIAN_KEY;
+    memcpy((uint8_t*) u->write_data + ESD_KEY_LEN, &key, sizeof(key));
+
+    /* Reserve space for the response */
+    u->read_data = pa_xmalloc(u->read_length = sizeof(int32_t));
+
+    if (!(u->thread = pa_thread_new("esound-sink", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_sink_put(u->sink);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_sink_linked_by(u->sink);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->io)
+        pa_iochannel_free(u->io);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->client)
+        pa_socket_client_unref(u->client);
+
+    pa_xfree(u->read_data);
+    pa_xfree(u->write_data);
+
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+
+    if (u->fd >= 0)
+        pa_close(u->fd);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c
new file mode 100644 (file)
index 0000000..e91fb37
--- /dev/null
@@ -0,0 +1,783 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2011 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/hook-list.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/proplist-util.h>
+
+#include "module-filter-apply-symdef.h"
+
+#define PA_PROP_FILTER_APPLY_PARAMETERS PA_PROP_FILTER_APPLY".%s.parameters"
+#define PA_PROP_FILTER_APPLY_MOVING     "filter.apply.moving"
+#define PA_PROP_MDM_AUTO_FILTERED       "module-device-manager.auto_filtered"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("Load filter sinks automatically when needed");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(_("autoclean=<automatically unload unused filters?>"));
+
+static const char* const valid_modargs[] = {
+    "autoclean",
+    NULL
+};
+
+#define DEFAULT_AUTOCLEAN true
+#define HOUSEKEEPING_INTERVAL (10 * PA_USEC_PER_SEC)
+
+struct filter {
+    char *name;
+    char *parameters;
+    uint32_t module_index;
+    pa_sink *sink;
+    pa_sink *sink_master;
+    pa_source *source;
+    pa_source *source_master;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_hashmap *filters;
+    /* Keep track of streams we're managing PA_PROP_MDM_AUTO_FILTERED on, we're
+     * only maintaining membership, so key and value are just the
+     * pa_sink_input/pa_source_output. */
+    pa_hashmap *mdm_ignored_inputs, *mdm_ignored_outputs;
+    bool autoclean;
+    pa_time_event *housekeeping_time_event;
+};
+
+static unsigned filter_hash(const void *p) {
+    const struct filter *f = p;
+
+    if (f->sink_master && !f->source_master)
+        return (unsigned) (f->sink_master->index + pa_idxset_string_hash_func(f->name));
+    else if (!f->sink_master && f->source_master)
+        return (unsigned) ((f->source_master->index << 16) + pa_idxset_string_hash_func(f->name));
+    else
+        return (unsigned) (f->sink_master->index + (f->source_master->index << 16) + pa_idxset_string_hash_func(f->name));
+}
+
+static int filter_compare(const void *a, const void *b) {
+    const struct filter *fa = a, *fb = b;
+    int r;
+
+    if (fa->sink_master != fb->sink_master || fa->source_master != fb->source_master)
+        return 1;
+    if ((r = strcmp(fa->name, fb->name)))
+        return r;
+
+    return 0;
+}
+
+static struct filter *filter_new(const char *name, const char *parameters, pa_sink *sink, pa_source *source) {
+    struct filter *f;
+
+    pa_assert(sink || source);
+
+    f = pa_xnew(struct filter, 1);
+    f->name = pa_xstrdup(name);
+    f->parameters = pa_xstrdup(parameters);
+    f->sink_master = sink;
+    f->source_master = source;
+    f->module_index = PA_INVALID_INDEX;
+    f->sink = NULL;
+    f->source = NULL;
+
+    return f;
+}
+
+static void filter_free(struct filter *f) {
+    if (f) {
+        pa_xfree(f->name);
+        pa_xfree(f->parameters);
+        pa_xfree(f);
+    }
+}
+
+static const char* get_filter_name(pa_object *o, bool is_sink_input) {
+    const char *apply;
+    pa_proplist *pl;
+
+    if (is_sink_input)
+        pl = PA_SINK_INPUT(o)->proplist;
+    else
+        pl = PA_SOURCE_OUTPUT(o)->proplist;
+
+    /* If the stream doesn't want any filter, then let it be. */
+    if ((apply = pa_proplist_gets(pl, PA_PROP_FILTER_APPLY)) && !pa_streq(apply, "")) {
+        const char* suppress = pa_proplist_gets(pl, PA_PROP_FILTER_SUPPRESS);
+
+        if (!suppress || !pa_streq(suppress, apply))
+            return apply;
+    }
+
+    return NULL;
+}
+
+static const char* get_filter_parameters(pa_object *o, const char *want, bool is_sink_input) {
+    const char *parameters;
+    char *prop_parameters;
+    pa_proplist *pl;
+
+    if (is_sink_input)
+        pl = PA_SINK_INPUT(o)->proplist;
+    else
+        pl = PA_SOURCE_OUTPUT(o)->proplist;
+
+    prop_parameters = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_PARAMETERS, want);
+    parameters = pa_proplist_gets(pl, prop_parameters);
+    pa_xfree(prop_parameters);
+
+    return parameters;
+}
+
+static bool should_group_filter(struct filter *filter) {
+    return pa_streq(filter->name, "echo-cancel");
+}
+
+static char* get_group(pa_object *o, bool is_sink_input) {
+    pa_proplist *pl;
+
+    if (is_sink_input)
+        pl = PA_SINK_INPUT(o)->proplist;
+    else
+        pl = PA_SOURCE_OUTPUT(o)->proplist;
+
+    /* There's a bit of cleverness here -- the second argument ensures that we
+     * only group streams that require the same filter */
+    return pa_proplist_get_stream_group(pl, pa_proplist_gets(pl, PA_PROP_FILTER_APPLY), NULL);
+}
+
+/* For filters that apply on a source-output/sink-input pair, this finds the
+ * master sink if we know the master source, or vice versa. It does this by
+ * looking up streams that belong to the same stream group as the original
+ * object. The idea is that streams from the sam group are always routed
+ * together. */
+static bool find_paired_master(struct userdata *u, struct filter *filter, pa_object *o, bool is_sink_input) {
+    char *group;
+
+    if ((group = get_group(o, is_sink_input))) {
+        uint32_t idx;
+        char *g;
+        char *module_name = pa_sprintf_malloc("module-%s", filter->name);
+
+        if (is_sink_input) {
+            pa_source_output *so;
+
+            PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
+                g = get_group(PA_OBJECT(so), false);
+
+                if (pa_streq(g, group)) {
+                    if (pa_streq(module_name, so->source->module->name)) {
+                        /* Make sure we're not routing to another instance of
+                         * the same filter. */
+                        filter->source_master = so->source->output_from_master->source;
+                    } else {
+                        filter->source_master = so->source;
+                    }
+
+                    pa_xfree(g);
+                    break;
+                }
+
+                pa_xfree (g);
+            }
+        } else {
+            pa_sink_input *si;
+
+            PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
+                g = get_group(PA_OBJECT(si), true);
+
+                if (pa_streq(g, group)) {
+                    if (pa_streq(module_name, si->sink->module->name)) {
+                        /* Make sure we're not routing to another instance of
+                         * the same filter. */
+                        filter->sink_master = si->sink->input_to_master->sink;
+                    } else {
+                        filter->sink_master = si->sink;
+                    }
+
+                    pa_xfree(g);
+                    break;
+                }
+
+                pa_xfree(g);
+            }
+        }
+
+        pa_xfree(group);
+        pa_xfree(module_name);
+
+        if (!filter->sink_master || !filter->source_master)
+            return false;
+    }
+
+    return true;
+}
+
+static bool nothing_attached(struct filter *f) {
+    bool no_si = true, no_so = true;
+
+    if (f->sink)
+        no_si = pa_idxset_isempty(f->sink->inputs);
+    if (f->source)
+        no_so = pa_idxset_isempty(f->source->outputs);
+
+    return no_si && no_so;
+}
+
+static void housekeeping_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+    struct filter *filter;
+    void *state;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(u);
+
+    pa_assert(e == u->housekeeping_time_event);
+    u->core->mainloop->time_free(u->housekeeping_time_event);
+    u->housekeeping_time_event = NULL;
+
+    PA_HASHMAP_FOREACH(filter, u->filters, state) {
+        if (nothing_attached(filter)) {
+            uint32_t idx;
+
+            pa_log_debug("Detected filter %s as no longer used. Unloading.", filter->name);
+            idx = filter->module_index;
+            pa_hashmap_remove(u->filters, filter);
+            filter_free(filter);
+            pa_module_unload_request_by_index(u->core, idx, true);
+        }
+    }
+
+    pa_log_info("Housekeeping Done.");
+}
+
+static void trigger_housekeeping(struct userdata *u) {
+    pa_assert(u);
+
+    if (!u->autoclean)
+        return;
+
+    if (u->housekeeping_time_event)
+        return;
+
+    u->housekeeping_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + HOUSEKEEPING_INTERVAL, housekeeping_time_callback, u);
+}
+
+static int do_move(struct userdata *u, pa_object *obj, pa_object *parent, bool is_input) {
+    /* Keep track of objects that we've marked for module-device-manager to ignore */
+    pa_hashmap_put(is_input ? u->mdm_ignored_inputs : u->mdm_ignored_outputs, obj, obj);
+
+    if (is_input) {
+        pa_sink_input_set_property(PA_SINK_INPUT(obj), PA_PROP_MDM_AUTO_FILTERED, "1");
+        return pa_sink_input_move_to(PA_SINK_INPUT(obj), PA_SINK(parent), false);
+    } else {
+        pa_source_output_set_property(PA_SOURCE_OUTPUT(obj), PA_PROP_MDM_AUTO_FILTERED, "1");
+        return pa_source_output_move_to(PA_SOURCE_OUTPUT(obj), PA_SOURCE(parent), false);
+    }
+}
+
+static void move_object_for_filter(struct userdata *u, pa_object *o, struct filter* filter, bool restore, bool is_sink_input) {
+    pa_object *parent;
+    pa_proplist *pl;
+    const char *name;
+
+    pa_assert(o);
+    pa_assert(filter);
+
+    if (is_sink_input) {
+        pl = PA_SINK_INPUT(o)->proplist;
+        parent = PA_OBJECT(restore ? filter->sink_master : filter->sink);
+        if (!parent)
+            return;
+        name = PA_SINK(parent)->name;
+    } else {
+        pl = PA_SOURCE_OUTPUT(o)->proplist;
+        parent = PA_OBJECT(restore ? filter->source_master : filter->source);
+        if (!parent)
+            return;
+        name = PA_SOURCE(parent)->name;
+    }
+
+    pa_proplist_sets(pl, PA_PROP_FILTER_APPLY_MOVING, "1");
+
+    if (do_move(u, o, parent, is_sink_input) < 0)
+        pa_log_info("Failed to move %s for \"%s\" to <%s>.", is_sink_input ? "sink-input" : "source-output",
+                    pa_strnull(pa_proplist_gets(pl, PA_PROP_APPLICATION_NAME)), name);
+    else
+        pa_log_info("Successfully moved %s for \"%s\" to <%s>.", is_sink_input ? "sink-input" : "source-output",
+                    pa_strnull(pa_proplist_gets(pl, PA_PROP_APPLICATION_NAME)), name);
+
+    pa_proplist_unset(pl, PA_PROP_FILTER_APPLY_MOVING);
+}
+
+static void move_objects_for_filter(struct userdata *u, pa_object *o, struct filter* filter, bool restore,
+        bool is_sink_input) {
+
+    if (!should_group_filter(filter))
+        move_object_for_filter(u, o, filter, restore, is_sink_input);
+    else {
+        pa_source_output *so;
+        pa_sink_input *si;
+        char *g, *group;
+        uint32_t idx;
+
+        group = get_group(o, is_sink_input);
+
+        PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
+            g = get_group(PA_OBJECT(so), false);
+
+            if (pa_streq(g, group))
+                move_object_for_filter(u, PA_OBJECT(so), filter, restore, false);
+
+            pa_xfree(g);
+        }
+
+        PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
+            g = get_group(PA_OBJECT(si), true);
+
+            if (pa_streq(g, group))
+                move_object_for_filter(u, PA_OBJECT(si), filter, restore, true);
+
+            pa_xfree(g);
+        }
+
+        pa_xfree(group);
+    }
+}
+
+/* Note that we assume a filter will provide at most one sink and at most one
+ * source (and at least one of either). */
+static void find_filters_for_module(struct userdata *u, pa_module *m, const char *name, const char *parameters) {
+    uint32_t idx;
+    pa_sink *sink;
+    pa_source *source;
+    struct filter *fltr = NULL;
+
+    PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
+        if (sink->module == m) {
+            pa_assert(pa_sink_is_filter(sink));
+
+            fltr = filter_new(name, parameters, sink->input_to_master->sink, NULL);
+            fltr->module_index = m->index;
+            fltr->sink = sink;
+
+            break;
+        }
+    }
+
+    PA_IDXSET_FOREACH(source, u->core->sources, idx) {
+        if (source->module == m && !source->monitor_of) {
+            pa_assert(pa_source_is_filter(source));
+
+            if (!fltr) {
+                fltr = filter_new(name, parameters, NULL, source->output_from_master->source);
+                fltr->module_index = m->index;
+                fltr->source = source;
+            } else {
+                fltr->source = source;
+                fltr->source_master = source->output_from_master->source;
+            }
+
+            break;
+        }
+    }
+
+    pa_hashmap_put(u->filters, fltr, fltr);
+}
+
+static bool can_unload_module(struct userdata *u, uint32_t idx) {
+    void *state;
+    struct filter *filter;
+
+    /* Check if any other struct filters point to the same module */
+    PA_HASHMAP_FOREACH(filter, u->filters, state) {
+        if (filter->module_index == idx && !nothing_attached(filter))
+            return false;
+    }
+
+    return true;
+}
+
+static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_input) {
+    const char *want;
+    const char *parameters;
+    bool done_something = false;
+    pa_sink *sink = NULL;
+    pa_source *source = NULL;
+    pa_module *module = NULL;
+    char *module_name = NULL;
+    struct filter *fltr = NULL, *filter = NULL;
+
+    if (is_sink_input) {
+        sink = PA_SINK_INPUT(o)->sink;
+
+        if (sink)
+            module = sink->module;
+    } else {
+        source = PA_SOURCE_OUTPUT(o)->source;
+
+        if (source)
+            module = source->module;
+    }
+
+    /* If there is no sink/source yet, we can't do much */
+    if ((is_sink_input && !sink) || (!is_sink_input && !source))
+        goto done;
+
+    /* If the stream doesn't what any filter, then let it be. */
+    if ((want = get_filter_name(o, is_sink_input))) {
+        /* We need to ensure the SI is playing on a sink of this type
+         * attached to the sink it's "officially" playing on */
+
+        if (!module)
+            goto done;
+
+        module_name = pa_sprintf_malloc("module-%s", want);
+        if (pa_streq(module->name, module_name)) {
+            pa_log_debug("Stream appears to be playing on an appropriate sink already. Ignoring.");
+            goto done;
+        }
+
+        /* Some filter modules might require parameters by default.
+         * (e.g 'plugin', 'label', 'control' of module-ladspa-sink) */
+        parameters = get_filter_parameters(o, want, is_sink_input);
+
+        fltr = filter_new(want, parameters, sink, source);
+
+        if (should_group_filter(fltr) && !find_paired_master(u, fltr, o, is_sink_input)) {
+            pa_log_debug("Want group filtering but don't have enough streams.");
+            goto done;
+        }
+
+        if (!(filter = pa_hashmap_get(u->filters, fltr))) {
+            char *args;
+            pa_module *m;
+
+            args = pa_sprintf_malloc("autoloaded=1 %s%s %s%s %s",
+                    fltr->sink_master ? "sink_master=" : "",
+                    fltr->sink_master ? fltr->sink_master->name : "",
+                    fltr->source_master ? "source_master=" : "",
+                    fltr->source_master ? fltr->source_master->name : "",
+                    fltr->parameters ? fltr->parameters : "");
+
+            pa_log_debug("Loading %s with arguments '%s'", module_name, args);
+
+            if ((m = pa_module_load(u->core, module_name, args))) {
+                find_filters_for_module(u, m, want, parameters);
+                filter = pa_hashmap_get(u->filters, fltr);
+                done_something = true;
+            }
+            pa_xfree(args);
+        }
+
+        if (!filter) {
+            pa_log("Unable to load %s", module_name);
+            goto done;
+        }
+
+        /* We can move the stream now as we know the destination. If this
+         * isn't true, we will do it later when the sink appears. */
+        if ((is_sink_input && filter->sink) || (!is_sink_input && filter->source)) {
+            move_objects_for_filter(u, o, filter, false, is_sink_input);
+            done_something = true;
+        }
+    } else {
+        void *state;
+
+        /* We do not want to filter... but are we already filtered?
+         * This can happen if an input's proplist changes */
+        PA_HASHMAP_FOREACH(filter, u->filters, state) {
+            if ((is_sink_input && sink == filter->sink) || (!is_sink_input && source == filter->source)) {
+                move_objects_for_filter(u, o, filter, true, is_sink_input);
+                done_something = true;
+                break;
+            }
+        }
+    }
+
+    if (done_something)
+        trigger_housekeeping(u);
+
+done:
+    pa_xfree(module_name);
+    filter_free(fltr);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, PA_OBJECT(i), true);
+}
+
+static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    if (pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY_MOVING))
+        return PA_HOOK_OK;
+
+    /* If we're managing m-d-m.auto_filtered on this, remove and re-add if we're continuing to manage it */
+    pa_hashmap_remove(u->mdm_ignored_inputs, i);
+
+    return process(u, PA_OBJECT(i), true);
+}
+
+static pa_hook_result_t sink_input_proplist_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, PA_OBJECT(i), true);
+}
+
+static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    pa_assert(u);
+
+    if (pa_hashmap_size(u->filters) > 0)
+        trigger_housekeeping(u);
+
+    pa_hashmap_remove(u->mdm_ignored_inputs, i);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_unlink_cb(pa_core *core, pa_sink *sink, struct userdata *u) {
+    void *state;
+    struct filter *filter = NULL;
+
+    pa_core_assert_ref(core);
+    pa_sink_assert_ref(sink);
+    pa_assert(u);
+
+    /* If either the parent or the sink we've loaded disappears,
+     * we should remove it from our hashmap */
+    PA_HASHMAP_FOREACH(filter, u->filters, state) {
+        if (filter->sink_master == sink || filter->sink == sink) {
+            uint32_t idx;
+
+            /* Attempt to rescue any streams to the parent sink as this is likely
+             * the best course of action (as opposed to a generic rescue via
+             * module-rescue-streams */
+            if (filter->sink == sink) {
+                pa_sink_input *i;
+
+                PA_IDXSET_FOREACH(i, sink->inputs, idx)
+                    move_objects_for_filter(u, PA_OBJECT(i), filter, true, true);
+            }
+
+            idx = filter->module_index;
+            pa_hashmap_remove(u->filters, filter);
+            filter_free(filter);
+
+            if (can_unload_module(u, idx))
+                pa_module_unload_request_by_index(u->core, idx, true);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_put_cb(pa_core *core, pa_source_output *o, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_source_output_assert_ref(o);
+
+    return process(u, PA_OBJECT(o), false);
+}
+
+static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_output *o, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_source_output_assert_ref(o);
+
+    if (pa_proplist_gets(o->proplist, PA_PROP_FILTER_APPLY_MOVING))
+        return PA_HOOK_OK;
+
+    /* If we're managing m-d-m.auto_filtered on this, remove and re-add if we're continuing to manage it */
+    pa_hashmap_remove(u->mdm_ignored_outputs, o);
+
+    return process(u, PA_OBJECT(o), false);
+}
+
+static pa_hook_result_t source_output_proplist_cb(pa_core *core, pa_source_output *o, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_source_output_assert_ref(o);
+
+    return process(u, PA_OBJECT(o), false);
+}
+
+static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output *o, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_source_output_assert_ref(o);
+
+    pa_assert(u);
+
+    if (pa_hashmap_size(u->filters) > 0)
+        trigger_housekeeping(u);
+
+    pa_hashmap_remove(u->mdm_ignored_outputs, o);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_unlink_cb(pa_core *core, pa_source *source, struct userdata *u) {
+    void *state;
+    struct filter *filter = NULL;
+
+    pa_core_assert_ref(core);
+    pa_source_assert_ref(source);
+    pa_assert(u);
+
+    /* If either the parent or the source we've loaded disappears,
+     * we should remove it from our hashmap */
+    PA_HASHMAP_FOREACH(filter, u->filters, state) {
+        if (filter->source_master == source || filter->source == source) {
+            uint32_t idx;
+
+            /* Attempt to rescue any streams to the parent source as this is likely
+             * the best course of action (as opposed to a generic rescue via
+             * module-rescue-streams */
+            if (filter->source == source) {
+                pa_source_output *o;
+
+                PA_IDXSET_FOREACH(o, source->outputs, idx)
+                    move_objects_for_filter(u, PA_OBJECT(o), filter, true, false);
+            }
+
+            idx = filter->module_index;
+            pa_hashmap_remove(u->filters, filter);
+            filter_free(filter);
+
+            if (can_unload_module(u, idx))
+                pa_module_unload_request_by_index(u->core, idx, true);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static void unset_mdm_ignore_input(pa_sink_input *i)
+{
+    pa_sink_input_set_property(i, PA_PROP_MDM_AUTO_FILTERED, NULL);
+}
+
+static void unset_mdm_ignore_output(pa_source_output *o)
+{
+    pa_source_output_set_property(o, PA_PROP_MDM_AUTO_FILTERED, NULL);
+}
+
+int pa__init(pa_module *m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+
+    u->core = m->core;
+
+    u->autoclean = DEFAULT_AUTOCLEAN;
+    if (pa_modargs_get_value_boolean(ma, "autoclean", &u->autoclean) < 0) {
+        pa_log("Failed to parse autoclean value");
+        goto fail;
+    }
+
+    u->filters = pa_hashmap_new(filter_hash, filter_compare);
+    u->mdm_ignored_inputs = pa_hashmap_new_full(NULL, NULL, (pa_free_cb_t) unset_mdm_ignore_input, NULL);
+    u->mdm_ignored_outputs = pa_hashmap_new_full(NULL, NULL, (pa_free_cb_t) unset_mdm_ignore_output, NULL);
+
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_proplist_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE-1, (pa_hook_cb_t) sink_unlink_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) source_output_put_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) source_output_move_finish_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) source_output_proplist_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_output_unlink_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE-1, (pa_hook_cb_t) source_unlink_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->housekeeping_time_event)
+        u->core->mainloop->time_free(u->housekeeping_time_event);
+
+    if (u->filters) {
+        struct filter *f;
+
+        while ((f = pa_hashmap_steal_first(u->filters))) {
+            pa_module_unload_request_by_index(u->core, f->module_index, true);
+            filter_free(f);
+        }
+
+        pa_hashmap_free(u->filters);
+    }
+
+    if (u->mdm_ignored_inputs)
+        pa_hashmap_free(u->mdm_ignored_inputs);
+
+    if (u->mdm_ignored_outputs)
+        pa_hashmap_free(u->mdm_ignored_outputs);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-filter-heuristics.c b/src/modules/module-filter-heuristics.c
new file mode 100644 (file)
index 0000000..423e873
--- /dev/null
@@ -0,0 +1,174 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2011 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/hook-list.h>
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/modargs.h>
+
+#include "module-filter-heuristics-symdef.h"
+
+#define PA_PROP_FILTER_APPLY_MOVING "filter.apply.moving"
+#define PA_PROP_FILTER_HEURISTICS "filter.heuristics"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("Detect when various filters are desirable");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+};
+
+static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_input) {
+    const char *want;
+    pa_proplist *pl, *parent_pl;
+
+    if (is_sink_input) {
+        pl = PA_SINK_INPUT(o)->proplist;
+        parent_pl = PA_SINK_INPUT(o)->sink->proplist;
+    } else {
+        pl = PA_SOURCE_OUTPUT(o)->proplist;
+        parent_pl = PA_SOURCE_OUTPUT(o)->source->proplist;
+    }
+
+    /* If the stream already specifies what it must have, then let it be. */
+    if (!pa_proplist_gets(pl, PA_PROP_FILTER_HEURISTICS) && pa_proplist_gets(pl, PA_PROP_FILTER_APPLY))
+        return PA_HOOK_OK;
+
+    /* On phone sinks, make sure we're not applying echo cancellation */
+    if (pa_str_in_list_spaces(pa_proplist_gets(parent_pl, PA_PROP_DEVICE_INTENDED_ROLES), "phone")) {
+        const char *apply = pa_proplist_gets(pl, PA_PROP_FILTER_APPLY);
+
+        if (apply && pa_streq(apply, "echo-cancel")) {
+            pa_proplist_unset(pl, PA_PROP_FILTER_APPLY);
+            pa_proplist_unset(pl, PA_PROP_FILTER_HEURISTICS);
+        }
+
+        return PA_HOOK_OK;
+    }
+
+    want = pa_proplist_gets(pl, PA_PROP_FILTER_WANT);
+
+    if (want) {
+        /* There's a filter that we want, ask module-filter-apply to apply it, and remember that we're managing filter.apply */
+        pa_proplist_sets(pl, PA_PROP_FILTER_APPLY, want);
+        pa_proplist_sets(pl, PA_PROP_FILTER_HEURISTICS, "1");
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+    pa_assert(u);
+
+    return process(u, PA_OBJECT(i), true);
+}
+
+static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+    pa_assert(u);
+
+    /* module-filter-apply triggered this move, ignore */
+    if (pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY_MOVING))
+        return PA_HOOK_OK;
+
+    return process(u, PA_OBJECT(i), true);
+}
+
+static pa_hook_result_t source_output_put_cb(pa_core *core, pa_source_output *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_source_output_assert_ref(i);
+    pa_assert(u);
+
+    return process(u, PA_OBJECT(i), false);
+}
+
+static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_output *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_source_output_assert_ref(i);
+    pa_assert(u);
+
+    /* module-filter-apply triggered this move, ignore */
+    if (pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY_MOVING))
+        return PA_HOOK_OK;
+
+    return process(u, PA_OBJECT(i), false);
+}
+
+int pa__init(pa_module *m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+
+    u->core = m->core;
+
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE-1, (pa_hook_cb_t) sink_input_put_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE-1, (pa_hook_cb_t) sink_input_move_finish_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_LATE-1, (pa_hook_cb_t) source_output_put_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_LATE-1, (pa_hook_cb_t) source_output_move_finish_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+
+}
+
+void pa__done(pa_module *m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    pa_xfree(u);
+
+}
diff --git a/src/modules/module-hal-detect-compat.c b/src/modules/module-hal-detect-compat.c
new file mode 100644 (file)
index 0000000..719e357
--- /dev/null
@@ -0,0 +1,82 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+#include "module-hal-detect-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Compatibility module");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!");
+
+static const char* const valid_modargs[] = {
+    "api",
+    "tsched",
+    "subdevices",
+    NULL,
+};
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    bool tsched = true;
+    pa_module *n;
+    char *t;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "tsched", &tsched) < 0) {
+        pa_log("tsched= expects boolean arguments");
+        goto fail;
+    }
+
+    pa_log_warn("We will now load module-udev-detect. Please make sure to remove module-hal-detect from your configuration.");
+
+    t = pa_sprintf_malloc("tsched=%s", pa_yes_no(tsched));
+    n = pa_module_load(m->core, "module-udev-detect", t);
+    pa_xfree(t);
+
+    if (n)
+        pa_module_unload_request(m, true);
+
+    pa_modargs_free(ma);
+
+    return n ? 0 : -1;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c
new file mode 100644 (file)
index 0000000..f906b88
--- /dev/null
@@ -0,0 +1,444 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/namereg.h>
+
+#include "module-intended-roles-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically set device of streams based on intended roles of devices");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+        "on_hotplug=<When new device becomes available, recheck streams?> "
+        "on_rescue=<When device becomes unavailable, recheck streams?>");
+
+static const char* const valid_modargs[] = {
+    "on_hotplug",
+    "on_rescue",
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    pa_hook_slot
+        *sink_input_new_hook_slot,
+        *source_output_new_hook_slot,
+        *sink_put_hook_slot,
+        *source_put_hook_slot,
+        *sink_unlink_hook_slot,
+        *source_unlink_hook_slot;
+
+    bool on_hotplug:1;
+    bool on_rescue:1;
+};
+
+static bool role_match(pa_proplist *proplist, const char *role) {
+    return pa_str_in_list_spaces(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES), role);
+}
+
+static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
+    const char *role;
+    pa_sink *s;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    if (!new_data->proplist) {
+        pa_log_debug("New stream lacks property data.");
+        return PA_HOOK_OK;
+    }
+
+    if (new_data->sink) {
+        pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
+        return PA_HOOK_OK;
+    }
+
+    if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) {
+        pa_log_debug("Not setting device for stream %s, because it lacks role.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
+        return PA_HOOK_OK;
+    }
+
+    /* Prefer the default sink over any other sink, just in case... */
+    if (c->default_sink)
+        if (role_match(c->default_sink->proplist, role) && pa_sink_input_new_data_set_sink(new_data, c->default_sink, false))
+            return PA_HOOK_OK;
+
+    /* @todo: favour the highest priority device, not the first one we find? */
+    PA_IDXSET_FOREACH(s, c->sinks, idx) {
+        if (s == c->default_sink)
+            continue;
+
+        if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)))
+            continue;
+
+        if (role_match(s->proplist, role) && pa_sink_input_new_data_set_sink(new_data, s, false))
+            return PA_HOOK_OK;
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
+    const char *role;
+    pa_source *s;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    if (!new_data->proplist) {
+        pa_log_debug("New stream lacks property data.");
+        return PA_HOOK_OK;
+    }
+
+    if (new_data->source) {
+        pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
+        return PA_HOOK_OK;
+    }
+
+    if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) {
+        pa_log_debug("Not setting device for stream %s, because it lacks role.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
+        return PA_HOOK_OK;
+    }
+
+    /* Prefer the default source over any other source, just in case... */
+    if (c->default_source)
+        if (role_match(c->default_source->proplist, role)) {
+            pa_source_output_new_data_set_source(new_data, c->default_source, false);
+            return PA_HOOK_OK;
+        }
+
+    PA_IDXSET_FOREACH(s, c->sources, idx) {
+        if (s->monitor_of)
+            continue;
+
+        if (s == c->default_source)
+            continue;
+
+        if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
+            continue;
+
+        /* @todo: favour the highest priority device, not the first one we find? */
+        if (role_match(s->proplist, role)) {
+            pa_source_output_new_data_set_source(new_data, s, false);
+            return PA_HOOK_OK;
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+    pa_sink_input *si;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+    pa_assert(u->on_hotplug);
+
+    PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
+        const char *role;
+
+        if (si->sink == sink)
+            continue;
+
+        if (si->save_sink)
+            continue;
+
+        /* Skip this if it is already in the process of being moved
+         * anyway */
+        if (!si->sink)
+            continue;
+
+        /* It might happen that a stream and a sink are set up at the
+           same time, in which case we want to make sure we don't
+           interfere with that */
+        if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
+            continue;
+
+        if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
+            continue;
+
+        if (role_match(si->sink->proplist, role))
+            continue;
+
+        if (!role_match(sink->proplist, role))
+            continue;
+
+        pa_sink_input_move_to(si, sink, false);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
+    pa_source_output *so;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(source);
+    pa_assert(u);
+    pa_assert(u->on_hotplug);
+
+    if (source->monitor_of)
+        return PA_HOOK_OK;
+
+    PA_IDXSET_FOREACH(so, c->source_outputs, idx) {
+        const char *role;
+
+        if (so->source == source)
+            continue;
+
+        if (so->save_source)
+            continue;
+
+        if (so->direct_on_input)
+            continue;
+
+        /* Skip this if it is already in the process of being moved
+         * anyway */
+        if (!so->source)
+            continue;
+
+        /* It might happen that a stream and a source are set up at the
+           same time, in which case we want to make sure we don't
+           interfere with that */
+        if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
+            continue;
+
+        if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
+            continue;
+
+        if (role_match(so->source->proplist, role))
+            continue;
+
+        if (!role_match(source->proplist, role))
+            continue;
+
+        pa_source_output_move_to(so, source, false);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+    pa_sink_input *si;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+    pa_assert(u->on_rescue);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    /* If there not default sink, then there is no sink at all */
+    if (!c->default_sink)
+        return PA_HOOK_OK;
+
+    PA_IDXSET_FOREACH(si, sink->inputs, idx) {
+        const char *role;
+        uint32_t jdx;
+        pa_sink *d;
+
+        if (!si->sink)
+            continue;
+
+        if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
+            continue;
+
+        /* Would the default sink fit? If so, let's use it */
+        if (c->default_sink != sink && role_match(c->default_sink->proplist, role))
+            if (pa_sink_input_move_to(si, c->default_sink, false) >= 0)
+                continue;
+
+        /* Try to find some other fitting sink */
+        /* @todo: favour the highest priority device, not the first one we find? */
+        PA_IDXSET_FOREACH(d, c->sinks, jdx) {
+            if (d == c->default_sink || d == sink)
+                continue;
+
+            if (!PA_SINK_IS_LINKED(pa_sink_get_state(d)))
+                continue;
+
+            if (role_match(d->proplist, role))
+                if (pa_sink_input_move_to(si, d, false) >= 0)
+                    break;
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
+    pa_source_output *so;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(source);
+    pa_assert(u);
+    pa_assert(u->on_rescue);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    /* If there not default source, then there is no source at all */
+    if (!c->default_source)
+        return PA_HOOK_OK;
+
+    PA_IDXSET_FOREACH(so, source->outputs, idx) {
+        const char *role;
+        uint32_t jdx;
+        pa_source *d;
+
+        if (so->direct_on_input)
+            continue;
+
+        if (!so->source)
+            continue;
+
+        if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
+            continue;
+
+        /* Would the default source fit? If so, let's use it */
+        if (c->default_source != source && role_match(c->default_source->proplist, role)
+                && !source->monitor_of == !c->default_source->monitor_of) {
+            pa_source_output_move_to(so, c->default_source, false);
+            continue;
+        }
+
+        /* Try to find some other fitting source */
+        /* @todo: favour the highest priority device, not the first one we find? */
+        PA_IDXSET_FOREACH(d, c->sources, jdx) {
+            if (d == c->default_source || d == source)
+                continue;
+
+            if (!PA_SOURCE_IS_LINKED(pa_source_get_state(d)))
+                continue;
+
+            /* If moving from a monitor, move to another monitor */
+            if (!source->monitor_of == !d->monitor_of && role_match(d->proplist, role)) {
+                pa_source_output_move_to(so, d, false);
+                break;
+            }
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    bool on_hotplug = true, on_rescue = true;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
+        pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
+        pa_log("on_hotplug= and on_rescue= expect boolean arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->on_hotplug = on_hotplug;
+    u->on_rescue = on_rescue;
+
+    /* A little bit later than module-stream-restore */
+    u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) sink_input_new_hook_callback, u);
+    u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) source_output_new_hook_callback, u);
+
+    if (on_hotplug) {
+        /* A little bit later than module-stream-restore */
+        u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_put_hook_callback, u);
+        u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) source_put_hook_callback, u);
+    }
+
+    if (on_rescue) {
+        /* A little bit later than module-stream-restore, a little bit earlier than module-rescue-streams, ... */
+        u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_unlink_hook_callback, u);
+        u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) source_unlink_hook_callback, u);
+    }
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink_input_new_hook_slot)
+        pa_hook_slot_free(u->sink_input_new_hook_slot);
+    if (u->source_output_new_hook_slot)
+        pa_hook_slot_free(u->source_output_new_hook_slot);
+
+    if (u->sink_put_hook_slot)
+        pa_hook_slot_free(u->sink_put_hook_slot);
+    if (u->source_put_hook_slot)
+        pa_hook_slot_free(u->source_put_hook_slot);
+
+    if (u->sink_unlink_hook_slot)
+        pa_hook_slot_free(u->sink_unlink_hook_slot);
+    if (u->source_unlink_hook_slot)
+        pa_hook_slot_free(u->source_unlink_hook_slot);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
new file mode 100644 (file)
index 0000000..c2c7c85
--- /dev/null
@@ -0,0 +1,1434 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* TODO: Some plugins cause latency, and some even report it by using a control
+   out port. We don't currently use the latency information. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/ltdl-helper.h>
+
+#ifdef HAVE_DBUS
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/dbus-util.h>
+#endif
+
+#include "module-ladspa-sink-symdef.h"
+#include "ladspa.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION(_("Virtual LADSPA sink"));
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+    _("sink_name=<name for the sink> "
+      "sink_properties=<properties for the sink> "
+      "master=<name of sink to filter> "
+      "sink_master=<name of sink to filter> "
+      "format=<sample format> "
+      "rate=<sample rate> "
+      "channels=<number of channels> "
+      "channel_map=<input channel map> "
+      "plugin=<ladspa plugin name> "
+      "label=<ladspa plugin label> "
+      "control=<comma separated list of input control values> "
+      "input_ladspaport_map=<comma separated list of input LADSPA port names> "
+      "output_ladspaport_map=<comma separated list of output LADSPA port names> "
+      "autoloaded=<set if this module is being loaded automatically> "));
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+#define DEFAULT_AUTOLOADED false
+
+/* PLEASE NOTICE: The PortAudio ports and the LADSPA ports are two different concepts.
+They are not related and where possible the names of the LADSPA port variables contains "ladspa" to avoid confusion */
+
+struct userdata {
+    pa_module *module;
+
+    pa_sink *sink;
+    pa_sink_input *sink_input;
+
+    const LADSPA_Descriptor *descriptor;
+    LADSPA_Handle handle[PA_CHANNELS_MAX];
+    unsigned long max_ladspaport_count, input_count, output_count, channels;
+    LADSPA_Data **input, **output;
+    size_t block_size;
+    LADSPA_Data *control;
+    long unsigned n_control;
+
+    /* This is a dummy buffer. Every port must be connected, but we don't care
+    about control out ports. We connect them all to this single buffer. */
+    LADSPA_Data control_out;
+
+    pa_memblockq *memblockq;
+
+    bool *use_default;
+    pa_sample_spec ss;
+
+#ifdef HAVE_DBUS
+    pa_dbus_protocol *dbus_protocol;
+    char *dbus_path;
+#endif
+
+    bool auto_desc;
+    bool autoloaded;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "master",  /* Will be deprecated. */
+    "sink_master",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "plugin",
+    "label",
+    "control",
+    "input_ladspaport_map",
+    "output_ladspaport_map",
+    "autoloaded",
+    NULL
+};
+
+/* The PA_SINK_MESSAGE types that extend the predefined messages. */
+enum {
+   LADSPA_SINK_MESSAGE_UPDATE_PARAMETERS = PA_SINK_MESSAGE_MAX
+};
+
+static int write_control_parameters(struct userdata *u, double *control_values, bool *use_default);
+static void connect_control_ports(struct userdata *u);
+
+#ifdef HAVE_DBUS
+
+#define LADSPA_IFACE "org.PulseAudio.Ext.Ladspa1"
+#define LADSPA_ALGORITHM_PARAMETERS "AlgorithmParameters"
+
+/* TODO: add a PropertyChanged signal to tell that the algorithm parameters have been changed */
+
+enum ladspa_handler_index {
+    LADSPA_HANDLER_ALGORITHM_PARAMETERS,
+    LADSPA_HANDLER_MAX
+};
+
+static void get_algorithm_parameters(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter, struct_iter;
+    unsigned long i;
+    double *control;
+    dbus_bool_t *use_default;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert_se(u = _u);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+
+    dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter);
+
+    /* copying because of the D-Bus type mapping */
+    control = pa_xnew(double, u->n_control);
+    use_default = pa_xnew(dbus_bool_t, u->n_control);
+
+    for (i = 0; i < u->n_control; i++) {
+        control[i] = (double) u->control[i];
+        use_default[i] = u->use_default[i];
+    }
+
+    pa_dbus_append_basic_array(&struct_iter, DBUS_TYPE_DOUBLE, control, u->n_control);
+    pa_dbus_append_basic_array(&struct_iter, DBUS_TYPE_BOOLEAN, use_default, u->n_control);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &struct_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+    pa_xfree(control);
+    pa_xfree(use_default);
+}
+
+static void set_algorithm_parameters(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *_u) {
+    struct userdata *u;
+    DBusMessageIter array_iter, struct_iter;
+    int n_control = 0, n_use_default;
+    unsigned n_dbus_control, n_dbus_use_default;
+    double *read_values = NULL;
+    dbus_bool_t *read_defaults = NULL;
+    bool *use_defaults = NULL;
+    unsigned long i;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert_se(u = _u);
+
+    /* The property we are expecting has signature (adab), meaning that it's a
+       struct of two arrays, the first containing doubles and the second containing
+       booleans. The first array has the algorithm configuration values and the
+       second array has booleans indicating whether the matching algorithm
+       configuration value should use (or try to use) the default value provided by
+       the algorithm module. The PulseAudio D-Bus infrastructure will take care of
+       checking the argument types for us. */
+
+    dbus_message_iter_recurse(iter, &struct_iter);
+
+    dbus_message_iter_recurse(&struct_iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &read_values, &n_control);
+
+    dbus_message_iter_next(&struct_iter);
+    dbus_message_iter_recurse(&struct_iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &read_defaults, &n_use_default);
+
+    n_dbus_control = n_control; /* handle the unsignedness */
+    n_dbus_use_default = n_use_default;
+
+    if (n_dbus_control != u->n_control || n_dbus_use_default != u->n_control) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong number of array values (expected %lu)", u->n_control);
+        return;
+    }
+
+    use_defaults = pa_xnew(bool, n_control);
+    for (i = 0; i < u->n_control; i++)
+        use_defaults[i] = read_defaults[i];
+
+    if (write_control_parameters(u, read_values, use_defaults) < 0) {
+        pa_log_warn("Failed writing control parameters");
+        goto error;
+    }
+
+    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), LADSPA_SINK_MESSAGE_UPDATE_PARAMETERS, NULL, 0, NULL);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_xfree(use_defaults);
+    return;
+
+error:
+    pa_xfree(use_defaults);
+    pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error");
+}
+
+static pa_dbus_property_handler ladspa_property_handlers[LADSPA_HANDLER_MAX] = {
+    [LADSPA_HANDLER_ALGORITHM_PARAMETERS] = {
+        .property_name = LADSPA_ALGORITHM_PARAMETERS,
+        .type = "(adab)",
+        .get_cb = get_algorithm_parameters,
+        .set_cb = set_algorithm_parameters
+    }
+};
+
+static void ladspa_get_all(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter, dict_iter, dict_entry_iter, variant_iter, struct_iter;
+    const char *key = LADSPA_ALGORITHM_PARAMETERS;
+    double *control;
+    dbus_bool_t *use_default;
+    long unsigned i;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert_se(u = _u);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    /* Currently, on this interface, only a single property is returned. */
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+    pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+
+    pa_assert_se(dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "(adab)", &variant_iter));
+    pa_assert_se(dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter));
+
+    control = pa_xnew(double, u->n_control);
+    use_default = pa_xnew(dbus_bool_t, u->n_control);
+
+    for (i = 0; i < u->n_control; i++) {
+        control[i] = (double) u->control[i];
+        use_default[i] = u->use_default[i];
+    }
+
+    pa_dbus_append_basic_array(&struct_iter, DBUS_TYPE_DOUBLE, control, u->n_control);
+    pa_dbus_append_basic_array(&struct_iter, DBUS_TYPE_BOOLEAN, use_default, u->n_control);
+
+    pa_assert_se(dbus_message_iter_close_container(&variant_iter, &struct_iter));
+    pa_assert_se(dbus_message_iter_close_container(&dict_entry_iter, &variant_iter));
+    pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+    pa_xfree(control);
+    pa_xfree(use_default);
+}
+
+static pa_dbus_interface_info ladspa_info = {
+    .name = LADSPA_IFACE,
+    .method_handlers = NULL,
+    .n_method_handlers = 0,
+    .property_handlers = ladspa_property_handlers,
+    .n_property_handlers = LADSPA_HANDLER_MAX,
+    .get_all_properties_cb = ladspa_get_all,
+    .signals = NULL,
+    .n_signals = 0
+};
+
+static void dbus_init(struct userdata *u) {
+    pa_assert_se(u);
+
+    u->dbus_protocol = pa_dbus_protocol_get(u->sink->core);
+    u->dbus_path = pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index);
+
+    pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &ladspa_info, u);
+}
+
+static void dbus_done(struct userdata *u) {
+    pa_assert_se(u);
+
+    if (!u->dbus_protocol) {
+        pa_assert(!u->dbus_path);
+        return;
+    }
+
+    pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, ladspa_info.name);
+    pa_xfree(u->dbus_path);
+    pa_dbus_protocol_unref(u->dbus_protocol);
+
+    u->dbus_path = NULL;
+    u->dbus_protocol = NULL;
+}
+
+#endif /* HAVE_DBUS */
+
+/* Called from I/O thread context */
+static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+    case PA_SINK_MESSAGE_GET_LATENCY:
+
+        /* The sink is _put() before the sink input is, so let's
+         * make sure we don't access it in that time. Also, the
+         * sink input is first shut down, the sink second. */
+        if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+            !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) {
+            *((int64_t*) data) = 0;
+            return 0;
+        }
+
+        *((int64_t*) data) =
+
+            /* Get the latency of the master sink */
+            pa_sink_get_latency_within_thread(u->sink_input->sink, true) +
+
+            /* Add the latency internal to our sink input on top */
+            pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
+
+        return 0;
+
+    case LADSPA_SINK_MESSAGE_UPDATE_PARAMETERS:
+
+        /* rewind the stream to throw away the previously rendered data */
+
+        pa_log_debug("Requesting rewind due to parameter update.");
+        pa_sink_request_rewind(u->sink, -1);
+
+        /* change the sink parameters */
+        connect_control_ports(u);
+
+        return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
+
+            /* When set to running or idle for the first time, request a rewind
+             * of the master sink to make sure we are heard immediately */
+            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
+                pa_log_debug("Requesting rewind due to state change.");
+                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
+            }
+            break;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(state) ||
+            !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return 0;
+
+    pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_request_rewind_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+            !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_request_rewind(u->sink_input,
+                                 s->thread_info.rewind_nbytes +
+                                 pa_memblockq_get_length(u->memblockq), true, false, false);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+            !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_set_requested_latency_within_thread(
+        u->sink_input,
+        pa_sink_get_requested_latency_within_thread(s));
+}
+
+/* Called from main context */
+static void sink_set_mute_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
+            !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return;
+
+    pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted);
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+    float *src, *dst;
+    size_t fs;
+    unsigned n, h, c;
+    pa_memchunk tchunk;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    pa_assert_se(u = i->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return -1;
+
+    /* Hmm, process any rewind request that might be queued up */
+    pa_sink_process_rewind(u->sink, 0);
+
+    while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
+        pa_memchunk nchunk;
+
+        pa_sink_render(u->sink, nbytes, &nchunk);
+        pa_memblockq_push(u->memblockq, &nchunk);
+        pa_memblock_unref(nchunk.memblock);
+    }
+
+    tchunk.length = PA_MIN(nbytes, tchunk.length);
+    pa_assert(tchunk.length > 0);
+
+    fs = pa_frame_size(&i->sample_spec);
+    n = (unsigned) (PA_MIN(tchunk.length, u->block_size) / fs);
+
+    pa_assert(n > 0);
+
+    chunk->index = 0;
+    chunk->length = n*fs;
+    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
+
+    pa_memblockq_drop(u->memblockq, chunk->length);
+
+    src = pa_memblock_acquire_chunk(&tchunk);
+    dst = pa_memblock_acquire(chunk->memblock);
+
+    for (h = 0; h < (u->channels / u->max_ladspaport_count); h++) {
+        for (c = 0; c < u->input_count; c++)
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c], sizeof(float), src+ h*u->max_ladspaport_count + c, u->channels*sizeof(float), n);
+        u->descriptor->run(u->handle[h], n);
+        for (c = 0; c < u->output_count; c++)
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + h*u->max_ladspaport_count + c, u->channels*sizeof(float), u->output[c], sizeof(float), n);
+    }
+
+    pa_memblock_release(tchunk.memblock);
+    pa_memblock_release(chunk->memblock);
+
+    pa_memblock_unref(tchunk.memblock);
+
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+    size_t amount = 0;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If the sink is not yet linked, there is nothing to rewind */
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    if (u->sink->thread_info.rewind_nbytes > 0) {
+        size_t max_rewrite;
+
+        max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq);
+        amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
+        u->sink->thread_info.rewind_nbytes = 0;
+
+        if (amount > 0) {
+            unsigned c;
+
+            pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, true);
+
+            pa_log_debug("Resetting plugin");
+
+            /* Reset the plugin */
+            if (u->descriptor->deactivate)
+                for (c = 0; c < (u->channels / u->max_ladspaport_count); c++)
+                    u->descriptor->deactivate(u->handle[c]);
+            if (u->descriptor->activate)
+                for (c = 0; c < (u->channels / u->max_ladspaport_count); c++)
+                    u->descriptor->activate(u->handle[c]);
+        }
+    }
+
+    pa_sink_process_rewind(u->sink, amount);
+    pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+    pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_max_request_within_thread(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_detach_within_thread(u->sink);
+
+    pa_sink_set_rtpoll(u->sink, NULL);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll);
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+    pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i));
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_attach_within_thread(u->sink);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* The order here matters! We first kill the sink so that streams
+     * can properly be moved away while the sink input is still connected
+     * to the master. */
+    pa_sink_input_cork(u->sink_input, true);
+    pa_sink_unlink(u->sink);
+    pa_sink_input_unlink(u->sink_input);
+
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    pa_sink_unref(u->sink);
+    u->sink = NULL;
+
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main context */
+static bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (u->autoloaded)
+        return false;
+
+    return u->sink != dest;
+}
+
+/* Called from main context */
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (dest) {
+        pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
+        pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
+    } else
+        pa_sink_set_asyncmsgq(u->sink, NULL);
+
+    if (u->auto_desc && dest) {
+        const char *z;
+        pa_proplist *pl;
+
+        pl = pa_proplist_new();
+        z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s",
+                         pa_proplist_gets(u->sink->proplist, "device.ladspa.name"), z ? z : dest->name);
+
+        pa_sink_update_proplist(u->sink, PA_UPDATE_REPLACE, pl);
+        pa_proplist_free(pl);
+    }
+}
+
+/* Called from main context */
+static void sink_input_mute_changed_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_mute_changed(u->sink, i->muted);
+}
+
+static int parse_control_parameters(struct userdata *u, const char *cdata, double *read_values, bool *use_default) {
+    unsigned long p = 0;
+    const char *state = NULL;
+    char *k;
+
+    pa_assert(read_values);
+    pa_assert(use_default);
+    pa_assert(u);
+
+    pa_log_debug("Trying to read %lu control values", u->n_control);
+
+    if (!cdata && u->n_control > 0)
+        return -1;
+
+    pa_log_debug("cdata: '%s'", cdata);
+
+    while ((k = pa_split(cdata, ",", &state)) && p < u->n_control) {
+        double f;
+
+        if (*k == 0) {
+            pa_log_debug("Read empty config value (p=%lu)", p);
+            use_default[p++] = true;
+            pa_xfree(k);
+            continue;
+        }
+
+        if (pa_atod(k, &f) < 0) {
+            pa_log_debug("Failed to parse control value '%s' (p=%lu)", k, p);
+            pa_xfree(k);
+            goto fail;
+        }
+
+        pa_xfree(k);
+
+        pa_log_debug("Read config value %f (p=%lu)", f, p);
+
+        use_default[p] = false;
+        read_values[p++] = f;
+    }
+
+    /* The previous loop doesn't take the last control value into account
+       if it is left empty, so we do it here. */
+    if (*cdata == 0 || cdata[strlen(cdata) - 1] == ',') {
+        if (p < u->n_control)
+            use_default[p] = true;
+        p++;
+    }
+
+    if (p > u->n_control || k) {
+        pa_log("Too many control values passed, %lu expected.", u->n_control);
+        pa_xfree(k);
+        goto fail;
+    }
+
+    if (p < u->n_control) {
+        pa_log("Not enough control values passed, %lu expected, %lu passed.", u->n_control, p);
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+static void connect_control_ports(struct userdata *u) {
+    unsigned long p = 0, h = 0, c;
+    const LADSPA_Descriptor *d;
+
+    pa_assert(u);
+    pa_assert_se(d = u->descriptor);
+
+    for (p = 0; p < d->PortCount; p++) {
+        if (!LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]))
+            continue;
+
+        if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) {
+            for (c = 0; c < (u->channels / u->max_ladspaport_count); c++)
+                d->connect_port(u->handle[c], p, &u->control_out);
+            continue;
+        }
+
+        /* input control port */
+
+        pa_log_debug("Binding %f to port %s", u->control[h], d->PortNames[p]);
+
+        for (c = 0; c < (u->channels / u->max_ladspaport_count); c++)
+            d->connect_port(u->handle[c], p, &u->control[h]);
+
+        h++;
+    }
+}
+
+static int validate_control_parameters(struct userdata *u, double *control_values, bool *use_default) {
+    unsigned long p = 0, h = 0;
+    const LADSPA_Descriptor *d;
+    pa_sample_spec ss;
+
+    pa_assert(control_values);
+    pa_assert(use_default);
+    pa_assert(u);
+    pa_assert_se(d = u->descriptor);
+
+    ss = u->ss;
+
+    /* Iterate over all ports. Check for every control port that 1) it
+     * supports default values if a default value is provided and 2) the
+     * provided value is within the limits specified in the plugin. */
+
+    for (p = 0; p < d->PortCount; p++) {
+        LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor;
+
+        if (!LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]))
+            continue;
+
+        if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p]))
+            continue;
+
+        if (use_default[h]) {
+            /* User wants to use default value. Check if the plugin
+             * provides it. */
+            if (!LADSPA_IS_HINT_HAS_DEFAULT(hint)) {
+                pa_log_warn("Control port value left empty but plugin defines no default.");
+                return -1;
+            }
+        }
+        else {
+            /* Check if the user-provided value is within the bounds. */
+            LADSPA_Data lower = d->PortRangeHints[p].LowerBound;
+            LADSPA_Data upper = d->PortRangeHints[p].UpperBound;
+
+            if (LADSPA_IS_HINT_SAMPLE_RATE(hint)) {
+                upper *= (LADSPA_Data) ss.rate;
+                lower *= (LADSPA_Data) ss.rate;
+            }
+
+            if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint)) {
+                if (control_values[h] > upper) {
+                    pa_log_warn("Control value %lu over upper bound: %f (upper bound: %f)", h, control_values[h], upper);
+                    return -1;
+                }
+            }
+            if (LADSPA_IS_HINT_BOUNDED_BELOW(hint)) {
+                if (control_values[h] < lower) {
+                    pa_log_warn("Control value %lu below lower bound: %f (lower bound: %f)", h, control_values[h], lower);
+                    return -1;
+                }
+            }
+        }
+
+        h++;
+    }
+
+    return 0;
+}
+
+static int write_control_parameters(struct userdata *u, double *control_values, bool *use_default) {
+    unsigned long p = 0, h = 0, c;
+    const LADSPA_Descriptor *d;
+    pa_sample_spec ss;
+
+    pa_assert(control_values);
+    pa_assert(use_default);
+    pa_assert(u);
+    pa_assert_se(d = u->descriptor);
+
+    ss = u->ss;
+
+    if (validate_control_parameters(u, control_values, use_default) < 0)
+        return -1;
+
+    /* p iterates over all ports, h is the control port iterator */
+
+    for (p = 0; p < d->PortCount; p++) {
+        LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor;
+
+        if (!LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]))
+            continue;
+
+        if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) {
+            for (c = 0; c < (u->channels / u->max_ladspaport_count); c++)
+                d->connect_port(u->handle[c], p, &u->control_out);
+            continue;
+        }
+
+        if (use_default[h]) {
+
+            LADSPA_Data lower, upper;
+
+            lower = d->PortRangeHints[p].LowerBound;
+            upper = d->PortRangeHints[p].UpperBound;
+
+            if (LADSPA_IS_HINT_SAMPLE_RATE(hint)) {
+                lower *= (LADSPA_Data) ss.rate;
+                upper *= (LADSPA_Data) ss.rate;
+            }
+
+            switch (hint & LADSPA_HINT_DEFAULT_MASK) {
+
+            case LADSPA_HINT_DEFAULT_MINIMUM:
+                u->control[h] = lower;
+                break;
+
+            case LADSPA_HINT_DEFAULT_MAXIMUM:
+                u->control[h] = upper;
+                break;
+
+            case LADSPA_HINT_DEFAULT_LOW:
+                if (LADSPA_IS_HINT_LOGARITHMIC(hint))
+                    u->control[h] = (LADSPA_Data) exp(log(lower) * 0.75 + log(upper) * 0.25);
+                else
+                    u->control[h] = (LADSPA_Data) (lower * 0.75 + upper * 0.25);
+                break;
+
+            case LADSPA_HINT_DEFAULT_MIDDLE:
+                if (LADSPA_IS_HINT_LOGARITHMIC(hint))
+                    u->control[h] = (LADSPA_Data) exp(log(lower) * 0.5 + log(upper) * 0.5);
+                else
+                    u->control[h] = (LADSPA_Data) (lower * 0.5 + upper * 0.5);
+                break;
+
+            case LADSPA_HINT_DEFAULT_HIGH:
+                if (LADSPA_IS_HINT_LOGARITHMIC(hint))
+                    u->control[h] = (LADSPA_Data) exp(log(lower) * 0.25 + log(upper) * 0.75);
+                else
+                    u->control[h] = (LADSPA_Data) (lower * 0.25 + upper * 0.75);
+                break;
+
+            case LADSPA_HINT_DEFAULT_0:
+                u->control[h] = 0;
+                break;
+
+            case LADSPA_HINT_DEFAULT_1:
+                u->control[h] = 1;
+                break;
+
+            case LADSPA_HINT_DEFAULT_100:
+                u->control[h] = 100;
+                break;
+
+            case LADSPA_HINT_DEFAULT_440:
+                u->control[h] = 440;
+                break;
+
+            default:
+                pa_assert_not_reached();
+            }
+        }
+        else {
+            if (LADSPA_IS_HINT_INTEGER(hint)) {
+                u->control[h] = roundf(control_values[h]);
+            }
+            else {
+                u->control[h] = control_values[h];
+            }
+        }
+
+        h++;
+    }
+
+    /* set the use_default array to the user data */
+    memcpy(u->use_default, use_default, u->n_control * sizeof(u->use_default[0]));
+
+    return 0;
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma;
+    char *t;
+    const char *master_name;
+    pa_sink *master;
+    pa_sink_input_new_data sink_input_data;
+    pa_sink_new_data sink_data;
+    const char *plugin, *label, *input_ladspaport_map, *output_ladspaport_map;
+    LADSPA_Descriptor_Function descriptor_func;
+    unsigned long input_ladspaport[PA_CHANNELS_MAX], output_ladspaport[PA_CHANNELS_MAX];
+    const char *e, *cdata;
+    const LADSPA_Descriptor *d;
+    unsigned long p, h, j, n_control, c;
+    pa_memchunk silence;
+
+    pa_assert(m);
+
+    pa_assert_cc(sizeof(LADSPA_Data) == sizeof(float));
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    master_name = pa_modargs_get_value(ma, "sink_master", NULL);
+    if (!master_name) {
+        master_name = pa_modargs_get_value(ma, "master", NULL);
+        if (master_name)
+            pa_log_warn("The 'master' module argument is deprecated and may be removed in the future, "
+                        "please use the 'sink_master' argument instead.");
+    }
+
+    master = pa_namereg_get(m->core, master_name, PA_NAMEREG_SINK);
+    if (!master) {
+        pa_log("Master sink not found.");
+        goto fail;
+    }
+
+    ss = master->sample_spec;
+    ss.format = PA_SAMPLE_FLOAT32;
+    map = master->channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    if (ss.format != PA_SAMPLE_FLOAT32) {
+        pa_log("LADSPA accepts float format only");
+        goto fail;
+    }
+
+    if (!(plugin = pa_modargs_get_value(ma, "plugin", NULL))) {
+        pa_log("Missing LADSPA plugin name");
+        goto fail;
+    }
+
+    if (!(label = pa_modargs_get_value(ma, "label", NULL))) {
+        pa_log("Missing LADSPA plugin label");
+        goto fail;
+    }
+
+    if (!(input_ladspaport_map = pa_modargs_get_value(ma, "input_ladspaport_map", NULL)))
+        pa_log_debug("Using default input ladspa port mapping");
+
+    if (!(output_ladspaport_map = pa_modargs_get_value(ma, "output_ladspaport_map", NULL)))
+        pa_log_debug("Using default output ladspa port mapping");
+
+    cdata = pa_modargs_get_value(ma, "control", NULL);
+
+    u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    m->userdata = u;
+    u->max_ladspaport_count = 1; /*to avoid division by zero etc. in pa__done when failing before this value has been set*/
+    u->channels = 0;
+    u->input = NULL;
+    u->output = NULL;
+    u->ss = ss;
+
+    if (!(e = getenv("LADSPA_PATH")))
+        e = LADSPA_PATH;
+
+    /* FIXME: This is not exactly thread safe */
+    t = pa_xstrdup(lt_dlgetsearchpath());
+    lt_dlsetsearchpath(e);
+    m->dl = lt_dlopenext(plugin);
+    lt_dlsetsearchpath(t);
+    pa_xfree(t);
+
+    if (!m->dl) {
+        pa_log("Failed to load LADSPA plugin: %s", lt_dlerror());
+        goto fail;
+    }
+
+    if (!(descriptor_func = (LADSPA_Descriptor_Function) pa_load_sym(m->dl, NULL, "ladspa_descriptor"))) {
+        pa_log("LADSPA module lacks ladspa_descriptor() symbol.");
+        goto fail;
+    }
+
+    for (j = 0;; j++) {
+
+        if (!(d = descriptor_func(j))) {
+            pa_log("Failed to find plugin label '%s' in plugin '%s'.", label, plugin);
+            goto fail;
+        }
+
+        if (pa_streq(d->Label, label))
+            break;
+    }
+
+    u->descriptor = d;
+
+    pa_log_debug("Module: %s", plugin);
+    pa_log_debug("Label: %s", d->Label);
+    pa_log_debug("Unique ID: %lu", d->UniqueID);
+    pa_log_debug("Name: %s", d->Name);
+    pa_log_debug("Maker: %s", d->Maker);
+    pa_log_debug("Copyright: %s", d->Copyright);
+
+    n_control = 0;
+    u->channels = ss.channels;
+
+    /*
+    * Enumerate ladspa ports
+    * Default mapping is in order given by the plugin
+    */
+    for (p = 0; p < d->PortCount; p++) {
+        if (LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) {
+            if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p])) {
+                pa_log_debug("Port %lu is input: %s", p, d->PortNames[p]);
+                input_ladspaport[u->input_count] = p;
+                u->input_count++;
+            } else if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) {
+                pa_log_debug("Port %lu is output: %s", p, d->PortNames[p]);
+                output_ladspaport[u->output_count] = p;
+                u->output_count++;
+            }
+        } else if (LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]) && LADSPA_IS_PORT_INPUT(d->PortDescriptors[p])) {
+            pa_log_debug("Port %lu is control: %s", p, d->PortNames[p]);
+            n_control++;
+        } else
+            pa_log_debug("Ignored port %s", d->PortNames[p]);
+        /* XXX: Has anyone ever seen an in-place plugin with non-equal number of input and output ports? */
+        /* Could be if the plugin is for up-mixing stereo to 5.1 channels */
+        /* Or if the plugin is down-mixing 5.1 to two channel stereo or binaural encoded signal */
+        if (u->input_count > u->max_ladspaport_count)
+            u->max_ladspaport_count = u->input_count;
+        else
+            u->max_ladspaport_count = u->output_count;
+    }
+
+    if (u->channels % u->max_ladspaport_count) {
+        pa_log("Cannot handle non-integral number of plugins required for given number of channels");
+        goto fail;
+    }
+
+    pa_log_debug("Will run %lu plugin instances", u->channels / u->max_ladspaport_count);
+
+    /* Parse data for input ladspa port map */
+    if (input_ladspaport_map) {
+        const char *state = NULL;
+        char *pname;
+        c = 0;
+        while ((pname = pa_split(input_ladspaport_map, ",", &state))) {
+            if (c == u->input_count) {
+                pa_log("Too many ports in input ladspa port map");
+                pa_xfree(pname);
+                goto fail;
+            }
+
+            for (p = 0; p < d->PortCount; p++) {
+                if (pa_streq(d->PortNames[p], pname)) {
+                    if (LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p]) && LADSPA_IS_PORT_INPUT(d->PortDescriptors[p])) {
+                        input_ladspaport[c] = p;
+                    } else {
+                        pa_log("Port %s is not an audio input ladspa port", pname);
+                        pa_xfree(pname);
+                        goto fail;
+                    }
+                }
+            }
+            c++;
+            pa_xfree(pname);
+        }
+    }
+
+    /* Parse data for output port map */
+    if (output_ladspaport_map) {
+        const char *state = NULL;
+        char *pname;
+        c = 0;
+        while ((pname = pa_split(output_ladspaport_map, ",", &state))) {
+            if (c == u->output_count) {
+                pa_log("Too many ports in output ladspa port map");
+                pa_xfree(pname);
+                goto fail;
+            }
+            for (p = 0; p < d->PortCount; p++) {
+                if (pa_streq(d->PortNames[p], pname)) {
+                    if (LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p]) && LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) {
+                        output_ladspaport[c] = p;
+                    } else {
+                        pa_log("Port %s is not an output ladspa port", pname);
+                        pa_xfree(pname);
+                        goto fail;
+                    }
+                }
+            }
+            c++;
+            pa_xfree(pname);
+        }
+    }
+
+    u->block_size = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss);
+
+    /* Create buffers */
+    if (LADSPA_IS_INPLACE_BROKEN(d->Properties)) {
+        u->input = (LADSPA_Data**) pa_xnew(LADSPA_Data*, (unsigned) u->input_count);
+        for (c = 0; c < u->input_count; c++)
+            u->input[c] = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size);
+        u->output = (LADSPA_Data**) pa_xnew(LADSPA_Data*, (unsigned) u->output_count);
+        for (c = 0; c < u->output_count; c++)
+            u->output[c] = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size);
+    } else {
+        u->input = (LADSPA_Data**) pa_xnew(LADSPA_Data*, (unsigned) u->max_ladspaport_count);
+        for (c = 0; c < u->max_ladspaport_count; c++)
+            u->input[c] = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size);
+        u->output = u->input;
+    }
+    /* Initialize plugin instances */
+    for (h = 0; h < (u->channels / u->max_ladspaport_count); h++) {
+        if (!(u->handle[h] = d->instantiate(d, ss.rate))) {
+            pa_log("Failed to instantiate plugin %s with label %s", plugin, d->Label);
+            goto fail;
+        }
+
+        for (c = 0; c < u->input_count; c++)
+            d->connect_port(u->handle[h], input_ladspaport[c], u->input[c]);
+        for (c = 0; c < u->output_count; c++)
+            d->connect_port(u->handle[h], output_ladspaport[c], u->output[c]);
+    }
+
+    u->n_control = n_control;
+
+    if (u->n_control > 0) {
+        double *control_values;
+        bool *use_default;
+
+        /* temporary storage for parser */
+        control_values = pa_xnew(double, (unsigned) u->n_control);
+        use_default = pa_xnew(bool, (unsigned) u->n_control);
+
+        /* real storage */
+        u->control = pa_xnew(LADSPA_Data, (unsigned) u->n_control);
+        u->use_default = pa_xnew(bool, (unsigned) u->n_control);
+
+        if ((parse_control_parameters(u, cdata, control_values, use_default) < 0) ||
+            (write_control_parameters(u, control_values, use_default) < 0)) {
+            pa_xfree(control_values);
+            pa_xfree(use_default);
+
+            pa_log("Failed to parse, validate or set control parameters");
+
+            goto fail;
+        }
+        connect_control_ports(u);
+        pa_xfree(control_values);
+        pa_xfree(use_default);
+    }
+
+    if (d->activate)
+        for (c = 0; c < (u->channels / u->max_ladspaport_count); c++)
+            d->activate(u->handle[c]);
+
+    /* Create sink */
+    pa_sink_new_data_init(&sink_data);
+    sink_data.driver = __FILE__;
+    sink_data.module = m;
+    if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+        sink_data.name = pa_sprintf_malloc("%s.ladspa", master->name);
+    pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+    pa_sink_new_data_set_channel_map(&sink_data, &map);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+    pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin);
+    pa_proplist_sets(sink_data.proplist, "device.ladspa.label", d->Label);
+    pa_proplist_sets(sink_data.proplist, "device.ladspa.name", d->Name);
+    pa_proplist_sets(sink_data.proplist, "device.ladspa.maker", d->Maker);
+    pa_proplist_sets(sink_data.proplist, "device.ladspa.copyright", d->Copyright);
+    pa_proplist_setf(sink_data.proplist, "device.ladspa.unique_id", "%lu", (unsigned long) d->UniqueID);
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&sink_data);
+        goto fail;
+    }
+
+    u->autoloaded = DEFAULT_AUTOLOADED;
+    if (pa_modargs_get_value_boolean(ma, "autoloaded", &u->autoloaded) < 0) {
+        pa_log("Failed to parse autoloaded value");
+        goto fail;
+    }
+
+    if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+        const char *z;
+
+        z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", d->Name, z ? z : master->name);
+    }
+
+    u->sink = pa_sink_new(m->core, &sink_data,
+                          (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)) | PA_SINK_SHARE_VOLUME_WITH_MASTER);
+    pa_sink_new_data_done(&sink_data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg_cb;
+    u->sink->set_state = sink_set_state_cb;
+    u->sink->update_requested_latency = sink_update_requested_latency_cb;
+    u->sink->request_rewind = sink_request_rewind_cb;
+    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
+
+    /* Create sink input */
+    pa_sink_input_new_data_init(&sink_input_data);
+    sink_input_data.driver = __FILE__;
+    sink_input_data.module = m;
+    pa_sink_input_new_data_set_sink(&sink_input_data, master, false);
+    sink_input_data.origin_sink = u->sink;
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream");
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+    pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+    sink_input_data.flags |= PA_SINK_INPUT_START_CORKED;
+
+    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
+    pa_sink_input_new_data_done(&sink_input_data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->update_max_request = sink_input_update_max_request_cb;
+    u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+    u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->attach = sink_input_attach_cb;
+    u->sink_input->detach = sink_input_detach_cb;
+    u->sink_input->may_move_to = sink_input_may_move_to_cb;
+    u->sink_input->moving = sink_input_moving_cb;
+    u->sink_input->mute_changed = sink_input_mute_changed_cb;
+    u->sink_input->userdata = u;
+
+    u->sink->input_to_master = u->sink_input;
+
+    pa_sink_input_get_silence(u->sink_input, &silence);
+    u->memblockq = pa_memblockq_new("module-ladspa-sink memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, &silence);
+    pa_memblock_unref(silence.memblock);
+
+    /* The order here is important. The input must be put first,
+     * otherwise streams might attach to the sink before the sink
+     * input is attached to the master. */
+    pa_sink_input_put(u->sink_input);
+    pa_sink_put(u->sink);
+    pa_sink_input_cork(u->sink_input, false);
+
+#ifdef HAVE_DBUS
+    dbus_init(u);
+#endif
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_sink_linked_by(u->sink);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    unsigned c;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    /* See comments in sink_input_kill_cb() above regarding
+    * destruction order! */
+
+#ifdef HAVE_DBUS
+    dbus_done(u);
+#endif
+
+    if (u->sink_input)
+        pa_sink_input_cork(u->sink_input, true);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+    }
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    for (c = 0; c < (u->channels / u->max_ladspaport_count); c++) {
+        if (u->handle[c]) {
+            if (u->descriptor->deactivate)
+                u->descriptor->deactivate(u->handle[c]);
+            u->descriptor->cleanup(u->handle[c]);
+        }
+    }
+
+    if (u->output == u->input) {
+        if (u->input != NULL) {
+            for (c = 0; c < u->max_ladspaport_count; c++)
+                pa_xfree(u->input[c]);
+            pa_xfree(u->input);
+        }
+    } else {
+        if (u->input != NULL) {
+            for (c = 0; c < u->input_count; c++)
+                pa_xfree(u->input[c]);
+            pa_xfree(u->input);
+        }
+        if (u->output != NULL) {
+            for (c = 0; c < u->output_count; c++)
+                pa_xfree(u->output[c]);
+            pa_xfree(u->output);
+        }
+    }
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    pa_xfree(u->control);
+    pa_xfree(u->use_default);
+    pa_xfree(u);
+}
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
new file mode 100644 (file)
index 0000000..d63fdbd
--- /dev/null
@@ -0,0 +1,246 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <lirc/lirc_client.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
+
+#include "module-lirc-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("LIRC volume control");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE("config=<config file> sink=<sink name> appname=<lirc application name> volume_limit=<volume limit> volume_step=<volume change step>");
+
+static const char* const valid_modargs[] = {
+    "config",
+    "sink",
+    "appname",
+    "volume_limit",
+    "volume_step",
+    NULL,
+};
+
+struct userdata {
+    int lirc_fd;
+    pa_io_event *io;
+    struct lirc_config *config;
+    char *sink_name;
+    pa_module *module;
+    float mute_toggle_save;
+    pa_volume_t volume_limit;
+    pa_volume_t volume_step;
+};
+
+static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void*userdata) {
+    struct userdata *u = userdata;
+    char *name = NULL, *code = NULL;
+
+    pa_assert(io);
+    pa_assert(u);
+
+    if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
+        pa_log("Lost connection to LIRC daemon.");
+        goto fail;
+    }
+
+    if (events & PA_IO_EVENT_INPUT) {
+        char *c;
+
+        if (lirc_nextcode(&code) != 0 || !code) {
+            pa_log("lirc_nextcode() failed.");
+            goto fail;
+        }
+
+        c = pa_xstrdup(code);
+        c[strcspn(c, "\n\r")] = 0;
+        pa_log_debug("Raw IR code '%s'", c);
+        pa_xfree(c);
+
+        while (lirc_code2char(u->config, code, &name) == 0 && name) {
+            enum {
+                INVALID,
+                UP,
+                DOWN,
+                MUTE,
+                RESET,
+                MUTE_TOGGLE
+            } volchange = INVALID;
+
+            pa_log_info("Translated IR code '%s'", name);
+
+            if (strcasecmp(name, "volume-up") == 0)
+                volchange = UP;
+            else if (strcasecmp(name, "volume-down") == 0)
+                volchange = DOWN;
+            else if (strcasecmp(name, "mute") == 0)
+                volchange = MUTE;
+            else if (strcasecmp(name, "mute-toggle") == 0)
+                volchange = MUTE_TOGGLE;
+            else if (strcasecmp(name, "reset") == 0)
+                volchange = RESET;
+
+            if (volchange == INVALID)
+                pa_log_warn("Received unknown IR code '%s'", name);
+            else {
+                pa_sink *s;
+
+                if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK)))
+                    pa_log("Failed to get sink '%s'", u->sink_name);
+                else {
+                    pa_cvolume cv = *pa_sink_get_volume(s, false);
+
+                    switch (volchange) {
+                        case UP:
+                            pa_cvolume_inc_clamp(&cv, u->volume_step, u->volume_limit);
+                            pa_sink_set_volume(s, &cv, true, true);
+                            break;
+
+                        case DOWN:
+                            pa_cvolume_dec(&cv, u->volume_step);
+                            pa_sink_set_volume(s, &cv, true, true);
+                            break;
+
+                        case MUTE:
+                            pa_sink_set_mute(s, true, true);
+                            break;
+
+                        case RESET:
+                            pa_sink_set_mute(s, false, true);
+                            break;
+
+                        case MUTE_TOGGLE:
+                            pa_sink_set_mute(s, !pa_sink_get_mute(s, false), true);
+                            break;
+
+                        case INVALID:
+                            pa_assert_not_reached();
+                    }
+                }
+            }
+        }
+    }
+
+    pa_xfree(code);
+
+    return;
+
+fail:
+    u->module->core->mainloop->io_free(u->io);
+    u->io = NULL;
+
+    pa_module_unload_request(u->module, true);
+
+    pa_xfree(code);
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    pa_volume_t volume_limit = PA_CLAMP_VOLUME(PA_VOLUME_NORM*3/2);
+    pa_volume_t volume_step = PA_VOLUME_NORM/20;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "volume_limit", &volume_limit) < 0) {
+        pa_log("Failed to parse volume limit");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "volume_step", &volume_step) < 0) {
+        pa_log("Failed to parse volume step");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->module = m;
+    u->io = NULL;
+    u->config = NULL;
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+    u->lirc_fd = -1;
+    u->mute_toggle_save = 0;
+    u->volume_limit = PA_CLAMP_VOLUME(volume_limit);
+    u->volume_step = PA_CLAMP_VOLUME(volume_step);
+
+    if ((u->lirc_fd = lirc_init((char*) pa_modargs_get_value(ma, "appname", "pulseaudio"), 1)) < 0) {
+        pa_log("lirc_init() failed.");
+        goto fail;
+    }
+
+    if (lirc_readconfig((char*) pa_modargs_get_value(ma, "config", NULL), &u->config, NULL) < 0) {
+        pa_log("lirc_readconfig() failed.");
+        goto fail;
+    }
+
+    u->io = m->core->mainloop->io_new(m->core->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->io)
+        m->core->mainloop->io_free(u->io);
+
+    if (u->config)
+        lirc_freeconfig(u->config);
+
+    if (u->lirc_fd >= 0)
+        lirc_deinit();
+
+    pa_xfree(u->sink_name);
+    pa_xfree(u);
+}
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
new file mode 100644 (file)
index 0000000..ded59e1
--- /dev/null
@@ -0,0 +1,1540 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2009 Intel Corporation
+    Contributor: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+
+#include "module-loopback-symdef.h"
+
+PA_MODULE_AUTHOR("Pierre-Louis Bossart");
+PA_MODULE_DESCRIPTION("Loopback from source to sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "source=<source to connect to> "
+        "sink=<sink to connect to> "
+        "adjust_time=<how often to readjust rates in s> "
+        "latency_msec=<latency in ms> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map> "
+        "sink_input_properties=<proplist> "
+        "source_output_properties=<proplist> "
+        "source_dont_move=<boolean> "
+        "sink_dont_move=<boolean> "
+        "remix=<remix channels?> ");
+
+#define DEFAULT_LATENCY_MSEC 200
+
+#define MEMBLOCKQ_MAXLENGTH (1024*1024*32)
+
+#define MIN_DEVICE_LATENCY (2.5*PA_USEC_PER_MSEC)
+
+#define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC)
+
+typedef struct loopback_msg loopback_msg;
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    loopback_msg *msg;
+
+    pa_sink_input *sink_input;
+    pa_source_output *source_output;
+
+    pa_asyncmsgq *asyncmsgq;
+    pa_memblockq *memblockq;
+
+    pa_rtpoll_item *rtpoll_item_read, *rtpoll_item_write;
+
+    pa_time_event *time_event;
+
+    /* Values from command line configuration */
+    pa_usec_t latency;
+    pa_usec_t adjust_time;
+
+    /* Latency boundaries and current values */
+    pa_usec_t min_source_latency;
+    pa_usec_t max_source_latency;
+    pa_usec_t min_sink_latency;
+    pa_usec_t max_sink_latency;
+    pa_usec_t configured_sink_latency;
+    pa_usec_t configured_source_latency;
+    int64_t source_latency_offset;
+    int64_t sink_latency_offset;
+    pa_usec_t minimum_latency;
+
+    /* lower latency limit found by underruns */
+    pa_usec_t underrun_latency_limit;
+
+    /* Various counters */
+    uint32_t iteration_counter;
+    uint32_t underrun_counter;
+
+    bool fixed_alsa_source;
+
+    /* Used for sink input and source output snapshots */
+    struct {
+        int64_t send_counter;
+        pa_usec_t source_latency;
+        pa_usec_t source_timestamp;
+
+        int64_t recv_counter;
+        size_t loopback_memblockq_length;
+        pa_usec_t sink_latency;
+        pa_usec_t sink_timestamp;
+    } latency_snapshot;
+
+    /* Input thread variable */
+    int64_t send_counter;
+
+    /* Output thread variables */
+    struct {
+        int64_t recv_counter;
+
+        /* Copied from main thread */
+        pa_usec_t effective_source_latency;
+        pa_usec_t minimum_latency;
+
+        /* Various booleans */
+        bool in_pop;
+        bool pop_called;
+        bool pop_adjust;
+        bool first_pop_done;
+        bool push_called;
+    } output_thread_info;
+};
+
+struct loopback_msg {
+    pa_msgobject parent;
+    struct userdata *userdata;
+};
+
+PA_DEFINE_PRIVATE_CLASS(loopback_msg, pa_msgobject);
+#define LOOPBACK_MSG(o) (loopback_msg_cast(o))
+
+static const char* const valid_modargs[] = {
+    "source",
+    "sink",
+    "adjust_time",
+    "latency_msec",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "sink_input_properties",
+    "source_output_properties",
+    "source_dont_move",
+    "sink_dont_move",
+    "remix",
+    NULL,
+};
+
+enum {
+    SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX,
+    SINK_INPUT_MESSAGE_REWIND,
+    SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT,
+    SINK_INPUT_MESSAGE_SOURCE_CHANGED,
+    SINK_INPUT_MESSAGE_SET_EFFECTIVE_SOURCE_LATENCY,
+    SINK_INPUT_MESSAGE_UPDATE_MIN_LATENCY,
+};
+
+enum {
+    SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT = PA_SOURCE_OUTPUT_MESSAGE_MAX,
+};
+
+enum {
+    LOOPBACK_MESSAGE_SOURCE_LATENCY_RANGE_CHANGED,
+    LOOPBACK_MESSAGE_SINK_LATENCY_RANGE_CHANGED,
+    LOOPBACK_MESSAGE_UNDERRUN,
+};
+
+static void enable_adjust_timer(struct userdata *u, bool enable);
+
+/* Called from main context */
+static void teardown(struct userdata *u) {
+    pa_assert(u);
+    pa_assert_ctl_context();
+
+    u->adjust_time = 0;
+    enable_adjust_timer(u, false);
+
+    /* Handling the asyncmsgq between the source output and the sink input
+     * requires some care. When the source output is unlinked, nothing needs
+     * to be done for the asyncmsgq, because the source output is the sending
+     * end. But when the sink input is unlinked, we should ensure that the
+     * asyncmsgq is emptied, because the messages in the queue hold references
+     * to the sink input. Also, we need to ensure that new messages won't be
+     * written to the queue after we have emptied it.
+     *
+     * Emptying the queue can be done in the state_changed() callback of the
+     * sink input, when the new state is "unlinked".
+     *
+     * Preventing new messages from being written to the queue can be achieved
+     * by unlinking the source output before unlinking the sink input. There
+     * are no other writers for that queue, so this is sufficient. */
+
+    if (u->source_output) {
+        pa_source_output_unlink(u->source_output);
+        pa_source_output_unref(u->source_output);
+        u->source_output = NULL;
+    }
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+        u->sink_input = NULL;
+    }
+}
+
+/* rate controller, called from main context
+ * - maximum deviation from base rate is less than 1%
+ * - can create audible artifacts by changing the rate too quickly
+ * - exhibits hunting with USB or Bluetooth sources
+ */
+static uint32_t rate_controller(
+                uint32_t base_rate,
+                pa_usec_t adjust_time,
+                int32_t latency_difference_usec) {
+
+    uint32_t new_rate;
+    double min_cycles;
+
+    /* Calculate best rate to correct the current latency offset, limit at
+     * slightly below 1% difference from base_rate */
+    min_cycles = (double)abs(latency_difference_usec) / adjust_time / 0.01 + 1;
+    new_rate = base_rate * (1.0 + (double)latency_difference_usec / min_cycles / adjust_time);
+
+    return new_rate;
+}
+
+/* Called from main thread.
+ * It has been a matter of discussion how to correctly calculate the minimum
+ * latency that module-loopback can deliver with a given source and sink.
+ * The calculation has been placed in a separate function so that the definition
+ * can easily be changed. The resulting estimate is not very exact because it
+ * depends on the reported latency ranges. In cases were the lower bounds of
+ * source and sink latency are not reported correctly (USB) the result will
+ * be wrong. */
+static void update_minimum_latency(struct userdata *u, pa_sink *sink, bool print_msg) {
+
+    if (u->underrun_latency_limit)
+        /* If we already detected a real latency limit because of underruns, use it */
+        u->minimum_latency = u->underrun_latency_limit;
+
+    else {
+        /* Calculate latency limit from latency ranges */
+
+        u->minimum_latency = u->min_sink_latency;
+        if (u->fixed_alsa_source)
+            /* If we are using an alsa source with fixed latency, we will get a wakeup when
+             * one fragment is filled, and then we empty the source buffer, so the source
+             * latency never grows much beyond one fragment (assuming that the CPU doesn't
+             * cause a bottleneck). */
+            u->minimum_latency += u->core->default_fragment_size_msec * PA_USEC_PER_MSEC;
+
+        else
+            /* In all other cases the source will deliver new data at latest after one source latency.
+             * Make sure there is enough data available that the sink can keep on playing until new
+             * data is pushed. */
+            u->minimum_latency += u->min_source_latency;
+
+        /* Multiply by 1.1 as a safety margin for delays that are proportional to the buffer sizes */
+        u->minimum_latency *= 1.1;
+
+        /* Add 1.5 ms as a safety margin for delays not related to the buffer sizes */
+        u->minimum_latency += 1.5 * PA_USEC_PER_MSEC;
+    }
+
+    /* Add the latency offsets */
+    if (-(u->sink_latency_offset + u->source_latency_offset) <= (int64_t)u->minimum_latency)
+        u->minimum_latency += u->sink_latency_offset + u->source_latency_offset;
+    else
+        u->minimum_latency = 0;
+
+    /* If the sink is valid, send a message to update the minimum latency to
+     * the output thread, else set the variable directly */
+    if (sink)
+        pa_asyncmsgq_send(sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_UPDATE_MIN_LATENCY, NULL, u->minimum_latency, NULL);
+    else
+        u->output_thread_info.minimum_latency = u->minimum_latency;
+
+    if (print_msg) {
+        pa_log_info("Minimum possible end to end latency: %0.2f ms", (double)u->minimum_latency / PA_USEC_PER_MSEC);
+        if (u->latency < u->minimum_latency)
+            pa_log_warn("Configured latency of %0.2f ms is smaller than minimum latency, using minimum instead", (double)u->latency / PA_USEC_PER_MSEC);
+    }
+}
+
+/* Called from main context */
+static void adjust_rates(struct userdata *u) {
+    size_t buffer;
+    uint32_t old_rate, base_rate, new_rate, run_hours;
+    int32_t latency_difference;
+    pa_usec_t current_buffer_latency, snapshot_delay, current_source_sink_latency, current_latency, latency_at_optimum_rate;
+    pa_usec_t final_latency;
+
+    pa_assert(u);
+    pa_assert_ctl_context();
+
+    /* Runtime and counters since last change of source or sink
+     * or source/sink latency */
+    run_hours = u->iteration_counter * u->adjust_time / PA_USEC_PER_SEC / 3600;
+    u->iteration_counter +=1;
+
+    /* If we are seeing underruns then the latency is too small */
+    if (u->underrun_counter > 2) {
+        u->underrun_latency_limit = PA_MAX(u->latency, u->minimum_latency) + 5 * PA_USEC_PER_MSEC;
+        u->underrun_latency_limit = PA_CLIP_SUB((int64_t)u->underrun_latency_limit, u->sink_latency_offset + u->source_latency_offset);
+        update_minimum_latency(u, u->sink_input->sink, false);
+        pa_log_warn("Too many underruns, increasing latency to %0.2f ms", (double)u->minimum_latency / PA_USEC_PER_MSEC);
+        u->underrun_counter = 0;
+    }
+
+    /* Allow one underrun per hour */
+    if (u->iteration_counter * u->adjust_time / PA_USEC_PER_SEC / 3600 > run_hours) {
+        u->underrun_counter = PA_CLIP_SUB(u->underrun_counter, 1u);
+        pa_log_info("Underrun counter: %u", u->underrun_counter);
+    }
+
+    /* Rates and latencies*/
+    old_rate = u->sink_input->sample_spec.rate;
+    base_rate = u->source_output->sample_spec.rate;
+
+    buffer = u->latency_snapshot.loopback_memblockq_length;
+    if (u->latency_snapshot.recv_counter <= u->latency_snapshot.send_counter)
+        buffer += (size_t) (u->latency_snapshot.send_counter - u->latency_snapshot.recv_counter);
+    else
+        buffer = PA_CLIP_SUB(buffer, (size_t) (u->latency_snapshot.recv_counter - u->latency_snapshot.send_counter));
+
+    current_buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec);
+    snapshot_delay = u->latency_snapshot.source_timestamp - u->latency_snapshot.sink_timestamp;
+    current_source_sink_latency = u->latency_snapshot.sink_latency + u->latency_snapshot.source_latency - snapshot_delay;
+
+    /* Current latency */
+    current_latency = current_source_sink_latency + current_buffer_latency;
+
+    /* Latency at base rate */
+    latency_at_optimum_rate = current_source_sink_latency + current_buffer_latency * old_rate / base_rate;
+
+    final_latency = PA_MAX(u->latency, u->minimum_latency);
+    latency_difference = (int32_t)((int64_t)latency_at_optimum_rate - final_latency);
+
+    pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms",
+                (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC,
+                (double) current_buffer_latency / PA_USEC_PER_MSEC,
+                (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC,
+                (double) current_latency / PA_USEC_PER_MSEC);
+
+    pa_log_debug("Loopback latency at base rate is %0.2f ms", (double)latency_at_optimum_rate / PA_USEC_PER_MSEC);
+
+    /* Calculate new rate */
+    new_rate = rate_controller(base_rate, u->adjust_time, latency_difference);
+
+    /* Set rate */
+    pa_sink_input_set_rate(u->sink_input, new_rate);
+    pa_log_debug("[%s] Updated sampling rate to %lu Hz.", u->sink_input->sink->name, (unsigned long) new_rate);
+}
+
+/* Called from main context */
+static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+    pa_assert(a);
+    pa_assert(u->time_event == e);
+
+    /* Restart timer right away */
+    pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time);
+
+    /* Get sink and source latency snapshot */
+    pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL);
+    pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL);
+
+    adjust_rates(u);
+}
+
+/* Called from main context
+ * When source or sink changes, give it a third of a second to settle down, then call adjust_rates for the first time */
+static void enable_adjust_timer(struct userdata *u, bool enable) {
+    if (enable) {
+        if (!u->adjust_time)
+            return;
+        if (u->time_event)
+            u->core->mainloop->time_free(u->time_event);
+
+        u->time_event = pa_core_rttime_new(u->module->core, pa_rtclock_now() + 333 * PA_USEC_PER_MSEC, time_callback, u);
+    } else {
+        if (!u->time_event)
+            return;
+
+        u->core->mainloop->time_free(u->time_event);
+        u->time_event = NULL;
+    }
+}
+
+/* Called from main context */
+static void update_adjust_timer(struct userdata *u) {
+    if (u->sink_input->state == PA_SINK_INPUT_CORKED || u->source_output->state == PA_SOURCE_OUTPUT_CORKED)
+        enable_adjust_timer(u, false);
+    else
+        enable_adjust_timer(u, true);
+}
+
+/* Called from main thread
+ * Calculates minimum and maximum possible latency for source and sink */
+static void update_latency_boundaries(struct userdata *u, pa_source *source, pa_sink *sink) {
+    const char *s;
+
+    if (source) {
+        /* Source latencies */
+        u->fixed_alsa_source = false;
+        if (source->flags & PA_SOURCE_DYNAMIC_LATENCY)
+            pa_source_get_latency_range(source, &u->min_source_latency, &u->max_source_latency);
+        else {
+            u->min_source_latency = pa_source_get_fixed_latency(source);
+            u->max_source_latency = u->min_source_latency;
+            if ((s = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_API))) {
+                if (pa_streq(s, "alsa"))
+                    u->fixed_alsa_source = true;
+            }
+        }
+        /* Source offset */
+        u->source_latency_offset = source->port_latency_offset;
+
+        /* Latencies below 2.5 ms cause problems, limit source latency if possible */
+        if (u->max_source_latency >= MIN_DEVICE_LATENCY)
+            u->min_source_latency = PA_MAX(u->min_source_latency, MIN_DEVICE_LATENCY);
+        else
+            u->min_source_latency = u->max_source_latency;
+    }
+
+    if (sink) {
+        /* Sink latencies */
+        if (sink->flags & PA_SINK_DYNAMIC_LATENCY)
+            pa_sink_get_latency_range(sink, &u->min_sink_latency, &u->max_sink_latency);
+        else {
+            u->min_sink_latency = pa_sink_get_fixed_latency(sink);
+            u->max_sink_latency = u->min_sink_latency;
+        }
+        /* Sink offset */
+        u->sink_latency_offset = sink->port_latency_offset;
+
+        /* Latencies below 2.5 ms cause problems, limit sink latency if possible */
+        if (u->max_sink_latency >= MIN_DEVICE_LATENCY)
+            u->min_sink_latency = PA_MAX(u->min_sink_latency, MIN_DEVICE_LATENCY);
+        else
+            u->min_sink_latency = u->max_sink_latency;
+    }
+
+    update_minimum_latency(u, sink, true);
+}
+
+/* Called from output context
+ * Sets the memblockq to the configured latency corrected by latency_offset_usec */
+static void memblockq_adjust(struct userdata *u, pa_usec_t latency_offset_usec, bool allow_push) {
+    size_t current_memblockq_length, requested_memblockq_length, buffer_correction;
+    pa_usec_t requested_buffer_latency, final_latency;
+
+    final_latency = PA_MAX(u->latency, u->output_thread_info.minimum_latency);
+    requested_buffer_latency = PA_CLIP_SUB(final_latency, latency_offset_usec);
+    requested_memblockq_length = pa_usec_to_bytes(requested_buffer_latency, &u->sink_input->sample_spec);
+    current_memblockq_length = pa_memblockq_get_length(u->memblockq);
+
+    if (current_memblockq_length > requested_memblockq_length) {
+        /* Drop audio from queue */
+        buffer_correction = current_memblockq_length - requested_memblockq_length;
+        pa_log_info("Dropping %lu usec of audio from queue", pa_bytes_to_usec(buffer_correction, &u->sink_input->sample_spec));
+        pa_memblockq_drop(u->memblockq, buffer_correction);
+
+    } else if (current_memblockq_length < requested_memblockq_length && allow_push) {
+        /* Add silence to queue */
+        buffer_correction = requested_memblockq_length - current_memblockq_length;
+        pa_log_info("Adding %lu usec of silence to queue", pa_bytes_to_usec(buffer_correction, &u->sink_input->sample_spec));
+        pa_memblockq_seek(u->memblockq, (int64_t)buffer_correction, PA_SEEK_RELATIVE, true);
+    }
+}
+
+/* Called from input thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    struct userdata *u;
+    pa_usec_t push_time, current_source_latency;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    /* Send current source latency and timestamp with the message */
+    push_time = pa_rtclock_now();
+    current_source_latency = pa_source_get_latency_within_thread(u->source_output->source, false);
+
+    pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_POST, PA_UINT_TO_PTR(current_source_latency), push_time, chunk, NULL);
+    u->send_counter += (int64_t) chunk->length;
+}
+
+/* Called from input thread context */
+static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL);
+    u->send_counter -= (int64_t) nbytes;
+}
+
+/* Called from input thread context */
+static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE_OUTPUT(obj)->userdata;
+
+    switch (code) {
+
+        case SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT: {
+            size_t length;
+
+            length = pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq);
+
+            u->latency_snapshot.send_counter = u->send_counter;
+            /* Add content of delay memblockq to the source latency */
+            u->latency_snapshot.source_latency = pa_source_get_latency_within_thread(u->source_output->source, false) +
+                                                 pa_bytes_to_usec(length, &u->source_output->source->sample_spec);
+            u->latency_snapshot.source_timestamp = pa_rtclock_now();
+
+            return 0;
+        }
+    }
+
+    return pa_source_output_process_msg(obj, code, data, offset, chunk);
+}
+
+/* Called from main thread.
+ * Get current effective latency of the source. If the source is in use with
+ * smaller latency than the configured latency, it will continue running with
+ * the smaller value when the source output is switched to the source. */
+static void update_effective_source_latency(struct userdata *u, pa_source *source, pa_sink *sink) {
+    pa_usec_t effective_source_latency;
+
+    effective_source_latency = u->configured_source_latency;
+
+    if (source) {
+        effective_source_latency = pa_source_get_requested_latency(source);
+        if (effective_source_latency == 0 || effective_source_latency > u->configured_source_latency)
+            effective_source_latency = u->configured_source_latency;
+    }
+
+    /* If the sink is valid, send a message to the output thread, else set the variable directly */
+    if (sink)
+        pa_asyncmsgq_send(sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_SET_EFFECTIVE_SOURCE_LATENCY, NULL, (int64_t)effective_source_latency, NULL);
+    else
+       u->output_thread_info.effective_source_latency = effective_source_latency;
+}
+
+/* Called from main thread.
+ * Set source output latency to one third of the overall latency if possible.
+ * The choice of one third is rather arbitrary somewhere between the minimum
+ * possible latency which would cause a lot of CPU load and half the configured
+ * latency which would quickly lead to underruns */
+static void set_source_output_latency(struct userdata *u, pa_source *source) {
+    pa_usec_t latency, requested_latency;
+
+    requested_latency = u->latency / 3;
+
+    /* Normally we try to configure sink and source latency equally. If the
+     * sink latency cannot match the requested source latency try to set the
+     * source latency to a smaller value to avoid underruns */
+    if (u->min_sink_latency > requested_latency) {
+        latency = PA_MAX(u->latency, u->minimum_latency);
+        requested_latency = (latency - u->min_sink_latency) / 2;
+    }
+
+    latency = PA_CLAMP(requested_latency , u->min_source_latency, u->max_source_latency);
+    u->configured_source_latency = pa_source_output_set_requested_latency(u->source_output, latency);
+    if (u->configured_source_latency != requested_latency)
+        pa_log_warn("Cannot set requested source latency of %0.2f ms, adjusting to %0.2f ms", (double)requested_latency / PA_USEC_PER_MSEC, (double)u->configured_source_latency / PA_USEC_PER_MSEC);
+}
+
+/* Called from input thread context */
+static void source_output_attach_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    u->rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+            o->source->thread_info.rtpoll,
+            PA_RTPOLL_LATE,
+            u->asyncmsgq);
+}
+
+/* Called from input thread context */
+static void source_output_detach_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    if (u->rtpoll_item_write) {
+        pa_rtpoll_item_free(u->rtpoll_item_write);
+        u->rtpoll_item_write = NULL;
+    }
+}
+
+/* Called from main thread */
+static void source_output_kill_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert_se(u = o->userdata);
+
+    teardown(u);
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main thread */
+static bool source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert_se(u = o->userdata);
+
+    if (!u->sink_input || !u->sink_input->sink)
+        return true;
+
+    return dest != u->sink_input->sink->monitor_source;
+}
+
+/* Called from main thread */
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
+    struct userdata *u;
+    char *input_description;
+    const char *n;
+
+    if (!dest)
+        return;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert_se(u = o->userdata);
+
+    input_description = pa_sprintf_malloc("Loopback of %s",
+                                          pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+    pa_sink_input_set_property(u->sink_input, PA_PROP_MEDIA_NAME, input_description);
+    pa_xfree(input_description);
+
+    if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
+        pa_sink_input_set_property(u->sink_input, PA_PROP_DEVICE_ICON_NAME, n);
+
+    /* Set latency and calculate latency limits */
+    u->underrun_latency_limit = 0;
+    update_latency_boundaries(u, dest, u->sink_input->sink);
+    set_source_output_latency(u, dest);
+    update_effective_source_latency(u, dest, u->sink_input->sink);
+
+    /* Uncork the sink input unless the destination is suspended for other
+     * reasons than idle. */
+    if (pa_source_get_state(dest) == PA_SOURCE_SUSPENDED)
+        pa_sink_input_cork(u->sink_input, (dest->suspend_cause != PA_SUSPEND_IDLE));
+    else
+        pa_sink_input_cork(u->sink_input, false);
+
+    update_adjust_timer(u);
+
+    /* Reset counters */
+    u->iteration_counter = 0;
+    u->underrun_counter = 0;
+
+    /* Send a mesage to the output thread that the source has changed.
+     * If the sink is invalid here during a profile switching situation
+     * we can safely set push_called to false directly. */
+    if (u->sink_input->sink)
+        pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_SOURCE_CHANGED, NULL, 0, NULL);
+    else
+        u->output_thread_info.push_called = false;
+
+    /* The sampling rate may be far away from the default rate if we are still
+     * recovering from a previous source or sink change, so reset rate to
+     * default before moving the source. */
+    pa_sink_input_set_rate(u->sink_input, u->source_output->sample_spec.rate);
+}
+
+/* Called from main thread */
+static void source_output_suspend_cb(pa_source_output *o, bool suspended) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert_se(u = o->userdata);
+
+    /* If the source has been suspended, we need to handle this like
+     * a source change when the source is resumed */
+    if (suspended) {
+        if (u->sink_input->sink)
+            pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_SOURCE_CHANGED, NULL, 0, NULL);
+        else
+            u->output_thread_info.push_called = false;
+
+    } else
+        /* Get effective source latency on unsuspend */
+        update_effective_source_latency(u, u->source_output->source, u->sink_input->sink);
+
+    pa_sink_input_cork(u->sink_input, suspended);
+
+    update_adjust_timer(u);
+}
+
+/* Called from input thread context */
+static void update_source_latency_range_cb(pa_source_output *i) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(i);
+    pa_source_output_assert_io_context(i);
+    pa_assert_se(u = i->userdata);
+
+    /* Source latency may have changed */
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), LOOPBACK_MESSAGE_SOURCE_LATENCY_RANGE_CHANGED, NULL, 0, NULL, NULL);
+}
+
+/* Called from output thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert_se(u = i->userdata);
+    pa_assert(chunk);
+
+    /* It seems necessary to handle outstanding push messages here, though it is not clear
+     * why. Removing this part leads to underruns when low latencies are configured. */
+    u->output_thread_info.in_pop = true;
+    while (pa_asyncmsgq_process_one(u->asyncmsgq) > 0)
+        ;
+    u->output_thread_info.in_pop = false;
+
+    /* While pop has not been called, latency adjustments in SINK_INPUT_MESSAGE_POST are
+     * enabled. Disable them on second pop and enable the final adjustment during the
+     * next push. The adjustment must be done on the next push, because there is no way
+     * to retrieve the source latency here. We are waiting for the second pop, because
+     * the first pop may be called before the sink is actually started. */
+    if (!u->output_thread_info.pop_called && u->output_thread_info.first_pop_done) {
+        u->output_thread_info.pop_adjust = true;
+        u->output_thread_info.pop_called = true;
+    }
+    u->output_thread_info.first_pop_done = true;
+
+    if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
+        pa_log_info("Could not peek into queue");
+        return -1;
+    }
+
+    chunk->length = PA_MIN(chunk->length, nbytes);
+    pa_memblockq_drop(u->memblockq, chunk->length);
+
+    /* Adjust the memblockq to ensure that there is
+     * enough data in the queue to avoid underruns. */
+    if (!u->output_thread_info.push_called)
+        memblockq_adjust(u, 0, true);
+
+    return 0;
+}
+
+/* Called from output thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+/* Called from output thread context */
+static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK_INPUT(obj)->userdata;
+
+    pa_sink_input_assert_io_context(u->sink_input);
+
+    switch (code) {
+
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = data;
+
+            *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+            break;
+        }
+
+        case SINK_INPUT_MESSAGE_POST:
+
+            pa_memblockq_push_align(u->memblockq, chunk);
+
+            /* If push has not been called yet, latency adjustments in sink_input_pop_cb()
+             * are enabled. Disable them on first push and correct the memblockq. If pop
+             * has not been called yet, wait until the pop_cb() requests the adjustment */
+            if (u->output_thread_info.pop_called && (!u->output_thread_info.push_called || u->output_thread_info.pop_adjust)) {
+                pa_usec_t time_delta;
+
+                /* This is the source latency at the time push was called */
+                time_delta = PA_PTR_TO_UINT(data);
+                /* Add the time between push and post */
+                time_delta += pa_rtclock_now() - (pa_usec_t) offset;
+                /* Add the sink latency */
+                time_delta += pa_sink_get_latency_within_thread(u->sink_input->sink, false);
+
+                /* The source latency report includes the audio in the chunk,
+                 * but since we already pushed the chunk to the memblockq, we need
+                 * to subtract the chunk size from the source latency so that it
+                 * won't be counted towards both the memblockq latency and the
+                 * source latency.
+                 *
+                 * Sometimes the alsa source reports way too low latency (might
+                 * be a bug in the alsa source code). This seems to happen when
+                 * there's an overrun. As an attempt to detect overruns, we
+                 * check if the chunk size is larger than the configured source
+                 * latency. If so, we assume that the source should have pushed
+                 * a chunk whose size equals the configured latency, so we
+                 * modify time_delta only by that amount, which makes
+                 * memblockq_adjust() drop more data than it would otherwise.
+                 * This seems to work quite well, but it's possible that the
+                 * next push also contains too much data, and in that case the
+                 * resulting latency will be wrong. */
+                if (pa_bytes_to_usec(chunk->length, &u->sink_input->sample_spec) > u->output_thread_info.effective_source_latency)
+                    time_delta = PA_CLIP_SUB(time_delta, u->output_thread_info.effective_source_latency);
+                else
+                    time_delta = PA_CLIP_SUB(time_delta, pa_bytes_to_usec(chunk->length, &u->sink_input->sample_spec));
+
+                /* FIXME: We allow pushing silence here to fix up the latency. This
+                 * might lead to a gap in the stream */
+                memblockq_adjust(u, time_delta, true);
+
+                u->output_thread_info.pop_adjust = false;
+                u->output_thread_info.push_called = true;
+            }
+
+            /* If pop has not been called yet, make sure the latency does not grow too much.
+             * Don't push any silence here, because we already have new data in the queue */
+            if (!u->output_thread_info.pop_called)
+                 memblockq_adjust(u, 0, false);
+
+            /* Is this the end of an underrun? Then let's start things
+             * right-away */
+            if (u->sink_input->sink->thread_info.state != PA_SINK_SUSPENDED &&
+                u->sink_input->thread_info.underrun_for > 0 &&
+                pa_memblockq_is_readable(u->memblockq)) {
+
+                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), LOOPBACK_MESSAGE_UNDERRUN, NULL, 0, NULL, NULL);
+                /* If called from within the pop callback skip the rewind */
+                if (!u->output_thread_info.in_pop) {
+                    pa_log_debug("Requesting rewind due to end of underrun.");
+                    pa_sink_input_request_rewind(u->sink_input,
+                                                 (size_t) (u->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : u->sink_input->thread_info.underrun_for),
+                                                 false, true, false);
+                }
+            }
+
+            u->output_thread_info.recv_counter += (int64_t) chunk->length;
+
+            return 0;
+
+        case SINK_INPUT_MESSAGE_REWIND:
+
+            /* Do not try to rewind if no data was pushed yet */
+            if (u->output_thread_info.push_called)
+                pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, true);
+
+            u->output_thread_info.recv_counter -= offset;
+
+            return 0;
+
+        case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: {
+            size_t length;
+
+            length = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq);
+
+            u->latency_snapshot.recv_counter = u->output_thread_info.recv_counter;
+            u->latency_snapshot.loopback_memblockq_length = pa_memblockq_get_length(u->memblockq);
+            /* Add content of render memblockq to sink latency */
+            u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink, false) +
+                                               pa_bytes_to_usec(length, &u->sink_input->sink->sample_spec);
+            u->latency_snapshot.sink_timestamp = pa_rtclock_now();
+
+            return 0;
+        }
+
+        case SINK_INPUT_MESSAGE_SOURCE_CHANGED:
+
+            u->output_thread_info.push_called = false;
+
+            return 0;
+
+        case SINK_INPUT_MESSAGE_SET_EFFECTIVE_SOURCE_LATENCY:
+
+            u->output_thread_info.effective_source_latency = (pa_usec_t)offset;
+
+            return 0;
+
+        case SINK_INPUT_MESSAGE_UPDATE_MIN_LATENCY:
+
+            u->output_thread_info.minimum_latency = (pa_usec_t)offset;
+
+            return 0;
+    }
+
+    return pa_sink_input_process_msg(obj, code, data, offset, chunk);
+}
+/* Called from main thread.
+ * Set sink input latency to one third of the overall latency if possible.
+ * The choice of one third is rather arbitrary somewhere between the minimum
+ * possible latency which would cause a lot of CPU load and half the configured
+ * latency which would quickly lead to underruns. */
+static void set_sink_input_latency(struct userdata *u, pa_sink *sink) {
+     pa_usec_t latency, requested_latency;
+
+    requested_latency = u->latency / 3;
+
+    /* Normally we try to configure sink and source latency equally. If the
+     * source latency cannot match the requested sink latency try to set the
+     * sink latency to a smaller value to avoid underruns */
+    if (u->min_source_latency > requested_latency) {
+        latency = PA_MAX(u->latency, u->minimum_latency);
+        requested_latency = (latency - u->min_source_latency) / 2;
+    }
+
+    latency = PA_CLAMP(requested_latency , u->min_sink_latency, u->max_sink_latency);
+    u->configured_sink_latency = pa_sink_input_set_requested_latency(u->sink_input, latency);
+    if (u->configured_sink_latency != requested_latency)
+        pa_log_warn("Cannot set requested sink latency of %0.2f ms, adjusting to %0.2f ms", (double)requested_latency / PA_USEC_PER_MSEC, (double)u->configured_sink_latency / PA_USEC_PER_MSEC);
+}
+
+/* Called from output thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert_se(u = i->userdata);
+
+    u->rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
+            i->sink->thread_info.rtpoll,
+            PA_RTPOLL_LATE,
+            u->asyncmsgq);
+
+    pa_memblockq_set_prebuf(u->memblockq, pa_sink_input_get_max_request(i)*2);
+    pa_memblockq_set_maxrewind(u->memblockq, pa_sink_input_get_max_rewind(i));
+}
+
+/* Called from output thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert_se(u = i->userdata);
+
+    if (u->rtpoll_item_read) {
+        pa_rtpoll_item_free(u->rtpoll_item_read);
+        u->rtpoll_item_read = NULL;
+    }
+}
+
+/* Called from output thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+}
+
+/* Called from output thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_memblockq_set_prebuf(u->memblockq, nbytes*2);
+    pa_log_info("Max request changed");
+}
+
+/* Called from main thread */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert_se(u = i->userdata);
+
+    teardown(u);
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from the output thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (state == PA_SINK_INPUT_UNLINKED)
+        pa_asyncmsgq_flush(u->asyncmsgq, false);
+}
+
+/* Called from main thread */
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+    char *output_description;
+    const char *n;
+
+    if (!dest)
+        return;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert_se(u = i->userdata);
+
+    output_description = pa_sprintf_malloc("Loopback to %s",
+                                           pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+    pa_source_output_set_property(u->source_output, PA_PROP_MEDIA_NAME, output_description);
+    pa_xfree(output_description);
+
+    if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
+        pa_source_output_set_property(u->source_output, PA_PROP_MEDIA_ICON_NAME, n);
+
+    /* Set latency and calculate latency limits */
+    u->underrun_latency_limit = 0;
+    update_latency_boundaries(u, NULL, dest);
+    set_sink_input_latency(u, dest);
+    update_effective_source_latency(u, u->source_output->source, dest);
+
+    /* Uncork the source output unless the destination is suspended for other
+     * reasons than idle */
+    if (pa_sink_get_state(dest) == PA_SINK_SUSPENDED)
+        pa_source_output_cork(u->source_output, (dest->suspend_cause != PA_SUSPEND_IDLE));
+    else
+        pa_source_output_cork(u->source_output, false);
+
+    update_adjust_timer(u);
+
+    /* Reset counters */
+    u->iteration_counter = 0;
+    u->underrun_counter = 0;
+
+    u->output_thread_info.pop_called = false;
+    u->output_thread_info.first_pop_done = false;
+
+    /* Sample rate may be far away from the default rate if we are still
+     * recovering from a previous source or sink change, so reset rate to
+     * default before moving the sink. */
+    pa_sink_input_set_rate(u->sink_input, u->source_output->sample_spec.rate);
+}
+
+/* Called from main thread */
+static bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert_se(u = i->userdata);
+
+    if (!u->source_output || !u->source_output->source)
+        return true;
+
+    return dest != u->source_output->source->monitor_of;
+}
+
+/* Called from main thread */
+static void sink_input_suspend_cb(pa_sink_input *i, bool suspended) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert_se(u = i->userdata);
+
+    /* If the sink has been suspended, we need to handle this like
+     * a sink change when the sink is resumed. Because the sink
+     * is suspended, we can set the variables directly. */
+    if (suspended) {
+        u->output_thread_info.pop_called = false;
+        u->output_thread_info.first_pop_done = false;
+
+    } else
+        /* Set effective source latency on unsuspend */
+        update_effective_source_latency(u, u->source_output->source, u->sink_input->sink);
+
+    pa_source_output_cork(u->source_output, suspended);
+
+    update_adjust_timer(u);
+}
+
+/* Called from output thread context */
+static void update_sink_latency_range_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert_se(u = i->userdata);
+
+    /* Sink latency may have changed */
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), LOOPBACK_MESSAGE_SINK_LATENCY_RANGE_CHANGED, NULL, 0, NULL, NULL);
+}
+
+/* Called from main context */
+static int loopback_process_msg_cb(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    struct loopback_msg *msg;
+    struct userdata *u;
+    pa_usec_t current_latency;
+
+    pa_assert(o);
+    pa_assert_ctl_context();
+
+    msg = LOOPBACK_MSG(o);
+    pa_assert_se(u = msg->userdata);
+
+    switch (code) {
+
+        case LOOPBACK_MESSAGE_SOURCE_LATENCY_RANGE_CHANGED:
+
+            update_effective_source_latency(u, u->source_output->source, u->sink_input->sink);
+            current_latency = pa_source_get_requested_latency(u->source_output->source);
+            if (current_latency > u->configured_source_latency) {
+                /* The minimum latency has changed to a value larger than the configured latency, so
+                 * the source latency has been increased. The case that the minimum latency changes
+                 * back to a smaller value is not handled because this never happens with the current
+                 * source implementations. */
+                pa_log_warn("Source minimum latency increased to %0.2f ms", (double)current_latency / PA_USEC_PER_MSEC);
+                u->configured_source_latency = current_latency;
+                update_latency_boundaries(u, u->source_output->source, u->sink_input->sink);
+                /* We re-start counting when the latency has changed */
+                u->iteration_counter = 0;
+                u->underrun_counter = 0;
+            }
+
+            return 0;
+
+        case LOOPBACK_MESSAGE_SINK_LATENCY_RANGE_CHANGED:
+
+            current_latency = pa_sink_get_requested_latency(u->sink_input->sink);
+            if (current_latency > u->configured_sink_latency) {
+                /* The minimum latency has changed to a value larger than the configured latency, so
+                 * the sink latency has been increased. The case that the minimum latency changes back
+                 * to a smaller value is not handled because this never happens with the current sink
+                 * implementations. */
+                pa_log_warn("Sink minimum latency increased to %0.2f ms", (double)current_latency / PA_USEC_PER_MSEC);
+                u->configured_sink_latency = current_latency;
+                update_latency_boundaries(u, u->source_output->source, u->sink_input->sink);
+                /* We re-start counting when the latency has changed */
+                u->iteration_counter = 0;
+                u->underrun_counter = 0;
+            }
+
+            return 0;
+
+        case LOOPBACK_MESSAGE_UNDERRUN:
+
+            u->underrun_counter++;
+            pa_log_debug("Underrun detected, counter incremented to %u", u->underrun_counter);
+
+            return 0;
+
+    }
+
+    return 0;
+}
+
+static pa_hook_result_t sink_port_latency_offset_changed_cb(pa_core *core, pa_sink *sink, struct userdata *u) {
+
+    if (sink != u->sink_input->sink)
+        return PA_HOOK_OK;
+
+    u->sink_latency_offset = sink->port_latency_offset;
+    update_minimum_latency(u, sink, true);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_port_latency_offset_changed_cb(pa_core *core, pa_source *source, struct userdata *u) {
+
+    if (source != u->source_output->source)
+        return PA_HOOK_OK;
+
+    u->source_latency_offset = source->port_latency_offset;
+    update_minimum_latency(u, u->sink_input->sink, true);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module *m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    pa_sink *sink = NULL;
+    pa_sink_input_new_data sink_input_data;
+    bool sink_dont_move;
+    pa_source *source = NULL;
+    pa_source_output_new_data source_output_data;
+    bool source_dont_move;
+    uint32_t latency_msec;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    bool format_set = false;
+    bool rate_set = false;
+    bool channels_set = false;
+    pa_memchunk silence;
+    uint32_t adjust_time_sec;
+    const char *n;
+    bool remix = true;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    n = pa_modargs_get_value(ma, "source", NULL);
+    if (n && !(source = pa_namereg_get(m->core, n, PA_NAMEREG_SOURCE))) {
+        pa_log("No such source.");
+        goto fail;
+    }
+
+    n = pa_modargs_get_value(ma, "sink", NULL);
+    if (n && !(sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK))) {
+        pa_log("No such sink.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
+        pa_log("Invalid boolean remix parameter");
+        goto fail;
+    }
+
+    if (sink) {
+        ss = sink->sample_spec;
+        map = sink->channel_map;
+        format_set = true;
+        rate_set = true;
+        channels_set = true;
+    } else if (source) {
+        ss = source->sample_spec;
+        map = source->channel_map;
+        format_set = true;
+        rate_set = true;
+        channels_set = true;
+    } else {
+        /* FIXME: Dummy stream format, needed because pa_sink_input_new()
+         * requires valid sample spec and channel map even when all the FIX_*
+         * stream flags are specified. pa_sink_input_new() should be changed
+         * to ignore the sample spec and channel map when the FIX_* flags are
+         * present. */
+        ss.format = PA_SAMPLE_U8;
+        ss.rate = 8000;
+        ss.channels = 1;
+        map.channels = 1;
+        map.map[0] = PA_CHANNEL_POSITION_MONO;
+    }
+
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    if (ss.rate < 4000 || ss.rate > PA_RATE_MAX) {
+        pa_log("Invalid rate specification, valid range is 4000 Hz to %i Hz", PA_RATE_MAX);
+        goto fail;
+    }
+
+    if (pa_modargs_get_value(ma, "format", NULL))
+        format_set = true;
+
+    if (pa_modargs_get_value(ma, "rate", NULL))
+        rate_set = true;
+
+    if (pa_modargs_get_value(ma, "channels", NULL) || pa_modargs_get_value(ma, "channel_map", NULL))
+        channels_set = true;
+
+    latency_msec = DEFAULT_LATENCY_MSEC;
+    if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 30000) {
+        pa_log("Invalid latency specification");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC;
+    u->output_thread_info.pop_called = false;
+    u->output_thread_info.pop_adjust = false;
+    u->output_thread_info.push_called = false;
+    u->iteration_counter = 0;
+    u->underrun_counter = 0;
+    u->underrun_latency_limit = 0;
+
+    adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
+    if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
+        pa_log("Failed to parse adjust_time value");
+        goto fail;
+    }
+
+    if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC)
+        u->adjust_time = adjust_time_sec * PA_USEC_PER_SEC;
+    else
+        u->adjust_time = DEFAULT_ADJUST_TIME_USEC;
+
+    pa_sink_input_new_data_init(&sink_input_data);
+    sink_input_data.driver = __FILE__;
+    sink_input_data.module = m;
+
+    if (sink)
+        pa_sink_input_new_data_set_sink(&sink_input_data, sink, false);
+
+    if (pa_modargs_get_proplist(ma, "sink_input_properties", sink_input_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Failed to parse the sink_input_properties value.");
+        pa_sink_input_new_data_done(&sink_input_data);
+        goto fail;
+    }
+
+    if (!pa_proplist_contains(sink_input_data.proplist, PA_PROP_MEDIA_ROLE))
+        pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
+
+    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+    pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+    sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE | PA_SINK_INPUT_START_CORKED;
+
+    if (!remix)
+        sink_input_data.flags |= PA_SINK_INPUT_NO_REMIX;
+
+    if (!format_set)
+        sink_input_data.flags |= PA_SINK_INPUT_FIX_FORMAT;
+
+    if (!rate_set)
+        sink_input_data.flags |= PA_SINK_INPUT_FIX_RATE;
+
+    if (!channels_set)
+        sink_input_data.flags |= PA_SINK_INPUT_FIX_CHANNELS;
+
+    sink_dont_move = false;
+    if (pa_modargs_get_value_boolean(ma, "sink_dont_move", &sink_dont_move) < 0) {
+        pa_log("sink_dont_move= expects a boolean argument.");
+        goto fail;
+    }
+
+    if (sink_dont_move)
+        sink_input_data.flags |= PA_SINK_INPUT_DONT_MOVE;
+
+    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
+    pa_sink_input_new_data_done(&sink_input_data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    /* If format, rate or channels were originally unset, they are set now
+     * after the pa_sink_input_new() call. */
+    ss = u->sink_input->sample_spec;
+    map = u->sink_input->channel_map;
+
+    u->sink_input->parent.process_msg = sink_input_process_msg_cb;
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
+    u->sink_input->attach = sink_input_attach_cb;
+    u->sink_input->detach = sink_input_detach_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->update_max_request = sink_input_update_max_request_cb;
+    u->sink_input->may_move_to = sink_input_may_move_to_cb;
+    u->sink_input->moving = sink_input_moving_cb;
+    u->sink_input->suspend = sink_input_suspend_cb;
+    u->sink_input->update_sink_latency_range = update_sink_latency_range_cb;
+    u->sink_input->update_sink_fixed_latency = update_sink_latency_range_cb;
+    u->sink_input->userdata = u;
+
+    pa_source_output_new_data_init(&source_output_data);
+    source_output_data.driver = __FILE__;
+    source_output_data.module = m;
+    if (source)
+        pa_source_output_new_data_set_source(&source_output_data, source, false);
+
+    if (pa_modargs_get_proplist(ma, "source_output_properties", source_output_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Failed to parse the source_output_properties value.");
+        pa_source_output_new_data_done(&source_output_data);
+        goto fail;
+    }
+
+    if (!pa_proplist_contains(source_output_data.proplist, PA_PROP_MEDIA_ROLE))
+        pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
+
+    pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
+    pa_source_output_new_data_set_channel_map(&source_output_data, &map);
+    source_output_data.flags = PA_SOURCE_OUTPUT_START_CORKED;
+
+    if (!remix)
+        source_output_data.flags |= PA_SOURCE_OUTPUT_NO_REMIX;
+
+    source_dont_move = false;
+    if (pa_modargs_get_value_boolean(ma, "source_dont_move", &source_dont_move) < 0) {
+        pa_log("source_dont_move= expects a boolean argument.");
+        goto fail;
+    }
+
+    if (source_dont_move)
+        source_output_data.flags |= PA_SOURCE_OUTPUT_DONT_MOVE;
+
+    pa_source_output_new(&u->source_output, m->core, &source_output_data);
+    pa_source_output_new_data_done(&source_output_data);
+
+    if (!u->source_output)
+        goto fail;
+
+    u->source_output->parent.process_msg = source_output_process_msg_cb;
+    u->source_output->push = source_output_push_cb;
+    u->source_output->process_rewind = source_output_process_rewind_cb;
+    u->source_output->kill = source_output_kill_cb;
+    u->source_output->attach = source_output_attach_cb;
+    u->source_output->detach = source_output_detach_cb;
+    u->source_output->may_move_to = source_output_may_move_to_cb;
+    u->source_output->moving = source_output_moving_cb;
+    u->source_output->suspend = source_output_suspend_cb;
+    u->source_output->update_source_latency_range = update_source_latency_range_cb;
+    u->source_output->update_source_fixed_latency = update_source_latency_range_cb;
+    u->source_output->userdata = u;
+
+    update_latency_boundaries(u, u->source_output->source, u->sink_input->sink);
+    set_sink_input_latency(u, u->sink_input->sink);
+    set_source_output_latency(u, u->source_output->source);
+
+    pa_sink_input_get_silence(u->sink_input, &silence);
+    u->memblockq = pa_memblockq_new(
+            "module-loopback memblockq",
+            0,                      /* idx */
+            MEMBLOCKQ_MAXLENGTH,    /* maxlength */
+            MEMBLOCKQ_MAXLENGTH,    /* tlength */
+            &ss,                    /* sample_spec */
+            0,                      /* prebuf */
+            0,                      /* minreq */
+            0,                      /* maxrewind */
+            &silence);              /* silence frame */
+    pa_memblock_unref(silence.memblock);
+    /* Fill the memblockq with silence */
+    pa_memblockq_seek(u->memblockq, pa_usec_to_bytes(u->latency, &u->sink_input->sample_spec), PA_SEEK_RELATIVE, true);
+
+    u->asyncmsgq = pa_asyncmsgq_new(0);
+    if (!u->asyncmsgq) {
+        pa_log("pa_asyncmsgq_new() failed.");
+        goto fail;
+    }
+
+    if (!pa_proplist_contains(u->source_output->proplist, PA_PROP_MEDIA_NAME))
+        pa_proplist_setf(u->source_output->proplist, PA_PROP_MEDIA_NAME, "Loopback to %s",
+                         pa_strnull(pa_proplist_gets(u->sink_input->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+
+    if (!pa_proplist_contains(u->source_output->proplist, PA_PROP_MEDIA_ICON_NAME)
+            && (n = pa_proplist_gets(u->sink_input->sink->proplist, PA_PROP_DEVICE_ICON_NAME)))
+        pa_proplist_sets(u->source_output->proplist, PA_PROP_MEDIA_ICON_NAME, n);
+
+    if (!pa_proplist_contains(u->sink_input->proplist, PA_PROP_MEDIA_NAME))
+        pa_proplist_setf(u->sink_input->proplist, PA_PROP_MEDIA_NAME, "Loopback from %s",
+                         pa_strnull(pa_proplist_gets(u->source_output->source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+
+    if (source && !pa_proplist_contains(u->sink_input->proplist, PA_PROP_MEDIA_ICON_NAME)
+            && (n = pa_proplist_gets(u->source_output->source->proplist, PA_PROP_DEVICE_ICON_NAME)))
+        pa_proplist_sets(u->sink_input->proplist, PA_PROP_MEDIA_ICON_NAME, n);
+
+    /* Hooks to track changes of latency offsets */
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PORT_LATENCY_OFFSET_CHANGED],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) sink_port_latency_offset_changed_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PORT_LATENCY_OFFSET_CHANGED],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) source_port_latency_offset_changed_cb, u);
+
+    /* Setup message handler for main thread */
+    u->msg = pa_msgobject_new(loopback_msg);
+    u->msg->parent.process_msg = loopback_process_msg_cb;
+    u->msg->userdata = u;
+
+    /* The output thread is not yet running, set effective_source_latency directly */
+    update_effective_source_latency(u, u->source_output->source, NULL);
+
+    pa_sink_input_put(u->sink_input);
+    pa_source_output_put(u->source_output);
+
+    if (pa_source_get_state(u->source_output->source) != PA_SOURCE_SUSPENDED)
+        pa_sink_input_cork(u->sink_input, false);
+
+    if (pa_sink_get_state(u->sink_input->sink) != PA_SINK_SUSPENDED)
+        pa_source_output_cork(u->source_output, false);
+
+    update_adjust_timer(u);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    teardown(u);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    if (u->asyncmsgq)
+        pa_asyncmsgq_unref(u->asyncmsgq);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-match.c b/src/modules/module-match.c
new file mode 100644 (file)
index 0000000..559687c
--- /dev/null
@@ -0,0 +1,303 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(HAVE_REGEX_H)
+#include <regex.h>
+#elif defined(HAVE_PCREPOSIX_H)
+#include <pcreposix.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/core-util.h>
+
+#include "module-match-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Playback stream expression matching module");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE("table=<filename> "
+                "key=<property_key>");
+
+#define WHITESPACE "\n\r \t"
+
+#define DEFAULT_MATCH_TABLE_FILE PA_DEFAULT_CONFIG_DIR"/match.table"
+#define DEFAULT_MATCH_TABLE_FILE_USER "match.table"
+
+#define UPDATE_REPLACE "replace"
+#define UPDATE_MERGE "merge"
+
+static const char* const valid_modargs[] = {
+    "table",
+    "key",
+    NULL,
+};
+
+struct rule {
+    regex_t regex;
+    pa_volume_t volume;
+    pa_proplist *proplist;
+    pa_update_mode_t mode;
+    struct rule *next;
+};
+
+struct userdata {
+    struct rule *rules;
+    char *property_key;
+    pa_hook_slot *sink_input_fixate_hook_slot;
+};
+
+static int load_rules(struct userdata *u, const char *filename) {
+    FILE *f;
+    int n = 0;
+    int ret = -1;
+    struct rule *end = NULL;
+    char *fn = NULL;
+
+    pa_assert(u);
+
+    if (filename)
+        f = pa_fopen_cloexec(fn = pa_xstrdup(filename), "r");
+    else
+        f = pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn);
+
+    if (!f) {
+        pa_log("Failed to open file config file: %s", pa_cstrerror(errno));
+        goto finish;
+    }
+
+    pa_lock_fd(fileno(f), 1);
+
+    while (!feof(f)) {
+        char *token_end, *value_str;
+        pa_volume_t volume = PA_VOLUME_NORM;
+        uint32_t k;
+        regex_t regex;
+        char ln[256];
+        struct rule *rule;
+        pa_proplist *proplist = NULL;
+        pa_update_mode_t mode = (pa_update_mode_t) -1;
+
+        if (!fgets(ln, sizeof(ln), f))
+            break;
+
+        n++;
+
+        pa_strip_nl(ln);
+
+        if (ln[0] == '#' || !*ln )
+            continue;
+
+        token_end = ln + strcspn(ln, WHITESPACE);
+        value_str = token_end + strspn(token_end, WHITESPACE);
+        *token_end = 0;
+
+        if (!*ln) {
+            pa_log("[%s:%u] failed to parse line - missing regexp", fn, n);
+            goto finish;
+        }
+
+        if (!*value_str) {
+            pa_log("[%s:%u] failed to parse line - too few words", fn, n);
+            goto finish;
+        }
+
+        if (pa_atou(value_str, &k) >= 0)
+            volume = (pa_volume_t) PA_CLAMP_VOLUME(k);
+        else {
+            size_t len;
+
+            token_end = value_str + strcspn(value_str, WHITESPACE);
+
+            len = token_end - value_str;
+            if (len == (sizeof(UPDATE_REPLACE) - 1) && !strncmp(value_str, UPDATE_REPLACE, len))
+                mode = PA_UPDATE_REPLACE;
+            else if (len == (sizeof(UPDATE_MERGE) - 1) && !strncmp(value_str, UPDATE_MERGE, len))
+                mode = PA_UPDATE_MERGE;
+
+            if (mode != (pa_update_mode_t) -1) {
+                value_str = token_end + strspn(token_end, WHITESPACE);
+
+                if (!*value_str) {
+                    pa_log("[%s:%u] failed to parse line - too few words", fn, n);
+                    goto finish;
+                }
+            } else
+                mode = PA_UPDATE_MERGE;
+
+            if (*value_str == '"') {
+                value_str++;
+
+                token_end = strchr(value_str, '"');
+                if (!token_end) {
+                    pa_log("[%s:%u] failed to parse line - missing role closing quote", fn, n);
+                    goto finish;
+                }
+            } else
+                token_end = value_str + strcspn(value_str, WHITESPACE);
+
+            *token_end = 0;
+
+            value_str = pa_sprintf_malloc("media.role=\"%s\"", value_str);
+            proplist = pa_proplist_from_string(value_str);
+            pa_xfree(value_str);
+        }
+
+        if (regcomp(&regex, ln, REG_EXTENDED|REG_NOSUB) != 0) {
+            pa_log("[%s:%u] invalid regular expression", fn, n);
+            if (proplist)
+                pa_proplist_free(proplist);
+            goto finish;
+        }
+
+        rule = pa_xnew(struct rule, 1);
+        rule->regex = regex;
+        rule->proplist = proplist;
+        rule->mode = mode;
+        rule->volume = volume;
+        rule->next = NULL;
+
+        if (end)
+            end->next = rule;
+        else
+            u->rules = rule;
+        end = rule;
+    }
+
+    ret = 0;
+
+finish:
+    if (f) {
+        pa_lock_fd(fileno(f), 0);
+        fclose(f);
+    }
+
+    pa_xfree(fn);
+
+    return ret;
+}
+
+static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *si, struct userdata *u) {
+    struct rule *r;
+    const char *n;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    if (!(n = pa_proplist_gets(si->proplist, u->property_key)))
+        return PA_HOOK_OK;
+
+    pa_log_debug("Matching with %s", n);
+
+    for (r = u->rules; r; r = r->next) {
+        if (!regexec(&r->regex, n, 0, NULL, 0)) {
+            if (r->proplist) {
+                pa_log_debug("updating proplist of sink input '%s'", n);
+                pa_proplist_update(si->proplist, r->mode, r->proplist);
+            } else if (si->volume_writable) {
+                pa_cvolume cv;
+                pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
+                pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
+                pa_sink_input_new_data_set_volume(si, &cv);
+            } else
+                pa_log_debug("the volume of sink input '%s' is not writable, can't change it", n);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->rules = NULL;
+    m->userdata = u;
+
+    u->property_key = pa_xstrdup(pa_modargs_get_value(ma, "key", PA_PROP_MEDIA_NAME));
+
+    if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0)
+        goto fail;
+
+    /* hook EARLY - 1, to match before stream-restore */
+    u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY - 1, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+    struct rule *r, *n;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink_input_fixate_hook_slot)
+        pa_hook_slot_free(u->sink_input_fixate_hook_slot);
+
+    if (u->property_key)
+        pa_xfree(u->property_key);
+
+    for (r = u->rules; r; r = n) {
+        n = r->next;
+
+        regfree(&r->regex);
+        if (r->proplist)
+            pa_proplist_free(r->proplist);
+        pa_xfree(r);
+    }
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
new file mode 100644 (file)
index 0000000..6c9cf34
--- /dev/null
@@ -0,0 +1,265 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <linux/input.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-util.h>
+
+#include "module-mmkbd-evdev-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Multimedia keyboard support via Linux evdev");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE("device=<evdev device> sink=<sink name> volume_limit=<volume limit> volume_step=<volume change step>");
+
+#define DEFAULT_DEVICE "/dev/input/event0"
+
+static const char* const valid_modargs[] = {
+    "device",
+    "sink",
+    "volume_limit",
+    "volume_step",
+    NULL,
+};
+
+struct userdata {
+    int fd, fd_type;
+    pa_io_event *io;
+    char *sink_name;
+    pa_module *module;
+    pa_volume_t volume_limit;
+    pa_volume_t volume_step;
+};
+
+static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void*userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(io);
+    pa_assert(u);
+
+    if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
+        pa_log("Lost connection to evdev device.");
+        goto fail;
+    }
+
+    if (events & PA_IO_EVENT_INPUT) {
+        struct input_event ev;
+
+        if (pa_loop_read(u->fd, &ev, sizeof(ev), &u->fd_type) <= 0) {
+            pa_log("Failed to read from event device: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+        if (ev.type == EV_KEY && (ev.value == 1 || ev.value == 2)) {
+            enum {
+                INVALID,
+                UP,
+                DOWN,
+                MUTE_TOGGLE
+            } volchange = INVALID;
+
+            pa_log_debug("Key code=%u, value=%u", ev.code, ev.value);
+
+            switch (ev.code) {
+                case KEY_VOLUMEDOWN:
+                    volchange = DOWN;
+                    break;
+
+                case KEY_VOLUMEUP:
+                    volchange = UP;
+                    break;
+
+                case KEY_MUTE:
+                    volchange = MUTE_TOGGLE;
+                    break;
+            }
+
+            if (volchange != INVALID) {
+                pa_sink *s;
+
+                if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK)))
+                    pa_log("Failed to get sink '%s'", u->sink_name);
+                else {
+                    pa_cvolume cv = *pa_sink_get_volume(s, false);
+
+                    switch (volchange) {
+                        case UP:
+                            pa_cvolume_inc_clamp(&cv, u->volume_step, u->volume_limit);
+                            pa_sink_set_volume(s, &cv, true, true);
+                            break;
+
+                        case DOWN:
+                            pa_cvolume_dec(&cv, u->volume_step);
+                            pa_sink_set_volume(s, &cv, true, true);
+                            break;
+
+                        case MUTE_TOGGLE:
+                            pa_sink_set_mute(s, !pa_sink_get_mute(s, false), true);
+                            break;
+
+                        case INVALID:
+                            pa_assert_not_reached();
+                    }
+                }
+            }
+        }
+    }
+
+    return;
+
+fail:
+    u->module->core->mainloop->io_free(u->io);
+    u->io = NULL;
+
+    pa_module_unload_request(u->module, true);
+}
+
+#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
+
+int pa__init(pa_module*m) {
+
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    int version;
+    struct input_id input_id;
+    char name[256];
+    uint8_t evtype_bitmask[EV_MAX/8 + 1];
+    pa_volume_t volume_limit = PA_VOLUME_NORM*3/2;
+    pa_volume_t volume_step = PA_VOLUME_NORM/20;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "volume_limit", &volume_limit) < 0) {
+        pa_log("Failed to parse volume limit");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "volume_step", &volume_step) < 0) {
+        pa_log("Failed to parse volume step");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->module = m;
+    u->io = NULL;
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+    u->fd = -1;
+    u->fd_type = 0;
+    u->volume_limit = PA_CLAMP_VOLUME(volume_limit);
+    u->volume_step = PA_CLAMP_VOLUME(volume_step);
+
+    if ((u->fd = pa_open_cloexec(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), O_RDONLY, 0)) < 0) {
+        pa_log("Failed to open evdev device: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (ioctl(u->fd, EVIOCGVERSION, &version) < 0) {
+        pa_log("EVIOCGVERSION failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_log_info("evdev driver version %i.%i.%i", version >> 16, (version >> 8) & 0xff, version & 0xff);
+
+    if (ioctl(u->fd, EVIOCGID, &input_id)) {
+        pa_log("EVIOCGID failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_log_info("evdev vendor 0x%04hx product 0x%04hx version 0x%04hx bustype %u",
+                input_id.vendor, input_id.product, input_id.version, input_id.bustype);
+
+    memset(name, 0, sizeof(name));
+    if (ioctl(u->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
+        pa_log("EVIOCGNAME failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_log_info("evdev device name: %s", name);
+
+    memset(evtype_bitmask, 0, sizeof(evtype_bitmask));
+    if (ioctl(u->fd, EVIOCGBIT(0, EV_MAX), evtype_bitmask) < 0) {
+        pa_log("EVIOCGBIT failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (!test_bit(EV_KEY, evtype_bitmask)) {
+        pa_log("Device has no keys.");
+        goto fail;
+    }
+
+    u->io = m->core->mainloop->io_new(m->core->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->io)
+        m->core->mainloop->io_free(u->io);
+
+    if (u->fd >= 0) {
+        int r = pa_close(u->fd);
+        if (r < 0) /* https://bugs.freedesktop.org/show_bug.cgi?id=80867 */
+            pa_log("Closing fd failed: %s", pa_cstrerror(errno));
+    }
+
+    pa_xfree(u->sink_name);
+    pa_xfree(u);
+}
diff --git a/src/modules/module-native-protocol-fd.c b/src/modules/module-native-protocol-fd.c
new file mode 100644 (file)
index 0000000..5fd7f6a
--- /dev/null
@@ -0,0 +1,94 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/protocol-native.h>
+#include <pulsecore/log.h>
+
+#include "module-native-protocol-fd-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Native protocol autospawn helper");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+static const char* const valid_modargs[] = {
+    "fd",
+    NULL,
+};
+
+int pa__init(pa_module*m) {
+    pa_iochannel *io;
+    pa_modargs *ma;
+    int32_t fd = -1;
+    int r = -1;
+    pa_native_options *options = NULL;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto finish;
+    }
+
+    if (pa_modargs_get_value_s32(ma, "fd", &fd) < 0 || fd < 0) {
+        pa_log("Invalid file descriptor.");
+        goto finish;
+    }
+
+    m->userdata = pa_native_protocol_get(m->core);
+
+    io = pa_iochannel_new(m->core->mainloop, fd, fd);
+
+    options = pa_native_options_new();
+    options->module = m;
+    options->auth_anonymous = true;
+
+    pa_native_protocol_connect(m->userdata, io, options);
+
+    r = 0;
+
+finish:
+    if (ma)
+        pa_modargs_free(ma);
+
+    if (options)
+        pa_native_options_unref(options);
+
+    return r;
+}
+
+void pa__done(pa_module*m) {
+    pa_assert(m);
+
+    if (m->userdata) {
+        pa_native_protocol_disconnect(m->userdata, m);
+        pa_native_protocol_unref(m->userdata);
+    }
+}
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
new file mode 100644 (file)
index 0000000..9237656
--- /dev/null
@@ -0,0 +1,369 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+
+#include "module-null-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION(_("Clocked NULL sink"));
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "sink_name=<name of sink> "
+        "sink_properties=<properties for the sink> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map>");
+
+#define DEFAULT_SINK_NAME "null"
+#define BLOCK_USEC (PA_USEC_PER_SEC * 2)
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    pa_usec_t block_usec;
+    pa_usec_t timestamp;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    NULL
+};
+
+static int sink_process_msg(
+        pa_msgobject *o,
+        int code,
+        void *data,
+        int64_t offset,
+        pa_memchunk *chunk) {
+
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            if (pa_sink_get_state(u->sink) == PA_SINK_SUSPENDED || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
+                if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING || PA_PTR_TO_UINT(data) == PA_SINK_IDLE)
+                    u->timestamp = pa_rtclock_now();
+            }
+
+            break;
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t now;
+
+            now = pa_rtclock_now();
+            *((int64_t*) data) = (int64_t)u->timestamp - (int64_t)now;
+
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u;
+    size_t nbytes;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    u->block_usec = pa_sink_get_requested_latency_within_thread(s);
+
+    if (u->block_usec == (pa_usec_t) -1)
+        u->block_usec = s->thread_info.max_latency;
+
+    nbytes = pa_usec_to_bytes(u->block_usec, &s->sample_spec);
+    pa_sink_set_max_rewind_within_thread(s, nbytes);
+    pa_sink_set_max_request_within_thread(s, nbytes);
+}
+
+static void process_rewind(struct userdata *u, pa_usec_t now) {
+    size_t rewind_nbytes, in_buffer;
+    pa_usec_t delay;
+
+    pa_assert(u);
+
+    rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+
+    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state) || rewind_nbytes <= 0)
+        goto do_nothing;
+
+    pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+
+    if (u->timestamp <= now)
+        goto do_nothing;
+
+    delay = u->timestamp - now;
+    in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);
+
+    if (in_buffer <= 0)
+        goto do_nothing;
+
+    if (rewind_nbytes > in_buffer)
+        rewind_nbytes = in_buffer;
+
+    pa_sink_process_rewind(u->sink, rewind_nbytes);
+    u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);
+
+    pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+    return;
+
+do_nothing:
+
+    pa_sink_process_rewind(u->sink, 0);
+}
+
+static void process_render(struct userdata *u, pa_usec_t now) {
+    size_t ate = 0;
+
+    pa_assert(u);
+
+    /* This is the configured latency. Sink inputs connected to us
+    might not have a single frame more than the maxrequest value
+    queued. Hence: at maximum read this many bytes from the sink
+    inputs. */
+
+    /* Fill the buffer up the latency size */
+    while (u->timestamp < now + u->block_usec) {
+        pa_memchunk chunk;
+
+        pa_sink_render(u->sink, u->sink->thread_info.max_request, &chunk);
+        pa_memblock_unref(chunk.memblock);
+
+/*         pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */
+        u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
+
+        ate += chunk.length;
+
+        if (ate >= u->sink->thread_info.max_request)
+            break;
+    }
+
+/*     pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    u->timestamp = pa_rtclock_now();
+
+    for (;;) {
+        pa_usec_t now = 0;
+        int ret;
+
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+            now = pa_rtclock_now();
+
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+            process_rewind(u, now);
+
+        /* Render some data and drop it immediately */
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+            if (u->timestamp <= now)
+                process_render(u, now);
+
+            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
+        } else
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    pa_sink_new_data data;
+    size_t nbytes;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, _("Null Output"));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&data);
+        goto fail;
+    }
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink object.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->update_requested_latency = sink_update_requested_latency_cb;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    u->block_usec = BLOCK_USEC;
+    nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+    pa_sink_set_max_rewind(u->sink, nbytes);
+    pa_sink_set_max_request(u->sink, nbytes);
+
+    if (!(u->thread = pa_thread_new("null-sink", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_sink_set_latency_range(u->sink, 0, BLOCK_USEC);
+
+    pa_sink_put(u->sink);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_sink_linked_by(u->sink);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-null-source.c b/src/modules/module-null-source.c
new file mode 100644 (file)
index 0000000..5bfa1e1
--- /dev/null
@@ -0,0 +1,291 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/module.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/source.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/thread.h>
+
+#include "module-null-source-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering & Marc-Andre Lureau");
+PA_MODULE_DESCRIPTION("Clocked NULL source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "source_name=<name of source> "
+        "channel_map=<channel map> "
+        "description=<description for the source> "
+        "latency_time=<latency time in ms>");
+
+#define DEFAULT_SOURCE_NAME "source.null"
+#define DEFAULT_LATENCY_TIME 20
+#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2)
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_source *source;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    size_t block_size;
+
+    pa_usec_t block_usec;
+    pa_usec_t timestamp;
+    pa_usec_t latency_time;
+};
+
+static const char* const valid_modargs[] = {
+    "rate",
+    "format",
+    "channels",
+    "source_name",
+    "channel_map",
+    "description",
+    "latency_time",
+    NULL
+};
+
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+        case PA_SOURCE_MESSAGE_SET_STATE:
+
+            if (PA_PTR_TO_UINT(data) == PA_SOURCE_RUNNING)
+                u->timestamp = pa_rtclock_now();
+
+            break;
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            pa_usec_t now;
+
+            now = pa_rtclock_now();
+            *((int64_t*) data) = (int64_t)u->timestamp - (int64_t)now;
+
+            return 0;
+        }
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static void source_update_requested_latency_cb(pa_source *s) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    u = s->userdata;
+    pa_assert(u);
+
+    u->block_usec = pa_source_get_requested_latency_within_thread(s);
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    u->timestamp = pa_rtclock_now();
+
+    for (;;) {
+        int ret;
+
+        /* Generate some null data */
+        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+            pa_usec_t now;
+            pa_memchunk chunk;
+
+            now = pa_rtclock_now();
+
+            if ((chunk.length = pa_usec_to_bytes(now - u->timestamp, &u->source->sample_spec)) > 0) {
+
+                chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); /* or chunk.length? */
+                chunk.index = 0;
+                pa_source_post(u->source, &chunk);
+                pa_memblock_unref(chunk.memblock);
+
+                u->timestamp = now;
+            }
+
+            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp + u->latency_time * PA_USEC_PER_MSEC);
+        } else
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    pa_source_new_data data;
+    uint32_t latency_time = DEFAULT_LATENCY_TIME;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    pa_source_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+    pa_source_new_data_set_sample_spec(&data, &ss);
+    pa_source_new_data_set_channel_map(&data, &map);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Input"));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
+
+    u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY | PA_SOURCE_DYNAMIC_LATENCY);
+    pa_source_new_data_done(&data);
+
+    if (!u->source) {
+        pa_log("Failed to create source object.");
+        goto fail;
+    }
+
+    u->latency_time = DEFAULT_LATENCY_TIME;
+    if (pa_modargs_get_value_u32(ma, "latency_time", &latency_time) < 0) {
+        pa_log("Failed to parse latency_time value.");
+        goto fail;
+    }
+    u->latency_time = latency_time;
+
+    u->source->parent.process_msg = source_process_msg;
+    u->source->update_requested_latency = source_update_requested_latency_cb;
+    u->source->userdata = u;
+
+    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+
+    pa_source_set_latency_range(u->source, 0, MAX_LATENCY_USEC);
+    u->block_usec = u->source->thread_info.max_latency;
+
+    u->source->thread_info.max_rewind =
+        pa_usec_to_bytes(u->block_usec, &u->source->sample_spec);
+
+    if (!(u->thread = pa_thread_new("null-source", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_source_put(u->source);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
new file mode 100644 (file)
index 0000000..8396a63
--- /dev/null
@@ -0,0 +1,388 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/poll.h>
+
+#include "module-pipe-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("UNIX pipe sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "sink_properties=<properties for the sink> "
+        "file=<path of the FIFO> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map>");
+
+#define DEFAULT_FILE_NAME "fifo_output"
+#define DEFAULT_SINK_NAME "fifo_output"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    char *filename;
+    int fd;
+    size_t buffer_size;
+
+    pa_memchunk memchunk;
+
+    pa_rtpoll_item *rtpoll_item;
+
+    int write_type;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "file",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    NULL
+};
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            size_t n = 0;
+
+#ifdef FIONREAD
+            int l;
+
+            if (ioctl(u->fd, FIONREAD, &l) >= 0 && l > 0)
+                n = (size_t) l;
+#endif
+
+            n += u->memchunk.length;
+
+            *((int64_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static int process_render(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->memchunk.length <= 0)
+        pa_sink_render(u->sink, u->buffer_size, &u->memchunk);
+
+    pa_assert(u->memchunk.length > 0);
+
+    for (;;) {
+        ssize_t l;
+        void *p;
+
+        p = pa_memblock_acquire(u->memchunk.memblock);
+        l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &u->write_type);
+        pa_memblock_release(u->memchunk.memblock);
+
+        pa_assert(l != 0);
+
+        if (l < 0) {
+
+            if (errno == EINTR)
+                continue;
+            else if (errno == EAGAIN)
+                return 0;
+            else {
+                pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+                return -1;
+            }
+
+        } else {
+
+            u->memchunk.index += (size_t) l;
+            u->memchunk.length -= (size_t) l;
+
+            if (u->memchunk.length <= 0) {
+                pa_memblock_unref(u->memchunk.memblock);
+                pa_memchunk_reset(&u->memchunk);
+            }
+        }
+
+        return 0;
+    }
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        struct pollfd *pollfd;
+        int ret;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+            pa_sink_process_rewind(u->sink, 0);
+
+        /* Render some data and write it to the fifo */
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+            if (pollfd->revents) {
+                if (process_render(u) < 0)
+                    goto fail;
+
+                pollfd->revents = 0;
+            }
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        pollfd->events = (short) (u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0);
+
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        if (pollfd->revents & ~POLLOUT) {
+            pa_log("FIFO shutdown.");
+            goto fail;
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u;
+    struct stat st;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma;
+    struct pollfd *pollfd;
+    pa_sink_new_data data;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    pa_memchunk_reset(&u->memchunk);
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->write_type = 0;
+
+    u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
+
+    if (mkfifo(u->filename, 0666) < 0) {
+        pa_log("mkfifo('%s'): %s", u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+    if ((u->fd = pa_open_cloexec(u->filename, O_RDWR, 0)) < 0) {
+        pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_make_fd_nonblock(u->fd);
+
+    if (fstat(u->fd, &st) < 0) {
+        pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (!S_ISFIFO(st.st_mode)) {
+        pa_log("'%s' is not a FIFO.", u->filename);
+        goto fail;
+    }
+
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO sink %s", u->filename);
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&data);
+        goto fail;
+    }
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    u->buffer_size = pa_frame_align(pa_pipe_buf(u->fd), &u->sink->sample_spec);
+    pa_sink_set_max_request(u->sink, u->buffer_size);
+    pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->buffer_size, &u->sink->sample_spec));
+
+    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+    pollfd->fd = u->fd;
+    pollfd->events = pollfd->revents = 0;
+
+    if (!(u->thread = pa_thread_new("pipe-sink", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_sink_put(u->sink);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_sink_linked_by(u->sink);
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->filename) {
+        unlink(u->filename);
+        pa_xfree(u->filename);
+    }
+
+    if (u->fd >= 0)
+        pa_assert_se(pa_close(u->fd) == 0);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
new file mode 100644 (file)
index 0000000..411f1fd
--- /dev/null
@@ -0,0 +1,369 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/poll.h>
+
+#include "module-pipe-source-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("UNIX pipe source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "source_name=<name for the source> "
+        "source_properties=<properties for the source> "
+        "file=<path of the FIFO> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map>");
+
+#define DEFAULT_FILE_NAME "/tmp/music.input"
+#define DEFAULT_SOURCE_NAME "fifo_input"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_source *source;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    char *filename;
+    int fd;
+
+    pa_memchunk memchunk;
+
+    pa_rtpoll_item *rtpoll_item;
+};
+
+static const char* const valid_modargs[] = {
+    "source_name",
+    "source_properties",
+    "file",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    NULL
+};
+
+static int source_process_msg(
+        pa_msgobject *o,
+        int code,
+        void *data,
+        int64_t offset,
+        pa_memchunk *chunk) {
+
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            size_t n = 0;
+
+#ifdef FIONREAD
+            int l;
+
+            if (ioctl(u->fd, FIONREAD, &l) >= 0 && l > 0)
+                n = (size_t) l;
+#endif
+
+            *((int64_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec);
+            return 0;
+        }
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    int read_type = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        int ret;
+        struct pollfd *pollfd;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        /* Try to read some data and pass it on to the source driver */
+        if (u->source->thread_info.state == PA_SOURCE_RUNNING && pollfd->revents) {
+            ssize_t l;
+            void *p;
+
+            if (!u->memchunk.memblock) {
+                u->memchunk.memblock = pa_memblock_new(u->core->mempool, pa_pipe_buf(u->fd));
+                u->memchunk.index = u->memchunk.length = 0;
+            }
+
+            pa_assert(pa_memblock_get_length(u->memchunk.memblock) > u->memchunk.index);
+
+            p = pa_memblock_acquire(u->memchunk.memblock);
+            l = pa_read(u->fd, (uint8_t*) p + u->memchunk.index, pa_memblock_get_length(u->memchunk.memblock) - u->memchunk.index, &read_type);
+            pa_memblock_release(u->memchunk.memblock);
+
+            pa_assert(l != 0); /* EOF cannot happen, since we opened the fifo for both reading and writing */
+
+            if (l < 0) {
+
+                if (errno == EINTR)
+                    continue;
+                else if (errno != EAGAIN) {
+                    pa_log("Failed to read data from FIFO: %s", pa_cstrerror(errno));
+                    goto fail;
+                }
+
+            } else {
+
+                u->memchunk.length = (size_t) l;
+                pa_source_post(u->source, &u->memchunk);
+                u->memchunk.index += (size_t) l;
+
+                if (u->memchunk.index >= pa_memblock_get_length(u->memchunk.memblock)) {
+                    pa_memblock_unref(u->memchunk.memblock);
+                    pa_memchunk_reset(&u->memchunk);
+                }
+
+                pollfd->revents = 0;
+            }
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        pollfd->events = (short) (u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0);
+
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        if (pollfd->revents & ~POLLIN) {
+            pa_log("FIFO shutdown.");
+            goto fail;
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u;
+    struct stat st;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma;
+    struct pollfd *pollfd;
+    pa_source_new_data data;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    pa_memchunk_reset(&u->memchunk);
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
+
+    if (mkfifo(u->filename, 0666) < 0) {
+        pa_log("mkfifo('%s'): %s", u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+    if ((u->fd = pa_open_cloexec(u->filename, O_RDWR, 0)) < 0) {
+        pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_make_fd_nonblock(u->fd);
+
+    if (fstat(u->fd, &st) < 0) {
+        pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (!S_ISFIFO(st.st_mode)) {
+        pa_log("'%s' is not a FIFO.", u->filename);
+        goto fail;
+    }
+
+    pa_source_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO source %s", u->filename);
+    pa_source_new_data_set_sample_spec(&data, &ss);
+    pa_source_new_data_set_channel_map(&data, &map);
+
+    if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_source_new_data_done(&data);
+        goto fail;
+    }
+
+    u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY);
+    pa_source_new_data_done(&data);
+
+    if (!u->source) {
+        pa_log("Failed to create source.");
+        goto fail;
+    }
+
+    u->source->parent.process_msg = source_process_msg;
+    u->source->userdata = u;
+
+    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+    pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(pa_pipe_buf(u->fd), &u->source->sample_spec));
+
+    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+    pollfd->fd = u->fd;
+    pollfd->events = pollfd->revents = 0;
+
+    if (!(u->thread = pa_thread_new("pipe-source", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_source_put(u->source);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_source_linked_by(u->source);
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->filename) {
+        unlink(u->filename);
+        pa_xfree(u->filename);
+    }
+
+    if (u->fd >= 0)
+        pa_assert_se(pa_close(u->fd) == 0);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
new file mode 100644 (file)
index 0000000..fb9f5b3
--- /dev/null
@@ -0,0 +1,181 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/channelmap.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/sink-input.h>
+
+#include "module-position-event-sounds-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Position event sounds between L and R depending on the position on screen of the widget triggering them.");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct userdata {
+    pa_hook_slot *sink_input_fixate_hook_slot;
+    const char *name;
+};
+
+static int parse_pos(const char *pos, double *f) {
+
+    if (pa_atod(pos, f) < 0) {
+        pa_log_warn("Failed to parse hpos/vpos property '%s'.", pos);
+        return -1;
+    }
+
+    if (*f < 0.0 || *f > 1.0) {
+        pa_log_debug("Property hpos/vpos out of range %0.2f", *f);
+
+        *f = PA_CLAMP(*f, 0.0, 1.0);
+    }
+
+    return 0;
+}
+
+static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) {
+    const char *hpos, *vpos, *role, *id;
+    double f;
+    char t[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    pa_cvolume v;
+
+    pa_assert(data);
+
+    if (!(role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE)))
+        return PA_HOOK_OK;
+
+    if (!pa_streq(role, "event"))
+        return PA_HOOK_OK;
+
+    if ((id = pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID))) {
+
+        /* The test sounds should never be positioned in space, since
+         * they might be triggered themselves to configure the speakers
+         * in space, which we don't want to mess up. */
+
+        if (pa_startswith(id, "audio-channel-"))
+            return PA_HOOK_OK;
+
+        if (pa_streq(id, "audio-volume-change"))
+            return PA_HOOK_OK;
+
+        if (pa_streq(id, "audio-test-signal"))
+            return PA_HOOK_OK;
+    }
+
+    if (!(hpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_HPOS)))
+        hpos = pa_proplist_gets(data->proplist, PA_PROP_WINDOW_HPOS);
+
+    if (!(vpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_VPOS)))
+        vpos = pa_proplist_gets(data->proplist, PA_PROP_WINDOW_VPOS);
+
+    if (!hpos && !vpos)
+        return PA_HOOK_OK;
+
+    pa_cvolume_reset(&v, data->sink->sample_spec.channels);
+
+    if (hpos) {
+        if (parse_pos(hpos, &f) < 0)
+            return PA_HOOK_OK;
+
+        if (pa_channel_map_can_balance(&data->sink->channel_map)) {
+            pa_log_debug("Positioning event sound '%s' horizontally at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f);
+            pa_cvolume_set_balance(&v, &data->sink->channel_map, f*2.0-1.0);
+        }
+    }
+
+    if (vpos) {
+        if (parse_pos(vpos, &f) < 0)
+            return PA_HOOK_OK;
+
+        if (pa_channel_map_can_fade(&data->sink->channel_map)) {
+            pa_log_debug("Positioning event sound '%s' vertically at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f);
+            pa_cvolume_set_fade(&v, &data->sink->channel_map, f*2.0-1.0);
+        }
+    }
+
+    pa_log_debug("Final volume factor %s.",
+                 pa_cvolume_snprint_verbose(t,
+                                            sizeof(t),
+                                            &v,
+                                            &data->sink->channel_map,
+                                            data->sink->flags & PA_SINK_DECIBEL_VOLUME));
+    pa_sink_input_new_data_add_volume_factor_sink(data, u->name, &v);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
+
+    pa_modargs_free(ma);
+    u->name = m->name;
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink_input_fixate_hook_slot)
+        pa_hook_slot_free(u->sink_input_fixate_hook_slot);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c
new file mode 100644 (file)
index 0000000..5a183c8
--- /dev/null
@@ -0,0 +1,519 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/socket-server.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/arpa-inet.h>
+
+#ifdef USE_TCP_SOCKETS
+#define SOCKET_DESCRIPTION "(TCP sockets)"
+#define SOCKET_USAGE "port=<TCP port number> listen=<address to listen on>"
+#else
+#define SOCKET_DESCRIPTION "(UNIX sockets)"
+#define SOCKET_USAGE "socket=<path to UNIX socket>"
+#endif
+
+#if defined(USE_PROTOCOL_SIMPLE)
+#  include <pulsecore/protocol-simple.h>
+#  define TCPWRAP_SERVICE "pulseaudio-simple"
+#  define IPV4_PORT 4711
+#  define UNIX_SOCKET "simple"
+#  define MODULE_ARGUMENTS "rate", "format", "channels", "sink", "source", "playback", "record",
+
+#  if defined(USE_TCP_SOCKETS)
+#    include "module-simple-protocol-tcp-symdef.h"
+#  else
+#    include "module-simple-protocol-unix-symdef.h"
+#  endif
+
+  PA_MODULE_DESCRIPTION("Simple protocol "SOCKET_DESCRIPTION);
+  PA_MODULE_USAGE("rate=<sample rate> "
+                  "format=<sample format> "
+                  "channels=<number of channels> "
+                  "sink=<sink to connect to> "
+                  "source=<source to connect to> "
+                  "playback=<enable playback?> "
+                  "record=<enable record?> "
+                  SOCKET_USAGE);
+#elif defined(USE_PROTOCOL_CLI)
+#  include <pulsecore/protocol-cli.h>
+#  define TCPWRAP_SERVICE "pulseaudio-cli"
+#  define IPV4_PORT 4712
+#  define UNIX_SOCKET "cli"
+#  define MODULE_ARGUMENTS
+
+#  ifdef USE_TCP_SOCKETS
+#    include "module-cli-protocol-tcp-symdef.h"
+#  else
+#   include "module-cli-protocol-unix-symdef.h"
+#  endif
+
+  PA_MODULE_DESCRIPTION("Command line interface protocol "SOCKET_DESCRIPTION);
+  PA_MODULE_USAGE(SOCKET_USAGE);
+#elif defined(USE_PROTOCOL_HTTP)
+#  include <pulsecore/protocol-http.h>
+#  define TCPWRAP_SERVICE "pulseaudio-http"
+#  define IPV4_PORT 4714
+#  define UNIX_SOCKET "http"
+#  define MODULE_ARGUMENTS
+
+#  ifdef USE_TCP_SOCKETS
+#    include "module-http-protocol-tcp-symdef.h"
+#  else
+#    include "module-http-protocol-unix-symdef.h"
+#  endif
+
+  PA_MODULE_DESCRIPTION("HTTP "SOCKET_DESCRIPTION);
+  PA_MODULE_USAGE(SOCKET_USAGE);
+#elif defined(USE_PROTOCOL_NATIVE)
+#  include <pulsecore/protocol-native.h>
+#  define TCPWRAP_SERVICE "pulseaudio-native"
+#  define IPV4_PORT PA_NATIVE_DEFAULT_PORT
+#  define UNIX_SOCKET PA_NATIVE_DEFAULT_UNIX_SOCKET
+#  define MODULE_ARGUMENTS_COMMON "cookie", "auth-cookie", "auth-cookie-enabled", "auth-anonymous",
+
+#  ifdef USE_TCP_SOCKETS
+#    include "module-native-protocol-tcp-symdef.h"
+#  else
+#    include "module-native-protocol-unix-symdef.h"
+#  endif
+
+#  if defined(HAVE_CREDS) && !defined(USE_TCP_SOCKETS)
+#    define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-group", "auth-group-enable", "srbchannel",
+#    define AUTH_USAGE "auth-group=<system group to allow access> auth-group-enable=<enable auth by UNIX group?> "
+#    define SRB_USAGE "srbchannel=<enable shared ringbuffer communication channel?> "
+#  elif defined(USE_TCP_SOCKETS)
+#    define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-ip-acl",
+#    define AUTH_USAGE "auth-ip-acl=<IP address ACL to allow access> "
+#    define SRB_USAGE
+#  else
+#    define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON
+#    define AUTH_USAGE
+#    define SRB_USAGE
+#    endif
+
+  PA_MODULE_DESCRIPTION("Native protocol "SOCKET_DESCRIPTION);
+  PA_MODULE_USAGE("auth-anonymous=<don't check for cookies?> "
+                  "auth-cookie=<path to cookie file> "
+                  "auth-cookie-enabled=<enable cookie authentication?> "
+                  AUTH_USAGE
+                  SRB_USAGE
+                  SOCKET_USAGE);
+#elif defined(USE_PROTOCOL_ESOUND)
+#  include <pulsecore/protocol-esound.h>
+#  include <pulsecore/esound.h>
+#  define TCPWRAP_SERVICE "esound"
+#  define IPV4_PORT ESD_DEFAULT_PORT
+#  define MODULE_ARGUMENTS_COMMON "sink", "source", "auth-anonymous", "cookie", "auth-cookie", "auth-cookie-enabled",
+
+#  ifdef USE_TCP_SOCKETS
+#    include "module-esound-protocol-tcp-symdef.h"
+#  else
+#    include "module-esound-protocol-unix-symdef.h"
+#  endif
+
+#  if defined(USE_TCP_SOCKETS)
+#    define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-ip-acl",
+#    define AUTH_USAGE "auth-ip-acl=<IP address ACL to allow access> "
+#  else
+#    define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON
+#    define AUTH_USAGE
+#  endif
+
+  PA_MODULE_DESCRIPTION("ESOUND protocol "SOCKET_DESCRIPTION);
+  PA_MODULE_USAGE("sink=<sink to connect to> "
+                  "source=<source to connect to> "
+                  "auth-anonymous=<don't verify cookies?> "
+                  "auth-cookie=<path to cookie file> "
+                  "auth-cookie-enabled=<enable cookie authentication?> "
+                  AUTH_USAGE
+                  SOCKET_USAGE);
+#else
+#  error "Broken build system"
+#endif
+
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+
+static const char* const valid_modargs[] = {
+    MODULE_ARGUMENTS
+#if defined(USE_TCP_SOCKETS)
+    "port",
+    "listen",
+#else
+    "socket",
+#endif
+    NULL
+};
+
+struct userdata {
+    pa_module *module;
+
+#if defined(USE_PROTOCOL_SIMPLE)
+    pa_simple_protocol *simple_protocol;
+    pa_simple_options *simple_options;
+#elif defined(USE_PROTOCOL_CLI)
+    pa_cli_protocol *cli_protocol;
+#elif defined(USE_PROTOCOL_HTTP)
+    pa_http_protocol *http_protocol;
+#elif defined(USE_PROTOCOL_NATIVE)
+    pa_native_protocol *native_protocol;
+    pa_native_options *native_options;
+#else
+    pa_esound_protocol *esound_protocol;
+    pa_esound_options *esound_options;
+#endif
+
+#if defined(USE_TCP_SOCKETS)
+    pa_socket_server *socket_server_ipv4;
+#  ifdef HAVE_IPV6
+    pa_socket_server *socket_server_ipv6;
+#  endif
+#else
+    pa_socket_server *socket_server_unix;
+    char *socket_path;
+#endif
+};
+
+static void socket_server_on_connection_cb(pa_socket_server*s, pa_iochannel *io, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(s);
+    pa_assert(io);
+    pa_assert(u);
+
+#if defined(USE_PROTOCOL_SIMPLE)
+    pa_simple_protocol_connect(u->simple_protocol, io, u->simple_options);
+#elif defined(USE_PROTOCOL_CLI)
+    pa_cli_protocol_connect(u->cli_protocol, io, u->module);
+#elif defined(USE_PROTOCOL_HTTP)
+    pa_http_protocol_connect(u->http_protocol, io, u->module);
+#elif defined(USE_PROTOCOL_NATIVE)
+    pa_native_protocol_connect(u->native_protocol, io, u->native_options);
+#else
+    pa_esound_protocol_connect(u->esound_protocol, io, u->esound_options);
+#endif
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u = NULL;
+
+#if defined(USE_TCP_SOCKETS)
+    uint32_t port = IPV4_PORT;
+    bool port_fallback = true;
+    const char *listen_on;
+#else
+    int r;
+#endif
+
+#if defined(USE_PROTOCOL_NATIVE) || defined(USE_PROTOCOL_HTTP)
+    char t[256];
+#endif
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+
+#if defined(USE_PROTOCOL_SIMPLE)
+    u->simple_protocol = pa_simple_protocol_get(m->core);
+
+    u->simple_options = pa_simple_options_new();
+    if (pa_simple_options_parse(u->simple_options, m->core, ma) < 0)
+        goto fail;
+    u->simple_options->module = m;
+#elif defined(USE_PROTOCOL_CLI)
+    u->cli_protocol = pa_cli_protocol_get(m->core);
+#elif defined(USE_PROTOCOL_HTTP)
+    u->http_protocol = pa_http_protocol_get(m->core);
+#elif defined(USE_PROTOCOL_NATIVE)
+    u->native_protocol = pa_native_protocol_get(m->core);
+
+    u->native_options = pa_native_options_new();
+    if (pa_native_options_parse(u->native_options, m->core, ma) < 0)
+        goto fail;
+    u->native_options->module = m;
+#else
+    u->esound_protocol = pa_esound_protocol_get(m->core);
+
+    u->esound_options = pa_esound_options_new();
+    if (pa_esound_options_parse(u->esound_options, m->core, ma) < 0)
+        goto fail;
+    u->esound_options->module = m;
+#endif
+
+#if defined(USE_TCP_SOCKETS)
+
+    if (pa_in_system_mode() || pa_modargs_get_value(ma, "port", NULL))
+        port_fallback = false;
+
+    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) {
+        pa_log("port= expects a numerical argument between 1 and 65535.");
+        goto fail;
+    }
+
+    listen_on = pa_modargs_get_value(ma, "listen", NULL);
+
+    if (listen_on) {
+#  ifdef HAVE_IPV6
+        u->socket_server_ipv6 = pa_socket_server_new_ipv6_string(m->core->mainloop, listen_on, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
+#  endif
+        u->socket_server_ipv4 = pa_socket_server_new_ipv4_string(m->core->mainloop, listen_on, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
+    } else {
+#  ifdef HAVE_IPV6
+        u->socket_server_ipv6 = pa_socket_server_new_ipv6_any(m->core->mainloop, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
+#  endif
+        u->socket_server_ipv4 = pa_socket_server_new_ipv4_any(m->core->mainloop, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
+    }
+
+#  ifdef HAVE_IPV6
+    if (!u->socket_server_ipv4 && !u->socket_server_ipv6)
+#  else
+    if (!u->socket_server_ipv4)
+#  endif
+        goto fail;
+
+    if (u->socket_server_ipv4)
+        pa_socket_server_set_callback(u->socket_server_ipv4, socket_server_on_connection_cb, u);
+#  ifdef HAVE_IPV6
+    if (u->socket_server_ipv6)
+        pa_socket_server_set_callback(u->socket_server_ipv6, socket_server_on_connection_cb, u);
+#  endif
+
+#else
+
+#  if defined(USE_PROTOCOL_ESOUND)
+
+#    if defined(USE_PER_USER_ESOUND_SOCKET)
+    u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid());
+#    else
+    u->socket_path = pa_xstrdup("/tmp/.esd/socket");
+#    endif
+
+    /* This socket doesn't reside in our own runtime dir but in
+     * /tmp/.esd/, hence we have to create the dir first */
+
+    if (pa_make_secure_parent_dir(u->socket_path, pa_in_system_mode() ? 0755U : 0700U, (uid_t)-1, (gid_t)-1, false) < 0) {
+        pa_log("Failed to create socket directory '%s': %s\n", u->socket_path, pa_cstrerror(errno));
+        goto fail;
+    }
+
+#  else
+    if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) {
+        pa_log("Failed to generate socket path.");
+        goto fail;
+    }
+#  endif
+
+    if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) {
+        pa_log("Failed to remove stale UNIX socket '%s': %s", u->socket_path, pa_cstrerror(errno));
+        goto fail;
+    } else if (r > 0)
+        pa_log_info("Removed stale UNIX socket '%s'.", u->socket_path);
+
+    if (!(u->socket_server_unix = pa_socket_server_new_unix(m->core->mainloop, u->socket_path)))
+        goto fail;
+
+    pa_socket_server_set_callback(u->socket_server_unix, socket_server_on_connection_cb, u);
+
+#endif
+
+#if defined(USE_PROTOCOL_NATIVE)
+#  if defined(USE_TCP_SOCKETS)
+    if (u->socket_server_ipv4)
+        if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
+            pa_native_protocol_add_server_string(u->native_protocol, t);
+
+#    ifdef HAVE_IPV6
+    if (u->socket_server_ipv6)
+        if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
+            pa_native_protocol_add_server_string(u->native_protocol, t);
+#    endif
+#  else
+    if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
+        pa_native_protocol_add_server_string(u->native_protocol, t);
+
+#  endif
+#endif
+
+#if defined(USE_PROTOCOL_HTTP)
+#if defined(USE_TCP_SOCKETS)
+    if (u->socket_server_ipv4)
+        if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
+            pa_http_protocol_add_server_string(u->http_protocol, t);
+
+#ifdef HAVE_IPV6
+    if (u->socket_server_ipv6)
+        if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
+            pa_http_protocol_add_server_string(u->http_protocol, t);
+#endif /* HAVE_IPV6 */
+#else /* USE_TCP_SOCKETS */
+    if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
+        pa_http_protocol_add_server_string(u->http_protocol, t);
+
+#endif /* USE_TCP_SOCKETS */
+#endif /* USE_PROTOCOL_HTTP */
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+#if defined(USE_PROTOCOL_SIMPLE)
+    if (u->simple_protocol) {
+        pa_simple_protocol_disconnect(u->simple_protocol, u->module);
+        pa_simple_protocol_unref(u->simple_protocol);
+    }
+    if (u->simple_options)
+        pa_simple_options_unref(u->simple_options);
+#elif defined(USE_PROTOCOL_CLI)
+    if (u->cli_protocol) {
+        pa_cli_protocol_disconnect(u->cli_protocol, u->module);
+        pa_cli_protocol_unref(u->cli_protocol);
+    }
+#elif defined(USE_PROTOCOL_HTTP)
+    if (u->http_protocol) {
+        char t[256];
+
+#if defined(USE_TCP_SOCKETS)
+        if (u->socket_server_ipv4)
+            if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
+                pa_http_protocol_remove_server_string(u->http_protocol, t);
+
+#ifdef HAVE_IPV6
+        if (u->socket_server_ipv6)
+            if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
+                pa_http_protocol_remove_server_string(u->http_protocol, t);
+#endif /* HAVE_IPV6 */
+#else /* USE_TCP_SOCKETS */
+        if (u->socket_server_unix)
+            if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
+                pa_http_protocol_remove_server_string(u->http_protocol, t);
+#endif /* USE_PROTOCOL_HTTP */
+
+        pa_http_protocol_disconnect(u->http_protocol, u->module);
+        pa_http_protocol_unref(u->http_protocol);
+    }
+#elif defined(USE_PROTOCOL_NATIVE)
+    if (u->native_protocol) {
+
+        char t[256];
+
+#  if defined(USE_TCP_SOCKETS)
+        if (u->socket_server_ipv4)
+            if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
+                pa_native_protocol_remove_server_string(u->native_protocol, t);
+
+#    ifdef HAVE_IPV6
+        if (u->socket_server_ipv6)
+            if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
+                pa_native_protocol_remove_server_string(u->native_protocol, t);
+#    endif
+#  else
+        if (u->socket_server_unix)
+            if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
+                pa_native_protocol_remove_server_string(u->native_protocol, t);
+#  endif
+
+        pa_native_protocol_disconnect(u->native_protocol, u->module);
+        pa_native_protocol_unref(u->native_protocol);
+    }
+    if (u->native_options)
+        pa_native_options_unref(u->native_options);
+#else
+    if (u->esound_protocol) {
+        pa_esound_protocol_disconnect(u->esound_protocol, u->module);
+        pa_esound_protocol_unref(u->esound_protocol);
+    }
+    if (u->esound_options)
+        pa_esound_options_unref(u->esound_options);
+#endif
+
+#if defined(USE_TCP_SOCKETS)
+    if (u->socket_server_ipv4)
+        pa_socket_server_unref(u->socket_server_ipv4);
+#  ifdef HAVE_IPV6
+    if (u->socket_server_ipv6)
+        pa_socket_server_unref(u->socket_server_ipv6);
+#  endif
+#else
+    if (u->socket_server_unix)
+        pa_socket_server_unref(u->socket_server_unix);
+
+# if defined(USE_PROTOCOL_ESOUND) && !defined(USE_PER_USER_ESOUND_SOCKET)
+    if (u->socket_path) {
+        char *p = pa_parent_dir(u->socket_path);
+        rmdir(p);
+        pa_xfree(p);
+    }
+# endif
+
+    pa_xfree(u->socket_path);
+#endif
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
new file mode 100644 (file)
index 0000000..a2a8e31
--- /dev/null
@@ -0,0 +1,510 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/rtpoll.h>
+
+#include "module-remap-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Virtual channel remapping sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "sink_properties=<properties for the sink> "
+        "master=<name of sink to remap> "
+        "master_channel_map=<channel map> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map> "
+        "resample_method=<resampler> "
+        "remix=<remix channels?>");
+
+struct userdata {
+    pa_module *module;
+
+    pa_sink *sink;
+    pa_sink_input *sink_input;
+
+    bool auto_desc;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "master",
+    "master_channel_map",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "resample_method",
+    "remix",
+    NULL
+};
+
+/* Called from I/O thread context */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY:
+
+            /* The sink is _put() before the sink input is, so let's
+             * make sure we don't access it yet */
+            if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+                !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            *((int64_t*) data) =
+                /* Get the latency of the master sink */
+                pa_sink_get_latency_within_thread(u->sink_input->sink, true) +
+
+                /* Add the latency internal to our sink input on top */
+                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
+
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
+
+            /* When set to running or idle for the first time, request a rewind
+             * of the master sink to make sure we are heard immediately */
+            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
+                pa_log_debug("Requesting rewind due to state change.");
+                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
+            }
+            break;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(state) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return 0;
+
+    pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_request_rewind(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, true, false, false);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_set_requested_latency_within_thread(
+            u->sink_input,
+            pa_sink_get_requested_latency_within_thread(s));
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    pa_assert_se(u = i->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return -1;
+
+    /* Hmm, process any rewind request that might be queued up */
+    pa_sink_process_rewind(u->sink, 0);
+
+    pa_sink_render(u->sink, nbytes, chunk);
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    size_t amount = 0;
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If the sink is not yet linked, there is nothing to rewind */
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    if (u->sink->thread_info.rewind_nbytes > 0) {
+        amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes);
+        u->sink->thread_info.rewind_nbytes = 0;
+    }
+
+    pa_sink_process_rewind(u->sink, amount);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_max_request_within_thread(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_detach_within_thread(u->sink);
+
+    pa_sink_set_rtpoll(u->sink, NULL);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll);
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+    pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i));
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_attach_within_thread(u->sink);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* The order here matters! We first kill the sink so that streams
+     * can properly be moved away while the sink input is still connected
+     * to the master. */
+    pa_sink_input_cork(u->sink_input, true);
+    pa_sink_unlink(u->sink);
+    pa_sink_input_unlink(u->sink_input);
+
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    pa_sink_unref(u->sink);
+    u->sink = NULL;
+
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main context */
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (dest) {
+        pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
+        pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
+    } else
+        pa_sink_set_asyncmsgq(u->sink, NULL);
+
+    if (u->auto_desc && dest) {
+        const char *k;
+        pa_proplist *pl;
+
+        pl = pa_proplist_new();
+        k = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : dest->name);
+
+        pa_sink_update_proplist(u->sink, PA_UPDATE_REPLACE, pl);
+        pa_proplist_free(pl);
+    }
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_sample_spec ss;
+    pa_resample_method_t resample_method = PA_RESAMPLER_INVALID;
+    pa_channel_map sink_map, stream_map;
+    pa_modargs *ma;
+    pa_sink *master;
+    pa_sink_input_new_data sink_input_data;
+    pa_sink_new_data sink_data;
+    bool remix = true;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) {
+        pa_log("Master sink not found");
+        goto fail;
+    }
+
+    ss = master->sample_spec;
+    sink_map = master->channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &sink_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    stream_map = sink_map;
+    if (pa_modargs_get_channel_map(ma, "master_channel_map", &stream_map) < 0) {
+        pa_log("Invalid master channel map");
+        goto fail;
+    }
+
+    if (stream_map.channels != ss.channels) {
+        pa_log("Number of channels doesn't match");
+        goto fail;
+    }
+
+    if (pa_channel_map_equal(&stream_map, &master->channel_map))
+        pa_log_warn("No remapping configured, proceeding nonetheless!");
+
+    if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
+        pa_log("Invalid boolean remix parameter");
+        goto fail;
+    }
+
+    if (pa_modargs_get_resample_method(ma, &resample_method) < 0) {
+        pa_log("Invalid resampling method");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    m->userdata = u;
+
+    /* Create sink */
+    pa_sink_new_data_init(&sink_data);
+    sink_data.driver = __FILE__;
+    sink_data.module = m;
+    if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+        sink_data.name = pa_sprintf_malloc("%s.remapped", master->name);
+    pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+    pa_sink_new_data_set_channel_map(&sink_data, &sink_map);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&sink_data);
+        goto fail;
+    }
+
+    if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+        const char *k;
+
+        k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name);
+    }
+
+    u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY));
+    pa_sink_new_data_done(&sink_data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->set_state = sink_set_state;
+    u->sink->update_requested_latency = sink_update_requested_latency;
+    u->sink->request_rewind = sink_request_rewind;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
+
+    /* Create sink input */
+    pa_sink_input_new_data_init(&sink_input_data);
+    sink_input_data.driver = __FILE__;
+    sink_input_data.module = m;
+    pa_sink_input_new_data_set_sink(&sink_input_data, master, false);
+    sink_input_data.origin_sink = u->sink;
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+    pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map);
+    sink_input_data.flags = (remix ? 0 : PA_SINK_INPUT_NO_REMIX) | PA_SINK_INPUT_START_CORKED;
+    sink_input_data.resample_method = resample_method;
+
+    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
+    pa_sink_input_new_data_done(&sink_input_data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->update_max_request = sink_input_update_max_request_cb;
+    u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+    u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb;
+    u->sink_input->attach = sink_input_attach_cb;
+    u->sink_input->detach = sink_input_detach_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->moving = sink_input_moving_cb;
+    u->sink_input->userdata = u;
+
+    u->sink->input_to_master = u->sink_input;
+
+    /* The order here is important. The input must be put first,
+     * otherwise streams might attach to the sink before the sink
+     * input is attached to the master. */
+    pa_sink_input_put(u->sink_input);
+    pa_sink_put(u->sink);
+    pa_sink_input_cork(u->sink_input, false);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_sink_linked_by(u->sink);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    /* See comments in sink_input_kill_cb() above regarding
+     * destruction order! */
+
+    if (u->sink_input)
+        pa_sink_input_cork(u->sink_input, true);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+    }
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-remap-source.c b/src/modules/module-remap-source.c
new file mode 100644 (file)
index 0000000..281d651
--- /dev/null
@@ -0,0 +1,454 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2013 bct electronic GmbH
+    Contributor: Stefan Huber <s.huber@bct-electronic.com>
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/ltdl-helper.h>
+#include <pulsecore/mix.h>
+
+#include "module-remap-source-symdef.h"
+
+PA_MODULE_AUTHOR("Stefan Huber");
+PA_MODULE_DESCRIPTION("Virtual channel remapping source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "source_name=<name for the source> "
+        "source_properties=<properties for the source> "
+        "master=<name of source to filter> "
+        "master_channel_map=<channel map> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map> "
+        "resample_method=<resampler> "
+        "remix=<remix channels?>");
+
+struct userdata {
+    pa_module *module;
+
+    pa_source *source;
+    pa_source_output *source_output;
+
+    bool auto_desc;
+};
+
+static const char* const valid_modargs[] = {
+    "source_name",
+    "source_properties",
+    "master",
+    "master_channel_map",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "resample_method",
+    "remix",
+    NULL
+};
+
+/* Called from I/O thread context */
+static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY:
+
+            /* The source is _put() before the source output is, so let's
+             * make sure we don't access it in that time. Also, the
+             * source output is first shut down, the source second. */
+            if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
+                !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            *((int64_t*) data) =
+
+                /* Get the latency of the master source */
+                pa_source_get_latency_within_thread(u->source_output->source, true) +
+                /* Add the latency internal to our source output on top */
+                pa_bytes_to_usec(pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq), &u->source_output->source->sample_spec);
+
+            return 0;
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int source_set_state_cb(pa_source *s, pa_source_state_t state) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(state) ||
+        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
+        return 0;
+
+    pa_source_output_cork(u->source_output, state == PA_SOURCE_SUSPENDED);
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void source_update_requested_latency_cb(pa_source *s) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
+        !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state))
+        return;
+
+    pa_log_debug("Source update requested latency.");
+
+    /* Just hand this one over to the master source */
+    pa_source_output_set_requested_latency_within_thread(
+            u->source_output,
+            pa_source_get_requested_latency_within_thread(s));
+}
+
+/* Called from output thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        return;
+
+    if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) {
+        pa_log("push when no link?");
+        return;
+    }
+
+    pa_source_post(u->source, chunk);
+}
+
+/* Called from output thread context */
+static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    /* If the source is not yet linked, there is nothing to rewind */
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_process_rewind(u->source, nbytes);
+}
+
+/* Called from output thread context */
+static void source_output_detach_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_detach_within_thread(u->source);
+
+    pa_source_set_rtpoll(u->source, NULL);
+}
+
+/* Called from output thread context */
+static void source_output_attach_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    pa_source_set_rtpoll(u->source, o->source->thread_info.rtpoll);
+    pa_source_set_latency_range_within_thread(u->source, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
+    pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency);
+    pa_source_set_max_rewind_within_thread(u->source, pa_source_output_get_max_rewind(o));
+
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_attach_within_thread(u->source);
+}
+
+/* Called from main thread */
+static void source_output_kill_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert_se(u = o->userdata);
+
+    /* The order here matters! We first kill the source so that streams
+     * can properly be moved away while the source output is still connected
+     * to the master. */
+    pa_source_output_cork(u->source_output, true);
+    pa_source_unlink(u->source);
+    pa_source_output_unlink(u->source_output);
+
+    pa_source_output_unref(u->source_output);
+    u->source_output = NULL;
+
+    pa_source_unref(u->source);
+    u->source = NULL;
+
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from output thread context except when cork() is called without valid source. */
+static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_se(u = o->userdata);
+
+    pa_log_debug("Source output %d state %d.", o->index, state);
+}
+
+/* Called from main thread */
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert_se(u = o->userdata);
+
+    if (dest) {
+        pa_source_set_asyncmsgq(u->source, dest->asyncmsgq);
+        pa_source_update_flags(u->source, PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY, dest->flags);
+    } else
+        pa_source_set_asyncmsgq(u->source, NULL);
+
+    if (u->auto_desc && dest) {
+        const char *k;
+        pa_proplist *pl;
+
+        pl = pa_proplist_new();
+        k = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : dest->name);
+
+        pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, pl);
+        pa_proplist_free(pl);
+    }
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_sample_spec ss;
+    pa_resample_method_t resample_method = PA_RESAMPLER_INVALID;
+    pa_channel_map source_map, stream_map;
+    pa_modargs *ma;
+    pa_source *master;
+    pa_source_output_new_data source_output_data;
+    pa_source_new_data source_data;
+    bool remix = true;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SOURCE))) {
+        pa_log("Master source not found.");
+        goto fail;
+    }
+
+    ss = master->sample_spec;
+    source_map = master->channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &source_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map.");
+        goto fail;
+    }
+
+    stream_map = source_map;
+    if (pa_modargs_get_channel_map(ma, "master_channel_map", &stream_map) < 0) {
+        pa_log("Invalid master channel map.");
+        goto fail;
+    }
+
+    if (stream_map.channels != ss.channels) {
+        pa_log("Number of channels doesn't match.");
+        goto fail;
+    }
+
+    if (pa_channel_map_equal(&stream_map, &master->channel_map))
+        pa_log_warn("No remapping configured, proceeding nonetheless!");
+
+    if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
+        pa_log("Invalid boolean remix parameter.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_resample_method(ma, &resample_method) < 0) {
+        pa_log("Invalid resampling method");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    m->userdata = u;
+
+    /* Create source */
+    pa_source_new_data_init(&source_data);
+    source_data.driver = __FILE__;
+    source_data.module = m;
+    if (!(source_data.name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
+        source_data.name = pa_sprintf_malloc("%s.remapped", master->name);
+    pa_source_new_data_set_sample_spec(&source_data, &ss);
+    pa_source_new_data_set_channel_map(&source_data, &source_map);
+    pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+    pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+
+    if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties.");
+        pa_source_new_data_done(&source_data);
+        goto fail;
+    }
+
+    if ((u->auto_desc = !pa_proplist_contains(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+        const char *k;
+
+        k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name);
+    }
+
+    u->source = pa_source_new(m->core, &source_data, master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY));
+    pa_source_new_data_done(&source_data);
+
+    if (!u->source) {
+        pa_log("Failed to create source.");
+        goto fail;
+    }
+
+    u->source->parent.process_msg = source_process_msg_cb;
+    u->source->set_state = source_set_state_cb;
+    u->source->update_requested_latency = source_update_requested_latency_cb;
+
+    u->source->userdata = u;
+
+    pa_source_set_asyncmsgq(u->source, master->asyncmsgq);
+
+    /* Create source output */
+    pa_source_output_new_data_init(&source_output_data);
+    source_output_data.driver = __FILE__;
+    source_output_data.module = m;
+    pa_source_output_new_data_set_source(&source_output_data, master, false);
+    source_output_data.destination_source = u->source;
+
+    pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
+    pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
+    pa_source_output_new_data_set_channel_map(&source_output_data, &stream_map);
+    source_output_data.flags = (remix ? 0 : PA_SOURCE_OUTPUT_NO_REMIX) | PA_SOURCE_OUTPUT_START_CORKED;
+    source_output_data.resample_method = resample_method;
+
+    pa_source_output_new(&u->source_output, m->core, &source_output_data);
+    pa_source_output_new_data_done(&source_output_data);
+
+    if (!u->source_output)
+        goto fail;
+
+    u->source_output->push = source_output_push_cb;
+    u->source_output->process_rewind = source_output_process_rewind_cb;
+    u->source_output->kill = source_output_kill_cb;
+    u->source_output->attach = source_output_attach_cb;
+    u->source_output->detach = source_output_detach_cb;
+    u->source_output->state_change = source_output_state_change_cb;
+    u->source_output->moving = source_output_moving_cb;
+    u->source_output->userdata = u;
+
+    u->source->output_from_master = u->source_output;
+
+    /* The order here is important. The output must be put first,
+     * otherwise streams might attach to the source before the
+     * source output is attached to the master. */
+    pa_source_output_put(u->source_output);
+    pa_source_put(u->source);
+    pa_source_output_cork(u->source_output, false);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_source_linked_by(u->source);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    /* See comments in source_output_kill_cb() above regarding
+     * destruction order! */
+
+    if (u->source_output)
+        pa_source_output_cork(u->source_output, true);
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->source_output) {
+        pa_source_output_unlink(u->source_output);
+        pa_source_output_unref(u->source_output);
+    }
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
new file mode 100644 (file)
index 0000000..d3c9953
--- /dev/null
@@ -0,0 +1,357 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+
+#include "module-rescue-streams-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("When a sink/source is removed, try to move its streams to the default sink/source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+static const char* const valid_modargs[] = {
+    NULL,
+};
+
+struct userdata {
+    pa_hook_slot
+        *sink_unlink_slot,
+        *source_unlink_slot,
+        *sink_input_move_fail_slot,
+        *source_output_move_fail_slot;
+};
+
+static pa_source* find_source_from_port(pa_core *c, pa_device_port *port) {
+    pa_source *target;
+    uint32_t idx;
+    void *state;
+    pa_device_port *p;
+
+    if (!port)
+        return NULL;
+
+    PA_IDXSET_FOREACH(target, c->sources, idx)
+        PA_HASHMAP_FOREACH(p, target->ports, state)
+            if (port == p)
+                return target;
+
+    return NULL;
+}
+
+static pa_sink* find_sink_from_port(pa_core *c, pa_device_port *port) {
+    pa_sink *target;
+    uint32_t idx;
+    void *state;
+    pa_device_port *p;
+
+    if (!port)
+        return NULL;
+
+    PA_IDXSET_FOREACH(target, c->sinks, idx)
+        PA_HASHMAP_FOREACH(p, target->ports, state)
+            if (port == p)
+                return target;
+
+    return NULL;
+}
+
+static void build_group_ports(pa_hashmap *g_ports, pa_hashmap *s_ports) {
+    void *state;
+    pa_device_port *p;
+
+    if (!g_ports || !s_ports)
+        return;
+
+    PA_HASHMAP_FOREACH(p, s_ports, state)
+        pa_hashmap_put(g_ports, p, p);
+}
+
+static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip) {
+    pa_sink *target, *fb_sink = NULL;
+    uint32_t idx;
+    pa_hashmap *all_ports;
+    pa_device_port *best_port;
+
+    pa_assert(c);
+    pa_assert(i);
+
+    if (c->default_sink && c->default_sink != skip && pa_sink_input_may_move_to(i, c->default_sink))
+        return c->default_sink;
+
+    all_ports = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    PA_IDXSET_FOREACH(target, c->sinks, idx) {
+        if (target == c->default_sink)
+            continue;
+
+        if (target == skip)
+            continue;
+
+        if (!PA_SINK_IS_LINKED(pa_sink_get_state(target)))
+            continue;
+
+        if (!pa_sink_input_may_move_to(i, target))
+            continue;
+
+        if (!fb_sink)
+            fb_sink = target;
+
+        build_group_ports(all_ports, target->ports);
+    }
+
+    best_port = pa_device_port_find_best(all_ports);
+
+    pa_hashmap_free(all_ports);
+
+    if (best_port)
+        target = find_sink_from_port(c, best_port);
+    else
+        target = fb_sink;
+
+    if (!target)
+       pa_log_debug("No evacuation sink found.");
+
+    return target;
+}
+
+static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+    pa_sink_input *i;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(sink);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    if (pa_idxset_size(sink->inputs) <= 0) {
+        pa_log_debug("No sink inputs to move away.");
+        return PA_HOOK_OK;
+    }
+
+    PA_IDXSET_FOREACH(i, sink->inputs, idx) {
+        pa_sink *target;
+
+        if (!(target = find_evacuation_sink(c, i, sink)))
+            continue;
+
+        if (pa_sink_input_move_to(i, target, false) < 0)
+            pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
+                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        else
+            pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
+                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_move_fail_hook_callback(pa_core *c, pa_sink_input *i, void *userdata) {
+    pa_sink *target;
+
+    pa_assert(c);
+    pa_assert(i);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    if (!(target = find_evacuation_sink(c, i, NULL)))
+        return PA_HOOK_OK;
+
+    if (pa_sink_input_finish_move(i, target, false) < 0) {
+        pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
+                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        return PA_HOOK_OK;
+
+    } else {
+        pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
+                    pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        return PA_HOOK_STOP;
+    }
+}
+
+static pa_source* find_evacuation_source(pa_core *c, pa_source_output *o, pa_source *skip) {
+    pa_source *target, *fb_source = NULL;
+    uint32_t idx;
+    pa_hashmap *all_ports;
+    pa_device_port *best_port;
+
+    pa_assert(c);
+    pa_assert(o);
+
+    if (c->default_source && c->default_source != skip && pa_source_output_may_move_to(o, c->default_source))
+        return c->default_source;
+
+    all_ports = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    PA_IDXSET_FOREACH(target, c->sources, idx) {
+        if (target == c->default_source)
+            continue;
+
+        if (target == skip)
+            continue;
+
+        /* We only move to a monitor source if we're already on one */
+        if (skip && !target->monitor_of != !skip->monitor_of)
+            continue;
+
+        if (!PA_SOURCE_IS_LINKED(pa_source_get_state(target)))
+            continue;
+
+        if (!pa_source_output_may_move_to(o, target))
+            continue;
+
+        if (!fb_source)
+            fb_source = target;
+
+        build_group_ports(all_ports, target->ports);
+    }
+
+    best_port = pa_device_port_find_best(all_ports);
+
+    pa_hashmap_free(all_ports);
+
+    if (best_port)
+        target = find_source_from_port(c, best_port);
+    else
+        target = fb_source;
+
+    if (!target)
+        pa_log_debug("No evacuation source found.");
+
+    return target;
+}
+
+static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, void* userdata) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(source);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    if (pa_idxset_size(source->outputs) <= 0) {
+        pa_log_debug("No source outputs to move away.");
+        return PA_HOOK_OK;
+    }
+
+    PA_IDXSET_FOREACH(o, source->outputs, idx) {
+        pa_source *target;
+
+        if (!(target = find_evacuation_source(c, o, source)))
+            continue;
+
+        if (pa_source_output_move_to(o, target, false) < 0)
+            pa_log_info("Failed to move source output %u \"%s\" to %s.", o->index,
+                        pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        else
+            pa_log_info("Successfully moved source output %u \"%s\" to %s.", o->index,
+                        pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_move_fail_hook_callback(pa_core *c, pa_source_output *i, void *userdata) {
+    pa_source *target;
+
+    pa_assert(c);
+    pa_assert(i);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    if (!(target = find_evacuation_source(c, i, NULL)))
+        return PA_HOOK_OK;
+
+    if (pa_source_output_finish_move(i, target, false) < 0) {
+        pa_log_info("Failed to move source output %u \"%s\" to %s.", i->index,
+                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        return PA_HOOK_OK;
+
+    } else {
+        pa_log_info("Successfully moved source output %u \"%s\" to %s.", i->index,
+                    pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        return PA_HOOK_STOP;
+    }
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        return -1;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+
+    /* A little bit later than module-stream-restore, module-intended-roles... */
+    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_unlink_hook_callback, u);
+    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) source_unlink_hook_callback, u);
+
+    u->sink_input_move_fail_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_input_move_fail_hook_callback, u);
+    u->source_output_move_fail_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], PA_HOOK_LATE+20, (pa_hook_cb_t) source_output_move_fail_hook_callback, u);
+
+    pa_modargs_free(ma);
+    return 0;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink_unlink_slot)
+        pa_hook_slot_free(u->sink_unlink_slot);
+    if (u->source_unlink_slot)
+        pa_hook_slot_free(u->source_unlink_slot);
+
+    if (u->sink_input_move_fail_slot)
+        pa_hook_slot_free(u->sink_input_move_fail_slot);
+    if (u->source_output_move_fail_slot)
+        pa_hook_slot_free(u->source_output_move_fail_slot);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-role-cork.c b/src/modules/module-role-cork.c
new file mode 100644 (file)
index 0000000..d71cc38
--- /dev/null
@@ -0,0 +1,58 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core.h>
+#include <stream-interaction.h>
+
+#include "module-role-cork-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Mute & cork streams with certain roles while others exist");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+        "trigger_roles=<Comma separated list of roles which will trigger a cork> "
+        "cork_roles=<Comma separated list of roles which will be corked> "
+        "global=<Should we operate globally or only inside the same device?>");
+
+static const char* const valid_modargs[] = {
+    "trigger_roles",
+    "cork_roles",
+    "global",
+    NULL
+};
+
+int pa__init(pa_module *m) {
+
+    pa_assert(m);
+
+    return pa_stream_interaction_init(m, valid_modargs);
+}
+
+void pa__done(pa_module *m) {
+
+    pa_assert(m);
+
+    pa_stream_interaction_done(m);
+}
diff --git a/src/modules/module-role-ducking.c b/src/modules/module-role-ducking.c
new file mode 100644 (file)
index 0000000..add2d36
--- /dev/null
@@ -0,0 +1,61 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2012 Flavio Ceolin <flavio.ceolin@profusion.mobi>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core.h>
+#include <stream-interaction.h>
+
+#include "module-role-ducking-symdef.h"
+
+PA_MODULE_AUTHOR("Flavio Ceolin <flavio.ceolin@profusion.mobi>");
+PA_MODULE_DESCRIPTION("Apply a ducking effect based on streams roles");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+        "trigger_roles=<Comma(and slash) separated list of roles which will trigger a ducking. Slash can divide the roles into groups>"
+        "ducking_roles=<Comma(and slash) separated list of roles which will be ducked. Slash can divide the roles into groups>"
+        "global=<Should we operate globally or only inside the same device?>"
+        "volume=<Volume for the attenuated streams. Default: -20dB. If trigger_roles and ducking_roles are separated by slash, use slash for dividing volume group>"
+);
+
+static const char* const valid_modargs[] = {
+    "trigger_roles",
+    "ducking_roles",
+    "global",
+    "volume",
+    NULL
+};
+
+int pa__init(pa_module *m) {
+
+    pa_assert(m);
+
+    return pa_stream_interaction_init(m, valid_modargs);
+}
+
+void pa__done(pa_module *m) {
+
+    pa_assert(m);
+
+    pa_stream_interaction_done(m);
+}
diff --git a/src/modules/module-rygel-media-server.c b/src/modules/module-rygel-media-server.c
new file mode 100644 (file)
index 0000000..e2c2e6f
--- /dev/null
@@ -0,0 +1,1136 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/dbus-shared.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/mime-type.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/protocol-http.h>
+#include <pulsecore/parseaddr.h>
+
+#include "module-rygel-media-server-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("UPnP MediaServer Plugin for Rygel");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE("display_name=<UPnP Media Server name>");
+
+/* This implements http://live.gnome.org/Rygel/MediaServer2Spec */
+
+#define SERVICE_NAME "org.gnome.UPnP.MediaServer2.PulseAudio"
+
+#define OBJECT_ROOT "/org/gnome/UPnP/MediaServer2/PulseAudio"
+#define OBJECT_SINKS "/org/gnome/UPnP/MediaServer2/PulseAudio/Sinks"
+#define OBJECT_SOURCES "/org/gnome/UPnP/MediaServer2/PulseAudio/Sources"
+
+#define CONTAINER_INTROSPECT_XML_PREFIX                                 \
+    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
+    "<node>"                                                            \
+    " <!-- If you are looking for documentation make sure to check out" \
+    "      http://live.gnome.org/Rygel/MediaServer2Spec -->"            \
+    " <interface name=\"org.gnome.UPnP.MediaContainer2\">"              \
+    "  <method name='ListChildren'>"                                    \
+    "   <arg direction='in' name='offset' type='u' />"                  \
+    "   <arg direction='in' name='max' type='u' />"                     \
+    "   <arg direction='in' name='filter' type='as' />"                 \
+    "   <arg direction='out' type='aa{sv}' />"                          \
+    "  </method>"                                                       \
+    "  <method name='ListContainers'>"                                  \
+    "   <arg direction='in' name='offset' type='u' />"                  \
+    "   <arg direction='in' name='max' type='u' />"                     \
+    "   <arg direction='in' name='filter' type='as' />"                 \
+    "   <arg direction='out' type='aa{sv}' />"                          \
+    "  </method>"                                                       \
+    "  <method name='ListItems'>"                                       \
+    "   <arg direction='in' name='offset' type='u' />"                  \
+    "   <arg direction='in' name='max' type='u' />"                     \
+    "   <arg direction='in' name='filter' type='as' />"                 \
+    "   <arg direction='out' type='aa{sv}' />"                          \
+    "  </method>"                                                       \
+    "  <signal name=\"Updated\">"                                       \
+    "   <arg name=\"path\" type=\"o\"/>"                                \
+    "  </signal>"                                                       \
+    "  <property name=\"ChildCount\" type=\"u\" access=\"read\"/>"      \
+    "  <property name=\"ItemCount\" type=\"u\" access=\"read\"/>"       \
+    "  <property name=\"ContainerCount\" type=\"u\" access=\"read\"/>"  \
+    "  <property name=\"Searchable\" type=\"b\" access=\"read\"/>"      \
+    " </interface>"                                                     \
+    " <interface name=\"org.gnome.UPnP.MediaObject2\">"                 \
+    "  <property name=\"Parent\" type=\"s\" access=\"read\"/>"          \
+    "  <property name=\"Type\" type=\"s\" access=\"read\"/>"            \
+    "  <property name=\"Path\" type=\"s\" access=\"read\"/>"            \
+    "  <property name=\"DisplayName\" type=\"s\" access=\"read\"/>"     \
+    " </interface>"                                                     \
+    " <interface name=\"org.freedesktop.DBus.Properties\">"             \
+    "  <method name=\"Get\">"                                           \
+    "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>"          \
+    "   <arg name=\"property\" direction=\"in\" type=\"s\"/>"           \
+    "   <arg name=\"value\" direction=\"out\" type=\"v\"/>"             \
+    "  </method>"                                                       \
+    "  <method name=\"GetAll\">"                                        \
+    "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>"          \
+    "   <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>"    \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
+    "  <method name=\"Introspect\">"                                    \
+    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
+    "  </method>"                                                       \
+    " </interface>"
+
+#define CONTAINER_INTROSPECT_XML_POSTFIX                                \
+    "</node>"
+
+#define ROOT_INTROSPECT_XML                                             \
+    CONTAINER_INTROSPECT_XML_PREFIX                                     \
+    "<node name=\"Sinks\"/>"                                            \
+    "<node name=\"Sources\"/>"                                          \
+    CONTAINER_INTROSPECT_XML_POSTFIX
+
+#define ITEM_INTROSPECT_XML                                             \
+    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
+    "<node>"                                                            \
+    " <!-- If you are looking for documentation make sure to check out" \
+    "      http://live.gnome.org/Rygel/MediaProvider2Spec -->"          \
+    " <interface name=\"org.gnome.UPnP.MediaItem2\">"                   \
+    "  <property name=\"URLs\" type=\"as\" access=\"read\"/>"           \
+    "  <property name=\"MIMEType\" type=\"s\" access=\"read\"/>"        \
+    "  <property name=\"DLNAProfile\" type=\"s\" access=\"read\"/>"        \
+    " </interface>"                                                     \
+    " <interface name=\"org.gnome.UPnP.MediaObject2\">"                 \
+    "  <property name=\"Parent\" type=\"s\" access=\"read\"/>"          \
+    "  <property name=\"Type\" type=\"s\" access=\"read\"/>"            \
+    "  <property name=\"Path\" type=\"s\" access=\"read\"/>"            \
+    "  <property name=\"DisplayName\" type=\"s\" access=\"read\"/>"     \
+    " </interface>"                                                     \
+    " <interface name=\"org.freedesktop.DBus.Properties\">"             \
+    "  <method name=\"Get\">"                                           \
+    "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>"          \
+    "   <arg name=\"property\" direction=\"in\" type=\"s\"/>"           \
+    "   <arg name=\"value\" direction=\"out\" type=\"v\"/>"             \
+    "  </method>"                                                       \
+    "  <method name=\"GetAll\">"                                        \
+    "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>"          \
+    "   <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>"    \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
+    "  <method name=\"Introspect\">"                                    \
+    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    "</node>"
+
+static const char* const valid_modargs[] = {
+    "display_name",
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    pa_dbus_connection *bus;
+    bool got_name:1;
+
+    char *display_name;
+
+    pa_hook_slot *source_new_slot, *source_unlink_slot;
+
+    pa_http_protocol *http;
+};
+
+static char *compute_url(const struct userdata *u, const char *name);
+
+static void send_signal(struct userdata *u, pa_source *s) {
+    DBusMessage *m;
+    const char *parent;
+
+    pa_assert(u);
+    pa_source_assert_ref(s);
+
+    if (u->core->state == PA_CORE_SHUTDOWN)
+        return;
+
+    if (s->monitor_of)
+        parent = OBJECT_SINKS;
+    else
+        parent = OBJECT_SOURCES;
+
+    pa_assert_se(m = dbus_message_new_signal(parent, "org.gnome.UPnP.MediaContainer2", "Updated"));
+    pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->bus), m, NULL));
+
+    dbus_message_unref(m);
+}
+
+static pa_hook_result_t source_new_or_unlink_cb(pa_core *c, pa_source *s, struct userdata *u) {
+    pa_assert(c);
+    pa_source_assert_ref(s);
+
+    send_signal(u, s);
+
+    return PA_HOOK_OK;
+}
+
+static bool message_is_property_get(DBusMessage *m, const char *interface, const char *property) {
+    const char *i, *p;
+    DBusError error;
+
+    dbus_error_init(&error);
+
+    pa_assert(m);
+
+    if (!dbus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get"))
+        return false;
+
+    if (!dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &i, DBUS_TYPE_STRING, &p, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+        dbus_error_free(&error);
+        return false;
+    }
+
+    return pa_streq(i, interface) && pa_streq(p, property);
+}
+
+static bool message_is_property_get_all(DBusMessage *m, const char *interface) {
+    const char *i;
+    DBusError error;
+
+    dbus_error_init(&error);
+
+    pa_assert(m);
+
+    if (!dbus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "GetAll"))
+        return false;
+
+    if (!dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+        dbus_error_free(&error);
+        return false;
+    }
+
+    return pa_streq(i, interface);
+}
+
+static void append_variant_object_array(DBusMessage *m, DBusMessageIter *iter, const char *path[], unsigned n) {
+    DBusMessageIter _iter, variant, array;
+    unsigned c;
+
+    pa_assert(m);
+    pa_assert(path);
+
+    if (!iter) {
+        dbus_message_iter_init_append(m, &_iter);
+        iter = &_iter;
+    }
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "ao", &variant));
+    pa_assert_se(dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "o", &array));
+
+    for (c = 0; c < n; c++)
+        pa_assert_se(dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH, path + c));
+
+    pa_assert_se(dbus_message_iter_close_container(&variant, &array));
+    pa_assert_se(dbus_message_iter_close_container(iter, &variant));
+}
+
+static void append_variant_string(DBusMessage *m, DBusMessageIter *iter, const char *s) {
+    DBusMessageIter _iter, sub;
+
+    pa_assert(m);
+    pa_assert(s);
+
+    if (!iter) {
+        dbus_message_iter_init_append(m, &_iter);
+        iter = &_iter;
+    }
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "s", &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &s));
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_variant_object(DBusMessage *m, DBusMessageIter *iter, const char *s) {
+    DBusMessageIter _iter, sub;
+
+    pa_assert(m);
+    pa_assert(s);
+
+    if (!iter) {
+        dbus_message_iter_init_append(m, &_iter);
+        iter = &_iter;
+    }
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "o", &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &s));
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_variant_unsigned(DBusMessage *m, DBusMessageIter *iter, unsigned u) {
+    DBusMessageIter _iter, sub;
+
+    pa_assert(m);
+
+    if (!iter) {
+        dbus_message_iter_init_append(m, &_iter);
+        iter = &_iter;
+    }
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "u", &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u));
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_variant_boolean(DBusMessage *m, DBusMessageIter *iter, dbus_bool_t b) {
+    DBusMessageIter _iter, sub;
+
+    pa_assert(m);
+
+    if (!iter) {
+        dbus_message_iter_init_append(m, &_iter);
+        iter = &_iter;
+    }
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "b", &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &b));
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_variant_urls(DBusMessage *m, DBusMessageIter *iter, const struct userdata *u, pa_sink *sink, pa_source *source) {
+    DBusMessageIter _iter, sub, array;
+    char *url;
+
+    pa_assert(m);
+    pa_assert(u);
+    pa_assert(sink || source);
+
+    if (!iter) {
+        dbus_message_iter_init_append(m, &_iter);
+        iter = &_iter;
+    }
+
+    url = compute_url(u, sink ? sink->monitor_source->name : source->name);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "as", &sub));
+    pa_assert_se(dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "s", &array));
+    pa_assert_se(dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &url));
+    pa_assert_se(dbus_message_iter_close_container(&sub, &array));
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+
+    pa_xfree(url);
+}
+
+static void append_variant_mime_type(DBusMessage *m, DBusMessageIter *iter, pa_sink *sink, pa_source *source) {
+    char *mime_type;
+
+    pa_assert(sink || source);
+
+    if (sink)
+        mime_type = pa_sample_spec_to_mime_type_mimefy(&sink->sample_spec, &sink->channel_map);
+    else
+        mime_type = pa_sample_spec_to_mime_type_mimefy(&source->sample_spec, &source->channel_map);
+
+    append_variant_string(m, iter, mime_type);
+
+    pa_xfree(mime_type);
+}
+
+static void append_variant_item_display_name(DBusMessage *m, DBusMessageIter *iter, pa_sink *sink, pa_source *source) {
+    const char *display_name;
+
+    pa_assert(sink || source);
+
+    display_name = pa_strna(pa_proplist_gets(sink ? sink->proplist : source->proplist, PA_PROP_DEVICE_DESCRIPTION));
+    append_variant_string(m, iter, display_name);
+}
+
+PA_GCC_UNUSED
+static void append_property_dict_entry_object_array(DBusMessage *m, DBusMessageIter *iter, const char *name, const char *path[], unsigned n) {
+    DBusMessageIter sub;
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name));
+    append_variant_object_array(m, &sub, path, n);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_property_dict_entry_string(DBusMessage *m, DBusMessageIter *iter, const char *name, const char *value) {
+    DBusMessageIter sub;
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name));
+    append_variant_string(m, &sub, value);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_property_dict_entry_object(DBusMessage *m, DBusMessageIter *iter, const char *name, const char *value) {
+    DBusMessageIter sub;
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name));
+    append_variant_object(m, &sub, value);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_property_dict_entry_unsigned(DBusMessage *m, DBusMessageIter *iter, const char *name, unsigned u) {
+    DBusMessageIter sub;
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name));
+    append_variant_unsigned(m, &sub, u);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_property_dict_entry_boolean(DBusMessage *m, DBusMessageIter *iter, const char *name, dbus_bool_t b) {
+    DBusMessageIter sub;
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name));
+    append_variant_boolean(m, &sub, b);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_property_dict_entry_urls(DBusMessage *m, DBusMessageIter *iter, const struct userdata *u, pa_sink *sink, pa_source *source) {
+    DBusMessageIter sub;
+    const char *property_name = "URLs";
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property_name));
+    append_variant_urls(m, &sub, u, sink, source);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_property_dict_entry_mime_type(DBusMessage *m, DBusMessageIter *iter, pa_sink *sink, pa_source *source) {
+    DBusMessageIter sub;
+    const char *property_name = "MIMEType";
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property_name));
+    append_variant_mime_type(m, &sub, sink, source);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_property_dict_entry_item_display_name(DBusMessage *m, DBusMessageIter *iter, pa_sink *sink, pa_source *source) {
+    DBusMessageIter sub;
+    const char *property_name = "DisplayName";
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property_name));
+    append_variant_item_display_name(m, &sub, sink, source);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static bool get_mediacontainer2_list_args(DBusMessage *m, unsigned *offset, unsigned *max_entries, char ***filter, int *filter_len) {
+    DBusError error;
+
+    dbus_error_init(&error);
+
+    pa_assert(m);
+    pa_assert(offset);
+    pa_assert(max_entries);
+    pa_assert(filter);
+
+    if (!dbus_message_get_args(m, &error, DBUS_TYPE_UINT32, offset, DBUS_TYPE_UINT32, max_entries, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, filter, filter_len, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+        dbus_error_free(&error);
+        return false;
+    }
+
+    return true;
+}
+
+static unsigned get_sinks_or_sources_count(const char *path, const struct userdata *u) {
+    unsigned n, k;
+
+    n = pa_idxset_size(u->core->sinks);
+    k = pa_idxset_size(u->core->sources);
+    pa_assert(k >= n);
+
+    return pa_streq(path, OBJECT_SINKS) ? n : k - n;
+}
+
+static void append_sink_or_source_container_mediaobject2_properties(DBusMessage *r, DBusMessageIter *sub, const char *path) {
+    append_property_dict_entry_object(r, sub, "Parent", OBJECT_ROOT);
+    append_property_dict_entry_string(r, sub, "Type", "container");
+    append_property_dict_entry_object(r, sub, "Path", path);
+    append_property_dict_entry_string(r, sub, "DisplayName",
+                                      pa_streq(path, OBJECT_SINKS) ?
+                                      _("Output Devices") :
+                                      _("Input Devices"));
+}
+
+static void append_sink_or_source_container_properties(
+    DBusMessage *r, DBusMessageIter *iter,
+    const char *path, const struct userdata *user_data,
+    char * const * filter, int filter_len) {
+
+    DBusMessageIter sub;
+
+    pa_assert(r);
+    pa_assert(iter);
+    pa_assert(path);
+    pa_assert(filter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+
+    if (filter_len == 1 && (*filter)[0] == '*' && (*filter)[1] == '\0') {
+        append_sink_or_source_container_mediaobject2_properties(r, &sub, path);
+        append_property_dict_entry_unsigned(r, &sub, "ChildCount", get_sinks_or_sources_count(path, user_data));
+        append_property_dict_entry_boolean(r, &sub, "Searchable", FALSE);
+    }
+    else {
+        for (int i = 0; i < filter_len; ++i) {
+            const char *property_name = filter[i];
+            if (pa_streq(property_name, "Parent")) {
+                append_property_dict_entry_object(r, &sub, "Parent", OBJECT_ROOT);
+            }
+            else if (pa_streq(property_name, "Type")) {
+                append_property_dict_entry_string(r, &sub, "Type", "container");
+            }
+            else if (pa_streq(property_name, "Path")) {
+                append_property_dict_entry_object(r, &sub, "Path", path);
+            }
+            else if (pa_streq(property_name, "DisplayName")) {
+                append_property_dict_entry_string(r, &sub, "DisplayName",
+                                                  pa_streq(path, OBJECT_SINKS) ?
+                                                  _("Output Devices") :
+                                                  _("Input Devices"));
+            }
+            else if (pa_streq(property_name, "ChildCount")) {
+                append_property_dict_entry_unsigned(r, &sub, "ChildCount", get_sinks_or_sources_count(path, user_data));
+            }
+            else if (pa_streq(property_name, "Searchable")) {
+                append_property_dict_entry_boolean(r, &sub, "Searchable", FALSE);
+            }
+        }
+    }
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_sink_or_source_item_mediaobject2_properties(DBusMessage *r, DBusMessageIter *sub, const char *path, pa_sink *sink, pa_source *source) {
+    append_property_dict_entry_object(r, sub, "Parent", sink ? OBJECT_SINKS : OBJECT_SOURCES);
+    append_property_dict_entry_string(r, sub, "Type", "audio");
+    append_property_dict_entry_object(r, sub, "Path", path);
+    append_property_dict_entry_item_display_name(r, sub, sink, source);
+}
+
+static void append_sink_or_source_item_properties(
+    DBusMessage *r, DBusMessageIter *iter,
+    const char *path, const struct userdata *user_data,
+    pa_sink *sink, pa_source *source,
+    char * const * filter, int filter_len) {
+
+    DBusMessageIter sub;
+
+    pa_assert(r);
+    pa_assert(iter);
+    pa_assert(path);
+    pa_assert(filter);
+    pa_assert(sink || source);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+
+    if (filter_len == 1 && (*filter)[0] == '*' && (*filter)[1] == '\0') {
+        append_sink_or_source_item_mediaobject2_properties(r, &sub, path, sink, source);
+        append_property_dict_entry_urls(r, &sub, user_data, sink, source);
+        append_property_dict_entry_mime_type(r, &sub, sink, source);
+        append_property_dict_entry_string(r, &sub, "DLNAProfile", "LPCM");
+    }
+    else {
+        for (int i = 0; i < filter_len; ++i) {
+            const char *property_name = filter[i];
+            if (pa_streq(property_name, "Parent")) {
+                append_property_dict_entry_object(r, &sub, "Parent", sink ? OBJECT_SINKS : OBJECT_SOURCES);
+            }
+            else if (pa_streq(property_name, "Type")) {
+                append_property_dict_entry_string(r, &sub, "Type", "audio");
+            }
+            else if (pa_streq(property_name, "Path")) {
+                append_property_dict_entry_object(r, &sub, "Path", path);
+            }
+            else if (pa_streq(property_name, "DisplayName")) {
+                append_property_dict_entry_item_display_name(r, &sub, sink, source);
+            }
+            else if (pa_streq(property_name, "URLs")) {
+                append_property_dict_entry_urls(r, &sub, user_data, sink, source);
+            }
+            else if (pa_streq(property_name, "MIMEType")) {
+                append_property_dict_entry_mime_type(r, &sub, sink, source);
+            }
+            else if (pa_streq(property_name, "DLNAProfile")) {
+                append_property_dict_entry_string(r, &sub, "DLNAProfile", "LPCM");
+            }
+        }
+    }
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static const char *array_root_containers[] = { OBJECT_SINKS, OBJECT_SOURCES };
+static const char *array_no_children[] = { };
+
+static DBusHandlerResult root_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
+    struct userdata *u = userdata;
+    DBusMessage *r = NULL;
+
+    pa_assert(u);
+
+    if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ChildCount")) {
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        append_variant_unsigned(r, NULL, PA_ELEMENTSOF(array_root_containers));
+
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ItemCount")) {
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        append_variant_unsigned(r, NULL, PA_ELEMENTSOF(array_no_children));
+
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ContainerCount")) {
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        append_variant_unsigned(r, NULL, PA_ELEMENTSOF(array_root_containers));
+
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "Searchable")) {
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        append_variant_boolean(r, NULL, FALSE);
+
+    } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaContainer2")) {
+        DBusMessageIter iter, sub;
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        dbus_message_iter_init_append(r, &iter);
+
+        pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+        append_property_dict_entry_unsigned(r, &sub, "ChildCount", PA_ELEMENTSOF(array_root_containers));
+        append_property_dict_entry_unsigned(r, &sub, "ItemCount", PA_ELEMENTSOF(array_no_children));
+        append_property_dict_entry_unsigned(r, &sub, "ContainerCount", PA_ELEMENTSOF(array_root_containers));
+        append_property_dict_entry_boolean(r, &sub, "Searchable", FALSE);
+        pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+    } else if (dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListChildren")
+        || dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListContainers")) {
+        DBusMessageIter iter, sub;
+        unsigned offset, max;
+        char ** filter;
+        int filter_len;
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+
+        dbus_message_iter_init_append(r, &iter);
+        pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "a{sv}", &sub));
+
+        if (get_mediacontainer2_list_args(m, &offset, &max, &filter, &filter_len)) {
+            unsigned end = (max != 0 && offset + max < PA_ELEMENTSOF(array_root_containers))
+                                ? max + offset
+                                : PA_ELEMENTSOF(array_root_containers);
+
+            for (unsigned i = offset; i < end; ++i) {
+                const char *container_path = array_root_containers[i];
+                append_sink_or_source_container_properties(r, &sub, container_path, u, filter, filter_len);
+            }
+
+            dbus_free_string_array(filter);
+        }
+
+        pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+    } else if (dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListItems")) {
+        DBusMessageIter iter, sub;
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+
+        dbus_message_iter_init_append(r, &iter);
+        pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "a{sv}", &sub));
+        pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Parent")) {
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        append_variant_object(r, NULL, OBJECT_ROOT);
+
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Type")) {
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        append_variant_string(r, NULL, "container");
+
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Path")) {
+        const char *path = dbus_message_get_path(m);
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        append_variant_object(r, NULL, path);
+
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "DisplayName")) {
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        append_variant_string(r, NULL, u->display_name);
+
+    } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaObject2")) {
+        DBusMessageIter iter, sub;
+        const char *path = dbus_message_get_path(m);
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        dbus_message_iter_init_append(r, &iter);
+
+        pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+        append_property_dict_entry_object(r, &sub, "Parent", OBJECT_ROOT);
+        append_property_dict_entry_string(r, &sub, "Type", "container");
+        append_property_dict_entry_object(r, &sub, "Path", path);
+        append_property_dict_entry_string(r, &sub, "DisplayName", u->display_name);
+        pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+    } else if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+        const char *xml = ROOT_INTROSPECT_XML;
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
+
+    } else
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (r) {
+        pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->bus), r, NULL));
+        dbus_message_unref(r);
+    }
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static char *compute_url(const struct userdata *u, const char *name) {
+    pa_strlist *i;
+
+    pa_assert(u);
+    pa_assert(name);
+
+    for (i = pa_http_protocol_servers(u->http); i; i = pa_strlist_next(i)) {
+        pa_parsed_address a;
+
+        if (pa_parse_address(pa_strlist_data(i), &a) >= 0 &&
+            (a.type == PA_PARSED_ADDRESS_TCP4 ||
+             a.type == PA_PARSED_ADDRESS_TCP6 ||
+             a.type == PA_PARSED_ADDRESS_TCP_AUTO)) {
+
+            const char *address;
+            char *s;
+
+            if (pa_is_ip_address(a.path_or_host))
+                address = a.path_or_host;
+            else
+                address = "@ADDRESS@";
+
+            if (a.port <= 0)
+                a.port = 4714;
+
+            s = pa_sprintf_malloc("http://%s:%u/listen/source/%s", address, a.port, name);
+
+            pa_xfree(a.path_or_host);
+            return s;
+        }
+
+        pa_xfree(a.path_or_host);
+    }
+
+    return pa_sprintf_malloc("http://@ADDRESS@:4714/listen/source/%s", name);
+}
+
+static DBusHandlerResult sinks_and_sources_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
+    struct userdata *u = userdata;
+    DBusMessage *r = NULL;
+    const char *path;
+
+    pa_assert(u);
+
+    path = dbus_message_get_path(m);
+
+    if (pa_streq(path, OBJECT_SINKS) || pa_streq(path, OBJECT_SOURCES)) {
+
+        /* Container nodes */
+
+        if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ChildCount")
+            || message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ItemCount")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_unsigned(r, NULL, get_sinks_or_sources_count(path, u));
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ContainerCount")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_unsigned(r, NULL, 0);
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "Searchable")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_boolean(r, NULL, FALSE);
+
+        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaContainer2")) {
+            DBusMessageIter iter, sub;
+            unsigned item_count;
+
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            dbus_message_iter_init_append(r, &iter);
+
+            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+
+            item_count = get_sinks_or_sources_count(path, u);
+
+            append_property_dict_entry_unsigned(r, &sub, "ChildCount", item_count);
+            append_property_dict_entry_unsigned(r, &sub, "ItemCount", item_count);
+            append_property_dict_entry_unsigned(r, &sub, "ContainerCount", 0);
+            append_property_dict_entry_boolean(r, &sub, "Searchable", FALSE);
+
+            pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+        } else if (dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListChildren")
+            || dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListItems")) {
+            DBusMessageIter iter, sub;
+            unsigned offset, max;
+            char **filter;
+            int filter_len;
+
+            pa_assert_se(r = dbus_message_new_method_return(m));
+
+            dbus_message_iter_init_append(r, &iter);
+            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "a{sv}", &sub));
+
+            if (get_mediacontainer2_list_args(m, &offset, &max, &filter, &filter_len)) {
+                unsigned end = (max != 0) ? max + offset : UINT_MAX;
+
+                if (pa_streq(path, OBJECT_SINKS)) {
+                    pa_sink *sink;
+                    char sink_path[sizeof(OBJECT_SINKS) + 32];
+                    char *path_end = sink_path + sizeof(OBJECT_SINKS);
+                    unsigned item_index = 0;
+                    uint32_t idx;
+
+                    strcpy(sink_path, OBJECT_SINKS "/");
+
+                    PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
+                        if (item_index >= offset && item_index < end) {
+                            sprintf(path_end, "%u", sink->index);
+                            append_sink_or_source_item_properties(r, &sub, sink_path, u, sink, NULL, filter, filter_len);
+                        }
+                        ++item_index;
+                    }
+                } else {
+                    pa_source *source;
+                    char source_path[sizeof(OBJECT_SOURCES) + 32];
+                    char *path_end = source_path + sizeof(OBJECT_SOURCES);
+                    unsigned item_index = 0;
+                    uint32_t idx;
+
+                    strcpy(source_path, OBJECT_SOURCES "/");
+
+                    PA_IDXSET_FOREACH(source, u->core->sources, idx)
+                        if (!source->monitor_of) {
+                            if (item_index >= offset && item_index < end) {
+                                sprintf(path_end, "%u", source->index);
+                                append_sink_or_source_item_properties(r, &sub, source_path, u, NULL, source, filter, filter_len);
+                            }
+                            ++item_index;
+                        }
+                }
+
+                dbus_free_string_array(filter);
+            }
+
+            pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+        } else if (dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListContainers")) {
+            DBusMessageIter iter, sub;
+
+            pa_assert_se(r = dbus_message_new_method_return(m));
+
+            dbus_message_iter_init_append(r, &iter);
+            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "a{sv}", &sub));
+            pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Parent")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_object(r, NULL, OBJECT_ROOT);
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Type")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_string(r, NULL, "container");
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Path")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_object(r, NULL, path);
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "DisplayName")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_string(r,
+                                  NULL,
+                                  pa_streq(path, OBJECT_SINKS) ?
+                                  _("Output Devices") :
+                                  _("Input Devices"));
+
+        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaObject2")) {
+            DBusMessageIter iter, sub;
+
+            pa_assert_se(r = dbus_message_new_method_return(m));
+
+            dbus_message_iter_init_append(r, &iter);
+            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+            append_sink_or_source_container_mediaobject2_properties(r, &sub, path);
+            pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+        } else if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+            pa_strbuf *sb;
+            char *xml;
+            uint32_t idx;
+
+            sb = pa_strbuf_new();
+            pa_strbuf_puts(sb, CONTAINER_INTROSPECT_XML_PREFIX);
+
+            if (pa_streq(path, OBJECT_SINKS)) {
+                pa_sink *sink;
+
+                PA_IDXSET_FOREACH(sink, u->core->sinks, idx)
+                    pa_strbuf_printf(sb, "<node name=\"%u\"/>", sink->index);
+            } else {
+                pa_source *source;
+
+                PA_IDXSET_FOREACH(source, u->core->sources, idx)
+                    if (!source->monitor_of)
+                        pa_strbuf_printf(sb, "<node name=\"%u\"/>", source->index);
+            }
+
+            pa_strbuf_puts(sb, CONTAINER_INTROSPECT_XML_POSTFIX);
+            xml = pa_strbuf_to_string_free(sb);
+
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
+
+            pa_xfree(xml);
+        } else
+            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    } else {
+        pa_sink *sink = NULL;
+        pa_source *source = NULL;
+
+        /* Child nodes */
+
+        if (pa_startswith(path, OBJECT_SINKS "/"))
+            sink = pa_namereg_get(u->core, path + sizeof(OBJECT_SINKS), PA_NAMEREG_SINK);
+        else if (pa_startswith(path, OBJECT_SOURCES "/"))
+            source = pa_namereg_get(u->core, path + sizeof(OBJECT_SOURCES), PA_NAMEREG_SOURCE);
+
+        if (!sink && !source)
+            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+        if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Parent")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_object(r, NULL, sink ? OBJECT_SINKS : OBJECT_SOURCES);
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Type")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_string(r, NULL, "audio");
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Path")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_object(r, NULL, path);
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "DisplayName")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_item_display_name(r, NULL, sink, source);
+
+        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaObject2")) {
+            DBusMessageIter iter, sub;
+
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            dbus_message_iter_init_append(r, &iter);
+
+            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+            append_sink_or_source_item_mediaobject2_properties(r, &sub, path, sink, source);
+            pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaItem2", "MIMEType")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_mime_type(r, NULL, sink, source);
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaItem2", "DLNAProfile")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_string(r, NULL, "LPCM");
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaItem2", "URLs")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_urls(r, NULL, u, sink, source);
+
+        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaItem2")) {
+            DBusMessageIter iter, sub;
+
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            dbus_message_iter_init_append(r, &iter);
+
+            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+
+            append_property_dict_entry_mime_type(r, &sub, sink, source);
+            append_property_dict_entry_string(r, &sub, "DLNAProfile", "LPCM");
+            append_property_dict_entry_urls(r, &sub, u, sink, source);
+
+            pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+        } else if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+            const char *xml =
+                ITEM_INTROSPECT_XML;
+
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
+
+        } else
+            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+
+    if (r) {
+        pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->bus), r, NULL));
+        dbus_message_unref(r);
+    }
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+int pa__init(pa_module *m) {
+
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    DBusError error;
+    const char *t;
+
+    static const DBusObjectPathVTable vtable_root = {
+        .message_function = root_handler,
+    };
+    static const DBusObjectPathVTable vtable_sinks_and_sources = {
+        .message_function = sinks_and_sources_handler,
+    };
+
+    dbus_error_init(&error);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->http = pa_http_protocol_get(u->core);
+
+    if ((t = pa_modargs_get_value(ma, "display_name", NULL)))
+        u->display_name = pa_utf8_filter(t);
+    else
+        u->display_name = pa_xstrdup(_("Audio on @HOSTNAME@"));
+
+    u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) source_new_or_unlink_cb, u);
+    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_new_or_unlink_cb, u);
+
+    if (!(u->bus = pa_dbus_bus_get(m->core, DBUS_BUS_SESSION, &error))) {
+        pa_log("Failed to get session bus connection: %s", error.message);
+        goto fail;
+    }
+
+    pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(u->bus), OBJECT_ROOT, &vtable_root, u));
+    pa_assert_se(dbus_connection_register_fallback(pa_dbus_connection_get(u->bus), OBJECT_SINKS, &vtable_sinks_and_sources, u));
+    pa_assert_se(dbus_connection_register_fallback(pa_dbus_connection_get(u->bus), OBJECT_SOURCES, &vtable_sinks_and_sources, u));
+
+    if (dbus_bus_request_name(pa_dbus_connection_get(u->bus), SERVICE_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+        pa_log("Failed to request service name " SERVICE_NAME ": %s", error.message);
+        goto fail;
+    }
+
+    u->got_name = true;
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    dbus_error_free(&error);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata*u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->source_new_slot)
+        pa_hook_slot_free(u->source_new_slot);
+    if (u->source_unlink_slot)
+        pa_hook_slot_free(u->source_unlink_slot);
+
+    if (u->bus) {
+        DBusError error;
+
+        dbus_error_init(&error);
+
+        dbus_connection_unregister_object_path(pa_dbus_connection_get(u->bus), OBJECT_ROOT);
+        dbus_connection_unregister_object_path(pa_dbus_connection_get(u->bus), OBJECT_SINKS);
+        dbus_connection_unregister_object_path(pa_dbus_connection_get(u->bus), OBJECT_SOURCES);
+
+        if (u->got_name) {
+            if (dbus_bus_release_name(pa_dbus_connection_get(u->bus), SERVICE_NAME, &error) != DBUS_RELEASE_NAME_REPLY_RELEASED) {
+                pa_log("Failed to release service name " SERVICE_NAME ": %s", error.message);
+                dbus_error_free(&error);
+            }
+        }
+
+        pa_dbus_connection_unref(u->bus);
+    }
+
+    pa_xfree(u->display_name);
+
+    if (u->http)
+        pa_http_protocol_unref(u->http);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c
new file mode 100644 (file)
index 0000000..3f275e2
--- /dev/null
@@ -0,0 +1,328 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+
+#include "module-sine-source-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Sine wave generator source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "source_name=<name for the source> "
+        "source_properties=<properties for the source> "
+        "rate=<sample rate> "
+        "frequency=<frequency in Hz>");
+
+#define DEFAULT_SOURCE_NAME "sine_input"
+#define BLOCK_USEC (PA_USEC_PER_SEC * 2)
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_source *source;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    pa_memchunk memchunk;
+    size_t peek_index;
+
+    pa_usec_t block_usec; /* how much to push at once */
+    pa_usec_t timestamp;  /* when to push next */
+};
+
+static const char* const valid_modargs[] = {
+    "source_name",
+    "source_properties",
+    "rate",
+    "frequency",
+    NULL
+};
+
+static int source_process_msg(
+        pa_msgobject *o,
+        int code,
+        void *data,
+        int64_t offset,
+        pa_memchunk *chunk) {
+
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_SET_STATE:
+
+            if (PA_PTR_TO_UINT(data) == PA_SOURCE_RUNNING)
+                u->timestamp = pa_rtclock_now();
+
+            break;
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            pa_usec_t now, left_to_fill;
+
+            now = pa_rtclock_now();
+            left_to_fill = u->timestamp > now ? u->timestamp - now : 0ULL;
+
+            *((int64_t*) data) = (int64_t)u->block_usec - left_to_fill;
+
+            return 0;
+        }
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static void source_update_requested_latency_cb(pa_source *s) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    u->block_usec = pa_source_get_requested_latency_within_thread(s);
+
+    if (u->block_usec == (pa_usec_t) -1)
+        u->block_usec = s->thread_info.max_latency;
+
+    pa_log_debug("new block msec = %lu", (unsigned long) (u->block_usec / PA_USEC_PER_MSEC));
+}
+
+static void process_render(struct userdata *u, pa_usec_t now) {
+    pa_assert(u);
+
+    while (u->timestamp < now + u->block_usec) {
+        pa_memchunk chunk;
+        size_t k;
+
+        k = pa_usec_to_bytes_round_up(now + u->block_usec - u->timestamp, &u->source->sample_spec);
+
+        chunk = u->memchunk;
+        chunk.index += u->peek_index;
+        chunk.length = PA_MIN(chunk.length - u->peek_index, k);
+
+/*         pa_log_debug("posting %lu", (unsigned long) chunk.length); */
+        pa_source_post(u->source, &chunk);
+
+        u->peek_index += chunk.length;
+        while (u->peek_index >= u->memchunk.length)
+            u->peek_index -= u->memchunk.length;
+
+        u->timestamp += pa_bytes_to_usec(chunk.length, &u->source->sample_spec);
+    }
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    u->timestamp = pa_rtclock_now();
+
+    for (;;) {
+        int ret;
+
+        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+            pa_usec_t now;
+
+            now = pa_rtclock_now();
+
+            if (u->timestamp <= now)
+                process_render(u, now);
+
+            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
+        } else
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_modargs *ma;
+    pa_source_new_data data;
+    uint32_t frequency;
+    pa_sample_spec ss;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments.");
+        goto fail;
+    }
+
+    ss.format = PA_SAMPLE_FLOAT32;
+    ss.channels = 1;
+    ss.rate = 44100;
+
+    if (pa_modargs_get_sample_rate(ma, &ss.rate) < 0) {
+        pa_log("Invalid rate specification");
+        goto fail;
+    }
+
+    frequency = 440;
+    if (pa_modargs_get_value_u32(ma, "frequency", &frequency) < 0 || frequency < 1 || frequency > ss.rate/2) {
+        pa_log("Invalid frequency specification");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->peek_index = 0;
+    pa_memchunk_sine(&u->memchunk, m->core->mempool, ss.rate, frequency);
+
+    pa_source_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Sine source at %u Hz", (unsigned) frequency);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
+    pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);
+    pa_source_new_data_set_sample_spec(&data, &ss);
+
+    if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_source_new_data_done(&data);
+        goto fail;
+    }
+
+    u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY);
+    pa_source_new_data_done(&data);
+
+    if (!u->source) {
+        pa_log("Failed to create source.");
+        goto fail;
+    }
+
+    u->source->parent.process_msg = source_process_msg;
+    u->source->update_requested_latency = source_update_requested_latency_cb;
+    u->source->userdata = u;
+
+    u->block_usec = BLOCK_USEC;
+
+    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+    pa_source_set_fixed_latency(u->source, u->block_usec);
+
+    if (!(u->thread = pa_thread_new("sine-source", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_source_put(u->source);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_source_linked_by(u->source);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
new file mode 100644 (file)
index 0000000..4592071
--- /dev/null
@@ -0,0 +1,211 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+
+#include "module-sine-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Sine wave generator");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "sink=<sink to connect to> "
+        "rate=<sample rate> "
+        "frequency=<frequency in Hz>");
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink_input *sink_input;
+    pa_memchunk memchunk;
+    size_t peek_index;
+};
+
+static const char* const valid_modargs[] = {
+    "sink",
+    "rate",
+    "frequency",
+    NULL,
+};
+
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+    pa_assert(chunk);
+
+    *chunk = u->memchunk;
+    pa_memblock_ref(chunk->memblock);
+
+    chunk->index += u->peek_index;
+    chunk->length -= u->peek_index;
+
+    u->peek_index = 0;
+
+    return 0;
+}
+
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    nbytes %= u->memchunk.length;
+
+    if (u->peek_index >= nbytes)
+        u->peek_index -= nbytes;
+    else
+        u->peek_index = u->memchunk.length + u->peek_index - nbytes;
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_input_unlink(u->sink_input);
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If we are added for the first time, ask for a rewinding so that
+     * we are heard right-away. */
+    if (PA_SINK_INPUT_IS_LINKED(state) &&
+        i->thread_info.state == PA_SINK_INPUT_INIT && i->sink)
+        pa_sink_input_request_rewind(i, 0, false, true, true);
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    pa_sink *sink;
+    pa_sample_spec ss;
+    uint32_t frequency;
+    pa_sink_input_new_data data;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK))) {
+        pa_log("No such sink.");
+        goto fail;
+    }
+
+    ss.format = PA_SAMPLE_FLOAT32;
+    ss.rate = sink->sample_spec.rate;
+    ss.channels = 1;
+
+    if (pa_modargs_get_sample_rate(ma, &ss.rate) < 0) {
+        pa_log("Invalid rate specification");
+        goto fail;
+    }
+
+    frequency = 440;
+    if (pa_modargs_get_value_u32(ma, "frequency", &frequency) < 0 || frequency < 1 || frequency > ss.rate/2) {
+        pa_log("Invalid frequency specification");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->sink_input = NULL;
+
+    u->peek_index = 0;
+    pa_memchunk_sine(&u->memchunk, m->core->mempool, ss.rate, frequency);
+
+    pa_sink_input_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_input_new_data_set_sink(&data, sink, false);
+    pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency);
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
+    pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);
+    pa_sink_input_new_data_set_sample_spec(&data, &ss);
+
+    pa_sink_input_new(&u->sink_input, m->core, &data);
+    pa_sink_input_new_data_done(&data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
+    u->sink_input->userdata = u;
+
+    pa_sink_input_put(u->sink_input);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+    }
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
new file mode 100644 (file)
index 0000000..f44bc7c
--- /dev/null
@@ -0,0 +1,1134 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+  Copyright 2009 Finn Thain
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#include <signal.h>
+#include <stropts.h>
+#include <sys/audio.h>
+
+#ifdef HAVE_SYS_CONF_H
+#include <sys/conf.h>
+#endif
+
+#include <pulse/mainloop-signal.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/time-smoother.h>
+
+#include "module-solaris-symdef.h"
+
+PA_MODULE_AUTHOR("Pierre Ossman");
+PA_MODULE_DESCRIPTION("Solaris Sink/Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_USAGE(
+    "sink_name=<name for the sink> "
+    "sink_properties=<properties for the sink> "
+    "source_name=<name for the source> "
+    "source_properties=<properties for the source> "
+    "device=<audio device file name> "
+    "record=<enable source?> "
+    "playback=<enable sink?> "
+    "format=<sample format> "
+    "channels=<number of channels> "
+    "rate=<sample rate> "
+    "buffer_length=<milliseconds> "
+    "channel_map=<channel map>");
+PA_MODULE_LOAD_ONCE(false);
+
+struct userdata {
+    pa_core *core;
+    pa_sink *sink;
+    pa_source *source;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    pa_signal_event *sig;
+
+    pa_memchunk memchunk;
+
+    uint32_t frame_size;
+    int32_t buffer_size;
+    uint64_t written_bytes, read_bytes;
+
+    char *device_name;
+    int mode;
+    int fd;
+    pa_rtpoll_item *rtpoll_item;
+    pa_module *module;
+
+    bool sink_suspended, source_suspended;
+
+    uint32_t play_samples_msw, record_samples_msw;
+    uint32_t prev_playback_samples, prev_record_samples;
+
+    int32_t minimum_request;
+
+    pa_smoother *smoother;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "source_name",
+    "source_properties",
+    "device",
+    "record",
+    "playback",
+    "buffer_length",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    NULL
+};
+
+#define DEFAULT_DEVICE "/dev/audio"
+
+#define MAX_RENDER_HZ   (300)
+/* This render rate limit imposes a minimum latency, but without it we waste too much CPU time. */
+
+#define MAX_BUFFER_SIZE (128 * 1024)
+/* An attempt to buffer more than 128 KB causes write() to fail with errno == EAGAIN. */
+
+static uint64_t get_playback_buffered_bytes(struct userdata *u) {
+    audio_info_t info;
+    uint64_t played_bytes;
+    int err;
+
+    pa_assert(u->sink);
+
+    err = ioctl(u->fd, AUDIO_GETINFO, &info);
+    pa_assert(err >= 0);
+
+    /* Handle wrap-around of the device's sample counter, which is a uint_32. */
+    if (u->prev_playback_samples > info.play.samples) {
+        /*
+         * Unfortunately info.play.samples can sometimes go backwards, even before it wraps!
+         * The bug seems to be absent on Solaris x86 nv117 with audio810 driver, at least on this (UP) machine.
+         * The bug is present on a different (SMP) machine running Solaris x86 nv103 with audioens driver.
+         * An earlier revision of this file mentions the same bug independently (unknown configuration).
+         */
+        if (u->prev_playback_samples + info.play.samples < 240000) {
+            ++u->play_samples_msw;
+        } else {
+            pa_log_debug("play.samples went backwards %d bytes", u->prev_playback_samples - info.play.samples);
+        }
+    }
+    u->prev_playback_samples = info.play.samples;
+    played_bytes = (((uint64_t)u->play_samples_msw << 32) + info.play.samples) * u->frame_size;
+
+    pa_smoother_put(u->smoother, pa_rtclock_now(), pa_bytes_to_usec(played_bytes, &u->sink->sample_spec));
+
+    if (u->written_bytes > played_bytes)
+        return u->written_bytes - played_bytes;
+    else
+        return 0;
+}
+
+static pa_usec_t sink_get_latency(struct userdata *u, pa_sample_spec *ss) {
+    pa_usec_t r = 0;
+
+    pa_assert(u);
+    pa_assert(ss);
+
+    if (u->fd >= 0) {
+        r = pa_bytes_to_usec(get_playback_buffered_bytes(u), ss);
+        if (u->memchunk.memblock)
+            r += pa_bytes_to_usec(u->memchunk.length, ss);
+    }
+    return r;
+}
+
+static uint64_t get_recorded_bytes(struct userdata *u) {
+    audio_info_t info;
+    uint64_t result;
+    int err;
+
+    pa_assert(u->source);
+
+    err = ioctl(u->fd, AUDIO_GETINFO, &info);
+    pa_assert(err >= 0);
+
+    if (u->prev_record_samples > info.record.samples)
+        ++u->record_samples_msw;
+    u->prev_record_samples = info.record.samples;
+    result = (((uint64_t)u->record_samples_msw << 32) + info.record.samples) * u->frame_size;
+
+    return result;
+}
+
+static pa_usec_t source_get_latency(struct userdata *u, pa_sample_spec *ss) {
+    pa_usec_t r = 0;
+    audio_info_t info;
+
+    pa_assert(u);
+    pa_assert(ss);
+
+    if (u->fd) {
+        int err = ioctl(u->fd, AUDIO_GETINFO, &info);
+        pa_assert(err >= 0);
+
+        r = pa_bytes_to_usec(get_recorded_bytes(u), ss) - pa_bytes_to_usec(u->read_bytes, ss);
+    }
+    return r;
+}
+
+static void build_pollfd(struct userdata *u) {
+    struct pollfd *pollfd;
+
+    pa_assert(u);
+    pa_assert(!u->rtpoll_item);
+    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+
+    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+    pollfd->fd = u->fd;
+    pollfd->events = 0;
+    pollfd->revents = 0;
+}
+
+static int set_buffer(int fd, int buffer_size) {
+    audio_info_t info;
+
+    pa_assert(fd >= 0);
+
+    AUDIO_INITINFO(&info);
+    info.play.buffer_size = buffer_size;
+    info.record.buffer_size = buffer_size;
+
+    if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
+        if (errno == EINVAL)
+            pa_log("AUDIO_SETINFO: Unsupported buffer size.");
+        else
+            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int auto_format(int fd, int mode, pa_sample_spec *ss) {
+    audio_info_t info;
+
+    pa_assert(fd >= 0);
+    pa_assert(ss);
+
+    AUDIO_INITINFO(&info);
+
+    if (mode != O_RDONLY) {
+        info.play.sample_rate = ss->rate;
+        info.play.channels = ss->channels;
+        switch (ss->format) {
+        case PA_SAMPLE_U8:
+            info.play.precision = 8;
+            info.play.encoding = AUDIO_ENCODING_LINEAR;
+            break;
+        case PA_SAMPLE_ALAW:
+            info.play.precision = 8;
+            info.play.encoding = AUDIO_ENCODING_ALAW;
+            break;
+        case PA_SAMPLE_ULAW:
+            info.play.precision = 8;
+            info.play.encoding = AUDIO_ENCODING_ULAW;
+            break;
+        case PA_SAMPLE_S16NE:
+            info.play.precision = 16;
+            info.play.encoding = AUDIO_ENCODING_LINEAR;
+            break;
+        default:
+            pa_log("AUDIO_SETINFO: Unsupported sample format.");
+            return -1;
+        }
+    }
+
+    if (mode != O_WRONLY) {
+        info.record.sample_rate = ss->rate;
+        info.record.channels = ss->channels;
+        switch (ss->format) {
+        case PA_SAMPLE_U8:
+            info.record.precision = 8;
+            info.record.encoding = AUDIO_ENCODING_LINEAR;
+            break;
+        case PA_SAMPLE_ALAW:
+            info.record.precision = 8;
+            info.record.encoding = AUDIO_ENCODING_ALAW;
+            break;
+        case PA_SAMPLE_ULAW:
+            info.record.precision = 8;
+            info.record.encoding = AUDIO_ENCODING_ULAW;
+            break;
+        case PA_SAMPLE_S16NE:
+            info.record.precision = 16;
+            info.record.encoding = AUDIO_ENCODING_LINEAR;
+            break;
+        default:
+            pa_log("AUDIO_SETINFO: Unsupported sample format.");
+            return -1;
+        }
+    }
+
+    if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
+        if (errno == EINVAL)
+            pa_log("AUDIO_SETINFO: Failed to set sample format.");
+        else
+            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int open_audio_device(struct userdata *u, pa_sample_spec *ss) {
+    pa_assert(u);
+    pa_assert(ss);
+
+    if ((u->fd = pa_open_cloexec(u->device_name, u->mode | O_NONBLOCK, 0)) < 0) {
+        pa_log_warn("open %s failed (%s)", u->device_name, pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_log_info("device opened in %s mode.", u->mode == O_WRONLY ? "O_WRONLY" : (u->mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+
+    if (auto_format(u->fd, u->mode, ss) < 0)
+        return -1;
+
+    if (set_buffer(u->fd, u->buffer_size) < 0)
+        return -1;
+
+    u->written_bytes = u->read_bytes = 0;
+    u->play_samples_msw = u->record_samples_msw = 0;
+    u->prev_playback_samples = u->prev_record_samples = 0;
+
+    return u->fd;
+}
+
+static int suspend(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->fd >= 0);
+
+    pa_log_info("Suspending...");
+
+    ioctl(u->fd, I_FLUSH, FLUSHRW);
+    pa_close(u->fd);
+    u->fd = -1;
+
+    if (u->rtpoll_item) {
+        pa_rtpoll_item_free(u->rtpoll_item);
+        u->rtpoll_item = NULL;
+    }
+
+    pa_log_info("Device suspended.");
+
+    return 0;
+}
+
+static int unsuspend(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->fd < 0);
+
+    pa_log_info("Resuming...");
+
+    if (open_audio_device(u, u->sink ? &u->sink->sample_spec : &u->source->sample_spec) < 0)
+        return -1;
+
+    build_pollfd(u);
+
+    pa_log_info("Device resumed.");
+
+    return 0;
+}
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY:
+            *((int64_t*) data) = sink_get_latency(u, &PA_SINK(o)->sample_spec);
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SINK_SUSPENDED:
+
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+
+                    pa_smoother_pause(u->smoother, pa_rtclock_now());
+
+                    if (!u->source || u->source_suspended) {
+                        if (suspend(u) < 0)
+                            return -1;
+                    }
+                    u->sink_suspended = true;
+                    break;
+
+                case PA_SINK_IDLE:
+                case PA_SINK_RUNNING:
+
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+                        pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
+
+                        if (!u->source || u->source_suspended) {
+                            bool mute;
+                            if (unsuspend(u) < 0)
+                                return -1;
+                            u->sink->get_volume(u->sink);
+                            if (u->sink->get_mute(u->sink, &mute) >= 0)
+                                pa_sink_set_mute(u->sink, mute, false);
+                        }
+                        u->sink_suspended = false;
+                    }
+                    break;
+
+                case PA_SINK_INVALID_STATE:
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                    ;
+            }
+
+            break;
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY:
+            *((pa_usec_t*) data) = source_get_latency(u, &PA_SOURCE(o)->sample_spec);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_SET_STATE:
+
+            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SOURCE_SUSPENDED:
+
+                    pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
+
+                    if (!u->sink || u->sink_suspended) {
+                        if (suspend(u) < 0)
+                            return -1;
+                    }
+                    u->source_suspended = true;
+                    break;
+
+                case PA_SOURCE_IDLE:
+                case PA_SOURCE_RUNNING:
+
+                    if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
+                        if (!u->sink || u->sink_suspended) {
+                            if (unsuspend(u) < 0)
+                                return -1;
+                            u->source->get_volume(u->source);
+                        }
+                        u->source_suspended = false;
+                    }
+                    break;
+
+                case PA_SOURCE_UNLINKED:
+                case PA_SOURCE_INIT:
+                case PA_SOURCE_INVALID_STATE:
+                    ;
+
+            }
+            break;
+
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static void sink_set_volume(pa_sink *s) {
+    struct userdata *u;
+    audio_info_t info;
+
+    pa_assert_se(u = s->userdata);
+
+    if (u->fd >= 0) {
+        AUDIO_INITINFO(&info);
+
+        info.play.gain = pa_cvolume_max(&s->real_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+        pa_assert(info.play.gain <= AUDIO_MAX_GAIN);
+
+        if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
+            if (errno == EINVAL)
+                pa_log("AUDIO_SETINFO: Unsupported volume.");
+            else
+                pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+        }
+    }
+}
+
+static void sink_get_volume(pa_sink *s) {
+    struct userdata *u;
+    audio_info_t info;
+
+    pa_assert_se(u = s->userdata);
+
+    if (u->fd >= 0) {
+        if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
+            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+        else
+            pa_cvolume_set(&s->real_volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+    }
+}
+
+static void source_set_volume(pa_source *s) {
+    struct userdata *u;
+    audio_info_t info;
+
+    pa_assert_se(u = s->userdata);
+
+    if (u->fd >= 0) {
+        AUDIO_INITINFO(&info);
+
+        info.play.gain = pa_cvolume_max(&s->real_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+        pa_assert(info.play.gain <= AUDIO_MAX_GAIN);
+
+        if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
+            if (errno == EINVAL)
+                pa_log("AUDIO_SETINFO: Unsupported volume.");
+            else
+                pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+        }
+    }
+}
+
+static void source_get_volume(pa_source *s) {
+    struct userdata *u;
+    audio_info_t info;
+
+    pa_assert_se(u = s->userdata);
+
+    if (u->fd >= 0) {
+        if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
+            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+        else
+            pa_cvolume_set(&s->real_volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+    }
+}
+
+static void sink_set_mute(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    audio_info_t info;
+
+    pa_assert(u);
+
+    if (u->fd >= 0) {
+        AUDIO_INITINFO(&info);
+
+        info.output_muted = s->muted;
+
+        if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+    }
+}
+
+static int sink_get_mute(pa_sink *s, bool *mute) {
+    struct userdata *u = s->userdata;
+    audio_info_t info;
+
+    pa_assert(u);
+
+    if (u->fd < 0)
+        return -1;
+
+    if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0) {
+        pa_log("AUDIO_GETINFO: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    *mute = info.output_muted;
+
+    return 0;
+}
+
+static void process_rewind(struct userdata *u) {
+    size_t rewind_nbytes;
+
+    pa_assert(u);
+
+    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+        pa_sink_process_rewind(u->sink, 0);
+        return;
+    }
+
+    rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+
+    if (rewind_nbytes > 0) {
+        pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+        rewind_nbytes = PA_MIN(u->memchunk.length, rewind_nbytes);
+        u->memchunk.length -= rewind_nbytes;
+        if (u->memchunk.length <= 0 && u->memchunk.memblock) {
+            pa_memblock_unref(u->memchunk.memblock);
+            pa_memchunk_reset(&u->memchunk);
+        }
+        pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+    }
+
+    pa_sink_process_rewind(u->sink, rewind_nbytes);
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    unsigned short revents = 0;
+    int ret, err;
+    audio_info_t info;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
+
+    for (;;) {
+        /* Render some data and write it to the dsp */
+
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+            process_rewind(u);
+
+        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+            pa_usec_t xtime0, ysleep_interval, xsleep_interval;
+            uint64_t buffered_bytes;
+
+            err = ioctl(u->fd, AUDIO_GETINFO, &info);
+            if (err < 0) {
+                pa_log("AUDIO_GETINFO ioctl failed: %s", pa_cstrerror(errno));
+                goto fail;
+            }
+
+            if (info.play.error) {
+                pa_log_debug("buffer under-run!");
+
+                AUDIO_INITINFO(&info);
+                info.play.error = 0;
+                if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+                    pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+
+                pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
+            }
+
+            for (;;) {
+                void *p;
+                ssize_t w;
+                size_t len;
+                int write_type = 1;
+
+                /*
+                 * Since we cannot modify the size of the output buffer we fake it
+                 * by not filling it more than u->buffer_size.
+                 */
+                xtime0 = pa_rtclock_now();
+                buffered_bytes = get_playback_buffered_bytes(u);
+                if (buffered_bytes >= (uint64_t)u->buffer_size)
+                    break;
+
+                len = u->buffer_size - buffered_bytes;
+                len -= len % u->frame_size;
+
+                if (len < (size_t) u->minimum_request)
+                    break;
+
+                if (!u->memchunk.length)
+                    pa_sink_render(u->sink, u->sink->thread_info.max_request, &u->memchunk);
+
+                len = PA_MIN(u->memchunk.length, len);
+
+                p = pa_memblock_acquire(u->memchunk.memblock);
+                w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, len, &write_type);
+                pa_memblock_release(u->memchunk.memblock);
+
+                if (w <= 0) {
+                    if (errno == EINTR) {
+                        continue;
+                    } else if (errno == EAGAIN) {
+                        /* We may have realtime priority so yield the CPU to ensure that fd can become writable again. */
+                        pa_log_debug("EAGAIN with %llu bytes buffered.", buffered_bytes);
+                        break;
+                    } else {
+                        pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
+                        goto fail;
+                    }
+                } else {
+                    pa_assert(w % u->frame_size == 0);
+
+                    u->written_bytes += w;
+                    u->memchunk.index += w;
+                    u->memchunk.length -= w;
+                    if (u->memchunk.length <= 0) {
+                        pa_memblock_unref(u->memchunk.memblock);
+                        pa_memchunk_reset(&u->memchunk);
+                    }
+                }
+            }
+
+            ysleep_interval = pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec);
+            xsleep_interval = pa_smoother_translate(u->smoother, xtime0, ysleep_interval);
+            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + PA_MIN(xsleep_interval, ysleep_interval));
+        } else
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Try to read some data and pass it on to the source driver */
+
+        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && (revents & POLLIN)) {
+            pa_memchunk memchunk;
+            void *p;
+            ssize_t r;
+            size_t len;
+
+            err = ioctl(u->fd, AUDIO_GETINFO, &info);
+            pa_assert(err >= 0);
+
+            if (info.record.error) {
+                pa_log_debug("buffer overflow!");
+
+                AUDIO_INITINFO(&info);
+                info.record.error = 0;
+                if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+                    pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+            }
+
+            err = ioctl(u->fd, I_NREAD, &len);
+            pa_assert(err >= 0);
+
+            if (len > 0) {
+                memchunk.memblock = pa_memblock_new(u->core->mempool, len);
+                pa_assert(memchunk.memblock);
+
+                p = pa_memblock_acquire(memchunk.memblock);
+                r = pa_read(u->fd, p, len, NULL);
+                pa_memblock_release(memchunk.memblock);
+
+                if (r < 0) {
+                    pa_memblock_unref(memchunk.memblock);
+                    if (errno == EAGAIN)
+                        break;
+                    else {
+                        pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
+                        goto fail;
+                    }
+                } else {
+                    u->read_bytes += r;
+
+                    memchunk.index = 0;
+                    memchunk.length = r;
+
+                    pa_source_post(u->source, &memchunk);
+                    pa_memblock_unref(memchunk.memblock);
+
+                    revents &= ~POLLIN;
+                }
+            }
+        }
+
+        if (u->rtpoll_item) {
+            struct pollfd *pollfd;
+
+            pa_assert(u->fd >= 0);
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+            pollfd->events = (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0;
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        if (u->rtpoll_item) {
+            struct pollfd *pollfd;
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
+                pa_log("DSP shutdown.");
+                goto fail;
+            }
+
+            revents = pollfd->revents;
+        } else
+            revents = 0;
+    }
+
+fail:
+    /* We have to continue processing messages until we receive the
+     * SHUTDOWN message */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("caught signal");
+
+    if (u->sink) {
+        pa_sink_get_volume(u->sink, true);
+        pa_sink_get_mute(u->sink, true);
+    }
+
+    if (u->source)
+        pa_source_get_volume(u->source, true);
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u = NULL;
+    bool record = true, playback = true;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    uint32_t buffer_length_msec;
+    int fd = -1;
+    pa_sink_new_data sink_new_data;
+    pa_source_new_data source_new_data;
+    char const *name;
+    char *name_buf;
+    bool namereg_fail;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
+        pa_log("record= and playback= expect a boolean argument.");
+        goto fail;
+    }
+
+    if (!playback && !record) {
+        pa_log("neither playback nor record enabled for device.");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+
+    if (!(u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC * 2, true, true, 10, pa_rtclock_now(), true)))
+        goto fail;
+
+    /*
+     * For a process (or several processes) to use the same audio device for both
+     * record and playback at the same time, the device's mixer must be enabled.
+     * See mixerctl(1). It may be turned off for playback only or record only.
+     */
+    u->mode = (playback && record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("failed to parse sample specification");
+        goto fail;
+    }
+    u->frame_size = pa_frame_size(&ss);
+
+    u->minimum_request = pa_usec_to_bytes(PA_USEC_PER_SEC / MAX_RENDER_HZ, &ss);
+
+    buffer_length_msec = 100;
+    if (pa_modargs_get_value_u32(ma, "buffer_length", &buffer_length_msec) < 0) {
+        pa_log("failed to parse buffer_length argument");
+        goto fail;
+    }
+    u->buffer_size = pa_usec_to_bytes(1000 * buffer_length_msec, &ss);
+    if (u->buffer_size < 2 * u->minimum_request) {
+        pa_log("buffer_length argument cannot be smaller than %u",
+               (unsigned)(pa_bytes_to_usec(2 * u->minimum_request, &ss) / 1000));
+        goto fail;
+    }
+    if (u->buffer_size > MAX_BUFFER_SIZE) {
+        pa_log("buffer_length argument cannot be greater than %u",
+               (unsigned)(pa_bytes_to_usec(MAX_BUFFER_SIZE, &ss) / 1000));
+        goto fail;
+    }
+
+    u->device_name = pa_xstrdup(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
+
+    if ((fd = open_audio_device(u, &ss)) < 0)
+        goto fail;
+
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+
+    pa_memchunk_reset(&u->memchunk);
+
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->rtpoll_item = NULL;
+    build_pollfd(u);
+
+    if (u->mode != O_WRONLY) {
+        name_buf = NULL;
+        namereg_fail = true;
+
+        if (!(name = pa_modargs_get_value(ma, "source_name", NULL))) {
+            name = name_buf = pa_sprintf_malloc("solaris_input.%s", pa_path_get_filename(u->device_name));
+            namereg_fail = false;
+        }
+
+        pa_source_new_data_init(&source_new_data);
+        source_new_data.driver = __FILE__;
+        source_new_data.module = m;
+        pa_source_new_data_set_name(&source_new_data, name);
+        source_new_data.namereg_fail = namereg_fail;
+        pa_source_new_data_set_sample_spec(&source_new_data, &ss);
+        pa_source_new_data_set_channel_map(&source_new_data, &map);
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "solaris");
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Solaris PCM source");
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "serial");
+        pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) u->buffer_size);
+
+        if (pa_modargs_get_proplist(ma, "source_properties", source_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
+            pa_log("Invalid properties");
+            pa_source_new_data_done(&source_new_data);
+            goto fail;
+        }
+
+        u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+        pa_source_new_data_done(&source_new_data);
+        pa_xfree(name_buf);
+
+        if (!u->source) {
+            pa_log("Failed to create source object");
+            goto fail;
+        }
+
+        u->source->userdata = u;
+        u->source->parent.process_msg = source_process_msg;
+
+        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+        pa_source_set_rtpoll(u->source, u->rtpoll);
+        pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &u->source->sample_spec));
+
+        pa_source_set_get_volume_callback(u->source, source_get_volume);
+        pa_source_set_set_volume_callback(u->source, source_set_volume);
+        u->source->refresh_volume = true;
+    } else
+        u->source = NULL;
+
+    if (u->mode != O_RDONLY) {
+        name_buf = NULL;
+        namereg_fail = true;
+        if (!(name = pa_modargs_get_value(ma, "sink_name", NULL))) {
+            name = name_buf = pa_sprintf_malloc("solaris_output.%s", pa_path_get_filename(u->device_name));
+            namereg_fail = false;
+        }
+
+        pa_sink_new_data_init(&sink_new_data);
+        sink_new_data.driver = __FILE__;
+        sink_new_data.module = m;
+        pa_sink_new_data_set_name(&sink_new_data, name);
+        sink_new_data.namereg_fail = namereg_fail;
+        pa_sink_new_data_set_sample_spec(&sink_new_data, &ss);
+        pa_sink_new_data_set_channel_map(&sink_new_data, &map);
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_API, "solaris");
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Solaris PCM sink");
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "serial");
+
+        if (pa_modargs_get_proplist(ma, "sink_properties", sink_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
+            pa_log("Invalid properties");
+            pa_sink_new_data_done(&sink_new_data);
+            goto fail;
+        }
+
+        u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+        pa_sink_new_data_done(&sink_new_data);
+
+        pa_assert(u->sink);
+        u->sink->userdata = u;
+        u->sink->parent.process_msg = sink_process_msg;
+
+        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+        pa_sink_set_rtpoll(u->sink, u->rtpoll);
+        pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->buffer_size, &u->sink->sample_spec));
+        pa_sink_set_max_request(u->sink, u->buffer_size);
+        pa_sink_set_max_rewind(u->sink, u->buffer_size);
+
+        pa_sink_set_get_volume_callback(u->sink, sink_get_volume);
+        pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
+        pa_sink_set_get_mute_callback(u->sink, sink_get_mute);
+        pa_sink_set_set_mute_callback(u->sink, sink_set_mute);
+        u->sink->refresh_volume = u->sink->refresh_muted = true;
+    } else
+        u->sink = NULL;
+
+    pa_assert(u->source || u->sink);
+
+    u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
+    if (u->sig)
+        ioctl(u->fd, I_SETSIG, S_MSG);
+    else
+        pa_log_warn("Could not register SIGPOLL handler");
+
+    if (!(u->thread = pa_thread_new("solaris", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    /* Read mixer settings */
+    if (u->sink) {
+        if (sink_new_data.volume_is_set)
+            u->sink->set_volume(u->sink);
+        else
+            u->sink->get_volume(u->sink);
+
+        if (sink_new_data.muted_is_set)
+            u->sink->set_mute(u->sink);
+        else {
+            bool mute;
+
+            if (u->sink->get_mute(u->sink, &mute) >= 0)
+                pa_sink_set_mute(u->sink, mute, false);
+        }
+
+        pa_sink_put(u->sink);
+    }
+
+    if (u->source) {
+        if (source_new_data.volume_is_set)
+            u->source->set_volume(u->source);
+        else
+            u->source->get_volume(u->source);
+
+        pa_source_put(u->source);
+    }
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (u)
+        pa__done(m);
+    else if (fd >= 0)
+        close(fd);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sig) {
+        ioctl(u->fd, I_SETSIG, 0);
+        pa_signal_free(u->sig);
+    }
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->fd >= 0)
+        close(u->fd);
+
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+
+    pa_xfree(u->device_name);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
new file mode 100644 (file)
index 0000000..eb75833
--- /dev/null
@@ -0,0 +1,2570 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/protocol-native.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/database.h>
+#include <pulsecore/tagstruct.h>
+#include <pulsecore/proplist-util.h>
+
+#ifdef HAVE_DBUS
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+#endif
+
+#include "module-stream-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore the volume/mute/device state of streams");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+        "restore_device=<Save/restore sinks/sources?> "
+        "restore_volume=<Save/restore volumes?> "
+        "restore_muted=<Save/restore muted states?> "
+        "on_hotplug=<When new device becomes available, recheck streams?> "
+        "on_rescue=<When device becomes unavailable, recheck streams?> "
+        "fallback_table=<filename>");
+
+#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
+#define IDENTIFICATION_PROPERTY "module-stream-restore.id"
+
+#define DEFAULT_FALLBACK_FILE PA_DEFAULT_CONFIG_DIR"/stream-restore.table"
+#define DEFAULT_FALLBACK_FILE_USER "stream-restore.table"
+
+#define WHITESPACE "\n\r \t"
+
+static const char* const valid_modargs[] = {
+    "restore_device",
+    "restore_volume",
+    "restore_muted",
+    "on_hotplug",
+    "on_rescue",
+    "fallback_table",
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_subscription *subscription;
+    pa_hook_slot
+        *sink_input_new_hook_slot,
+        *sink_input_fixate_hook_slot,
+        *source_output_new_hook_slot,
+        *source_output_fixate_hook_slot,
+        *sink_put_hook_slot,
+        *source_put_hook_slot,
+        *sink_unlink_hook_slot,
+        *source_unlink_hook_slot,
+        *connection_unlink_hook_slot;
+    pa_time_event *save_time_event;
+    pa_database* database;
+
+    bool restore_device:1;
+    bool restore_volume:1;
+    bool restore_muted:1;
+    bool on_hotplug:1;
+    bool on_rescue:1;
+
+    pa_native_protocol *protocol;
+    pa_idxset *subscribed;
+
+#ifdef HAVE_DBUS
+    pa_dbus_protocol *dbus_protocol;
+    pa_hashmap *dbus_entries;
+    uint32_t next_index; /* For generating object paths for entries. */
+#endif
+};
+
+#define ENTRY_VERSION 1
+
+struct entry {
+    uint8_t version;
+    bool muted_valid, volume_valid, device_valid, card_valid;
+    bool muted;
+    pa_channel_map channel_map;
+    pa_cvolume volume;
+    char* device;
+    char* card;
+};
+
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_READ,
+    SUBCOMMAND_WRITE,
+    SUBCOMMAND_DELETE,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT
+};
+
+static struct entry* entry_new(void);
+static void entry_free(struct entry *e);
+static struct entry *entry_read(struct userdata *u, const char *name);
+static bool entry_write(struct userdata *u, const char *name, const struct entry *e, bool replace);
+static struct entry* entry_copy(const struct entry *e);
+static void entry_apply(struct userdata *u, const char *name, struct entry *e);
+static void trigger_save(struct userdata *u);
+
+#ifdef HAVE_DBUS
+
+#define OBJECT_PATH "/org/pulseaudio/stream_restore1"
+#define ENTRY_OBJECT_NAME "entry"
+#define INTERFACE_STREAM_RESTORE "org.PulseAudio.Ext.StreamRestore1"
+#define INTERFACE_ENTRY INTERFACE_STREAM_RESTORE ".RestoreEntry"
+
+#define DBUS_INTERFACE_REVISION 0
+
+struct dbus_entry {
+    struct userdata *userdata;
+
+    char *entry_name;
+    uint32_t index;
+    char *object_path;
+};
+
+static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_entries(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_entry_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+
+static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_entry_remove(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INTERFACE_REVISION,
+    PROPERTY_HANDLER_ENTRIES,
+    PROPERTY_HANDLER_MAX
+};
+
+enum entry_property_handler_index {
+    ENTRY_PROPERTY_HANDLER_INDEX,
+    ENTRY_PROPERTY_HANDLER_NAME,
+    ENTRY_PROPERTY_HANDLER_DEVICE,
+    ENTRY_PROPERTY_HANDLER_VOLUME,
+    ENTRY_PROPERTY_HANDLER_MUTE,
+    ENTRY_PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INTERFACE_REVISION] = { .property_name = "InterfaceRevision", .type = "u",  .get_cb = handle_get_interface_revision, .set_cb = NULL },
+    [PROPERTY_HANDLER_ENTRIES]            = { .property_name = "Entries",           .type = "ao", .get_cb = handle_get_entries,            .set_cb = NULL }
+};
+
+static pa_dbus_property_handler entry_property_handlers[ENTRY_PROPERTY_HANDLER_MAX] = {
+    [ENTRY_PROPERTY_HANDLER_INDEX]    = { .property_name = "Index",   .type = "u",     .get_cb = handle_entry_get_index,    .set_cb = NULL },
+    [ENTRY_PROPERTY_HANDLER_NAME]     = { .property_name = "Name",    .type = "s",     .get_cb = handle_entry_get_name,     .set_cb = NULL },
+    [ENTRY_PROPERTY_HANDLER_DEVICE]   = { .property_name = "Device",  .type = "s",     .get_cb = handle_entry_get_device,   .set_cb = handle_entry_set_device },
+    [ENTRY_PROPERTY_HANDLER_VOLUME]   = { .property_name = "Volume",  .type = "a(uu)", .get_cb = handle_entry_get_volume,   .set_cb = handle_entry_set_volume },
+    [ENTRY_PROPERTY_HANDLER_MUTE]     = { .property_name = "Mute",    .type = "b",     .get_cb = handle_entry_get_mute,     .set_cb = handle_entry_set_mute }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_ADD_ENTRY,
+    METHOD_HANDLER_GET_ENTRY_BY_NAME,
+    METHOD_HANDLER_MAX
+};
+
+enum entry_method_handler_index {
+    ENTRY_METHOD_HANDLER_REMOVE,
+    ENTRY_METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info add_entry_args[] = { { "name",              "s",     "in" },
+                                             { "device",            "s",     "in" },
+                                             { "volume",            "a(uu)", "in" },
+                                             { "mute",              "b",     "in" },
+                                             { "apply_immediately", "b",     "in" },
+                                             { "entry",             "o",     "out" } };
+static pa_dbus_arg_info get_entry_by_name_args[] = { { "name", "s", "in" }, { "entry", "o", "out" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_ADD_ENTRY] = {
+        .method_name = "AddEntry",
+        .arguments = add_entry_args,
+        .n_arguments = sizeof(add_entry_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_add_entry },
+    [METHOD_HANDLER_GET_ENTRY_BY_NAME] = {
+        .method_name = "GetEntryByName",
+        .arguments = get_entry_by_name_args,
+        .n_arguments = sizeof(get_entry_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_entry_by_name }
+};
+
+static pa_dbus_method_handler entry_method_handlers[ENTRY_METHOD_HANDLER_MAX] = {
+    [ENTRY_METHOD_HANDLER_REMOVE] = {
+        .method_name = "Remove",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_entry_remove }
+};
+
+enum signal_index {
+    SIGNAL_NEW_ENTRY,
+    SIGNAL_ENTRY_REMOVED,
+    SIGNAL_MAX
+};
+
+enum entry_signal_index {
+    ENTRY_SIGNAL_DEVICE_UPDATED,
+    ENTRY_SIGNAL_VOLUME_UPDATED,
+    ENTRY_SIGNAL_MUTE_UPDATED,
+    ENTRY_SIGNAL_MAX
+};
+
+static pa_dbus_arg_info new_entry_args[]     = { { "entry", "o", NULL } };
+static pa_dbus_arg_info entry_removed_args[] = { { "entry", "o", NULL } };
+
+static pa_dbus_arg_info entry_device_updated_args[] = { { "device", "s",     NULL } };
+static pa_dbus_arg_info entry_volume_updated_args[] = { { "volume", "a(uu)", NULL } };
+static pa_dbus_arg_info entry_mute_updated_args[]   = { { "muted",  "b",     NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_NEW_ENTRY]     = { .name = "NewEntry",     .arguments = new_entry_args,     .n_arguments = 1 },
+    [SIGNAL_ENTRY_REMOVED] = { .name = "EntryRemoved", .arguments = entry_removed_args, .n_arguments = 1 }
+};
+
+static pa_dbus_signal_info entry_signals[ENTRY_SIGNAL_MAX] = {
+    [ENTRY_SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = entry_device_updated_args, .n_arguments = 1 },
+    [ENTRY_SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = entry_volume_updated_args, .n_arguments = 1 },
+    [ENTRY_SIGNAL_MUTE_UPDATED]   = { .name = "MuteUpdated",   .arguments = entry_mute_updated_args,   .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info stream_restore_interface_info = {
+    .name = INTERFACE_STREAM_RESTORE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static pa_dbus_interface_info entry_interface_info = {
+    .name = INTERFACE_ENTRY,
+    .method_handlers = entry_method_handlers,
+    .n_method_handlers = ENTRY_METHOD_HANDLER_MAX,
+    .property_handlers = entry_property_handlers,
+    .n_property_handlers = ENTRY_PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_entry_get_all,
+    .signals = entry_signals,
+    .n_signals = ENTRY_SIGNAL_MAX
+};
+
+static struct dbus_entry *dbus_entry_new(struct userdata *u, const char *entry_name) {
+    struct dbus_entry *de;
+
+    pa_assert(u);
+    pa_assert(entry_name);
+    pa_assert(*entry_name);
+
+    de = pa_xnew(struct dbus_entry, 1);
+    de->userdata = u;
+    de->entry_name = pa_xstrdup(entry_name);
+    de->index = u->next_index++;
+    de->object_path = pa_sprintf_malloc("%s/%s%u", OBJECT_PATH, ENTRY_OBJECT_NAME, de->index);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(u->dbus_protocol, de->object_path, &entry_interface_info, de) >= 0);
+
+    return de;
+}
+
+static void dbus_entry_free(struct dbus_entry *de) {
+    pa_assert(de);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(de->userdata->dbus_protocol, de->object_path, entry_interface_info.name) >= 0);
+
+    pa_xfree(de->entry_name);
+    pa_xfree(de->object_path);
+    pa_xfree(de);
+}
+
+/* Reads an array [(UInt32, UInt32)] from the iterator. The struct items are
+ * are a channel position and a volume value, respectively. The result is
+ * stored in the map and vol arguments. The iterator must point to a "a(uu)"
+ * element. If the data is invalid, an error reply is sent and a negative
+ * number is returned. In case of a failure we make no guarantees about the
+ * state of map and vol. In case of an empty array the channels field of both
+ * map and vol are set to 0. This function calls dbus_message_iter_next(iter)
+ * before returning. */
+static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, pa_channel_map *map, pa_cvolume *vol) {
+    DBusMessageIter array_iter;
+    DBusMessageIter struct_iter;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(pa_streq(dbus_message_iter_get_signature(iter), "a(uu)"));
+    pa_assert(map);
+    pa_assert(vol);
+
+    pa_channel_map_init(map);
+    pa_cvolume_init(vol);
+
+    map->channels = 0;
+    vol->channels = 0;
+
+    dbus_message_iter_recurse(iter, &array_iter);
+
+    while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID) {
+        dbus_uint32_t chan_pos;
+        dbus_uint32_t chan_vol;
+
+        dbus_message_iter_recurse(&array_iter, &struct_iter);
+
+        dbus_message_iter_get_basic(&struct_iter, &chan_pos);
+
+        if (chan_pos >= PA_CHANNEL_POSITION_MAX) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u", chan_pos);
+            return -1;
+        }
+
+        pa_assert_se(dbus_message_iter_next(&struct_iter));
+        dbus_message_iter_get_basic(&struct_iter, &chan_vol);
+
+        if (!PA_VOLUME_IS_VALID(chan_vol)) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u", chan_vol);
+            return -1;
+        }
+
+        if (map->channels < PA_CHANNELS_MAX) {
+            map->map[map->channels] = chan_pos;
+            vol->values[map->channels] = chan_vol;
+        }
+        ++map->channels;
+        ++vol->channels;
+
+        dbus_message_iter_next(&array_iter);
+    }
+
+    if (map->channels > PA_CHANNELS_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels: %u. The maximum is %u.", map->channels, PA_CHANNELS_MAX);
+        return -1;
+    }
+
+    dbus_message_iter_next(iter);
+
+    return 0;
+}
+
+static void append_volume(DBusMessageIter *iter, struct entry *e) {
+    DBusMessageIter array_iter;
+    DBusMessageIter struct_iter;
+    unsigned i;
+
+    pa_assert(iter);
+    pa_assert(e);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(uu)", &array_iter));
+
+    if (!e->volume_valid) {
+        pa_assert_se(dbus_message_iter_close_container(iter, &array_iter));
+        return;
+    }
+
+    for (i = 0; i < e->channel_map.channels; ++i) {
+        pa_assert_se(dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter));
+
+        pa_assert_se(dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &e->channel_map.map[i]));
+        pa_assert_se(dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &e->volume.values[i]));
+
+        pa_assert_se(dbus_message_iter_close_container(&array_iter, &struct_iter));
+    }
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &array_iter));
+}
+
+static void append_volume_variant(DBusMessageIter *iter, struct entry *e) {
+    DBusMessageIter variant_iter;
+
+    pa_assert(iter);
+    pa_assert(e);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a(uu)", &variant_iter));
+
+    append_volume(&variant_iter, e);
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+}
+
+static void send_new_entry_signal(struct dbus_entry *entry) {
+    DBusMessage *signal_msg;
+
+    pa_assert(entry);
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(OBJECT_PATH, INTERFACE_STREAM_RESTORE, signals[SIGNAL_NEW_ENTRY].name));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &entry->object_path, DBUS_TYPE_INVALID));
+    pa_dbus_protocol_send_signal(entry->userdata->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+}
+
+static void send_entry_removed_signal(struct dbus_entry *entry) {
+    DBusMessage *signal_msg;
+
+    pa_assert(entry);
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(OBJECT_PATH, INTERFACE_STREAM_RESTORE, signals[SIGNAL_ENTRY_REMOVED].name));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &entry->object_path, DBUS_TYPE_INVALID));
+    pa_dbus_protocol_send_signal(entry->userdata->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+}
+
+static void send_device_updated_signal(struct dbus_entry *de, struct entry *e) {
+    DBusMessage *signal_msg;
+    const char *device;
+
+    pa_assert(de);
+    pa_assert(e);
+
+    device = e->device_valid ? e->device : "";
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_DEVICE_UPDATED].name));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID));
+    pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+}
+
+static void send_volume_updated_signal(struct dbus_entry *de, struct entry *e) {
+    DBusMessage *signal_msg;
+    DBusMessageIter msg_iter;
+
+    pa_assert(de);
+    pa_assert(e);
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_VOLUME_UPDATED].name));
+    dbus_message_iter_init_append(signal_msg, &msg_iter);
+    append_volume(&msg_iter, e);
+    pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+}
+
+static void send_mute_updated_signal(struct dbus_entry *de, struct entry *e) {
+    DBusMessage *signal_msg;
+    dbus_bool_t muted;
+
+    pa_assert(de);
+    pa_assert(e);
+
+    pa_assert(e->muted_valid);
+
+    muted = e->muted;
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_MUTE_UPDATED].name));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_BOOLEAN, &muted, DBUS_TYPE_INVALID));
+    pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+}
+
+static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    dbus_uint32_t interface_revision = DBUS_INTERFACE_REVISION;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &interface_revision);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_entries(struct userdata *u, unsigned *n) {
+    const char **entries;
+    unsigned i = 0;
+    void *state = NULL;
+    struct dbus_entry *de;
+
+    pa_assert(u);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(u->dbus_entries);
+
+    if (*n == 0)
+        return NULL;
+
+    entries = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(de, u->dbus_entries, state)
+        entries[i++] = de->object_path;
+
+    return entries;
+}
+
+static void handle_get_entries(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct userdata *u = userdata;
+    const char **entries;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    entries = get_entries(u, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, entries, n);
+
+    pa_xfree(entries);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct userdata *u = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t interface_revision;
+    const char **entries;
+    unsigned n_entries;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    interface_revision = DBUS_INTERFACE_REVISION;
+    entries = get_entries(u, &n_entries);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INTERFACE_REVISION].property_name, DBUS_TYPE_UINT32, &interface_revision);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ENTRIES].property_name, DBUS_TYPE_OBJECT_PATH, entries, n_entries);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(entries);
+}
+
+static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct userdata *u = userdata;
+    DBusMessageIter msg_iter;
+    const char *name = NULL;
+    const char *device = NULL;
+    pa_channel_map map;
+    pa_cvolume vol;
+    dbus_bool_t muted = FALSE;
+    dbus_bool_t apply_immediately = FALSE;
+    struct dbus_entry *dbus_entry = NULL;
+    struct entry *e = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &name);
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &device);
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0)
+        return;
+
+    dbus_message_iter_get_basic(&msg_iter, &muted);
+
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &apply_immediately);
+
+    if (!*name) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "An empty string was given as the entry name.");
+        return;
+    }
+
+    if ((dbus_entry = pa_hashmap_get(u->dbus_entries, name))) {
+        bool mute_updated = false;
+        bool volume_updated = false;
+        bool device_updated = false;
+
+        pa_assert_se(e = entry_read(u, name));
+        mute_updated = e->muted != muted;
+        e->muted = muted;
+        e->muted_valid = true;
+
+        volume_updated = (e->volume_valid != !!map.channels) || !pa_cvolume_equal(&e->volume, &vol);
+        e->volume = vol;
+        e->channel_map = map;
+        e->volume_valid = !!map.channels;
+
+        device_updated = (e->device_valid != !!device[0]) || !pa_safe_streq(e->device, device);
+        pa_xfree(e->device);
+        e->device = pa_xstrdup(device);
+        e->device_valid = !!device[0];
+
+        if (mute_updated)
+            send_mute_updated_signal(dbus_entry, e);
+        if (volume_updated)
+            send_volume_updated_signal(dbus_entry, e);
+        if (device_updated)
+            send_device_updated_signal(dbus_entry, e);
+
+    } else {
+        dbus_entry = dbus_entry_new(u, name);
+        pa_assert_se(pa_hashmap_put(u->dbus_entries, dbus_entry->entry_name, dbus_entry) == 0);
+
+        e = entry_new();
+        e->muted_valid = true;
+        e->volume_valid = !!map.channels;
+        e->device_valid = !!device[0];
+        e->muted = muted;
+        e->volume = vol;
+        e->channel_map = map;
+        e->device = pa_xstrdup(device);
+
+        send_new_entry_signal(dbus_entry);
+    }
+
+    pa_assert_se(entry_write(u, name, e, true));
+
+    if (apply_immediately)
+        entry_apply(u, name, e);
+
+    trigger_save(u);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    entry_free(e);
+}
+
+static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct userdata *u = userdata;
+    const char *name;
+    struct dbus_entry *de;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID));
+
+    if (!(de = pa_hashmap_get(u->dbus_entries, name))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such stream restore entry.");
+        return;
+    }
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &de->object_path);
+}
+
+static void handle_entry_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &de->index);
+}
+
+static void handle_entry_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &de->entry_name);
+}
+
+static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    struct entry *e;
+    const char *device;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
+
+    device = e->device_valid ? e->device : "";
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &device);
+
+    entry_free(e);
+}
+
+static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    struct dbus_entry *de = userdata;
+    const char *device;
+    struct entry *e;
+    bool updated;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(de);
+
+    dbus_message_iter_get_basic(iter, &device);
+
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
+
+    updated = (e->device_valid != !!device[0]) || !pa_safe_streq(e->device, device);
+
+    if (updated) {
+        pa_xfree(e->device);
+        e->device = pa_xstrdup(device);
+        e->device_valid = !!device[0];
+
+        pa_assert_se(entry_write(de->userdata, de->entry_name, e, true));
+
+        entry_apply(de->userdata, de->entry_name, e);
+        send_device_updated_signal(de, e);
+        trigger_save(de->userdata);
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    entry_free(e);
+}
+
+static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    DBusMessage *reply;
+    DBusMessageIter msg_iter;
+    struct entry *e;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
+
+    pa_assert_se(reply = dbus_message_new_method_return(msg));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    append_volume_variant(&msg_iter, e);
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    entry_free(e);
+}
+
+static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    struct dbus_entry *de = userdata;
+    pa_channel_map map;
+    pa_cvolume vol;
+    struct entry *e = NULL;
+    bool updated = false;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(de);
+
+    if (get_volume_arg(conn, msg, iter, &map, &vol) < 0)
+        return;
+
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
+
+    updated = (e->volume_valid != !!map.channels) || !pa_cvolume_equal(&e->volume, &vol);
+
+    if (updated) {
+        e->volume = vol;
+        e->channel_map = map;
+        e->volume_valid = !!map.channels;
+
+        pa_assert_se(entry_write(de->userdata, de->entry_name, e, true));
+
+        entry_apply(de->userdata, de->entry_name, e);
+        send_volume_updated_signal(de, e);
+        trigger_save(de->userdata);
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    entry_free(e);
+}
+
+static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    struct entry *e;
+    dbus_bool_t mute;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
+
+    mute = e->muted_valid ? e->muted : FALSE;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &mute);
+
+    entry_free(e);
+}
+
+static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+    struct dbus_entry *de = userdata;
+    dbus_bool_t mute;
+    struct entry *e;
+    bool updated;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(de);
+
+    dbus_message_iter_get_basic(iter, &mute);
+
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
+
+    updated = !e->muted_valid || e->muted != mute;
+
+    if (updated) {
+        e->muted = mute;
+        e->muted_valid = true;
+
+        pa_assert_se(entry_write(de->userdata, de->entry_name, e, true));
+
+        entry_apply(de->userdata, de->entry_name, e);
+        send_mute_updated_signal(de, e);
+        trigger_save(de->userdata);
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    entry_free(e);
+}
+
+static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    struct entry *e;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    const char *device;
+    dbus_bool_t mute;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
+
+    device = e->device_valid ? e->device : "";
+    mute = e->muted_valid ? e->muted : FALSE;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &de->index);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &de->entry_name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_DEVICE].property_name, DBUS_TYPE_STRING, &device);
+
+    pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+
+    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &entry_property_handlers[ENTRY_PROPERTY_HANDLER_VOLUME].property_name));
+    append_volume_variant(&dict_entry_iter, e);
+
+    pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &mute);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    entry_free(e);
+}
+
+static void handle_entry_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    pa_datum key;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    key.data = de->entry_name;
+    key.size = strlen(de->entry_name);
+
+    pa_assert_se(pa_database_unset(de->userdata->database, &key) == 0);
+
+    send_entry_removed_signal(de);
+    trigger_save(de->userdata);
+
+    pa_assert_se(pa_hashmap_remove_and_free(de->userdata->dbus_entries, de->entry_name) >= 0);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+#endif /* HAVE_DBUS */
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(u);
+
+    pa_assert(e == u->save_time_event);
+    u->core->mainloop->time_free(u->save_time_event);
+    u->save_time_event = NULL;
+
+    pa_database_sync(u->database);
+    pa_log_info("Synced.");
+}
+
+static struct entry* entry_new(void) {
+    struct entry *r = pa_xnew0(struct entry, 1);
+    r->version = ENTRY_VERSION;
+    return r;
+}
+
+static void entry_free(struct entry* e) {
+    pa_assert(e);
+
+    pa_xfree(e->device);
+    pa_xfree(e->card);
+    pa_xfree(e);
+}
+
+static bool entry_write(struct userdata *u, const char *name, const struct entry *e, bool replace) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    bool r;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_put_boolean(t, e->volume_valid);
+    pa_tagstruct_put_channel_map(t, &e->channel_map);
+    pa_tagstruct_put_cvolume(t, &e->volume);
+    pa_tagstruct_put_boolean(t, e->muted_valid);
+    pa_tagstruct_put_boolean(t, e->muted);
+    pa_tagstruct_put_boolean(t, e->device_valid);
+    pa_tagstruct_puts(t, e->device);
+    pa_tagstruct_put_boolean(t, e->card_valid);
+    pa_tagstruct_puts(t, e->card);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, replace) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+
+#define LEGACY_ENTRY_VERSION 3
+static struct entry *legacy_entry_read(struct userdata *u, const char *name) {
+    struct legacy_entry {
+        uint8_t version;
+        bool muted_valid:1, volume_valid:1, device_valid:1, card_valid:1;
+        bool muted:1;
+        pa_channel_map channel_map;
+        pa_cvolume volume;
+        char device[PA_NAME_MAX];
+        char card[PA_NAME_MAX];
+    } PA_GCC_PACKED;
+
+    pa_datum key;
+    pa_datum data;
+    struct legacy_entry *le;
+    struct entry *e;
+
+    pa_assert(u);
+    pa_assert(name);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    pa_zero(data);
+
+    if (!pa_database_get(u->database, &key, &data))
+        goto fail;
+
+    if (data.size != sizeof(struct legacy_entry)) {
+        pa_log_debug("Size does not match.");
+        goto fail;
+    }
+
+    le = (struct legacy_entry *) data.data;
+
+    if (le->version != LEGACY_ENTRY_VERSION) {
+        pa_log_debug("Version mismatch.");
+        goto fail;
+    }
+
+    if (!memchr(le->device, 0, sizeof(le->device))) {
+        pa_log_warn("Device has missing NUL byte.");
+        goto fail;
+    }
+
+    if (!memchr(le->card, 0, sizeof(le->card))) {
+        pa_log_warn("Card has missing NUL byte.");
+        goto fail;
+    }
+
+    if (le->device_valid && !pa_namereg_is_valid_name(le->device)) {
+        pa_log_warn("Invalid device name stored in database for legacy stream");
+        goto fail;
+    }
+
+    if (le->card_valid && !pa_namereg_is_valid_name(le->card)) {
+        pa_log_warn("Invalid card name stored in database for legacy stream");
+        goto fail;
+    }
+
+    if (le->volume_valid && !pa_channel_map_valid(&le->channel_map)) {
+        pa_log_warn("Invalid channel map stored in database for legacy stream");
+        goto fail;
+    }
+
+    if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
+        pa_log_warn("Invalid volume stored in database for legacy stream");
+        goto fail;
+    }
+
+    e = entry_new();
+    e->muted_valid = le->muted_valid;
+    e->muted = le->muted;
+    e->volume_valid = le->volume_valid;
+    e->channel_map = le->channel_map;
+    e->volume = le->volume;
+    e->device_valid = le->device_valid;
+    e->device = pa_xstrdup(le->device);
+    e->card_valid = le->card_valid;
+    e->card = pa_xstrdup(le->card);
+    return e;
+
+fail:
+    pa_datum_free(&data);
+
+    return NULL;
+}
+#endif
+
+static struct entry *entry_read(struct userdata *u, const char *name) {
+    pa_datum key, data;
+    struct entry *e = NULL;
+    pa_tagstruct *t = NULL;
+    const char *device, *card;
+
+    pa_assert(u);
+    pa_assert(name);
+
+    key.data = (char*) name;
+    key.size = strlen(name);
+
+    pa_zero(data);
+
+    if (!pa_database_get(u->database, &key, &data))
+        goto fail;
+
+    t = pa_tagstruct_new_fixed(data.data, data.size);
+    e = entry_new();
+
+    if (pa_tagstruct_getu8(t, &e->version) < 0 ||
+        e->version > ENTRY_VERSION ||
+        pa_tagstruct_get_boolean(t, &e->volume_valid) < 0 ||
+        pa_tagstruct_get_channel_map(t, &e->channel_map) < 0 ||
+        pa_tagstruct_get_cvolume(t, &e->volume) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->device_valid) < 0 ||
+        pa_tagstruct_gets(t, &device) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->card_valid) < 0 ||
+        pa_tagstruct_gets(t, &card) < 0) {
+
+        goto fail;
+    }
+
+    e->device = pa_xstrdup(device);
+    e->card = pa_xstrdup(card);
+
+    if (!pa_tagstruct_eof(t))
+        goto fail;
+
+    if (e->device_valid && !pa_namereg_is_valid_name(e->device)) {
+        pa_log_warn("Invalid device name stored in database for stream %s", name);
+        goto fail;
+    }
+
+    if (e->card_valid && !pa_namereg_is_valid_name(e->card)) {
+        pa_log_warn("Invalid card name stored in database for stream %s", name);
+        goto fail;
+    }
+
+    if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
+        pa_log_warn("Invalid channel map stored in database for stream %s", name);
+        goto fail;
+    }
+
+    if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) {
+        pa_log_warn("Invalid volume stored in database for stream %s", name);
+        goto fail;
+    }
+
+    pa_tagstruct_free(t);
+    pa_datum_free(&data);
+
+    return e;
+
+fail:
+    if (e)
+        entry_free(e);
+    if (t)
+        pa_tagstruct_free(t);
+
+    pa_datum_free(&data);
+    return NULL;
+}
+
+static struct entry* entry_copy(const struct entry *e) {
+    struct entry* r;
+
+    pa_assert(e);
+    r = entry_new();
+    *r = *e;
+    r->device = pa_xstrdup(e->device);
+    r->card = pa_xstrdup(e->card);
+    return r;
+}
+
+static void trigger_save(struct userdata *u) {
+    pa_native_connection *c;
+    uint32_t idx;
+
+    PA_IDXSET_FOREACH(c, u->subscribed, idx) {
+        pa_tagstruct *t;
+
+        t = pa_tagstruct_new();
+        pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
+        pa_tagstruct_putu32(t, 0);
+        pa_tagstruct_putu32(t, u->module->index);
+        pa_tagstruct_puts(t, u->module->name);
+        pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
+
+        pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
+    }
+
+    if (u->save_time_event)
+        return;
+
+    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
+}
+
+static bool entries_equal(const struct entry *a, const struct entry *b) {
+    pa_cvolume t;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    if (a->device_valid != b->device_valid ||
+        (a->device_valid && !pa_streq(a->device, b->device)))
+        return false;
+
+    if (a->card_valid != b->card_valid ||
+        (a->card_valid && !pa_streq(a->card, b->card)))
+        return false;
+
+    if (a->muted_valid != b->muted_valid ||
+        (a->muted_valid && (a->muted != b->muted)))
+        return false;
+
+    t = b->volume;
+    if (a->volume_valid != b->volume_valid ||
+        (a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume)))
+        return false;
+
+    return true;
+}
+
+static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    struct userdata *u = userdata;
+    struct entry *entry, *old = NULL;
+    char *name = NULL;
+
+    /* These are only used when D-Bus is enabled, but in order to reduce ifdef
+     * clutter these are defined here unconditionally. */
+    bool created_new_entry = true;
+    bool device_updated = false;
+    bool volume_updated = false;
+    bool mute_updated = false;
+
+#ifdef HAVE_DBUS
+    struct dbus_entry *de = NULL;
+#endif
+
+    pa_assert(c);
+    pa_assert(u);
+
+    if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
+        return;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
+        pa_sink_input *sink_input;
+
+        if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx)))
+            return;
+
+        /* Ignore this sink input if it is connecting a filter sink to
+         * the master */
+        if (sink_input->origin_sink)
+            return;
+
+        if (!(name = pa_proplist_get_stream_group(sink_input->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
+            return;
+
+        if ((old = entry_read(u, name))) {
+            entry = entry_copy(old);
+            created_new_entry = false;
+        } else
+            entry = entry_new();
+
+        if (sink_input->save_volume && pa_sink_input_is_volume_readable(sink_input)) {
+            pa_assert(sink_input->volume_writable);
+
+            entry->channel_map = sink_input->channel_map;
+            pa_sink_input_get_volume(sink_input, &entry->volume, false);
+            entry->volume_valid = true;
+
+            volume_updated = !created_new_entry
+                             && (!old->volume_valid
+                                 || !pa_channel_map_equal(&entry->channel_map, &old->channel_map)
+                                 || !pa_cvolume_equal(&entry->volume, &old->volume));
+        }
+
+        if (sink_input->save_muted) {
+            entry->muted = sink_input->muted;
+            entry->muted_valid = true;
+
+            mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
+        }
+
+        if (sink_input->save_sink) {
+            pa_xfree(entry->device);
+            entry->device = pa_xstrdup(sink_input->sink->name);
+            entry->device_valid = true;
+
+            device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry->device, old->device));
+            if (sink_input->sink->card) {
+                pa_xfree(entry->card);
+                entry->card = pa_xstrdup(sink_input->sink->card->name);
+                entry->card_valid = true;
+            }
+        }
+
+    } else {
+        pa_source_output *source_output;
+
+        pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
+
+        if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx)))
+            return;
+
+        /* Ignore this source output if it is connecting a filter source to
+         * the master */
+        if (source_output->destination_source)
+            return;
+
+        if (!(name = pa_proplist_get_stream_group(source_output->proplist, "source-output", IDENTIFICATION_PROPERTY)))
+            return;
+
+        if ((old = entry_read(u, name))) {
+            entry = entry_copy(old);
+            created_new_entry = false;
+        } else
+            entry = entry_new();
+
+        if (source_output->save_volume && pa_source_output_is_volume_readable(source_output)) {
+            pa_assert(source_output->volume_writable);
+
+            entry->channel_map = source_output->channel_map;
+            pa_source_output_get_volume(source_output, &entry->volume, false);
+            entry->volume_valid = true;
+
+            volume_updated = !created_new_entry
+                             && (!old->volume_valid
+                                 || !pa_channel_map_equal(&entry->channel_map, &old->channel_map)
+                                 || !pa_cvolume_equal(&entry->volume, &old->volume));
+        }
+
+        if (source_output->save_muted) {
+            entry->muted = source_output->muted;
+            entry->muted_valid = true;
+
+            mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
+        }
+
+        if (source_output->save_source) {
+            pa_xfree(entry->device);
+            entry->device = pa_xstrdup(source_output->source->name);
+            entry->device_valid = true;
+
+            device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry->device, old->device));
+
+            if (source_output->source->card) {
+                pa_xfree(entry->card);
+                entry->card = pa_xstrdup(source_output->source->card->name);
+                entry->card_valid = true;
+            }
+        }
+    }
+
+    pa_assert(entry);
+
+    if (old) {
+
+        if (entries_equal(old, entry)) {
+            entry_free(old);
+            entry_free(entry);
+            pa_xfree(name);
+            return;
+        }
+
+        entry_free(old);
+    }
+
+    pa_log_info("Storing volume/mute/device for stream %s.", name);
+
+    if (entry_write(u, name, entry, true))
+        trigger_save(u);
+
+#ifdef HAVE_DBUS
+    if (created_new_entry) {
+        de = dbus_entry_new(u, name);
+        pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) == 0);
+        send_new_entry_signal(de);
+    } else {
+        pa_assert_se(de = pa_hashmap_get(u->dbus_entries, name));
+
+        if (device_updated)
+            send_device_updated_signal(de, entry);
+        if (volume_updated)
+            send_volume_updated_signal(de, entry);
+        if (mute_updated)
+            send_mute_updated_signal(de, entry);
+    }
+#endif
+
+    entry_free(entry);
+    pa_xfree(name);
+}
+
+static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+    pa_assert(u->restore_device);
+
+    if (!(name = pa_proplist_get_stream_group(new_data->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
+        return PA_HOOK_OK;
+
+    if (new_data->sink)
+        pa_log_debug("Not restoring device for stream %s, because already set to '%s'.", name, new_data->sink->name);
+    else if (new_data->origin_sink)
+        pa_log_debug("Not restoring device for stream %s, because it connects a filter to the master sink.", name);
+    else if ((e = entry_read(u, name))) {
+        pa_sink *s = NULL;
+
+        if (e->device_valid)
+            s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK);
+
+        if (!s && e->card_valid) {
+            pa_card *card;
+
+            if ((card = pa_namereg_get(c, e->card, PA_NAMEREG_CARD)))
+                s = pa_idxset_first(card->sinks, NULL);
+        }
+
+        /* It might happen that a stream and a sink are set up at the
+           same time, in which case we want to make sure we don't
+           interfere with that */
+        if (s && PA_SINK_IS_LINKED(pa_sink_get_state(s)))
+            if (pa_sink_input_new_data_set_sink(new_data, s, true))
+                pa_log_info("Restoring device for stream %s.", name);
+
+        entry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+    pa_assert(u->restore_volume || u->restore_muted);
+
+    if (!(name = pa_proplist_get_stream_group(new_data->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
+        return PA_HOOK_OK;
+
+    if (new_data->origin_sink) {
+        pa_log_debug("Not restoring volume for sink input %s, because it connects a filter to the master sink.", name);
+        return PA_HOOK_OK;
+    }
+
+    if ((e = entry_read(u, name))) {
+
+        if (u->restore_volume && e->volume_valid) {
+            if (!new_data->volume_writable)
+                pa_log_debug("Not restoring volume for sink input %s, because its volume can't be changed.", name);
+            else if (new_data->volume_is_set)
+                pa_log_debug("Not restoring volume for sink input %s, because already set.", name);
+            else {
+                pa_cvolume v;
+
+                pa_log_info("Restoring volume for sink input %s.", name);
+
+                v = e->volume;
+                pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
+                pa_sink_input_new_data_set_volume(new_data, &v);
+
+                new_data->volume_is_absolute = false;
+                new_data->save_volume = true;
+            }
+        }
+
+        if (u->restore_muted && e->muted_valid) {
+
+            if (!new_data->muted_is_set) {
+                pa_log_info("Restoring mute state for sink input %s.", name);
+                pa_sink_input_new_data_set_muted(new_data, e->muted);
+                new_data->save_muted = true;
+            } else
+                pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);
+        }
+
+        entry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+    pa_assert(u->restore_device);
+
+    if (new_data->direct_on_input)
+        return PA_HOOK_OK;
+
+    if (!(name = pa_proplist_get_stream_group(new_data->proplist, "source-output", IDENTIFICATION_PROPERTY)))
+        return PA_HOOK_OK;
+
+    if (new_data->source)
+        pa_log_debug("Not restoring device for stream %s, because already set", name);
+    else if (new_data->destination_source)
+        pa_log_debug("Not restoring device for stream %s, because it connects a filter to the master source.", name);
+    else if ((e = entry_read(u, name))) {
+        pa_source *s = NULL;
+
+        if (e->device_valid)
+            s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE);
+
+        if (!s && e->card_valid) {
+            pa_card *card;
+
+            if ((card = pa_namereg_get(c, e->card, PA_NAMEREG_CARD)))
+                s = pa_idxset_first(card->sources, NULL);
+        }
+
+        /* It might happen that a stream and a sink are set up at the
+           same time, in which case we want to make sure we don't
+           interfere with that */
+        if (s && PA_SOURCE_IS_LINKED(pa_source_get_state(s))) {
+            pa_log_info("Restoring device for stream %s.", name);
+            pa_source_output_new_data_set_source(new_data, s, true);
+        }
+
+        entry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_fixate_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+    pa_assert(u->restore_volume || u->restore_muted);
+
+    if (!(name = pa_proplist_get_stream_group(new_data->proplist, "source-output", IDENTIFICATION_PROPERTY)))
+        return PA_HOOK_OK;
+
+    if (new_data->destination_source) {
+        pa_log_debug("Not restoring volume for source output %s, because it connects a filter to the master source.", name);
+        return PA_HOOK_OK;
+    }
+
+    if ((e = entry_read(u, name))) {
+
+        if (u->restore_volume && e->volume_valid) {
+            if (!new_data->volume_writable)
+                pa_log_debug("Not restoring volume for source output %s, because its volume can't be changed.", name);
+            else if (new_data->volume_is_set)
+                pa_log_debug("Not restoring volume for source output %s, because already set.", name);
+            else {
+                pa_cvolume v;
+
+                pa_log_info("Restoring volume for source output %s.", name);
+
+                v = e->volume;
+                pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
+                pa_source_output_new_data_set_volume(new_data, &v);
+
+                new_data->volume_is_absolute = false;
+                new_data->save_volume = true;
+            }
+        }
+
+        if (u->restore_muted && e->muted_valid) {
+
+            if (!new_data->muted_is_set) {
+                pa_log_info("Restoring mute state for source output %s.", name);
+                pa_source_output_new_data_set_muted(new_data, e->muted);
+                new_data->save_muted = true;
+            } else
+                pa_log_debug("Not restoring mute state for source output %s, because already set.", name);
+        }
+
+        entry_free(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+    pa_sink_input *si;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+    pa_assert(u->on_hotplug && u->restore_device);
+
+    PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
+        char *name;
+        struct entry *e;
+
+        if (si->sink == sink)
+            continue;
+
+        if (si->save_sink)
+            continue;
+
+        /* Skip this if it is already in the process of being moved
+         * anyway */
+        if (!si->sink)
+            continue;
+
+        /* Skip this sink input if it is connecting a filter sink to
+         * the master */
+        if (si->origin_sink)
+            continue;
+
+        /* It might happen that a stream and a sink are set up at the
+           same time, in which case we want to make sure we don't
+           interfere with that */
+        if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
+            continue;
+
+        if (!(name = pa_proplist_get_stream_group(si->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
+            continue;
+
+        if ((e = entry_read(u, name))) {
+            if (e->device_valid && pa_streq(e->device, sink->name))
+                pa_sink_input_move_to(si, sink, true);
+
+            entry_free(e);
+        }
+
+        pa_xfree(name);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
+    pa_source_output *so;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(source);
+    pa_assert(u);
+    pa_assert(u->on_hotplug && u->restore_device);
+
+    PA_IDXSET_FOREACH(so, c->source_outputs, idx) {
+        char *name;
+        struct entry *e;
+
+        if (so->source == source)
+            continue;
+
+        if (so->save_source)
+            continue;
+
+        if (so->direct_on_input)
+            continue;
+
+        /* Skip this if it is already in the process of being moved anyway */
+        if (!so->source)
+            continue;
+
+        /* Skip this source output if it is connecting a filter source to
+         * the master */
+        if (so->destination_source)
+            continue;
+
+        /* It might happen that a stream and a source are set up at the
+           same time, in which case we want to make sure we don't
+           interfere with that */
+        if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
+            continue;
+
+        if (!(name = pa_proplist_get_stream_group(so->proplist, "source-output", IDENTIFICATION_PROPERTY)))
+            continue;
+
+        if ((e = entry_read(u, name))) {
+            if (e->device_valid && pa_streq(e->device, source->name))
+                pa_source_output_move_to(so, source, true);
+
+            entry_free(e);
+        }
+
+        pa_xfree(name);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+    pa_sink_input *si;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+    pa_assert(u->on_rescue && u->restore_device);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    PA_IDXSET_FOREACH(si, sink->inputs, idx) {
+        char *name;
+        struct entry *e;
+
+        if (!si->sink)
+            continue;
+
+        /* Skip this sink input if it is connecting a filter sink to
+         * the master */
+        if (si->origin_sink)
+            continue;
+
+        if (!(name = pa_proplist_get_stream_group(si->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
+            continue;
+
+        if ((e = entry_read(u, name))) {
+
+            if (e->device_valid) {
+                pa_sink *d;
+
+                if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) &&
+                    d != sink &&
+                    PA_SINK_IS_LINKED(pa_sink_get_state(d)))
+                    pa_sink_input_move_to(si, d, true);
+            }
+
+            entry_free(e);
+        }
+
+        pa_xfree(name);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
+    pa_source_output *so;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(source);
+    pa_assert(u);
+    pa_assert(u->on_rescue && u->restore_device);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    PA_IDXSET_FOREACH(so, source->outputs, idx) {
+        char *name;
+        struct entry *e;
+
+        if (so->direct_on_input)
+            continue;
+
+        if (!so->source)
+            continue;
+
+        /* Skip this source output if it is connecting a filter source to
+         * the master */
+        if (so->destination_source)
+            continue;
+
+        if (!(name = pa_proplist_get_stream_group(so->proplist, "source-output", IDENTIFICATION_PROPERTY)))
+            continue;
+
+        if ((e = entry_read(u, name))) {
+
+            if (e->device_valid) {
+                pa_source *d;
+
+                if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE)) &&
+                    d != source &&
+                    PA_SOURCE_IS_LINKED(pa_source_get_state(d)))
+                    pa_source_output_move_to(so, d, true);
+            }
+
+            entry_free(e);
+        }
+
+        pa_xfree(name);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static int fill_db(struct userdata *u, const char *filename) {
+    FILE *f;
+    int n = 0;
+    int ret = -1;
+    char *fn = NULL;
+
+    pa_assert(u);
+
+    if (filename)
+        f = fopen(fn = pa_xstrdup(filename), "r");
+    else
+        f = pa_open_config_file(DEFAULT_FALLBACK_FILE, DEFAULT_FALLBACK_FILE_USER, NULL, &fn);
+
+    if (!f) {
+        if (filename)
+            pa_log("Failed to open %s: %s", filename, pa_cstrerror(errno));
+        else
+            ret = 0;
+
+        goto finish;
+    }
+
+    while (!feof(f)) {
+        char ln[256];
+        char *d, *v;
+        double db;
+
+        if (!fgets(ln, sizeof(ln), f))
+            break;
+
+        n++;
+
+        pa_strip_nl(ln);
+
+        if (!*ln || ln[0] == '#' || ln[0] == ';')
+            continue;
+
+        d = ln+strcspn(ln, WHITESPACE);
+        v = d+strspn(d, WHITESPACE);
+
+        if (!*v) {
+            pa_log("[%s:%u] failed to parse line - too few words", fn, n);
+            goto finish;
+        }
+
+        *d = 0;
+        if (pa_atod(v, &db) >= 0) {
+            if (db <= 0.0) {
+                struct entry e;
+
+                pa_zero(e);
+                e.version = ENTRY_VERSION;
+                e.volume_valid = true;
+                pa_cvolume_set(&e.volume, 1, pa_sw_volume_from_dB(db));
+                pa_channel_map_init_mono(&e.channel_map);
+
+                if (entry_write(u, ln, &e, false))
+                    pa_log_debug("Setting %s to %0.2f dB.", ln, db);
+            } else
+                pa_log_warn("[%s:%u] Positive dB values are not allowed, not setting entry %s.", fn, n, ln);
+        } else
+            pa_log_warn("[%s:%u] Couldn't parse '%s' as a double, not setting entry %s.", fn, n, v, ln);
+    }
+
+    trigger_save(u);
+    ret = 0;
+
+finish:
+    if (f)
+        fclose(f);
+
+    pa_xfree(fn);
+
+    return ret;
+}
+
+static void entry_apply(struct userdata *u, const char *name, struct entry *e) {
+    pa_sink_input *si;
+    pa_source_output *so;
+    uint32_t idx;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
+        char *n;
+        pa_sink *s;
+
+        if (!(n = pa_proplist_get_stream_group(si->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
+            continue;
+
+        if (!pa_streq(name, n)) {
+            pa_xfree(n);
+            continue;
+        }
+        pa_xfree(n);
+
+        if (u->restore_volume && e->volume_valid && si->volume_writable) {
+            pa_cvolume v;
+
+            v = e->volume;
+            pa_log_info("Restoring volume for sink input %s.", name);
+            pa_cvolume_remap(&v, &e->channel_map, &si->channel_map);
+            pa_sink_input_set_volume(si, &v, true, false);
+        }
+
+        if (u->restore_muted && e->muted_valid) {
+            pa_log_info("Restoring mute state for sink input %s.", name);
+            pa_sink_input_set_mute(si, e->muted, true);
+        }
+
+        if (u->restore_device) {
+            if (!e->device_valid) {
+                if (si->save_sink) {
+                    pa_log_info("Ensuring device is not saved for stream %s.", name);
+                    /* If the device is not valid we should make sure the
+                       save flag is cleared as the user may have specifically
+                       removed the sink element from the rule. */
+                    si->save_sink = false;
+                    /* This is cheating a bit. The sink input itself has not changed
+                       but the rules governing its routing have, so we fire this event
+                       such that other routing modules (e.g. module-device-manager)
+                       will pick up the change and reapply their routing */
+                    pa_subscription_post(si->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, si->index);
+                }
+            } else if ((s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {
+                pa_log_info("Restoring device for stream %s.", name);
+                pa_sink_input_move_to(si, s, true);
+            }
+        }
+    }
+
+    PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
+        char *n;
+        pa_source *s;
+
+        if (!(n = pa_proplist_get_stream_group(so->proplist, "source-output", IDENTIFICATION_PROPERTY)))
+            continue;
+
+        if (!pa_streq(name, n)) {
+            pa_xfree(n);
+            continue;
+        }
+        pa_xfree(n);
+
+        if (u->restore_volume && e->volume_valid && so->volume_writable) {
+            pa_cvolume v;
+
+            v = e->volume;
+            pa_log_info("Restoring volume for source output %s.", name);
+            pa_cvolume_remap(&v, &e->channel_map, &so->channel_map);
+            pa_source_output_set_volume(so, &v, true, false);
+        }
+
+        if (u->restore_muted && e->muted_valid) {
+            pa_log_info("Restoring mute state for source output %s.", name);
+            pa_source_output_set_mute(so, e->muted, true);
+        }
+
+        if (u->restore_device) {
+            if (!e->device_valid) {
+                if (so->save_source) {
+                    pa_log_info("Ensuring device is not saved for stream %s.", name);
+                    /* If the device is not valid we should make sure the
+                       save flag is cleared as the user may have specifically
+                       removed the source element from the rule. */
+                    so->save_source = false;
+                    /* This is cheating a bit. The source output itself has not changed
+                       but the rules governing its routing have, so we fire this event
+                       such that other routing modules (e.g. module-device-manager)
+                       will pick up the change and reapply their routing */
+                    pa_subscription_post(so->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, so->index);
+                }
+            } else if ((s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
+                pa_log_info("Restoring device for stream %s.", name);
+                pa_source_output_move_to(so, s, true);
+            }
+        }
+    }
+}
+
+#ifdef DEBUG_VOLUME
+PA_GCC_UNUSED static void stream_restore_dump_database(struct userdata *u) {
+    pa_datum key;
+    bool done;
+
+    done = !pa_database_first(u->database, &key, NULL);
+
+    while (!done) {
+        pa_datum next_key;
+        struct entry *e;
+        char *name;
+
+        done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+        name = pa_xstrndup(key.data, key.size);
+        pa_datum_free(&key);
+
+        if ((e = entry_read(u, name))) {
+            char t[256];
+            pa_log("name=%s", name);
+            pa_log("device=%s %s", e->device, pa_yes_no(e->device_valid));
+            pa_log("channel_map=%s", pa_channel_map_snprint(t, sizeof(t), &e->channel_map));
+            pa_log("volume=%s %s",
+                   pa_cvolume_snprint_verbose(t, sizeof(t), &e->volume, &e->channel_map, true),
+                   pa_yes_no(e->volume_valid));
+            pa_log("mute=%s %s", pa_yes_no(e->muted), pa_yes_no(e->volume_valid));
+            entry_free(e);
+        }
+
+        pa_xfree(name);
+
+        key = next_key;
+    }
+}
+#endif
+
+#define EXT_VERSION 1
+
+static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
+    struct userdata *u;
+    uint32_t command;
+    pa_tagstruct *reply = NULL;
+
+    pa_assert(p);
+    pa_assert(m);
+    pa_assert(c);
+    pa_assert(t);
+
+    u = m->userdata;
+
+    if (pa_tagstruct_getu32(t, &command) < 0)
+        goto fail;
+
+    reply = pa_tagstruct_new();
+    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+    pa_tagstruct_putu32(reply, tag);
+
+    switch (command) {
+        case SUBCOMMAND_TEST: {
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            pa_tagstruct_putu32(reply, EXT_VERSION);
+            break;
+        }
+
+        case SUBCOMMAND_READ: {
+            pa_datum key;
+            bool done;
+
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            done = !pa_database_first(u->database, &key, NULL);
+
+            while (!done) {
+                pa_datum next_key;
+                struct entry *e;
+                char *name;
+
+                done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+                name = pa_xstrndup(key.data, key.size);
+                pa_datum_free(&key);
+
+                if ((e = entry_read(u, name))) {
+                    pa_cvolume r;
+                    pa_channel_map cm;
+
+                    pa_tagstruct_puts(reply, name);
+                    pa_tagstruct_put_channel_map(reply, e->volume_valid ? &e->channel_map : pa_channel_map_init(&cm));
+                    pa_tagstruct_put_cvolume(reply, e->volume_valid ? &e->volume : pa_cvolume_init(&r));
+                    pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
+                    pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : false);
+
+                    entry_free(e);
+                }
+
+                pa_xfree(name);
+
+                key = next_key;
+            }
+
+            break;
+        }
+
+        case SUBCOMMAND_WRITE: {
+            uint32_t mode;
+            bool apply_immediately = false;
+
+            if (pa_tagstruct_getu32(t, &mode) < 0 ||
+                pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
+                goto fail;
+
+            if (mode != PA_UPDATE_MERGE &&
+                mode != PA_UPDATE_REPLACE &&
+                mode != PA_UPDATE_SET)
+                goto fail;
+
+            if (mode == PA_UPDATE_SET) {
+#ifdef HAVE_DBUS
+                struct dbus_entry *de;
+                void *state = NULL;
+
+                PA_HASHMAP_FOREACH(de, u->dbus_entries, state) {
+                    send_entry_removed_signal(de);
+                    pa_hashmap_remove_and_free(u->dbus_entries, de->entry_name);
+                }
+#endif
+                pa_database_clear(u->database);
+            }
+
+            while (!pa_tagstruct_eof(t)) {
+                const char *name, *device;
+                bool muted;
+                struct entry *entry;
+#ifdef HAVE_DBUS
+                struct entry *old;
+#endif
+
+                entry = entry_new();
+
+                if (pa_tagstruct_gets(t, &name) < 0 ||
+                    pa_tagstruct_get_channel_map(t, &entry->channel_map) ||
+                    pa_tagstruct_get_cvolume(t, &entry->volume) < 0 ||
+                    pa_tagstruct_gets(t, &device) < 0 ||
+                    pa_tagstruct_get_boolean(t, &muted) < 0) {
+                    entry_free(entry);
+                    goto fail;
+                }
+
+                if (!name || !*name) {
+                    entry_free(entry);
+                    goto fail;
+                }
+
+                entry->volume_valid = entry->volume.channels > 0;
+
+                if (entry->volume_valid)
+                    if (!pa_cvolume_compatible_with_channel_map(&entry->volume, &entry->channel_map)) {
+                        entry_free(entry);
+                        goto fail;
+                    }
+
+                entry->muted = muted;
+                entry->muted_valid = true;
+
+                entry->device = pa_xstrdup(device);
+                entry->device_valid = device && !!entry->device[0];
+
+                if (entry->device_valid && !pa_namereg_is_valid_name(entry->device)) {
+                    entry_free(entry);
+                    goto fail;
+                }
+
+#ifdef HAVE_DBUS
+                old = entry_read(u, name);
+#endif
+
+                pa_log_debug("Client %s changes entry %s.",
+                             pa_strnull(pa_proplist_gets(pa_native_connection_get_client(c)->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)),
+                             name);
+
+                if (entry_write(u, name, entry, mode == PA_UPDATE_REPLACE)) {
+#ifdef HAVE_DBUS
+                    struct dbus_entry *de;
+
+                    if (old) {
+                        pa_assert_se((de = pa_hashmap_get(u->dbus_entries, name)));
+
+                        if ((old->device_valid != entry->device_valid)
+                            || (entry->device_valid && !pa_streq(entry->device, old->device)))
+                            send_device_updated_signal(de, entry);
+
+                        if ((old->volume_valid != entry->volume_valid)
+                            || (entry->volume_valid && (!pa_cvolume_equal(&entry->volume, &old->volume)
+                                                       || !pa_channel_map_equal(&entry->channel_map, &old->channel_map))))
+                            send_volume_updated_signal(de, entry);
+
+                        if (!old->muted_valid || (entry->muted != old->muted))
+                            send_mute_updated_signal(de, entry);
+
+                    } else {
+                        de = dbus_entry_new(u, name);
+                        pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) == 0);
+                        send_new_entry_signal(de);
+                    }
+#endif
+
+                    if (apply_immediately)
+                        entry_apply(u, name, entry);
+                }
+
+#ifdef HAVE_DBUS
+                if (old)
+                    entry_free(old);
+#endif
+                entry_free(entry);
+            }
+
+            trigger_save(u);
+
+            break;
+        }
+
+        case SUBCOMMAND_DELETE:
+
+            while (!pa_tagstruct_eof(t)) {
+                const char *name;
+                pa_datum key;
+#ifdef HAVE_DBUS
+                struct dbus_entry *de;
+#endif
+
+                if (pa_tagstruct_gets(t, &name) < 0)
+                    goto fail;
+
+#ifdef HAVE_DBUS
+                if ((de = pa_hashmap_get(u->dbus_entries, name))) {
+                    send_entry_removed_signal(de);
+                    pa_hashmap_remove_and_free(u->dbus_entries, name);
+                }
+#endif
+
+                key.data = (char*) name;
+                key.size = strlen(name);
+
+                pa_database_unset(u->database, &key);
+            }
+
+            trigger_save(u);
+
+            break;
+
+        case SUBCOMMAND_SUBSCRIBE: {
+
+            bool enabled;
+
+            if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
+                !pa_tagstruct_eof(t))
+                goto fail;
+
+            if (enabled)
+                pa_idxset_put(u->subscribed, c, NULL);
+            else
+                pa_idxset_remove_by_data(u->subscribed, c, NULL);
+
+            break;
+        }
+
+        default:
+            goto fail;
+    }
+
+    pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
+    return 0;
+
+fail:
+
+    if (reply)
+        pa_tagstruct_free(reply);
+
+    return -1;
+}
+
+static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
+    pa_assert(p);
+    pa_assert(c);
+    pa_assert(u);
+
+    pa_idxset_remove_by_data(u->subscribed, c, NULL);
+    return PA_HOOK_OK;
+}
+
+static void clean_up_db(struct userdata *u) {
+    struct clean_up_item {
+        PA_LLIST_FIELDS(struct clean_up_item);
+        char *entry_name;
+        struct entry *entry;
+    };
+
+    PA_LLIST_HEAD(struct clean_up_item, to_be_removed);
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+    PA_LLIST_HEAD(struct clean_up_item, to_be_converted);
+#endif
+    bool done = false;
+    pa_datum key;
+    struct clean_up_item *item = NULL;
+    struct clean_up_item *next = NULL;
+
+    pa_assert(u);
+
+    /* It would be convenient to remove or replace the entries in the database
+     * in the same loop that iterates through the database, but modifying the
+     * database is not supported while iterating through it. That's why we
+     * collect the entries that need to be removed or replaced to these
+     * lists. */
+    PA_LLIST_HEAD_INIT(struct clean_up_item, to_be_removed);
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+    PA_LLIST_HEAD_INIT(struct clean_up_item, to_be_converted);
+#endif
+
+    done = !pa_database_first(u->database, &key, NULL);
+    while (!done) {
+        pa_datum next_key;
+        char *entry_name = NULL;
+        struct entry *e = NULL;
+
+        entry_name = pa_xstrndup(key.data, key.size);
+
+        /* Use entry_read() to check whether this entry is valid. */
+        if (!(e = entry_read(u, entry_name))) {
+            item = pa_xnew0(struct clean_up_item, 1);
+            PA_LLIST_INIT(struct clean_up_item, item);
+            item->entry_name = entry_name;
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+            /* entry_read() failed, but what about legacy_entry_read()? */
+            if (!(e = legacy_entry_read(u, entry_name)))
+                /* Not a legacy entry either, let's remove this. */
+                PA_LLIST_PREPEND(struct clean_up_item, to_be_removed, item);
+            else {
+                /* Yay, it's valid after all! Now let's convert the entry to the current format. */
+                item->entry = e;
+                PA_LLIST_PREPEND(struct clean_up_item, to_be_converted, item);
+            }
+#else
+            /* Invalid entry, let's remove this. */
+            PA_LLIST_PREPEND(struct clean_up_item, to_be_removed, item);
+#endif
+        } else {
+            pa_xfree(entry_name);
+            entry_free(e);
+        }
+
+        done = !pa_database_next(u->database, &key, &next_key, NULL);
+        pa_datum_free(&key);
+        key = next_key;
+    }
+
+    PA_LLIST_FOREACH_SAFE(item, next, to_be_removed) {
+        key.data = item->entry_name;
+        key.size = strlen(item->entry_name);
+
+        pa_log_debug("Removing an invalid entry: %s", item->entry_name);
+
+        pa_assert_se(pa_database_unset(u->database, &key) >= 0);
+        trigger_save(u);
+
+        PA_LLIST_REMOVE(struct clean_up_item, to_be_removed, item);
+        pa_xfree(item->entry_name);
+        pa_xfree(item);
+    }
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+    PA_LLIST_FOREACH_SAFE(item, next, to_be_converted) {
+        pa_log_debug("Upgrading a legacy entry to the current format: %s", item->entry_name);
+
+        pa_assert_se(entry_write(u, item->entry_name, item->entry, true));
+        trigger_save(u);
+
+        PA_LLIST_REMOVE(struct clean_up_item, to_be_converted, item);
+        pa_xfree(item->entry_name);
+        entry_free(item->entry);
+        pa_xfree(item);
+    }
+#endif
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    char *fname;
+    pa_sink_input *si;
+    pa_source_output *so;
+    uint32_t idx;
+    bool restore_device = true, restore_volume = true, restore_muted = true, on_hotplug = true, on_rescue = true;
+#ifdef HAVE_DBUS
+    pa_datum key;
+    bool done;
+#endif
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
+        pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 ||
+        pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 ||
+        pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
+        pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
+        pa_log("restore_device=, restore_volume=, restore_muted=, on_hotplug= and on_rescue= expect boolean arguments");
+        goto fail;
+    }
+
+    if (!restore_muted && !restore_volume && !restore_device)
+        pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring device enabled!");
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->restore_device = restore_device;
+    u->restore_volume = restore_volume;
+    u->restore_muted = restore_muted;
+    u->on_hotplug = on_hotplug;
+    u->on_rescue = on_rescue;
+    u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->protocol = pa_native_protocol_get(m->core);
+    pa_native_protocol_install_ext(u->protocol, m, extension_cb);
+
+    pa_module_hook_connect(m, &pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
+
+    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
+
+    if (restore_device) {
+        /* A little bit earlier than module-intended-roles ... */
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u);
+    }
+
+    if (restore_device && on_hotplug) {
+        /* A little bit earlier than module-intended-roles ... */
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_hook_callback, u);
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) source_put_hook_callback, u);
+    }
+
+    if (restore_device && on_rescue) {
+        /* A little bit earlier than module-intended-roles, module-rescue-streams, ... */
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_unlink_hook_callback, u);
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_unlink_hook_callback, u);
+    }
+
+    if (restore_volume || restore_muted) {
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
+        pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_fixate_hook_callback, u);
+    }
+
+    if (!(fname = pa_state_path("stream-volumes", true)))
+        goto fail;
+
+    if (!(u->database = pa_database_open(fname, true))) {
+        pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
+        pa_xfree(fname);
+        goto fail;
+    }
+
+    pa_log_info("Successfully opened database file '%s'.", fname);
+    pa_xfree(fname);
+
+    clean_up_db(u);
+
+    if (fill_db(u, pa_modargs_get_value(ma, "fallback_table", NULL)) < 0)
+        goto fail;
+
+#ifdef HAVE_DBUS
+    u->dbus_protocol = pa_dbus_protocol_get(u->core);
+    u->dbus_entries = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) dbus_entry_free);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(u->dbus_protocol, OBJECT_PATH, &stream_restore_interface_info, u) >= 0);
+    pa_assert_se(pa_dbus_protocol_register_extension(u->dbus_protocol, INTERFACE_STREAM_RESTORE) >= 0);
+
+    /* Create the initial dbus entries. */
+    done = !pa_database_first(u->database, &key, NULL);
+    while (!done) {
+        pa_datum next_key;
+        char *name;
+        struct dbus_entry *de;
+
+        name = pa_xstrndup(key.data, key.size);
+        de = dbus_entry_new(u, name);
+        pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) == 0);
+        pa_xfree(name);
+
+        done = !pa_database_next(u->database, &key, &next_key, NULL);
+        pa_datum_free(&key);
+        key = next_key;
+    }
+#endif
+
+    PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
+        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
+
+    PA_IDXSET_FOREACH(so, m->core->source_outputs, idx)
+        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+#ifdef HAVE_DBUS
+    if (u->dbus_protocol) {
+        pa_assert(u->dbus_entries);
+
+        pa_assert_se(pa_dbus_protocol_unregister_extension(u->dbus_protocol, INTERFACE_STREAM_RESTORE) >= 0);
+        pa_assert_se(pa_dbus_protocol_remove_interface(u->dbus_protocol, OBJECT_PATH, stream_restore_interface_info.name) >= 0);
+
+        pa_hashmap_free(u->dbus_entries);
+
+        pa_dbus_protocol_unref(u->dbus_protocol);
+    }
+#endif
+
+    if (u->subscription)
+        pa_subscription_free(u->subscription);
+
+    if (u->save_time_event)
+        u->core->mainloop->time_free(u->save_time_event);
+
+    if (u->database)
+        pa_database_close(u->database);
+
+    if (u->protocol) {
+        pa_native_protocol_remove_ext(u->protocol, m);
+        pa_native_protocol_unref(u->protocol);
+    }
+
+    if (u->subscribed)
+        pa_idxset_free(u->subscribed, NULL);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
new file mode 100644 (file)
index 0000000..90d356f
--- /dev/null
@@ -0,0 +1,504 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+
+#include "module-suspend-on-idle-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE("timeout=<timeout>");
+
+static const char* const valid_modargs[] = {
+    "timeout",
+    NULL,
+};
+
+struct userdata {
+    pa_core *core;
+    pa_usec_t timeout;
+    pa_hashmap *device_infos;
+};
+
+struct device_info {
+    struct userdata *userdata;
+    pa_sink *sink;
+    pa_source *source;
+    pa_usec_t last_use;
+    pa_time_event *time_event;
+    pa_usec_t timeout;
+};
+
+static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
+    struct device_info *d = userdata;
+
+    pa_assert(d);
+
+    d->userdata->core->mainloop->time_restart(d->time_event, NULL);
+
+    if (d->sink && pa_sink_check_suspend(d->sink, NULL, NULL) <= 0 && !(d->sink->suspend_cause & PA_SUSPEND_IDLE)) {
+        pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);
+        pa_sink_suspend(d->sink, true, PA_SUSPEND_IDLE);
+        pa_core_maybe_vacuum(d->userdata->core);
+    }
+
+    if (d->source && pa_source_check_suspend(d->source, NULL) <= 0 && !(d->source->suspend_cause & PA_SUSPEND_IDLE)) {
+        pa_log_info("Source %s idle for too long, suspending ...", d->source->name);
+        pa_source_suspend(d->source, true, PA_SUSPEND_IDLE);
+        pa_core_maybe_vacuum(d->userdata->core);
+    }
+}
+
+static void restart(struct device_info *d) {
+    pa_usec_t now;
+
+    pa_assert(d);
+    pa_assert(d->sink || d->source);
+
+    d->last_use = now = pa_rtclock_now();
+    pa_core_rttime_restart(d->userdata->core, d->time_event, now + d->timeout);
+
+    if (d->sink)
+        pa_log_debug("Sink %s becomes idle, timeout in %" PRIu64 " seconds.", d->sink->name, d->timeout / PA_USEC_PER_SEC);
+    if (d->source)
+        pa_log_debug("Source %s becomes idle, timeout in %" PRIu64 " seconds.", d->source->name, d->timeout / PA_USEC_PER_SEC);
+}
+
+static void resume(struct device_info *d) {
+    pa_assert(d);
+
+    d->userdata->core->mainloop->time_restart(d->time_event, NULL);
+
+    if (d->sink) {
+        pa_log_debug("Sink %s becomes busy, resuming.", d->sink->name);
+        pa_sink_suspend(d->sink, false, PA_SUSPEND_IDLE);
+    }
+
+    if (d->source) {
+        pa_log_debug("Source %s becomes busy, resuming.", d->source->name);
+        pa_source_suspend(d->source, false, PA_SUSPEND_IDLE);
+    }
+}
+
+static pa_hook_result_t sink_input_fixate_hook_cb(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_assert(data);
+    pa_assert(u);
+
+    /* We need to resume the audio device here even for
+     * PA_SINK_INPUT_START_CORKED, since we need the device parameters
+     * to be fully available while the stream is set up. In that case,
+     * make sure we close the sink again after the timeout interval. */
+
+    if ((d = pa_hashmap_get(u->device_infos, data->sink))) {
+        resume(d);
+        if (pa_sink_check_suspend(d->sink, NULL, NULL) <= 0)
+            restart(d);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_assert(data);
+    pa_assert(u);
+
+    if (data->source->monitor_of)
+        d = pa_hashmap_get(u->device_infos, data->source->monitor_of);
+    else
+        d = pa_hashmap_get(u->device_infos, data->source);
+
+    if (d) {
+        resume(d);
+        if (d->source) {
+            if (pa_source_check_suspend(d->source, NULL) <= 0)
+                restart(d);
+        } else {
+            /* The source output is connected to a monitor source. */
+            pa_assert(d->sink);
+            if (pa_sink_check_suspend(d->sink, NULL, NULL) <= 0)
+                restart(d);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
+    pa_assert(c);
+    pa_sink_input_assert_ref(s);
+    pa_assert(u);
+
+    if (!s->sink)
+        return PA_HOOK_OK;
+
+    if (pa_sink_check_suspend(s->sink, s, NULL) <= 0) {
+        struct device_info *d;
+        if ((d = pa_hashmap_get(u->device_infos, s->sink)))
+            restart(d);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+    struct device_info *d = NULL;
+
+    pa_assert(c);
+    pa_source_output_assert_ref(s);
+    pa_assert(u);
+
+    if (!s->source)
+        return PA_HOOK_OK;
+
+    if (s->source->monitor_of) {
+        if (pa_sink_check_suspend(s->source->monitor_of, NULL, s) <= 0)
+            d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
+    } else {
+        if (pa_source_check_suspend(s->source, s) <= 0)
+            d = pa_hashmap_get(u->device_infos, s->source);
+    }
+
+    if (d)
+        restart(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_move_start_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_sink_input_assert_ref(s);
+    pa_assert(u);
+
+    if (pa_sink_check_suspend(s->sink, s, NULL) <= 0)
+        if ((d = pa_hashmap_get(u->device_infos, s->sink)))
+            restart(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_move_finish_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
+    struct device_info *d;
+    pa_sink_input_state_t state;
+
+    pa_assert(c);
+    pa_sink_input_assert_ref(s);
+    pa_assert(u);
+
+    state = pa_sink_input_get_state(s);
+    if (state != PA_SINK_INPUT_RUNNING && state != PA_SINK_INPUT_DRAINED)
+        return PA_HOOK_OK;
+
+    if ((d = pa_hashmap_get(u->device_infos, s->sink)))
+        resume(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_move_start_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+    struct device_info *d = NULL;
+
+    pa_assert(c);
+    pa_source_output_assert_ref(s);
+    pa_assert(u);
+
+    if (s->source->monitor_of) {
+        if (pa_sink_check_suspend(s->source->monitor_of, NULL, s) <= 0)
+            d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
+    } else {
+        if (pa_source_check_suspend(s->source, s) <= 0)
+            d = pa_hashmap_get(u->device_infos, s->source);
+    }
+
+    if (d)
+        restart(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_source_output_assert_ref(s);
+    pa_assert(u);
+
+    if (pa_source_output_get_state(s) != PA_SOURCE_OUTPUT_RUNNING)
+        return PA_HOOK_OK;
+
+    if (s->source->monitor_of)
+        d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
+    else
+        d = pa_hashmap_get(u->device_infos, s->source);
+
+    if (d)
+        resume(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
+    struct device_info *d;
+    pa_sink_input_state_t state;
+
+    pa_assert(c);
+    pa_sink_input_assert_ref(s);
+    pa_assert(u);
+
+    state = pa_sink_input_get_state(s);
+    if ((state == PA_SINK_INPUT_RUNNING || state == PA_SINK_INPUT_DRAINED) && s->sink)
+        if ((d = pa_hashmap_get(u->device_infos, s->sink)))
+            resume(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+    pa_assert(c);
+    pa_source_output_assert_ref(s);
+    pa_assert(u);
+
+    if (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_RUNNING && s->source) {
+        struct device_info *d;
+
+        if (s->source->monitor_of)
+            d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
+        else
+            d = pa_hashmap_get(u->device_infos, s->source);
+
+        if (d)
+            resume(d);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    struct device_info *d;
+    pa_source *source;
+    pa_sink *sink;
+    const char *timeout_str;
+    int32_t timeout;
+    bool timeout_valid;
+
+    pa_assert(c);
+    pa_object_assert_ref(o);
+    pa_assert(u);
+
+    source = pa_source_isinstance(o) ? PA_SOURCE(o) : NULL;
+    sink = pa_sink_isinstance(o) ? PA_SINK(o) : NULL;
+
+    /* Never suspend monitors */
+    if (source && source->monitor_of)
+        return PA_HOOK_OK;
+
+    pa_assert(source || sink);
+
+    timeout_str = pa_proplist_gets(sink ? sink->proplist : source->proplist, "module-suspend-on-idle.timeout");
+    if (timeout_str && pa_atoi(timeout_str, &timeout) >= 0)
+        timeout_valid = true;
+    else
+        timeout_valid = false;
+
+    if (timeout_valid && timeout < 0)
+        return PA_HOOK_OK;
+
+    d = pa_xnew(struct device_info, 1);
+    d->userdata = u;
+    d->source = source ? pa_source_ref(source) : NULL;
+    d->sink = sink ? pa_sink_ref(sink) : NULL;
+    d->time_event = pa_core_rttime_new(c, PA_USEC_INVALID, timeout_cb, d);
+
+    if (timeout_valid)
+        d->timeout = timeout * PA_USEC_PER_SEC;
+    else
+        d->timeout = d->userdata->timeout;
+
+    pa_hashmap_put(u->device_infos, o, d);
+
+    if ((d->sink && pa_sink_check_suspend(d->sink, NULL, NULL) <= 0) ||
+        (d->source && pa_source_check_suspend(d->source, NULL) <= 0))
+        restart(d);
+
+    return PA_HOOK_OK;
+}
+
+static void device_info_free(struct device_info *d) {
+    pa_assert(d);
+
+    if (d->source)
+        pa_source_unref(d->source);
+    if (d->sink)
+        pa_sink_unref(d->sink);
+
+    d->userdata->core->mainloop->time_free(d->time_event);
+
+    pa_xfree(d);
+}
+
+static pa_hook_result_t device_unlink_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    pa_assert(c);
+    pa_object_assert_ref(o);
+    pa_assert(u);
+
+    pa_hashmap_remove_and_free(u->device_infos, o);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_object_assert_ref(o);
+    pa_assert(u);
+
+    if (!(d = pa_hashmap_get(u->device_infos, o)))
+        return PA_HOOK_OK;
+
+    if (pa_sink_isinstance(o)) {
+        pa_sink *s = PA_SINK(o);
+        pa_sink_state_t state = pa_sink_get_state(s);
+
+        if (pa_sink_check_suspend(s, NULL, NULL) <= 0)
+            if (PA_SINK_IS_OPENED(state))
+                restart(d);
+
+    } else if (pa_source_isinstance(o)) {
+        pa_source *s = PA_SOURCE(o);
+        pa_source_state_t state = pa_source_get_state(s);
+
+        if (pa_source_check_suspend(s, NULL) <= 0)
+            if (PA_SOURCE_IS_OPENED(state))
+                restart(d);
+    }
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    uint32_t timeout = 5;
+    uint32_t idx;
+    pa_sink *sink;
+    pa_source *source;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "timeout", &timeout) < 0) {
+        pa_log("Failed to parse timeout value.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->timeout = timeout * PA_USEC_PER_SEC;
+    u->device_infos = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) device_info_free);
+
+    PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
+        device_new_hook_cb(m->core, PA_OBJECT(sink), u);
+
+    PA_IDXSET_FOREACH(source, m->core->sources, idx)
+        device_new_hook_cb(m->core, PA_OBJECT(source), u);
+
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u);
+
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_fixate_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_fixate_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_unlink_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_unlink_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_start_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_start_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_finish_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_finish_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_state_changed_hook_cb, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_state_changed_hook_cb, u);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    struct device_info *d;
+    void *state;
+
+    pa_assert(m);
+
+    if (!m->userdata)
+        return;
+
+    u = m->userdata;
+
+    PA_HASHMAP_FOREACH(d, u->device_infos, state) {
+        if (d->sink && pa_sink_get_state(d->sink) == PA_SINK_SUSPENDED) {
+            pa_log_debug("Resuming sink %s on module unload.", d->sink->name);
+            pa_sink_suspend(d->sink, false, PA_SUSPEND_IDLE);
+        }
+
+        if (d->source && pa_source_get_state(d->source) == PA_SOURCE_SUSPENDED) {
+            pa_log_debug("Resuming source %s on module unload.", d->source->name);
+            pa_source_suspend(d->source, false, PA_SUSPEND_IDLE);
+        }
+    }
+
+    pa_hashmap_free(u->device_infos);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-switch-on-connect.c b/src/modules/module-switch-on-connect.c
new file mode 100644 (file)
index 0000000..640024e
--- /dev/null
@@ -0,0 +1,226 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2009 Canonical Ltd
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/source.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+
+#include "module-switch-on-connect-symdef.h"
+
+PA_MODULE_AUTHOR("Michael Terry");
+PA_MODULE_DESCRIPTION("When a sink/source is added, switch to it or conditionally switch to it");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+        "only_from_unavailable=<boolean, only switch from unavailable ports> "
+);
+
+static const char* const valid_modargs[] = {
+    "only_from_unavailable",
+    NULL,
+};
+
+struct userdata {
+    bool only_from_unavailable;
+};
+
+static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+    pa_sink_input *i;
+    uint32_t idx;
+    pa_sink *old_default_sink;
+    const char *s;
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(userdata);
+
+    /* Don't want to run during startup or shutdown */
+    if (c->state != PA_CORE_RUNNING)
+        return PA_HOOK_OK;
+
+    /* Don't switch to any internal devices */
+    if ((s = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_BUS))) {
+        if (pa_streq(s, "pci"))
+            return PA_HOOK_OK;
+        else if (pa_streq(s, "isa"))
+            return PA_HOOK_OK;
+    }
+
+    /* No default sink, nothing to move away, just set the new default */
+    if (!c->default_sink) {
+        pa_core_set_configured_default_sink(c, sink->name);
+        return PA_HOOK_OK;
+    }
+
+    if (c->default_sink == sink)
+        return PA_HOOK_OK;
+
+    if (u->only_from_unavailable)
+        if (!c->default_sink->active_port || c->default_sink->active_port->available != PA_AVAILABLE_NO)
+            return PA_HOOK_OK;
+
+    old_default_sink = c->default_sink;
+
+    /* Actually do the switch to the new sink */
+    pa_core_set_configured_default_sink(c, sink->name);
+
+    /* Now move all old inputs over */
+    if (pa_idxset_size(old_default_sink->inputs) <= 0) {
+        pa_log_debug("No sink inputs to move away.");
+        return PA_HOOK_OK;
+    }
+
+    PA_IDXSET_FOREACH(i, old_default_sink->inputs, idx) {
+        if (i->save_sink || !PA_SINK_INPUT_IS_LINKED(i->state))
+            continue;
+
+        if (pa_sink_input_move_to(i, sink, false) < 0)
+            pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
+                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), sink->name);
+        else
+            pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
+                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), sink->name);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, void* userdata) {
+    pa_source_output *o;
+    uint32_t idx;
+    pa_source *old_default_source;
+    const char *s;
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(source);
+    pa_assert(userdata);
+
+    /* Don't want to run during startup or shutdown */
+    if (c->state != PA_CORE_RUNNING)
+        return PA_HOOK_OK;
+
+    /* Don't switch to a monitoring source */
+    if (source->monitor_of)
+        return PA_HOOK_OK;
+
+    /* Don't switch to any internal devices */
+    if ((s = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_BUS))) {
+        if (pa_streq(s, "pci"))
+            return PA_HOOK_OK;
+        else if (pa_streq(s, "isa"))
+            return PA_HOOK_OK;
+    }
+
+    /* No default source, nothing to move away, just set the new default */
+    if (!c->default_source) {
+        pa_core_set_configured_default_source(c, source->name);
+        return PA_HOOK_OK;
+    }
+
+    if (c->default_source == source)
+        return PA_HOOK_OK;
+
+    if (u->only_from_unavailable)
+        if (!c->default_source->active_port || c->default_source->active_port->available != PA_AVAILABLE_NO)
+            return PA_HOOK_OK;
+
+    old_default_source = c->default_source;
+
+    /* Actually do the switch to the new source */
+    pa_core_set_configured_default_source(c, source->name);
+
+    /* Now move all old outputs over */
+    if (pa_idxset_size(old_default_source->outputs) <= 0) {
+        pa_log_debug("No source outputs to move away.");
+        return PA_HOOK_OK;
+    }
+
+    PA_IDXSET_FOREACH(o, old_default_source->outputs, idx) {
+        if (o->save_source || !PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+            continue;
+
+        if (pa_source_output_move_to(o, source, false) < 0)
+            pa_log_info("Failed to move source output %u \"%s\" to %s.", o->index,
+                        pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), source->name);
+        else
+            pa_log_info("Successfully moved source output %u \"%s\" to %s.", o->index,
+                        pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), source->name);
+    }
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        return -1;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+
+    /* A little bit later than module-rescue-streams... */
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+30, (pa_hook_cb_t) sink_put_hook_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+20, (pa_hook_cb_t) source_put_hook_callback, u);
+
+    if (pa_modargs_get_value_boolean(ma, "only_from_unavailable", &u->only_from_unavailable) < 0) {
+       pa_log("Failed to get a boolean value for only_from_unavailable.");
+       goto fail;
+    }
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c
new file mode 100644 (file)
index 0000000..b9a0f3b
--- /dev/null
@@ -0,0 +1,536 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2011 Canonical Ltd
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/device-port.h>
+#include <pulsecore/hashmap.h>
+
+#include "module-switch-on-port-available-symdef.h"
+
+struct card_info {
+    struct userdata *userdata;
+    pa_card *card;
+
+    /* We need to cache the active profile, because we want to compare the old
+     * and new profiles in the PROFILE_CHANGED hook. Without this we'd only
+     * have access to the new profile. */
+    pa_card_profile *active_profile;
+};
+
+struct userdata {
+    pa_hashmap *card_infos; /* pa_card -> struct card_info */
+};
+
+static void card_info_new(struct userdata *u, pa_card *card) {
+    struct card_info *info;
+
+    info = pa_xnew0(struct card_info, 1);
+    info->userdata = u;
+    info->card = card;
+    info->active_profile = card->active_profile;
+
+    pa_hashmap_put(u->card_infos, card, info);
+}
+
+static void card_info_free(struct card_info *info) {
+    pa_hashmap_remove(info->userdata->card_infos, info->card);
+    pa_xfree(info);
+}
+
+static bool profile_good_for_output(pa_card_profile *profile, pa_device_port *port) {
+    pa_card *card;
+    pa_sink *sink;
+    uint32_t idx;
+
+    pa_assert(profile);
+
+    card = profile->card;
+
+    if (!pa_safe_streq(card->active_profile->input_name, profile->input_name))
+        return false;
+
+    if (card->active_profile->n_sources != profile->n_sources)
+        return false;
+
+    if (card->active_profile->max_source_channels != profile->max_source_channels)
+        return false;
+
+    if (port == card->preferred_output_port)
+        return true;
+
+    PA_IDXSET_FOREACH(sink, card->sinks, idx) {
+        if (!sink->active_port)
+            continue;
+
+        if ((sink->active_port->available != PA_AVAILABLE_NO) && (sink->active_port->priority >= port->priority))
+            return false;
+    }
+
+    return true;
+}
+
+static bool profile_good_for_input(pa_card_profile *profile, pa_device_port *port) {
+    pa_card *card;
+    pa_source *source;
+    uint32_t idx;
+
+    pa_assert(profile);
+
+    card = profile->card;
+
+    if (!pa_safe_streq(card->active_profile->output_name, profile->output_name))
+        return false;
+
+    if (card->active_profile->n_sinks != profile->n_sinks)
+        return false;
+
+    if (card->active_profile->max_sink_channels != profile->max_sink_channels)
+        return false;
+
+    if (port == card->preferred_input_port)
+        return true;
+
+    PA_IDXSET_FOREACH(source, card->sources, idx) {
+        if (!source->active_port)
+            continue;
+
+        if ((source->active_port->available != PA_AVAILABLE_NO) && (source->active_port->priority >= port->priority))
+            return false;
+    }
+
+    return true;
+}
+
+static int try_to_switch_profile(pa_device_port *port) {
+    pa_card_profile *best_profile = NULL, *profile;
+    void *state;
+    unsigned best_prio = 0;
+
+    pa_log_debug("Finding best profile for port %s, preferred = %s",
+                 port->name, pa_strnull(port->preferred_profile));
+
+    PA_HASHMAP_FOREACH(profile, port->profiles, state) {
+        bool good = false;
+        const char *name;
+        unsigned prio = profile->priority;
+
+        /* We make a best effort to keep other direction unchanged */
+        switch (port->direction) {
+            case PA_DIRECTION_OUTPUT:
+                name = profile->output_name;
+                good = profile_good_for_output(profile, port);
+                break;
+
+            case PA_DIRECTION_INPUT:
+                name = profile->input_name;
+                good = profile_good_for_input(profile, port);
+                break;
+        }
+
+        if (!good)
+            continue;
+
+        /* Give a high bonus in case this is the preferred profile */
+        if (port->preferred_profile && pa_streq(name ? name : profile->name, port->preferred_profile))
+            prio += 1000000;
+
+        if (best_profile && best_prio >= prio)
+            continue;
+
+        best_profile = profile;
+        best_prio = prio;
+    }
+
+    if (!best_profile) {
+        pa_log_debug("No suitable profile found");
+        return -1;
+    }
+
+    if (pa_card_set_profile(port->card, best_profile, false) != 0) {
+        pa_log_debug("Could not set profile %s", best_profile->name);
+        return -1;
+    }
+
+    return 0;
+}
+
+struct port_pointers {
+    pa_device_port *port;
+    pa_sink *sink;
+    pa_source *source;
+    bool is_possible_profile_active;
+    bool is_preferred_profile_active;
+    bool is_port_active;
+};
+
+static const char* profile_name_for_dir(pa_card_profile *cp, pa_direction_t dir) {
+    if (dir == PA_DIRECTION_OUTPUT && cp->output_name)
+        return cp->output_name;
+    if (dir == PA_DIRECTION_INPUT && cp->input_name)
+        return cp->input_name;
+    return cp->name;
+}
+
+static struct port_pointers find_port_pointers(pa_device_port *port) {
+    struct port_pointers pp = { .port = port };
+    uint32_t state;
+    pa_card *card;
+
+    pa_assert(port);
+    pa_assert_se(card = port->card);
+
+    switch (port->direction) {
+        case PA_DIRECTION_OUTPUT:
+            PA_IDXSET_FOREACH(pp.sink, card->sinks, state)
+                if (port == pa_hashmap_get(pp.sink->ports, port->name))
+                    break;
+            break;
+
+        case PA_DIRECTION_INPUT:
+            PA_IDXSET_FOREACH(pp.source, card->sources, state)
+                if (port == pa_hashmap_get(pp.source->ports, port->name))
+                    break;
+            break;
+    }
+
+    pp.is_possible_profile_active =
+        card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name);
+    pp.is_preferred_profile_active = pp.is_possible_profile_active && (!port->preferred_profile ||
+        pa_safe_streq(port->preferred_profile, profile_name_for_dir(card->active_profile, port->direction)));
+    pp.is_port_active = (pp.sink && pp.sink->active_port == port) || (pp.source && pp.source->active_port == port);
+
+    return pp;
+}
+
+/* Switches to a port, switching profiles if necessary or preferred */
+static bool switch_to_port(pa_device_port *port) {
+    struct port_pointers pp = find_port_pointers(port);
+
+    if (pp.is_port_active)
+        return true; /* Already selected */
+
+    pa_log_debug("Trying to switch to port %s", port->name);
+    if (!pp.is_preferred_profile_active) {
+        if (try_to_switch_profile(port) < 0) {
+            if (!pp.is_possible_profile_active)
+                return false;
+        }
+        else
+            /* Now that profile has changed, our sink and source pointers must be updated */
+            pp = find_port_pointers(port);
+    }
+
+    if (pp.source)
+        pa_source_set_port(pp.source, port->name, false);
+    if (pp.sink)
+        pa_sink_set_port(pp.sink, port->name, false);
+    return true;
+}
+
+/* Switches away from a port, switching profiles if necessary or preferred */
+static bool switch_from_port(pa_device_port *port) {
+    struct port_pointers pp = find_port_pointers(port);
+    pa_device_port *p, *best_port = NULL;
+    void *state;
+
+    if (!pp.is_port_active)
+        return true; /* Already deselected */
+
+    /* Try to find a good enough port to switch to */
+    PA_HASHMAP_FOREACH(p, port->card->ports, state)
+        if (p->direction == port->direction && p != port && p->available != PA_AVAILABLE_NO &&
+           (!best_port || best_port->priority < p->priority))
+           best_port = p;
+
+    pa_log_debug("Trying to switch away from port %s, found %s", port->name, best_port ? best_port->name : "no better option");
+
+    if (best_port)
+        return switch_to_port(best_port);
+
+    return false;
+}
+
+
+static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port *port, void* userdata) {
+    pa_assert(port);
+
+    if (!port->card) {
+        pa_log_warn("Port %s does not have a card", port->name);
+        return PA_HOOK_OK;
+    }
+
+    if (pa_idxset_size(port->card->sinks) == 0 && pa_idxset_size(port->card->sources) == 0)
+        /* This card is not initialized yet. We'll handle it in
+           sink_new / source_new callbacks later. */
+        return PA_HOOK_OK;
+
+    switch (port->available) {
+    case PA_AVAILABLE_YES:
+        switch_to_port(port);
+        break;
+    case PA_AVAILABLE_NO:
+        switch_from_port(port);
+        break;
+    default:
+        break;
+    }
+
+    return PA_HOOK_OK;
+}
+
+static void handle_all_unavailable(pa_core *core) {
+    pa_card *card;
+    uint32_t state;
+
+    PA_IDXSET_FOREACH(card, core->cards, state) {
+        pa_device_port *port;
+        void *state2;
+
+        PA_HASHMAP_FOREACH(port, card->ports, state2) {
+            if (port->available == PA_AVAILABLE_NO)
+                port_available_hook_callback(core, port, NULL);
+        }
+    }
+}
+
+static pa_device_port *new_sink_source(pa_hashmap *ports, const char *name) {
+
+    void *state;
+    pa_device_port *i, *p = NULL;
+
+    if (!ports)
+        return NULL;
+    if (name)
+        p = pa_hashmap_get(ports, name);
+    if (!p)
+        PA_HASHMAP_FOREACH(i, ports, state)
+            if (!p || i->priority > p->priority)
+                p = i;
+    if (!p)
+        return NULL;
+    if (p->available != PA_AVAILABLE_NO)
+        return NULL;
+
+    pa_assert_se(p = pa_device_port_find_best(ports));
+    return p;
+}
+
+static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, void *u) {
+
+    pa_device_port *p = new_sink_source(new_data->ports, new_data->active_port);
+
+    if (p) {
+        pa_log_debug("Switching initial port for sink '%s' to '%s'", new_data->name, p->name);
+        pa_sink_new_data_set_port(new_data, p->name);
+    }
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, void *u) {
+
+    pa_device_port *p = new_sink_source(new_data->ports, new_data->active_port);
+
+    if (p) {
+        pa_log_debug("Switching initial port for source '%s' to '%s'", new_data->name, p->name);
+        pa_source_new_data_set_port(new_data, p->name);
+    }
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_put_hook_callback(pa_core *core, pa_card *card, struct userdata *u) {
+    card_info_new(u, card);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_unlink_hook_callback(pa_core *core, pa_card *card, struct userdata *u) {
+    card_info_free(pa_hashmap_get(u->card_infos, card));
+
+    return PA_HOOK_OK;
+}
+
+static void update_preferred_input_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile) {
+    pa_source *source;
+
+    /* If the profile change didn't affect input, it doesn't indicate change in
+     * the user's input port preference. */
+    if (pa_safe_streq(old_profile->input_name, new_profile->input_name))
+        return;
+
+    /* If there are more than one source, we don't know which of those the user
+     * prefers. If there are no sources, then the user doesn't seem to care
+     * about input at all. */
+    if (pa_idxset_size(card->sources) != 1) {
+        pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, NULL);
+        return;
+    }
+
+    /* If the profile change modified the set of sinks, then it's unclear
+     * whether the user wanted to activate some specific input port, or was the
+     * input change only a side effect of activating some output. If the new
+     * profile contains no sinks, though, then we know the user only cares
+     * about input. */
+    if (pa_idxset_size(card->sinks) > 0 && !pa_safe_streq(old_profile->output_name, new_profile->output_name)) {
+        pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, NULL);
+        return;
+    }
+
+    source = pa_idxset_first(card->sources, NULL);
+
+    /* We know the user wanted to activate this source. The user might not have
+     * wanted to activate the port that was selected by default, but if that's
+     * the case, the user will change the port manually, and we'll update the
+     * port preference at that time. If no port change occurs, we can assume
+     * that the user likes the port that is now active. */
+    pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, source->active_port);
+}
+
+static void update_preferred_output_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile) {
+    pa_sink *sink;
+
+    /* If the profile change didn't affect output, it doesn't indicate change in
+     * the user's output port preference. */
+    if (pa_safe_streq(old_profile->output_name, new_profile->output_name))
+        return;
+
+    /* If there are more than one sink, we don't know which of those the user
+     * prefers. If there are no sinks, then the user doesn't seem to care about
+     * output at all. */
+    if (pa_idxset_size(card->sinks) != 1) {
+        pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, NULL);
+        return;
+    }
+
+    /* If the profile change modified the set of sources, then it's unclear
+     * whether the user wanted to activate some specific output port, or was
+     * the output change only a side effect of activating some input. If the
+     * new profile contains no sources, though, then we know the user only
+     * cares about output. */
+    if (pa_idxset_size(card->sources) > 0 && !pa_safe_streq(old_profile->input_name, new_profile->input_name)) {
+        pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, NULL);
+        return;
+    }
+
+    sink = pa_idxset_first(card->sinks, NULL);
+
+    /* We know the user wanted to activate this sink. The user might not have
+     * wanted to activate the port that was selected by default, but if that's
+     * the case, the user will change the port manually, and we'll update the
+     * port preference at that time. If no port change occurs, we can assume
+     * that the user likes the port that is now active. */
+    pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, sink->active_port);
+}
+
+static pa_hook_result_t card_profile_changed_callback(pa_core *core, pa_card *card, struct userdata *u) {
+    struct card_info *info;
+    pa_card_profile *old_profile;
+    pa_card_profile *new_profile;
+
+    info = pa_hashmap_get(u->card_infos, card);
+    old_profile = info->active_profile;
+    new_profile = card->active_profile;
+    info->active_profile = new_profile;
+
+    /* This profile change wasn't initiated by the user, so it doesn't signal
+     * a change in the user's port preferences. */
+    if (!card->save_profile)
+        return PA_HOOK_OK;
+
+    update_preferred_input_port(card, old_profile, new_profile);
+    update_preferred_output_port(card, old_profile, new_profile);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_port_changed_callback(pa_core *core, pa_source *source, void *userdata) {
+    if (!source->save_port)
+        return PA_HOOK_OK;
+
+    pa_card_set_preferred_port(source->card, PA_DIRECTION_INPUT, source->active_port);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_port_changed_callback(pa_core *core, pa_sink *sink, void *userdata) {
+    if (!sink->save_port)
+        return PA_HOOK_OK;
+
+    pa_card_set_preferred_port(sink->card, PA_DIRECTION_OUTPUT, sink->active_port);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_card *card;
+    uint32_t idx;
+
+    pa_assert(m);
+
+    u = m->userdata = pa_xnew0(struct userdata, 1);
+    u->card_infos = pa_hashmap_new(NULL, NULL);
+
+    PA_IDXSET_FOREACH(card, m->core->cards, idx)
+        card_info_new(u, card);
+
+    /* Make sure we are after module-device-restore, so we can overwrite that suggestion if necessary */
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_NEW],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) sink_new_hook_callback, NULL);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_NEW],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) source_new_hook_callback, NULL);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED],
+                           PA_HOOK_LATE, (pa_hook_cb_t) port_available_hook_callback, NULL);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PUT],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) card_put_hook_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_UNLINK],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) card_unlink_hook_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_changed_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) source_port_changed_callback, NULL);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) sink_port_changed_callback, NULL);
+
+    handle_all_unavailable(m->core);
+
+    return 0;
+}
+
+void pa__done(pa_module *module) {
+    struct userdata *u;
+    struct card_info *info;
+
+    pa_assert(module);
+
+    if (!(u = module->userdata))
+        return;
+
+    while ((info = pa_hashmap_last(u->card_infos)))
+        card_info_free(info);
+
+    pa_hashmap_free(u->card_infos);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-systemd-login.c b/src/modules/module-systemd-login.c
new file mode 100644 (file)
index 0000000..d15bee5
--- /dev/null
@@ -0,0 +1,231 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2012 Lennart Poettering
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <systemd/sd-login.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/modargs.h>
+
+#include "module-systemd-login-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Create a client for each login session of this user");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct session {
+    char *id;
+    pa_client *client;
+};
+
+struct userdata {
+    pa_module *module;
+    pa_core *core;
+    pa_hashmap *sessions, *previous_sessions;
+    sd_login_monitor *monitor;
+    pa_io_event *io;
+};
+
+static int add_session(struct userdata *u, const char *id) {
+    struct session *session;
+    pa_client_new_data data;
+
+    session = pa_xnew(struct session, 1);
+    session->id = pa_xstrdup(id);
+
+    pa_client_new_data_init(&data);
+    data.module = u->module;
+    data.driver = __FILE__;
+    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "Login Session %s", id);
+    pa_proplist_sets(data.proplist, "systemd-login.session", id);
+    session->client = pa_client_new(u->core, &data);
+    pa_client_new_data_done(&data);
+
+    if (!session->client) {
+        pa_xfree(session->id);
+        pa_xfree(session);
+        return -1;
+    }
+
+    pa_hashmap_put(u->sessions, session->id, session);
+
+    pa_log_debug("Added new session %s", id);
+    return 0;
+}
+
+static void free_session(struct session *session) {
+    pa_assert(session);
+
+    pa_log_debug("Removing session %s", session->id);
+
+    pa_client_free(session->client);
+    pa_xfree(session->id);
+    pa_xfree(session);
+}
+
+static int get_session_list(struct userdata *u) {
+    int r;
+    char **sessions;
+    pa_hashmap *h;
+    struct session *o;
+
+    pa_assert(u);
+
+    r = sd_uid_get_sessions(getuid(), 0, &sessions);
+    if (r < 0)
+        return -1;
+
+    /* We copy all sessions that still exist from one hashmap to the
+     * other and then flush the remaining ones */
+
+    h = u->previous_sessions;
+    u->previous_sessions = u->sessions;
+    u->sessions = h;
+
+    if (sessions) {
+        char **s;
+
+        /* Note that the sessions array is allocated with libc's
+         * malloc()/free() calls, hence do not use pa_xfree() to free
+         * this here. */
+
+        for (s = sessions; *s; s++) {
+            o = pa_hashmap_remove(u->previous_sessions, *s);
+            if (o)
+                pa_hashmap_put(u->sessions, o->id, o);
+            else
+                add_session(u, *s);
+
+            free(*s);
+        }
+
+        free(sessions);
+    }
+
+    pa_hashmap_remove_all(u->previous_sessions);
+
+    return 0;
+}
+
+static void monitor_cb(
+        pa_mainloop_api*a,
+        pa_io_event* e,
+        int fd,
+        pa_io_event_flags_t events,
+        void *userdata) {
+
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    sd_login_monitor_flush(u->monitor);
+    get_session_list(u);
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u = NULL;
+    pa_modargs *ma;
+    sd_login_monitor *monitor = NULL;
+    int r;
+
+    pa_assert(m);
+
+    /* If we are not actually running logind become a NOP */
+    if (access("/run/systemd/seats/", F_OK) < 0)
+        return 0;
+
+    ma = pa_modargs_new(m->argument, valid_modargs);
+    if (!ma) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    r = sd_login_monitor_new("session", &monitor);
+    if (r < 0) {
+        pa_log("Failed to create session monitor: %s", strerror(-r));
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->sessions = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) free_session);
+    u->previous_sessions = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) free_session);
+    u->monitor = monitor;
+
+    u->io = u->core->mainloop->io_new(u->core->mainloop, sd_login_monitor_get_fd(monitor), PA_IO_EVENT_INPUT, monitor_cb, u);
+
+    if (get_session_list(u) < 0)
+        goto fail;
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    u = m->userdata;
+    if (!u)
+        return;
+
+    if (u->sessions) {
+        pa_hashmap_free(u->sessions);
+        pa_hashmap_free(u->previous_sessions);
+    }
+
+    if (u->io)
+        m->core->mainloop->io_free(u->io);
+
+    if (u->monitor)
+        sd_login_monitor_unref(u->monitor);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-tunnel-sink-new.c b/src/modules/module-tunnel-sink-new.c
new file mode 100644 (file)
index 0000000..7962a5a
--- /dev/null
@@ -0,0 +1,620 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2013 Alexander Couzens
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/context.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+#include <pulse/stream.h>
+#include <pulse/mainloop.h>
+#include <pulse/introspect.h>
+#include <pulse/error.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/poll.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/proplist-util.h>
+
+#include "module-tunnel-sink-new-symdef.h"
+
+PA_MODULE_AUTHOR("Alexander Couzens");
+PA_MODULE_DESCRIPTION("Create a network sink which connects via a stream to a remote PulseAudio server");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "server=<address> "
+        "sink=<name of the remote sink> "
+        "sink_name=<name for the local sink> "
+        "sink_properties=<properties for the local sink> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "channel_map=<channel map> "
+        "cookie=<cookie file path>"
+        );
+
+#define MAX_LATENCY_USEC (200 * PA_USEC_PER_MSEC)
+#define TUNNEL_THREAD_FAILED_MAINLOOP 1
+
+static void stream_state_cb(pa_stream *stream, void *userdata);
+static void stream_changed_buffer_attr_cb(pa_stream *stream, void *userdata);
+static void stream_set_buffer_attr_cb(pa_stream *stream, int success, void *userdata);
+static void context_state_cb(pa_context *c, void *userdata);
+static void sink_update_requested_latency_cb(pa_sink *s);
+
+struct userdata {
+    pa_module *module;
+    pa_sink *sink;
+    pa_thread *thread;
+    pa_thread_mq *thread_mq;
+    pa_mainloop *thread_mainloop;
+    pa_mainloop_api *thread_mainloop_api;
+
+    pa_context *context;
+    pa_stream *stream;
+    pa_rtpoll *rtpoll;
+
+    bool update_stream_bufferattr_after_connect;
+
+    bool connected;
+
+    char *cookie_file;
+    char *remote_server;
+    char *remote_sink_name;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "server",
+    "sink",
+    "format",
+    "channels",
+    "rate",
+    "channel_map",
+    "cookie",
+   /* "reconnect", reconnect if server comes back again - unimplemented */
+    NULL,
+};
+
+static void cork_stream(struct userdata *u, bool cork) {
+    pa_operation *operation;
+
+    pa_assert(u);
+    pa_assert(u->stream);
+
+    if (cork) {
+        /* When the sink becomes suspended (which is the only case where we
+         * cork the stream), we don't want to keep any old data around, because
+         * the old data is most likely unrelated to the audio that will be
+         * played at the time when the sink starts running again. */
+        if ((operation = pa_stream_flush(u->stream, NULL, NULL)))
+            pa_operation_unref(operation);
+    }
+
+    if ((operation = pa_stream_cork(u->stream, cork, NULL, NULL)))
+        pa_operation_unref(operation);
+}
+
+static void reset_bufferattr(pa_buffer_attr *bufferattr) {
+    pa_assert(bufferattr);
+    bufferattr->fragsize = (uint32_t) -1;
+    bufferattr->minreq = (uint32_t) -1;
+    bufferattr->maxlength = (uint32_t) -1;
+    bufferattr->prebuf = (uint32_t) -1;
+    bufferattr->tlength = (uint32_t) -1;
+}
+
+static pa_proplist* tunnel_new_proplist(struct userdata *u) {
+    pa_proplist *proplist = pa_proplist_new();
+    pa_assert(proplist);
+    pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "PulseAudio");
+    pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");
+    pa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
+    pa_init_proplist(proplist);
+
+    return proplist;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    pa_proplist *proplist;
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+    pa_thread_mq_install(u->thread_mq);
+
+    proplist = tunnel_new_proplist(u);
+    u->context = pa_context_new_with_proplist(u->thread_mainloop_api,
+                                              "PulseAudio",
+                                              proplist);
+    pa_proplist_free(proplist);
+
+    if (!u->context) {
+        pa_log("Failed to create libpulse context");
+        goto fail;
+    }
+
+    if (u->cookie_file && pa_context_load_cookie_from_file(u->context, u->cookie_file) != 0) {
+        pa_log_error("Can not load cookie file!");
+        goto fail;
+    }
+
+    pa_context_set_state_callback(u->context, context_state_cb, u);
+    if (pa_context_connect(u->context,
+                           u->remote_server,
+                           PA_CONTEXT_NOAUTOSPAWN,
+                           NULL) < 0) {
+        pa_log("Failed to connect libpulse context");
+        goto fail;
+    }
+
+    for (;;) {
+        int ret;
+
+        if (pa_mainloop_iterate(u->thread_mainloop, 1, &ret) < 0) {
+            if (ret == 0)
+                goto finish;
+            else
+                goto fail;
+        }
+
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+            pa_sink_process_rewind(u->sink, 0);
+
+        if (u->connected &&
+                pa_stream_get_state(u->stream) == PA_STREAM_READY &&
+                PA_SINK_IS_LINKED(u->sink->thread_info.state)) {
+            size_t writable;
+
+            writable = pa_stream_writable_size(u->stream);
+            if (writable > 0) {
+                pa_memchunk memchunk;
+                const void *p;
+
+                pa_sink_render_full(u->sink, writable, &memchunk);
+
+                pa_assert(memchunk.length > 0);
+
+                /* we have new data to write */
+                p = pa_memblock_acquire(memchunk.memblock);
+                /* TODO: Use pa_stream_begin_write() to reduce copying. */
+                ret = pa_stream_write(u->stream,
+                                      (uint8_t*) p + memchunk.index,
+                                      memchunk.length,
+                                      NULL,     /**< A cleanup routine for the data or NULL to request an internal copy */
+                                      0,        /** offset */
+                                      PA_SEEK_RELATIVE);
+                pa_memblock_release(memchunk.memblock);
+                pa_memblock_unref(memchunk.memblock);
+
+                if (ret != 0) {
+                    pa_log_error("Could not write data into the stream ... ret = %i", ret);
+                    u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+                }
+
+            }
+        }
+    }
+fail:
+    pa_asyncmsgq_post(u->thread_mq->outq, PA_MSGOBJECT(u->module->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq->inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    if (u->stream) {
+        pa_stream_disconnect(u->stream);
+        pa_stream_unref(u->stream);
+        u->stream = NULL;
+    }
+
+    if (u->context) {
+        pa_context_disconnect(u->context);
+        pa_context_unref(u->context);
+        u->context = NULL;
+    }
+
+    pa_log_debug("Thread shutting down");
+}
+
+static void stream_state_cb(pa_stream *stream, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    switch (pa_stream_get_state(stream)) {
+        case PA_STREAM_FAILED:
+            pa_log_error("Stream failed.");
+            u->connected = false;
+            u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+            break;
+        case PA_STREAM_TERMINATED:
+            pa_log_debug("Stream terminated.");
+            break;
+        case PA_STREAM_READY:
+            if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+                cork_stream(u, false);
+
+            /* Only call our requested_latency_cb when requested_latency
+             * changed between PA_STREAM_CREATING -> PA_STREAM_READY, because
+             * we don't want to override the initial tlength set by the server
+             * without a good reason. */
+            if (u->update_stream_bufferattr_after_connect)
+                sink_update_requested_latency_cb(u->sink);
+            else
+                stream_changed_buffer_attr_cb(stream, userdata);
+        case PA_STREAM_CREATING:
+        case PA_STREAM_UNCONNECTED:
+            break;
+    }
+}
+
+/* called when remote server changes the stream buffer_attr */
+static void stream_changed_buffer_attr_cb(pa_stream *stream, void *userdata) {
+    struct userdata *u = userdata;
+    const pa_buffer_attr *bufferattr;
+    pa_assert(u);
+
+    bufferattr = pa_stream_get_buffer_attr(u->stream);
+    pa_sink_set_max_request_within_thread(u->sink, bufferattr->tlength);
+}
+
+/* called after we requested a change of the stream buffer_attr */
+static void stream_set_buffer_attr_cb(pa_stream *stream, int success, void *userdata) {
+    stream_changed_buffer_attr_cb(stream, userdata);
+}
+
+static void context_state_cb(pa_context *c, void *userdata) {
+    struct userdata *u = userdata;
+    pa_assert(u);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_UNCONNECTED:
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+        case PA_CONTEXT_READY: {
+            pa_proplist *proplist;
+            pa_buffer_attr bufferattr;
+            pa_usec_t requested_latency;
+            char *username = pa_get_user_name_malloc();
+            char *hostname = pa_get_host_name_malloc();
+            /* TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus' */
+            char *stream_name = pa_sprintf_malloc(_("Tunnel for %s@%s"), username, hostname);
+            pa_xfree(hostname);
+            pa_xfree(username);
+
+            pa_log_debug("Connection successful. Creating stream.");
+            pa_assert(!u->stream);
+
+            proplist = tunnel_new_proplist(u);
+            u->stream = pa_stream_new_with_proplist(u->context,
+                                                    stream_name,
+                                                    &u->sink->sample_spec,
+                                                    &u->sink->channel_map,
+                                                    proplist);
+            pa_proplist_free(proplist);
+            pa_xfree(stream_name);
+
+            if (!u->stream) {
+                pa_log_error("Could not create a stream.");
+                u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+                return;
+            }
+
+            requested_latency = pa_sink_get_requested_latency_within_thread(u->sink);
+            if (requested_latency == (pa_usec_t) -1)
+                requested_latency = u->sink->thread_info.max_latency;
+
+            reset_bufferattr(&bufferattr);
+            bufferattr.tlength = pa_usec_to_bytes(requested_latency, &u->sink->sample_spec);
+
+            pa_stream_set_state_callback(u->stream, stream_state_cb, userdata);
+            pa_stream_set_buffer_attr_callback(u->stream, stream_changed_buffer_attr_cb, userdata);
+            if (pa_stream_connect_playback(u->stream,
+                                           u->remote_sink_name,
+                                           &bufferattr,
+                                           PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_DONT_MOVE | PA_STREAM_START_CORKED | PA_STREAM_AUTO_TIMING_UPDATE,
+                                           NULL,
+                                           NULL) < 0) {
+                pa_log_error("Could not connect stream.");
+                u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+            }
+            u->connected = true;
+            break;
+        }
+        case PA_CONTEXT_FAILED:
+            pa_log_debug("Context failed: %s.", pa_strerror(pa_context_errno(u->context)));
+            u->connected = false;
+            u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+            break;
+        case PA_CONTEXT_TERMINATED:
+            pa_log_debug("Context terminated.");
+            u->connected = false;
+            u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+            break;
+    }
+}
+
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u;
+    pa_operation *operation;
+    size_t nbytes;
+    pa_usec_t block_usec;
+    pa_buffer_attr bufferattr;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    block_usec = pa_sink_get_requested_latency_within_thread(s);
+    if (block_usec == (pa_usec_t) -1)
+        block_usec = s->thread_info.max_latency;
+
+    nbytes = pa_usec_to_bytes(block_usec, &s->sample_spec);
+    pa_sink_set_max_request_within_thread(s, nbytes);
+
+    if (u->stream) {
+        switch (pa_stream_get_state(u->stream)) {
+            case PA_STREAM_READY:
+                if (pa_stream_get_buffer_attr(u->stream)->tlength == nbytes)
+                    break;
+
+                reset_bufferattr(&bufferattr);
+                bufferattr.tlength = nbytes;
+                if ((operation = pa_stream_set_buffer_attr(u->stream, &bufferattr, stream_set_buffer_attr_cb, u)))
+                    pa_operation_unref(operation);
+                break;
+            case PA_STREAM_CREATING:
+                /* we have to delay our request until stream is ready */
+                u->update_stream_bufferattr_after_connect = true;
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            int negative;
+            pa_usec_t remote_latency;
+
+            if (!PA_SINK_IS_LINKED(u->sink->thread_info.state)) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            if (!u->stream) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            if (pa_stream_get_state(u->stream) != PA_STREAM_READY) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            if (pa_stream_get_latency(u->stream, &remote_latency, &negative) < 0) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            *((int64_t*) data) = remote_latency;
+            return 0;
+        }
+        case PA_SINK_MESSAGE_SET_STATE:
+            if (!u->stream || pa_stream_get_state(u->stream) != PA_STREAM_READY)
+                break;
+
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+                case PA_SINK_SUSPENDED: {
+                    cork_stream(u, true);
+                    break;
+                }
+                case PA_SINK_IDLE:
+                case PA_SINK_RUNNING: {
+                    cork_stream(u, false);
+                    break;
+                }
+                case PA_SINK_INVALID_STATE:
+                case PA_SINK_INIT:
+                case PA_SINK_UNLINKED:
+                    break;
+            }
+            break;
+    }
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u = NULL;
+    pa_modargs *ma = NULL;
+    pa_sink_new_data sink_data;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    const char *remote_server = NULL;
+    const char *sink_name = NULL;
+    char *default_sink_name = NULL;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    remote_server = pa_modargs_get_value(ma, "server", NULL);
+    if (!remote_server) {
+        pa_log("No server given!");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    m->userdata = u;
+    u->remote_server = pa_xstrdup(remote_server);
+    u->thread_mainloop = pa_mainloop_new();
+    if (u->thread_mainloop == NULL) {
+        pa_log("Failed to create mainloop");
+        goto fail;
+    }
+    u->thread_mainloop_api = pa_mainloop_get_api(u->thread_mainloop);
+    u->cookie_file = pa_xstrdup(pa_modargs_get_value(ma, "cookie", NULL));
+    u->remote_sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+
+    u->thread_mq = pa_xnew0(pa_thread_mq, 1);
+
+    if (pa_thread_mq_init_thread_mainloop(u->thread_mq, m->core->mainloop, u->thread_mainloop_api) < 0) {
+        pa_log("pa_thread_mq_init_thread_mainloop() failed.");
+        goto fail;
+    }
+
+    /* The rtpoll created here is never run. It is only necessary to avoid crashes
+     * when module-tunnel-sink-new is used together with module-loopback or
+     * module-combine-sink. Both modules base their asyncmsq on the rtpoll provided
+     * by the sink. module-loopback and combine-sink only work because they call
+     * pa_asyncmsq_process_one() themselves. module_rtp_recv also uses the rtpoll,
+     * but never calls pa_asyncmsq_process_one(), so it will not work in combination
+     * with module-tunnel-sink-new. */
+    u->rtpoll = pa_rtpoll_new();
+
+    /* Create sink */
+    pa_sink_new_data_init(&sink_data);
+    sink_data.driver = __FILE__;
+    sink_data.module = m;
+
+    default_sink_name = pa_sprintf_malloc("tunnel-sink-new.%s", remote_server);
+    sink_name = pa_modargs_get_value(ma, "sink_name", default_sink_name);
+
+    pa_sink_new_data_set_name(&sink_data, sink_name);
+    pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+    pa_sink_new_data_set_channel_map(&sink_data, &map);
+
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+    pa_proplist_setf(sink_data.proplist,
+                     PA_PROP_DEVICE_DESCRIPTION,
+                     _("Tunnel to %s/%s"),
+                     remote_server,
+                     pa_strempty(u->remote_sink_name));
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&sink_data);
+        goto fail;
+    }
+    if (!(u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY | PA_SINK_NETWORK))) {
+        pa_log("Failed to create sink.");
+        pa_sink_new_data_done(&sink_data);
+        goto fail;
+    }
+
+    pa_sink_new_data_done(&sink_data);
+    u->sink->userdata = u;
+    u->sink->parent.process_msg = sink_process_msg_cb;
+    u->sink->update_requested_latency = sink_update_requested_latency_cb;
+    pa_sink_set_latency_range(u->sink, 0, MAX_LATENCY_USEC);
+
+    /* set thread message queue */
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq->inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    if (!(u->thread = pa_thread_new("tunnel-sink", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_sink_put(u->sink);
+    pa_modargs_free(ma);
+    pa_xfree(default_sink_name);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    if (default_sink_name)
+        pa_xfree(default_sink_name);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq->inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    if (u->thread_mq) {
+        pa_thread_mq_done(u->thread_mq);
+        pa_xfree(u->thread_mq);
+    }
+
+    if (u->thread_mainloop)
+        pa_mainloop_free(u->thread_mainloop);
+
+    if (u->cookie_file)
+        pa_xfree(u->cookie_file);
+
+    if (u->remote_sink_name)
+        pa_xfree(u->remote_sink_name);
+
+    if (u->remote_server)
+        pa_xfree(u->remote_server);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-tunnel-source-new.c b/src/modules/module-tunnel-source-new.c
new file mode 100644 (file)
index 0000000..f547911
--- /dev/null
@@ -0,0 +1,614 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2013 Alexander Couzens
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/context.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+#include <pulse/stream.h>
+#include <pulse/mainloop.h>
+#include <pulse/introspect.h>
+#include <pulse/error.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/source.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/poll.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/proplist-util.h>
+
+#include "module-tunnel-source-new-symdef.h"
+
+PA_MODULE_AUTHOR("Alexander Couzens");
+PA_MODULE_DESCRIPTION("Create a network source which connects via a stream to a remote PulseAudio server");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "server=<address> "
+        "source=<name of the remote source> "
+        "source_name=<name for the local source> "
+        "source_properties=<properties for the local source> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "channel_map=<channel map> "
+        "cookie=<cookie file path>"
+        );
+
+#define TUNNEL_THREAD_FAILED_MAINLOOP 1
+
+static void stream_state_cb(pa_stream *stream, void *userdata);
+static void stream_read_cb(pa_stream *s, size_t length, void *userdata);
+static void context_state_cb(pa_context *c, void *userdata);
+static void source_update_requested_latency_cb(pa_source *s);
+
+struct userdata {
+    pa_module *module;
+    pa_source *source;
+    pa_thread *thread;
+    pa_thread_mq *thread_mq;
+    pa_mainloop *thread_mainloop;
+    pa_mainloop_api *thread_mainloop_api;
+
+    pa_context *context;
+    pa_stream *stream;
+    pa_rtpoll *rtpoll;
+
+    bool update_stream_bufferattr_after_connect;
+    bool connected;
+    bool new_data;
+
+    char *cookie_file;
+    char *remote_server;
+    char *remote_source_name;
+};
+
+static const char* const valid_modargs[] = {
+    "source_name",
+    "source_properties",
+    "server",
+    "source",
+    "format",
+    "channels",
+    "rate",
+    "channel_map",
+    "cookie",
+   /* "reconnect", reconnect if server comes back again - unimplemented */
+    NULL,
+};
+
+static void cork_stream(struct userdata *u, bool cork) {
+    pa_operation *operation;
+
+    pa_assert(u);
+    pa_assert(u->stream);
+
+    if ((operation = pa_stream_cork(u->stream, cork, NULL, NULL)))
+        pa_operation_unref(operation);
+}
+
+static void reset_bufferattr(pa_buffer_attr *bufferattr) {
+    pa_assert(bufferattr);
+    bufferattr->fragsize = (uint32_t) -1;
+    bufferattr->minreq = (uint32_t) -1;
+    bufferattr->maxlength = (uint32_t) -1;
+    bufferattr->prebuf = (uint32_t) -1;
+    bufferattr->tlength = (uint32_t) -1;
+}
+
+static pa_proplist* tunnel_new_proplist(struct userdata *u) {
+    pa_proplist *proplist = pa_proplist_new();
+    pa_assert(proplist);
+    pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "PulseAudio");
+    pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");
+    pa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
+    pa_init_proplist(proplist);
+
+    return proplist;
+}
+
+static void stream_read_cb(pa_stream *s, size_t length, void *userdata) {
+    struct userdata *u = userdata;
+    u->new_data = true;
+}
+
+/* called from io context to read samples from the stream into our source */
+static void read_new_samples(struct userdata *u) {
+    const void *p;
+    size_t readable = 0;
+    pa_memchunk memchunk;
+
+    pa_assert(u);
+    u->new_data = false;
+
+    pa_memchunk_reset(&memchunk);
+
+    if (PA_UNLIKELY(!u->connected || pa_stream_get_state(u->stream) != PA_STREAM_READY))
+        return;
+
+    readable = pa_stream_readable_size(u->stream);
+    while (readable > 0) {
+        size_t nbytes = 0;
+        if (PA_UNLIKELY(pa_stream_peek(u->stream, &p, &nbytes) != 0)) {
+            pa_log("pa_stream_peek() failed: %s", pa_strerror(pa_context_errno(u->context)));
+            u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+            return;
+        }
+
+        if (PA_LIKELY(p)) {
+            /* we have valid data */
+            memchunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, (void *) p, nbytes, true);
+            memchunk.length = nbytes;
+            memchunk.index = 0;
+
+            pa_source_post(u->source, &memchunk);
+            pa_memblock_unref_fixed(memchunk.memblock);
+        } else {
+            size_t bytes_to_generate = nbytes;
+
+            /* we have a hole. generate silence */
+            memchunk = u->source->silence;
+            pa_memblock_ref(memchunk.memblock);
+
+            while (bytes_to_generate > 0) {
+                if (bytes_to_generate < memchunk.length)
+                    memchunk.length = bytes_to_generate;
+
+                pa_source_post(u->source, &memchunk);
+                bytes_to_generate -= memchunk.length;
+            }
+
+            pa_memblock_unref(memchunk.memblock);
+        }
+
+        pa_stream_drop(u->stream);
+        readable -= nbytes;
+    }
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    pa_proplist *proplist;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+    pa_thread_mq_install(u->thread_mq);
+
+    proplist = tunnel_new_proplist(u);
+    u->context = pa_context_new_with_proplist(u->thread_mainloop_api,
+                                              "PulseAudio",
+                                              proplist);
+    pa_proplist_free(proplist);
+
+    if (!u->context) {
+        pa_log("Failed to create libpulse context");
+        goto fail;
+    }
+
+    if (u->cookie_file && pa_context_load_cookie_from_file(u->context, u->cookie_file) != 0) {
+        pa_log_error("Can not load cookie file!");
+        goto fail;
+    }
+
+    pa_context_set_state_callback(u->context, context_state_cb, u);
+    if (pa_context_connect(u->context,
+                           u->remote_server,
+                           PA_CONTEXT_NOAUTOSPAWN,
+                           NULL) < 0) {
+        pa_log("Failed to connect libpulse context: %s", pa_strerror(pa_context_errno(u->context)));
+        goto fail;
+    }
+
+    for (;;) {
+        int ret;
+
+        if (pa_mainloop_iterate(u->thread_mainloop, 1, &ret) < 0) {
+            if (ret == 0)
+                goto finish;
+            else
+                goto fail;
+        }
+
+        if (u->new_data)
+            read_new_samples(u);
+    }
+fail:
+    pa_asyncmsgq_post(u->thread_mq->outq, PA_MSGOBJECT(u->module->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq->inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    if (u->stream) {
+        pa_stream_disconnect(u->stream);
+        pa_stream_unref(u->stream);
+        u->stream = NULL;
+    }
+
+    if (u->context) {
+        pa_context_disconnect(u->context);
+        pa_context_unref(u->context);
+        u->context = NULL;
+    }
+
+    pa_log_debug("Thread shutting down");
+}
+
+static void stream_state_cb(pa_stream *stream, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    switch (pa_stream_get_state(stream)) {
+        case PA_STREAM_FAILED:
+            pa_log_error("Stream failed: %s", pa_strerror(pa_context_errno(u->context)));
+            u->connected = false;
+            u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+            break;
+        case PA_STREAM_TERMINATED:
+            pa_log_debug("Stream terminated.");
+            break;
+        case PA_STREAM_READY:
+            if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+                cork_stream(u, false);
+
+            /* Only call our requested_latency_cb when requested_latency
+             * changed between PA_STREAM_CREATING -> PA_STREAM_READY, because
+             * we don't want to override the initial fragsize set by the server
+             * without a good reason. */
+            if (u->update_stream_bufferattr_after_connect)
+                source_update_requested_latency_cb(u->source);
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+            break;
+    }
+}
+
+static void context_state_cb(pa_context *c, void *userdata) {
+    struct userdata *u = userdata;
+    pa_assert(u);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_UNCONNECTED:
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+        case PA_CONTEXT_READY: {
+            pa_proplist *proplist;
+            pa_buffer_attr bufferattr;
+            pa_usec_t requested_latency;
+            char *username = pa_get_user_name_malloc();
+            char *hostname = pa_get_host_name_malloc();
+            /* TODO: old tunnel put here the remote source_name into stream name e.g. 'Null Output for lynxis@lazus' */
+            char *stream_name = pa_sprintf_malloc(_("Tunnel for %s@%s"), username, hostname);
+            pa_xfree(username);
+            pa_xfree(hostname);
+
+            pa_log_debug("Connection successful. Creating stream.");
+            pa_assert(!u->stream);
+
+            proplist = tunnel_new_proplist(u);
+            u->stream = pa_stream_new_with_proplist(u->context,
+                                                    stream_name,
+                                                    &u->source->sample_spec,
+                                                    &u->source->channel_map,
+                                                    proplist);
+            pa_proplist_free(proplist);
+            pa_xfree(stream_name);
+
+            if (!u->stream) {
+                pa_log_error("Could not create a stream: %s", pa_strerror(pa_context_errno(u->context)));
+                u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+                return;
+            }
+
+            requested_latency = pa_source_get_requested_latency_within_thread(u->source);
+            if (requested_latency == (uint32_t) -1)
+                requested_latency = u->source->thread_info.max_latency;
+
+            reset_bufferattr(&bufferattr);
+            bufferattr.fragsize = pa_usec_to_bytes(requested_latency, &u->source->sample_spec);
+
+            pa_stream_set_state_callback(u->stream, stream_state_cb, userdata);
+            pa_stream_set_read_callback(u->stream, stream_read_cb, userdata);
+            if (pa_stream_connect_record(u->stream,
+                                         u->remote_source_name,
+                                         &bufferattr,
+                                         PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_DONT_MOVE|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_START_CORKED) < 0) {
+                pa_log_debug("Could not create stream: %s", pa_strerror(pa_context_errno(u->context)));
+                u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+            }
+            u->connected = true;
+            break;
+        }
+        case PA_CONTEXT_FAILED:
+            pa_log_debug("Context failed with err %s.", pa_strerror(pa_context_errno(u->context)));
+            u->connected = false;
+            u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+            break;
+        case PA_CONTEXT_TERMINATED:
+            pa_log_debug("Context terminated.");
+            u->connected = false;
+            u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
+            break;
+    }
+}
+
+static void source_update_requested_latency_cb(pa_source *s) {
+    struct userdata *u;
+    pa_operation *operation;
+    size_t nbytes;
+    pa_usec_t block_usec;
+    pa_buffer_attr bufferattr;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    block_usec = pa_source_get_requested_latency_within_thread(s);
+    if (block_usec == (pa_usec_t) -1)
+        block_usec = s->thread_info.max_latency;
+
+    nbytes = pa_usec_to_bytes(block_usec, &s->sample_spec);
+
+    if (u->stream) {
+        switch (pa_stream_get_state(u->stream)) {
+            case PA_STREAM_READY:
+                if (pa_stream_get_buffer_attr(u->stream)->fragsize == nbytes)
+                    break;
+
+                reset_bufferattr(&bufferattr);
+                bufferattr.fragsize = nbytes;
+                if ((operation = pa_stream_set_buffer_attr(u->stream, &bufferattr, NULL, NULL)))
+                    pa_operation_unref(operation);
+                break;
+            case PA_STREAM_CREATING:
+                /* we have to delay our request until stream is ready */
+                u->update_stream_bufferattr_after_connect = true;
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            int negative;
+            pa_usec_t remote_latency;
+
+            if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state)) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            if (!u->stream) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            if (pa_stream_get_state(u->stream) != PA_STREAM_READY) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            if (pa_stream_get_latency(u->stream, &remote_latency, &negative) < 0) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            if (negative)
+                *((int64_t*) data) = - (int64_t)remote_latency;
+            else
+                *((int64_t*) data) = remote_latency;
+
+            return 0;
+        }
+        case PA_SOURCE_MESSAGE_SET_STATE:
+            if (!u->stream || pa_stream_get_state(u->stream) != PA_STREAM_READY)
+                break;
+
+            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+                case PA_SOURCE_SUSPENDED: {
+                    cork_stream(u, true);
+                    break;
+                }
+                case PA_SOURCE_IDLE:
+                case PA_SOURCE_RUNNING: {
+                    cork_stream(u, false);
+                    break;
+                }
+                case PA_SOURCE_INVALID_STATE:
+                case PA_SOURCE_INIT:
+                case PA_SOURCE_UNLINKED:
+                    break;
+            }
+            break;
+    }
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u = NULL;
+    pa_modargs *ma = NULL;
+    pa_source_new_data source_data;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    const char *remote_server = NULL;
+    const char *source_name = NULL;
+    char *default_source_name = NULL;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    remote_server = pa_modargs_get_value(ma, "server", NULL);
+    if (!remote_server) {
+        pa_log("No server given!");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    m->userdata = u;
+    u->remote_server = pa_xstrdup(remote_server);
+    u->thread_mainloop = pa_mainloop_new();
+    if (u->thread_mainloop == NULL) {
+        pa_log("Failed to create mainloop");
+        goto fail;
+    }
+    u->thread_mainloop_api = pa_mainloop_get_api(u->thread_mainloop);
+    u->cookie_file = pa_xstrdup(pa_modargs_get_value(ma, "cookie", NULL));
+    u->remote_source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
+
+    u->thread_mq = pa_xnew0(pa_thread_mq, 1);
+
+    if (pa_thread_mq_init_thread_mainloop(u->thread_mq, m->core->mainloop, u->thread_mainloop_api) < 0) {
+        pa_log("pa_thread_mq_init_thread_mainloop() failed.");
+        goto fail;
+    }
+
+    /* The rtpoll created here is never run. It is only necessary to avoid crashes
+     * when module-tunnel-source-new is used together with module-loopback.
+     * module-loopback bases the asyncmsq on the rtpoll provided by the source and
+     * only works because it calls pa_asyncmsq_process_one(). */
+    u->rtpoll = pa_rtpoll_new();
+
+    /* Create source */
+    pa_source_new_data_init(&source_data);
+    source_data.driver = __FILE__;
+    source_data.module = m;
+
+    default_source_name = pa_sprintf_malloc("tunnel-source-new.%s", remote_server);
+    source_name = pa_modargs_get_value(ma, "source_name", default_source_name);
+
+    pa_source_new_data_set_name(&source_data, source_name);
+    pa_source_new_data_set_sample_spec(&source_data, &ss);
+    pa_source_new_data_set_channel_map(&source_data, &map);
+
+    pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+    pa_proplist_setf(source_data.proplist,
+                     PA_PROP_DEVICE_DESCRIPTION,
+                     _("Tunnel to %s/%s"),
+                     remote_server,
+                     pa_strempty(u->remote_source_name));
+
+    if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_source_new_data_done(&source_data);
+        goto fail;
+    }
+    if (!(u->source = pa_source_new(m->core, &source_data, PA_SOURCE_LATENCY | PA_SOURCE_DYNAMIC_LATENCY | PA_SOURCE_NETWORK))) {
+        pa_log("Failed to create source.");
+        pa_source_new_data_done(&source_data);
+        goto fail;
+    }
+
+    pa_source_new_data_done(&source_data);
+    u->source->userdata = u;
+    u->source->parent.process_msg = source_process_msg_cb;
+    u->source->update_requested_latency = source_update_requested_latency_cb;
+
+    pa_source_set_asyncmsgq(u->source, u->thread_mq->inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+
+    if (!(u->thread = pa_thread_new("tunnel-source", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_source_put(u->source);
+    pa_modargs_free(ma);
+    pa_xfree(default_source_name);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    if (default_source_name)
+        pa_xfree(default_source_name);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq->inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    if (u->thread_mq) {
+        pa_thread_mq_done(u->thread_mq);
+        pa_xfree(u->thread_mq);
+    }
+
+    if (u->thread_mainloop)
+        pa_mainloop_free(u->thread_mainloop);
+
+    if (u->cookie_file)
+        pa_xfree(u->cookie_file);
+
+    if (u->remote_source_name)
+        pa_xfree(u->remote_source_name);
+
+    if (u->remote_server)
+        pa_xfree(u->remote_server);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
new file mode 100644 (file)
index 0000000..94ea4fb
--- /dev/null
@@ -0,0 +1,2339 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_X11
+#include <xcb/xcb.h>
+#endif
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/version.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/socket-client.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/proplist-util.h>
+#include <pulsecore/auth-cookie.h>
+#include <pulsecore/mcalign.h>
+#include <pulsecore/strlist.h>
+
+#ifdef HAVE_X11
+#include <pulsecore/x11prop.h>
+#endif
+
+#ifdef TUNNEL_SINK
+#include "module-tunnel-sink-symdef.h"
+#else
+#include "module-tunnel-source-symdef.h"
+#endif
+
+#define ENV_DEFAULT_SINK "PULSE_SINK"
+#define ENV_DEFAULT_SOURCE "PULSE_SOURCE"
+#define ENV_DEFAULT_SERVER "PULSE_SERVER"
+#define ENV_COOKIE_FILE "PULSE_COOKIE"
+
+#ifdef TUNNEL_SINK
+PA_MODULE_DESCRIPTION("Tunnel module for sinks");
+PA_MODULE_USAGE(
+        "sink_name=<name for the local sink> "
+        "sink_properties=<properties for the local sink> "
+        "auto=<determine server/sink/cookie automatically> "
+        "server=<address> "
+        "sink=<remote sink name> "
+        "cookie=<filename> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "channel_map=<channel map>");
+#else
+PA_MODULE_DESCRIPTION("Tunnel module for sources");
+PA_MODULE_USAGE(
+        "source_name=<name for the local source> "
+        "source_properties=<properties for the local source> "
+        "auto=<determine server/source/cookie automatically> "
+        "server=<address> "
+        "source=<remote source name> "
+        "cookie=<filename> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "channel_map=<channel map>");
+#endif
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+
+static const char* const valid_modargs[] = {
+    "auto",
+    "server",
+    "cookie",
+    "format",
+    "channels",
+    "rate",
+#ifdef TUNNEL_SINK
+    "sink_name",
+    "sink_properties",
+    "sink",
+#else
+    "source_name",
+    "source_properties",
+    "source",
+#endif
+    "channel_map",
+    NULL,
+};
+
+#define DEFAULT_TIMEOUT 5
+
+#define LATENCY_INTERVAL (10*PA_USEC_PER_SEC)
+
+#define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC)
+
+#ifdef TUNNEL_SINK
+
+enum {
+    SINK_MESSAGE_REQUEST = PA_SINK_MESSAGE_MAX,
+    SINK_MESSAGE_REMOTE_SUSPEND,
+    SINK_MESSAGE_UPDATE_LATENCY,
+    SINK_MESSAGE_POST
+};
+
+#define DEFAULT_TLENGTH_MSEC 150
+#define DEFAULT_MINREQ_MSEC 25
+
+#else
+
+enum {
+    SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX,
+    SOURCE_MESSAGE_REMOTE_SUSPEND,
+    SOURCE_MESSAGE_UPDATE_LATENCY
+};
+
+#define DEFAULT_FRAGSIZE_MSEC 25
+
+#endif
+
+#ifdef TUNNEL_SINK
+static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+#endif
+static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_stream_buffer_attr_changed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
+#ifdef TUNNEL_SINK
+    [PA_COMMAND_REQUEST] = command_request,
+    [PA_COMMAND_STARTED] = command_started,
+#endif
+    [PA_COMMAND_SUBSCRIBE_EVENT] = command_subscribe_event,
+    [PA_COMMAND_OVERFLOW] = command_overflow_or_underflow,
+    [PA_COMMAND_UNDERFLOW] = command_overflow_or_underflow,
+    [PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed,
+    [PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed,
+    [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended,
+    [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended,
+    [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved,
+    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved,
+    [PA_COMMAND_PLAYBACK_STREAM_EVENT] = command_stream_or_client_event,
+    [PA_COMMAND_RECORD_STREAM_EVENT] = command_stream_or_client_event,
+    [PA_COMMAND_CLIENT_EVENT] = command_stream_or_client_event,
+    [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = command_stream_buffer_attr_changed,
+    [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = command_stream_buffer_attr_changed
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    pa_thread *thread;
+
+    pa_socket_client *client;
+    pa_pstream *pstream;
+    pa_pdispatch *pdispatch;
+
+    char *server_name;
+#ifdef TUNNEL_SINK
+    char *sink_name;
+    pa_sink *sink;
+    size_t requested_bytes;
+#else
+    char *source_name;
+    pa_source *source;
+    pa_mcalign *mcalign;
+#endif
+
+    pa_auth_cookie *auth_cookie;
+
+    uint32_t version;
+    uint32_t ctag;
+    uint32_t device_index;
+    uint32_t channel;
+
+    int64_t counter, counter_delta;
+
+    bool remote_corked:1;
+    bool remote_suspended:1;
+
+    pa_usec_t transport_usec; /* maintained in the main thread */
+    pa_usec_t thread_transport_usec; /* maintained in the IO thread */
+
+    uint32_t ignore_latency_before;
+
+    pa_time_event *time_event;
+
+    pa_smoother *smoother;
+
+    char *device_description;
+    char *server_fqdn;
+    char *user_name;
+
+    uint32_t maxlength;
+#ifdef TUNNEL_SINK
+    uint32_t tlength;
+    uint32_t minreq;
+    uint32_t prebuf;
+#else
+    uint32_t fragsize;
+#endif
+};
+
+static void request_latency(struct userdata *u);
+
+/* Called from main context */
+static void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_log_debug("Got stream or client event.");
+}
+
+/* Called from main context */
+static void command_stream_killed(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    pa_log_warn("Stream killed");
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main context */
+static void command_overflow_or_underflow(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    pa_log_info("Server signalled buffer overrun/underrun.");
+    request_latency(u);
+}
+
+/* Called from main context */
+static void command_suspended(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t channel;
+    bool suspended;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_get_boolean(t, &suspended) < 0 ||
+        !pa_tagstruct_eof(t)) {
+
+        pa_log("Invalid packet.");
+        pa_module_unload_request(u->module, true);
+        return;
+    }
+
+    pa_log_debug("Server reports device suspend.");
+
+#ifdef TUNNEL_SINK
+    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
+#else
+    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
+#endif
+
+    request_latency(u);
+}
+
+/* Called from main context */
+static void command_moved(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t channel, di;
+    const char *dn;
+    bool suspended;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_getu32(t, &di) < 0 ||
+        pa_tagstruct_gets(t, &dn) < 0 ||
+        pa_tagstruct_get_boolean(t, &suspended) < 0) {
+
+        pa_log_error("Invalid packet.");
+        pa_module_unload_request(u->module, true);
+        return;
+    }
+
+    pa_log_debug("Server reports a stream move.");
+
+#ifdef TUNNEL_SINK
+    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
+#else
+    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
+#endif
+
+    request_latency(u);
+}
+
+static void command_stream_buffer_attr_changed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t channel, maxlength, tlength = 0, fragsize, prebuf, minreq;
+    pa_usec_t usec;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_getu32(t, &maxlength) < 0) {
+
+        pa_log_error("Invalid packet.");
+        pa_module_unload_request(u->module, true);
+        return;
+    }
+
+    if (command == PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED) {
+        if (pa_tagstruct_getu32(t, &fragsize) < 0 ||
+            pa_tagstruct_get_usec(t, &usec) < 0) {
+
+            pa_log_error("Invalid packet.");
+            pa_module_unload_request(u->module, true);
+            return;
+        }
+    } else {
+        if (pa_tagstruct_getu32(t, &tlength) < 0 ||
+            pa_tagstruct_getu32(t, &prebuf) < 0 ||
+            pa_tagstruct_getu32(t, &minreq) < 0 ||
+            pa_tagstruct_get_usec(t, &usec) < 0) {
+
+            pa_log_error("Invalid packet.");
+            pa_module_unload_request(u->module, true);
+            return;
+        }
+    }
+
+#ifdef TUNNEL_SINK
+    pa_log_debug("Server reports buffer attrs changed. tlength now at %lu, before %lu.", (unsigned long) tlength, (unsigned long) u->tlength);
+#endif
+
+    request_latency(u);
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from main context */
+static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    pa_log_debug("Server reports playback started.");
+    request_latency(u);
+}
+
+#endif
+
+/* Called from IO thread context */
+static void check_smoother_status(struct userdata *u, bool past) {
+    pa_usec_t x;
+
+    pa_assert(u);
+
+    x = pa_rtclock_now();
+
+    /* Correct by the time the requested issued needs to travel to the
+     * other side.  This is a valid thread-safe access, because the
+     * main thread is waiting for us */
+
+    if (past)
+        x -= u->thread_transport_usec;
+    else
+        x += u->thread_transport_usec;
+
+    if (u->remote_suspended || u->remote_corked)
+        pa_smoother_pause(u->smoother, x);
+    else
+        pa_smoother_resume(u->smoother, x, true);
+}
+
+/* Called from IO thread context */
+static void stream_cork_within_thread(struct userdata *u, bool cork) {
+    pa_assert(u);
+
+    if (u->remote_corked == cork)
+        return;
+
+    u->remote_corked = cork;
+    check_smoother_status(u, false);
+}
+
+/* Called from main context */
+static void stream_cork(struct userdata *u, bool cork) {
+    pa_tagstruct *t;
+    pa_assert(u);
+
+    if (!u->pstream)
+        return;
+
+    t = pa_tagstruct_new();
+#ifdef TUNNEL_SINK
+    pa_tagstruct_putu32(t, PA_COMMAND_CORK_PLAYBACK_STREAM);
+#else
+    pa_tagstruct_putu32(t, PA_COMMAND_CORK_RECORD_STREAM);
+#endif
+    pa_tagstruct_putu32(t, u->ctag++);
+    pa_tagstruct_putu32(t, u->channel);
+    pa_tagstruct_put_boolean(t, cork);
+    pa_pstream_send_tagstruct(u->pstream, t);
+
+    request_latency(u);
+}
+
+/* Called from IO thread context */
+static void stream_suspend_within_thread(struct userdata *u, bool suspend) {
+    pa_assert(u);
+
+    if (u->remote_suspended == suspend)
+        return;
+
+    u->remote_suspended = suspend;
+    check_smoother_status(u, true);
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from IO thread context */
+static void send_data(struct userdata *u) {
+    pa_assert(u);
+
+    while (u->requested_bytes > 0) {
+        pa_memchunk memchunk;
+
+        pa_sink_render(u->sink, u->requested_bytes, &memchunk);
+        pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_POST, NULL, 0, &memchunk, NULL);
+        pa_memblock_unref(memchunk.memblock);
+
+        u->requested_bytes -= memchunk.length;
+
+        u->counter += (int64_t) memchunk.length;
+    }
+}
+
+/* This function is called from IO context -- except when it is not. */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            int r;
+
+            /* First, change the state, because otherwise pa_sink_render() would fail */
+            if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) {
+
+                stream_cork_within_thread(u, u->sink->thread_info.state == PA_SINK_SUSPENDED);
+
+                if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+                    send_data(u);
+            }
+
+            return r;
+        }
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t yl, yr;
+            int64_t *usec = data;
+
+            yl = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
+            yr = pa_smoother_get(u->smoother, pa_rtclock_now());
+
+            *usec = (int64_t)yl - yr;
+            return 0;
+        }
+
+        case SINK_MESSAGE_REQUEST:
+
+            pa_assert(offset > 0);
+            u->requested_bytes += (size_t) offset;
+
+            if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+                send_data(u);
+
+            return 0;
+
+        case SINK_MESSAGE_REMOTE_SUSPEND:
+
+            stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
+            return 0;
+
+        case SINK_MESSAGE_UPDATE_LATENCY: {
+            pa_usec_t y;
+
+            y = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
+
+            if (y > (pa_usec_t) offset)
+                y -= (pa_usec_t) offset;
+            else
+                y = 0;
+
+            pa_smoother_put(u->smoother, pa_rtclock_now(), y);
+
+            /* We can access this freely here, since the main thread is waiting for us */
+            u->thread_transport_usec = u->transport_usec;
+
+            return 0;
+        }
+
+        case SINK_MESSAGE_POST:
+
+            /* OK, This might be a bit confusing. This message is
+             * delivered to us from the main context -- NOT from the
+             * IO thread context where the rest of the messages are
+             * dispatched. Yeah, ugly, but I am a lazy bastard. */
+
+            pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, chunk);
+
+            u->counter_delta += (int64_t) chunk->length;
+
+            return 0;
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+    pa_sink_assert_ref(s);
+    u = s->userdata;
+
+    switch ((pa_sink_state_t) state) {
+
+        case PA_SINK_SUSPENDED:
+            pa_assert(PA_SINK_IS_OPENED(s->state));
+            stream_cork(u, true);
+            break;
+
+        case PA_SINK_IDLE:
+        case PA_SINK_RUNNING:
+            if (s->state == PA_SINK_SUSPENDED)
+                stream_cork(u, false);
+            break;
+
+        case PA_SINK_UNLINKED:
+        case PA_SINK_INIT:
+        case PA_SINK_INVALID_STATE:
+            ;
+    }
+
+    return 0;
+}
+
+#else
+
+/* This function is called from IO context -- except when it is not. */
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_SET_STATE: {
+            int r;
+
+            if ((r = pa_source_process_msg(o, code, data, offset, chunk)) >= 0)
+                stream_cork_within_thread(u, u->source->thread_info.state == PA_SOURCE_SUSPENDED);
+
+            return r;
+        }
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            pa_usec_t yr, yl;
+            int64_t *usec = data;
+
+            yl = pa_bytes_to_usec((uint64_t) u->counter, &PA_SOURCE(o)->sample_spec);
+            yr = pa_smoother_get(u->smoother, pa_rtclock_now());
+
+            *usec = (int64_t)yr - yl;
+            return 0;
+        }
+
+        case SOURCE_MESSAGE_POST: {
+            pa_memchunk c;
+
+            pa_mcalign_push(u->mcalign, chunk);
+
+            while (pa_mcalign_pop(u->mcalign, &c) >= 0) {
+
+                if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+                    pa_source_post(u->source, &c);
+
+                pa_memblock_unref(c.memblock);
+
+                u->counter += (int64_t) c.length;
+            }
+
+            return 0;
+        }
+
+        case SOURCE_MESSAGE_REMOTE_SUSPEND:
+
+            stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
+            return 0;
+
+        case SOURCE_MESSAGE_UPDATE_LATENCY: {
+            pa_usec_t y;
+
+            y = pa_bytes_to_usec((uint64_t) u->counter, &u->source->sample_spec);
+            y += (pa_usec_t) offset;
+
+            pa_smoother_put(u->smoother, pa_rtclock_now(), y);
+
+            /* We can access this freely here, since the main thread is waiting for us */
+            u->thread_transport_usec = u->transport_usec;
+
+            return 0;
+        }
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int source_set_state(pa_source *s, pa_source_state_t state) {
+    struct userdata *u;
+    pa_source_assert_ref(s);
+    u = s->userdata;
+
+    switch ((pa_source_state_t) state) {
+
+        case PA_SOURCE_SUSPENDED:
+            pa_assert(PA_SOURCE_IS_OPENED(s->state));
+            stream_cork(u, true);
+            break;
+
+        case PA_SOURCE_IDLE:
+        case PA_SOURCE_RUNNING:
+            if (s->state == PA_SOURCE_SUSPENDED)
+                stream_cork(u, false);
+            break;
+
+        case PA_SOURCE_UNLINKED:
+        case PA_SOURCE_INIT:
+        case PA_SOURCE_INVALID_STATE:
+            ;
+    }
+
+    return 0;
+}
+
+#endif
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        int ret;
+
+#ifdef TUNNEL_SINK
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+            pa_sink_process_rewind(u->sink, 0);
+#endif
+
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+#ifdef TUNNEL_SINK
+/* Called from main context */
+static void command_request(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t bytes, channel;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_REQUEST);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_getu32(t, &bytes) < 0) {
+        pa_log("Invalid protocol reply");
+        goto fail;
+    }
+
+    if (channel != u->channel) {
+        pa_log("Received data for invalid channel");
+        goto fail;
+    }
+
+    pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
+    return;
+
+fail:
+    pa_module_unload_request(u->module, true);
+}
+
+#endif
+
+/* Called from main context */
+static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    pa_usec_t sink_usec, source_usec;
+    bool playing;
+    int64_t write_index, read_index;
+    struct timeval local, remote, now;
+    pa_sample_spec *ss;
+    int64_t delay;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get latency.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
+        pa_tagstruct_get_usec(t, &source_usec) < 0 ||
+        pa_tagstruct_get_boolean(t, &playing) < 0 ||
+        pa_tagstruct_get_timeval(t, &local) < 0 ||
+        pa_tagstruct_get_timeval(t, &remote) < 0 ||
+        pa_tagstruct_gets64(t, &write_index) < 0 ||
+        pa_tagstruct_gets64(t, &read_index) < 0) {
+        pa_log("Invalid reply.");
+        goto fail;
+    }
+
+#ifdef TUNNEL_SINK
+    if (u->version >= 13) {
+        uint64_t underrun_for = 0, playing_for = 0;
+
+        if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
+            pa_tagstruct_getu64(t, &playing_for) < 0) {
+            pa_log("Invalid reply.");
+            goto fail;
+        }
+    }
+#endif
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_log("Invalid reply.");
+        goto fail;
+    }
+
+    if (tag < u->ignore_latency_before) {
+        return;
+    }
+
+    pa_gettimeofday(&now);
+
+    /* Calculate transport usec */
+    if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) {
+        /* local and remote seem to have synchronized clocks */
+#ifdef TUNNEL_SINK
+        u->transport_usec = pa_timeval_diff(&remote, &local);
+#else
+        u->transport_usec = pa_timeval_diff(&now, &remote);
+#endif
+    } else
+        u->transport_usec = pa_timeval_diff(&now, &local)/2;
+
+    /* First, take the device's delay */
+#ifdef TUNNEL_SINK
+    delay = (int64_t) sink_usec;
+    ss = &u->sink->sample_spec;
+#else
+    delay = (int64_t) source_usec;
+    ss = &u->source->sample_spec;
+#endif
+
+    /* Add the length of our server-side buffer */
+    if (write_index >= read_index)
+        delay += (int64_t) pa_bytes_to_usec((uint64_t) (write_index-read_index), ss);
+    else
+        delay -= (int64_t) pa_bytes_to_usec((uint64_t) (read_index-write_index), ss);
+
+    /* Our measurements are already out of date, hence correct by the     *
+     * transport latency */
+#ifdef TUNNEL_SINK
+    delay -= (int64_t) u->transport_usec;
+#else
+    delay += (int64_t) u->transport_usec;
+#endif
+
+    /* Now correct by what we have have read/written since we requested the update */
+#ifdef TUNNEL_SINK
+    delay += (int64_t) pa_bytes_to_usec((uint64_t) u->counter_delta, ss);
+#else
+    delay -= (int64_t) pa_bytes_to_usec((uint64_t) u->counter_delta, ss);
+#endif
+
+#ifdef TUNNEL_SINK
+    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
+#else
+    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
+#endif
+
+    return;
+
+fail:
+
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main context */
+static void request_latency(struct userdata *u) {
+    pa_tagstruct *t;
+    struct timeval now;
+    uint32_t tag;
+    pa_assert(u);
+
+    t = pa_tagstruct_new();
+#ifdef TUNNEL_SINK
+    pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
+#else
+    pa_tagstruct_putu32(t, PA_COMMAND_GET_RECORD_LATENCY);
+#endif
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, u->channel);
+
+    pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
+
+    pa_pstream_send_tagstruct(u->pstream, t);
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u, NULL);
+
+    u->ignore_latency_before = tag;
+    u->counter_delta = 0;
+}
+
+/* Called from main context */
+static void timeout_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(u);
+
+    request_latency(u);
+
+    pa_core_rttime_restart(u->core, e, pa_rtclock_now() + LATENCY_INTERVAL);
+}
+
+/* Called from main context */
+static void update_description(struct userdata *u) {
+    char *d;
+    char un[128], hn[128];
+    pa_tagstruct *t;
+
+    pa_assert(u);
+
+    if (!u->server_fqdn || !u->user_name || !u->device_description)
+        return;
+
+    d = pa_sprintf_malloc("%s on %s@%s", u->device_description, u->user_name, u->server_fqdn);
+
+#ifdef TUNNEL_SINK
+    pa_sink_set_description(u->sink, d);
+    pa_proplist_sets(u->sink->proplist, "tunnel.remote.user", u->user_name);
+    pa_proplist_sets(u->sink->proplist, "tunnel.remote.fqdn", u->server_fqdn);
+    pa_proplist_sets(u->sink->proplist, "tunnel.remote.description", u->device_description);
+#else
+    pa_source_set_description(u->source, d);
+    pa_proplist_sets(u->source->proplist, "tunnel.remote.user", u->user_name);
+    pa_proplist_sets(u->source->proplist, "tunnel.remote.fqdn", u->server_fqdn);
+    pa_proplist_sets(u->source->proplist, "tunnel.remote.description", u->device_description);
+#endif
+
+    pa_xfree(d);
+
+    d = pa_sprintf_malloc("%s for %s@%s", u->device_description,
+                          pa_get_user_name(un, sizeof(un)),
+                          pa_get_host_name(hn, sizeof(hn)));
+
+    t = pa_tagstruct_new();
+#ifdef TUNNEL_SINK
+    pa_tagstruct_putu32(t, PA_COMMAND_SET_PLAYBACK_STREAM_NAME);
+#else
+    pa_tagstruct_putu32(t, PA_COMMAND_SET_RECORD_STREAM_NAME);
+#endif
+    pa_tagstruct_putu32(t, u->ctag++);
+    pa_tagstruct_putu32(t, u->channel);
+    pa_tagstruct_puts(t, d);
+    pa_pstream_send_tagstruct(u->pstream, t);
+
+    pa_xfree(d);
+}
+
+/* Called from main context */
+static void server_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name;
+    uint32_t cookie;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get info.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_gets(t, &server_name) < 0 ||
+        pa_tagstruct_gets(t, &server_version) < 0 ||
+        pa_tagstruct_gets(t, &user_name) < 0 ||
+        pa_tagstruct_gets(t, &host_name) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_gets(t, &default_sink_name) < 0 ||
+        pa_tagstruct_gets(t, &default_source_name) < 0 ||
+        pa_tagstruct_getu32(t, &cookie) < 0 ||
+        (u->version >= 15 && pa_tagstruct_get_channel_map(t, &cm) < 0)) {
+
+        pa_log("Parse failure");
+        goto fail;
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_log("Packet too long");
+        goto fail;
+    }
+
+    pa_xfree(u->server_fqdn);
+    u->server_fqdn = pa_xstrdup(host_name);
+
+    pa_xfree(u->user_name);
+    u->user_name = pa_xstrdup(user_name);
+
+    update_description(u);
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module, true);
+}
+
+static int read_ports(struct userdata *u, pa_tagstruct *t) {
+    if (u->version >= 16) {
+        uint32_t n_ports;
+        const char *s;
+
+        if (pa_tagstruct_getu32(t, &n_ports)) {
+            pa_log("Parse failure");
+            return -PA_ERR_PROTOCOL;
+        }
+
+        for (uint32_t j = 0; j < n_ports; j++) {
+            uint32_t priority;
+
+            if (pa_tagstruct_gets(t, &s) < 0 || /* name */
+                pa_tagstruct_gets(t, &s) < 0 || /* description */
+                pa_tagstruct_getu32(t, &priority) < 0) {
+
+                pa_log("Parse failure");
+                return -PA_ERR_PROTOCOL;
+            }
+            if (u->version >= 24 && pa_tagstruct_getu32(t, &priority) < 0) { /* available */
+                pa_log("Parse failure");
+                return -PA_ERR_PROTOCOL;
+            }
+        }
+
+        if (pa_tagstruct_gets(t, &s) < 0) { /* active port */
+            pa_log("Parse failure");
+            return -PA_ERR_PROTOCOL;
+        }
+    }
+    return 0;
+}
+
+static int read_formats(struct userdata *u, pa_tagstruct *t) {
+    uint8_t n_formats;
+    pa_format_info *format;
+
+    if (pa_tagstruct_getu8(t, &n_formats) < 0) { /* no. of formats */
+        pa_log("Parse failure");
+        return -PA_ERR_PROTOCOL;
+    }
+
+    for (uint8_t j = 0; j < n_formats; j++) {
+        format = pa_format_info_new();
+        if (pa_tagstruct_get_format_info(t, format)) { /* format info */
+            pa_format_info_free(format);
+            pa_log("Parse failure");
+            return -PA_ERR_PROTOCOL;
+        }
+        pa_format_info_free(format);
+    }
+    return 0;
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from main context */
+static void sink_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t idx, owner_module, monitor_source, flags;
+    const char *name, *description, *monitor_source_name, *driver;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    pa_cvolume volume;
+    bool mute;
+    pa_usec_t latency;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get info.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &description) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+        pa_tagstruct_getu32(t, &owner_module) < 0 ||
+        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+        pa_tagstruct_get_boolean(t, &mute) < 0 ||
+        pa_tagstruct_getu32(t, &monitor_source) < 0 ||
+        pa_tagstruct_gets(t, &monitor_source_name) < 0 ||
+        pa_tagstruct_get_usec(t, &latency) < 0 ||
+        pa_tagstruct_gets(t, &driver) < 0 ||
+        pa_tagstruct_getu32(t, &flags) < 0) {
+
+        pa_log("Parse failure");
+        goto fail;
+    }
+
+    if (u->version >= 13) {
+        pa_usec_t configured_latency;
+
+        if (pa_tagstruct_get_proplist(t, NULL) < 0 ||
+            pa_tagstruct_get_usec(t, &configured_latency) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (u->version >= 15) {
+        pa_volume_t base_volume;
+        uint32_t state, n_volume_steps, card;
+
+        if (pa_tagstruct_get_volume(t, &base_volume) < 0 ||
+            pa_tagstruct_getu32(t, &state) < 0 ||
+            pa_tagstruct_getu32(t, &n_volume_steps) < 0 ||
+            pa_tagstruct_getu32(t, &card) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (read_ports(u, t) < 0)
+        goto fail;
+
+    if (u->version >= 21 && read_formats(u, t) < 0)
+        goto fail;
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_log("Packet too long");
+        goto fail;
+    }
+
+    if (!u->sink_name || !pa_streq(name, u->sink_name))
+        return;
+
+    pa_xfree(u->device_description);
+    u->device_description = pa_xstrdup(description);
+
+    update_description(u);
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main context */
+static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t idx, owner_module, client, sink;
+    pa_usec_t buffer_usec, sink_usec;
+    const char *name, *driver, *resample_method;
+    bool mute = false;
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    pa_cvolume volume;
+    bool b;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get info.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_getu32(t, &owner_module) < 0 ||
+        pa_tagstruct_getu32(t, &client) < 0 ||
+        pa_tagstruct_getu32(t, &sink) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &sample_spec) < 0 ||
+        pa_tagstruct_get_channel_map(t, &channel_map) < 0 ||
+        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+        pa_tagstruct_get_usec(t, &buffer_usec) < 0 ||
+        pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
+        pa_tagstruct_gets(t, &resample_method) < 0 ||
+        pa_tagstruct_gets(t, &driver) < 0) {
+
+        pa_log("Parse failure");
+        goto fail;
+    }
+
+    if (u->version >= 11) {
+        if (pa_tagstruct_get_boolean(t, &mute) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (u->version >= 13) {
+        if (pa_tagstruct_get_proplist(t, NULL) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (u->version >= 19) {
+        if (pa_tagstruct_get_boolean(t, &b) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (u->version >= 20) {
+        if (pa_tagstruct_get_boolean(t, &b) < 0 ||
+            pa_tagstruct_get_boolean(t, &b) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (u->version >= 21) {
+        pa_format_info *format = pa_format_info_new();
+
+        if (pa_tagstruct_get_format_info(t, format) < 0) {
+            pa_format_info_free(format);
+            pa_log("Parse failure");
+            goto fail;
+        }
+        pa_format_info_free(format);
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_log("Packet too long");
+        goto fail;
+    }
+
+    if (idx != u->device_index)
+        return;
+
+    pa_assert(u->sink);
+
+    if ((u->version < 11 || mute == u->sink->muted) &&
+        pa_cvolume_equal(&volume, &u->sink->real_volume))
+        return;
+
+    pa_sink_volume_changed(u->sink, &volume);
+
+    if (u->version >= 11)
+        pa_sink_mute_changed(u->sink, mute);
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module, true);
+}
+
+#else
+
+/* Called from main context */
+static void source_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t idx, owner_module, monitor_of_sink, flags;
+    const char *name, *description, *monitor_of_sink_name, *driver;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    pa_cvolume volume;
+    bool mute;
+    pa_usec_t latency, configured_latency;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get info.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &description) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+        pa_tagstruct_getu32(t, &owner_module) < 0 ||
+        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+        pa_tagstruct_get_boolean(t, &mute) < 0 ||
+        pa_tagstruct_getu32(t, &monitor_of_sink) < 0 ||
+        pa_tagstruct_gets(t, &monitor_of_sink_name) < 0 ||
+        pa_tagstruct_get_usec(t, &latency) < 0 ||
+        pa_tagstruct_gets(t, &driver) < 0 ||
+        pa_tagstruct_getu32(t, &flags) < 0) {
+
+        pa_log("Parse failure");
+        goto fail;
+    }
+
+    if (u->version >= 13) {
+        if (pa_tagstruct_get_proplist(t, NULL) < 0 ||
+            pa_tagstruct_get_usec(t, &configured_latency) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (u->version >= 15) {
+        pa_volume_t base_volume;
+        uint32_t state, n_volume_steps, card;
+
+        if (pa_tagstruct_get_volume(t, &base_volume) < 0 ||
+            pa_tagstruct_getu32(t, &state) < 0 ||
+            pa_tagstruct_getu32(t, &n_volume_steps) < 0 ||
+            pa_tagstruct_getu32(t, &card) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (read_ports(u, t) < 0)
+        goto fail;
+
+    if (u->version >= 22 && read_formats(u, t) < 0)
+        goto fail;
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_log("Packet too long");
+        goto fail;
+    }
+
+    if (!u->source_name || !pa_streq(name, u->source_name))
+        return;
+
+    pa_xfree(u->device_description);
+    u->device_description = pa_xstrdup(description);
+
+    update_description(u);
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module, true);
+}
+
+#endif
+
+/* Called from main context */
+static void request_info(struct userdata *u) {
+    pa_tagstruct *t;
+    uint32_t tag;
+    pa_assert(u);
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_GET_SERVER_INFO);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_pstream_send_tagstruct(u->pstream, t);
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, server_info_cb, u, NULL);
+
+#ifdef TUNNEL_SINK
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, u->device_index);
+    pa_pstream_send_tagstruct(u->pstream, t);
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_input_info_cb, u, NULL);
+
+    if (u->sink_name) {
+        t = pa_tagstruct_new();
+        pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
+        pa_tagstruct_putu32(t, tag = u->ctag++);
+        pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+        pa_tagstruct_puts(t, u->sink_name);
+        pa_pstream_send_tagstruct(u->pstream, t);
+        pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_info_cb, u, NULL);
+    }
+#else
+    if (u->source_name) {
+        t = pa_tagstruct_new();
+        pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
+        pa_tagstruct_putu32(t, tag = u->ctag++);
+        pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+        pa_tagstruct_puts(t, u->source_name);
+        pa_pstream_send_tagstruct(u->pstream, t);
+        pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, source_info_cb, u, NULL);
+    }
+#endif
+}
+
+/* Called from main context */
+static void command_subscribe_event(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    pa_subscription_event_type_t e;
+    uint32_t idx;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
+
+    if (pa_tagstruct_getu32(t, &e) < 0 ||
+        pa_tagstruct_getu32(t, &idx) < 0) {
+        pa_log("Invalid protocol reply");
+        pa_module_unload_request(u->module, true);
+        return;
+    }
+
+    if (e != (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+#ifdef TUNNEL_SINK
+        e != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        e != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE)
+#else
+        e != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)
+#endif
+        )
+        return;
+
+    request_info(u);
+}
+
+/* Called from main context */
+static void start_subscribe(struct userdata *u) {
+    pa_tagstruct *t;
+    pa_assert(u);
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
+    pa_tagstruct_putu32(t, u->ctag++);
+    pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SERVER|
+#ifdef TUNNEL_SINK
+                        PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SINK
+#else
+                        PA_SUBSCRIPTION_MASK_SOURCE
+#endif
+                        );
+
+    pa_pstream_send_tagstruct(u->pstream, t);
+}
+
+/* Called from main context */
+static void create_stream_callback(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+#ifdef TUNNEL_SINK
+    uint32_t bytes;
+#endif
+
+    pa_assert(pd);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to create stream.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_getu32(t, &u->channel) < 0 ||
+        pa_tagstruct_getu32(t, &u->device_index) < 0
+#ifdef TUNNEL_SINK
+        || pa_tagstruct_getu32(t, &bytes) < 0
+#endif
+        )
+        goto parse_error;
+
+    if (u->version >= 9) {
+#ifdef TUNNEL_SINK
+        if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
+            pa_tagstruct_getu32(t, &u->tlength) < 0 ||
+            pa_tagstruct_getu32(t, &u->prebuf) < 0 ||
+            pa_tagstruct_getu32(t, &u->minreq) < 0)
+            goto parse_error;
+#else
+        if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
+            pa_tagstruct_getu32(t, &u->fragsize) < 0)
+            goto parse_error;
+#endif
+    }
+
+    if (u->version >= 12) {
+        pa_sample_spec ss;
+        pa_channel_map cm;
+        uint32_t device_index;
+        const char *dn;
+        bool suspended;
+
+        if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+            pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+            pa_tagstruct_getu32(t, &device_index) < 0 ||
+            pa_tagstruct_gets(t, &dn) < 0 ||
+            pa_tagstruct_get_boolean(t, &suspended) < 0)
+            goto parse_error;
+
+#ifdef TUNNEL_SINK
+        pa_xfree(u->sink_name);
+        u->sink_name = pa_xstrdup(dn);
+#else
+        pa_xfree(u->source_name);
+        u->source_name = pa_xstrdup(dn);
+#endif
+    }
+
+    if (u->version >= 13) {
+        pa_usec_t usec;
+
+        if (pa_tagstruct_get_usec(t, &usec) < 0)
+            goto parse_error;
+
+/* #ifdef TUNNEL_SINK */
+/*         pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0); */
+/* #else */
+/*         pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0); */
+/* #endif */
+    }
+
+    if (u->version >= 21) {
+        pa_format_info *format = pa_format_info_new();
+
+        if (pa_tagstruct_get_format_info(t, format) < 0) {
+            pa_format_info_free(format);
+            goto parse_error;
+        }
+
+        pa_format_info_free(format);
+    }
+
+    if (!pa_tagstruct_eof(t))
+        goto parse_error;
+
+    start_subscribe(u);
+    request_info(u);
+
+    pa_assert(!u->time_event);
+    u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + LATENCY_INTERVAL, timeout_callback, u);
+
+    request_latency(u);
+
+    pa_log_debug("Stream created.");
+
+#ifdef TUNNEL_SINK
+    pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
+#endif
+
+    return;
+
+parse_error:
+    pa_log("Invalid reply. (Create stream)");
+
+fail:
+    pa_module_unload_request(u->module, true);
+
+}
+
+/* Called from main context */
+static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    pa_tagstruct *reply;
+    char name[256], un[128], hn[128];
+    pa_cvolume volume;
+
+    pa_assert(pd);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    if (command != PA_COMMAND_REPLY ||
+        pa_tagstruct_getu32(t, &u->version) < 0 ||
+        !pa_tagstruct_eof(t)) {
+
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to authenticate");
+        else
+            pa_log("Protocol error.");
+
+        goto fail;
+    }
+
+    /* Minimum supported protocol version */
+    if (u->version < 8) {
+        pa_log("Incompatible protocol version");
+        goto fail;
+    }
+
+    /* Starting with protocol version 13 the MSB of the version tag
+    reflects if shm is enabled for this connection or not. We don't
+    support SHM here at all, so we just ignore this. */
+
+    if (u->version >= 13)
+        u->version &= 0x7FFFFFFFU;
+
+    pa_log_debug("Protocol version: remote %u, local %u", u->version, PA_PROTOCOL_VERSION);
+
+#ifdef TUNNEL_SINK
+    pa_proplist_setf(u->sink->proplist, "tunnel.remote_version", "%u", u->version);
+    pa_sink_update_proplist(u->sink, 0, NULL);
+
+    pa_snprintf(name, sizeof(name), "%s for %s@%s",
+                u->sink_name,
+                pa_get_user_name(un, sizeof(un)),
+                pa_get_host_name(hn, sizeof(hn)));
+#else
+    pa_proplist_setf(u->source->proplist, "tunnel.remote_version", "%u", u->version);
+    pa_source_update_proplist(u->source, 0, NULL);
+
+    pa_snprintf(name, sizeof(name), "%s for %s@%s",
+                u->source_name,
+                pa_get_user_name(un, sizeof(un)),
+                pa_get_host_name(hn, sizeof(hn)));
+#endif
+
+    reply = pa_tagstruct_new();
+    pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
+    pa_tagstruct_putu32(reply, u->ctag++);
+
+    if (u->version >= 13) {
+        pa_proplist *pl;
+        pl = pa_proplist_new();
+        pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");
+        pa_proplist_sets(pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
+        pa_init_proplist(pl);
+        pa_tagstruct_put_proplist(reply, pl);
+        pa_proplist_free(pl);
+    } else
+        pa_tagstruct_puts(reply, "PulseAudio");
+
+    pa_pstream_send_tagstruct(u->pstream, reply);
+    /* We ignore the server's reply here */
+
+    reply = pa_tagstruct_new();
+
+    if (u->version < 13)
+        /* Only for older PA versions we need to fill in the maxlength */
+        u->maxlength = 4*1024*1024;
+
+#ifdef TUNNEL_SINK
+    u->tlength = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_TLENGTH_MSEC, &u->sink->sample_spec);
+    u->minreq = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_MINREQ_MSEC, &u->sink->sample_spec);
+    u->prebuf = u->tlength;
+#else
+    u->fragsize = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_FRAGSIZE_MSEC, &u->source->sample_spec);
+#endif
+
+#ifdef TUNNEL_SINK
+    pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM);
+    pa_tagstruct_putu32(reply, tag = u->ctag++);
+
+    if (u->version < 13)
+        pa_tagstruct_puts(reply, name);
+
+    pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec);
+    pa_tagstruct_put_channel_map(reply, &u->sink->channel_map);
+    pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
+    pa_tagstruct_puts(reply, u->sink_name);
+    pa_tagstruct_putu32(reply, u->maxlength);
+    pa_tagstruct_put_boolean(reply, !PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
+    pa_tagstruct_putu32(reply, u->tlength);
+    pa_tagstruct_putu32(reply, u->prebuf);
+    pa_tagstruct_putu32(reply, u->minreq);
+    pa_tagstruct_putu32(reply, 0);
+    pa_cvolume_reset(&volume, u->sink->sample_spec.channels);
+    pa_tagstruct_put_cvolume(reply, &volume);
+#else
+    pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM);
+    pa_tagstruct_putu32(reply, tag = u->ctag++);
+
+    if (u->version < 13)
+        pa_tagstruct_puts(reply, name);
+
+    pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec);
+    pa_tagstruct_put_channel_map(reply, &u->source->channel_map);
+    pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
+    pa_tagstruct_puts(reply, u->source_name);
+    pa_tagstruct_putu32(reply, u->maxlength);
+    pa_tagstruct_put_boolean(reply, !PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)));
+    pa_tagstruct_putu32(reply, u->fragsize);
+#endif
+
+    if (u->version >= 12) {
+        pa_tagstruct_put_boolean(reply, false); /* no_remap */
+        pa_tagstruct_put_boolean(reply, false); /* no_remix */
+        pa_tagstruct_put_boolean(reply, false); /* fix_format */
+        pa_tagstruct_put_boolean(reply, false); /* fix_rate */
+        pa_tagstruct_put_boolean(reply, false); /* fix_channels */
+        pa_tagstruct_put_boolean(reply, true); /* no_move */
+        pa_tagstruct_put_boolean(reply, false); /* variable_rate */
+    }
+
+    if (u->version >= 13) {
+        pa_proplist *pl;
+
+        pa_tagstruct_put_boolean(reply, false); /* start muted/peak detect*/
+        pa_tagstruct_put_boolean(reply, true); /* adjust_latency */
+
+        pl = pa_proplist_new();
+        pa_proplist_sets(pl, PA_PROP_MEDIA_NAME, name);
+        pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "abstract");
+        pa_tagstruct_put_proplist(reply, pl);
+        pa_proplist_free(pl);
+
+#ifndef TUNNEL_SINK
+        pa_tagstruct_putu32(reply, PA_INVALID_INDEX); /* direct on input */
+#endif
+    }
+
+    if (u->version >= 14) {
+#ifdef TUNNEL_SINK
+        pa_tagstruct_put_boolean(reply, false); /* volume_set */
+#endif
+        pa_tagstruct_put_boolean(reply, true); /* early rquests */
+    }
+
+    if (u->version >= 15) {
+#ifdef TUNNEL_SINK
+        pa_tagstruct_put_boolean(reply, false); /* muted_set */
+#endif
+        pa_tagstruct_put_boolean(reply, false); /* don't inhibit auto suspend */
+        pa_tagstruct_put_boolean(reply, false); /* fail on suspend */
+    }
+
+#ifdef TUNNEL_SINK
+    if (u->version >= 17)
+        pa_tagstruct_put_boolean(reply, false); /* relative volume */
+
+    if (u->version >= 18)
+        pa_tagstruct_put_boolean(reply, false); /* passthrough stream */
+#endif
+
+#ifdef TUNNEL_SINK
+    if (u->version >= 21) {
+        /* We're not using the extended API, so n_formats = 0 and that's that */
+        pa_tagstruct_putu8(reply, 0);
+    }
+#else
+    if (u->version >= 22) {
+        /* We're not using the extended API, so n_formats = 0 and that's that */
+        pa_tagstruct_putu8(reply, 0);
+        pa_cvolume_reset(&volume, u->source->sample_spec.channels);
+        pa_tagstruct_put_cvolume(reply, &volume);
+        pa_tagstruct_put_boolean(reply, false); /* muted */
+        pa_tagstruct_put_boolean(reply, false); /* volume_set */
+        pa_tagstruct_put_boolean(reply, false); /* muted_set */
+        pa_tagstruct_put_boolean(reply, false); /* relative volume */
+        pa_tagstruct_put_boolean(reply, false); /* passthrough stream */
+    }
+#endif
+
+    pa_pstream_send_tagstruct(u->pstream, reply);
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
+
+    pa_log_debug("Connection authenticated, creating stream ...");
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main context */
+static void pstream_die_callback(pa_pstream *p, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(p);
+    pa_assert(u);
+
+    pa_log_warn("Stream died.");
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main context */
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(p);
+    pa_assert(packet);
+    pa_assert(u);
+
+    if (pa_pdispatch_run(u->pdispatch, packet, ancil_data, u) < 0) {
+        pa_log("Invalid packet");
+        pa_module_unload_request(u->module, true);
+        return;
+    }
+}
+
+#ifndef TUNNEL_SINK
+/* Called from main context */
+static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(p);
+    pa_assert(chunk);
+    pa_assert(u);
+
+    if (channel != u->channel) {
+        pa_log("Received memory block on bad channel.");
+        pa_module_unload_request(u->module, true);
+        return;
+    }
+
+    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, PA_UINT_TO_PTR(seek), offset, chunk);
+
+    u->counter_delta += (int64_t) chunk->length;
+}
+#endif
+
+/* Called from main context */
+static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
+    struct userdata *u = userdata;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(sc);
+    pa_assert(u);
+    pa_assert(u->client == sc);
+
+    pa_socket_client_unref(u->client);
+    u->client = NULL;
+
+    if (!io) {
+        pa_log("Connection failed: %s", pa_cstrerror(errno));
+        pa_module_unload_request(u->module, true);
+        return;
+    }
+
+    u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->mempool);
+    u->pdispatch = pa_pdispatch_new(u->core->mainloop, true, command_table, PA_COMMAND_MAX);
+
+    pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u);
+    pa_pstream_set_receive_packet_callback(u->pstream, pstream_packet_callback, u);
+#ifndef TUNNEL_SINK
+    pa_pstream_set_receive_memblock_callback(u->pstream, pstream_memblock_callback, u);
+#endif
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
+
+    pa_tagstruct_put_arbitrary(t, pa_auth_cookie_read(u->auth_cookie, PA_NATIVE_COOKIE_LENGTH), PA_NATIVE_COOKIE_LENGTH);
+
+#ifdef HAVE_CREDS
+{
+    pa_creds ucred;
+
+    if (pa_iochannel_creds_supported(io))
+        pa_iochannel_creds_enable(io);
+
+    ucred.uid = getuid();
+    ucred.gid = getgid();
+
+    pa_pstream_send_tagstruct_with_creds(u->pstream, t, &ucred);
+}
+#else
+    pa_pstream_send_tagstruct(u->pstream, t);
+#endif
+
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u, NULL);
+
+    pa_log_debug("Connection established, authenticating ...");
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from main context */
+static void sink_set_volume(pa_sink *sink) {
+    struct userdata *u;
+    pa_tagstruct *t;
+
+    pa_assert(sink);
+    u = sink->userdata;
+    pa_assert(u);
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
+    pa_tagstruct_putu32(t, u->ctag++);
+    pa_tagstruct_putu32(t, u->device_index);
+    pa_tagstruct_put_cvolume(t, &sink->real_volume);
+    pa_pstream_send_tagstruct(u->pstream, t);
+}
+
+/* Called from main context */
+static void sink_set_mute(pa_sink *sink) {
+    struct userdata *u;
+    pa_tagstruct *t;
+
+    pa_assert(sink);
+    u = sink->userdata;
+    pa_assert(u);
+
+    if (u->version < 11)
+        return;
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
+    pa_tagstruct_putu32(t, u->ctag++);
+    pa_tagstruct_putu32(t, u->device_index);
+    pa_tagstruct_put_boolean(t, sink->muted);
+    pa_pstream_send_tagstruct(u->pstream, t);
+}
+
+#endif
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u = NULL;
+    char *server = NULL;
+    pa_strlist *server_list = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    char *dn = NULL;
+#ifdef TUNNEL_SINK
+    pa_sink_new_data data;
+#else
+    pa_source_new_data data;
+#endif
+    bool automatic;
+#ifdef HAVE_X11
+    xcb_connection_t *xcb = NULL;
+#endif
+    const char *cookie_path;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->client = NULL;
+    u->pdispatch = NULL;
+    u->pstream = NULL;
+    u->server_name = NULL;
+#ifdef TUNNEL_SINK
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));;
+    u->sink = NULL;
+    u->requested_bytes = 0;
+#else
+    u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
+    u->source = NULL;
+#endif
+    u->smoother = pa_smoother_new(
+            PA_USEC_PER_SEC,
+            PA_USEC_PER_SEC*2,
+            true,
+            true,
+            10,
+            pa_rtclock_now(),
+            false);
+    u->ctag = 1;
+    u->device_index = u->channel = PA_INVALID_INDEX;
+    u->time_event = NULL;
+    u->ignore_latency_before = 0;
+    u->transport_usec = u->thread_transport_usec = 0;
+    u->remote_suspended = u->remote_corked = false;
+    u->counter = u->counter_delta = 0;
+
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "auto", &automatic) < 0) {
+        pa_log("Failed to parse argument \"auto\".");
+        goto fail;
+    }
+
+    cookie_path = pa_modargs_get_value(ma, "cookie", NULL);
+    server = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL));
+
+    if (automatic) {
+#ifdef HAVE_X11
+        /* Need an X11 connection to get root properties */
+        if (getenv("DISPLAY") != NULL) {
+            if (!(xcb = xcb_connect(getenv("DISPLAY"), NULL)))
+                pa_log("xcb_connect() failed");
+            else {
+                if (xcb_connection_has_error(xcb)) {
+                    pa_log("xcb_connection_has_error() returned true");
+                    xcb_disconnect(xcb);
+                    xcb = NULL;
+                }
+            }
+        }
+#endif
+
+        /* Figure out the cookie the same way a normal client would */
+        if (!cookie_path)
+            cookie_path = getenv(ENV_COOKIE_FILE);
+
+#ifdef HAVE_X11
+        if (!cookie_path && xcb) {
+            char t[1024];
+            if (pa_x11_get_prop(xcb, 0, "PULSE_COOKIE", t, sizeof(t))) {
+                uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
+
+                if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie))
+                    pa_log("Failed to parse cookie data");
+                else {
+                    if (!(u->auth_cookie = pa_auth_cookie_create(u->core, cookie, sizeof(cookie))))
+                        goto fail;
+                }
+            }
+        }
+#endif
+
+        /* Same thing for the server name */
+        if (!server)
+            server = pa_xstrdup(getenv(ENV_DEFAULT_SERVER));
+
+#ifdef HAVE_X11
+        if (!server && xcb) {
+            char t[1024];
+            if (pa_x11_get_prop(xcb, 0, "PULSE_SERVER", t, sizeof(t)))
+                server = pa_xstrdup(t);
+        }
+#endif
+
+        /* Also determine the default sink/source on the other server */
+#ifdef TUNNEL_SINK
+        if (!u->sink_name)
+            u->sink_name = pa_xstrdup(getenv(ENV_DEFAULT_SINK));
+
+#ifdef HAVE_X11
+        if (!u->sink_name && xcb) {
+            char t[1024];
+            if (pa_x11_get_prop(xcb, 0, "PULSE_SINK", t, sizeof(t)))
+                u->sink_name = pa_xstrdup(t);
+        }
+#endif
+#else
+        if (!u->source_name)
+            u->source_name = pa_xstrdup(getenv(ENV_DEFAULT_SOURCE));
+
+#ifdef HAVE_X11
+        if (!u->source_name && xcb) {
+            char t[1024];
+            if (pa_x11_get_prop(xcb, 0, "PULSE_SOURCE", t, sizeof(t)))
+                u->source_name = pa_xstrdup(t);
+        }
+#endif
+#endif
+    }
+
+    if (!cookie_path && !u->auth_cookie)
+        cookie_path = PA_NATIVE_COOKIE_FILE;
+
+    if (cookie_path) {
+        if (!(u->auth_cookie = pa_auth_cookie_get(u->core, cookie_path, true, PA_NATIVE_COOKIE_LENGTH)))
+            goto fail;
+    }
+
+    if (server) {
+        if (!(server_list = pa_strlist_parse(server))) {
+            pa_log("Invalid server specified.");
+            goto fail;
+        }
+    } else {
+        char *ufn;
+
+        if (!automatic) {
+            pa_log("No server specified.");
+            goto fail;
+        }
+
+        pa_log("No server address found. Attempting default local sockets.");
+
+        /* The system wide instance via PF_LOCAL */
+        server_list = pa_strlist_prepend(server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
+
+        /* The user instance via PF_LOCAL */
+        if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
+            server_list = pa_strlist_prepend(server_list, ufn);
+            pa_xfree(ufn);
+        }
+    }
+
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification");
+        goto fail;
+    }
+
+    for (;;) {
+        server_list = pa_strlist_pop(server_list, &u->server_name);
+
+        if (!u->server_name) {
+            pa_log("Failed to connect to server '%s'", server);
+            goto fail;
+        }
+
+        pa_log_debug("Trying to connect to %s...", u->server_name);
+
+        if (!(u->client = pa_socket_client_new_string(m->core->mainloop, true, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
+            pa_xfree(u->server_name);
+            u->server_name = NULL;
+            continue;
+        }
+
+        break;
+     }
+
+    pa_socket_client_set_callback(u->client, on_connection, u);
+
+#ifdef TUNNEL_SINK
+
+    if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+        dn = pa_sprintf_malloc("tunnel-sink.%s", u->server_name);
+
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    data.namereg_fail = false;
+    pa_sink_new_data_set_name(&data, dn);
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->sink_name), u->sink_name ? " on " : "", u->server_name);
+    pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
+    if (u->sink_name)
+        pa_proplist_sets(data.proplist, "tunnel.remote.sink", u->sink_name);
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&data);
+        goto fail;
+    }
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->userdata = u;
+    u->sink->set_state = sink_set_state;
+    pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
+    pa_sink_set_set_mute_callback(u->sink, sink_set_mute);
+
+    u->sink->refresh_volume = u->sink->refresh_muted = false;
+
+/*     pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0); */
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+#else
+
+    if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
+        dn = pa_sprintf_malloc("tunnel-source.%s", u->server_name);
+
+    pa_source_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    data.namereg_fail = false;
+    pa_source_new_data_set_name(&data, dn);
+    pa_source_new_data_set_sample_spec(&data, &ss);
+    pa_source_new_data_set_channel_map(&data, &map);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->source_name), u->source_name ? " on " : "", u->server_name);
+    pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
+    if (u->source_name)
+        pa_proplist_sets(data.proplist, "tunnel.remote.source", u->source_name);
+
+    if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_source_new_data_done(&data);
+        goto fail;
+    }
+
+    u->source = pa_source_new(m->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY);
+    pa_source_new_data_done(&data);
+
+    if (!u->source) {
+        pa_log("Failed to create source.");
+        goto fail;
+    }
+
+    u->source->parent.process_msg = source_process_msg;
+    u->source->set_state = source_set_state;
+    u->source->userdata = u;
+
+/*     pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0); */
+
+    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+
+    u->mcalign = pa_mcalign_new(pa_frame_size(&u->source->sample_spec));
+#endif
+
+    u->time_event = NULL;
+
+    u->maxlength = (uint32_t) -1;
+#ifdef TUNNEL_SINK
+    u->tlength = u->minreq = u->prebuf = (uint32_t) -1;
+#else
+    u->fragsize = (uint32_t) -1;
+#endif
+
+    if (!(u->thread = pa_thread_new("module-tunnel", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+#ifdef TUNNEL_SINK
+    pa_sink_put(u->sink);
+#else
+    pa_source_put(u->source);
+#endif
+
+    pa_xfree(dn);
+
+    if (server)
+        pa_xfree(server);
+
+    if (server_list)
+        pa_strlist_free(server_list);
+
+#ifdef HAVE_X11
+    if (xcb)
+        xcb_disconnect(xcb);
+#endif
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (server)
+        pa_xfree(server);
+
+    if (server_list)
+        pa_strlist_free(server_list);
+
+#ifdef HAVE_X11
+    if (xcb)
+        xcb_disconnect(xcb);
+#endif
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa_xfree(dn);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+#ifdef TUNNEL_SINK
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+#else
+    if (u->source)
+        pa_source_unlink(u->source);
+#endif
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+#ifdef TUNNEL_SINK
+    if (u->sink)
+        pa_sink_unref(u->sink);
+#else
+    if (u->source)
+        pa_source_unref(u->source);
+#endif
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->pstream) {
+        pa_pstream_unlink(u->pstream);
+        pa_pstream_unref(u->pstream);
+    }
+
+    if (u->pdispatch)
+        pa_pdispatch_unref(u->pdispatch);
+
+    if (u->client)
+        pa_socket_client_unref(u->client);
+
+    if (u->auth_cookie)
+        pa_auth_cookie_unref(u->auth_cookie);
+
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+
+    if (u->time_event)
+        u->core->mainloop->time_free(u->time_event);
+
+#ifndef TUNNEL_SINK
+    if (u->mcalign)
+        pa_mcalign_free(u->mcalign);
+#endif
+
+#ifdef TUNNEL_SINK
+    pa_xfree(u->sink_name);
+#else
+    pa_xfree(u->source_name);
+#endif
+    pa_xfree(u->server_name);
+
+    pa_xfree(u->device_description);
+    pa_xfree(u->server_fqdn);
+    pa_xfree(u->user_name);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c
new file mode 100644 (file)
index 0000000..3d7064f
--- /dev/null
@@ -0,0 +1,841 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/inotify.h>
+#include <libudev.h>
+
+#include <pulse/timeval.h>
+
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/ratelimit.h>
+#include <pulsecore/strbuf.h>
+
+#include "module-udev-detect-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+        "tsched=<enable system timer based scheduling mode?> "
+        "tsched_buffer_size=<buffer size when using timer based scheduling> "
+        "fixed_latency_range=<disable latency range changes on underrun?> "
+        "ignore_dB=<ignore dB information from the device?> "
+        "deferred_volume=<syncronize sw and hw volume changes in IO-thread?> "
+        "use_ucm=<use ALSA UCM for card configuration?>");
+
+struct device {
+    char *path;
+    bool need_verify;
+    char *card_name;
+    char *args;
+    uint32_t module;
+    pa_ratelimit ratelimit;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_hashmap *devices;
+
+    bool use_tsched:1;
+    bool tsched_buffer_size_valid:1;
+    bool fixed_latency_range:1;
+    bool ignore_dB:1;
+    bool deferred_volume:1;
+    bool use_ucm:1;
+
+    uint32_t tsched_buffer_size;
+
+    struct udev* udev;
+    struct udev_monitor *monitor;
+    pa_io_event *udev_io;
+
+    int inotify_fd;
+    pa_io_event *inotify_io;
+};
+
+static const char* const valid_modargs[] = {
+    "tsched",
+    "tsched_buffer_size",
+    "fixed_latency_range",
+    "ignore_dB",
+    "deferred_volume",
+    "use_ucm",
+    NULL
+};
+
+static int setup_inotify(struct userdata *u);
+
+static void device_free(struct device *d) {
+    pa_assert(d);
+
+    pa_xfree(d->path);
+    pa_xfree(d->card_name);
+    pa_xfree(d->args);
+    pa_xfree(d);
+}
+
+static const char *path_get_card_id(const char *path) {
+    const char *e;
+
+    if (!path)
+        return NULL;
+
+    if (!(e = strrchr(path, '/')))
+        return NULL;
+
+    if (!pa_startswith(e, "/card"))
+        return NULL;
+
+    return e + 5;
+}
+
+static char *card_get_sysattr(const char *card_idx, const char *name) {
+    struct udev *udev;
+    struct udev_device *card = NULL;
+    char *t, *r = NULL;
+    const char *v;
+
+    pa_assert(card_idx);
+    pa_assert(name);
+
+    if (!(udev = udev_new())) {
+        pa_log_error("Failed to allocate udev context.");
+        goto finish;
+    }
+
+    t = pa_sprintf_malloc("/sys/class/sound/card%s", card_idx);
+    card = udev_device_new_from_syspath(udev, t);
+    pa_xfree(t);
+
+    if (!card) {
+        pa_log_error("Failed to get card object.");
+        goto finish;
+    }
+
+    if ((v = udev_device_get_sysattr_value(card, name)) && *v)
+        r = pa_xstrdup(v);
+
+finish:
+
+    if (card)
+        udev_device_unref(card);
+
+    if (udev)
+        udev_unref(udev);
+
+    return r;
+}
+
+static bool pcm_is_modem(const char *card_idx, const char *pcm) {
+    char *sysfs_path, *pcm_class;
+    bool is_modem;
+
+    pa_assert(card_idx);
+    pa_assert(pcm);
+
+    /* Check /sys/class/sound/card.../pcmC...../pcm_class. An HDA
+     * modem can be used simultaneously with generic
+     * playback/record. */
+
+    sysfs_path = pa_sprintf_malloc("pcmC%sD%s/pcm_class", card_idx, pcm);
+    pcm_class = card_get_sysattr(card_idx, sysfs_path);
+    is_modem = pcm_class && pa_streq(pcm_class, "modem");
+    pa_xfree(pcm_class);
+    pa_xfree(sysfs_path);
+
+    return is_modem;
+}
+
+static bool is_card_busy(const char *id) {
+    char *card_path = NULL, *pcm_path = NULL, *sub_status = NULL;
+    DIR *card_dir = NULL, *pcm_dir = NULL;
+    FILE *status_file = NULL;
+    struct dirent *de;
+    bool busy = false;
+
+    pa_assert(id);
+
+    /* This simply uses /proc/asound/card.../pcm.../sub.../status to
+     * check whether there is still a process using this audio device. */
+
+    card_path = pa_sprintf_malloc("/proc/asound/card%s", id);
+
+    if (!(card_dir = opendir(card_path))) {
+        pa_log_warn("Failed to open %s: %s", card_path, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    for (;;) {
+        errno = 0;
+        de = readdir(card_dir);
+        if (!de && errno) {
+            pa_log_warn("readdir() failed: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+        if (!de)
+            break;
+
+        if (!pa_startswith(de->d_name, "pcm"))
+            continue;
+
+        if (pcm_is_modem(id, de->d_name + 3))
+            continue;
+
+        pa_xfree(pcm_path);
+        pcm_path = pa_sprintf_malloc("%s/%s", card_path, de->d_name);
+
+        if (pcm_dir)
+            closedir(pcm_dir);
+
+        if (!(pcm_dir = opendir(pcm_path))) {
+            pa_log_warn("Failed to open %s: %s", pcm_path, pa_cstrerror(errno));
+            continue;
+        }
+
+        for (;;) {
+            char line[32];
+
+            errno = 0;
+            de = readdir(pcm_dir);
+            if (!de && errno) {
+                pa_log_warn("readdir() failed: %s", pa_cstrerror(errno));
+                goto fail;
+            }
+
+            if (!de)
+                break;
+
+            if (!pa_startswith(de->d_name, "sub"))
+                continue;
+
+            pa_xfree(sub_status);
+            sub_status = pa_sprintf_malloc("%s/%s/status", pcm_path, de->d_name);
+
+            if (status_file)
+                fclose(status_file);
+
+            if (!(status_file = pa_fopen_cloexec(sub_status, "r"))) {
+                pa_log_warn("Failed to open %s: %s", sub_status, pa_cstrerror(errno));
+                continue;
+            }
+
+            if (!(fgets(line, sizeof(line)-1, status_file))) {
+                pa_log_warn("Failed to read from %s: %s", sub_status, pa_cstrerror(errno));
+                continue;
+            }
+
+            if (!pa_streq(line, "closed\n")) {
+                busy = true;
+                break;
+            }
+        }
+    }
+
+fail:
+
+    pa_xfree(card_path);
+    pa_xfree(pcm_path);
+    pa_xfree(sub_status);
+
+    if (card_dir)
+        closedir(card_dir);
+
+    if (pcm_dir)
+        closedir(pcm_dir);
+
+    if (status_file)
+        fclose(status_file);
+
+    return busy;
+}
+
+static void verify_access(struct userdata *u, struct device *d) {
+    char *cd;
+    pa_card *card;
+    bool accessible;
+
+    pa_assert(u);
+    pa_assert(d);
+
+    cd = pa_sprintf_malloc("/dev/snd/controlC%s", path_get_card_id(d->path));
+    accessible = access(cd, R_OK|W_OK) >= 0;
+    pa_log_debug("%s is accessible: %s", cd, pa_yes_no(accessible));
+
+    pa_xfree(cd);
+
+    if (d->module == PA_INVALID_INDEX) {
+
+        /* If we are not loaded, try to load */
+
+        if (accessible) {
+            pa_module *m;
+            bool busy;
+
+            /* Check if any of the PCM devices that belong to this
+             * card are currently busy. If they are, don't try to load
+             * right now, to make sure the probing phase can
+             * successfully complete. When the current user of the
+             * device closes it we will get another notification via
+             * inotify and can then recheck. */
+
+            busy = is_card_busy(path_get_card_id(d->path));
+            pa_log_debug("%s is busy: %s", d->path, pa_yes_no(busy));
+
+            if (!busy) {
+
+                /* So, why do we rate limit here? It's certainly ugly,
+                 * but there seems to be no other way. Problem is
+                 * this: if we are unable to configure/probe an audio
+                 * device after opening it we will close it again and
+                 * the module initialization will fail. This will then
+                 * cause an inotify event on the device node which
+                 * will be forwarded to us. We then try to reopen the
+                 * audio device again, practically entering a busy
+                 * loop.
+                 *
+                 * A clean fix would be if we would be able to ignore
+                 * our own inotify close events. However, inotify
+                 * lacks such functionality. Also, during probing of
+                 * the device we cannot really distinguish between
+                 * other processes causing EBUSY or ourselves, which
+                 * means we have no way to figure out if the probing
+                 * during opening was canceled by a "try again"
+                 * failure or a "fatal" failure. */
+
+                if (pa_ratelimit_test(&d->ratelimit, PA_LOG_DEBUG)) {
+                    pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args);
+                    m = pa_module_load(u->core, "module-alsa-card", d->args);
+
+                    if (m) {
+                        d->module = m->index;
+                        pa_log_info("Card %s (%s) module loaded.", d->path, d->card_name);
+                    } else
+                        pa_log_info("Card %s (%s) failed to load module.", d->path, d->card_name);
+                } else
+                    pa_log_warn("Tried to configure %s (%s) more often than %u times in %llus",
+                                d->path,
+                                d->card_name,
+                                d->ratelimit.burst,
+                                (long long unsigned) (d->ratelimit.interval / PA_USEC_PER_SEC));
+            }
+        }
+
+    } else {
+
+        /* If we are already loaded update suspend status with
+         * accessible boolean */
+
+        if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) {
+            pa_log_debug("%s all sinks and sources of card %s.", accessible ? "Resuming" : "Suspending", d->card_name);
+            pa_card_suspend(card, !accessible, PA_SUSPEND_SESSION);
+        }
+    }
+}
+
+static void card_changed(struct userdata *u, struct udev_device *dev) {
+    struct device *d;
+    const char *path;
+    const char *t;
+    char *n;
+    pa_strbuf *args_buf;
+
+    pa_assert(u);
+    pa_assert(dev);
+
+    /* Maybe /dev/snd is now available? */
+    setup_inotify(u);
+
+    path = udev_device_get_devpath(dev);
+
+    if ((d = pa_hashmap_get(u->devices, path))) {
+        verify_access(u, d);
+        return;
+    }
+
+    d = pa_xnew0(struct device, 1);
+    d->path = pa_xstrdup(path);
+    d->module = PA_INVALID_INDEX;
+    PA_INIT_RATELIMIT(d->ratelimit, 10*PA_USEC_PER_SEC, 5);
+
+    if (!(t = udev_device_get_property_value(dev, "PULSE_NAME")))
+        if (!(t = udev_device_get_property_value(dev, "ID_ID")))
+            if (!(t = udev_device_get_property_value(dev, "ID_PATH")))
+                t = path_get_card_id(path);
+
+    n = pa_namereg_make_valid_name(t);
+    d->card_name = pa_sprintf_malloc("alsa_card.%s", n);
+    args_buf = pa_strbuf_new();
+    pa_strbuf_printf(args_buf,
+                     "device_id=\"%s\" "
+                     "name=\"%s\" "
+                     "card_name=\"%s\" "
+                     "namereg_fail=false "
+                     "tsched=%s "
+                     "fixed_latency_range=%s "
+                     "ignore_dB=%s "
+                     "deferred_volume=%s "
+                     "use_ucm=%s "
+                     "card_properties=\"module-udev-detect.discovered=1\"",
+                     path_get_card_id(path),
+                     n,
+                     d->card_name,
+                     pa_yes_no(u->use_tsched),
+                     pa_yes_no(u->fixed_latency_range),
+                     pa_yes_no(u->ignore_dB),
+                     pa_yes_no(u->deferred_volume),
+                     pa_yes_no(u->use_ucm));
+    pa_xfree(n);
+
+    if (u->tsched_buffer_size_valid)
+        pa_strbuf_printf(args_buf, " tsched_buffer_size=%" PRIu32, u->tsched_buffer_size);
+
+    d->args = pa_strbuf_to_string_free(args_buf);
+
+    pa_hashmap_put(u->devices, d->path, d);
+
+    verify_access(u, d);
+}
+
+static void remove_card(struct userdata *u, struct udev_device *dev) {
+    struct device *d;
+
+    pa_assert(u);
+    pa_assert(dev);
+
+    if (!(d = pa_hashmap_remove(u->devices, udev_device_get_devpath(dev))))
+        return;
+
+    pa_log_info("Card %s removed.", d->path);
+
+    if (d->module != PA_INVALID_INDEX)
+        pa_module_unload_request_by_index(u->core, d->module, true);
+
+    device_free(d);
+}
+
+static void process_device(struct userdata *u, struct udev_device *dev) {
+    const char *action, *ff;
+
+    pa_assert(u);
+    pa_assert(dev);
+
+    if (udev_device_get_property_value(dev, "PULSE_IGNORE")) {
+        pa_log_debug("Ignoring %s, because marked so.", udev_device_get_devpath(dev));
+        return;
+    }
+
+    if ((ff = udev_device_get_property_value(dev, "SOUND_CLASS")) &&
+        pa_streq(ff, "modem")) {
+        pa_log_debug("Ignoring %s, because it is a modem.", udev_device_get_devpath(dev));
+        return;
+    }
+
+    action = udev_device_get_action(dev);
+
+    if (action && pa_streq(action, "remove"))
+        remove_card(u, dev);
+    else if ((!action || pa_streq(action, "change")) && udev_device_get_property_value(dev, "SOUND_INITIALIZED"))
+        card_changed(u, dev);
+
+    /* For an explanation why we don't look for 'add' events here
+     * have a look into /lib/udev/rules.d/78-sound-card.rules! */
+}
+
+static void process_path(struct userdata *u, const char *path) {
+    struct udev_device *dev;
+
+    if (!path_get_card_id(path))
+        return;
+
+    if (!(dev = udev_device_new_from_syspath(u->udev, path))) {
+        pa_log("Failed to get udev device object from udev.");
+        return;
+    }
+
+    process_device(u, dev);
+    udev_device_unref(dev);
+}
+
+static void monitor_cb(
+        pa_mainloop_api*a,
+        pa_io_event* e,
+        int fd,
+        pa_io_event_flags_t events,
+        void *userdata) {
+
+    struct userdata *u = userdata;
+    struct udev_device *dev;
+
+    pa_assert(a);
+
+    if (!(dev = udev_monitor_receive_device(u->monitor))) {
+        pa_log("Failed to get udev device object from monitor.");
+        goto fail;
+    }
+
+    if (!path_get_card_id(udev_device_get_devpath(dev))) {
+        udev_device_unref(dev);
+        return;
+    }
+
+    process_device(u, dev);
+    udev_device_unref(dev);
+    return;
+
+fail:
+    a->io_free(u->udev_io);
+    u->udev_io = NULL;
+}
+
+static bool pcm_node_belongs_to_device(
+        struct device *d,
+        const char *node) {
+
+    char *cd;
+    bool b;
+
+    cd = pa_sprintf_malloc("pcmC%sD", path_get_card_id(d->path));
+    b = pa_startswith(node, cd);
+    pa_xfree(cd);
+
+    return b;
+}
+
+static bool control_node_belongs_to_device(
+        struct device *d,
+        const char *node) {
+
+    char *cd;
+    bool b;
+
+    cd = pa_sprintf_malloc("controlC%s", path_get_card_id(d->path));
+    b = pa_streq(node, cd);
+    pa_xfree(cd);
+
+    return b;
+}
+
+static void inotify_cb(
+        pa_mainloop_api*a,
+        pa_io_event* e,
+        int fd,
+        pa_io_event_flags_t events,
+        void *userdata) {
+
+    struct {
+        struct inotify_event e;
+        char name[NAME_MAX];
+    } buf;
+    struct userdata *u = userdata;
+    static int type = 0;
+    bool deleted = false;
+    struct device *d;
+    void *state;
+
+    for (;;) {
+        ssize_t r;
+        struct inotify_event *event;
+
+        pa_zero(buf);
+        if ((r = pa_read(fd, &buf, sizeof(buf), &type)) <= 0) {
+
+            if (r < 0 && errno == EAGAIN)
+                break;
+
+            pa_log("read() from inotify failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            goto fail;
+        }
+
+        event = &buf.e;
+        while (r > 0) {
+            size_t len;
+
+            if ((size_t) r < sizeof(struct inotify_event)) {
+                pa_log("read() too short.");
+                goto fail;
+            }
+
+            len = sizeof(struct inotify_event) + event->len;
+
+            if ((size_t) r < len) {
+                pa_log("Payload missing.");
+                goto fail;
+            }
+
+            /* From udev we get the guarantee that the control
+             * device's ACL is changed last. To avoid races when ACLs
+             * are changed we hence watch only the control device */
+            if (((event->mask & IN_ATTRIB) && pa_startswith(event->name, "controlC")))
+                PA_HASHMAP_FOREACH(d, u->devices, state)
+                    if (control_node_belongs_to_device(d, event->name))
+                        d->need_verify = true;
+
+            /* ALSA doesn't really give us any guarantee on the closing
+             * order, so let's simply hope */
+            if (((event->mask & IN_CLOSE_WRITE) && pa_startswith(event->name, "pcmC")))
+                PA_HASHMAP_FOREACH(d, u->devices, state)
+                    if (pcm_node_belongs_to_device(d, event->name))
+                        d->need_verify = true;
+
+            /* /dev/snd/ might have been removed */
+            if ((event->mask & (IN_DELETE_SELF|IN_MOVE_SELF)))
+                deleted = true;
+
+            event = (struct inotify_event*) ((uint8_t*) event + len);
+            r -= len;
+        }
+    }
+
+    PA_HASHMAP_FOREACH(d, u->devices, state)
+        if (d->need_verify) {
+            d->need_verify = false;
+            verify_access(u, d);
+        }
+
+    if (!deleted)
+        return;
+
+fail:
+    if (u->inotify_io) {
+        a->io_free(u->inotify_io);
+        u->inotify_io = NULL;
+    }
+
+    if (u->inotify_fd >= 0) {
+        pa_close(u->inotify_fd);
+        u->inotify_fd = -1;
+    }
+}
+
+static int setup_inotify(struct userdata *u) {
+    int r;
+
+    if (u->inotify_fd >= 0)
+        return 0;
+
+    if ((u->inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
+        pa_log("inotify_init1() failed: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    r = inotify_add_watch(u->inotify_fd, "/dev/snd", IN_ATTRIB|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF);
+
+    if (r < 0) {
+        int saved_errno = errno;
+
+        pa_close(u->inotify_fd);
+        u->inotify_fd = -1;
+
+        if (saved_errno == ENOENT) {
+            pa_log_debug("/dev/snd/ is apparently not existing yet, retrying to create inotify watch later.");
+            return 0;
+        }
+
+        if (saved_errno == ENOSPC) {
+            pa_log("You apparently ran out of inotify watches, probably because Tracker/Beagle took them all away. "
+                   "I wished people would do their homework first and fix inotify before using it for watching whole "
+                   "directory trees which is something the current inotify is certainly not useful for. "
+                   "Please make sure to drop the Tracker/Beagle guys a line complaining about their broken use of inotify.");
+            return 0;
+        }
+
+        pa_log("inotify_add_watch() failed: %s", pa_cstrerror(saved_errno));
+        return -1;
+    }
+
+    pa_assert_se(u->inotify_io = u->core->mainloop->io_new(u->core->mainloop, u->inotify_fd, PA_IO_EVENT_INPUT, inotify_cb, u));
+
+    return 0;
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u = NULL;
+    pa_modargs *ma;
+    struct udev_enumerate *enumerate = NULL;
+    struct udev_list_entry *item = NULL, *first = NULL;
+    int fd;
+    bool use_tsched = true, fixed_latency_range = false, ignore_dB = false, deferred_volume = m->core->deferred_volume;
+    bool use_ucm = true;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->devices = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) device_free);
+    u->inotify_fd = -1;
+
+    if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+        pa_log("Failed to parse tsched= argument.");
+        goto fail;
+    }
+    u->use_tsched = use_tsched;
+
+    if (pa_modargs_get_value(ma, "tsched_buffer_size", NULL)) {
+        if (pa_modargs_get_value_u32(ma, "tsched_buffer_size", &u->tsched_buffer_size) < 0) {
+            pa_log("Failed to parse tsched_buffer_size= argument.");
+            goto fail;
+        }
+
+        u->tsched_buffer_size_valid = true;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "fixed_latency_range", &fixed_latency_range) < 0) {
+        pa_log("Failed to parse fixed_latency_range= argument.");
+        goto fail;
+    }
+    u->fixed_latency_range = fixed_latency_range;
+
+    if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
+        pa_log("Failed to parse ignore_dB= argument.");
+        goto fail;
+    }
+    u->ignore_dB = ignore_dB;
+
+    if (pa_modargs_get_value_boolean(ma, "deferred_volume", &deferred_volume) < 0) {
+        pa_log("Failed to parse deferred_volume= argument.");
+        goto fail;
+    }
+    u->deferred_volume = deferred_volume;
+
+    if (pa_modargs_get_value_boolean(ma, "use_ucm", &use_ucm) < 0) {
+        pa_log("Failed to parse use_ucm= argument.");
+        goto fail;
+    }
+    u->use_ucm = use_ucm;
+
+    if (!(u->udev = udev_new())) {
+        pa_log("Failed to initialize udev library.");
+        goto fail;
+    }
+
+    if (setup_inotify(u) < 0)
+        goto fail;
+
+    if (!(u->monitor = udev_monitor_new_from_netlink(u->udev, "udev"))) {
+        pa_log("Failed to initialize monitor.");
+        goto fail;
+    }
+
+    if (udev_monitor_filter_add_match_subsystem_devtype(u->monitor, "sound", NULL) < 0) {
+        pa_log("Failed to subscribe to sound devices.");
+        goto fail;
+    }
+
+    errno = 0;
+    if (udev_monitor_enable_receiving(u->monitor) < 0) {
+        pa_log("Failed to enable monitor: %s", pa_cstrerror(errno));
+        if (errno == EPERM)
+            pa_log_info("Most likely your kernel is simply too old and "
+                        "allows only privileged processes to listen to device events. "
+                        "Please upgrade your kernel to at least 2.6.30.");
+        goto fail;
+    }
+
+    if ((fd = udev_monitor_get_fd(u->monitor)) < 0) {
+        pa_log("Failed to get udev monitor fd.");
+        goto fail;
+    }
+
+    pa_assert_se(u->udev_io = u->core->mainloop->io_new(u->core->mainloop, fd, PA_IO_EVENT_INPUT, monitor_cb, u));
+
+    if (!(enumerate = udev_enumerate_new(u->udev))) {
+        pa_log("Failed to initialize udev enumerator.");
+        goto fail;
+    }
+
+    if (udev_enumerate_add_match_subsystem(enumerate, "sound") < 0) {
+        pa_log("Failed to match to subsystem.");
+        goto fail;
+    }
+
+    if (udev_enumerate_scan_devices(enumerate) < 0) {
+        pa_log("Failed to scan for devices.");
+        goto fail;
+    }
+
+    first = udev_enumerate_get_list_entry(enumerate);
+    udev_list_entry_foreach(item, first)
+        process_path(u, udev_list_entry_get_name(item));
+
+    udev_enumerate_unref(enumerate);
+
+    pa_log_info("Found %u cards.", pa_hashmap_size(u->devices));
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (enumerate)
+        udev_enumerate_unref(enumerate);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->udev_io)
+        m->core->mainloop->io_free(u->udev_io);
+
+    if (u->monitor)
+        udev_monitor_unref(u->monitor);
+
+    if (u->udev)
+        udev_unref(u->udev);
+
+    if (u->inotify_io)
+        m->core->mainloop->io_free(u->inotify_io);
+
+    if (u->inotify_fd >= 0)
+        pa_close(u->inotify_fd);
+
+    if (u->devices)
+        pa_hashmap_free(u->devices);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c
new file mode 100644 (file)
index 0000000..faa5524
--- /dev/null
@@ -0,0 +1,673 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2010 Intel Corporation
+    Contributor: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/ltdl-helper.h>
+
+#include "module-virtual-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Pierre-Louis Bossart");
+PA_MODULE_DESCRIPTION(_("Virtual sink"));
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        _("sink_name=<name for the sink> "
+          "sink_properties=<properties for the sink> "
+          "master=<name of sink to filter> "
+          "rate=<sample rate> "
+          "channels=<number of channels> "
+          "channel_map=<channel map> "
+          "use_volume_sharing=<yes or no> "
+          "force_flat_volume=<yes or no> "
+        ));
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+
+struct userdata {
+    pa_module *module;
+
+    /* FIXME: Uncomment this and take "autoloaded" as a modarg if this is a filter */
+    /* bool autoloaded; */
+
+    pa_sink *sink;
+    pa_sink_input *sink_input;
+
+    pa_memblockq *memblockq;
+
+    bool auto_desc;
+    unsigned channels;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "master",
+    "rate",
+    "channels",
+    "channel_map",
+    "use_volume_sharing",
+    "force_flat_volume",
+    NULL
+};
+
+/* Called from I/O thread context */
+static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY:
+
+            /* The sink is _put() before the sink input is, so let's
+             * make sure we don't access it in that time. Also, the
+             * sink input is first shut down, the sink second. */
+            if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+                !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            *((int64_t*) data) =
+
+                /* Get the latency of the master sink */
+                pa_sink_get_latency_within_thread(u->sink_input->sink, true) +
+
+                /* Add the latency internal to our sink input on top */
+                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
+
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
+
+            /* When set to running or idle for the first time, request a rewind
+             * of the master sink to make sure we are heard immediately */
+            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
+                pa_log_debug("Requesting rewind due to state change.");
+                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
+            }
+            break;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(state) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return 0;
+
+    pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_request_rewind_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_request_rewind(u->sink_input,
+                                 s->thread_info.rewind_nbytes +
+                                 pa_memblockq_get_length(u->memblockq), true, false, false);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_set_requested_latency_within_thread(
+            u->sink_input,
+            pa_sink_get_requested_latency_within_thread(s));
+}
+
+/* Called from main context */
+static void sink_set_volume_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return;
+
+    pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, true);
+}
+
+/* Called from main context */
+static void sink_set_mute_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return;
+
+    pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted);
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+    float *src, *dst;
+    size_t fs;
+    unsigned n, c;
+    pa_memchunk tchunk;
+    pa_usec_t current_latency PA_GCC_UNUSED;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    pa_assert_se(u = i->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return -1;
+
+    /* Hmm, process any rewind request that might be queued up */
+    pa_sink_process_rewind(u->sink, 0);
+
+    /* (1) IF YOU NEED A FIXED BLOCK SIZE USE
+     * pa_memblockq_peek_fixed_size() HERE INSTEAD. NOTE THAT FILTERS
+     * WHICH CAN DEAL WITH DYNAMIC BLOCK SIZES ARE HIGHLY
+     * PREFERRED. */
+    while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
+        pa_memchunk nchunk;
+
+        pa_sink_render(u->sink, nbytes, &nchunk);
+        pa_memblockq_push(u->memblockq, &nchunk);
+        pa_memblock_unref(nchunk.memblock);
+    }
+
+    /* (2) IF YOU NEED A FIXED BLOCK SIZE, THIS NEXT LINE IS NOT
+     * NECESSARY */
+    tchunk.length = PA_MIN(nbytes, tchunk.length);
+    pa_assert(tchunk.length > 0);
+
+    fs = pa_frame_size(&i->sample_spec);
+    n = (unsigned) (tchunk.length / fs);
+
+    pa_assert(n > 0);
+
+    chunk->index = 0;
+    chunk->length = n*fs;
+    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
+
+    pa_memblockq_drop(u->memblockq, chunk->length);
+
+    src = pa_memblock_acquire_chunk(&tchunk);
+    dst = pa_memblock_acquire(chunk->memblock);
+
+    /* (3) PUT YOUR CODE HERE TO DO SOMETHING WITH THE DATA */
+
+    /* As an example, copy input to output */
+    for (c = 0; c < u->channels; c++) {
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE,
+                        dst+c, u->channels * sizeof(float),
+                        src+c, u->channels * sizeof(float),
+                        n);
+    }
+
+    pa_memblock_release(tchunk.memblock);
+    pa_memblock_release(chunk->memblock);
+
+    pa_memblock_unref(tchunk.memblock);
+
+    /* (4) IF YOU NEED THE LATENCY FOR SOMETHING ACQUIRE IT LIKE THIS: */
+    current_latency =
+        /* Get the latency of the master sink */
+        pa_sink_get_latency_within_thread(i->sink, false) +
+
+        /* Add the latency internal to our sink input on top */
+        pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec);
+
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+    size_t amount = 0;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If the sink is not yet linked, there is nothing to rewind */
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    if (u->sink->thread_info.rewind_nbytes > 0) {
+        size_t max_rewrite;
+
+        max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq);
+        amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
+        u->sink->thread_info.rewind_nbytes = 0;
+
+        if (amount > 0) {
+            pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, true);
+
+            /* (5) PUT YOUR CODE HERE TO RESET YOUR FILTER  */
+        }
+    }
+
+    pa_sink_process_rewind(u->sink, amount);
+    pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+    pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* (6) IF YOU NEED A FIXED BLOCK SIZE ROUND nbytes UP TO MULTIPLES
+     * OF IT HERE. THE PA_ROUND_UP MACRO IS USEFUL FOR THAT. */
+
+    pa_sink_set_max_request_within_thread(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* (7) IF YOU NEED A FIXED BLOCK SIZE ADD THE LATENCY FOR ONE
+     * BLOCK MINUS ONE SAMPLE HERE. pa_usec_to_bytes_round_up() IS
+     * USEFUL FOR THAT. */
+
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_detach_within_thread(u->sink);
+
+    pa_sink_set_rtpoll(u->sink, NULL);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll);
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+
+    /* (8.1) IF YOU NEED A FIXED BLOCK SIZE ADD THE LATENCY FOR ONE
+     * BLOCK MINUS ONE SAMPLE HERE. SEE (7) */
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+
+    /* (8.2) IF YOU NEED A FIXED BLOCK SIZE ROUND
+     * pa_sink_input_get_max_request(i) UP TO MULTIPLES OF IT
+     * HERE. SEE (6) */
+    pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i));
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_attach_within_thread(u->sink);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* The order here matters! We first kill the sink so that streams
+     * can properly be moved away while the sink input is still connected
+     * to the master. */
+    pa_sink_input_cork(u->sink_input, true);
+    pa_sink_unlink(u->sink);
+    pa_sink_input_unlink(u->sink_input);
+
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    pa_sink_unref(u->sink);
+    u->sink = NULL;
+
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main context */
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (dest) {
+        pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
+        pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
+    } else
+        pa_sink_set_asyncmsgq(u->sink, NULL);
+
+    if (u->auto_desc && dest) {
+        const char *z;
+        pa_proplist *pl;
+
+        pl = pa_proplist_new();
+        z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Virtual Sink %s on %s",
+                         pa_proplist_gets(u->sink->proplist, "device.vsink.name"), z ? z : dest->name);
+
+        pa_sink_update_proplist(u->sink, PA_UPDATE_REPLACE, pl);
+        pa_proplist_free(pl);
+    }
+}
+
+/* Called from main context */
+static void sink_input_volume_changed_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_volume_changed(u->sink, &i->volume);
+}
+
+/* Called from main context */
+static void sink_input_mute_changed_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_mute_changed(u->sink, i->muted);
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma;
+    pa_sink *master=NULL;
+    pa_sink_input_new_data sink_input_data;
+    pa_sink_new_data sink_data;
+    bool use_volume_sharing = true;
+    bool force_flat_volume = false;
+    pa_memchunk silence;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) {
+        pa_log("Master sink not found");
+        goto fail;
+    }
+
+    pa_assert(master);
+
+    ss = master->sample_spec;
+    ss.format = PA_SAMPLE_FLOAT32;
+    map = master->channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) {
+        pa_log("use_volume_sharing= expects a boolean argument");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) {
+        pa_log("force_flat_volume= expects a boolean argument");
+        goto fail;
+    }
+
+    if (use_volume_sharing && force_flat_volume) {
+        pa_log("Flat volume can't be forced when using volume sharing.");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    m->userdata = u;
+    u->channels = ss.channels;
+
+    /* Create sink */
+    pa_sink_new_data_init(&sink_data);
+    sink_data.driver = __FILE__;
+    sink_data.module = m;
+    if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+        sink_data.name = pa_sprintf_malloc("%s.vsink", master->name);
+    pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+    pa_sink_new_data_set_channel_map(&sink_data, &map);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+    pa_proplist_sets(sink_data.proplist, "device.vsink.name", sink_data.name);
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&sink_data);
+        goto fail;
+    }
+
+    if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+        const char *z;
+
+        z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Sink %s on %s", sink_data.name, z ? z : master->name);
+    }
+
+    u->sink = pa_sink_new(m->core, &sink_data, (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))
+                                               | (use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0));
+    pa_sink_new_data_done(&sink_data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg_cb;
+    u->sink->set_state = sink_set_state_cb;
+    u->sink->update_requested_latency = sink_update_requested_latency_cb;
+    u->sink->request_rewind = sink_request_rewind_cb;
+    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
+    if (!use_volume_sharing) {
+        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
+        pa_sink_enable_decibel_volume(u->sink, true);
+    }
+    /* Normally this flag would be enabled automatically be we can force it. */
+    if (force_flat_volume)
+        u->sink->flags |= PA_SINK_FLAT_VOLUME;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
+
+    /* Create sink input */
+    pa_sink_input_new_data_init(&sink_input_data);
+    sink_input_data.driver = __FILE__;
+    sink_input_data.module = m;
+    pa_sink_input_new_data_set_sink(&sink_input_data, master, false);
+    sink_input_data.origin_sink = u->sink;
+    pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+    pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+    sink_input_data.flags |= PA_SINK_INPUT_START_CORKED;
+
+    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
+    pa_sink_input_new_data_done(&sink_input_data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->update_max_request = sink_input_update_max_request_cb;
+    u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+    u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->attach = sink_input_attach_cb;
+    u->sink_input->detach = sink_input_detach_cb;
+    u->sink_input->moving = sink_input_moving_cb;
+    u->sink_input->volume_changed = use_volume_sharing ? NULL : sink_input_volume_changed_cb;
+    u->sink_input->mute_changed = sink_input_mute_changed_cb;
+    u->sink_input->userdata = u;
+
+    u->sink->input_to_master = u->sink_input;
+
+    pa_sink_input_get_silence(u->sink_input, &silence);
+    u->memblockq = pa_memblockq_new("module-virtual-sink memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, &silence);
+    pa_memblock_unref(silence.memblock);
+
+    /* (9) INITIALIZE ANYTHING ELSE YOU NEED HERE */
+
+    /* The order here is important. The input must be put first,
+     * otherwise streams might attach to the sink before the sink
+     * input is attached to the master. */
+    pa_sink_input_put(u->sink_input);
+    pa_sink_put(u->sink);
+    pa_sink_input_cork(u->sink_input, false);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_sink_linked_by(u->sink);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    /* See comments in sink_input_kill_cb() above regarding
+     * destruction order! */
+
+    if (u->sink_input)
+        pa_sink_input_cork(u->sink_input, true);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+    }
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c
new file mode 100644 (file)
index 0000000..94907df
--- /dev/null
@@ -0,0 +1,735 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2010 Intel Corporation
+    Contributor: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/ltdl-helper.h>
+#include <pulsecore/mix.h>
+
+#include "module-virtual-source-symdef.h"
+
+PA_MODULE_AUTHOR("Pierre-Louis Bossart");
+PA_MODULE_DESCRIPTION("Virtual source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        _("source_name=<name for the source> "
+          "source_properties=<properties for the source> "
+          "master=<name of source to filter> "
+          "uplink_sink=<name> (optional)"
+          "format=<sample format> "
+          "rate=<sample rate> "
+          "channels=<number of channels> "
+          "channel_map=<channel map> "
+          "use_volume_sharing=<yes or no> "
+          "force_flat_volume=<yes or no> "
+        ));
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+#define BLOCK_USEC 1000 /* FIXME */
+
+struct userdata {
+    pa_module *module;
+
+    /* FIXME: Uncomment this and take "autoloaded" as a modarg if this is a filter */
+    /* bool autoloaded; */
+
+    pa_source *source;
+    pa_source_output *source_output;
+
+    pa_memblockq *memblockq;
+
+    bool auto_desc;
+    unsigned channels;
+
+    /* optional fields for uplink sink */
+    pa_sink *sink;
+    pa_usec_t block_usec;
+    pa_memblockq *sink_memblockq;
+
+};
+
+static const char* const valid_modargs[] = {
+    "source_name",
+    "source_properties",
+    "master",
+    "uplink_sink",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "use_volume_sharing",
+    "force_flat_volume",
+    NULL
+};
+
+/* Called from I/O thread context */
+static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY:
+
+            /* there's no real latency here */
+            *((int64_t*) data) = 0;
+
+            return 0;
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(state)) {
+        return 0;
+    }
+
+    if (state == PA_SINK_RUNNING) {
+        /* need to wake-up source if it was suspended */
+        pa_log_debug("Resuming source %s, because its uplink sink became active.", u->source->name);
+        pa_source_suspend(u->source, false, PA_SUSPEND_ALL);
+
+        /* FIXME: if there's no client connected, the source will suspend
+           and playback will be stuck. You'd want to prevent the source from
+           sleeping when the uplink sink is active; even if the audio is
+           discarded at least the app isn't stuck */
+
+    } else {
+        /* nothing to do, if the sink becomes idle or suspended let
+           module-suspend-idle handle the sources later */
+    }
+
+    return 0;
+}
+
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    /* FIXME: there's no latency support */
+
+}
+
+/* Called from I/O thread context */
+static void sink_request_rewind_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    /* Do nothing */
+    pa_sink_process_rewind(u->sink, 0);
+
+}
+
+/* Called from I/O thread context */
+static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY:
+
+            /* The source is _put() before the source output is, so let's
+             * make sure we don't access it in that time. Also, the
+             * source output is first shut down, the source second. */
+            if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
+                !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) {
+                *((pa_usec_t*) data) = 0;
+                return 0;
+            }
+
+            *((pa_usec_t*) data) =
+
+                /* Get the latency of the master source */
+                pa_source_get_latency_within_thread(u->source_output->source, true) +
+
+                /* Add the latency internal to our source output on top */
+                /* FIXME, no idea what I am doing here */
+                pa_bytes_to_usec(pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq), &u->source_output->source->sample_spec);
+
+            return 0;
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int source_set_state_cb(pa_source *s, pa_source_state_t state) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(state) ||
+        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
+        return 0;
+
+    pa_source_output_cork(u->source_output, state == PA_SOURCE_SUSPENDED);
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void source_update_requested_latency_cb(pa_source *s) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
+        !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state))
+        return;
+
+    /* Just hand this one over to the master source */
+    pa_source_output_set_requested_latency_within_thread(
+            u->source_output,
+            pa_source_get_requested_latency_within_thread(s));
+}
+
+/* Called from main context */
+static void source_set_volume_cb(pa_source *s) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
+        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
+        return;
+
+    pa_source_output_set_volume(u->source_output, &s->real_volume, s->save_volume, true);
+}
+
+/* Called from main context */
+static void source_set_mute_cb(pa_source *s) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
+        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
+        return;
+
+    pa_source_output_set_mute(u->source_output, s->muted, s->save_muted);
+}
+
+/* Called from input thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        return;
+
+    if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) {
+        pa_log("push when no link?");
+        return;
+    }
+
+    /* PUT YOUR CODE HERE TO DO SOMETHING WITH THE SOURCE DATA */
+
+    /* if uplink sink exists, pull data from there; simplify by using
+       same length as chunk provided by source */
+    if (u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) {
+        pa_memchunk tchunk;
+        size_t nbytes = chunk->length;
+        pa_mix_info streams[2];
+        pa_memchunk target_chunk;
+        void *target;
+        int ch;
+
+        /* Hmm, process any rewind request that might be queued up */
+        pa_sink_process_rewind(u->sink, 0);
+
+        /* get data from the sink */
+        while (pa_memblockq_peek(u->sink_memblockq, &tchunk) < 0) {
+            pa_memchunk nchunk;
+
+            /* make sure we get nbytes from the sink with render_full,
+               otherwise we cannot mix with the uplink */
+            pa_sink_render_full(u->sink, nbytes, &nchunk);
+            pa_memblockq_push(u->sink_memblockq, &nchunk);
+            pa_memblock_unref(nchunk.memblock);
+        }
+        pa_assert(tchunk.length == chunk->length);
+
+        /* move the read pointer for sink memblockq */
+        pa_memblockq_drop(u->sink_memblockq, tchunk.length);
+
+        /* allocate target chunk */
+        /* this could probably be done in-place, but having chunk as both
+           the input and output creates issues with reference counts */
+        target_chunk.index = 0;
+        target_chunk.length = chunk->length;
+        pa_assert(target_chunk.length == chunk->length);
+
+        target_chunk.memblock = pa_memblock_new(o->source->core->mempool,
+                                                target_chunk.length);
+        pa_assert( target_chunk.memblock );
+
+        /* get target pointer */
+        target = pa_memblock_acquire_chunk(&target_chunk);
+
+        /* set-up mixing structure
+           volume was taken care of in sink and source already */
+        streams[0].chunk = *chunk;
+        for(ch=0;ch<o->sample_spec.channels;ch++)
+            streams[0].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
+        streams[0].volume.channels = o->sample_spec.channels;
+
+        streams[1].chunk = tchunk;
+        for(ch=0;ch<o->sample_spec.channels;ch++)
+            streams[1].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
+        streams[1].volume.channels = o->sample_spec.channels;
+
+        /* do mixing */
+        pa_mix(streams,                /* 2 streams to be mixed */
+               2,
+               target,                 /* put result in target chunk */
+               chunk->length,          /* same length as input */
+               (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */
+               NULL,                   /* no volume information */
+               false);                 /* no mute */
+
+        pa_memblock_release(target_chunk.memblock);
+        pa_memblock_unref(tchunk.memblock); /* clean-up */
+
+        /* forward the data to the virtual source */
+        pa_source_post(u->source, &target_chunk);
+
+        pa_memblock_unref(target_chunk.memblock); /* clean-up */
+
+    } else {
+        /* forward the data to the virtual source */
+        pa_source_post(u->source, chunk);
+    }
+
+}
+
+/* Called from input thread context */
+static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    /* If the source is not yet linked, there is nothing to rewind */
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_process_rewind(u->source, nbytes);
+
+    /* FIXME, no idea what I am doing here */
+#if 0
+    pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL);
+    u->send_counter -= (int64_t) nbytes;
+#endif
+}
+
+/* Called from output thread context */
+static void source_output_attach_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    pa_source_set_rtpoll(u->source, o->source->thread_info.rtpoll);
+    pa_source_set_latency_range_within_thread(u->source, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
+    pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency);
+    pa_source_set_max_rewind_within_thread(u->source, pa_source_output_get_max_rewind(o));
+
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_attach_within_thread(u->source);
+}
+
+/* Called from output thread context */
+static void source_output_detach_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert_se(u = o->userdata);
+
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_detach_within_thread(u->source);
+    pa_source_set_rtpoll(u->source, NULL);
+}
+
+/* Called from output thread context except when cork() is called without valid source.*/
+static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_se(u = o->userdata);
+
+    /* FIXME */
+#if 0
+    if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT && o->source) {
+
+        u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source, false),
+                                               u->latency),
+                                   &o->sample_spec);
+
+        pa_log_info("Skipping %lu bytes", (unsigned long) u->skip);
+    }
+#endif
+}
+
+/* Called from main thread */
+static void source_output_kill_cb(pa_source_output *o) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert_se(u = o->userdata);
+
+    /* The order here matters! We first kill the source so that streams
+     * can properly be moved away while the source output is still connected
+     * to the master. */
+    pa_source_output_cork(u->source_output, true);
+    pa_source_unlink(u->source);
+    pa_source_output_unlink(u->source_output);
+
+    pa_source_output_unref(u->source_output);
+    u->source_output = NULL;
+
+    pa_source_unref(u->source);
+    u->source = NULL;
+
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main thread */
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
+    struct userdata *u;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert_se(u = o->userdata);
+
+    if (dest) {
+        pa_source_set_asyncmsgq(u->source, dest->asyncmsgq);
+        pa_source_update_flags(u->source, PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY, dest->flags);
+    } else
+        pa_source_set_asyncmsgq(u->source, NULL);
+
+    if (u->auto_desc && dest) {
+        const char *z;
+        pa_proplist *pl;
+
+        pl = pa_proplist_new();
+        z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Virtual Source %s on %s",
+                         pa_proplist_gets(u->source->proplist, "device.vsource.name"), z ? z : dest->name);
+
+        pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, pl);
+        pa_proplist_free(pl);
+    }
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma;
+    pa_source *master=NULL;
+    pa_source_output_new_data source_output_data;
+    pa_source_new_data source_data;
+    bool use_volume_sharing = true;
+    bool force_flat_volume = false;
+
+    /* optional for uplink_sink */
+    pa_sink_new_data sink_data;
+    size_t nbytes;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SOURCE))) {
+        pa_log("Master source not found");
+        goto fail;
+    }
+
+    pa_assert(master);
+
+    ss = master->sample_spec;
+    ss.format = PA_SAMPLE_FLOAT32;
+    map = master->channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) {
+        pa_log("use_volume_sharing= expects a boolean argument");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) {
+        pa_log("force_flat_volume= expects a boolean argument");
+        goto fail;
+    }
+
+    if (use_volume_sharing && force_flat_volume) {
+        pa_log("Flat volume can't be forced when using volume sharing.");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    m->userdata = u;
+    u->memblockq = pa_memblockq_new("module-virtual-source memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, NULL);
+    if (!u->memblockq) {
+        pa_log("Failed to create source memblockq.");
+        goto fail;
+    }
+    u->channels = ss.channels;
+
+    /* Create source */
+    pa_source_new_data_init(&source_data);
+    source_data.driver = __FILE__;
+    source_data.module = m;
+    if (!(source_data.name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
+        source_data.name = pa_sprintf_malloc("%s.vsource", master->name);
+    pa_source_new_data_set_sample_spec(&source_data, &ss);
+    pa_source_new_data_set_channel_map(&source_data, &map);
+    pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+    pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+    pa_proplist_sets(source_data.proplist, "device.vsource.name", source_data.name);
+
+    if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_source_new_data_done(&source_data);
+        goto fail;
+    }
+
+    if ((u->auto_desc = !pa_proplist_contains(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+        const char *z;
+
+        z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Source %s on %s", source_data.name, z ? z : master->name);
+    }
+
+    u->source = pa_source_new(m->core, &source_data, (master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY))
+                                                     | (use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0));
+
+    pa_source_new_data_done(&source_data);
+
+    if (!u->source) {
+        pa_log("Failed to create source.");
+        goto fail;
+    }
+
+    u->source->parent.process_msg = source_process_msg_cb;
+    u->source->set_state = source_set_state_cb;
+    u->source->update_requested_latency = source_update_requested_latency_cb;
+    pa_source_set_set_mute_callback(u->source, source_set_mute_cb);
+    if (!use_volume_sharing) {
+        pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
+        pa_source_enable_decibel_volume(u->source, true);
+    }
+    /* Normally this flag would be enabled automatically be we can force it. */
+    if (force_flat_volume)
+        u->source->flags |= PA_SOURCE_FLAT_VOLUME;
+    u->source->userdata = u;
+
+    pa_source_set_asyncmsgq(u->source, master->asyncmsgq);
+
+    /* Create source output */
+    pa_source_output_new_data_init(&source_output_data);
+    source_output_data.driver = __FILE__;
+    source_output_data.module = m;
+    pa_source_output_new_data_set_source(&source_output_data, master, false);
+    source_output_data.destination_source = u->source;
+
+    pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Source Stream of %s", pa_proplist_gets(u->source->proplist, PA_PROP_DEVICE_DESCRIPTION));
+    pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
+    pa_source_output_new_data_set_channel_map(&source_output_data, &map);
+    source_output_data.flags |= PA_SOURCE_OUTPUT_START_CORKED;
+
+    pa_source_output_new(&u->source_output, m->core, &source_output_data);
+    pa_source_output_new_data_done(&source_output_data);
+
+    if (!u->source_output)
+        goto fail;
+
+    u->source_output->push = source_output_push_cb;
+    u->source_output->process_rewind = source_output_process_rewind_cb;
+    u->source_output->kill = source_output_kill_cb;
+    u->source_output->attach = source_output_attach_cb;
+    u->source_output->detach = source_output_detach_cb;
+    u->source_output->state_change = source_output_state_change_cb;
+    u->source_output->moving = source_output_moving_cb;
+    u->source_output->userdata = u;
+
+    u->source->output_from_master = u->source_output;
+
+    /* The order here is important. The output must be put first,
+     * otherwise streams might attach to the source before the
+     * source output is attached to the master. */
+    pa_source_output_put(u->source_output);
+    pa_source_put(u->source);
+    pa_source_output_cork(u->source_output, false);
+
+    /* Create optional uplink sink */
+    pa_sink_new_data_init(&sink_data);
+    sink_data.driver = __FILE__;
+    sink_data.module = m;
+    if ((sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "uplink_sink", NULL)))) {
+        pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+        pa_sink_new_data_set_channel_map(&sink_data, &map);
+        pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+        pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "uplink sink");
+        pa_proplist_sets(sink_data.proplist, "device.uplink_sink.name", sink_data.name);
+
+        if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+            const char *z;
+
+            z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+            pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Uplink Sink %s on %s", sink_data.name, z ? z : master->name);
+        }
+
+        u->sink_memblockq = pa_memblockq_new("module-virtual-source sink_memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, NULL);
+        if (!u->sink_memblockq) {
+            pa_sink_new_data_done(&sink_data);
+            pa_log("Failed to create sink memblockq.");
+            goto fail;
+        }
+
+        u->sink = pa_sink_new(m->core, &sink_data, 0);  /* FIXME, sink has no capabilities */
+        pa_sink_new_data_done(&sink_data);
+
+        if (!u->sink) {
+            pa_log("Failed to create sink.");
+            goto fail;
+        }
+
+        u->sink->parent.process_msg = sink_process_msg_cb;
+        u->sink->update_requested_latency = sink_update_requested_latency_cb;
+        u->sink->request_rewind = sink_request_rewind_cb;
+        u->sink->set_state = sink_set_state_cb;
+        u->sink->userdata = u;
+
+        pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
+
+        /* FIXME: no idea what I am doing here */
+        u->block_usec = BLOCK_USEC;
+        nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+        pa_sink_set_max_rewind(u->sink, nbytes);
+        pa_sink_set_max_request(u->sink, nbytes);
+
+        pa_sink_put(u->sink);
+    } else {
+        pa_sink_new_data_done(&sink_data);
+        /* optional uplink sink not enabled */
+        u->sink = NULL;
+    }
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_source_linked_by(u->source);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    /* See comments in source_output_kill_cb() above regarding
+     * destruction order! */
+
+    if (u->source_output)
+        pa_source_output_cork(u->source_output, true);
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->source_output) {
+        pa_source_output_unlink(u->source_output);
+        pa_source_output_unref(u->source_output);
+    }
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    if (u->sink_memblockq)
+        pa_memblockq_free(u->sink_memblockq);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-virtual-surround-sink.c b/src/modules/module-virtual-surround-sink.c
new file mode 100644 (file)
index 0000000..09c5e6d
--- /dev/null
@@ -0,0 +1,948 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2010 Intel Corporation
+    Contributor: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
+    Copyright 2012 Niels Ole Salscheider <niels_ole@salscheider-online.de>
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/ltdl-helper.h>
+#include <pulsecore/sound-file.h>
+#include <pulsecore/resampler.h>
+
+#include <math.h>
+
+#include "module-virtual-surround-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Niels Ole Salscheider");
+PA_MODULE_DESCRIPTION(_("Virtual surround sink"));
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        _("sink_name=<name for the sink> "
+          "sink_properties=<properties for the sink> "
+          "master=<name of sink to filter> "
+          "sink_master=<name of sink to filter> "
+          "format=<sample format> "
+          "rate=<sample rate> "
+          "channels=<number of channels> "
+          "channel_map=<channel map> "
+          "use_volume_sharing=<yes or no> "
+          "force_flat_volume=<yes or no> "
+          "hrir=/path/to/left_hrir.wav "
+          "autoloaded=<set if this module is being loaded automatically> "
+        ));
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+#define DEFAULT_AUTOLOADED false
+
+struct userdata {
+    pa_module *module;
+
+    /* FIXME: Uncomment this and take "autoloaded" as a modarg if this is a filter */
+    /* bool autoloaded; */
+
+    pa_sink *sink;
+    pa_sink_input *sink_input;
+
+    pa_memblockq *memblockq;
+
+    bool auto_desc;
+    unsigned channels;
+    unsigned hrir_channels;
+
+    unsigned fs, sink_fs;
+
+    unsigned *mapping_left;
+    unsigned *mapping_right;
+
+    unsigned hrir_samples;
+    float *hrir_data;
+
+    float *input_buffer;
+    int input_buffer_offset;
+
+    bool autoloaded;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "master",  /* Will be deprecated. */
+    "sink_master",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "use_volume_sharing",
+    "force_flat_volume",
+    "hrir",
+    "autoloaded",
+    NULL
+};
+
+/* Called from I/O thread context */
+static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY:
+
+            /* The sink is _put() before the sink input is, so let's
+             * make sure we don't access it in that time. Also, the
+             * sink input is first shut down, the sink second. */
+            if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+                !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) {
+                *((int64_t*) data) = 0;
+                return 0;
+            }
+
+            *((int64_t*) data) =
+
+                /* Get the latency of the master sink */
+                pa_sink_get_latency_within_thread(u->sink_input->sink, true) +
+
+                /* Add the latency internal to our sink input on top */
+                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
+
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
+
+            /* When set to running or idle for the first time, request a rewind
+             * of the master sink to make sure we are heard immediately */
+            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
+                pa_log_debug("Requesting rewind due to state change.");
+                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
+            }
+            break;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(state) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return 0;
+
+    pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_request_rewind_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_request_rewind(u->sink_input,
+                                 s->thread_info.rewind_nbytes +
+                                 pa_memblockq_get_length(u->memblockq), true, false, false);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_set_requested_latency_within_thread(
+            u->sink_input,
+            pa_sink_get_requested_latency_within_thread(s));
+}
+
+/* Called from main context */
+static void sink_set_volume_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return;
+
+    pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, true);
+}
+
+/* Called from main context */
+static void sink_set_mute_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return;
+
+    pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted);
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+    float *src, *dst;
+    unsigned n;
+    pa_memchunk tchunk;
+
+    unsigned j, k, l;
+    float sum_right, sum_left;
+    float current_sample;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    pa_assert_se(u = i->userdata);
+
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return -1;
+
+    /* Hmm, process any rewind request that might be queued up */
+    pa_sink_process_rewind(u->sink, 0);
+
+    while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
+        pa_memchunk nchunk;
+
+        pa_sink_render(u->sink, nbytes * u->sink_fs / u->fs, &nchunk);
+        pa_memblockq_push(u->memblockq, &nchunk);
+        pa_memblock_unref(nchunk.memblock);
+    }
+
+    tchunk.length = PA_MIN(nbytes * u->sink_fs / u->fs, tchunk.length);
+    pa_assert(tchunk.length > 0);
+
+    n = (unsigned) (tchunk.length / u->sink_fs);
+
+    pa_assert(n > 0);
+
+    chunk->index = 0;
+    chunk->length = n * u->fs;
+    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
+
+    pa_memblockq_drop(u->memblockq, n * u->sink_fs);
+
+    src = pa_memblock_acquire_chunk(&tchunk);
+    dst = pa_memblock_acquire(chunk->memblock);
+
+    for (l = 0; l < n; l++) {
+        memcpy(((char*) u->input_buffer) + u->input_buffer_offset * u->sink_fs, ((char *) src) + l * u->sink_fs, u->sink_fs);
+
+        sum_right = 0;
+        sum_left = 0;
+
+        /* fold the input buffer with the impulse response */
+        for (j = 0; j < u->hrir_samples; j++) {
+            for (k = 0; k < u->channels; k++) {
+                current_sample = u->input_buffer[((u->input_buffer_offset + j) % u->hrir_samples) * u->channels + k];
+
+                sum_left += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_left[k]];
+                sum_right += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_right[k]];
+            }
+        }
+
+        dst[2 * l] = PA_CLAMP_UNLIKELY(sum_left, -1.0f, 1.0f);
+        dst[2 * l + 1] = PA_CLAMP_UNLIKELY(sum_right, -1.0f, 1.0f);
+
+        u->input_buffer_offset--;
+        if (u->input_buffer_offset < 0)
+            u->input_buffer_offset += u->hrir_samples;
+    }
+
+    pa_memblock_release(tchunk.memblock);
+    pa_memblock_release(chunk->memblock);
+
+    pa_memblock_unref(tchunk.memblock);
+
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+    size_t amount = 0;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If the sink is not yet linked, there is nothing to rewind */
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    if (u->sink->thread_info.rewind_nbytes > 0) {
+        size_t max_rewrite;
+
+        max_rewrite = nbytes * u->sink_fs / u->fs + pa_memblockq_get_length(u->memblockq);
+        amount = PA_MIN(u->sink->thread_info.rewind_nbytes * u->sink_fs / u->fs, max_rewrite);
+        u->sink->thread_info.rewind_nbytes = 0;
+
+        if (amount > 0) {
+            pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, true);
+
+            /* Reset the input buffer */
+            memset(u->input_buffer, 0, u->hrir_samples * u->sink_fs);
+            u->input_buffer_offset = 0;
+        }
+    }
+
+    pa_sink_process_rewind(u->sink, amount);
+    pa_memblockq_rewind(u->memblockq, nbytes * u->sink_fs / u->fs);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_memblockq_set_maxrewind(u->memblockq, nbytes * u->sink_fs / u->fs);
+    pa_sink_set_max_rewind_within_thread(u->sink, nbytes * u->sink_fs / u->fs);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_max_request_within_thread(u->sink, nbytes * u->sink_fs / u->fs);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_detach_within_thread(u->sink);
+
+    pa_sink_set_rtpoll(u->sink, NULL);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll);
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+
+    pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i) * u->sink_fs / u->fs);
+
+    /* FIXME: Too small max_rewind:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
+    pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i) * u->sink_fs / u->fs);
+
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_attach_within_thread(u->sink);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* The order here matters! We first kill the sink so that streams
+     * can properly be moved away while the sink input is still connected
+     * to the master. */
+    pa_sink_input_cork(u->sink_input, true);
+    pa_sink_unlink(u->sink);
+    pa_sink_input_unlink(u->sink_input);
+
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    pa_sink_unref(u->sink);
+    u->sink = NULL;
+
+    pa_module_unload_request(u->module, true);
+}
+
+/* Called from main context */
+static bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (u->autoloaded)
+        return false;
+
+    return u->sink != dest;
+}
+
+/* Called from main context */
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (dest) {
+        pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
+        pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
+    } else
+        pa_sink_set_asyncmsgq(u->sink, NULL);
+
+    if (u->auto_desc && dest) {
+        const char *z;
+        pa_proplist *pl;
+
+        pl = pa_proplist_new();
+        z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Virtual Surround Sink %s on %s",
+                         pa_proplist_gets(u->sink->proplist, "device.vsurroundsink.name"), z ? z : dest->name);
+
+        pa_sink_update_proplist(u->sink, PA_UPDATE_REPLACE, pl);
+        pa_proplist_free(pl);
+    }
+}
+
+/* Called from main context */
+static void sink_input_volume_changed_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_volume_changed(u->sink, &i->volume);
+}
+
+/* Called from main context */
+static void sink_input_mute_changed_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_mute_changed(u->sink, i->muted);
+}
+
+static pa_channel_position_t mirror_channel(pa_channel_position_t channel) {
+    switch (channel) {
+        case PA_CHANNEL_POSITION_FRONT_LEFT:
+            return PA_CHANNEL_POSITION_FRONT_RIGHT;
+
+        case PA_CHANNEL_POSITION_FRONT_RIGHT:
+            return PA_CHANNEL_POSITION_FRONT_LEFT;
+
+        case PA_CHANNEL_POSITION_REAR_LEFT:
+            return PA_CHANNEL_POSITION_REAR_RIGHT;
+
+        case PA_CHANNEL_POSITION_REAR_RIGHT:
+            return PA_CHANNEL_POSITION_REAR_LEFT;
+
+        case PA_CHANNEL_POSITION_SIDE_LEFT:
+            return PA_CHANNEL_POSITION_SIDE_RIGHT;
+
+        case PA_CHANNEL_POSITION_SIDE_RIGHT:
+            return PA_CHANNEL_POSITION_SIDE_LEFT;
+
+        case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
+            return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+
+        case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
+            return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
+
+        case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
+            return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
+
+        case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
+            return PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
+
+        case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
+            return PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+
+        case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
+            return PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+
+        default:
+            return channel;
+    }
+}
+
+static void normalize_hrir(struct userdata *u) {
+    /* normalize hrir to avoid audible clipping
+     *
+     * The following heuristic tries to avoid audible clipping. It cannot avoid
+     * clipping in the worst case though, because the scaling factor would
+     * become too large resulting in a too quiet signal.
+     * The idea of the heuristic is to avoid clipping when a single click is
+     * played back on all channels. The scaling factor describes the additional
+     * factor that is necessary to avoid clipping for "normal" signals.
+     *
+     * This algorithm doesn't pretend to be perfect, it's just something that
+     * appears to work (not too quiet, no audible clipping) on the material that
+     * it has been tested on. If you find a real-world example where this
+     * algorithm results in audible clipping, please write a patch that adjusts
+     * the scaling factor constants or improves the algorithm (or if you can't
+     * write a patch, at least report the problem to the PulseAudio mailing list
+     * or bug tracker). */
+
+    const float scaling_factor = 2.5;
+
+    float hrir_sum, hrir_max;
+    unsigned i, j;
+
+    hrir_max = 0;
+    for (i = 0; i < u->hrir_samples; i++) {
+        hrir_sum = 0;
+        for (j = 0; j < u->hrir_channels; j++)
+            hrir_sum += fabs(u->hrir_data[i * u->hrir_channels + j]);
+
+        if (hrir_sum > hrir_max)
+            hrir_max = hrir_sum;
+    }
+
+    for (i = 0; i < u->hrir_samples; i++) {
+        for (j = 0; j < u->hrir_channels; j++)
+            u->hrir_data[i * u->hrir_channels + j] /= hrir_max * scaling_factor;
+    }
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_sample_spec ss, sink_input_ss;
+    pa_channel_map map, sink_input_map;
+    pa_modargs *ma;
+    const char *master_name;
+    pa_sink *master = NULL;
+    pa_sink_input_new_data sink_input_data;
+    pa_sink_new_data sink_data;
+    bool use_volume_sharing = true;
+    bool force_flat_volume = false;
+    pa_memchunk silence;
+
+    const char *hrir_file;
+    unsigned i, j, found_channel_left, found_channel_right;
+    float *hrir_data;
+
+    pa_sample_spec hrir_ss;
+    pa_channel_map hrir_map;
+
+    pa_sample_spec hrir_temp_ss;
+    pa_memchunk hrir_temp_chunk, hrir_temp_chunk_resampled;
+    pa_resampler *resampler;
+
+    size_t hrir_copied_length, hrir_total_length;
+
+    hrir_temp_chunk.memblock = NULL;
+    hrir_temp_chunk_resampled.memblock = NULL;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    master_name = pa_modargs_get_value(ma, "sink_master", NULL);
+    if (!master_name) {
+        master_name = pa_modargs_get_value(ma, "master", NULL);
+        if (master_name)
+            pa_log_warn("The 'master' module argument is deprecated and may be removed in the future, "
+                        "please use the 'sink_master' argument instead.");
+    }
+
+    master = pa_namereg_get(m->core, master_name, PA_NAMEREG_SINK);
+    if (!master) {
+        pa_log("Master sink not found.");
+        goto fail;
+    }
+
+    pa_assert(master);
+
+    u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    m->userdata = u;
+
+    /* Initialize hrir and input buffer */
+    /* this is the hrir file for the left ear! */
+    if (!(hrir_file = pa_modargs_get_value(ma, "hrir", NULL))) {
+        pa_log("The mandatory 'hrir' module argument is missing.");
+        goto fail;
+    }
+
+    if (pa_sound_file_load(master->core->mempool, hrir_file, &hrir_temp_ss, &hrir_map, &hrir_temp_chunk, NULL) < 0) {
+        pa_log("Cannot load hrir file.");
+        goto fail;
+    }
+
+    /* sample spec / map of hrir */
+    hrir_ss.format = PA_SAMPLE_FLOAT32;
+    hrir_ss.rate = master->sample_spec.rate;
+    hrir_ss.channels = hrir_temp_ss.channels;
+
+    /* sample spec of sink */
+    ss = hrir_ss;
+    map = hrir_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+    ss.format = PA_SAMPLE_FLOAT32;
+    hrir_ss.rate = ss.rate;
+    u->channels = ss.channels;
+
+    if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) {
+        pa_log("use_volume_sharing= expects a boolean argument");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) {
+        pa_log("force_flat_volume= expects a boolean argument");
+        goto fail;
+    }
+
+    if (use_volume_sharing && force_flat_volume) {
+        pa_log("Flat volume can't be forced when using volume sharing.");
+        goto fail;
+    }
+
+    /* sample spec / map of sink input */
+    pa_channel_map_init_stereo(&sink_input_map);
+    sink_input_ss.channels = 2;
+    sink_input_ss.format = PA_SAMPLE_FLOAT32;
+    sink_input_ss.rate = ss.rate;
+
+    u->sink_fs = pa_frame_size(&ss);
+    u->fs = pa_frame_size(&sink_input_ss);
+
+    /* Create sink */
+    pa_sink_new_data_init(&sink_data);
+    sink_data.driver = __FILE__;
+    sink_data.module = m;
+    if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+        sink_data.name = pa_sprintf_malloc("%s.vsurroundsink", master->name);
+    pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+    pa_sink_new_data_set_channel_map(&sink_data, &map);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+    pa_proplist_sets(sink_data.proplist, "device.vsurroundsink.name", sink_data.name);
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&sink_data);
+        goto fail;
+    }
+
+    u->autoloaded = DEFAULT_AUTOLOADED;
+    if (pa_modargs_get_value_boolean(ma, "autoloaded", &u->autoloaded) < 0) {
+        pa_log("Failed to parse autoloaded value");
+        goto fail;
+    }
+
+    if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
+        const char *z;
+
+        z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+        pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Surround Sink %s on %s", sink_data.name, z ? z : master->name);
+    }
+
+    u->sink = pa_sink_new(m->core, &sink_data, (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))
+                                               | (use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0));
+    pa_sink_new_data_done(&sink_data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg_cb;
+    u->sink->set_state = sink_set_state_cb;
+    u->sink->update_requested_latency = sink_update_requested_latency_cb;
+    u->sink->request_rewind = sink_request_rewind_cb;
+    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
+    if (!use_volume_sharing) {
+        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
+        pa_sink_enable_decibel_volume(u->sink, true);
+    }
+    /* Normally this flag would be enabled automatically be we can force it. */
+    if (force_flat_volume)
+        u->sink->flags |= PA_SINK_FLAT_VOLUME;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
+
+    /* Create sink input */
+    pa_sink_input_new_data_init(&sink_input_data);
+    sink_input_data.driver = __FILE__;
+    sink_input_data.module = m;
+    pa_sink_input_new_data_set_sink(&sink_input_data, master, false);
+    sink_input_data.origin_sink = u->sink;
+    pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Surround Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &sink_input_ss);
+    pa_sink_input_new_data_set_channel_map(&sink_input_data, &sink_input_map);
+    sink_input_data.flags |= PA_SINK_INPUT_START_CORKED;
+
+    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
+    pa_sink_input_new_data_done(&sink_input_data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->update_max_request = sink_input_update_max_request_cb;
+    u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+    u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->attach = sink_input_attach_cb;
+    u->sink_input->detach = sink_input_detach_cb;
+    u->sink_input->may_move_to = sink_input_may_move_to_cb;
+    u->sink_input->moving = sink_input_moving_cb;
+    u->sink_input->volume_changed = use_volume_sharing ? NULL : sink_input_volume_changed_cb;
+    u->sink_input->mute_changed = sink_input_mute_changed_cb;
+    u->sink_input->userdata = u;
+
+    u->sink->input_to_master = u->sink_input;
+
+    pa_sink_input_get_silence(u->sink_input, &silence);
+    u->memblockq = pa_memblockq_new("module-virtual-surround-sink memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &sink_input_ss, 1, 1, 0, &silence);
+    pa_memblock_unref(silence.memblock);
+
+    /* resample hrir */
+    resampler = pa_resampler_new(u->sink->core->mempool, &hrir_temp_ss, &hrir_map, &hrir_ss, &hrir_map, u->sink->core->lfe_crossover_freq,
+                                 PA_RESAMPLER_SRC_SINC_BEST_QUALITY, PA_RESAMPLER_NO_REMAP);
+
+    u->hrir_samples = hrir_temp_chunk.length / pa_frame_size(&hrir_temp_ss) * hrir_ss.rate / hrir_temp_ss.rate;
+    if (u->hrir_samples > 64) {
+        u->hrir_samples = 64;
+        pa_log("The (resampled) hrir contains more than 64 samples. Only the first 64 samples will be used to limit processor usage.");
+    }
+
+    hrir_total_length = u->hrir_samples * pa_frame_size(&hrir_ss);
+    u->hrir_channels = hrir_ss.channels;
+
+    u->hrir_data = (float *) pa_xmalloc(hrir_total_length);
+    hrir_copied_length = 0;
+
+    /* add silence to the hrir until we get enough samples out of the resampler */
+    while (hrir_copied_length < hrir_total_length) {
+        pa_resampler_run(resampler, &hrir_temp_chunk, &hrir_temp_chunk_resampled);
+        if (hrir_temp_chunk.memblock != hrir_temp_chunk_resampled.memblock) {
+            /* Silence input block */
+            pa_silence_memblock(hrir_temp_chunk.memblock, &hrir_temp_ss);
+        }
+
+        if (hrir_temp_chunk_resampled.memblock) {
+            /* Copy hrir data */
+            hrir_data = (float *) pa_memblock_acquire(hrir_temp_chunk_resampled.memblock);
+
+            if (hrir_total_length - hrir_copied_length >= hrir_temp_chunk_resampled.length) {
+                memcpy(u->hrir_data + hrir_copied_length, hrir_data, hrir_temp_chunk_resampled.length);
+                hrir_copied_length += hrir_temp_chunk_resampled.length;
+            } else {
+                memcpy(u->hrir_data + hrir_copied_length, hrir_data, hrir_total_length - hrir_copied_length);
+                hrir_copied_length = hrir_total_length;
+            }
+
+            pa_memblock_release(hrir_temp_chunk_resampled.memblock);
+            pa_memblock_unref(hrir_temp_chunk_resampled.memblock);
+            hrir_temp_chunk_resampled.memblock = NULL;
+        }
+    }
+
+    pa_resampler_free(resampler);
+
+    pa_memblock_unref(hrir_temp_chunk.memblock);
+    hrir_temp_chunk.memblock = NULL;
+
+    if (hrir_map.channels < map.channels) {
+        pa_log("hrir file does not have enough channels!");
+        goto fail;
+    }
+
+    normalize_hrir(u);
+
+    /* create mapping between hrir and input */
+    u->mapping_left = (unsigned *) pa_xnew0(unsigned, u->channels);
+    u->mapping_right = (unsigned *) pa_xnew0(unsigned, u->channels);
+    for (i = 0; i < map.channels; i++) {
+        found_channel_left = 0;
+        found_channel_right = 0;
+
+        for (j = 0; j < hrir_map.channels; j++) {
+            if (hrir_map.map[j] == map.map[i]) {
+                u->mapping_left[i] = j;
+                found_channel_left = 1;
+            }
+
+            if (hrir_map.map[j] == mirror_channel(map.map[i])) {
+                u->mapping_right[i] = j;
+                found_channel_right = 1;
+            }
+        }
+
+        if (!found_channel_left) {
+            pa_log("Cannot find mapping for channel %s", pa_channel_position_to_string(map.map[i]));
+            goto fail;
+        }
+        if (!found_channel_right) {
+            pa_log("Cannot find mapping for channel %s", pa_channel_position_to_string(mirror_channel(map.map[i])));
+            goto fail;
+        }
+    }
+
+    u->input_buffer = pa_xmalloc0(u->hrir_samples * u->sink_fs);
+    u->input_buffer_offset = 0;
+
+    /* The order here is important. The input must be put first,
+     * otherwise streams might attach to the sink before the sink
+     * input is attached to the master. */
+    pa_sink_input_put(u->sink_input);
+    pa_sink_put(u->sink);
+    pa_sink_input_cork(u->sink_input, false);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    if (hrir_temp_chunk.memblock)
+        pa_memblock_unref(hrir_temp_chunk.memblock);
+
+    if (hrir_temp_chunk_resampled.memblock)
+        pa_memblock_unref(hrir_temp_chunk_resampled.memblock);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_sink_linked_by(u->sink);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    /* See comments in sink_input_kill_cb() above regarding
+     * destruction order! */
+
+    if (u->sink_input)
+        pa_sink_input_cork(u->sink_input, true);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+    }
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    if (u->hrir_data)
+        pa_xfree(u->hrir_data);
+
+    if (u->input_buffer)
+        pa_xfree(u->input_buffer);
+
+    if (u->mapping_left)
+        pa_xfree(u->mapping_left);
+    if (u->mapping_right)
+        pa_xfree(u->mapping_right);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c
new file mode 100644 (file)
index 0000000..e7dbe94
--- /dev/null
@@ -0,0 +1,83 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+#include "module-volume-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Compatibility module");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_DEPRECATED("Please use module-stream-restore instead of module-volume-restore!");
+
+static const char* const valid_modargs[] = {
+    "table",
+    "restore_device",
+    "restore_volume",
+    NULL,
+};
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    bool restore_device = true, restore_volume = true;
+    pa_module *n;
+    char *t;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
+        pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0) {
+        pa_log("restore_volume= and restore_device= expect boolean arguments");
+        goto fail;
+    }
+
+    pa_log_warn("We will now load module-stream-restore. Please make sure to remove module-volume-restore from your configuration.");
+
+    t = pa_sprintf_malloc("restore_volume=%s restore_device=%s", pa_yes_no(restore_volume), pa_yes_no(restore_device));
+    n = pa_module_load(m->core, "module-stream-restore", t);
+    pa_xfree(t);
+
+    if (n)
+        pa_module_unload_request(m, true);
+
+    pa_modargs_free(ma);
+
+    return n ? 0 : -1;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
diff --git a/src/modules/module-waveout.c b/src/modules/module-waveout.c
new file mode 100644 (file)
index 0000000..8d32e7b
--- /dev/null
@@ -0,0 +1,817 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+
+#include "module-waveout-symdef.h"
+
+PA_MODULE_AUTHOR("Pierre Ossman");
+PA_MODULE_DESCRIPTION("Windows waveOut Sink/Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_USAGE(
+    "sink_name=<name for the sink> "
+    "source_name=<name for the source> "
+    "output_device=<device number for the sink> "
+    "output_device_name=<name of the output device> "
+    "input_device=<device number for the source> "
+    "input_device_name=<name of the input device> "
+    "record=<enable source?> "
+    "playback=<enable sink?> "
+    "format=<sample format> "
+    "rate=<sample rate> "
+    "channels=<number of channels> "
+    "channel_map=<channel map> "
+    "fragments=<number of fragments> "
+    "fragment_size=<fragment size>"
+    "device=<device number - deprecated>"
+    "device_name=<name of the device - deprecated>");
+
+#define DEFAULT_SINK_NAME "wave_output"
+#define DEFAULT_SOURCE_NAME "wave_input"
+
+#define WAVEOUT_MAX_VOLUME 0xFFFF
+
+struct userdata {
+    pa_sink *sink;
+    pa_source *source;
+    pa_core *core;
+    pa_usec_t poll_timeout;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    uint32_t fragments, fragment_size;
+
+    uint32_t free_ofrags, free_ifrags;
+
+    DWORD written_bytes;
+    int sink_underflow;
+
+    int cur_ohdr, cur_ihdr;
+    WAVEHDR *ohdrs, *ihdrs;
+
+    HWAVEOUT hwo;
+    HWAVEIN hwi;
+    pa_module *module;
+
+    CRITICAL_SECTION crit;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "source_name",
+    "output_device",
+    "output_device_name",
+    "input_device",
+    "input_device_name",
+    "record",
+    "playback",
+    "fragments",
+    "fragment_size",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "device",
+    "device_name",
+    NULL
+};
+
+static void do_write(struct userdata *u) {
+    uint32_t free_frags;
+    pa_memchunk memchunk;
+    WAVEHDR *hdr;
+    MMRESULT res;
+    void *p;
+
+    if (!u->sink)
+        return;
+
+    if (!PA_SINK_IS_LINKED(u->sink->state))
+        return;
+
+    EnterCriticalSection(&u->crit);
+    free_frags = u->free_ofrags;
+    LeaveCriticalSection(&u->crit);
+
+    if (!u->sink_underflow && (free_frags == u->fragments))
+        pa_log_debug("WaveOut underflow!");
+
+    while (free_frags) {
+        hdr = &u->ohdrs[u->cur_ohdr];
+        if (hdr->dwFlags & WHDR_PREPARED)
+            waveOutUnprepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
+
+        hdr->dwBufferLength = 0;
+        while (hdr->dwBufferLength < u->fragment_size) {
+            size_t len;
+
+            len = u->fragment_size - hdr->dwBufferLength;
+
+            pa_sink_render(u->sink, len, &memchunk);
+
+            pa_assert(memchunk.memblock);
+            pa_assert(memchunk.length);
+
+            if (memchunk.length < len)
+                len = memchunk.length;
+
+            p = pa_memblock_acquire(memchunk.memblock);
+            memcpy(hdr->lpData + hdr->dwBufferLength, (char*) p + memchunk.index, len);
+            pa_memblock_release(memchunk.memblock);
+
+            hdr->dwBufferLength += len;
+
+            pa_memblock_unref(memchunk.memblock);
+            memchunk.memblock = NULL;
+        }
+
+        /* Underflow detection */
+        if (hdr->dwBufferLength == 0) {
+            u->sink_underflow = 1;
+            break;
+        }
+        u->sink_underflow = 0;
+
+        res = waveOutPrepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
+        if (res != MMSYSERR_NOERROR)
+            pa_log_error("Unable to prepare waveOut block: %d", res);
+
+        res = waveOutWrite(u->hwo, hdr, sizeof(WAVEHDR));
+        if (res != MMSYSERR_NOERROR)
+            pa_log_error("Unable to write waveOut block: %d", res);
+
+        u->written_bytes += hdr->dwBufferLength;
+
+        EnterCriticalSection(&u->crit);
+        u->free_ofrags--;
+        LeaveCriticalSection(&u->crit);
+
+        free_frags--;
+        u->cur_ohdr++;
+        u->cur_ohdr %= u->fragments;
+    }
+}
+
+static void do_read(struct userdata *u) {
+    uint32_t free_frags;
+    pa_memchunk memchunk;
+    WAVEHDR *hdr;
+    MMRESULT res;
+    void *p;
+
+    if (!u->source)
+        return;
+
+    if (!PA_SOURCE_IS_LINKED(u->source->state))
+        return;
+
+    EnterCriticalSection(&u->crit);
+    free_frags = u->free_ifrags;
+    u->free_ifrags = 0;
+    LeaveCriticalSection(&u->crit);
+
+    if (free_frags == u->fragments)
+        pa_log_debug("WaveIn overflow!");
+
+    while (free_frags) {
+        hdr = &u->ihdrs[u->cur_ihdr];
+        if (hdr->dwFlags & WHDR_PREPARED)
+            waveInUnprepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
+
+        if (hdr->dwBytesRecorded) {
+            memchunk.memblock = pa_memblock_new(u->core->mempool, hdr->dwBytesRecorded);
+            pa_assert(memchunk.memblock);
+
+            p = pa_memblock_acquire(memchunk.memblock);
+            memcpy((char*) p, hdr->lpData, hdr->dwBytesRecorded);
+            pa_memblock_release(memchunk.memblock);
+
+            memchunk.length = hdr->dwBytesRecorded;
+            memchunk.index = 0;
+
+            pa_source_post(u->source, &memchunk);
+            pa_memblock_unref(memchunk.memblock);
+        }
+
+        res = waveInPrepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
+        if (res != MMSYSERR_NOERROR)
+            pa_log_error("Unable to prepare waveIn block: %d", res);
+
+        res = waveInAddBuffer(u->hwi, hdr, sizeof(WAVEHDR));
+        if (res != MMSYSERR_NOERROR)
+            pa_log_error("Unable to add waveIn block: %d", res);
+
+        free_frags--;
+        u->cur_ihdr++;
+        u->cur_ihdr %= u->fragments;
+    }
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+    pa_assert(u->sink || u->source);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        int ret;
+        bool need_timer = false;
+
+        if (u->sink) {
+            if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+                pa_sink_process_rewind(u->sink, 0);
+
+            if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+                do_write(u);
+                need_timer = true;
+            }
+        }
+
+        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+            do_read(u);
+            need_timer = true;
+        }
+
+        if (need_timer)
+            pa_rtpoll_set_timer_relative(u->rtpoll, u->poll_timeout);
+        else
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+static void CALLBACK chunk_done_cb(HWAVEOUT hwo, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) {
+    struct userdata *u = (struct userdata*) inst;
+
+    if (msg == WOM_OPEN)
+        pa_log_debug("WaveOut subsystem opened.");
+    if (msg == WOM_CLOSE)
+        pa_log_debug("WaveOut subsystem closed.");
+    if (msg != WOM_DONE)
+        return;
+
+    EnterCriticalSection(&u->crit);
+    u->free_ofrags++;
+    pa_assert(u->free_ofrags <= u->fragments);
+    LeaveCriticalSection(&u->crit);
+}
+
+static void CALLBACK chunk_ready_cb(HWAVEIN hwi, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) {
+    struct userdata *u = (struct userdata*) inst;
+
+    if (msg == WIM_OPEN)
+        pa_log_debug("WaveIn subsystem opened.");
+    if (msg == WIM_CLOSE)
+        pa_log_debug("WaveIn subsystem closed.");
+    if (msg != WIM_DATA)
+        return;
+
+    EnterCriticalSection(&u->crit);
+    u->free_ifrags++;
+    pa_assert(u->free_ifrags <= u->fragments);
+    LeaveCriticalSection(&u->crit);
+}
+
+static pa_usec_t sink_get_latency(struct userdata *u) {
+    uint32_t free_frags;
+    MMTIME mmt;
+    pa_assert(u);
+    pa_assert(u->sink);
+
+    memset(&mmt, 0, sizeof(mmt));
+    mmt.wType = TIME_BYTES;
+    if (waveOutGetPosition(u->hwo, &mmt, sizeof(mmt)) == MMSYSERR_NOERROR)
+        return pa_bytes_to_usec(u->written_bytes - mmt.u.cb, &u->sink->sample_spec);
+    else {
+        EnterCriticalSection(&u->crit);
+        free_frags = u->free_ofrags;
+        LeaveCriticalSection(&u->crit);
+
+        return pa_bytes_to_usec((u->fragments - free_frags) * u->fragment_size, &u->sink->sample_spec);
+    }
+}
+
+static pa_usec_t source_get_latency(struct userdata *u) {
+    pa_usec_t r = 0;
+    uint32_t free_frags;
+    pa_assert(u);
+    pa_assert(u->source);
+
+    EnterCriticalSection(&u->crit);
+    free_frags = u->free_ifrags;
+    LeaveCriticalSection(&u->crit);
+
+    r += pa_bytes_to_usec((free_frags + 1) * u->fragment_size, &u->source->sample_spec);
+
+    return r;
+}
+
+static int process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u;
+
+    if (pa_sink_isinstance(o)) {
+        u = PA_SINK(o)->userdata;
+
+        switch (code) {
+
+            case PA_SINK_MESSAGE_GET_LATENCY: {
+                pa_usec_t r = 0;
+                if (u->hwo)
+                    r = sink_get_latency(u);
+                *((int64_t*) data) = (int64_t)r;
+                return 0;
+            }
+
+        }
+
+        return pa_sink_process_msg(o, code, data, offset, chunk);
+    }
+
+    if (pa_source_isinstance(o)) {
+        u = PA_SOURCE(o)->userdata;
+
+        switch (code) {
+
+            case PA_SOURCE_MESSAGE_GET_LATENCY: {
+                pa_usec_t r = 0;
+                if (u->hwi)
+                    r = source_get_latency(u);
+                *((int64_t*) data) = (int64_t)r;
+                return 0;
+            }
+
+        }
+
+        return pa_source_process_msg(o, code, data, offset, chunk);
+    }
+
+    return -1;
+}
+
+static void sink_get_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    WAVEOUTCAPS caps;
+    DWORD vol;
+    pa_volume_t left, right;
+
+    if (waveOutGetDevCaps(u->hwo, &caps, sizeof(caps)) != MMSYSERR_NOERROR)
+        return;
+    if (!(caps.dwSupport & WAVECAPS_VOLUME))
+        return;
+
+    if (waveOutGetVolume(u->hwo, &vol) != MMSYSERR_NOERROR)
+        return;
+
+    left = PA_CLAMP_VOLUME((vol & 0xFFFF) * PA_VOLUME_NORM / WAVEOUT_MAX_VOLUME);
+    if (caps.dwSupport & WAVECAPS_LRVOLUME)
+        right = PA_CLAMP_VOLUME(((vol >> 16) & 0xFFFF) * PA_VOLUME_NORM / WAVEOUT_MAX_VOLUME);
+    else
+        right = left;
+
+    /* Windows supports > 2 channels, except for volume control */
+    if (s->real_volume.channels > 2)
+        pa_cvolume_set(&s->real_volume, s->real_volume.channels, (left + right)/2);
+
+    s->real_volume.values[0] = left;
+    if (s->real_volume.channels > 1)
+        s->real_volume.values[1] = right;
+}
+
+static void sink_set_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    WAVEOUTCAPS caps;
+    DWORD vol;
+
+    if (waveOutGetDevCaps(u->hwo, &caps, sizeof(caps)) != MMSYSERR_NOERROR)
+        return;
+    if (!(caps.dwSupport & WAVECAPS_VOLUME))
+        return;
+
+    if (s->real_volume.channels == 2 && caps.dwSupport & WAVECAPS_LRVOLUME) {
+        vol = (s->real_volume.values[0] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM)
+            | (s->real_volume.values[1] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM) << 16;
+    } else {
+        vol = (pa_cvolume_avg(&(s->real_volume)) * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM)
+            | (pa_cvolume_avg(&(s->real_volume)) * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM) << 16;
+    }
+
+    if (waveOutSetVolume(u->hwo, vol) != MMSYSERR_NOERROR)
+        return;
+}
+
+static int ss_to_waveformat(pa_sample_spec *ss, LPWAVEFORMATEX wf) {
+    wf->wFormatTag = WAVE_FORMAT_PCM;
+
+    if (ss->channels > 2) {
+        pa_log_error("More than two channels not supported.");
+        return -1;
+    }
+
+    wf->nChannels = ss->channels;
+
+    wf->nSamplesPerSec = ss->rate;
+
+    if (ss->format == PA_SAMPLE_U8)
+        wf->wBitsPerSample = 8;
+    else if (ss->format == PA_SAMPLE_S16NE)
+        wf->wBitsPerSample = 16;
+    else {
+        pa_log_error("Unsupported sample format, only u8 and s16 are supported.");
+        return -1;
+    }
+
+    wf->nBlockAlign = wf->nChannels * wf->wBitsPerSample/8;
+    wf->nAvgBytesPerSec = wf->nSamplesPerSec * wf->nBlockAlign;
+
+    wf->cbSize = 0;
+
+    return 0;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+    pa_assert(m);
+    pa_assert(m->userdata);
+    u = (struct userdata*) m->userdata;
+
+    return (u->sink ? pa_sink_used_by(u->sink) : 0) +
+           (u->source ? pa_source_used_by(u->source) : 0);
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u = NULL;
+    HWAVEOUT hwo = INVALID_HANDLE_VALUE;
+    HWAVEIN hwi = INVALID_HANDLE_VALUE;
+    WAVEFORMATEX wf;
+    WAVEOUTCAPS pwoc;
+    WAVEINCAPS pwic;
+    MMRESULT result;
+    int nfrags, frag_size;
+    bool record = true, playback = true;
+    unsigned int input_device;
+    unsigned int output_device;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    const char *input_device_name = NULL;
+    const char *output_device_name = NULL;
+    unsigned int i;
+
+    pa_assert(m);
+    pa_assert(m->core);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments.");
+        goto fail;
+    }
+
+    /* Check whether deprecated arguments have been used. */
+    if (pa_modargs_get_value(ma, "device", NULL) != NULL || pa_modargs_get_value(ma, "device_name", NULL) != NULL) {
+        pa_log("device and device_name are no longer supported. Please use input_device, input_device_name, output_device and output_device_name.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
+        pa_log("record= and playback= expect boolean argument.");
+        goto fail;
+    }
+
+    if (!playback && !record) {
+        pa_log("neither playback nor record enabled for device.");
+        goto fail;
+    }
+
+    /* Set the output_device to be opened. If set output_device_name is used,
+     * else output_device if set and lastly WAVE_MAPPER is the default */
+    output_device = WAVE_MAPPER;
+    if (pa_modargs_get_value_u32(ma, "output_device", &output_device) < 0) {
+        pa_log("failed to parse output_device argument");
+        goto fail;
+    }
+    if ((output_device_name = pa_modargs_get_value(ma, "output_device_name", NULL)) != NULL) {
+        unsigned int num_output_devices = waveOutGetNumDevs();
+        for (i = 0; i < num_output_devices; i++) {
+            if (waveOutGetDevCaps(i, &pwoc, sizeof(pwoc)) == MMSYSERR_NOERROR)
+                if (strcmp(output_device_name, pwoc.szPname) == 0)
+                    break;
+        }
+        if (i < num_output_devices)
+            output_device = i;
+        else {
+            pa_log("output_device not found: %s", output_device_name);
+            goto fail;
+        }
+    }
+    if (waveOutGetDevCaps(output_device, &pwoc, sizeof(pwoc)) == MMSYSERR_NOERROR)
+        output_device_name = pwoc.szPname;
+    else
+        output_device_name = "unknown";
+
+    /* Set the input_device to be opened. If set input_device_name is used,
+     * else input_device if set and lastly WAVE_MAPPER is the default */
+    input_device = WAVE_MAPPER;
+    if (pa_modargs_get_value_u32(ma, "input_device", &input_device) < 0) {
+        pa_log("failed to parse input_device argument");
+        goto fail;
+    }
+    if ((input_device_name = pa_modargs_get_value(ma, "input_device_name", NULL)) != NULL) {
+        unsigned int num_input_devices = waveInGetNumDevs();
+        for (i = 0; i < num_input_devices; i++) {
+            if (waveInGetDevCaps(i, &pwic, sizeof(pwic)) == MMSYSERR_NOERROR)
+                if (strcmp(input_device_name, pwic.szPname) == 0)
+                    break;
+        }
+        if (i < num_input_devices)
+            input_device = i;
+        else {
+            pa_log("input_device not found: %s", input_device_name);
+            goto fail;
+        }
+    }
+    if (waveInGetDevCaps(input_device, &pwic, sizeof(pwic)) == MMSYSERR_NOERROR)
+        input_device_name = pwic.szPname;
+    else
+        input_device_name = "unknown";
+
+
+    nfrags = 5;
+    frag_size = 8192;
+    if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
+        pa_log("failed to parse fragments arguments");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_WAVEEX) < 0) {
+        pa_log("failed to parse sample specification");
+        goto fail;
+    }
+
+    if (ss_to_waveformat(&ss, &wf) < 0)
+        goto fail;
+
+    u = pa_xmalloc(sizeof(struct userdata));
+
+    if (record) {
+        result = waveInOpen(&hwi, input_device, &wf, 0, 0, WAVE_FORMAT_DIRECT | WAVE_FORMAT_QUERY);
+        if (result != MMSYSERR_NOERROR) {
+            pa_log_warn("Sample spec not supported by WaveIn, falling back to default sample rate.");
+            ss.rate = wf.nSamplesPerSec = m->core->default_sample_spec.rate;
+        }
+        result = waveInOpen(&hwi, input_device, &wf, (DWORD_PTR) chunk_ready_cb, (DWORD_PTR) u, CALLBACK_FUNCTION);
+        if (result != MMSYSERR_NOERROR) {
+            char errortext[MAXERRORLENGTH];
+            pa_log("Failed to open WaveIn.");
+            if (waveInGetErrorText(result, errortext, sizeof(errortext)) == MMSYSERR_NOERROR)
+                pa_log("Error: %s", errortext);
+            goto fail;
+        }
+        if (waveInStart(hwi) != MMSYSERR_NOERROR) {
+            pa_log("failed to start waveIn");
+            goto fail;
+        }
+    }
+
+    if (playback) {
+        result = waveOutOpen(&hwo, output_device, &wf, 0, 0, WAVE_FORMAT_DIRECT | WAVE_FORMAT_QUERY);
+        if (result != MMSYSERR_NOERROR) {
+            pa_log_warn("Sample spec not supported by WaveOut, falling back to default sample rate.");
+            ss.rate = wf.nSamplesPerSec = m->core->default_sample_spec.rate;
+        }
+        result = waveOutOpen(&hwo, output_device, &wf, (DWORD_PTR) chunk_done_cb, (DWORD_PTR) u, CALLBACK_FUNCTION);
+        if (result != MMSYSERR_NOERROR) {
+            char errortext[MAXERRORLENGTH];
+            pa_log("Failed to open WaveOut.");
+            if (waveOutGetErrorText(result, errortext, sizeof(errortext)) == MMSYSERR_NOERROR)
+                pa_log("Error: %s", errortext);
+            goto fail;
+        }
+    }
+
+    InitializeCriticalSection(&u->crit);
+
+    if (hwi != INVALID_HANDLE_VALUE) {
+        pa_source_new_data data;
+        pa_source_new_data_init(&data);
+        data.driver = __FILE__;
+        data.module = m;
+        pa_source_new_data_set_sample_spec(&data, &ss);
+        pa_source_new_data_set_channel_map(&data, &map);
+        pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+        pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "WaveIn on %s", input_device_name);
+        u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+        pa_source_new_data_done(&data);
+
+        pa_assert(u->source);
+        u->source->userdata = u;
+        u->source->parent.process_msg = process_msg;
+    } else
+        u->source = NULL;
+
+    if (hwo != INVALID_HANDLE_VALUE) {
+        pa_sink_new_data data;
+        pa_sink_new_data_init(&data);
+        data.driver = __FILE__;
+        data.module = m;
+        pa_sink_new_data_set_sample_spec(&data, &ss);
+        pa_sink_new_data_set_channel_map(&data, &map);
+        pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+        pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "WaveOut on %s", output_device_name);
+        u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+        pa_sink_new_data_done(&data);
+
+        pa_assert(u->sink);
+        pa_sink_set_get_volume_callback(u->sink, sink_get_volume_cb);
+        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
+        u->sink->userdata = u;
+        u->sink->parent.process_msg = process_msg;
+    } else
+        u->sink = NULL;
+
+    pa_assert(u->source || u->sink);
+    pa_modargs_free(ma);
+
+    u->core = m->core;
+    u->hwi = hwi;
+    u->hwo = hwo;
+
+    u->fragments = nfrags;
+    u->free_ifrags = u->fragments;
+    u->free_ofrags = u->fragments;
+    u->fragment_size = frag_size - (frag_size % pa_frame_size(&ss));
+
+    u->written_bytes = 0;
+    u->sink_underflow = 1;
+
+    u->poll_timeout = pa_bytes_to_usec(u->fragments * u->fragment_size / 10, &ss);
+    pa_log_debug("Poll timeout = %.1f ms", (double) u->poll_timeout / PA_USEC_PER_MSEC);
+
+    u->cur_ihdr = 0;
+    u->cur_ohdr = 0;
+    u->ihdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments);
+    pa_assert(u->ihdrs);
+    u->ohdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments);
+    pa_assert(u->ohdrs);
+    for (i = 0; i < u->fragments; i++) {
+        u->ihdrs[i].dwBufferLength = u->fragment_size;
+        u->ohdrs[i].dwBufferLength = u->fragment_size;
+        u->ihdrs[i].lpData = pa_xmalloc(u->fragment_size);
+        pa_assert(u->ihdrs);
+        u->ohdrs[i].lpData = pa_xmalloc(u->fragment_size);
+        pa_assert(u->ohdrs);
+    }
+
+    u->module = m;
+    m->userdata = u;
+
+    /* Read mixer settings */
+    if (u->sink)
+        sink_get_volume_cb(u->sink);
+
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    if (u->sink) {
+        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+        pa_sink_set_rtpoll(u->sink, u->rtpoll);
+    }
+    if (u->source) {
+        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+        pa_source_set_rtpoll(u->source, u->rtpoll);
+    }
+
+    if (!(u->thread = pa_thread_new("waveout", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    if (u->sink)
+        pa_sink_put(u->sink);
+    if (u->source)
+        pa_source_put(u->source);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+    unsigned int i;
+
+    pa_assert(m);
+    pa_assert(m->core);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+    if (u->thread)
+        pa_thread_free(u->thread);
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->hwi != INVALID_HANDLE_VALUE) {
+        waveInReset(u->hwi);
+        waveInClose(u->hwi);
+    }
+
+    if (u->hwo != INVALID_HANDLE_VALUE) {
+        waveOutReset(u->hwo);
+        waveOutClose(u->hwo);
+    }
+
+    for (i = 0; i < u->fragments; i++) {
+        pa_xfree(u->ihdrs[i].lpData);
+        pa_xfree(u->ohdrs[i].lpData);
+    }
+
+    pa_xfree(u->ihdrs);
+    pa_xfree(u->ohdrs);
+
+    DeleteCriticalSection(&u->crit);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c
new file mode 100644 (file)
index 0000000..96476b7
--- /dev/null
@@ -0,0 +1,448 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/avahi-wrap.h>
+
+#include "module-zeroconf-discover-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+#define SERVICE_TYPE_SINK "_pulse-sink._tcp"
+#define SERVICE_TYPE_SOURCE "_non-monitor._sub._pulse-source._tcp"
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct tunnel {
+    AvahiIfIndex interface;
+    AvahiProtocol protocol;
+    char *name, *type, *domain;
+    uint32_t module_index;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    AvahiPoll *avahi_poll;
+    AvahiClient *client;
+    AvahiServiceBrowser *source_browser, *sink_browser;
+
+    pa_hashmap *tunnels;
+};
+
+static unsigned tunnel_hash(const void *p) {
+    const struct tunnel *t = p;
+
+    return
+        (unsigned) t->interface +
+        (unsigned) t->protocol +
+        pa_idxset_string_hash_func(t->name) +
+        pa_idxset_string_hash_func(t->type) +
+        pa_idxset_string_hash_func(t->domain);
+}
+
+static int tunnel_compare(const void *a, const void *b) {
+    const struct tunnel *ta = a, *tb = b;
+    int r;
+
+    if (ta->interface != tb->interface)
+        return 1;
+    if (ta->protocol != tb->protocol)
+        return 1;
+    if ((r = strcmp(ta->name, tb->name)))
+        return r;
+    if ((r = strcmp(ta->type, tb->type)))
+        return r;
+    if ((r = strcmp(ta->domain, tb->domain)))
+        return r;
+
+    return 0;
+}
+
+static struct tunnel *tunnel_new(
+        AvahiIfIndex interface, AvahiProtocol protocol,
+        const char *name, const char *type, const char *domain) {
+
+    struct tunnel *t;
+    t = pa_xnew(struct tunnel, 1);
+    t->interface = interface;
+    t->protocol = protocol;
+    t->name = pa_xstrdup(name);
+    t->type = pa_xstrdup(type);
+    t->domain = pa_xstrdup(domain);
+    t->module_index = PA_IDXSET_INVALID;
+    return t;
+}
+
+static void tunnel_free(struct tunnel *t) {
+    pa_assert(t);
+    pa_xfree(t->name);
+    pa_xfree(t->type);
+    pa_xfree(t->domain);
+    pa_xfree(t);
+}
+
+static void resolver_cb(
+        AvahiServiceResolver *r,
+        AvahiIfIndex interface, AvahiProtocol protocol,
+        AvahiResolverEvent event,
+        const char *name, const char *type, const char *domain,
+        const char *host_name, const AvahiAddress *a, uint16_t port,
+        AvahiStringList *txt,
+        AvahiLookupResultFlags flags,
+        void *userdata) {
+
+    struct userdata *u = userdata;
+    struct tunnel *tnl;
+
+    pa_assert(u);
+
+    tnl = tunnel_new(interface, protocol, name, type, domain);
+
+    if (event != AVAHI_RESOLVER_FOUND)
+        pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
+    else {
+        char *device = NULL, *dname, *module_name, *args;
+        const char *t;
+        char *if_suffix = NULL;
+        char at[AVAHI_ADDRESS_STR_MAX], cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
+        char *properties = NULL;
+        pa_sample_spec ss;
+        pa_channel_map cm;
+        AvahiStringList *l;
+        bool channel_map_set = false;
+        pa_module *m;
+
+        ss = u->core->default_sample_spec;
+        cm = u->core->default_channel_map;
+
+        for (l = txt; l; l = l->next) {
+            char *key, *value;
+            pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0);
+
+            if (pa_streq(key, "device")) {
+                pa_xfree(device);
+                device = value;
+                value = NULL;
+            } else if (pa_streq(key, "rate"))
+                ss.rate = (uint32_t) atoi(value);
+            else if (pa_streq(key, "channels"))
+                ss.channels = (uint8_t) atoi(value);
+            else if (pa_streq(key, "format"))
+                ss.format = pa_parse_sample_format(value);
+            else if (pa_streq(key, "icon-name")) {
+                pa_xfree(properties);
+                properties = pa_sprintf_malloc("device.icon_name=%s", value);
+            } else if (pa_streq(key, "channel_map")) {
+                pa_channel_map_parse(&cm, value);
+                channel_map_set = true;
+            }
+
+            avahi_free(key);
+            avahi_free(value);
+        }
+
+        if (!channel_map_set && cm.channels != ss.channels)
+            pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+        if (!pa_sample_spec_valid(&ss)) {
+            pa_log("Service '%s' contains an invalid sample specification.", name);
+            avahi_free(device);
+            pa_xfree(properties);
+            goto finish;
+        }
+
+        if (!pa_channel_map_valid(&cm) || cm.channels != ss.channels) {
+            pa_log("Service '%s' contains an invalid channel map.", name);
+            avahi_free(device);
+            pa_xfree(properties);
+            goto finish;
+        }
+
+        if (device)
+            dname = pa_sprintf_malloc("tunnel.%s.%s", host_name, device);
+        else
+            dname = pa_sprintf_malloc("tunnel.%s", host_name);
+
+        if (!pa_namereg_is_valid_name(dname)) {
+            pa_log("Cannot construct valid device name from credentials of service '%s'.", dname);
+            avahi_free(device);
+            pa_xfree(dname);
+            pa_xfree(properties);
+            goto finish;
+        }
+
+        t = strstr(type, "sink") ? "sink" : "source";
+        if (a->proto == AVAHI_PROTO_INET6 &&
+            a->data.ipv6.address[0] == 0xfe &&
+            (a->data.ipv6.address[1] & 0xc0) == 0x80)
+            if_suffix = pa_sprintf_malloc("%%%d", interface);
+
+        module_name = pa_sprintf_malloc("module-tunnel-%s", t);
+        args = pa_sprintf_malloc("server=[%s%s]:%u "
+                                 "%s=%s "
+                                 "format=%s "
+                                 "channels=%u "
+                                 "rate=%u "
+                                 "%s_properties=%s "
+                                 "%s_name=%s "
+                                 "channel_map=%s",
+                                 avahi_address_snprint(at, sizeof(at), a),
+                                 if_suffix ? if_suffix : "", port,
+                                 t, device,
+                                 pa_sample_format_to_string(ss.format),
+                                 ss.channels,
+                                 ss.rate,
+                                 t, properties ? properties : "",
+                                 t, dname,
+                                 pa_channel_map_snprint(cmt, sizeof(cmt), &cm));
+
+        pa_log_debug("Loading %s with arguments '%s'", module_name, args);
+
+        if ((m = pa_module_load(u->core, module_name, args))) {
+            tnl->module_index = m->index;
+            pa_hashmap_put(u->tunnels, tnl, tnl);
+            tnl = NULL;
+        }
+
+        pa_xfree(module_name);
+        pa_xfree(dname);
+        pa_xfree(args);
+        pa_xfree(if_suffix);
+        pa_xfree(properties);
+        avahi_free(device);
+    }
+
+finish:
+
+    avahi_service_resolver_free(r);
+
+    if (tnl)
+        tunnel_free(tnl);
+}
+
+static void browser_cb(
+        AvahiServiceBrowser *b,
+        AvahiIfIndex interface, AvahiProtocol protocol,
+        AvahiBrowserEvent event,
+        const char *name, const char *type, const char *domain,
+        AvahiLookupResultFlags flags,
+        void *userdata) {
+
+    struct userdata *u = userdata;
+    struct tunnel *t;
+
+    pa_assert(u);
+
+    if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
+        return;
+
+    t = tunnel_new(interface, protocol, name, type, domain);
+
+    if (event == AVAHI_BROWSER_NEW) {
+
+        if (!pa_hashmap_get(u->tunnels, t))
+            if (!(avahi_service_resolver_new(u->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolver_cb, u)))
+                pa_log("avahi_service_resolver_new() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+
+        /* We ignore the returned resolver object here, since the we don't
+         * need to attach any special data to it, and we can still destroy
+         * it from the callback */
+
+    } else if (event == AVAHI_BROWSER_REMOVE) {
+        struct tunnel *t2;
+
+        if ((t2 = pa_hashmap_get(u->tunnels, t))) {
+            pa_module_unload_request_by_index(u->core, t2->module_index, true);
+            pa_hashmap_remove(u->tunnels, t2);
+            tunnel_free(t2);
+        }
+    }
+
+    tunnel_free(t);
+}
+
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    u->client = c;
+
+    switch (state) {
+        case AVAHI_CLIENT_S_REGISTERING:
+        case AVAHI_CLIENT_S_RUNNING:
+        case AVAHI_CLIENT_S_COLLISION:
+
+            if (!u->sink_browser) {
+
+                if (!(u->sink_browser = avahi_service_browser_new(
+                              c,
+                              AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                              SERVICE_TYPE_SINK,
+                              NULL,
+                              0,
+                              browser_cb, u))) {
+
+                    pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
+                    pa_module_unload_request(u->module, true);
+                }
+            }
+
+            if (!u->source_browser) {
+
+                if (!(u->source_browser = avahi_service_browser_new(
+                              c,
+                              AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                              SERVICE_TYPE_SOURCE,
+                              NULL,
+                              0,
+                              browser_cb, u))) {
+
+                    pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
+                    pa_module_unload_request(u->module, true);
+                }
+            }
+
+            break;
+
+        case AVAHI_CLIENT_FAILURE:
+            if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+                int error;
+
+                pa_log_debug("Avahi daemon disconnected.");
+
+                if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+                    pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+                    pa_module_unload_request(u->module, true);
+                }
+            }
+
+            /* Fall through */
+
+        case AVAHI_CLIENT_CONNECTING:
+
+            if (u->sink_browser) {
+                avahi_service_browser_free(u->sink_browser);
+                u->sink_browser = NULL;
+            }
+
+            if (u->source_browser) {
+                avahi_service_browser_free(u->source_browser);
+                u->source_browser = NULL;
+            }
+
+            break;
+
+        default: ;
+    }
+}
+
+int pa__init(pa_module*m) {
+
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    int error;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->sink_browser = u->source_browser = NULL;
+
+    u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare);
+
+    u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
+
+    if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+        pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error));
+        goto fail;
+    }
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata*u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->client)
+        avahi_client_free(u->client);
+
+    if (u->avahi_poll)
+        pa_avahi_poll_free(u->avahi_poll);
+
+    if (u->tunnels) {
+        struct tunnel *t;
+
+        while ((t = pa_hashmap_steal_first(u->tunnels))) {
+            pa_module_unload_request_by_index(u->core, t->module_index, true);
+            tunnel_free(t);
+        }
+
+        pa_hashmap_free(u->tunnels);
+    }
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c
new file mode 100644 (file)
index 0000000..482881c
--- /dev/null
@@ -0,0 +1,937 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulse/thread-mainloop.h>
+
+#include <pulsecore/parseaddr.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/dynarray.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/avahi-wrap.h>
+#include <pulsecore/protocol-native.h>
+
+#ifdef HAVE_DBUS
+#include <pulsecore/dbus-shared.h>
+
+#define HOSTNAME_DBUS_INTERFACE "org.freedesktop.hostname1"
+#define HOSTNAME_DBUS_PATH "/org/freedesktop/hostname1"
+#define HOSTNAME_DBUS_ICON_PROPERTY "IconName"
+#endif
+
+#include "module-zeroconf-publish-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+#define SERVICE_TYPE_SINK "_pulse-sink._tcp"
+#define SERVICE_TYPE_SOURCE "_pulse-source._tcp"
+#define SERVICE_TYPE_SERVER "_pulse-server._tcp"
+#define SERVICE_SUBTYPE_SINK_HARDWARE "_hardware._sub."SERVICE_TYPE_SINK
+#define SERVICE_SUBTYPE_SINK_VIRTUAL "_virtual._sub."SERVICE_TYPE_SINK
+#define SERVICE_SUBTYPE_SOURCE_HARDWARE "_hardware._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_VIRTUAL "_virtual._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_MONITOR "_monitor._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_NON_MONITOR "_non-monitor._sub."SERVICE_TYPE_SOURCE
+
+/*
+ * Note: Because the core avahi-client calls result in synchronous D-Bus
+ * communication, calling any of those functions in the PA mainloop context
+ * could lead to the mainloop being blocked for long periods.
+ *
+ * To avoid this, we create a threaded-mainloop for Avahi calls, and push all
+ * D-Bus communication into that thread. The thumb-rule for the split is:
+ *
+ * 1. If access to PA data structures is needed, use the PA mainloop context
+ *
+ * 2. If a (blocking) avahi-client call is needed, use the Avahi mainloop
+ *
+ * We do have message queue to pass messages from the Avahi mainloop to the PA
+ * mainloop.
+ */
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct avahi_msg {
+    pa_msgobject parent;
+};
+
+typedef struct avahi_msg avahi_msg;
+PA_DEFINE_PRIVATE_CLASS(avahi_msg, pa_msgobject);
+
+enum {
+    AVAHI_MESSAGE_PUBLISH_ALL,
+    AVAHI_MESSAGE_SHUTDOWN_START,
+    AVAHI_MESSAGE_SHUTDOWN_COMPLETE,
+};
+
+enum service_subtype {
+    SUBTYPE_HARDWARE,
+    SUBTYPE_VIRTUAL,
+    SUBTYPE_MONITOR
+};
+
+struct service {
+    void *key;
+
+    struct userdata *userdata;
+    AvahiEntryGroup *entry_group;
+    char *service_name;
+    const char *service_type;
+    enum service_subtype subtype;
+
+    char *name;
+    bool is_sink;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    pa_proplist *proplist;
+};
+
+struct userdata {
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    avahi_msg *msg;
+
+    pa_core *core;
+    pa_module *module;
+    pa_mainloop_api *api;
+    pa_threaded_mainloop *mainloop;
+
+    AvahiPoll *avahi_poll;
+    AvahiClient *client;
+
+    pa_hashmap *services; /* protect with mainloop lock */
+    char *service_name;
+    char *icon_name;
+
+    AvahiEntryGroup *main_entry_group;
+
+    pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot;
+
+    pa_native_protocol *native;
+
+    bool shutting_down; /* Used in the main thread. */
+    bool client_freed; /* Used in the Avahi thread. */
+};
+
+/* Runs in PA mainloop context */
+static void get_service_data(struct service *s, pa_object *device) {
+    pa_assert(s);
+
+    if (pa_sink_isinstance(device)) {
+        pa_sink *sink = PA_SINK(device);
+
+        s->is_sink = true;
+        s->service_type = SERVICE_TYPE_SINK;
+        s->ss = sink->sample_spec;
+        s->cm = sink->channel_map;
+        s->name = pa_xstrdup(sink->name);
+        s->proplist = pa_proplist_copy(sink->proplist);
+        s->subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
+
+    } else if (pa_source_isinstance(device)) {
+        pa_source *source = PA_SOURCE(device);
+
+        s->is_sink = false;
+        s->service_type = SERVICE_TYPE_SOURCE;
+        s->ss = source->sample_spec;
+        s->cm = source->channel_map;
+        s->name = pa_xstrdup(source->name);
+        s->proplist = pa_proplist_copy(source->proplist);
+        s->subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL);
+
+    } else
+        pa_assert_not_reached();
+}
+
+/* Can be used in either PA or Avahi mainloop context since the bits of u->core
+ * that we access don't change after startup. */
+static AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) {
+    char s[128];
+    char *t;
+
+    pa_assert(c);
+
+    l = avahi_string_list_add_pair(l, "server-version", PACKAGE_NAME" "PACKAGE_VERSION);
+
+    t = pa_get_user_name_malloc();
+    l = avahi_string_list_add_pair(l, "user-name", t);
+    pa_xfree(t);
+
+    t = pa_machine_id();
+    l = avahi_string_list_add_pair(l, "machine-id", t);
+    pa_xfree(t);
+
+    t = pa_uname_string();
+    l = avahi_string_list_add_pair(l, "uname", t);
+    pa_xfree(t);
+
+    l = avahi_string_list_add_pair(l, "fqdn", pa_get_fqdn(s, sizeof(s)));
+    l = avahi_string_list_add_printf(l, "cookie=0x%08x", c->cookie);
+
+    return l;
+}
+
+static void publish_service(pa_mainloop_api *api, void *service);
+
+/* Runs in Avahi mainloop context */
+static void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
+    struct service *s = userdata;
+
+    pa_assert(s);
+
+    switch (state) {
+
+        case AVAHI_ENTRY_GROUP_ESTABLISHED:
+            pa_log_info("Successfully established service %s.", s->service_name);
+            break;
+
+        case AVAHI_ENTRY_GROUP_COLLISION: {
+            char *t;
+
+            t = avahi_alternative_service_name(s->service_name);
+            pa_log_info("Name collision, renaming %s to %s.", s->service_name, t);
+            pa_xfree(s->service_name);
+            s->service_name = t;
+
+            publish_service(NULL, s);
+            break;
+        }
+
+        case AVAHI_ENTRY_GROUP_FAILURE: {
+            pa_log("Failed to register service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
+
+            avahi_entry_group_free(g);
+            s->entry_group = NULL;
+
+            break;
+        }
+
+        case AVAHI_ENTRY_GROUP_UNCOMMITED:
+        case AVAHI_ENTRY_GROUP_REGISTERING:
+            ;
+    }
+}
+
+static void service_free(struct service *s);
+
+/* Can run in either context */
+static uint16_t compute_port(struct userdata *u) {
+    pa_strlist *i;
+
+    pa_assert(u);
+
+    for (i = pa_native_protocol_servers(u->native); i; i = pa_strlist_next(i)) {
+        pa_parsed_address a;
+
+        if (pa_parse_address(pa_strlist_data(i), &a) >= 0 &&
+            (a.type == PA_PARSED_ADDRESS_TCP4 ||
+             a.type == PA_PARSED_ADDRESS_TCP6 ||
+             a.type == PA_PARSED_ADDRESS_TCP_AUTO) &&
+            a.port > 0) {
+
+            pa_xfree(a.path_or_host);
+            return a.port;
+        }
+
+        pa_xfree(a.path_or_host);
+    }
+
+    return PA_NATIVE_DEFAULT_PORT;
+}
+
+/* Runs in Avahi mainloop context */
+static void publish_service(pa_mainloop_api *api PA_GCC_UNUSED, void *service) {
+    struct service *s = (struct service *) service;
+    int r = -1;
+    AvahiStringList *txt = NULL;
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    const char *t;
+
+    const char * const subtype_text[] = {
+        [SUBTYPE_HARDWARE] = "hardware",
+        [SUBTYPE_VIRTUAL] = "virtual",
+        [SUBTYPE_MONITOR] = "monitor"
+    };
+
+    pa_assert(s);
+
+    if (!s->userdata->client || avahi_client_get_state(s->userdata->client) != AVAHI_CLIENT_S_RUNNING)
+        return;
+
+    if (!s->entry_group) {
+        if (!(s->entry_group = avahi_entry_group_new(s->userdata->client, service_entry_group_callback, s))) {
+            pa_log("avahi_entry_group_new(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+            goto finish;
+        }
+    } else
+        avahi_entry_group_reset(s->entry_group);
+
+    txt = txt_record_server_data(s->userdata->core, txt);
+
+    txt = avahi_string_list_add_pair(txt, "device", s->name);
+    txt = avahi_string_list_add_printf(txt, "rate=%u", s->ss.rate);
+    txt = avahi_string_list_add_printf(txt, "channels=%u", s->ss.channels);
+    txt = avahi_string_list_add_pair(txt, "format", pa_sample_format_to_string(s->ss.format));
+    txt = avahi_string_list_add_pair(txt, "channel_map", pa_channel_map_snprint(cm, sizeof(cm), &s->cm));
+    txt = avahi_string_list_add_pair(txt, "subtype", subtype_text[s->subtype]);
+
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+        txt = avahi_string_list_add_pair(txt, "description", t);
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_VENDOR_NAME)))
+        txt = avahi_string_list_add_pair(txt, "vendor-name", t);
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_PRODUCT_NAME)))
+        txt = avahi_string_list_add_pair(txt, "product-name", t);
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS)))
+        txt = avahi_string_list_add_pair(txt, "class", t);
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_FORM_FACTOR)))
+        txt = avahi_string_list_add_pair(txt, "form-factor", t);
+
+    if (s->userdata->icon_name) {
+        txt = avahi_string_list_add_pair(txt, "icon-name", s->userdata->icon_name);
+    } else if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_ICON_NAME))) {
+        txt = avahi_string_list_add_pair(txt, "icon-name", t);
+    }
+
+    if (avahi_entry_group_add_service_strlst(
+                s->entry_group,
+                AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                0,
+                s->service_name,
+                s->service_type,
+                NULL,
+                NULL,
+                compute_port(s->userdata),
+                txt) < 0) {
+
+        pa_log("avahi_entry_group_add_service_strlst(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+        goto finish;
+    }
+
+    if (avahi_entry_group_add_service_subtype(
+                s->entry_group,
+                AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                0,
+                s->service_name,
+                s->service_type,
+                NULL,
+                s->is_sink ? (s->subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SINK_HARDWARE : SERVICE_SUBTYPE_SINK_VIRTUAL) :
+                (s->subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SOURCE_HARDWARE : (s->subtype == SUBTYPE_VIRTUAL ? SERVICE_SUBTYPE_SOURCE_VIRTUAL : SERVICE_SUBTYPE_SOURCE_MONITOR))) < 0) {
+
+        pa_log("avahi_entry_group_add_service_subtype(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+        goto finish;
+    }
+
+    if (!s->is_sink && s->subtype != SUBTYPE_MONITOR) {
+        if (avahi_entry_group_add_service_subtype(
+                    s->entry_group,
+                    AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                    0,
+                    s->service_name,
+                    SERVICE_TYPE_SOURCE,
+                    NULL,
+                    SERVICE_SUBTYPE_SOURCE_NON_MONITOR) < 0) {
+
+            pa_log("avahi_entry_group_add_service_subtype(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+            goto finish;
+        }
+    }
+
+    if (avahi_entry_group_commit(s->entry_group) < 0) {
+        pa_log("avahi_entry_group_commit(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+        goto finish;
+    }
+
+    r = 0;
+    pa_log_debug("Successfully created entry group for %s.", s->service_name);
+
+finish:
+
+    /* Remove this service */
+    if (r < 0)
+        pa_hashmap_remove_and_free(s->userdata->services, s->key);
+
+    avahi_string_list_free(txt);
+}
+
+/* Runs in PA mainloop context */
+static struct service *get_service(struct userdata *u, pa_object *device) {
+    struct service *s;
+    char *hn, *un;
+    const char *n;
+
+    pa_assert(u);
+    pa_object_assert_ref(device);
+
+    pa_threaded_mainloop_lock(u->mainloop);
+
+    if ((s = pa_hashmap_get(u->services, device)))
+        goto out;
+
+    s = pa_xnew(struct service, 1);
+    s->key = device;
+    s->userdata = u;
+    s->entry_group = NULL;
+
+    get_service_data(s, device);
+
+    if (!(n = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+        n = s->name;
+
+    hn = pa_get_host_name_malloc();
+    un = pa_get_user_name_malloc();
+
+    s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s", un, hn, n), AVAHI_LABEL_MAX-1);
+
+    pa_xfree(un);
+    pa_xfree(hn);
+
+    pa_hashmap_put(u->services, device, s);
+
+out:
+    pa_threaded_mainloop_unlock(u->mainloop);
+
+    return s;
+}
+
+/* Run from Avahi mainloop context */
+static void service_free(struct service *s) {
+    pa_assert(s);
+
+    if (s->entry_group) {
+        pa_log_debug("Removing entry group for %s.", s->service_name);
+        avahi_entry_group_free(s->entry_group);
+    }
+
+    pa_xfree(s->service_name);
+
+    pa_xfree(s->name);
+    pa_proplist_free(s->proplist);
+
+    pa_xfree(s);
+}
+
+/* Runs in PA mainloop context */
+static bool shall_ignore(pa_object *o) {
+    pa_object_assert_ref(o);
+
+    if (pa_sink_isinstance(o))
+        return !!(PA_SINK(o)->flags & PA_SINK_NETWORK);
+
+    if (pa_source_isinstance(o))
+        return PA_SOURCE(o)->monitor_of || (PA_SOURCE(o)->flags & PA_SOURCE_NETWORK);
+
+    pa_assert_not_reached();
+}
+
+/* Runs in PA mainloop context */
+static pa_hook_result_t device_new_or_changed_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    pa_assert(c);
+    pa_object_assert_ref(o);
+
+    if (!shall_ignore(o)) {
+        pa_threaded_mainloop_lock(u->mainloop);
+        pa_mainloop_api_once(u->api, publish_service, get_service(u, o));
+        pa_threaded_mainloop_unlock(u->mainloop);
+    }
+
+    return PA_HOOK_OK;
+}
+
+/* Runs in PA mainloop context */
+static pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    pa_assert(c);
+    pa_object_assert_ref(o);
+
+    pa_threaded_mainloop_lock(u->mainloop);
+    pa_hashmap_remove_and_free(u->services, o);
+    pa_threaded_mainloop_unlock(u->mainloop);
+
+    return PA_HOOK_OK;
+}
+
+static int publish_main_service(struct userdata *u);
+
+/* Runs in Avahi mainloop context */
+static void main_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
+    struct userdata *u = userdata;
+    pa_assert(u);
+
+    switch (state) {
+
+        case AVAHI_ENTRY_GROUP_ESTABLISHED:
+            pa_log_info("Successfully established main service.");
+            break;
+
+        case AVAHI_ENTRY_GROUP_COLLISION: {
+            char *t;
+
+            t = avahi_alternative_service_name(u->service_name);
+            pa_log_info("Name collision: renaming main service %s to %s.", u->service_name, t);
+            pa_xfree(u->service_name);
+            u->service_name = t;
+
+            publish_main_service(u);
+            break;
+        }
+
+        case AVAHI_ENTRY_GROUP_FAILURE: {
+            pa_log("Failed to register main service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
+
+            avahi_entry_group_free(g);
+            u->main_entry_group = NULL;
+            break;
+        }
+
+        case AVAHI_ENTRY_GROUP_UNCOMMITED:
+        case AVAHI_ENTRY_GROUP_REGISTERING:
+            break;
+    }
+}
+
+/* Runs in Avahi mainloop context */
+static int publish_main_service(struct userdata *u) {
+    AvahiStringList *txt = NULL;
+    int r = -1;
+
+    pa_assert(u);
+
+    if (!u->main_entry_group) {
+        if (!(u->main_entry_group = avahi_entry_group_new(u->client, main_entry_group_callback, u))) {
+            pa_log("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+            goto fail;
+        }
+    } else
+        avahi_entry_group_reset(u->main_entry_group);
+
+    txt = txt_record_server_data(u->core, txt);
+
+    if (avahi_entry_group_add_service_strlst(
+                u->main_entry_group,
+                AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                0,
+                u->service_name,
+                SERVICE_TYPE_SERVER,
+                NULL,
+                NULL,
+                compute_port(u),
+                txt) < 0) {
+
+        pa_log("avahi_entry_group_add_service_strlst() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+        goto fail;
+    }
+
+    if (avahi_entry_group_commit(u->main_entry_group) < 0) {
+        pa_log("avahi_entry_group_commit() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+        goto fail;
+    }
+
+    r = 0;
+
+fail:
+    avahi_string_list_free(txt);
+
+    return r;
+}
+
+/* Runs in PA mainloop context */
+static int publish_all_services(struct userdata *u) {
+    pa_sink *sink;
+    pa_source *source;
+    int r = -1;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    pa_log_debug("Publishing services in Zeroconf");
+
+    for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx)))
+        if (!shall_ignore(PA_OBJECT(sink))) {
+            pa_threaded_mainloop_lock(u->mainloop);
+            pa_mainloop_api_once(u->api, publish_service, get_service(u, PA_OBJECT(sink)));
+            pa_threaded_mainloop_unlock(u->mainloop);
+        }
+
+    for (source = PA_SOURCE(pa_idxset_first(u->core->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(u->core->sources, &idx)))
+        if (!shall_ignore(PA_OBJECT(source))) {
+            pa_threaded_mainloop_lock(u->mainloop);
+            pa_mainloop_api_once(u->api, publish_service, get_service(u, PA_OBJECT(source)));
+            pa_threaded_mainloop_unlock(u->mainloop);
+        }
+
+    if (publish_main_service(u) < 0)
+        goto fail;
+
+    r = 0;
+
+fail:
+    return r;
+}
+
+/* Runs in Avahi mainloop context */
+static void unpublish_all_services(struct userdata *u, bool rem) {
+    void *state = NULL;
+    struct service *s;
+
+    pa_assert(u);
+
+    pa_log_debug("Unpublishing services in Zeroconf");
+
+    while ((s = pa_hashmap_iterate(u->services, &state, NULL))) {
+        if (s->entry_group) {
+            if (rem) {
+                pa_log_debug("Removing entry group for %s.", s->service_name);
+                avahi_entry_group_free(s->entry_group);
+                s->entry_group = NULL;
+            } else {
+                avahi_entry_group_reset(s->entry_group);
+                pa_log_debug("Resetting entry group for %s.", s->service_name);
+            }
+        }
+    }
+
+    if (u->main_entry_group) {
+        if (rem) {
+            pa_log_debug("Removing main entry group.");
+            avahi_entry_group_free(u->main_entry_group);
+            u->main_entry_group = NULL;
+        } else {
+            avahi_entry_group_reset(u->main_entry_group);
+            pa_log_debug("Resetting main entry group.");
+        }
+    }
+}
+
+/* Runs in PA mainloop context */
+static int avahi_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = (struct userdata *) data;
+
+    pa_assert(u);
+
+    if (u->shutting_down)
+        return 0;
+
+    switch (code) {
+        case AVAHI_MESSAGE_PUBLISH_ALL:
+            publish_all_services(u);
+            break;
+
+        case AVAHI_MESSAGE_SHUTDOWN_START:
+            pa_module_unload(u->module, true);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    return 0;
+}
+
+#ifdef HAVE_DBUS
+static char *get_icon_name(pa_module*m) {
+    const char *interface = HOSTNAME_DBUS_INTERFACE;
+    const char *property = HOSTNAME_DBUS_ICON_PROPERTY;
+    char *icon_name = NULL;
+    pa_dbus_connection *bus;
+    DBusError error;
+    DBusMessageIter args;
+    DBusMessage *msg = NULL;
+    DBusMessage *reply = NULL;
+    DBusConnection *conn = NULL;
+    DBusMessageIter sub;
+
+    dbus_error_init(&error);
+
+    if (!(bus = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error))) {
+        pa_log("Failed to get system bus connection: %s", error.message);
+        dbus_error_free(&error);
+        goto out;
+    }
+
+    conn = pa_dbus_connection_get(bus);
+
+    msg = dbus_message_new_method_call(HOSTNAME_DBUS_INTERFACE,
+                                       HOSTNAME_DBUS_PATH,
+                                       "org.freedesktop.DBus.Properties",
+                                       "Get");
+    dbus_message_append_args(msg, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID);
+
+    if ((reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &error)) == NULL) {
+        pa_log("Failed to send: %s:%s", error.name, error.message);
+        dbus_error_free(&error);
+        goto out;
+    }
+
+    dbus_message_iter_init(reply, &args);
+    if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_VARIANT) {
+        pa_log("Incorrect reply type");
+        goto out;
+    }
+
+    dbus_message_iter_recurse(&args, &sub);
+
+    if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
+        pa_log("Incorrect value type");
+        goto out;
+    }
+
+    dbus_message_iter_get_basic(&sub, &icon_name);
+    icon_name = pa_xstrdup(icon_name);
+
+out:
+    if (reply)
+        dbus_message_unref(reply);
+
+    if (msg)
+        dbus_message_unref(msg);
+
+    if (bus)
+        pa_dbus_connection_unref(bus);
+
+    return icon_name;
+}
+#endif
+
+/* Runs in Avahi mainloop context */
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    u->client = c;
+
+    switch (state) {
+        case AVAHI_CLIENT_S_RUNNING:
+            /* Collect all sinks/sources, and publish them */
+            pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_PUBLISH_ALL, u, 0, NULL, NULL);
+
+#ifdef HAVE_DBUS
+            /* Request icon name through D-BUS */
+            u->icon_name = get_icon_name(u->module);
+#endif
+
+            break;
+
+        case AVAHI_CLIENT_S_COLLISION:
+            pa_log_debug("Host name collision");
+            unpublish_all_services(u, false);
+            break;
+
+        case AVAHI_CLIENT_FAILURE:
+            if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+                int error;
+
+                pa_log_debug("Avahi daemon disconnected.");
+
+                unpublish_all_services(u, true);
+                avahi_client_free(u->client);
+
+                if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+                    pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+                    pa_module_unload_request(u->module, true);
+                }
+            }
+
+            break;
+
+        default: ;
+    }
+}
+
+/* Runs in Avahi mainloop context */
+static void create_client(pa_mainloop_api *api PA_GCC_UNUSED, void *userdata) {
+    struct userdata *u = (struct userdata *) userdata;
+    int error;
+
+    /* create_client() and client_free() are called via defer events. If the
+     * two defer events are created very quickly one after another, we can't
+     * assume that the defer event that runs create_client() will be dispatched
+     * before the defer event that runs client_free() (at the time of writing,
+     * pa_mainloop actually always dispatches queued defer events in reverse
+     * creation order). For that reason we must be prepared for the case where
+     * client_free() has already been called. */
+    if (u->client_freed)
+        return;
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+        pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+        goto fail;
+    }
+
+    pa_log_debug("Started Avahi threaded mainloop");
+
+    return;
+
+fail:
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_SHUTDOWN_START, u, 0, NULL, NULL);
+}
+
+int pa__init(pa_module*m) {
+
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    char *hn, *un;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->native = pa_native_protocol_get(u->core);
+
+    u->rtpoll = pa_rtpoll_new();
+    u->mainloop = pa_threaded_mainloop_new();
+    u->api = pa_threaded_mainloop_get_api(u->mainloop);
+
+    if (pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->msg = pa_msgobject_new(avahi_msg);
+    u->msg->parent.process_msg = avahi_process_msg;
+
+    u->avahi_poll = pa_avahi_poll_new(u->api);
+
+    u->services = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) service_free);
+
+    u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
+    u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
+
+    un = pa_get_user_name_malloc();
+    hn = pa_get_host_name_malloc();
+    u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", un, hn), AVAHI_LABEL_MAX-1);
+    pa_xfree(un);
+    pa_xfree(hn);
+
+    pa_threaded_mainloop_set_name(u->mainloop, "avahi-ml");
+    if (pa_threaded_mainloop_start(u->mainloop) < 0)
+        goto fail;
+
+    pa_threaded_mainloop_lock(u->mainloop);
+    pa_mainloop_api_once(u->api, create_client, u);
+    pa_threaded_mainloop_unlock(u->mainloop);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+/* Runs in Avahi mainloop context */
+static void client_free(pa_mainloop_api *api PA_GCC_UNUSED, void *userdata) {
+    struct userdata *u = (struct userdata *) userdata;
+
+    pa_hashmap_free(u->services);
+
+    if (u->main_entry_group)
+        avahi_entry_group_free(u->main_entry_group);
+
+    if (u->client)
+        avahi_client_free(u->client);
+
+    if (u->avahi_poll)
+        pa_avahi_poll_free(u->avahi_poll);
+
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_SHUTDOWN_COMPLETE, u, 0, NULL, NULL);
+
+    u->client_freed = true;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata*u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    u->shutting_down = true;
+
+    pa_threaded_mainloop_lock(u->mainloop);
+    pa_mainloop_api_once(u->api, client_free, u);
+    pa_threaded_mainloop_unlock(u->mainloop);
+    pa_asyncmsgq_wait_for(u->thread_mq.outq, AVAHI_MESSAGE_SHUTDOWN_COMPLETE);
+
+    pa_threaded_mainloop_stop(u->mainloop);
+    pa_threaded_mainloop_free(u->mainloop);
+
+    pa_thread_mq_done(&u->thread_mq);
+    pa_rtpoll_free(u->rtpoll);
+
+    if (u->sink_new_slot)
+        pa_hook_slot_free(u->sink_new_slot);
+    if (u->source_new_slot)
+        pa_hook_slot_free(u->source_new_slot);
+    if (u->sink_changed_slot)
+        pa_hook_slot_free(u->sink_changed_slot);
+    if (u->source_changed_slot)
+        pa_hook_slot_free(u->source_changed_slot);
+    if (u->sink_unlink_slot)
+        pa_hook_slot_free(u->sink_unlink_slot);
+    if (u->source_unlink_slot)
+        pa_hook_slot_free(u->source_unlink_slot);
+
+    if (u->native)
+        pa_native_protocol_unref(u->native);
+
+    pa_xfree(u->msg);
+    pa_xfree(u->service_name);
+    pa_xfree(u->icon_name);
+    pa_xfree(u);
+}
diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
new file mode 100644 (file)
index 0000000..4631ff3
--- /dev/null
@@ -0,0 +1,1582 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* General power management rules:
+ *
+ *   When SUSPENDED we close the audio device.
+ *
+ *   We make no difference between IDLE and RUNNING in our handling.
+ *
+ *   As long as we are in RUNNING/IDLE state we will *always* write data to
+ *   the device. If none is available from the inputs, we write silence
+ *   instead.
+ *
+ *   If power should be saved on IDLE module-suspend-on-idle should be used.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/poll.h>
+
+#if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
+#include <sys/audioio.h>
+#include <sys/syscall.h>
+#endif
+
+#include "oss-util.h"
+#include "module-oss-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("OSS Sink/Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "sink_properties=<properties for the sink> "
+        "source_name=<name for the source> "
+        "source_properties=<properties for the source> "
+        "device=<OSS device> "
+        "record=<enable source?> "
+        "playback=<enable sink?> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map> "
+        "fragments=<number of fragments> "
+        "fragment_size=<fragment size> "
+        "mmap=<enable memory mapping?>");
+#ifdef __linux__
+PA_MODULE_DEPRECATED("Please use module-alsa-card instead of module-oss!");
+#endif
+
+#define DEFAULT_DEVICE "/dev/dsp"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+    pa_source *source;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    char *device_name;
+
+    pa_memchunk memchunk;
+
+    size_t frame_size;
+    uint32_t in_fragment_size, out_fragment_size, in_nfrags, out_nfrags, in_hwbuf_size, out_hwbuf_size;
+    bool use_getospace, use_getispace;
+    bool use_getodelay;
+
+    bool sink_suspended, source_suspended;
+
+    int fd;
+    int mode;
+
+    int mixer_fd;
+    int mixer_devmask;
+
+    int nfrags, frag_size, orig_frag_size;
+
+    bool use_mmap;
+    unsigned out_mmap_current, in_mmap_current;
+    void *in_mmap, *out_mmap;
+    pa_memblock **in_mmap_memblocks, **out_mmap_memblocks;
+
+    int in_mmap_saved_nfrags, out_mmap_saved_nfrags;
+
+    pa_rtpoll_item *rtpoll_item;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "source_name",
+    "source_properties",
+    "device",
+    "record",
+    "playback",
+    "fragments",
+    "fragment_size",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "mmap",
+    NULL
+};
+
+static int trigger(struct userdata *u, bool quick) {
+    int enable_bits = 0, zero = 0;
+
+    pa_assert(u);
+
+    if (u->fd < 0)
+        return 0;
+
+    pa_log_debug("trigger");
+
+    if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+        enable_bits |= PCM_ENABLE_INPUT;
+
+    if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
+        enable_bits |= PCM_ENABLE_OUTPUT;
+
+    pa_log_debug("trigger: %i", enable_bits);
+
+    if (u->use_mmap) {
+
+        if (!quick)
+            ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero);
+
+#ifdef SNDCTL_DSP_HALT
+        if (enable_bits == 0)
+            if (ioctl(u->fd, SNDCTL_DSP_HALT, NULL) < 0)
+                pa_log_warn("SNDCTL_DSP_HALT: %s", pa_cstrerror(errno));
+#endif
+
+        if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0)
+            pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno));
+
+        if (u->sink && !(enable_bits & PCM_ENABLE_OUTPUT)) {
+            pa_log_debug("clearing playback buffer");
+            pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &u->sink->sample_spec);
+        }
+
+    } else {
+
+        if (enable_bits)
+            if (ioctl(u->fd, SNDCTL_DSP_POST, NULL) < 0)
+                pa_log_warn("SNDCTL_DSP_POST: %s", pa_cstrerror(errno));
+
+        if (!quick) {
+            /*
+             * Some crappy drivers do not start the recording until we
+             * read something.  Without this snippet, poll will never
+             * register the fd as ready.
+             */
+
+            if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+                uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size);
+
+                if (pa_read(u->fd, buf, u->in_fragment_size, NULL) < 0) {
+                    pa_log("pa_read() failed: %s", pa_cstrerror(errno));
+                    pa_xfree(buf);
+                    return -1;
+                }
+
+                pa_xfree(buf);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static void mmap_fill_memblocks(struct userdata *u, unsigned n) {
+    pa_assert(u);
+    pa_assert(u->out_mmap_memblocks);
+
+/*     pa_log("Mmmap writing %u blocks", n); */
+
+    while (n > 0) {
+        pa_memchunk chunk;
+
+        if (u->out_mmap_memblocks[u->out_mmap_current])
+            pa_memblock_unref_fixed(u->out_mmap_memblocks[u->out_mmap_current]);
+
+        chunk.memblock = u->out_mmap_memblocks[u->out_mmap_current] =
+            pa_memblock_new_fixed(
+                    u->core->mempool,
+                    (uint8_t*) u->out_mmap + u->out_fragment_size * u->out_mmap_current,
+                    u->out_fragment_size,
+                    1);
+
+        chunk.length = pa_memblock_get_length(chunk.memblock);
+        chunk.index = 0;
+
+        pa_sink_render_into_full(u->sink, &chunk);
+
+        u->out_mmap_current++;
+        while (u->out_mmap_current >= u->out_nfrags)
+            u->out_mmap_current -= u->out_nfrags;
+
+        n--;
+    }
+}
+
+static int mmap_write(struct userdata *u) {
+    struct count_info info;
+
+    pa_assert(u);
+    pa_assert(u->sink);
+
+/*     pa_log("Mmmap writing..."); */
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
+        pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    info.blocks += u->out_mmap_saved_nfrags;
+    u->out_mmap_saved_nfrags = 0;
+
+    if (info.blocks > 0)
+        mmap_fill_memblocks(u, (unsigned) info.blocks);
+
+    return info.blocks;
+}
+
+static void mmap_post_memblocks(struct userdata *u, unsigned n) {
+    pa_assert(u);
+    pa_assert(u->in_mmap_memblocks);
+
+/*     pa_log("Mmmap reading %u blocks", n); */
+
+    while (n > 0) {
+        pa_memchunk chunk;
+
+        if (!u->in_mmap_memblocks[u->in_mmap_current]) {
+
+            chunk.memblock = u->in_mmap_memblocks[u->in_mmap_current] =
+                pa_memblock_new_fixed(
+                        u->core->mempool,
+                        (uint8_t*) u->in_mmap + u->in_fragment_size*u->in_mmap_current,
+                        u->in_fragment_size,
+                        1);
+
+            chunk.length = pa_memblock_get_length(chunk.memblock);
+            chunk.index = 0;
+
+            pa_source_post(u->source, &chunk);
+        }
+
+        u->in_mmap_current++;
+        while (u->in_mmap_current >= u->in_nfrags)
+            u->in_mmap_current -= u->in_nfrags;
+
+        n--;
+    }
+}
+
+static void mmap_clear_memblocks(struct userdata*u, unsigned n) {
+    unsigned i = u->in_mmap_current;
+
+    pa_assert(u);
+    pa_assert(u->in_mmap_memblocks);
+
+    if (n > u->in_nfrags)
+        n = u->in_nfrags;
+
+    while (n > 0) {
+        if (u->in_mmap_memblocks[i]) {
+            pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
+            u->in_mmap_memblocks[i] = NULL;
+        }
+
+        i++;
+        while (i >= u->in_nfrags)
+            i -= u->in_nfrags;
+
+        n--;
+    }
+}
+
+static int mmap_read(struct userdata *u) {
+    struct count_info info;
+    pa_assert(u);
+    pa_assert(u->source);
+
+/*     pa_log("Mmmap reading..."); */
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
+        pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+/*     pa_log("... %i", info.blocks); */
+
+    info.blocks += u->in_mmap_saved_nfrags;
+    u->in_mmap_saved_nfrags = 0;
+
+    if (info.blocks > 0) {
+        mmap_post_memblocks(u, (unsigned) info.blocks);
+        mmap_clear_memblocks(u, u->in_nfrags/2);
+    }
+
+    return info.blocks;
+}
+
+static pa_usec_t mmap_sink_get_latency(struct userdata *u) {
+    struct count_info info;
+    size_t bpos, n;
+
+    pa_assert(u);
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
+        pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
+        return 0;
+    }
+
+    u->out_mmap_saved_nfrags += info.blocks;
+
+    bpos = ((u->out_mmap_current + (unsigned) u->out_mmap_saved_nfrags) * u->out_fragment_size) % u->out_hwbuf_size;
+
+    if (bpos <= (size_t) info.ptr)
+        n = u->out_hwbuf_size - ((size_t) info.ptr - bpos);
+    else
+        n = bpos - (size_t) info.ptr;
+
+/*     pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->out_fragment_size, u->out_fragments); */
+
+    return pa_bytes_to_usec(n, &u->sink->sample_spec);
+}
+
+static pa_usec_t mmap_source_get_latency(struct userdata *u) {
+    struct count_info info;
+    size_t bpos, n;
+
+    pa_assert(u);
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
+        pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
+        return 0;
+    }
+
+    u->in_mmap_saved_nfrags += info.blocks;
+    bpos = ((u->in_mmap_current + (unsigned) u->in_mmap_saved_nfrags) * u->in_fragment_size) % u->in_hwbuf_size;
+
+    if (bpos <= (size_t) info.ptr)
+        n = (size_t) info.ptr - bpos;
+    else
+        n = u->in_hwbuf_size - bpos + (size_t) info.ptr;
+
+/*     pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->in_fragment_size, u->in_fragments);  */
+
+    return pa_bytes_to_usec(n, &u->source->sample_spec);
+}
+
+static pa_usec_t io_sink_get_latency(struct userdata *u) {
+    pa_usec_t r = 0;
+
+    pa_assert(u);
+
+    if (u->use_getodelay) {
+        int arg;
+#if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
+#if defined(AUDIO_GETBUFINFO)
+        struct audio_info info;
+        if (syscall(SYS_ioctl, u->fd, AUDIO_GETBUFINFO, &info) < 0) {
+            pa_log_info("Device doesn't support AUDIO_GETBUFINFO: %s", pa_cstrerror(errno));
+            u->use_getodelay = 0;
+        } else {
+            arg = info.play.seek + info.blocksize / 2;
+            r = pa_bytes_to_usec((size_t) arg, &u->sink->sample_spec);
+        }
+#else
+        pa_log_info("System doesn't support AUDIO_GETBUFINFO");
+        u->use_getodelay = 0;
+#endif
+#else
+        if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
+            pa_log_info("Device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno));
+            u->use_getodelay = 0;
+        } else
+            r = pa_bytes_to_usec((size_t) arg, &u->sink->sample_spec);
+#endif
+    }
+
+    if (!u->use_getodelay && u->use_getospace) {
+        struct audio_buf_info info;
+
+        if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
+            pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
+            u->use_getospace = 0;
+        } else
+            r = pa_bytes_to_usec((size_t) info.bytes, &u->sink->sample_spec);
+    }
+
+    if (u->memchunk.memblock)
+        r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
+
+    return r;
+}
+
+static pa_usec_t io_source_get_latency(struct userdata *u) {
+    pa_usec_t r = 0;
+
+    pa_assert(u);
+
+    if (u->use_getispace) {
+        struct audio_buf_info info;
+
+        if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
+            pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
+            u->use_getispace = 0;
+        } else
+            r = pa_bytes_to_usec((size_t) info.bytes, &u->source->sample_spec);
+    }
+
+    return r;
+}
+
+static void build_pollfd(struct userdata *u) {
+    struct pollfd *pollfd;
+
+    pa_assert(u);
+    pa_assert(u->fd >= 0);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+    pollfd->fd = u->fd;
+    pollfd->events = 0;
+    pollfd->revents = 0;
+}
+
+/* Called from IO context */
+static int suspend(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->fd >= 0);
+
+    pa_log_info("Suspending...");
+
+    if (u->out_mmap_memblocks) {
+        unsigned i;
+        for (i = 0; i < u->out_nfrags; i++)
+            if (u->out_mmap_memblocks[i]) {
+                pa_memblock_unref_fixed(u->out_mmap_memblocks[i]);
+                u->out_mmap_memblocks[i] = NULL;
+            }
+    }
+
+    if (u->in_mmap_memblocks) {
+        unsigned i;
+        for (i = 0; i < u->in_nfrags; i++)
+            if (u->in_mmap_memblocks[i]) {
+                pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
+                u->in_mmap_memblocks[i] = NULL;
+            }
+    }
+
+    if (u->in_mmap && u->in_mmap != MAP_FAILED) {
+        munmap(u->in_mmap, u->in_hwbuf_size);
+        u->in_mmap = NULL;
+    }
+
+    if (u->out_mmap && u->out_mmap != MAP_FAILED) {
+        munmap(u->out_mmap, u->out_hwbuf_size);
+        u->out_mmap = NULL;
+    }
+
+    /* Let's suspend */
+    ioctl(u->fd, SNDCTL_DSP_SYNC, NULL);
+    pa_close(u->fd);
+    u->fd = -1;
+
+    if (u->rtpoll_item) {
+        pa_rtpoll_item_free(u->rtpoll_item);
+        u->rtpoll_item = NULL;
+    }
+
+    pa_log_info("Device suspended...");
+
+    return 0;
+}
+
+/* Called from IO context */
+static int unsuspend(struct userdata *u) {
+    int m;
+    pa_sample_spec ss, *ss_original;
+    int frag_size, in_frag_size, out_frag_size;
+    int in_nfrags, out_nfrags;
+    struct audio_buf_info info;
+
+    pa_assert(u);
+    pa_assert(u->fd < 0);
+
+    m = u->mode;
+
+    pa_log_info("Trying resume...");
+
+    if ((u->fd = pa_oss_open(u->device_name, &m, NULL)) < 0) {
+        pa_log_warn("Resume failed, device busy (%s)", pa_cstrerror(errno));
+        return -1;
+    }
+
+    if (m != u->mode) {
+        pa_log_warn("Resume failed, couldn't open device with original access mode.");
+        goto fail;
+    }
+
+    if (u->nfrags >= 2 && u->frag_size >= 1)
+        if (pa_oss_set_fragments(u->fd, u->nfrags, u->orig_frag_size) < 0) {
+            pa_log_warn("Resume failed, couldn't set original fragment settings.");
+            goto fail;
+        }
+
+    ss = *(ss_original = u->sink ? &u->sink->sample_spec : &u->source->sample_spec);
+    if (pa_oss_auto_format(u->fd, &ss) < 0 || !pa_sample_spec_equal(&ss, ss_original)) {
+        pa_log_warn("Resume failed, couldn't set original sample format settings.");
+        goto fail;
+    }
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
+        pa_log_warn("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    in_frag_size = out_frag_size = frag_size;
+    in_nfrags = out_nfrags = u->nfrags;
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
+        in_frag_size = info.fragsize;
+        in_nfrags = info.fragstotal;
+    }
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
+        out_frag_size = info.fragsize;
+        out_nfrags = info.fragstotal;
+    }
+
+    if ((u->source && (in_frag_size != (int) u->in_fragment_size || in_nfrags != (int) u->in_nfrags)) ||
+        (u->sink && (out_frag_size != (int) u->out_fragment_size || out_nfrags != (int) u->out_nfrags))) {
+        pa_log_warn("Resume failed, input fragment settings don't match.");
+        goto fail;
+    }
+
+    if (u->use_mmap) {
+        if (u->source) {
+            if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
+                pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno));
+                goto fail;
+            }
+        }
+
+        if (u->sink) {
+            if ((u->out_mmap = mmap(NULL, u->out_hwbuf_size, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
+                pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno));
+                if (u->in_mmap && u->in_mmap != MAP_FAILED) {
+                    munmap(u->in_mmap, u->in_hwbuf_size);
+                    u->in_mmap = NULL;
+                }
+
+                goto fail;
+            }
+
+            pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &ss);
+        }
+    }
+
+    u->out_mmap_current = u->in_mmap_current = 0;
+    u->out_mmap_saved_nfrags = u->in_mmap_saved_nfrags = 0;
+
+    pa_assert(!u->rtpoll_item);
+
+    build_pollfd(u);
+
+    if (u->sink && u->sink->get_volume)
+        u->sink->get_volume(u->sink);
+    if (u->source && u->source->get_volume)
+        u->source->get_volume(u->source);
+
+    pa_log_info("Resumed successfully...");
+
+    return 0;
+
+fail:
+    pa_close(u->fd);
+    u->fd = -1;
+    return -1;
+}
+
+/* Called from IO context */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+    int ret;
+    bool do_trigger = false, quick = true;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t r = 0;
+
+            if (u->fd >= 0) {
+                if (u->use_mmap)
+                    r = mmap_sink_get_latency(u);
+                else
+                    r = io_sink_get_latency(u);
+            }
+
+            *((int64_t*) data) = (int64_t)r;
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SINK_SUSPENDED:
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+
+                    if (!u->source || u->source_suspended) {
+                        if (suspend(u) < 0)
+                            return -1;
+                    }
+
+                    do_trigger = true;
+
+                    u->sink_suspended = true;
+                    break;
+
+                case PA_SINK_IDLE:
+                case PA_SINK_RUNNING:
+
+                    if (u->sink->thread_info.state == PA_SINK_INIT) {
+                        do_trigger = true;
+                        quick = u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state);
+                    }
+
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+
+                        if (!u->source || u->source_suspended) {
+                            if (unsuspend(u) < 0)
+                                return -1;
+                            quick = false;
+                        }
+
+                        do_trigger = true;
+
+                        u->out_mmap_current = 0;
+                        u->out_mmap_saved_nfrags = 0;
+
+                        u->sink_suspended = false;
+                    }
+
+                    break;
+
+                case PA_SINK_INVALID_STATE:
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                    ;
+            }
+
+            break;
+
+    }
+
+    ret = pa_sink_process_msg(o, code, data, offset, chunk);
+
+    if (ret >= 0 && do_trigger) {
+        if (trigger(u, quick) < 0)
+            return -1;
+    }
+
+    return ret;
+}
+
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+    int ret;
+    int do_trigger = false, quick = true;
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            pa_usec_t r = 0;
+
+            if (u->fd >= 0) {
+                if (u->use_mmap)
+                    r = mmap_source_get_latency(u);
+                else
+                    r = io_source_get_latency(u);
+            }
+
+            *((int64_t*) data) = (int64_t)r;
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_SET_STATE:
+
+            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+                case PA_SOURCE_SUSPENDED:
+                    pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
+
+                    if (!u->sink || u->sink_suspended) {
+                        if (suspend(u) < 0)
+                            return -1;
+                    }
+
+                    do_trigger = true;
+
+                    u->source_suspended = true;
+                    break;
+
+                case PA_SOURCE_IDLE:
+                case PA_SOURCE_RUNNING:
+
+                    if (u->source->thread_info.state == PA_SOURCE_INIT) {
+                        do_trigger = true;
+                        quick = u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state);
+                    }
+
+                    if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
+
+                        if (!u->sink || u->sink_suspended) {
+                            if (unsuspend(u) < 0)
+                                return -1;
+                            quick = false;
+                        }
+
+                        do_trigger = true;
+
+                        u->in_mmap_current = 0;
+                        u->in_mmap_saved_nfrags = 0;
+
+                        u->source_suspended = false;
+                    }
+                    break;
+
+                case PA_SOURCE_UNLINKED:
+                case PA_SOURCE_INIT:
+                case PA_SOURCE_INVALID_STATE:
+                    ;
+
+            }
+            break;
+
+    }
+
+    ret = pa_source_process_msg(o, code, data, offset, chunk);
+
+    if (ret >= 0 && do_trigger) {
+        if (trigger(u, quick) < 0)
+            return -1;
+    }
+
+    return ret;
+}
+
+static void sink_get_volume(pa_sink *s) {
+    struct userdata *u;
+
+    pa_assert_se(u = s->userdata);
+
+    pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
+
+    if (u->mixer_devmask & SOUND_MASK_VOLUME)
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->real_volume) >= 0)
+            return;
+
+    if (u->mixer_devmask & SOUND_MASK_PCM)
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->real_volume) >= 0)
+            return;
+
+    pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
+}
+
+static void sink_set_volume(pa_sink *s) {
+    struct userdata *u;
+
+    pa_assert_se(u = s->userdata);
+
+    pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
+
+    if (u->mixer_devmask & SOUND_MASK_VOLUME)
+        if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->real_volume) >= 0)
+            return;
+
+    if (u->mixer_devmask & SOUND_MASK_PCM)
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->real_volume) >= 0)
+            return;
+
+    pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
+}
+
+static void source_get_volume(pa_source *s) {
+    struct userdata *u;
+
+    pa_assert_se(u = s->userdata);
+
+    pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
+
+    if (u->mixer_devmask & SOUND_MASK_IGAIN)
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->real_volume) >= 0)
+            return;
+
+    if (u->mixer_devmask & SOUND_MASK_RECLEV)
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->real_volume) >= 0)
+            return;
+
+    pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
+}
+
+static void source_set_volume(pa_source *s) {
+    struct userdata *u;
+
+    pa_assert_se(u = s->userdata);
+
+    pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
+
+    if (u->mixer_devmask & SOUND_MASK_IGAIN)
+        if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->real_volume) >= 0)
+            return;
+
+    if (u->mixer_devmask & SOUND_MASK_RECLEV)
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->real_volume) >= 0)
+            return;
+
+    pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    int write_type = 0, read_type = 0;
+    short revents = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        int ret;
+
+/*        pa_log("loop");    */
+
+        if (PA_UNLIKELY(u->sink && u->sink->thread_info.rewind_requested))
+            pa_sink_process_rewind(u->sink, 0);
+
+        /* Render some data and write it to the dsp */
+
+        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
+
+            if (u->use_mmap) {
+
+                if ((ret = mmap_write(u)) < 0)
+                    goto fail;
+
+                revents &= ~POLLOUT;
+
+                if (ret > 0)
+                    continue;
+
+            } else {
+                ssize_t l;
+                bool loop = false, work_done = false;
+
+                l = (ssize_t) u->out_fragment_size;
+
+                if (u->use_getospace) {
+                    audio_buf_info info;
+
+                    if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
+                        pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
+                        u->use_getospace = false;
+                    } else {
+                        l = info.bytes;
+
+                        /* We loop only if GETOSPACE worked and we
+                         * actually *know* that we can write more than
+                         * one fragment at a time */
+                        loop = true;
+                    }
+                }
+
+                /* Round down to multiples of the fragment size,
+                 * because OSS needs that (at least some versions
+                 * do) */
+                l = (l/(ssize_t) u->out_fragment_size) * (ssize_t) u->out_fragment_size;
+
+                /* Hmm, so poll() signalled us that we can read
+                 * something, but GETOSPACE told us there was nothing?
+                 * Hmm, make the best of it, try to read some data, to
+                 * avoid spinning forever. */
+                if (l <= 0 && (revents & POLLOUT)) {
+                    l = (ssize_t) u->out_fragment_size;
+                    loop = false;
+                }
+
+                while (l > 0) {
+                    void *p;
+                    ssize_t t;
+
+                    if (u->memchunk.length <= 0)
+                        pa_sink_render(u->sink, (size_t) l, &u->memchunk);
+
+                    pa_assert(u->memchunk.length > 0);
+
+                    p = pa_memblock_acquire(u->memchunk.memblock);
+                    t = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
+                    pa_memblock_release(u->memchunk.memblock);
+
+/*                     pa_log("wrote %i bytes of %u", t, l); */
+
+                    pa_assert(t != 0);
+
+                    if (t < 0) {
+
+                        if (errno == EINTR)
+                            continue;
+
+                        else if (errno == EAGAIN) {
+                            pa_log_debug("EAGAIN");
+
+                            revents &= ~POLLOUT;
+                            break;
+
+                        } else {
+                            pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
+                            goto fail;
+                        }
+
+                    } else {
+
+                        u->memchunk.index += (size_t) t;
+                        u->memchunk.length -= (size_t) t;
+
+                        if (u->memchunk.length <= 0) {
+                            pa_memblock_unref(u->memchunk.memblock);
+                            pa_memchunk_reset(&u->memchunk);
+                        }
+
+                        l -= t;
+
+                        revents &= ~POLLOUT;
+                        work_done = true;
+                    }
+
+                    if (!loop)
+                        break;
+                }
+
+                if (work_done)
+                    continue;
+            }
+        }
+
+        /* Try to read some data and pass it on to the source driver. */
+
+        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
+
+            if (u->use_mmap) {
+
+                if ((ret = mmap_read(u)) < 0)
+                    goto fail;
+
+                revents &= ~POLLIN;
+
+                if (ret > 0)
+                    continue;
+
+            } else {
+
+                void *p;
+                ssize_t l;
+                pa_memchunk memchunk;
+                bool loop = false, work_done = false;
+
+                l = (ssize_t) u->in_fragment_size;
+
+                if (u->use_getispace) {
+                    audio_buf_info info;
+
+                    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
+                        pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
+                        u->use_getispace = false;
+                    } else {
+                        l = info.bytes;
+                        loop = true;
+                    }
+                }
+
+                l = (l/(ssize_t) u->in_fragment_size) * (ssize_t) u->in_fragment_size;
+
+                if (l <= 0 && (revents & POLLIN)) {
+                    l = (ssize_t) u->in_fragment_size;
+                    loop = false;
+                }
+
+                while (l > 0) {
+                    ssize_t t;
+                    size_t k;
+
+                    pa_assert(l > 0);
+
+                    memchunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
+
+                    k = pa_memblock_get_length(memchunk.memblock);
+
+                    if (k > (size_t) l)
+                        k = (size_t) l;
+
+                    k = (k/u->frame_size)*u->frame_size;
+
+                    p = pa_memblock_acquire(memchunk.memblock);
+                    t = pa_read(u->fd, p, k, &read_type);
+                    pa_memblock_release(memchunk.memblock);
+
+                    pa_assert(t != 0); /* EOF cannot happen */
+
+/*                     pa_log("read %i bytes of %u", t, l); */
+
+                    if (t < 0) {
+                        pa_memblock_unref(memchunk.memblock);
+
+                        if (errno == EINTR)
+                            continue;
+
+                        else if (errno == EAGAIN) {
+                            pa_log_debug("EAGAIN");
+
+                            revents &= ~POLLIN;
+                            break;
+
+                        } else {
+                            pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
+                            goto fail;
+                        }
+
+                    } else {
+                        memchunk.index = 0;
+                        memchunk.length = (size_t) t;
+
+                        pa_source_post(u->source, &memchunk);
+                        pa_memblock_unref(memchunk.memblock);
+
+                        l -= t;
+
+                        revents &= ~POLLIN;
+                        work_done = true;
+                    }
+
+                    if (!loop)
+                        break;
+                }
+
+                if (work_done)
+                    continue;
+            }
+        }
+
+/*         pa_log("loop2 revents=%i", revents); */
+
+        if (u->rtpoll_item) {
+            struct pollfd *pollfd;
+
+            pa_assert(u->fd >= 0);
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+            pollfd->events = (short)
+                (((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
+                 ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0));
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        if (u->rtpoll_item) {
+            struct pollfd *pollfd;
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
+                pa_log("DSP shutdown.");
+                goto fail;
+            }
+
+            revents = pollfd->revents;
+        } else
+            revents = 0;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+
+    struct audio_buf_info info;
+    struct userdata *u = NULL;
+    const char *dev;
+    int fd = -1;
+    int nfrags, orig_frag_size, frag_size;
+    int mode, caps;
+    bool record = true, playback = true, use_mmap = true;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    char hwdesc[64];
+    const char *name;
+    bool namereg_fail;
+    pa_sink_new_data sink_new_data;
+    pa_source_new_data source_new_data;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
+        pa_log("record= and playback= expect boolean argument.");
+        goto fail;
+    }
+
+    if (!playback && !record) {
+        pa_log("Neither playback nor record enabled for device.");
+        goto fail;
+    }
+
+    mode = (playback && record) ? O_RDWR : (playback ? O_WRONLY : O_RDONLY);
+
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_OSS) < 0) {
+        pa_log("Failed to parse sample specification or channel map");
+        goto fail;
+    }
+
+    nfrags = (int) m->core->default_n_fragments;
+    frag_size = (int) pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss);
+    if (frag_size <= 0)
+        frag_size = (int) pa_frame_size(&ss);
+
+    if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
+        pa_log("Failed to parse fragments arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+        pa_log("Failed to parse mmap argument.");
+        goto fail;
+    }
+
+    if ((fd = pa_oss_open(dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
+        goto fail;
+
+    if (use_mmap && (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER))) {
+        pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
+        use_mmap = false;
+    }
+
+    if (use_mmap && mode == O_WRONLY) {
+        pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
+        use_mmap = false;
+    }
+
+    if (pa_oss_get_hw_description(dev, hwdesc, sizeof(hwdesc)) >= 0)
+        pa_log_info("Hardware name is '%s'.", hwdesc);
+    else
+        hwdesc[0] = 0;
+
+    pa_log_info("Device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+
+    orig_frag_size = frag_size;
+    if (nfrags >= 2 && frag_size >= 1)
+        if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)
+            goto fail;
+
+    if (pa_oss_auto_format(fd, &ss) < 0)
+        goto fail;
+
+    if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
+        pa_log("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+    pa_assert(frag_size > 0);
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->fd = fd;
+    u->mixer_fd = -1;
+    u->mixer_devmask = 0;
+    u->use_getospace = u->use_getispace = true;
+    u->use_getodelay = true;
+    u->mode = mode;
+    u->frame_size = pa_frame_size(&ss);
+    u->device_name = pa_xstrdup(dev);
+    u->in_nfrags = u->out_nfrags = (uint32_t) (u->nfrags = nfrags);
+    u->out_fragment_size = u->in_fragment_size = (uint32_t) (u->frag_size = frag_size);
+    u->orig_frag_size = orig_frag_size;
+    u->use_mmap = use_mmap;
+    u->rtpoll = pa_rtpoll_new();
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->rtpoll_item = NULL;
+    build_pollfd(u);
+
+    if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
+        pa_log_info("Input -- %u fragments of size %u.", info.fragstotal, info.fragsize);
+        u->in_fragment_size = (uint32_t) info.fragsize;
+        u->in_nfrags = (uint32_t) info.fragstotal;
+        u->use_getispace = true;
+    }
+
+    if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
+        pa_log_info("Output -- %u fragments of size %u.", info.fragstotal, info.fragsize);
+        u->out_fragment_size = (uint32_t) info.fragsize;
+        u->out_nfrags = (uint32_t) info.fragstotal;
+        u->use_getospace = true;
+    }
+
+    u->in_hwbuf_size = u->in_nfrags * u->in_fragment_size;
+    u->out_hwbuf_size = u->out_nfrags * u->out_fragment_size;
+
+    if (mode != O_WRONLY) {
+        char *name_buf = NULL;
+
+        if (use_mmap) {
+            if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+                pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno));
+                use_mmap = u->use_mmap = false;
+                u->in_mmap = NULL;
+            } else
+                pa_log_debug("Successfully mmap()ed input buffer.");
+        }
+
+        if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
+            namereg_fail = true;
+        else {
+            name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev));
+            namereg_fail = false;
+        }
+
+        pa_source_new_data_init(&source_new_data);
+        source_new_data.driver = __FILE__;
+        source_new_data.module = m;
+        pa_source_new_data_set_name(&source_new_data, name);
+        source_new_data.namereg_fail = namereg_fail;
+        pa_source_new_data_set_sample_spec(&source_new_data, &ss);
+        pa_source_new_data_set_channel_map(&source_new_data, &map);
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "oss");
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
+        pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->in_hwbuf_size));
+        pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->in_fragment_size));
+
+        if (pa_modargs_get_proplist(ma, "source_properties", source_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
+            pa_log("Invalid properties");
+            pa_source_new_data_done(&source_new_data);
+            pa_xfree(name_buf);
+            goto fail;
+        }
+
+        u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+        pa_source_new_data_done(&source_new_data);
+        pa_xfree(name_buf);
+
+        if (!u->source) {
+            pa_log("Failed to create source object");
+            goto fail;
+        }
+
+        u->source->parent.process_msg = source_process_msg;
+        u->source->userdata = u;
+
+        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+        pa_source_set_rtpoll(u->source, u->rtpoll);
+        pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->in_hwbuf_size, &u->source->sample_spec));
+        u->source->refresh_volume = true;
+
+        if (use_mmap)
+            u->in_mmap_memblocks = pa_xnew0(pa_memblock*, u->in_nfrags);
+    }
+
+    if (mode != O_RDONLY) {
+        char *name_buf = NULL;
+
+        if (use_mmap) {
+            if ((u->out_mmap = mmap(NULL, u->out_hwbuf_size, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+                if (mode == O_RDWR) {
+                    pa_log_debug("mmap() failed for input. Changing to O_WRONLY mode.");
+                    mode = O_WRONLY;
+                    goto go_on;
+                } else {
+                    pa_log_warn("mmap(PROT_WRITE) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno));
+                    u->use_mmap = use_mmap = false;
+                    u->out_mmap = NULL;
+                }
+            } else {
+                pa_log_debug("Successfully mmap()ed output buffer.");
+                pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &ss);
+            }
+        }
+
+        if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
+            namereg_fail = true;
+        else {
+            name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev));
+            namereg_fail = false;
+        }
+
+        pa_sink_new_data_init(&sink_new_data);
+        sink_new_data.driver = __FILE__;
+        sink_new_data.module = m;
+        pa_sink_new_data_set_name(&sink_new_data, name);
+        sink_new_data.namereg_fail = namereg_fail;
+        pa_sink_new_data_set_sample_spec(&sink_new_data, &ss);
+        pa_sink_new_data_set_channel_map(&sink_new_data, &map);
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_API, "oss");
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
+        pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->out_hwbuf_size));
+        pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->out_fragment_size));
+
+        if (pa_modargs_get_proplist(ma, "sink_properties", sink_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
+            pa_log("Invalid properties");
+            pa_sink_new_data_done(&sink_new_data);
+            pa_xfree(name_buf);
+            goto fail;
+        }
+
+        u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+        pa_sink_new_data_done(&sink_new_data);
+        pa_xfree(name_buf);
+
+        if (!u->sink) {
+            pa_log("Failed to create sink object");
+            goto fail;
+        }
+
+        u->sink->parent.process_msg = sink_process_msg;
+        u->sink->userdata = u;
+
+        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+        pa_sink_set_rtpoll(u->sink, u->rtpoll);
+        pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->out_hwbuf_size, &u->sink->sample_spec));
+        u->sink->refresh_volume = true;
+
+        pa_sink_set_max_request(u->sink, u->out_hwbuf_size);
+
+        if (use_mmap)
+            u->out_mmap_memblocks = pa_xnew0(pa_memblock*, u->out_nfrags);
+    }
+
+    if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
+        bool do_close = true;
+
+        if (ioctl(u->mixer_fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
+            pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno));
+        else {
+            if (u->sink && (u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM))) {
+                pa_log_debug("Found hardware mixer track for playback.");
+                pa_sink_set_get_volume_callback(u->sink, sink_get_volume);
+                pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
+                u->sink->n_volume_steps = 101;
+                do_close = false;
+            }
+
+            if (u->source && (u->mixer_devmask & (SOUND_MASK_RECLEV|SOUND_MASK_IGAIN))) {
+                pa_log_debug("Found hardware mixer track for recording.");
+                pa_source_set_get_volume_callback(u->source, source_get_volume);
+                pa_source_set_set_volume_callback(u->source, source_set_volume);
+                u->source->n_volume_steps = 101;
+                do_close = false;
+            }
+        }
+
+        if (do_close) {
+            pa_close(u->mixer_fd);
+            u->mixer_fd = -1;
+            u->mixer_devmask = 0;
+        }
+    }
+
+go_on:
+
+    pa_assert(u->source || u->sink);
+
+    pa_memchunk_reset(&u->memchunk);
+
+    if (!(u->thread = pa_thread_new("oss", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    /* Read mixer settings */
+    if (u->sink) {
+        if (sink_new_data.volume_is_set) {
+            if (u->sink->set_volume)
+                u->sink->set_volume(u->sink);
+        } else {
+            if (u->sink->get_volume)
+                u->sink->get_volume(u->sink);
+        }
+    }
+
+    if (u->source) {
+        if (source_new_data.volume_is_set) {
+            if (u->source->set_volume)
+                u->source->set_volume(u->source);
+        } else {
+            if (u->source->get_volume)
+                u->source->get_volume(u->source);
+        }
+    }
+
+    if (u->sink)
+        pa_sink_put(u->sink);
+    if (u->source)
+        pa_source_put(u->source);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (u)
+        pa__done(m);
+    else if (fd >= 0)
+        pa_close(fd);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->out_mmap_memblocks) {
+        unsigned i;
+        for (i = 0; i < u->out_nfrags; i++)
+            if (u->out_mmap_memblocks[i])
+                pa_memblock_unref_fixed(u->out_mmap_memblocks[i]);
+        pa_xfree(u->out_mmap_memblocks);
+    }
+
+    if (u->in_mmap_memblocks) {
+        unsigned i;
+        for (i = 0; i < u->in_nfrags; i++)
+            if (u->in_mmap_memblocks[i])
+                pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
+        pa_xfree(u->in_mmap_memblocks);
+    }
+
+    if (u->in_mmap && u->in_mmap != MAP_FAILED)
+        munmap(u->in_mmap, u->in_hwbuf_size);
+
+    if (u->out_mmap && u->out_mmap != MAP_FAILED)
+        munmap(u->out_mmap, u->out_hwbuf_size);
+
+    if (u->fd >= 0)
+        pa_close(u->fd);
+
+    if (u->mixer_fd >= 0)
+        pa_close(u->mixer_fd);
+
+    pa_xfree(u->device_name);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/oss/oss-util.c b/src/modules/oss/oss-util.c
new file mode 100644 (file)
index 0000000..42ee7f5
--- /dev/null
@@ -0,0 +1,418 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "oss-util.h"
+
+int pa_oss_open(const char *device, int *mode, int* pcaps) {
+    int fd = -1;
+    int caps;
+    char *t;
+
+    pa_assert(device);
+    pa_assert(mode);
+    pa_assert(*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY);
+
+    if (!pcaps)
+        pcaps = &caps;
+
+    if (*mode == O_RDWR) {
+        if ((fd = pa_open_cloexec(device, O_RDWR|O_NDELAY, 0)) >= 0) {
+            ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
+
+            if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
+                pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));
+                goto fail;
+            }
+
+            if (*pcaps & DSP_CAP_DUPLEX)
+                goto success;
+
+            pa_log_warn("'%s' doesn't support full duplex", device);
+
+            pa_close(fd);
+        }
+
+        if ((fd = pa_open_cloexec(device, (*mode = O_WRONLY)|O_NDELAY, 0)) < 0) {
+            if ((fd = pa_open_cloexec(device, (*mode = O_RDONLY)|O_NDELAY, 0)) < 0) {
+                pa_log("open('%s'): %s", device, pa_cstrerror(errno));
+                goto fail;
+            }
+        }
+    } else {
+        if ((fd = pa_open_cloexec(device, *mode|O_NDELAY, 0)) < 0) {
+            pa_log("open('%s'): %s", device, pa_cstrerror(errno));
+            goto fail;
+        }
+    }
+
+    *pcaps = 0;
+
+    if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
+        pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+success:
+
+    t = pa_sprintf_malloc(
+            "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+                 *pcaps & DSP_CAP_BATCH ? " BATCH" : "",
+#ifdef DSP_CAP_BIND
+                 *pcaps & DSP_CAP_BIND ? " BIND" : "",
+#else
+                 "",
+#endif
+                 *pcaps & DSP_CAP_COPROC ? " COPROC" : "",
+                 *pcaps & DSP_CAP_DUPLEX ? " DUPLEX" : "",
+#ifdef DSP_CAP_FREERATE
+                 *pcaps & DSP_CAP_FREERATE ? " FREERATE" : "",
+#else
+                 "",
+#endif
+#ifdef DSP_CAP_INPUT
+                 *pcaps & DSP_CAP_INPUT ? " INPUT" : "",
+#else
+                 "",
+#endif
+                 *pcaps & DSP_CAP_MMAP ? " MMAP" : "",
+#ifdef DSP_CAP_MODEM
+                 *pcaps & DSP_CAP_MODEM ? " MODEM" : "",
+#else
+                 "",
+#endif
+#ifdef DSP_CAP_MULTI
+                 *pcaps & DSP_CAP_MULTI ? " MULTI" : "",
+#else
+                 "",
+#endif
+#ifdef DSP_CAP_OUTPUT
+                 *pcaps & DSP_CAP_OUTPUT ? " OUTPUT" : "",
+#else
+                 "",
+#endif
+                 *pcaps & DSP_CAP_REALTIME ? " REALTIME" : "",
+#ifdef DSP_CAP_SHADOW
+                 *pcaps & DSP_CAP_SHADOW ? " SHADOW" : "",
+#else
+                 "",
+#endif
+#ifdef DSP_CAP_VIRTUAL
+                 *pcaps & DSP_CAP_VIRTUAL ? " VIRTUAL" : "",
+#else
+                 "",
+#endif
+                 *pcaps & DSP_CAP_TRIGGER ? " TRIGGER" : "");
+
+    pa_log_debug("capabilities:%s", t);
+    pa_xfree(t);
+
+    return fd;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+    return -1;
+}
+
+int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
+    int format, channels, speed, reqformat;
+    pa_sample_format_t orig_format;
+
+    static const int format_trans[PA_SAMPLE_MAX] = {
+        [PA_SAMPLE_U8] = AFMT_U8,
+        [PA_SAMPLE_ALAW] = AFMT_A_LAW,
+        [PA_SAMPLE_ULAW] = AFMT_MU_LAW,
+        [PA_SAMPLE_S16LE] = AFMT_S16_LE,
+        [PA_SAMPLE_S16BE] = AFMT_S16_BE,
+        [PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S32LE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S32BE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S24LE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S24BE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S24_32LE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S24_32BE] = AFMT_QUERY, /* not supported */
+    };
+
+    pa_assert(fd >= 0);
+    pa_assert(ss);
+
+    orig_format = ss->format;
+
+    reqformat = format = format_trans[ss->format];
+    if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) {
+        format = AFMT_S16_NE;
+        if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) {
+            int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE;
+            format = f;
+            if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) {
+                format = AFMT_U8;
+                if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) {
+                    pa_log("SNDCTL_DSP_SETFMT: %s", format != AFMT_U8 ? "No supported sample format" : pa_cstrerror(errno));
+                    return -1;
+                } else
+                    ss->format = PA_SAMPLE_U8;
+            } else
+                ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
+        } else
+            ss->format = PA_SAMPLE_S16NE;
+    }
+
+    if (orig_format != ss->format)
+        pa_log_warn("device doesn't support sample format %s, changed to %s.",
+               pa_sample_format_to_string(orig_format),
+               pa_sample_format_to_string(ss->format));
+
+    channels = ss->channels;
+    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
+        pa_log("SNDCTL_DSP_CHANNELS: %s", pa_cstrerror(errno));
+        return -1;
+    }
+    pa_assert(channels > 0);
+
+    if (ss->channels != channels) {
+        pa_log_warn("device doesn't support %i channels, using %i channels.", ss->channels, channels);
+        ss->channels = (uint8_t) channels;
+    }
+
+    speed = (int) ss->rate;
+    if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
+        pa_log("SNDCTL_DSP_SPEED: %s", pa_cstrerror(errno));
+        return -1;
+    }
+    pa_assert(speed > 0);
+
+    if (ss->rate != (unsigned) speed) {
+        pa_log_warn("device doesn't support %i Hz, changed to %i Hz.", ss->rate, speed);
+
+        /* If the sample rate deviates too much, we need to resample */
+        if (speed < ss->rate*.95 || speed > ss->rate*1.05)
+            ss->rate = (uint32_t) speed;
+    }
+
+    return 0;
+}
+
+int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
+    int arg;
+
+    pa_assert(frag_size >= 0);
+
+    arg = ((int) nfrags << 16) | pa_ulog2(frag_size);
+
+    pa_log_debug("Asking for %i fragments of size %i (requested %i)", nfrags, 1 << pa_ulog2(frag_size), frag_size);
+
+    if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
+        pa_log("SNDCTL_DSP_SETFRAGMENT: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
+    char cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    unsigned vol;
+
+    pa_assert(fd >= 0);
+    pa_assert(ss);
+    pa_assert(volume);
+
+    if (ioctl(fd, mixer, &vol) < 0)
+        return -1;
+
+    pa_cvolume_reset(volume, ss->channels);
+
+    volume->values[0] = PA_CLAMP_VOLUME(((vol & 0xFF) * PA_VOLUME_NORM) / 100);
+
+    if (volume->channels >= 2)
+        volume->values[1] = PA_CLAMP_VOLUME((((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100);
+
+    pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint_verbose(cv, sizeof(cv), volume, NULL, false));
+    return 0;
+}
+
+int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
+    char cv[PA_CVOLUME_SNPRINT_MAX];
+    unsigned vol;
+    pa_volume_t l, r;
+
+    l = volume->values[0] > PA_VOLUME_NORM ? PA_VOLUME_NORM : volume->values[0];
+
+    vol = (l*100)/PA_VOLUME_NORM;
+
+    if (ss->channels >= 2) {
+        r = volume->values[1] > PA_VOLUME_NORM ? PA_VOLUME_NORM : volume->values[1];
+        vol |= ((r*100)/PA_VOLUME_NORM) << 8;
+    }
+
+    if (ioctl(fd, mixer, &vol) < 0)
+        return -1;
+
+    pa_log_debug("Wrote mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume));
+    return 0;
+}
+
+static int get_device_number(const char *dev) {
+    const char *p, *e;
+    char *rp = NULL;
+    int r;
+
+    if (!(p = rp = pa_readlink(dev))) {
+#ifdef ENOLINK
+        if (errno != EINVAL && errno != ENOLINK) {
+#else
+        if (errno != EINVAL) {
+#endif
+            r = -1;
+            goto finish;
+        }
+
+        p = dev;
+    }
+
+    if ((e = strrchr(p, '/')))
+        p = e+1;
+
+    if (p == 0) {
+        r = 0;
+        goto finish;
+    }
+
+    p = strchr(p, 0) -1;
+
+    if (*p >= '0' && *p <= '9') {
+        r = *p - '0';
+        goto finish;
+    }
+
+    r = -1;
+
+finish:
+    pa_xfree(rp);
+    return r;
+}
+
+int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
+    FILE *f;
+    int n, r = -1;
+    int b = 0;
+
+    if ((n = get_device_number(dev)) < 0)
+        return -1;
+
+    if (!(f = pa_fopen_cloexec("/dev/sndstat", "r")) &&
+        !(f = pa_fopen_cloexec("/proc/sndstat", "r")) &&
+        !(f = pa_fopen_cloexec("/proc/asound/oss/sndstat", "r"))) {
+
+        if (errno != ENOENT)
+            pa_log_warn("failed to open OSS sndstat device: %s", pa_cstrerror(errno));
+
+        return -1;
+    }
+
+    while (!feof(f)) {
+        char line[64];
+        int device;
+
+        if (!fgets(line, sizeof(line), f))
+            break;
+
+        line[strcspn(line, "\r\n")] = 0;
+
+        if (!b) {
+            b = pa_streq(line, "Audio devices:");
+            continue;
+        }
+
+        if (line[0] == 0)
+            break;
+
+        if (sscanf(line, "%i: ", &device) != 1)
+            continue;
+
+        if (device == n) {
+            char *k = strchr(line, ':');
+            pa_assert(k);
+            k++;
+            k += strspn(k, " ");
+
+            if (pa_endswith(k, " (DUPLEX)"))
+                k[strlen(k)-9] = 0;
+
+            pa_strlcpy(name, k, l);
+            r = 0;
+            break;
+        }
+    }
+
+    fclose(f);
+    return r;
+}
+
+static int open_mixer(const char *mixer) {
+    int fd;
+
+    if ((fd = pa_open_cloexec(mixer, O_RDWR|O_NDELAY, 0)) >= 0)
+        return fd;
+
+    return -1;
+}
+
+int pa_oss_open_mixer_for_device(const char *device) {
+    int n;
+    char *fn;
+    int fd;
+
+    if ((n = get_device_number(device)) < 0)
+        return -1;
+
+    if (n == 0)
+        if ((fd = open_mixer("/dev/mixer")) >= 0)
+            return fd;
+
+    fn = pa_sprintf_malloc("/dev/mixer%i", n);
+    fd = open_mixer(fn);
+    pa_xfree(fn);
+
+    if (fd < 0)
+        pa_log_warn("Failed to open mixer '%s': %s", device, pa_cstrerror(errno));
+
+    return fd;
+}
diff --git a/src/modules/oss/oss-util.h b/src/modules/oss/oss-util.h
new file mode 100644 (file)
index 0000000..8af27de
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef fooossutilhfoo
+#define fooossutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+
+int pa_oss_open(const char *device, int *mode, int* pcaps);
+int pa_oss_auto_format(int fd, pa_sample_spec *ss);
+
+int pa_oss_set_fragments(int fd, int frags, int frag_size);
+
+int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume);
+int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume);
+
+int pa_oss_get_hw_description(const char *dev, char *name, size_t l);
+
+int pa_oss_open_mixer_for_device(const char *device);
+
+#endif
diff --git a/src/modules/raop/module-raop-discover.c b/src/modules/raop/module-raop-discover.c
new file mode 100644 (file)
index 0000000..9c7ac3c
--- /dev/null
@@ -0,0 +1,488 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2008 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/avahi-wrap.h>
+
+#include "module-raop-discover-symdef.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery of RAOP devices");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+
+#define SERVICE_TYPE_SINK "_raop._tcp"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    AvahiPoll *avahi_poll;
+    AvahiClient *client;
+    AvahiServiceBrowser *sink_browser;
+
+    pa_hashmap *tunnels;
+};
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct tunnel {
+    AvahiIfIndex interface;
+    AvahiProtocol protocol;
+    char *name, *type, *domain;
+    uint32_t module_index;
+};
+
+static unsigned tunnel_hash(const void *p) {
+    const struct tunnel *t = p;
+
+    return
+        (unsigned) t->interface +
+        (unsigned) t->protocol +
+        pa_idxset_string_hash_func(t->name) +
+        pa_idxset_string_hash_func(t->type) +
+        pa_idxset_string_hash_func(t->domain);
+}
+
+static int tunnel_compare(const void *a, const void *b) {
+    const struct tunnel *ta = a, *tb = b;
+    int r;
+
+    if (ta->interface != tb->interface)
+        return 1;
+    if (ta->protocol != tb->protocol)
+        return 1;
+    if ((r = strcmp(ta->name, tb->name)))
+        return r;
+    if ((r = strcmp(ta->type, tb->type)))
+        return r;
+    if ((r = strcmp(ta->domain, tb->domain)))
+        return r;
+
+    return 0;
+}
+
+static struct tunnel* tunnel_new(
+        AvahiIfIndex interface, AvahiProtocol protocol,
+        const char *name, const char *type, const char *domain) {
+    struct tunnel *t;
+
+    t = pa_xnew(struct tunnel, 1);
+    t->interface = interface;
+    t->protocol = protocol;
+    t->name = pa_xstrdup(name);
+    t->type = pa_xstrdup(type);
+    t->domain = pa_xstrdup(domain);
+    t->module_index = PA_IDXSET_INVALID;
+
+    return t;
+}
+
+static void tunnel_free(struct tunnel *t) {
+    pa_assert(t);
+    pa_xfree(t->name);
+    pa_xfree(t->type);
+    pa_xfree(t->domain);
+    pa_xfree(t);
+}
+
+static void resolver_cb(
+        AvahiServiceResolver *r,
+        AvahiIfIndex interface, AvahiProtocol protocol,
+        AvahiResolverEvent event,
+        const char *name, const char *type, const char *domain,
+        const char *host_name, const AvahiAddress *a, uint16_t port,
+        AvahiStringList *txt,
+        AvahiLookupResultFlags flags,
+        void *userdata) {
+    struct userdata *u = userdata;
+    struct tunnel *tnl;
+    char *device = NULL, *nicename, *dname, *vname, *args;
+    char *tp = NULL, *et = NULL, *cn = NULL;
+    char *ch = NULL, *ss = NULL, *sr = NULL;
+    char *t = NULL;
+    char at[AVAHI_ADDRESS_STR_MAX];
+    AvahiStringList *l;
+    pa_module *m;
+
+    pa_assert(u);
+
+    tnl = tunnel_new(interface, protocol, name, type, domain);
+
+    if (event != AVAHI_RESOLVER_FOUND) {
+        pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
+        goto finish;
+    }
+
+    if ((nicename = strstr(name, "@"))) {
+        ++nicename;
+        if (strlen(nicename) > 0) {
+            pa_log_debug("Found RAOP: %s", nicename);
+            nicename = pa_escape(nicename, "\"'");
+        } else
+            nicename = NULL;
+    }
+
+    for (l = txt; l; l = l->next) {
+        char *key, *value;
+        pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0);
+
+        pa_log_debug("Found key: '%s' with value: '%s'", key, value);
+        if (pa_streq(key, "device")) {
+            device = value;
+            value = NULL;
+        } else if (pa_streq(key, "tp")) {
+            /* Transport protocol:
+             *  - TCP = only TCP,
+             *  - UDP = only UDP,
+             *  - TCP,UDP = both supported (UDP should be prefered) */
+            pa_xfree(tp);
+            if (pa_str_in_list(value, ",", "UDP"))
+                tp = pa_xstrdup("UDP");
+            else if (pa_str_in_list(value, ",", "TCP"))
+                tp = pa_xstrdup("TCP");
+            else
+                tp = pa_xstrdup(value);
+        } else if (pa_streq(key, "et")) {
+            /* Supported encryption types:
+             *  - 0 = none,
+             *  - 1 = RSA,
+             *  - 2 = FairPlay,
+             *  - 3 = MFiSAP,
+             *  - 4 = FairPlay SAPv2.5. */
+            pa_xfree(et);
+            if (pa_str_in_list(value, ",", "1"))
+                et = pa_xstrdup("RSA");
+            else
+                et = pa_xstrdup("none");
+        } else if (pa_streq(key, "cn")) {
+            /* Suported audio codecs:
+             *  - 0 = PCM,
+             *  - 1 = ALAC,
+             *  - 2 = AAC,
+             *  - 3 = AAC ELD. */
+            pa_xfree(cn);
+            if (pa_str_in_list(value, ",", "1"))
+                cn = pa_xstrdup("ALAC");
+            else
+                cn = pa_xstrdup("PCM");
+        } else if (pa_streq(key, "md")) {
+            /* Supported metadata types:
+             *  - 0 = text,
+             *  - 1 = artwork,
+             *  - 2 = progress. */
+        } else if (pa_streq(key, "pw")) {
+            /* Requires password ? (true/false) */
+        } else if (pa_streq(key, "ch")) {
+            /* Number of channels */
+            pa_xfree(ch);
+            ch = pa_xstrdup(value);
+        } else if (pa_streq(key, "ss")) {
+            /* Sample size */
+            pa_xfree(ss);
+            ss = pa_xstrdup(value);
+        } else if (pa_streq(key, "sr")) {
+            /* Sample rate */
+            pa_xfree(sr);
+            sr = pa_xstrdup(value);
+        }
+
+        avahi_free(key);
+        avahi_free(value);
+    }
+
+    if (device)
+        dname = pa_sprintf_malloc("raop_output.%s.%s", host_name, device);
+    else
+        dname = pa_sprintf_malloc("raop_output.%s", host_name);
+
+    if (!(vname = pa_namereg_make_valid_name(dname))) {
+        pa_log("Cannot construct valid device name from '%s'.", dname);
+        avahi_free(device);
+        pa_xfree(dname);
+        pa_xfree(tp);
+        pa_xfree(et);
+        pa_xfree(cn);
+        pa_xfree(ch);
+        pa_xfree(ss);
+        pa_xfree(sr);
+        goto finish;
+    }
+
+    avahi_free(device);
+    pa_xfree(dname);
+
+    avahi_address_snprint(at, sizeof(at), a);
+    if (nicename) {
+        args = pa_sprintf_malloc("server=[%s]:%u "
+                                 "sink_name=%s "
+                                 "sink_properties='device.description=\"%s (%s:%u)\"'",
+                                 at, port,
+                                 vname,
+                                 nicename, at, port);
+        pa_xfree(nicename);
+    } else {
+        args = pa_sprintf_malloc("server=[%s]:%u "
+                                 "sink_name=%s"
+                                 "sink_properties='device.description=\"%s:%u\"'",
+                                 at, port,
+                                 vname,
+                                 at, port);
+    }
+
+    if (tp != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s protocol=%s", args, tp);
+        pa_xfree(tp);
+        pa_xfree(t);
+    }
+    if (et != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s encryption=%s", args, et);
+        pa_xfree(et);
+        pa_xfree(t);
+    }
+    if (cn != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s codec=%s", args, cn);
+        pa_xfree(cn);
+        pa_xfree(t);
+    }
+    if (ch != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s channels=%s", args, ch);
+        pa_xfree(ch);
+        pa_xfree(t);
+    }
+    if (ss != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s format=%s", args, ss);
+        pa_xfree(ss);
+        pa_xfree(t);
+    }
+    if (sr != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s rate=%s", args, sr);
+        pa_xfree(sr);
+        pa_xfree(t);
+    }
+
+    pa_log_debug("Loading module-raop-sink with arguments '%s'", args);
+
+    if ((m = pa_module_load(u->core, "module-raop-sink", args))) {
+        tnl->module_index = m->index;
+        pa_hashmap_put(u->tunnels, tnl, tnl);
+        tnl = NULL;
+    }
+
+    pa_xfree(vname);
+    pa_xfree(args);
+
+finish:
+    avahi_service_resolver_free(r);
+
+    if (tnl)
+        tunnel_free(tnl);
+}
+
+static void browser_cb(
+        AvahiServiceBrowser *b,
+        AvahiIfIndex interface, AvahiProtocol protocol,
+        AvahiBrowserEvent event,
+        const char *name, const char *type, const char *domain,
+        AvahiLookupResultFlags flags,
+        void *userdata) {
+    struct userdata *u = userdata;
+    struct tunnel *t;
+
+    pa_assert(u);
+
+    if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
+        return;
+
+    t = tunnel_new(interface, protocol, name, type, domain);
+
+    if (event == AVAHI_BROWSER_NEW) {
+
+        if (!pa_hashmap_get(u->tunnels, t))
+            if (!(avahi_service_resolver_new(u->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolver_cb, u)))
+                pa_log("avahi_service_resolver_new() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+
+        /* We ignore the returned resolver object here, since the we don't
+         * need to attach any special data to it, and we can still destroy
+         * it from the callback. */
+
+    } else if (event == AVAHI_BROWSER_REMOVE) {
+        struct tunnel *t2;
+
+        if ((t2 = pa_hashmap_get(u->tunnels, t))) {
+            pa_module_unload_request_by_index(u->core, t2->module_index, true);
+            pa_hashmap_remove(u->tunnels, t2);
+            tunnel_free(t2);
+        }
+    }
+
+    tunnel_free(t);
+}
+
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    u->client = c;
+
+    switch (state) {
+        case AVAHI_CLIENT_S_REGISTERING:
+        case AVAHI_CLIENT_S_RUNNING:
+        case AVAHI_CLIENT_S_COLLISION:
+            if (!u->sink_browser) {
+                if (!(u->sink_browser = avahi_service_browser_new(
+                              c,
+                              AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                              SERVICE_TYPE_SINK,
+                              NULL,
+                              0,
+                              browser_cb, u))) {
+
+                    pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
+                    pa_module_unload_request(u->module, true);
+                }
+            }
+
+            break;
+
+        case AVAHI_CLIENT_FAILURE:
+            if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+                int error;
+
+                pa_log_debug("Avahi daemon disconnected.");
+
+                /* Try to reconnect. */
+                if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+                    pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+                    pa_module_unload_request(u->module, true);
+                }
+            }
+
+            /* Fall through. */
+
+        case AVAHI_CLIENT_CONNECTING:
+            if (u->sink_browser) {
+                avahi_service_browser_free(u->sink_browser);
+                u->sink_browser = NULL;
+            }
+
+            break;
+
+        default:
+            break;
+    }
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    int error;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->sink_browser = NULL;
+
+    u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare);
+
+    u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
+
+    if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+        pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error));
+        goto fail;
+    }
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->client)
+        avahi_client_free(u->client);
+
+    if (u->avahi_poll)
+        pa_avahi_poll_free(u->avahi_poll);
+
+    if (u->tunnels) {
+        struct tunnel *t;
+
+        while ((t = pa_hashmap_steal_first(u->tunnels))) {
+            pa_module_unload_request_by_index(u->core, t->module_index, true);
+            tunnel_free(t);
+        }
+
+        pa_hashmap_free(u->tunnels);
+    }
+
+    pa_xfree(u);
+}
diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c
new file mode 100644 (file)
index 0000000..82fa48d
--- /dev/null
@@ -0,0 +1,110 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2008 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/module.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+
+#include "raop-sink.h"
+
+#include "module-raop-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("RAOP Sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "name=<name of the sink, to be prefixed> "
+        "sink_name=<name for the sink> "
+        "sink_properties=<properties for the sink> "
+        "server=<address> "
+        "protocol=<transport protocol> "
+        "encryption=<encryption type> "
+        "codec=<audio codec> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "username=<authentication user name, default: \"iTunes\"> "
+        "password=<authentication password>");
+
+static const char* const valid_modargs[] = {
+    "name",
+    "sink_name",
+    "sink_properties",
+    "server",
+    "protocol",
+    "encryption",
+    "codec",
+    "format",
+    "rate",
+    "channels",
+    "username",
+    "password",
+    NULL
+};
+
+int pa__init(pa_module *m) {
+    pa_modargs *ma = NULL;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (!(m->userdata = pa_raop_sink_new(m, ma, __FILE__)))
+        goto fail;
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    pa_sink *sink;
+
+    pa_assert(m);
+    pa_assert_se(sink = m->userdata);
+
+    return pa_sink_linked_by(sink);
+}
+
+void pa__done(pa_module *m) {
+    pa_sink *sink;
+
+    pa_assert(m);
+
+    if ((sink = m->userdata))
+        pa_raop_sink_free(sink);
+}
diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
new file mode 100644 (file)
index 0000000..ae950f7
--- /dev/null
@@ -0,0 +1,1785 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+  Copyright 2013 Hajime Fujita
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <math.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/sample.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/arpa-inet.h>
+#include <pulsecore/socket-client.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/parseaddr.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/random.h>
+#include <pulsecore/poll.h>
+
+#include "raop-client.h"
+#include "raop-packet-buffer.h"
+#include "raop-crypto.h"
+#include "raop-util.h"
+
+#include "rtsp_client.h"
+
+#define DEFAULT_RAOP_PORT 5000
+
+#define FRAMES_PER_TCP_PACKET 4096
+#define FRAMES_PER_UDP_PACKET 352
+
+#define RTX_BUFFERING_SECONDS 4
+
+#define DEFAULT_TCP_AUDIO_PORT   6000
+#define DEFAULT_UDP_AUDIO_PORT   6000
+#define DEFAULT_UDP_CONTROL_PORT 6001
+#define DEFAULT_UDP_TIMING_PORT  6002
+
+#define DEFAULT_USER_AGENT "iTunes/11.0.4 (Windows; N)"
+#define DEFAULT_USER_NAME  "iTunes"
+
+#define JACK_STATUS_DISCONNECTED 0
+#define JACK_STATUS_CONNECTED    1
+#define JACK_TYPE_ANALOG         0
+#define JACK_TYPE_DIGITAL        1
+
+#define VOLUME_MAX  0.0
+#define VOLUME_DEF -30.0
+#define VOLUME_MIN -144.0
+
+#define UDP_DEFAULT_PKT_BUF_SIZE 1000
+#define APPLE_CHALLENGE_LENGTH 16
+
+struct pa_raop_client {
+    pa_core *core;
+    char *host;
+    uint16_t port;
+    pa_rtsp_client *rtsp;
+    char *sci, *sid;
+    char *password;
+
+    pa_raop_protocol_t protocol;
+    pa_raop_encryption_t encryption;
+    pa_raop_codec_t codec;
+
+    pa_raop_secret *secret;
+
+    int tcp_sfd;
+
+    int udp_sfd;
+    int udp_cfd;
+    int udp_tfd;
+
+    pa_raop_packet_buffer *pbuf;
+
+    uint16_t seq;
+    uint32_t rtptime;
+    bool is_recording;
+    uint32_t ssrc;
+
+    bool is_first_packet;
+    uint32_t sync_interval;
+    uint32_t sync_count;
+
+    uint8_t jack_type;
+    uint8_t jack_status;
+
+    pa_raop_client_state_cb_t state_callback;
+    void *state_userdata;
+};
+
+/* Audio TCP packet header [16x8] (cf. rfc4571):
+ *  [0,1]   Frame marker; seems always 0x2400
+ *  [2,3]   RTP packet size (following): 0x0000 (to be set)
+ *   [4,5]   RTP v2: 0x80
+ *   [5]     Payload type: 0x60 | Marker bit: 0x80 (always set)
+ *   [6,7]   Sequence number: 0x0000 (to be set)
+ *   [8,11]  Timestamp: 0x00000000 (to be set)
+ *   [12,15] SSRC: 0x00000000 (to be set) */
+#define PAYLOAD_TCP_AUDIO_DATA 0x60
+static const uint8_t tcp_audio_header[16] = {
+    0x24, 0x00, 0x00, 0x00,
+    0x80, 0xe0, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00
+};
+
+/* Audio UDP packet header [12x8] (cf. rfc3550):
+ *  [0]    RTP v2: 0x80
+ *  [1]    Payload type: 0x60
+ *  [2,3]  Sequence number: 0x0000 (to be set)
+ *  [4,7]  Timestamp: 0x00000000 (to be set)
+ *  [8,12] SSRC: 0x00000000 (to be set) */
+#define PAYLOAD_UDP_AUDIO_DATA 0x60
+static const uint8_t udp_audio_header[12] = {
+    0x80, 0x60, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00
+};
+
+/* Audio retransmission UDP packet header [4x8]:
+ *  [0] RTP v2: 0x80
+ *  [1] Payload type: 0x56 | Marker bit: 0x80 (always set)
+ *  [2] Unknown; seems always 0x01
+ *  [3] Unknown; seems some random number around 0x20~0x40 */
+#define PAYLOAD_RETRANSMIT_REQUEST 0x55
+#define PAYLOAD_RETRANSMIT_REPLY   0x56
+static const uint8_t udp_audio_retrans_header[4] = {
+    0x80, 0xd6, 0x00, 0x00
+};
+
+/* Sync packet header [8x8] (cf. rfc3550):
+ *  [0]   RTP v2: 0x80
+ *  [1]   Payload type: 0x54 | Marker bit: 0x80 (always set)
+ *  [2,3] Sequence number: 0x0007
+ *  [4,7] Timestamp: 0x00000000 (to be set) */
+static const uint8_t udp_sync_header[8] = {
+    0x80, 0xd4, 0x00, 0x07,
+    0x00, 0x00, 0x00, 0x00
+};
+
+/* Timing packet header [8x8] (cf. rfc3550):
+ *  [0]   RTP v2: 0x80
+ *  [1]   Payload type: 0x53 | Marker bit: 0x80 (always set)
+ *  [2,3] Sequence number: 0x0007
+ *  [4,7] Timestamp: 0x00000000 (unused) */
+#define PAYLOAD_TIMING_REQUEST  0x52
+#define PAYLOAD_TIMING_REPLY    0x53
+static const uint8_t udp_timing_header[8] = {
+    0x80, 0xd3, 0x00, 0x07,
+    0x00, 0x00, 0x00, 0x00
+};
+
+/**
+ * Function to trim a given character at the end of a string (no realloc).
+ * @param str Pointer to string
+ * @param rc Character to trim
+ */
+static inline void rtrim_char(char *str, char rc) {
+    char *sp = str + strlen(str) - 1;
+    while (sp >= str && *sp == rc) {
+        *sp = '\0';
+        sp -= 1;
+    }
+}
+
+/**
+ * Function to convert a timeval to ntp timestamp.
+ * @param tv Pointer to the timeval structure
+ * @return The NTP timestamp
+ */
+static inline uint64_t timeval_to_ntp(struct timeval *tv) {
+    uint64_t ntp = 0;
+
+    /* Converting micro seconds to a fraction. */
+    ntp = (uint64_t) tv->tv_usec * UINT32_MAX / PA_USEC_PER_SEC;
+    /* Moving reference from  1 Jan 1970 to 1 Jan 1900 (seconds). */
+    ntp |= (uint64_t) (tv->tv_sec + 0x83aa7e80) << 32;
+
+    return ntp;
+}
+
+/**
+ * Function to write bits into a buffer.
+ * @param buffer Handle to the buffer. It will be incremented if new data requires it.
+ * @param bit_pos A pointer to a position buffer to keep track the current write location (0 for MSB, 7 for LSB)
+ * @param size A pointer to the byte size currently written. This allows the calling function to do simple buffer overflow checks
+ * @param data The data to write
+ * @param data_bit_len The number of bits from data to write
+ */
+static inline void bit_writer(uint8_t **buffer, uint8_t *bit_pos, size_t *size, uint8_t data, uint8_t data_bit_len) {
+    int bits_left, bit_overflow;
+    uint8_t bit_data;
+
+    if (!data_bit_len)
+        return;
+
+    /* If bit pos is zero, we will definately use at least one bit from the current byte so size increments. */
+    if (!*bit_pos)
+        *size += 1;
+
+    /* Calc the number of bits left in the current byte of buffer. */
+    bits_left = 7 - *bit_pos  + 1;
+    /* Calc the overflow of bits in relation to how much space we have left... */
+    bit_overflow = bits_left - data_bit_len;
+    if (bit_overflow >= 0) {
+        /* We can fit the new data in our current byte.
+         * As we write from MSB->LSB we need to left shift by the overflow amount. */
+        bit_data = data << bit_overflow;
+        if (*bit_pos)
+            **buffer |= bit_data;
+        else
+            **buffer = bit_data;
+        /* If our data fits exactly into the current byte, we need to increment our pointer. */
+        if (0 == bit_overflow) {
+            /* Do not increment size as it will be incremented on next call as bit_pos is zero. */
+            *buffer += 1;
+            *bit_pos = 0;
+        } else {
+            *bit_pos += data_bit_len;
+        }
+    } else {
+        /* bit_overflow is negative, there for we will need a new byte from our buffer
+         * Firstly fill up what's left in the current byte. */
+        bit_data = data >> -bit_overflow;
+        **buffer |= bit_data;
+        /* Increment our buffer pointer and size counter. */
+        *buffer += 1;
+        *size += 1;
+        **buffer = data << (8 + bit_overflow);
+        *bit_pos = -bit_overflow;
+    }
+}
+
+static size_t write_ALAC_data(uint8_t *packet, const size_t max, uint8_t *raw, size_t *length, bool compress) {
+    uint32_t nbs = (*length / 2) / 2;
+    uint8_t *ibp, *maxibp;
+    uint8_t *bp, bpos;
+    size_t size = 0;
+
+    bp = packet;
+    pa_memzero(packet, max);
+    size = bpos = 0;
+
+    bit_writer(&bp, &bpos, &size, 1, 3); /* channel=1, stereo */
+    bit_writer(&bp, &bpos, &size, 0, 4); /* Unknown */
+    bit_writer(&bp, &bpos, &size, 0, 8); /* Unknown */
+    bit_writer(&bp, &bpos, &size, 0, 4); /* Unknown */
+    bit_writer(&bp, &bpos, &size, 1, 1); /* Hassize */
+    bit_writer(&bp, &bpos, &size, 0, 2); /* Unused */
+    bit_writer(&bp, &bpos, &size, 1, 1); /* Is-not-compressed */
+    /* Size of data, integer, big endian. */
+    bit_writer(&bp, &bpos, &size, (nbs >> 24) & 0xff, 8);
+    bit_writer(&bp, &bpos, &size, (nbs >> 16) & 0xff, 8);
+    bit_writer(&bp, &bpos, &size, (nbs >> 8)  & 0xff, 8);
+    bit_writer(&bp, &bpos, &size, (nbs)       & 0xff, 8);
+
+    ibp = raw;
+    maxibp = raw + (4 * nbs) - 4;
+    while (ibp <= maxibp) {
+        /* Byte swap stereo data. */
+        bit_writer(&bp, &bpos, &size, *(ibp + 1), 8);
+        bit_writer(&bp, &bpos, &size, *(ibp + 0), 8);
+        bit_writer(&bp, &bpos, &size, *(ibp + 3), 8);
+        bit_writer(&bp, &bpos, &size, *(ibp + 2), 8);
+        ibp += 4;
+    }
+
+    *length = (ibp - raw);
+    return size;
+}
+
+static size_t build_tcp_audio_packet(pa_raop_client *c, pa_memchunk *block, pa_memchunk *packet) {
+    const size_t head = sizeof(tcp_audio_header);
+    uint32_t *buffer = NULL;
+    uint8_t *raw = NULL;
+    size_t length, size;
+
+    raw = pa_memblock_acquire(block->memblock);
+    buffer = pa_memblock_acquire(packet->memblock);
+    buffer += packet->index / sizeof(uint32_t);
+    raw += block->index;
+
+    /* Wrap sequence number to 0 then UINT16_MAX is reached */
+    if (c->seq == UINT16_MAX)
+        c->seq = 0;
+    else
+        c->seq++;
+
+    memcpy(buffer, tcp_audio_header, sizeof(tcp_audio_header));
+    buffer[1] |= htonl((uint32_t) c->seq);
+    buffer[2] = htonl(c->rtptime);
+    buffer[3] = htonl(c->ssrc);
+
+    length = block->length;
+    size = sizeof(tcp_audio_header);
+    if (c->codec == PA_RAOP_CODEC_ALAC)
+        size += write_ALAC_data(((uint8_t *) buffer + head), packet->length - head, raw, &length, false);
+    else {
+        pa_log_debug("Only ALAC encoding is supported, sending zeros...");
+        pa_memzero(((uint8_t *) buffer + head), packet->length - head);
+        size += length;
+    }
+
+    c->rtptime += length / 4;
+
+    pa_memblock_release(block->memblock);
+
+    buffer[0] |= htonl((uint32_t) size - 4);
+    if (c->encryption == PA_RAOP_ENCRYPTION_RSA)
+        pa_raop_aes_encrypt(c->secret, (uint8_t *) buffer + head, size - head);
+
+    pa_memblock_release(packet->memblock);
+    packet->length = size;
+
+    return size;
+}
+
+static ssize_t send_tcp_audio_packet(pa_raop_client *c, pa_memchunk *block, size_t offset) {
+    static int write_type = 0;
+    const size_t max = sizeof(tcp_audio_header) + 8 + 16384;
+    pa_memchunk *packet = NULL;
+    uint8_t *buffer = NULL;
+    double progress = 0.0;
+    ssize_t written = -1;
+    size_t done = 0;
+
+    if (!(packet = pa_raop_packet_buffer_retrieve(c->pbuf, c->seq)))
+        return -1;
+
+    if (packet->length <= 0) {
+        pa_assert(block->index == offset);
+
+        if (!(packet = pa_raop_packet_buffer_prepare(c->pbuf, c->seq + 1, max)))
+            return -1;
+
+        packet->index = 0;
+        packet->length = max;
+        if (!build_tcp_audio_packet(c, block, packet))
+            return -1;
+    }
+
+    buffer = pa_memblock_acquire(packet->memblock);
+
+    pa_assert(buffer);
+
+    buffer += packet->index;
+    if (buffer && packet->length > 0)
+        written = pa_write(c->tcp_sfd, buffer, packet->length, &write_type);
+    if (written > 0) {
+        progress = (double) written / (double) packet->length;
+        packet->length -= written;
+        packet->index += written;
+
+        done = block->length * progress;
+        block->length -= done;
+        block->index += done;
+    }
+
+    pa_memblock_release(packet->memblock);
+
+    return written;
+}
+
+static size_t build_udp_audio_packet(pa_raop_client *c, pa_memchunk *block, pa_memchunk *packet) {
+    const size_t head = sizeof(udp_audio_header);
+    uint32_t *buffer = NULL;
+    uint8_t *raw = NULL;
+    size_t length, size;
+
+    raw = pa_memblock_acquire(block->memblock);
+    buffer = pa_memblock_acquire(packet->memblock);
+    buffer += packet->index / sizeof(uint32_t);
+    raw += block->index;
+
+    memcpy(buffer, udp_audio_header, sizeof(udp_audio_header));
+    if (c->is_first_packet)
+        buffer[0] |= htonl((uint32_t) 0x80 << 16);
+    buffer[0] |= htonl((uint32_t) c->seq);
+    buffer[1] = htonl(c->rtptime);
+    buffer[2] = htonl(c->ssrc);
+
+    length = block->length;
+    size = sizeof(udp_audio_header);
+    if (c->codec == PA_RAOP_CODEC_ALAC)
+        size += write_ALAC_data(((uint8_t *) buffer + head), packet->length - head, raw, &length, false);
+    else {
+        pa_log_debug("Only ALAC encoding is supported, sending zeros...");
+        pa_memzero(((uint8_t *) buffer + head), packet->length - head);
+        size += length;
+    }
+
+    c->rtptime += length / 4;
+
+    /* Wrap sequence number to 0 then UINT16_MAX is reached */
+    if (c->seq == UINT16_MAX)
+        c->seq = 0;
+    else
+        c->seq++;
+
+    pa_memblock_release(block->memblock);
+
+    if (c->encryption == PA_RAOP_ENCRYPTION_RSA)
+        pa_raop_aes_encrypt(c->secret, (uint8_t *) buffer + head, size - head);
+
+    pa_memblock_release(packet->memblock);
+    packet->length = size;
+
+    return size;
+}
+
+static ssize_t send_udp_audio_packet(pa_raop_client *c, pa_memchunk *block, size_t offset) {
+    const size_t max = sizeof(udp_audio_retrans_header) + sizeof(udp_audio_header) + 8 + 1408;
+    pa_memchunk *packet = NULL;
+    uint8_t *buffer = NULL;
+    ssize_t written = -1;
+
+    /* UDP packet has to be sent at once ! */
+    pa_assert(block->index == offset);
+
+    if (!(packet = pa_raop_packet_buffer_prepare(c->pbuf, c->seq, max)))
+        return -1;
+
+    packet->index = sizeof(udp_audio_retrans_header);
+    packet->length = max - sizeof(udp_audio_retrans_header);
+    if (!build_udp_audio_packet(c, block, packet))
+        return -1;
+
+    buffer = pa_memblock_acquire(packet->memblock);
+
+    pa_assert(buffer);
+
+    buffer += packet->index;
+    if (buffer && packet->length > 0)
+        written = pa_write(c->udp_sfd, buffer, packet->length, NULL);
+    if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+        pa_log_debug("Discarding UDP (audio, seq=%d) packet due to EAGAIN (%s)", c->seq, pa_cstrerror(errno));
+        written = packet->length;
+    }
+
+    pa_memblock_release(packet->memblock);
+    /* It is meaningless to preseve the partial data */
+    block->index += block->length;
+    block->length = 0;
+
+    return written;
+}
+
+static size_t rebuild_udp_audio_packet(pa_raop_client *c, uint16_t seq, pa_memchunk *packet) {
+    size_t size = sizeof(udp_audio_retrans_header);
+    uint32_t *buffer = NULL;
+
+    buffer = pa_memblock_acquire(packet->memblock);
+
+    memcpy(buffer, udp_audio_retrans_header, sizeof(udp_audio_retrans_header));
+    buffer[0] |= htonl((uint32_t) seq);
+    size += packet->length;
+
+    pa_memblock_release(packet->memblock);
+    packet->length += sizeof(udp_audio_retrans_header);
+    packet->index -= sizeof(udp_audio_retrans_header);
+
+    return size;
+}
+
+static ssize_t resend_udp_audio_packets(pa_raop_client *c, uint16_t seq, uint16_t nbp) {
+    ssize_t total = 0;
+    int i = 0;
+
+    for (i = 0; i < nbp; i++) {
+        pa_memchunk *packet = NULL;
+        uint8_t *buffer = NULL;
+        ssize_t written = -1;
+
+        if (!(packet = pa_raop_packet_buffer_retrieve(c->pbuf, seq + i)))
+            continue;
+
+        if (packet->index > 0) {
+            if (!rebuild_udp_audio_packet(c, seq + i, packet))
+                continue;
+        }
+
+        pa_assert(packet->index == 0);
+
+        buffer = pa_memblock_acquire(packet->memblock);
+
+        pa_assert(buffer);
+
+        if (buffer && packet->length > 0)
+            written = pa_write(c->udp_cfd, buffer, packet->length, NULL);
+        if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+            pa_log_debug("Discarding UDP (audio-restransmitted, seq=%d) packet due to EAGAIN", seq + i);
+            pa_memblock_release(packet->memblock);
+            continue;
+        }
+
+        pa_memblock_release(packet->memblock);
+        total +=  written;
+    }
+
+    return total;
+}
+
+/* Caller has to free the allocated memory region for packet */
+static size_t build_udp_sync_packet(pa_raop_client *c, uint32_t stamp, uint32_t **packet) {
+    const size_t size = sizeof(udp_sync_header) + 12;
+    const uint32_t delay = 88200;
+    uint32_t *buffer = NULL;
+    uint64_t transmitted = 0;
+    struct timeval tv;
+
+    *packet = NULL;
+    if (!(buffer = pa_xmalloc0(size)))
+        return 0;
+
+    memcpy(buffer, udp_sync_header, sizeof(udp_sync_header));
+    if (c->is_first_packet)
+        buffer[0] |= 0x10;
+    stamp -= delay;
+    buffer[1] = htonl(stamp);
+    /* Set the transmited timestamp to current time. */
+    transmitted = timeval_to_ntp(pa_rtclock_get(&tv));
+    buffer[2] = htonl(transmitted >> 32);
+    buffer[3] = htonl(transmitted & 0xffffffff);
+    stamp += delay;
+    buffer[4] = htonl(stamp);
+
+    *packet = buffer;
+    return size;
+}
+
+static ssize_t send_udp_sync_packet(pa_raop_client *c, uint32_t stamp) {
+    uint32_t * packet = NULL;
+    ssize_t written = 0;
+    size_t size = 0;
+
+    size = build_udp_sync_packet(c, stamp, &packet);
+    if (packet != NULL && size > 0) {
+        written = pa_loop_write(c->udp_cfd, packet, size, NULL);
+        pa_xfree(packet);
+    }
+
+    return written;
+}
+
+static size_t handle_udp_control_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size) {
+    uint8_t payload = 0;
+    uint16_t seq, nbp = 0;
+    ssize_t written = 0;
+
+    /* Control packets are 8 bytes long:  */
+    if (size != 8 || packet[0] != 0x80)
+        return 1;
+
+    seq = ntohs((uint16_t) (packet[4] | packet[5] << 8));
+    nbp = ntohs((uint16_t) (packet[6] | packet[7] << 8));
+    if (nbp <= 0)
+        return 1;
+
+    /* The marker bit is always set (see rfc3550 for packet structure) ! */
+    payload = packet[1] ^ 0x80;
+    switch (payload) {
+        case PAYLOAD_RETRANSMIT_REQUEST:
+            pa_log_debug("Resending %u packets starting at %u", nbp, seq);
+            written = resend_udp_audio_packets(c, seq, nbp);
+            break;
+        case PAYLOAD_RETRANSMIT_REPLY:
+        default:
+            pa_log_debug("Got an unexpected payload type on control channel (%u) !", payload);
+            break;
+    }
+
+    return written;
+}
+
+/* Caller has to free the allocated memory region for packet */
+static size_t build_udp_timing_packet(pa_raop_client *c, const uint32_t data[6], uint64_t received, uint32_t **packet) {
+    const size_t size = sizeof(udp_timing_header) + 24;
+    uint32_t *buffer = NULL;
+    uint64_t transmitted = 0;
+    struct timeval tv;
+
+    *packet = NULL;
+    if (!(buffer = pa_xmalloc0(size)))
+        return 0;
+
+    memcpy(buffer, udp_timing_header, sizeof(udp_timing_header));
+    /* Copying originate timestamp from the incoming request packet. */
+    buffer[2] = data[4];
+    buffer[3] = data[5];
+    /* Set the receive timestamp to reception time. */
+    buffer[4] = htonl(received >> 32);
+    buffer[5] = htonl(received & 0xffffffff);
+    /* Set the transmit timestamp to current time. */
+    transmitted = timeval_to_ntp(pa_rtclock_get(&tv));
+    buffer[6] = htonl(transmitted >> 32);
+    buffer[7] = htonl(transmitted & 0xffffffff);
+
+    *packet = buffer;
+    return size;
+}
+
+static ssize_t send_udp_timing_packet(pa_raop_client *c, const uint32_t data[6], uint64_t received) {
+    uint32_t * packet = NULL;
+    ssize_t written = 0;
+    size_t size = 0;
+
+    size = build_udp_timing_packet(c, data, received, &packet);
+    if (packet != NULL && size > 0) {
+        written = pa_loop_write(c->udp_tfd, packet, size, NULL);
+        pa_xfree(packet);
+    }
+
+    return written;
+}
+
+static size_t handle_udp_timing_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size) {
+    const uint32_t * data = NULL;
+    uint8_t payload = 0;
+    struct timeval tv;
+    size_t written = 0;
+    uint64_t rci = 0;
+
+    /* Timing packets are 32 bytes long: 1 x 8 RTP header (no ssrc) + 3 x 8 NTP timestamps */
+    if (size != 32 || packet[0] != 0x80)
+        return 0;
+
+    rci = timeval_to_ntp(pa_rtclock_get(&tv));
+    data = (uint32_t *) (packet + sizeof(udp_timing_header));
+
+    /* The marker bit is always set (see rfc3550 for packet structure) ! */
+    payload = packet[1] ^ 0x80;
+    switch (payload) {
+        case PAYLOAD_TIMING_REQUEST:
+            pa_log_debug("Sending timing packet at %lu", rci);
+            written = send_udp_timing_packet(c, data, rci);
+            break;
+        case PAYLOAD_TIMING_REPLY:
+        default:
+            pa_log_debug("Got an unexpected payload type on timing channel (%u) !", payload);
+            break;
+    }
+
+    return written;
+}
+
+static int connect_udp_socket(pa_raop_client *c, int fd, uint16_t port) {
+    struct sockaddr_in sa4;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 sa6;
+#endif
+    struct sockaddr *sa;
+    socklen_t salen;
+    sa_family_t af;
+
+    pa_zero(sa4);
+#ifdef HAVE_IPV6
+    pa_zero(sa6);
+#endif
+    if (inet_pton(AF_INET, c->host, &sa4.sin_addr) > 0) {
+        sa4.sin_family = af = AF_INET;
+        sa4.sin_port = htons(port);
+        sa = (struct sockaddr *) &sa4;
+        salen = sizeof(sa4);
+#ifdef HAVE_IPV6
+    } else if (inet_pton(AF_INET6, c->host, &sa6.sin6_addr) > 0) {
+        sa6.sin6_family = af = AF_INET6;
+        sa6.sin6_port = htons(port);
+        sa = (struct sockaddr *) &sa6;
+        salen = sizeof(sa6);
+#endif
+    } else {
+        pa_log("Invalid destination '%s'", c->host);
+        goto fail;
+    }
+
+    if (fd < 0 && (fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("socket() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    /* If the socket queue is full, let's drop packets */
+    pa_make_udp_socket_low_delay(fd);
+    pa_make_fd_nonblock(fd);
+
+    if (connect(fd, sa, salen) < 0) {
+        pa_log("connect() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_log_debug("Connected to %s on port %d (SOCK_DGRAM)", c->host, port);
+    return fd;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return -1;
+}
+
+static int open_bind_udp_socket(pa_raop_client *c, uint16_t *actual_port) {
+    int fd = -1;
+    uint16_t port;
+    struct sockaddr_in sa4;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 sa6;
+#endif
+    struct sockaddr *sa;
+    uint16_t *sa_port;
+    socklen_t salen;
+    sa_family_t af;
+    int one = 1;
+
+    pa_assert(actual_port);
+
+    port = *actual_port;
+
+    pa_zero(sa4);
+#ifdef HAVE_IPV6
+    pa_zero(sa6);
+#endif
+    if (inet_pton(AF_INET, pa_rtsp_localip(c->rtsp), &sa4.sin_addr) > 0) {
+        sa4.sin_family = af = AF_INET;
+        sa4.sin_port = htons(port);
+        sa4.sin_addr.s_addr = INADDR_ANY;
+        sa = (struct sockaddr *) &sa4;
+        salen = sizeof(sa4);
+        sa_port = &sa4.sin_port;
+#ifdef HAVE_IPV6
+    } else if (inet_pton(AF_INET6, pa_rtsp_localip(c->rtsp), &sa6.sin6_addr) > 0) {
+        sa6.sin6_family = af = AF_INET6;
+        sa6.sin6_port = htons(port);
+        sa6.sin6_addr = in6addr_any;
+        sa = (struct sockaddr *) &sa6;
+        salen = sizeof(sa6);
+        sa_port = &sa6.sin6_port;
+#endif
+    } else {
+        pa_log("Could not determine which address family to use");
+        goto fail;
+    }
+
+    if ((fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("socket() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+#ifdef SO_TIMESTAMP
+    if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) {
+        pa_log("setsockopt(SO_TIMESTAMP) failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+#else
+    pa_log("SO_TIMESTAMP unsupported on this platform");
+    goto fail;
+#endif
+
+    one = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
+        pa_log("setsockopt(SO_REUSEADDR) failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    do {
+        int ret;
+
+        *sa_port = htons(port);
+        ret = bind(fd, sa, salen);
+        if (!ret)
+            break;
+        if (ret < 0 && errno != EADDRINUSE) {
+            pa_log("bind() failed: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+    } while (++port > 0);
+
+    if (!port) {
+        pa_log("Could not bind port");
+        goto fail;
+    }
+
+    pa_log_debug("Socket bound to port %d (SOCK_DGRAM)", port);
+    *actual_port = port;
+
+    return fd;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return -1;
+}
+
+static void tcp_connection_cb(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
+    pa_raop_client *c = userdata;
+
+    pa_assert(sc);
+    pa_assert(c);
+
+    pa_socket_client_unref(sc);
+
+    if (!io) {
+        pa_log("Connection failed: %s", pa_cstrerror(errno));
+        return;
+    }
+
+    c->tcp_sfd = pa_iochannel_get_send_fd(io);
+    pa_iochannel_set_noclose(io, true);
+    pa_make_tcp_socket_low_delay(c->tcp_sfd);
+
+    pa_iochannel_free(io);
+
+    pa_log_debug("Connection established (TCP)");
+
+    if (c->state_callback)
+        c->state_callback(PA_RAOP_CONNECTED, c->state_userdata);
+}
+
+static void rtsp_stream_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_status_t status, pa_headerlist *headers, void *userdata) {
+    pa_raop_client *c = userdata;
+
+    pa_assert(c);
+    pa_assert(rtsp);
+    pa_assert(rtsp == c->rtsp);
+
+    switch (state) {
+        case STATE_CONNECT: {
+            char *key, *iv, *sdp = NULL;
+            int frames = 0;
+            const char *ip;
+            char *url;
+            int ipv;
+
+            pa_log_debug("RAOP: CONNECTED");
+
+            ip = pa_rtsp_localip(c->rtsp);
+            if (pa_is_ip6_address(ip)) {
+                ipv = 6;
+                url = pa_sprintf_malloc("rtsp://[%s]/%s", ip, c->sid);
+            } else {
+                ipv = 4;
+                url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid);
+            }
+            pa_rtsp_set_url(c->rtsp, url);
+
+            if (c->protocol == PA_RAOP_PROTOCOL_TCP)
+                frames = FRAMES_PER_TCP_PACKET;
+            else if (c->protocol == PA_RAOP_PROTOCOL_UDP)
+                frames = FRAMES_PER_UDP_PACKET;
+
+            switch(c->encryption) {
+                case PA_RAOP_ENCRYPTION_NONE: {
+                    sdp = pa_sprintf_malloc(
+                        "v=0\r\n"
+                        "o=iTunes %s 0 IN IP%d %s\r\n"
+                        "s=iTunes\r\n"
+                        "c=IN IP%d %s\r\n"
+                        "t=0 0\r\n"
+                        "m=audio 0 RTP/AVP 96\r\n"
+                        "a=rtpmap:96 AppleLossless\r\n"
+                        "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n",
+                        c->sid, ipv, ip, ipv, c->host, frames);
+
+                    break;
+                }
+
+                case PA_RAOP_ENCRYPTION_RSA:
+                case PA_RAOP_ENCRYPTION_FAIRPLAY:
+                case PA_RAOP_ENCRYPTION_MFISAP:
+                case PA_RAOP_ENCRYPTION_FAIRPLAY_SAP25: {
+                    key = pa_raop_secret_get_key(c->secret);
+                    iv = pa_raop_secret_get_iv(c->secret);
+
+                    sdp = pa_sprintf_malloc(
+                        "v=0\r\n"
+                        "o=iTunes %s 0 IN IP%d %s\r\n"
+                        "s=iTunes\r\n"
+                        "c=IN IP%d %s\r\n"
+                        "t=0 0\r\n"
+                        "m=audio 0 RTP/AVP 96\r\n"
+                        "a=rtpmap:96 AppleLossless\r\n"
+                        "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n"
+                        "a=rsaaeskey:%s\r\n"
+                        "a=aesiv:%s\r\n",
+                        c->sid, ipv, ip, ipv, c->host, frames, key, iv);
+
+                    pa_xfree(key);
+                    pa_xfree(iv);
+                    break;
+                }
+            }
+
+            pa_rtsp_announce(c->rtsp, sdp);
+
+            pa_xfree(sdp);
+            pa_xfree(url);
+            break;
+        }
+
+        case STATE_OPTIONS: {
+            pa_log_debug("RAOP: OPTIONS (stream cb)");
+
+            break;
+        }
+
+        case STATE_ANNOUNCE: {
+            uint16_t cport = DEFAULT_UDP_CONTROL_PORT;
+            uint16_t tport = DEFAULT_UDP_TIMING_PORT;
+            char *trs = NULL;
+
+            pa_log_debug("RAOP: ANNOUNCE");
+
+            if (c->protocol == PA_RAOP_PROTOCOL_TCP) {
+                trs = pa_sprintf_malloc(
+                    "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
+            } else if (c->protocol == PA_RAOP_PROTOCOL_UDP) {
+                c->udp_cfd = open_bind_udp_socket(c, &cport);
+                c->udp_tfd  = open_bind_udp_socket(c, &tport);
+                if (c->udp_cfd < 0 || c->udp_tfd < 0)
+                    goto annonce_error;
+
+                trs = pa_sprintf_malloc(
+                    "RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;"
+                    "control_port=%d;timing_port=%d",
+                    cport, tport);
+            }
+
+            pa_rtsp_setup(c->rtsp, trs);
+
+            pa_xfree(trs);
+            break;
+
+        annonce_error:
+            if (c->udp_cfd >= 0)
+                pa_close(c->udp_cfd);
+            c->udp_cfd = -1;
+            if (c->udp_tfd >= 0)
+                pa_close(c->udp_tfd);
+            c->udp_tfd = -1;
+
+            pa_rtsp_client_free(c->rtsp);
+
+            pa_log_error("Aborting RTSP announce, failed creating required sockets");
+
+            c->rtsp = NULL;
+            pa_xfree(trs);
+            break;
+        }
+
+        case STATE_SETUP: {
+            pa_socket_client *sc = NULL;
+            uint32_t sport = DEFAULT_UDP_AUDIO_PORT;
+            uint32_t cport =0, tport = 0;
+            char *ajs, *token, *pc, *trs;
+            const char *token_state = NULL;
+            char delimiters[] = ";";
+
+            pa_log_debug("RAOP: SETUP");
+
+            ajs = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Jack-Status"));
+
+            if (ajs) {
+                c->jack_type = JACK_TYPE_ANALOG;
+                c->jack_status = JACK_STATUS_DISCONNECTED;
+
+                while ((token = pa_split(ajs, delimiters, &token_state))) {
+                    if ((pc = strstr(token, "="))) {
+                      *pc = 0;
+                      if (pa_streq(token, "type") && pa_streq(pc + 1, "digital"))
+                          c->jack_type = JACK_TYPE_DIGITAL;
+                    } else {
+                        if (pa_streq(token, "connected"))
+                            c->jack_status = JACK_STATUS_CONNECTED;
+                    }
+                    pa_xfree(token);
+                }
+
+            } else {
+                pa_log_warn("\"Audio-Jack-Status\" missing in RTSP setup response");
+            }
+
+            sport = pa_rtsp_serverport(c->rtsp);
+            if (sport <= 0)
+                goto setup_error;
+
+            token_state = NULL;
+            if (c->protocol == PA_RAOP_PROTOCOL_TCP) {
+                if (!(sc = pa_socket_client_new_string(c->core->mainloop, true, c->host, sport)))
+                    goto setup_error;
+
+                pa_socket_client_ref(sc);
+                pa_socket_client_set_callback(sc, tcp_connection_cb, c);
+
+                pa_socket_client_unref(sc);
+                sc = NULL;
+            } else if (c->protocol == PA_RAOP_PROTOCOL_UDP) {
+                trs = pa_xstrdup(pa_headerlist_gets(headers, "Transport"));
+
+                if (trs) {
+                    /* Now parse out the server port component of the response. */
+                    while ((token = pa_split(trs, delimiters, &token_state))) {
+                        if ((pc = strstr(token, "="))) {
+                            *pc = 0;
+                            if (pa_streq(token, "control_port")) {
+                                if (pa_atou(pc + 1, &cport) < 0)
+                                    goto setup_error_parse;
+                            }
+                            if (pa_streq(token, "timing_port")) {
+                                if (pa_atou(pc + 1, &tport) < 0)
+                                    goto setup_error_parse;
+                            }
+                            *pc = '=';
+                        }
+                        pa_xfree(token);
+                    }
+                    pa_xfree(trs);
+                } else {
+                    pa_log_warn("\"Transport\" missing in RTSP setup response");
+                }
+
+                if (cport <= 0 || tport <= 0)
+                    goto setup_error;
+
+                if ((c->udp_sfd = connect_udp_socket(c, -1, sport)) <= 0)
+                    goto setup_error;
+                if ((c->udp_cfd = connect_udp_socket(c, c->udp_cfd, cport)) <= 0)
+                    goto setup_error;
+                if ((c->udp_tfd = connect_udp_socket(c, c->udp_tfd, tport)) <= 0)
+                    goto setup_error;
+
+                pa_log_debug("Connection established (UDP;control_port=%d;timing_port=%d)", cport, tport);
+
+                if (c->state_callback)
+                    c->state_callback(PA_RAOP_CONNECTED, c->state_userdata);
+            }
+
+            pa_rtsp_record(c->rtsp, &c->seq, &c->rtptime);
+
+            pa_xfree(ajs);
+            break;
+
+        setup_error_parse:
+            pa_log("Failed parsing server port components");
+            pa_xfree(token);
+            pa_xfree(trs);
+            /* fall-thru */
+        setup_error:
+            if (c->tcp_sfd >= 0)
+                pa_close(c->tcp_sfd);
+            c->tcp_sfd = -1;
+
+            if (c->udp_sfd >= 0)
+                pa_close(c->udp_sfd);
+            c->udp_sfd = -1;
+
+            c->udp_cfd = c->udp_tfd = -1;
+
+            pa_rtsp_client_free(c->rtsp);
+
+            pa_log_error("aborting RTSP setup, failed creating required sockets");
+
+            if (c->state_callback)
+                c->state_callback(PA_RAOP_DISCONNECTED, c->state_userdata);
+
+            c->rtsp = NULL;
+            break;
+        }
+
+        case STATE_RECORD: {
+            int32_t latency = 0;
+            uint32_t ssrc;
+            char *alt;
+
+            pa_log_debug("RAOP: RECORD");
+
+            alt = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Latency"));
+            if (alt) {
+                if (pa_atoi(alt, &latency) < 0)
+                    pa_log("Failed to parse audio latency");
+            }
+
+            pa_raop_packet_buffer_reset(c->pbuf, c->seq);
+
+            pa_random(&ssrc, sizeof(ssrc));
+            c->is_first_packet = true;
+            c->is_recording = true;
+            c->sync_count = 0;
+            c->ssrc = ssrc;
+
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_RECORDING, c->state_userdata);
+
+            pa_xfree(alt);
+            break;
+        }
+
+        case STATE_SET_PARAMETER: {
+            pa_log_debug("RAOP: SET_PARAMETER");
+
+            break;
+        }
+
+        case STATE_FLUSH: {
+            pa_log_debug("RAOP: FLUSHED");
+
+            break;
+        }
+
+        case STATE_TEARDOWN: {
+            pa_log_debug("RAOP: TEARDOWN");
+
+            if (c->tcp_sfd >= 0)
+                pa_close(c->tcp_sfd);
+            c->tcp_sfd = -1;
+
+            if (c->udp_sfd >= 0)
+                pa_close(c->udp_sfd);
+            c->udp_sfd = -1;
+
+            /* Polling sockets will be closed by sink */
+            c->udp_cfd = c->udp_tfd = -1;
+            c->tcp_sfd = -1;
+
+            pa_rtsp_client_free(c->rtsp);
+            pa_xfree(c->sid);
+            c->rtsp = NULL;
+            c->sid = NULL;
+
+            if (c->state_callback)
+                c->state_callback(PA_RAOP_DISCONNECTED, c->state_userdata);
+
+            break;
+        }
+
+        case STATE_DISCONNECTED: {
+            pa_log_debug("RAOP: DISCONNECTED");
+
+            c->is_recording = false;
+
+            if (c->tcp_sfd >= 0)
+                pa_close(c->tcp_sfd);
+            c->tcp_sfd = -1;
+
+            if (c->udp_sfd >= 0)
+                pa_close(c->udp_sfd);
+            c->udp_sfd = -1;
+
+            /* Polling sockets will be closed by sink */
+            c->udp_cfd = c->udp_tfd = -1;
+            c->tcp_sfd = -1;
+
+            pa_log_error("RTSP control channel closed (disconnected)");
+
+            pa_rtsp_client_free(c->rtsp);
+            pa_xfree(c->sid);
+            c->rtsp = NULL;
+            c->sid = NULL;
+
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_DISCONNECTED, c->state_userdata);
+
+            break;
+        }
+    }
+}
+
+static void rtsp_auth_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_status_t status, pa_headerlist *headers, void *userdata) {
+    pa_raop_client *c = userdata;
+
+    pa_assert(c);
+    pa_assert(rtsp);
+    pa_assert(rtsp == c->rtsp);
+
+    switch (state) {
+        case STATE_CONNECT: {
+            char *sci = NULL, *sac = NULL;
+            uint8_t rac[APPLE_CHALLENGE_LENGTH];
+            struct {
+                uint32_t ci1;
+                uint32_t ci2;
+            } rci;
+
+            pa_random(&rci, sizeof(rci));
+            /* Generate a random Client-Instance number */
+            sci = pa_sprintf_malloc("%08x%08x",rci.ci1, rci.ci2);
+            pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
+
+            pa_random(rac, APPLE_CHALLENGE_LENGTH);
+            /* Generate a random Apple-Challenge key */
+            pa_raop_base64_encode(rac, APPLE_CHALLENGE_LENGTH, &sac);
+            rtrim_char(sac, '=');
+            pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
+
+            pa_rtsp_options(c->rtsp);
+
+            pa_xfree(sac);
+            pa_xfree(sci);
+            break;
+        }
+
+        case STATE_OPTIONS: {
+            static bool waiting = false;
+            const char *current = NULL;
+            char space[] = " ";
+            char *token, *ath = NULL;
+            char *publ, *wath, *mth = NULL, *val;
+            char *realm = NULL, *nonce = NULL, *response = NULL;
+            char comma[] = ",";
+
+            pa_log_debug("RAOP: OPTIONS (auth cb)");
+            /* We do not consider the Apple-Response */
+            pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
+
+            if (STATUS_UNAUTHORIZED == status) {
+                wath = pa_xstrdup(pa_headerlist_gets(headers, "WWW-Authenticate"));
+                if (true == waiting) {
+                    pa_xfree(wath);
+                    goto fail;
+                }
+
+                if (wath) {
+                    mth = pa_split(wath, space, &current);
+                    while ((token = pa_split(wath, comma, &current))) {
+                        if ((val = strstr(token, "="))) {
+                            if (NULL == realm && val > strstr(token, "realm"))
+                                realm = pa_xstrdup(val + 2);
+                            else if (NULL == nonce && val > strstr(token, "nonce"))
+                                nonce = pa_xstrdup(val + 2);
+                        }
+
+                        pa_xfree(token);
+                    }
+                }
+
+                if (pa_safe_streq(mth, "Basic") && realm) {
+                    rtrim_char(realm, '\"');
+
+                    pa_raop_basic_response(DEFAULT_USER_NAME, c->password, &response);
+                    ath = pa_sprintf_malloc("Basic %s",
+                        response);
+                } else if (pa_safe_streq(mth, "Digest") && realm && nonce) {
+                    rtrim_char(realm, '\"');
+                    rtrim_char(nonce, '\"');
+
+                    pa_raop_digest_response(DEFAULT_USER_NAME, realm, c->password, nonce, "*", &response);
+                    ath = pa_sprintf_malloc("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"*\", response=\"%s\"",
+                        DEFAULT_USER_NAME, realm, nonce,
+                        response);
+                } else {
+                    pa_log_error("unsupported authentication method: %s", mth);
+                    pa_xfree(realm);
+                    pa_xfree(nonce);
+                    pa_xfree(wath);
+                    pa_xfree(mth);
+                    goto error;
+                }
+
+                pa_xfree(response);
+                pa_xfree(realm);
+                pa_xfree(nonce);
+                pa_xfree(wath);
+                pa_xfree(mth);
+
+                pa_rtsp_add_header(c->rtsp, "Authorization", ath);
+                pa_xfree(ath);
+
+                waiting = true;
+                pa_rtsp_options(c->rtsp);
+                break;
+            }
+
+            if (STATUS_OK == status) {
+                publ = pa_xstrdup(pa_headerlist_gets(headers, "Public"));
+                c->sci = pa_xstrdup(pa_rtsp_get_header(c->rtsp, "Client-Instance"));
+
+                if (c->password)
+                    pa_xfree(c->password);
+                pa_xfree(publ);
+                c->password = NULL;
+            }
+
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_AUTHENTICATED, c->state_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            waiting = false;
+            break;
+
+        fail:
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_DISCONNECTED, c->state_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            pa_log_error("aborting authentication, wrong password");
+
+            waiting = false;
+            break;
+
+        error:
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_DISCONNECTED, c->state_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            pa_log_error("aborting authentication, unexpected failure");
+
+            waiting = false;
+            break;
+        }
+
+        case STATE_ANNOUNCE:
+        case STATE_SETUP:
+        case STATE_RECORD:
+        case STATE_SET_PARAMETER:
+        case STATE_FLUSH:
+        case STATE_TEARDOWN:
+        case STATE_DISCONNECTED:
+        default: {
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_DISCONNECTED, c->state_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            if (c->sci)
+                pa_xfree(c->sci);
+            c->sci = NULL;
+
+            break;
+        }
+    }
+}
+
+pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol,
+                                   pa_raop_encryption_t encryption, pa_raop_codec_t codec) {
+    pa_raop_client *c;
+
+    pa_parsed_address a;
+    pa_sample_spec ss;
+    size_t size = 2;
+
+    pa_assert(core);
+    pa_assert(host);
+
+    if (pa_parse_address(host, &a) < 0)
+        return NULL;
+
+    if (a.type == PA_PARSED_ADDRESS_UNIX) {
+        pa_xfree(a.path_or_host);
+        return NULL;
+    }
+
+    c = pa_xnew0(pa_raop_client, 1);
+    c->core = core;
+    c->host = a.path_or_host; /* Will eventually be freed on destruction of c */
+    if (a.port > 0)
+        c->port = a.port;
+    else
+        c->port = DEFAULT_RAOP_PORT;
+    c->rtsp = NULL;
+    c->sci = c->sid = NULL;
+    c->password = NULL;
+
+    c->protocol = protocol;
+    c->encryption = encryption;
+    c->codec = codec;
+
+    c->tcp_sfd = -1;
+
+    c->udp_sfd = -1;
+    c->udp_cfd = -1;
+    c->udp_tfd = -1;
+
+    c->secret = NULL;
+    if (c->encryption != PA_RAOP_ENCRYPTION_NONE)
+        c->secret = pa_raop_secret_new();
+
+    ss = core->default_sample_spec;
+    if (c->protocol == PA_RAOP_PROTOCOL_UDP)
+        size = RTX_BUFFERING_SECONDS * ss.rate / FRAMES_PER_UDP_PACKET;
+
+    c->is_recording = false;
+    c->is_first_packet = true;
+    /* Packet sync interval should be around 1s (UDP only) */
+    c->sync_interval = ss.rate / FRAMES_PER_UDP_PACKET;
+    c->sync_count = 0;
+
+    c->pbuf = pa_raop_packet_buffer_new(c->core->mempool, size);
+
+    return c;
+}
+
+void pa_raop_client_free(pa_raop_client *c) {
+    pa_assert(c);
+
+    pa_raop_packet_buffer_free(c->pbuf);
+
+    pa_xfree(c->sid);
+    pa_xfree(c->sci);
+    if (c->secret)
+        pa_raop_secret_free(c->secret);
+    pa_xfree(c->password);
+    c->sci = c->sid = NULL;
+    c->password = NULL;
+    c->secret = NULL;
+
+    if (c->rtsp)
+        pa_rtsp_client_free(c->rtsp);
+    c->rtsp = NULL;
+
+    pa_xfree(c->host);
+    pa_xfree(c);
+}
+
+int pa_raop_client_authenticate (pa_raop_client *c, const char *password) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (c->rtsp || c->password) {
+        pa_log_debug("Authentication/Connection already in progress...");
+        return 0;
+    }
+
+    c->password = NULL;
+    if (password)
+        c->password = pa_xstrdup(password);
+    c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, DEFAULT_USER_AGENT);
+
+    pa_assert(c->rtsp);
+
+    pa_rtsp_set_callback(c->rtsp, rtsp_auth_cb, c);
+    rv = pa_rtsp_connect(c->rtsp);
+    return rv;
+}
+
+bool pa_raop_client_is_authenticated(pa_raop_client *c) {
+    pa_assert(c);
+
+    return (c->sci != NULL);
+}
+
+int pa_raop_client_announce(pa_raop_client *c) {
+    uint32_t sid;
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (c->rtsp) {
+        pa_log_debug("Connection already in progress...");
+        return 0;
+    } else if (!c->sci) {
+        pa_log_debug("ANNOUNCE requires a preliminary authentication");
+        return 1;
+    }
+
+    c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, DEFAULT_USER_AGENT);
+
+    pa_assert(c->rtsp);
+
+    c->sync_count = 0;
+    c->is_recording = false;
+    c->is_first_packet = true;
+    pa_random(&sid, sizeof(sid));
+    c->sid = pa_sprintf_malloc("%u", sid);
+    pa_rtsp_set_callback(c->rtsp, rtsp_stream_cb, c);
+
+    rv = pa_rtsp_connect(c->rtsp);
+    return rv;
+}
+
+bool pa_raop_client_is_alive(pa_raop_client *c) {
+    pa_assert(c);
+
+    if (!c->rtsp || !c->sci) {
+        pa_log_debug("Not alive, connection not established yet...");
+        return false;
+    }
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            if (c->tcp_sfd >= 0)
+                return true;
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            if (c->udp_sfd >= 0)
+                return true;
+            break;
+        default:
+            break;
+    }
+
+    return false;
+}
+
+bool pa_raop_client_can_stream(pa_raop_client *c) {
+    pa_assert(c);
+
+    if (!c->rtsp || !c->sci) {
+        pa_log_debug("Can't stream, connection not established yet...");
+        return false;
+    }
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            if (c->tcp_sfd >= 0 && c->is_recording)
+                return true;
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            if (c->udp_sfd >= 0 && c->is_recording)
+                return true;
+            break;
+        default:
+            break;
+    }
+
+    return false;
+}
+
+int pa_raop_client_stream(pa_raop_client *c) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (!c->rtsp || !c->sci) {
+        pa_log_debug("Streaming's impossible, connection not established yet...");
+        return 0;
+    }
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            if (c->tcp_sfd >= 0 && !c->is_recording) {
+                c->is_recording = true;
+                c->is_first_packet = true;
+                c->sync_count = 0;
+            }
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            if (c->udp_sfd >= 0 && !c->is_recording) {
+                c->is_recording = true;
+                c->is_first_packet = true;
+                c->sync_count = 0;
+            }
+            break;
+        default:
+            rv = 1;
+            break;
+    }
+
+    return rv;
+}
+
+int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume) {
+    char *param;
+    int rv = 0;
+    double db;
+
+    pa_assert(c);
+
+    if (!c->rtsp) {
+        pa_log_debug("Cannot SET_PARAMETER, connection not established yet...");
+        return 0;
+    } else if (!c->sci) {
+        pa_log_debug("SET_PARAMETER requires a preliminary authentication");
+        return 1;
+    }
+
+    db = pa_sw_volume_to_dB(volume);
+    if (db < VOLUME_MIN)
+        db = VOLUME_MIN;
+    else if (db > VOLUME_MAX)
+        db = VOLUME_MAX;
+
+    pa_log_debug("volume=%u db=%.6f", volume, db);
+
+    param = pa_sprintf_malloc("volume: %0.6f\r\n", db);
+    /* We just hit and hope, cannot wait for the callback. */
+    if (c->rtsp != NULL && pa_rtsp_exec_ready(c->rtsp))
+        rv = pa_rtsp_setparameter(c->rtsp, param);
+
+    pa_xfree(param);
+    return rv;
+}
+
+int pa_raop_client_flush(pa_raop_client *c) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (!c->rtsp || !pa_rtsp_exec_ready(c->rtsp)) {
+        pa_log_debug("Cannot FLUSH, connection not established yet...)");
+        return 0;
+    } else if (!c->sci) {
+        pa_log_debug("FLUSH requires a preliminary authentication");
+        return 1;
+    }
+
+    c->is_recording = false;
+
+    rv = pa_rtsp_flush(c->rtsp, c->seq, c->rtptime);
+    return rv;
+}
+
+int pa_raop_client_teardown(pa_raop_client *c) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (!c->rtsp) {
+        pa_log_debug("Cannot TEARDOWN, connection not established yet...");
+        return 0;
+    } else if (!c->sci) {
+        pa_log_debug("TEARDOWN requires a preliminary authentication");
+        return 1;
+    }
+
+    c->is_recording = false;
+
+    rv = pa_rtsp_teardown(c->rtsp);
+    return rv;
+}
+
+void pa_raop_client_get_frames_per_block(pa_raop_client *c, size_t *frames) {
+    pa_assert(c);
+    pa_assert(frames);
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            *frames = FRAMES_PER_TCP_PACKET;
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            *frames = FRAMES_PER_UDP_PACKET;
+            break;
+        default:
+            *frames = 0;
+            break;
+    }
+}
+
+bool pa_raop_client_register_pollfd(pa_raop_client *c, pa_rtpoll *poll, pa_rtpoll_item **poll_item) {
+    struct pollfd *pollfd = NULL;
+    pa_rtpoll_item *item = NULL;
+    bool oob = true;
+
+    pa_assert(c);
+    pa_assert(poll);
+    pa_assert(poll_item);
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            item = pa_rtpoll_item_new(poll, PA_RTPOLL_NEVER, 1);
+            pollfd = pa_rtpoll_item_get_pollfd(item, NULL);
+            pollfd->fd = c->tcp_sfd;
+            pollfd->events = POLLOUT;
+            pollfd->revents = 0;
+            *poll_item = item;
+            oob = false;
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            item = pa_rtpoll_item_new(poll, PA_RTPOLL_NEVER, 2);
+            pollfd = pa_rtpoll_item_get_pollfd(item, NULL);
+            pollfd->fd = c->udp_cfd;
+            pollfd->events = POLLIN | POLLPRI;
+            pollfd->revents = 0;
+            pollfd++;
+            pollfd->fd = c->udp_tfd;
+            pollfd->events = POLLIN | POLLPRI;
+            pollfd->revents = 0;
+            *poll_item = item;
+            oob = true;
+            break;
+        default:
+            *poll_item = NULL;
+            break;
+    }
+
+    return oob;
+}
+
+pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume) {
+    double minv, maxv;
+
+    pa_assert(c);
+
+    if (c->protocol != PA_RAOP_PROTOCOL_UDP)
+        return volume;
+
+    maxv = pa_sw_volume_from_dB(0.0);
+    minv = maxv * pow(10.0, VOLUME_DEF / 60.0);
+
+    /* Adjust volume so that it fits into VOLUME_DEF <= v <= 0 dB */
+    return volume - volume * (minv / maxv) + minv;
+}
+
+void pa_raop_client_handle_oob_packet(pa_raop_client *c, const int fd, const uint8_t packet[], ssize_t size) {
+    pa_assert(c);
+    pa_assert(fd >= 0);
+    pa_assert(packet);
+
+    if (c->protocol == PA_RAOP_PROTOCOL_UDP) {
+        if (fd == c->udp_cfd) {
+            pa_log_debug("Received UDP control packet...");
+            handle_udp_control_packet(c, packet, size);
+        } else if (fd == c->udp_tfd) {
+            pa_log_debug("Received UDP timing packet...");
+            handle_udp_timing_packet(c, packet, size);
+        }
+    }
+}
+
+ssize_t pa_raop_client_send_audio_packet(pa_raop_client *c, pa_memchunk *block, size_t offset) {
+    ssize_t written = 0;
+
+    pa_assert(c);
+    pa_assert(block);
+
+    /* Sync RTP & NTP timestamp if required (UDP). */
+    if (c->protocol == PA_RAOP_PROTOCOL_UDP) {
+        c->sync_count++;
+        if (c->is_first_packet || c->sync_count >= c->sync_interval) {
+            send_udp_sync_packet(c, c->rtptime);
+            c->sync_count = 0;
+        }
+    }
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            written = send_tcp_audio_packet(c, block, offset);
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            written = send_udp_audio_packet(c, block, offset);
+            break;
+        default:
+            written = -1;
+            break;
+    }
+
+    c->is_first_packet = false;
+    return written;
+}
+
+void pa_raop_client_set_state_callback(pa_raop_client *c, pa_raop_client_state_cb_t callback, void *userdata) {
+    pa_assert(c);
+
+    c->state_callback = callback;
+    c->state_userdata = userdata;
+}
diff --git a/src/modules/raop/raop-client.h b/src/modules/raop/raop-client.h
new file mode 100644 (file)
index 0000000..72e6018
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef fooraopclientfoo
+#define fooraopclientfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/volume.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/rtpoll.h>
+
+typedef enum pa_raop_protocol {
+    PA_RAOP_PROTOCOL_TCP,
+    PA_RAOP_PROTOCOL_UDP
+} pa_raop_protocol_t;
+
+typedef enum pa_raop_encryption {
+    PA_RAOP_ENCRYPTION_NONE,
+    PA_RAOP_ENCRYPTION_RSA,
+    PA_RAOP_ENCRYPTION_FAIRPLAY,
+    PA_RAOP_ENCRYPTION_MFISAP,
+    PA_RAOP_ENCRYPTION_FAIRPLAY_SAP25
+} pa_raop_encryption_t;
+
+typedef enum pa_raop_codec {
+    PA_RAOP_CODEC_PCM,
+    PA_RAOP_CODEC_ALAC,
+    PA_RAOP_CODEC_AAC,
+    PA_RAOP_CODEC_AAC_ELD
+} pa_raop_codec_t;
+
+typedef struct pa_raop_client pa_raop_client;
+
+typedef enum pa_raop_state {
+    PA_RAOP_INVALID_STATE,
+    PA_RAOP_AUTHENTICATED,
+    PA_RAOP_CONNECTED,
+    PA_RAOP_RECORDING,
+    PA_RAOP_DISCONNECTED
+} pa_raop_state_t;
+
+pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol,
+                                   pa_raop_encryption_t encryption, pa_raop_codec_t codec);
+void pa_raop_client_free(pa_raop_client *c);
+
+int pa_raop_client_authenticate(pa_raop_client *c, const char *password);
+bool pa_raop_client_is_authenticated(pa_raop_client *c);
+
+int pa_raop_client_announce(pa_raop_client *c);
+bool pa_raop_client_is_alive(pa_raop_client *c);
+bool pa_raop_client_can_stream(pa_raop_client *c);
+int pa_raop_client_stream(pa_raop_client *c);
+int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume);
+int pa_raop_client_flush(pa_raop_client *c);
+int pa_raop_client_teardown(pa_raop_client *c);
+
+void pa_raop_client_get_frames_per_block(pa_raop_client *c, size_t *size);
+bool pa_raop_client_register_pollfd(pa_raop_client *c, pa_rtpoll *poll, pa_rtpoll_item **poll_item);
+pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume);
+void pa_raop_client_handle_oob_packet(pa_raop_client *c, const int fd, const uint8_t packet[], ssize_t size);
+ssize_t pa_raop_client_send_audio_packet(pa_raop_client *c, pa_memchunk *block, size_t offset);
+
+typedef void (*pa_raop_client_state_cb_t)(pa_raop_state_t state, void *userdata);
+void pa_raop_client_set_state_callback(pa_raop_client *c, pa_raop_client_state_cb_t callback, void *userdata);
+
+#endif
diff --git a/src/modules/raop/raop-crypto.c b/src/modules/raop/raop-crypto.c
new file mode 100644 (file)
index 0000000..0dd6463
--- /dev/null
@@ -0,0 +1,164 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/aes.h>
+#include <openssl/rsa.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/random.h>
+
+#include "raop-crypto.h"
+#include "raop-util.h"
+
+#define AES_CHUNK_SIZE 16
+
+/* Openssl 1.1.0 broke compatibility. Before 1.1.0 we had to set RSA->n and
+ * RSA->e manually, but after 1.1.0 the RSA struct is opaque and we have to use
+ * RSA_set0_key(). RSA_set0_key() is a new function added in 1.1.0. We could
+ * depend on openssl 1.1.0, but it may take some time before distributions will
+ * be able to upgrade to the new openssl version. To insulate ourselves from
+ * such transition problems, let's implement RSA_set0_key() ourselves if it's
+ * not available. */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) {
+    r->n = n;
+    r->e = e;
+    return 1;
+}
+#endif
+
+struct pa_raop_secret {
+    uint8_t key[AES_CHUNK_SIZE]; /* Key for aes-cbc */
+    uint8_t iv[AES_CHUNK_SIZE];  /* Initialization vector for cbc */
+    AES_KEY aes;                 /* AES encryption */
+};
+
+static const char rsa_modulus[] =
+    "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC"
+    "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR"
+    "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB"
+    "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ"
+    "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh"
+    "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew==";
+
+static const char rsa_exponent[] =
+    "AQAB";
+
+static int rsa_encrypt(uint8_t *data, int len, uint8_t *str) {
+    uint8_t modules[256];
+    uint8_t exponent[8];
+    int size;
+    RSA *rsa;
+    BIGNUM *n_bn;
+    BIGNUM *e_bn;
+
+    pa_assert(data);
+    pa_assert(str);
+
+    rsa = RSA_new();
+    size = pa_raop_base64_decode(rsa_modulus, modules);
+    n_bn = BN_bin2bn(modules, size, NULL);
+    size = pa_raop_base64_decode(rsa_exponent, exponent);
+    e_bn = BN_bin2bn(exponent, size, NULL);
+    RSA_set0_key(rsa, n_bn, e_bn, NULL);
+
+    size = RSA_public_encrypt(len, data, str, rsa, RSA_PKCS1_OAEP_PADDING);
+
+    RSA_free(rsa);
+    return size;
+}
+
+pa_raop_secret* pa_raop_secret_new(void) {
+    pa_raop_secret *s = pa_xnew0(pa_raop_secret, 1);
+
+    pa_assert(s);
+
+    pa_random(s->key, sizeof(s->key));
+    AES_set_encrypt_key(s->key, 128, &s->aes);
+    pa_random(s->iv, sizeof(s->iv));
+
+    return s;
+}
+
+void pa_raop_secret_free(pa_raop_secret *s) {
+    pa_assert(s);
+
+    pa_xfree(s);
+}
+
+char* pa_raop_secret_get_iv(pa_raop_secret *s) {
+    char *base64_iv = NULL;
+
+    pa_assert(s);
+
+    pa_raop_base64_encode(s->iv, AES_CHUNK_SIZE, &base64_iv);
+
+    return base64_iv;
+}
+
+char* pa_raop_secret_get_key(pa_raop_secret *s) {
+    char *base64_key = NULL;
+    uint8_t rsa_key[512];
+    int size = 0;
+
+    pa_assert(s);
+
+    /* Encrypt our AES public key to send to the device */
+    size = rsa_encrypt(s->key, AES_CHUNK_SIZE, rsa_key);
+    pa_raop_base64_encode(rsa_key, size, &base64_key);
+
+    return base64_key;
+}
+
+int pa_raop_aes_encrypt(pa_raop_secret *s, uint8_t *data, int len) {
+    static uint8_t nv[AES_CHUNK_SIZE];
+    uint8_t *buffer;
+    int i = 0, j;
+
+    pa_assert(s);
+    pa_assert(data);
+
+    memcpy(nv, s->iv, AES_CHUNK_SIZE);
+
+    while (i + AES_CHUNK_SIZE <= len) {
+        buffer = data + i;
+        for (j = 0; j < AES_CHUNK_SIZE; ++j)
+            buffer[j] ^= nv[j];
+
+        AES_encrypt(buffer, buffer, &s->aes);
+
+        memcpy(nv, buffer, AES_CHUNK_SIZE);
+        i += AES_CHUNK_SIZE;
+    }
+
+    return i;
+}
diff --git a/src/modules/raop/raop-crypto.h b/src/modules/raop/raop-crypto.h
new file mode 100644 (file)
index 0000000..65f7577
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef fooraopcryptofoo
+#define fooraopcryptofoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+typedef struct pa_raop_secret pa_raop_secret;
+
+pa_raop_secret* pa_raop_secret_new(void);
+void pa_raop_secret_free(pa_raop_secret *s);
+
+char* pa_raop_secret_get_iv(pa_raop_secret *s);
+char* pa_raop_secret_get_key(pa_raop_secret *s);
+
+int pa_raop_aes_encrypt(pa_raop_secret *s, uint8_t *data, int len);
+
+#endif
diff --git a/src/modules/raop/raop-packet-buffer.c b/src/modules/raop/raop-packet-buffer.c
new file mode 100644 (file)
index 0000000..72fd729
--- /dev/null
@@ -0,0 +1,161 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Matthias Wabersich
+  Copyright 2013 Hajime Fujita
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/macro.h>
+
+#include "raop-packet-buffer.h"
+
+struct pa_raop_packet_buffer {
+    pa_memchunk *packets;
+    pa_mempool *mempool;
+
+    size_t size;
+    size_t count;
+
+    uint16_t seq;
+    size_t pos;
+};
+
+pa_raop_packet_buffer *pa_raop_packet_buffer_new(pa_mempool *mempool, const size_t size) {
+    pa_raop_packet_buffer *pb = pa_xnew0(pa_raop_packet_buffer, 1);
+
+    pa_assert(mempool);
+    pa_assert(size > 0);
+
+    pb->count = 0;
+    pb->size = size;
+    pb->mempool = mempool;
+    pb->packets = pa_xnew0(pa_memchunk, size);
+    pb->seq = pb->pos = 0;
+
+    return pb;
+}
+
+void pa_raop_packet_buffer_free(pa_raop_packet_buffer *pb) {
+    size_t i;
+
+    pa_assert(pb);
+
+    for (i = 0; pb->packets && i < pb->size; i++) {
+        if (pb->packets[i].memblock)
+            pa_memblock_unref(pb->packets[i].memblock);
+        pa_memchunk_reset(&pb->packets[i]);
+    }
+
+    pa_xfree(pb->packets);
+    pb->packets = NULL;
+    pa_xfree(pb);
+}
+
+void pa_raop_packet_buffer_reset(pa_raop_packet_buffer *pb, uint16_t seq) {
+    size_t i;
+
+    pa_assert(pb);
+    pa_assert(pb->packets);
+
+    pb->pos = 0;
+    pb->count = 0;
+    pb->seq = (!seq) ? UINT16_MAX : seq - 1;
+    for (i = 0; i < pb->size; i++) {
+        if (pb->packets[i].memblock)
+            pa_memblock_unref(pb->packets[i].memblock);
+        pa_memchunk_reset(&pb->packets[i]);
+    }
+}
+
+pa_memchunk *pa_raop_packet_buffer_prepare(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size) {
+    pa_memchunk *packet = NULL;
+    size_t i;
+
+    pa_assert(pb);
+    pa_assert(pb->packets);
+
+    if (seq == 0) {
+        /* 0 means seq reached UINT16_MAX and has been wrapped... */
+        pa_assert(pb->seq == UINT16_MAX);
+        pb->seq = 0;
+    } else {
+        /* ...otherwise, seq MUST have be increased! */
+        pa_assert(seq == pb->seq + 1);
+        pb->seq++;
+    }
+
+    i = (pb->pos + 1) % pb->size;
+
+    if (pb->packets[i].memblock)
+        pa_memblock_unref(pb->packets[i].memblock);
+    pa_memchunk_reset(&pb->packets[i]);
+
+    pb->packets[i].memblock = pa_memblock_new(pb->mempool, size);
+    pb->packets[i].length = size;
+    pb->packets[i].index = 0;
+
+    packet = &pb->packets[i];
+
+    if (pb->count < pb->size)
+        pb->count++;
+    pb->pos = i;
+
+    return packet;
+}
+
+pa_memchunk *pa_raop_packet_buffer_retrieve(pa_raop_packet_buffer *pb, uint16_t seq) {
+    pa_memchunk *packet = NULL;
+    size_t delta, i;
+
+    pa_assert(pb);
+    pa_assert(pb->packets);
+
+    if (seq == pb->seq)
+        packet = &pb->packets[pb->pos];
+    else {
+        if (seq < pb->seq) {
+            /* Regular case: pb->seq did not wrapped since seq. */
+            delta = pb->seq - seq;
+        } else {
+            /* Tricky case: pb->seq wrapped since seq! */
+            delta = pb->seq + (UINT16_MAX - seq);
+        }
+
+        /* If the requested packet is too old, do nothing and return */
+        if (delta > pb->count)
+            return NULL;
+
+        i = (pb->size + pb->pos - delta) % pb->size;
+
+        if (delta < pb->size && pb->packets[i].memblock)
+            packet = &pb->packets[i];
+    }
+
+    return packet;
+}
diff --git a/src/modules/raop/raop-packet-buffer.h b/src/modules/raop/raop-packet-buffer.h
new file mode 100644 (file)
index 0000000..c410298
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef fooraoppacketbufferfoo
+#define fooraoppacketbufferfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Matthias Wabersich
+  Copyright 2013 Hajime Fujita
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_raop_packet_buffer pa_raop_packet_buffer;
+
+/* Allocates a new circular packet buffer, size: Maximum number of packets to store */
+pa_raop_packet_buffer *pa_raop_packet_buffer_new(pa_mempool *mempool, const size_t size);
+void pa_raop_packet_buffer_free(pa_raop_packet_buffer *pb);
+
+void pa_raop_packet_buffer_reset(pa_raop_packet_buffer *pb, uint16_t seq);
+
+pa_memchunk *pa_raop_packet_buffer_prepare(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size);
+pa_memchunk *pa_raop_packet_buffer_retrieve(pa_raop_packet_buffer *pb, uint16_t seq);
+
+#endif
diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c
new file mode 100644 (file)
index 0000000..e5d219e
--- /dev/null
@@ -0,0 +1,675 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2008 Colin Guthrie
+  Copyright 2013 Hajime Fujita
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_LINUX_SOCKIOS_H
+#include <linux/sockios.h>
+#endif
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/module.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/poll.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+
+#include "raop-sink.h"
+#include "raop-client.h"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+    bool oob;
+
+    pa_raop_client *raop;
+    pa_raop_protocol_t protocol;
+    pa_raop_encryption_t encryption;
+    pa_raop_codec_t codec;
+
+    size_t block_size;
+    pa_memchunk memchunk;
+
+    pa_usec_t delay;
+    pa_usec_t start;
+    pa_smoother *smoother;
+    uint64_t write_count;
+};
+
+enum {
+    PA_SINK_MESSAGE_SET_RAOP_STATE = PA_SINK_MESSAGE_MAX
+};
+
+static void userdata_free(struct userdata *u);
+
+static void sink_set_volume_cb(pa_sink *s);
+
+static void raop_state_cb(pa_raop_state_t state, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("State change recieved, informing IO thread...");
+
+    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_SET_RAOP_STATE, PA_INT_TO_PTR(state), 0, NULL, NULL);
+}
+
+static int64_t sink_get_latency(const struct userdata *u) {
+    pa_usec_t now;
+    int64_t latency;
+
+    pa_assert(u);
+    pa_assert(u->smoother);
+
+    now = pa_rtclock_now();
+    now = pa_smoother_get(u->smoother, now);
+
+    latency = pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) - (int64_t) now;
+
+    return latency;
+}
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    pa_assert(u);
+    pa_assert(u->raop);
+
+    switch (code) {
+        case PA_SINK_MESSAGE_SET_STATE: {
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+                case PA_SINK_SUSPENDED: {
+                    pa_log_debug("RAOP: SUSPENDED");
+
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+
+                    pa_smoother_pause(u->smoother, pa_rtclock_now());
+                    /* Issue a TEARDOWN if we are still connected */
+                    if (pa_raop_client_is_alive(u->raop)) {
+                        pa_raop_client_teardown(u->raop);
+                    }
+
+                    break;
+                }
+
+                case PA_SINK_IDLE: {
+                    pa_log_debug("RAOP: IDLE");
+
+                    /* Issue a FLUSH if we're comming from running state */
+                    if (u->sink->thread_info.state == PA_SINK_RUNNING) {
+                        pa_rtpoll_set_timer_disabled(u->rtpoll);
+                        pa_raop_client_flush(u->raop);
+                    }
+
+                    break;
+                }
+
+                case PA_SINK_RUNNING: {
+                    pa_usec_t now;
+
+                    pa_log_debug("RAOP: RUNNING");
+
+                    now = pa_rtclock_now();
+                    pa_smoother_resume(u->smoother, now, true);
+
+                    if (!pa_raop_client_is_alive(u->raop)) {
+                        /* Connecting will trigger a RECORD and start steaming */
+                        pa_raop_client_announce(u->raop);
+                    } else if (!pa_raop_client_can_stream(u->raop)) {
+                        /* RECORD alredy sent, simply start streaming */
+                        pa_raop_client_stream(u->raop);
+                        pa_rtpoll_set_timer_absolute(u->rtpoll, now);
+                        u->write_count = 0;
+                        u->start = now;
+                    }
+
+                    break;
+                }
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                case PA_SINK_INVALID_STATE:
+                    break;
+            }
+
+            break;
+        }
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            int64_t r = 0;
+
+            if (pa_raop_client_can_stream(u->raop))
+                r = sink_get_latency(u);
+
+            *((int64_t*) data) = r;
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_RAOP_STATE: {
+            switch ((pa_raop_state_t) PA_PTR_TO_UINT(data)) {
+                case PA_RAOP_AUTHENTICATED: {
+                    if (!pa_raop_client_is_authenticated(u->raop)) {
+                        pa_module_unload_request(u->module, true);
+                    }
+
+                    return 0;
+                }
+
+                case PA_RAOP_CONNECTED: {
+                    pa_assert(!u->rtpoll_item);
+
+                    u->oob = pa_raop_client_register_pollfd(u->raop, u->rtpoll, &u->rtpoll_item);
+
+                    return 0;
+                }
+
+                case PA_RAOP_RECORDING: {
+                    pa_usec_t now;
+
+                    now = pa_rtclock_now();
+                    pa_rtpoll_set_timer_absolute(u->rtpoll, now);
+                    u->write_count = 0;
+                    u->start = now;
+
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+                        /* Our stream has been suspended so we just flush it... */
+                        pa_rtpoll_set_timer_disabled(u->rtpoll);
+                        pa_raop_client_flush(u->raop);
+                    } else {
+                        /* Set the initial volume */
+                        sink_set_volume_cb(u->sink);
+                    }
+
+                    return 0;
+                }
+
+                case PA_RAOP_INVALID_STATE:
+                case PA_RAOP_DISCONNECTED: {
+                    unsigned int nbfds = 0;
+                    struct pollfd *pollfd;
+                    unsigned int i;
+
+                    if (u->rtpoll_item) {
+                        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, &nbfds);
+                        if (pollfd) {
+                            for (i = 0; i < nbfds; i++) {
+                                if (pollfd->fd >= 0)
+                                   pa_close(pollfd->fd);
+                                pollfd++;
+                            }
+                        }
+                        pa_rtpoll_item_free(u->rtpoll_item);
+                        u->rtpoll_item = NULL;
+                    }
+
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
+                        pa_rtpoll_set_timer_disabled(u->rtpoll);
+                    else if (u->sink->thread_info.state != PA_SINK_IDLE)
+                        pa_module_unload_request(u->module, true);
+
+                    return 0;
+                }
+            }
+
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void sink_set_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    pa_cvolume hw;
+    pa_volume_t v, v_orig;
+    char t[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(u);
+
+    /* If we're muted we don't need to do anything. */
+    if (s->muted)
+        return;
+
+    /* Calculate the max volume of all channels.
+     * We'll use this as our (single) volume on the APEX device and emulate
+     * any variation in channel volumes in software. */
+    v = pa_cvolume_max(&s->real_volume);
+
+    v_orig = v;
+    v = pa_raop_client_adjust_volume(u->raop, v_orig);
+
+    pa_log_debug("Volume adjusted: orig=%u adjusted=%u", v_orig, v);
+
+    /* Create a pa_cvolume version of our single value. */
+    pa_cvolume_set(&hw, s->sample_spec.channels, v);
+
+    /* Perform any software manipulation of the volume needed. */
+    pa_sw_cvolume_divide(&s->soft_volume, &s->real_volume, &hw);
+
+    pa_log_debug("Requested volume: %s", pa_cvolume_snprint_verbose(t, sizeof(t), &s->real_volume, &s->channel_map, false));
+    pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint_verbose(t, sizeof(t), &hw, &s->channel_map, false));
+    pa_log_debug("Calculated software volume: %s",
+                 pa_cvolume_snprint_verbose(t, sizeof(t), &s->soft_volume, &s->channel_map, true));
+
+    /* Any necessary software volume manipulation is done so set
+     * our hw volume (or v as a single value) on the device. */
+    pa_raop_client_set_volume(u->raop, v);
+}
+
+static void sink_set_mute_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(u->raop);
+
+    if (s->muted) {
+        pa_raop_client_set_volume(u->raop, PA_VOLUME_MUTED);
+    } else {
+        sink_set_volume_cb(s);
+    }
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    size_t offset = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
+
+    for (;;) {
+        struct pollfd *pollfd = NULL;
+        unsigned int i, nbfds = 0;
+        pa_usec_t now, estimated, intvl;
+        uint64_t position;
+        size_t index;
+        int ret;
+
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+            if (u->sink->thread_info.rewind_requested)
+                pa_sink_process_rewind(u->sink, 0);
+        }
+
+        /* Polling (audio data + control socket + timing socket). */
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+        else if (ret == 0)
+            goto finish;
+
+        if (u->rtpoll_item) {
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, &nbfds);
+            /* If !oob: streaming driven by pollds (POLLOUT) */
+            if (pollfd && !u->oob && !pollfd->revents) {
+                for (i = 0; i < nbfds; i++) {
+                    pollfd->events = POLLOUT;
+                    pollfd->revents = 0;
+
+                    pollfd++;
+                }
+
+                continue;
+            }
+
+            /* if oob: streaming managed by timing, pollfd for oob sockets */
+            if (pollfd && u->oob && !pa_rtpoll_timer_elapsed(u->rtpoll)) {
+                uint8_t packet[32];
+                ssize_t read;
+
+                for (i = 0; i < nbfds; i++) {
+                    if (pollfd->revents & pollfd->events) {
+                        pollfd->revents = 0;
+                        read = pa_read(pollfd->fd, packet, sizeof(packet), NULL);
+                        pa_raop_client_handle_oob_packet(u->raop, pollfd->fd, packet, read);
+                    }
+
+                    pollfd++;
+                }
+
+                continue;
+            }
+        }
+
+        if (u->sink->thread_info.state != PA_SINK_RUNNING)
+            continue;
+        if (!pa_raop_client_can_stream(u->raop))
+            continue;
+
+        if (u->memchunk.length <= 0) {
+            if (u->memchunk.memblock)
+                pa_memblock_unref(u->memchunk.memblock);
+            pa_memchunk_reset(&u->memchunk);
+
+            /* Grab unencoded audio data from PulseAudio */
+            pa_sink_render_full(u->sink, u->block_size, &u->memchunk);
+            offset = u->memchunk.index;
+        }
+
+        pa_assert(u->memchunk.length > 0);
+
+        index = u->memchunk.index;
+        if (pa_raop_client_send_audio_packet(u->raop, &u->memchunk, offset) < 0) {
+            if (errno == EINTR) {
+                /* Just try again. */
+                pa_log_debug("Failed to write data to FIFO (EINTR), retrying");
+                goto fail;
+            } else if (errno != EAGAIN) {
+                /* Buffer is full, wait for POLLOUT. */
+                pollfd->events = POLLOUT;
+                pollfd->revents = 0;
+            } else {
+                pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+                goto fail;
+            }
+        } else {
+            u->write_count += (uint64_t) u->memchunk.index - (uint64_t) index;
+            position = u->write_count - pa_usec_to_bytes(u->delay, &u->sink->sample_spec);
+
+            now = pa_rtclock_now();
+            estimated = pa_bytes_to_usec(position, &u->sink->sample_spec);
+            pa_smoother_put(u->smoother, now, estimated);
+
+            if (u->oob && !pollfd->revents) {
+                /* Sleep until next packet transmission */
+                intvl = u->start + pa_bytes_to_usec(u->write_count, &u->sink->sample_spec);
+                pa_rtpoll_set_timer_absolute(u->rtpoll, intvl);
+            } else if (!u->oob) {
+                if (u->memchunk.length > 0) {
+                    pollfd->events = POLLOUT;
+                    pollfd->revents = 0;
+                } else {
+                    intvl = u->start + pa_bytes_to_usec(u->write_count, &u->sink->sample_spec);
+                    pa_rtpoll_set_timer_absolute(u->rtpoll, intvl);
+                    pollfd->revents = 0;
+                    pollfd->events = 0;
+                }
+            }
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, const char *driver) {
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    char *thread_name = NULL;
+    const char *server, *protocol, *encryption, *codec;
+    const char /* *username, */ *password;
+    pa_sink_new_data data;
+    const char *name = NULL;
+
+    pa_assert(m);
+    pa_assert(ma);
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+        pa_log("Failed to parse sample specification");
+        goto fail;
+    }
+
+    if (!(server = pa_modargs_get_value(ma, "server", NULL))) {
+        pa_log("Failed to parse server argument");
+        goto fail;
+    }
+
+    if (!(protocol = pa_modargs_get_value(ma, "protocol", NULL))) {
+        pa_log("Failed to parse protocol argument");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->thread = NULL;
+    u->rtpoll = pa_rtpoll_new();
+    u->rtpoll_item = NULL;
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->oob = true;
+
+    u->block_size = 0;
+    pa_memchunk_reset(&u->memchunk);
+
+    u->delay = 0;
+    u->smoother = pa_smoother_new(
+            PA_USEC_PER_SEC,
+            PA_USEC_PER_SEC*2,
+            true,
+            true,
+            10,
+            0,
+            false);
+    u->write_count = 0;
+
+    if (pa_streq(protocol, "TCP")) {
+        u->protocol = PA_RAOP_PROTOCOL_TCP;
+    } else if (pa_streq(protocol, "UDP")) {
+        u->protocol = PA_RAOP_PROTOCOL_UDP;
+    } else {
+        pa_log("Unsupported transport protocol argument: %s", protocol);
+        goto fail;
+    }
+
+    encryption = pa_modargs_get_value(ma, "encryption", NULL);
+    codec = pa_modargs_get_value(ma, "codec", NULL);
+
+    if (!encryption) {
+        u->encryption = PA_RAOP_ENCRYPTION_NONE;
+    } else if (pa_streq(encryption, "none")) {
+        u->encryption = PA_RAOP_ENCRYPTION_NONE;
+    } else if (pa_streq(encryption, "RSA")) {
+        u->encryption = PA_RAOP_ENCRYPTION_RSA;
+    } else {
+        pa_log("Unsupported encryption type argument: %s", encryption);
+        goto fail;
+    }
+
+    if (!codec) {
+        u->codec = PA_RAOP_CODEC_PCM;
+    } else if (pa_streq(codec, "PCM")) {
+        u->codec = PA_RAOP_CODEC_PCM;
+    } else if (pa_streq(codec, "ALAC")) {
+        u->codec = PA_RAOP_CODEC_ALAC;
+    } else {
+        pa_log("Unsupported audio codec argument: %s", codec);
+        goto fail;
+    }
+
+    pa_sink_new_data_init(&data);
+    data.driver = driver;
+    data.module = m;
+
+    if ((name = pa_modargs_get_value(ma, "sink_name", NULL))) {
+        pa_sink_new_data_set_name(&data, name);
+    } else {
+        char *nick;
+
+        if ((name = pa_modargs_get_value(ma, "name", NULL)))
+            nick = pa_sprintf_malloc("raop_client.%s", name);
+        else
+            nick = pa_sprintf_malloc("raop_client.%s", server);
+        pa_sink_new_data_set_name(&data, nick);
+        pa_xfree(nick);
+    }
+
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "music");
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "RAOP sink '%s'", server);
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&data);
+        goto fail;
+    }
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY | PA_SINK_NETWORK);
+    pa_sink_new_data_done(&data);
+
+    if (!(u->sink)) {
+        pa_log("Failed to create sink object");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
+    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    u->raop = pa_raop_client_new(u->core, server, u->protocol, u->encryption, u->codec);
+
+    if (!(u->raop)) {
+        pa_log("Failed to create RAOP client object");
+        goto fail;
+    }
+
+    /* The number of frames per blocks is not negotiable... */
+    pa_raop_client_get_frames_per_block(u->raop, &u->block_size);
+    u->block_size *= pa_frame_size(&ss);
+    pa_sink_set_max_request(u->sink, u->block_size);
+
+    pa_raop_client_set_state_callback(u->raop, raop_state_cb, u);
+
+    thread_name = pa_sprintf_malloc("raop-sink-%s", server);
+    if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) {
+        pa_log("Failed to create sink thread");
+        goto fail;
+    }
+    pa_xfree(thread_name);
+    thread_name = NULL;
+
+    pa_sink_put(u->sink);
+
+    /* username = pa_modargs_get_value(ma, "username", NULL); */
+    password = pa_modargs_get_value(ma, "password", NULL);
+    pa_raop_client_authenticate(u->raop, password );
+
+    return u->sink;
+
+fail:
+    pa_xfree(thread_name);
+
+    if (u)
+        userdata_free(u);
+
+    return NULL;
+}
+
+static void userdata_free(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+    u->sink = NULL;
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+    u->rtpoll_item = NULL;
+    u->rtpoll = NULL;
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->raop)
+        pa_raop_client_free(u->raop);
+    u->raop = NULL;
+
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+    u->smoother = NULL;
+
+    pa_xfree(u);
+}
+
+void pa_raop_sink_free(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    userdata_free(u);
+}
diff --git a/src/modules/raop/raop-sink.h b/src/modules/raop/raop-sink.h
new file mode 100644 (file)
index 0000000..dfa2f0c
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef fooraopsinkfoo
+#define fooraopsinkfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/sink.h>
+
+pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, const char *driver);
+
+void pa_raop_sink_free(pa_sink *s);
+
+#endif
diff --git a/src/modules/raop/raop-util.c b/src/modules/raop/raop-util.c
new file mode 100644 (file)
index 0000000..febc204
--- /dev/null
@@ -0,0 +1,211 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+  Copyright Kungliga Tekniska högskolan
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/***
+  The base64 implementation was originally inspired by a file developed
+  by Kungliga Tekniska högskolan.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/md5.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "raop-util.h"
+
+#ifndef MD5_DIGEST_LENGTH
+#define MD5_DIGEST_LENGTH 16
+#endif
+
+#define MD5_HASH_LENGTH (2*MD5_DIGEST_LENGTH)
+
+#define BASE64_DECODE_ERROR 0xffffffff
+
+static const char base64_chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int char_position(char c) {
+    if (c >= 'A' && c <= 'Z')
+        return c - 'A' + 0;
+    if (c >= 'a' && c <= 'z')
+        return c - 'a' + 26;
+    if (c >= '0' && c <= '9')
+        return c - '0' + 52;
+    if (c == '+')
+        return 62;
+    if (c == '/')
+        return 63;
+
+    return -1;
+}
+
+static unsigned int token_decode(const char *token) {
+    unsigned int val = 0;
+    int marker = 0;
+    int i;
+
+    if (strlen(token) < 4)
+        return BASE64_DECODE_ERROR;
+    for (i = 0; i < 4; i++) {
+        val *= 64;
+        if (token[i] == '=')
+            marker++;
+        else if (marker > 0)
+            return BASE64_DECODE_ERROR;
+        else {
+            int lpos = char_position(token[i]);
+            if (lpos < 0)
+                return BASE64_DECODE_ERROR;
+            val += lpos;
+        }
+    }
+
+    if (marker > 2)
+        return BASE64_DECODE_ERROR;
+
+    return (marker << 24) | val;
+}
+
+int pa_raop_base64_encode(const void *data, int len, char **str) {
+    const unsigned char *q;
+    char *p, *s = NULL;
+    int i, c;
+
+    pa_assert(data);
+    pa_assert(str);
+
+    p = s = pa_xnew(char, len * 4 / 3 + 4);
+    q = (const unsigned char *) data;
+    for (i = 0; i < len;) {
+        c = q[i++];
+        c *= 256;
+        if (i < len)
+            c += q[i];
+        i++;
+        c *= 256;
+        if (i < len)
+            c += q[i];
+        i++;
+        p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+        p[1] = base64_chars[(c & 0x0003f000) >> 12];
+        p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+        p[3] = base64_chars[(c & 0x0000003f) >> 0];
+        if (i > len)
+            p[3] = '=';
+        if (i > len + 1)
+            p[2] = '=';
+        p += 4;
+    }
+
+    *p = 0;
+    *str = s;
+    return strlen(s);
+}
+
+int pa_raop_base64_decode(const char *str, void *data) {
+    const char *p;
+    unsigned char *q;
+
+    pa_assert(str);
+    pa_assert(data);
+
+    q = data;
+    for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
+        unsigned int val = token_decode(p);
+        unsigned int marker = (val >> 24) & 0xff;
+        if (val == BASE64_DECODE_ERROR)
+            return -1;
+        *q++ = (val >> 16) & 0xff;
+        if (marker < 2)
+            *q++ = (val >> 8) & 0xff;
+        if (marker < 1)
+            *q++ = val & 0xff;
+    }
+
+    return q - (unsigned char *) data;
+}
+
+int pa_raop_md5_hash(const char *data, int len, char **str) {
+    unsigned char d[MD5_DIGEST_LENGTH];
+    char *s = NULL;
+    int i;
+
+    pa_assert(data);
+    pa_assert(str);
+
+    MD5((unsigned char*) data, len, d);
+    s = pa_xnew(char, MD5_HASH_LENGTH);
+    for (i = 0; i < MD5_DIGEST_LENGTH; i++)
+        sprintf(&s[2*i], "%02x", (unsigned int) d[i]);
+
+    *str = s;
+    s[MD5_HASH_LENGTH] = 0;
+    return strlen(s);
+}
+
+int pa_raop_basic_response(const char *user, const char *pwd, char **str) {
+    char *tmp, *B = NULL;
+
+    pa_assert(str);
+
+    tmp = pa_sprintf_malloc("%s:%s", user, pwd);
+    pa_raop_base64_encode(tmp, strlen(tmp), &B);
+    pa_xfree(tmp);
+
+    *str = B;
+    return strlen(B);
+}
+
+int pa_raop_digest_response(const char *user, const char *realm, const char *password,
+                            const char *nonce, const char *uri, char **str) {
+    char *A1, *HA1, *A2, *HA2;
+    char *tmp, *KD = NULL;
+
+    pa_assert(str);
+
+    A1 = pa_sprintf_malloc("%s:%s:%s", user, realm, password);
+    pa_raop_md5_hash(A1, strlen(A1), &HA1);
+    pa_xfree(A1);
+
+    A2 = pa_sprintf_malloc("OPTIONS:%s", uri);
+    pa_raop_md5_hash(A2, strlen(A2), &HA2);
+    pa_xfree(A2);
+
+    tmp = pa_sprintf_malloc("%s:%s:%s", HA1, nonce, HA2);
+    pa_raop_md5_hash(tmp, strlen(tmp), &KD);
+    pa_xfree(tmp);
+
+    pa_xfree(HA1);
+    pa_xfree(HA2);
+
+    *str = KD;
+    return strlen(KD);
+}
diff --git a/src/modules/raop/raop-util.h b/src/modules/raop/raop-util.h
new file mode 100644 (file)
index 0000000..d3f7566
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef fooraoputilfoo
+#define fooraoputilfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+  Copyright Kungliga Tekniska högskolan
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/***
+  This file was originally inspired by a file developed by
+  Kungliga Tekniska högskolan.
+***/
+
+int pa_raop_base64_encode(const void *data, int len, char **str);
+int pa_raop_base64_decode(const char *str, void *data);
+
+int pa_raop_md5_hash(const char *data, int len, char **str);
+
+int pa_raop_basic_response(const char *user, const char *pwd, char **str);
+int pa_raop_digest_response(const char *user, const char *realm, const char *password,
+                            const char *nonce, const char *uri, char **str);
+
+#endif
diff --git a/src/modules/reserve-monitor.c b/src/modules/reserve-monitor.c
new file mode 100644 (file)
index 0000000..70de870
--- /dev/null
@@ -0,0 +1,267 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
+
+/***
+  Copyright 2009 Lennart Poettering
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "reserve-monitor.h"
+#include "reserve.h"
+
+struct rm_monitor {
+       int ref;
+
+       char *device_name;
+       char *service_name;
+       char *match;
+
+       DBusConnection *connection;
+
+       unsigned busy:1;
+       unsigned filtering:1;
+       unsigned matching:1;
+
+       rm_change_cb_t change_cb;
+       void *userdata;
+};
+
+#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
+
+#define SERVICE_FILTER                         \
+       "type='signal',"                        \
+       "sender='" DBUS_SERVICE_DBUS "',"       \
+       "interface='" DBUS_INTERFACE_DBUS "',"  \
+       "member='NameOwnerChanged',"            \
+       "arg0='%s'"
+
+static unsigned get_busy(
+       DBusConnection *c,
+       const char *name_owner) {
+
+       const char *un;
+
+       if (!name_owner || !*name_owner)
+               return FALSE;
+
+       /* If we ourselves own the device, then don't consider this 'busy' */
+       if ((un = dbus_bus_get_unique_name(c)))
+               if (strcmp(name_owner, un) == 0)
+                       return FALSE;
+
+       return TRUE;
+}
+
+static DBusHandlerResult filter_handler(
+       DBusConnection *c,
+       DBusMessage *s,
+       void *userdata) {
+
+       rm_monitor *m;
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       m = userdata;
+       assert(m->ref >= 1);
+
+       if (dbus_message_is_signal(s, "org.freedesktop.DBus", "NameOwnerChanged")) {
+               const char *name, *old, *new;
+
+               if (!dbus_message_get_args(
+                           s,
+                           &error,
+                           DBUS_TYPE_STRING, &name,
+                           DBUS_TYPE_STRING, &old,
+                           DBUS_TYPE_STRING, &new,
+                           DBUS_TYPE_INVALID))
+                       goto invalid;
+
+               if (strcmp(name, m->service_name) == 0) {
+                       unsigned old_busy = m->busy;
+
+                       m->busy = get_busy(c, new);
+
+                       if (m->busy != old_busy && m->change_cb) {
+                               m->ref++;
+                               m->change_cb(m);
+                               rm_release(m);
+                       }
+               }
+       }
+
+invalid:
+       dbus_error_free(&error);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int rm_watch(
+       rm_monitor **_m,
+       DBusConnection *connection,
+       const char *device_name,
+       rm_change_cb_t change_cb,
+       DBusError *error)  {
+
+       rm_monitor *m = NULL;
+       char *name_owner;
+       int r;
+       DBusError _error;
+
+       if (!error)
+               error = &_error;
+
+       dbus_error_init(error);
+
+       if (!_m)
+               return -EINVAL;
+
+       if (!connection)
+               return -EINVAL;
+
+       if (!device_name)
+               return -EINVAL;
+
+       if (!(m = calloc(sizeof(rm_monitor), 1)))
+               return -ENOMEM;
+
+       m->ref = 1;
+
+       if (!(m->device_name = strdup(device_name))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       m->connection = dbus_connection_ref(connection);
+       m->change_cb = change_cb;
+
+       if (!(m->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+       sprintf(m->service_name, SERVICE_PREFIX "%s", m->device_name);
+
+       if (!(dbus_connection_add_filter(m->connection, filter_handler, m, NULL))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       m->filtering = 1;
+
+       if (!(m->match = malloc(sizeof(SERVICE_FILTER) - 2 + strlen(m->service_name)))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       sprintf(m->match, SERVICE_FILTER, m->service_name);
+       dbus_bus_add_match(m->connection, m->match, error);
+
+       if (dbus_error_is_set(error)) {
+               r = -EIO;
+               goto fail;
+       }
+
+       m->matching = 1;
+
+       if ((r = rd_dbus_get_name_owner(m->connection, m->service_name, &name_owner, error)) < 0)
+               goto fail;
+
+       m->busy = get_busy(m->connection, name_owner);
+       free(name_owner);
+
+       *_m = m;
+       return 0;
+
+fail:
+       if (&_error == error)
+               dbus_error_free(&_error);
+
+       if (m)
+               rm_release(m);
+
+       return r;
+}
+
+void rm_release(rm_monitor *m) {
+       if (!m)
+               return;
+
+       assert(m->ref > 0);
+
+       if (--m->ref > 0)
+               return;
+
+       if (m->matching)
+               dbus_bus_remove_match(
+                       m->connection,
+                       m->match,
+                       NULL);
+
+       if (m->filtering)
+               dbus_connection_remove_filter(
+                       m->connection,
+                       filter_handler,
+                       m);
+
+       free(m->device_name);
+       free(m->service_name);
+       free(m->match);
+
+       if (m->connection)
+               dbus_connection_unref(m->connection);
+
+       free(m);
+}
+
+int rm_busy(rm_monitor *m) {
+       if (!m)
+               return -EINVAL;
+
+       assert(m->ref > 0);
+
+       return m->busy;
+}
+
+void rm_set_userdata(rm_monitor *m, void *userdata) {
+
+       if (!m)
+               return;
+
+       assert(m->ref > 0);
+       m->userdata = userdata;
+}
+
+void* rm_get_userdata(rm_monitor *m) {
+
+       if (!m)
+               return NULL;
+
+       assert(m->ref > 0);
+
+       return m->userdata;
+}
diff --git a/src/modules/reserve-monitor.h b/src/modules/reserve-monitor.h
new file mode 100644 (file)
index 0000000..85a7ebb
--- /dev/null
@@ -0,0 +1,71 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
+
+#ifndef fooreservemonitorhfoo
+#define fooreservemonitorhfoo
+
+/***
+  Copyright 2009 Lennart Poettering
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#include <dbus/dbus.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct rm_monitor rm_monitor;
+
+/* Prototype for a function that is called whenever the reservation
+ * device of a device changes. Use rm_monitor_busy() to find out the
+ * new state.*/
+typedef void (*rm_change_cb_t)(rm_monitor *m);
+
+/* Creates a monitor for watching the lock status of a device. Returns
+ * 0 on success, a negative errno style return value on error.  The
+ * DBus error might be set as well if the error was caused D-Bus. */
+int rm_watch(
+       rm_monitor **m,              /* On success a pointer to the newly allocated rm_device object will be filled in here */
+       DBusConnection *connection,  /* Session bus (when D-Bus learns about user busses we should switch to user busses) */
+       const char *device_name,     /* The device to monitor, e.g. "Audio0" */
+       rm_change_cb_t change_cb,    /* Will be called whenever the lock status changes. May be NULL */
+       DBusError *error);           /* If we fail due to a D-Bus related issue the error will be filled in here. May be NULL. */
+
+/* Free a rm_monitor object */
+void rm_release(rm_monitor *m);
+
+/* Checks whether the device is currently reserved, and returns 1
+ * then, 0 if not, negative errno style error code value on error. */
+int rm_busy(rm_monitor *m);
+
+/* Attach a userdata pointer to an rm_monitor */
+void rm_set_userdata(rm_monitor *m, void *userdata);
+
+/* Query the userdata pointer from an rm_monitor. Returns NULL if no
+ * userdata was set. */
+void* rm_get_userdata(rm_monitor *m);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/modules/reserve-wrap.c b/src/modules/reserve-wrap.c
new file mode 100644 (file)
index 0000000..202311a
--- /dev/null
@@ -0,0 +1,342 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/shared.h>
+
+#ifdef HAVE_DBUS
+#include <pulsecore/dbus-shared.h>
+#include "reserve.h"
+#include "reserve-monitor.h"
+#endif
+
+#include "reserve-wrap.h"
+
+struct pa_reserve_wrapper {
+    PA_REFCNT_DECLARE;
+    pa_core *core;
+    pa_hook hook;
+    char *shared_name;
+#ifdef HAVE_DBUS
+    pa_dbus_connection *connection;
+    struct rd_device *device;
+#endif
+};
+
+struct pa_reserve_monitor_wrapper {
+    PA_REFCNT_DECLARE;
+    pa_core *core;
+    pa_hook hook;
+    char *shared_name;
+#ifdef HAVE_DBUS
+    pa_dbus_connection *connection;
+    struct rm_monitor *monitor;
+#endif
+};
+
+static void reserve_wrapper_free(pa_reserve_wrapper *r) {
+    pa_assert(r);
+
+#ifdef HAVE_DBUS
+    if (r->device)
+        rd_release(r->device);
+
+    if (r->connection)
+        pa_dbus_connection_unref(r->connection);
+#endif
+
+    pa_hook_done(&r->hook);
+
+    if (r->shared_name) {
+        pa_assert_se(pa_shared_remove(r->core, r->shared_name) >= 0);
+        pa_xfree(r->shared_name);
+    }
+
+    pa_xfree(r);
+}
+
+#ifdef HAVE_DBUS
+static int request_cb(rd_device *d, int forced) {
+    pa_reserve_wrapper *r;
+    int k;
+
+    pa_assert(d);
+    pa_assert_se(r = rd_get_userdata(d));
+    pa_assert(PA_REFCNT_VALUE(r) >= 1);
+
+    PA_REFCNT_INC(r);
+
+    k = pa_hook_fire(&r->hook, PA_INT_TO_PTR(forced));
+    pa_log_debug("Device unlock of %s has been requested and %s.", r->shared_name, k < 0 ? "failed" : "succeeded");
+
+    pa_reserve_wrapper_unref(r);
+
+    return k < 0 ? -1 : 1;
+}
+#endif
+
+pa_reserve_wrapper* pa_reserve_wrapper_get(pa_core *c, const char *device_name) {
+    pa_reserve_wrapper *r;
+    char *t;
+#ifdef HAVE_DBUS
+    int k;
+    DBusError error;
+
+    dbus_error_init(&error);
+#endif
+
+    pa_assert(c);
+    pa_assert(device_name);
+
+    t = pa_sprintf_malloc("reserve-wrapper@%s", device_name);
+
+    if ((r = pa_shared_get(c, t))) {
+        pa_xfree(t);
+
+        pa_assert(PA_REFCNT_VALUE(r) >= 1);
+        PA_REFCNT_INC(r);
+
+        return r;
+    }
+
+    r = pa_xnew0(pa_reserve_wrapper, 1);
+    PA_REFCNT_INIT(r);
+    r->core = c;
+    pa_hook_init(&r->hook, r);
+    r->shared_name = t;
+
+    pa_assert_se(pa_shared_set(c, r->shared_name, r) >= 0);
+
+#ifdef HAVE_DBUS
+    if (!(r->connection = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
+        pa_log_debug("Unable to contact D-Bus session bus: %s: %s", error.name, error.message);
+
+        /* We don't treat this as error here because we want allow PA
+         * to run even when no session bus is available. */
+        return r;
+    }
+
+    if ((k = rd_acquire(
+                 &r->device,
+                 pa_dbus_connection_get(r->connection),
+                 device_name,
+                 _("PulseAudio Sound Server"),
+                 0,
+                 request_cb,
+                 NULL)) < 0) {
+
+        if (k == -EBUSY) {
+            pa_log_debug("Device '%s' already locked.", device_name);
+            goto fail;
+        } else {
+            pa_log_debug("Failed to acquire reservation lock on device '%s': %s", device_name, pa_cstrerror(-k));
+            return r;
+        }
+    }
+
+    pa_log_debug("Successfully acquired reservation lock on device '%s'", device_name);
+
+    rd_set_userdata(r->device, r);
+
+    return r;
+fail:
+    dbus_error_free(&error);
+
+    reserve_wrapper_free(r);
+
+    return NULL;
+#else
+    return r;
+#endif
+}
+
+void pa_reserve_wrapper_unref(pa_reserve_wrapper *r) {
+    pa_assert(r);
+    pa_assert(PA_REFCNT_VALUE(r) >= 1);
+
+    if (PA_REFCNT_DEC(r) > 0)
+        return;
+
+    reserve_wrapper_free(r);
+}
+
+pa_hook* pa_reserve_wrapper_hook(pa_reserve_wrapper *r) {
+    pa_assert(r);
+    pa_assert(PA_REFCNT_VALUE(r) >= 1);
+
+    return &r->hook;
+}
+
+void pa_reserve_wrapper_set_application_device_name(pa_reserve_wrapper *r, const char *name) {
+    pa_assert(r);
+    pa_assert(PA_REFCNT_VALUE(r) >= 1);
+
+#ifdef HAVE_DBUS
+    rd_set_application_device_name(r->device, name);
+#endif
+}
+
+static void reserve_monitor_wrapper_free(pa_reserve_monitor_wrapper *w) {
+    pa_assert(w);
+
+#ifdef HAVE_DBUS
+    if (w->monitor)
+        rm_release(w->monitor);
+
+    if (w->connection)
+        pa_dbus_connection_unref(w->connection);
+#endif
+
+    pa_hook_done(&w->hook);
+
+    if (w->shared_name) {
+        pa_assert_se(pa_shared_remove(w->core, w->shared_name) >= 0);
+        pa_xfree(w->shared_name);
+    }
+
+    pa_xfree(w);
+}
+
+#ifdef HAVE_DBUS
+static void change_cb(rm_monitor *m) {
+    pa_reserve_monitor_wrapper *w;
+    int k;
+
+    pa_assert(m);
+    pa_assert_se(w = rm_get_userdata(m));
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    PA_REFCNT_INC(w);
+
+    if ((k = rm_busy(w->monitor)) < 0)
+        return;
+
+    pa_hook_fire(&w->hook, PA_INT_TO_PTR(!!k));
+    pa_log_debug("Device lock status of %s changed: %s", w->shared_name, k ? "busy" : "not busy");
+
+    pa_reserve_monitor_wrapper_unref(w);
+}
+#endif
+
+pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const char *device_name) {
+    pa_reserve_monitor_wrapper *w;
+    char *t;
+#ifdef HAVE_DBUS
+    int k;
+    DBusError error;
+
+    dbus_error_init(&error);
+#endif
+
+    pa_assert(c);
+    pa_assert(device_name);
+
+    t = pa_sprintf_malloc("reserve-monitor-wrapper@%s", device_name);
+
+    if ((w = pa_shared_get(c, t))) {
+        pa_xfree(t);
+
+        pa_assert(PA_REFCNT_VALUE(w) >= 1);
+        PA_REFCNT_INC(w);
+
+        return w;
+    }
+
+    w = pa_xnew0(pa_reserve_monitor_wrapper, 1);
+    PA_REFCNT_INIT(w);
+    w->core = c;
+    pa_hook_init(&w->hook, w);
+    w->shared_name = t;
+
+    pa_assert_se(pa_shared_set(c, w->shared_name, w) >= 0);
+
+#ifdef HAVE_DBUS
+    if (!(w->connection = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
+        pa_log_debug("Unable to contact D-Bus session bus: %s: %s", error.name, error.message);
+
+        /* We don't treat this as error here because we want allow PA
+         * to run even when no session bus is available. */
+        return w;
+    }
+
+    if ((k = rm_watch(
+                 &w->monitor,
+                 pa_dbus_connection_get(w->connection),
+                 device_name,
+                 change_cb,
+                 NULL)) < 0) {
+
+        pa_log_debug("Failed to create watch on device '%s': %s", device_name, pa_cstrerror(-k));
+        goto fail;
+    }
+
+    pa_log_debug("Successfully create reservation lock monitor for device '%s'", device_name);
+
+    rm_set_userdata(w->monitor, w);
+    return w;
+
+fail:
+    dbus_error_free(&error);
+
+    reserve_monitor_wrapper_free(w);
+
+    return NULL;
+#else
+    return w;
+#endif
+}
+
+void pa_reserve_monitor_wrapper_unref(pa_reserve_monitor_wrapper *w) {
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    if (PA_REFCNT_DEC(w) > 0)
+        return;
+
+    reserve_monitor_wrapper_free(w);
+}
+
+pa_hook* pa_reserve_monitor_wrapper_hook(pa_reserve_monitor_wrapper *w) {
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    return &w->hook;
+}
+
+bool pa_reserve_monitor_wrapper_busy(pa_reserve_monitor_wrapper *w) {
+    pa_assert(w);
+
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+#ifdef HAVE_DBUS
+    return rm_busy(w->monitor) > 0;
+#else
+    return false;
+#endif
+}
diff --git a/src/modules/reserve-wrap.h b/src/modules/reserve-wrap.h
new file mode 100644 (file)
index 0000000..772cc37
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef fooreservewraphfoo
+#define fooreservewraphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/hook-list.h>
+
+typedef struct pa_reserve_wrapper pa_reserve_wrapper;
+
+pa_reserve_wrapper* pa_reserve_wrapper_get(pa_core *c, const char *device_name);
+void pa_reserve_wrapper_unref(pa_reserve_wrapper *r);
+
+pa_hook* pa_reserve_wrapper_hook(pa_reserve_wrapper *r);
+
+void pa_reserve_wrapper_set_application_device_name(pa_reserve_wrapper *r, const char *name);
+
+typedef struct pa_reserve_monitor_wrapper pa_reserve_monitor_wrapper;
+
+pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const char *device_name);
+void pa_reserve_monitor_wrapper_unref(pa_reserve_monitor_wrapper *m);
+
+pa_hook* pa_reserve_monitor_wrapper_hook(pa_reserve_monitor_wrapper *m);
+bool pa_reserve_monitor_wrapper_busy(pa_reserve_monitor_wrapper *m);
+
+#endif
diff --git a/src/modules/reserve.c b/src/modules/reserve.c
new file mode 100644 (file)
index 0000000..b0038e6
--- /dev/null
@@ -0,0 +1,687 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
+
+/***
+  Copyright 2009 Lennart Poettering
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "reserve.h"
+
+struct rd_device {
+       int ref;
+
+       char *device_name;
+       char *application_name;
+       char *application_device_name;
+       char *service_name;
+       char *object_path;
+       int32_t priority;
+
+       DBusConnection *connection;
+
+       unsigned owning:1;
+       unsigned registered:1;
+       unsigned filtering:1;
+       unsigned gave_up:1;
+
+       rd_request_cb_t request_cb;
+       void *userdata;
+};
+
+#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
+#define OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/"
+
+static const char introspection[] =
+       DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+       "<node>"
+       " <!-- If you are looking for documentation make sure to check out\n"
+       "      http://git.0pointer.de/?p=reserve.git;a=blob;f=reserve.txt -->\n"
+       " <interface name=\"org.freedesktop.ReserveDevice1\">"
+       "  <method name=\"RequestRelease\">"
+       "   <arg name=\"priority\" type=\"i\" direction=\"in\"/>"
+       "   <arg name=\"result\" type=\"b\" direction=\"out\"/>"
+       "  </method>"
+       "  <property name=\"Priority\" type=\"i\" access=\"read\"/>"
+       "  <property name=\"ApplicationName\" type=\"s\" access=\"read\"/>"
+       "  <property name=\"ApplicationDeviceName\" type=\"s\" access=\"read\"/>"
+       " </interface>"
+       " <interface name=\"org.freedesktop.DBus.Properties\">"
+       "  <method name=\"Get\">"
+       "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>"
+       "   <arg name=\"property\" direction=\"in\" type=\"s\"/>"
+       "   <arg name=\"value\" direction=\"out\" type=\"v\"/>"
+       "  </method>"
+       " </interface>"
+       " <interface name=\"org.freedesktop.DBus.Introspectable\">"
+       "  <method name=\"Introspect\">"
+       "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"
+       "  </method>"
+       " </interface>"
+       "</node>";
+
+static dbus_bool_t add_variant(
+       DBusMessage *m,
+       int type,
+       const void *data) {
+
+       DBusMessageIter iter, sub;
+       char t[2];
+
+       t[0] = (char) type;
+       t[1] = 0;
+
+       dbus_message_iter_init_append(m, &iter);
+
+       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, t, &sub))
+               return FALSE;
+
+       if (!dbus_message_iter_append_basic(&sub, type, data))
+               return FALSE;
+
+       if (!dbus_message_iter_close_container(&iter, &sub))
+               return FALSE;
+
+       return TRUE;
+}
+
+static DBusHandlerResult object_handler(
+       DBusConnection *c,
+       DBusMessage *m,
+       void *userdata) {
+
+       rd_device *d;
+       DBusError error;
+       DBusMessage *reply = NULL;
+
+       dbus_error_init(&error);
+
+       d = userdata;
+       assert(d->ref >= 1);
+
+       if (dbus_message_is_method_call(
+                   m,
+                   "org.freedesktop.ReserveDevice1",
+                   "RequestRelease")) {
+
+               int32_t priority;
+               dbus_bool_t ret;
+
+               if (!dbus_message_get_args(
+                           m,
+                           &error,
+                           DBUS_TYPE_INT32, &priority,
+                           DBUS_TYPE_INVALID))
+                       goto invalid;
+
+               ret = FALSE;
+
+               if (priority > d->priority && d->request_cb) {
+                       d->ref++;
+
+                       if (d->request_cb(d, 0) > 0) {
+                               ret = TRUE;
+                               d->gave_up = 1;
+                       }
+
+                       rd_release(d);
+               }
+
+               if (!(reply = dbus_message_new_method_return(m)))
+                       goto oom;
+
+               if (!dbus_message_append_args(
+                           reply,
+                           DBUS_TYPE_BOOLEAN, &ret,
+                           DBUS_TYPE_INVALID))
+                       goto oom;
+
+               if (!dbus_connection_send(c, reply, NULL))
+                       goto oom;
+
+               dbus_message_unref(reply);
+
+               return DBUS_HANDLER_RESULT_HANDLED;
+
+       } else if (dbus_message_is_method_call(
+                          m,
+                          "org.freedesktop.DBus.Properties",
+                          "Get")) {
+
+               const char *interface, *property;
+
+               if (!dbus_message_get_args(
+                           m,
+                           &error,
+                           DBUS_TYPE_STRING, &interface,
+                           DBUS_TYPE_STRING, &property,
+                           DBUS_TYPE_INVALID))
+                       goto invalid;
+
+               if (strcmp(interface, "org.freedesktop.ReserveDevice1") == 0) {
+                       const char *empty = "";
+
+                       if (strcmp(property, "ApplicationName") == 0 && d->application_name) {
+                               if (!(reply = dbus_message_new_method_return(m)))
+                                       goto oom;
+
+                               if (!add_variant(
+                                           reply,
+                                           DBUS_TYPE_STRING,
+                                           d->application_name ? (const char**) &d->application_name : &empty))
+                                       goto oom;
+
+                       } else if (strcmp(property, "ApplicationDeviceName") == 0) {
+                               if (!(reply = dbus_message_new_method_return(m)))
+                                       goto oom;
+
+                               if (!add_variant(
+                                           reply,
+                                           DBUS_TYPE_STRING,
+                                           d->application_device_name ? (const char**) &d->application_device_name : &empty))
+                                       goto oom;
+
+                       } else if (strcmp(property, "Priority") == 0) {
+                               if (!(reply = dbus_message_new_method_return(m)))
+                                       goto oom;
+
+                               if (!add_variant(
+                                           reply,
+                                           DBUS_TYPE_INT32,
+                                           &d->priority))
+                                       goto oom;
+                       } else {
+                               if (!(reply = dbus_message_new_error_printf(
+                                             m,
+                                             DBUS_ERROR_UNKNOWN_METHOD,
+                                             "Unknown property %s",
+                                             property)))
+                                       goto oom;
+                       }
+
+                       if (!dbus_connection_send(c, reply, NULL))
+                               goto oom;
+
+                       dbus_message_unref(reply);
+
+                       return DBUS_HANDLER_RESULT_HANDLED;
+               }
+
+       } else if (dbus_message_is_method_call(
+                          m,
+                          "org.freedesktop.DBus.Introspectable",
+                          "Introspect")) {
+                           const char *i = introspection;
+
+               if (!(reply = dbus_message_new_method_return(m)))
+                       goto oom;
+
+               if (!dbus_message_append_args(
+                           reply,
+                           DBUS_TYPE_STRING,
+                           &i,
+                           DBUS_TYPE_INVALID))
+                       goto oom;
+
+               if (!dbus_connection_send(c, reply, NULL))
+                       goto oom;
+
+               dbus_message_unref(reply);
+
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+invalid:
+       if (reply)
+               dbus_message_unref(reply);
+
+       if (!(reply = dbus_message_new_error(
+                     m,
+                     DBUS_ERROR_INVALID_ARGS,
+                     "Invalid arguments")))
+               goto oom;
+
+       if (!dbus_connection_send(c, reply, NULL))
+               goto oom;
+
+       dbus_message_unref(reply);
+
+       dbus_error_free(&error);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+       if (reply)
+               dbus_message_unref(reply);
+
+       dbus_error_free(&error);
+
+       return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static DBusHandlerResult filter_handler(
+       DBusConnection *c,
+       DBusMessage *m,
+       void *userdata) {
+
+       rd_device *d;
+       DBusError error;
+       char *name_owner = NULL;
+
+       dbus_error_init(&error);
+
+       d = userdata;
+       assert(d->ref >= 1);
+
+       if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameLost")) {
+               const char *name;
+
+               if (!dbus_message_get_args(
+                           m,
+                           &error,
+                           DBUS_TYPE_STRING, &name,
+                           DBUS_TYPE_INVALID))
+                       goto invalid;
+
+               if (strcmp(name, d->service_name) == 0 && d->owning) {
+                       /* Verify the actual owner of the name to avoid leaked NameLost
+                        * signals from previous reservations. The D-Bus daemon will send
+                        * all messages asynchronously in the correct order, but we could
+                        * potentially process them too late due to the pseudo-blocking
+                        * call mechanism used during both acquisition and release. This
+                        * can happen if we release the device and immediately after
+                        * reacquire it before NameLost is processed. */
+                       if (!d->gave_up) {
+                               const char *un;
+
+                               if ((un = dbus_bus_get_unique_name(c)) && rd_dbus_get_name_owner(c, d->service_name, &name_owner, &error) == 0)
+                                       if (strcmp(name_owner, un) == 0)
+                                               goto invalid; /* Name still owned by us */
+                       }
+
+                       d->owning = 0;
+
+                       if (!d->gave_up)  {
+                               d->ref++;
+
+                               if (d->request_cb)
+                                       d->request_cb(d, 1);
+                               d->gave_up = 1;
+
+                               rd_release(d);
+                       }
+
+               }
+       }
+
+invalid:
+       free(name_owner);
+       dbus_error_free(&error);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static const struct DBusObjectPathVTable vtable ={
+       .message_function = object_handler
+};
+
+int rd_acquire(
+       rd_device **_d,
+       DBusConnection *connection,
+       const char *device_name,
+       const char *application_name,
+       int32_t priority,
+       rd_request_cb_t request_cb,
+       DBusError *error) {
+
+       rd_device *d = NULL;
+       int r, k;
+       DBusError _error;
+       DBusMessage *m = NULL, *reply = NULL;
+       dbus_bool_t good;
+
+       if (!error)
+               error = &_error;
+
+       dbus_error_init(error);
+
+       if (!_d)
+               return -EINVAL;
+
+       if (!connection)
+               return -EINVAL;
+
+       if (!device_name)
+               return -EINVAL;
+
+       if (!request_cb && priority != INT32_MAX)
+               return -EINVAL;
+
+       if (!(d = calloc(sizeof(rd_device), 1)))
+               return -ENOMEM;
+
+       d->ref = 1;
+
+       if (!(d->device_name = strdup(device_name))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       if (!(d->application_name = strdup(application_name))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       d->priority = priority;
+       d->connection = dbus_connection_ref(connection);
+       d->request_cb = request_cb;
+
+       if (!(d->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+       sprintf(d->service_name, SERVICE_PREFIX "%s", d->device_name);
+
+       if (!(d->object_path = malloc(sizeof(OBJECT_PREFIX) + strlen(device_name)))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+       sprintf(d->object_path, OBJECT_PREFIX "%s", d->device_name);
+
+       if ((k = dbus_bus_request_name(
+                    d->connection,
+                    d->service_name,
+                    DBUS_NAME_FLAG_DO_NOT_QUEUE|
+                    (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0),
+                    error)) < 0) {
+               r = -EIO;
+               goto fail;
+       }
+
+       if (k == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+               goto success;
+
+       if (k != DBUS_REQUEST_NAME_REPLY_EXISTS) {
+               r = -EIO;
+               goto fail;
+       }
+
+       if (priority <= INT32_MIN) {
+               r = -EBUSY;
+               goto fail;
+       }
+
+       if (!(m = dbus_message_new_method_call(
+                     d->service_name,
+                     d->object_path,
+                     "org.freedesktop.ReserveDevice1",
+                     "RequestRelease"))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       if (!dbus_message_append_args(
+                   m,
+                   DBUS_TYPE_INT32, &d->priority,
+                   DBUS_TYPE_INVALID)) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       if (!(reply = dbus_connection_send_with_reply_and_block(
+                     d->connection,
+                     m,
+                     5000, /* 5s */
+                     error))) {
+
+               if (dbus_error_has_name(error, DBUS_ERROR_TIMED_OUT) ||
+                   dbus_error_has_name(error, DBUS_ERROR_UNKNOWN_METHOD) ||
+                   dbus_error_has_name(error, DBUS_ERROR_NO_REPLY)) {
+                       /* This must be treated as denied. */
+                       r = -EBUSY;
+                       goto fail;
+               }
+
+               r = -EIO;
+               goto fail;
+       }
+
+        dbus_message_unref(m);
+        m = NULL;
+
+       if (!dbus_message_get_args(
+                   reply,
+                   error,
+                   DBUS_TYPE_BOOLEAN, &good,
+                   DBUS_TYPE_INVALID)) {
+               r = -EIO;
+               goto fail;
+       }
+
+        dbus_message_unref(reply);
+        reply = NULL;
+
+       if (!good) {
+               r = -EBUSY;
+               goto fail;
+       }
+
+       if ((k = dbus_bus_request_name(
+                    d->connection,
+                    d->service_name,
+                    DBUS_NAME_FLAG_DO_NOT_QUEUE|
+                    (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0)|
+                    DBUS_NAME_FLAG_REPLACE_EXISTING,
+                    error)) < 0) {
+               r = -EIO;
+               goto fail;
+       }
+
+       if (k != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+               r = -EIO;
+               goto fail;
+       }
+
+success:
+       d->owning = 1;
+
+       if (!(dbus_connection_register_object_path(
+                     d->connection,
+                     d->object_path,
+                     &vtable,
+                     d))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       d->registered = 1;
+
+       if (!dbus_connection_add_filter(
+                   d->connection,
+                   filter_handler,
+                   d,
+                   NULL)) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       d->filtering = 1;
+
+       *_d = d;
+       return 0;
+
+fail:
+       if (m)
+               dbus_message_unref(m);
+
+       if (reply)
+               dbus_message_unref(reply);
+
+       if (&_error == error)
+               dbus_error_free(&_error);
+
+       if (d)
+               rd_release(d);
+
+       return r;
+}
+
+void rd_release(
+       rd_device *d) {
+
+       if (!d)
+               return;
+
+       assert(d->ref > 0);
+
+       if (--d->ref > 0)
+               return;
+
+
+       if (d->filtering)
+               dbus_connection_remove_filter(
+                       d->connection,
+                       filter_handler,
+                       d);
+
+       if (d->registered)
+               dbus_connection_unregister_object_path(
+                       d->connection,
+                       d->object_path);
+
+       if (d->owning)
+               dbus_bus_release_name(
+                       d->connection,
+                       d->service_name,
+                       NULL);
+
+       free(d->device_name);
+       free(d->application_name);
+       free(d->application_device_name);
+       free(d->service_name);
+       free(d->object_path);
+
+       if (d->connection)
+               dbus_connection_unref(d->connection);
+
+       free(d);
+}
+
+int rd_set_application_device_name(rd_device *d, const char *n) {
+       char *t;
+
+       if (!d)
+               return -EINVAL;
+
+       assert(d->ref > 0);
+
+       if (!(t = strdup(n)))
+               return -ENOMEM;
+
+       free(d->application_device_name);
+       d->application_device_name = t;
+       return 0;
+}
+
+void rd_set_userdata(rd_device *d, void *userdata) {
+
+       if (!d)
+               return;
+
+       assert(d->ref > 0);
+       d->userdata = userdata;
+}
+
+void* rd_get_userdata(rd_device *d) {
+
+       if (!d)
+               return NULL;
+
+       assert(d->ref > 0);
+
+       return d->userdata;
+}
+
+int rd_dbus_get_name_owner(
+       DBusConnection *connection,
+       const char *name,
+       char **name_owner,
+       DBusError *error) {
+
+       DBusMessage *msg, *reply;
+       int r;
+
+       *name_owner = NULL;
+
+       if (!(msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       reply = dbus_connection_send_with_reply_and_block(connection, msg, DBUS_TIMEOUT_USE_DEFAULT, error);
+       dbus_message_unref(msg);
+       msg = NULL;
+
+       if (reply) {
+               if (!dbus_message_get_args(reply, error, DBUS_TYPE_STRING, name_owner, DBUS_TYPE_INVALID)) {
+                       dbus_message_unref(reply);
+                       r = -EIO;
+                       goto fail;
+               }
+
+               *name_owner = strdup(*name_owner);
+               dbus_message_unref(reply);
+
+               if (!*name_owner) {
+                       r = -ENOMEM;
+                       goto fail;
+               }
+
+       } else if (dbus_error_has_name(error, "org.freedesktop.DBus.Error.NameHasNoOwner"))
+               dbus_error_free(error);
+       else {
+               r = -EIO;
+               goto fail;
+       }
+
+       return 0;
+
+fail:
+       if (msg)
+               dbus_message_unref(msg);
+
+       return r;
+}
diff --git a/src/modules/reserve.h b/src/modules/reserve.h
new file mode 100644 (file)
index 0000000..6527bd7
--- /dev/null
@@ -0,0 +1,88 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
+
+#ifndef fooreservehfoo
+#define fooreservehfoo
+
+/***
+  Copyright 2009 Lennart Poettering
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#include <dbus/dbus.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct rd_device rd_device;
+
+/* Prototype for a function that is called whenever someone else wants
+ * your application to release the device it has locked. A return
+ * value <= 0 denies the request, a positive return value agrees to
+ * it. Before returning your application should close the device in
+ * question completely to make sure the new application may access
+ * it. */
+typedef int (*rd_request_cb_t)(
+       rd_device *d,
+       int forced);                  /* Non-zero if an application forcibly took the lock away without asking. If this is the case then the return value of this call is ignored. */
+
+/* Try to lock the device. Returns 0 on success, a negative errno
+ * style return value on error. The DBus error might be set as well if
+ * the error was caused D-Bus. */
+int rd_acquire(
+       rd_device **d,                /* On success a pointer to the newly allocated rd_device object will be filled in here */
+       DBusConnection *connection,   /* Session bus (when D-Bus learns about user busses we should switch to user busses) */
+       const char *device_name,      /* The device to lock, e.g. "Audio0" */
+       const char *application_name, /* A human readable name of the application, e.g. "PulseAudio Sound Server" */
+       int32_t priority,             /* The priority for this application. If unsure use 0 */
+       rd_request_cb_t request_cb,   /* Will be called whenever someone requests that this device shall be released. May be NULL if priority is INT32_MAX */
+       DBusError *error);            /* If we fail due to a D-Bus related issue the error will be filled in here. May be NULL. */
+
+/* Unlock (if needed) and destroy an rd_device object again */
+void rd_release(rd_device *d);
+
+/* Set the application device name for an rd_device object. Returns 0
+ * on success, a negative errno style return value on error. */
+int rd_set_application_device_name(rd_device *d, const char *name);
+
+/* Attach a userdata pointer to an rd_device */
+void rd_set_userdata(rd_device *d, void *userdata);
+
+/* Query the userdata pointer from an rd_device. Returns NULL if no
+ * userdata was set. */
+void* rd_get_userdata(rd_device *d);
+
+/* Helper function to get the unique connection name owning a given
+ * name. Returns 0 on success, a negative errno style return value on
+ * error. */
+int rd_dbus_get_name_owner(
+       DBusConnection *connection,
+       const char *name,
+       char **name_owner,
+       DBusError *error);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/modules/rtp/headerlist.c b/src/modules/rtp/headerlist.c
new file mode 100644 (file)
index 0000000..1fb0b41
--- /dev/null
@@ -0,0 +1,173 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/hashmap.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core-util.h>
+
+#include "headerlist.h"
+
+struct header {
+    char *key;
+    void *value;
+    size_t nbytes;
+};
+
+#define MAKE_HASHMAP(p) ((pa_hashmap*) (p))
+#define MAKE_HEADERLIST(p) ((pa_headerlist*) (p))
+
+static void header_free(struct header *hdr) {
+    pa_assert(hdr);
+
+    pa_xfree(hdr->key);
+    pa_xfree(hdr->value);
+    pa_xfree(hdr);
+}
+
+pa_headerlist* pa_headerlist_new(void) {
+    return MAKE_HEADERLIST(pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) header_free));
+}
+
+void pa_headerlist_free(pa_headerlist* p) {
+    pa_hashmap_free(MAKE_HASHMAP(p));
+}
+
+int pa_headerlist_puts(pa_headerlist *p, const char *key, const char *value) {
+    struct header *hdr;
+    bool add = false;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+        hdr = pa_xnew(struct header, 1);
+        hdr->key = pa_xstrdup(key);
+        add = true;
+    } else
+        pa_xfree(hdr->value);
+
+    hdr->value = pa_xstrdup(value);
+    hdr->nbytes = strlen(value)+1;
+
+    if (add)
+        pa_hashmap_put(MAKE_HASHMAP(p), hdr->key, hdr);
+
+    return 0;
+}
+
+int pa_headerlist_putsappend(pa_headerlist *p, const char *key, const char *value) {
+    struct header *hdr;
+    bool add = false;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+        hdr = pa_xnew(struct header, 1);
+        hdr->key = pa_xstrdup(key);
+        hdr->value = pa_xstrdup(value);
+        add = true;
+    } else {
+        void *newval = pa_sprintf_malloc("%s%s", (char*)hdr->value, value);
+        pa_xfree(hdr->value);
+        hdr->value = newval;
+    }
+    hdr->nbytes = strlen(hdr->value)+1;
+
+    if (add)
+        pa_hashmap_put(MAKE_HASHMAP(p), hdr->key, hdr);
+
+    return 0;
+}
+
+const char *pa_headerlist_gets(pa_headerlist *p, const char *key) {
+    struct header *hdr;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key)))
+        return NULL;
+
+    if (hdr->nbytes <= 0)
+        return NULL;
+
+    if (((char*) hdr->value)[hdr->nbytes-1] != 0)
+        return NULL;
+
+    if (strlen((char*) hdr->value) != hdr->nbytes-1)
+        return NULL;
+
+    return (char*) hdr->value;
+}
+
+int pa_headerlist_remove(pa_headerlist *p, const char *key) {
+    pa_assert(p);
+    pa_assert(key);
+
+    return pa_hashmap_remove_and_free(MAKE_HASHMAP(p), key);
+}
+
+const char *pa_headerlist_iterate(pa_headerlist *p, void **state) {
+    struct header *hdr;
+
+    if (!(hdr = pa_hashmap_iterate(MAKE_HASHMAP(p), state, NULL)))
+        return NULL;
+
+    return hdr->key;
+}
+
+char *pa_headerlist_to_string(pa_headerlist *p) {
+    const char *key;
+    void *state = NULL;
+    pa_strbuf *buf;
+
+    pa_assert(p);
+
+    buf = pa_strbuf_new();
+
+    while ((key = pa_headerlist_iterate(p, &state))) {
+
+        const char *v;
+
+        if ((v = pa_headerlist_gets(p, key)))
+            pa_strbuf_printf(buf, "%s: %s\r\n", key, v);
+    }
+
+    return pa_strbuf_to_string_free(buf);
+}
+
+int pa_headerlist_contains(pa_headerlist *p, const char *key) {
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!(pa_hashmap_get(MAKE_HASHMAP(p), key)))
+        return 0;
+
+    return 1;
+}
diff --git a/src/modules/rtp/headerlist.h b/src/modules/rtp/headerlist.h
new file mode 100644 (file)
index 0000000..f38fb78
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef foopulseheaderlisthfoo
+#define foopulseheaderlisthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/macro.h>
+
+typedef struct pa_headerlist pa_headerlist;
+
+pa_headerlist* pa_headerlist_new(void);
+void pa_headerlist_free(pa_headerlist* p);
+
+int pa_headerlist_puts(pa_headerlist *p, const char *key, const char *value);
+int pa_headerlist_putsappend(pa_headerlist *p, const char *key, const char *value);
+
+const char *pa_headerlist_gets(pa_headerlist *p, const char *key);
+
+int pa_headerlist_remove(pa_headerlist *p, const char *key);
+
+const char *pa_headerlist_iterate(pa_headerlist *p, void **state);
+
+char *pa_headerlist_to_string(pa_headerlist *p);
+
+int pa_headerlist_contains(pa_headerlist *p, const char *key);
+
+#endif
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
new file mode 100644 (file)
index 0000000..f512a8a
--- /dev/null
@@ -0,0 +1,792 @@
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/once.h>
+#include <pulsecore/poll.h>
+#include <pulsecore/arpa-inet.h>
+
+#include "module-rtp-recv-symdef.h"
+
+#include "rtp.h"
+#include "sdp.h"
+#include "sap.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Receive data from a network via RTP/SAP/SDP");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "sink=<name of the sink> "
+        "sap_address=<multicast address to listen on> "
+        "latency_msec=<latency in ms> "
+);
+
+#define SAP_PORT 9875
+#define DEFAULT_SAP_ADDRESS "224.0.0.56"
+#define DEFAULT_LATENCY_MSEC 500
+#define MEMBLOCKQ_MAXLENGTH (1024*1024*40)
+#define MAX_SESSIONS 16
+#define DEATH_TIMEOUT 20
+#define RATE_UPDATE_INTERVAL (5*PA_USEC_PER_SEC)
+
+static const char* const valid_modargs[] = {
+    "sink",
+    "sap_address",
+    "latency_msec",
+    NULL
+};
+
+struct session {
+    struct userdata *userdata;
+    PA_LLIST_FIELDS(struct session);
+
+    pa_sink_input *sink_input;
+    pa_memblockq *memblockq;
+
+    bool first_packet;
+    uint32_t ssrc;
+    uint32_t offset;
+
+    struct pa_sdp_info sdp_info;
+
+    pa_rtp_context rtp_context;
+
+    pa_rtpoll_item *rtpoll_item;
+
+    pa_atomic_t timestamp;
+
+    pa_usec_t intended_latency;
+    pa_usec_t sink_latency;
+
+    unsigned int base_rate;
+    pa_usec_t last_rate_update;
+    pa_usec_t last_latency;
+    double estimated_rate;
+    double avg_estimated_rate;
+};
+
+struct userdata {
+    pa_module *module;
+    pa_core *core;
+
+    pa_sap_context sap_context;
+    pa_io_event* sap_event;
+
+    pa_time_event *check_death_event;
+
+    char *sink_name;
+
+    PA_LLIST_HEAD(struct session, sessions);
+    pa_hashmap *by_origin;
+    int n_sessions;
+
+    pa_usec_t latency;
+};
+
+static void session_free(struct session *s);
+
+/* Called from I/O thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct session *s = PA_SINK_INPUT(o)->userdata;
+
+    switch (code) {
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
+            *((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+            break;
+    }
+
+    return pa_sink_input_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+    struct session *s;
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    if (pa_memblockq_peek(s->memblockq, chunk) < 0)
+        return -1;
+
+    pa_memblockq_drop(s->memblockq, chunk->length);
+
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct session *s;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    pa_memblockq_rewind(s->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct session *s;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    pa_memblockq_set_maxrewind(s->memblockq, nbytes);
+}
+
+/* Called from main context */
+static void sink_input_kill(pa_sink_input* i) {
+    struct session *s;
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    pa_hashmap_remove_and_free(s->userdata->by_origin, s->sdp_info.origin);
+}
+
+/* Called from IO context */
+static void sink_input_suspend_within_thread(pa_sink_input* i, bool b) {
+    struct session *s;
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    if (b)
+        pa_memblockq_flush_read(s->memblockq);
+    else
+        s->first_packet = false;
+}
+
+/* Called from I/O thread context */
+static int rtpoll_work_cb(pa_rtpoll_item *i) {
+    pa_memchunk chunk;
+    int64_t k, j, delta;
+    struct timeval now = { 0, 0 };
+    struct session *s;
+    struct pollfd *p;
+
+    pa_assert_se(s = pa_rtpoll_item_get_userdata(i));
+
+    p = pa_rtpoll_item_get_pollfd(i, NULL);
+
+    if (p->revents & (POLLERR|POLLNVAL|POLLHUP|POLLOUT)) {
+        pa_log("poll() signalled bad revents.");
+        return -1;
+    }
+
+    if ((p->revents & POLLIN) == 0)
+        return 0;
+
+    p->revents = 0;
+
+    if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->module->core->mempool, &now) < 0)
+        return 0;
+
+    if (s->sdp_info.payload != s->rtp_context.payload ||
+        !PA_SINK_IS_OPENED(s->sink_input->sink->thread_info.state)) {
+        pa_memblock_unref(chunk.memblock);
+        return 0;
+    }
+
+    if (!s->first_packet) {
+        s->first_packet = true;
+
+        s->ssrc = s->rtp_context.ssrc;
+        s->offset = s->rtp_context.timestamp;
+
+        if (s->ssrc == s->userdata->module->core->cookie)
+            pa_log_warn("Detected RTP packet loop!");
+    } else {
+        if (s->ssrc != s->rtp_context.ssrc) {
+            pa_memblock_unref(chunk.memblock);
+            return 0;
+        }
+    }
+
+    /* Check whether there was a timestamp overflow */
+    k = (int64_t) s->rtp_context.timestamp - (int64_t) s->offset;
+    j = (int64_t) 0x100000000LL - (int64_t) s->offset + (int64_t) s->rtp_context.timestamp;
+
+    if ((k < 0 ? -k : k) < (j < 0 ? -j : j))
+        delta = k;
+    else
+        delta = j;
+
+    pa_memblockq_seek(s->memblockq, delta * (int64_t) s->rtp_context.frame_size, PA_SEEK_RELATIVE, true);
+
+    if (now.tv_sec == 0) {
+        PA_ONCE_BEGIN {
+            pa_log_warn("Using artificial time instead of timestamp");
+        } PA_ONCE_END;
+        pa_rtclock_get(&now);
+    } else
+        pa_rtclock_from_wallclock(&now);
+
+    if (pa_memblockq_push(s->memblockq, &chunk) < 0) {
+        pa_log_warn("Queue overrun");
+        pa_memblockq_seek(s->memblockq, (int64_t) chunk.length, PA_SEEK_RELATIVE, true);
+    }
+
+/*     pa_log("blocks in q: %u", pa_memblockq_get_nblocks(s->memblockq)); */
+
+    pa_memblock_unref(chunk.memblock);
+
+    /* The next timestamp we expect */
+    s->offset = s->rtp_context.timestamp + (uint32_t) (chunk.length / s->rtp_context.frame_size);
+
+    pa_atomic_store(&s->timestamp, (int) now.tv_sec);
+
+    if (s->last_rate_update + RATE_UPDATE_INTERVAL < pa_timeval_load(&now)) {
+        pa_usec_t wi, ri, render_delay, sink_delay = 0, latency;
+        uint32_t current_rate = s->sink_input->sample_spec.rate;
+        uint32_t new_rate;
+        double estimated_rate, alpha = 0.02;
+
+        pa_log_debug("Updating sample rate");
+
+        wi = pa_bytes_to_usec((uint64_t) pa_memblockq_get_write_index(s->memblockq), &s->sink_input->sample_spec);
+        ri = pa_bytes_to_usec((uint64_t) pa_memblockq_get_read_index(s->memblockq), &s->sink_input->sample_spec);
+
+        pa_log_debug("wi=%lu ri=%lu", (unsigned long) wi, (unsigned long) ri);
+
+        sink_delay = pa_sink_get_latency_within_thread(s->sink_input->sink, false);
+        render_delay = pa_bytes_to_usec(pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq), &s->sink_input->sink->sample_spec);
+
+        if (ri > render_delay+sink_delay)
+            ri -= render_delay+sink_delay;
+        else
+            ri = 0;
+
+        if (wi < ri)
+            latency = 0;
+        else
+            latency = wi - ri;
+
+        pa_log_debug("Write index deviates by %0.2f ms, expected %0.2f ms", (double) latency/PA_USEC_PER_MSEC, (double) s->intended_latency/PA_USEC_PER_MSEC);
+
+        /* The buffer is filling with some unknown rate R̂ samples/second. If the rate of reading in
+         * the last T seconds was Rⁿ, then the increase in buffer latency ΔLⁿ = Lⁿ - Lⁿ⁻ⁱ in that
+         * same period is ΔLⁿ = (TR̂ - TRⁿ) / R̂, giving the estimated target rate
+         *                                           T
+         *                                 R̂ = ─────────────── Rⁿ .                             (1)
+         *                                     T - (Lⁿ - Lⁿ⁻ⁱ)
+         *
+         * Setting the sample rate to R̂ results in the latency being constant (if the estimate of R̂
+         * is correct).  But there is also the requirement to keep the buffer at a predefined target
+         * latency L̂.  So instead of setting Rⁿ⁺ⁱ to R̂ immediately, the strategy will be to reduce R
+         * from Rⁿ⁺ⁱ to R̂ in a steps of T seconds, where Rⁿ⁺ⁱ is chosen such that in the total time
+         * aT the latency is reduced from Lⁿ to L̂.  This strategy translates to the requirements
+         *            ₐ      R̂ - Rⁿ⁺ʲ                            a-j+1         j-1
+         *            Σ  T ────────── = L̂ - Lⁿ    with    Rⁿ⁺ʲ = ───── Rⁿ⁺ⁱ + ───── R̂ .
+         *           ʲ⁼ⁱ        R̂                                  a            a
+         * Solving for Rⁿ⁺ⁱ gives
+         *                                     T - ²∕ₐ₊₁(L̂ - Lⁿ)
+         *                              Rⁿ⁺ⁱ = ───────────────── R̂ .                            (2)
+         *                                            T
+         * In the code below a = 7 is used.
+         *
+         * Equation (1) is not directly used in (2), but instead an exponentially weighted average
+         * of the estimated rate R̂ is used.  This average R̅ is defined as
+         *                                R̅ⁿ = α R̂ⁿ + (1-α) R̅ⁿ⁻ⁱ .
+         * Because it is difficult to find a fixed value for the coefficient α such that the
+         * averaging is without significant lag but oscillations are filtered out, a heuristic is
+         * used.  When the successive estimates R̂ⁿ do not change much then α→1, but when there is a
+         * sudden spike in the estimated rate α→0, such that the deviation is given little weight.
+         */
+        estimated_rate = (double) current_rate * (double) RATE_UPDATE_INTERVAL / (double) (RATE_UPDATE_INTERVAL + s->last_latency - latency);
+        if (fabs(s->estimated_rate - s->avg_estimated_rate) > 1) {
+          double ratio = (estimated_rate + s->estimated_rate - 2*s->avg_estimated_rate) / (s->estimated_rate - s->avg_estimated_rate);
+          alpha = PA_CLAMP(2 * (ratio + fabs(ratio)) / (4 + ratio*ratio), 0.02, 0.8);
+        }
+        s->avg_estimated_rate = alpha * estimated_rate + (1-alpha) * s->avg_estimated_rate;
+        s->estimated_rate = estimated_rate;
+        pa_log_debug("Estimated target rate: %.0f Hz, using average of %.0f Hz  (α=%.3f)", estimated_rate, s->avg_estimated_rate, alpha);
+        new_rate = (uint32_t) ((double) (RATE_UPDATE_INTERVAL + latency/4 - s->intended_latency/4) / (double) RATE_UPDATE_INTERVAL * s->avg_estimated_rate);
+        s->last_latency = latency;
+
+        if (new_rate < (uint32_t) (s->base_rate*0.8) || new_rate > (uint32_t) (s->base_rate*1.25)) {
+            pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", s->base_rate, new_rate);
+            new_rate = s->base_rate;
+        } else {
+            if (s->base_rate < new_rate + 20 && new_rate < s->base_rate + 20)
+                new_rate = s->base_rate;
+            /* Do the adjustment in small steps; 2‰ can be considered inaudible */
+            if (new_rate < (uint32_t) (current_rate*0.998) || new_rate > (uint32_t) (current_rate*1.002)) {
+                pa_log_info("New rate of %u Hz not within 2‰ of %u Hz, forcing smaller adjustment", new_rate, current_rate);
+                new_rate = PA_CLAMP(new_rate, (uint32_t) (current_rate*0.998), (uint32_t) (current_rate*1.002));
+            }
+        }
+        s->sink_input->sample_spec.rate = new_rate;
+
+        pa_assert(pa_sample_spec_valid(&s->sink_input->sample_spec));
+
+        pa_resampler_set_input_rate(s->sink_input->thread_info.resampler, s->sink_input->sample_spec.rate);
+
+        pa_log_debug("Updated sampling rate to %lu Hz.", (unsigned long) s->sink_input->sample_spec.rate);
+
+        s->last_rate_update = pa_timeval_load(&now);
+    }
+
+    if (pa_memblockq_is_readable(s->memblockq) &&
+        s->sink_input->thread_info.underrun_for > 0) {
+        pa_log_debug("Requesting rewind due to end of underrun");
+        pa_sink_input_request_rewind(s->sink_input,
+                                     (size_t) (s->sink_input->thread_info.underrun_for == (uint64_t) -1 ? 0 : s->sink_input->thread_info.underrun_for),
+                                     false, true, false);
+    }
+
+    return 1;
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach(pa_sink_input *i) {
+    struct session *s;
+    struct pollfd *p;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    pa_assert(!s->rtpoll_item);
+    s->rtpoll_item = pa_rtpoll_item_new(i->sink->thread_info.rtpoll, PA_RTPOLL_LATE, 1);
+
+    p = pa_rtpoll_item_get_pollfd(s->rtpoll_item, NULL);
+    p->fd = s->rtp_context.fd;
+    p->events = POLLIN;
+    p->revents = 0;
+
+    pa_rtpoll_item_set_work_callback(s->rtpoll_item, rtpoll_work_cb);
+    pa_rtpoll_item_set_userdata(s->rtpoll_item, s);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach(pa_sink_input *i) {
+    struct session *s;
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    pa_assert(s->rtpoll_item);
+    pa_rtpoll_item_free(s->rtpoll_item);
+    s->rtpoll_item = NULL;
+}
+
+static int mcast_socket(const struct sockaddr* sa, socklen_t salen) {
+    int af, fd = -1, r, one;
+
+    pa_assert(sa);
+    pa_assert(salen > 0);
+
+    af = sa->sa_family;
+    if ((fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("Failed to create socket: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_make_udp_socket_low_delay(fd);
+
+#ifdef SO_TIMESTAMP
+    one = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) {
+        pa_log("SO_TIMESTAMP failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+#else
+    pa_log("SO_TIMESTAMP unsupported on this platform");
+    goto fail;
+#endif
+
+    one = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
+        pa_log("SO_REUSEADDR failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    r = 0;
+    if (af == AF_INET) {
+        /* IPv4 multicast addresses are in the 224.0.0.0-239.255.255.255 range */
+        static const uint32_t ipv4_mcast_mask = 0xe0000000;
+
+        if ((ntohl(((const struct sockaddr_in*) sa)->sin_addr.s_addr) & ipv4_mcast_mask) == ipv4_mcast_mask) {
+            struct ip_mreq mr4;
+            memset(&mr4, 0, sizeof(mr4));
+            mr4.imr_multiaddr = ((const struct sockaddr_in*) sa)->sin_addr;
+            r = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr4, sizeof(mr4));
+        }
+#ifdef HAVE_IPV6
+    } else if (af == AF_INET6) {
+        /* IPv6 multicast addresses have 255 as the most significant byte */
+        if (((const struct sockaddr_in6*) sa)->sin6_addr.s6_addr[0] == 0xff) {
+            struct ipv6_mreq mr6;
+            memset(&mr6, 0, sizeof(mr6));
+            mr6.ipv6mr_multiaddr = ((const struct sockaddr_in6*) sa)->sin6_addr;
+            r = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mr6, sizeof(mr6));
+        }
+#endif
+    } else
+        pa_assert_not_reached();
+
+    if (r < 0) {
+        pa_log_info("Joining mcast group failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (bind(fd, sa, salen) < 0) {
+        pa_log("bind() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    return fd;
+
+fail:
+    if (fd >= 0)
+        close(fd);
+
+    return -1;
+}
+
+static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_info) {
+    struct session *s = NULL;
+    pa_sink *sink;
+    int fd = -1;
+    pa_memchunk silence;
+    pa_sink_input_new_data data;
+    struct timeval now;
+
+    pa_assert(u);
+    pa_assert(sdp_info);
+
+    if (u->n_sessions >= MAX_SESSIONS) {
+        pa_log("Session limit reached.");
+        goto fail;
+    }
+
+    if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
+        pa_log("Sink does not exist.");
+        goto fail;
+    }
+
+    pa_rtclock_get(&now);
+
+    s = pa_xnew0(struct session, 1);
+    s->userdata = u;
+    s->first_packet = false;
+    s->sdp_info = *sdp_info;
+    s->rtpoll_item = NULL;
+    s->intended_latency = u->latency;
+    s->last_rate_update = pa_timeval_load(&now);
+    s->last_latency = u->latency;
+    pa_atomic_store(&s->timestamp, (int) now.tv_sec);
+
+    if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0)
+        goto fail;
+
+    pa_sink_input_new_data_init(&data);
+    pa_sink_input_new_data_set_sink(&data, sink, false);
+    data.driver = __FILE__;
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream");
+    pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME,
+                     "RTP Stream%s%s%s",
+                     sdp_info->session_name ? " (" : "",
+                     sdp_info->session_name ? sdp_info->session_name : "",
+                     sdp_info->session_name ? ")" : "");
+
+    if (sdp_info->session_name)
+        pa_proplist_sets(data.proplist, "rtp.session", sdp_info->session_name);
+    pa_proplist_sets(data.proplist, "rtp.origin", sdp_info->origin);
+    pa_proplist_setf(data.proplist, "rtp.payload", "%u", (unsigned) sdp_info->payload);
+    data.module = u->module;
+    pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec);
+    data.flags = PA_SINK_INPUT_VARIABLE_RATE;
+
+    pa_sink_input_new(&s->sink_input, u->module->core, &data);
+    pa_sink_input_new_data_done(&data);
+
+    if (!s->sink_input) {
+        pa_log("Failed to create sink input.");
+        goto fail;
+    }
+
+    s->base_rate = (double) s->sink_input->sample_spec.rate;
+    s->estimated_rate = (double) s->sink_input->sample_spec.rate;
+    s->avg_estimated_rate = (double) s->sink_input->sample_spec.rate;
+
+    s->sink_input->userdata = s;
+
+    s->sink_input->parent.process_msg = sink_input_process_msg;
+    s->sink_input->pop = sink_input_pop_cb;
+    s->sink_input->process_rewind = sink_input_process_rewind_cb;
+    s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    s->sink_input->kill = sink_input_kill;
+    s->sink_input->attach = sink_input_attach;
+    s->sink_input->detach = sink_input_detach;
+    s->sink_input->suspend_within_thread = sink_input_suspend_within_thread;
+
+    pa_sink_input_get_silence(s->sink_input, &silence);
+
+    s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, s->intended_latency/2);
+
+    if (s->intended_latency < s->sink_latency*2)
+        s->intended_latency = s->sink_latency*2;
+
+    s->memblockq = pa_memblockq_new(
+            "module-rtp-recv memblockq",
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            MEMBLOCKQ_MAXLENGTH,
+            &s->sink_input->sample_spec,
+            pa_usec_to_bytes(s->intended_latency - s->sink_latency, &s->sink_input->sample_spec),
+            0,
+            0,
+            &silence);
+
+    pa_memblock_unref(silence.memblock);
+
+    pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec));
+
+    pa_hashmap_put(s->userdata->by_origin, s->sdp_info.origin, s);
+    u->n_sessions++;
+    PA_LLIST_PREPEND(struct session, s->userdata->sessions, s);
+
+    pa_sink_input_put(s->sink_input);
+
+    pa_log_info("New session '%s'", s->sdp_info.session_name);
+
+    return s;
+
+fail:
+    pa_xfree(s);
+
+    if (fd >= 0)
+        pa_close(fd);
+
+    return NULL;
+}
+
+static void session_free(struct session *s) {
+    pa_assert(s);
+
+    pa_log_info("Freeing session '%s'", s->sdp_info.session_name);
+
+    pa_sink_input_unlink(s->sink_input);
+    pa_sink_input_unref(s->sink_input);
+
+    PA_LLIST_REMOVE(struct session, s->userdata->sessions, s);
+    pa_assert(s->userdata->n_sessions >= 1);
+    s->userdata->n_sessions--;
+
+    pa_memblockq_free(s->memblockq);
+    pa_sdp_info_destroy(&s->sdp_info);
+    pa_rtp_context_destroy(&s->rtp_context);
+
+    pa_xfree(s);
+}
+
+static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
+    struct userdata *u = userdata;
+    bool goodbye = false;
+    pa_sdp_info info;
+    struct session *s;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(u);
+    pa_assert(fd == u->sap_context.fd);
+    pa_assert(flags == PA_IO_EVENT_INPUT);
+
+    if (pa_sap_recv(&u->sap_context, &goodbye) < 0)
+        return;
+
+    if (!pa_sdp_parse(u->sap_context.sdp_data, &info, goodbye))
+        return;
+
+    if (goodbye) {
+        pa_hashmap_remove_and_free(u->by_origin, info.origin);
+        pa_sdp_info_destroy(&info);
+    } else {
+
+        if (!(s = pa_hashmap_get(u->by_origin, info.origin))) {
+            if (!session_new(u, &info))
+                pa_sdp_info_destroy(&info);
+
+        } else {
+            struct timeval now;
+            pa_rtclock_get(&now);
+            pa_atomic_store(&s->timestamp, (int) now.tv_sec);
+
+            pa_sdp_info_destroy(&info);
+        }
+    }
+}
+
+static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {
+    struct session *s, *n;
+    struct userdata *u = userdata;
+    struct timeval now;
+
+    pa_assert(m);
+    pa_assert(t);
+    pa_assert(u);
+
+    pa_rtclock_get(&now);
+
+    pa_log_debug("Checking for dead streams ...");
+
+    for (s = u->sessions; s; s = n) {
+        int k;
+        n = s->next;
+
+        k = pa_atomic_load(&s->timestamp);
+
+        if (k + DEATH_TIMEOUT < now.tv_sec)
+            pa_hashmap_remove_and_free(u->by_origin, s->sdp_info.origin);
+    }
+
+    /* Restart timer */
+    pa_core_rttime_restart(u->module->core, t, pa_rtclock_now() + DEATH_TIMEOUT * PA_USEC_PER_SEC);
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    struct sockaddr_in sa4;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 sa6;
+#endif
+    struct sockaddr *sa;
+    socklen_t salen;
+    const char *sap_address;
+    uint32_t latency_msec;
+    int fd = -1;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments");
+        goto fail;
+    }
+
+    sap_address = pa_modargs_get_value(ma, "sap_address", DEFAULT_SAP_ADDRESS);
+
+    if (inet_pton(AF_INET, sap_address, &sa4.sin_addr) > 0) {
+        sa4.sin_family = AF_INET;
+        sa4.sin_port = htons(SAP_PORT);
+        sa = (struct sockaddr*) &sa4;
+        salen = sizeof(sa4);
+#ifdef HAVE_IPV6
+    } else if (inet_pton(AF_INET6, sap_address, &sa6.sin6_addr) > 0) {
+        sa6.sin6_family = AF_INET6;
+        sa6.sin6_port = htons(SAP_PORT);
+        sa = (struct sockaddr*) &sa6;
+        salen = sizeof(sa6);
+#endif
+    } else {
+        pa_log("Invalid SAP address '%s'", sap_address);
+        goto fail;
+    }
+
+    latency_msec = DEFAULT_LATENCY_MSEC;
+    if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 300000) {
+        pa_log("Invalid latency specification");
+        goto fail;
+    }
+
+    if ((fd = mcast_socket(sa, salen)) < 0)
+        goto fail;
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->module = m;
+    u->core = m->core;
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+    u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC;
+
+    u->sap_event = m->core->mainloop->io_new(m->core->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u);
+    pa_sap_context_init_recv(&u->sap_context, fd);
+
+    PA_LLIST_HEAD_INIT(struct session, u->sessions);
+    u->n_sessions = 0;
+    u->by_origin = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) session_free);
+
+    u->check_death_event = pa_core_rttime_new(m->core, pa_rtclock_now() + DEATH_TIMEOUT * PA_USEC_PER_SEC, check_death_event_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    if (fd >= 0)
+        pa_close(fd);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sap_event)
+        m->core->mainloop->io_free(u->sap_event);
+
+    if (u->check_death_event)
+        m->core->mainloop->time_free(u->check_death_event);
+
+    pa_sap_context_destroy(&u->sap_context);
+
+    if (u->by_origin)
+        pa_hashmap_free(u->by_origin);
+
+    pa_xfree(u->sink_name);
+    pa_xfree(u);
+}
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
new file mode 100644 (file)
index 0000000..15a7436
--- /dev/null
@@ -0,0 +1,551 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/source.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/arpa-inet.h>
+
+#include "module-rtp-send-symdef.h"
+
+#include "rtp.h"
+#include "sdp.h"
+#include "sap.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Read data from source and send it to the network via RTP/SAP/SDP");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "source=<name of the source> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "destination_ip=<destination IP address> "
+        "source_ip=<source IP address> "
+        "port=<port number> "
+        "mtu=<maximum transfer unit> "
+        "loop=<loopback to local host?> "
+        "ttl=<ttl value> "
+        "inhibit_auto_suspend=<always|never|only_with_non_monitor_sources>"
+);
+
+#define DEFAULT_PORT 46000
+#define DEFAULT_TTL 1
+#define SAP_PORT 9875
+#define DEFAULT_SOURCE_IP "0.0.0.0"
+#define DEFAULT_DESTINATION_IP "224.0.0.56"
+#define MEMBLOCKQ_MAXLENGTH (1024*170)
+#define DEFAULT_MTU 1280
+#define SAP_INTERVAL (5*PA_USEC_PER_SEC)
+
+static const char* const valid_modargs[] = {
+    "source",
+    "format",
+    "channels",
+    "rate",
+    "destination", /* Compatbility */
+    "destination_ip",
+    "source_ip",
+    "port",
+    "mtu" ,
+    "loop",
+    "ttl",
+    "inhibit_auto_suspend",
+    NULL
+};
+
+enum inhibit_auto_suspend {
+    INHIBIT_AUTO_SUSPEND_ALWAYS,
+    INHIBIT_AUTO_SUSPEND_NEVER,
+    INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES
+};
+
+struct userdata {
+    pa_module *module;
+
+    pa_source_output *source_output;
+    pa_memblockq *memblockq;
+
+    pa_rtp_context rtp_context;
+    pa_sap_context sap_context;
+    size_t mtu;
+
+    pa_time_event *sap_event;
+
+    enum inhibit_auto_suspend inhibit_auto_suspend;
+};
+
+/* Called from I/O thread context */
+static int source_output_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u;
+    pa_assert_se(u = PA_SOURCE_OUTPUT(o)->userdata);
+
+    switch (code) {
+        case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY:
+            *((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->source_output->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+            break;
+    }
+
+    return pa_source_output_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from I/O thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    struct userdata *u;
+    pa_source_output_assert_ref(o);
+    pa_assert_se(u = o->userdata);
+
+    if (pa_memblockq_push(u->memblockq, chunk) < 0) {
+        pa_log_warn("Failed to push chunk into memblockq.");
+        return;
+    }
+
+    pa_rtp_send(&u->rtp_context, u->mtu, u->memblockq);
+}
+
+static pa_source_output_flags_t get_dont_inhibit_auto_suspend_flag(pa_source *source,
+                                                                   enum inhibit_auto_suspend inhibit_auto_suspend) {
+    pa_assert(source);
+
+    switch (inhibit_auto_suspend) {
+        case INHIBIT_AUTO_SUSPEND_ALWAYS:
+            return 0;
+
+        case INHIBIT_AUTO_SUSPEND_NEVER:
+            return PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND;
+
+        case INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES:
+            return source->monitor_of ? 0 : PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND;
+    }
+
+    pa_assert_not_reached();
+}
+
+/* Called from the main thread. */
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
+    struct userdata *u;
+
+    pa_assert(o);
+
+    u = o->userdata;
+
+    if (!dest)
+        return;
+
+    o->flags &= ~PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND;
+    o->flags |= get_dont_inhibit_auto_suspend_flag(dest, u->inhibit_auto_suspend);
+}
+
+/* Called from main context */
+static void source_output_kill_cb(pa_source_output* o) {
+    struct userdata *u;
+    pa_source_output_assert_ref(o);
+    pa_assert_se(u = o->userdata);
+
+    pa_module_unload_request(u->module, true);
+
+    pa_source_output_unlink(u->source_output);
+    pa_source_output_unref(u->source_output);
+    u->source_output = NULL;
+}
+
+static void sap_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(m);
+    pa_assert(t);
+    pa_assert(u);
+
+    pa_sap_send(&u->sap_context, 0);
+
+    pa_core_rttime_restart(u->module->core, t, pa_rtclock_now() + SAP_INTERVAL);
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    const char *dst_addr;
+    const char *src_addr;
+    uint32_t port = DEFAULT_PORT, mtu;
+    uint32_t ttl = DEFAULT_TTL;
+    sa_family_t af;
+    int fd = -1, sap_fd = -1;
+    pa_source *s;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    struct sockaddr_in dst_sa4, dst_sap_sa4, src_sa4, src_sap_sa4;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 dst_sa6, dst_sap_sa6, src_sa6, src_sap_sa6;
+#endif
+    struct sockaddr_storage sa_dst;
+    pa_source_output *o = NULL;
+    uint8_t payload;
+    char *p;
+    int r, j;
+    socklen_t k;
+    char hn[128], *n;
+    bool loop = false;
+    enum inhibit_auto_suspend inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES;
+    const char *inhibit_auto_suspend_str;
+    pa_source_output_new_data data;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) {
+        pa_log("Source does not exist.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "loop", &loop) < 0) {
+        pa_log("Failed to parse \"loop\" parameter.");
+        goto fail;
+    }
+
+    if ((inhibit_auto_suspend_str = pa_modargs_get_value(ma, "inhibit_auto_suspend", NULL))) {
+        if (pa_streq(inhibit_auto_suspend_str, "always"))
+            inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ALWAYS;
+        else if (pa_streq(inhibit_auto_suspend_str, "never"))
+            inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_NEVER;
+        else if (pa_streq(inhibit_auto_suspend_str, "only_with_non_monitor_sources"))
+            inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES;
+        else {
+            pa_log("Failed to parse the \"inhibit_auto_suspend\" parameter.");
+            goto fail;
+        }
+    }
+
+    ss = s->sample_spec;
+    pa_rtp_sample_spec_fixup(&ss);
+    cm = s->channel_map;
+    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+        pa_log("Failed to parse sample specification");
+        goto fail;
+    }
+
+    if (!pa_rtp_sample_spec_valid(&ss)) {
+        pa_log("Specified sample type not compatible with RTP");
+        goto fail;
+    }
+
+    if (ss.channels != cm.channels)
+        pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_AIFF);
+
+    payload = pa_rtp_payload_from_sample_spec(&ss);
+
+    mtu = (uint32_t) pa_frame_align(DEFAULT_MTU, &ss);
+
+    if (pa_modargs_get_value_u32(ma, "mtu", &mtu) < 0 || mtu < 1 || mtu % pa_frame_size(&ss) != 0) {
+        pa_log("Invalid MTU.");
+        goto fail;
+    }
+
+    port = DEFAULT_PORT + ((uint32_t) (rand() % 512) << 1);
+    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) {
+        pa_log("port= expects a numerical argument between 1 and 65535.");
+        goto fail;
+    }
+
+    if (port & 1)
+        pa_log_warn("Port number not even as suggested in RFC3550!");
+
+    if (pa_modargs_get_value_u32(ma, "ttl", &ttl) < 0 || ttl < 1 || ttl > 0xFF) {
+        pa_log("ttl= expects a numerical argument between 1 and 255.");
+        goto fail;
+    }
+
+    src_addr = pa_modargs_get_value(ma, "source_ip", DEFAULT_SOURCE_IP);
+
+    if (inet_pton(AF_INET, src_addr, &src_sa4.sin_addr) > 0) {
+        src_sa4.sin_family = af = AF_INET;
+        src_sa4.sin_port = htons(0);
+        memset(&src_sa4.sin_zero, 0, sizeof(src_sa4.sin_zero));
+        src_sap_sa4 = src_sa4;
+#ifdef HAVE_IPV6
+    } else if (inet_pton(AF_INET6, src_addr, &src_sa6.sin6_addr) > 0) {
+        src_sa6.sin6_family = af = AF_INET6;
+        src_sa6.sin6_port = htons(0);
+        src_sa6.sin6_flowinfo = 0;
+        src_sa6.sin6_scope_id = 0;
+        src_sap_sa6 = src_sa6;
+#endif
+    } else {
+        pa_log("Invalid source address '%s'", src_addr);
+        goto fail;
+    }
+
+    dst_addr = pa_modargs_get_value(ma, "destination", NULL);
+    if (dst_addr == NULL)
+        dst_addr = pa_modargs_get_value(ma, "destination_ip", DEFAULT_DESTINATION_IP);
+
+    if (inet_pton(AF_INET, dst_addr, &dst_sa4.sin_addr) > 0) {
+        dst_sa4.sin_family = af = AF_INET;
+        dst_sa4.sin_port = htons((uint16_t) port);
+        memset(&dst_sa4.sin_zero, 0, sizeof(dst_sa4.sin_zero));
+        dst_sap_sa4 = dst_sa4;
+        dst_sap_sa4.sin_port = htons(SAP_PORT);
+#ifdef HAVE_IPV6
+    } else if (inet_pton(AF_INET6, dst_addr, &dst_sa6.sin6_addr) > 0) {
+        dst_sa6.sin6_family = af = AF_INET6;
+        dst_sa6.sin6_port = htons((uint16_t) port);
+        dst_sa6.sin6_flowinfo = 0;
+        dst_sa6.sin6_scope_id = 0;
+        dst_sap_sa6 = dst_sa6;
+        dst_sap_sa6.sin6_port = htons(SAP_PORT);
+#endif
+    } else {
+        pa_log("Invalid destination '%s'", dst_addr);
+        goto fail;
+    }
+
+    if ((fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("socket() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (af == AF_INET && bind(fd, (struct sockaddr*) &src_sa4, sizeof(src_sa4)) < 0) {
+        pa_log("bind() failed: %s", pa_cstrerror(errno));
+        goto fail;
+#ifdef HAVE_IPV6
+    } else if (af == AF_INET6 && bind(fd, (struct sockaddr*) &src_sa6, sizeof(src_sa6)) < 0) {
+        pa_log("bind() failed: %s", pa_cstrerror(errno));
+        goto fail;
+#endif
+    }
+
+    if (af == AF_INET && connect(fd, (struct sockaddr*) &dst_sa4, sizeof(dst_sa4)) < 0) {
+        pa_log("connect() failed: %s", pa_cstrerror(errno));
+        goto fail;
+#ifdef HAVE_IPV6
+    } else if (af == AF_INET6 && connect(fd, (struct sockaddr*) &dst_sa6, sizeof(dst_sa6)) < 0) {
+        pa_log("connect() failed: %s", pa_cstrerror(errno));
+        goto fail;
+#endif
+    }
+
+    if ((sap_fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("socket() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (af == AF_INET && bind(sap_fd, (struct sockaddr*) &src_sap_sa4, sizeof(src_sap_sa4)) < 0) {
+        pa_log("bind() failed: %s", pa_cstrerror(errno));
+        goto fail;
+#ifdef HAVE_IPV6
+    } else if (af == AF_INET6 && bind(sap_fd, (struct sockaddr*) &src_sap_sa6, sizeof(src_sap_sa6)) < 0) {
+        pa_log("bind() failed: %s", pa_cstrerror(errno));
+        goto fail;
+#endif
+    }
+
+    if (af == AF_INET && connect(sap_fd, (struct sockaddr*) &dst_sap_sa4, sizeof(dst_sap_sa4)) < 0) {
+        pa_log("connect() failed: %s", pa_cstrerror(errno));
+        goto fail;
+#ifdef HAVE_IPV6
+    } else if (af == AF_INET6 && connect(sap_fd, (struct sockaddr*) &dst_sap_sa6, sizeof(dst_sap_sa6)) < 0) {
+        pa_log("connect() failed: %s", pa_cstrerror(errno));
+        goto fail;
+#endif
+    }
+
+    j = loop;
+    if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0 ||
+        setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0) {
+        pa_log("IP_MULTICAST_LOOP failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (ttl != DEFAULT_TTL) {
+        int _ttl = (int) ttl;
+
+        if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &_ttl, sizeof(_ttl)) < 0) {
+            pa_log("IP_MULTICAST_TTL failed: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+        if (setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_TTL, &_ttl, sizeof(_ttl)) < 0) {
+            pa_log("IP_MULTICAST_TTL (sap) failed: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+    }
+
+    /* If the socket queue is full, let's drop packets */
+    pa_make_fd_nonblock(fd);
+    pa_make_udp_socket_low_delay(fd);
+
+    pa_source_output_new_data_init(&data);
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, "RTP Monitor Stream");
+    pa_proplist_sets(data.proplist, "rtp.source", src_addr);
+    pa_proplist_sets(data.proplist, "rtp.destination", dst_addr);
+    pa_proplist_setf(data.proplist, "rtp.mtu", "%lu", (unsigned long) mtu);
+    pa_proplist_setf(data.proplist, "rtp.port", "%lu", (unsigned long) port);
+    pa_proplist_setf(data.proplist, "rtp.ttl", "%lu", (unsigned long) ttl);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_source_output_new_data_set_source(&data, s, false);
+    pa_source_output_new_data_set_sample_spec(&data, &ss);
+    pa_source_output_new_data_set_channel_map(&data, &cm);
+    data.flags |= get_dont_inhibit_auto_suspend_flag(s, inhibit_auto_suspend);
+
+    pa_source_output_new(&o, m->core, &data);
+    pa_source_output_new_data_done(&data);
+
+    if (!o) {
+        pa_log("failed to create source output.");
+        goto fail;
+    }
+
+    o->parent.process_msg = source_output_process_msg;
+    o->push = source_output_push_cb;
+    o->moving = source_output_moving_cb;
+    o->kill = source_output_kill_cb;
+
+    pa_log_info("Configured source latency of %llu ms.",
+                (unsigned long long) pa_source_output_set_requested_latency(o, pa_bytes_to_usec(mtu, &o->sample_spec)) / PA_USEC_PER_MSEC);
+
+    m->userdata = o->userdata = u = pa_xnew(struct userdata, 1);
+    u->module = m;
+    u->source_output = o;
+
+    u->memblockq = pa_memblockq_new(
+            "module-rtp-send memblockq",
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            MEMBLOCKQ_MAXLENGTH,
+            &ss,
+            1,
+            0,
+            0,
+            NULL);
+
+    u->mtu = mtu;
+
+    k = sizeof(sa_dst);
+    pa_assert_se((r = getsockname(fd, (struct sockaddr*) &sa_dst, &k)) >= 0);
+
+    n = pa_sprintf_malloc("PulseAudio RTP Stream on %s", pa_get_fqdn(hn, sizeof(hn)));
+
+    if (af == AF_INET) {
+        p = pa_sdp_build(af,
+                     (void*) &((struct sockaddr_in*) &sa_dst)->sin_addr,
+                     (void*) &dst_sa4.sin_addr,
+                     n, (uint16_t) port, payload, &ss);
+#ifdef HAVE_IPV6
+    } else {
+        p = pa_sdp_build(af,
+                     (void*) &((struct sockaddr_in6*) &sa_dst)->sin6_addr,
+                     (void*) &dst_sa6.sin6_addr,
+                     n, (uint16_t) port, payload, &ss);
+#endif
+    }
+
+    pa_xfree(n);
+
+    pa_rtp_context_init_send(&u->rtp_context, fd, m->core->cookie, payload, pa_frame_size(&ss));
+    pa_sap_context_init_send(&u->sap_context, sap_fd, p);
+
+    pa_log_info("RTP stream initialized with mtu %u on %s:%u from %s ttl=%u, SSRC=0x%08x, payload=%u, initial sequence #%u", mtu, dst_addr, port, src_addr, ttl, u->rtp_context.ssrc, payload, u->rtp_context.sequence);
+    pa_log_info("SDP-Data:\n%s\nEOF", p);
+
+    pa_sap_send(&u->sap_context, 0);
+
+    u->sap_event = pa_core_rttime_new(m->core, pa_rtclock_now() + SAP_INTERVAL, sap_event_cb, u);
+    u->inhibit_auto_suspend = inhibit_auto_suspend;
+
+    pa_source_output_put(u->source_output);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    if (fd >= 0)
+        pa_close(fd);
+
+    if (sap_fd >= 0)
+        pa_close(sap_fd);
+
+    if (o) {
+        pa_source_output_unlink(o);
+        pa_source_output_unref(o);
+    }
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sap_event)
+        m->core->mainloop->time_free(u->sap_event);
+
+    if (u->source_output) {
+        pa_source_output_unlink(u->source_output);
+        pa_source_output_unref(u->source_output);
+    }
+
+    pa_rtp_context_destroy(&u->rtp_context);
+
+    pa_sap_send(&u->sap_context, 1);
+    pa_sap_context_destroy(&u->sap_context);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/rtp/rfc2327.txt b/src/modules/rtp/rfc2327.txt
new file mode 100644 (file)
index 0000000..ce77de6
--- /dev/null
@@ -0,0 +1,2355 @@
+
+
+
+
+
+
+Network Working Group                                           M. Handley
+Request for Comments: 2327                                     V. Jacobson
+Category: Standards Track                                         ISI/LBNL
+                                                                April 1998
+
+
+                   SDP: Session Description Protocol
+
+Status of this Memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The Internet Society (1998).  All Rights Reserved.
+
+Abstract
+
+   This document defines the Session Description Protocol, SDP.  SDP is
+   intended for describing multimedia sessions for the purposes of
+   session announcement, session invitation, and other forms of
+   multimedia session initiation.
+
+   This document is a product of the Multiparty Multimedia Session
+   Control (MMUSIC) working group of the Internet Engineering Task
+   Force. Comments are solicited and should be addressed to the working
+   group's mailing list at confctrl@isi.edu and/or the authors.
+
+1.  Introduction
+
+   On the Internet multicast backbone (Mbone), a session directory tool
+   is used to advertise multimedia conferences and communicate the
+   conference addresses and conference tool-specific information
+   necessary for participation.  This document defines a session
+   description protocol for this purpose, and for general real-time
+   multimedia session description purposes. This memo does not describe
+   multicast address allocation or the distribution of SDP messages in
+   detail.  These are described in accompanying memos.  SDP is not
+   intended for negotiation of media encodings.
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                     [Page 1]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+2.  Background
+
+   The Mbone is the part of the internet that supports IP multicast, and
+   thus permits efficient many-to-many communication.  It is used
+   extensively for multimedia conferencing.  Such conferences usually
+   have the property that tight coordination of conference membership is
+   not necessary; to receive a conference, a user at an Mbone site only
+   has to know the conference's multicast group address and the UDP
+   ports for the conference data streams.
+
+   Session directories assist the advertisement of conference sessions
+   and communicate the relevant conference setup information to
+   prospective participants.  SDP is designed to convey such information
+   to recipients.  SDP is purely a format for session description - it
+   does not incorporate a transport protocol, and is intended to use
+   different transport protocols as appropriate including the Session
+   Announcement Protocol [4], Session Initiation Protocol [11], Real-
+   Time Streaming Protocol [12], electronic mail using the MIME
+   extensions, and the Hypertext Transport Protocol.
+
+   SDP is intended to be general purpose so that it can be used for a
+   wider range of network environments and applications than just
+   multicast session directories.  However, it is not intended to
+   support negotiation of session content or media encodings - this is
+   viewed as outside the scope of session description.
+
+3.  Glossary of Terms
+
+   The following terms are used in this document, and have specific
+   meaning within the context of this document.
+
+   Conference
+     A multimedia conference is a set of two or more communicating users
+     along with the software they are using to communicate.
+
+   Session
+     A multimedia session is a set of multimedia senders and receivers
+     and the data streams flowing from senders to receivers.  A
+     multimedia conference is an example of a multimedia session.
+
+   Session Advertisement
+     See session announcement.
+
+   Session Announcement
+     A session announcement is a mechanism by which a session
+     description is conveyed to users in a proactive fashion, i.e., the
+     session description was not explicitly requested by the user.
+
+
+
+
+Handley & Jacobson          Standards Track                     [Page 2]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Session Description
+     A well defined format for conveying sufficient information to
+     discover and participate in a multimedia session.
+
+3.1.  Terminology
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in RFC 2119.
+
+4.  SDP Usage
+
+4.1.  Multicast Announcements
+
+   SDP is a session description protocol for multimedia sessions. A
+   common mode of usage is for a client to announce a conference session
+   by periodically multicasting an announcement packet to a well known
+   multicast address and port using the Session Announcement Protocol
+   (SAP).
+
+   SAP packets are UDP packets with the following format:
+
+         |--------------------|
+         | SAP header         |
+         |--------------------|
+         | text payload       |
+         |//////////
+
+
+   The header is the Session Announcement Protocol header.  SAP is
+   described in more detail in a companion memo [4]
+
+   The text payload is an SDP session description, as described in this
+   memo.  The text payload should be no greater than 1 Kbyte in length.
+   If announced by SAP, only one session announcement is permitted in a
+   single packet.
+
+4.2.  Email and WWW Announcements
+
+   Alternative means of conveying session descriptions include
+   electronic mail and the World Wide Web. For both email and WWW
+   distribution, the use of the MIME content type "application/sdp"
+   should be used.  This enables the automatic launching of applications
+   for participation in the session from the WWW client or mail reader
+   in a standard manner.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                     [Page 3]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Note that announcements of multicast sessions made only via email or
+   the World Wide Web (WWW) do not have the property that the receiver
+   of a session announcement can necessarily receive the session because
+   the multicast sessions may be restricted in scope, and access to the
+   WWW server or reception of email is possible outside this scope.  SAP
+   announcements do not suffer from this mismatch.
+
+5.  Requirements and Recommendations
+
+   The purpose of SDP is to convey information about media streams in
+   multimedia sessions to allow the recipients of a session description
+   to participate in the session.  SDP is primarily intended for use in
+   an internetwork, although it is sufficiently general that it can
+   describe conferences in other network environments.
+
+   A multimedia session, for these purposes, is defined as a set of
+   media streams that exist for some duration of time.  Media streams
+   can be many-to-many.  The times during which the session is active
+   need not be continuous.
+
+   Thus far, multicast based sessions on the Internet have differed from
+   many other forms of conferencing in that anyone receiving the traffic
+   can join the session (unless the session traffic is encrypted).  In
+   such an environment, SDP serves two primary purposes.  It is a means
+   to communicate the existence of a session, and is a means to convey
+   sufficient information to enable joining and participating in the
+   session.  In a unicast environment, only the latter purpose is likely
+   to be relevant.
+
+   Thus SDP includes:
+
+   o Session name and purpose
+
+   o Time(s) the session is active
+
+   o The media comprising the session
+
+   o Information to receive those media (addresses, ports, formats and
+     so on)
+
+   As resources necessary to participate in a session may be limited,
+   some additional information may also be desirable:
+
+   o Information about the bandwidth to be used by the conference
+
+   o Contact information for the person responsible for the session
+
+
+
+
+
+Handley & Jacobson          Standards Track                     [Page 4]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   In general, SDP must convey sufficient information to be able to join
+   a session (with the possible exception of encryption keys) and to
+   announce the resources to be used to non-participants that may need
+   to know.
+
+5.1.  Media Information
+
+   SDP includes:
+
+   o The type of media (video, audio, etc)
+
+   o The transport protocol (RTP/UDP/IP, H.320, etc)
+
+   o The format of the media (H.261 video, MPEG video, etc)
+
+   For an IP multicast session, the following are also conveyed:
+
+   o Multicast address for media
+
+   o Transport Port for media
+
+   This address and port are the destination address and destination
+   port of the multicast stream, whether being sent, received, or both.
+
+   For an IP unicast session, the following are conveyed:
+
+   o Remote address for media
+
+   o Transport port for contact address
+
+   The semantics of this address and port depend on the media and
+   transport protocol defined.  By default, this is the remote address
+   and remote port to which data is sent, and the remote address and
+   local port on which to receive data.  However, some media may define
+   to use these to establish a control channel for the actual media
+   flow.
+
+5.2.  Timing Information
+
+   Sessions may either be bounded or unbounded in time. Whether or not
+   they are bounded, they may be only active at specific times.
+
+   SDP can convey:
+
+   o An arbitrary list of start and stop times bounding the session
+
+   o For each bound, repeat times such as "every Wednesday at 10am for
+     one hour"
+
+
+
+Handley & Jacobson          Standards Track                     [Page 5]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   This timing information is globally consistent, irrespective of local
+   time zone or daylight saving time.
+
+5.3.  Private Sessions
+
+   It is possible to create both public sessions and private sessions.
+   Private sessions will typically be conveyed by encrypting the session
+   description to distribute it.  The details of how encryption is
+   performed are dependent on the mechanism used to convey SDP - see [4]
+   for how this is done for session announcements.
+
+   If a session announcement is private it is possible to use that
+   private announcement to convey encryption keys necessary to decode
+   each of the media in a conference, including enough information to
+   know which encryption scheme is used for each media.
+
+5.4.  Obtaining Further Information about a Session
+
+   A session description should convey enough information to decide
+   whether or not to participate in a session.  SDP may include
+   additional pointers in the form of Universal Resources Identifiers
+   (URIs) for more information about the session.
+
+5.5.  Categorisation
+
+   When many session descriptions are being distributed by SAP or any
+   other advertisement mechanism, it may be desirable to filter
+   announcements that are of interest from those that are not.  SDP
+   supports a categorisation mechanism for sessions that is capable of
+   being automated.
+
+5.6.  Internationalization
+
+   The SDP specification recommends the use of the ISO 10646 character
+   sets in the UTF-8 encoding (RFC 2044) to allow many different
+   languages to be represented.  However, to assist in compact
+   representations, SDP also allows other character sets such as ISO
+   8859-1 to be used when desired.  Internationalization only applies to
+   free-text fields (session name and background information), and not
+   to SDP as a whole.
+
+6.  SDP Specification
+
+   SDP session descriptions are entirely textual using the ISO 10646
+   character set in UTF-8 encoding. SDP field names and attributes names
+   use only the US-ASCII subset of UTF-8, but textual fields and
+   attribute values may use the full ISO 10646 character set.  The
+   textual form, as opposed to a binary encoding such as ASN/1 or XDR,
+
+
+
+Handley & Jacobson          Standards Track                     [Page 6]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   was chosen to enhance portability, to enable a variety of transports
+   to be used (e.g, session description in a MIME email message) and to
+   allow flexible, text-based toolkits (e.g., Tcl/Tk ) to be used to
+   generate and to process session descriptions.  However, since the
+   total bandwidth allocated to all SAP announcements is strictly
+   limited, the encoding is deliberately compact.  Also, since
+   announcements may be transported via very unreliable means (e.g.,
+   email) or damaged by an intermediate caching server, the encoding was
+   designed with strict order and formatting rules so that most errors
+   would result in malformed announcements which could be detected
+   easily and discarded. This also allows rapid discarding of encrypted
+   announcements for which a receiver does not have the correct key.
+
+   An SDP session description consists of a number of lines of text of
+   the form <type>=<value> <type> is always exactly one character and is
+   case-significant.  <value> is a structured text string whose format
+   depends on <type>.  It also will be case-significant unless a
+   specific field defines otherwise.  Whitespace is not permitted either
+   side of the `=' sign. In general <value> is either a number of fields
+   delimited by a single space character or a free format string.
+
+   A session description consists of a session-level description
+   (details that apply to the whole session and all media streams) and
+   optionally several media-level descriptions (details that apply onto
+   to a single media stream).
+
+   An announcement consists of a session-level section followed by zero
+   or more media-level sections.  The session-level part starts with a
+   `v=' line and continues to the first media-level section.  The media
+   description starts with an `m=' line and continues to the next media
+   description or end of the whole session description.  In general,
+   session-level values are the default for all media unless overridden
+   by an equivalent media-level value.
+
+   When SDP is conveyed by SAP, only one session description is allowed
+   per packet.  When SDP is conveyed by other means, many SDP session
+   descriptions may be concatenated together (the `v=' line indicating
+   the start of a session description terminates the previous
+   description).  Some lines in each description are required and some
+   are optional but all must appear in exactly the order given here (the
+   fixed order greatly enhances error detection and allows for a simple
+   parser). Optional items are marked with a `*'.
+
+Session description
+        v=  (protocol version)
+        o=  (owner/creator and session identifier).
+        s=  (session name)
+        i=* (session information)
+
+
+
+Handley & Jacobson          Standards Track                     [Page 7]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+        u=* (URI of description)
+        e=* (email address)
+        p=* (phone number)
+        c=* (connection information - not required if included in all media)
+        b=* (bandwidth information)
+        One or more time descriptions (see below)
+        z=* (time zone adjustments)
+        k=* (encryption key)
+        a=* (zero or more session attribute lines)
+        Zero or more media descriptions (see below)
+
+Time description
+        t=  (time the session is active)
+        r=* (zero or more repeat times)
+
+Media description
+        m=  (media name and transport address)
+        i=* (media title)
+        c=* (connection information - optional if included at session-level)
+        b=* (bandwidth information)
+        k=* (encryption key)
+        a=* (zero or more media attribute lines)
+
+   The set of `type' letters is deliberately small and not intended to
+   be extensible -- SDP parsers must completely ignore any announcement
+   that contains a `type' letter that it does not understand. The
+   `attribute' mechanism ("a=" described below) is the primary means for
+   extending SDP and tailoring it to particular applications or media.
+   Some attributes (the ones listed in this document) have a defined
+   meaning but others may be added on an application-, media- or
+   session-specific basis.  A session directory must ignore any
+   attribute it doesn't understand.
+
+   The connection (`c=') and attribute (`a=') information in the
+   session-level section applies to all the media of that session unless
+   overridden by connection information or an attribute of the same name
+   in the media description.  For instance, in the example below, each
+   media behaves as if it were given a `recvonly' attribute.
+
+   An example SDP description is:
+
+        v=0
+        o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4
+        s=SDP Seminar
+        i=A Seminar on the session description protocol
+        u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps
+        e=mjh@isi.edu (Mark Handley)
+        c=IN IP4 224.2.17.12/127
+
+
+
+Handley & Jacobson          Standards Track                     [Page 8]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+        t=2873397496 2873404696
+        a=recvonly
+        m=audio 49170 RTP/AVP 0
+        m=video 51372 RTP/AVP 31
+        m=application 32416 udp wb
+        a=orient:portrait
+
+   Text records such as the session name and information are bytes
+   strings which may contain any byte with the exceptions of 0x00 (Nul),
+   0x0a (ASCII newline) and 0x0d (ASCII carriage return).  The sequence
+   CRLF (0x0d0a) is used to end a record, although parsers should be
+   tolerant and also accept records terminated with a single newline
+   character.  By default these byte strings contain ISO-10646
+   characters in UTF-8 encoding, but this default may be changed using
+   the `charset' attribute.
+
+   Protocol Version
+
+   v=0
+
+   The "v=" field gives the version of the Session Description Protocol.
+   There is no minor version number.
+
+   Origin
+
+   o=<username> <session id> <version> <network type> <address type>
+   <address>
+
+   The "o=" field gives the originator of the session (their username
+   and the address of the user's host) plus a session id and session
+   version number.
+
+   <username> is the user's login on the originating host, or it is "-"
+   if the originating host does not support the concept of user ids.
+   <username> must not contain spaces.  <session id> is a numeric string
+   such that the tuple of <username>, <session id>, <network type>,
+   <address type> and <address> form a globally unique identifier for
+   the session.
+
+   The method of <session id> allocation is up to the creating tool, but
+   it has been suggested that a Network Time Protocol (NTP) timestamp be
+   used to ensure uniqueness [1].
+
+   <version> is a version number for this announcement.  It is needed
+   for proxy announcements to detect which of several announcements for
+   the same session is the most recent.  Again its usage is up to the
+
+
+
+
+
+Handley & Jacobson          Standards Track                     [Page 9]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   creating tool, so long as <version> is increased when a modification
+   is made to the session data.  Again, it is recommended (but not
+   mandatory) that an NTP timestamp is used.
+
+   <network type> is a text string giving the type of network.
+   Initially "IN" is defined to have the meaning "Internet".  <address
+   type> is a text string giving the type of the address that follows.
+   Initially "IP4" and "IP6" are defined.  <address> is the globally
+   unique address of the machine from which the session was created.
+   For an address type of IP4, this is either the fully-qualified domain
+   name of the machine, or the dotted-decimal representation of the IP
+   version 4 address of the machine.  For an address type of IP6, this
+   is either the fully-qualified domain name of the machine, or the
+   compressed textual representation of the IP version 6 address of the
+   machine.  For both IP4 and IP6, the fully-qualified domain name is
+   the form that SHOULD be given unless this is unavailable, in which
+   case the globally unique address may be substituted.  A local IP
+   address MUST NOT be used in any context where the SDP description
+   might leave the scope in which the address is meaningful.
+
+   In general, the "o=" field serves as a globally unique identifier for
+   this version of this session description, and the subfields excepting
+   the version taken together identify the session irrespective of any
+   modifications.
+
+   Session Name
+
+   s=<session name>
+
+   The "s=" field is the session name.  There must be one and only one
+   "s=" field per session description, and it must contain ISO 10646
+   characters (but see also the `charset' attribute below).
+
+   Session and Media Information
+
+   i=<session description>
+
+   The "i=" field is information about the session.  There may be at
+   most one session-level "i=" field per session description, and at
+   most one "i=" field per media. Although it may be omitted, this is
+   discouraged for session announcements, and user interfaces for
+   composing sessions should require text to be entered.  If it is
+   present it must contain ISO 10646 characters (but see also the
+   `charset' attribute below).
+
+   A single "i=" field can also be used for each media definition.  In
+   media definitions, "i=" fields are primarily intended for labeling
+   media streams. As such, they are most likely to be useful when a
+
+
+
+Handley & Jacobson          Standards Track                    [Page 10]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   single session has more than one distinct media stream of the same
+   media type.  An example would be two different whiteboards, one for
+   slides and one for feedback and questions.
+
+   URI
+
+   u=<URI>
+
+   o A URI is a Universal Resource Identifier as used by WWW clients
+
+   o The URI should be a pointer to additional information about the
+     conference
+
+   o This field is optional, but if it is present it should be specified
+     before the first media field
+
+   o No more than one URI field is allowed per session description
+
+
+   Email Address and Phone Number
+
+   e=<email address>
+   p=<phone number>
+
+   o These specify contact information for the person responsible for
+     the conference.  This is not necessarily the same person that
+     created the conference announcement.
+
+   o Either an email field or a phone field must be specified.
+     Additional email and phone fields are allowed.
+
+   o If these are present, they should be specified before the first
+     media field.
+
+   o More than one email or phone field can be given for a session
+     description.
+
+   o Phone numbers should be given in the conventional international
+
+     format - preceded by a "+ and the international country code.
+     There must be a space or a hyphen ("-") between the country code
+     and the rest of the phone number.  Spaces and hyphens may be used
+     to split up a phone field to aid readability if desired. For
+     example:
+
+                   p=+44-171-380-7777 or p=+1 617 253 6011
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 11]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   o Both email addresses and phone numbers can have an optional free
+     text string associated with them, normally giving the name of the
+     person who may be contacted.  This should be enclosed in
+     parenthesis if it is present.  For example:
+
+                        e=mjh@isi.edu (Mark Handley)
+
+     The alternative RFC822 name quoting convention is also allowed for
+     both email addresses and phone numbers.  For example,
+
+                        e=Mark Handley <mjh@isi.edu>
+
+     The free text string should be in the ISO-10646 character set with
+     UTF-8 encoding, or alternatively in ISO-8859-1 or other encodings
+     if the appropriate charset session-level attribute is set.
+
+   Connection Data
+
+   c=<network type> <address type> <connection address>
+
+   The "c=" field contains connection data.
+
+   A session announcement must contain one "c=" field in each media
+   description (see below) or a "c=" field at the session-level.  It may
+   contain a session-level "c=" field and one additional "c=" field per
+   media description, in which case the per-media values override the
+   session-level settings for the relevant media.
+
+   The first sub-field is the network type, which is a text string
+   giving the type of network.  Initially "IN" is defined to have the
+   meaning "Internet".
+
+   The second sub-field is the address type.  This allows SDP to be used
+   for sessions that are not IP based.  Currently only IP4 is defined.
+
+   The third sub-field is the connection address.  Optional extra
+   subfields may be added after the connection address depending on the
+   value of the <address type> field.
+
+   For IP4 addresses, the connection address is defined as follows:
+
+   o Typically the connection address will be a class-D IP multicast
+
+     group address.  If the session is not multicast, then the
+     connection address contains the fully-qualified domain name or the
+     unicast IP address of the expected data source or data relay or
+     data sink as determined by additional attribute fields. It is not
+     expected that fully-qualified domain names or unicast addresses
+
+
+
+Handley & Jacobson          Standards Track                    [Page 12]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     will be given in a session description that is communicated by a
+     multicast announcement, though this is not prohibited.  If a
+     unicast data stream is to pass through a network address
+     translator, the use of a fully-qualified domain name rather than an
+     unicast IP address is RECOMMENDED.  In other cases, the use of an
+     IP address to specify a particular interface on a multi-homed host
+     might be required.  Thus this specification leaves the decision as
+     to which to use up to the individual application, but all
+     applications MUST be able to cope with receiving both formats.
+
+   o Conferences using an IP multicast connection address must also have
+     a time to live (TTL) value present in addition to the multicast
+     address.  The TTL and the address together define the scope with
+     which multicast packets sent in this conference will be sent. TTL
+     values must be in the range 0-255.
+
+     The TTL for the session is appended to the address using a slash as
+     a separator.  An example is:
+
+                           c=IN IP4 224.2.1.1/127
+
+     Hierarchical or layered encoding schemes are data streams where the
+     encoding from a single media source is split into a number of
+     layers.  The receiver can choose the desired quality (and hence
+     bandwidth) by only subscribing to a subset of these layers.  Such
+     layered encodings are normally transmitted in multiple multicast
+     groups to allow multicast pruning.  This technique keeps unwanted
+     traffic from sites only requiring certain levels of the hierarchy.
+     For applications requiring multiple multicast groups, we allow the
+     following notation to be used for the connection address:
+
+            <base multicast address>/<ttl>/<number of addresses>
+
+     If the number of addresses is not given it is assumed to be one.
+     Multicast addresses so assigned are contiguously allocated above
+     the base address, so that, for example:
+
+                          c=IN IP4 224.2.1.1/127/3
+
+     would state that addresses 224.2.1.1, 224.2.1.2 and 224.2.1.3 are
+     to be used at a ttl of 127.  This is semantically identical to
+     including multiple "c=" lines in a media description:
+
+                           c=IN IP4 224.2.1.1/127
+                           c=IN IP4 224.2.1.2/127
+                           c=IN IP4 224.2.1.3/127
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 13]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     Multiple addresses or "c=" lines can only be specified on a per-
+     media basis, and not for a session-level "c=" field.
+
+     It is illegal for the slash notation described above to be used for
+     IP unicast addresses.
+
+   Bandwidth
+
+   b=<modifier>:<bandwidth-value>
+
+   o This specifies the proposed bandwidth to be used by the session or
+     media, and is optional.
+
+   o <bandwidth-value> is in kilobits per second
+
+   o <modifier> is a single alphanumeric word giving the meaning of the
+     bandwidth figure.
+
+   o Two modifiers are initially defined:
+
+   CT Conference Total: An implicit maximum bandwidth is associated with
+     each TTL on the Mbone or within a particular multicast
+     administrative scope region (the Mbone bandwidth vs. TTL limits are
+     given in the MBone FAQ). If the bandwidth of a session or media in
+     a session is different from the bandwidth implicit from the scope,
+     a `b=CT:...' line should be supplied for the session giving the
+     proposed upper limit to the bandwidth used. The primary purpose of
+     this is to give an approximate idea as to whether two or more
+     conferences can co-exist simultaneously.
+
+   AS Application-Specific Maximum: The bandwidth is interpreted to be
+     application-specific, i.e., will be the application's concept of
+     maximum bandwidth.  Normally this will coincide with what is set on
+     the application's "maximum bandwidth" control if applicable.
+
+     Note that CT gives a total bandwidth figure for all the media at
+     all sites.  AS gives a bandwidth figure for a single media at a
+     single site, although there may be many sites sending
+     simultaneously.
+
+   o Extension Mechanism: Tool writers can define experimental bandwidth
+     modifiers by prefixing their modifier with "X-". For example:
+
+                                 b=X-YZ:128
+
+     SDP parsers should ignore bandwidth fields with unknown modifiers.
+     Modifiers should be alpha-numeric and, although no length limit is
+     given, they are recommended to be short.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 14]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Times, Repeat Times and Time Zones
+
+   t=<start time>  <stop time>
+
+   o "t=" fields specify the start and stop times for a conference
+     session.  Multiple "t=" fields may be used if a session is active
+     at multiple irregularly spaced times; each additional "t=" field
+     specifies an additional period of time for which the session will
+     be active.  If the session is active at regular times, an "r="
+     field (see below) should be used in addition to and following a
+     "t=" field - in which case the "t=" field specifies the start and
+     stop times of the repeat sequence.
+
+   o The first and second sub-fields give the start and stop times for
+     the conference respectively.  These values are the decimal
+     representation of Network Time Protocol (NTP) time values in
+     seconds [1].  To convert these values to UNIX time, subtract
+     decimal 2208988800.
+
+   o If the stop-time is set to zero, then the session is not bounded,
+     though it will not become active until after the start-time.  If
+     the start-time is also zero, the session is regarded as permanent.
+
+     User interfaces should strongly discourage the creation of
+     unbounded and permanent sessions as they give no information about
+     when the session is actually going to terminate, and so make
+     scheduling difficult.
+
+     The general assumption may be made, when displaying unbounded
+     sessions that have not timed out to the user, that an unbounded
+     session will only be active until half an hour from the current
+     time or the session start time, whichever is the later.  If
+     behaviour other than this is required, an end-time should be given
+     and modified as appropriate when new information becomes available
+     about when the session should really end.
+
+     Permanent sessions may be shown to the user as never being active
+     unless there are associated repeat times which state precisely when
+     the session will be active.  In general, permanent sessions should
+     not be created for any session expected to have a duration of less
+     than 2 months, and should be discouraged for sessions expected to
+     have a duration of less than 6 months.
+
+     r=<repeat interval> <active duration> <list of offsets from start-
+     time>
+
+   o "r=" fields specify repeat times for a session.  For example, if
+     a session is active at 10am on Monday and 11am on Tuesday for one
+
+
+
+Handley & Jacobson          Standards Track                    [Page 15]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     hour each week for three months, then the <start time> in the
+     corresponding "t=" field would be the NTP representation of 10am on
+     the first Monday, the <repeat interval> would be 1 week, the
+     <active duration> would be 1 hour, and the offsets would be zero
+     and 25 hours. The corresponding "t=" field stop time would be the
+     NTP representation of the end of the last session three months
+     later. By default all fields are in seconds, so the "r=" and "t="
+     fields might be:
+
+                           t=3034423619 3042462419
+                            r=604800 3600 0 90000
+
+    To make announcements more compact, times may also be given in units
+    of days, hours or minutes. The syntax for these is a number
+    immediately followed by a single case-sensitive character.
+    Fractional units are not allowed - a smaller unit should be used
+    instead.  The following unit specification characters are allowed:
+
+                         d - days (86400 seconds)
+                        h - minutes (3600 seconds)
+                         m - minutes (60 seconds)
+         s - seconds (allowed for completeness but not recommended)
+
+   Thus, the above announcement could also have been written:
+
+                               r=7d 1h 0 25h
+
+     Monthly and yearly repeats cannot currently be directly specified
+     with a single SDP repeat time - instead separate "t" fields should
+     be used to explicitly list the session times.
+
+        z=<adjustment time> <offset> <adjustment time> <offset> ....
+
+   o To schedule a repeated session which spans a change from daylight-
+     saving time to standard time or vice-versa, it is necessary to
+     specify offsets from the base repeat times. This is required
+     because different time zones change time at different times of day,
+     different countries change to or from daylight time on different
+     dates, and some countries do not have daylight saving time at all.
+
+     Thus in order to schedule a session that is at the same time winter
+     and summer, it must be possible to specify unambiguously by whose
+     time zone a session is scheduled.  To simplify this task for
+     receivers, we allow the sender to specify the NTP time that a time
+     zone adjustment happens and the offset from the time when the
+     session was first scheduled.  The "z" field allows the sender to
+     specify a list of these adjustment times and offsets from the base
+     time.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 16]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     An example might be:
+
+                        z=2882844526 -1h 2898848070 0
+
+     This specifies that at time 2882844526 the time base by which the
+     session's repeat times are calculated is shifted back by 1 hour,
+     and that at time 2898848070 the session's original time base is
+     restored. Adjustments are always relative to the specified start
+     time - they are not cumulative.
+
+   o    If a session is likely to last several years, it is  expected
+   that
+     the session announcement will be modified periodically rather than
+     transmit several years worth of adjustments in one announcement.
+
+   Encryption Keys
+
+   k=<method>
+   k=<method>:<encryption key>
+
+   o The session description protocol may be used to convey encryption
+     keys.  A key field is permitted before the first media entry (in
+     which case it applies to all media in the session), or for each
+     media entry as required.
+
+   o The format of keys and their usage is outside the scope of this
+     document, but see [3].
+
+   o The method indicates the mechanism to be used to obtain a usable
+     key by external means, or from the encoded encryption key given.
+
+     The following methods are defined:
+
+      k=clear:<encryption key>
+        The encryption key (as described in [3] for  RTP  media  streams
+        under  the  AV  profile)  is  included untransformed in this key
+        field.
+
+      k=base64:<encoded encryption key>
+        The encryption key (as described in [3] for RTP media streams
+        under the AV profile) is included in this key field but has been
+        base64 encoded because it includes characters that are
+        prohibited in SDP.
+
+      k=uri:<URI to obtain key>
+        A Universal Resource Identifier as used by WWW clients is
+        included in this key field.  The URI refers to the data
+        containing the key, and may require additional authentication
+
+
+
+Handley & Jacobson          Standards Track                    [Page 17]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+        before the key can be returned.  When a request is made to the
+        given URI, the MIME content-type of the reply specifies the
+        encoding for the key in the reply.  The key should not be
+        obtained until the user wishes to join the session to reduce
+        synchronisation of requests to the WWW server(s).
+
+      k=prompt
+        No key is included in this SDP description, but the session or
+        media stream referred to by this key field is encrypted.  The
+        user should be prompted for the key when attempting to join the
+        session, and this user-supplied key should then be used to
+        decrypt the media streams.
+
+   Attributes
+
+   a=<attribute>
+   a=<attribute>:<value>
+
+   Attributes are the primary means for extending SDP.  Attributes may
+   be defined to be used as "session-level" attributes, "media-level"
+   attributes, or both.
+
+   A media description may have any number of attributes ("a=" fields)
+   which are media specific.  These are referred to as "media-level"
+   attributes and add information about the media stream.  Attribute
+   fields can also be added before the first media field; these
+   "session-level" attributes convey additional information that applies
+   to the conference as a whole rather than to individual media; an
+   example might be the conference's floor control policy.
+
+   Attribute fields may be of two forms:
+
+   o property attributes.  A property attribute is simply of the form
+     "a=<flag>".  These are binary attributes, and the presence of the
+     attribute conveys that the attribute is a property of the session.
+     An example might be "a=recvonly".
+
+   o value attributes.  A value attribute is of the form
+     "a=<attribute>:<value>".  An example might be that a whiteboard
+     could have the value attribute "a=orient:landscape"
+
+   Attribute interpretation depends on the media tool being invoked.
+   Thus receivers of session descriptions should be configurable in
+   their interpretation of announcements in general and of attributes in
+   particular.
+
+   Attribute names must be in the US-ASCII subset of ISO-10646/UTF-8.
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 18]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Attribute values are byte strings, and MAY use any byte value except
+   0x00 (Nul), 0x0A (LF), and 0x0D (CR). By default, attribute values
+   are to be interpreted as in ISO-10646 character set with UTF-8
+   encoding.  Unlike other text fields, attribute values are NOT
+   normally affected by the `charset' attribute as this would make
+   comparisons against known values problematic.  However, when an
+   attribute is defined, it can be defined to be charset-dependent, in
+   which case it's value should be interpreted in the session charset
+   rather than in ISO-10646.
+
+   Attributes that will be commonly used can be registered with IANA
+   (see Appendix B).  Unregistered attributes should begin with "X-" to
+   prevent inadvertent collision with registered attributes.  In either
+   case, if an attribute is received that is not understood, it should
+   simply be ignored by the receiver.
+
+   Media Announcements
+
+   m=<media> <port> <transport> <fmt list>
+
+   A session description may contain a number of media descriptions.
+   Each media description starts with an "m=" field, and is terminated
+   by either the next "m=" field or by the end of the session
+   description.  A media field also has several sub-fields:
+
+   o The first sub-field is the media type.  Currently defined media are
+     "audio", "video", "application", "data" and "control", though this
+     list may be extended as new communication modalities emerge (e.g.,
+     telepresense).  The difference between "application" and "data" is
+     that the former is a media flow such as whiteboard information, and
+     the latter is bulk-data transfer such as multicasting of program
+     executables which will not typically be displayed to the user.
+     "control" is used to specify an additional conference control
+     channel for the session.
+
+   o The second sub-field is the transport port to which the media
+     stream will be sent.  The meaning of the transport port depends on
+     the network being used as specified in the relevant "c" field and
+     on the transport protocol defined in the third sub-field.  Other
+     ports used by the media application (such as the RTCP port, see
+     [2]) should be derived algorithmically from the base media port.
+
+     Note: For transports based on UDP, the value should be in the range
+     1024 to 65535 inclusive.  For RTP compliance it should be an even
+     number.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 19]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     For applications where hierarchically encoded streams are being
+     sent to a unicast address, it may be necessary to specify multiple
+     transport ports.  This is done using a similar notation to that
+     used for IP multicast addresses in the "c=" field:
+
+          m=<media> <port>/<number of ports> <transport> <fmt list>
+
+     In such a case, the ports used depend on the transport protocol.
+     For RTP, only the even ports are used for data and the
+     corresponding one-higher odd port is used for RTCP.  For example:
+
+                         m=video 49170/2 RTP/AVP 31
+
+     would specify that ports 49170 and 49171 form one RTP/RTCP pair and
+     49172 and 49173 form the second RTP/RTCP pair.  RTP/AVP is the
+     transport protocol and 31 is the format (see below).
+
+     It is illegal for both multiple addresses to be specified in the
+     "c=" field and for multiple ports to be specified in the "m=" field
+     in the same session description.
+
+   o The third sub-field is the transport protocol.  The transport
+     protocol values are dependent on the address-type field in the "c="
+     fields.  Thus a "c=" field of IP4 defines that the transport
+     protocol runs over IP4.  For IP4, it is normally expected that most
+     media traffic will be carried as RTP over UDP.  The following
+     transport protocols are preliminarily defined, but may be extended
+     through registration of new protocols with IANA:
+
+     - RTP/AVP - the IETF's Realtime Transport Protocol using the
+       Audio/Video profile carried over UDP.
+
+     - udp - User Datagram Protocol
+
+     If an application uses a single combined proprietary media format
+     and transport protocol over UDP, then simply specifying the
+     transport protocol as udp and using the format field to distinguish
+     the combined protocol is recommended.  If a transport protocol is
+     used over UDP to carry several distinct media types that need to be
+     distinguished by a session directory, then specifying the transport
+     protocol and media format separately is necessary. RTP is an
+     example of a transport-protocol that carries multiple payload
+     formats that must be distinguished by the session directory for it
+     to know how to start appropriate tools, relays, mixers or
+     recorders.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 20]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     The main reason to specify the transport-protocol in addition to
+     the media format is that the same standard media formats may be
+     carried over different transport protocols even when the network
+     protocol is the same - a historical example is vat PCM audio and
+     RTP PCM audio.  In addition, relays and monitoring tools that are
+     transport-protocol-specific but format-independent are possible.
+
+     For RTP media streams operating under the RTP Audio/Video Profile
+     [3], the protocol field is "RTP/AVP".  Should other RTP profiles be
+     defined in the future, their profiles will be specified in the same
+     way.  For example, the protocol field "RTP/XYZ" would specify RTP
+     operating under a profile whose short name is "XYZ".
+
+   o The fourth and subsequent sub-fields are media formats.  For audio
+     and video, these will normally be a media payload type as defined
+     in the RTP Audio/Video Profile.
+
+     When a list of payload formats is given, this implies that all of
+     these formats may be used in the session, but the first of these
+     formats is the default format for the session.
+
+     For media whose transport protocol is not RTP or UDP the format
+     field is protocol specific.  Such formats should be defined in an
+     additional specification document.
+
+     For media whose transport protocol is RTP, SDP can be used to
+     provide a dynamic binding of media encoding to RTP payload type.
+     The encoding names in the RTP AV Profile do not specify unique
+     audio encodings (in terms of clock rate and number of audio
+     channels), and so they are not used directly in SDP format fields.
+     Instead, the payload type number should be used to specify the
+     format for static payload types and the payload type number along
+     with additional encoding information should be used for dynamically
+     allocated payload types.
+
+     An example of a static payload type is u-law PCM coded single
+     channel audio sampled at 8KHz.  This is completely defined in the
+     RTP Audio/Video profile as payload type 0, so the media field for
+     such a stream sent to UDP port 49232 is:
+
+                           m=video 49232 RTP/AVP 0
+
+     An example of a dynamic payload type is 16 bit linear encoded
+     stereo audio sampled at 16KHz.  If we wish to use dynamic RTP/AVP
+     payload type 98 for such a stream, additional information is
+     required to decode it:
+
+                          m=video 49232 RTP/AVP 98
+
+
+
+Handley & Jacobson          Standards Track                    [Page 21]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+                           a=rtpmap:98 L16/16000/2
+
+     The general form of an rtpmap attribute is:
+
+     a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding
+     parameters>]
+
+     For audio streams, <encoding parameters> may specify the number of
+     audio channels.  This parameter may be omitted if the number of
+     channels is one provided no additional parameters are needed.  For
+     video streams, no encoding parameters are currently specified.
+
+     Additional parameters may be defined in the future, but
+     codecspecific parameters should not be added.  Parameters added to
+     an rtpmap attribute should only be those required for a session
+     directory to make the choice of appropriate media too to
+     participate in a session.  Codec-specific parameters should be
+     added in other attributes.
+
+     Up to one rtpmap attribute can be defined for each media format
+     specified. Thus we might have:
+
+                       m=audio 49230 RTP/AVP 96 97 98
+                             a=rtpmap:96 L8/8000
+                            a=rtpmap:97 L16/8000
+                           a=rtpmap:98 L16/11025/2
+
+     RTP profiles that specify the use of dynamic payload types must
+     define the set of valid encoding names and/or a means to register
+     encoding names if that profile is to be used with SDP.
+
+     Experimental encoding formats can also be specified using rtpmap.
+     RTP formats that are not registered as standard format names must
+     be preceded by "X-".  Thus a new experimental redundant audio
+     stream called GSMLPC using dynamic payload type 99 could be
+     specified as:
+
+                          m=video 49232 RTP/AVP 99
+                          a=rtpmap:99 X-GSMLPC/8000
+
+     Such an experimental encoding requires that any site wishing to
+     receive the media stream has relevant configured state in its
+     session directory to know which tools are appropriate.
+
+     Note that RTP audio formats typically do not include information
+     about the number of samples per packet.  If a non-default (as
+     defined in the RTP Audio/Video Profile) packetisation is required,
+     the "ptime" attribute is used as given below.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 22]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     For more details on RTP audio and video formats, see [3].
+
+   o Formats for non-RTP media should be registered as MIME content
+     types as described in Appendix B.  For example, the LBL whiteboard
+     application might be registered as MIME content-type application/wb
+     with encoding considerations specifying that it operates over UDP,
+     with no appropriate file format.  In SDP this would then be
+     expressed using a combination of the "media" field and the "fmt"
+     field, as follows:
+
+                         m=application 32416 udp wb
+
+   Suggested Attributes
+
+   The following attributes are suggested.  Since application writers
+   may add new attributes as they are required, this list is not
+   exhaustive.
+
+   a=cat:<category>
+       This attribute gives the dot-separated hierarchical category of
+       the session.  This is to enable a receiver to filter unwanted
+       sessions by category.  It would probably have been a compulsory
+       separate field, except for its experimental nature at this time.
+       It is a session-level attribute, and is not dependent on charset.
+
+   a=keywds:<keywords>
+       Like the cat attribute, this is to assist identifying wanted
+       sessions at the receiver.  This allows a receiver to select
+       interesting session based on keywords describing the purpose of
+       the session.  It is a session-level attribute. It is a charset
+       dependent attribute, meaning that its value should be interpreted
+       in the charset specified for the session description if one is
+       specified, or by default in ISO 10646/UTF-8.
+
+   a=tool:<name and version of tool>
+       This gives the name and version number of the tool used to create
+       the session description.  It is a session-level attribute, and is
+       not dependent on charset.
+
+   a=ptime:<packet time>
+       This gives the length of time in milliseconds represented by the
+       media in a packet. This is probably only meaningful for audio
+       data.  It should not be necessary to know ptime to decode RTP or
+       vat audio, and it is intended as a recommendation for the
+       encoding/packetisation of audio.  It is a media attribute, and is
+       not dependent on charset.
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 23]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   a=recvonly
+       This specifies that the tools should be started in receive-only
+       mode where applicable. It can be either a session or media
+       attribute, and is not dependent on charset.
+
+   a=sendrecv
+       This specifies that the tools should be started in send and
+       receive mode.  This is necessary for interactive conferences with
+       tools such as wb which defaults to receive only mode. It can be
+       either a session or media attribute, and is not dependent on
+       charset.
+
+   a=sendonly
+       This specifies that the tools should be started in send-only
+       mode.  An example may be where a different unicast address is to
+       be used for a traffic destination than for a traffic source. In
+       such a case, two media descriptions may be use, one sendonly and
+       one recvonly. It can be either a session or media attribute, but
+       would normally only be used as a media attribute, and is not
+       dependent on charset.
+
+   a=orient:<whiteboard orientation>
+       Normally this is only used in a whiteboard media specification.
+       It specifies the orientation of a the whiteboard on the screen.
+       It is a media attribute. Permitted values are `portrait',
+       `landscape' and `seascape' (upside down landscape). It is not
+       dependent on charset
+
+   a=type:<conference type>
+       This specifies the type of the conference.  Suggested values are
+       `broadcast', `meeting', `moderated', `test' and `H332'.
+       `recvonly' should be the default for `type:broadcast' sessions,
+       `type:meeting' should imply `sendrecv' and `type:moderated'
+       should indicate the use of a floor control tool and that the
+       media tools are started so as to "mute" new sites joining the
+       conference.
+
+       Specifying the attribute type:H332 indicates that this loosely
+       coupled session is part of a H.332 session as defined in the ITU
+       H.332 specification [10].  Media tools should be started
+       `recvonly'.
+
+       Specifying the attribute type:test is suggested as a hint that,
+       unless explicitly requested otherwise, receivers can safely avoid
+       displaying this session description to users.
+
+       The type attribute is a session-level attribute, and is not
+       dependent on charset.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 24]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   a=charset:<character set>
+       This specifies the character set to be used to display the
+       session name and information data.  By default, the ISO-10646
+       character set in UTF-8 encoding is used. If a more compact
+       representation is required, other character sets may be used such
+       as ISO-8859-1 for Northern European languages.  In particular,
+       the ISO 8859-1 is specified with the following SDP attribute:
+
+                             a=charset:ISO-8859-1
+
+       This is a session-level attribute; if this attribute is present,
+       it must be before the first media field.  The charset specified
+       MUST be one of those registered with IANA, such as ISO-8859-1.
+       The character set identifier is a US-ASCII string and MUST be
+       compared against the IANA identifiers using a case-insensitive
+       comparison.  If the identifier is not recognised or not
+       supported, all strings that are affected by it SHOULD be regarded
+       as byte strings.
+
+       Note that a character set specified MUST still prohibit the use
+       of bytes 0x00 (Nul), 0x0A (LF) and 0x0d (CR). Character sets
+       requiring the use of these characters MUST define a quoting
+       mechanism that prevents these bytes appearing within text fields.
+
+   a=sdplang:<language tag>
+       This can be a session level attribute or a media level attribute.
+       As a session level attribute, it specifies the language for the
+       session description.  As a media level attribute, it specifies
+       the language for any media-level SDP information field associated
+       with that media.  Multiple sdplang attributes can be provided
+       either at session or media level if multiple languages in the
+       session description or media use multiple languages, in which
+       case the order of the attributes indicates the order of
+       importance of the various languages in the session or media from
+       most important to least important.
+
+       In general, sending session descriptions consisting of multiple
+       languages should be discouraged.  Instead, multiple descriptions
+       should be sent describing the session, one in each language.
+       However this is not possible with all transport mechanisms, and
+       so multiple sdplang attributes are allowed although not
+       recommended.
+
+       The sdplang attribute value must be a single RFC 1766 language
+       tag in US-ASCII.  It is not dependent on the charset attribute.
+       An sdplang attribute SHOULD be specified when a session is of
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 25]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+       sufficient scope to cross geographic boundaries where the
+       language of recipients cannot be assumed, or where the session is
+       in a different language from the locally assumed norm.
+
+   a=lang:<language tag>
+       This can be a session level attribute or a media level attribute.
+       As a session level attribute, it specifies the default language
+       for the session being described.  As a media level attribute, it
+       specifies the language for that media, overriding any session-
+       level language specified.  Multiple lang attributes can be
+       provided either at session or media level if multiple languages
+       if the session description or media use multiple languages, in
+       which case the order of the attributes indicates the order of
+       importance of the various languages in the session or media from
+       most important to least important.
+
+       The lang attribute value must be a single RFC 1766 language tag
+       in US-ASCII. It is not dependent on the charset attribute.  A
+       lang attribute SHOULD be specified when a session is of
+       sufficient scope to cross geographic boundaries where the
+       language of recipients cannot be assumed, or where the session is
+       in a different language from the locally assumed norm.
+
+   a=framerate:<frame rate>
+       This gives the maximum video frame rate in frames/sec.  It is
+       intended as a recommendation for the encoding of video data.
+       Decimal representations of fractional values using the notation
+       "<integer>.<fraction>" are allowed.  It is a media attribute, is
+       only defined for video media, and is not dependent on charset.
+
+   a=quality:<quality>
+       This gives a suggestion for the quality of the encoding as an
+       integer value.
+
+       The intention of the quality attribute for video is to specify a
+       non-default trade-off between frame-rate and still-image quality.
+       For video, the value in the range 0 to 10, with the following
+       suggested meaning:
+
+       10 - the best still-image quality the compression scheme can
+       give.
+
+       5 - the default behaviour given no quality suggestion.
+
+       0 - the worst still-image quality the codec designer thinks is
+           still usable.
+
+       It is a media attribute, and is not dependent on charset.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 26]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   a=fmtp:<format> <format specific parameters>
+       This attribute allows parameters that are specific to a
+       particular format to be conveyed in a way that SDP doesn't have
+       to understand them.  The format must be one of the formats
+       specified for the media.  Format-specific parameters may be any
+       set of parameters required to be conveyed by SDP and given
+       unchanged to the media tool that will use this format.
+
+       It is a media attribute, and is not dependent on charset.
+
+6.1.  Communicating Conference Control Policy
+
+   There is some debate over the way conference control policy should be
+   communicated.  In general, the authors believe that an implicit
+   declarative style of specifying conference control is desirable where
+   possible.
+
+   A simple declarative style uses a single conference attribute field
+   before the first media field, possibly supplemented by properties
+   such as `recvonly' for some of the media tools.  This conference
+   attribute conveys the conference control policy. An example might be:
+
+                             a=type:moderated
+
+   In some cases, however, it is possible that this may be insufficient
+   to communicate the details of an unusual conference control policy.
+   If this is the case, then a conference attribute specifying external
+   control might be set, and then one or more "media" fields might be
+   used to specify the conference control tools and configuration data
+   for those tools. An example is an ITU H.332 session:
+
+                c=IN IP4 224.5.6.7
+                a=type:H332
+                m=audio 49230 RTP/AVP 0
+                m=video 49232 RTP/AVP 31
+                m=application 12349 udp wb
+                m=control 49234 H323 mc
+                c=IN IP4 134.134.157.81
+
+   In this example, a general conference attribute (type:H332) is
+   specified stating that conference control will be provided by an
+   external H.332 tool, and a contact addresses for the H.323 session
+   multipoint controller is given.
+
+   In this document, only the declarative style of conference control
+   declaration is specified.  Other forms of conference control should
+   specify an appropriate type attribute, and should define the
+   implications this has for control media.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 27]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+7.  Security Considerations
+
+   SDP is a session description format that describes multimedia
+   sessions.  A session description should not be trusted unless it has
+   been obtained by an authenticated transport protocol from a trusted
+   source.  Many different transport protocols may be used to distribute
+   session description, and the nature of the authentication will differ
+   from transport to transport.
+
+   One transport that will frequently be used to distribute session
+   descriptions is the Session Announcement Protocol (SAP).  SAP
+   provides both encryption and authentication mechanisms but due to the
+   nature of session announcements it is likely that there are many
+   occasions where the originator of a session announcement cannot be
+   authenticated because they are previously unknown to the receiver of
+   the announcement and because no common public key infrastructure is
+   available.
+
+   On receiving a session description over an unauthenticated transport
+   mechanism or from an untrusted party, software parsing the session
+   should take a few precautions. Session description contain
+   information required to start software on the receivers system.
+   Software that parses a session description MUST not be able to start
+   other software except that which is specifically configured as
+   appropriate software to participate in multimedia sessions.  It is
+   normally considered INAPPROPRIATE for software parsing a session
+   description to start, on a user's system, software that is
+   appropriate to participate in multimedia sessions, without the user
+   first being informed that such software will be started and giving
+   their consent.  Thus a session description arriving by session
+   announcement, email, session invitation, or WWW page SHOULD not
+   deliver the user into an {it interactive} multimedia session without
+   the user being aware that this will happen.  As it is not always
+   simple to tell whether a session is interactive or not, applications
+   that are unsure should assume sessions are interactive.
+
+   In this specification, there are no attributes which would allow the
+   recipient of a session description to be informed to start multimedia
+   tools in a mode where they default to transmitting.  Under some
+   circumstances it might be appropriate to define such attributes.  If
+   this is done an application parsing a session description containing
+   such attributes SHOULD either ignore them, or inform the user that
+   joining this session will result in the automatic transmission of
+   multimedia data.  The default behaviour for an unknown attribute is
+   to ignore it.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 28]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Session descriptions may be parsed at intermediate systems such as
+   firewalls for the purposes of opening a hole in the firewall to allow
+   the participation in multimedia sessions.  It is considered
+   INAPPROPRIATE for a firewall to open such holes for unicast data
+   streams unless the session description comes in a request from inside
+   the firewall.
+
+   For multicast sessions, it is likely that local administrators will
+   apply their own policies, but the exclusive use of "local" or "site-
+   local" administrative scope within the firewall and the refusal of
+   the firewall to open a hole for such scopes will provide separation
+   of global multicast sessions from local ones.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 29]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+Appendix A: SDP Grammar
+
+   This appendix provides an Augmented BNF grammar for SDP. ABNF is
+   defined in RFC 2234.
+
+
+   announcement =        proto-version
+                         origin-field
+                         session-name-field
+                         information-field
+                         uri-field
+                         email-fields
+                         phone-fields
+                         connection-field
+                         bandwidth-fields
+                         time-fields
+                         key-field
+                         attribute-fields
+                         media-descriptions
+
+   proto-version =       "v=" 1*DIGIT CRLF
+                         ;this memo describes version 0
+
+   origin-field =        "o=" username space
+                         sess-id space sess-version space
+                         nettype space addrtype space
+                         addr CRLF
+
+   session-name-field =  "s=" text CRLF
+
+   information-field =   ["i=" text CRLF]
+
+   uri-field =           ["u=" uri CRLF]
+
+   email-fields =        *("e=" email-address CRLF)
+
+   phone-fields =        *("p=" phone-number CRLF)
+
+
+   connection-field =    ["c=" nettype space addrtype space
+                         connection-address CRLF]
+                         ;a connection field must be present
+                         ;in every media description or at the
+                         ;session-level
+
+
+   bandwidth-fields =    *("b=" bwtype ":" bandwidth CRLF)
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 30]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   time-fields =         1*( "t=" start-time space stop-time
+                         *(CRLF repeat-fields) CRLF)
+                         [zone-adjustments CRLF]
+
+
+   repeat-fields =       "r=" repeat-interval space typed-time
+                         1*(space typed-time)
+
+
+   zone-adjustments =    time space ["-"] typed-time
+                         *(space time space ["-"] typed-time)
+
+
+   key-field =           ["k=" key-type CRLF]
+
+
+   key-type =            "prompt" |
+                         "clear:" key-data |
+                         "base64:" key-data |
+                         "uri:" uri
+
+
+   key-data =            email-safe | "~" | "
+
+
+   attribute-fields =    *("a=" attribute CRLF)
+
+
+   media-descriptions =  *( media-field
+                         information-field
+                         *(connection-field)
+                         bandwidth-fields
+                         key-field
+                         attribute-fields )
+
+
+   media-field =         "m=" media space port ["/" integer]
+                         space proto 1*(space fmt) CRLF
+
+
+   media =               1*(alpha-numeric)
+                         ;typically "audio", "video", "application"
+                         ;or "data"
+
+   fmt =                 1*(alpha-numeric)
+                         ;typically an RTP payload type for audio
+                         ;and video media
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 31]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   proto =               1*(alpha-numeric)
+                         ;typically "RTP/AVP" or "udp" for IP4
+
+
+   port =                1*(DIGIT)
+                         ;should in the range "1024" to "65535" inclusive
+                         ;for UDP based media
+
+
+   attribute =           (att-field ":" att-value) | att-field
+
+
+   att-field =           1*(alpha-numeric)
+
+
+   att-value =           byte-string
+
+
+   sess-id =             1*(DIGIT)
+                         ;should be unique for this originating username/host
+
+
+   sess-version =        1*(DIGIT)
+                         ;0 is a new session
+
+
+   connection-address =  multicast-address
+                         | addr
+
+
+   multicast-address =   3*(decimal-uchar ".") decimal-uchar "/" ttl
+                         [ "/" integer ]
+                         ;multicast addresses may be in the range
+                         ;224.0.0.0 to 239.255.255.255
+
+   ttl =                 decimal-uchar
+
+   start-time =          time | "0"
+
+   stop-time =           time | "0"
+
+   time =                POS-DIGIT 9*(DIGIT)
+                         ;sufficient for 2 more centuries
+
+
+   repeat-interval =     typed-time
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 32]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   typed-time =          1*(DIGIT) [fixed-len-time-unit]
+
+
+   fixed-len-time-unit = "d" | "h" | "m" | "s"
+
+
+   bwtype =              1*(alpha-numeric)
+
+   bandwidth =           1*(DIGIT)
+
+
+   username =            safe
+                         ;pretty wide definition, but doesn't include space
+
+
+   email-address =       email | email "(" email-safe ")" |
+                         email-safe "<" email ">"
+
+
+   email =               ;defined in RFC822
+
+
+   uri=                  ;defined in RFC1630
+
+
+   phone-number =        phone | phone "(" email-safe ")" |
+                         email-safe "<" phone ">"
+
+
+   phone =               "+" POS-DIGIT 1*(space | "-" | DIGIT)
+                         ;there must be a space or hyphen between the
+                         ;international code and the rest of the number.
+
+
+   nettype =             "IN"
+                         ;list to be extended
+
+
+   addrtype =            "IP4" | "IP6"
+                         ;list to be extended
+
+
+   addr =                FQDN | unicast-address
+
+
+   FQDN =                4*(alpha-numeric|"-"|".")
+                         ;fully qualified domain name as specified in RFC1035
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 33]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   unicast-address =     IP4-address | IP6-address
+
+
+   IP4-address =         b1 "." decimal-uchar "." decimal-uchar "." b4
+   b1 =                  decimal-uchar
+                         ;less than "224"; not "0" or "127"
+   b4 =                  decimal-uchar
+                         ;not "0"
+
+   IP6-address =         ;to be defined
+
+
+   text =                byte-string
+                         ;default is to interpret this as IS0-10646 UTF8
+                         ;ISO 8859-1 requires a "a=charset:ISO-8859-1"
+                         ;session-level attribute to be used
+
+
+   byte-string =         1*(0x01..0x09|0x0b|0x0c|0x0e..0xff)
+                         ;any byte except NUL, CR or LF
+
+
+   decimal-uchar =       DIGIT
+                         | POS-DIGIT DIGIT
+                         | ("1" 2*(DIGIT))
+                         | ("2" ("0"|"1"|"2"|"3"|"4") DIGIT)
+                         | ("2" "5" ("0"|"1"|"2"|"3"|"4"|"5"))
+
+
+   integer =             POS-DIGIT *(DIGIT)
+
+
+   alpha-numeric =       ALPHA | DIGIT
+
+
+   DIGIT =               "0" | POS-DIGIT
+
+
+   POS-DIGIT =           "1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
+
+
+   ALPHA =               "a"|"b"|"c"|"d"|"e"|"f"|"g"|"h"|"i"|"j"|"k"|
+                         "l"|"m"|"n"|"o "|"p"|"q"|"r"|"s"|"t"|"u"|"v"|
+                         "w"|"x"|"y"|"z"|"A"|"B"|"C "|"D"|"E"|"F"|"G"|
+                         "H"|"I"|"J"|"K"|"L"|"M"|"N"|"O"|"P"|" Q"|"R"|
+                         "S"|"T"|"U"|"V"|"W"|"X"|"Y"|"Z"
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 34]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   email-safe =          safe | space | tab
+
+
+   safe =                alpha-numeric |
+                         "'" | "'" | "-" | "." | "/" | ":" | "?" | """ |
+                         "#" | "$" | "&" | "*" | ";" | "=" | "@" | "[" |
+                         "]" | "^" | "_" | "`" | "{" | "|" | "}" | "+" |
+                         "~" | "
+
+
+   space =               %d32
+   tab =                 %d9
+   CRLF =                %d13.10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 35]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+Appendix B: Guidelines for registering SDP names with IANA
+
+   There are seven field names that may be registered with IANA. Using
+   the terminology in the SDP specification BNF, they are "media",
+   "proto", "fmt", "att-field", "bwtype", "nettype" and "addrtype".
+
+   "media" (eg, audio, video, application, data).
+
+       Packetized media types, such as those used by RTP, share the
+       namespace used by media types registry [RFC 2048] (i.e. "MIME
+       types").  The list of valid media names is the set of top-level
+       MIME content types.  The set of media is intended to be small and
+       not to be extended except under rare circumstances.  (The MIME
+       subtype corresponds to the "fmt" parameter below).
+
+   "proto"
+
+       In general this should be an IETF standards-track transport
+       protocol identifier such as RTP/AVP (rfc 1889 under the rfc 1890
+       profile).
+
+       However, people will want to invent their own proprietary
+       transport protocols.  Some of these should be registered as a
+       "fmt" using "udp" as the protocol and some of which probably
+       can't be.
+
+       Where the protocol and the application are intimately linked,
+       such as with the LBL whiteboard wb which used a proprietary and
+       special purpose protocol over UDP, the protocol name should be
+       "udp" and the format name that should be registered is "wb".  The
+       rules for formats (see below) apply to such registrations.
+
+       Where the proprietary transport protocol really carries many
+       different data formats, it is possible to register a new protocol
+       name with IANA. In such a case, an RFC MUST be produced
+       describing the protocol and referenced in the registration.  Such
+       an RFC MAY be informational, although it is preferable if it is
+       standards-track.
+
+   "fmt"
+
+       The format namespace is dependent on the context of the "proto"
+       field, so a format cannot be registered without specifying one or
+       more transport protocols that it applies to.
+
+       Formats cover all the possible encodings that might want to be
+       transported in a multimedia session.
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 36]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+       For RTP formats that have been assigned static payload types, the
+       payload type number is used.  For RTP formats using a dynamic
+       payload type number, the dynamic payload type number is given as
+       the format and an additional "rtpmap" attribute specifies the
+       format and parameters.
+
+       For non-RTP formats, any unregistered format name may be
+       registered through the MIME-type registration process [RFC 2048].
+       The type given here is the MIME subtype only (the top-level MIME
+       content type is specified by the media parameter).  The MIME type
+       registration SHOULD reference a standards-track RFC which
+       describes the transport protocol for this media type.  If there
+       is an existing MIME type for this format, the MIME registration
+       should be augmented to reference the transport specification for
+       this media type.  If there is not an existing MIME type for this
+       format, and there exists no appropriate file format, this should
+       be noted in the encoding considerations as "no appropriate file
+       format".
+
+   "att-field" (Attribute names)
+
+       Attribute field names MAY be registered with IANA, although this
+       is not compulsory, and unknown attributes are simply ignored.
+
+       When an attribute is registered, it must be accompanied by a
+       brief specification stating the following:
+
+       o contact name, email address and telephone number
+
+       o attribute-name (as it will appear in SDP)
+
+       o long-form attribute name in English
+
+       o type of attribute (session level, media level, or both)
+
+       o whether the attribute value is subject to the charset
+       attribute.
+
+       o a one paragraph explanation of the purpose of the attribute.
+
+       o a specification of appropriate attribute values for this
+         attribute.
+
+       IANA will not sanity check such attribute registrations except to
+       ensure that they do not clash with existing registrations.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 37]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+       Although the above is the minimum that IANA will accept, if the
+       attribute is expected to see widespread use and interoperability
+       is an issue, authors are encouraged to produce a standards-track
+       RFC that specifies the attribute more precisely.
+
+       Submitters of registrations should ensure that the specification
+       is in the spirit of SDP attributes, most notably that the
+       attribute is platform independent in the sense that it makes no
+       implicit assumptions about operating systems and does not name
+       specific pieces of software in a manner that might inhibit
+       interoperability.
+
+   "bwtype" (bandwidth specifiers)
+
+       A proliferation of bandwidth specifiers is strongly discouraged.
+
+       New bandwidth specifiers may be registered with IANA.  The
+       submission MUST reference a standards-track RFC specifying the
+       semantics of the bandwidth specifier precisely, and indicating
+       when it should be used, and why the existing registered bandwidth
+       specifiers do not suffice.
+
+   "nettype" (Network Type)
+
+       New network types may be registered with IANA if SDP needs to be
+       used in the context of non-internet environments. Whilst these
+       are not normally the preserve of IANA, there may be circumstances
+       when an Internet application needs to interoperate with a non-
+       internet application, such as when gatewaying an internet
+       telephony call into the PSTN.  The number of network types should
+       be small and should be rarely extended.  A new network type
+       cannot be registered without registering at least one address
+       type to be used with that network type.  A new network type
+       registration MUST reference an RFC which gives details of the
+       network type and address type and specifies how and when they
+       would be used.  Such an RFC MAY be Informational.
+
+   "addrtype" (Address Type)
+
+       New address types may be registered with IANA.  An address type
+       is only meaningful in the context of a network type, and any
+       registration of an address type MUST specify a registered network
+       type, or be submitted along with a network type registration.  A
+       new address type registration MUST reference an RFC giving
+       details of the syntax of the address type.  Such an RFC MAY be
+       Informational.  Address types are not expected to be registered
+       frequently.
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 38]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Registration Procedure
+
+   To register a name the above guidelines should be followed regarding
+   the required  level  of  documentation  that  is required.  The
+   registration itself should be sent to IANA.  Attribute registrations
+   should  include the  information  given  above.   Other registrations
+   should include the following additional information:
+
+   o contact name, email address and telephone number
+
+   o name being registered (as it will appear in SDP)
+
+   o long-form name in English
+
+   o type of name ("media", "proto", "fmt", "bwtype", "nettype", or
+     "addrtype")
+
+   o a one paragraph explanation of the purpose of the registered name.
+
+   o a reference to the specification (eg RFC number) of the registered
+     name.
+
+   IANA may refer any registration to the IESG or to any appropriate
+   IETF working group for review, and may request revisions to be made
+   before a registration will be made.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 39]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+Appendix C: Authors' Addresses
+
+   Mark Handley
+   Information Sciences Institute
+   c/o MIT Laboratory for Computer Science
+   545 Technology Square
+   Cambridge, MA 02139
+   United States
+   electronic mail: mjh@isi.edu
+
+   Van Jacobson
+   MS 46a-1121
+   Lawrence Berkeley Laboratory
+   Berkeley, CA 94720
+   United States
+   electronic mail: van@ee.lbl.gov
+
+Acknowledgments
+
+   Many people in the IETF MMUSIC working group have made comments and
+   suggestions contributing to this document.  In particular, we would
+   like to thank Eve Schooler, Steve Casner, Bill Fenner, Allison
+   Mankin, Ross Finlayson, Peter Parnes, Joerg Ott, Carsten Bormann, Rob
+   Lanphier and Steve Hanna.
+
+References
+
+   [1] Mills, D., "Network Time Protocol (version 3) specification and
+   implementation", RFC 1305, March 1992.
+
+   [2] Schulzrinne, H., Casner, S., Frederick, R. and V. Jacobson, "RTP:
+   A Transport Protocol for Real-Time Applications", RFC 1889, January
+   1996.
+
+   [3] Schulzrinne, H., "RTP Profile for Audio and Video Conferences
+   with Minimal Control", RFC 1890, January 1996
+
+   [4] Handley, M., "SAP - Session Announcement Protocol", Work in
+   Progress.
+
+   [5] V. Jacobson, S. McCanne, "vat - X11-based audio teleconferencing
+   tool" vat manual page, Lawrence Berkeley Laboratory, 1994.
+
+   [6] The Unicode Consortium, "The Unicode Standard -- Version 2.0",
+   Addison-Wesley, 1996.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 40]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   [7] ISO/IEC 10646-1:1993. International Standard -- Information
+   technol- ogy -- Universal Multiple-Octet Coded Character Set (UCS) --
+   Part 1: Architecture and Basic Multilingual Plane.  Five amendments
+   and a techn- ical  corrigendum  have been published up to now.  UTF-8
+   is described in Annex R, published as Amendment 2.
+
+   [8] Goldsmith, D., and M. Davis, "Using Unicode with MIME", RFC 1641,
+   July 1994.
+
+   [9] Yergeau, F., "UTF-8, a transformation format of Unicode and ISO
+   10646", RFC 2044, October 1996.
+
+   [10] ITU-T Recommendation H.332 (1998): "Multimedia Terminal for
+   Receiving Internet-based H.323 Conferences", ITU, Geneva.
+
+   [11] Handley, M., Schooler, E., and H. Schulzrinne, "Session
+   Initiation Protocol (SIP)", Work in Progress.
+
+   [12] Schulzrinne, H., Rao, A., and R. Lanphier, "Real Time Streaming
+   Protocol (RTSP)", RFC 2326, April 1998.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 41]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+Full Copyright Statement
+
+   Copyright (C) The Internet Society (1998).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 42]
+\f
diff --git a/src/modules/rtp/rfc2974.txt b/src/modules/rtp/rfc2974.txt
new file mode 100644 (file)
index 0000000..4a5aa62
--- /dev/null
@@ -0,0 +1,1011 @@
+
+
+
+
+
+
+Network Working Group                                         M. Handley
+Request for Comments: 2974                                         ACIRI
+Category: Experimental                                        C. Perkins
+                                                                 USC/ISI
+                                                               E. Whelan
+                                                                     UCL
+                                                            October 2000
+
+
+                     Session Announcement Protocol
+
+Status of this Memo
+
+   This memo defines an Experimental Protocol for the Internet
+   community.  It does not specify an Internet standard of any kind.
+   Discussion and suggestions for improvement are requested.
+   Distribution of this memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The Internet Society (2000).  All Rights Reserved.
+
+Abstract
+
+   This document describes version 2 of the multicast session directory
+   announcement protocol, Session Announcement Protocol (SAP), and the
+   related issues affecting security and scalability that should be
+   taken into account by implementors.
+
+1  Introduction
+
+   In order to assist the advertisement of multicast multimedia
+   conferences and other multicast sessions, and to communicate the
+   relevant session setup information to prospective participants, a
+   distributed session directory may be used.  An instance of such a
+   session directory periodically multicasts packets containing a
+   description of the session, and these advertisements are received by
+   other session directories such that potential remote participants can
+   use the session description to start the tools required to
+   participate in the session.
+
+   This memo describes the issues involved in the multicast announcement
+   of session description information and defines an announcement
+   protocol to be used.  Sessions are described using the session
+   description protocol which is described in a companion memo [4].
+
+
+
+
+
+
+Handley, et al.               Experimental                      [Page 1]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+2  Terminology
+
+   A SAP announcer periodically multicasts an announcement packet to a
+   well known multicast address and port.  The announcement is multicast
+   with the same scope as the session it is announcing, ensuring that
+   the recipients of the announcement are within the scope of the
+   session the announcement describes (bandwidth and other such
+   constraints permitting).  This is also important for the scalability
+   of the protocol, as it keeps local session announcements local.
+
+   A SAP listener learns of the multicast scopes it is within (for
+   example, using the Multicast-Scope Zone Announcement Protocol [5])
+   and listens on the well known SAP address and port for those scopes.
+   In this manner, it will eventually learn of all the sessions being
+   announced, allowing those sessions to be joined.
+
+   The key words `MUST', `MUST NOT', `REQUIRED', `SHALL', `SHALL NOT',
+   `SHOULD', `SHOULD NOT', `RECOMMENDED', `MAY', and `OPTIONAL' in this
+   document are to be interpreted as described in [1].
+
+3  Session Announcement
+
+   As noted previously, a SAP announcer periodically sends an
+   announcement packet to a well known multicast address and port.
+   There is no rendezvous mechanism - the SAP announcer is not aware of
+   the presence or absence of any SAP listeners - and no additional
+   reliability is provided over the standard best-effort UDP/IP
+   semantics.
+
+   That announcement contains a session description and SHOULD contain
+   an authentication header.  The session description MAY be encrypted
+   although this is NOT RECOMMENDED (see section 7).
+
+   A SAP announcement is multicast with the same scope as the session it
+   is announcing, ensuring that the recipients of the announcement are
+   within the scope of the session the announcement describes. There are
+   a number of possibilities:
+
+   IPv4 global scope sessions use multicast addresses in the range
+      224.2.128.0 - 224.2.255.255 with SAP announcements being sent to
+      224.2.127.254 (note that 224.2.127.255 is used by the obsolete
+      SAPv0 and MUST NOT be used).
+
+
+
+
+
+
+
+
+
+Handley, et al.               Experimental                      [Page 2]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   IPv4 administrative scope sessions using administratively scoped IP
+      multicast as defined in [7].  The multicast address to be used for
+      announcements is the highest multicast address in the relevant
+      administrative scope zone.  For example, if the scope range is
+      239.16.32.0 - 239.16.33.255, then 239.16.33.255 is used for SAP
+      announcements.
+
+   IPv6 sessions are announced on the address FF0X:0:0:0:0:0:2:7FFE
+      where X is the 4-bit scope value.  For example, an announcement
+      for a link-local session assigned the address
+      FF02:0:0:0:0:0:1234:5678, should be advertised on SAP address
+      FF02:0:0:0:0:0:2:7FFE.
+
+   Ensuring that a description is not used by a potential participant
+   outside the session scope is not addressed in this memo.
+
+   SAP announcements MUST be sent on port 9875 and SHOULD be sent with
+   an IP time-to-live of 255 (the use of TTL scoping for multicast is
+   discouraged [7]).
+
+   If a session uses addresses in multiple administrative scope ranges,
+   it is necessary for the announcer to send identical copies of the
+   announcement to each administrative scope range.  It is up to the
+   listeners to parse such multiple announcements as the same session
+   (as identified by the SDP origin field, for example).  The
+   announcement rate for each administrative scope range MUST be
+   calculated separately, as if the multiple announcements were
+   separate.
+
+   Multiple announcers may announce a single session, as an aid to
+   robustness in the face of packet loss and failure of one or more
+   announcers.  The rate at which each announcer repeats its
+   announcement MUST be scaled back such that the total announcement
+   rate is equal to that which a single server would choose.
+   Announcements made in this manner MUST be identical.
+
+   If multiple announcements are being made for a session, then each
+   announcement MUST carry an authentication header signed by the same
+   key, or be treated as a completely separate announcement by
+   listeners.
+
+   An IPv4 SAP listener SHOULD listen on the IPv4 global scope SAP
+   address and on the SAP addresses for each IPv4 administrative scope
+   zone it is within.  The discovery of administrative scope zones is
+   outside the scope of this memo, but it is assumed that each SAP
+   listener within a particular scope zone is aware of that scope zone.
+   A SAP listener which supports IPv6 SHOULD also listen to the IPv6 SAP
+   addresses.
+
+
+
+Handley, et al.               Experimental                      [Page 3]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+3.1 Announcement Interval
+
+   The time period between repetitions of an announcement is chosen such
+   that the total bandwidth used by all announcements on a single SAP
+   group remains below a preconfigured limit.  If not otherwise
+   specified, the bandwidth limit SHOULD be assumed to be 4000 bits per
+   second.
+
+   Each announcer is expected to listen to other announcements in order
+   to determine the total number of sessions being announced on a
+   particular group.  Sessions are uniquely identified by the
+   combination of the message identifier hash and originating source
+   fields of the SAP header (note that SAP v0 announcers always set the
+   message identifier hash to zero, and if such an announcement is
+   received the entire message MUST be compared to determine
+   uniqueness).
+
+   Announcements are made by periodic multicast to the group.  The base
+   interval between announcements is derived from the number of
+   announcements being made in that group, the size of the announcement
+   and the configured bandwidth limit.  The actual transmission time is
+   derived from this base interval as follows:
+
+      1. The announcer initializes the variable tp to be the last time a
+         particular announcement was transmitted (or the current time if
+         this is the first time this announcement is to be made).
+
+      2. Given a configured bandwidth limit in bits/second and an
+         announcement of ad_size bytes, the base announcement interval
+         in seconds is
+
+                interval =max(300; (8*no_of_ads*ad_size)/limit)
+
+      3. An offset is calculated based on the base announcement interval
+
+                offset= rand(interval* 2/3)-(interval/3)
+
+      4. The next transmission time for an announcement derived as
+
+                tn =tp+ interval+ offset
+
+   The announcer then sets a timer to expire at tn and waits.  At time
+   tn the announcer SHOULD recalculate the next transmission time.  If
+   the new value of tn is before the current time, the announcement is
+   sent immediately.  Otherwise the transmission is rescheduled for the
+   new tn.  This reconsideration prevents transient packet bursts on
+   startup and when a network partition heals.
+
+
+
+
+Handley, et al.               Experimental                      [Page 4]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+4  Session Deletion
+
+   Sessions may be deleted in one of several ways:
+
+   Explicit Timeout The session description payload may contain
+      timestamp information specifying the start- and end-times of the
+      session.  If the current time is later than the end-time of the
+      session, then the session SHOULD be deleted from the receiver's
+      session cache.
+
+   Implicit Timeout A session announcement message should be received
+      periodically for each session description in a receiver's session
+      cache.  The announcement period can be predicted by the receiver
+      from the set of sessions currently being announced.  If a session
+      announcement message has not been received for ten times the
+      announcement period, or one hour, whichever is the greater, then
+      the session is deleted from the receiver's session cache.  The one
+      hour minimum is to allow for transient network partitionings.
+
+   Explicit Deletion A session deletion packet is received specifying
+      the session to be deleted.  Session deletion packets SHOULD have a
+      valid authentication header, matching that used to authenticate
+      previous announcement packets.  If this authentication is missing,
+      the deletion message SHOULD be ignored.
+
+5  Session Modification
+
+   A pre-announced session can be modified by simply announcing the
+   modified session description.  In this case, the version hash in the
+   SAP header MUST be changed to indicate to receivers that the packet
+   contents should be parsed (or decrypted and parsed if it is
+   encrypted).  The session itself, as distinct from the session
+   announcement, is uniquely identified by the payload and not by the
+   message identifier hash in the header.
+
+   The same rules apply for session modification as for session
+   deletion:
+
+    o Either the modified announcement must contain an authentication
+      header signed by the same key as the cached session announcement
+      it is modifying, or:
+
+    o The cached session announcement must not contain an authentication
+      header, and the session modification announcement must originate
+      from the same host as the session it is modifying.
+
+
+
+
+
+
+Handley, et al.               Experimental                      [Page 5]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   If an announcement is received containing an authentication header
+   and the cached announcement did not contain an authentication header,
+   or it contained a different authentication header, then the modified
+   announcement MUST be treated as a new and different announcement, and
+   displayed in addition to the un-authenticated announcement.  The same
+   should happen if a modified packet without an authentication header
+   is received from a different source than the original announcement.
+
+   These rules prevent an announcement having an authentication header
+   added by a malicious user and then being deleted using that header,
+   and it also prevents a denial-of-service attack by someone putting
+   out a spoof announcement which, due to packet loss, reaches some
+   participants before the original announcement.  Note that under such
+   circumstances, being able to authenticate the message originator is
+   the only way to discover which session is the correct session.
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | V=1 |A|R|T|E|C|   auth len    |         msg id hash           |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                                                               |
+   :                originating source (32 or 128 bits)            :
+   :                                                               :
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                    optional authentication data               |
+   :                              ....                             :
+   *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
+   |                      optional payload type                    |
+   +                                         +-+- - - - - - - - - -+
+   |                                         |0|                   |
+   + - - - - - - - - - - - - - - - - - - - - +-+                   |
+   |                                                               |
+   :                            payload                            :
+   |                                                               |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                     Figure 1: Packet format
+
+6  Packet Format
+
+   SAP data packets have the format described in figure 1.
+
+   V: Version Number. The version number field MUST be set to 1 (SAPv2
+      announcements which use only SAPv1 features are backwards
+      compatible, those which use new features can be detected by other
+      means, so the SAP version number doesn't need to change).
+
+
+
+
+Handley, et al.               Experimental                      [Page 6]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   A: Address type. If the A bit is 0, the originating source field
+      contains a 32-bit IPv4 address.  If the A bit is 1, the
+      originating source contains a 128-bit IPv6 address.
+
+   R: Reserved. SAP announcers MUST set this to 0, SAP listeners MUST
+      ignore the contents of this field.
+
+   T: Message Type. If the T field is set to 0 this is a session
+      announcement packet, if 1 this is a session deletion packet.
+
+   E: Encryption Bit. If the encryption bit is set to 1, the payload of
+      the SAP packet is encrypted.  If this bit is 0 the packet is not
+      encrypted.  See section 7 for details of the encryption process.
+
+   C: Compressed bit. If the compressed bit is set to 1, the payload is
+      compressed using the zlib compression algorithm [3].  If the
+      payload is to be compressed and encrypted, the compression MUST be
+      performed first.
+
+   Authentication Length. An 8 bit unsigned quantity giving the number
+      of 32 bit words following the main SAP header that contain
+      authentication data.  If it is zero, no authentication header is
+      present.
+
+   Authentication data containing a digital signature of the packet,
+      with length as specified by the authentication length header
+      field.  See section 8 for details of the authentication process.
+
+   Message Identifier Hash. A 16 bit quantity that, used in combination
+      with the originating source, provides a globally unique identifier
+      indicating the precise version of this announcement.  The choice
+      of value for this field is not specified here, except that it MUST
+      be unique for each session announced by a particular SAP announcer
+      and it MUST be changed if the session description is modified (and
+      a session deletion message SHOULD be sent for the old version of
+      the session).
+
+      Earlier versions of SAP used a value of zero to mean that the hash
+      should be ignored and the payload should always be parsed.  This
+      had the unfortunate side-effect that SAP announcers had to study
+      the payload data to determine how many unique sessions were being
+      advertised, making the calculation of the announcement interval
+      more complex that necessary.  In order to decouple the session
+      announcement process from the contents of those announcements, SAP
+      announcers SHOULD NOT set the message identifier hash to zero.
+
+      SAP listeners MAY silently discard messages if the message
+      identifier hash is set to zero.
+
+
+
+Handley, et al.               Experimental                      [Page 7]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   Originating Source. This gives the IP address of the original source
+      of the message.  This is an IPv4 address if the A field is set to
+      zero, else it is an IPv6 address.  The address is stored in
+      network byte order.
+
+      SAPv0 permitted the originating source to be zero if the message
+      identifier hash was also zero.  This practise is no longer legal,
+      and SAP announcers SHOULD NOT set the originating source to zero.
+      SAP listeners MAY silently discard packets with the originating
+      source set to zero.
+
+   The header is followed by an optional payload type field and the
+   payload data itself.  If the E or C bits are set in the header both
+   the payload type and payload are encrypted and/or compressed.
+
+   The payload type field is a MIME content type specifier, describing
+   the format of the payload.  This is a variable length ASCII text
+   string, followed by a single zero byte (ASCII NUL).  The payload type
+   SHOULD be included in all packets.  If the payload type is
+   `application/sdp' both the payload type and its terminating zero byte
+   MAY be omitted, although this is intended for backwards compatibility
+   with SAP v1 listeners only.
+
+   The absence of a payload type field may be noted since the payload
+   section of such a packet will start with an SDP `v=0' field, which is
+   not a legal MIME content type specifier.
+
+   All implementations MUST support payloads of type `application/sdp'
+   [4].  Other formats MAY be supported although since there is no
+   negotiation in SAP an announcer which chooses to use a session
+   description format other than SDP cannot know that the listeners are
+   able to understand the announcement.  A proliferation of payload
+   types in announcements has the potential to lead to severe
+   interoperability problems, and for this reason, the use of non-SDP
+   payloads is NOT RECOMMENDED.
+
+   If the packet is an announcement packet, the payload contains a
+   session description.
+
+   If the packet is a session deletion packet, the payload contains a
+   session deletion message.  If the payload format is `application/sdp'
+   the deletion message is a single SDP line consisting of the origin
+   field of the announcement to be deleted.
+
+   It is desirable for the payload to be sufficiently small that SAP
+   packets do not get fragmented by the underlying network.
+   Fragmentation has a loss multiplier effect, which is known to
+   significantly affect the reliability of announcements.  It is
+
+
+
+Handley, et al.               Experimental                      [Page 8]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   RECOMMENDED that SAP packets are smaller than 1kByte in length,
+   although if it is known that announcements will use a network with a
+   smaller MTU than this, then that SHOULD be used as the maximum
+   recommended packet size.
+
+7  Encrypted Announcements
+
+   An announcement is received by all listeners in the scope to which it
+   is sent.  If an announcement is encrypted, and many of the receivers
+   do not have the encryption key, there is a considerable waste of
+   bandwidth since those receivers cannot use the announcement they have
+   received.  For this reason, the use of encrypted SAP announcements is
+   NOT RECOMMENDED on the global scope SAP group or on administrative
+   scope groups which may have many receivers which cannot decrypt those
+   announcements.
+
+   The opinion of the authors is that encrypted SAP is useful in special
+   cases only, and that the vast majority of scenarios where encrypted
+   SAP has been proposed may be better served by distributing session
+   details using another mechanism.  There are, however, certain
+   scenarios where encrypted announcements may be useful.  For this
+   reason, the encryption bit is included in the SAP header to allow
+   experimentation with encrypted announcements.
+
+   This memo does not specify details of the encryption algorithm to be
+   used or the means by which keys are generated and distributed.  An
+   additional specification should define these, if it is desired to use
+   encrypted SAP.
+
+   Note that if an encrypted announcement is being announced via a
+   proxy, then there may be no way for the proxy to discover that the
+   announcement has been superseded, and so it may continue to relay the
+   old announcement in addition to the new announcement.  SAP provides
+   no mechanism to chain modified encrypted announcements, so it is
+   advisable to announce the unmodified session as deleted for a short
+   time after the modification has occurred.  This does not guarantee
+   that all proxies have deleted the session, and so receivers of
+   encrypted sessions should be prepared to discard old versions of
+   session announcements that they may receive.  In most cases however,
+   the only stateful proxy will be local to (and known to) the sender,
+   and an additional (local-area) protocol involving a handshake for
+   such session modifications can be used to avoid this problem.
+
+   Session announcements that are encrypted with a symmetric algorithm
+   may allow a degree of privacy in the announcement of a session, but
+   it should be recognized that a user in possession of such a key can
+   pass it on to other users who should not be in possession of such a
+   key.  Thus announcements to such a group of key holders cannot be
+
+
+
+Handley, et al.               Experimental                      [Page 9]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   assumed to have come from an authorized key holder unless there is an
+   appropriate authentication header signed by an authorized key holder.
+   In addition the recipients of such encrypted announcements cannot be
+   assumed to only be authorized key holders.  Such encrypted
+   announcements do not provide any real security unless all of the
+   authorized key holders are trusted to maintain security of such
+   session directory keys.  This property is shared by the multicast
+   session tools themselves, where it is possible for an un-trustworthy
+   member of the session to pass on encryption keys to un-authorized
+   users.  However it is likely that keys used for the session tools
+   will be more short lived than those used for session directories.
+
+   Similar considerations should apply when session announcements are
+   encrypted with an asymmetric algorithm, but then it is possible to
+   restrict the possessor(s) of the private key, so that announcements
+   to a key-holder group can not be made, even if one of the untrusted
+   members of the group proves to be un-trustworthy.
+
+                        1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | V=1 |P| Auth  |                                               |
+   +-+-+-+-+-+-+-+-+                                               |
+   |              Format  specific authentication subheader        |
+   :                        ..................                     :
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+    Figure 2:  Format of the authentication data in the SAP header
+
+8  Authenticated Announcements
+
+   The authentication header can be used for two purposes:
+
+    o Verification that changes to a session description or deletion of
+      a session are permitted.
+
+    o Authentication of the identity of the session creator.
+
+   In some circumstances only verification is possible because a
+   certificate signed by a mutually trusted person or authority is not
+   available.  However, under such circumstances, the session originator
+   may still be authenticated to be the same as the session originator
+   of previous sessions claiming to be from the same person.  This may
+   or may not be sufficient depending on the purpose of the session and
+   the people involved.
+
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 10]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   Clearly the key used for the authentication should not be trusted to
+   belong to the session originator unless it has been separately
+   authenticated by some other means, such as being certified by a
+   trusted third party.  Such certificates are not normally included in
+   an SAP header because they take more space than can normally be
+   afforded in an SAP packet, and such verification must therefore take
+   place by some other mechanism.  However, as certified public keys are
+   normally locally cached, authentication of a particular key only has
+   to take place once, rather than every time the session directory
+   retransmits the announcement.
+
+   SAP is not tied to any single authentication mechanism.
+   Authentication data in the header is self-describing, but the precise
+   format depends on the authentication mechanism in use.  The generic
+   format of the authentication data is given in figure 2.  The
+   structure of the format specific authentication subheader, using both
+   the PGP and the CMS formats, is discussed in sections 8.1 and 8.2
+   respectively.  Additional formats may be added in future.
+
+   Version Number, V:  The version number of the authentication format
+      specified by this memo is 1.
+
+   Padding Bit, P:  If necessary the authentication data is padded to be
+      a multiple of 32 bits and the padding bit is set.  In this case
+      the last byte of the authentication data contains the number of
+      padding bytes (including the last byte) that must be discarded.
+
+   Authentication Type, Auth: The authentication type is a  4 bit
+      encoded field that denotes the authentication infrastructure the
+      sender expects the recipients to use to check the authenticity and
+      integrity of the information.  This defines the format of the
+      authentication subheader and can take the values:  0 = PGP format,
+      1 = CMS format.  All other values are undefined and SHOULD be
+      ignored.
+
+   If a SAP packet is to be compressed or encrypted, this MUST be done
+   before the authentication is added.
+
+   The digital signature in the authentication data MUST be calculated
+   over the entire packet, including the header.  The authentication
+   length MUST be set to zero and the authentication data excluded when
+   calculating the digital signature.
+
+   It is to be expected that sessions may be announced by a number of
+   different mechanisms, not only SAP.  For example, a session
+   description may placed on a web page, sent by email or conveyed in a
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 11]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   session initiation protocol.  To ease interoperability with these
+   other mechanisms, application level security is employed, rather than
+   using IPsec authentication headers.
+
+8.1 PGP Authentication
+
+   A full description of the PGP protocol can be found in [2].  When
+   using PGP for SAP authentication the basic format specific
+   authentication subheader comprises a digital signature packet as
+   described in [2].  The signature type MUST be 0x01 which means the
+   signature is that of a canonical text document.
+
+8.2 CMS Authentication
+
+   A full description of the Cryptographic Message Syntax can be found
+   in [6].  The format specific authentication subheader will, in the
+   CMS case, have an ASN.1 ContentInfo type with the ContentType being
+   signedData.
+
+   Use is made of the option available in PKCS#7 to leave the content
+   itself blank as the content which is signed is already present in the
+   packet.  Inclusion of it within the SignedData type would duplicate
+   this data and increase the packet length unnecessarily.  In addition
+   this allows recipients with either no interest in the authentication,
+   or with no mechanism for checking it, to more easily skip the
+   authentication information.
+
+   There SHOULD be only one signerInfo and related fields corresponding
+   to the originator of the SAP announcement.  The signingTime SHOULD be
+   present as a signedAttribute.  However, due to the strict size
+   limitations on the size of SAP packets, certificates and CRLs SHOULD
+   NOT be included in the signedData structure.  It is expected that
+   users of the protocol will have other methods for certificate and CRL
+   distribution.
+
+9  Scalability and caching
+
+   SAP is intended to announce the existence of long-lived wide-area
+   multicast sessions.  It is not an especially timely protocol:
+   sessions are announced by periodic multicast with a repeat rate on
+   the order of tens of minutes, and no enhanced reliability over UDP.
+   This leads to a long startup delay before a complete set of
+   announcements is heard by a listener.  This delay is clearly
+   undesirable for interactive browsing of announced sessions.
+
+   In order to reduce the delays inherent in SAP, it is recommended that
+   proxy caches are deployed.  A SAP proxy cache is expected to listen
+   to all SAP groups in its scope, and to maintain an up-to-date list of
+
+
+
+Handley, et al.               Experimental                     [Page 12]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   all announced sessions along with the time each announcement was last
+   received.  When a new SAP listeners starts, it should contact its
+   local proxy to download this information, which is then sufficient
+   for it to process future announcements directly, as if it has been
+   continually listening.
+
+   The protocol by which a SAP listener contacts its local proxy cache
+   is not specified here.
+
+10 Security Considerations
+
+   SAP contains mechanisms for ensuring integrity of session
+   announcements, for authenticating the origin of an announcement and
+   for encrypting such announcements (sections 7 and 8).
+
+   As stated in section 5, if a session modification announcement is
+   received that contains a valid authentication header, but which is
+   not signed by the original creator of the session, then the session
+   must be treated as a new session in addition to the original session
+   with the same SDP origin information unless the originator of one of
+   the session descriptions can be authenticated using a certificate
+   signed by a trusted third party.  If this were not done, there would
+   be a possible denial of service attack whereby a party listens for
+   new announcements, strips off the original authentication header,
+   modifies the session description, adds a new authentication header
+   and re-announces the session.  If a rule was imposed that such spoof
+   announcements were ignored, then if packet loss or late starting of a
+   session directory instance caused the original announcement to fail
+   to arrive at a site, but the spoof announcement did so, this would
+   then prevent the original announcement from being accepted at that
+   site.
+
+   A similar denial-of-service attack is possible if a session
+   announcement receiver relies completely on the originating source and
+   hash fields to indicate change, and fails to parse the remainder of
+   announcements for which it has seen the origin/hash combination
+   before.
+
+   A denial of service attack is possible from a malicious site close to
+   a legitimate site which is making a session announcement.  This can
+   happen if the malicious site floods the legitimate site with huge
+   numbers of (illegal) low TTL announcements describing high TTL
+   sessions.  This may reduce the session announcement rate of the
+   legitimate announcement to below a tenth of the rate expected at
+   remote sites and therefore cause the session to time out.  Such an
+   attack is likely to be easily detectable, and we do not provide any
+   mechanism here to prevent it.
+
+
+
+
+Handley, et al.               Experimental                     [Page 13]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+A. Summary of differences between SAPv0 and SAPv1
+
+   For this purpose SAPv0 is defined as the protocol in use by version
+   2.2 of the session directory tool, sdr.  SAPv1 is the protocol
+   described in the 19 November 1996 version of this memo.  The packet
+   headers of SAP messages are the same in V0 and V1 in that a V1 tool
+   can parse a V0 announcement header but not vice-versa.  In SAPv0, the
+   fields have the following values:
+
+     o Version Number:  0
+
+     o Message Type:  0 (Announcement)
+
+     o Authentication Type:  0 (No Authentication)
+
+     o Encryption Bit:  0 (No Encryption)
+
+     o Compression Bit:  0 (No compression)
+
+     o Message Id Hash:  0 (No Hash Specified)
+
+     o Originating Source:  0 (No source specified, announcement has
+       not been relayed)
+
+B. Summary of differences between SAPv1 and SAPv2
+
+   The packet headers of SAP messages are the same in V1 and V2 in that
+   a V2 tool can parse a V1 announcement header but not necessarily
+   vice-versa.
+
+    o The A bit has been added to the SAP header, replacing one of the
+      bits of the SAPv1 message type field.  If set to zero the
+      announcement is of an IPv4 session, and the packet is backwards
+      compatible with SAPv1.  If set to one the announcement is of an
+      IPv6 session, and SAPv1 listeners (which do not support IPv6) will
+      see this as an illegal message type (MT) field.
+
+    o The second bit of the message type field in SAPv1 has been
+      replaced by a reserved, must-be-zero, bit.  This bit was unused in
+      SAPv1, so this change just codifies existing usage.
+
+    o SAPv1 specified encryption of the payload.  SAPv2 includes the E
+      bit in the SAP header to indicate that the payload is encrypted,
+      but does not specify any details of the encryption.
+
+    o SAPv1 allowed the message identifier hash and originating source
+      fields to be set to zero, for backwards compatibility.  This is no
+      longer legal.
+
+
+
+Handley, et al.               Experimental                     [Page 14]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+    o SAPv1 specified gzip compression.  SAPv2 uses zlib (the only known
+      implementation of SAP compression used zlib, and gzip compression
+      was a mistake).
+
+    o SAPv2 provides a more complete specification for authentication.
+
+    o SAPv2 allows for non-SDP payloads to be transported.  SAPv1
+      required that the payload was SDP.
+
+    o SAPv1 included a timeout field for encrypted announcement, SAPv2
+      does not (and relies of explicit deletion messages or implicit
+      timeouts).
+
+C. Acknowledgements
+
+   SAP and SDP were originally based on the protocol used by the sd
+   session directory from Van Jacobson at LBNL.  Version 1 of SAP was
+   designed by Mark Handley as part of the European Commission MICE
+   (Esprit 7602) and MERCI (Telematics 1007) projects.  Version 2
+   includes authentication features developed by Edmund Whelan, Goli
+   Montasser-Kohsari and Peter Kirstein as part of the European
+   Commission ICE-TEL project (Telematics 1005), and support for IPv6
+   developed by Maryann P. Maher and Colin Perkins.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 15]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+D. Authors' Addresses
+
+   Mark Handley
+   AT&T Center for Internet Research at ICSI,
+   International Computer Science Institute,
+   1947 Center Street, Suite 600,
+   Berkeley, CA 94704, USA
+
+   EMail: mjh@aciri.org
+
+
+   Colin Perkins
+   USC Information Sciences Institute
+   4350 N. Fairfax Drive, Suite 620
+   Arlington, VA 22203, USA
+
+   EMail: csp@isi.edu
+
+
+   Edmund Whelan
+   Department of Computer Science,
+   University College London,
+   Gower Street,
+   London, WC1E 6BT, UK
+
+   EMail: e.whelan@cs.ucl.ac.uk
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 16]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+References
+
+   [1] Bradner, S., "Key words for use in RFCs to indicate requirement
+       levels", BCP 14, RFC 2119, March 1997.
+
+   [2] Callas, J., Donnerhacke, L., Finney, H. and R. Thayer. "OpenPGP
+       message format", RFC 2440, November 1998.
+
+   [3] Deutsch, P. and J.-L. Gailly, "Zlib compressed data format
+       specification version 3.3", RFC 1950, May 1996.
+
+   [4] Handley, M. and V. Jacobson, "SDP: Session Description Protocol",
+       RFC 2327, April 1998.
+
+   [5] Handley, M., Thaler, D. and R. Kermode, "Multicast-scope zone
+       announcement protocol (MZAP)", RFC 2776, February 2000.
+
+   [6] Housley, R., "Cryptographic message syntax", RFC 2630, June 1999.
+
+   [7] Mayer, D., "Administratively scoped IP multicast", RFC 2365, July
+       1998.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 17]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+Full Copyright Statement
+
+   Copyright (C) The Internet Society (2000).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+   Funding for the RFC Editor function is currently provided by the
+   Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 18]
+\f
diff --git a/src/modules/rtp/rfc3550.txt b/src/modules/rtp/rfc3550.txt
new file mode 100644 (file)
index 0000000..165736c
--- /dev/null
@@ -0,0 +1,5827 @@
+
+
+
+
+
+
+Network Working Group                                     H. Schulzrinne
+Request for Comments: 3550                           Columbia University
+Obsoletes: 1889                                               S.  Casner
+Category: Standards Track                                  Packet Design
+                                                            R. Frederick
+                                                  Blue Coat Systems Inc.
+                                                             V. Jacobson
+                                                           Packet Design
+                                                               July 2003
+
+
+          RTP: A Transport Protocol for Real-Time Applications
+
+Status of this Memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The Internet Society (2003).  All Rights Reserved.
+
+Abstract
+
+   This memorandum describes RTP, the real-time transport protocol.  RTP
+   provides end-to-end network transport functions suitable for
+   applications transmitting real-time data, such as audio, video or
+   simulation data, over multicast or unicast network services.  RTP
+   does not address resource reservation and does not guarantee
+   quality-of-service for real-time services.  The data transport is
+   augmented by a control protocol (RTCP) to allow monitoring of the
+   data delivery in a manner scalable to large multicast networks, and
+   to provide minimal control and identification functionality.  RTP and
+   RTCP are designed to be independent of the underlying transport and
+   network layers.  The protocol supports the use of RTP-level
+   translators and mixers.
+
+   Most of the text in this memorandum is identical to RFC 1889 which it
+   obsoletes.  There are no changes in the packet formats on the wire,
+   only changes to the rules and algorithms governing how the protocol
+   is used.  The biggest change is an enhancement to the scalable timer
+   algorithm for calculating when to send RTCP packets in order to
+   minimize transmission in excess of the intended rate when many
+   participants join a session simultaneously.
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 1]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+Table of Contents
+
+   1.  Introduction ................................................   4
+       1.1  Terminology ............................................   5
+   2.  RTP Use Scenarios ...........................................   5
+       2.1  Simple Multicast Audio Conference ......................   6
+       2.2  Audio and Video Conference .............................   7
+       2.3  Mixers and Translators .................................   7
+       2.4  Layered Encodings ......................................   8
+   3.  Definitions .................................................   8
+   4.  Byte Order, Alignment, and Time Format ......................  12
+   5.  RTP Data Transfer Protocol ..................................  13
+       5.1  RTP Fixed Header Fields ................................  13
+       5.2  Multiplexing RTP Sessions ..............................  16
+       5.3  Profile-Specific Modifications to the RTP Header .......  18
+            5.3.1  RTP Header Extension ............................  18
+   6.  RTP Control Protocol -- RTCP ................................  19
+       6.1  RTCP Packet Format .....................................  21
+       6.2  RTCP Transmission Interval .............................  24
+            6.2.1  Maintaining the Number of Session Members .......  28
+       6.3  RTCP Packet Send and Receive Rules .....................  28
+            6.3.1  Computing the RTCP Transmission Interval ........  29
+            6.3.2  Initialization ..................................  30
+            6.3.3  Receiving an RTP or Non-BYE RTCP Packet .........  31
+            6.3.4  Receiving an RTCP BYE Packet ....................  31
+            6.3.5  Timing Out an SSRC ..............................  32
+            6.3.6  Expiration of Transmission Timer ................  32
+            6.3.7  Transmitting a BYE Packet .......................  33
+            6.3.8  Updating we_sent ................................  34
+            6.3.9  Allocation of Source Description Bandwidth ......  34
+       6.4  Sender and Receiver Reports ............................  35
+            6.4.1  SR: Sender Report RTCP Packet ...................  36
+            6.4.2  RR: Receiver Report RTCP Packet .................  42
+            6.4.3  Extending the Sender and Receiver Reports .......  42
+            6.4.4  Analyzing Sender and Receiver Reports ...........  43
+       6.5  SDES: Source Description RTCP Packet ...................  45
+            6.5.1  CNAME: Canonical End-Point Identifier SDES Item .  46
+            6.5.2  NAME: User Name SDES Item .......................  48
+            6.5.3  EMAIL: Electronic Mail Address SDES Item ........  48
+            6.5.4  PHONE: Phone Number SDES Item ...................  49
+            6.5.5  LOC: Geographic User Location SDES Item .........  49
+            6.5.6  TOOL: Application or Tool Name SDES Item ........  49
+            6.5.7  NOTE: Notice/Status SDES Item ...................  50
+            6.5.8  PRIV: Private Extensions SDES Item ..............  50
+       6.6  BYE: Goodbye RTCP Packet ...............................  51
+       6.7  APP: Application-Defined RTCP Packet ...................  52
+   7.  RTP Translators and Mixers ..................................  53
+       7.1  General Description ....................................  53
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 2]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+       7.2  RTCP Processing in Translators .........................  55
+       7.3  RTCP Processing in Mixers ..............................  57
+       7.4  Cascaded Mixers ........................................  58
+   8.  SSRC Identifier Allocation and Use ..........................  59
+       8.1  Probability of Collision ...............................  59
+       8.2  Collision Resolution and Loop Detection ................  60
+       8.3  Use with Layered Encodings .............................  64
+   9.  Security ....................................................  65
+       9.1  Confidentiality ........................................  65
+       9.2  Authentication and Message Integrity ...................  67
+   10. Congestion Control ..........................................  67
+   11. RTP over Network and Transport Protocols ....................  68
+   12. Summary of Protocol Constants ...............................  69
+       12.1 RTCP Packet Types ......................................  70
+       12.2 SDES Types .............................................  70
+   13. RTP Profiles and Payload Format Specifications ..............  71
+   14. Security Considerations .....................................  73
+   15. IANA Considerations .........................................  73
+   16. Intellectual Property Rights Statement ......................  74
+   17. Acknowledgments .............................................  74
+   Appendix A.   Algorithms ........................................  75
+   Appendix A.1  RTP Data Header Validity Checks ...................  78
+   Appendix A.2  RTCP Header Validity Checks .......................  82
+   Appendix A.3  Determining Number of Packets Expected and Lost ...  83
+   Appendix A.4  Generating RTCP SDES Packets ......................  84
+   Appendix A.5  Parsing RTCP SDES Packets .........................  85
+   Appendix A.6  Generating a Random 32-bit Identifier .............  85
+   Appendix A.7  Computing the RTCP Transmission Interval ..........  87
+   Appendix A.8  Estimating the Interarrival Jitter ................  94
+   Appendix B.   Changes from RFC 1889 .............................  95
+   References ...................................................... 100
+   Normative References ............................................ 100
+   Informative References .......................................... 100
+   Authors' Addresses .............................................. 103
+   Full Copyright Statement ........................................ 104
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 3]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+1. Introduction
+
+   This memorandum specifies the real-time transport protocol (RTP),
+   which provides end-to-end delivery services for data with real-time
+   characteristics, such as interactive audio and video.  Those services
+   include payload type identification, sequence numbering, timestamping
+   and delivery monitoring.  Applications typically run RTP on top of
+   UDP to make use of its multiplexing and checksum services; both
+   protocols contribute parts of the transport protocol functionality.
+   However, RTP may be used with other suitable underlying network or
+   transport protocols (see Section 11).  RTP supports data transfer to
+   multiple destinations using multicast distribution if provided by the
+   underlying network.
+
+   Note that RTP itself does not provide any mechanism to ensure timely
+   delivery or provide other quality-of-service guarantees, but relies
+   on lower-layer services to do so.  It does not guarantee delivery or
+   prevent out-of-order delivery, nor does it assume that the underlying
+   network is reliable and delivers packets in sequence.  The sequence
+   numbers included in RTP allow the receiver to reconstruct the
+   sender's packet sequence, but sequence numbers might also be used to
+   determine the proper location of a packet, for example in video
+   decoding, without necessarily decoding packets in sequence.
+
+   While RTP is primarily designed to satisfy the needs of multi-
+   participant multimedia conferences, it is not limited to that
+   particular application.  Storage of continuous data, interactive
+   distributed simulation, active badge, and control and measurement
+   applications may also find RTP applicable.
+
+   This document defines RTP, consisting of two closely-linked parts:
+
+   o  the real-time transport protocol (RTP), to carry data that has
+      real-time properties.
+
+   o  the RTP control protocol (RTCP), to monitor the quality of service
+      and to convey information about the participants in an on-going
+      session.  The latter aspect of RTCP may be sufficient for "loosely
+      controlled" sessions, i.e., where there is no explicit membership
+      control and set-up, but it is not necessarily intended to support
+      all of an application's control communication requirements.  This
+      functionality may be fully or partially subsumed by a separate
+      session control protocol, which is beyond the scope of this
+      document.
+
+   RTP represents a new style of protocol following the principles of
+   application level framing and integrated layer processing proposed by
+   Clark and Tennenhouse [10].  That is, RTP is intended to be malleable
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 4]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   to provide the information required by a particular application and
+   will often be integrated into the application processing rather than
+   being implemented as a separate layer.  RTP is a protocol framework
+   that is deliberately not complete.  This document specifies those
+   functions expected to be common across all the applications for which
+   RTP would be appropriate.  Unlike conventional protocols in which
+   additional functions might be accommodated by making the protocol
+   more general or by adding an option mechanism that would require
+   parsing, RTP is intended to be tailored through modifications and/or
+   additions to the headers as needed.  Examples are given in Sections
+   5.3 and 6.4.3.
+
+   Therefore, in addition to this document, a complete specification of
+   RTP for a particular application will require one or more companion
+   documents (see Section 13):
+
+   o  a profile specification document, which defines a set of payload
+      type codes and their mapping to payload formats (e.g., media
+      encodings).  A profile may also define extensions or modifications
+      to RTP that are specific to a particular class of applications.
+      Typically an application will operate under only one profile.  A
+      profile for audio and video data may be found in the companion RFC
+      3551 [1].
+
+   o  payload format specification documents, which define how a
+      particular payload, such as an audio or video encoding, is to be
+      carried in RTP.
+
+   A discussion of real-time services and algorithms for their
+   implementation as well as background discussion on some of the RTP
+   design decisions can be found in [11].
+
+1.1 Terminology
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in BCP 14, RFC 2119 [2]
+   and indicate requirement levels for compliant RTP implementations.
+
+2. RTP Use Scenarios
+
+   The following sections describe some aspects of the use of RTP.  The
+   examples were chosen to illustrate the basic operation of
+   applications using RTP, not to limit what RTP may be used for.  In
+   these examples, RTP is carried on top of IP and UDP, and follows the
+   conventions established by the profile for audio and video specified
+   in the companion RFC 3551.
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 5]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+2.1 Simple Multicast Audio Conference
+
+   A working group of the IETF meets to discuss the latest protocol
+   document, using the IP multicast services of the Internet for voice
+   communications.  Through some allocation mechanism the working group
+   chair obtains a multicast group address and pair of ports.  One port
+   is used for audio data, and the other is used for control (RTCP)
+   packets.  This address and port information is distributed to the
+   intended participants.  If privacy is desired, the data and control
+   packets may be encrypted as specified in Section 9.1, in which case
+   an encryption key must also be generated and distributed.  The exact
+   details of these allocation and distribution mechanisms are beyond
+   the scope of RTP.
+
+   The audio conferencing application used by each conference
+   participant sends audio data in small chunks of, say, 20 ms duration.
+   Each chunk of audio data is preceded by an RTP header; RTP header and
+   data are in turn contained in a UDP packet.  The RTP header indicates
+   what type of audio encoding (such as PCM, ADPCM or LPC) is contained
+   in each packet so that senders can change the encoding during a
+   conference, for example, to accommodate a new participant that is
+   connected through a low-bandwidth link or react to indications of
+   network congestion.
+
+   The Internet, like other packet networks, occasionally loses and
+   reorders packets and delays them by variable amounts of time.  To
+   cope with these impairments, the RTP header contains timing
+   information and a sequence number that allow the receivers to
+   reconstruct the timing produced by the source, so that in this
+   example, chunks of audio are contiguously played out the speaker
+   every 20 ms.  This timing reconstruction is performed separately for
+   each source of RTP packets in the conference.  The sequence number
+   can also be used by the receiver to estimate how many packets are
+   being lost.
+
+   Since members of the working group join and leave during the
+   conference, it is useful to know who is participating at any moment
+   and how well they are receiving the audio data.  For that purpose,
+   each instance of the audio application in the conference periodically
+   multicasts a reception report plus the name of its user on the RTCP
+   (control) port.  The reception report indicates how well the current
+   speaker is being received and may be used to control adaptive
+   encodings.  In addition to the user name, other identifying
+   information may also be included subject to control bandwidth limits.
+   A site sends the RTCP BYE packet (Section 6.6) when it leaves the
+   conference.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 6]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+2.2 Audio and Video Conference
+
+   If both audio and video media are used in a conference, they are
+   transmitted as separate RTP sessions.  That is, separate RTP and RTCP
+   packets are transmitted for each medium using two different UDP port
+   pairs and/or multicast addresses.  There is no direct coupling at the
+   RTP level between the audio and video sessions, except that a user
+   participating in both sessions should use the same distinguished
+   (canonical) name in the RTCP packets for both so that the sessions
+   can be associated.
+
+   One motivation for this separation is to allow some participants in
+   the conference to receive only one medium if they choose.  Further
+   explanation is given in Section 5.2.  Despite the separation,
+   synchronized playback of a source's audio and video can be achieved
+   using timing information carried in the RTCP packets for both
+   sessions.
+
+2.3 Mixers and Translators
+
+   So far, we have assumed that all sites want to receive media data in
+   the same format.  However, this may not always be appropriate.
+   Consider the case where participants in one area are connected
+   through a low-speed link to the majority of the conference
+   participants who enjoy high-speed network access.  Instead of forcing
+   everyone to use a lower-bandwidth, reduced-quality audio encoding, an
+   RTP-level relay called a mixer may be placed near the low-bandwidth
+   area.  This mixer resynchronizes incoming audio packets to
+   reconstruct the constant 20 ms spacing generated by the sender, mixes
+   these reconstructed audio streams into a single stream, translates
+   the audio encoding to a lower-bandwidth one and forwards the lower-
+   bandwidth packet stream across the low-speed link.  These packets
+   might be unicast to a single recipient or multicast on a different
+   address to multiple recipients.  The RTP header includes a means for
+   mixers to identify the sources that contributed to a mixed packet so
+   that correct talker indication can be provided at the receivers.
+
+   Some of the intended participants in the audio conference may be
+   connected with high bandwidth links but might not be directly
+   reachable via IP multicast.  For example, they might be behind an
+   application-level firewall that will not let any IP packets pass.
+   For these sites, mixing may not be necessary, in which case another
+   type of RTP-level relay called a translator may be used.  Two
+   translators are installed, one on either side of the firewall, with
+   the outside one funneling all multicast packets received through a
+   secure connection to the translator inside the firewall.  The
+   translator inside the firewall sends them again as multicast packets
+   to a multicast group restricted to the site's internal network.
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 7]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Mixers and translators may be designed for a variety of purposes.  An
+   example is a video mixer that scales the images of individual people
+   in separate video streams and composites them into one video stream
+   to simulate a group scene.  Other examples of translation include the
+   connection of a group of hosts speaking only IP/UDP to a group of
+   hosts that understand only ST-II, or the packet-by-packet encoding
+   translation of video streams from individual sources without
+   resynchronization or mixing.  Details of the operation of mixers and
+   translators are given in Section 7.
+
+2.4 Layered Encodings
+
+   Multimedia applications should be able to adjust the transmission
+   rate to match the capacity of the receiver or to adapt to network
+   congestion.  Many implementations place the responsibility of rate-
+   adaptivity at the source.  This does not work well with multicast
+   transmission because of the conflicting bandwidth requirements of
+   heterogeneous receivers.  The result is often a least-common
+   denominator scenario, where the smallest pipe in the network mesh
+   dictates the quality and fidelity of the overall live multimedia
+   "broadcast".
+
+   Instead, responsibility for rate-adaptation can be placed at the
+   receivers by combining a layered encoding with a layered transmission
+   system.  In the context of RTP over IP multicast, the source can
+   stripe the progressive layers of a hierarchically represented signal
+   across multiple RTP sessions each carried on its own multicast group.
+   Receivers can then adapt to network heterogeneity and control their
+   reception bandwidth by joining only the appropriate subset of the
+   multicast groups.
+
+   Details of the use of RTP with layered encodings are given in
+   Sections 6.3.9, 8.3 and 11.
+
+3. Definitions
+
+   RTP payload: The data transported by RTP in a packet, for
+      example audio samples or compressed video data.  The payload
+      format and interpretation are beyond the scope of this document.
+
+   RTP packet: A data packet consisting of the fixed RTP header, a
+      possibly empty list of contributing sources (see below), and the
+      payload data.  Some underlying protocols may require an
+      encapsulation of the RTP packet to be defined.  Typically one
+      packet of the underlying protocol contains a single RTP packet,
+      but several RTP packets MAY be contained if permitted by the
+      encapsulation method (see Section 11).
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 8]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   RTCP packet: A control packet consisting of a fixed header part
+      similar to that of RTP data packets, followed by structured
+      elements that vary depending upon the RTCP packet type.  The
+      formats are defined in Section 6.  Typically, multiple RTCP
+      packets are sent together as a compound RTCP packet in a single
+      packet of the underlying protocol; this is enabled by the length
+      field in the fixed header of each RTCP packet.
+
+   Port: The "abstraction that transport protocols use to
+      distinguish among multiple destinations within a given host
+      computer.  TCP/IP protocols identify ports using small positive
+      integers." [12] The transport selectors (TSEL) used by the OSI
+      transport layer are equivalent to ports.  RTP depends upon the
+      lower-layer protocol to provide some mechanism such as ports to
+      multiplex the RTP and RTCP packets of a session.
+
+   Transport address: The combination of a network address and port
+      that identifies a transport-level endpoint, for example an IP
+      address and a UDP port.  Packets are transmitted from a source
+      transport address to a destination transport address.
+
+   RTP media type: An RTP media type is the collection of payload
+      types which can be carried within a single RTP session.  The RTP
+      Profile assigns RTP media types to RTP payload types.
+
+   Multimedia session: A set of concurrent RTP sessions among a
+      common group of participants.  For example, a videoconference
+      (which is a multimedia session) may contain an audio RTP session
+      and a video RTP session.
+
+   RTP session: An association among a set of participants
+      communicating with RTP.  A participant may be involved in multiple
+      RTP sessions at the same time.  In a multimedia session, each
+      medium is typically carried in a separate RTP session with its own
+      RTCP packets unless the the encoding itself multiplexes multiple
+      media into a single data stream.  A participant distinguishes
+      multiple RTP sessions by reception of different sessions using
+      different pairs of destination transport addresses, where a pair
+      of transport addresses comprises one network address plus a pair
+      of ports for RTP and RTCP.  All participants in an RTP session may
+      share a common destination transport address pair, as in the case
+      of IP multicast, or the pairs may be different for each
+      participant, as in the case of individual unicast network
+      addresses and port pairs.  In the unicast case, a participant may
+      receive from all other participants in the session using the same
+      pair of ports, or may use a distinct pair of ports for each.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 9]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      The distinguishing feature of an RTP session is that each
+      maintains a full, separate space of SSRC identifiers (defined
+      next).  The set of participants included in one RTP session
+      consists of those that can receive an SSRC identifier transmitted
+      by any one of the participants either in RTP as the SSRC or a CSRC
+      (also defined below) or in RTCP.  For example, consider a three-
+      party conference implemented using unicast UDP with each
+      participant receiving from the other two on separate port pairs.
+      If each participant sends RTCP feedback about data received from
+      one other participant only back to that participant, then the
+      conference is composed of three separate point-to-point RTP
+      sessions.  If each participant provides RTCP feedback about its
+      reception of one other participant to both of the other
+      participants, then the conference is composed of one multi-party
+      RTP session.  The latter case simulates the behavior that would
+      occur with IP multicast communication among the three
+      participants.
+
+      The RTP framework allows the variations defined here, but a
+      particular control protocol or application design will usually
+      impose constraints on these variations.
+
+   Synchronization source (SSRC): The source of a stream of RTP
+      packets, identified by a 32-bit numeric SSRC identifier carried in
+      the RTP header so as not to be dependent upon the network address.
+      All packets from a synchronization source form part of the same
+      timing and sequence number space, so a receiver groups packets by
+      synchronization source for playback.  Examples of synchronization
+      sources include the sender of a stream of packets derived from a
+      signal source such as a microphone or a camera, or an RTP mixer
+      (see below).  A synchronization source may change its data format,
+      e.g., audio encoding, over time.  The SSRC identifier is a
+      randomly chosen value meant to be globally unique within a
+      particular RTP session (see Section 8).  A participant need not
+      use the same SSRC identifier for all the RTP sessions in a
+      multimedia session; the binding of the SSRC identifiers is
+      provided through RTCP (see Section 6.5.1).  If a participant
+      generates multiple streams in one RTP session, for example from
+      separate video cameras, each MUST be identified as a different
+      SSRC.
+
+   Contributing source (CSRC): A source of a stream of RTP packets
+      that has contributed to the combined stream produced by an RTP
+      mixer (see below).  The mixer inserts a list of the SSRC
+      identifiers of the sources that contributed to the generation of a
+      particular packet into the RTP header of that packet.  This list
+      is called the CSRC list.  An example application is audio
+      conferencing where a mixer indicates all the talkers whose speech
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 10]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      was combined to produce the outgoing packet, allowing the receiver
+      to indicate the current talker, even though all the audio packets
+      contain the same SSRC identifier (that of the mixer).
+
+   End system: An application that generates the content to be sent
+      in RTP packets and/or consumes the content of received RTP
+      packets.  An end system can act as one or more synchronization
+      sources in a particular RTP session, but typically only one.
+
+   Mixer: An intermediate system that receives RTP packets from one
+      or more sources, possibly changes the data format, combines the
+      packets in some manner and then forwards a new RTP packet.  Since
+      the timing among multiple input sources will not generally be
+      synchronized, the mixer will make timing adjustments among the
+      streams and generate its own timing for the combined stream.
+      Thus, all data packets originating from a mixer will be identified
+      as having the mixer as their synchronization source.
+
+   Translator: An intermediate system that forwards RTP packets
+      with their synchronization source identifier intact.  Examples of
+      translators include devices that convert encodings without mixing,
+      replicators from multicast to unicast, and application-level
+      filters in firewalls.
+
+   Monitor: An application that receives RTCP packets sent by
+      participants in an RTP session, in particular the reception
+      reports, and estimates the current quality of service for
+      distribution monitoring, fault diagnosis and long-term statistics.
+      The monitor function is likely to be built into the application(s)
+      participating in the session, but may also be a separate
+      application that does not otherwise participate and does not send
+      or receive the RTP data packets (since they are on a separate
+      port).  These are called third-party monitors.  It is also
+      acceptable for a third-party monitor to receive the RTP data
+      packets but not send RTCP packets or otherwise be counted in the
+      session.
+
+   Non-RTP means: Protocols and mechanisms that may be needed in
+      addition to RTP to provide a usable service.  In particular, for
+      multimedia conferences, a control protocol may distribute
+      multicast addresses and keys for encryption, negotiate the
+      encryption algorithm to be used, and define dynamic mappings
+      between RTP payload type values and the payload formats they
+      represent for formats that do not have a predefined payload type
+      value.  Examples of such protocols include the Session Initiation
+      Protocol (SIP) (RFC 3261 [13]), ITU Recommendation H.323 [14] and
+      applications using SDP (RFC 2327 [15]), such as RTSP (RFC 2326
+      [16]).  For simple
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 11]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      applications, electronic mail or a conference database may also be
+      used.  The specification of such protocols and mechanisms is
+      outside the scope of this document.
+
+4. Byte Order, Alignment, and Time Format
+
+   All integer fields are carried in network byte order, that is, most
+   significant byte (octet) first.  This byte order is commonly known as
+   big-endian.  The transmission order is described in detail in [3].
+   Unless otherwise noted, numeric constants are in decimal (base 10).
+
+   All header data is aligned to its natural length, i.e., 16-bit fields
+   are aligned on even offsets, 32-bit fields are aligned at offsets
+   divisible by four, etc.  Octets designated as padding have the value
+   zero.
+
+   Wallclock time (absolute date and time) is represented using the
+   timestamp format of the Network Time Protocol (NTP), which is in
+   seconds relative to 0h UTC on 1 January 1900 [4].  The full
+   resolution NTP timestamp is a 64-bit unsigned fixed-point number with
+   the integer part in the first 32 bits and the fractional part in the
+   last 32 bits.  In some fields where a more compact representation is
+   appropriate, only the middle 32 bits are used; that is, the low 16
+   bits of the integer part and the high 16 bits of the fractional part.
+   The high 16 bits of the integer part must be determined
+   independently.
+
+   An implementation is not required to run the Network Time Protocol in
+   order to use RTP.  Other time sources, or none at all, may be used
+   (see the description of the NTP timestamp field in Section 6.4.1).
+   However, running NTP may be useful for synchronizing streams
+   transmitted from separate hosts.
+
+   The NTP timestamp will wrap around to zero some time in the year
+   2036, but for RTP purposes, only differences between pairs of NTP
+   timestamps are used.  So long as the pairs of timestamps can be
+   assumed to be within 68 years of each other, using modular arithmetic
+   for subtractions and comparisons makes the wraparound irrelevant.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 12]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+5. RTP Data Transfer Protocol
+
+5.1 RTP Fixed Header Fields
+
+   The RTP header has the following format:
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |V=2|P|X|  CC   |M|     PT      |       sequence number         |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                           timestamp                           |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |           synchronization source (SSRC) identifier            |
+   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+   |            contributing source (CSRC) identifiers             |
+   |                             ....                              |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The first twelve octets are present in every RTP packet, while the
+   list of CSRC identifiers is present only when inserted by a mixer.
+   The fields have the following meaning:
+
+   version (V): 2 bits
+      This field identifies the version of RTP.  The version defined by
+      this specification is two (2).  (The value 1 is used by the first
+      draft version of RTP and the value 0 is used by the protocol
+      initially implemented in the "vat" audio tool.)
+
+   padding (P): 1 bit
+      If the padding bit is set, the packet contains one or more
+      additional padding octets at the end which are not part of the
+      payload.  The last octet of the padding contains a count of how
+      many padding octets should be ignored, including itself.  Padding
+      may be needed by some encryption algorithms with fixed block sizes
+      or for carrying several RTP packets in a lower-layer protocol data
+      unit.
+
+   extension (X): 1 bit
+      If the extension bit is set, the fixed header MUST be followed by
+      exactly one header extension, with a format defined in Section
+      5.3.1.
+
+   CSRC count (CC): 4 bits
+      The CSRC count contains the number of CSRC identifiers that follow
+      the fixed header.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 13]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   marker (M): 1 bit
+      The interpretation of the marker is defined by a profile.  It is
+      intended to allow significant events such as frame boundaries to
+      be marked in the packet stream.  A profile MAY define additional
+      marker bits or specify that there is no marker bit by changing the
+      number of bits in the payload type field (see Section 5.3).
+
+   payload type (PT): 7 bits
+      This field identifies the format of the RTP payload and determines
+      its interpretation by the application.  A profile MAY specify a
+      default static mapping of payload type codes to payload formats.
+      Additional payload type codes MAY be defined dynamically through
+      non-RTP means (see Section 3).  A set of default mappings for
+      audio and video is specified in the companion RFC 3551 [1].  An
+      RTP source MAY change the payload type during a session, but this
+      field SHOULD NOT be used for multiplexing separate media streams
+      (see Section 5.2).
+
+      A receiver MUST ignore packets with payload types that it does not
+      understand.
+
+   sequence number: 16 bits
+      The sequence number increments by one for each RTP data packet
+      sent, and may be used by the receiver to detect packet loss and to
+      restore packet sequence.  The initial value of the sequence number
+      SHOULD be random (unpredictable) to make known-plaintext attacks
+      on encryption more difficult, even if the source itself does not
+      encrypt according to the method in Section 9.1, because the
+      packets may flow through a translator that does.  Techniques for
+      choosing unpredictable numbers are discussed in [17].
+
+   timestamp: 32 bits
+      The timestamp reflects the sampling instant of the first octet in
+      the RTP data packet.  The sampling instant MUST be derived from a
+      clock that increments monotonically and linearly in time to allow
+      synchronization and jitter calculations (see Section 6.4.1).  The
+      resolution of the clock MUST be sufficient for the desired
+      synchronization accuracy and for measuring packet arrival jitter
+      (one tick per video frame is typically not sufficient).  The clock
+      frequency is dependent on the format of data carried as payload
+      and is specified statically in the profile or payload format
+      specification that defines the format, or MAY be specified
+      dynamically for payload formats defined through non-RTP means.  If
+      RTP packets are generated periodically, the nominal sampling
+      instant as determined from the sampling clock is to be used, not a
+      reading of the system clock.  As an example, for fixed-rate audio
+      the timestamp clock would likely increment by one for each
+      sampling period.  If an audio application reads blocks covering
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 14]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      160 sampling periods from the input device, the timestamp would be
+      increased by 160 for each such block, regardless of whether the
+      block is transmitted in a packet or dropped as silent.
+
+      The initial value of the timestamp SHOULD be random, as for the
+      sequence number.  Several consecutive RTP packets will have equal
+      timestamps if they are (logically) generated at once, e.g., belong
+      to the same video frame.  Consecutive RTP packets MAY contain
+      timestamps that are not monotonic if the data is not transmitted
+      in the order it was sampled, as in the case of MPEG interpolated
+      video frames.  (The sequence numbers of the packets as transmitted
+      will still be monotonic.)
+
+      RTP timestamps from different media streams may advance at
+      different rates and usually have independent, random offsets.
+      Therefore, although these timestamps are sufficient to reconstruct
+      the timing of a single stream, directly comparing RTP timestamps
+      from different media is not effective for synchronization.
+      Instead, for each medium the RTP timestamp is related to the
+      sampling instant by pairing it with a timestamp from a reference
+      clock (wallclock) that represents the time when the data
+      corresponding to the RTP timestamp was sampled.  The reference
+      clock is shared by all media to be synchronized.  The timestamp
+      pairs are not transmitted in every data packet, but at a lower
+      rate in RTCP SR packets as described in Section 6.4.
+
+      The sampling instant is chosen as the point of reference for the
+      RTP timestamp because it is known to the transmitting endpoint and
+      has a common definition for all media, independent of encoding
+      delays or other processing.  The purpose is to allow synchronized
+      presentation of all media sampled at the same time.
+
+      Applications transmitting stored data rather than data sampled in
+      real time typically use a virtual presentation timeline derived
+      from wallclock time to determine when the next frame or other unit
+      of each medium in the stored data should be presented.  In this
+      case, the RTP timestamp would reflect the presentation time for
+      each unit.  That is, the RTP timestamp for each unit would be
+      related to the wallclock time at which the unit becomes current on
+      the virtual presentation timeline.  Actual presentation occurs
+      some time later as determined by the receiver.
+
+      An example describing live audio narration of prerecorded video
+      illustrates the significance of choosing the sampling instant as
+      the reference point.  In this scenario, the video would be
+      presented locally for the narrator to view and would be
+      simultaneously transmitted using RTP.  The "sampling instant" of a
+      video frame transmitted in RTP would be established by referencing
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 15]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      its timestamp to the wallclock time when that video frame was
+      presented to the narrator.  The sampling instant for the audio RTP
+      packets containing the narrator's speech would be established by
+      referencing the same wallclock time when the audio was sampled.
+      The audio and video may even be transmitted by different hosts if
+      the reference clocks on the two hosts are synchronized by some
+      means such as NTP.  A receiver can then synchronize presentation
+      of the audio and video packets by relating their RTP timestamps
+      using the timestamp pairs in RTCP SR packets.
+
+   SSRC: 32 bits
+      The SSRC field identifies the synchronization source.  This
+      identifier SHOULD be chosen randomly, with the intent that no two
+      synchronization sources within the same RTP session will have the
+      same SSRC identifier.  An example algorithm for generating a
+      random identifier is presented in Appendix A.6.  Although the
+      probability of multiple sources choosing the same identifier is
+      low, all RTP implementations must be prepared to detect and
+      resolve collisions.  Section 8 describes the probability of
+      collision along with a mechanism for resolving collisions and
+      detecting RTP-level forwarding loops based on the uniqueness of
+      the SSRC identifier.  If a source changes its source transport
+      address, it must also choose a new SSRC identifier to avoid being
+      interpreted as a looped source (see Section 8.2).
+
+   CSRC list: 0 to 15 items, 32 bits each
+      The CSRC list identifies the contributing sources for the payload
+      contained in this packet.  The number of identifiers is given by
+      the CC field.  If there are more than 15 contributing sources,
+      only 15 can be identified.  CSRC identifiers are inserted by
+      mixers (see Section 7.1), using the SSRC identifiers of
+      contributing sources.  For example, for audio packets the SSRC
+      identifiers of all sources that were mixed together to create a
+      packet are listed, allowing correct talker indication at the
+      receiver.
+
+5.2 Multiplexing RTP Sessions
+
+   For efficient protocol processing, the number of multiplexing points
+   should be minimized, as described in the integrated layer processing
+   design principle [10].  In RTP, multiplexing is provided by the
+   destination transport address (network address and port number) which
+   is different for each RTP session.  For example, in a teleconference
+   composed of audio and video media encoded separately, each medium
+   SHOULD be carried in a separate RTP session with its own destination
+   transport address.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 16]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Separate audio and video streams SHOULD NOT be carried in a single
+   RTP session and demultiplexed based on the payload type or SSRC
+   fields.  Interleaving packets with different RTP media types but
+   using the same SSRC would introduce several problems:
+
+   1. If, say, two audio streams shared the same RTP session and the
+      same SSRC value, and one were to change encodings and thus acquire
+      a different RTP payload type, there would be no general way of
+      identifying which stream had changed encodings.
+
+   2. An SSRC is defined to identify a single timing and sequence number
+      space.  Interleaving multiple payload types would require
+      different timing spaces if the media clock rates differ and would
+      require different sequence number spaces to tell which payload
+      type suffered packet loss.
+
+   3. The RTCP sender and receiver reports (see Section 6.4) can only
+      describe one timing and sequence number space per SSRC and do not
+      carry a payload type field.
+
+   4. An RTP mixer would not be able to combine interleaved streams of
+      incompatible media into one stream.
+
+   5. Carrying multiple media in one RTP session precludes: the use of
+      different network paths or network resource allocations if
+      appropriate; reception of a subset of the media if desired, for
+      example just audio if video would exceed the available bandwidth;
+      and receiver implementations that use separate processes for the
+      different media, whereas using separate RTP sessions permits
+      either single- or multiple-process implementations.
+
+   Using a different SSRC for each medium but sending them in the same
+   RTP session would avoid the first three problems but not the last
+   two.
+
+   On the other hand, multiplexing multiple related sources of the same
+   medium in one RTP session using different SSRC values is the norm for
+   multicast sessions.  The problems listed above don't apply: an RTP
+   mixer can combine multiple audio sources, for example, and the same
+   treatment is applicable for all of them.  It may also be appropriate
+   to multiplex streams of the same medium using different SSRC values
+   in other scenarios where the last two problems do not apply.
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 17]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+5.3 Profile-Specific Modifications to the RTP Header
+
+   The existing RTP data packet header is believed to be complete for
+   the set of functions required in common across all the application
+   classes that RTP might support.  However, in keeping with the ALF
+   design principle, the header MAY be tailored through modifications or
+   additions defined in a profile specification while still allowing
+   profile-independent monitoring and recording tools to function.
+
+   o  The marker bit and payload type field carry profile-specific
+      information, but they are allocated in the fixed header since many
+      applications are expected to need them and might otherwise have to
+      add another 32-bit word just to hold them.  The octet containing
+      these fields MAY be redefined by a profile to suit different
+      requirements, for example with more or fewer marker bits.  If
+      there are any marker bits, one SHOULD be located in the most
+      significant bit of the octet since profile-independent monitors
+      may be able to observe a correlation between packet loss patterns
+      and the marker bit.
+
+   o  Additional information that is required for a particular payload
+      format, such as a video encoding, SHOULD be carried in the payload
+      section of the packet.  This might be in a header that is always
+      present at the start of the payload section, or might be indicated
+      by a reserved value in the data pattern.
+
+   o  If a particular class of applications needs additional
+      functionality independent of payload format, the profile under
+      which those applications operate SHOULD define additional fixed
+      fields to follow immediately after the SSRC field of the existing
+      fixed header.  Those applications will be able to quickly and
+      directly access the additional fields while profile-independent
+      monitors or recorders can still process the RTP packets by
+      interpreting only the first twelve octets.
+
+   If it turns out that additional functionality is needed in common
+   across all profiles, then a new version of RTP should be defined to
+   make a permanent change to the fixed header.
+
+5.3.1 RTP Header Extension
+
+   An extension mechanism is provided to allow individual
+   implementations to experiment with new payload-format-independent
+   functions that require additional information to be carried in the
+   RTP data packet header.  This mechanism is designed so that the
+   header extension may be ignored by other interoperating
+   implementations that have not been extended.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 18]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Note that this header extension is intended only for limited use.
+   Most potential uses of this mechanism would be better done another
+   way, using the methods described in the previous section.  For
+   example, a profile-specific extension to the fixed header is less
+   expensive to process because it is not conditional nor in a variable
+   location.  Additional information required for a particular payload
+   format SHOULD NOT use this header extension, but SHOULD be carried in
+   the payload section of the packet.
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |      defined by profile       |           length              |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                        header extension                       |
+   |                             ....                              |
+
+   If the X bit in the RTP header is one, a variable-length header
+   extension MUST be appended to the RTP header, following the CSRC list
+   if present.  The header extension contains a 16-bit length field that
+   counts the number of 32-bit words in the extension, excluding the
+   four-octet extension header (therefore zero is a valid length).  Only
+   a single extension can be appended to the RTP data header.  To allow
+   multiple interoperating implementations to each experiment
+   independently with different header extensions, or to allow a
+   particular implementation to experiment with more than one type of
+   header extension, the first 16 bits of the header extension are left
+   open for distinguishing identifiers or parameters.  The format of
+   these 16 bits is to be defined by the profile specification under
+   which the implementations are operating.  This RTP specification does
+   not define any header extensions itself.
+
+6. RTP Control Protocol -- RTCP
+
+   The RTP control protocol (RTCP) is based on the periodic transmission
+   of control packets to all participants in the session, using the same
+   distribution mechanism as the data packets.  The underlying protocol
+   MUST provide multiplexing of the data and control packets, for
+   example using separate port numbers with UDP.  RTCP performs four
+   functions:
+
+   1. The primary function is to provide feedback on the quality of the
+      data distribution.  This is an integral part of the RTP's role as
+      a transport protocol and is related to the flow and congestion
+      control functions of other transport protocols (see Section 10 on
+      the requirement for congestion control).  The feedback may be
+      directly useful for control of adaptive encodings [18,19], but
+      experiments with IP multicasting have shown that it is also
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 19]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      critical to get feedback from the receivers to diagnose faults in
+      the distribution.  Sending reception feedback reports to all
+      participants allows one who is observing problems to evaluate
+      whether those problems are local or global.  With a distribution
+      mechanism like IP multicast, it is also possible for an entity
+      such as a network service provider who is not otherwise involved
+      in the session to receive the feedback information and act as a
+      third-party monitor to diagnose network problems.  This feedback
+      function is performed by the RTCP sender and receiver reports,
+      described below in Section 6.4.
+
+   2. RTCP carries a persistent transport-level identifier for an RTP
+      source called the canonical name or CNAME, Section 6.5.1.  Since
+      the SSRC identifier may change if a conflict is discovered or a
+      program is restarted, receivers require the CNAME to keep track of
+      each participant.  Receivers may also require the CNAME to
+      associate multiple data streams from a given participant in a set
+      of related RTP sessions, for example to synchronize audio and
+      video.  Inter-media synchronization also requires the NTP and RTP
+      timestamps included in RTCP packets by data senders.
+
+   3. The first two functions require that all participants send RTCP
+      packets, therefore the rate must be controlled in order for RTP to
+      scale up to a large number of participants.  By having each
+      participant send its control packets to all the others, each can
+      independently observe the number of participants.  This number is
+      used to calculate the rate at which the packets are sent, as
+      explained in Section 6.2.
+
+   4. A fourth, OPTIONAL function is to convey minimal session control
+      information, for example participant identification to be
+      displayed in the user interface.  This is most likely to be useful
+      in "loosely controlled" sessions where participants enter and
+      leave without membership control or parameter negotiation.  RTCP
+      serves as a convenient channel to reach all the participants, but
+      it is not necessarily expected to support all the control
+      communication requirements of an application.  A higher-level
+      session control protocol, which is beyond the scope of this
+      document, may be needed.
+
+   Functions 1-3 SHOULD be used in all environments, but particularly in
+   the IP multicast environment.  RTP application designers SHOULD avoid
+   mechanisms that can only work in unicast mode and will not scale to
+   larger numbers.  Transmission of RTCP MAY be controlled separately
+   for senders and receivers, as described in Section 6.2, for cases
+   such as unidirectional links where feedback from receivers is not
+   possible.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 20]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Non-normative note:  In the multicast routing approach
+      called Source-Specific Multicast (SSM), there is only one sender
+      per "channel" (a source address, group address pair), and
+      receivers (except for the channel source) cannot use multicast to
+      communicate directly with other channel members.  The
+      recommendations here accommodate SSM only through Section 6.2's
+      option of turning off receivers' RTCP entirely.  Future work will
+      specify adaptation of RTCP for SSM so that feedback from receivers
+      can be maintained.
+
+6.1 RTCP Packet Format
+
+   This specification defines several RTCP packet types to carry a
+   variety of control information:
+
+   SR:   Sender report, for transmission and reception statistics from
+         participants that are active senders
+
+   RR:   Receiver report, for reception statistics from participants
+         that are not active senders and in combination with SR for
+         active senders reporting on more than 31 sources
+
+   SDES: Source description items, including CNAME
+
+   BYE:  Indicates end of participation
+
+   APP:  Application-specific functions
+
+   Each RTCP packet begins with a fixed part similar to that of RTP data
+   packets, followed by structured elements that MAY be of variable
+   length according to the packet type but MUST end on a 32-bit
+   boundary.  The alignment requirement and a length field in the fixed
+   part of each packet are included to make RTCP packets "stackable".
+   Multiple RTCP packets can be concatenated without any intervening
+   separators to form a compound RTCP packet that is sent in a single
+   packet of the lower layer protocol, for example UDP.  There is no
+   explicit count of individual RTCP packets in the compound packet
+   since the lower layer protocols are expected to provide an overall
+   length to determine the end of the compound packet.
+
+   Each individual RTCP packet in the compound packet may be processed
+   independently with no requirements upon the order or combination of
+   packets.  However, in order to perform the functions of the protocol,
+   the following constraints are imposed:
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 21]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  Reception statistics (in SR or RR) should be sent as often as
+      bandwidth constraints will allow to maximize the resolution of the
+      statistics, therefore each periodically transmitted compound RTCP
+      packet MUST include a report packet.
+
+   o  New receivers need to receive the CNAME for a source as soon as
+      possible to identify the source and to begin associating media for
+      purposes such as lip-sync, so each compound RTCP packet MUST also
+      include the SDES CNAME except when the compound RTCP packet is
+      split for partial encryption as described in Section 9.1.
+
+   o  The number of packet types that may appear first in the compound
+      packet needs to be limited to increase the number of constant bits
+      in the first word and the probability of successfully validating
+      RTCP packets against misaddressed RTP data packets or other
+      unrelated packets.
+
+   Thus, all RTCP packets MUST be sent in a compound packet of at least
+   two individual packets, with the following format:
+
+   Encryption prefix:  If and only if the compound packet is to be
+      encrypted according to the method in Section 9.1, it MUST be
+      prefixed by a random 32-bit quantity redrawn for every compound
+      packet transmitted.  If padding is required for the encryption, it
+      MUST be added to the last packet of the compound packet.
+
+   SR or RR:  The first RTCP packet in the compound packet MUST
+      always be a report packet to facilitate header validation as
+      described in Appendix A.2.  This is true even if no data has been
+      sent or received, in which case an empty RR MUST be sent, and even
+      if the only other RTCP packet in the compound packet is a BYE.
+
+   Additional RRs:  If the number of sources for which reception
+      statistics are being reported exceeds 31, the number that will fit
+      into one SR or RR packet, then additional RR packets SHOULD follow
+      the initial report packet.
+
+   SDES:  An SDES packet containing a CNAME item MUST be included
+      in each compound RTCP packet, except as noted in Section 9.1.
+      Other source description items MAY optionally be included if
+      required by a particular application, subject to bandwidth
+      constraints (see Section 6.3.9).
+
+   BYE or APP:  Other RTCP packet types, including those yet to be
+      defined, MAY follow in any order, except that BYE SHOULD be the
+      last packet sent with a given SSRC/CSRC.  Packet types MAY appear
+      more than once.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 22]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   An individual RTP participant SHOULD send only one compound RTCP
+   packet per report interval in order for the RTCP bandwidth per
+   participant to be estimated correctly (see Section 6.2), except when
+   the compound RTCP packet is split for partial encryption as described
+   in Section 9.1.  If there are too many sources to fit all the
+   necessary RR packets into one compound RTCP packet without exceeding
+   the maximum transmission unit (MTU) of the network path, then only
+   the subset that will fit into one MTU SHOULD be included in each
+   interval.  The subsets SHOULD be selected round-robin across multiple
+   intervals so that all sources are reported.
+
+   It is RECOMMENDED that translators and mixers combine individual RTCP
+   packets from the multiple sources they are forwarding into one
+   compound packet whenever feasible in order to amortize the packet
+   overhead (see Section 7).  An example RTCP compound packet as might
+   be produced by a mixer is shown in Fig. 1.  If the overall length of
+   a compound packet would exceed the MTU of the network path, it SHOULD
+   be segmented into multiple shorter compound packets to be transmitted
+   in separate packets of the underlying protocol.  This does not impair
+   the RTCP bandwidth estimation because each compound packet represents
+   at least one distinct participant.  Note that each of the compound
+   packets MUST begin with an SR or RR packet.
+
+   An implementation SHOULD ignore incoming RTCP packets with types
+   unknown to it.  Additional RTCP packet types may be registered with
+   the Internet Assigned Numbers Authority (IANA) as described in
+   Section 15.
+
+   if encrypted: random 32-bit integer
+   |
+   |[--------- packet --------][---------- packet ----------][-packet-]
+   |
+   |                receiver            chunk        chunk
+   V                reports           item  item   item  item
+   --------------------------------------------------------------------
+   R[SR #sendinfo #site1#site2][SDES #CNAME PHONE #CNAME LOC][BYE##why]
+   --------------------------------------------------------------------
+   |                                                                  |
+   |<-----------------------  compound packet ----------------------->|
+   |<--------------------------  UDP packet ------------------------->|
+
+   #: SSRC/CSRC identifier
+
+              Figure 1: Example of an RTCP compound packet
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 23]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.2 RTCP Transmission Interval
+
+   RTP is designed to allow an application to scale automatically over
+   session sizes ranging from a few participants to thousands.  For
+   example, in an audio conference the data traffic is inherently self-
+   limiting because only one or two people will speak at a time, so with
+   multicast distribution the data rate on any given link remains
+   relatively constant independent of the number of participants.
+   However, the control traffic is not self-limiting.  If the reception
+   reports from each participant were sent at a constant rate, the
+   control traffic would grow linearly with the number of participants.
+   Therefore, the rate must be scaled down by dynamically calculating
+   the interval between RTCP packet transmissions.
+
+   For each session, it is assumed that the data traffic is subject to
+   an aggregate limit called the "session bandwidth" to be divided among
+   the participants.  This bandwidth might be reserved and the limit
+   enforced by the network.  If there is no reservation, there may be
+   other constraints, depending on the environment, that establish the
+   "reasonable" maximum for the session to use, and that would be the
+   session bandwidth.  The session bandwidth may be chosen based on some
+   cost or a priori knowledge of the available network bandwidth for the
+   session.  It is somewhat independent of the media encoding, but the
+   encoding choice may be limited by the session bandwidth.  Often, the
+   session bandwidth is the sum of the nominal bandwidths of the senders
+   expected to be concurrently active.  For teleconference audio, this
+   number would typically be one sender's bandwidth.  For layered
+   encodings, each layer is a separate RTP session with its own session
+   bandwidth parameter.
+
+   The session bandwidth parameter is expected to be supplied by a
+   session management application when it invokes a media application,
+   but media applications MAY set a default based on the single-sender
+   data bandwidth for the encoding selected for the session.  The
+   application MAY also enforce bandwidth limits based on multicast
+   scope rules or other criteria.  All participants MUST use the same
+   value for the session bandwidth so that the same RTCP interval will
+   be calculated.
+
+   Bandwidth calculations for control and data traffic include lower-
+   layer transport and network protocols (e.g., UDP and IP) since that
+   is what the resource reservation system would need to know.  The
+   application can also be expected to know which of these protocols are
+   in use.  Link level headers are not included in the calculation since
+   the packet will be encapsulated with different link level headers as
+   it travels.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 24]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   The control traffic should be limited to a small and known fraction
+   of the session bandwidth: small so that the primary function of the
+   transport protocol to carry data is not impaired; known so that the
+   control traffic can be included in the bandwidth specification given
+   to a resource reservation protocol, and so that each participant can
+   independently calculate its share.  The control traffic bandwidth is
+   in addition to the session bandwidth for the data traffic.  It is
+   RECOMMENDED that the fraction of the session bandwidth added for RTCP
+   be fixed at 5%.  It is also RECOMMENDED that 1/4 of the RTCP
+   bandwidth be dedicated to participants that are sending data so that
+   in sessions with a large number of receivers but a small number of
+   senders, newly joining participants will more quickly receive the
+   CNAME for the sending sites.  When the proportion of senders is
+   greater than 1/4 of the participants, the senders get their
+   proportion of the full RTCP bandwidth.  While the values of these and
+   other constants in the interval calculation are not critical, all
+   participants in the session MUST use the same values so the same
+   interval will be calculated.  Therefore, these constants SHOULD be
+   fixed for a particular profile.
+
+   A profile MAY specify that the control traffic bandwidth may be a
+   separate parameter of the session rather than a strict percentage of
+   the session bandwidth.  Using a separate parameter allows rate-
+   adaptive applications to set an RTCP bandwidth consistent with a
+   "typical" data bandwidth that is lower than the maximum bandwidth
+   specified by the session bandwidth parameter.
+
+   The profile MAY further specify that the control traffic bandwidth
+   may be divided into two separate session parameters for those
+   participants which are active data senders and those which are not;
+   let us call the parameters S and R.  Following the recommendation
+   that 1/4 of the RTCP bandwidth be dedicated to data senders, the
+   RECOMMENDED default values for these two parameters would be 1.25%
+   and 3.75%, respectively.  When the proportion of senders is greater
+   than S/(S+R) of the participants, the senders get their proportion of
+   the sum of these parameters.  Using two parameters allows RTCP
+   reception reports to be turned off entirely for a particular session
+   by setting the RTCP bandwidth for non-data-senders to zero while
+   keeping the RTCP bandwidth for data senders non-zero so that sender
+   reports can still be sent for inter-media synchronization.  Turning
+   off RTCP reception reports is NOT RECOMMENDED because they are needed
+   for the functions listed at the beginning of Section 6, particularly
+   reception quality feedback and congestion control.  However, doing so
+   may be appropriate for systems operating on unidirectional links or
+   for sessions that don't require feedback on the quality of reception
+   or liveness of receivers and that have other means to avoid
+   congestion.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 25]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   The calculated interval between transmissions of compound RTCP
+   packets SHOULD also have a lower bound to avoid having bursts of
+   packets exceed the allowed bandwidth when the number of participants
+   is small and the traffic isn't smoothed according to the law of large
+   numbers.  It also keeps the report interval from becoming too small
+   during transient outages like a network partition such that
+   adaptation is delayed when the partition heals.  At application
+   startup, a delay SHOULD be imposed before the first compound RTCP
+   packet is sent to allow time for RTCP packets to be received from
+   other participants so the report interval will converge to the
+   correct value more quickly.  This delay MAY be set to half the
+   minimum interval to allow quicker notification that the new
+   participant is present.  The RECOMMENDED value for a fixed minimum
+   interval is 5 seconds.
+
+   An implementation MAY scale the minimum RTCP interval to a smaller
+   value inversely proportional to the session bandwidth parameter with
+   the following limitations:
+
+   o  For multicast sessions, only active data senders MAY use the
+      reduced minimum value to calculate the interval for transmission
+      of compound RTCP packets.
+
+   o  For unicast sessions, the reduced value MAY be used by
+      participants that are not active data senders as well, and the
+      delay before sending the initial compound RTCP packet MAY be zero.
+
+   o  For all sessions, the fixed minimum SHOULD be used when
+      calculating the participant timeout interval (see Section 6.3.5)
+      so that implementations which do not use the reduced value for
+      transmitting RTCP packets are not timed out by other participants
+      prematurely.
+
+   o  The RECOMMENDED value for the reduced minimum in seconds is 360
+      divided by the session bandwidth in kilobits/second.  This minimum
+      is smaller than 5 seconds for bandwidths greater than 72 kb/s.
+
+   The algorithm described in Section 6.3 and Appendix A.7 was designed
+   to meet the goals outlined in this section.  It calculates the
+   interval between sending compound RTCP packets to divide the allowed
+   control traffic bandwidth among the participants.  This allows an
+   application to provide fast response for small sessions where, for
+   example, identification of all participants is important, yet
+   automatically adapt to large sessions.  The algorithm incorporates
+   the following characteristics:
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 26]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  The calculated interval between RTCP packets scales linearly with
+      the number of members in the group.  It is this linear factor
+      which allows for a constant amount of control traffic when summed
+      across all members.
+
+   o  The interval between RTCP packets is varied randomly over the
+      range [0.5,1.5] times the calculated interval to avoid unintended
+      synchronization of all participants [20].  The first RTCP packet
+      sent after joining a session is also delayed by a random variation
+      of half the minimum RTCP interval.
+
+   o  A dynamic estimate of the average compound RTCP packet size is
+      calculated, including all those packets received and sent, to
+      automatically adapt to changes in the amount of control
+      information carried.
+
+   o  Since the calculated interval is dependent on the number of
+      observed group members, there may be undesirable startup effects
+      when a new user joins an existing session, or many users
+      simultaneously join a new session.  These new users will initially
+      have incorrect estimates of the group membership, and thus their
+      RTCP transmission interval will be too short.  This problem can be
+      significant if many users join the session simultaneously.  To
+      deal with this, an algorithm called "timer reconsideration" is
+      employed.  This algorithm implements a simple back-off mechanism
+      which causes users to hold back RTCP packet transmission if the
+      group sizes are increasing.
+
+   o  When users leave a session, either with a BYE or by timeout, the
+      group membership decreases, and thus the calculated interval
+      should decrease.  A "reverse reconsideration" algorithm is used to
+      allow members to more quickly reduce their intervals in response
+      to group membership decreases.
+
+   o  BYE packets are given different treatment than other RTCP packets.
+      When a user leaves a group, and wishes to send a BYE packet, it
+      may do so before its next scheduled RTCP packet.  However,
+      transmission of BYEs follows a back-off algorithm which avoids
+      floods of BYE packets should a large number of members
+      simultaneously leave the session.
+
+   This algorithm may be used for sessions in which all participants are
+   allowed to send.  In that case, the session bandwidth parameter is
+   the product of the individual sender's bandwidth times the number of
+   participants, and the RTCP bandwidth is 5% of that.
+
+   Details of the algorithm's operation are given in the sections that
+   follow.  Appendix A.7 gives an example implementation.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 27]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.2.1 Maintaining the Number of Session Members
+
+   Calculation of the RTCP packet interval depends upon an estimate of
+   the number of sites participating in the session.  New sites are
+   added to the count when they are heard, and an entry for each SHOULD
+   be created in a table indexed by the SSRC or CSRC identifier (see
+   Section 8.2) to keep track of them.  New entries MAY be considered
+   not valid until multiple packets carrying the new SSRC have been
+   received (see Appendix A.1), or until an SDES RTCP packet containing
+   a CNAME for that SSRC has been received.  Entries MAY be deleted from
+   the table when an RTCP BYE packet with the corresponding SSRC
+   identifier is received, except that some straggler data packets might
+   arrive after the BYE and cause the entry to be recreated.  Instead,
+   the entry SHOULD be marked as having received a BYE and then deleted
+   after an appropriate delay.
+
+   A participant MAY mark another site inactive, or delete it if not yet
+   valid, if no RTP or RTCP packet has been received for a small number
+   of RTCP report intervals (5 is RECOMMENDED).  This provides some
+   robustness against packet loss.  All sites must have the same value
+   for this multiplier and must calculate roughly the same value for the
+   RTCP report interval in order for this timeout to work properly.
+   Therefore, this multiplier SHOULD be fixed for a particular profile.
+
+   For sessions with a very large number of participants, it may be
+   impractical to maintain a table to store the SSRC identifier and
+   state information for all of them.  An implementation MAY use SSRC
+   sampling, as described in [21], to reduce the storage requirements.
+   An implementation MAY use any other algorithm with similar
+   performance.  A key requirement is that any algorithm considered
+   SHOULD NOT substantially underestimate the group size, although it
+   MAY overestimate.
+
+6.3 RTCP Packet Send and Receive Rules
+
+   The rules for how to send, and what to do when receiving an RTCP
+   packet are outlined here.  An implementation that allows operation in
+   a multicast environment or a multipoint unicast environment MUST meet
+   the requirements in Section 6.2.  Such an implementation MAY use the
+   algorithm defined in this section to meet those requirements, or MAY
+   use some other algorithm so long as it provides equivalent or better
+   performance.  An implementation which is constrained to two-party
+   unicast operation SHOULD still use randomization of the RTCP
+   transmission interval to avoid unintended synchronization of multiple
+   instances operating in the same environment, but MAY omit the "timer
+   reconsideration" and "reverse reconsideration" algorithms in Sections
+   6.3.3, 6.3.6 and 6.3.7.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 28]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   To execute these rules, a session participant must maintain several
+   pieces of state:
+
+   tp: the last time an RTCP packet was transmitted;
+
+   tc: the current time;
+
+   tn: the next scheduled transmission time of an RTCP packet;
+
+   pmembers: the estimated number of session members at the time tn
+      was last recomputed;
+
+   members: the most current estimate for the number of session
+      members;
+
+   senders: the most current estimate for the number of senders in
+      the session;
+
+   rtcp_bw: The target RTCP bandwidth, i.e., the total bandwidth
+      that will be used for RTCP packets by all members of this session,
+      in octets per second.  This will be a specified fraction of the
+      "session bandwidth" parameter supplied to the application at
+      startup.
+
+   we_sent: Flag that is true if the application has sent data
+      since the 2nd previous RTCP report was transmitted.
+
+   avg_rtcp_size: The average compound RTCP packet size, in octets,
+      over all RTCP packets sent and received by this participant.  The
+      size includes lower-layer transport and network protocol headers
+      (e.g., UDP and IP) as explained in Section 6.2.
+
+   initial: Flag that is true if the application has not yet sent
+      an RTCP packet.
+
+   Many of these rules make use of the "calculated interval" between
+   packet transmissions.  This interval is described in the following
+   section.
+
+6.3.1 Computing the RTCP Transmission Interval
+
+   To maintain scalability, the average interval between packets from a
+   session participant should scale with the group size.  This interval
+   is called the calculated interval.  It is obtained by combining a
+   number of the pieces of state described above.  The calculated
+   interval T is then determined as follows:
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 29]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   1. If the number of senders is less than or equal to 25% of the
+      membership (members), the interval depends on whether the
+      participant is a sender or not (based on the value of we_sent).
+      If the participant is a sender (we_sent true), the constant C is
+      set to the average RTCP packet size (avg_rtcp_size) divided by 25%
+      of the RTCP bandwidth (rtcp_bw), and the constant n is set to the
+      number of senders.  If we_sent is not true, the constant C is set
+      to the average RTCP packet size divided by 75% of the RTCP
+      bandwidth.  The constant n is set to the number of receivers
+      (members - senders).  If the number of senders is greater than
+      25%, senders and receivers are treated together.  The constant C
+      is set to the average RTCP packet size divided by the total RTCP
+      bandwidth and n is set to the total number of members.  As stated
+      in Section 6.2, an RTP profile MAY specify that the RTCP bandwidth
+      may be explicitly defined by two separate parameters (call them S
+      and R) for those participants which are senders and those which
+      are not.  In that case, the 25% fraction becomes S/(S+R) and the
+      75% fraction becomes R/(S+R).  Note that if R is zero, the
+      percentage of senders is never greater than S/(S+R), and the
+      implementation must avoid division by zero.
+
+   2. If the participant has not yet sent an RTCP packet (the variable
+      initial is true), the constant Tmin is set to 2.5 seconds, else it
+      is set to 5 seconds.
+
+   3. The deterministic calculated interval Td is set to max(Tmin, n*C).
+
+   4. The calculated interval T is set to a number uniformly distributed
+      between 0.5 and 1.5 times the deterministic calculated interval.
+
+   5. The resulting value of T is divided by e-3/2=1.21828 to compensate
+      for the fact that the timer reconsideration algorithm converges to
+      a value of the RTCP bandwidth below the intended average.
+
+   This procedure results in an interval which is random, but which, on
+   average, gives at least 25% of the RTCP bandwidth to senders and the
+   rest to receivers.  If the senders constitute more than one quarter
+   of the membership, this procedure splits the bandwidth equally among
+   all participants, on average.
+
+6.3.2 Initialization
+
+   Upon joining the session, the participant initializes tp to 0, tc to
+   0, senders to 0, pmembers to 1, members to 1, we_sent to false,
+   rtcp_bw to the specified fraction of the session bandwidth, initial
+   to true, and avg_rtcp_size to the probable size of the first RTCP
+   packet that the application will later construct.  The calculated
+   interval T is then computed, and the first packet is scheduled for
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 30]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   time tn = T.  This means that a transmission timer is set which
+   expires at time T.  Note that an application MAY use any desired
+   approach for implementing this timer.
+
+   The participant adds its own SSRC to the member table.
+
+6.3.3 Receiving an RTP or Non-BYE RTCP Packet
+
+   When an RTP or RTCP packet is received from a participant whose SSRC
+   is not in the member table, the SSRC is added to the table, and the
+   value for members is updated once the participant has been validated
+   as described in Section 6.2.1.  The same processing occurs for each
+   CSRC in a validated RTP packet.
+
+   When an RTP packet is received from a participant whose SSRC is not
+   in the sender table, the SSRC is added to the table, and the value
+   for senders is updated.
+
+   For each compound RTCP packet received, the value of avg_rtcp_size is
+   updated:
+
+      avg_rtcp_size = (1/16) * packet_size + (15/16) * avg_rtcp_size
+
+   where packet_size is the size of the RTCP packet just received.
+
+6.3.4 Receiving an RTCP BYE Packet
+
+   Except as described in Section 6.3.7 for the case when an RTCP BYE is
+   to be transmitted, if the received packet is an RTCP BYE packet, the
+   SSRC is checked against the member table.  If present, the entry is
+   removed from the table, and the value for members is updated.  The
+   SSRC is then checked against the sender table.  If present, the entry
+   is removed from the table, and the value for senders is updated.
+
+   Furthermore, to make the transmission rate of RTCP packets more
+   adaptive to changes in group membership, the following "reverse
+   reconsideration" algorithm SHOULD be executed when a BYE packet is
+   received that reduces members to a value less than pmembers:
+
+   o  The value for tn is updated according to the following formula:
+
+         tn = tc + (members/pmembers) * (tn - tc)
+
+   o  The value for tp is updated according the following formula:
+
+         tp = tc - (members/pmembers) * (tc - tp).
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 31]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  The next RTCP packet is rescheduled for transmission at time tn,
+      which is now earlier.
+
+   o  The value of pmembers is set equal to members.
+
+   This algorithm does not prevent the group size estimate from
+   incorrectly dropping to zero for a short time due to premature
+   timeouts when most participants of a large session leave at once but
+   some remain.  The algorithm does make the estimate return to the
+   correct value more rapidly.  This situation is unusual enough and the
+   consequences are sufficiently harmless that this problem is deemed
+   only a secondary concern.
+
+6.3.5 Timing Out an SSRC
+
+   At occasional intervals, the participant MUST check to see if any of
+   the other participants time out.  To do this, the participant
+   computes the deterministic (without the randomization factor)
+   calculated interval Td for a receiver, that is, with we_sent false.
+   Any other session member who has not sent an RTP or RTCP packet since
+   time tc - MTd (M is the timeout multiplier, and defaults to 5) is
+   timed out.  This means that its SSRC is removed from the member list,
+   and members is updated.  A similar check is performed on the sender
+   list.  Any member on the sender list who has not sent an RTP packet
+   since time tc - 2T (within the last two RTCP report intervals) is
+   removed from the sender list, and senders is updated.
+
+   If any members time out, the reverse reconsideration algorithm
+   described in Section 6.3.4 SHOULD be performed.
+
+   The participant MUST perform this check at least once per RTCP
+   transmission interval.
+
+6.3.6 Expiration of Transmission Timer
+
+   When the packet transmission timer expires, the participant performs
+   the following operations:
+
+   o  The transmission interval T is computed as described in Section
+      6.3.1, including the randomization factor.
+
+   o  If tp + T is less than or equal to tc, an RTCP packet is
+      transmitted.  tp is set to tc, then another value for T is
+      calculated as in the previous step and tn is set to tc + T.  The
+      transmission timer is set to expire again at time tn.  If tp + T
+      is greater than tc, tn is set to tp + T.  No RTCP packet is
+      transmitted.  The transmission timer is set to expire at time tn.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 32]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  pmembers is set to members.
+
+   If an RTCP packet is transmitted, the value of initial is set to
+   FALSE.  Furthermore, the value of avg_rtcp_size is updated:
+
+      avg_rtcp_size = (1/16) * packet_size + (15/16) * avg_rtcp_size
+
+   where packet_size is the size of the RTCP packet just transmitted.
+
+6.3.7 Transmitting a BYE Packet
+
+   When a participant wishes to leave a session, a BYE packet is
+   transmitted to inform the other participants of the event.  In order
+   to avoid a flood of BYE packets when many participants leave the
+   system, a participant MUST execute the following algorithm if the
+   number of members is more than 50 when the participant chooses to
+   leave.  This algorithm usurps the normal role of the members variable
+   to count BYE packets instead:
+
+   o  When the participant decides to leave the system, tp is reset to
+      tc, the current time, members and pmembers are initialized to 1,
+      initial is set to 1, we_sent is set to false, senders is set to 0,
+      and avg_rtcp_size is set to the size of the compound BYE packet.
+      The calculated interval T is computed.  The BYE packet is then
+      scheduled for time tn = tc + T.
+
+   o  Every time a BYE packet from another participant is received,
+      members is incremented by 1 regardless of whether that participant
+      exists in the member table or not, and when SSRC sampling is in
+      use, regardless of whether or not the BYE SSRC would be included
+      in the sample.  members is NOT incremented when other RTCP packets
+      or RTP packets are received, but only for BYE packets.  Similarly,
+      avg_rtcp_size is updated only for received BYE packets.  senders
+      is NOT updated when RTP packets arrive; it remains 0.
+
+   o  Transmission of the BYE packet then follows the rules for
+      transmitting a regular RTCP packet, as above.
+
+   This allows BYE packets to be sent right away, yet controls their
+   total bandwidth usage.  In the worst case, this could cause RTCP
+   control packets to use twice the bandwidth as normal (10%) -- 5% for
+   non-BYE RTCP packets and 5% for BYE.
+
+   A participant that does not want to wait for the above mechanism to
+   allow transmission of a BYE packet MAY leave the group without
+   sending a BYE at all.  That participant will eventually be timed out
+   by the other group members.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 33]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   If the group size estimate members is less than 50 when the
+   participant decides to leave, the participant MAY send a BYE packet
+   immediately.  Alternatively, the participant MAY choose to execute
+   the above BYE backoff algorithm.
+
+   In either case, a participant which never sent an RTP or RTCP packet
+   MUST NOT send a BYE packet when they leave the group.
+
+6.3.8 Updating we_sent
+
+   The variable we_sent contains true if the participant has sent an RTP
+   packet recently, false otherwise.  This determination is made by
+   using the same mechanisms as for managing the set of other
+   participants listed in the senders table.  If the participant sends
+   an RTP packet when we_sent is false, it adds itself to the sender
+   table and sets we_sent to true.  The reverse reconsideration
+   algorithm described in Section 6.3.4 SHOULD be performed to possibly
+   reduce the delay before sending an SR packet.  Every time another RTP
+   packet is sent, the time of transmission of that packet is maintained
+   in the table.  The normal sender timeout algorithm is then applied to
+   the participant -- if an RTP packet has not been transmitted since
+   time tc - 2T, the participant removes itself from the sender table,
+   decrements the sender count, and sets we_sent to false.
+
+6.3.9 Allocation of Source Description Bandwidth
+
+   This specification defines several source description (SDES) items in
+   addition to the mandatory CNAME item, such as NAME (personal name)
+   and EMAIL (email address).  It also provides a means to define new
+   application-specific RTCP packet types.  Applications should exercise
+   caution in allocating control bandwidth to this additional
+   information because it will slow down the rate at which reception
+   reports and CNAME are sent, thus impairing the performance of the
+   protocol.  It is RECOMMENDED that no more than 20% of the RTCP
+   bandwidth allocated to a single participant be used to carry the
+   additional information.  Furthermore, it is not intended that all
+   SDES items will be included in every application.  Those that are
+   included SHOULD be assigned a fraction of the bandwidth according to
+   their utility.  Rather than estimate these fractions dynamically, it
+   is recommended that the percentages be translated statically into
+   report interval counts based on the typical length of an item.
+
+   For example, an application may be designed to send only CNAME, NAME
+   and EMAIL and not any others.  NAME might be given much higher
+   priority than EMAIL because the NAME would be displayed continuously
+   in the application's user interface, whereas EMAIL would be displayed
+   only when requested.  At every RTCP interval, an RR packet and an
+   SDES packet with the CNAME item would be sent.  For a small session
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 34]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   operating at the minimum interval, that would be every 5 seconds on
+   the average.  Every third interval (15 seconds), one extra item would
+   be included in the SDES packet.  Seven out of eight times this would
+   be the NAME item, and every eighth time (2 minutes) it would be the
+   EMAIL item.
+
+   When multiple applications operate in concert using cross-application
+   binding through a common CNAME for each participant, for example in a
+   multimedia conference composed of an RTP session for each medium, the
+   additional SDES information MAY be sent in only one RTP session.  The
+   other sessions would carry only the CNAME item.  In particular, this
+   approach should be applied to the multiple sessions of a layered
+   encoding scheme (see Section 2.4).
+
+6.4 Sender and Receiver Reports
+
+   RTP receivers provide reception quality feedback using RTCP report
+   packets which may take one of two forms depending upon whether or not
+   the receiver is also a sender.  The only difference between the
+   sender report (SR) and receiver report (RR) forms, besides the packet
+   type code, is that the sender report includes a 20-byte sender
+   information section for use by active senders.  The SR is issued if a
+   site has sent any data packets during the interval since issuing the
+   last report or the previous one, otherwise the RR is issued.
+
+   Both the SR and RR forms include zero or more reception report
+   blocks, one for each of the synchronization sources from which this
+   receiver has received RTP data packets since the last report.
+   Reports are not issued for contributing sources listed in the CSRC
+   list.  Each reception report block provides statistics about the data
+   received from the particular source indicated in that block.  Since a
+   maximum of 31 reception report blocks will fit in an SR or RR packet,
+   additional RR packets SHOULD be stacked after the initial SR or RR
+   packet as needed to contain the reception reports for all sources
+   heard during the interval since the last report.  If there are too
+   many sources to fit all the necessary RR packets into one compound
+   RTCP packet without exceeding the MTU of the network path, then only
+   the subset that will fit into one MTU SHOULD be included in each
+   interval.  The subsets SHOULD be selected round-robin across multiple
+   intervals so that all sources are reported.
+
+   The next sections define the formats of the two reports, how they may
+   be extended in a profile-specific manner if an application requires
+   additional feedback information, and how the reports may be used.
+   Details of reception reporting by translators and mixers is given in
+   Section 7.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 35]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.4.1 SR: Sender Report RTCP Packet
+
+        0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+header |V=2|P|    RC   |   PT=SR=200   |             length            |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                         SSRC of sender                        |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+sender |              NTP timestamp, most significant word             |
+info   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |             NTP timestamp, least significant word             |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                         RTP timestamp                         |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                     sender's packet count                     |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                      sender's octet count                     |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+report |                 SSRC_1 (SSRC of first source)                 |
+block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  1    | fraction lost |       cumulative number of packets lost       |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |           extended highest sequence number received           |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                      interarrival jitter                      |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                         last SR (LSR)                         |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                   delay since last SR (DLSR)                  |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+report |                 SSRC_2 (SSRC of second source)                |
+block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  2    :                               ...                             :
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+       |                  profile-specific extensions                  |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The sender report packet consists of three sections, possibly
+   followed by a fourth profile-specific extension section if defined.
+   The first section, the header, is 8 octets long.  The fields have the
+   following meaning:
+
+   version (V): 2 bits
+      Identifies the version of RTP, which is the same in RTCP packets
+      as in RTP data packets.  The version defined by this specification
+      is two (2).
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 36]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   padding (P): 1 bit
+      If the padding bit is set, this individual RTCP packet contains
+      some additional padding octets at the end which are not part of
+      the control information but are included in the length field.  The
+      last octet of the padding is a count of how many padding octets
+      should be ignored, including itself (it will be a multiple of
+      four).  Padding may be needed by some encryption algorithms with
+      fixed block sizes.  In a compound RTCP packet, padding is only
+      required on one individual packet because the compound packet is
+      encrypted as a whole for the method in Section 9.1.  Thus, padding
+      MUST only be added to the last individual packet, and if padding
+      is added to that packet, the padding bit MUST be set only on that
+      packet.  This convention aids the header validity checks described
+      in Appendix A.2 and allows detection of packets from some early
+      implementations that incorrectly set the padding bit on the first
+      individual packet and add padding to the last individual packet.
+
+   reception report count (RC): 5 bits
+      The number of reception report blocks contained in this packet.  A
+      value of zero is valid.
+
+   packet type (PT): 8 bits
+      Contains the constant 200 to identify this as an RTCP SR packet.
+
+   length: 16 bits
+      The length of this RTCP packet in 32-bit words minus one,
+      including the header and any padding.  (The offset of one makes
+      zero a valid length and avoids a possible infinite loop in
+      scanning a compound RTCP packet, while counting 32-bit words
+      avoids a validity check for a multiple of 4.)
+
+   SSRC: 32 bits
+      The synchronization source identifier for the originator of this
+      SR packet.
+
+   The second section, the sender information, is 20 octets long and is
+   present in every sender report packet.  It summarizes the data
+   transmissions from this sender.  The fields have the following
+   meaning:
+
+   NTP timestamp: 64 bits
+      Indicates the wallclock time (see Section 4) when this report was
+      sent so that it may be used in combination with timestamps
+      returned in reception reports from other receivers to measure
+      round-trip propagation to those receivers.  Receivers should
+      expect that the measurement accuracy of the timestamp may be
+      limited to far less than the resolution of the NTP timestamp.  The
+      measurement uncertainty of the timestamp is not indicated as it
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 37]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      may not be known.  On a system that has no notion of wallclock
+      time but does have some system-specific clock such as "system
+      uptime", a sender MAY use that clock as a reference to calculate
+      relative NTP timestamps.  It is important to choose a commonly
+      used clock so that if separate implementations are used to produce
+      the individual streams of a multimedia session, all
+      implementations will use the same clock.  Until the year 2036,
+      relative and absolute timestamps will differ in the high bit so
+      (invalid) comparisons will show a large difference; by then one
+      hopes relative timestamps will no longer be needed.  A sender that
+      has no notion of wallclock or elapsed time MAY set the NTP
+      timestamp to zero.
+
+   RTP timestamp: 32 bits
+      Corresponds to the same time as the NTP timestamp (above), but in
+      the same units and with the same random offset as the RTP
+      timestamps in data packets.  This correspondence may be used for
+      intra- and inter-media synchronization for sources whose NTP
+      timestamps are synchronized, and may be used by media-independent
+      receivers to estimate the nominal RTP clock frequency.  Note that
+      in most cases this timestamp will not be equal to the RTP
+      timestamp in any adjacent data packet.  Rather, it MUST be
+      calculated from the corresponding NTP timestamp using the
+      relationship between the RTP timestamp counter and real time as
+      maintained by periodically checking the wallclock time at a
+      sampling instant.
+
+   sender's packet count: 32 bits
+      The total number of RTP data packets transmitted by the sender
+      since starting transmission up until the time this SR packet was
+      generated.  The count SHOULD be reset if the sender changes its
+      SSRC identifier.
+
+   sender's octet count: 32 bits
+      The total number of payload octets (i.e., not including header or
+      padding) transmitted in RTP data packets by the sender since
+      starting transmission up until the time this SR packet was
+      generated.  The count SHOULD be reset if the sender changes its
+      SSRC identifier.  This field can be used to estimate the average
+      payload data rate.
+
+   The third section contains zero or more reception report blocks
+   depending on the number of other sources heard by this sender since
+   the last report.  Each reception report block conveys statistics on
+   the reception of RTP packets from a single synchronization source.
+   Receivers SHOULD NOT carry over statistics when a source changes its
+   SSRC identifier due to a collision.  These statistics are:
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 38]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   SSRC_n (source identifier): 32 bits
+      The SSRC identifier of the source to which the information in this
+      reception report block pertains.
+
+   fraction lost: 8 bits
+      The fraction of RTP data packets from source SSRC_n lost since the
+      previous SR or RR packet was sent, expressed as a fixed point
+      number with the binary point at the left edge of the field.  (That
+      is equivalent to taking the integer part after multiplying the
+      loss fraction by 256.)  This fraction is defined to be the number
+      of packets lost divided by the number of packets expected, as
+      defined in the next paragraph.  An implementation is shown in
+      Appendix A.3.  If the loss is negative due to duplicates, the
+      fraction lost is set to zero.  Note that a receiver cannot tell
+      whether any packets were lost after the last one received, and
+      that there will be no reception report block issued for a source
+      if all packets from that source sent during the last reporting
+      interval have been lost.
+
+   cumulative number of packets lost: 24 bits
+      The total number of RTP data packets from source SSRC_n that have
+      been lost since the beginning of reception.  This number is
+      defined to be the number of packets expected less the number of
+      packets actually received, where the number of packets received
+      includes any which are late or duplicates.  Thus, packets that
+      arrive late are not counted as lost, and the loss may be negative
+      if there are duplicates.  The number of packets expected is
+      defined to be the extended last sequence number received, as
+      defined next, less the initial sequence number received.  This may
+      be calculated as shown in Appendix A.3.
+
+   extended highest sequence number received: 32 bits
+      The low 16 bits contain the highest sequence number received in an
+      RTP data packet from source SSRC_n, and the most significant 16
+      bits extend that sequence number with the corresponding count of
+      sequence number cycles, which may be maintained according to the
+      algorithm in Appendix A.1.  Note that different receivers within
+      the same session will generate different extensions to the
+      sequence number if their start times differ significantly.
+
+   interarrival jitter: 32 bits
+      An estimate of the statistical variance of the RTP data packet
+      interarrival time, measured in timestamp units and expressed as an
+      unsigned integer.  The interarrival jitter J is defined to be the
+      mean deviation (smoothed absolute value) of the difference D in
+      packet spacing at the receiver compared to the sender for a pair
+      of packets.  As shown in the equation below, this is equivalent to
+      the difference in the "relative transit time" for the two packets;
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 39]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      the relative transit time is the difference between a packet's RTP
+      timestamp and the receiver's clock at the time of arrival,
+      measured in the same units.
+
+      If Si is the RTP timestamp from packet i, and Ri is the time of
+      arrival in RTP timestamp units for packet i, then for two packets
+      i and j, D may be expressed as
+
+         D(i,j) = (Rj - Ri) - (Sj - Si) = (Rj - Sj) - (Ri - Si)
+
+      The interarrival jitter SHOULD be calculated continuously as each
+      data packet i is received from source SSRC_n, using this
+      difference D for that packet and the previous packet i-1 in order
+      of arrival (not necessarily in sequence), according to the formula
+
+         J(i) = J(i-1) + (|D(i-1,i)| - J(i-1))/16
+
+      Whenever a reception report is issued, the current value of J is
+      sampled.
+
+      The jitter calculation MUST conform to the formula specified here
+      in order to allow profile-independent monitors to make valid
+      interpretations of reports coming from different implementations.
+      This algorithm is the optimal first-order estimator and the gain
+      parameter 1/16 gives a good noise reduction ratio while
+      maintaining a reasonable rate of convergence [22].  A sample
+      implementation is shown in Appendix A.8.  See Section 6.4.4 for a
+      discussion of the effects of varying packet duration and delay
+      before transmission.
+
+   last SR timestamp (LSR): 32 bits
+      The middle 32 bits out of 64 in the NTP timestamp (as explained in
+      Section 4) received as part of the most recent RTCP sender report
+      (SR) packet from source SSRC_n.  If no SR has been received yet,
+      the field is set to zero.
+
+   delay since last SR (DLSR): 32 bits
+      The delay, expressed in units of 1/65536 seconds, between
+      receiving the last SR packet from source SSRC_n and sending this
+      reception report block.  If no SR packet has been received yet
+      from SSRC_n, the DLSR field is set to zero.
+
+      Let SSRC_r denote the receiver issuing this receiver report.
+      Source SSRC_n can compute the round-trip propagation delay to
+      SSRC_r by recording the time A when this reception report block is
+      received.  It calculates the total round-trip time A-LSR using the
+      last SR timestamp (LSR) field, and then subtracting this field to
+      leave the round-trip propagation delay as (A - LSR - DLSR).  This
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 40]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      is illustrated in Fig. 2.  Times are shown in both a hexadecimal
+      representation of the 32-bit fields and the equivalent floating-
+      point decimal representation.  Colons indicate a 32-bit field
+      divided into a 16-bit integer part and 16-bit fraction part.
+
+      This may be used as an approximate measure of distance to cluster
+      receivers, although some links have very asymmetric delays.
+
+   [10 Nov 1995 11:33:25.125 UTC]       [10 Nov 1995 11:33:36.5 UTC]
+   n                 SR(n)              A=b710:8000 (46864.500 s)
+   ---------------------------------------------------------------->
+                      v                 ^
+   ntp_sec =0xb44db705 v               ^ dlsr=0x0005:4000 (    5.250s)
+   ntp_frac=0x20000000  v             ^  lsr =0xb705:2000 (46853.125s)
+     (3024992005.125 s)  v           ^
+   r                      v         ^ RR(n)
+   ---------------------------------------------------------------->
+                          |<-DLSR->|
+                           (5.250 s)
+
+   A     0xb710:8000 (46864.500 s)
+   DLSR -0x0005:4000 (    5.250 s)
+   LSR  -0xb705:2000 (46853.125 s)
+   -------------------------------
+   delay 0x0006:2000 (    6.125 s)
+
+           Figure 2: Example for round-trip time computation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 41]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.4.2 RR: Receiver Report RTCP Packet
+
+        0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+header |V=2|P|    RC   |   PT=RR=201   |             length            |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                     SSRC of packet sender                     |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+report |                 SSRC_1 (SSRC of first source)                 |
+block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  1    | fraction lost |       cumulative number of packets lost       |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |           extended highest sequence number received           |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                      interarrival jitter                      |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                         last SR (LSR)                         |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                   delay since last SR (DLSR)                  |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+report |                 SSRC_2 (SSRC of second source)                |
+block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  2    :                               ...                             :
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+       |                  profile-specific extensions                  |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The format of the receiver report (RR) packet is the same as that of
+   the SR packet except that the packet type field contains the constant
+   201 and the five words of sender information are omitted (these are
+   the NTP and RTP timestamps and sender's packet and octet counts).
+   The remaining fields have the same meaning as for the SR packet.
+
+   An empty RR packet (RC = 0) MUST be put at the head of a compound
+   RTCP packet when there is no data transmission or reception to
+   report.
+
+6.4.3 Extending the Sender and Receiver Reports
+
+   A profile SHOULD define profile-specific extensions to the sender
+   report and receiver report if there is additional information that
+   needs to be reported regularly about the sender or receivers.  This
+   method SHOULD be used in preference to defining another RTCP packet
+   type because it requires less overhead:
+
+   o  fewer octets in the packet (no RTCP header or SSRC field);
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 42]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  simpler and faster parsing because applications running under that
+      profile would be programmed to always expect the extension fields
+      in the directly accessible location after the reception reports.
+
+   The extension is a fourth section in the sender- or receiver-report
+   packet which comes at the end after the reception report blocks, if
+   any.  If additional sender information is required, then for sender
+   reports it would be included first in the extension section, but for
+   receiver reports it would not be present.  If information about
+   receivers is to be included, that data SHOULD be structured as an
+   array of blocks parallel to the existing array of reception report
+   blocks; that is, the number of blocks would be indicated by the RC
+   field.
+
+6.4.4 Analyzing Sender and Receiver Reports
+
+   It is expected that reception quality feedback will be useful not
+   only for the sender but also for other receivers and third-party
+   monitors.  The sender may modify its transmissions based on the
+   feedback; receivers can determine whether problems are local,
+   regional or global; network managers may use profile-independent
+   monitors that receive only the RTCP packets and not the corresponding
+   RTP data packets to evaluate the performance of their networks for
+   multicast distribution.
+
+   Cumulative counts are used in both the sender information and
+   receiver report blocks so that differences may be calculated between
+   any two reports to make measurements over both short and long time
+   periods, and to provide resilience against the loss of a report.  The
+   difference between the last two reports received can be used to
+   estimate the recent quality of the distribution.  The NTP timestamp
+   is included so that rates may be calculated from these differences
+   over the interval between two reports.  Since that timestamp is
+   independent of the clock rate for the data encoding, it is possible
+   to implement encoding- and profile-independent quality monitors.
+
+   An example calculation is the packet loss rate over the interval
+   between two reception reports.  The difference in the cumulative
+   number of packets lost gives the number lost during that interval.
+   The difference in the extended last sequence numbers received gives
+   the number of packets expected during the interval.  The ratio of
+   these two is the packet loss fraction over the interval.  This ratio
+   should equal the fraction lost field if the two reports are
+   consecutive, but otherwise it may not.  The loss rate per second can
+   be obtained by dividing the loss fraction by the difference in NTP
+   timestamps, expressed in seconds.  The number of packets received is
+   the number of packets expected minus the number lost.  The number of
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 43]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   packets expected may also be used to judge the statistical validity
+   of any loss estimates.  For example, 1 out of 5 packets lost has a
+   lower significance than 200 out of 1000.
+
+   From the sender information, a third-party monitor can calculate the
+   average payload data rate and the average packet rate over an
+   interval without receiving the data.  Taking the ratio of the two
+   gives the average payload size.  If it can be assumed that packet
+   loss is independent of packet size, then the number of packets
+   received by a particular receiver times the average payload size (or
+   the corresponding packet size) gives the apparent throughput
+   available to that receiver.
+
+   In addition to the cumulative counts which allow long-term packet
+   loss measurements using differences between reports, the fraction
+   lost field provides a short-term measurement from a single report.
+   This becomes more important as the size of a session scales up enough
+   that reception state information might not be kept for all receivers
+   or the interval between reports becomes long enough that only one
+   report might have been received from a particular receiver.
+
+   The interarrival jitter field provides a second short-term measure of
+   network congestion.  Packet loss tracks persistent congestion while
+   the jitter measure tracks transient congestion.  The jitter measure
+   may indicate congestion before it leads to packet loss.  The
+   interarrival jitter field is only a snapshot of the jitter at the
+   time of a report and is not intended to be taken quantitatively.
+   Rather, it is intended for comparison across a number of reports from
+   one receiver over time or from multiple receivers, e.g., within a
+   single network, at the same time.  To allow comparison across
+   receivers, it is important the the jitter be calculated according to
+   the same formula by all receivers.
+
+   Because the jitter calculation is based on the RTP timestamp which
+   represents the instant when the first data in the packet was sampled,
+   any variation in the delay between that sampling instant and the time
+   the packet is transmitted will affect the resulting jitter that is
+   calculated.  Such a variation in delay would occur for audio packets
+   of varying duration.  It will also occur for video encodings because
+   the timestamp is the same for all the packets of one frame but those
+   packets are not all transmitted at the same time.  The variation in
+   delay until transmission does reduce the accuracy of the jitter
+   calculation as a measure of the behavior of the network by itself,
+   but it is appropriate to include considering that the receiver buffer
+   must accommodate it.  When the jitter calculation is used as a
+   comparative measure, the (constant) component due to variation in
+   delay until transmission subtracts out so that a change in the
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 44]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   network jitter component can then be observed unless it is relatively
+   small.  If the change is small, then it is likely to be
+   inconsequential.
+
+6.5 SDES: Source Description RTCP Packet
+
+        0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+header |V=2|P|    SC   |  PT=SDES=202  |             length            |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+chunk  |                          SSRC/CSRC_1                          |
+  1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                           SDES items                          |
+       |                              ...                              |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+chunk  |                          SSRC/CSRC_2                          |
+  2    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                           SDES items                          |
+       |                              ...                              |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+
+   The SDES packet is a three-level structure composed of a header and
+   zero or more chunks, each of which is composed of items describing
+   the source identified in that chunk.  The items are described
+   individually in subsequent sections.
+
+   version (V), padding (P), length:
+      As described for the SR packet (see Section 6.4.1).
+
+   packet type (PT): 8 bits
+      Contains the constant 202 to identify this as an RTCP SDES packet.
+
+   source count (SC): 5 bits
+      The number of SSRC/CSRC chunks contained in this SDES packet.  A
+      value of zero is valid but useless.
+
+   Each chunk consists of an SSRC/CSRC identifier followed by a list of
+   zero or more items, which carry information about the SSRC/CSRC.
+   Each chunk starts on a 32-bit boundary.  Each item consists of an 8-
+   bit type field, an 8-bit octet count describing the length of the
+   text (thus, not including this two-octet header), and the text
+   itself.  Note that the text can be no longer than 255 octets, but
+   this is consistent with the need to limit RTCP bandwidth consumption.
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 45]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   The text is encoded according to the UTF-8 encoding specified in RFC
+   2279 [5].  US-ASCII is a subset of this encoding and requires no
+   additional encoding.  The presence of multi-octet encodings is
+   indicated by setting the most significant bit of a character to a
+   value of one.
+
+   Items are contiguous, i.e., items are not individually padded to a
+   32-bit boundary.  Text is not null terminated because some multi-
+   octet encodings include null octets.  The list of items in each chunk
+   MUST be terminated by one or more null octets, the first of which is
+   interpreted as an item type of zero to denote the end of the list.
+   No length octet follows the null item type octet, but additional null
+   octets MUST be included if needed to pad until the next 32-bit
+   boundary.  Note that this padding is separate from that indicated by
+   the P bit in the RTCP header.  A chunk with zero items (four null
+   octets) is valid but useless.
+
+   End systems send one SDES packet containing their own source
+   identifier (the same as the SSRC in the fixed RTP header).  A mixer
+   sends one SDES packet containing a chunk for each contributing source
+   from which it is receiving SDES information, or multiple complete
+   SDES packets in the format above if there are more than 31 such
+   sources (see Section 7).
+
+   The SDES items currently defined are described in the next sections.
+   Only the CNAME item is mandatory.  Some items shown here may be
+   useful only for particular profiles, but the item types are all
+   assigned from one common space to promote shared use and to simplify
+   profile-independent applications.  Additional items may be defined in
+   a profile by registering the type numbers with IANA as described in
+   Section 15.
+
+6.5.1 CNAME: Canonical End-Point Identifier SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    CNAME=1    |     length    | user and domain name        ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The CNAME identifier has the following properties:
+
+   o  Because the randomly allocated SSRC identifier may change if a
+      conflict is discovered or if a program is restarted, the CNAME
+      item MUST be included to provide the binding from the SSRC
+      identifier to an identifier for the source (sender or receiver)
+      that remains constant.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 46]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  Like the SSRC identifier, the CNAME identifier SHOULD also be
+      unique among all participants within one RTP session.
+
+   o  To provide a binding across multiple media tools used by one
+      participant in a set of related RTP sessions, the CNAME SHOULD be
+      fixed for that participant.
+
+   o  To facilitate third-party monitoring, the CNAME SHOULD be suitable
+      for either a program or a person to locate the source.
+
+   Therefore, the CNAME SHOULD be derived algorithmically and not
+   entered manually, when possible.  To meet these requirements, the
+   following format SHOULD be used unless a profile specifies an
+   alternate syntax or semantics.  The CNAME item SHOULD have the format
+   "user@host", or "host" if a user name is not available as on single-
+   user systems.  For both formats, "host" is either the fully qualified
+   domain name of the host from which the real-time data originates,
+   formatted according to the rules specified in RFC 1034 [6], RFC 1035
+   [7] and Section 2.1 of RFC 1123 [8]; or the standard ASCII
+   representation of the host's numeric address on the interface used
+   for the RTP communication.  For example, the standard ASCII
+   representation of an IP Version 4 address is "dotted decimal", also
+   known as dotted quad, and for IP Version 6, addresses are textually
+   represented as groups of hexadecimal digits separated by colons (with
+   variations as detailed in RFC 3513 [23]).  Other address types are
+   expected to have ASCII representations that are mutually unique.  The
+   fully qualified domain name is more convenient for a human observer
+   and may avoid the need to send a NAME item in addition, but it may be
+   difficult or impossible to obtain reliably in some operating
+   environments.  Applications that may be run in such environments
+   SHOULD use the ASCII representation of the address instead.
+
+   Examples are "doe@sleepy.example.com", "doe@192.0.2.89" or
+   "doe@2201:056D::112E:144A:1E24" for a multi-user system.  On a system
+   with no user name, examples would be "sleepy.example.com",
+   "192.0.2.89" or "2201:056D::112E:144A:1E24".
+
+   The user name SHOULD be in a form that a program such as "finger" or
+   "talk" could use, i.e., it typically is the login name rather than
+   the personal name.  The host name is not necessarily identical to the
+   one in the participant's electronic mail address.
+
+   This syntax will not provide unique identifiers for each source if an
+   application permits a user to generate multiple sources from one
+   host.  Such an application would have to rely on the SSRC to further
+   identify the source, or the profile for that application would have
+   to specify additional syntax for the CNAME identifier.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 47]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   If each application creates its CNAME independently, the resulting
+   CNAMEs may not be identical as would be required to provide a binding
+   across multiple media tools belonging to one participant in a set of
+   related RTP sessions.  If cross-media binding is required, it may be
+   necessary for the CNAME of each tool to be externally configured with
+   the same value by a coordination tool.
+
+   Application writers should be aware that private network address
+   assignments such as the Net-10 assignment proposed in RFC 1918 [24]
+   may create network addresses that are not globally unique.  This
+   would lead to non-unique CNAMEs if hosts with private addresses and
+   no direct IP connectivity to the public Internet have their RTP
+   packets forwarded to the public Internet through an RTP-level
+   translator.  (See also RFC 1627 [25].)  To handle this case,
+   applications MAY provide a means to configure a unique CNAME, but the
+   burden is on the translator to translate CNAMEs from private
+   addresses to public addresses if necessary to keep private addresses
+   from being exposed.
+
+6.5.2 NAME: User Name SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     NAME=2    |     length    | common name of source       ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   This is the real name used to describe the source, e.g., "John Doe,
+   Bit Recycler".  It may be in any form desired by the user.  For
+   applications such as conferencing, this form of name may be the most
+   desirable for display in participant lists, and therefore might be
+   sent most frequently of those items other than CNAME.  Profiles MAY
+   establish such priorities.  The NAME value is expected to remain
+   constant at least for the duration of a session.  It SHOULD NOT be
+   relied upon to be unique among all participants in the session.
+
+6.5.3 EMAIL: Electronic Mail Address SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    EMAIL=3    |     length    | email address of source     ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The email address is formatted according to RFC 2822 [9], for
+   example, "John.Doe@example.com".  The EMAIL value is expected to
+   remain constant for the duration of a session.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 48]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.5.4 PHONE: Phone Number SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    PHONE=4    |     length    | phone number of source      ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The phone number SHOULD be formatted with the plus sign replacing the
+   international access code.  For example, "+1 908 555 1212" for a
+   number in the United States.
+
+6.5.5 LOC: Geographic User Location SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     LOC=5     |     length    | geographic location of site ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Depending on the application, different degrees of detail are
+   appropriate for this item.  For conference applications, a string
+   like "Murray Hill, New Jersey" may be sufficient, while, for an
+   active badge system, strings like "Room 2A244, AT&T BL MH" might be
+   appropriate.  The degree of detail is left to the implementation
+   and/or user, but format and content MAY be prescribed by a profile.
+   The LOC value is expected to remain constant for the duration of a
+   session, except for mobile hosts.
+
+6.5.6 TOOL: Application or Tool Name SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     TOOL=6    |     length    |name/version of source appl. ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   A string giving the name and possibly version of the application
+   generating the stream, e.g., "videotool 1.2".  This information may
+   be useful for debugging purposes and is similar to the Mailer or
+   Mail-System-Version SMTP headers.  The TOOL value is expected to
+   remain constant for the duration of the session.
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 49]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.5.7 NOTE: Notice/Status SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     NOTE=7    |     length    | note about the source       ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The following semantics are suggested for this item, but these or
+   other semantics MAY be explicitly defined by a profile.  The NOTE
+   item is intended for transient messages describing the current state
+   of the source, e.g., "on the phone, can't talk".  Or, during a
+   seminar, this item might be used to convey the title of the talk.  It
+   should be used only to carry exceptional information and SHOULD NOT
+   be included routinely by all participants because this would slow
+   down the rate at which reception reports and CNAME are sent, thus
+   impairing the performance of the protocol.  In particular, it SHOULD
+   NOT be included as an item in a user's configuration file nor
+   automatically generated as in a quote-of-the-day.
+
+   Since the NOTE item may be important to display while it is active,
+   the rate at which other non-CNAME items such as NAME are transmitted
+   might be reduced so that the NOTE item can take that part of the RTCP
+   bandwidth.  When the transient message becomes inactive, the NOTE
+   item SHOULD continue to be transmitted a few times at the same
+   repetition rate but with a string of length zero to signal the
+   receivers.  However, receivers SHOULD also consider the NOTE item
+   inactive if it is not received for a small multiple of the repetition
+   rate, or perhaps 20-30 RTCP intervals.
+
+6.5.8 PRIV: Private Extensions SDES Item
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |     PRIV=8    |     length    | prefix length |prefix string...
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    ...             |                  value string               ...
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   This item is used to define experimental or application-specific SDES
+   extensions.  The item contains a prefix consisting of a length-string
+   pair, followed by the value string filling the remainder of the item
+   and carrying the desired information.  The prefix length field is 8
+   bits long.  The prefix string is a name chosen by the person defining
+   the PRIV item to be unique with respect to other PRIV items this
+   application might receive.  The application creator might choose to
+   use the application name plus an additional subtype identification if
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 50]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   needed.  Alternatively, it is RECOMMENDED that others choose a name
+   based on the entity they represent, then coordinate the use of the
+   name within that entity.
+
+   Note that the prefix consumes some space within the item's total
+   length of 255 octets, so the prefix should be kept as short as
+   possible.  This facility and the constrained RTCP bandwidth SHOULD
+   NOT be overloaded; it is not intended to satisfy all the control
+   communication requirements of all applications.
+
+   SDES PRIV prefixes will not be registered by IANA.  If some form of
+   the PRIV item proves to be of general utility, it SHOULD instead be
+   assigned a regular SDES item type registered with IANA so that no
+   prefix is required.  This simplifies use and increases transmission
+   efficiency.
+
+6.6 BYE: Goodbye RTCP Packet
+
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |V=2|P|    SC   |   PT=BYE=203  |             length            |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                           SSRC/CSRC                           |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      :                              ...                              :
+      +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+(opt) |     length    |               reason for leaving            ...
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The BYE packet indicates that one or more sources are no longer
+   active.
+
+   version (V), padding (P), length:
+      As described for the SR packet (see Section 6.4.1).
+
+   packet type (PT): 8 bits
+      Contains the constant 203 to identify this as an RTCP BYE packet.
+
+   source count (SC): 5 bits
+      The number of SSRC/CSRC identifiers included in this BYE packet.
+      A count value of zero is valid, but useless.
+
+   The rules for when a BYE packet should be sent are specified in
+   Sections 6.3.7 and 8.2.
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 51]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   If a BYE packet is received by a mixer, the mixer SHOULD forward the
+   BYE packet with the SSRC/CSRC identifier(s) unchanged.  If a mixer
+   shuts down, it SHOULD send a BYE packet listing all contributing
+   sources it handles, as well as its own SSRC identifier.  Optionally,
+   the BYE packet MAY include an 8-bit octet count followed by that many
+   octets of text indicating the reason for leaving, e.g., "camera
+   malfunction" or "RTP loop detected".  The string has the same
+   encoding as that described for SDES.  If the string fills the packet
+   to the next 32-bit boundary, the string is not null terminated.  If
+   not, the BYE packet MUST be padded with null octets to the next 32-
+   bit boundary.  This padding is separate from that indicated by the P
+   bit in the RTCP header.
+
+6.7 APP: Application-Defined RTCP Packet
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |V=2|P| subtype |   PT=APP=204  |             length            |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                           SSRC/CSRC                           |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                          name (ASCII)                         |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                   application-dependent data                ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The APP packet is intended for experimental use as new applications
+   and new features are developed, without requiring packet type value
+   registration.  APP packets with unrecognized names SHOULD be ignored.
+   After testing and if wider use is justified, it is RECOMMENDED that
+   each APP packet be redefined without the subtype and name fields and
+   registered with IANA using an RTCP packet type.
+
+   version (V), padding (P), length:
+      As described for the SR packet (see Section 6.4.1).
+
+   subtype: 5 bits
+      May be used as a subtype to allow a set of APP packets to be
+      defined under one unique name, or for any application-dependent
+      data.
+
+   packet type (PT): 8 bits
+      Contains the constant 204 to identify this as an RTCP APP packet.
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 52]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   name: 4 octets
+      A name chosen by the person defining the set of APP packets to be
+      unique with respect to other APP packets this application might
+      receive.  The application creator might choose to use the
+      application name, and then coordinate the allocation of subtype
+      values to others who want to define new packet types for the
+      application.  Alternatively, it is RECOMMENDED that others choose
+      a name based on the entity they represent, then coordinate the use
+      of the name within that entity.  The name is interpreted as a
+      sequence of four ASCII characters, with uppercase and lowercase
+      characters treated as distinct.
+
+   application-dependent data: variable length
+      Application-dependent data may or may not appear in an APP packet.
+      It is interpreted by the application and not RTP itself.  It MUST
+      be a multiple of 32 bits long.
+
+7. RTP Translators and Mixers
+
+   In addition to end systems, RTP supports the notion of "translators"
+   and "mixers", which could be considered as "intermediate systems" at
+   the RTP level.  Although this support adds some complexity to the
+   protocol, the need for these functions has been clearly established
+   by experiments with multicast audio and video applications in the
+   Internet.  Example uses of translators and mixers given in Section
+   2.3 stem from the presence of firewalls and low bandwidth
+   connections, both of which are likely to remain.
+
+7.1 General Description
+
+   An RTP translator/mixer connects two or more transport-level
+   "clouds".  Typically, each cloud is defined by a common network and
+   transport protocol (e.g., IP/UDP) plus a multicast address and
+   transport level destination port or a pair of unicast addresses and
+   ports.  (Network-level protocol translators, such as IP version 4 to
+   IP version 6, may be present within a cloud invisibly to RTP.)  One
+   system may serve as a translator or mixer for a number of RTP
+   sessions, but each is considered a logically separate entity.
+
+   In order to avoid creating a loop when a translator or mixer is
+   installed, the following rules MUST be observed:
+
+   o  Each of the clouds connected by translators and mixers
+      participating in one RTP session either MUST be distinct from all
+      the others in at least one of these parameters (protocol, address,
+      port), or MUST be isolated at the network level from the others.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 53]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  A derivative of the first rule is that there MUST NOT be multiple
+      translators or mixers connected in parallel unless by some
+      arrangement they partition the set of sources to be forwarded.
+
+   Similarly, all RTP end systems that can communicate through one or
+   more RTP translators or mixers share the same SSRC space, that is,
+   the SSRC identifiers MUST be unique among all these end systems.
+   Section 8.2 describes the collision resolution algorithm by which
+   SSRC identifiers are kept unique and loops are detected.
+
+   There may be many varieties of translators and mixers designed for
+   different purposes and applications.  Some examples are to add or
+   remove encryption, change the encoding of the data or the underlying
+   protocols, or replicate between a multicast address and one or more
+   unicast addresses.  The distinction between translators and mixers is
+   that a translator passes through the data streams from different
+   sources separately, whereas a mixer combines them to form one new
+   stream:
+
+   Translator: Forwards RTP packets with their SSRC identifier
+      intact; this makes it possible for receivers to identify
+      individual sources even though packets from all the sources pass
+      through the same translator and carry the translator's network
+      source address.  Some kinds of translators will pass through the
+      data untouched, but others MAY change the encoding of the data and
+      thus the RTP data payload type and timestamp.  If multiple data
+      packets are re-encoded into one, or vice versa, a translator MUST
+      assign new sequence numbers to the outgoing packets.  Losses in
+      the incoming packet stream may induce corresponding gaps in the
+      outgoing sequence numbers.  Receivers cannot detect the presence
+      of a translator unless they know by some other means what payload
+      type or transport address was used by the original source.
+
+   Mixer: Receives streams of RTP data packets from one or more
+      sources, possibly changes the data format, combines the streams in
+      some manner and then forwards the combined stream.  Since the
+      timing among multiple input sources will not generally be
+      synchronized, the mixer will make timing adjustments among the
+      streams and generate its own timing for the combined stream, so it
+      is the synchronization source.  Thus, all data packets forwarded
+      by a mixer MUST be marked with the mixer's own SSRC identifier.
+      In order to preserve the identity of the original sources
+      contributing to the mixed packet, the mixer SHOULD insert their
+      SSRC identifiers into the CSRC identifier list following the fixed
+      RTP header of the packet.  A mixer that is also itself a
+      contributing source for some packet SHOULD explicitly include its
+      own SSRC identifier in the CSRC list for that packet.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 54]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      For some applications, it MAY be acceptable for a mixer not to
+      identify sources in the CSRC list.  However, this introduces the
+      danger that loops involving those sources could not be detected.
+
+   The advantage of a mixer over a translator for applications like
+   audio is that the output bandwidth is limited to that of one source
+   even when multiple sources are active on the input side.  This may be
+   important for low-bandwidth links.  The disadvantage is that
+   receivers on the output side don't have any control over which
+   sources are passed through or muted, unless some mechanism is
+   implemented for remote control of the mixer.  The regeneration of
+   synchronization information by mixers also means that receivers can't
+   do inter-media synchronization of the original streams.  A multi-
+   media mixer could do it.
+
+         [E1]                                    [E6]
+          |                                       |
+    E1:17 |                                 E6:15 |
+          |                                       |   E6:15
+          V  M1:48 (1,17)         M1:48 (1,17)    V   M1:48 (1,17)
+         (M1)-------------><T1>-----------------><T2>-------------->[E7]
+          ^                 ^     E4:47           ^   E4:47
+     E2:1 |           E4:47 |                     |   M3:89 (64,45)
+          |                 |                     |
+         [E2]              [E4]     M3:89 (64,45) |
+                                                  |        legend:
+   [E3] --------->(M2)----------->(M3)------------|        [End system]
+          E3:64        M2:12 (64)  ^                       (Mixer)
+                                   | E5:45                 <Translator>
+                                   |
+                                  [E5]          source: SSRC (CSRCs)
+                                                ------------------->
+
+   Figure 3: Sample RTP network with end systems, mixers and translators
+
+   A collection of mixers and translators is shown in Fig. 3 to
+   illustrate their effect on SSRC and CSRC identifiers.  In the figure,
+   end systems are shown as rectangles (named E), translators as
+   triangles (named T) and mixers as ovals (named M).  The notation "M1:
+   48(1,17)" designates a packet originating a mixer M1, identified by
+   M1's (random) SSRC value of 48 and two CSRC identifiers, 1 and 17,
+   copied from the SSRC identifiers of packets from E1 and E2.
+
+7.2 RTCP Processing in Translators
+
+   In addition to forwarding data packets, perhaps modified, translators
+   and mixers MUST also process RTCP packets.  In many cases, they will
+   take apart the compound RTCP packets received from end systems to
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 55]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   aggregate SDES information and to modify the SR or RR packets.
+   Retransmission of this information may be triggered by the packet
+   arrival or by the RTCP interval timer of the translator or mixer
+   itself.
+
+   A translator that does not modify the data packets, for example one
+   that just replicates between a multicast address and a unicast
+   address, MAY simply forward RTCP packets unmodified as well.  A
+   translator that transforms the payload in some way MUST make
+   corresponding transformations in the SR and RR information so that it
+   still reflects the characteristics of the data and the reception
+   quality.  These translators MUST NOT simply forward RTCP packets.  In
+   general, a translator SHOULD NOT aggregate SR and RR packets from
+   different sources into one packet since that would reduce the
+   accuracy of the propagation delay measurements based on the LSR and
+   DLSR fields.
+
+   SR sender information:  A translator does not generate its own
+      sender information, but forwards the SR packets received from one
+      cloud to the others.  The SSRC is left intact but the sender
+      information MUST be modified if required by the translation.  If a
+      translator changes the data encoding, it MUST change the "sender's
+      byte count" field.  If it also combines several data packets into
+      one output packet, it MUST change the "sender's packet count"
+      field.  If it changes the timestamp frequency, it MUST change the
+      "RTP timestamp" field in the SR packet.
+
+   SR/RR reception report blocks:  A translator forwards reception
+      reports received from one cloud to the others.  Note that these
+      flow in the direction opposite to the data.  The SSRC is left
+      intact.  If a translator combines several data packets into one
+      output packet, and therefore changes the sequence numbers, it MUST
+      make the inverse manipulation for the packet loss fields and the
+      "extended last sequence number" field.  This may be complex.  In
+      the extreme case, there may be no meaningful way to translate the
+      reception reports, so the translator MAY pass on no reception
+      report at all or a synthetic report based on its own reception.
+      The general rule is to do what makes sense for a particular
+      translation.
+
+      A translator does not require an SSRC identifier of its own, but
+      MAY choose to allocate one for the purpose of sending reports
+      about what it has received.  These would be sent to all the
+      connected clouds, each corresponding to the translation of the
+      data stream as sent to that cloud, since reception reports are
+      normally multicast to all participants.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 56]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   SDES:  Translators typically forward without change the SDES
+      information they receive from one cloud to the others, but MAY,
+      for example, decide to filter non-CNAME SDES information if
+      bandwidth is limited.  The CNAMEs MUST be forwarded to allow SSRC
+      identifier collision detection to work.  A translator that
+      generates its own RR packets MUST send SDES CNAME information
+      about itself to the same clouds that it sends those RR packets.
+
+   BYE:  Translators forward BYE packets unchanged.  A translator
+      that is about to cease forwarding packets SHOULD send a BYE packet
+      to each connected cloud containing all the SSRC identifiers that
+      were previously being forwarded to that cloud, including the
+      translator's own SSRC identifier if it sent reports of its own.
+
+   APP:  Translators forward APP packets unchanged.
+
+7.3 RTCP Processing in Mixers
+
+   Since a mixer generates a new data stream of its own, it does not
+   pass through SR or RR packets at all and instead generates new
+   information for both sides.
+
+   SR sender information:  A mixer does not pass through sender
+      information from the sources it mixes because the characteristics
+      of the source streams are lost in the mix.  As a synchronization
+      source, the mixer SHOULD generate its own SR packets with sender
+      information about the mixed data stream and send them in the same
+      direction as the mixed stream.
+
+   SR/RR reception report blocks:  A mixer generates its own
+      reception reports for sources in each cloud and sends them out
+      only to the same cloud.  It MUST NOT send these reception reports
+      to the other clouds and MUST NOT forward reception reports from
+      one cloud to the others because the sources would not be SSRCs
+      there (only CSRCs).
+
+   SDES:  Mixers typically forward without change the SDES
+      information they receive from one cloud to the others, but MAY,
+      for example, decide to filter non-CNAME SDES information if
+      bandwidth is limited.  The CNAMEs MUST be forwarded to allow SSRC
+      identifier collision detection to work.  (An identifier in a CSRC
+      list generated by a mixer might collide with an SSRC identifier
+      generated by an end system.)  A mixer MUST send SDES CNAME
+      information about itself to the same clouds that it sends SR or RR
+      packets.
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 57]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      Since mixers do not forward SR or RR packets, they will typically
+      be extracting SDES packets from a compound RTCP packet.  To
+      minimize overhead, chunks from the SDES packets MAY be aggregated
+      into a single SDES packet which is then stacked on an SR or RR
+      packet originating from the mixer.  A mixer which aggregates SDES
+      packets will use more RTCP bandwidth than an individual source
+      because the compound packets will be longer, but that is
+      appropriate since the mixer represents multiple sources.
+      Similarly, a mixer which passes through SDES packets as they are
+      received will be transmitting RTCP packets at higher than the
+      single source rate, but again that is correct since the packets
+      come from multiple sources.  The RTCP packet rate may be different
+      on each side of the mixer.
+
+      A mixer that does not insert CSRC identifiers MAY also refrain
+      from forwarding SDES CNAMEs.  In this case, the SSRC identifier
+      spaces in the two clouds are independent.  As mentioned earlier,
+      this mode of operation creates a danger that loops can't be
+      detected.
+
+   BYE:  Mixers MUST forward BYE packets.  A mixer that is about to
+      cease forwarding packets SHOULD send a BYE packet to each
+      connected cloud containing all the SSRC identifiers that were
+      previously being forwarded to that cloud, including the mixer's
+      own SSRC identifier if it sent reports of its own.
+
+   APP:  The treatment of APP packets by mixers is application-specific.
+
+7.4 Cascaded Mixers
+
+   An RTP session may involve a collection of mixers and translators as
+   shown in Fig. 3.  If two mixers are cascaded, such as M2 and M3 in
+   the figure, packets received by a mixer may already have been mixed
+   and may include a CSRC list with multiple identifiers.  The second
+   mixer SHOULD build the CSRC list for the outgoing packet using the
+   CSRC identifiers from already-mixed input packets and the SSRC
+   identifiers from unmixed input packets.  This is shown in the output
+   arc from mixer M3 labeled M3:89(64,45) in the figure.  As in the case
+   of mixers that are not cascaded, if the resulting CSRC list has more
+   than 15 identifiers, the remainder cannot be included.
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 58]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+8.  SSRC Identifier Allocation and Use
+
+   The SSRC identifier carried in the RTP header and in various fields
+   of RTCP packets is a random 32-bit number that is required to be
+   globally unique within an RTP session.  It is crucial that the number
+   be chosen with care in order that participants on the same network or
+   starting at the same time are not likely to choose the same number.
+
+   It is not sufficient to use the local network address (such as an
+   IPv4 address) for the identifier because the address may not be
+   unique.  Since RTP translators and mixers enable interoperation among
+   multiple networks with different address spaces, the allocation
+   patterns for addresses within two spaces might result in a much
+   higher rate of collision than would occur with random allocation.
+
+   Multiple sources running on one host would also conflict.
+
+   It is also not sufficient to obtain an SSRC identifier simply by
+   calling random() without carefully initializing the state.  An
+   example of how to generate a random identifier is presented in
+   Appendix A.6.
+
+8.1 Probability of Collision
+
+   Since the identifiers are chosen randomly, it is possible that two or
+   more sources will choose the same number.  Collision occurs with the
+   highest probability when all sources are started simultaneously, for
+   example when triggered automatically by some session management
+   event.  If N is the number of sources and L the length of the
+   identifier (here, 32 bits), the probability that two sources
+   independently pick the same value can be approximated for large N
+   [26] as 1 - exp(-N**2 / 2**(L+1)).  For N=1000, the probability is
+   roughly 10**-4.
+
+   The typical collision probability is much lower than the worst-case
+   above.  When one new source joins an RTP session in which all the
+   other sources already have unique identifiers, the probability of
+   collision is just the fraction of numbers used out of the space.
+   Again, if N is the number of sources and L the length of the
+   identifier, the probability of collision is N / 2**L.  For N=1000,
+   the probability is roughly 2*10**-7.
+
+   The probability of collision is further reduced by the opportunity
+   for a new source to receive packets from other participants before
+   sending its first packet (either data or control).  If the new source
+   keeps track of the other participants (by SSRC identifier), then
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 59]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   before transmitting its first packet the new source can verify that
+   its identifier does not conflict with any that have been received, or
+   else choose again.
+
+8.2 Collision Resolution and Loop Detection
+
+   Although the probability of SSRC identifier collision is low, all RTP
+   implementations MUST be prepared to detect collisions and take the
+   appropriate actions to resolve them.  If a source discovers at any
+   time that another source is using the same SSRC identifier as its
+   own, it MUST send an RTCP BYE packet for the old identifier and
+   choose another random one.  (As explained below, this step is taken
+   only once in case of a loop.)  If a receiver discovers that two other
+   sources are colliding, it MAY keep the packets from one and discard
+   the packets from the other when this can be detected by different
+   source transport addresses or CNAMEs.  The two sources are expected
+   to resolve the collision so that the situation doesn't last.
+
+   Because the random SSRC identifiers are kept globally unique for each
+   RTP session, they can also be used to detect loops that may be
+   introduced by mixers or translators.  A loop causes duplication of
+   data and control information, either unmodified or possibly mixed, as
+   in the following examples:
+
+   o  A translator may incorrectly forward a packet to the same
+      multicast group from which it has received the packet, either
+      directly or through a chain of translators.  In that case, the
+      same packet appears several times, originating from different
+      network sources.
+
+   o  Two translators incorrectly set up in parallel, i.e., with the
+      same multicast groups on both sides, would both forward packets
+      from one multicast group to the other.  Unidirectional translators
+      would produce two copies; bidirectional translators would form a
+      loop.
+
+   o  A mixer can close a loop by sending to the same transport
+      destination upon which it receives packets, either directly or
+      through another mixer or translator.  In this case a source might
+      show up both as an SSRC on a data packet and a CSRC in a mixed
+      data packet.
+
+   A source may discover that its own packets are being looped, or that
+   packets from another source are being looped (a third-party loop).
+   Both loops and collisions in the random selection of a source
+   identifier result in packets arriving with the same SSRC identifier
+   but a different source transport address, which may be that of the
+   end system originating the packet or an intermediate system.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 60]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Therefore, if a source changes its source transport address, it MAY
+   also choose a new SSRC identifier to avoid being interpreted as a
+   looped source.  (This is not MUST because in some applications of RTP
+   sources may be expected to change addresses during a session.)  Note
+   that if a translator restarts and consequently changes the source
+   transport address (e.g., changes the UDP source port number) on which
+   it forwards packets, then all those packets will appear to receivers
+   to be looped because the SSRC identifiers are applied by the original
+   source and will not change.  This problem can be avoided by keeping
+   the source transport address fixed across restarts, but in any case
+   will be resolved after a timeout at the receivers.
+
+   Loops or collisions occurring on the far side of a translator or
+   mixer cannot be detected using the source transport address if all
+   copies of the packets go through the translator or mixer, however,
+   collisions may still be detected when chunks from two RTCP SDES
+   packets contain the same SSRC identifier but different CNAMEs.
+
+   To detect and resolve these conflicts, an RTP implementation MUST
+   include an algorithm similar to the one described below, though the
+   implementation MAY choose a different policy for which packets from
+   colliding third-party sources are kept.  The algorithm described
+   below ignores packets from a new source or loop that collide with an
+   established source.  It resolves collisions with the participant's
+   own SSRC identifier by sending an RTCP BYE for the old identifier and
+   choosing a new one.  However, when the collision was induced by a
+   loop of the participant's own packets, the algorithm will choose a
+   new identifier only once and thereafter ignore packets from the
+   looping source transport address.  This is required to avoid a flood
+   of BYE packets.
+
+   This algorithm requires keeping a table indexed by the source
+   identifier and containing the source transport addresses from the
+   first RTP packet and first RTCP packet received with that identifier,
+   along with other state for that source.  Two source transport
+   addresses are required since, for example, the UDP source port
+   numbers may be different on RTP and RTCP packets.  However, it may be
+   assumed that the network address is the same in both source transport
+   addresses.
+
+   Each SSRC or CSRC identifier received in an RTP or RTCP packet is
+   looked up in the source identifier table in order to process that
+   data or control information.  The source transport address from the
+   packet is compared to the corresponding source transport address in
+   the table to detect a loop or collision if they don't match.  For
+   control packets, each element with its own SSRC identifier, for
+   example an SDES chunk, requires a separate lookup.  (The SSRC
+   identifier in a reception report block is an exception because it
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 61]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   identifies a source heard by the reporter, and that SSRC identifier
+   is unrelated to the source transport address of the RTCP packet sent
+   by the reporter.)  If the SSRC or CSRC is not found, a new entry is
+   created.  These table entries are removed when an RTCP BYE packet is
+   received with the corresponding SSRC identifier and validated by a
+   matching source transport address, or after no packets have arrived
+   for a relatively long time (see Section 6.2.1).
+
+   Note that if two sources on the same host are transmitting with the
+   same source identifier at the time a receiver begins operation, it
+   would be possible that the first RTP packet received came from one of
+   the sources while the first RTCP packet received came from the other.
+   This would cause the wrong RTCP information to be associated with the
+   RTP data, but this situation should be sufficiently rare and harmless
+   that it may be disregarded.
+
+   In order to track loops of the participant's own data packets, the
+   implementation MUST also keep a separate list of source transport
+   addresses (not identifiers) that have been found to be conflicting.
+   As in the source identifier table, two source transport addresses
+   MUST be kept to separately track conflicting RTP and RTCP packets.
+   Note that the conflicting address list should be short, usually
+   empty.  Each element in this list stores the source addresses plus
+   the time when the most recent conflicting packet was received.  An
+   element MAY be removed from the list when no conflicting packet has
+   arrived from that source for a time on the order of 10 RTCP report
+   intervals (see Section 6.2).
+
+   For the algorithm as shown, it is assumed that the participant's own
+   source identifier and state are included in the source identifier
+   table.  The algorithm could be restructured to first make a separate
+   comparison against the participant's own source identifier.
+
+      if (SSRC or CSRC identifier is not found in the source
+          identifier table) {
+          create a new entry storing the data or control source
+              transport address, the SSRC or CSRC and other state;
+      }
+
+      /* Identifier is found in the table */
+
+      else if (table entry was created on receipt of a control packet
+               and this is the first data packet or vice versa) {
+          store the source transport address from this packet;
+      }
+      else if (source transport address from the packet does not match
+               the one saved in the table entry for this identifier) {
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 62]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+          /* An identifier collision or a loop is indicated */
+
+          if (source identifier is not the participant's own) {
+              /* OPTIONAL error counter step */
+              if (source identifier is from an RTCP SDES chunk
+                  containing a CNAME item that differs from the CNAME
+                  in the table entry) {
+                  count a third-party collision;
+              } else {
+                  count a third-party loop;
+              }
+              abort processing of data packet or control element;
+              /* MAY choose a different policy to keep new source */
+          }
+
+          /* A collision or loop of the participant's own packets */
+
+          else if (source transport address is found in the list of
+                   conflicting data or control source transport
+                   addresses) {
+              /* OPTIONAL error counter step */
+              if (source identifier is not from an RTCP SDES chunk
+                  containing a CNAME item or CNAME is the
+                  participant's own) {
+                  count occurrence of own traffic looped;
+              }
+              mark current time in conflicting address list entry;
+              abort processing of data packet or control element;
+          }
+
+          /* New collision, change SSRC identifier */
+
+          else {
+              log occurrence of a collision;
+              create a new entry in the conflicting data or control
+                  source transport address list and mark current time;
+              send an RTCP BYE packet with the old SSRC identifier;
+              choose a new SSRC identifier;
+              create a new entry in the source identifier table with
+                  the old SSRC plus the source transport address from
+                  the data or control packet being processed;
+          }
+      }
+
+   In this algorithm, packets from a newly conflicting source address
+   will be ignored and packets from the original source address will be
+   kept.  If no packets arrive from the original source for an extended
+   period, the table entry will be timed out and the new source will be
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 63]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   able to take over.  This might occur if the original source detects
+   the collision and moves to a new source identifier, but in the usual
+   case an RTCP BYE packet will be received from the original source to
+   delete the state without having to wait for a timeout.
+
+   If the original source address was received through a mixer (i.e.,
+   learned as a CSRC) and later the same source is received directly,
+   the receiver may be well advised to switch to the new source address
+   unless other sources in the mix would be lost.  Furthermore, for
+   applications such as telephony in which some sources such as mobile
+   entities may change addresses during the course of an RTP session,
+   the RTP implementation SHOULD modify the collision detection
+   algorithm to accept packets from the new source transport address.
+   To guard against flip-flopping between addresses if a genuine
+   collision does occur, the algorithm SHOULD include some means to
+   detect this case and avoid switching.
+
+   When a new SSRC identifier is chosen due to a collision, the
+   candidate identifier SHOULD first be looked up in the source
+   identifier table to see if it was already in use by some other
+   source.  If so, another candidate MUST be generated and the process
+   repeated.
+
+   A loop of data packets to a multicast destination can cause severe
+   network flooding.  All mixers and translators MUST implement a loop
+   detection algorithm like the one here so that they can break loops.
+   This should limit the excess traffic to no more than one duplicate
+   copy of the original traffic, which may allow the session to continue
+   so that the cause of the loop can be found and fixed.  However, in
+   extreme cases where a mixer or translator does not properly break the
+   loop and high traffic levels result, it may be necessary for end
+   systems to cease transmitting data or control packets entirely.  This
+   decision may depend upon the application.  An error condition SHOULD
+   be indicated as appropriate.  Transmission MAY be attempted again
+   periodically after a long, random time (on the order of minutes).
+
+8.3 Use with Layered Encodings
+
+   For layered encodings transmitted on separate RTP sessions (see
+   Section 2.4), a single SSRC identifier space SHOULD be used across
+   the sessions of all layers and the core (base) layer SHOULD be used
+   for SSRC identifier allocation and collision resolution.  When a
+   source discovers that it has collided, it transmits an RTCP BYE
+   packet on only the base layer but changes the SSRC identifier to the
+   new value in all layers.
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 64]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+9. Security
+
+   Lower layer protocols may eventually provide all the security
+   services that may be desired for applications of RTP, including
+   authentication, integrity, and confidentiality.  These services have
+   been specified for IP in [27].  Since the initial audio and video
+   applications using RTP needed a confidentiality service before such
+   services were available for the IP layer, the confidentiality service
+   described in the next section was defined for use with RTP and RTCP.
+   That description is included here to codify existing practice.  New
+   applications of RTP MAY implement this RTP-specific confidentiality
+   service for backward compatibility, and/or they MAY implement
+   alternative security services.  The overhead on the RTP protocol for
+   this confidentiality service is low, so the penalty will be minimal
+   if this service is obsoleted by other services in the future.
+
+   Alternatively, other services, other implementations of services and
+   other algorithms may be defined for RTP in the future.  In
+   particular, an RTP profile called Secure Real-time Transport Protocol
+   (SRTP) [28] is being developed to provide confidentiality of the RTP
+   payload while leaving the RTP header in the clear so that link-level
+   header compression algorithms can still operate.  It is expected that
+   SRTP will be the correct choice for many applications.  SRTP is based
+   on the Advanced Encryption Standard (AES) and provides stronger
+   security than the service described here.  No claim is made that the
+   methods presented here are appropriate for a particular security
+   need.  A profile may specify which services and algorithms should be
+   offered by applications, and may provide guidance as to their
+   appropriate use.
+
+   Key distribution and certificates are outside the scope of this
+   document.
+
+9.1 Confidentiality
+
+   Confidentiality means that only the intended receiver(s) can decode
+   the received packets; for others, the packet contains no useful
+   information.  Confidentiality of the content is achieved by
+   encryption.
+
+   When it is desired to encrypt RTP or RTCP according to the method
+   specified in this section, all the octets that will be encapsulated
+   for transmission in a single lower-layer packet are encrypted as a
+   unit.  For RTCP, a 32-bit random number redrawn for each unit MUST be
+   prepended to the unit before encryption.  For RTP, no prefix is
+   prepended; instead, the sequence number and timestamp fields are
+   initialized with random offsets.  This is considered to be a weak
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 65]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   initialization vector (IV) because of poor randomness properties.  In
+   addition, if the subsequent field, the SSRC, can be manipulated by an
+   enemy, there is further weakness of the encryption method.
+
+   For RTCP, an implementation MAY segregate the individual RTCP packets
+   in a compound RTCP packet into two separate compound RTCP packets,
+   one to be encrypted and one to be sent in the clear.  For example,
+   SDES information might be encrypted while reception reports were sent
+   in the clear to accommodate third-party monitors that are not privy
+   to the encryption key.  In this example, depicted in Fig. 4, the SDES
+   information MUST be appended to an RR packet with no reports (and the
+   random number) to satisfy the requirement that all compound RTCP
+   packets begin with an SR or RR packet.  The SDES CNAME item is
+   required in either the encrypted or unencrypted packet, but not both.
+   The same SDES information SHOULD NOT be carried in both packets as
+   this may compromise the encryption.
+
+             UDP packet                     UDP packet
+   -----------------------------  ------------------------------
+   [random][RR][SDES #CNAME ...]  [SR #senderinfo #site1 #site2]
+   -----------------------------  ------------------------------
+             encrypted                     not encrypted
+
+   #: SSRC identifier
+
+       Figure 4: Encrypted and non-encrypted RTCP packets
+
+   The presence of encryption and the use of the correct key are
+   confirmed by the receiver through header or payload validity checks.
+   Examples of such validity checks for RTP and RTCP headers are given
+   in Appendices A.1 and A.2.
+
+   To be consistent with existing implementations of the initial
+   specification of RTP in RFC 1889, the default encryption algorithm is
+   the Data Encryption Standard (DES) algorithm in cipher block chaining
+   (CBC) mode, as described in Section 1.1 of RFC 1423 [29], except that
+   padding to a multiple of 8 octets is indicated as described for the P
+   bit in Section 5.1.  The initialization vector is zero because random
+   values are supplied in the RTP header or by the random prefix for
+   compound RTCP packets.  For details on the use of CBC initialization
+   vectors, see [30].
+
+   Implementations that support the encryption method specified here
+   SHOULD always support the DES algorithm in CBC mode as the default
+   cipher for this method to maximize interoperability.  This method was
+   chosen because it has been demonstrated to be easy and practical to
+   use in experimental audio and video tools in operation on the
+   Internet.  However, DES has since been found to be too easily broken.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 66]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   It is RECOMMENDED that stronger encryption algorithms such as
+   Triple-DES be used in place of the default algorithm.  Furthermore,
+   secure CBC mode requires that the first block of each packet be XORed
+   with a random, independent IV of the same size as the cipher's block
+   size.  For RTCP, this is (partially) achieved by prepending each
+   packet with a 32-bit random number, independently chosen for each
+   packet.  For RTP, the timestamp and sequence number start from random
+   values, but consecutive packets will not be independently randomized.
+   It should be noted that the randomness in both cases (RTP and RTCP)
+   is limited.  High-security applications SHOULD consider other, more
+   conventional, protection means.  Other encryption algorithms MAY be
+   specified dynamically for a session by non-RTP means.  In particular,
+   the SRTP profile [28] based on AES is being developed to take into
+   account known plaintext and CBC plaintext manipulation concerns, and
+   will be the correct choice in the future.
+
+   As an alternative to encryption at the IP level or at the RTP level
+   as described above, profiles MAY define additional payload types for
+   encrypted encodings.  Those encodings MUST specify how padding and
+   other aspects of the encryption are to be handled.  This method
+   allows encrypting only the data while leaving the headers in the
+   clear for applications where that is desired.  It may be particularly
+   useful for hardware devices that will handle both decryption and
+   decoding.  It is also valuable for applications where link-level
+   compression of RTP and lower-layer headers is desired and
+   confidentiality of the payload (but not addresses) is sufficient
+   since encryption of the headers precludes compression.
+
+9.2 Authentication and Message Integrity
+
+   Authentication and message integrity services are not defined at the
+   RTP level since these services would not be directly feasible without
+   a key management infrastructure.  It is expected that authentication
+   and integrity services will be provided by lower layer protocols.
+
+10. Congestion Control
+
+   All transport protocols used on the Internet need to address
+   congestion control in some way [31].  RTP is not an exception, but
+   because the data transported over RTP is often inelastic (generated
+   at a fixed or controlled rate), the means to control congestion in
+   RTP may be quite different from those for other transport protocols
+   such as TCP.  In one sense, inelasticity reduces the risk of
+   congestion because the RTP stream will not expand to consume all
+   available bandwidth as a TCP stream can.  However, inelasticity also
+   means that the RTP stream cannot arbitrarily reduce its load on the
+   network to eliminate congestion when it occurs.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 67]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Since RTP may be used for a wide variety of applications in many
+   different contexts, there is no single congestion control mechanism
+   that will work for all.  Therefore, congestion control SHOULD be
+   defined in each RTP profile as appropriate.  For some profiles, it
+   may be sufficient to include an applicability statement restricting
+   the use of that profile to environments where congestion is avoided
+   by engineering.  For other profiles, specific methods such as data
+   rate adaptation based on RTCP feedback may be required.
+
+11. RTP over Network and Transport Protocols
+
+   This section describes issues specific to carrying RTP packets within
+   particular network and transport protocols.  The following rules
+   apply unless superseded by protocol-specific definitions outside this
+   specification.
+
+   RTP relies on the underlying protocol(s) to provide demultiplexing of
+   RTP data and RTCP control streams.  For UDP and similar protocols,
+   RTP SHOULD use an even destination port number and the corresponding
+   RTCP stream SHOULD use the next higher (odd) destination port number.
+   For applications that take a single port number as a parameter and
+   derive the RTP and RTCP port pair from that number, if an odd number
+   is supplied then the application SHOULD replace that number with the
+   next lower (even) number to use as the base of the port pair.  For
+   applications in which the RTP and RTCP destination port numbers are
+   specified via explicit, separate parameters (using a signaling
+   protocol or other means), the application MAY disregard the
+   restrictions that the port numbers be even/odd and consecutive
+   although the use of an even/odd port pair is still encouraged.  The
+   RTP and RTCP port numbers MUST NOT be the same since RTP relies on
+   the port numbers to demultiplex the RTP data and RTCP control
+   streams.
+
+   In a unicast session, both participants need to identify a port pair
+   for receiving RTP and RTCP packets.  Both participants MAY use the
+   same port pair.  A participant MUST NOT assume that the source port
+   of the incoming RTP or RTCP packet can be used as the destination
+   port for outgoing RTP or RTCP packets.  When RTP data packets are
+   being sent in both directions, each participant's RTCP SR packets
+   MUST be sent to the port that the other participant has specified for
+   reception of RTCP.  The RTCP SR packets combine sender information
+   for the outgoing data plus reception report information for the
+   incoming data.  If a side is not actively sending data (see Section
+   6.4), an RTCP RR packet is sent instead.
+
+   It is RECOMMENDED that layered encoding applications (see Section
+   2.4) use a set of contiguous port numbers.  The port numbers MUST be
+   distinct because of a widespread deficiency in existing operating
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 68]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   systems that prevents use of the same port with multiple multicast
+   addresses, and for unicast, there is only one permissible address.
+   Thus for layer n, the data port is P + 2n, and the control port is P
+   + 2n + 1.  When IP multicast is used, the addresses MUST also be
+   distinct because multicast routing and group membership are managed
+   on an address granularity.  However, allocation of contiguous IP
+   multicast addresses cannot be assumed because some groups may require
+   different scopes and may therefore be allocated from different
+   address ranges.
+
+   The previous paragraph conflicts with the SDP specification, RFC 2327
+   [15], which says that it is illegal for both multiple addresses and
+   multiple ports to be specified in the same session description
+   because the association of addresses with ports could be ambiguous.
+   It is intended that this restriction will be relaxed in a revision of
+   RFC 2327 to allow an equal number of addresses and ports to be
+   specified with a one-to-one mapping implied.
+
+   RTP data packets contain no length field or other delineation,
+   therefore RTP relies on the underlying protocol(s) to provide a
+   length indication.  The maximum length of RTP packets is limited only
+   by the underlying protocols.
+
+   If RTP packets are to be carried in an underlying protocol that
+   provides the abstraction of a continuous octet stream rather than
+   messages (packets), an encapsulation of the RTP packets MUST be
+   defined to provide a framing mechanism.  Framing is also needed if
+   the underlying protocol may contain padding so that the extent of the
+   RTP payload cannot be determined.  The framing mechanism is not
+   defined here.
+
+   A profile MAY specify a framing method to be used even when RTP is
+   carried in protocols that do provide framing in order to allow
+   carrying several RTP packets in one lower-layer protocol data unit,
+   such as a UDP packet.  Carrying several RTP packets in one network or
+   transport packet reduces header overhead and may simplify
+   synchronization between different streams.
+
+12. Summary of Protocol Constants
+
+   This section contains a summary listing of the constants defined in
+   this specification.
+
+   The RTP payload type (PT) constants are defined in profiles rather
+   than this document.  However, the octet of the RTP header which
+   contains the marker bit(s) and payload type MUST avoid the reserved
+   values 200 and 201 (decimal) to distinguish RTP packets from the RTCP
+   SR and RR packet types for the header validation procedure described
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 69]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   in Appendix A.1.  For the standard definition of one marker bit and a
+   7-bit payload type field as shown in this specification, this
+   restriction means that payload types 72 and 73 are reserved.
+
+12.1 RTCP Packet Types
+
+   abbrev.  name                 value
+   SR       sender report          200
+   RR       receiver report        201
+   SDES     source description     202
+   BYE      goodbye                203
+   APP      application-defined    204
+
+   These type values were chosen in the range 200-204 for improved
+   header validity checking of RTCP packets compared to RTP packets or
+   other unrelated packets.  When the RTCP packet type field is compared
+   to the corresponding octet of the RTP header, this range corresponds
+   to the marker bit being 1 (which it usually is not in data packets)
+   and to the high bit of the standard payload type field being 1 (since
+   the static payload types are typically defined in the low half).
+   This range was also chosen to be some distance numerically from 0 and
+   255 since all-zeros and all-ones are common data patterns.
+
+   Since all compound RTCP packets MUST begin with SR or RR, these codes
+   were chosen as an even/odd pair to allow the RTCP validity check to
+   test the maximum number of bits with mask and value.
+
+   Additional RTCP packet types may be registered through IANA (see
+   Section 15).
+
+12.2 SDES Types
+
+   abbrev.  name                            value
+   END      end of SDES list                    0
+   CNAME    canonical name                      1
+   NAME     user name                           2
+   EMAIL    user's electronic mail address      3
+   PHONE    user's phone number                 4
+   LOC      geographic user location            5
+   TOOL     name of application or tool         6
+   NOTE     notice about the source             7
+   PRIV     private extensions                  8
+
+   Additional SDES types may be registered through IANA (see Section
+   15).
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 70]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+13.  RTP Profiles and Payload Format Specifications
+
+   A complete specification of RTP for a particular application will
+   require one or more companion documents of two types described here:
+   profiles, and payload format specifications.
+
+   RTP may be used for a variety of applications with somewhat differing
+   requirements.  The flexibility to adapt to those requirements is
+   provided by allowing multiple choices in the main protocol
+   specification, then selecting the appropriate choices or defining
+   extensions for a particular environment and class of applications in
+   a separate profile document.  Typically an application will operate
+   under only one profile in a particular RTP session, so there is no
+   explicit indication within the RTP protocol itself as to which
+   profile is in use.  A profile for audio and video applications may be
+   found in the companion RFC 3551.  Profiles are typically titled "RTP
+   Profile for ...".
+
+   The second type of companion document is a payload format
+   specification, which defines how a particular kind of payload data,
+   such as H.261 encoded video, should be carried in RTP.  These
+   documents are typically titled "RTP Payload Format for XYZ
+   Audio/Video Encoding".  Payload formats may be useful under multiple
+   profiles and may therefore be defined independently of any particular
+   profile.  The profile documents are then responsible for assigning a
+   default mapping of that format to a payload type value if needed.
+
+   Within this specification, the following items have been identified
+   for possible definition within a profile, but this list is not meant
+   to be exhaustive:
+
+   RTP data header: The octet in the RTP data header that contains
+      the marker bit and payload type field MAY be redefined by a
+      profile to suit different requirements, for example with more or
+      fewer marker bits (Section 5.3, p. 18).
+
+   Payload types: Assuming that a payload type field is included,
+      the profile will usually define a set of payload formats (e.g.,
+      media encodings) and a default static mapping of those formats to
+      payload type values.  Some of the payload formats may be defined
+      by reference to separate payload format specifications.  For each
+      payload type defined, the profile MUST specify the RTP timestamp
+      clock rate to be used (Section 5.1, p. 14).
+
+   RTP data header additions: Additional fields MAY be appended to
+      the fixed RTP data header if some additional functionality is
+      required across the profile's class of applications independent of
+      payload type (Section 5.3, p. 18).
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 71]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   RTP data header extensions: The contents of the first 16 bits of
+      the RTP data header extension structure MUST be defined if use of
+      that mechanism is to be allowed under the profile for
+      implementation-specific extensions (Section 5.3.1, p. 18).
+
+   RTCP packet types: New application-class-specific RTCP packet
+      types MAY be defined and registered with IANA.
+
+   RTCP report interval: A profile SHOULD specify that the values
+      suggested in Section 6.2 for the constants employed in the
+      calculation of the RTCP report interval will be used.  Those are
+      the RTCP fraction of session bandwidth, the minimum report
+      interval, and the bandwidth split between senders and receivers.
+      A profile MAY specify alternate values if they have been
+      demonstrated to work in a scalable manner.
+
+   SR/RR extension: An extension section MAY be defined for the
+      RTCP SR and RR packets if there is additional information that
+      should be reported regularly about the sender or receivers
+      (Section 6.4.3, p. 42 and 43).
+
+   SDES use: The profile MAY specify the relative priorities for
+      RTCP SDES items to be transmitted or excluded entirely (Section
+      6.3.9); an alternate syntax or semantics for the CNAME item
+      (Section 6.5.1); the format of the LOC item (Section 6.5.5); the
+      semantics and use of the NOTE item (Section 6.5.7); or new SDES
+      item types to be registered with IANA.
+
+   Security: A profile MAY specify which security services and
+      algorithms should be offered by applications, and MAY provide
+      guidance as to their appropriate use (Section 9, p. 65).
+
+   String-to-key mapping: A profile MAY specify how a user-provided
+      password or pass phrase is mapped into an encryption key.
+
+   Congestion: A profile SHOULD specify the congestion control
+      behavior appropriate for that profile.
+
+   Underlying protocol: Use of a particular underlying network or
+      transport layer protocol to carry RTP packets MAY be required.
+
+   Transport mapping: A mapping of RTP and RTCP to transport-level
+      addresses, e.g., UDP ports, other than the standard mapping
+      defined in Section 11, p. 68 may be specified.
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 72]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Encapsulation: An encapsulation of RTP packets may be defined to
+      allow multiple RTP data packets to be carried in one lower-layer
+      packet or to provide framing over underlying protocols that do not
+      already do so (Section 11, p. 69).
+
+   It is not expected that a new profile will be required for every
+   application.  Within one application class, it would be better to
+   extend an existing profile rather than make a new one in order to
+   facilitate interoperation among the applications since each will
+   typically run under only one profile.  Simple extensions such as the
+   definition of additional payload type values or RTCP packet types may
+   be accomplished by registering them through IANA and publishing their
+   descriptions in an addendum to the profile or in a payload format
+   specification.
+
+14. Security Considerations
+
+   RTP suffers from the same security liabilities as the underlying
+   protocols.  For example, an impostor can fake source or destination
+   network addresses, or change the header or payload.  Within RTCP, the
+   CNAME and NAME information may be used to impersonate another
+   participant.  In addition, RTP may be sent via IP multicast, which
+   provides no direct means for a sender to know all the receivers of
+   the data sent and therefore no measure of privacy.  Rightly or not,
+   users may be more sensitive to privacy concerns with audio and video
+   communication than they have been with more traditional forms of
+   network communication [33].  Therefore, the use of security
+   mechanisms with RTP is important.  These mechanisms are discussed in
+   Section 9.
+
+   RTP-level translators or mixers may be used to allow RTP traffic to
+   reach hosts behind firewalls.  Appropriate firewall security
+   principles and practices, which are beyond the scope of this
+   document, should be followed in the design and installation of these
+   devices and in the admission of RTP applications for use behind the
+   firewall.
+
+15. IANA Considerations
+
+   Additional RTCP packet types and SDES item types may be registered
+   through the Internet Assigned Numbers Authority (IANA).  Since these
+   number spaces are small, allowing unconstrained registration of new
+   values would not be prudent.  To facilitate review of requests and to
+   promote shared use of new types among multiple applications, requests
+   for registration of new values must be documented in an RFC or other
+   permanent and readily available reference such as the product of
+   another cooperative standards body (e.g., ITU-T).  Other requests may
+   also be accepted, under the advice of a "designated expert."
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 73]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   (Contact the IANA for the contact information of the current expert.)
+
+   RTP profile specifications SHOULD register with IANA a name for the
+   profile in the form "RTP/xxx", where xxx is a short abbreviation of
+   the profile title.  These names are for use by higher-level control
+   protocols, such as the Session Description Protocol (SDP), RFC 2327
+   [15], to refer to transport methods.
+
+16. Intellectual Property Rights Statement
+
+   The IETF takes no position regarding the validity or scope of any
+   intellectual property or other rights that might be claimed to
+   pertain to the implementation or use of the technology described in
+   this document or the extent to which any license under such rights
+   might or might not be available; neither does it represent that it
+   has made any effort to identify any such rights.  Information on the
+   IETF's procedures with respect to rights in standards-track and
+   standards-related documentation can be found in BCP-11.  Copies of
+   claims of rights made available for publication and any assurances of
+   licenses to be made available, or the result of an attempt made to
+   obtain a general license or permission for the use of such
+   proprietary rights by implementors or users of this specification can
+   be obtained from the IETF Secretariat.
+
+   The IETF invites any interested party to bring to its attention any
+   copyrights, patents or patent applications, or other proprietary
+   rights which may cover technology that may be required to practice
+   this standard.  Please address the information to the IETF Executive
+   Director.
+
+17.  Acknowledgments
+
+   This memorandum is based on discussions within the IETF Audio/Video
+   Transport working group chaired by Stephen Casner and Colin Perkins.
+   The current protocol has its origins in the Network Voice Protocol
+   and the Packet Video Protocol (Danny Cohen and Randy Cole) and the
+   protocol implemented by the vat application (Van Jacobson and Steve
+   McCanne).  Christian Huitema provided ideas for the random identifier
+   generator.  Extensive analysis and simulation of the timer
+   reconsideration algorithm was done by Jonathan Rosenberg.  The
+   additions for layered encodings were specified by Michael Speer and
+   Steve McCanne.
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 74]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+Appendix A - Algorithms
+
+   We provide examples of C code for aspects of RTP sender and receiver
+   algorithms.  There may be other implementation methods that are
+   faster in particular operating environments or have other advantages.
+   These implementation notes are for informational purposes only and
+   are meant to clarify the RTP specification.
+
+   The following definitions are used for all examples; for clarity and
+   brevity, the structure definitions are only valid for 32-bit big-
+   endian (most significant octet first) architectures.  Bit fields are
+   assumed to be packed tightly in big-endian bit order, with no
+   additional padding.  Modifications would be required to construct a
+   portable implementation.
+
+   /*
+    * rtp.h  --  RTP header file
+    */
+   #include <sys/types.h>
+
+   /*
+    * The type definitions below are valid for 32-bit architectures and
+    * may have to be adjusted for 16- or 64-bit architectures.
+    */
+   typedef unsigned char  u_int8;
+   typedef unsigned short u_int16;
+   typedef unsigned int   u_int32;
+   typedef          short int16;
+
+   /*
+    * Current protocol version.
+    */
+   #define RTP_VERSION    2
+
+   #define RTP_SEQ_MOD (1<<16)
+   #define RTP_MAX_SDES 255      /* maximum text length for SDES */
+
+   typedef enum {
+       RTCP_SR   = 200,
+       RTCP_RR   = 201,
+       RTCP_SDES = 202,
+       RTCP_BYE  = 203,
+       RTCP_APP  = 204
+   } rtcp_type_t;
+
+   typedef enum {
+       RTCP_SDES_END   = 0,
+       RTCP_SDES_CNAME = 1,
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 75]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+       RTCP_SDES_NAME  = 2,
+       RTCP_SDES_EMAIL = 3,
+       RTCP_SDES_PHONE = 4,
+       RTCP_SDES_LOC   = 5,
+       RTCP_SDES_TOOL  = 6,
+       RTCP_SDES_NOTE  = 7,
+       RTCP_SDES_PRIV  = 8
+   } rtcp_sdes_type_t;
+
+   /*
+    * RTP data header
+    */
+   typedef struct {
+       unsigned int version:2;   /* protocol version */
+       unsigned int p:1;         /* padding flag */
+       unsigned int x:1;         /* header extension flag */
+       unsigned int cc:4;        /* CSRC count */
+       unsigned int m:1;         /* marker bit */
+       unsigned int pt:7;        /* payload type */
+       unsigned int seq:16;      /* sequence number */
+       u_int32 ts;               /* timestamp */
+       u_int32 ssrc;             /* synchronization source */
+       u_int32 csrc[1];          /* optional CSRC list */
+   } rtp_hdr_t;
+
+   /*
+    * RTCP common header word
+    */
+   typedef struct {
+       unsigned int version:2;   /* protocol version */
+       unsigned int p:1;         /* padding flag */
+       unsigned int count:5;     /* varies by packet type */
+       unsigned int pt:8;        /* RTCP packet type */
+       u_int16 length;           /* pkt len in words, w/o this word */
+   } rtcp_common_t;
+
+   /*
+    * Big-endian mask for version, padding bit and packet type pair
+    */
+   #define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)
+   #define RTCP_VALID_VALUE ((RTP_VERSION << 14) | RTCP_SR)
+
+   /*
+    * Reception report block
+    */
+   typedef struct {
+       u_int32 ssrc;             /* data source being reported */
+       unsigned int fraction:8;  /* fraction lost since last SR/RR */
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 76]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+       int lost:24;              /* cumul. no. pkts lost (signed!) */
+       u_int32 last_seq;         /* extended last seq. no. received */
+       u_int32 jitter;           /* interarrival jitter */
+       u_int32 lsr;              /* last SR packet from this source */
+       u_int32 dlsr;             /* delay since last SR packet */
+   } rtcp_rr_t;
+
+   /*
+    * SDES item
+    */
+   typedef struct {
+       u_int8 type;              /* type of item (rtcp_sdes_type_t) */
+       u_int8 length;            /* length of item (in octets) */
+       char data[1];             /* text, not null-terminated */
+   } rtcp_sdes_item_t;
+
+   /*
+    * One RTCP packet
+    */
+   typedef struct {
+       rtcp_common_t common;     /* common header */
+       union {
+           /* sender report (SR) */
+           struct {
+               u_int32 ssrc;     /* sender generating this report */
+               u_int32 ntp_sec;  /* NTP timestamp */
+               u_int32 ntp_frac;
+               u_int32 rtp_ts;   /* RTP timestamp */
+               u_int32 psent;    /* packets sent */
+               u_int32 osent;    /* octets sent */
+               rtcp_rr_t rr[1];  /* variable-length list */
+           } sr;
+
+           /* reception report (RR) */
+           struct {
+               u_int32 ssrc;     /* receiver generating this report */
+               rtcp_rr_t rr[1];  /* variable-length list */
+           } rr;
+
+           /* source description (SDES) */
+           struct rtcp_sdes {
+               u_int32 src;      /* first SSRC/CSRC */
+               rtcp_sdes_item_t item[1]; /* list of SDES items */
+           } sdes;
+
+           /* BYE */
+           struct {
+               u_int32 src[1];   /* list of sources */
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 77]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+               /* can't express trailing text for reason */
+           } bye;
+       } r;
+   } rtcp_t;
+
+   typedef struct rtcp_sdes rtcp_sdes_t;
+
+   /*
+    * Per-source state information
+    */
+   typedef struct {
+       u_int16 max_seq;        /* highest seq. number seen */
+       u_int32 cycles;         /* shifted count of seq. number cycles */
+       u_int32 base_seq;       /* base seq number */
+       u_int32 bad_seq;        /* last 'bad' seq number + 1 */
+       u_int32 probation;      /* sequ. packets till source is valid */
+       u_int32 received;       /* packets received */
+       u_int32 expected_prior; /* packet expected at last interval */
+       u_int32 received_prior; /* packet received at last interval */
+       u_int32 transit;        /* relative trans time for prev pkt */
+       u_int32 jitter;         /* estimated jitter */
+       /* ... */
+   } source;
+
+A.1 RTP Data Header Validity Checks
+
+   An RTP receiver should check the validity of the RTP header on
+   incoming packets since they might be encrypted or might be from a
+   different application that happens to be misaddressed.  Similarly, if
+   encryption according to the method described in Section 9 is enabled,
+   the header validity check is needed to verify that incoming packets
+   have been correctly decrypted, although a failure of the header
+   validity check (e.g., unknown payload type) may not necessarily
+   indicate decryption failure.
+
+   Only weak validity checks are possible on an RTP data packet from a
+   source that has not been heard before:
+
+   o  RTP version field must equal 2.
+
+   o  The payload type must be known, and in particular it must not be
+      equal to SR or RR.
+
+   o  If the P bit is set, then the last octet of the packet must
+      contain a valid octet count, in particular, less than the total
+      packet length minus the header size.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 78]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  The X bit must be zero if the profile does not specify that the
+      header extension mechanism may be used.  Otherwise, the extension
+      length field must be less than the total packet size minus the
+      fixed header length and padding.
+
+   o  The length of the packet must be consistent with CC and payload
+      type (if payloads have a known length).
+
+   The last three checks are somewhat complex and not always possible,
+   leaving only the first two which total just a few bits.  If the SSRC
+   identifier in the packet is one that has been received before, then
+   the packet is probably valid and checking if the sequence number is
+   in the expected range provides further validation.  If the SSRC
+   identifier has not been seen before, then data packets carrying that
+   identifier may be considered invalid until a small number of them
+   arrive with consecutive sequence numbers.  Those invalid packets MAY
+   be discarded or they MAY be stored and delivered once validation has
+   been achieved if the resulting delay is acceptable.
+
+   The routine update_seq shown below ensures that a source is declared
+   valid only after MIN_SEQUENTIAL packets have been received in
+   sequence.  It also validates the sequence number seq of a newly
+   received packet and updates the sequence state for the packet's
+   source in the structure to which s points.
+
+   When a new source is heard for the first time, that is, its SSRC
+   identifier is not in the table (see Section 8.2), and the per-source
+   state is allocated for it, s->probation is set to the number of
+   sequential packets required before declaring a source valid
+   (parameter MIN_SEQUENTIAL) and other variables are initialized:
+
+      init_seq(s, seq);
+      s->max_seq = seq - 1;
+      s->probation = MIN_SEQUENTIAL;
+
+   A non-zero s->probation marks the source as not yet valid so the
+   state may be discarded after a short timeout rather than a long one,
+   as discussed in Section 6.2.1.
+
+   After a source is considered valid, the sequence number is considered
+   valid if it is no more than MAX_DROPOUT ahead of s->max_seq nor more
+   than MAX_MISORDER behind.  If the new sequence number is ahead of
+   max_seq modulo the RTP sequence number range (16 bits), but is
+   smaller than max_seq, it has wrapped around and the (shifted) count
+   of sequence number cycles is incremented.  A value of one is returned
+   to indicate a valid sequence number.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 79]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Otherwise, the value zero is returned to indicate that the validation
+   failed, and the bad sequence number plus 1 is stored.  If the next
+   packet received carries the next higher sequence number, it is
+   considered the valid start of a new packet sequence presumably caused
+   by an extended dropout or a source restart.  Since multiple complete
+   sequence number cycles may have been missed, the packet loss
+   statistics are reset.
+
+   Typical values for the parameters are shown, based on a maximum
+   misordering time of 2 seconds at 50 packets/second and a maximum
+   dropout of 1 minute.  The dropout parameter MAX_DROPOUT should be a
+   small fraction of the 16-bit sequence number space to give a
+   reasonable probability that new sequence numbers after a restart will
+   not fall in the acceptable range for sequence numbers from before the
+   restart.
+
+   void init_seq(source *s, u_int16 seq)
+   {
+       s->base_seq = seq;
+       s->max_seq = seq;
+       s->bad_seq = RTP_SEQ_MOD + 1;   /* so seq == bad_seq is false */
+       s->cycles = 0;
+       s->received = 0;
+       s->received_prior = 0;
+       s->expected_prior = 0;
+       /* other initialization */
+   }
+
+   int update_seq(source *s, u_int16 seq)
+   {
+       u_int16 udelta = seq - s->max_seq;
+       const int MAX_DROPOUT = 3000;
+       const int MAX_MISORDER = 100;
+       const int MIN_SEQUENTIAL = 2;
+
+       /*
+        * Source is not valid until MIN_SEQUENTIAL packets with
+        * sequential sequence numbers have been received.
+        */
+       if (s->probation) {
+           /* packet is in sequence */
+           if (seq == s->max_seq + 1) {
+               s->probation--;
+               s->max_seq = seq;
+               if (s->probation == 0) {
+                   init_seq(s, seq);
+                   s->received++;
+                   return 1;
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 80]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+               }
+           } else {
+               s->probation = MIN_SEQUENTIAL - 1;
+               s->max_seq = seq;
+           }
+           return 0;
+       } else if (udelta < MAX_DROPOUT) {
+           /* in order, with permissible gap */
+           if (seq < s->max_seq) {
+               /*
+                * Sequence number wrapped - count another 64K cycle.
+                */
+               s->cycles += RTP_SEQ_MOD;
+           }
+           s->max_seq = seq;
+       } else if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) {
+           /* the sequence number made a very large jump */
+           if (seq == s->bad_seq) {
+               /*
+                * Two sequential packets -- assume that the other side
+                * restarted without telling us so just re-sync
+                * (i.e., pretend this was the first packet).
+                */
+               init_seq(s, seq);
+           }
+           else {
+               s->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
+               return 0;
+           }
+       } else {
+           /* duplicate or reordered packet */
+       }
+       s->received++;
+       return 1;
+   }
+
+   The validity check can be made stronger requiring more than two
+   packets in sequence.  The disadvantages are that a larger number of
+   initial packets will be discarded (or delayed in a queue) and that
+   high packet loss rates could prevent validation.  However, because
+   the RTCP header validation is relatively strong, if an RTCP packet is
+   received from a source before the data packets, the count could be
+   adjusted so that only two packets are required in sequence.  If
+   initial data loss for a few seconds can be tolerated, an application
+   MAY choose to discard all data packets from a source until a valid
+   RTCP packet has been received from that source.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 81]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Depending on the application and encoding, algorithms may exploit
+   additional knowledge about the payload format for further validation.
+   For payload types where the timestamp increment is the same for all
+   packets, the timestamp values can be predicted from the previous
+   packet received from the same source using the sequence number
+   difference (assuming no change in payload type).
+
+   A strong "fast-path" check is possible since with high probability
+   the first four octets in the header of a newly received RTP data
+   packet will be just the same as that of the previous packet from the
+   same SSRC except that the sequence number will have increased by one.
+   Similarly, a single-entry cache may be used for faster SSRC lookups
+   in applications where data is typically received from one source at a
+   time.
+
+A.2 RTCP Header Validity Checks
+
+   The following checks should be applied to RTCP packets.
+
+   o  RTP version field must equal 2.
+
+   o  The payload type field of the first RTCP packet in a compound
+      packet must be equal to SR or RR.
+
+   o  The padding bit (P) should be zero for the first packet of a
+      compound RTCP packet because padding should only be applied, if it
+      is needed, to the last packet.
+
+   o  The length fields of the individual RTCP packets must add up to
+      the overall length of the compound RTCP packet as received.  This
+      is a fairly strong check.
+
+   The code fragment below performs all of these checks.  The packet
+   type is not checked for subsequent packets since unknown packet types
+   may be present and should be ignored.
+
+      u_int32 len;        /* length of compound RTCP packet in words */
+      rtcp_t *r;          /* RTCP header */
+      rtcp_t *end;        /* end of compound RTCP packet */
+
+      if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
+          /* something wrong with packet format */
+      }
+      end = (rtcp_t *)((u_int32 *)r + len);
+
+      do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
+      while (r < end && r->common.version == 2);
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 82]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      if (r != end) {
+          /* something wrong with packet format */
+      }
+
+A.3 Determining Number of Packets Expected and Lost
+
+   In order to compute packet loss rates, the number of RTP packets
+   expected and actually received from each source needs to be known,
+   using per-source state information defined in struct source
+   referenced via pointer s in the code below.  The number of packets
+   received is simply the count of packets as they arrive, including any
+   late or duplicate packets.  The number of packets expected can be
+   computed by the receiver as the difference between the highest
+   sequence number received (s->max_seq) and the first sequence number
+   received (s->base_seq).  Since the sequence number is only 16 bits
+   and will wrap around, it is necessary to extend the highest sequence
+   number with the (shifted) count of sequence number wraparounds
+   (s->cycles).  Both the received packet count and the count of cycles
+   are maintained the RTP header validity check routine in Appendix A.1.
+
+      extended_max = s->cycles + s->max_seq;
+      expected = extended_max - s->base_seq + 1;
+
+   The number of packets lost is defined to be the number of packets
+   expected less the number of packets actually received:
+
+      lost = expected - s->received;
+
+   Since this signed number is carried in 24 bits, it should be clamped
+   at 0x7fffff for positive loss or 0x800000 for negative loss rather
+   than wrapping around.
+
+   The fraction of packets lost during the last reporting interval
+   (since the previous SR or RR packet was sent) is calculated from
+   differences in the expected and received packet counts across the
+   interval, where expected_prior and received_prior are the values
+   saved when the previous reception report was generated:
+
+      expected_interval = expected - s->expected_prior;
+      s->expected_prior = expected;
+      received_interval = s->received - s->received_prior;
+      s->received_prior = s->received;
+      lost_interval = expected_interval - received_interval;
+      if (expected_interval == 0 || lost_interval <= 0) fraction = 0;
+      else fraction = (lost_interval << 8) / expected_interval;
+
+   The resulting fraction is an 8-bit fixed point number with the binary
+   point at the left edge.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 83]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+A.4 Generating RTCP SDES Packets
+
+   This function builds one SDES chunk into buffer b composed of argc
+   items supplied in arrays type, value and length.  It returns a
+   pointer to the next available location within b.
+
+   char *rtp_write_sdes(char *b, u_int32 src, int argc,
+                        rtcp_sdes_type_t type[], char *value[],
+                        int length[])
+   {
+       rtcp_sdes_t *s = (rtcp_sdes_t *)b;
+       rtcp_sdes_item_t *rsp;
+       int i;
+       int len;
+       int pad;
+
+       /* SSRC header */
+       s->src = src;
+       rsp = &s->item[0];
+
+       /* SDES items */
+       for (i = 0; i < argc; i++) {
+           rsp->type = type[i];
+           len = length[i];
+           if (len > RTP_MAX_SDES) {
+               /* invalid length, may want to take other action */
+               len = RTP_MAX_SDES;
+           }
+           rsp->length = len;
+           memcpy(rsp->data, value[i], len);
+           rsp = (rtcp_sdes_item_t *)&rsp->data[len];
+       }
+
+       /* terminate with end marker and pad to next 4-octet boundary */
+       len = ((char *) rsp) - b;
+       pad = 4 - (len & 0x3);
+       b = (char *) rsp;
+       while (pad--) *b++ = RTCP_SDES_END;
+
+       return b;
+   }
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 84]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+A.5 Parsing RTCP SDES Packets
+
+   This function parses an SDES packet, calling functions find_member()
+   to find a pointer to the information for a session member given the
+   SSRC identifier and member_sdes() to store the new SDES information
+   for that member.  This function expects a pointer to the header of
+   the RTCP packet.
+
+   void rtp_read_sdes(rtcp_t *r)
+   {
+       int count = r->common.count;
+       rtcp_sdes_t *sd = &r->r.sdes;
+       rtcp_sdes_item_t *rsp, *rspn;
+       rtcp_sdes_item_t *end = (rtcp_sdes_item_t *)
+                               ((u_int32 *)r + r->common.length + 1);
+       source *s;
+
+       while (--count >= 0) {
+           rsp = &sd->item[0];
+           if (rsp >= end) break;
+           s = find_member(sd->src);
+
+           for (; rsp->type; rsp = rspn ) {
+               rspn = (rtcp_sdes_item_t *)((char*)rsp+rsp->length+2);
+               if (rspn >= end) {
+                   rsp = rspn;
+                   break;
+               }
+               member_sdes(s, rsp->type, rsp->data, rsp->length);
+           }
+           sd = (rtcp_sdes_t *)
+                ((u_int32 *)sd + (((char *)rsp - (char *)sd) >> 2)+1);
+       }
+       if (count >= 0) {
+           /* invalid packet format */
+       }
+   }
+
+A.6 Generating a Random 32-bit Identifier
+
+   The following subroutine generates a random 32-bit identifier using
+   the MD5 routines published in RFC 1321 [32].  The system routines may
+   not be present on all operating systems, but they should serve as
+   hints as to what kinds of information may be used.  Other system
+   calls that may be appropriate include
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 85]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  getdomainname(),
+
+   o  getwd(), or
+
+   o  getrusage().
+
+   "Live" video or audio samples are also a good source of random
+   numbers, but care must be taken to avoid using a turned-off
+   microphone or blinded camera as a source [17].
+
+   Use of this or a similar routine is recommended to generate the
+   initial seed for the random number generator producing the RTCP
+   period (as shown in Appendix A.7), to generate the initial values for
+   the sequence number and timestamp, and to generate SSRC values.
+   Since this routine is likely to be CPU-intensive, its direct use to
+   generate RTCP periods is inappropriate because predictability is not
+   an issue.  Note that this routine produces the same result on
+   repeated calls until the value of the system clock changes unless
+   different values are supplied for the type argument.
+
+   /*
+    * Generate a random 32-bit quantity.
+    */
+   #include <sys/types.h>   /* u_long */
+   #include <sys/time.h>    /* gettimeofday() */
+   #include <unistd.h>      /* get..() */
+   #include <stdio.h>       /* printf() */
+   #include <time.h>        /* clock() */
+   #include <sys/utsname.h> /* uname() */
+   #include "global.h"      /* from RFC 1321 */
+   #include "md5.h"         /* from RFC 1321 */
+
+   #define MD_CTX MD5_CTX
+   #define MDInit MD5Init
+   #define MDUpdate MD5Update
+   #define MDFinal MD5Final
+
+   static u_long md_32(char *string, int length)
+   {
+       MD_CTX context;
+       union {
+           char   c[16];
+           u_long x[4];
+       } digest;
+       u_long r;
+       int i;
+
+       MDInit (&context);
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 86]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+       MDUpdate (&context, string, length);
+       MDFinal ((unsigned char *)&digest, &context);
+       r = 0;
+       for (i = 0; i < 3; i++) {
+           r ^= digest.x[i];
+       }
+       return r;
+   }                               /* md_32 */
+
+   /*
+    * Return random unsigned 32-bit quantity.  Use 'type' argument if
+    * you need to generate several different values in close succession.
+    */
+   u_int32 random32(int type)
+   {
+       struct {
+           int     type;
+           struct  timeval tv;
+           clock_t cpu;
+           pid_t   pid;
+           u_long  hid;
+           uid_t   uid;
+           gid_t   gid;
+           struct  utsname name;
+       } s;
+
+       gettimeofday(&s.tv, 0);
+       uname(&s.name);
+       s.type = type;
+       s.cpu  = clock();
+       s.pid  = getpid();
+       s.hid  = gethostid();
+       s.uid  = getuid();
+       s.gid  = getgid();
+       /* also: system uptime */
+
+       return md_32((char *)&s, sizeof(s));
+   }                               /* random32 */
+
+A.7 Computing the RTCP Transmission Interval
+
+   The following functions implement the RTCP transmission and reception
+   rules described in Section 6.2.  These rules are coded in several
+   functions:
+
+   o  rtcp_interval() computes the deterministic calculated interval,
+      measured in seconds.  The parameters are defined in Section 6.3.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 87]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  OnExpire() is called when the RTCP transmission timer expires.
+
+   o  OnReceive() is called whenever an RTCP packet is received.
+
+   Both OnExpire() and OnReceive() have event e as an argument.  This is
+   the next scheduled event for that participant, either an RTCP report
+   or a BYE packet.  It is assumed that the following functions are
+   available:
+
+   o  Schedule(time t, event e) schedules an event e to occur at time t.
+      When time t arrives, the function OnExpire is called with e as an
+      argument.
+
+   o  Reschedule(time t, event e) reschedules a previously scheduled
+      event e for time t.
+
+   o  SendRTCPReport(event e) sends an RTCP report.
+
+   o  SendBYEPacket(event e) sends a BYE packet.
+
+   o  TypeOfEvent(event e) returns EVENT_BYE if the event being
+      processed is for a BYE packet to be sent, else it returns
+      EVENT_REPORT.
+
+   o  PacketType(p) returns PACKET_RTCP_REPORT if packet p is an RTCP
+      report (not BYE), PACKET_BYE if its a BYE RTCP packet, and
+      PACKET_RTP if its a regular RTP data packet.
+
+   o  ReceivedPacketSize() and SentPacketSize() return the size of the
+      referenced packet in octets.
+
+   o  NewMember(p) returns a 1 if the participant who sent packet p is
+      not currently in the member list, 0 otherwise.  Note this function
+      is not sufficient for a complete implementation because each CSRC
+      identifier in an RTP packet and each SSRC in a BYE packet should
+      be processed.
+
+   o  NewSender(p) returns a 1 if the participant who sent packet p is
+      not currently in the sender sublist of the member list, 0
+      otherwise.
+
+   o  AddMember() and RemoveMember() to add and remove participants from
+      the member list.
+
+   o  AddSender() and RemoveSender() to add and remove participants from
+      the sender sublist of the member list.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 88]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   These functions would have to be extended for an implementation that
+   allows the RTCP bandwidth fractions for senders and non-senders to be
+   specified as explicit parameters rather than fixed values of 25% and
+   75%.  The extended implementation of rtcp_interval() would need to
+   avoid division by zero if one of the parameters was zero.
+
+   double rtcp_interval(int members,
+                        int senders,
+                        double rtcp_bw,
+                        int we_sent,
+                        double avg_rtcp_size,
+                        int initial)
+   {
+       /*
+        * Minimum average time between RTCP packets from this site (in
+        * seconds).  This time prevents the reports from `clumping' when
+        * sessions are small and the law of large numbers isn't helping
+        * to smooth out the traffic.  It also keeps the report interval
+        * from becoming ridiculously small during transient outages like
+        * a network partition.
+        */
+       double const RTCP_MIN_TIME = 5.;
+       /*
+        * Fraction of the RTCP bandwidth to be shared among active
+        * senders.  (This fraction was chosen so that in a typical
+        * session with one or two active senders, the computed report
+        * time would be roughly equal to the minimum report time so that
+        * we don't unnecessarily slow down receiver reports.)  The
+        * receiver fraction must be 1 - the sender fraction.
+        */
+       double const RTCP_SENDER_BW_FRACTION = 0.25;
+       double const RTCP_RCVR_BW_FRACTION = (1-RTCP_SENDER_BW_FRACTION);
+       /*
+       /* To compensate for "timer reconsideration" converging to a
+        * value below the intended average.
+        */
+       double const COMPENSATION = 2.71828 - 1.5;
+
+       double t;                   /* interval */
+       double rtcp_min_time = RTCP_MIN_TIME;
+       int n;                      /* no. of members for computation */
+
+       /*
+        * Very first call at application start-up uses half the min
+        * delay for quicker notification while still allowing some time
+        * before reporting for randomization and to learn about other
+        * sources so the report interval will converge to the correct
+        * interval more quickly.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 89]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+        */
+       if (initial) {
+           rtcp_min_time /= 2;
+       }
+       /*
+        * Dedicate a fraction of the RTCP bandwidth to senders unless
+        * the number of senders is large enough that their share is
+        * more than that fraction.
+        */
+       n = members;
+       if (senders <= members * RTCP_SENDER_BW_FRACTION) {
+           if (we_sent) {
+               rtcp_bw *= RTCP_SENDER_BW_FRACTION;
+               n = senders;
+           } else {
+               rtcp_bw *= RTCP_RCVR_BW_FRACTION;
+               n -= senders;
+           }
+       }
+
+       /*
+        * The effective number of sites times the average packet size is
+        * the total number of octets sent when each site sends a report.
+        * Dividing this by the effective bandwidth gives the time
+        * interval over which those packets must be sent in order to
+        * meet the bandwidth target, with a minimum enforced.  In that
+        * time interval we send one report so this time is also our
+        * average time between reports.
+        */
+       t = avg_rtcp_size * n / rtcp_bw;
+       if (t < rtcp_min_time) t = rtcp_min_time;
+
+       /*
+        * To avoid traffic bursts from unintended synchronization with
+        * other sites, we then pick our actual next report interval as a
+        * random number uniformly distributed between 0.5*t and 1.5*t.
+        */
+       t = t * (drand48() + 0.5);
+       t = t / COMPENSATION;
+       return t;
+   }
+
+   void OnExpire(event e,
+                 int    members,
+                 int    senders,
+                 double rtcp_bw,
+                 int    we_sent,
+                 double *avg_rtcp_size,
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 90]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+                 int    *initial,
+                 time_tp   tc,
+                 time_tp   *tp,
+                 int    *pmembers)
+   {
+       /* This function is responsible for deciding whether to send an
+        * RTCP report or BYE packet now, or to reschedule transmission.
+        * It is also responsible for updating the pmembers, initial, tp,
+        * and avg_rtcp_size state variables.  This function should be
+        * called upon expiration of the event timer used by Schedule().
+        */
+
+       double t;     /* Interval */
+       double tn;    /* Next transmit time */
+
+       /* In the case of a BYE, we use "timer reconsideration" to
+        * reschedule the transmission of the BYE if necessary */
+
+       if (TypeOfEvent(e) == EVENT_BYE) {
+           t = rtcp_interval(members,
+                             senders,
+                             rtcp_bw,
+                             we_sent,
+                             *avg_rtcp_size,
+                             *initial);
+           tn = *tp + t;
+           if (tn <= tc) {
+               SendBYEPacket(e);
+               exit(1);
+           } else {
+               Schedule(tn, e);
+           }
+
+       } else if (TypeOfEvent(e) == EVENT_REPORT) {
+           t = rtcp_interval(members,
+                             senders,
+                             rtcp_bw,
+                             we_sent,
+                             *avg_rtcp_size,
+                             *initial);
+           tn = *tp + t;
+           if (tn <= tc) {
+               SendRTCPReport(e);
+               *avg_rtcp_size = (1./16.)*SentPacketSize(e) +
+                   (15./16.)*(*avg_rtcp_size);
+               *tp = tc;
+
+               /* We must redraw the interval.  Don't reuse the
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 91]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+                  one computed above, since its not actually
+                  distributed the same, as we are conditioned
+                  on it being small enough to cause a packet to
+                  be sent */
+
+               t = rtcp_interval(members,
+                                 senders,
+                                 rtcp_bw,
+                                 we_sent,
+                                 *avg_rtcp_size,
+                                 *initial);
+
+               Schedule(t+tc,e);
+               *initial = 0;
+           } else {
+               Schedule(tn, e);
+           }
+           *pmembers = members;
+       }
+   }
+
+   void OnReceive(packet p,
+                  event e,
+                  int *members,
+                  int *pmembers,
+                  int *senders,
+                  double *avg_rtcp_size,
+                  double *tp,
+                  double tc,
+                  double tn)
+   {
+       /* What we do depends on whether we have left the group, and are
+        * waiting to send a BYE (TypeOfEvent(e) == EVENT_BYE) or an RTCP
+        * report.  p represents the packet that was just received.  */
+
+       if (PacketType(p) == PACKET_RTCP_REPORT) {
+           if (NewMember(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
+               AddMember(p);
+               *members += 1;
+           }
+           *avg_rtcp_size = (1./16.)*ReceivedPacketSize(p) +
+               (15./16.)*(*avg_rtcp_size);
+       } else if (PacketType(p) == PACKET_RTP) {
+           if (NewMember(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
+               AddMember(p);
+               *members += 1;
+           }
+           if (NewSender(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 92]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+               AddSender(p);
+               *senders += 1;
+           }
+       } else if (PacketType(p) == PACKET_BYE) {
+           *avg_rtcp_size = (1./16.)*ReceivedPacketSize(p) +
+               (15./16.)*(*avg_rtcp_size);
+
+           if (TypeOfEvent(e) == EVENT_REPORT) {
+               if (NewSender(p) == FALSE) {
+                   RemoveSender(p);
+                   *senders -= 1;
+               }
+
+               if (NewMember(p) == FALSE) {
+                   RemoveMember(p);
+                   *members -= 1;
+               }
+
+               if (*members < *pmembers) {
+                   tn = tc +
+                       (((double) *members)/(*pmembers))*(tn - tc);
+                   *tp = tc -
+                       (((double) *members)/(*pmembers))*(tc - *tp);
+
+                   /* Reschedule the next report for time tn */
+
+                   Reschedule(tn, e);
+                   *pmembers = *members;
+               }
+
+           } else if (TypeOfEvent(e) == EVENT_BYE) {
+               *members += 1;
+           }
+       }
+   }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 93]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+A.8 Estimating the Interarrival Jitter
+
+   The code fragments below implement the algorithm given in Section
+   6.4.1 for calculating an estimate of the statistical variance of the
+   RTP data interarrival time to be inserted in the interarrival jitter
+   field of reception reports.  The inputs are r->ts, the timestamp from
+   the incoming packet, and arrival, the current time in the same units.
+   Here s points to state for the source; s->transit holds the relative
+   transit time for the previous packet, and s->jitter holds the
+   estimated jitter.  The jitter field of the reception report is
+   measured in timestamp units and expressed as an unsigned integer, but
+   the jitter estimate is kept in a floating point.  As each data packet
+   arrives, the jitter estimate is updated:
+
+      int transit = arrival - r->ts;
+      int d = transit - s->transit;
+      s->transit = transit;
+      if (d < 0) d = -d;
+      s->jitter += (1./16.) * ((double)d - s->jitter);
+
+   When a reception report block (to which rr points) is generated for
+   this member, the current jitter estimate is returned:
+
+      rr->jitter = (u_int32) s->jitter;
+
+   Alternatively, the jitter estimate can be kept as an integer, but
+   scaled to reduce round-off error.  The calculation is the same except
+   for the last line:
+
+      s->jitter += d - ((s->jitter + 8) >> 4);
+
+   In this case, the estimate is sampled for the reception report as:
+
+      rr->jitter = s->jitter >> 4;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 94]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+Appendix B - Changes from RFC 1889
+
+   Most of this RFC is identical to RFC 1889.  There are no changes in
+   the packet formats on the wire, only changes to the rules and
+   algorithms governing how the protocol is used.  The biggest change is
+   an enhancement to the scalable timer algorithm for calculating when
+   to send RTCP packets:
+
+   o  The algorithm for calculating the RTCP transmission interval
+      specified in Sections 6.2 and 6.3 and illustrated in Appendix A.7
+      is augmented to include "reconsideration" to minimize transmission
+      in excess of the intended rate when many participants join a
+      session simultaneously, and "reverse reconsideration" to reduce
+      the incidence and duration of false participant timeouts when the
+      number of participants drops rapidly.  Reverse reconsideration is
+      also used to possibly shorten the delay before sending RTCP SR
+      when transitioning from passive receiver to active sender mode.
+
+   o  Section 6.3.7 specifies new rules controlling when an RTCP BYE
+      packet should be sent in order to avoid a flood of packets when
+      many participants leave a session simultaneously.
+
+   o  The requirement to retain state for inactive participants for a
+      period long enough to span typical network partitions was removed
+      from Section 6.2.1.  In a session where many participants join for
+      a brief time and fail to send BYE, this requirement would cause a
+      significant overestimate of the number of participants.  The
+      reconsideration algorithm added in this revision compensates for
+      the large number of new participants joining simultaneously when a
+      partition heals.
+
+   It should be noted that these enhancements only have a significant
+   effect when the number of session participants is large (thousands)
+   and most of the participants join or leave at the same time.  This
+   makes testing in a live network difficult.  However, the algorithm
+   was subjected to a thorough analysis and simulation to verify its
+   performance.  Furthermore, the enhanced algorithm was designed to
+   interoperate with the algorithm in RFC 1889 such that the degree of
+   reduction in excess RTCP bandwidth during a step join is proportional
+   to the fraction of participants that implement the enhanced
+   algorithm.  Interoperation of the two algorithms has been verified
+   experimentally on live networks.
+
+   Other functional changes were:
+
+   o  Section 6.2.1 specifies that implementations may store only a
+      sampling of the participants' SSRC identifiers to allow scaling to
+      very large sessions.  Algorithms are specified in RFC 2762 [21].
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 95]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  In Section 6.2 it is specified that RTCP sender and non-sender
+      bandwidths may be set as separate parameters of the session rather
+      than a strict percentage of the session bandwidth, and may be set
+      to zero.  The requirement that RTCP was mandatory for RTP sessions
+      using IP multicast was relaxed.  However, a clarification was also
+      added that turning off RTCP is NOT RECOMMENDED.
+
+   o  In Sections 6.2, 6.3.1 and Appendix A.7, it is specified that the
+      fraction of participants below which senders get dedicated RTCP
+      bandwidth changes from the fixed 1/4 to a ratio based on the RTCP
+      sender and non-sender bandwidth parameters when those are given.
+      The condition that no bandwidth is dedicated to senders when there
+      are no senders was removed since that is expected to be a
+      transitory state.  It also keeps non-senders from using sender
+      RTCP bandwidth when that is not intended.
+
+   o  Also in Section 6.2 it is specified that the minimum RTCP interval
+      may be scaled to smaller values for high bandwidth sessions, and
+      that the initial RTCP delay may be set to zero for unicast
+      sessions.
+
+   o  Timing out a participant is to be based on inactivity for a number
+      of RTCP report intervals calculated using the receiver RTCP
+      bandwidth fraction even for active senders.
+
+   o  Sections 7.2 and 7.3 specify that translators and mixers should
+      send BYE packets for the sources they are no longer forwarding.
+
+   o  Rule changes for layered encodings are defined in Sections 2.4,
+      6.3.9, 8.3 and 11.  In the last of these, it is noted that the
+      address and port assignment rule conflicts with the SDP
+      specification, RFC 2327 [15], but it is intended that this
+      restriction will be relaxed in a revision of RFC 2327.
+
+   o  The convention for using even/odd port pairs for RTP and RTCP in
+      Section 11 was clarified to refer to destination ports.  The
+      requirement to use an even/odd port pair was removed if the two
+      ports are specified explicitly.  For unicast RTP sessions,
+      distinct port pairs may be used for the two ends (Sections 3, 7.1
+      and 11).
+
+   o  A new Section 10 was added to explain the requirement for
+      congestion control in applications using RTP.
+
+   o  In Section 8.2, the requirement that a new SSRC identifier MUST be
+      chosen whenever the source transport address is changed has been
+      relaxed to say that a new SSRC identifier MAY be chosen.
+      Correspondingly, it was clarified that an implementation MAY
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 96]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      choose to keep packets from the new source address rather than the
+      existing source address when an SSRC collision occurs between two
+      other participants, and SHOULD do so for applications such as
+      telephony in which some sources such as mobile entities may change
+      addresses during the course of an RTP session.
+
+   o  An indentation bug in the RFC 1889 printing of the pseudo-code for
+      the collision detection and resolution algorithm in Section 8.2
+      has been corrected by translating the syntax to pseudo C language,
+      and the algorithm has been modified to remove the restriction that
+      both RTP and RTCP must be sent from the same source port number.
+
+   o  The description of the padding mechanism for RTCP packets was
+      clarified and it is specified that padding MUST only be applied to
+      the last packet of a compound RTCP packet.
+
+   o  In Section A.1, initialization of base_seq was corrected to be seq
+      rather than seq - 1, and the text was corrected to say the bad
+      sequence number plus 1 is stored.  The initialization of max_seq
+      and other variables for the algorithm was separated from the text
+      to make clear that this initialization must be done in addition to
+      calling the init_seq() function (and a few words lost in RFC 1889
+      when processing the document from source to output form were
+      restored).
+
+   o  Clamping of number of packets lost in Section A.3 was corrected to
+      use both positive and negative limits.
+
+   o  The specification of "relative" NTP timestamp in the RTCP SR
+      section now defines these timestamps to be based on the most
+      common system-specific clock, such as system uptime, rather than
+      on session elapsed time which would not be the same for multiple
+      applications started on the same machine at different times.
+
+   Non-functional changes:
+
+   o  It is specified that a receiver MUST ignore packets with payload
+      types it does not understand.
+
+   o  In Fig. 2, the floating point NTP timestamp value was corrected,
+      some missing leading zeros were added in a hex number, and the UTC
+      timezone was specified.
+
+   o  The inconsequence of NTP timestamps wrapping around in the year
+      2036 is explained.
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 97]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  The policy for registration of RTCP packet types and SDES types
+      was clarified in a new Section 15, IANA Considerations.  The
+      suggestion that experimenters register the numbers they need and
+      then unregister those which prove to be unneeded has been removed
+      in favor of using APP and PRIV.  Registration of profile names was
+      also specified.
+
+   o  The reference for the UTF-8 character set was changed from an
+      X/Open Preliminary Specification to be RFC 2279.
+
+   o  The reference for RFC 1597 was updated to RFC 1918 and the
+      reference for RFC 2543 was updated to RFC 3261.
+
+   o  The last paragraph of the introduction in RFC 1889, which
+      cautioned implementors to limit deployment in the Internet, was
+      removed because it was deemed no longer relevant.
+
+   o  A non-normative note regarding the use of RTP with Source-Specific
+      Multicast (SSM) was added in Section 6.
+
+   o  The definition of "RTP session" in Section 3 was expanded to
+      acknowledge that a single session may use multiple destination
+      transport addresses (as was always the case for a translator or
+      mixer) and to explain that the distinguishing feature of an RTP
+      session is that each corresponds to a separate SSRC identifier
+      space.  A new definition of "multimedia session" was added to
+      reduce confusion about the word "session".
+
+   o  The meaning of "sampling instant" was explained in more detail as
+      part of the definition of the timestamp field of the RTP header in
+      Section 5.1.
+
+   o  Small clarifications of the text have been made in several places,
+      some in response to questions from readers.  In particular:
+
+      -  In RFC 1889, the first five words of the second sentence of
+         Section 2.2 were lost in processing the document from source to
+         output form, but are now restored.
+
+      -  A definition for "RTP media type" was added in Section 3 to
+         allow the explanation of multiplexing RTP sessions in Section
+         5.2 to be more clear regarding the multiplexing of multiple
+         media.  That section also now explains that multiplexing
+         multiple sources of the same medium based on SSRC identifiers
+         may be appropriate and is the norm for multicast sessions.
+
+      -  The definition for "non-RTP means" was expanded to include
+         examples of other protocols constituting non-RTP means.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 98]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      -  The description of the session bandwidth parameter is expanded
+         in Section 6.2, including a clarification that the control
+         traffic bandwidth is in addition to the session bandwidth for
+         the data traffic.
+
+      -  The effect of varying packet duration on the jitter calculation
+         was explained in Section 6.4.4.
+
+      -  The method for terminating and padding a sequence of SDES items
+         was clarified in Section 6.5.
+
+      -  IPv6 address examples were added in the description of SDES
+         CNAME in Section 6.5.1, and "example.com" was used in place of
+         other example domain names.
+
+      -  The Security section added a formal reference to IPSEC now that
+         it is available, and says that the confidentiality method
+         defined in this specification is primarily to codify existing
+         practice.  It is RECOMMENDED that stronger encryption
+         algorithms such as Triple-DES be used in place of the default
+         algorithm, and noted that the SRTP profile based on AES will be
+         the correct choice in the future.  A caution about the weakness
+         of the RTP header as an initialization vector was added.  It
+         was also noted that payload-only encryption is necessary to
+         allow for header compression.
+
+      -  The method for partial encryption of RTCP was clarified; in
+         particular, SDES CNAME is carried in only one part when the
+         compound RTCP packet is split.
+
+      -  It is clarified that only one compound RTCP packet should be
+         sent per reporting interval and that if there are too many
+         active sources for the reports to fit in the MTU, then a subset
+         of the sources should be selected round-robin over multiple
+         intervals.
+
+      -  A note was added in Appendix A.1 that packets may be saved
+         during RTP header validation and delivered upon success.
+
+      -  Section 7.3 now explains that a mixer aggregating SDES packets
+         uses more RTCP bandwidth due to longer packets, and a mixer
+         passing through RTCP naturally sends packets at higher than the
+         single source rate, but both behaviors are valid.
+
+      -  Section 13 clarifies that an RTP application may use multiple
+         profiles but typically only one in a given session.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 99]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      -  The terms MUST, SHOULD, MAY, etc. are used as defined in RFC
+         2119.
+
+      -  The bibliography was divided into normative and informative
+         references.
+
+References
+
+Normative References
+
+   [1]  Schulzrinne, H. and S. Casner, "RTP Profile for Audio and Video
+        Conferences with Minimal Control", RFC 3551, July 2003.
+
+   [2]  Bradner, S., "Key Words for Use in RFCs to Indicate Requirement
+        Levels", BCP 14, RFC 2119, March 1997.
+
+   [3]  Postel, J., "Internet Protocol", STD 5, RFC 791, September 1981.
+
+   [4]  Mills, D., "Network Time Protocol (Version 3) Specification,
+        Implementation and Analysis", RFC 1305, March 1992.
+
+   [5]  Yergeau, F., "UTF-8, a Transformation Format of ISO 10646", RFC
+        2279, January 1998.
+
+   [6]  Mockapetris, P., "Domain Names - Concepts and Facilities", STD
+        13, RFC 1034, November 1987.
+
+   [7]  Mockapetris, P., "Domain Names - Implementation and
+        Specification", STD 13, RFC 1035, November 1987.
+
+   [8]  Braden, R., "Requirements for Internet Hosts - Application and
+        Support", STD 3, RFC 1123, October 1989.
+
+   [9]  Resnick, P., "Internet Message Format", RFC 2822, April 2001.
+
+Informative References
+
+   [10] Clark, D. and D. Tennenhouse, "Architectural Considerations for
+        a New Generation of Protocols," in SIGCOMM Symposium on
+        Communications Architectures and Protocols , (Philadelphia,
+        Pennsylvania), pp. 200--208, IEEE Computer Communications
+        Review, Vol. 20(4), September 1990.
+
+   [11] Schulzrinne, H., "Issues in designing a transport protocol for
+        audio and video conferences and other multiparticipant real-time
+        applications." expired Internet Draft, October 1993.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                   [Page 100]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   [12] Comer, D., Internetworking with TCP/IP , vol. 1.  Englewood
+        Cliffs, New Jersey: Prentice Hall, 1991.
+
+   [13] Rosenberg, J., Schulzrinne, H., Camarillo, G., Johnston, A.,
+        Peterson, J., Sparks, R., Handley, M. and E. Schooler, "SIP:
+        Session Initiation Protocol", RFC 3261, June 2002.
+
+   [14] International Telecommunication Union, "Visual telephone systems
+        and equipment for local area networks which provide a non-
+        guaranteed quality of service", Recommendation H.323,
+        Telecommunication Standardization Sector of ITU, Geneva,
+        Switzerland, July 2003.
+
+   [15] Handley, M. and V. Jacobson, "SDP: Session Description
+        Protocol", RFC 2327, April 1998.
+
+   [16] Schulzrinne, H., Rao, A. and R. Lanphier, "Real Time Streaming
+        Protocol (RTSP)", RFC 2326, April 1998.
+
+   [17] Eastlake 3rd, D., Crocker, S. and J. Schiller, "Randomness
+        Recommendations for Security", RFC 1750, December 1994.
+
+   [18] Bolot, J.-C., Turletti, T. and I. Wakeman, "Scalable Feedback
+        Control for Multicast Video Distribution in the Internet", in
+        SIGCOMM Symposium on Communications Architectures and Protocols,
+        (London, England), pp. 58--67, ACM, August 1994.
+
+   [19] Busse, I., Deffner, B. and H. Schulzrinne, "Dynamic QoS Control
+        of Multimedia Applications Based on RTP", Computer
+        Communications , vol. 19, pp. 49--58, January 1996.
+
+   [20] Floyd, S. and V. Jacobson, "The Synchronization of Periodic
+        Routing Messages", in SIGCOMM Symposium on Communications
+        Architectures and Protocols (D. P. Sidhu, ed.), (San Francisco,
+        California), pp. 33--44, ACM, September 1993.  Also in [34].
+
+   [21] Rosenberg, J. and H. Schulzrinne, "Sampling of the Group
+        Membership in RTP", RFC 2762, February 2000.
+
+   [22] Cadzow, J., Foundations of Digital Signal Processing and Data
+        Analysis New York, New York: Macmillan, 1987.
+
+   [23] Hinden, R. and S. Deering, "Internet Protocol Version 6 (IPv6)
+        Addressing Architecture", RFC 3513, April 2003.
+
+   [24] Rekhter, Y., Moskowitz, B., Karrenberg, D., de Groot, G. and E.
+        Lear, "Address Allocation for Private Internets", RFC 1918,
+        February 1996.
+
+
+
+Schulzrinne, et al.         Standards Track                   [Page 101]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   [25] Lear, E., Fair, E., Crocker, D. and T. Kessler, "Network 10
+        Considered Harmful (Some Practices Shouldn't be Codified)", RFC
+        1627, July 1994.
+
+   [26] Feller, W., An Introduction to Probability Theory and its
+        Applications, vol. 1.  New York, New York: John Wiley and Sons,
+        third ed., 1968.
+
+   [27] Kent, S. and R. Atkinson, "Security Architecture for the
+        Internet Protocol", RFC 2401, November 1998.
+
+   [28] Baugher, M., Blom, R., Carrara, E., McGrew, D., Naslund, M.,
+        Norrman, K. and D. Oran, "Secure Real-time Transport Protocol",
+        Work in Progress, April 2003.
+
+   [29] Balenson, D., "Privacy Enhancement for Internet Electronic Mail:
+        Part III", RFC 1423, February 1993.
+
+   [30] Voydock, V. and S. Kent, "Security Mechanisms in High-Level
+        Network Protocols", ACM Computing Surveys, vol. 15, pp. 135-171,
+        June 1983.
+
+   [31] Floyd, S., "Congestion Control Principles", BCP 41, RFC 2914,
+        September 2000.
+
+   [32] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April
+        1992.
+
+   [33] Stubblebine, S., "Security Services for Multimedia
+        Conferencing", in 16th National Computer Security Conference,
+        (Baltimore, Maryland), pp. 391--395, September 1993.
+
+   [34] Floyd, S. and V. Jacobson, "The Synchronization of Periodic
+        Routing Messages", IEEE/ACM Transactions on Networking, vol. 2,
+        pp. 122--136, April 1994.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                   [Page 102]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+Authors' Addresses
+
+   Henning Schulzrinne
+   Department of Computer Science
+   Columbia University
+   1214 Amsterdam Avenue
+   New York, NY 10027
+   United States
+
+   EMail: schulzrinne@cs.columbia.edu
+
+
+   Stephen L. Casner
+   Packet Design
+   3400 Hillview Avenue, Building 3
+   Palo Alto, CA 94304
+   United States
+
+   EMail: casner@acm.org
+
+
+   Ron Frederick
+   Blue Coat Systems Inc.
+   650 Almanor Avenue
+   Sunnyvale, CA 94085
+   United States
+
+   EMail: ronf@bluecoat.com
+
+
+   Van Jacobson
+   Packet Design
+   3400 Hillview Avenue, Building 3
+   Palo Alto, CA 94304
+   United States
+
+   EMail: van@packetdesign.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                   [Page 103]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+Full Copyright Statement
+
+   Copyright (C) The Internet Society (2003).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+   Funding for the RFC Editor function is currently provided by the
+   Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                   [Page 104]
+\f
diff --git a/src/modules/rtp/rfc3551.txt b/src/modules/rtp/rfc3551.txt
new file mode 100644 (file)
index 0000000..c43ff34
--- /dev/null
@@ -0,0 +1,2467 @@
+
+
+
+
+
+
+Network Working Group                                     H. Schulzrinne
+Request for Comments: 3551                           Columbia University
+Obsoletes: 1890                                                S. Casner
+Category: Standards Track                                  Packet Design
+                                                               July 2003
+
+
+              RTP Profile for Audio and Video Conferences
+                          with Minimal Control
+
+Status of this Memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The Internet Society (2003).  All Rights Reserved.
+
+Abstract
+
+   This document describes a profile called "RTP/AVP" for the use of the
+   real-time transport protocol (RTP), version 2, and the associated
+   control protocol, RTCP, within audio and video multiparticipant
+   conferences with minimal control.  It provides interpretations of
+   generic fields within the RTP specification suitable for audio and
+   video conferences.  In particular, this document defines a set of
+   default mappings from payload type numbers to encodings.
+
+   This document also describes how audio and video data may be carried
+   within RTP.  It defines a set of standard encodings and their names
+   when used within RTP.  The descriptions provide pointers to reference
+   implementations and the detailed standards.  This document is meant
+   as an aid for implementors of audio, video and other real-time
+   multimedia applications.
+
+   This memorandum obsoletes RFC 1890.  It is mostly backwards-
+   compatible except for functions removed because two interoperable
+   implementations were not found.  The additions to RFC 1890 codify
+   existing practice in the use of payload formats under this profile
+   and include new payload formats defined since RFC 1890 was published.
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 1]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+Table of Contents
+
+   1.  Introduction .................................................  3
+       1.1  Terminology .............................................  3
+   2.  RTP and RTCP Packet Forms and Protocol Behavior ..............  4
+   3.  Registering Additional Encodings .............................  6
+   4.  Audio ........................................................  8
+       4.1  Encoding-Independent Rules ..............................  8
+       4.2  Operating Recommendations ...............................  9
+       4.3  Guidelines for Sample-Based Audio Encodings ............. 10
+       4.4  Guidelines for Frame-Based Audio Encodings .............. 11
+       4.5  Audio Encodings ......................................... 12
+            4.5.1   DVI4 ............................................ 13
+            4.5.2   G722 ............................................ 14
+            4.5.3   G723 ............................................ 14
+            4.5.4   G726-40, G726-32, G726-24, and G726-16 .......... 18
+            4.5.5   G728 ............................................ 19
+            4.5.6   G729 ............................................ 20
+            4.5.7   G729D and G729E ................................. 22
+            4.5.8   GSM ............................................. 24
+            4.5.9   GSM-EFR ......................................... 27
+            4.5.10  L8 .............................................. 27
+            4.5.11  L16 ............................................. 27
+            4.5.12  LPC ............................................. 27
+            4.5.13  MPA ............................................. 28
+            4.5.14  PCMA and PCMU ................................... 28
+            4.5.15  QCELP ........................................... 28
+            4.5.16  RED ............................................. 29
+            4.5.17  VDVI ............................................ 29
+   5.  Video ........................................................ 30
+       5.1  CelB .................................................... 30
+       5.2  JPEG .................................................... 30
+       5.3  H261 .................................................... 30
+       5.4  H263 .................................................... 31
+       5.5  H263-1998 ............................................... 31
+       5.6  MPV ..................................................... 31
+       5.7  MP2T .................................................... 31
+       5.8  nv ...................................................... 32
+   6.  Payload Type Definitions ..................................... 32
+   7.  RTP over TCP and Similar Byte Stream Protocols ............... 34
+   8.  Port Assignment .............................................. 34
+   9.  Changes from RFC 1890 ........................................ 35
+   10. Security Considerations ...................................... 38
+   11. IANA Considerations .......................................... 39
+   12. References ................................................... 39
+       12.1 Normative References .................................... 39
+       12.2 Informative References .................................. 39
+   13. Current Locations of Related Resources ....................... 41
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 2]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   14. Acknowledgments .............................................. 42
+   15. Intellectual Property Rights Statement ....................... 43
+   16. Authors' Addresses ........................................... 43
+   17. Full Copyright Statement ..................................... 44
+
+1. Introduction
+
+   This profile defines aspects of RTP left unspecified in the RTP
+   Version 2 protocol definition (RFC 3550) [1].  This profile is
+   intended for the use within audio and video conferences with minimal
+   session control.  In particular, no support for the negotiation of
+   parameters or membership control is provided.  The profile is
+   expected to be useful in sessions where no negotiation or membership
+   control are used (e.g., using the static payload types and the
+   membership indications provided by RTCP), but this profile may also
+   be useful in conjunction with a higher-level control protocol.
+
+   Use of this profile may be implicit in the use of the appropriate
+   applications; there may be no explicit indication by port number,
+   protocol identifier or the like.  Applications such as session
+   directories may use the name for this profile specified in Section
+   11.
+
+   Other profiles may make different choices for the items specified
+   here.
+
+   This document also defines a set of encodings and payload formats for
+   audio and video.  These payload format descriptions are included here
+   only as a matter of convenience since they are too small to warrant
+   separate documents.  Use of these payload formats is NOT REQUIRED to
+   use this profile.  Only the binding of some of the payload formats to
+   static payload type numbers in Tables 4 and 5 is normative.
+
+1.1 Terminology
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in RFC 2119 [2] and
+   indicate requirement levels for implementations compliant with this
+   RTP profile.
+
+   This document defines the term media type as dividing encodings of
+   audio and video content into three classes: audio, video and
+   audio/video (interleaved).
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 3]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+2. RTP and RTCP Packet Forms and Protocol Behavior
+
+   The section "RTP Profiles and Payload Format Specifications" of RFC
+   3550 enumerates a number of items that can be specified or modified
+   in a profile.  This section addresses these items.  Generally, this
+   profile follows the default and/or recommended aspects of the RTP
+   specification.
+
+   RTP data header: The standard format of the fixed RTP data
+      header is used (one marker bit).
+
+   Payload types: Static payload types are defined in Section 6.
+
+   RTP data header additions: No additional fixed fields are
+      appended to the RTP data header.
+
+   RTP data header extensions: No RTP header extensions are
+      defined, but applications operating under this profile MAY use
+      such extensions.  Thus, applications SHOULD NOT assume that the
+      RTP header X bit is always zero and SHOULD be prepared to ignore
+      the header extension.  If a header extension is defined in the
+      future, that definition MUST specify the contents of the first 16
+      bits in such a way that multiple different extensions can be
+      identified.
+
+   RTCP packet types: No additional RTCP packet types are defined
+      by this profile specification.
+
+   RTCP report interval: The suggested constants are to be used for
+      the RTCP report interval calculation.  Sessions operating under
+      this profile MAY specify a separate parameter for the RTCP traffic
+      bandwidth rather than using the default fraction of the session
+      bandwidth.  The RTCP traffic bandwidth MAY be divided into two
+      separate session parameters for those participants which are
+      active data senders and those which are not.  Following the
+      recommendation in the RTP specification [1] that 1/4 of the RTCP
+      bandwidth be dedicated to data senders, the RECOMMENDED default
+      values for these two parameters would be 1.25% and 3.75%,
+      respectively.  For a particular session, the RTCP bandwidth for
+      non-data-senders MAY be set to zero when operating on
+      unidirectional links or for sessions that don't require feedback
+      on the quality of reception.  The RTCP bandwidth for data senders
+      SHOULD be kept non-zero so that sender reports can still be sent
+      for inter-media synchronization and to identify the source by
+      CNAME.  The means by which the one or two session parameters for
+      RTCP bandwidth are specified is beyond the scope of this memo.
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 4]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   SR/RR extension: No extension section is defined for the RTCP SR
+      or RR packet.
+
+   SDES use: Applications MAY use any of the SDES items described
+      in the RTP specification.  While CNAME information MUST be sent
+      every reporting interval, other items SHOULD only be sent every
+      third reporting interval, with NAME sent seven out of eight times
+      within that slot and the remaining SDES items cyclically taking up
+      the eighth slot, as defined in Section 6.2.2 of the RTP
+      specification.  In other words, NAME is sent in RTCP packets 1, 4,
+      7, 10, 13, 16, 19, while, say, EMAIL is used in RTCP packet 22.
+
+   Security: The RTP default security services are also the default
+      under this profile.
+
+   String-to-key mapping: No mapping is specified by this profile.
+
+   Congestion: RTP and this profile may be used in the context of
+      enhanced network service, for example, through Integrated Services
+      (RFC 1633) [4] or Differentiated Services (RFC 2475) [5], or they
+      may be used with best effort service.
+
+      If enhanced service is being used, RTP receivers SHOULD monitor
+      packet loss to ensure that the service that was requested is
+      actually being delivered.  If it is not, then they SHOULD assume
+      that they are receiving best-effort service and behave
+      accordingly.
+
+      If best-effort service is being used, RTP receivers SHOULD monitor
+      packet loss to ensure that the packet loss rate is within
+      acceptable parameters.  Packet loss is considered acceptable if a
+      TCP flow across the same network path and experiencing the same
+      network conditions would achieve an average throughput, measured
+      on a reasonable timescale, that is not less than the RTP flow is
+      achieving.  This condition can be satisfied by implementing
+      congestion control mechanisms to adapt the transmission rate (or
+      the number of layers subscribed for a layered multicast session),
+      or by arranging for a receiver to leave the session if the loss
+      rate is unacceptably high.
+
+      The comparison to TCP cannot be specified exactly, but is intended
+      as an "order-of-magnitude" comparison in timescale and throughput.
+      The timescale on which TCP throughput is measured is the round-
+      trip time of the connection.  In essence, this requirement states
+      that it is not acceptable to deploy an application (using RTP or
+      any other transport protocol) on the best-effort Internet which
+      consumes bandwidth arbitrarily and does not compete fairly with
+      TCP within an order of magnitude.
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 5]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   Underlying protocol: The profile specifies the use of RTP over
+      unicast and multicast UDP as well as TCP.  (This does not preclude
+      the use of these definitions when RTP is carried by other lower-
+      layer protocols.)
+
+   Transport mapping: The standard mapping of RTP and RTCP to
+      transport-level addresses is used.
+
+   Encapsulation: This profile leaves to applications the
+      specification of RTP encapsulation in protocols other than UDP.
+
+3.  Registering Additional Encodings
+
+   This profile lists a set of encodings, each of which is comprised of
+   a particular media data compression or representation plus a payload
+   format for encapsulation within RTP.  Some of those payload formats
+   are specified here, while others are specified in separate RFCs.  It
+   is expected that additional encodings beyond the set listed here will
+   be created in the future and specified in additional payload format
+   RFCs.
+
+   This profile also assigns to each encoding a short name which MAY be
+   used by higher-level control protocols, such as the Session
+   Description Protocol (SDP), RFC 2327 [6], to identify encodings
+   selected for a particular RTP session.
+
+   In some contexts it may be useful to refer to these encodings in the
+   form of a MIME content-type.  To facilitate this, RFC 3555 [7]
+   provides registrations for all of the encodings names listed here as
+   MIME subtype names under the "audio" and "video" MIME types through
+   the MIME registration procedure as specified in RFC 2048 [8].
+
+   Any additional encodings specified for use under this profile (or
+   others) may also be assigned names registered as MIME subtypes with
+   the Internet Assigned Numbers Authority (IANA).  This registry
+   provides a means to insure that the names assigned to the additional
+   encodings are kept unique.  RFC 3555 specifies the information that
+   is required for the registration of RTP encodings.
+
+   In addition to assigning names to encodings, this profile also
+   assigns static RTP payload type numbers to some of them.  However,
+   the payload type number space is relatively small and cannot
+   accommodate assignments for all existing and future encodings.
+   During the early stages of RTP development, it was necessary to use
+   statically assigned payload types because no other mechanism had been
+   specified to bind encodings to payload types.  It was anticipated
+   that non-RTP means beyond the scope of this memo (such as directory
+   services or invitation protocols) would be specified to establish a
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 6]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   dynamic mapping between a payload type and an encoding.  Now,
+   mechanisms for defining dynamic payload type bindings have been
+   specified in the Session Description Protocol (SDP) and in other
+   protocols such as ITU-T Recommendation H.323/H.245.  These mechanisms
+   associate the registered name of the encoding/payload format, along
+   with any additional required parameters, such as the RTP timestamp
+   clock rate and number of channels, with a payload type number.  This
+   association is effective only for the duration of the RTP session in
+   which the dynamic payload type binding is made.  This association
+   applies only to the RTP session for which it is made, thus the
+   numbers can be re-used for different encodings in different sessions
+   so the number space limitation is avoided.
+
+   This profile reserves payload type numbers in the range 96-127
+   exclusively for dynamic assignment.  Applications SHOULD first use
+   values in this range for dynamic payload types.  Those applications
+   which need to define more than 32 dynamic payload types MAY bind
+   codes below 96, in which case it is RECOMMENDED that unassigned
+   payload type numbers be used first.  However, the statically assigned
+   payload types are default bindings and MAY be dynamically bound to
+   new encodings if needed.  Redefining payload types below 96 may cause
+   incorrect operation if an attempt is made to join a session without
+   obtaining session description information that defines the dynamic
+   payload types.
+
+   Dynamic payload types SHOULD NOT be used without a well-defined
+   mechanism to indicate the mapping.  Systems that expect to
+   interoperate with others operating under this profile SHOULD NOT make
+   their own assignments of proprietary encodings to particular, fixed
+   payload types.
+
+   This specification establishes the policy that no additional static
+   payload types will be assigned beyond the ones defined in this
+   document.  Establishing this policy avoids the problem of trying to
+   create a set of criteria for accepting static assignments and
+   encourages the implementation and deployment of the dynamic payload
+   type mechanisms.
+
+   The final set of static payload type assignments is provided in
+   Tables 4 and 5.
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 7]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.  Audio
+
+4.1  Encoding-Independent Rules
+
+   Since the ability to suppress silence is one of the primary
+   motivations for using packets to transmit voice, the RTP header
+   carries both a sequence number and a timestamp to allow a receiver to
+   distinguish between lost packets and periods of time when no data was
+   transmitted.  Discontiguous transmission (silence suppression) MAY be
+   used with any audio payload format.  Receivers MUST assume that
+   senders may suppress silence unless this is restricted by signaling
+   specified elsewhere.  (Even if the transmitter does not suppress
+   silence, the receiver should be prepared to handle periods when no
+   data is present since packets may be lost.)
+
+   Some payload formats (see Sections 4.5.3 and 4.5.6) define a "silence
+   insertion descriptor" or "comfort noise" frame to specify parameters
+   for artificial noise that may be generated during a period of silence
+   to approximate the background noise at the source.  For other payload
+   formats, a generic Comfort Noise (CN) payload format is specified in
+   RFC 3389 [9].  When the CN payload format is used with another
+   payload format, different values in the RTP payload type field
+   distinguish comfort-noise packets from those of the selected payload
+   format.
+
+   For applications which send either no packets or occasional comfort-
+   noise packets during silence, the first packet of a talkspurt, that
+   is, the first packet after a silence period during which packets have
+   not been transmitted contiguously, SHOULD be distinguished by setting
+   the marker bit in the RTP data header to one.  The marker bit in all
+   other packets is zero.  The beginning of a talkspurt MAY be used to
+   adjust the playout delay to reflect changing network delays.
+   Applications without silence suppression MUST set the marker bit to
+   zero.
+
+   The RTP clock rate used for generating the RTP timestamp is
+   independent of the number of channels and the encoding; it usually
+   equals the number of sampling periods per second.  For N-channel
+   encodings, each sampling period (say, 1/8,000 of a second) generates
+   N samples.  (This terminology is standard, but somewhat confusing, as
+   the total number of samples generated per second is then the sampling
+   rate times the channel count.)
+
+   If multiple audio channels are used, channels are numbered left-to-
+   right, starting at one.  In RTP audio packets, information from
+   lower-numbered channels precedes that from higher-numbered channels.
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 8]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   For more than two channels, the convention followed by the AIFF-C
+   audio interchange format SHOULD be followed [3], using the following
+   notation, unless some other convention is specified for a particular
+   encoding or payload format:
+
+      l  left
+      r  right
+      c  center
+      S  surround
+      F  front
+      R  rear
+
+      channels  description  channel
+                                1     2   3   4   5   6
+      _________________________________________________
+      2         stereo          l     r
+      3                         l     r   c
+      4                         l     c   r   S
+      5                        Fl     Fr  Fc  Sl  Sr
+      6                         l     lc  c   r   rc  S
+
+         Note: RFC 1890 defined two conventions for the ordering of four
+         audio channels.  Since the ordering is indicated implicitly by
+         the number of channels, this was ambiguous.  In this revision,
+         the order described as "quadrophonic" has been eliminated to
+         remove the ambiguity.  This choice was based on the observation
+         that quadrophonic consumer audio format did not become popular
+         whereas surround-sound subsequently has.
+
+   Samples for all channels belonging to a single sampling instant MUST
+   be within the same packet.  The interleaving of samples from
+   different channels depends on the encoding.  General guidelines are
+   given in Section 4.3 and 4.4.
+
+   The sampling frequency SHOULD be drawn from the set:  8,000, 11,025,
+   16,000, 22,050, 24,000, 32,000, 44,100 and 48,000 Hz.  (Older Apple
+   Macintosh computers had a native sample rate of 22,254.54 Hz, which
+   can be converted to 22,050 with acceptable quality by dropping 4
+   samples in a 20 ms frame.)  However, most audio encodings are defined
+   for a more restricted set of sampling frequencies.  Receivers SHOULD
+   be prepared to accept multi-channel audio, but MAY choose to only
+   play a single channel.
+
+4.2  Operating Recommendations
+
+   The following recommendations are default operating parameters.
+   Applications SHOULD be prepared to handle other values.  The ranges
+   given are meant to give guidance to application writers, allowing a
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 9]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   set of applications conforming to these guidelines to interoperate
+   without additional negotiation.  These guidelines are not intended to
+   restrict operating parameters for applications that can negotiate a
+   set of interoperable parameters, e.g., through a conference control
+   protocol.
+
+   For packetized audio, the default packetization interval SHOULD have
+   a duration of 20 ms or one frame, whichever is longer, unless
+   otherwise noted in Table 1 (column "ms/packet").  The packetization
+   interval determines the minimum end-to-end delay; longer packets
+   introduce less header overhead but higher delay and make packet loss
+   more noticeable.  For non-interactive applications such as lectures
+   or for links with severe bandwidth constraints, a higher
+   packetization delay MAY be used.  A receiver SHOULD accept packets
+   representing between 0 and 200 ms of audio data.  (For framed audio
+   encodings, a receiver SHOULD accept packets with a number of frames
+   equal to 200 ms divided by the frame duration, rounded up.)  This
+   restriction allows reasonable buffer sizing for the receiver.
+
+4.3  Guidelines for Sample-Based Audio Encodings
+
+   In sample-based encodings, each audio sample is represented by a
+   fixed number of bits.  Within the compressed audio data, codes for
+   individual samples may span octet boundaries.  An RTP audio packet
+   may contain any number of audio samples, subject to the constraint
+   that the number of bits per sample times the number of samples per
+   packet yields an integral octet count.  Fractional encodings produce
+   less than one octet per sample.
+
+   The duration of an audio packet is determined by the number of
+   samples in the packet.
+
+   For sample-based encodings producing one or more octets per sample,
+   samples from different channels sampled at the same sampling instant
+   SHOULD be packed in consecutive octets.  For example, for a two-
+   channel encoding, the octet sequence is (left channel, first sample),
+   (right channel, first sample), (left channel, second sample), (right
+   channel, second sample), ....  For multi-octet encodings, octets
+   SHOULD be transmitted in network byte order (i.e., most significant
+   octet first).
+
+   The packing of sample-based encodings producing less than one octet
+   per sample is encoding-specific.
+
+   The RTP timestamp reflects the instant at which the first sample in
+   the packet was sampled, that is, the oldest information in the
+   packet.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 10]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.4  Guidelines for Frame-Based Audio Encodings
+
+   Frame-based encodings encode a fixed-length block of audio into
+   another block of compressed data, typically also of fixed length.
+   For frame-based encodings, the sender MAY choose to combine several
+   such frames into a single RTP packet.  The receiver can tell the
+   number of frames contained in an RTP packet, if all the frames have
+   the same length, by dividing the RTP payload length by the audio
+   frame size which is defined as part of the encoding.  This does not
+   work when carrying frames of different sizes unless the frame sizes
+   are relatively prime.  If not, the frames MUST indicate their size.
+
+   For frame-based codecs, the channel order is defined for the whole
+   block.  That is, for two-channel audio, right and left samples SHOULD
+   be coded independently, with the encoded frame for the left channel
+   preceding that for the right channel.
+
+   All frame-oriented audio codecs SHOULD be able to encode and decode
+   several consecutive frames within a single packet.  Since the frame
+   size for the frame-oriented codecs is given, there is no need to use
+   a separate designation for the same encoding, but with different
+   number of frames per packet.
+
+   RTP packets SHALL contain a whole number of frames, with frames
+   inserted according to age within a packet, so that the oldest frame
+   (to be played first) occurs immediately after the RTP packet header.
+   The RTP timestamp reflects the instant at which the first sample in
+   the first frame was sampled, that is, the oldest information in the
+   packet.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 11]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5 Audio Encodings
+
+   name of                              sampling              default
+   encoding  sample/frame  bits/sample      rate  ms/frame  ms/packet
+   __________________________________________________________________
+   DVI4      sample        4                var.                   20
+   G722      sample        8              16,000                   20
+   G723      frame         N/A             8,000        30         30
+   G726-40   sample        5               8,000                   20
+   G726-32   sample        4               8,000                   20
+   G726-24   sample        3               8,000                   20
+   G726-16   sample        2               8,000                   20
+   G728      frame         N/A             8,000       2.5         20
+   G729      frame         N/A             8,000        10         20
+   G729D     frame         N/A             8,000        10         20
+   G729E     frame         N/A             8,000        10         20
+   GSM       frame         N/A             8,000        20         20
+   GSM-EFR   frame         N/A             8,000        20         20
+   L8        sample        8                var.                   20
+   L16       sample        16               var.                   20
+   LPC       frame         N/A             8,000        20         20
+   MPA       frame         N/A              var.      var.
+   PCMA      sample        8                var.                   20
+   PCMU      sample        8                var.                   20
+   QCELP     frame         N/A             8,000        20         20
+   VDVI      sample        var.             var.                   20
+
+   Table 1: Properties of Audio Encodings (N/A: not applicable; var.:
+            variable)
+
+   The characteristics of the audio encodings described in this document
+   are shown in Table 1; they are listed in order of their payload type
+   in Table 4.  While most audio codecs are only specified for a fixed
+   sampling rate, some sample-based algorithms (indicated by an entry of
+   "var." in the sampling rate column of Table 1) may be used with
+   different sampling rates, resulting in different coded bit rates.
+   When used with a sampling rate other than that for which a static
+   payload type is defined, non-RTP means beyond the scope of this memo
+   MUST be used to define a dynamic payload type and MUST indicate the
+   selected RTP timestamp clock rate, which is usually the same as the
+   sampling rate for audio.
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 12]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.1 DVI4
+
+   DVI4 uses an adaptive delta pulse code modulation (ADPCM) encoding
+   scheme that was specified by the Interactive Multimedia Association
+   (IMA) as the "IMA ADPCM wave type".  However, the encoding defined
+   here as DVI4 differs in three respects from the IMA specification:
+
+   o  The RTP DVI4 header contains the predicted value rather than the
+      first sample value contained the IMA ADPCM block header.
+
+   o  IMA ADPCM blocks contain an odd number of samples, since the first
+      sample of a block is contained just in the header (uncompressed),
+      followed by an even number of compressed samples.  DVI4 has an
+      even number of compressed samples only, using the `predict' word
+      from the header to decode the first sample.
+
+   o  For DVI4, the 4-bit samples are packed with the first sample in
+      the four most significant bits and the second sample in the four
+      least significant bits.  In the IMA ADPCM codec, the samples are
+      packed in the opposite order.
+
+   Each packet contains a single DVI block.  This profile only defines
+   the 4-bit-per-sample version, while IMA also specified a 3-bit-per-
+   sample encoding.
+
+   The "header" word for each channel has the following structure:
+
+      int16  predict;  /* predicted value of first sample
+                          from the previous block (L16 format) */
+      u_int8 index;    /* current index into stepsize table */
+      u_int8 reserved; /* set to zero by sender, ignored by receiver */
+
+   Each octet following the header contains two 4-bit samples, thus the
+   number of samples per packet MUST be even because there is no means
+   to indicate a partially filled last octet.
+
+   Packing of samples for multiple channels is for further study.
+
+   The IMA ADPCM algorithm was described in the document IMA Recommended
+   Practices for Enhancing Digital Audio Compatibility in Multimedia
+   Systems (version 3.0).  However, the Interactive Multimedia
+   Association ceased operations in 1997.  Resources for an archived
+   copy of that document and a software implementation of the RTP DVI4
+   encoding are listed in Section 13.
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 13]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.2 G722
+
+   G722 is specified in ITU-T Recommendation G.722, "7 kHz audio-coding
+   within 64 kbit/s".  The G.722 encoder produces a stream of octets,
+   each of which SHALL be octet-aligned in an RTP packet.  The first bit
+   transmitted in the G.722 octet, which is the most significant bit of
+   the higher sub-band sample, SHALL correspond to the most significant
+   bit of the octet in the RTP packet.
+
+   Even though the actual sampling rate for G.722 audio is 16,000 Hz,
+   the RTP clock rate for the G722 payload format is 8,000 Hz because
+   that value was erroneously assigned in RFC 1890 and must remain
+   unchanged for backward compatibility.  The octet rate or sample-pair
+   rate is 8,000 Hz.
+
+4.5.3 G723
+
+   G723 is specified in ITU Recommendation G.723.1, "Dual-rate speech
+   coder for multimedia communications transmitting at 5.3 and 6.3
+   kbit/s".  The G.723.1 5.3/6.3 kbit/s codec was defined by the ITU-T
+   as a mandatory codec for ITU-T H.324 GSTN videophone terminal
+   applications.  The algorithm has a floating point specification in
+   Annex B to G.723.1, a silence compression algorithm in Annex A to
+   G.723.1 and a scalable channel coding scheme for wireless
+   applications in G.723.1 Annex C.
+
+   This Recommendation specifies a coded representation that can be used
+   for compressing the speech signal component of multi-media services
+   at a very low bit rate.  Audio is encoded in 30 ms frames, with an
+   additional delay of 7.5 ms due to look-ahead.  A G.723.1 frame can be
+   one of three sizes:  24 octets (6.3 kb/s frame), 20 octets (5.3 kb/s
+   frame), or 4 octets.  These 4-octet frames are called SID frames
+   (Silence Insertion Descriptor) and are used to specify comfort noise
+   parameters.  There is no restriction on how 4, 20, and 24 octet
+   frames are intermixed.  The least significant two bits of the first
+   octet in the frame determine the frame size and codec type:
+
+         bits  content                      octets/frame
+         00    high-rate speech (6.3 kb/s)            24
+         01    low-rate speech  (5.3 kb/s)            20
+         10    SID frame                               4
+         11    reserved
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 14]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   It is possible to switch between the two rates at any 30 ms frame
+   boundary.  Both (5.3 kb/s and 6.3 kb/s) rates are a mandatory part of
+   the encoder and decoder.  Receivers MUST accept both data rates and
+   MUST accept SID frames unless restriction of these capabilities has
+   been signaled.  The MIME registration for G723 in RFC 3555 [7]
+   specifies parameters that MAY be used with MIME or SDP to restrict to
+   a single data rate or to restrict the use of SID frames.  This coder
+   was optimized to represent speech with near-toll quality at the above
+   rates using a limited amount of complexity.
+
+   The packing of the encoded bit stream into octets and the
+   transmission order of the octets is specified in Rec. G.723.1 and is
+   the same as that produced by the G.723 C code reference
+   implementation.  For the 6.3 kb/s data rate, this packing is
+   illustrated as follows, where the header (HDR) bits are always "0 0"
+   as shown in Fig. 1 to indicate operation at 6.3 kb/s, and the Z bit
+   is always set to zero.  The diagrams show the bit packing in "network
+   byte order", also known as big-endian order.  The bits of each 32-bit
+   word are numbered 0 to 31, with the most significant bit on the left
+   and numbered 0.  The octets (bytes) of each word are transmitted most
+   significant octet first.  The bits of each data field are numbered in
+   the order of the bit stream representation of the encoding (least
+   significant bit first).  The vertical bars indicate the boundaries
+   between field fragments.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 15]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    LPC    |HDR|      LPC      |      LPC      |    ACL0   |LPC|
+   |           |   |               |               |           |   |
+   |0 0 0 0 0 0|0 0|1 1 1 1 0 0 0 0|2 2 1 1 1 1 1 1|0 0 0 0 0 0|2 2|
+   |5 4 3 2 1 0|   |3 2 1 0 9 8 7 6|1 0 9 8 7 6 5 4|5 4 3 2 1 0|3 2|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |  ACL2   |ACL|A| GAIN0 |ACL|ACL|    GAIN0      |    GAIN1      |
+   |         | 1 |C|       | 3 | 2 |               |               |
+   |0 0 0 0 0|0 0|0|0 0 0 0|0 0|0 0|1 1 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
+   |4 3 2 1 0|1 0|6|3 2 1 0|1 0|6 5|1 0 9 8 7 6 5 4|7 6 5 4 3 2 1 0|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | GAIN2 | GAIN1 |     GAIN2     |     GAIN3     | GRID  | GAIN3 |
+   |       |       |               |               |       |       |
+   |0 0 0 0|1 1 0 0|1 1 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0|1 1 0 0|
+   |3 2 1 0|1 0 9 8|1 0 9 8 7 6 5 4|7 6 5 4 3 2 1 0|3 2 1 0|1 0 9 8|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |   MSBPOS    |Z|POS|  MSBPOS   |     POS0      |POS|   POS0    |
+   |             | | 0 |           |               | 1 |           |
+   |0 0 0 0 0 0 0|0|0 0|1 1 1 0 0 0|0 0 0 0 0 0 0 0|0 0|1 1 1 1 1 1|
+   |6 5 4 3 2 1 0| |1 0|2 1 0 9 8 7|9 8 7 6 5 4 3 2|1 0|5 4 3 2 1 0|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     POS1      | POS2  | POS1  |     POS2      | POS3  | POS2  |
+   |               |       |       |               |       |       |
+   |0 0 0 0 0 0 0 0|0 0 0 0|1 1 1 1|1 1 0 0 0 0 0 0|0 0 0 0|1 1 1 1|
+   |9 8 7 6 5 4 3 2|3 2 1 0|3 2 1 0|1 0 9 8 7 6 5 4|3 2 1 0|5 4 3 2|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     POS3      |   PSIG0   |POS|PSIG2|  PSIG1  |  PSIG3  |PSIG2|
+   |               |           | 3 |     |         |         |     |
+   |1 1 0 0 0 0 0 0|0 0 0 0 0 0|1 1|0 0 0|0 0 0 0 0|0 0 0 0 0|0 0 0|
+   |1 0 9 8 7 6 5 4|5 4 3 2 1 0|3 2|2 1 0|4 3 2 1 0|4 3 2 1 0|5 4 3|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                  Figure 1: G.723 (6.3 kb/s) bit packing
+
+   For the 5.3 kb/s data rate, the header (HDR) bits are always "0 1",
+   as shown in Fig. 2, to indicate operation at 5.3 kb/s.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 16]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    LPC    |HDR|      LPC      |      LPC      |   ACL0    |LPC|
+   |           |   |               |               |           |   |
+   |0 0 0 0 0 0|0 1|1 1 1 1 0 0 0 0|2 2 1 1 1 1 1 1|0 0 0 0 0 0|2 2|
+   |5 4 3 2 1 0|   |3 2 1 0 9 8 7 6|1 0 9 8 7 6 5 4|5 4 3 2 1 0|3 2|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |  ACL2   |ACL|A| GAIN0 |ACL|ACL|     GAIN0     |     GAIN1     |
+   |         | 1 |C|       | 3 | 2 |               |               |
+   |0 0 0 0 0|0 0|0|0 0 0 0|0 0|0 0|1 1 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
+   |4 3 2 1 0|1 0|6|3 2 1 0|1 0|6 5|1 0 9 8 7 6 5 4|7 6 5 4 3 2 1 0|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | GAIN2 | GAIN1 |     GAIN2     |    GAIN3      | GRID  | GAIN3 |
+   |       |       |               |               |       |       |
+   |0 0 0 0|1 1 0 0|1 1 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0|1 1 0 0|
+   |3 2 1 0|1 0 9 8|1 0 9 8 7 6 5 4|7 6 5 4 3 2 1 0|4 3 2 1|1 0 9 8|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     POS0      | POS1  | POS0  |     POS1      |     POS2      |
+   |               |       |       |               |               |
+   |0 0 0 0 0 0 0 0|0 0 0 0|1 1 0 0|1 1 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
+   |7 6 5 4 3 2 1 0|3 2 1 0|1 0 9 8|1 0 9 8 7 6 5 4|7 6 5 4 3 2 1 0|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | POS3  | POS2  |     POS3      | PSIG1 | PSIG0 | PSIG3 | PSIG2 |
+   |       |       |               |       |       |       |       |
+   |0 0 0 0|1 1 0 0|1 1 0 0 0 0 0 0|0 0 0 0|0 0 0 0|0 0 0 0|0 0 0 0|
+   |3 2 1 0|1 0 9 8|1 0 9 8 7 6 5 4|3 2 1 0|3 2 1 0|3 2 1 0|3 2 1 0|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                  Figure 2: G.723 (5.3 kb/s) bit packing
+
+   The packing of G.723.1 SID (silence) frames, which are indicated by
+   the header (HDR) bits having the pattern "1 0", is depicted in Fig.
+   3.
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    LPC    |HDR|      LPC      |      LPC      |   GAIN    |LPC|
+   |           |   |               |               |           |   |
+   |0 0 0 0 0 0|1 0|1 1 1 1 0 0 0 0|2 2 1 1 1 1 1 1|0 0 0 0 0 0|2 2|
+   |5 4 3 2 1 0|   |3 2 1 0 9 8 7 6|1 0 9 8 7 6 5 4|5 4 3 2 1 0|3 2|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                   Figure 3: G.723 SID mode bit packing
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 17]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.4  G726-40, G726-32, G726-24, and G726-16
+
+   ITU-T Recommendation G.726 describes, among others, the algorithm
+   recommended for conversion of a single 64 kbit/s A-law or mu-law PCM
+   channel encoded at 8,000 samples/sec to and from a 40, 32, 24, or 16
+   kbit/s channel.  The conversion is applied to the PCM stream using an
+   Adaptive Differential Pulse Code Modulation (ADPCM) transcoding
+   technique.  The ADPCM representation consists of a series of
+   codewords with a one-to-one correspondence to the samples in the PCM
+   stream.  The G726 data rates of 40, 32, 24, and 16 kbit/s have
+   codewords of 5, 4, 3, and 2 bits, respectively.
+
+   The 16 and 24 kbit/s encodings do not provide toll quality speech.
+   They are designed for used in overloaded Digital Circuit
+   Multiplication Equipment (DCME).  ITU-T G.726 recommends that the 16
+   and 24 kbit/s encodings should be alternated with higher data rate
+   encodings to provide an average sample size of between 3.5 and 3.7
+   bits per sample.
+
+   The encodings of G.726 are here denoted as G726-40, G726-32, G726-24,
+   and G726-16.  Prior to 1990, G721 described the 32 kbit/s ADPCM
+   encoding, and G723 described the 40, 32, and 16 kbit/s encodings.
+   Thus, G726-32 designates the same algorithm as G721 in RFC 1890.
+
+   A stream of G726 codewords contains no information on the encoding
+   being used, therefore transitions between G726 encoding types are not
+   permitted within a sequence of packed codewords.  Applications MUST
+   determine the encoding type of packed codewords from the RTP payload
+   identifier.
+
+   No payload-specific header information SHALL be included as part of
+   the audio data.  A stream of G726 codewords MUST be packed into
+   octets as follows:  the first codeword is placed into the first octet
+   such that the least significant bit of the codeword aligns with the
+   least significant bit in the octet, the second codeword is then
+   packed so that its least significant bit coincides with the least
+   significant unoccupied bit in the octet.  When a complete codeword
+   cannot be placed into an octet, the bits overlapping the octet
+   boundary are placed into the least significant bits of the next
+   octet.  Packing MUST end with a completely packed final octet.  The
+   number of codewords packed will therefore be a multiple of 8, 2, 8,
+   and 4 for G726-40, G726-32, G726-24, and G726-16, respectively.  An
+   example of the packing scheme for G726-32 codewords is as shown,
+   where bit 7 is the least significant bit of the first octet, and bit
+   A3 is the least significant bit of the first codeword:
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 18]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+          0                   1
+          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+         |B B B B|A A A A|D D D D|C C C C| ...
+         |0 1 2 3|0 1 2 3|0 1 2 3|0 1 2 3|
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+
+   An example of the packing scheme for G726-24 codewords follows, where
+   again bit 7 is the least significant bit of the first octet, and bit
+   A2 is the least significant bit of the first codeword:
+
+          0                   1                   2
+          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+         |C C|B B B|A A A|F|E E E|D D D|C|H H H|G G G|F F| ...
+         |1 2|0 1 2|0 1 2|2|0 1 2|0 1 2|0|0 1 2|0 1 2|0 1|
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+
+   Note that the "little-endian" direction in which samples are packed
+   into octets in the G726-16, -24, -32 and -40 payload formats
+   specified here is consistent with ITU-T Recommendation X.420, but is
+   the opposite of what is specified in ITU-T Recommendation I.366.2
+   Annex E for ATM AAL2 transport.  A second set of RTP payload formats
+   matching the packetization of I.366.2 Annex E and identified by MIME
+   subtypes AAL2-G726-16, -24, -32 and -40 will be specified in a
+   separate document.
+
+4.5.5 G728
+
+   G728 is specified in ITU-T Recommendation G.728, "Coding of speech at
+   16 kbit/s using low-delay code excited linear prediction".
+
+   A G.278 encoder translates 5 consecutive audio samples into a 10-bit
+   codebook index, resulting in a bit rate of 16 kb/s for audio sampled
+   at 8,000 samples per second.  The group of five consecutive samples
+   is called a vector.  Four consecutive vectors, labeled V1 to V4
+   (where V1 is to be played first by the receiver), build one G.728
+   frame.  The four vectors of 40 bits are packed into 5 octets, labeled
+   B1 through B5.  B1 SHALL be placed first in the RTP packet.
+
+   Referring to the figure below, the principle for bit order is
+   "maintenance of bit significance".  Bits from an older vector are
+   more significant than bits from newer vectors.  The MSB of the frame
+   goes to the MSB of B1 and the LSB of the frame goes to LSB of B5.
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 19]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+                   1         2         3        3
+         0         0         0         0        9
+         ++++++++++++++++++++++++++++++++++++++++
+         <---V1---><---V2---><---V3---><---V4---> vectors
+         <--B1--><--B2--><--B3--><--B4--><--B5--> octets
+         <------------- frame 1 ---------------->
+
+   In particular, B1 contains the eight most significant bits of V1,
+   with the MSB of V1 being the MSB of B1.  B2 contains the two least
+   significant bits of V1, the more significant of the two in its MSB,
+   and the six most significant bits of V2.  B1 SHALL be placed first in
+   the RTP packet and B5 last.
+
+4.5.6 G729
+
+   G729 is specified in ITU-T Recommendation G.729, "Coding of speech at
+   8 kbit/s using conjugate structure-algebraic code excited linear
+   prediction (CS-ACELP)".  A reduced-complexity version of the G.729
+   algorithm is specified in Annex A to Rec. G.729.  The speech coding
+   algorithms in the main body of G.729 and in G.729 Annex A are fully
+   interoperable with each other, so there is no need to further
+   distinguish between them.  An implementation that signals or accepts
+   use of G729 payload format may implement either G.729 or G.729A
+   unless restricted by additional signaling specified elsewhere related
+   specifically to the encoding rather than the payload format.  The
+   G.729 and G.729 Annex A codecs were optimized to represent speech
+   with high quality, where G.729 Annex A trades some speech quality for
+   an approximate 50% complexity reduction [10].  See the next Section
+   (4.5.7) for other data rates added in later G.729 Annexes.  For all
+   data rates, the sampling frequency (and RTP timestamp clock rate) is
+   8,000 Hz.
+
+   A voice activity detector (VAD) and comfort noise generator (CNG)
+   algorithm in Annex B of G.729 is RECOMMENDED for digital simultaneous
+   voice and data applications and can be used in conjunction with G.729
+   or G.729 Annex A.  A G.729 or G.729 Annex A frame contains 10 octets,
+   while the G.729 Annex B comfort noise frame occupies 2 octets.
+   Receivers MUST accept comfort noise frames if restriction of their
+   use has not been signaled.  The MIME registration for G729 in RFC
+   3555 [7] specifies a parameter that MAY be used with MIME or SDP to
+   restrict the use of comfort noise frames.
+
+   A G729 RTP packet may consist of zero or more G.729 or G.729 Annex A
+   frames, followed by zero or one G.729 Annex B frames.  The presence
+   of a comfort noise frame can be deduced from the length of the RTP
+   payload.  The default packetization interval is 20 ms (two frames),
+   but in some situations it may be desirable to send 10 ms packets.  An
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 20]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   example would be a transition from speech to comfort noise in the
+   first 10 ms of the packet.  For some applications, a longer
+   packetization interval may be required to reduce the packet rate.
+
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |L|      L1     |    L2   |    L3   |       P1      |P|    C1   |
+      |0|             |         |         |               |0|         |
+      | |0 1 2 3 4 5 6|0 1 2 3 4|0 1 2 3 4|0 1 2 3 4 5 6 7| |0 1 2 3 4|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |       C1      |  S1   | GA1 |  GB1  |    P2   |      C2       |
+      |          1 1 1|       |     |       |         |               |
+      |5 6 7 8 9 0 1 2|0 1 2 3|0 1 2|0 1 2 3|0 1 2 3 4|0 1 2 3 4 5 6 7|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |   C2    |  S2   | GA2 |  GB2  |
+      |    1 1 1|       |     |       |
+      |8 9 0 1 2|0 1 2 3|0 1 2|0 1 2 3|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                    Figure 4: G.729 and G.729A bit packing
+
+   The transmitted parameters of a G.729/G.729A 10-ms frame, consisting
+   of 80 bits, are defined in Recommendation G.729, Table 8/G.729.  The
+   mapping of the these parameters is given below in Fig. 4.  The
+   diagrams show the bit packing in "network byte order", also known as
+   big-endian order.  The bits of each 32-bit word are numbered 0 to 31,
+   with the most significant bit on the left and numbered 0.  The octets
+   (bytes) of each word are transmitted most significant octet first.
+   The bits of each data field are numbered in the order as produced by
+   the G.729 C code reference implementation.
+
+   The packing of the G.729 Annex B comfort noise frame is shown in Fig.
+   5.
+
+          0                   1
+          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |L|  LSF1   |  LSF2 |   GAIN  |R|
+         |S|         |       |         |E|
+         |F|         |       |         |S|
+         |0|0 1 2 3 4|0 1 2 3|0 1 2 3 4|V|    RESV = Reserved (zero)
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                       Figure 5: G.729 Annex B bit packing
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 21]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.7 G729D and G729E
+
+   Annexes D and E to ITU-T Recommendation G.729 provide additional data
+   rates.  Because the data rate is not signaled in the bitstream, the
+   different data rates are given distinct RTP encoding names which are
+   mapped to distinct payload type numbers.  G729D indicates a 6.4
+   kbit/s coding mode (G.729 Annex D, for momentary reduction in channel
+   capacity), while G729E indicates an 11.8 kbit/s mode (G.729 Annex E,
+   for improved performance with a wide range of narrow-band input
+   signals, e.g., music and background noise).  Annex E has two
+   operating modes, backward adaptive and forward adaptive, which are
+   signaled by the first two bits in each frame (the most significant
+   two bits of the first octet).
+
+   The voice activity detector (VAD) and comfort noise generator (CNG)
+   algorithm specified in Annex B of G.729 may be used with Annex D and
+   Annex E frames in addition to G.729 and G.729 Annex A frames.  The
+   algorithm details for the operation of Annexes D and E with the Annex
+   B CNG are specified in G.729 Annexes F and G.  Note that Annexes F
+   and G do not introduce any new encodings.  Receivers MUST accept
+   comfort noise frames if restriction of their use has not been
+   signaled.  The MIME registrations for G729D and G729E in RFC 3555 [7]
+   specify a parameter that MAY be used with MIME or SDP to restrict the
+   use of comfort noise frames.
+
+   For G729D, an RTP packet may consist of zero or more G.729 Annex D
+   frames, followed by zero or one G.729 Annex B frame.  Similarly, for
+   G729E, an RTP packet may consist of zero or more G.729 Annex E
+   frames, followed by zero or one G.729 Annex B frame.  The presence of
+   a comfort noise frame can be deduced from the length of the RTP
+   payload.
+
+   A single RTP packet must contain frames of only one data rate,
+   optionally followed by one comfort noise frame.  The data rate may be
+   changed from packet to packet by changing the payload type number.
+   G.729 Annexes D, E and H describe what the encoding and decoding
+   algorithms must do to accommodate a change in data rate.
+
+   For G729D, the bits of a G.729 Annex D frame are formatted as shown
+   below in Fig. 6 (cf.  Table D.1/G.729).  The frame length is 64 bits.
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 22]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |L|      L1     |    L2   |    L3   |        P1     |     C1    |
+      |0|             |         |         |               |           |
+      | |0 1 2 3 4 5 6|0 1 2 3 4|0 1 2 3 4|0 1 2 3 4 5 6 7|0 1 2 3 4 5|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      | C1  |S1 | GA1 | GB1 |  P2   |        C2       |S2 | GA2 | GB2 |
+      |     |   |     |     |       |                 |   |     |     |
+      |6 7 8|0 1|0 1 2|0 1 2|0 1 2 3|0 1 2 3 4 5 6 7 8|0 1|0 1 2|0 1 2|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                     Figure 6: G.729 Annex D bit packing
+
+   The net bit rate for the G.729 Annex E algorithm is 11.8 kbit/s and a
+   total of 118 bits are used.  Two bits are appended as "don't care"
+   bits to complete an integer number of octets for the frame.  For
+   G729E, the bits of a data frame are formatted as shown in the next
+   two diagrams (cf. Table E.1/G.729).  The fields for the G729E forward
+   adaptive mode are packed as shown in Fig. 7.
+
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |0 0|L|      L1     |    L2   |    L3   |        P1     |P| C0_1|
+      |   |0|             |         |         |               |0|     |
+      |   | |0 1 2 3 4 5 6|0 1 2 3 4|0 1 2 3 4|0 1 2 3 4 5 6 7| |0 1 2|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |       |   C1_1      |     C2_1    |   C3_1      |    C4_1     |
+      |       |             |             |             |             |
+      |3 4 5 6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      | GA1 |  GB1  |    P2   |   C0_2      |     C1_2    |   C2_2    |
+      |     |       |         |             |             |           |
+      |0 1 2|0 1 2 3|0 1 2 3 4|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2 3 4 5|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      | |    C3_2     |     C4_2    | GA2 | GB2   |DC |
+      | |             |             |     |       |   |
+      |6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2|0 1 2 3|0 1|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+         Figure 7: G.729 Annex E (forward adaptive mode) bit packing
+
+   The fields for the G729E backward adaptive mode are packed as shown
+   in Fig. 8.
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 23]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |1 1|       P1      |P|       C0_1              |     C1_1      |
+      |   |               |0|                    1 1 1|               |
+      |   |0 1 2 3 4 5 6 7|0|0 1 2 3 4 5 6 7 8 9 0 1 2|0 1 2 3 4 5 6 7|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |   |  C2_1       | C3_1        | C4_1        |GA1  | GB1   |P2 |
+      |   |             |             |             |     |       |   |
+      |8 9|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2|0 1 2 3|0 1|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     |          C0_2           |       C1_2        |    C2_2   |
+      |     |                    1 1 1|                   |           |
+      |2 3 4|0 1 2 3 4 5 6 7 8 9 0 1 2|0 1 2 3 4 5 6 7 8 9|0 1 2 3 4 5|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      | |    C3_2     |     C4_2    | GA2 | GB2   |DC |
+      | |             |             |     |       |   |
+      |6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2|0 1 2 3|0 1|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+         Figure 8: G.729 Annex E (backward adaptive mode) bit packing
+
+4.5.8 GSM
+
+   GSM (Group Speciale Mobile) denotes the European GSM 06.10 standard
+   for full-rate speech transcoding, ETS 300 961, which is based on
+   RPE/LTP (residual pulse excitation/long term prediction) coding at a
+   rate of 13 kb/s [11,12,13].  The text of the standard can be obtained
+   from:
+
+   ETSI (European Telecommunications Standards Institute)
+   ETSI Secretariat: B.P.152
+   F-06561 Valbonne Cedex
+   France
+   Phone: +33 92 94 42 00
+   Fax:   +33 93 65 47 16
+
+   Blocks of 160 audio samples are compressed into 33 octets, for an
+   effective data rate of 13,200 b/s.
+
+4.5.8.1  General Packaging Issues
+
+   The GSM standard (ETS 300 961) specifies the bit stream produced by
+   the codec, but does not specify how these bits should be packed for
+   transmission.  The packetization specified here has subsequently been
+   adopted in ETSI Technical Specification TS 101 318.  Some software
+   implementations of the GSM codec use a different packing than that
+   specified here.
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 24]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+               field  field name  bits  field  field name  bits
+               ________________________________________________
+               1      LARc[0]     6     39     xmc[22]     3
+               2      LARc[1]     6     40     xmc[23]     3
+               3      LARc[2]     5     41     xmc[24]     3
+               4      LARc[3]     5     42     xmc[25]     3
+               5      LARc[4]     4     43     Nc[2]       7
+               6      LARc[5]     4     44     bc[2]       2
+               7      LARc[6]     3     45     Mc[2]       2
+               8      LARc[7]     3     46     xmaxc[2]    6
+               9      Nc[0]       7     47     xmc[26]     3
+               10     bc[0]       2     48     xmc[27]     3
+               11     Mc[0]       2     49     xmc[28]     3
+               12     xmaxc[0]    6     50     xmc[29]     3
+               13     xmc[0]      3     51     xmc[30]     3
+               14     xmc[1]      3     52     xmc[31]     3
+               15     xmc[2]      3     53     xmc[32]     3
+               16     xmc[3]      3     54     xmc[33]     3
+               17     xmc[4]      3     55     xmc[34]     3
+               18     xmc[5]      3     56     xmc[35]     3
+               19     xmc[6]      3     57     xmc[36]     3
+               20     xmc[7]      3     58     xmc[37]     3
+               21     xmc[8]      3     59     xmc[38]     3
+               22     xmc[9]      3     60     Nc[3]       7
+               23     xmc[10]     3     61     bc[3]       2
+               24     xmc[11]     3     62     Mc[3]       2
+               25     xmc[12]     3     63     xmaxc[3]    6
+               26     Nc[1]       7     64     xmc[39]     3
+               27     bc[1]       2     65     xmc[40]     3
+               28     Mc[1]       2     66     xmc[41]     3
+               29     xmaxc[1]    6     67     xmc[42]     3
+               30     xmc[13]     3     68     xmc[43]     3
+               31     xmc[14]     3     69     xmc[44]     3
+               32     xmc[15]     3     70     xmc[45]     3
+               33     xmc[16]     3     71     xmc[46]     3
+               34     xmc[17]     3     72     xmc[47]     3
+               35     xmc[18]     3     73     xmc[48]     3
+               36     xmc[19]     3     74     xmc[49]     3
+               37     xmc[20]     3     75     xmc[50]     3
+               38     xmc[21]     3     76     xmc[51]     3
+
+                      Table 2: Ordering of GSM variables
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 25]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   Octet  Bit 0   Bit 1   Bit 2   Bit 3   Bit 4   Bit 5   Bit 6   Bit 7
+   _____________________________________________________________________
+       0    1       1       0       1    LARc0.0 LARc0.1 LARc0.2 LARc0.3
+       1 LARc0.4 LARc0.5 LARc1.0 LARc1.1 LARc1.2 LARc1.3 LARc1.4 LARc1.5
+       2 LARc2.0 LARc2.1 LARc2.2 LARc2.3 LARc2.4 LARc3.0 LARc3.1 LARc3.2
+       3 LARc3.3 LARc3.4 LARc4.0 LARc4.1 LARc4.2 LARc4.3 LARc5.0 LARc5.1
+       4 LARc5.2 LARc5.3 LARc6.0 LARc6.1 LARc6.2 LARc7.0 LARc7.1 LARc7.2
+       5  Nc0.0   Nc0.1   Nc0.2   Nc0.3   Nc0.4   Nc0.5   Nc0.6  bc0.0
+       6  bc0.1   Mc0.0   Mc0.1  xmaxc00 xmaxc01 xmaxc02 xmaxc03 xmaxc04
+       7 xmaxc05 xmc0.0  xmc0.1  xmc0.2  xmc1.0  xmc1.1  xmc1.2  xmc2.0
+       8 xmc2.1  xmc2.2  xmc3.0  xmc3.1  xmc3.2  xmc4.0  xmc4.1  xmc4.2
+       9 xmc5.0  xmc5.1  xmc5.2  xmc6.0  xmc6.1  xmc6.2  xmc7.0  xmc7.1
+      10 xmc7.2  xmc8.0  xmc8.1  xmc8.2  xmc9.0  xmc9.1  xmc9.2  xmc10.0
+      11 xmc10.1 xmc10.2 xmc11.0 xmc11.1 xmc11.2 xmc12.0 xmc12.1 xcm12.2
+      12  Nc1.0   Nc1.1   Nc1.2   Nc1.3   Nc1.4   Nc1.5   Nc1.6   bc1.0
+      13  bc1.1   Mc1.0   Mc1.1  xmaxc10 xmaxc11 xmaxc12 xmaxc13 xmaxc14
+      14 xmax15  xmc13.0 xmc13.1 xmc13.2 xmc14.0 xmc14.1 xmc14.2 xmc15.0
+      15 xmc15.1 xmc15.2 xmc16.0 xmc16.1 xmc16.2 xmc17.0 xmc17.1 xmc17.2
+      16 xmc18.0 xmc18.1 xmc18.2 xmc19.0 xmc19.1 xmc19.2 xmc20.0 xmc20.1
+      17 xmc20.2 xmc21.0 xmc21.1 xmc21.2 xmc22.0 xmc22.1 xmc22.2 xmc23.0
+      18 xmc23.1 xmc23.2 xmc24.0 xmc24.1 xmc24.2 xmc25.0 xmc25.1 xmc25.2
+      19  Nc2.0   Nc2.1   Nc2.2   Nc2.3   Nc2.4   Nc2.5   Nc2.6   bc2.0
+      20  bc2.1   Mc2.0   Mc2.1  xmaxc20 xmaxc21 xmaxc22 xmaxc23 xmaxc24
+      21 xmaxc25 xmc26.0 xmc26.1 xmc26.2 xmc27.0 xmc27.1 xmc27.2 xmc28.0
+      22 xmc28.1 xmc28.2 xmc29.0 xmc29.1 xmc29.2 xmc30.0 xmc30.1 xmc30.2
+      23 xmc31.0 xmc31.1 xmc31.2 xmc32.0 xmc32.1 xmc32.2 xmc33.0 xmc33.1
+      24 xmc33.2 xmc34.0 xmc34.1 xmc34.2 xmc35.0 xmc35.1 xmc35.2 xmc36.0
+      25 Xmc36.1 xmc36.2 xmc37.0 xmc37.1 xmc37.2 xmc38.0 xmc38.1 xmc38.2
+      26  Nc3.0   Nc3.1   Nc3.2   Nc3.3   Nc3.4   Nc3.5   Nc3.6   bc3.0
+      27  bc3.1   Mc3.0   Mc3.1  xmaxc30 xmaxc31 xmaxc32 xmaxc33 xmaxc34
+      28 xmaxc35 xmc39.0 xmc39.1 xmc39.2 xmc40.0 xmc40.1 xmc40.2 xmc41.0
+      29 xmc41.1 xmc41.2 xmc42.0 xmc42.1 xmc42.2 xmc43.0 xmc43.1 xmc43.2
+      30 xmc44.0 xmc44.1 xmc44.2 xmc45.0 xmc45.1 xmc45.2 xmc46.0 xmc46.1
+      31 xmc46.2 xmc47.0 xmc47.1 xmc47.2 xmc48.0 xmc48.1 xmc48.2 xmc49.0
+      32 xmc49.1 xmc49.2 xmc50.0 xmc50.1 xmc50.2 xmc51.0 xmc51.1 xmc51.2
+
+                        Table 3: GSM payload format
+
+   In the GSM packing used by RTP, the bits SHALL be packed beginning
+   from the most significant bit.  Every 160 sample GSM frame is coded
+   into one 33 octet (264 bit) buffer.  Every such buffer begins with a
+   4 bit signature (0xD), followed by the MSB encoding of the fields of
+   the frame.  The first octet thus contains 1101 in the 4 most
+   significant bits (0-3) and the 4 most significant bits of F1 (0-3) in
+   the 4 least significant bits (4-7).  The second octet contains the 2
+   least significant bits of F1 in bits 0-1, and F2 in bits 2-7, and so
+   on.  The order of the fields in the frame is described in Table 2.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 26]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.8.2   GSM Variable Names and Numbers
+
+   In the RTP encoding we have the bit pattern described in Table 3,
+   where F.i signifies the ith bit of the field F, bit 0 is the most
+   significant bit, and the bits of every octet are numbered from 0 to 7
+   from most to least significant.
+
+4.5.9 GSM-EFR
+
+   GSM-EFR denotes GSM 06.60 enhanced full rate speech transcoding,
+   specified in ETS 300 726 which is available from ETSI at the address
+   given in Section 4.5.8.  This codec has a frame length of 244 bits.
+   For transmission in RTP, each codec frame is packed into a 31 octet
+   (248 bit) buffer beginning with a 4-bit signature 0xC in a manner
+   similar to that specified here for the original GSM 06.10 codec.  The
+   packing is specified in ETSI Technical Specification TS 101 318.
+
+4.5.10 L8
+
+   L8 denotes linear audio data samples, using 8-bits of precision with
+   an offset of 128, that is, the most negative signal is encoded as
+   zero.
+
+4.5.11 L16
+
+   L16 denotes uncompressed audio data samples, using 16-bit signed
+   representation with 65,535 equally divided steps between minimum and
+   maximum signal level, ranging from -32,768 to 32,767.  The value is
+   represented in two's complement notation and transmitted in network
+   byte order (most significant byte first).
+
+   The MIME registration for L16 in RFC 3555 [7] specifies parameters
+   that MAY be used with MIME or SDP to indicate that analog pre-
+   emphasis was applied to the signal before quantization or to indicate
+   that a multiple-channel audio stream follows a different channel
+   ordering convention than is specified in Section 4.1.
+
+4.5.12 LPC
+
+   LPC designates an experimental linear predictive encoding contributed
+   by Ron Frederick, which is based on an implementation written by Ron
+   Zuckerman posted to the Usenet group comp.dsp on June 26, 1992.  The
+   codec generates 14 octets for every frame.  The framesize is set to
+   20 ms, resulting in a bit rate of 5,600 b/s.
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 27]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.13 MPA
+
+   MPA denotes MPEG-1 or MPEG-2 audio encapsulated as elementary
+   streams.  The encoding is defined in ISO standards ISO/IEC 11172-3
+   and 13818-3.  The encapsulation is specified in RFC 2250 [14].
+
+   The encoding may be at any of three levels of complexity, called
+   Layer I, II and III.  The selected layer as well as the sampling rate
+   and channel count are indicated in the payload.  The RTP timestamp
+   clock rate is always 90,000, independent of the sampling rate.
+   MPEG-1 audio supports sampling rates of 32, 44.1, and 48 kHz (ISO/IEC
+   11172-3, section 1.1; "Scope").  MPEG-2 supports sampling rates of
+   16, 22.05 and 24 kHz.  The number of samples per frame is fixed, but
+   the frame size will vary with the sampling rate and bit rate.
+
+   The MIME registration for MPA in RFC 3555 [7] specifies parameters
+   that MAY be used with MIME or SDP to restrict the selection of layer,
+   channel count, sampling rate, and bit rate.
+
+4.5.14 PCMA and PCMU
+
+   PCMA and PCMU are specified in ITU-T Recommendation G.711.  Audio
+   data is encoded as eight bits per sample, after logarithmic scaling.
+   PCMU denotes mu-law scaling, PCMA A-law scaling.  A detailed
+   description is given by Jayant and Noll [15].  Each G.711 octet SHALL
+   be octet-aligned in an RTP packet.  The sign bit of each G.711 octet
+   SHALL correspond to the most significant bit of the octet in the RTP
+   packet (i.e., assuming the G.711 samples are handled as octets on the
+   host machine, the sign bit SHALL be the most significant bit of the
+   octet as defined by the host machine format).  The 56 kb/s and 48
+   kb/s modes of G.711 are not applicable to RTP, since PCMA and PCMU
+   MUST always be transmitted as 8-bit samples.
+
+   See Section 4.1 regarding silence suppression.
+
+4.5.15 QCELP
+
+   The Electronic Industries Association (EIA) & Telecommunications
+   Industry Association (TIA) standard IS-733, "TR45: High Rate Speech
+   Service Option for Wideband Spread Spectrum Communications Systems",
+   defines the QCELP audio compression algorithm for use in wireless
+   CDMA applications.  The QCELP CODEC compresses each 20 milliseconds
+   of 8,000 Hz, 16-bit sampled input speech into one of four different
+   size output frames:  Rate 1 (266 bits), Rate 1/2 (124 bits), Rate 1/4
+   (54 bits) or Rate 1/8 (20 bits).  For typical speech patterns, this
+   results in an average output of 6.8 kb/s for normal mode and 4.7 kb/s
+   for reduced rate mode.  The packetization of the QCELP audio codec is
+   described in [16].
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 28]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.16 RED
+
+   The redundant audio payload format "RED" is specified by RFC 2198
+   [17].  It defines a means by which multiple redundant copies of an
+   audio packet may be transmitted in a single RTP stream.  Each packet
+   in such a stream contains, in addition to the audio data for that
+   packetization interval, a (more heavily compressed) copy of the data
+   from a previous packetization interval.  This allows an approximation
+   of the data from lost packets to be recovered upon decoding of a
+   subsequent packet, giving much improved sound quality when compared
+   with silence substitution for lost packets.
+
+4.5.17 VDVI
+
+   VDVI is a variable-rate version of DVI4, yielding speech bit rates of
+   between 10 and 25 kb/s.  It is specified for single-channel operation
+   only.  Samples are packed into octets starting at the most-
+   significant bit.  The last octet is padded with 1 bits if the last
+   sample does not fill the last octet.  This padding is distinct from
+   the valid codewords.  The receiver needs to detect the padding
+   because there is no explicit count of samples in the packet.
+
+   It uses the following encoding:
+
+            DVI4 codeword  VDVI bit pattern
+            _______________________________
+                        0  00
+                        1  010
+                        2  1100
+                        3  11100
+                        4  111100
+                        5  1111100
+                        6  11111100
+                        7  11111110
+                        8  10
+                        9  011
+                       10  1101
+                       11  11101
+                       12  111101
+                       13  1111101
+                       14  11111101
+                       15  11111111
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 29]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+5.  Video
+
+   The following sections describe the video encodings that are defined
+   in this memo and give their abbreviated names used for
+   identification.  These video encodings and their payload types are
+   listed in Table 5.
+
+   All of these video encodings use an RTP timestamp frequency of 90,000
+   Hz, the same as the MPEG presentation time stamp frequency.  This
+   frequency yields exact integer timestamp increments for the typical
+   24 (HDTV), 25 (PAL), and 29.97 (NTSC) and 30 Hz (HDTV) frame rates
+   and 50, 59.94 and 60 Hz field rates.  While 90 kHz is the RECOMMENDED
+   rate for future video encodings used within this profile, other rates
+   MAY be used.  However, it is not sufficient to use the video frame
+   rate (typically between 15 and 30 Hz) because that does not provide
+   adequate resolution for typical synchronization requirements when
+   calculating the RTP timestamp corresponding to the NTP timestamp in
+   an RTCP SR packet.  The timestamp resolution MUST also be sufficient
+   for the jitter estimate contained in the receiver reports.
+
+   For most of these video encodings, the RTP timestamp encodes the
+   sampling instant of the video image contained in the RTP data packet.
+   If a video image occupies more than one packet, the timestamp is the
+   same on all of those packets.  Packets from different video images
+   are distinguished by their different timestamps.
+
+   Most of these video encodings also specify that the marker bit of the
+   RTP header SHOULD be set to one in the last packet of a video frame
+   and otherwise set to zero.  Thus, it is not necessary to wait for a
+   following packet with a different timestamp to detect that a new
+   frame should be displayed.
+
+5.1  CelB
+
+   The CELL-B encoding is a proprietary encoding proposed by Sun
+   Microsystems.  The byte stream format is described in RFC 2029 [18].
+
+5.2 JPEG
+
+   The encoding is specified in ISO Standards 10918-1 and 10918-2.  The
+   RTP payload format is as specified in RFC 2435 [19].
+
+5.3 H261
+
+   The encoding is specified in ITU-T Recommendation H.261, "Video codec
+   for audiovisual services at p x 64 kbit/s".  The packetization and
+   RTP-specific properties are described in RFC 2032 [20].
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 30]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+5.4 H263
+
+   The encoding is specified in the 1996 version of ITU-T Recommendation
+   H.263, "Video coding for low bit rate communication".  The
+   packetization and RTP-specific properties are described in RFC 2190
+   [21].  The H263-1998 payload format is RECOMMENDED over this one for
+   use by new implementations.
+
+5.5 H263-1998
+
+   The encoding is specified in the 1998 version of ITU-T Recommendation
+   H.263, "Video coding for low bit rate communication".  The
+   packetization and RTP-specific properties are described in RFC 2429
+   [22].  Because the 1998 version of H.263 is a superset of the 1996
+   syntax, this payload format can also be used with the 1996 version of
+   H.263, and is RECOMMENDED for this use by new implementations.  This
+   payload format does not replace RFC 2190, which continues to be used
+   by existing implementations, and may be required for backward
+   compatibility in new implementations.  Implementations using the new
+   features of the 1998 version of H.263 MUST use the payload format
+   described in RFC 2429.
+
+5.6 MPV
+
+   MPV designates the use of MPEG-1 and MPEG-2 video encoding elementary
+   streams as specified in ISO Standards ISO/IEC 11172 and 13818-2,
+   respectively.  The RTP payload format is as specified in RFC 2250
+   [14], Section 3.
+
+   The MIME registration for MPV in RFC 3555 [7] specifies a parameter
+   that MAY be used with MIME or SDP to restrict the selection of the
+   type of MPEG video.
+
+5.7 MP2T
+
+   MP2T designates the use of MPEG-2 transport streams, for either audio
+   or video.  The RTP payload format is described in RFC 2250 [14],
+   Section 2.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 31]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+5.8 nv
+
+   The encoding is implemented in the program `nv', version 4, developed
+   at Xerox PARC by Ron Frederick.  Further information is available
+   from the author:
+
+   Ron Frederick
+   Blue Coat Systems Inc.
+   650 Almanor Avenue
+   Sunnyvale, CA 94085
+   United States
+   EMail: ronf@bluecoat.com
+
+6.  Payload Type Definitions
+
+   Tables 4 and 5 define this profile's static payload type values for
+   the PT field of the RTP data header.  In addition, payload type
+   values in the range 96-127 MAY be defined dynamically through a
+   conference control protocol, which is beyond the scope of this
+   document.  For example, a session directory could specify that for a
+   given session, payload type 96 indicates PCMU encoding, 8,000 Hz
+   sampling rate, 2 channels.  Entries in Tables 4 and 5 with payload
+   type "dyn" have no static payload type assigned and are only used
+   with a dynamic payload type.  Payload type 2 was assigned to G721 in
+   RFC 1890 and to its equivalent successor G726-32 in draft versions of
+   this specification, but its use is now deprecated and that static
+   payload type is marked reserved due to conflicting use for the
+   payload formats G726-32 and AAL2-G726-32 (see Section 4.5.4).
+   Payload type 13 indicates the Comfort Noise (CN) payload format
+   specified in RFC 3389 [9].  Payload type 19 is marked "reserved"
+   because some draft versions of this specification assigned that
+   number to an earlier version of the comfort noise payload format.
+   The payload type range 72-76 is marked "reserved" so that RTCP and
+   RTP packets can be reliably distinguished (see Section "Summary of
+   Protocol Constants" of the RTP protocol specification).
+
+   The payload types currently defined in this profile are assigned to
+   exactly one of three categories or media types:  audio only, video
+   only and those combining audio and video.  The media types are marked
+   in Tables 4 and 5 as "A", "V" and "AV", respectively.  Payload types
+   of different media types SHALL NOT be interleaved or multiplexed
+   within a single RTP session, but multiple RTP sessions MAY be used in
+   parallel to send multiple media types.  An RTP source MAY change
+   payload types within the same media type during a session.  See the
+   section "Multiplexing RTP Sessions" of RFC 3550 for additional
+   explanation.
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 32]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+               PT   encoding    media type  clock rate   channels
+                    name                    (Hz)
+               ___________________________________________________
+               0    PCMU        A            8,000       1
+               1    reserved    A
+               2    reserved    A
+               3    GSM         A            8,000       1
+               4    G723        A            8,000       1
+               5    DVI4        A            8,000       1
+               6    DVI4        A           16,000       1
+               7    LPC         A            8,000       1
+               8    PCMA        A            8,000       1
+               9    G722        A            8,000       1
+               10   L16         A           44,100       2
+               11   L16         A           44,100       1
+               12   QCELP       A            8,000       1
+               13   CN          A            8,000       1
+               14   MPA         A           90,000       (see text)
+               15   G728        A            8,000       1
+               16   DVI4        A           11,025       1
+               17   DVI4        A           22,050       1
+               18   G729        A            8,000       1
+               19   reserved    A
+               20   unassigned  A
+               21   unassigned  A
+               22   unassigned  A
+               23   unassigned  A
+               dyn  G726-40     A            8,000       1
+               dyn  G726-32     A            8,000       1
+               dyn  G726-24     A            8,000       1
+               dyn  G726-16     A            8,000       1
+               dyn  G729D       A            8,000       1
+               dyn  G729E       A            8,000       1
+               dyn  GSM-EFR     A            8,000       1
+               dyn  L8          A            var.        var.
+               dyn  RED         A                        (see text)
+               dyn  VDVI        A            var.        1
+
+               Table 4: Payload types (PT) for audio encodings
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 33]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+               PT      encoding    media type  clock rate
+                       name                    (Hz)
+               _____________________________________________
+               24      unassigned  V
+               25      CelB        V           90,000
+               26      JPEG        V           90,000
+               27      unassigned  V
+               28      nv          V           90,000
+               29      unassigned  V
+               30      unassigned  V
+               31      H261        V           90,000
+               32      MPV         V           90,000
+               33      MP2T        AV          90,000
+               34      H263        V           90,000
+               35-71   unassigned  ?
+               72-76   reserved    N/A         N/A
+               77-95   unassigned  ?
+               96-127  dynamic     ?
+               dyn     H263-1998   V           90,000
+
+               Table 5: Payload types (PT) for video and combined
+                        encodings
+
+   Session participants agree through mechanisms beyond the scope of
+   this specification on the set of payload types allowed in a given
+   session.  This set MAY, for example, be defined by the capabilities
+   of the applications used, negotiated by a conference control protocol
+   or established by agreement between the human participants.
+
+   Audio applications operating under this profile SHOULD, at a minimum,
+   be able to send and/or receive payload types 0 (PCMU) and 5 (DVI4).
+   This allows interoperability without format negotiation and ensures
+   successful negotiation with a conference control protocol.
+
+7.  RTP over TCP and Similar Byte Stream Protocols
+
+   Under special circumstances, it may be necessary to carry RTP in
+   protocols offering a byte stream abstraction, such as TCP, possibly
+   multiplexed with other data.  The application MUST define its own
+   method of delineating RTP and RTCP packets (RTSP [23] provides an
+   example of such an encapsulation specification).
+
+8.  Port Assignment
+
+   As specified in the RTP protocol definition, RTP data SHOULD be
+   carried on an even UDP port number and the corresponding RTCP packets
+   SHOULD be carried on the next higher (odd) port number.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 34]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   Applications operating under this profile MAY use any such UDP port
+   pair.  For example, the port pair MAY be allocated randomly by a
+   session management program.  A single fixed port number pair cannot
+   be required because multiple applications using this profile are
+   likely to run on the same host, and there are some operating systems
+   that do not allow multiple processes to use the same UDP port with
+   different multicast addresses.
+
+   However, port numbers 5004 and 5005 have been registered for use with
+   this profile for those applications that choose to use them as the
+   default pair.  Applications that operate under multiple profiles MAY
+   use this port pair as an indication to select this profile if they
+   are not subject to the constraint of the previous paragraph.
+   Applications need not have a default and MAY require that the port
+   pair be explicitly specified.  The particular port numbers were
+   chosen to lie in the range above 5000 to accommodate port number
+   allocation practice within some versions of the Unix operating
+   system, where port numbers below 1024 can only be used by privileged
+   processes and port numbers between 1024 and 5000 are automatically
+   assigned by the operating system.
+
+9.  Changes from RFC 1890
+
+   This RFC revises RFC 1890.  It is mostly backwards-compatible with
+   RFC 1890 except for functions removed because two interoperable
+   implementations were not found.  The additions to RFC 1890 codify
+   existing practice in the use of payload formats under this profile.
+   Since this profile may be used without using any of the payload
+   formats listed here, the addition of new payload formats in this
+   revision does not affect backwards compatibility.  The changes are
+   listed below, categorized into functional and non-functional changes.
+
+   Functional changes:
+
+   o  Section 11, "IANA Considerations" was added to specify the
+      registration of the name for this profile.  That appendix also
+      references a new Section 3 "Registering Additional Encodings"
+      which establishes a policy that no additional registration of
+      static payload types for this profile will be made beyond those
+      added in this revision and included in Tables 4 and 5.  Instead,
+      additional encoding names may be registered as MIME subtypes for
+      binding to dynamic payload types.  Non-normative references were
+      added to RFC 3555 [7] where MIME subtypes for all the listed
+      payload formats are registered, some with optional parameters for
+      use of the payload formats.
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 35]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   o  Static payload types 4, 16, 17 and 34 were added to incorporate
+      IANA registrations made since the publication of RFC 1890, along
+      with the corresponding payload format descriptions for G723 and
+      H263.
+
+   o  Following working group discussion, static payload types 12 and 18
+      were added along with the corresponding payload format
+      descriptions for QCELP and G729.  Static payload type 13 was
+      assigned to the Comfort Noise (CN) payload format defined in RFC
+      3389.  Payload type 19 was marked reserved because it had been
+      temporarily allocated to an earlier version of Comfort Noise
+      present in some draft revisions of this document.
+
+   o  The payload format for G721 was renamed to G726-32 following the
+      ITU-T renumbering, and the payload format description for G726 was
+      expanded to include the -16, -24 and -40 data rates.  Because of
+      confusion regarding draft revisions of this document, some
+      implementations of these G726 payload formats packed samples into
+      octets starting with the most significant bit rather than the
+      least significant bit as specified here.  To partially resolve
+      this incompatibility, new payload formats named AAL2-G726-16, -24,
+      -32 and -40 will be specified in a separate document (see note in
+      Section 4.5.4), and use of static payload type 2 is deprecated as
+      explained in Section 6.
+
+   o  Payload formats G729D and G729E were added following the ITU-T
+      addition of Annexes D and E to Recommendation G.729.  Listings
+      were added for payload formats GSM-EFR, RED, and H263-1998
+      published in other documents subsequent to RFC 1890.  These
+      additional payload formats are referenced only by dynamic payload
+      type numbers.
+
+   o  The descriptions of the payload formats for G722, G728, GSM, VDVI
+      were expanded.
+
+   o  The payload format for 1016 audio was removed and its static
+      payload type assignment 1 was marked "reserved" because two
+      interoperable implementations were not found.
+
+   o  Requirements for congestion control were added in Section 2.
+
+   o  This profile follows the suggestion in the revised RTP spec that
+      RTCP bandwidth may be specified separately from the session
+      bandwidth and separately for active senders and passive receivers.
+
+   o  The mapping of a user pass-phrase string into an encryption key
+      was deleted from Section 2 because two interoperable
+      implementations were not found.
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 36]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   o  The "quadrophonic" sample ordering convention for four-channel
+      audio was removed to eliminate an ambiguity as noted in Section
+      4.1.
+
+   Non-functional changes:
+
+   o  In Section 4.1, it is now explicitly stated that silence
+      suppression is allowed for all audio payload formats.  (This has
+      always been the case and derives from a fundamental aspect of
+      RTP's design and the motivations for packet audio, but was not
+      explicit stated before.)  The use of comfort noise is also
+      explained.
+
+   o  In Section 4.1, the requirement level for setting of the marker
+      bit on the first packet after silence for audio was changed from
+      "is" to "SHOULD be", and clarified that the marker bit is set only
+      when packets are intentionally not sent.
+
+   o  Similarly, text was added to specify that the marker bit SHOULD be
+      set to one on the last packet of a video frame, and that video
+      frames are distinguished by their timestamps.
+
+   o  RFC references are added for payload formats published after RFC
+      1890.
+
+   o  The security considerations and full copyright sections were
+      added.
+
+   o  According to Peter Hoddie of Apple, only pre-1994 Macintosh used
+      the 22254.54 rate and none the 11127.27 rate, so the latter was
+      dropped from the discussion of suggested sampling frequencies.
+
+   o  Table 1 was corrected to move some values from the "ms/packet"
+      column to the "default ms/packet" column where they belonged.
+
+   o  Since the Interactive Multimedia Association ceased operations, an
+      alternate resource was provided for a referenced IMA document.
+
+   o  A note has been added for G722 to clarify a discrepancy between
+      the actual sampling rate and the RTP timestamp clock rate.
+
+   o  Small clarifications of the text have been made in several places,
+      some in response to questions from readers.  In particular:
+
+      -  A definition for "media type" is given in Section 1.1 to allow
+         the explanation of multiplexing RTP sessions in Section 6 to be
+         more clear regarding the multiplexing of multiple media.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 37]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+      -  The explanation of how to determine the number of audio frames
+         in a packet from the length was expanded.
+
+      -  More description of the allocation of bandwidth to SDES items
+         is given.
+
+      -  A note was added that the convention for the order of channels
+         specified in Section 4.1 may be overridden by a particular
+         encoding or payload format specification.
+
+      -  The terms MUST, SHOULD, MAY, etc. are used as defined in RFC
+         2119.
+
+   o  A second author for this document was added.
+
+10. Security Considerations
+
+   Implementations using the profile defined in this specification are
+   subject to the security considerations discussed in the RTP
+   specification [1].  This profile does not specify any different
+   security services.  The primary function of this profile is to list a
+   set of data compression encodings for audio and video media.
+
+   Confidentiality of the media streams is achieved by encryption.
+   Because the data compression used with the payload formats described
+   in this profile is applied end-to-end, encryption may be performed
+   after compression so there is no conflict between the two operations.
+
+   A potential denial-of-service threat exists for data encodings using
+   compression techniques that have non-uniform receiver-end
+   computational load.  The attacker can inject pathological datagrams
+   into the stream which are complex to decode and cause the receiver to
+   be overloaded.
+
+   As with any IP-based protocol, in some circumstances a receiver may
+   be overloaded simply by the receipt of too many packets, either
+   desired or undesired.  Network-layer authentication MAY be used to
+   discard packets from undesired sources, but the processing cost of
+   the authentication itself may be too high.  In a multicast
+   environment, source pruning is implemented in IGMPv3 (RFC 3376) [24]
+   and in multicast routing protocols to allow a receiver to select
+   which sources are allowed to reach it.
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 38]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+11. IANA Considerations
+
+   The RTP specification establishes a registry of profile names for use
+   by higher-level control protocols, such as the Session Description
+   Protocol (SDP), RFC 2327 [6], to refer to transport methods.  This
+   profile registers the name "RTP/AVP".
+
+   Section 3 establishes the policy that no additional registration of
+   static RTP payload types for this profile will be made beyond those
+   added in this document revision and included in Tables 4 and 5.  IANA
+   may reference that section in declining to accept any additional
+   registration requests.  In Tables 4 and 5, note that types 1 and 2
+   have been marked reserved and the set of "dyn" payload types included
+   has been updated.  These changes are explained in Sections 6 and 9.
+
+12.  References
+
+12.1 Normative References
+
+   [1]  Schulzrinne, H., Casner, S., Frederick, R. and V. Jacobson,
+        "RTP:  A Transport Protocol for Real-Time Applications", RFC
+        3550, July 2003.
+
+   [2]  Bradner, S., "Key Words for Use in RFCs to Indicate Requirement
+        Levels", BCP 14, RFC 2119, March 1997.
+
+   [3]  Apple Computer, "Audio Interchange File Format AIFF-C", August
+        1991.  (also ftp://ftp.sgi.com/sgi/aiff-c.9.26.91.ps.Z).
+
+12.2 Informative References
+
+   [4]  Braden, R., Clark, D. and S. Shenker, "Integrated Services in
+        the Internet Architecture: an Overview", RFC 1633, June 1994.
+
+   [5]  Blake, S., Black, D., Carlson, M., Davies, E., Wang, Z. and W.
+        Weiss, "An Architecture for Differentiated Service", RFC 2475,
+        December 1998.
+
+   [6]  Handley, M. and V. Jacobson, "SDP: Session Description
+        Protocol", RFC 2327, April 1998.
+
+   [7]  Casner, S. and P. Hoschka, "MIME Type Registration of RTP
+        Payload Types", RFC 3555, July 2003.
+
+   [8]  Freed, N., Klensin, J. and J. Postel, "Multipurpose Internet
+        Mail Extensions (MIME) Part Four: Registration Procedures", BCP
+        13, RFC 2048, November 1996.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 39]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   [9]  Zopf, R., "Real-time Transport Protocol (RTP) Payload for
+        Comfort Noise (CN)", RFC 3389, September 2002.
+
+   [10] Deleam, D. and J.-P. Petit, "Real-time implementations of the
+        recent ITU-T low bit rate speech coders on the TI TMS320C54X
+        DSP: results, methodology, and applications", in Proc. of
+        International Conference on Signal Processing, Technology, and
+        Applications (ICSPAT) , (Boston, Massachusetts), pp. 1656--1660,
+        October 1996.
+
+   [11] Mouly, M. and M.-B. Pautet, The GSM system for mobile
+        communications Lassay-les-Chateaux, France: Europe Media
+        Duplication, 1993.
+
+   [12] Degener, J., "Digital Speech Compression", Dr. Dobb's Journal,
+        December 1994.
+
+   [13] Redl, S., Weber, M. and M. Oliphant, An Introduction to GSM
+        Boston: Artech House, 1995.
+
+   [14] Hoffman, D., Fernando, G., Goyal, V. and M. Civanlar, "RTP
+        Payload Format for MPEG1/MPEG2 Video", RFC 2250, January 1998.
+
+   [15] Jayant, N. and P. Noll, Digital Coding of Waveforms--Principles
+        and Applications to Speech and Video Englewood Cliffs, New
+        Jersey: Prentice-Hall, 1984.
+
+   [16] McKay, K., "RTP Payload Format for PureVoice(tm) Audio", RFC
+        2658, August 1999.
+
+   [17] Perkins, C., Kouvelas, I., Hodson, O., Hardman, V., Handley, M.,
+        Bolot, J.-C., Vega-Garcia, A. and S. Fosse-Parisis, "RTP Payload
+        for Redundant Audio Data", RFC 2198, September 1997.
+
+   [18] Speer, M. and D. Hoffman, "RTP Payload Format of Sun's CellB
+        Video Encoding", RFC 2029, October 1996.
+
+   [19] Berc, L., Fenner, W., Frederick, R., McCanne, S. and P. Stewart,
+        "RTP Payload Format for JPEG-Compressed Video", RFC 2435,
+        October 1998.
+
+   [20] Turletti, T. and C. Huitema, "RTP Payload Format for H.261 Video
+        Streams", RFC 2032, October 1996.
+
+   [21] Zhu, C., "RTP Payload Format for H.263 Video Streams", RFC 2190,
+        September 1997.
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 40]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   [22] Bormann, C., Cline, L., Deisher, G., Gardos, T., Maciocco, C.,
+        Newell, D., Ott, J., Sullivan, G., Wenger, S. and C. Zhu, "RTP
+        Payload Format for the 1998 Version of ITU-T Rec. H.263 Video
+        (H.263+)", RFC 2429, October 1998.
+
+   [23] Schulzrinne, H., Rao, A. and R. Lanphier, "Real Time Streaming
+        Protocol (RTSP)", RFC 2326, April 1998.
+
+   [24] Cain, B., Deering, S., Kouvelas, I., Fenner, B. and A.
+        Thyagarajan, "Internet Group Management Protocol, Version 3",
+        RFC 3376, October 2002.
+
+13. Current Locations of Related Resources
+
+   Note:  Several sections below refer to the ITU-T Software Tool
+   Library (STL).  It is available from the ITU Sales Service, Place des
+   Nations, CH-1211 Geneve 20, Switzerland (also check
+   http://www.itu.int).  The ITU-T STL is covered by a license defined
+   in ITU-T Recommendation G.191, "Software tools for speech and audio
+   coding standardization".
+
+   DVI4
+
+   An archived copy of the document IMA Recommended Practices for
+   Enhancing Digital Audio Compatibility in Multimedia Systems (version
+   3.0), which describes the IMA ADPCM algorithm, is available at:
+
+      http://www.cs.columbia.edu/~hgs/audio/dvi/
+
+   An implementation is available from Jack Jansen at
+
+      ftp://ftp.cwi.nl/local/pub/audio/adpcm.shar
+
+   G722
+
+   An implementation of the G.722 algorithm is available as part of the
+   ITU-T STL, described above.
+
+   G723
+
+   The reference C code implementation defining the G.723.1 algorithm
+   and its Annexes A, B, and C are available as an integral part of
+   Recommendation G.723.1 from the ITU Sales Service, address listed
+   above.  Both the algorithm and C code are covered by a specific
+   license.  The ITU-T Secretariat should be contacted to obtain such
+   licensing information.
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 41]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   G726
+
+   G726 is specified in the ITU-T Recommendation G.726, "40, 32, 24, and
+   16 kb/s Adaptive Differential Pulse Code Modulation (ADPCM)".  An
+   implementation of the G.726 algorithm is available as part of the
+   ITU-T STL, described above.
+
+   G729
+
+   The reference C code implementation defining the G.729 algorithm and
+   its Annexes A through I are available as an integral part of
+   Recommendation G.729 from the ITU Sales Service, listed above.  Annex
+   I contains the integrated C source code for all G.729 operating
+   modes.  The G.729 algorithm and associated C code are covered by a
+   specific license.  The contact information for obtaining the license
+   is available from the ITU-T Secretariat.
+
+   GSM
+
+   A reference implementation was written by Carsten Bormann and Jutta
+   Degener (then at TU Berlin, Germany).  It is available at
+
+      http://www.dmn.tzi.org/software/gsm/
+
+   Although the RPE-LTP algorithm is not an ITU-T standard, there is a C
+   code implementation of the RPE-LTP algorithm available as part of the
+   ITU-T STL.  The STL implementation is an adaptation of the TU Berlin
+   version.
+
+   LPC
+
+   An implementation is available at
+
+      ftp://parcftp.xerox.com/pub/net-research/lpc.tar.Z
+
+   PCMU, PCMA
+
+   An implementation of these algorithms is available as part of the
+   ITU-T STL, described above.
+
+14. Acknowledgments
+
+   The comments and careful review of Simao Campos, Richard Cox and AVT
+   Working Group participants are gratefully acknowledged.  The GSM
+   description was adopted from the IMTC Voice over IP Forum Service
+   Interoperability Implementation Agreement (January 1997).  Fred Burg
+   and Terry Lyons helped with the G.729 description.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 42]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+15. Intellectual Property Rights Statement
+
+   The IETF takes no position regarding the validity or scope of any
+   intellectual property or other rights that might be claimed to
+   pertain to the implementation or use of the technology described in
+   this document or the extent to which any license under such rights
+   might or might not be available; neither does it represent that it
+   has made any effort to identify any such rights.  Information on the
+   IETF's procedures with respect to rights in standards-track and
+   standards-related documentation can be found in BCP-11.  Copies of
+   claims of rights made available for publication and any assurances of
+   licenses to be made available, or the result of an attempt made to
+   obtain a general license or permission for the use of such
+   proprietary rights by implementors or users of this specification can
+   be obtained from the IETF Secretariat.
+
+   The IETF invites any interested party to bring to its attention any
+   copyrights, patents or patent applications, or other proprietary
+   rights which may cover technology that may be required to practice
+   this standard.  Please address the information to the IETF Executive
+   Director.
+
+16. Authors' Addresses
+
+   Henning Schulzrinne
+   Department of Computer Science
+   Columbia University
+   1214 Amsterdam Avenue
+   New York, NY 10027
+   United States
+
+   EMail: schulzrinne@cs.columbia.edu
+
+
+   Stephen L. Casner
+   Packet Design
+   3400 Hillview Avenue, Building 3
+   Palo Alto, CA 94304
+   United States
+
+   EMail: casner@acm.org
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 43]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+17. Full Copyright Statement
+
+   Copyright (C) The Internet Society (2003).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+   Funding for the RFC Editor function is currently provided by the
+   Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 44]
+\f
diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c
new file mode 100644 (file)
index 0000000..5ab80e7
--- /dev/null
@@ -0,0 +1,452 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/arpa-inet.h>
+
+#include "rtp.h"
+
+pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size) {
+    pa_assert(c);
+    pa_assert(fd >= 0);
+
+    c->fd = fd;
+    c->sequence = (uint16_t) (rand()*rand());
+    c->timestamp = 0;
+    c->ssrc = ssrc ? ssrc : (uint32_t) (rand()*rand());
+    c->payload = (uint8_t) (payload & 127U);
+    c->frame_size = frame_size;
+
+    c->recv_buf = NULL;
+    c->recv_buf_size = 0;
+    pa_memchunk_reset(&c->memchunk);
+
+    return c;
+}
+
+#define MAX_IOVECS 16
+
+int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
+    struct iovec iov[MAX_IOVECS];
+    pa_memblock* mb[MAX_IOVECS];
+    int iov_idx = 1;
+    size_t n = 0;
+
+    pa_assert(c);
+    pa_assert(size > 0);
+    pa_assert(q);
+
+    if (pa_memblockq_get_length(q) < size)
+        return 0;
+
+    for (;;) {
+        int r;
+        pa_memchunk chunk;
+
+        pa_memchunk_reset(&chunk);
+
+        if ((r = pa_memblockq_peek(q, &chunk)) >= 0) {
+
+            size_t k = n + chunk.length > size ? size - n : chunk.length;
+
+            pa_assert(chunk.memblock);
+
+            iov[iov_idx].iov_base = pa_memblock_acquire_chunk(&chunk);
+            iov[iov_idx].iov_len = k;
+            mb[iov_idx] = chunk.memblock;
+            iov_idx ++;
+
+            n += k;
+            pa_memblockq_drop(q, k);
+        }
+
+        pa_assert(n % c->frame_size == 0);
+
+        if (r < 0 || n >= size || iov_idx >= MAX_IOVECS) {
+            uint32_t header[3];
+            struct msghdr m;
+            ssize_t k;
+            int i;
+
+            if (n > 0) {
+                header[0] = htonl(((uint32_t) 2 << 30) | ((uint32_t) c->payload << 16) | ((uint32_t) c->sequence));
+                header[1] = htonl(c->timestamp);
+                header[2] = htonl(c->ssrc);
+
+                iov[0].iov_base = (void*)header;
+                iov[0].iov_len = sizeof(header);
+
+                m.msg_name = NULL;
+                m.msg_namelen = 0;
+                m.msg_iov = iov;
+                m.msg_iovlen = (size_t) iov_idx;
+                m.msg_control = NULL;
+                m.msg_controllen = 0;
+                m.msg_flags = 0;
+
+                k = sendmsg(c->fd, &m, MSG_DONTWAIT);
+
+                for (i = 1; i < iov_idx; i++) {
+                    pa_memblock_release(mb[i]);
+                    pa_memblock_unref(mb[i]);
+                }
+
+                c->sequence++;
+            } else
+                k = 0;
+
+            c->timestamp += (unsigned) (n/c->frame_size);
+
+            if (k < 0) {
+                if (errno != EAGAIN && errno != EINTR) /* If the queue is full, just ignore it */
+                    pa_log("sendmsg() failed: %s", pa_cstrerror(errno));
+                return -1;
+            }
+
+            if (r < 0 || pa_memblockq_get_length(q) < size)
+                break;
+
+            n = 0;
+            iov_idx = 1;
+        }
+    }
+
+    return 0;
+}
+
+pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame_size) {
+    pa_assert(c);
+
+    c->fd = fd;
+    c->frame_size = frame_size;
+
+    c->recv_buf_size = 2000;
+    c->recv_buf = pa_xmalloc(c->recv_buf_size);
+    pa_memchunk_reset(&c->memchunk);
+    return c;
+}
+
+int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool, struct timeval *tstamp) {
+    int size;
+    size_t audio_length;
+    size_t metadata_length;
+    struct msghdr m;
+    struct cmsghdr *cm;
+    struct iovec iov;
+    uint32_t header;
+    unsigned cc;
+    ssize_t r;
+    uint8_t aux[1024];
+    bool found_tstamp = false;
+
+    pa_assert(c);
+    pa_assert(chunk);
+
+    pa_memchunk_reset(chunk);
+
+    if (ioctl(c->fd, FIONREAD, &size) < 0) {
+        pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (size <= 0) {
+        /* size can be 0 due to any of the following reasons:
+         *
+         * 1. Somebody sent us a perfectly valid zero-length UDP packet.
+         * 2. Somebody sent us a UDP packet with a bad CRC.
+         *
+         * It is unknown whether size can actually be less than zero.
+         *
+         * In the first case, the packet has to be read out, otherwise the
+         * kernel will tell us again and again about it, thus preventing
+         * reception of any further packets. So let's just read it out
+         * now and discard it later, when comparing the number of bytes
+         * received (0) with the number of bytes wanted (1, see below).
+         *
+         * In the second case, recvmsg() will fail, thus allowing us to
+         * return the error.
+         *
+         * Just to avoid passing zero-sized memchunks and NULL pointers to
+         * recvmsg(), let's force allocation of at least one byte by setting
+         * size to 1.
+         */
+        size = 1;
+    }
+
+    if (c->recv_buf_size < (size_t) size) {
+        do
+            c->recv_buf_size *= 2;
+        while (c->recv_buf_size < (size_t) size);
+
+        c->recv_buf = pa_xrealloc(c->recv_buf, c->recv_buf_size);
+    }
+
+    pa_assert(c->recv_buf_size >= (size_t) size);
+
+    iov.iov_base = c->recv_buf;
+    iov.iov_len = (size_t) size;
+
+    m.msg_name = NULL;
+    m.msg_namelen = 0;
+    m.msg_iov = &iov;
+    m.msg_iovlen = 1;
+    m.msg_control = aux;
+    m.msg_controllen = sizeof(aux);
+    m.msg_flags = 0;
+
+    r = recvmsg(c->fd, &m, 0);
+
+    if (r != size) {
+        if (r < 0 && errno != EAGAIN && errno != EINTR)
+            pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
+
+        goto fail;
+    }
+
+    if (size < 12) {
+        pa_log_warn("RTP packet too short.");
+        goto fail;
+    }
+
+    memcpy(&header, iov.iov_base, sizeof(uint32_t));
+    memcpy(&c->timestamp, (uint8_t*) iov.iov_base + 4, sizeof(uint32_t));
+    memcpy(&c->ssrc, (uint8_t*) iov.iov_base + 8, sizeof(uint32_t));
+
+    header = ntohl(header);
+    c->timestamp = ntohl(c->timestamp);
+    c->ssrc = ntohl(c->ssrc);
+
+    if ((header >> 30) != 2) {
+        pa_log_warn("Unsupported RTP version.");
+        goto fail;
+    }
+
+    if ((header >> 29) & 1) {
+        pa_log_warn("RTP padding not supported.");
+        goto fail;
+    }
+
+    if ((header >> 28) & 1) {
+        pa_log_warn("RTP header extensions not supported.");
+        goto fail;
+    }
+
+    cc = (header >> 24) & 0xF;
+    c->payload = (uint8_t) ((header >> 16) & 127U);
+    c->sequence = (uint16_t) (header & 0xFFFFU);
+
+    metadata_length = 12 + cc * 4;
+
+    if (metadata_length > (unsigned) size) {
+        pa_log_warn("RTP packet too short. (CSRC)");
+        goto fail;
+    }
+
+    audio_length = size - metadata_length;
+
+    if (audio_length % c->frame_size != 0) {
+        pa_log_warn("Bad RTP packet size.");
+        goto fail;
+    }
+
+    if (c->memchunk.length < (unsigned) audio_length) {
+        size_t l;
+
+        if (c->memchunk.memblock)
+            pa_memblock_unref(c->memchunk.memblock);
+
+        l = PA_MAX((size_t) audio_length, pa_mempool_block_size_max(pool));
+
+        c->memchunk.memblock = pa_memblock_new(pool, l);
+        c->memchunk.index = 0;
+        c->memchunk.length = pa_memblock_get_length(c->memchunk.memblock);
+    }
+
+    memcpy(pa_memblock_acquire_chunk(&c->memchunk), c->recv_buf + metadata_length, audio_length);
+    pa_memblock_release(c->memchunk.memblock);
+
+    chunk->memblock = pa_memblock_ref(c->memchunk.memblock);
+    chunk->index = c->memchunk.index;
+    chunk->length = audio_length;
+
+    c->memchunk.index += audio_length;
+    c->memchunk.length -= audio_length;
+
+    if (c->memchunk.length <= 0) {
+        pa_memblock_unref(c->memchunk.memblock);
+        pa_memchunk_reset(&c->memchunk);
+    }
+
+    for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm))
+        if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_TIMESTAMP) {
+            memcpy(tstamp, CMSG_DATA(cm), sizeof(struct timeval));
+            found_tstamp = true;
+            break;
+        }
+
+    if (!found_tstamp) {
+        pa_log_warn("Couldn't find SCM_TIMESTAMP data in auxiliary recvmsg() data!");
+        pa_zero(*tstamp);
+    }
+
+    return 0;
+
+fail:
+    if (chunk->memblock)
+        pa_memblock_unref(chunk->memblock);
+
+    return -1;
+}
+
+uint8_t pa_rtp_payload_from_sample_spec(const pa_sample_spec *ss) {
+    pa_assert(ss);
+
+    if (ss->format == PA_SAMPLE_ULAW && ss->rate == 8000 && ss->channels == 1)
+        return 0;
+    if (ss->format == PA_SAMPLE_ALAW && ss->rate == 8000 && ss->channels == 1)
+        return 8;
+    if (ss->format == PA_SAMPLE_S16BE && ss->rate == 44100 && ss->channels == 2)
+        return 10;
+    if (ss->format == PA_SAMPLE_S16BE && ss->rate == 44100 && ss->channels == 1)
+        return 11;
+
+    return 127;
+}
+
+pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec *ss) {
+    pa_assert(ss);
+
+    switch (payload) {
+        case 0:
+            ss->channels = 1;
+            ss->format = PA_SAMPLE_ULAW;
+            ss->rate = 8000;
+            break;
+
+        case 8:
+            ss->channels = 1;
+            ss->format = PA_SAMPLE_ALAW;
+            ss->rate = 8000;
+            break;
+
+        case 10:
+            ss->channels = 2;
+            ss->format = PA_SAMPLE_S16BE;
+            ss->rate = 44100;
+            break;
+
+        case 11:
+            ss->channels = 1;
+            ss->format = PA_SAMPLE_S16BE;
+            ss->rate = 44100;
+            break;
+
+        default:
+            return NULL;
+    }
+
+    return ss;
+}
+
+pa_sample_spec *pa_rtp_sample_spec_fixup(pa_sample_spec * ss) {
+    pa_assert(ss);
+
+    if (!pa_rtp_sample_spec_valid(ss))
+        ss->format = PA_SAMPLE_S16BE;
+
+    pa_assert(pa_rtp_sample_spec_valid(ss));
+    return ss;
+}
+
+int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) {
+    pa_assert(ss);
+
+    if (!pa_sample_spec_valid(ss))
+        return 0;
+
+    return
+        ss->format == PA_SAMPLE_U8 ||
+        ss->format == PA_SAMPLE_ALAW ||
+        ss->format == PA_SAMPLE_ULAW ||
+        ss->format == PA_SAMPLE_S16BE;
+}
+
+void pa_rtp_context_destroy(pa_rtp_context *c) {
+    pa_assert(c);
+
+    pa_assert_se(pa_close(c->fd) == 0);
+
+    if (c->memchunk.memblock)
+        pa_memblock_unref(c->memchunk.memblock);
+
+    pa_xfree(c->recv_buf);
+    c->recv_buf = NULL;
+    c->recv_buf_size = 0;
+}
+
+const char* pa_rtp_format_to_string(pa_sample_format_t f) {
+    switch (f) {
+        case PA_SAMPLE_S16BE:
+            return "L16";
+        case PA_SAMPLE_U8:
+            return "L8";
+        case PA_SAMPLE_ALAW:
+            return "PCMA";
+        case PA_SAMPLE_ULAW:
+            return "PCMU";
+        default:
+            return NULL;
+    }
+}
+
+pa_sample_format_t pa_rtp_string_to_format(const char *s) {
+    pa_assert(s);
+
+    if (pa_streq(s, "L16"))
+        return PA_SAMPLE_S16BE;
+    else if (pa_streq(s, "L8"))
+        return PA_SAMPLE_U8;
+    else if (pa_streq(s, "PCMA"))
+        return PA_SAMPLE_ALAW;
+    else if (pa_streq(s, "PCMU"))
+        return PA_SAMPLE_ULAW;
+    else
+        return PA_SAMPLE_INVALID;
+}
diff --git a/src/modules/rtp/rtp.h b/src/modules/rtp/rtp.h
new file mode 100644 (file)
index 0000000..0b877d5
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef foortphfoo
+#define foortphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_rtp_context {
+    int fd;
+    uint16_t sequence;
+    uint32_t timestamp;
+    uint32_t ssrc;
+    uint8_t payload;
+    size_t frame_size;
+
+    uint8_t *recv_buf;
+    size_t recv_buf_size;
+    pa_memchunk memchunk;
+} pa_rtp_context;
+
+pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size);
+
+/* If the memblockq doesn't have a silence memchunk set, then the caller must
+ * guarantee that the current read index doesn't point to a hole. */
+int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q);
+
+pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame_size);
+int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool, struct timeval *tstamp);
+
+void pa_rtp_context_destroy(pa_rtp_context *c);
+
+pa_sample_spec* pa_rtp_sample_spec_fixup(pa_sample_spec *ss);
+int pa_rtp_sample_spec_valid(const pa_sample_spec *ss);
+
+uint8_t pa_rtp_payload_from_sample_spec(const pa_sample_spec *ss);
+pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec *ss);
+
+const char* pa_rtp_format_to_string(pa_sample_format_t f);
+pa_sample_format_t pa_rtp_string_to_format(const char *s);
+
+#endif
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
new file mode 100644 (file)
index 0000000..34210f9
--- /dev/null
@@ -0,0 +1,607 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/ioline.h>
+#include <pulsecore/arpa-inet.h>
+#include <pulsecore/random.h>
+
+#include "rtsp_client.h"
+
+struct pa_rtsp_client {
+    pa_mainloop_api *mainloop;
+    char *hostname;
+    uint16_t port;
+
+    pa_socket_client *sc;
+    pa_ioline *ioline;
+
+    pa_rtsp_cb_t callback;
+
+    void *userdata;
+    const char *useragent;
+
+    pa_rtsp_state_t state;
+    pa_rtsp_status_t status;
+    uint8_t waiting;
+
+    pa_headerlist* headers;
+    char *last_header;
+    pa_strbuf *header_buffer;
+    pa_headerlist* response_headers;
+
+    char *localip;
+    char *url;
+    uint16_t rtp_port;
+    uint32_t cseq;
+    char *session;
+    char *transport;
+};
+
+pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char *hostname, uint16_t port, const char *useragent) {
+    pa_rtsp_client *c;
+
+    pa_assert(mainloop);
+    pa_assert(hostname);
+    pa_assert(port > 0);
+
+    c = pa_xnew0(pa_rtsp_client, 1);
+    c->mainloop = mainloop;
+    c->hostname = pa_xstrdup(hostname);
+    c->port = port;
+    c->headers = pa_headerlist_new();
+
+    if (useragent)
+        c->useragent = useragent;
+    else
+        c->useragent = "PulseAudio RTSP Client";
+
+    return c;
+}
+
+void pa_rtsp_client_free(pa_rtsp_client *c) {
+    pa_assert(c);
+
+    if (c->sc)
+        pa_socket_client_unref(c->sc);
+
+    pa_rtsp_disconnect(c);
+
+    pa_xfree(c->hostname);
+    pa_xfree(c->url);
+    pa_xfree(c->localip);
+    pa_xfree(c->session);
+    pa_xfree(c->transport);
+    pa_xfree(c->last_header);
+    if (c->header_buffer)
+        pa_strbuf_free(c->header_buffer);
+    if (c->response_headers)
+        pa_headerlist_free(c->response_headers);
+    pa_headerlist_free(c->headers);
+
+    pa_xfree(c);
+}
+
+static void headers_read(pa_rtsp_client *c) {
+    char delimiters[] = ";";
+    char* token;
+
+    pa_assert(c);
+    pa_assert(c->response_headers);
+    pa_assert(c->callback);
+
+    /* Deal with a SETUP response */
+    if (STATE_SETUP == c->state) {
+        const char* token_state = NULL;
+        const char* pc = NULL;
+        c->session = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Session"));
+        c->transport = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Transport"));
+
+        if (!c->session || !c->transport) {
+            pa_log("Invalid SETUP response.");
+            return;
+        }
+
+        /* Now parse out the server port component of the response. */
+        while ((token = pa_split(c->transport, delimiters, &token_state))) {
+            if ((pc = strchr(token, '='))) {
+                if (0 == strncmp(token, "server_port", 11)) {
+                    uint32_t p;
+
+                    if (pa_atou(pc + 1, &p) < 0 || p <= 0 || p > 0xffff) {
+                        pa_log("Invalid SETUP response (invalid server_port).");
+                        pa_xfree(token);
+                        return;
+                    }
+
+                    c->rtp_port = p;
+                    pa_xfree(token);
+                    break;
+                }
+            }
+            pa_xfree(token);
+        }
+        if (0 == c->rtp_port) {
+            /* Error no server_port in response */
+            pa_log("Invalid SETUP response (no port number).");
+            return;
+        }
+    }
+
+    /* Call our callback */
+    c->callback(c, c->state, c->status, c->response_headers, c->userdata);
+}
+
+static void line_callback(pa_ioline *line, const char *s, void *userdata) {
+    pa_rtsp_client *c = userdata;
+    char *delimpos;
+    char *s2, *s2p;
+
+    pa_assert(line);
+    pa_assert(c);
+    pa_assert(c->callback);
+
+    if (!s) {
+        /* Keep the ioline/iochannel open as they will be freed automatically */
+        c->ioline = NULL;
+        c->callback(c, STATE_DISCONNECTED, STATUS_NO_RESPONSE, NULL, c->userdata);
+        return;
+    }
+
+    s2 = pa_xstrdup(s);
+    /* Trim trailing carriage returns */
+    s2p = s2 + strlen(s2) - 1;
+    while (s2p >= s2 && '\r' == *s2p) {
+        *s2p = '\0';
+        s2p -= 1;
+    }
+
+    if (c->waiting && pa_streq(s2, "RTSP/1.0 200 OK")) {
+        if (c->response_headers)
+            pa_headerlist_free(c->response_headers);
+        c->response_headers = pa_headerlist_new();
+
+        c->status = STATUS_OK;
+        c->waiting = 0;
+        goto exit;
+    } else if (c->waiting && pa_streq(s2, "RTSP/1.0 401 Unauthorized")) {
+        if (c->response_headers)
+            pa_headerlist_free(c->response_headers);
+        c->response_headers = pa_headerlist_new();
+
+        c->status = STATUS_UNAUTHORIZED;
+        c->waiting = 0;
+        goto exit;
+    } else if (c->waiting) {
+        pa_log_warn("Unexpected/Unhandled response: %s", s2);
+
+        if (pa_streq(s2, "RTSP/1.0 400 Bad Request"))
+            c->status = STATUS_BAD_REQUEST;
+        else if (pa_streq(s2, "RTSP/1.0 500 Internal Server Error"))
+            c->status = STATUS_INTERNAL_ERROR;
+        else
+            c->status = STATUS_NO_RESPONSE;
+        goto exit;
+    }
+
+    if (!strlen(s2)) {
+        /* End of headers */
+        /* We will have a header left from our looping iteration, so add it in :) */
+        if (c->last_header) {
+            char *tmp = pa_strbuf_to_string_free(c->header_buffer);
+            /* This is not a continuation header so let's dump it into our proplist */
+            pa_headerlist_puts(c->response_headers, c->last_header, tmp);
+            pa_xfree(tmp);
+            pa_xfree(c->last_header);
+            c->last_header = NULL;
+            c->header_buffer = NULL;
+        }
+
+        pa_log_debug("Full response received. Dispatching");
+        headers_read(c);
+        goto exit;
+    }
+
+    /* Read and parse a header (we know it's not empty) */
+    /* TODO: Move header reading into the headerlist. */
+
+    /* If the first character is a space, it's a continuation header */
+    if (c->last_header && ' ' == s2[0]) {
+        pa_assert(c->header_buffer);
+
+        /* Add this line to the buffer (sans the space) */
+        pa_strbuf_puts(c->header_buffer, &(s2[1]));
+        goto exit;
+    }
+
+    if (c->last_header) {
+        char *tmp = pa_strbuf_to_string_free(c->header_buffer);
+        /* This is not a continuation header so let's dump the full
+          header/value into our proplist */
+        pa_headerlist_puts(c->response_headers, c->last_header, tmp);
+        pa_xfree(tmp);
+        pa_xfree(c->last_header);
+        c->last_header = NULL;
+        c->header_buffer = NULL;
+    }
+
+    delimpos = strstr(s2, ":");
+    if (!delimpos) {
+        pa_log_warn("Unexpected response when expecting header: %s", s);
+        goto exit;
+    }
+
+    pa_assert(!c->header_buffer);
+    pa_assert(!c->last_header);
+
+    c->header_buffer = pa_strbuf_new();
+    if (strlen(delimpos) > 1) {
+        /* Cut our line off so we can copy the header name out */
+        *delimpos++ = '\0';
+
+        /* Trim the front of any spaces */
+        while (' ' == *delimpos)
+            ++delimpos;
+
+        pa_strbuf_puts(c->header_buffer, delimpos);
+    } else {
+        /* Cut our line off so we can copy the header name out */
+        *delimpos = '\0';
+    }
+
+    /* Save the header name */
+    c->last_header = pa_xstrdup(s2);
+
+  exit:
+    pa_xfree(s2);
+}
+
+static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
+    pa_rtsp_client *c = userdata;
+    union {
+        struct sockaddr sa;
+        struct sockaddr_in in;
+        struct sockaddr_in6 in6;
+    } sa;
+    socklen_t sa_len = sizeof(sa);
+
+    pa_assert(sc);
+    pa_assert(c);
+    pa_assert(STATE_CONNECT == c->state);
+    pa_assert(c->sc == sc);
+    pa_socket_client_unref(c->sc);
+    c->sc = NULL;
+
+    if (!io) {
+        pa_log("Connection failed: %s", pa_cstrerror(errno));
+        return;
+    }
+    pa_assert(!c->ioline);
+
+    c->ioline = pa_ioline_new(io);
+    pa_ioline_set_callback(c->ioline, line_callback, c);
+
+    /* Get the local IP address for use externally */
+    if (0 == getsockname(pa_iochannel_get_recv_fd(io), &sa.sa, &sa_len)) {
+        char buf[INET6_ADDRSTRLEN];
+        const char *res = NULL;
+
+        if (AF_INET == sa.sa.sa_family) {
+            if ((res = inet_ntop(sa.sa.sa_family, &sa.in.sin_addr, buf, sizeof(buf)))) {
+                c->localip = pa_xstrdup(res);
+            }
+        } else if (AF_INET6 == sa.sa.sa_family) {
+            if ((res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf)))) {
+                c->localip = pa_xstrdup(res);
+            }
+        }
+    }
+    pa_log_debug("Established RTSP connection from local ip %s", c->localip);
+
+    if (c->callback)
+        c->callback(c, c->state, STATUS_OK, NULL, c->userdata);
+}
+
+int pa_rtsp_connect(pa_rtsp_client *c) {
+    pa_assert(c);
+    pa_assert(!c->sc);
+
+    pa_xfree(c->session);
+    c->session = NULL;
+
+    pa_log_debug("Attempting to connect to server '%s:%d'", c->hostname, c->port);
+    if (!(c->sc = pa_socket_client_new_string(c->mainloop, true, c->hostname, c->port))) {
+        pa_log("failed to connect to server '%s:%d'", c->hostname, c->port);
+        return -1;
+    }
+
+    pa_socket_client_set_callback(c->sc, on_connection, c);
+    c->waiting = 1;
+    c->state = STATE_CONNECT;
+    c->status = STATUS_NO_RESPONSE;
+    return 0;
+}
+
+void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata) {
+    pa_assert(c);
+
+    c->callback = callback;
+    c->userdata = userdata;
+}
+
+void pa_rtsp_disconnect(pa_rtsp_client *c) {
+    pa_assert(c);
+
+    if (c->ioline) {
+        pa_ioline_close(c->ioline);
+        pa_ioline_unref(c->ioline);
+    }
+    c->ioline = NULL;
+}
+
+const char* pa_rtsp_localip(pa_rtsp_client *c) {
+    pa_assert(c);
+
+    return c->localip;
+}
+
+uint32_t pa_rtsp_serverport(pa_rtsp_client *c) {
+    pa_assert(c);
+
+    return c->rtp_port;
+}
+
+bool pa_rtsp_exec_ready(const pa_rtsp_client *c) {
+    pa_assert(c);
+
+    return c->url != NULL && c->ioline != NULL;
+}
+
+void pa_rtsp_set_url(pa_rtsp_client *c, const char *url) {
+    pa_assert(c);
+
+    c->url = pa_xstrdup(url);
+}
+
+bool pa_rtsp_has_header(pa_rtsp_client *c, const char *key) {
+    pa_assert(c);
+    pa_assert(key);
+
+    return pa_headerlist_contains(c->headers, key);
+}
+
+void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value) {
+    pa_assert(c);
+    pa_assert(key);
+    pa_assert(value);
+
+    pa_headerlist_puts(c->headers, key, value);
+}
+
+const char* pa_rtsp_get_header(pa_rtsp_client *c, const char *key) {
+    pa_assert(c);
+    pa_assert(key);
+
+    return pa_headerlist_gets(c->headers, key);
+}
+
+void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key) {
+    pa_assert(c);
+    pa_assert(key);
+
+    pa_headerlist_remove(c->headers, key);
+}
+
+static int rtsp_exec(pa_rtsp_client *c, const char *cmd,
+                        const char *content_type, const char *content,
+                        int expect_response,
+                        pa_headerlist *headers) {
+    pa_strbuf *buf;
+    char *hdrs;
+
+    pa_assert(c);
+    pa_assert(c->url);
+    pa_assert(cmd);
+    pa_assert(c->ioline);
+
+    pa_log_debug("Sending command: %s", cmd);
+
+    buf = pa_strbuf_new();
+    pa_strbuf_printf(buf, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, c->url, ++c->cseq);
+    if (c->session)
+        pa_strbuf_printf(buf, "Session: %s\r\n", c->session);
+
+    /* Add the headers */
+    if (headers) {
+        hdrs = pa_headerlist_to_string(headers);
+        pa_strbuf_puts(buf, hdrs);
+        pa_xfree(hdrs);
+    }
+
+    if (content_type && content) {
+        pa_strbuf_printf(buf, "Content-Type: %s\r\nContent-Length: %d\r\n",
+          content_type, (int)strlen(content));
+    }
+
+    pa_strbuf_printf(buf, "User-Agent: %s\r\n", c->useragent);
+
+    if (c->headers) {
+        hdrs = pa_headerlist_to_string(c->headers);
+        pa_strbuf_puts(buf, hdrs);
+        pa_xfree(hdrs);
+    }
+
+    pa_strbuf_puts(buf, "\r\n");
+
+    if (content_type && content) {
+        pa_strbuf_puts(buf, content);
+    }
+
+    /* Our packet is created... now we can send it :) */
+    hdrs = pa_strbuf_to_string_free(buf);
+    /*pa_log_debug("Submitting request:");
+    pa_log_debug(hdrs);*/
+    pa_ioline_puts(c->ioline, hdrs);
+    pa_xfree(hdrs);
+    /* The command is sent we can configure the rtsp client structure to handle a new answer */
+    c->waiting = 1;
+    return 0;
+}
+
+int pa_rtsp_options(pa_rtsp_client *c) {
+    char *url;
+    int rv;
+
+    pa_assert(c);
+
+    url = c->url;
+    c->state = STATE_OPTIONS;
+
+    c->url = (char *)"*";
+    rv = rtsp_exec(c, "OPTIONS", NULL, NULL, 0, NULL);
+
+    c->url = url;
+    return rv;
+}
+
+int pa_rtsp_announce(pa_rtsp_client *c, const char *sdp) {
+    int rv;
+
+    pa_assert(c);
+
+    if (!sdp)
+        return -1;
+
+    c->state = STATE_ANNOUNCE;
+    rv = rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
+
+    return rv;
+}
+
+int pa_rtsp_setup(pa_rtsp_client *c, const char *transport) {
+    pa_headerlist *headers;
+    int rv;
+
+    pa_assert(c);
+
+    headers = pa_headerlist_new();
+    if (!transport)
+        pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
+    else
+        pa_headerlist_puts(headers, "Transport", transport);
+
+    c->state = STATE_SETUP;
+    rv = rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
+
+    pa_headerlist_free(headers);
+    return rv;
+}
+
+int pa_rtsp_record(pa_rtsp_client *c, uint16_t *seq, uint32_t *rtptime) {
+    pa_headerlist *headers;
+    char *info;
+    int rv;
+
+    pa_assert(c);
+
+    if (!c->session) {
+        /* No session in progress */
+        return -1;
+    }
+
+    pa_random(seq, sizeof(*seq));
+    pa_random(rtptime, sizeof(*rtptime));
+
+    headers = pa_headerlist_new();
+    pa_headerlist_puts(headers, "Range", "npt=0-");
+    info = pa_sprintf_malloc("seq=%u;rtptime=%u", *seq, *rtptime);
+    pa_headerlist_puts(headers, "RTP-Info", info);
+    pa_xfree(info);
+
+    c->state = STATE_RECORD;
+    rv = rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
+
+    pa_headerlist_free(headers);
+    return rv;
+}
+
+int pa_rtsp_setparameter(pa_rtsp_client *c, const char *param) {
+    int rv;
+
+    pa_assert(c);
+
+    if (!param)
+        return -1;
+
+    c->state = STATE_SET_PARAMETER;
+    rv = rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
+
+    return rv;
+}
+
+int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime) {
+    pa_headerlist* headers;
+    char *info;
+    int rv;
+
+    pa_assert(c);
+
+    headers = pa_headerlist_new();
+    info = pa_sprintf_malloc("seq=%u;rtptime=%u", seq, rtptime);
+    pa_headerlist_puts(headers, "RTP-Info", info);
+    pa_xfree(info);
+
+    c->state = STATE_FLUSH;
+    rv = rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
+
+    pa_headerlist_free(headers);
+    return rv;
+}
+
+int pa_rtsp_teardown(pa_rtsp_client *c) {
+    int rv;
+
+    pa_assert(c);
+
+    c->state = STATE_TEARDOWN;
+    rv = rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
+
+    return rv;
+}
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
new file mode 100644 (file)
index 0000000..4e031d8
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef foortspclienthfoo
+#define foortspclienthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+#include <pulsecore/socket-client.h>
+#include <pulse/mainloop-api.h>
+
+#include "headerlist.h"
+
+typedef struct pa_rtsp_client pa_rtsp_client;
+
+typedef enum pa_rtsp_state {
+  STATE_CONNECT,
+  STATE_OPTIONS,
+  STATE_ANNOUNCE,
+  STATE_SETUP,
+  STATE_RECORD,
+  STATE_SET_PARAMETER,
+  STATE_FLUSH,
+  STATE_TEARDOWN,
+  STATE_DISCONNECTED
+} pa_rtsp_state_t;
+
+typedef enum pa_rtsp_status {
+  STATUS_OK             = 200,
+  STATUS_BAD_REQUEST    = 400,
+  STATUS_UNAUTHORIZED   = 401,
+  STATUS_NO_RESPONSE    = 444,
+  STATUS_INTERNAL_ERROR = 500
+} pa_rtsp_status_t;
+
+typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state_t state, pa_rtsp_status_t code, pa_headerlist *headers, void *userdata);
+
+pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char *hostname, uint16_t port, const char *useragent);
+void pa_rtsp_client_free(pa_rtsp_client *c);
+
+int pa_rtsp_connect(pa_rtsp_client *c);
+void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata);
+void pa_rtsp_disconnect(pa_rtsp_client *c);
+
+const char* pa_rtsp_localip(pa_rtsp_client *c);
+uint32_t pa_rtsp_serverport(pa_rtsp_client *c);
+bool pa_rtsp_exec_ready(const pa_rtsp_client *c);
+
+void pa_rtsp_set_url(pa_rtsp_client *c, const char *url);
+
+bool pa_rtsp_has_header(pa_rtsp_client *c, const char *key);
+void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value);
+const char* pa_rtsp_get_header(pa_rtsp_client *c, const char *key);
+void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key);
+
+int pa_rtsp_options(pa_rtsp_client *c);
+int pa_rtsp_announce(pa_rtsp_client *c, const char *sdp);
+int pa_rtsp_setup(pa_rtsp_client *c, const char *transport);
+int pa_rtsp_record(pa_rtsp_client *c, uint16_t *seq, uint32_t *rtptime);
+int pa_rtsp_setparameter(pa_rtsp_client *c, const char *param);
+int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime);
+int pa_rtsp_teardown(pa_rtsp_client *c);
+
+#endif
diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c
new file mode 100644 (file)
index 0000000..7fb1a38
--- /dev/null
@@ -0,0 +1,235 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/arpa-inet.h>
+
+#include "sap.h"
+#include "sdp.h"
+
+#define MIME_TYPE "application/sdp"
+
+pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data) {
+    pa_assert(c);
+    pa_assert(fd >= 0);
+    pa_assert(sdp_data);
+
+    c->fd = fd;
+    c->sdp_data = sdp_data;
+    c->msg_id_hash = (uint16_t) (rand()*rand());
+
+    return c;
+}
+
+void pa_sap_context_destroy(pa_sap_context *c) {
+    pa_assert(c);
+
+    pa_close(c->fd);
+    pa_xfree(c->sdp_data);
+}
+
+int pa_sap_send(pa_sap_context *c, bool goodbye) {
+    uint32_t header;
+    struct sockaddr_storage sa_buf;
+    struct sockaddr *sa = (struct sockaddr*) &sa_buf;
+    socklen_t salen = sizeof(sa_buf);
+    struct iovec iov[4];
+    struct msghdr m;
+    ssize_t k;
+
+    if (getsockname(c->fd, sa, &salen) < 0) {
+        pa_log("getsockname() failed: %s\n", pa_cstrerror(errno));
+        return -1;
+    }
+
+#ifdef HAVE_IPV6
+    pa_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+#else
+    pa_assert(sa->sa_family == AF_INET);
+#endif
+
+    header = htonl(((uint32_t) 1 << 29) |
+#ifdef HAVE_IPV6
+                   (sa->sa_family == AF_INET6 ? (uint32_t) 1 << 28 : 0) |
+#endif
+                   (goodbye ? (uint32_t) 1 << 26 : 0) |
+                   (c->msg_id_hash));
+
+    iov[0].iov_base = &header;
+    iov[0].iov_len = sizeof(header);
+
+    if (sa->sa_family == AF_INET) {
+        iov[1].iov_base = (void*) &((struct sockaddr_in*) sa)->sin_addr;
+        iov[1].iov_len = 4U;
+#ifdef HAVE_IPV6
+    } else {
+        iov[1].iov_base = (void*) &((struct sockaddr_in6*) sa)->sin6_addr;
+        iov[1].iov_len = 16U;
+#endif
+    }
+
+    iov[2].iov_base = (char*) MIME_TYPE;
+    iov[2].iov_len = sizeof(MIME_TYPE);
+
+    iov[3].iov_base = c->sdp_data;
+    iov[3].iov_len = strlen(c->sdp_data);
+
+    m.msg_name = NULL;
+    m.msg_namelen = 0;
+    m.msg_iov = iov;
+    m.msg_iovlen = 4;
+    m.msg_control = NULL;
+    m.msg_controllen = 0;
+    m.msg_flags = 0;
+
+    if ((k = sendmsg(c->fd, &m, MSG_DONTWAIT)) < 0)
+        pa_log_warn("sendmsg() failed: %s\n", pa_cstrerror(errno));
+
+    return (int) k;
+}
+
+pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) {
+    pa_assert(c);
+    pa_assert(fd >= 0);
+
+    c->fd = fd;
+    c->sdp_data = NULL;
+    return c;
+}
+
+int pa_sap_recv(pa_sap_context *c, bool *goodbye) {
+    struct msghdr m;
+    struct iovec iov;
+    int size;
+    char *buf = NULL, *e;
+    uint32_t header;
+    unsigned six, ac, k;
+    ssize_t r;
+
+    pa_assert(c);
+    pa_assert(goodbye);
+
+    if (ioctl(c->fd, FIONREAD, &size) < 0) {
+        pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    buf = pa_xnew(char, (unsigned) size+1);
+    buf[size] = 0;
+
+    iov.iov_base = buf;
+    iov.iov_len = (size_t) size;
+
+    m.msg_name = NULL;
+    m.msg_namelen = 0;
+    m.msg_iov = &iov;
+    m.msg_iovlen = 1;
+    m.msg_control = NULL;
+    m.msg_controllen = 0;
+    m.msg_flags = 0;
+
+    if ((r = recvmsg(c->fd, &m, 0)) != size) {
+        pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
+        goto fail;
+    }
+
+    if (size < 4) {
+        pa_log_warn("SAP packet too short.");
+        goto fail;
+    }
+
+    memcpy(&header, buf, sizeof(uint32_t));
+    header = ntohl(header);
+
+    if (header >> 29 != 1) {
+        pa_log_warn("Unsupported SAP version.");
+        goto fail;
+    }
+
+    if ((header >> 25) & 1) {
+        pa_log_warn("Encrypted SAP not supported.");
+        goto fail;
+    }
+
+    if ((header >> 24) & 1) {
+        pa_log_warn("Compressed SAP not supported.");
+        goto fail;
+    }
+
+    six = (header >> 28) & 1U;
+    ac = (header >> 16) & 0xFFU;
+
+    k = 4 + (six ? 16U : 4U) + ac*4U;
+    if ((unsigned) size < k) {
+        pa_log_warn("SAP packet too short (AD).");
+        goto fail;
+    }
+
+    e = buf + k;
+    size -= (int) k;
+
+    if ((unsigned) size >= sizeof(MIME_TYPE) && pa_streq(e, MIME_TYPE)) {
+        e += sizeof(MIME_TYPE);
+        size -= (int) sizeof(MIME_TYPE);
+    } else if ((unsigned) size < sizeof(PA_SDP_HEADER)-1 || strncmp(e, PA_SDP_HEADER, sizeof(PA_SDP_HEADER)-1)) {
+        pa_log_warn("Invalid SDP header.");
+        goto fail;
+    }
+
+    if (c->sdp_data)
+        pa_xfree(c->sdp_data);
+
+    c->sdp_data = pa_xstrndup(e, (unsigned) size);
+    pa_xfree(buf);
+
+    *goodbye = !!((header >> 26) & 1);
+
+    return 0;
+
+fail:
+    pa_xfree(buf);
+
+    return -1;
+}
diff --git a/src/modules/rtp/sap.h b/src/modules/rtp/sap.h
new file mode 100644 (file)
index 0000000..becb4ec
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef foosaphfoo
+#define foosaphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_sap_context {
+    int fd;
+    char *sdp_data;
+
+    uint16_t msg_id_hash;
+} pa_sap_context;
+
+pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data);
+void pa_sap_context_destroy(pa_sap_context *c);
+
+int pa_sap_send(pa_sap_context *c, bool goodbye);
+
+pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd);
+int pa_sap_recv(pa_sap_context *c, bool *goodbye);
+
+#endif
diff --git a/src/modules/rtp/sdp.c b/src/modules/rtp/sdp.c
new file mode 100644 (file)
index 0000000..14953cf
--- /dev/null
@@ -0,0 +1,265 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <time.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/arpa-inet.h>
+
+#include "sdp.h"
+#include "rtp.h"
+
+char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss) {
+    uint32_t ntp;
+    char buf_src[64], buf_dst[64], un[64];
+    const char *u, *f;
+
+    pa_assert(src);
+    pa_assert(dst);
+
+#ifdef HAVE_IPV6
+    pa_assert(af == AF_INET || af == AF_INET6);
+#else
+    pa_assert(af == AF_INET);
+#endif
+
+    pa_assert_se(f = pa_rtp_format_to_string(ss->format));
+
+    if (!(u = pa_get_user_name(un, sizeof(un))))
+        u = "-";
+
+    ntp = (uint32_t) time(NULL) + 2208988800U;
+
+    pa_assert_se(inet_ntop(af, src, buf_src, sizeof(buf_src)));
+    pa_assert_se(inet_ntop(af, dst, buf_dst, sizeof(buf_dst)));
+
+    return pa_sprintf_malloc(
+            PA_SDP_HEADER
+            "o=%s %lu 0 IN %s %s\n"
+            "s=%s\n"
+            "c=IN %s %s\n"
+            "t=%lu 0\n"
+            "a=recvonly\n"
+            "m=audio %u RTP/AVP %i\n"
+            "a=rtpmap:%i %s/%u/%u\n"
+            "a=type:broadcast\n",
+            u, (unsigned long) ntp, af == AF_INET ? "IP4" : "IP6", buf_src,
+            name,
+            af == AF_INET ? "IP4" : "IP6", buf_dst,
+            (unsigned long) ntp,
+            port, payload,
+            payload, f, ss->rate, ss->channels);
+}
+
+static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) {
+    unsigned rate, channels;
+    pa_assert(ss);
+    pa_assert(c);
+
+    if (pa_startswith(c, "L16/")) {
+        ss->format = PA_SAMPLE_S16BE;
+        c += 4;
+    } else if (pa_startswith(c, "L8/")) {
+        ss->format = PA_SAMPLE_U8;
+        c += 3;
+    } else if (pa_startswith(c, "PCMA/")) {
+        ss->format = PA_SAMPLE_ALAW;
+        c += 5;
+    } else if (pa_startswith(c, "PCMU/")) {
+        ss->format = PA_SAMPLE_ULAW;
+        c += 5;
+    } else
+        return NULL;
+
+    if (sscanf(c, "%u/%u", &rate, &channels) == 2) {
+        ss->rate = (uint32_t) rate;
+        ss->channels = (uint8_t) channels;
+    } else if (sscanf(c, "%u", &rate) == 2) {
+        ss->rate = (uint32_t) rate;
+        ss->channels = 1;
+    } else
+        return NULL;
+
+    if (!pa_sample_spec_valid(ss))
+        return NULL;
+
+    return ss;
+}
+
+pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
+    uint16_t port = 0;
+    bool ss_valid = false;
+
+    pa_assert(t);
+    pa_assert(i);
+
+    i->origin = i->session_name = NULL;
+    i->salen = 0;
+    i->payload = 255;
+
+    if (!pa_startswith(t, PA_SDP_HEADER)) {
+        pa_log("Failed to parse SDP data: invalid header.");
+        goto fail;
+    }
+
+    t += sizeof(PA_SDP_HEADER)-1;
+
+    while (*t) {
+        size_t l;
+
+        l = strcspn(t, "\n");
+
+        if (l <= 2) {
+            pa_log("Failed to parse SDP data: line too short: >%s<.", t);
+            goto fail;
+        }
+
+        if (pa_startswith(t, "o="))
+            i->origin = pa_xstrndup(t+2, l-2);
+        else if (pa_startswith(t, "s="))
+            i->session_name = pa_xstrndup(t+2, l-2);
+        else if (pa_startswith(t, "c=IN IP4 ")) {
+            char a[64];
+            size_t k;
+
+            k = l-8 > sizeof(a) ? sizeof(a) : l-8;
+
+            pa_strlcpy(a, t+9, k);
+            a[strcspn(a, "/")] = 0;
+
+            if (inet_pton(AF_INET, a, &((struct sockaddr_in*) &i->sa)->sin_addr) <= 0) {
+                pa_log("Failed to parse SDP data: bad address: >%s<.", a);
+                goto fail;
+            }
+
+            ((struct sockaddr_in*) &i->sa)->sin_family = AF_INET;
+            ((struct sockaddr_in*) &i->sa)->sin_port = 0;
+            i->salen = sizeof(struct sockaddr_in);
+#ifdef HAVE_IPV6
+        } else if (pa_startswith(t, "c=IN IP6 ")) {
+            char a[64];
+            size_t k;
+
+            k = l-8 > sizeof(a) ? sizeof(a) : l-8;
+
+            pa_strlcpy(a, t+9, k);
+            a[strcspn(a, "/")] = 0;
+
+            if (inet_pton(AF_INET6, a, &((struct sockaddr_in6*) &i->sa)->sin6_addr) <= 0) {
+                pa_log("Failed to parse SDP data: bad address: >%s<.", a);
+                goto fail;
+            }
+
+            ((struct sockaddr_in6*) &i->sa)->sin6_family = AF_INET6;
+            ((struct sockaddr_in6*) &i->sa)->sin6_port = 0;
+            i->salen = sizeof(struct sockaddr_in6);
+#endif
+        } else if (pa_startswith(t, "m=audio ")) {
+
+            if (i->payload > 127) {
+                int _port, _payload;
+
+                if (sscanf(t+8, "%i RTP/AVP %i", &_port, &_payload) == 2) {
+
+                    if (_port <= 0 || _port > 0xFFFF) {
+                        pa_log("Failed to parse SDP data: invalid port %i.", _port);
+                        goto fail;
+                    }
+
+                    if (_payload < 0 || _payload > 127) {
+                        pa_log("Failed to parse SDP data: invalid payload %i.", _payload);
+                        goto fail;
+                    }
+
+                    port = (uint16_t) _port;
+                    i->payload = (uint8_t) _payload;
+
+                    if (pa_rtp_sample_spec_from_payload(i->payload, &i->sample_spec))
+                        ss_valid = true;
+                }
+            }
+        } else if (pa_startswith(t, "a=rtpmap:")) {
+
+            if (i->payload <= 127) {
+                char c[64];
+                int _payload;
+                int len;
+
+                if (sscanf(t + 9, "%i %n", &_payload, &len) == 1) {
+                    if (_payload < 0 || _payload > 127) {
+                        pa_log("Failed to parse SDP data: invalid payload %i.", _payload);
+                        goto fail;
+                    }
+                    if (_payload == i->payload) {
+                        strncpy(c, t + 9 + len, 63);
+                        c[63] = 0;
+                        c[strcspn(c, "\n")] = 0;
+
+                        if (parse_sdp_sample_spec(&i->sample_spec, c))
+                            ss_valid = true;
+                    }
+                }
+            }
+        }
+
+        t += l;
+
+        if (*t == '\n')
+            t++;
+    }
+
+    if (!i->origin || (!is_goodbye && (!i->salen || i->payload > 127 || !ss_valid || port == 0))) {
+        pa_log("Failed to parse SDP data: missing data.");
+        goto fail;
+    }
+
+    if (((struct sockaddr*) &i->sa)->sa_family == AF_INET)
+        ((struct sockaddr_in*) &i->sa)->sin_port = htons(port);
+    else
+        ((struct sockaddr_in6*) &i->sa)->sin6_port = htons(port);
+
+    return i;
+
+fail:
+    pa_xfree(i->origin);
+    pa_xfree(i->session_name);
+
+    return NULL;
+}
+
+void pa_sdp_info_destroy(pa_sdp_info *i) {
+    pa_assert(i);
+
+    pa_xfree(i->origin);
+    pa_xfree(i->session_name);
+}
diff --git a/src/modules/rtp/sdp.h b/src/modules/rtp/sdp.h
new file mode 100644 (file)
index 0000000..5e9b8fe
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef foosdphfoo
+#define foosdphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <pulse/sample.h>
+
+#define PA_SDP_HEADER "v=0\n"
+
+typedef struct pa_sdp_info {
+    char *origin;
+    char *session_name;
+
+    struct sockaddr_storage sa;
+    socklen_t salen;
+
+    pa_sample_spec sample_spec;
+    uint8_t payload;
+} pa_sdp_info;
+
+char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss);
+
+pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *info, int is_goodbye);
+
+void pa_sdp_info_destroy(pa_sdp_info *i);
+
+#endif
diff --git a/src/modules/stream-interaction.c b/src/modules/stream-interaction.c
new file mode 100644 (file)
index 0000000..4184786
--- /dev/null
@@ -0,0 +1,555 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/hook-list.h>
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/modargs.h>
+
+#include "stream-interaction.h"
+
+struct group {
+    char *name;
+    pa_idxset *trigger_roles;
+    pa_idxset *interaction_roles;
+    pa_hashmap *interaction_state;
+    pa_volume_t volume;
+};
+
+struct userdata {
+    pa_core *core;
+    uint32_t n_groups;
+    struct group **groups;
+    bool global:1;
+    bool duck:1;
+    pa_hook_slot
+        *sink_input_put_slot,
+        *sink_input_unlink_slot,
+        *sink_input_move_start_slot,
+        *sink_input_move_finish_slot,
+        *sink_input_state_changed_slot,
+        *sink_input_mute_changed_slot,
+        *sink_input_proplist_changed_slot;
+};
+
+static const char *get_trigger_role(struct userdata *u, pa_sink_input *i, struct group *g) {
+    const char *role, *trigger_role;
+    uint32_t role_idx;
+
+    if (!(role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)))
+        role = "no_role";
+
+    if (g == NULL) {
+        /* get it from all groups */
+        uint32_t j;
+        for (j = 0; j < u->n_groups; j++) {
+            PA_IDXSET_FOREACH(trigger_role, u->groups[j]->trigger_roles, role_idx) {
+                if (pa_streq(role, trigger_role))
+                    return trigger_role;
+            }
+        }
+    } else {
+        PA_IDXSET_FOREACH(trigger_role, g->trigger_roles, role_idx) {
+            if (pa_streq(role, trigger_role))
+                return trigger_role;
+        }
+    }
+
+    return NULL;
+}
+
+static const char *find_trigger_stream(struct userdata *u, pa_sink *s, pa_sink_input *ignore, struct group *g) {
+    pa_sink_input *j;
+    uint32_t idx;
+    const char *trigger_role;
+
+    pa_assert(u);
+    pa_sink_assert_ref(s);
+
+    for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
+
+        if (j == ignore)
+            continue;
+
+        trigger_role = get_trigger_role(u, j, g);
+        if (trigger_role && !j->muted && pa_sink_input_get_state(j) != PA_SINK_INPUT_CORKED)
+            return trigger_role;
+    }
+
+    return NULL;
+}
+
+static const char *find_global_trigger_stream(struct userdata *u, pa_sink *s, pa_sink_input *ignore, struct group *g) {
+    const char *trigger_role = NULL;
+
+    pa_assert(u);
+
+    if (u->global) {
+        uint32_t idx;
+        PA_IDXSET_FOREACH(s, u->core->sinks, idx)
+            if ((trigger_role = find_trigger_stream(u, s, ignore, g)))
+                break;
+    } else
+        trigger_role = find_trigger_stream(u, s, ignore, g);
+
+    return trigger_role;
+}
+
+static void cork_or_duck(struct userdata *u, pa_sink_input *i, const char *interaction_role,  const char *trigger_role, bool interaction_applied, struct group *g) {
+
+    if (u->duck && !interaction_applied) {
+        pa_cvolume vol;
+        vol.channels = 1;
+        vol.values[0] = g->volume;
+
+        pa_log_debug("Found a '%s' stream of '%s' that ducks a '%s' stream.", trigger_role, g->name, interaction_role);
+        pa_sink_input_add_volume_factor(i, g->name, &vol);
+
+    } else if (!u->duck) {
+        pa_log_debug("Found a '%s' stream that corks/mutes a '%s' stream.", trigger_role, interaction_role);
+        pa_sink_input_set_mute(i, true, false);
+        pa_sink_input_send_event(i, PA_STREAM_EVENT_REQUEST_CORK, NULL);
+    }
+}
+
+static void uncork_or_unduck(struct userdata *u, pa_sink_input *i, const char *interaction_role, bool corked, struct group *g) {
+
+    if (u->duck) {
+       pa_log_debug("In '%s', found a '%s' stream that should be unducked", g->name, interaction_role);
+       pa_sink_input_remove_volume_factor(i, g->name);
+    }
+    else if (corked || i->muted) {
+       pa_log_debug("Found a '%s' stream that should be uncorked/unmuted.", interaction_role);
+       if (i->muted)
+          pa_sink_input_set_mute(i, false, false);
+       if (corked)
+          pa_sink_input_send_event(i, PA_STREAM_EVENT_REQUEST_UNCORK, NULL);
+    }
+}
+
+static inline void apply_interaction_to_sink(struct userdata *u, pa_sink *s, const char *new_trigger, pa_sink_input *ignore, bool new_stream, struct group *g) {
+    pa_sink_input *j;
+    uint32_t idx, role_idx;
+    const char *interaction_role;
+    bool trigger = false;
+
+    pa_assert(u);
+    pa_sink_assert_ref(s);
+
+    for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
+        bool corked, interaction_applied;
+        const char *role;
+
+        if (j == ignore)
+            continue;
+
+        if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
+            role = "no_role";
+
+        PA_IDXSET_FOREACH(interaction_role, g->interaction_roles, role_idx) {
+            if ((trigger = pa_streq(role, interaction_role)))
+                break;
+            if ((trigger = (pa_streq(interaction_role, "any_role") && !get_trigger_role(u, j, g))))
+               break;
+        }
+        if (!trigger)
+            continue;
+
+        /* Some applications start their streams corked, so the stream is uncorked by */
+        /* the application only after sink_input_put() was called. If a new stream turns */
+        /* up, act as if it was not corked. In the case of module-role-cork this will */
+        /* only mute the stream because corking is reverted later by the application */
+        corked = (pa_sink_input_get_state(j) == PA_SINK_INPUT_CORKED);
+        if (new_stream && corked)
+            corked = false;
+        interaction_applied = !!pa_hashmap_get(g->interaction_state, j);
+
+        if (new_trigger && ((!corked && !j->muted) || u->duck)) {
+            if (!interaction_applied)
+                pa_hashmap_put(g->interaction_state, j, PA_INT_TO_PTR(1));
+
+            cork_or_duck(u, j, role, new_trigger, interaction_applied, g);
+
+        } else if (!new_trigger && interaction_applied) {
+            pa_hashmap_remove(g->interaction_state, j);
+
+            uncork_or_unduck(u, j, role, corked, g);
+        }
+    }
+}
+
+static void apply_interaction(struct userdata *u, pa_sink *s, const char *trigger_role, pa_sink_input *ignore, bool new_stream, struct group *g) {
+    pa_assert(u);
+
+    if (u->global) {
+        uint32_t idx;
+        PA_IDXSET_FOREACH(s, u->core->sinks, idx)
+            apply_interaction_to_sink(u, s, trigger_role, ignore, new_stream, g);
+    } else
+        apply_interaction_to_sink(u, s, trigger_role, ignore, new_stream, g);
+}
+
+static void remove_interactions(struct userdata *u, struct group *g) {
+    uint32_t idx, idx_input;
+    pa_sink *s;
+    pa_sink_input *j;
+    bool corked;
+    const char *role;
+
+    PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
+
+        for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx_input)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx_input))) {
+
+            if(!!pa_hashmap_get(g->interaction_state, j)) {
+                corked = (pa_sink_input_get_state(j) == PA_SINK_INPUT_CORKED);
+                if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
+                   role = "no_role";
+                uncork_or_unduck(u, j, role, corked, g);
+            }
+        }
+    }
+}
+
+static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool create, bool new_stream) {
+    const char *trigger_role;
+    uint32_t j;
+
+    pa_assert(u);
+    pa_sink_input_assert_ref(i);
+
+    if (!create)
+        for (j = 0; j < u->n_groups; j++)
+            pa_hashmap_remove(u->groups[j]->interaction_state, i);
+
+    if (!i->sink)
+        return PA_HOOK_OK;
+
+    for (j = 0; j < u->n_groups; j++) {
+        trigger_role = find_global_trigger_stream(u, i->sink, create ? NULL : i, u->groups[j]);
+        apply_interaction(u, i->sink, trigger_role, create ? NULL : i, new_stream, u->groups[j]);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, i, true, true);
+}
+
+static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_sink_input_assert_ref(i);
+
+    return process(u, i, false, false);
+}
+
+static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, i, false, false);
+}
+
+static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, i, true, false);
+}
+
+static pa_hook_result_t sink_input_state_changed_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    if (PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(i)) && get_trigger_role(u, i, NULL))
+        return process(u, i, true, false);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_mute_changed_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    if (PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(i)) && get_trigger_role(u, i, NULL))
+        return process(u, i, true, false);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_proplist_changed_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    if (PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(i)))
+        return process(u, i, true, false);
+
+    return PA_HOOK_OK;
+}
+
+int pa_stream_interaction_init(pa_module *m, const char* const v_modargs[]) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    const char *roles;
+    char *roles_in_group = NULL;
+    bool global = false;
+    uint32_t i = 0;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, v_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+
+    u->core = m->core;
+
+    u->duck = false;
+    if (pa_streq(m->name, "module-role-ducking"))
+        u->duck = true;
+
+    u->n_groups = 1;
+
+    if (u->duck) {
+        const char *volumes;
+        uint32_t group_count_tr = 0;
+        uint32_t group_count_du = 0;
+        uint32_t group_count_vol = 0;
+
+        roles = pa_modargs_get_value(ma, "trigger_roles", NULL);
+        if (roles) {
+            const char *split_state = NULL;
+            char *n = NULL;
+            while ((n = pa_split(roles, "/", &split_state))) {
+                group_count_tr++;
+                pa_xfree(n);
+            }
+        }
+        roles = pa_modargs_get_value(ma, "ducking_roles", NULL);
+        if (roles) {
+            const char *split_state = NULL;
+            char *n = NULL;
+            while ((n = pa_split(roles, "/", &split_state))) {
+                group_count_du++;
+                pa_xfree(n);
+            }
+        }
+        volumes = pa_modargs_get_value(ma, "volume", NULL);
+        if (volumes) {
+            const char *split_state = NULL;
+            char *n = NULL;
+            while ((n = pa_split(volumes, "/", &split_state))) {
+                group_count_vol++;
+                pa_xfree(n);
+            }
+        }
+
+        if ((group_count_tr > 1 || group_count_du > 1 || group_count_vol > 1) &&
+            ((group_count_tr != group_count_du) || (group_count_tr != group_count_vol))) {
+            pa_log("Invalid number of groups");
+            goto fail;
+        }
+
+        if (group_count_tr > 0)
+            u->n_groups = group_count_tr;
+    }
+
+    u->groups = pa_xnew0(struct group*, u->n_groups);
+    for (i = 0; i < u->n_groups; i++) {
+        u->groups[i] = pa_xnew0(struct group, 1);
+        u->groups[i]->trigger_roles = pa_idxset_new(NULL, NULL);
+        u->groups[i]->interaction_roles = pa_idxset_new(NULL, NULL);
+        u->groups[i]->interaction_state = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        if (u->duck)
+            u->groups[i]->name = pa_sprintf_malloc("ducking_group_%u", i);
+    }
+
+    roles = pa_modargs_get_value(ma, "trigger_roles", NULL);
+    if (roles) {
+        const char *group_split_state = NULL;
+        i = 0;
+
+        while ((roles_in_group = pa_split(roles, "/", &group_split_state))) {
+            if (roles_in_group[0] != '\0') {
+                const char *split_state = NULL;
+                char *n = NULL;
+                while ((n = pa_split(roles_in_group, ",", &split_state))) {
+                    if (n[0] != '\0')
+                        pa_idxset_put(u->groups[i]->trigger_roles, n, NULL);
+                    else {
+                        pa_log("empty trigger role");
+                        pa_xfree(n);
+                        goto fail;
+                    }
+                }
+                i++;
+            } else {
+                pa_log("empty trigger roles");
+                goto fail;
+            }
+
+            pa_xfree(roles_in_group);
+        }
+    }
+    if (pa_idxset_isempty(u->groups[0]->trigger_roles)) {
+        pa_log_debug("Using role 'phone' as trigger role.");
+        pa_idxset_put(u->groups[0]->trigger_roles, pa_xstrdup("phone"), NULL);
+    }
+
+    roles = pa_modargs_get_value(ma, u->duck ? "ducking_roles" : "cork_roles", NULL);
+    if (roles) {
+        const char *group_split_state = NULL;
+        i = 0;
+
+        while ((roles_in_group = pa_split(roles, "/", &group_split_state))) {
+            if (roles_in_group[0] != '\0') {
+                const char *split_state = NULL;
+                char *n = NULL;
+                while ((n = pa_split(roles_in_group, ",", &split_state))) {
+                    if (n[0] != '\0')
+                        pa_idxset_put(u->groups[i]->interaction_roles, n, NULL);
+                    else {
+                        pa_log("empty ducking role");
+                        pa_xfree(n);
+                        goto fail;
+                     }
+                }
+                i++;
+            } else {
+                pa_log("empty ducking roles");
+                goto fail;
+            }
+
+            pa_xfree(roles_in_group);
+        }
+    }
+    if (pa_idxset_isempty(u->groups[0]->interaction_roles)) {
+        pa_log_debug("Using roles 'music' and 'video' as %s roles.", u->duck ? "ducking" : "cork");
+        pa_idxset_put(u->groups[0]->interaction_roles, pa_xstrdup("music"), NULL);
+        pa_idxset_put(u->groups[0]->interaction_roles, pa_xstrdup("video"), NULL);
+    }
+
+    if (u->duck) {
+        const char *volumes;
+        u->groups[0]->volume = pa_sw_volume_from_dB(-20);
+        if ((volumes = pa_modargs_get_value(ma, "volume", NULL))) {
+            const char *group_split_state = NULL;
+            char *n = NULL;
+            i = 0;
+            while ((n = pa_split(volumes, "/", &group_split_state))) {
+                if (n[0] != '\0') {
+                    if (pa_parse_volume(n, &(u->groups[i++]->volume)) < 0) {
+                        pa_log("Failed to parse volume");
+                        pa_xfree(n);
+                        goto fail;
+                    }
+                } else {
+                    pa_log("empty volume");
+                    pa_xfree(n);
+                    goto fail;
+                }
+                pa_xfree(n);
+            }
+        }
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "global", &global) < 0) {
+        pa_log("Invalid boolean parameter: global");
+        goto fail;
+    }
+    u->global = global;
+
+    u->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u);
+    u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u);
+    u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u);
+    u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u);
+    u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_state_changed_cb, u);
+    u->sink_input_mute_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_mute_changed_cb, u);
+    u->sink_input_proplist_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_proplist_changed_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa_stream_interaction_done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+    if (roles_in_group)
+        pa_xfree(roles_in_group);
+
+    return -1;
+
+}
+
+void pa_stream_interaction_done(pa_module *m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->groups) {
+        uint32_t j;
+        for (j = 0; j < u->n_groups; j++) {
+            remove_interactions(u, u->groups[j]);
+            pa_idxset_free(u->groups[j]->trigger_roles, pa_xfree);
+            pa_idxset_free(u->groups[j]->interaction_roles, pa_xfree);
+            pa_hashmap_free(u->groups[j]->interaction_state);
+            if (u->duck)
+                pa_xfree(u->groups[j]->name);
+            pa_xfree(u->groups[j]);
+        }
+        pa_xfree(u->groups);
+    }
+
+    if (u->sink_input_put_slot)
+        pa_hook_slot_free(u->sink_input_put_slot);
+    if (u->sink_input_unlink_slot)
+        pa_hook_slot_free(u->sink_input_unlink_slot);
+    if (u->sink_input_move_start_slot)
+        pa_hook_slot_free(u->sink_input_move_start_slot);
+    if (u->sink_input_move_finish_slot)
+        pa_hook_slot_free(u->sink_input_move_finish_slot);
+    if (u->sink_input_state_changed_slot)
+        pa_hook_slot_free(u->sink_input_state_changed_slot);
+    if (u->sink_input_mute_changed_slot)
+        pa_hook_slot_free(u->sink_input_mute_changed_slot);
+    if (u->sink_input_proplist_changed_slot)
+        pa_hook_slot_free(u->sink_input_proplist_changed_slot);
+
+    pa_xfree(u);
+
+}
diff --git a/src/modules/stream-interaction.h b/src/modules/stream-interaction.h
new file mode 100644 (file)
index 0000000..d5bc626
--- /dev/null
@@ -0,0 +1,21 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2015 Georg Chini
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+int pa_stream_interaction_init(pa_module *m, const char* const v_modargs[]);
+void pa_stream_interaction_done(pa_module *m);
diff --git a/src/modules/udev-util.c b/src/modules/udev-util.c
new file mode 100644 (file)
index 0000000..026493b
--- /dev/null
@@ -0,0 +1,302 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libudev.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/proplist.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+#include "udev-util.h"
+
+static int read_id(struct udev_device *d, const char *n) {
+    const char *v;
+    unsigned u;
+
+    pa_assert(d);
+    pa_assert(n);
+
+    if (!(v = udev_device_get_property_value(d, n)))
+        return -1;
+
+    if (pa_startswith(v, "0x"))
+        v += 2;
+
+    if (!*v)
+        return -1;
+
+    if (sscanf(v, "%04x", &u) != 1)
+        return -1;
+
+    if (u > 0xFFFFU)
+        return -1;
+
+    return u;
+}
+
+static int dehex(char x) {
+    if (x >= '0' && x <= '9')
+        return x - '0';
+
+    if (x >= 'A' && x <= 'F')
+        return x - 'A' + 10;
+
+    if (x >= 'a' && x <= 'f')
+        return x - 'a' + 10;
+
+    return -1;
+}
+
+static void proplist_sets_unescape(pa_proplist *p, const char *prop, const char *s) {
+    const char *f;
+    char *t, *r;
+    int c = 0;
+
+    enum {
+        TEXT,
+        BACKSLASH,
+        EX,
+        FIRST
+    } state = TEXT;
+
+    /* The resulting string is definitely shorter than the source string */
+    r = pa_xnew(char, strlen(s)+1);
+
+    for (f = s, t = r; *f; f++) {
+
+        switch (state) {
+
+            case TEXT:
+                if (*f == '\\')
+                    state = BACKSLASH;
+                else
+                    *(t++) = *f;
+                break;
+
+            case BACKSLASH:
+                if (*f == 'x')
+                    state = EX;
+                else {
+                    *(t++) = '\\';
+                    *(t++) = *f;
+                    state = TEXT;
+                }
+                break;
+
+            case EX:
+                c = dehex(*f);
+
+                if (c < 0) {
+                    *(t++) = '\\';
+                    *(t++) = 'x';
+                    *(t++) = *f;
+                    state = TEXT;
+                } else
+                    state = FIRST;
+
+                break;
+
+            case FIRST: {
+                int d = dehex(*f);
+
+                if (d < 0) {
+                    *(t++) = '\\';
+                    *(t++) = 'x';
+                    *(t++) = *(f-1);
+                    *(t++) = *f;
+                } else
+                    *(t++) = (char) (c << 4) | d;
+
+                state = TEXT;
+                break;
+            }
+        }
+    }
+
+    switch (state) {
+
+        case TEXT:
+            break;
+
+        case BACKSLASH:
+            *(t++) = '\\';
+            break;
+
+        case EX:
+            *(t++) = '\\';
+            *(t++) = 'x';
+            break;
+
+        case FIRST:
+            *(t++) = '\\';
+            *(t++) = 'x';
+            *(t++) = *(f-1);
+            break;
+    }
+
+    *t = 0;
+
+    pa_proplist_sets(p, prop, r);
+    pa_xfree(r);
+}
+
+int pa_udev_get_info(int card_idx, pa_proplist *p) {
+    int r = -1;
+    struct udev *udev;
+    struct udev_device *card = NULL;
+    char *t;
+    const char *v;
+    const char *bus = NULL;
+    int id;
+
+    pa_assert(p);
+    pa_assert(card_idx >= 0);
+
+    if (!(udev = udev_new())) {
+        pa_log_error("Failed to allocate udev context.");
+        goto finish;
+    }
+
+    t = pa_sprintf_malloc("/sys/class/sound/card%i", card_idx);
+    card = udev_device_new_from_syspath(udev, t);
+    pa_xfree(t);
+
+    if (!card) {
+        pa_log_error("Failed to get card object.");
+        goto finish;
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS_PATH))
+        if (((v = udev_device_get_property_value(card, "ID_PATH")) && *v) ||
+            (v = udev_device_get_devpath(card)))
+            pa_proplist_sets(p, PA_PROP_DEVICE_BUS_PATH, v);
+
+    if (!pa_proplist_contains(p, "sysfs.path"))
+        if ((v = udev_device_get_devpath(card)))
+            pa_proplist_sets(p, "sysfs.path", v);
+
+    if (!pa_proplist_contains(p, "udev.id"))
+        if ((v = udev_device_get_property_value(card, "ID_ID")) && *v)
+            pa_proplist_sets(p, "udev.id", v);
+
+    if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS))
+        if ((bus = udev_device_get_property_value(card, "ID_BUS")) && *bus)
+            pa_proplist_sets(p, PA_PROP_DEVICE_BUS, bus);
+
+    if (!pa_proplist_contains(p, PA_PROP_DEVICE_VENDOR_ID))
+        if ((id = read_id(card, "ID_VENDOR_ID")) > 0)
+            pa_proplist_setf(p, PA_PROP_DEVICE_VENDOR_ID, "%04x", id);
+
+    if (!pa_proplist_contains(p, PA_PROP_DEVICE_VENDOR_NAME)) {
+        /* ID_VENDOR_FROM_DATABASE returns the name of IEEE 1394 Phy/Link chipset for FireWire devices */
+        if (!pa_safe_streq(bus, "firewire") && (v = udev_device_get_property_value(card, "ID_VENDOR_FROM_DATABASE")) && *v)
+            pa_proplist_sets(p, PA_PROP_DEVICE_VENDOR_NAME, v);
+        else if ((v = udev_device_get_property_value(card, "ID_VENDOR_ENC")) && *v)
+            proplist_sets_unescape(p, PA_PROP_DEVICE_VENDOR_NAME, v);
+        else if ((v = udev_device_get_property_value(card, "ID_VENDOR")) && *v)
+            pa_proplist_sets(p, PA_PROP_DEVICE_VENDOR_NAME, v);
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_DEVICE_PRODUCT_ID))
+        if ((id = read_id(card, "ID_MODEL_ID")) >= 0)
+            pa_proplist_setf(p, PA_PROP_DEVICE_PRODUCT_ID, "%04x", id);
+
+    if (!pa_proplist_contains(p, PA_PROP_DEVICE_PRODUCT_NAME)) {
+        /* ID_MODEL_FROM_DATABASE returns the name of IEEE 1394 Phy/Link chipset for FireWire devices */
+        if (!pa_safe_streq(bus, "firewire") && (v = udev_device_get_property_value(card, "ID_MODEL_FROM_DATABASE")) && *v)
+            pa_proplist_sets(p, PA_PROP_DEVICE_PRODUCT_NAME, v);
+        else if ((v = udev_device_get_property_value(card, "ID_MODEL_ENC")) && *v)
+            proplist_sets_unescape(p, PA_PROP_DEVICE_PRODUCT_NAME, v);
+        else if ((v = udev_device_get_property_value(card, "ID_MODEL")) && *v)
+            pa_proplist_sets(p, PA_PROP_DEVICE_PRODUCT_NAME, v);
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_DEVICE_SERIAL))
+        if ((v = udev_device_get_property_value(card, "ID_SERIAL")) && *v)
+            pa_proplist_sets(p, PA_PROP_DEVICE_SERIAL, v);
+
+    if (!pa_proplist_contains(p, PA_PROP_DEVICE_CLASS))
+        if ((v = udev_device_get_property_value(card, "SOUND_CLASS")) && *v)
+            pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, v);
+
+    if (!pa_proplist_contains(p, PA_PROP_DEVICE_FORM_FACTOR))
+        if ((v = udev_device_get_property_value(card, "SOUND_FORM_FACTOR")) && *v)
+            pa_proplist_sets(p, PA_PROP_DEVICE_FORM_FACTOR, v);
+
+    /* This is normally not set by the udev rules but may be useful to
+     * allow administrators to overwrite the device description.*/
+    if (!pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
+        if ((v = udev_device_get_property_value(card, "SOUND_DESCRIPTION")) && *v)
+            pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, v);
+
+    r = 0;
+
+finish:
+
+    if (card)
+        udev_device_unref(card);
+
+    if (udev)
+        udev_unref(udev);
+
+    return r;
+}
+
+char* pa_udev_get_property(int card_idx, const char *name) {
+    struct udev *udev;
+    struct udev_device *card = NULL;
+    char *t, *r = NULL;
+    const char *v;
+
+    pa_assert(card_idx >= 0);
+    pa_assert(name);
+
+    if (!(udev = udev_new())) {
+        pa_log_error("Failed to allocate udev context.");
+        goto finish;
+    }
+
+    t = pa_sprintf_malloc("/sys/class/sound/card%i", card_idx);
+    card = udev_device_new_from_syspath(udev, t);
+    pa_xfree(t);
+
+    if (!card) {
+        pa_log_error("Failed to get card object.");
+        goto finish;
+    }
+
+    if ((v = udev_device_get_property_value(card, name)) && *v)
+        r = pa_xstrdup(v);
+
+finish:
+
+    if (card)
+        udev_device_unref(card);
+
+    if (udev)
+        udev_unref(udev);
+
+    return r;
+}
diff --git a/src/modules/udev-util.h b/src/modules/udev-util.h
new file mode 100644 (file)
index 0000000..48ab9f6
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef fooudevutilhfoo
+#define fooudevutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/proplist.h>
+
+int pa_udev_get_info(int card_idx, pa_proplist *p);
+char* pa_udev_get_property(int card_idx, const char *name);
+
+#endif
diff --git a/src/modules/x11/module-x11-bell.c b/src/modules/x11/module-x11-bell.c
new file mode 100644 (file)
index 0000000..7b2be57
--- /dev/null
@@ -0,0 +1,187 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-scache.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/x11wrap.h>
+
+#include "module-x11-bell-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 bell interceptor");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE("sink=<sink to connect to> sample=<sample name> display=<X11 display>");
+
+static const char* const valid_modargs[] = {
+    "sink",
+    "sample",
+    "display",
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    int xkb_event_base;
+
+    char *sink_name;
+    char *scache_item;
+
+    pa_x11_wrapper *x11_wrapper;
+    pa_x11_client *x11_client;
+};
+
+static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) {
+    XkbBellNotifyEvent *bne;
+    struct userdata *u = userdata;
+
+    pa_assert(w);
+    pa_assert(e);
+    pa_assert(u);
+    pa_assert(u->x11_wrapper == w);
+
+    if (((XkbEvent*) e)->any.xkb_type != XkbBellNotify)
+        return 0;
+
+    bne = (XkbBellNotifyEvent*) e;
+
+    /* We could use bne->percent to set the volume, but then the "event" role
+     * volume wouldn't have effect. It's better to ignore the volume suggestion
+     * from X11. */
+    if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, PA_VOLUME_INVALID, NULL, NULL) < 0) {
+        pa_log_info("Ringing bell failed, reverting to X11 device bell.");
+        XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent);
+    }
+
+    return 1;
+}
+
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(w);
+    pa_assert(u);
+    pa_assert(u->x11_wrapper == w);
+
+    if (u->x11_client)
+        pa_x11_client_free(u->x11_client);
+
+    if (u->x11_wrapper)
+        pa_x11_wrapper_unref(u->x11_wrapper);
+
+    u->x11_client = NULL;
+    u->x11_wrapper = NULL;
+
+    pa_module_unload_request(u->module, true);
+}
+
+int pa__init(pa_module*m) {
+
+    struct userdata *u = NULL;
+    pa_modargs *ma = NULL;
+    int major, minor;
+    unsigned int auto_ctrls, auto_values;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "bell-window-system"));
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+    u->x11_client = NULL;
+
+    if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+        goto fail;
+
+    major = XkbMajorVersion;
+    minor = XkbMinorVersion;
+
+    if (!XkbLibraryVersion(&major, &minor)) {
+        pa_log("XkbLibraryVersion() failed");
+        goto fail;
+    }
+
+    major = XkbMajorVersion;
+    minor = XkbMinorVersion;
+
+    if (!XkbQueryExtension(pa_x11_wrapper_get_display(u->x11_wrapper), NULL, &u->xkb_event_base, NULL, &major, &minor)) {
+        pa_log("XkbQueryExtension() failed");
+        goto fail;
+    }
+
+    XkbSelectEvents(pa_x11_wrapper_get_display(u->x11_wrapper), XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask);
+    auto_ctrls = auto_values = XkbAudibleBellMask;
+    XkbSetAutoResetControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbAudibleBellMask, &auto_ctrls, &auto_values);
+    XkbChangeEnabledControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbUseCoreKbd, XkbAudibleBellMask, 0);
+
+    u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_cb, x11_kill_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    pa_xfree(u->scache_item);
+    pa_xfree(u->sink_name);
+
+    if (u->x11_client)
+        pa_x11_client_free(u->x11_client);
+
+    if (u->x11_wrapper)
+        pa_x11_wrapper_unref(u->x11_wrapper);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/x11/module-x11-cork-request.c b/src/modules/x11/module-x11-cork-request.c
new file mode 100644 (file)
index 0000000..5c76711
--- /dev/null
@@ -0,0 +1,185 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/XTest.h>
+#include <X11/XF86keysym.h>
+#include <X11/keysym.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/x11wrap.h>
+#include <pulsecore/core-util.h>
+
+#include "module-x11-cork-request-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Synthesize X11 media key events when cork/uncork is requested");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE("display=<X11 display>");
+
+static const char* const valid_modargs[] = {
+    "display",
+    NULL
+};
+
+struct userdata {
+    pa_module *module;
+
+    pa_x11_wrapper *x11_wrapper;
+    pa_x11_client *x11_client;
+
+    pa_hook_slot *hook_slot;
+};
+
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(w);
+    pa_assert(u);
+    pa_assert(u->x11_wrapper == w);
+
+    if (u->x11_client) {
+        pa_x11_client_free(u->x11_client);
+        u->x11_client = NULL;
+    }
+
+    if (u->x11_wrapper) {
+        pa_x11_wrapper_unref(u->x11_wrapper);
+        u->x11_wrapper = NULL;
+    }
+
+    pa_module_unload_request(u->module, true);
+}
+
+static pa_hook_result_t sink_input_send_event_hook_cb(
+        pa_core *c,
+        pa_sink_input_send_event_hook_data *data,
+        struct userdata *u) {
+
+    KeySym sym;
+    KeyCode code;
+    Display *display;
+
+    pa_assert(c);
+    pa_assert(data);
+    pa_assert(u);
+
+    if (pa_streq(data->event, PA_STREAM_EVENT_REQUEST_CORK))
+        sym = XF86XK_AudioPause;
+    else if (pa_streq(data->event, PA_STREAM_EVENT_REQUEST_UNCORK))
+        sym = XF86XK_AudioPlay;
+    else
+        return PA_HOOK_OK;
+
+    pa_log_debug("Triggering X11 keysym: %s", XKeysymToString(sym));
+
+    display = pa_x11_wrapper_get_display(u->x11_wrapper);
+    code = XKeysymToKeycode(display, sym);
+
+    XTestFakeKeyEvent(display, code, True, CurrentTime);
+    XSync(display, False);
+
+    XTestFakeKeyEvent(display, code, False, CurrentTime);
+    XSync(display, False);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u;
+    pa_modargs *ma;
+    int xtest_event_base, xtest_error_base;
+    int major_version, minor_version;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+
+    if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+        goto fail;
+
+    if (!XTestQueryExtension(
+                pa_x11_wrapper_get_display(u->x11_wrapper),
+                &xtest_event_base, &xtest_error_base,
+                &major_version, &minor_version)) {
+
+        pa_log("XTest extension not supported.");
+        goto fail;
+    }
+
+    pa_log_debug("XTest %i.%i supported.", major_version, minor_version);
+
+    u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u);
+
+    u->hook_slot = pa_hook_connect(
+            &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT],
+            PA_HOOK_NORMAL,
+            (pa_hook_cb_t) sink_input_send_event_hook_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata*u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->x11_client)
+        pa_x11_client_free(u->x11_client);
+
+    if (u->x11_wrapper)
+        pa_x11_wrapper_unref(u->x11_wrapper);
+
+    if (u->hook_slot)
+        pa_hook_slot_free(u->hook_slot);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/x11/module-x11-publish.c b/src/modules/x11/module-x11-publish.c
new file mode 100644 (file)
index 0000000..553b341
--- /dev/null
@@ -0,0 +1,244 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <xcb/xcb.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/x11wrap.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/auth-cookie.h>
+#include <pulsecore/x11prop.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/protocol-native.h>
+
+#include "module-x11-publish-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 credential publisher");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE(
+        "display=<X11 display> "
+        "sink=<Sink to publish> "
+        "source=<Source to publish> "
+        "cookie=<Cookie file to publish> ");
+
+static const char* const valid_modargs[] = {
+    "display",
+    "sink",
+    "source",
+    "cookie",
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_native_protocol *protocol;
+
+    char *id;
+    pa_auth_cookie *auth_cookie;
+
+    pa_x11_wrapper *x11_wrapper;
+    pa_x11_client *x11_client;
+
+    pa_hook_slot *hook_slot;
+};
+
+static void publish_servers(struct userdata *u, pa_strlist *l) {
+
+    int screen = DefaultScreen(pa_x11_wrapper_get_display(u->x11_wrapper));
+
+    if (l) {
+        char *s;
+
+        l = pa_strlist_reverse(l);
+        s = pa_strlist_to_string(l);
+        pa_strlist_reverse(l);
+
+        pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SERVER", s);
+        pa_xfree(s);
+    } else
+        pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SERVER");
+}
+
+static pa_hook_result_t servers_changed_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_strlist *servers = call_data;
+    struct userdata *u = slot_data;
+    char t[256];
+    int screen;
+
+    pa_assert(u);
+
+    screen = DefaultScreen(pa_x11_wrapper_get_display(u->x11_wrapper));
+    if (!pa_x11_get_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_ID", t, sizeof(t)) || !pa_streq(t, u->id)) {
+        pa_log_warn("PulseAudio information vanished from X11!");
+        return PA_HOOK_OK;
+    }
+
+    publish_servers(u, servers);
+    return PA_HOOK_OK;
+}
+
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(w);
+    pa_assert(u);
+    pa_assert(u->x11_wrapper == w);
+
+    if (u->x11_client)
+        pa_x11_client_free(u->x11_client);
+
+    if (u->x11_wrapper)
+        pa_x11_wrapper_unref(u->x11_wrapper);
+
+    u->x11_client = NULL;
+    u->x11_wrapper = NULL;
+
+    pa_module_unload_request(u->module, true);
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    char *mid, *sid;
+    char hx[PA_NATIVE_COOKIE_LENGTH*2+1];
+    const char *t;
+    int screen;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->protocol = pa_native_protocol_get(m->core);
+    u->id = NULL;
+    u->auth_cookie = NULL;
+    u->x11_client = NULL;
+    u->x11_wrapper = NULL;
+
+    u->hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_SERVERS_CHANGED], PA_HOOK_NORMAL, servers_changed_cb, u);
+
+    if (!(u->auth_cookie = pa_auth_cookie_get(m->core, pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), true, PA_NATIVE_COOKIE_LENGTH)))
+        goto fail;
+
+    if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+        goto fail;
+
+    screen = DefaultScreen(pa_x11_wrapper_get_display(u->x11_wrapper));
+    mid = pa_machine_id();
+    u->id = pa_sprintf_malloc("%lu@%s/%lu", (unsigned long) getuid(), mid, (unsigned long) getpid());
+    pa_xfree(mid);
+
+    pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_ID", u->id);
+
+    if ((sid = pa_session_id())) {
+        pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SESSION_ID", sid);
+        pa_xfree(sid);
+    }
+
+    publish_servers(u, pa_native_protocol_servers(u->protocol));
+
+    if ((t = pa_modargs_get_value(ma, "source", NULL)))
+        pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SOURCE", t);
+
+    if ((t = pa_modargs_get_value(ma, "sink", NULL)))
+        pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SINK", t);
+
+    pa_x11_set_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_COOKIE",
+                    pa_hexstr(pa_auth_cookie_read(u->auth_cookie, PA_NATIVE_COOKIE_LENGTH), PA_NATIVE_COOKIE_LENGTH, hx, sizeof(hx)));
+
+    u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata*u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->x11_client)
+        pa_x11_client_free(u->x11_client);
+
+    if (u->x11_wrapper) {
+        char t[256];
+        int screen = DefaultScreen(pa_x11_wrapper_get_display(u->x11_wrapper));
+
+        /* Yes, here is a race condition */
+        if (!pa_x11_get_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_ID", t, sizeof(t)) || !pa_streq(t, u->id))
+            pa_log_warn("PulseAudio information vanished from X11!");
+        else {
+            pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_ID");
+            pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SERVER");
+            pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SINK");
+            pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SOURCE");
+            pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_COOKIE");
+            pa_x11_del_prop(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper), screen, "PULSE_SESSION_ID");
+            xcb_flush(pa_x11_wrapper_get_xcb_connection(u->x11_wrapper));
+        }
+
+        pa_x11_wrapper_unref(u->x11_wrapper);
+    }
+
+    if (u->auth_cookie)
+        pa_auth_cookie_unref(u->auth_cookie);
+
+    if (u->hook_slot)
+        pa_hook_slot_free(u->hook_slot);
+
+    if (u->protocol)
+        pa_native_protocol_unref(u->protocol);
+
+    pa_xfree(u->id);
+    pa_xfree(u);
+}
diff --git a/src/modules/x11/module-x11-xsmp.c b/src/modules/x11/module-x11-xsmp.c
new file mode 100644 (file)
index 0000000..7c6fb23
--- /dev/null
@@ -0,0 +1,247 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/Xlib.h>
+#include <X11/SM/SMlib.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/x11wrap.h>
+
+#include "module-x11-xsmp-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 session management");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE("session_manager=<session manager string> display=<X11 display>");
+
+static bool ice_in_use = false;
+
+static const char* const valid_modargs[] = {
+    "session_manager",
+    "display",
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_client *client;
+    SmcConn connection;
+    pa_x11_wrapper *x11;
+};
+
+static void die_cb(SmcConn connection, SmPointer client_data) {
+    struct userdata *u = client_data;
+    pa_assert(u);
+
+    pa_log_debug("Got die message from XSMP.");
+
+    pa_x11_wrapper_kill(u->x11);
+
+    pa_x11_wrapper_unref(u->x11);
+    u->x11 = NULL;
+
+    pa_module_unload_request(u->module, true);
+}
+
+static void save_complete_cb(SmcConn connection, SmPointer client_data) {
+}
+
+static void shutdown_cancelled_cb(SmcConn connection, SmPointer client_data) {
+    SmcSaveYourselfDone(connection, True);
+}
+
+static void save_yourself_cb(SmcConn connection, SmPointer client_data, int save_type, Bool _shutdown, int interact_style, Bool fast) {
+    SmcSaveYourselfDone(connection, True);
+}
+
+static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
+    IceConn connection = userdata;
+
+    if (IceProcessMessages(connection, NULL, NULL) == IceProcessMessagesIOError) {
+        IceSetShutdownNegotiation(connection, False);
+        IceCloseConnection(connection);
+    }
+}
+
+static void new_ice_connection(IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) {
+    pa_core *c = client_data;
+
+    if (opening)
+        *watch_data = c->mainloop->io_new(
+                c->mainloop,
+                IceConnectionNumber(connection),
+                PA_IO_EVENT_INPUT,
+                ice_io_cb,
+                connection);
+    else
+        c->mainloop->io_free(*watch_data);
+}
+
+int pa__init(pa_module*m) {
+
+    pa_modargs *ma = NULL;
+    char t[256], *vendor, *client_id;
+    SmcCallbacks callbacks;
+    SmProp prop_program, prop_user;
+    SmProp *prop_list[2];
+    SmPropValue val_program, val_user;
+    struct userdata *u;
+    const char *e;
+    pa_client_new_data data;
+
+    pa_assert(m);
+
+    if (ice_in_use) {
+        pa_log("module-x11-xsmp may not be loaded twice.");
+        return -1;
+    }
+
+    IceAddConnectionWatch(new_ice_connection, m->core);
+    ice_in_use = true;
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->client = NULL;
+    u->connection = NULL;
+    u->x11 = NULL;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (!(u->x11 = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+        goto fail;
+
+    e = pa_modargs_get_value(ma, "session_manager", NULL);
+
+    if (!e && !getenv("SESSION_MANAGER")) {
+        pa_log("X11 session manager not running.");
+        goto fail;
+    }
+
+    memset(&callbacks, 0, sizeof(callbacks));
+    callbacks.die.callback = die_cb;
+    callbacks.die.client_data = u;
+    callbacks.save_yourself.callback = save_yourself_cb;
+    callbacks.save_yourself.client_data = m->core;
+    callbacks.save_complete.callback = save_complete_cb;
+    callbacks.save_complete.client_data = m->core;
+    callbacks.shutdown_cancelled.callback = shutdown_cancelled_cb;
+    callbacks.shutdown_cancelled.client_data = m->core;
+
+    if (!(u->connection = SmcOpenConnection(
+                  (char*) e, m->core,
+                  SmProtoMajor, SmProtoMinor,
+                  SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask,
+                  &callbacks, NULL, &client_id,
+                  sizeof(t), t))) {
+
+        pa_log("Failed to open connection to session manager: %s", t);
+        goto fail;
+    }
+
+    prop_program.name = (char*) SmProgram;
+    prop_program.type = (char*) SmARRAY8;
+    val_program.value = (char*) PACKAGE_NAME;
+    val_program.length = (int) strlen(val_program.value);
+    prop_program.num_vals = 1;
+    prop_program.vals = &val_program;
+    prop_list[0] = &prop_program;
+
+    prop_user.name = (char*) SmUserID;
+    prop_user.type = (char*) SmARRAY8;
+    pa_get_user_name(t, sizeof(t));
+    val_user.value = t;
+    val_user.length = (int) strlen(val_user.value);
+    prop_user.num_vals = 1;
+    prop_user.vals = &val_user;
+    prop_list[1] = &prop_user;
+
+    SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list);
+
+    pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id);
+
+    pa_client_new_data_init(&data);
+    data.module = m;
+    data.driver = __FILE__;
+    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "XSMP Session on %s as %s", vendor, client_id);
+    pa_proplist_sets(data.proplist, "xsmp.vendor", vendor);
+    pa_proplist_sets(data.proplist, "xsmp.client.id", client_id);
+    u->client = pa_client_new(u->core, &data);
+    pa_client_new_data_done(&data);
+
+    free(vendor);
+    free(client_id);
+
+    if (!u->client)
+        goto fail;
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if ((u = m->userdata)) {
+
+        if (u->connection)
+            SmcCloseConnection(u->connection, 0, NULL);
+
+        if (u->client)
+            pa_client_free(u->client);
+
+        if (u->x11)
+            pa_x11_wrapper_unref(u->x11);
+
+        pa_xfree(u);
+    }
+
+    if (ice_in_use) {
+        IceRemoveConnectionWatch(new_ice_connection, m->core);
+        ice_in_use = false;
+    }
+}
diff --git a/src/pulse/.gitignore b/src/pulse/.gitignore
new file mode 100644 (file)
index 0000000..6702033
--- /dev/null
@@ -0,0 +1 @@
+version.h
diff --git a/src/pulse/Makefile b/src/pulse/Makefile
new file mode 120000 (symlink)
index 0000000..f5b1dba
--- /dev/null
@@ -0,0 +1 @@
+../modules/Makefile
\ No newline at end of file
diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h
new file mode 100644 (file)
index 0000000..ac817d5
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef foopulsecdeclhfoo
+#define foopulsecdeclhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/** \file
+ * C++ compatibility support */
+
+#ifdef __cplusplus
+/** If using C++ this macro enables C mode, otherwise does nothing */
+#define PA_C_DECL_BEGIN extern "C" {
+/** If using C++ this macros switches back to C++ mode, otherwise does nothing */
+#define PA_C_DECL_END }
+
+#else
+/** If using C++ this macro enables C mode, otherwise does nothing */
+#define PA_C_DECL_BEGIN
+/** If using C++ this macros switches back to C++ mode, otherwise does nothing */
+#define PA_C_DECL_END
+
+#endif
+
+#endif
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
new file mode 100644 (file)
index 0000000..c44dca4
--- /dev/null
@@ -0,0 +1,833 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/bitset.h>
+#include <pulsecore/sample-util.h>
+
+#include "channelmap.h"
+
+const char *const table[PA_CHANNEL_POSITION_MAX] = {
+    [PA_CHANNEL_POSITION_MONO] = "mono",
+
+    [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
+    [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left",
+    [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right",
+
+    [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center",
+    [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left",
+    [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right",
+
+    [PA_CHANNEL_POSITION_LFE] = "lfe",
+
+    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center",
+    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center",
+
+    [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left",
+    [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right",
+
+    [PA_CHANNEL_POSITION_AUX0] = "aux0",
+    [PA_CHANNEL_POSITION_AUX1] = "aux1",
+    [PA_CHANNEL_POSITION_AUX2] = "aux2",
+    [PA_CHANNEL_POSITION_AUX3] = "aux3",
+    [PA_CHANNEL_POSITION_AUX4] = "aux4",
+    [PA_CHANNEL_POSITION_AUX5] = "aux5",
+    [PA_CHANNEL_POSITION_AUX6] = "aux6",
+    [PA_CHANNEL_POSITION_AUX7] = "aux7",
+    [PA_CHANNEL_POSITION_AUX8] = "aux8",
+    [PA_CHANNEL_POSITION_AUX9] = "aux9",
+    [PA_CHANNEL_POSITION_AUX10] = "aux10",
+    [PA_CHANNEL_POSITION_AUX11] = "aux11",
+    [PA_CHANNEL_POSITION_AUX12] = "aux12",
+    [PA_CHANNEL_POSITION_AUX13] = "aux13",
+    [PA_CHANNEL_POSITION_AUX14] = "aux14",
+    [PA_CHANNEL_POSITION_AUX15] = "aux15",
+    [PA_CHANNEL_POSITION_AUX16] = "aux16",
+    [PA_CHANNEL_POSITION_AUX17] = "aux17",
+    [PA_CHANNEL_POSITION_AUX18] = "aux18",
+    [PA_CHANNEL_POSITION_AUX19] = "aux19",
+    [PA_CHANNEL_POSITION_AUX20] = "aux20",
+    [PA_CHANNEL_POSITION_AUX21] = "aux21",
+    [PA_CHANNEL_POSITION_AUX22] = "aux22",
+    [PA_CHANNEL_POSITION_AUX23] = "aux23",
+    [PA_CHANNEL_POSITION_AUX24] = "aux24",
+    [PA_CHANNEL_POSITION_AUX25] = "aux25",
+    [PA_CHANNEL_POSITION_AUX26] = "aux26",
+    [PA_CHANNEL_POSITION_AUX27] = "aux27",
+    [PA_CHANNEL_POSITION_AUX28] = "aux28",
+    [PA_CHANNEL_POSITION_AUX29] = "aux29",
+    [PA_CHANNEL_POSITION_AUX30] = "aux30",
+    [PA_CHANNEL_POSITION_AUX31] = "aux31",
+
+    [PA_CHANNEL_POSITION_TOP_CENTER] = "top-center",
+
+    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center",
+    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left",
+    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right",
+
+    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center",
+    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left",
+    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right"
+};
+
+const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = {
+    [PA_CHANNEL_POSITION_MONO] = N_("Mono"),
+
+    [PA_CHANNEL_POSITION_FRONT_CENTER] = N_("Front Center"),
+    [PA_CHANNEL_POSITION_FRONT_LEFT] = N_("Front Left"),
+    [PA_CHANNEL_POSITION_FRONT_RIGHT] = N_("Front Right"),
+
+    [PA_CHANNEL_POSITION_REAR_CENTER] = N_("Rear Center"),
+    [PA_CHANNEL_POSITION_REAR_LEFT] = N_("Rear Left"),
+    [PA_CHANNEL_POSITION_REAR_RIGHT] = N_("Rear Right"),
+
+    [PA_CHANNEL_POSITION_LFE] = N_("Subwoofer"),
+
+    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = N_("Front Left-of-center"),
+    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = N_("Front Right-of-center"),
+
+    [PA_CHANNEL_POSITION_SIDE_LEFT] = N_("Side Left"),
+    [PA_CHANNEL_POSITION_SIDE_RIGHT] = N_("Side Right"),
+
+    [PA_CHANNEL_POSITION_AUX0] = N_("Auxiliary 0"),
+    [PA_CHANNEL_POSITION_AUX1] = N_("Auxiliary 1"),
+    [PA_CHANNEL_POSITION_AUX2] = N_("Auxiliary 2"),
+    [PA_CHANNEL_POSITION_AUX3] = N_("Auxiliary 3"),
+    [PA_CHANNEL_POSITION_AUX4] = N_("Auxiliary 4"),
+    [PA_CHANNEL_POSITION_AUX5] = N_("Auxiliary 5"),
+    [PA_CHANNEL_POSITION_AUX6] = N_("Auxiliary 6"),
+    [PA_CHANNEL_POSITION_AUX7] = N_("Auxiliary 7"),
+    [PA_CHANNEL_POSITION_AUX8] = N_("Auxiliary 8"),
+    [PA_CHANNEL_POSITION_AUX9] = N_("Auxiliary 9"),
+    [PA_CHANNEL_POSITION_AUX10] = N_("Auxiliary 10"),
+    [PA_CHANNEL_POSITION_AUX11] = N_("Auxiliary 11"),
+    [PA_CHANNEL_POSITION_AUX12] = N_("Auxiliary 12"),
+    [PA_CHANNEL_POSITION_AUX13] = N_("Auxiliary 13"),
+    [PA_CHANNEL_POSITION_AUX14] = N_("Auxiliary 14"),
+    [PA_CHANNEL_POSITION_AUX15] = N_("Auxiliary 15"),
+    [PA_CHANNEL_POSITION_AUX16] = N_("Auxiliary 16"),
+    [PA_CHANNEL_POSITION_AUX17] = N_("Auxiliary 17"),
+    [PA_CHANNEL_POSITION_AUX18] = N_("Auxiliary 18"),
+    [PA_CHANNEL_POSITION_AUX19] = N_("Auxiliary 19"),
+    [PA_CHANNEL_POSITION_AUX20] = N_("Auxiliary 20"),
+    [PA_CHANNEL_POSITION_AUX21] = N_("Auxiliary 21"),
+    [PA_CHANNEL_POSITION_AUX22] = N_("Auxiliary 22"),
+    [PA_CHANNEL_POSITION_AUX23] = N_("Auxiliary 23"),
+    [PA_CHANNEL_POSITION_AUX24] = N_("Auxiliary 24"),
+    [PA_CHANNEL_POSITION_AUX25] = N_("Auxiliary 25"),
+    [PA_CHANNEL_POSITION_AUX26] = N_("Auxiliary 26"),
+    [PA_CHANNEL_POSITION_AUX27] = N_("Auxiliary 27"),
+    [PA_CHANNEL_POSITION_AUX28] = N_("Auxiliary 28"),
+    [PA_CHANNEL_POSITION_AUX29] = N_("Auxiliary 29"),
+    [PA_CHANNEL_POSITION_AUX30] = N_("Auxiliary 30"),
+    [PA_CHANNEL_POSITION_AUX31] = N_("Auxiliary 31"),
+
+    [PA_CHANNEL_POSITION_TOP_CENTER] = N_("Top Center"),
+
+    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = N_("Top Front Center"),
+    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = N_("Top Front Left"),
+    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = N_("Top Front Right"),
+
+    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = N_("Top Rear Center"),
+    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = N_("Top Rear Left"),
+    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = N_("Top Rear Right")
+};
+
+pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
+    unsigned c;
+    pa_assert(m);
+
+    m->channels = 0;
+
+    for (c = 0; c < PA_CHANNELS_MAX; c++)
+        m->map[c] = PA_CHANNEL_POSITION_INVALID;
+
+    return m;
+}
+
+pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) {
+    pa_assert(m);
+
+    pa_channel_map_init(m);
+
+    m->channels = 1;
+    m->map[0] = PA_CHANNEL_POSITION_MONO;
+    return m;
+}
+
+pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {
+    pa_assert(m);
+
+    pa_channel_map_init(m);
+
+    m->channels = 2;
+    m->map[0] = PA_CHANNEL_POSITION_LEFT;
+    m->map[1] = PA_CHANNEL_POSITION_RIGHT;
+    return m;
+}
+
+pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
+    pa_assert(m);
+    pa_assert(pa_channels_valid(channels));
+    pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
+
+    pa_channel_map_init(m);
+
+    m->channels = (uint8_t) channels;
+
+    switch (def) {
+        case PA_CHANNEL_MAP_AIFF:
+
+            /* This is somewhat compatible with RFC3551 */
+
+            switch (channels) {
+                case 1:
+                    m->map[0] = PA_CHANNEL_POSITION_MONO;
+                    return m;
+
+                case 6:
+                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
+                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+                    m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+                    m->map[4] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+                    m->map[5] = PA_CHANNEL_POSITION_REAR_CENTER;
+                    return m;
+
+                case 5:
+                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+                    m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
+                    m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
+                    /* Fall through */
+
+                case 2:
+                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+                    return m;
+
+                case 3:
+                    m->map[0] = PA_CHANNEL_POSITION_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_RIGHT;
+                    m->map[2] = PA_CHANNEL_POSITION_CENTER;
+                    return m;
+
+                case 4:
+                    m->map[0] = PA_CHANNEL_POSITION_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_CENTER;
+                    m->map[2] = PA_CHANNEL_POSITION_RIGHT;
+                    m->map[3] = PA_CHANNEL_POSITION_REAR_CENTER;
+                    return m;
+
+                default:
+                    return NULL;
+            }
+
+        case PA_CHANNEL_MAP_ALSA:
+
+            switch (channels) {
+                case 1:
+                    m->map[0] = PA_CHANNEL_POSITION_MONO;
+                    return m;
+
+                case 8:
+                    m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
+                    m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+                    /* Fall through */
+
+                case 6:
+                    m->map[5] = PA_CHANNEL_POSITION_LFE;
+                    /* Fall through */
+
+                case 5:
+                    m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+                    /* Fall through */
+
+                case 4:
+                    m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+                    m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+                    /* Fall through */
+
+                case 2:
+                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+                    return m;
+
+                default:
+                    return NULL;
+            }
+
+        case PA_CHANNEL_MAP_AUX: {
+            unsigned i;
+
+            for (i = 0; i < channels; i++)
+                m->map[i] = PA_CHANNEL_POSITION_AUX0 + i;
+
+            return m;
+        }
+
+        case PA_CHANNEL_MAP_WAVEEX:
+
+            /* Following http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EKLAC */
+
+            switch (channels) {
+                case 1:
+                    m->map[0] = PA_CHANNEL_POSITION_MONO;
+                    return m;
+
+                case 18:
+                    m->map[15] = PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+                    m->map[16] = PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+                    m->map[17] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+                    /* Fall through */
+
+                case 15:
+                    m->map[12] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
+                    m->map[13] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
+                    m->map[14] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
+                    /* Fall through */
+
+                case 12:
+                    m->map[11] = PA_CHANNEL_POSITION_TOP_CENTER;
+                    /* Fall through */
+
+                case 11:
+                    m->map[9] = PA_CHANNEL_POSITION_SIDE_LEFT;
+                    m->map[10] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+                    /* Fall through */
+
+                case 9:
+                    m->map[8] = PA_CHANNEL_POSITION_REAR_CENTER;
+                    /* Fall through */
+
+                case 8:
+                    m->map[6] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
+                    m->map[7] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+                    /* Fall through */
+
+                case 6:
+                    m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
+                    m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
+                    /* Fall through */
+
+                case 4:
+                    m->map[3] = PA_CHANNEL_POSITION_LFE;
+                    /* Fall through */
+
+                case 3:
+                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+                    /* Fall through */
+
+                case 2:
+                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+                    return m;
+
+                default:
+                    return NULL;
+            }
+
+        case PA_CHANNEL_MAP_OSS:
+
+            switch (channels) {
+                case 1:
+                    m->map[0] = PA_CHANNEL_POSITION_MONO;
+                    return m;
+
+                case 8:
+                    m->map[6] = PA_CHANNEL_POSITION_REAR_LEFT;
+                    m->map[7] = PA_CHANNEL_POSITION_REAR_RIGHT;
+                    /* Fall through */
+
+                case 6:
+                    m->map[4] = PA_CHANNEL_POSITION_SIDE_LEFT;
+                    m->map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+                    /* Fall through */
+
+                case 4:
+                    m->map[3] = PA_CHANNEL_POSITION_LFE;
+                    /* Fall through */
+
+                case 3:
+                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+                    /* Fall through */
+
+                case 2:
+                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+                    return m;
+
+                default:
+                    return NULL;
+            }
+
+        default:
+            pa_assert_not_reached();
+    }
+}
+
+pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
+    unsigned c;
+
+    pa_assert(m);
+    pa_assert(pa_channels_valid(channels));
+    pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
+
+    pa_channel_map_init(m);
+
+    for (c = channels; c > 0; c--) {
+
+        if (pa_channel_map_init_auto(m, c, def)) {
+            unsigned i = 0;
+
+            for (; c < channels; c++) {
+
+                m->map[c] = PA_CHANNEL_POSITION_AUX0 + i;
+                i++;
+            }
+
+            m->channels = (uint8_t) channels;
+
+            return m;
+        }
+    }
+
+    return NULL;
+}
+
+const char* pa_channel_position_to_string(pa_channel_position_t pos) {
+
+    if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
+        return NULL;
+
+    return table[pos];
+}
+
+const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) {
+
+    if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
+        return NULL;
+
+    pa_init_i18n();
+
+    return _(pretty_table[pos]);
+}
+
+int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
+    unsigned c;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    pa_return_val_if_fail(pa_channel_map_valid(a), 0);
+
+    if (PA_UNLIKELY(a == b))
+        return 1;
+
+    pa_return_val_if_fail(pa_channel_map_valid(b), 0);
+
+    if (a->channels != b->channels)
+        return 0;
+
+    for (c = 0; c < a->channels; c++)
+        if (a->map[c] != b->map[c])
+            return 0;
+
+    return 1;
+}
+
+char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
+    unsigned channel;
+    bool first = true;
+    char *e;
+
+    pa_assert(s);
+    pa_assert(l > 0);
+    pa_assert(map);
+
+    pa_init_i18n();
+
+    if (!pa_channel_map_valid(map)) {
+        pa_snprintf(s, l, _("(invalid)"));
+        return s;
+    }
+
+    *(e = s) = 0;
+
+    for (channel = 0; channel < map->channels && l > 1; channel++) {
+        l -= pa_snprintf(e, l, "%s%s",
+                      first ? "" : ",",
+                      pa_channel_position_to_string(map->map[channel]));
+
+        e = strchr(e, 0);
+        first = false;
+    }
+
+    return s;
+}
+
+pa_channel_position_t pa_channel_position_from_string(const char *p) {
+    pa_channel_position_t i;
+    pa_assert(p);
+
+    /* Some special aliases */
+    if (pa_streq(p, "left"))
+        return PA_CHANNEL_POSITION_LEFT;
+    else if (pa_streq(p, "right"))
+        return PA_CHANNEL_POSITION_RIGHT;
+    else if (pa_streq(p, "center"))
+        return PA_CHANNEL_POSITION_CENTER;
+    else if (pa_streq(p, "subwoofer"))
+        return PA_CHANNEL_POSITION_SUBWOOFER;
+
+    for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
+        if (pa_streq(p, table[i]))
+            return i;
+
+    return PA_CHANNEL_POSITION_INVALID;
+}
+
+pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
+    const char *state;
+    pa_channel_map map;
+    char *p;
+
+    pa_assert(rmap);
+    pa_assert(s);
+
+    pa_channel_map_init(&map);
+
+    /* We don't need to match against the well known channel mapping
+     * "mono" here explicitly, because that can be understood as
+     * listing with one channel called "mono". */
+
+    if (pa_streq(s, "stereo")) {
+        map.channels = 2;
+        map.map[0] = PA_CHANNEL_POSITION_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_RIGHT;
+        goto finish;
+    } else if (pa_streq(s, "surround-21")) {
+        map.channels = 3;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_LFE;
+        goto finish;
+    } else if (pa_streq(s, "surround-40")) {
+        map.channels = 4;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        goto finish;
+    } else if (pa_streq(s, "surround-41")) {
+        map.channels = 5;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_LFE;
+        goto finish;
+    } else if (pa_streq(s, "surround-50")) {
+        map.channels = 5;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+        goto finish;
+    } else if (pa_streq(s, "surround-51")) {
+        map.channels = 6;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+        map.map[5] = PA_CHANNEL_POSITION_LFE;
+        goto finish;
+    } else if (pa_streq(s, "surround-71")) {
+        map.channels = 8;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+        map.map[5] = PA_CHANNEL_POSITION_LFE;
+        map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
+        map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+        goto finish;
+    }
+
+    state = NULL;
+    map.channels = 0;
+
+    while ((p = pa_split(s, ",", &state))) {
+        pa_channel_position_t f;
+
+        if (map.channels >= PA_CHANNELS_MAX) {
+            pa_xfree(p);
+            return NULL;
+        }
+
+        if ((f = pa_channel_position_from_string(p)) == PA_CHANNEL_POSITION_INVALID) {
+            pa_xfree(p);
+            return NULL;
+        }
+
+        map.map[map.channels++] = f;
+        pa_xfree(p);
+    }
+
+finish:
+
+    if (!pa_channel_map_valid(&map))
+        return NULL;
+
+    *rmap = map;
+    return rmap;
+}
+
+int pa_channel_map_valid(const pa_channel_map *map) {
+    unsigned c;
+
+    pa_assert(map);
+
+    if (!pa_channels_valid(map->channels))
+        return 0;
+
+    for (c = 0; c < map->channels; c++)
+        if (map->map[c] < 0 || map->map[c] >= PA_CHANNEL_POSITION_MAX)
+            return 0;
+
+    return 1;
+}
+
+int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) {
+    pa_assert(map);
+    pa_assert(ss);
+
+    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+    pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
+
+    return map->channels == ss->channels;
+}
+
+int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
+    pa_channel_position_mask_t am, bm;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    pa_return_val_if_fail(pa_channel_map_valid(a), 0);
+
+    if (PA_UNLIKELY(a == b))
+        return 1;
+
+    pa_return_val_if_fail(pa_channel_map_valid(b), 0);
+
+    am = pa_channel_map_mask(a);
+    bm = pa_channel_map_mask(b);
+
+    return (bm & am) == bm;
+}
+
+int pa_channel_map_can_balance(const pa_channel_map *map) {
+    pa_channel_position_mask_t m;
+
+    pa_assert(map);
+    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+
+    m = pa_channel_map_mask(map);
+
+    return
+        (PA_CHANNEL_POSITION_MASK_LEFT & m) &&
+        (PA_CHANNEL_POSITION_MASK_RIGHT & m);
+}
+
+int pa_channel_map_can_fade(const pa_channel_map *map) {
+    pa_channel_position_mask_t m;
+
+    pa_assert(map);
+    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+
+    m = pa_channel_map_mask(map);
+
+    return
+        (PA_CHANNEL_POSITION_MASK_FRONT & m) &&
+        (PA_CHANNEL_POSITION_MASK_REAR & m);
+}
+
+int pa_channel_map_can_lfe_balance(const pa_channel_map *map) {
+    pa_channel_position_mask_t m;
+
+    pa_assert(map);
+    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+
+    m = pa_channel_map_mask(map);
+
+    return
+        (PA_CHANNEL_POSITION_MASK_LFE & m) &&
+        (PA_CHANNEL_POSITION_MASK_HFE & m);
+}
+
+const char* pa_channel_map_to_name(const pa_channel_map *map) {
+    pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
+    unsigned c;
+
+    pa_assert(map);
+
+    pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+
+    memset(in_map, 0, sizeof(in_map));
+
+    for (c = 0; c < map->channels; c++)
+        pa_bitset_set(in_map, map->map[c], true);
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_MONO, -1))
+        return "mono";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
+        return "stereo";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
+        return "surround-40";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_LFE, -1))
+        return "surround-41";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, -1))
+        return "surround-50";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
+        return "surround-51";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+                         PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
+        return "surround-71";
+
+    return NULL;
+}
+
+const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) {
+    pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
+    unsigned c;
+
+    pa_assert(map);
+
+    pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+
+    memset(in_map, 0, sizeof(in_map));
+
+    for (c = 0; c < map->channels; c++)
+        pa_bitset_set(in_map, map->map[c], true);
+
+    pa_init_i18n();
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_MONO, -1))
+        return _("Mono");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
+        return _("Stereo");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
+        return _("Surround 4.0");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_LFE, -1))
+        return _("Surround 4.1");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, -1))
+        return _("Surround 5.0");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
+        return _("Surround 5.1");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+                         PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
+        return _("Surround 7.1");
+
+    return NULL;
+}
+
+int pa_channel_map_has_position(const pa_channel_map *map, pa_channel_position_t p) {
+    unsigned c;
+
+    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+    pa_return_val_if_fail(p < PA_CHANNEL_POSITION_MAX, 0);
+
+    for (c = 0; c < map->channels; c++)
+        if (map->map[c] == p)
+            return 1;
+
+    return 0;
+}
+
+pa_channel_position_mask_t pa_channel_map_mask(const pa_channel_map *map) {
+    unsigned c;
+    pa_channel_position_mask_t r = 0;
+
+    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+
+    for (c = 0; c < map->channels; c++)
+        r |= PA_CHANNEL_POSITION_MASK(map->map[c]);
+
+    return r;
+}
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
new file mode 100644 (file)
index 0000000..6eabe20
--- /dev/null
@@ -0,0 +1,366 @@
+#ifndef foochannelmaphfoo
+#define foochannelmaphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+#include <pulse/version.h>
+
+/** \page channelmap Channel Maps
+ *
+ * \section overv_sec Overview
+ *
+ * Channel maps provide a way to associate channels in a stream with a
+ * specific speaker position. This relieves applications of having to
+ * make sure their channel order is identical to the final output.
+ *
+ * \section init_sec Initialisation
+ *
+ * A channel map consists of an array of \ref pa_channel_position values,
+ * one for each channel. This array is stored together with a channel count
+ * in a pa_channel_map structure.
+ *
+ * Before filling the structure, the application must initialise it using
+ * pa_channel_map_init(). There are also a number of convenience functions
+ * for standard channel mappings:
+ *
+ * \li pa_channel_map_init_mono() - Create a channel map with only mono audio.
+ * \li pa_channel_map_init_stereo() - Create a standard stereo mapping.
+ * \li pa_channel_map_init_auto() - Create a standard channel map for a specific number of channels
+ * \li pa_channel_map_init_extend() - Similar to
+ * pa_channel_map_init_auto() but synthesize a channel map if no
+ * predefined one is known for the specified number of channels.
+ *
+ * \section conv_sec Convenience Functions
+ *
+ * The library contains a number of convenience functions for dealing with
+ * channel maps:
+ *
+ * \li pa_channel_map_valid() - Tests if a channel map is valid.
+ * \li pa_channel_map_equal() - Tests if two channel maps are identical.
+ * \li pa_channel_map_snprint() - Creates a textual description of a channel
+ *                                map.
+ */
+
+/** \file
+ * Constants and routines for channel mapping handling
+ *
+ * See also \subpage channelmap
+ */
+
+PA_C_DECL_BEGIN
+
+/** A list of channel labels */
+typedef enum pa_channel_position {
+    PA_CHANNEL_POSITION_INVALID = -1,
+    PA_CHANNEL_POSITION_MONO = 0,
+
+    PA_CHANNEL_POSITION_FRONT_LEFT,               /**< Apple, Dolby call this 'Left' */
+    PA_CHANNEL_POSITION_FRONT_RIGHT,              /**< Apple, Dolby call this 'Right' */
+    PA_CHANNEL_POSITION_FRONT_CENTER,             /**< Apple, Dolby call this 'Center' */
+
+/** \cond fulldocs */
+    PA_CHANNEL_POSITION_LEFT = PA_CHANNEL_POSITION_FRONT_LEFT,
+    PA_CHANNEL_POSITION_RIGHT = PA_CHANNEL_POSITION_FRONT_RIGHT,
+    PA_CHANNEL_POSITION_CENTER = PA_CHANNEL_POSITION_FRONT_CENTER,
+/** \endcond */
+
+    PA_CHANNEL_POSITION_REAR_CENTER,              /**< Microsoft calls this 'Back Center', Apple calls this 'Center Surround', Dolby calls this 'Surround Rear Center' */
+    PA_CHANNEL_POSITION_REAR_LEFT,                /**< Microsoft calls this 'Back Left', Apple calls this 'Left Surround' (!), Dolby calls this 'Surround Rear Left'  */
+    PA_CHANNEL_POSITION_REAR_RIGHT,               /**< Microsoft calls this 'Back Right', Apple calls this 'Right Surround' (!), Dolby calls this 'Surround Rear Right'  */
+
+    PA_CHANNEL_POSITION_LFE,                      /**< Microsoft calls this 'Low Frequency', Apple calls this 'LFEScreen' */
+/** \cond fulldocs */
+    PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE,
+/** \endcond */
+
+    PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,     /**< Apple, Dolby call this 'Left Center' */
+    PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,    /**< Apple, Dolby call this 'Right Center */
+
+    PA_CHANNEL_POSITION_SIDE_LEFT,                /**< Apple calls this 'Left Surround Direct', Dolby calls this 'Surround Left' (!) */
+    PA_CHANNEL_POSITION_SIDE_RIGHT,               /**< Apple calls this 'Right Surround Direct', Dolby calls this 'Surround Right' (!) */
+
+    PA_CHANNEL_POSITION_AUX0,
+    PA_CHANNEL_POSITION_AUX1,
+    PA_CHANNEL_POSITION_AUX2,
+    PA_CHANNEL_POSITION_AUX3,
+    PA_CHANNEL_POSITION_AUX4,
+    PA_CHANNEL_POSITION_AUX5,
+    PA_CHANNEL_POSITION_AUX6,
+    PA_CHANNEL_POSITION_AUX7,
+    PA_CHANNEL_POSITION_AUX8,
+    PA_CHANNEL_POSITION_AUX9,
+    PA_CHANNEL_POSITION_AUX10,
+    PA_CHANNEL_POSITION_AUX11,
+    PA_CHANNEL_POSITION_AUX12,
+    PA_CHANNEL_POSITION_AUX13,
+    PA_CHANNEL_POSITION_AUX14,
+    PA_CHANNEL_POSITION_AUX15,
+    PA_CHANNEL_POSITION_AUX16,
+    PA_CHANNEL_POSITION_AUX17,
+    PA_CHANNEL_POSITION_AUX18,
+    PA_CHANNEL_POSITION_AUX19,
+    PA_CHANNEL_POSITION_AUX20,
+    PA_CHANNEL_POSITION_AUX21,
+    PA_CHANNEL_POSITION_AUX22,
+    PA_CHANNEL_POSITION_AUX23,
+    PA_CHANNEL_POSITION_AUX24,
+    PA_CHANNEL_POSITION_AUX25,
+    PA_CHANNEL_POSITION_AUX26,
+    PA_CHANNEL_POSITION_AUX27,
+    PA_CHANNEL_POSITION_AUX28,
+    PA_CHANNEL_POSITION_AUX29,
+    PA_CHANNEL_POSITION_AUX30,
+    PA_CHANNEL_POSITION_AUX31,
+
+    PA_CHANNEL_POSITION_TOP_CENTER,               /**< Apple calls this 'Top Center Surround' */
+
+    PA_CHANNEL_POSITION_TOP_FRONT_LEFT,           /**< Apple calls this 'Vertical Height Left' */
+    PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,          /**< Apple calls this 'Vertical Height Right' */
+    PA_CHANNEL_POSITION_TOP_FRONT_CENTER,         /**< Apple calls this 'Vertical Height Center' */
+
+    PA_CHANNEL_POSITION_TOP_REAR_LEFT,            /**< Microsoft and Apple call this 'Top Back Left' */
+    PA_CHANNEL_POSITION_TOP_REAR_RIGHT,           /**< Microsoft and Apple call this 'Top Back Right' */
+    PA_CHANNEL_POSITION_TOP_REAR_CENTER,          /**< Microsoft and Apple call this 'Top Back Center' */
+
+    PA_CHANNEL_POSITION_MAX
+} pa_channel_position_t;
+
+/** \cond fulldocs */
+#define PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
+#define PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
+#define PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
+#define PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
+#define PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
+#define PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
+#define PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
+#define PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
+#define PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
+#define PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
+#define PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
+#define PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
+#define PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
+#define PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
+#define PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
+#define PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
+#define PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
+#define PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
+#define PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
+#define PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
+#define PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
+#define PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
+#define PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
+#define PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
+#define PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
+#define PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
+#define PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
+#define PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
+#define PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
+#define PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
+#define PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
+#define PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
+#define PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
+#define PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
+#define PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
+#define PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
+#define PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
+#define PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
+#define PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
+#define PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
+#define PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
+#define PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
+#define PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
+#define PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
+#define PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
+#define PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
+#define PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
+#define PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
+#define PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
+#define PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
+#define PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
+#define PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
+#define PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
+#define PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
+#define PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
+#define PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
+#define PA_CHANNEL_POSITION_MAX PA_CHANNEL_POSITION_MAX
+/** \endcond */
+
+/** A mask of channel positions. \since 0.9.16 */
+typedef uint64_t pa_channel_position_mask_t;
+
+/** Makes a bit mask from a channel position. \since 0.9.16 */
+#define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1ULL << (f)))
+
+/** A list of channel mapping definitions for pa_channel_map_init_auto() */
+typedef enum pa_channel_map_def {
+    PA_CHANNEL_MAP_AIFF,
+    /**< The mapping from RFC3551, which is based on AIFF-C */
+
+/** \cond fulldocs */
+    PA_CHANNEL_MAP_ALSA,
+    /**< The default mapping used by ALSA. This mapping is probably
+     * not too useful since ALSA's default channel mapping depends on
+     * the device string used. */
+/** \endcond */
+
+    PA_CHANNEL_MAP_AUX,
+    /**< Only aux channels */
+
+    PA_CHANNEL_MAP_WAVEEX,
+    /**< Microsoft's WAVEFORMATEXTENSIBLE mapping. This mapping works
+     * as if all LSBs of dwChannelMask are set.  */
+
+/** \cond fulldocs */
+    PA_CHANNEL_MAP_OSS,
+    /**< The default channel mapping used by OSS as defined in the OSS
+     * 4.0 API specs. This mapping is probably not too useful since
+     * the OSS API has changed in this respect and no longer knows a
+     * default channel mapping based on the number of channels. */
+/** \endcond */
+
+    /**< Upper limit of valid channel mapping definitions */
+    PA_CHANNEL_MAP_DEF_MAX,
+
+    PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF
+    /**< The default channel map */
+} pa_channel_map_def_t;
+
+/** \cond fulldocs */
+#define PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
+#define PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
+#define PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
+#define PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
+#define PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
+#define PA_CHANNEL_MAP_DEF_MAX PA_CHANNEL_MAP_DEF_MAX
+#define PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
+/** \endcond */
+
+/** A channel map which can be used to attach labels to specific
+ * channels of a stream. These values are relevant for conversion and
+ * mixing of streams */
+typedef struct pa_channel_map {
+    uint8_t channels;
+    /**< Number of channels */
+
+    pa_channel_position_t map[PA_CHANNELS_MAX];
+    /**< Channel labels */
+} pa_channel_map;
+
+/** Initialize the specified channel map and return a pointer to
+ * it. The channel map will have a defined state but
+ * pa_channel_map_valid() will fail for it. */
+pa_channel_map* pa_channel_map_init(pa_channel_map *m);
+
+/** Initialize the specified channel map for monaural audio and return a pointer to it */
+pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m);
+
+/** Initialize the specified channel map for stereophonic audio and return a pointer to it */
+pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m);
+
+/** Initialize the specified channel map for the specified number of
+ * channels using default labels and return a pointer to it. This call
+ * will fail (return NULL) if there is no default channel map known for this
+ * specific number of channels and mapping. */
+pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def);
+
+/** Similar to pa_channel_map_init_auto() but instead of failing if no
+ * default mapping is known with the specified parameters it will
+ * synthesize a mapping based on a known mapping with fewer channels
+ * and fill up the rest with AUX0...AUX31 channels  \since 0.9.11 */
+pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def);
+
+/** Return a text label for the specified channel position */
+const char* pa_channel_position_to_string(pa_channel_position_t pos) PA_GCC_PURE;
+
+/** The inverse of pa_channel_position_to_string(). \since 0.9.16 */
+pa_channel_position_t pa_channel_position_from_string(const char *s) PA_GCC_PURE;
+
+/** Return a human readable text label for the specified channel position. \since 0.9.7 */
+const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos);
+
+/** The maximum length of strings returned by
+ * pa_channel_map_snprint(). Please note that this value can change
+ * with any release without warning and without being considered API
+ * or ABI breakage. You should not use this definition anywhere where
+ * it might become part of an ABI. */
+#define PA_CHANNEL_MAP_SNPRINT_MAX 336
+
+/** Make a human readable string from the specified channel map */
+char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map);
+
+/** Parse a channel position list or well-known mapping name into a
+ * channel map structure. This turns the output of
+ * pa_channel_map_snprint() and pa_channel_map_to_name() back into a
+ * pa_channel_map */
+pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s);
+
+/** Compare two channel maps. Return 1 if both match. */
+int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) PA_GCC_PURE;
+
+/** Return non-zero if the specified channel map is considered valid */
+int pa_channel_map_valid(const pa_channel_map *map) PA_GCC_PURE;
+
+/** Return non-zero if the specified channel map is compatible with
+ * the specified sample spec. \since 0.9.12 */
+int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) PA_GCC_PURE;
+
+/** Returns non-zero if every channel defined in b is also defined in a. \since 0.9.15 */
+int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) PA_GCC_PURE;
+
+/** Returns non-zero if it makes sense to apply a volume 'balance'
+ * with this mapping, i.e.\ if there are left/right channels
+ * available. \since 0.9.15 */
+int pa_channel_map_can_balance(const pa_channel_map *map) PA_GCC_PURE;
+
+/** Returns non-zero if it makes sense to apply a volume 'fade'
+ * (i.e.\ 'balance' between front and rear) with this mapping, i.e.\ if
+ * there are front/rear channels available. \since 0.9.15 */
+int pa_channel_map_can_fade(const pa_channel_map *map) PA_GCC_PURE;
+
+/** Returns non-zero if it makes sense to apply a volume 'lfe balance'
+ * (i.e.\ 'balance' between LFE and non-LFE channels) with this mapping,
+ *  i.e.\ if there are LFE and non-LFE channels available. \since 8.0 */
+int pa_channel_map_can_lfe_balance(const pa_channel_map *map) PA_GCC_PURE;
+
+/** Tries to find a well-known channel mapping name for this channel
+ * mapping, i.e.\ "stereo", "surround-71" and so on. If the channel
+ * mapping is unknown NULL will be returned. This name can be parsed
+ * with pa_channel_map_parse() \since 0.9.15 */
+const char* pa_channel_map_to_name(const pa_channel_map *map) PA_GCC_PURE;
+
+/** Tries to find a human readable text label for this channel
+mapping, i.e.\ "Stereo", "Surround 7.1" and so on. If the channel
+mapping is unknown NULL will be returned. \since 0.9.15 */
+const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) PA_GCC_PURE;
+
+/** Returns non-zero if the specified channel position is available at
+ * least once in the channel map. \since 0.9.16 */
+int pa_channel_map_has_position(const pa_channel_map *map, pa_channel_position_t p) PA_GCC_PURE;
+
+/** Generates a bit mask from a channel map. \since 0.9.16 */
+pa_channel_position_mask_t pa_channel_map_mask(const pa_channel_map *map) PA_GCC_PURE;
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c
new file mode 100644 (file)
index 0000000..60c645e
--- /dev/null
@@ -0,0 +1,109 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <xcb/xcb.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/x11prop.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "client-conf-x11.h"
+
+int pa_client_conf_from_x11(pa_client_conf *c) {
+    const char *dname;
+    xcb_connection_t *xcb = NULL;
+    int ret = -1, screen = 0;
+    char t[1024];
+
+    pa_assert(c);
+
+    if (!(dname = getenv("DISPLAY")))
+        goto finish;
+
+    if (*dname == 0)
+        goto finish;
+
+    if (!(xcb = xcb_connect(dname, NULL))) {
+        pa_log(_("xcb_connect() failed"));
+        goto finish;
+    }
+
+    if (xcb_connection_has_error(xcb)) {
+        pa_log(_("xcb_connection_has_error() returned true"));
+        goto finish;
+    }
+
+    if (pa_x11_get_prop(xcb, screen, "PULSE_SERVER", t, sizeof(t))) {
+        bool disable_autospawn = true;
+
+        pa_xfree(c->default_server);
+        c->default_server = pa_xstrdup(t);
+
+        if (pa_x11_get_prop(xcb, screen, "PULSE_SESSION_ID", t, sizeof(t))) {
+            char *id;
+
+            if ((id = pa_session_id())) {
+                if (pa_streq(t, id))
+                    disable_autospawn = false;
+                pa_xfree(id);
+            }
+        }
+
+        if (disable_autospawn)
+            c->autospawn = false;
+    }
+
+    if (pa_x11_get_prop(xcb, screen, "PULSE_SINK", t, sizeof(t))) {
+        pa_xfree(c->default_sink);
+        c->default_sink = pa_xstrdup(t);
+    }
+
+    if (pa_x11_get_prop(xcb, screen, "PULSE_SOURCE", t, sizeof(t))) {
+        pa_xfree(c->default_source);
+        c->default_source = pa_xstrdup(t);
+    }
+
+    if (pa_x11_get_prop(xcb, screen, "PULSE_COOKIE", t, sizeof(t))) {
+        if (pa_parsehex(t, c->cookie_from_x11, sizeof(c->cookie_from_x11)) != sizeof(c->cookie_from_x11)) {
+            pa_log(_("Failed to parse cookie data"));
+            goto finish;
+        }
+
+        c->cookie_from_x11_valid = true;
+    }
+
+    ret = 0;
+
+finish:
+    if (xcb)
+        xcb_disconnect(xcb);
+
+    return ret;
+
+}
diff --git a/src/pulse/client-conf-x11.h b/src/pulse/client-conf-x11.h
new file mode 100644 (file)
index 0000000..4f82041
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef fooclientconfx11hfoo
+#define fooclientconfx11hfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "client-conf.h"
+
+/* Load client configuration data from X11, overwriting the current settings in
+ * *c. */
+int pa_client_conf_from_x11(pa_client_conf *c);
+
+#endif
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
new file mode 100644 (file)
index 0000000..a3c9486
--- /dev/null
@@ -0,0 +1,239 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/conf-parser.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/authkey.h>
+
+#include "client-conf.h"
+
+#ifdef HAVE_X11
+#include <pulse/client-conf-x11.h>
+#endif
+
+#define DEFAULT_CLIENT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "client.conf"
+#define DEFAULT_CLIENT_CONFIG_FILE_USER "client.conf"
+
+#define ENV_CLIENT_CONFIG_FILE "PULSE_CLIENTCONFIG"
+#define ENV_DEFAULT_SINK "PULSE_SINK"
+#define ENV_DEFAULT_SOURCE "PULSE_SOURCE"
+#define ENV_DEFAULT_SERVER "PULSE_SERVER"
+#define ENV_DAEMON_BINARY "PULSE_BINARY"
+#define ENV_COOKIE_FILE "PULSE_COOKIE"
+
+static const pa_client_conf default_conf = {
+    .daemon_binary = NULL,
+    .extra_arguments = NULL,
+    .default_sink = NULL,
+    .default_source = NULL,
+    .default_server = NULL,
+    .default_dbus_server = NULL,
+    .cookie_file_from_env = NULL,
+    .cookie_from_x11_valid = false,
+    .cookie_file_from_application = NULL,
+    .cookie_file_from_client_conf = NULL,
+    .autospawn = true,
+    .disable_shm = false,
+    .shm_size = 0,
+    .auto_connect_localhost = false,
+    .auto_connect_display = false
+};
+
+pa_client_conf *pa_client_conf_new(void) {
+    pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
+
+    c->daemon_binary = pa_xstrdup(PA_BINARY);
+    c->extra_arguments = pa_xstrdup("--log-target=syslog");
+
+    return c;
+}
+
+void pa_client_conf_free(pa_client_conf *c) {
+    pa_assert(c);
+    pa_xfree(c->daemon_binary);
+    pa_xfree(c->extra_arguments);
+    pa_xfree(c->default_sink);
+    pa_xfree(c->default_source);
+    pa_xfree(c->default_server);
+    pa_xfree(c->default_dbus_server);
+    pa_xfree(c->cookie_file_from_env);
+    pa_xfree(c->cookie_file_from_application);
+    pa_xfree(c->cookie_file_from_client_conf);
+    pa_xfree(c);
+}
+
+static void load_env(pa_client_conf *c) {
+    char *e;
+
+    if ((e = getenv(ENV_DEFAULT_SINK))) {
+        pa_xfree(c->default_sink);
+        c->default_sink = pa_xstrdup(e);
+    }
+
+    if ((e = getenv(ENV_DEFAULT_SOURCE))) {
+        pa_xfree(c->default_source);
+        c->default_source = pa_xstrdup(e);
+    }
+
+    if ((e = getenv(ENV_DEFAULT_SERVER))) {
+        pa_xfree(c->default_server);
+        c->default_server = pa_xstrdup(e);
+
+        /* We disable autospawning automatically if a specific server was set */
+        c->autospawn = false;
+    }
+
+    if ((e = getenv(ENV_DAEMON_BINARY))) {
+        pa_xfree(c->daemon_binary);
+        c->daemon_binary = pa_xstrdup(e);
+    }
+
+    if ((e = getenv(ENV_COOKIE_FILE)) && *e) {
+        pa_xfree(c->cookie_file_from_env);
+        c->cookie_file_from_env = pa_xstrdup(e);
+    }
+}
+
+void pa_client_conf_load(pa_client_conf *c, bool load_from_x11, bool load_from_env) {
+    FILE *f = NULL;
+    char *fn = NULL;
+
+    /* Prepare the configuration parse table */
+    pa_config_item table[] = {
+        { "daemon-binary",          pa_config_parse_string,   &c->daemon_binary, NULL },
+        { "extra-arguments",        pa_config_parse_string,   &c->extra_arguments, NULL },
+        { "default-sink",           pa_config_parse_string,   &c->default_sink, NULL },
+        { "default-source",         pa_config_parse_string,   &c->default_source, NULL },
+        { "default-server",         pa_config_parse_string,   &c->default_server, NULL },
+        { "default-dbus-server",    pa_config_parse_string,   &c->default_dbus_server, NULL },
+        { "autospawn",              pa_config_parse_bool,     &c->autospawn, NULL },
+        { "cookie-file",            pa_config_parse_string,   &c->cookie_file_from_client_conf, NULL },
+        { "disable-shm",            pa_config_parse_bool,     &c->disable_shm, NULL },
+        { "enable-shm",             pa_config_parse_not_bool, &c->disable_shm, NULL },
+        { "enable-memfd",           pa_config_parse_not_bool, &c->disable_memfd, NULL },
+        { "shm-size-bytes",         pa_config_parse_size,     &c->shm_size, NULL },
+        { "auto-connect-localhost", pa_config_parse_bool,     &c->auto_connect_localhost, NULL },
+        { "auto-connect-display",   pa_config_parse_bool,     &c->auto_connect_display, NULL },
+        { NULL,                     NULL,                     NULL, NULL },
+    };
+
+    f = pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn);
+    if (f) {
+        pa_config_parse(fn, f, table, NULL, true, NULL);
+        pa_xfree(fn);
+        fclose(f);
+    }
+
+    if (load_from_x11) {
+#ifdef HAVE_X11
+        pa_client_conf_from_x11(c);
+#endif
+    }
+
+    if (load_from_env)
+        load_env(c);
+}
+
+int pa_client_conf_load_cookie(pa_client_conf *c, uint8_t *cookie, size_t cookie_length) {
+    int r;
+    char *fallback_path;
+
+    pa_assert(c);
+    pa_assert(cookie);
+    pa_assert(cookie_length > 0);
+
+    if (c->cookie_file_from_env) {
+        r = pa_authkey_load(c->cookie_file_from_env, true, cookie, cookie_length);
+        if (r >= 0)
+            return 0;
+
+        pa_log_warn("Failed to load cookie from %s (configured with environment variable PULSE_COOKIE): %s",
+                    c->cookie_file_from_env, pa_cstrerror(errno));
+    }
+
+    if (c->cookie_from_x11_valid) {
+        if (cookie_length == sizeof(c->cookie_from_x11)) {
+            memcpy(cookie, c->cookie_from_x11, cookie_length);
+            return 0;
+        }
+
+        pa_log_warn("Failed to load cookie from X11 root window property PULSE_COOKIE: size mismatch.");
+    }
+
+    if (c->cookie_file_from_application) {
+        r = pa_authkey_load(c->cookie_file_from_application, true, cookie, cookie_length);
+        if (r >= 0)
+            return 0;
+
+        pa_log_warn("Failed to load cookie from %s (configured by the application): %s", c->cookie_file_from_application,
+                    pa_cstrerror(errno));
+    }
+
+    if (c->cookie_file_from_client_conf) {
+        r = pa_authkey_load(c->cookie_file_from_client_conf, true, cookie, cookie_length);
+        if (r >= 0)
+            return 0;
+
+        pa_log_warn("Failed to load cookie from %s (configured in client.conf): %s", c->cookie_file_from_client_conf,
+                    pa_cstrerror(errno));
+    }
+
+    r = pa_authkey_load(PA_NATIVE_COOKIE_FILE, false, cookie, cookie_length);
+    if (r >= 0)
+        return 0;
+
+    if (pa_append_to_home_dir(PA_NATIVE_COOKIE_FILE_FALLBACK, &fallback_path) >= 0) {
+        r = pa_authkey_load(fallback_path, false, cookie, cookie_length);
+        pa_xfree(fallback_path);
+        if (r >= 0)
+            return 0;
+    }
+
+    r = pa_authkey_load(PA_NATIVE_COOKIE_FILE, true, cookie, cookie_length);
+    if (r >= 0)
+        return 0;
+
+    pa_log("Failed to load cookie file from %s: %s", PA_NATIVE_COOKIE_FILE, pa_cstrerror(errno));
+    memset(cookie, 0, cookie_length);
+
+    return -1;
+}
+
+void pa_client_conf_set_cookie_file_from_application(pa_client_conf *c, const char *cookie_file) {
+    pa_assert(c);
+    pa_assert(!cookie_file || *cookie_file);
+
+    pa_xfree(c->cookie_file_from_application);
+    c->cookie_file_from_application = pa_xstrdup(cookie_file);
+}
diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h
new file mode 100644 (file)
index 0000000..7691ec7
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef fooclientconfhfoo
+#define fooclientconfhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/macro.h>
+#include <pulsecore/native-common.h>
+
+/* A structure containing configuration data for PulseAudio clients. */
+
+typedef struct pa_client_conf {
+    char *daemon_binary;
+    char *extra_arguments;
+    char *default_sink;
+    char *default_source;
+    char *default_server;
+    char *default_dbus_server;
+    char *cookie_file_from_env;
+    uint8_t cookie_from_x11[PA_NATIVE_COOKIE_LENGTH];
+    bool cookie_from_x11_valid;
+    char *cookie_file_from_application;
+    char *cookie_file_from_client_conf;
+    bool autospawn, disable_shm, disable_memfd, auto_connect_localhost, auto_connect_display;
+    size_t shm_size;
+} pa_client_conf;
+
+/* Create a new configuration data object and reset it to defaults */
+pa_client_conf *pa_client_conf_new(void);
+void pa_client_conf_free(pa_client_conf *c);
+
+/* Load the configuration data from the client configuration file and
+ * optionally from X11 and/or environment variables, overwriting the current
+ * settings in *c. */
+void pa_client_conf_load(pa_client_conf *c, bool load_from_x11, bool load_from_env);
+
+/* Load the cookie from the cookie sources specified in the configuration, or
+ * if nothing is specified or none of the sources work, load the cookie from
+ * the default source. If the default source doesn't work either, this function
+ * returns a negative value and initializes the cookie to all-zeroes. */
+int pa_client_conf_load_cookie(pa_client_conf *c, uint8_t *cookie, size_t cookie_length);
+
+void pa_client_conf_set_cookie_file_from_application(pa_client_conf *c, const char *cookie_file);
+
+#endif
diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in
new file mode 100644 (file)
index 0000000..26b7790
--- /dev/null
@@ -0,0 +1,35 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+## Configuration file for PulseAudio clients. See pulse-client.conf(5) for
+## more information. Default values are commented out.  Use either ; or # for
+## commenting.
+
+; default-sink =
+; default-source =
+; default-server =
+; default-dbus-server =
+
+; autospawn = yes
+; daemon-binary = @PA_BINARY@
+; extra-arguments = --log-target=syslog
+
+; cookie-file =
+
+; enable-shm = yes
+; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
+
+; auto-connect-localhost = no
+; auto-connect-display = no
diff --git a/src/pulse/context.c b/src/pulse/context.c
new file mode 100644 (file)
index 0000000..6adfc5a
--- /dev/null
@@ -0,0 +1,1646 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include <pulse/version.h>
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulse/mainloop.h>
+#include <pulse/timeval.h>
+#include <pulse/fork-detect.h>
+#include <pulse/client-conf.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/socket-client.h>
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/proplist-util.h>
+
+#include "internal.h"
+#include "context.h"
+
+void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void pa_command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void pa_command_disable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void pa_command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
+    [PA_COMMAND_REQUEST] = pa_command_request,
+    [PA_COMMAND_OVERFLOW] = pa_command_overflow_or_underflow,
+    [PA_COMMAND_UNDERFLOW] = pa_command_overflow_or_underflow,
+    [PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed,
+    [PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed,
+    [PA_COMMAND_PLAYBACK_STREAM_MOVED] = pa_command_stream_moved,
+    [PA_COMMAND_RECORD_STREAM_MOVED] = pa_command_stream_moved,
+    [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended,
+    [PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
+    [PA_COMMAND_STARTED] = pa_command_stream_started,
+    [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event,
+    [PA_COMMAND_EXTENSION] = pa_command_extension,
+    [PA_COMMAND_PLAYBACK_STREAM_EVENT] = pa_command_stream_event,
+    [PA_COMMAND_RECORD_STREAM_EVENT] = pa_command_stream_event,
+    [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event,
+    [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr,
+    [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr,
+    [PA_COMMAND_ENABLE_SRBCHANNEL] = pa_command_enable_srbchannel,
+    [PA_COMMAND_DISABLE_SRBCHANNEL] = pa_command_disable_srbchannel,
+    [PA_COMMAND_REGISTER_MEMFD_SHMID] = pa_command_register_memfd_shmid,
+};
+static void context_free(pa_context *c);
+
+#ifdef HAVE_DBUS
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata);
+#endif
+
+pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
+    return pa_context_new_with_proplist(mainloop, name, NULL);
+}
+
+static void reset_callbacks(pa_context *c) {
+    pa_assert(c);
+
+    c->state_callback = NULL;
+    c->state_userdata = NULL;
+
+    c->subscribe_callback = NULL;
+    c->subscribe_userdata = NULL;
+
+    c->event_callback = NULL;
+    c->event_userdata = NULL;
+
+    c->ext_device_manager.callback = NULL;
+    c->ext_device_manager.userdata = NULL;
+
+    c->ext_device_restore.callback = NULL;
+    c->ext_device_restore.userdata = NULL;
+
+    c->ext_stream_restore.callback = NULL;
+    c->ext_stream_restore.userdata = NULL;
+}
+
+pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
+    pa_context *c;
+    pa_mem_type_t type;
+
+    pa_assert(mainloop);
+
+    if (pa_detect_fork())
+        return NULL;
+
+    pa_init_i18n();
+
+    c = pa_xnew0(pa_context, 1);
+    PA_REFCNT_INIT(c);
+
+    c->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
+
+    if (name)
+        pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+
+#ifdef HAVE_DBUS
+    c->system_bus = c->session_bus = NULL;
+#endif
+    c->mainloop = mainloop;
+    c->playback_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->record_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->client_index = PA_INVALID_INDEX;
+    c->use_rtclock = pa_mainloop_is_our_api(mainloop);
+
+    PA_LLIST_HEAD_INIT(pa_stream, c->streams);
+    PA_LLIST_HEAD_INIT(pa_operation, c->operations);
+
+    c->error = PA_OK;
+    c->state = PA_CONTEXT_UNCONNECTED;
+
+    reset_callbacks(c);
+
+#ifndef MSG_NOSIGNAL
+#ifdef SIGPIPE
+    pa_check_signal_is_blocked(SIGPIPE);
+#endif
+#endif
+
+    c->conf = pa_client_conf_new();
+    pa_client_conf_load(c->conf, true, true);
+
+    c->srb_template.readfd = -1;
+    c->srb_template.writefd = -1;
+
+    c->memfd_on_local = (!c->conf->disable_memfd && pa_memfd_is_locally_supported());
+
+    type = (c->conf->disable_shm) ? PA_MEM_TYPE_PRIVATE :
+           ((!c->memfd_on_local) ?
+               PA_MEM_TYPE_SHARED_POSIX : PA_MEM_TYPE_SHARED_MEMFD);
+
+    if (!(c->mempool = pa_mempool_new(type, c->conf->shm_size, true))) {
+
+        if (!c->conf->disable_shm) {
+            pa_log_warn("Failed to allocate shared memory pool. Falling back to a normal private one.");
+            c->mempool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, c->conf->shm_size, true);
+        }
+
+        if (!c->mempool) {
+            context_free(c);
+            return NULL;
+        }
+    }
+
+    return c;
+}
+
+static void context_unlink(pa_context *c) {
+    pa_stream *s;
+
+    pa_assert(c);
+
+    s = c->streams ? pa_stream_ref(c->streams) : NULL;
+    while (s) {
+        pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
+        pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
+        pa_stream_unref(s);
+        s = n;
+    }
+
+    while (c->operations)
+        pa_operation_cancel(c->operations);
+
+    if (c->pdispatch) {
+        pa_pdispatch_unref(c->pdispatch);
+        c->pdispatch = NULL;
+    }
+
+    if (c->pstream) {
+        pa_pstream_unlink(c->pstream);
+        pa_pstream_unref(c->pstream);
+        c->pstream = NULL;
+    }
+
+    if (c->srb_template.memblock) {
+        pa_memblock_unref(c->srb_template.memblock);
+        c->srb_template.memblock = NULL;
+    }
+
+    if (c->client) {
+        pa_socket_client_unref(c->client);
+        c->client = NULL;
+    }
+
+    reset_callbacks(c);
+}
+
+static void context_free(pa_context *c) {
+    pa_assert(c);
+
+    context_unlink(c);
+
+#ifdef HAVE_DBUS
+    if (c->system_bus) {
+        if (c->filter_added)
+            dbus_connection_remove_filter(pa_dbus_wrap_connection_get(c->system_bus), filter_cb, c);
+        pa_dbus_wrap_connection_free(c->system_bus);
+    }
+
+    if (c->session_bus) {
+        if (c->filter_added)
+            dbus_connection_remove_filter(pa_dbus_wrap_connection_get(c->session_bus), filter_cb, c);
+        pa_dbus_wrap_connection_free(c->session_bus);
+    }
+#endif
+
+    if (c->record_streams)
+        pa_hashmap_free(c->record_streams);
+    if (c->playback_streams)
+        pa_hashmap_free(c->playback_streams);
+
+    if (c->mempool)
+        pa_mempool_unref(c->mempool);
+
+    if (c->conf)
+        pa_client_conf_free(c->conf);
+
+    pa_strlist_free(c->server_list);
+
+    if (c->proplist)
+        pa_proplist_free(c->proplist);
+
+    pa_xfree(c->server);
+    pa_xfree(c);
+}
+
+pa_context* pa_context_ref(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_REFCNT_INC(c);
+    return c;
+}
+
+void pa_context_unref(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (PA_REFCNT_DEC(c) <= 0)
+        context_free(c);
+}
+
+void pa_context_set_state(pa_context *c, pa_context_state_t st) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (c->state == st)
+        return;
+
+    pa_context_ref(c);
+
+    c->state = st;
+
+    if (c->state_callback)
+        c->state_callback(c, c->state_userdata);
+
+    if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
+        context_unlink(c);
+
+    pa_context_unref(c);
+}
+
+int pa_context_set_error(pa_context *c, int error) {
+    pa_assert(error >= 0);
+    pa_assert(error < PA_ERR_MAX);
+
+    if (c)
+        c->error = error;
+
+    return error;
+}
+
+void pa_context_fail(pa_context *c, int error) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_set_error(c, error);
+    pa_context_set_state(c, PA_CONTEXT_FAILED);
+}
+
+static void pstream_die_callback(pa_pstream *p, void *userdata) {
+    pa_context *c = userdata;
+
+    pa_assert(p);
+    pa_assert(c);
+
+    pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED);
+}
+
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
+    pa_context *c = userdata;
+
+    pa_assert(p);
+    pa_assert(packet);
+    pa_assert(c);
+
+    pa_context_ref(c);
+
+    if (pa_pdispatch_run(c->pdispatch, packet, ancil_data, c) < 0)
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+
+    pa_context_unref(c);
+}
+
+static void handle_srbchannel_memblock(pa_context *c, pa_memblock *memblock) {
+    pa_srbchannel *sr;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+
+    /* Memblock sanity check */
+    if (!memblock) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    } else if (pa_memblock_is_read_only(memblock)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    } else if (pa_memblock_is_ours(memblock)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    /* Create the srbchannel */
+    c->srb_template.memblock = memblock;
+    pa_memblock_ref(memblock);
+    sr = pa_srbchannel_new_from_template(c->mainloop, &c->srb_template);
+    if (!sr) {
+        pa_log_warn("Failed to create srbchannel from template");
+        c->srb_template.readfd = -1;
+        c->srb_template.writefd = -1;
+        pa_memblock_unref(c->srb_template.memblock);
+        c->srb_template.memblock = NULL;
+        return;
+    }
+
+    /* Ack the enable command */
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_ENABLE_SRBCHANNEL);
+    pa_tagstruct_putu32(t, c->srb_setup_tag);
+    pa_pstream_send_tagstruct(c->pstream, t);
+
+    /* ...and switch over */
+    pa_pstream_set_srbchannel(c->pstream, sr);
+}
+
+static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+
+    pa_assert(p);
+    pa_assert(chunk);
+    pa_assert(chunk->length > 0);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->srb_template.readfd != -1 && c->srb_template.memblock == NULL) {
+        handle_srbchannel_memblock(c, chunk->memblock);
+        pa_context_unref(c);
+        return;
+    }
+
+    if ((s = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(channel)))) {
+
+        if (chunk->memblock) {
+            pa_memblockq_seek(s->record_memblockq, offset, seek, true);
+            pa_memblockq_push_align(s->record_memblockq, chunk);
+        } else
+            pa_memblockq_seek(s->record_memblockq, offset+chunk->length, seek, true);
+
+        if (s->read_callback) {
+            size_t l;
+
+            if ((l = pa_memblockq_get_length(s->record_memblockq)) > 0)
+                s->read_callback(s, l, s->read_userdata);
+        }
+    }
+
+    pa_context_unref(c);
+}
+
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, bool fail) {
+    uint32_t err;
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (command == PA_COMMAND_ERROR) {
+        pa_assert(t);
+
+        if (pa_tagstruct_getu32(t, &err) < 0 ||
+            !pa_tagstruct_eof(t)) {
+            pa_context_fail(c, PA_ERR_PROTOCOL);
+            return -1;
+        }
+
+    } else if (command == PA_COMMAND_TIMEOUT)
+        err = PA_ERR_TIMEOUT;
+    else {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return -1;
+    }
+
+    if (err == PA_OK) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return -1;
+    }
+
+    if (err >= PA_ERR_MAX)
+        err = PA_ERR_UNKNOWN;
+
+    if (fail) {
+        pa_context_fail(c, (int) err);
+        return -1;
+    }
+
+    pa_context_set_error(c, (int) err);
+
+    return 0;
+}
+
+static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+
+    pa_assert(pd);
+    pa_assert(c);
+    pa_assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME);
+
+    pa_context_ref(c);
+
+    if (command != PA_COMMAND_REPLY) {
+        pa_context_handle_error(c, command, t, true);
+        goto finish;
+    }
+
+    switch(c->state) {
+        case PA_CONTEXT_AUTHORIZING: {
+            pa_tagstruct *reply;
+            bool shm_on_remote = false;
+            bool memfd_on_remote = false;
+
+            if (pa_tagstruct_getu32(t, &c->version) < 0 ||
+                !pa_tagstruct_eof(t)) {
+                pa_context_fail(c, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            /* Minimum supported version */
+            if (c->version < 8) {
+                pa_context_fail(c, PA_ERR_VERSION);
+                goto finish;
+            }
+
+            /* Starting with protocol version 13 the MSB of the version
+               tag reflects if shm is available for this connection or
+               not. */
+            if ((c->version & PA_PROTOCOL_VERSION_MASK) >= 13) {
+                shm_on_remote = !!(c->version & PA_PROTOCOL_FLAG_SHM);
+
+                /* Starting with protocol version 31, the second MSB of the version
+                 * tag reflects whether memfd is supported on the other PA end. */
+                if ((c->version & PA_PROTOCOL_VERSION_MASK) >= 31)
+                    memfd_on_remote = !!(c->version & PA_PROTOCOL_FLAG_MEMFD);
+
+                /* Reserve the two most-significant _bytes_ of the version tag
+                 * for flags. */
+                c->version &= PA_PROTOCOL_VERSION_MASK;
+            }
+
+            pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
+
+            /* Enable shared memory support if possible */
+            if (c->do_shm)
+                if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
+                    c->do_shm = false;
+
+            if (c->do_shm) {
+
+                /* Only enable SHM if both sides are owned by the same
+                 * user. This is a security measure because otherwise
+                 * data private to the user might leak. */
+
+#ifdef HAVE_CREDS
+                const pa_creds *creds;
+                if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
+                    c->do_shm = false;
+#endif
+            }
+
+            pa_log_debug("Negotiated SHM: %s", pa_yes_no(c->do_shm));
+            pa_pstream_enable_shm(c->pstream, c->do_shm);
+
+            c->shm_type = PA_MEM_TYPE_PRIVATE;
+            if (c->do_shm) {
+                if (c->version >= 31 && memfd_on_remote && c->memfd_on_local) {
+                    const char *reason;
+
+                    pa_pstream_enable_memfd(c->pstream);
+                    if (pa_mempool_is_memfd_backed(c->mempool))
+                        if (pa_pstream_register_memfd_mempool(c->pstream, c->mempool, &reason))
+                            pa_log("Failed to regester memfd mempool. Reason: %s", reason);
+
+                    /* Even if memfd pool registration fails, the negotiated SHM type
+                     * shall remain memfd as both endpoints claim to support it. */
+                    c->shm_type = PA_MEM_TYPE_SHARED_MEMFD;
+                } else
+                    c->shm_type = PA_MEM_TYPE_SHARED_POSIX;
+            }
+
+            pa_log_debug("Memfd possible: %s", pa_yes_no(c->memfd_on_local));
+            pa_log_debug("Negotiated SHM type: %s", pa_mem_type_to_string(c->shm_type));
+
+            reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
+
+            if (c->version >= 13) {
+                pa_init_proplist(c->proplist);
+                pa_tagstruct_put_proplist(reply, c->proplist);
+            } else
+                pa_tagstruct_puts(reply, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
+
+            pa_pstream_send_tagstruct(c->pstream, reply);
+            pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
+
+            pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
+            break;
+        }
+
+        case PA_CONTEXT_SETTING_NAME :
+
+            if ((c->version >= 13 && (pa_tagstruct_getu32(t, &c->client_index) < 0 ||
+                                      c->client_index == PA_INVALID_INDEX)) ||
+                !pa_tagstruct_eof(t)) {
+                pa_context_fail(c, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            pa_context_set_state(c, PA_CONTEXT_READY);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+finish:
+    pa_context_unref(c);
+}
+
+static void setup_context(pa_context *c, pa_iochannel *io) {
+    uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(io);
+
+    pa_context_ref(c);
+
+    pa_assert(!c->pstream);
+    c->pstream = pa_pstream_new(c->mainloop, io, c->mempool);
+
+    pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
+    pa_pstream_set_receive_packet_callback(c->pstream, pstream_packet_callback, c);
+    pa_pstream_set_receive_memblock_callback(c->pstream, pstream_memblock_callback, c);
+
+    pa_assert(!c->pdispatch);
+    c->pdispatch = pa_pdispatch_new(c->mainloop, c->use_rtclock, command_table, PA_COMMAND_MAX);
+
+    if (pa_client_conf_load_cookie(c->conf, cookie, sizeof(cookie)) < 0)
+        pa_log_info("No cookie loaded. Attempting to connect without.");
+
+    t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag);
+
+    c->do_shm =
+        pa_mempool_is_shared(c->mempool) &&
+        c->is_local;
+
+    pa_log_debug("SHM possible: %s", pa_yes_no(c->do_shm));
+
+    /* Starting with protocol version 13 we use the MSB of the version
+     * tag for informing the other side if we could do SHM or not.
+     * Starting from version 31, second MSB is used to flag memfd support. */
+    pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION | (c->do_shm ? PA_PROTOCOL_FLAG_SHM : 0) |
+                        (c->memfd_on_local ? PA_PROTOCOL_FLAG_MEMFD: 0));
+    pa_tagstruct_put_arbitrary(t, cookie, sizeof(cookie));
+
+#ifdef HAVE_CREDS
+{
+    pa_creds ucred;
+
+    if (pa_iochannel_creds_supported(io))
+        pa_iochannel_creds_enable(io);
+
+    ucred.uid = getuid();
+    ucred.gid = getgid();
+
+    pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred);
+}
+#else
+    pa_pstream_send_tagstruct(c->pstream, t);
+#endif
+
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
+
+    pa_context_set_state(c, PA_CONTEXT_AUTHORIZING);
+
+    pa_context_unref(c);
+}
+
+static pa_strlist *prepend_per_user(pa_strlist *l) {
+    char *ufn;
+
+    /* The per-user instance */
+    if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
+        l = pa_strlist_prepend(l, ufn);
+        pa_xfree(ufn);
+    }
+
+    return l;
+}
+
+#ifndef OS_IS_WIN32
+
+static int context_autospawn(pa_context *c) {
+    pid_t pid;
+    int status, r;
+    struct sigaction sa;
+
+    pa_context_ref(c);
+
+    if (sigaction(SIGCHLD, NULL, &sa) < 0) {
+        pa_log_debug("sigaction() failed: %s", pa_cstrerror(errno));
+        pa_context_fail(c, PA_ERR_INTERNAL);
+        goto fail;
+    }
+
+#ifdef SA_NOCLDWAIT
+    if ((sa.sa_flags & SA_NOCLDWAIT) || sa.sa_handler == SIG_IGN) {
+#else
+    if (sa.sa_handler == SIG_IGN) {
+#endif
+        pa_log_debug("Process disabled waitpid(), cannot autospawn.");
+        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+        goto fail;
+    }
+
+    pa_log_debug("Trying to autospawn...");
+
+    if (c->spawn_api.prefork)
+        c->spawn_api.prefork();
+
+    if ((pid = fork()) < 0) {
+        pa_log_error(_("fork(): %s"), pa_cstrerror(errno));
+        pa_context_fail(c, PA_ERR_INTERNAL);
+
+        if (c->spawn_api.postfork)
+            c->spawn_api.postfork();
+
+        goto fail;
+    } else if (!pid) {
+        /* Child */
+
+        const char *state = NULL;
+        const char * argv[32];
+        unsigned n = 0;
+
+        if (c->spawn_api.atfork)
+            c->spawn_api.atfork();
+
+        /* We leave most of the cleaning up of the process environment
+         * to the executable. We only clean up the file descriptors to
+         * make sure the executable can actually be loaded
+         * correctly. */
+        pa_close_all(-1);
+
+        /* Setup argv */
+        argv[n++] = c->conf->daemon_binary;
+        argv[n++] = "--start";
+
+        while (n < PA_ELEMENTSOF(argv)-1) {
+            char *a;
+
+            if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
+                break;
+
+            argv[n++] = a;
+        }
+
+        argv[n++] = NULL;
+        pa_assert(n <= PA_ELEMENTSOF(argv));
+
+        execv(argv[0], (char * const *) argv);
+        _exit(1);
+    }
+
+    /* Parent */
+
+    if (c->spawn_api.postfork)
+        c->spawn_api.postfork();
+
+    do {
+        r = waitpid(pid, &status, 0);
+    } while (r < 0 && errno == EINTR);
+
+    if (r < 0) {
+
+        if (errno != ECHILD) {
+            pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
+            pa_context_fail(c, PA_ERR_INTERNAL);
+            goto fail;
+        }
+
+        /* hmm, something already reaped our child, so we assume
+         * startup worked, even if we cannot know */
+
+    } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+        goto fail;
+    }
+
+    pa_context_unref(c);
+
+    return 0;
+
+fail:
+
+    pa_context_unref(c);
+
+    return -1;
+}
+
+#endif /* OS_IS_WIN32 */
+
+static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata);
+
+#ifdef HAVE_DBUS
+static void track_pulseaudio_on_dbus(pa_context *c, DBusBusType type, pa_dbus_wrap_connection **conn) {
+    DBusError error;
+
+    pa_assert(c);
+    pa_assert(conn);
+
+    dbus_error_init(&error);
+
+    if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, c->use_rtclock, type, &error)) || dbus_error_is_set(&error)) {
+        pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    if (!dbus_connection_add_filter(pa_dbus_wrap_connection_get(*conn), filter_cb, c, NULL)) {
+        pa_log_warn("Failed to add filter function");
+        goto fail;
+    }
+    c->filter_added = true;
+
+    if (pa_dbus_add_matches(
+                pa_dbus_wrap_connection_get(*conn), &error,
+                "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio.Server',arg1=''", NULL) < 0) {
+
+        pa_log_warn("Unable to track org.pulseaudio.Server: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    return;
+
+fail:
+    if (*conn) {
+        pa_dbus_wrap_connection_free(*conn);
+        *conn = NULL;
+    }
+
+    dbus_error_free(&error);
+}
+#endif
+
+static int try_next_connection(pa_context *c) {
+    char *u = NULL;
+    int r = -1;
+
+    pa_assert(c);
+    pa_assert(!c->client);
+
+    for (;;) {
+        pa_xfree(u);
+        u = NULL;
+
+        c->server_list = pa_strlist_pop(c->server_list, &u);
+
+        if (!u) {
+
+#ifndef OS_IS_WIN32
+            if (c->do_autospawn) {
+
+                if ((r = context_autospawn(c)) < 0)
+                    goto finish;
+
+                /* Autospawn only once */
+                c->do_autospawn = false;
+
+                /* Connect only to per-user sockets this time */
+                c->server_list = prepend_per_user(c->server_list);
+
+                /* Retry connection */
+                continue;
+            }
+#endif
+
+#ifdef HAVE_DBUS
+            if (c->no_fail && !c->server_specified) {
+                if (!c->session_bus)
+                    track_pulseaudio_on_dbus(c, DBUS_BUS_SESSION, &c->session_bus);
+                if (!c->system_bus)
+                    track_pulseaudio_on_dbus(c, DBUS_BUS_SYSTEM, &c->system_bus);
+
+                if (c->session_bus || c->system_bus) {
+                    pa_log_debug("Waiting for PA on D-Bus...");
+                    break;
+                }
+            } else
+#endif
+                pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+
+            goto finish;
+        }
+
+        pa_log_debug("Trying to connect to %s...", u);
+
+        pa_xfree(c->server);
+        c->server = pa_xstrdup(u);
+
+        if (!(c->client = pa_socket_client_new_string(c->mainloop, c->use_rtclock, u, PA_NATIVE_DEFAULT_PORT)))
+            continue;
+
+        c->is_local = pa_socket_client_is_local(c->client);
+        pa_socket_client_set_callback(c->client, on_connection, c);
+        break;
+    }
+
+    r = 0;
+
+finish:
+    pa_xfree(u);
+
+    return r;
+}
+
+static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
+    pa_context *c = userdata;
+    int saved_errno = errno;
+
+    pa_assert(client);
+    pa_assert(c);
+    pa_assert(c->state == PA_CONTEXT_CONNECTING);
+
+    pa_context_ref(c);
+
+    pa_socket_client_unref(client);
+    c->client = NULL;
+
+    if (!io) {
+        /* Try the next item in the list */
+        if (saved_errno == ECONNREFUSED ||
+            saved_errno == ETIMEDOUT ||
+            saved_errno == EHOSTUNREACH) {
+            try_next_connection(c);
+            goto finish;
+        }
+
+        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+        goto finish;
+    }
+
+    setup_context(c, io);
+
+finish:
+    pa_context_unref(c);
+}
+
+#ifdef HAVE_DBUS
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
+    pa_context *c = userdata;
+    bool is_session;
+
+    pa_assert(bus);
+    pa_assert(message);
+    pa_assert(c);
+
+    if (c->state != PA_CONTEXT_CONNECTING)
+        goto finish;
+
+    if (!c->no_fail)
+        goto finish;
+
+    /* FIXME: We probably should check if this is actually the NameOwnerChanged we were looking for */
+
+    is_session = c->session_bus && bus == pa_dbus_wrap_connection_get(c->session_bus);
+    pa_log_debug("Rock!! PulseAudio might be back on %s bus", is_session ? "session" : "system");
+
+    if (is_session)
+        /* The user instance via PF_LOCAL */
+        c->server_list = prepend_per_user(c->server_list);
+    else
+        /* The system wide instance via PF_LOCAL */
+        c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
+
+    if (!c->client)
+        try_next_connection(c);
+
+finish:
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+#endif
+
+int pa_context_connect(
+        pa_context *c,
+        const char *server,
+        pa_context_flags_t flags,
+        const pa_spawn_api *api) {
+
+    int r = -1;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(c, !(flags & ~(PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL)), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID);
+
+    if (server)
+        c->conf->autospawn = false;
+    else
+        server = c->conf->default_server;
+
+    pa_context_ref(c);
+
+    c->no_fail = !!(flags & PA_CONTEXT_NOFAIL);
+    c->server_specified = !!server;
+    pa_assert(!c->server_list);
+
+    if (server) {
+        if (!(c->server_list = pa_strlist_parse(server))) {
+            pa_context_fail(c, PA_ERR_INVALIDSERVER);
+            goto finish;
+        }
+
+    } else {
+        char *d;
+
+        /* Prepend in reverse order */
+
+        /* Follow the X display */
+        if (c->conf->auto_connect_display) {
+            if ((d = getenv("DISPLAY"))) {
+                d = pa_xstrndup(d, strcspn(d, ":"));
+
+                if (*d)
+                    c->server_list = pa_strlist_prepend(c->server_list, d);
+
+                pa_xfree(d);
+            }
+        }
+
+        /* Add TCP/IP on the localhost */
+        if (c->conf->auto_connect_localhost) {
+            c->server_list = pa_strlist_prepend(c->server_list, "tcp6:[::1]");
+            c->server_list = pa_strlist_prepend(c->server_list, "tcp4:127.0.0.1");
+        }
+
+        /* The system wide instance via PF_LOCAL */
+        c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
+
+        /* The user instance via PF_LOCAL */
+        c->server_list = prepend_per_user(c->server_list);
+    }
+
+    /* Set up autospawning */
+    if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
+
+#ifdef HAVE_GETUID
+        if (getuid() == 0)
+            pa_log_debug("Not doing autospawn since we are root.");
+        else {
+            c->do_autospawn = true;
+
+            if (api)
+                c->spawn_api = *api;
+        }
+#endif
+    }
+
+    pa_context_set_state(c, PA_CONTEXT_CONNECTING);
+    r = try_next_connection(c);
+
+finish:
+    pa_context_unref(c);
+
+    return r;
+}
+
+void pa_context_disconnect(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (PA_CONTEXT_IS_GOOD(c->state))
+        pa_context_set_state(c, PA_CONTEXT_TERMINATED);
+}
+
+pa_context_state_t pa_context_get_state(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    return c->state;
+}
+
+int pa_context_errno(pa_context *c) {
+
+    if (!c)
+        return PA_ERR_INVALID;
+
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    return c->error;
+}
+
+void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+        return;
+
+    c->state_callback = cb;
+    c->state_userdata = userdata;
+}
+
+void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+        return;
+
+    c->event_callback = cb;
+    c->event_userdata = userdata;
+}
+
+int pa_context_is_pending(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE);
+
+    return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
+        (c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
+        c->client;
+}
+
+static void set_dispatch_callbacks(pa_operation *o);
+
+static void pdispatch_drain_callback(pa_pdispatch*pd, void *userdata) {
+    set_dispatch_callbacks(userdata);
+}
+
+static void pstream_drain_callback(pa_pstream *s, void *userdata) {
+    set_dispatch_callbacks(userdata);
+}
+
+static void set_dispatch_callbacks(pa_operation *o) {
+    int done = 1;
+
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+    pa_assert(o->context);
+    pa_assert(PA_REFCNT_VALUE(o->context) >= 1);
+    pa_assert(o->context->state == PA_CONTEXT_READY);
+
+    pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL);
+    pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL);
+
+    if (pa_pdispatch_is_pending(o->context->pdispatch)) {
+        pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o);
+        done = 0;
+    }
+
+    if (pa_pstream_is_pending(o->context->pstream)) {
+        pa_pstream_set_drain_callback(o->context->pstream, pstream_drain_callback, o);
+        done = 0;
+    }
+
+    if (done) {
+        if (o->callback) {
+            pa_context_notify_cb_t cb = (pa_context_notify_cb_t) o->callback;
+            cb(o->context, o->userdata);
+        }
+
+        pa_operation_done(o);
+        pa_operation_unref(o);
+    }
+}
+
+pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_context_is_pending(c), PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+    set_dispatch_callbacks(pa_operation_ref(o));
+
+    return o;
+}
+
+void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int success = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        success = 0;
+    } else if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback;
+        cb(o->context, success, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, cb, userdata);
+
+    t = pa_tagstruct_command(c, command, &tag);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    return pa_context_send_simple_command(c, PA_COMMAND_EXIT, pa_context_simple_ack_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT,  pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT,  pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+int pa_context_is_local(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(c, !pa_detect_fork(), PA_ERR_FORKED, -1);
+    PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, -1);
+
+    return c->is_local;
+}
+
+pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(name);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    if (c->version >= 13) {
+        pa_proplist *p = pa_proplist_new();
+
+        pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
+        o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata);
+        pa_proplist_free(p);
+    } else {
+        pa_tagstruct *t;
+        uint32_t tag;
+
+        o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+        t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
+        pa_tagstruct_puts(t, name);
+        pa_pstream_send_tagstruct(c->pstream, t);
+        pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT,  pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+    }
+
+    return o;
+}
+
+const char* pa_get_library_version(void) {
+    return pa_get_headers_version();
+}
+
+const char* pa_context_get_server(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->server, PA_ERR_NOENTITY);
+
+    if (*c->server == '{') {
+        char *e = strchr(c->server+1, '}');
+        return e ? e+1 : c->server;
+    }
+
+    return c->server;
+}
+
+uint32_t pa_context_get_protocol_version(pa_context *c) {
+    return PA_PROTOCOL_VERSION;
+}
+
+uint32_t pa_context_get_server_protocol_version(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(c, !pa_detect_fork(), PA_ERR_FORKED, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, PA_INVALID_INDEX);
+
+    return c->version;
+}
+
+pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag) {
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(tag);
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, command);
+    pa_tagstruct_putu32(t, *tag = c->ctag++);
+
+    return t;
+}
+
+uint32_t pa_context_get_index(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(c, !pa_detect_fork(), PA_ERR_FORKED, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(c, c->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+
+    return c->client_index;
+}
+
+pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_UPDATE_CLIENT_PROPLIST, &tag);
+    pa_tagstruct_putu32(t, (uint32_t) mode);
+    pa_tagstruct_put_proplist(t, p);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    /* Please note that we don't update c->proplist here, because we
+     * don't export that field */
+
+    return o;
+}
+
+pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+    const char * const *k;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, keys && keys[0], PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_CLIENT_PROPLIST, &tag);
+
+    for (k = keys; *k; k++)
+        pa_tagstruct_puts(t, *k);
+
+    pa_tagstruct_puts(t, NULL);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    /* Please note that we don't update c->proplist here, because we
+     * don't export that field */
+
+    return o;
+}
+
+void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    uint32_t idx;
+    const char *name;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_EXTENSION);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 15) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_streq(name, "module-device-manager"))
+        pa_ext_device_manager_command(c, tag, t);
+    else if (pa_streq(name, "module-device-restore"))
+        pa_ext_device_restore_command(c, tag, t);
+    else if (pa_streq(name, "module-stream-restore"))
+        pa_ext_stream_restore_command(c, tag, t);
+    else
+        pa_log(_("Received message for unknown extension '%s'"), name);
+
+finish:
+    pa_context_unref(c);
+}
+
+static void pa_command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+
+#ifdef HAVE_CREDS
+    pa_cmsg_ancil_data *ancil = NULL;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_ENABLE_SRBCHANNEL);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    ancil = pa_pdispatch_take_ancil_data(pd);
+    if (!ancil)
+        goto fail;
+
+    /* Currently only one srb channel is supported, might change in future versions */
+    if (c->srb_template.readfd != -1)
+        goto fail;
+
+    if (ancil->nfd != 2 || ancil->fds[0] == -1 || ancil->fds[1] == -1)
+        goto fail;
+
+    pa_context_ref(c);
+
+    c->srb_template.readfd = ancil->fds[0];
+    c->srb_template.writefd = ancil->fds[1];
+    c->srb_setup_tag = tag;
+
+    pa_context_unref(c);
+
+    ancil->close_fds_on_cleanup = false;
+    return;
+
+fail:
+    if (ancil)
+        pa_cmsg_ancil_data_close_fds(ancil);
+
+    pa_context_fail(c, PA_ERR_PROTOCOL);
+    return;
+#else
+    pa_assert(c);
+    pa_context_fail(c, PA_ERR_PROTOCOL);
+#endif
+}
+
+static void pa_command_disable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_tagstruct *t2;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_DISABLE_SRBCHANNEL);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_pstream_set_srbchannel(c->pstream, NULL);
+
+    c->srb_template.readfd = -1;
+    c->srb_template.writefd = -1;
+    if (c->srb_template.memblock) {
+        pa_memblock_unref(c->srb_template.memblock);
+        c->srb_template.memblock = NULL;
+    }
+
+    /* Send disable command back again */
+    t2 = pa_tagstruct_new();
+    pa_tagstruct_putu32(t2, PA_COMMAND_DISABLE_SRBCHANNEL);
+    pa_tagstruct_putu32(t2, tag);
+    pa_pstream_send_tagstruct(c->pstream, t2);
+}
+
+static void pa_command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_REGISTER_MEMFD_SHMID);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (pa_common_command_register_memfd_shmid(c->pstream, pd, c->version, command, t))
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+}
+
+void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_proplist *pl = NULL;
+    const char *event;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_CLIENT_EVENT);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 15) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    pl = pa_proplist_new();
+
+    if (pa_tagstruct_gets(t, &event) < 0 ||
+        pa_tagstruct_get_proplist(t, pl) < 0 ||
+        !pa_tagstruct_eof(t) || !event) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (c->event_callback)
+        c->event_callback(c, event, pl, c->event_userdata);
+
+finish:
+    pa_context_unref(c);
+
+    if (pl)
+        pa_proplist_free(pl);
+}
+
+pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) {
+    struct timeval tv;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(c->mainloop);
+
+    if (usec == PA_USEC_INVALID)
+        return c->mainloop->time_new(c->mainloop, NULL, cb, userdata);
+
+    pa_timeval_rtstore(&tv, usec, c->use_rtclock);
+
+    return c->mainloop->time_new(c->mainloop, &tv, cb, userdata);
+}
+
+void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) {
+    struct timeval tv;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(c->mainloop);
+
+    if (usec == PA_USEC_INVALID)
+        c->mainloop->time_restart(e, NULL);
+    else {
+        pa_timeval_rtstore(&tv, usec, c->use_rtclock);
+        c->mainloop->time_restart(e, &tv);
+    }
+}
+
+size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss) {
+    size_t fs, mbs;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(c, !pa_detect_fork(), PA_ERR_FORKED, (size_t) -1);
+    PA_CHECK_VALIDITY_RETURN_ANY(c, !ss || pa_sample_spec_valid(ss), PA_ERR_INVALID, (size_t) -1);
+
+    fs = ss ? pa_frame_size(ss) : 1;
+    mbs = PA_ROUND_DOWN(pa_mempool_block_size_max(c->mempool), fs);
+    return PA_MAX(mbs, fs);
+}
+
+int pa_context_load_cookie_from_file(pa_context *c, const char *cookie_file_path) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(c, !cookie_file_path || *cookie_file_path, PA_ERR_INVALID);
+
+    pa_client_conf_set_cookie_file_from_application(c->conf, cookie_file_path);
+
+    return 0;
+}
diff --git a/src/pulse/context.h b/src/pulse/context.h
new file mode 100644 (file)
index 0000000..ae2a068
--- /dev/null
@@ -0,0 +1,291 @@
+#ifndef foocontexthfoo
+#define foocontexthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/def.h>
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+#include <pulse/operation.h>
+#include <pulse/proplist.h>
+#include <pulse/version.h>
+
+/** \page async Asynchronous API
+ *
+ * \section overv_sec Overview
+ *
+ * The asynchronous API is the native interface to the PulseAudio library.
+ * It allows full access to all available functionality. This however means that
+ * it is rather complex and can take some time to fully master.
+ *
+ * \section mainloop_sec Main Loop Abstraction
+ *
+ * The API is based around an asynchronous event loop, or main loop,
+ * abstraction. This abstraction contains three basic elements:
+ *
+ * \li Deferred events - Events that will trigger as soon as possible. Note
+ *                       that some implementations may block all other events
+ *                       when a deferred event is active.
+ * \li I/O events - Events that trigger on file descriptor activities.
+ * \li Times events - Events that trigger after a fixed amount of time.
+ *
+ * The abstraction is represented as a number of function pointers in the
+ * pa_mainloop_api structure.
+ *
+ * To actually be able to use these functions, an implementation needs to
+ * be coupled to the abstraction. There are three of these shipped with
+ * PulseAudio, but any other can be used with a minimal amount of work,
+ * provided it supports the three basic events listed above.
+ *
+ * The implementations shipped with PulseAudio are:
+ *
+ * \li \subpage mainloop - A minimal but fast implementation based on poll().
+ * \li \subpage threaded_mainloop - A special version of the previous
+ *                                  implementation where all of PulseAudio's
+ *                                  internal handling runs in a separate
+ *                                  thread.
+ * \li \subpage glib-mainloop - A wrapper around GLib's main loop.
+ *
+ * UNIX signals may be hooked to a main loop using the functions from
+ * \ref mainloop-signal.h. These rely only on the main loop abstraction
+ * and can therefore be used with any of the implementations.
+ *
+ * \section refcnt_sec Reference Counting
+ *
+ * Almost all objects in PulseAudio are reference counted. What that means
+ * is that you rarely malloc() or free() any objects. Instead you increase
+ * and decrease their reference counts. Whenever an object's reference
+ * count reaches zero, that object gets destroy and any resources it uses
+ * get freed.
+ *
+ * The benefit of this design is that an application need not worry about
+ * whether or not it needs to keep an object around in case the library is
+ * using it internally. If it is, then it has made sure it has its own
+ * reference to it.
+ *
+ * Whenever the library creates an object, it will have an initial
+ * reference count of one. Most of the time, this single reference will be
+ * sufficient for the application, so all required reference count
+ * interaction will be a single call to the object's unref function.
+ *
+ * \section context_sec Context
+ *
+ * A context is the basic object for a connection to a PulseAudio server.
+ * It multiplexes commands, data streams and events through a single
+ * channel.
+ *
+ * There is no need for more than one context per application, unless
+ * connections to multiple servers are needed.
+ *
+ * \subsection ops_subsec Operations
+ *
+ * All operations on the context are performed asynchronously. I.e. the
+ * client will not wait for the server to complete the request. To keep
+ * track of all these in-flight operations, the application is given a
+ * pa_operation object for each asynchronous operation.
+ *
+ * There are only two actions (besides reference counting) that can be
+ * performed on a pa_operation: querying its state with
+ * pa_operation_get_state() and aborting it with pa_operation_cancel().
+ *
+ * A pa_operation object is reference counted, so an application must
+ * make sure to unreference it, even if it has no intention of using it.
+ *
+ * \subsection conn_subsec Connecting
+ *
+ * A context must be connected to a server before any operation can be
+ * issued. Calling pa_context_connect() will initiate the connection
+ * procedure. Unlike most asynchronous operations, connecting does not
+ * result in a pa_operation object. Instead, the application should
+ * register a callback using pa_context_set_state_callback().
+ *
+ * \subsection disc_subsec Disconnecting
+ *
+ * When the sound support is no longer needed, the connection needs to be
+ * closed using pa_context_disconnect(). This is an immediate function that
+ * works synchronously.
+ *
+ * Since the context object has references to other objects it must be
+ * disconnected after use or there is a high risk of memory leaks. If the
+ * connection has terminated by itself, then there is no need to explicitly
+ * disconnect the context using pa_context_disconnect().
+ *
+ * \section Functions
+ *
+ * The sound server's functionality can be divided into a number of
+ * subsections:
+ *
+ * \li \subpage streams
+ * \li \subpage scache
+ * \li \subpage introspect
+ * \li \subpage subscribe
+ */
+
+/** \file
+ * Connection contexts for asynchronous communication with a
+ * server. A pa_context object wraps a connection to a PulseAudio
+ * server using its native protocol.
+ *
+ * See also \subpage async
+ */
+
+PA_C_DECL_BEGIN
+
+/** An opaque connection context to a daemon */
+typedef struct pa_context pa_context;
+
+/** Generic notification callback prototype */
+typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata);
+
+/** A generic callback for operation completion */
+typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);
+
+/** A callback for asynchronous meta/policy event messages. The set
+ * of defined events can be extended at any time. Also, server modules
+ * may introduce additional message types so make sure that your
+ * callback function ignores messages it doesn't know. \since
+ * 0.9.15 */
+typedef void (*pa_context_event_cb_t)(pa_context *c, const char *name, pa_proplist *p, void *userdata);
+
+/** Instantiate a new connection context with an abstract mainloop API
+ * and an application name. It is recommended to use pa_context_new_with_proplist()
+ * instead and specify some initial properties.*/
+pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
+
+/** Instantiate a new connection context with an abstract mainloop API
+ * and an application name, and specify the initial client property
+ * list. \since 0.9.11 */
+pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist);
+
+/** Decrease the reference counter of the context by one */
+void pa_context_unref(pa_context *c);
+
+/** Increase the reference counter of the context by one */
+pa_context* pa_context_ref(pa_context *c);
+
+/** Set a callback function that is called whenever the context status changes */
+void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata);
+
+/** Set a callback function that is called whenever a meta/policy
+ * control event is received. \since 0.9.15 */
+void pa_context_set_event_callback(pa_context *p, pa_context_event_cb_t cb, void *userdata);
+
+/** Return the error number of the last failed operation */
+int pa_context_errno(pa_context *c);
+
+/** Return non-zero if some data is pending to be written to the connection */
+int pa_context_is_pending(pa_context *c);
+
+/** Return the current context status */
+pa_context_state_t pa_context_get_state(pa_context *c);
+
+/** Connect the context to the specified server. If server is NULL,
+connect to the default server. This routine may but will not always
+return synchronously on error. Use pa_context_set_state_callback() to
+be notified when the connection is established. If flags doesn't have
+PA_CONTEXT_NOAUTOSPAWN set and no specific server is specified or
+accessible a new daemon is spawned. If api is non-NULL, the functions
+specified in the structure are used when forking a new child
+process. */
+int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api);
+
+/** Terminate the context connection immediately */
+void pa_context_disconnect(pa_context *c);
+
+/** Drain the context. If there is nothing to drain, the function returns NULL */
+pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata);
+
+/** Tell the daemon to exit. The returned operation is unlikely to
+ * complete successfully, since the daemon probably died before
+ * returning a success notification */
+pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the name of the default sink. */
+pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the name of the default source. */
+pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. */
+int pa_context_is_local(pa_context *c);
+
+/** Set a different application name for context on the server. */
+pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Return the server name this context is connected to. */
+const char* pa_context_get_server(pa_context *c);
+
+/** Return the protocol version of the library. */
+uint32_t pa_context_get_protocol_version(pa_context *c);
+
+/** Return the protocol version of the connected server. */
+uint32_t pa_context_get_server_protocol_version(pa_context *c);
+
+/** Update the property list of the client, adding new entries. Please
+ * note that it is highly recommended to set as much properties
+ * initially via pa_context_new_with_proplist() as possible instead a
+ * posteriori with this function, since that information may then be
+ * used to route streams of the client to the right device. \since 0.9.11 */
+pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata);
+
+/** Update the property list of the client, remove entries. \since 0.9.11 */
+pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata);
+
+/** Return the client index this context is
+ * identified in the server with. This is useful for usage with the
+ * introspection functions, such as pa_context_get_client_info(). \since 0.9.11 */
+uint32_t pa_context_get_index(pa_context *s);
+
+/** Create a new timer event source for the specified time (wrapper
+ * for mainloop->time_new). \since 0.9.16 */
+pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata);
+
+/** Restart a running or expired timer event source (wrapper for
+ * mainloop->time_restart). \since 0.9.16 */
+void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec);
+
+/** Return the optimal block size for passing around audio buffers. It
+ * is recommended to allocate buffers of the size returned here when
+ * writing audio data to playback streams, if the latency constraints
+ * permit this. It is not recommended writing larger blocks than this
+ * because usually they will then be split up internally into chunks
+ * of this size. It is not recommended writing smaller blocks than
+ * this (unless required due to latency demands) because this
+ * increases CPU usage. If ss is NULL you will be returned the
+ * byte-exact tile size. If you pass a valid ss, then the tile size
+ * will be rounded down to multiple of the frame size. This is
+ * supposed to be used in a construct such as
+ * pa_context_get_tile_size(pa_stream_get_context(s),
+ * pa_stream_get_sample_spec(ss)); \since 0.9.20 */
+size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss);
+
+/** Load the authentication cookie from a file. This function is primarily
+ * meant for PulseAudio's own tunnel modules, which need to load the cookie
+ * from a custom location. Applications don't usually need to care about the
+ * cookie at all, but if it happens that you know what the authentication
+ * cookie is and your application needs to load it from a non-standard
+ * location, feel free to use this function. \since 5.0 */
+int pa_context_load_cookie_from_file(pa_context *c, const char *cookie_file_path);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/def.h b/src/pulse/def.h
new file mode 100644 (file)
index 0000000..680bdc9
--- /dev/null
@@ -0,0 +1,1052 @@
+#ifndef foodefhfoo
+#define foodefhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/time.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/sample.h>
+#include <pulse/version.h>
+
+/** \file
+ * Global definitions */
+
+PA_C_DECL_BEGIN
+
+/** The state of a connection context */
+typedef enum pa_context_state {
+    PA_CONTEXT_UNCONNECTED,    /**< The context hasn't been connected yet */
+    PA_CONTEXT_CONNECTING,     /**< A connection is being established */
+    PA_CONTEXT_AUTHORIZING,    /**< The client is authorizing itself to the daemon */
+    PA_CONTEXT_SETTING_NAME,   /**< The client is passing its application name to the daemon */
+    PA_CONTEXT_READY,          /**< The connection is established, the context is ready to execute operations */
+    PA_CONTEXT_FAILED,         /**< The connection failed or was disconnected */
+    PA_CONTEXT_TERMINATED      /**< The connection was terminated cleanly */
+} pa_context_state_t;
+
+/** Return non-zero if the passed state is one of the connected states. \since 0.9.11 */
+static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
+    return
+        x == PA_CONTEXT_CONNECTING ||
+        x == PA_CONTEXT_AUTHORIZING ||
+        x == PA_CONTEXT_SETTING_NAME ||
+        x == PA_CONTEXT_READY;
+}
+
+/** \cond fulldocs */
+#define PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
+#define PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
+#define PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
+#define PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
+#define PA_CONTEXT_READY PA_CONTEXT_READY
+#define PA_CONTEXT_FAILED PA_CONTEXT_FAILED
+#define PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
+#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD
+/** \endcond */
+
+/** The state of a stream */
+typedef enum pa_stream_state {
+    PA_STREAM_UNCONNECTED,  /**< The stream is not yet connected to any sink or source */
+    PA_STREAM_CREATING,     /**< The stream is being created */
+    PA_STREAM_READY,        /**< The stream is established, you may pass audio data to it now */
+    PA_STREAM_FAILED,       /**< An error occurred that made the stream invalid */
+    PA_STREAM_TERMINATED    /**< The stream has been terminated cleanly */
+} pa_stream_state_t;
+
+/** Return non-zero if the passed state is one of the connected states. \since 0.9.11 */
+static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
+    return
+        x == PA_STREAM_CREATING ||
+        x == PA_STREAM_READY;
+}
+
+/** \cond fulldocs */
+#define PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
+#define PA_STREAM_CREATING PA_STREAM_CREATING
+#define PA_STREAM_READY PA_STREAM_READY
+#define PA_STREAM_FAILED PA_STREAM_FAILED
+#define PA_STREAM_TERMINATED PA_STREAM_TERMINATED
+#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD
+/** \endcond */
+
+/** The state of an operation */
+typedef enum pa_operation_state {
+    PA_OPERATION_RUNNING,
+    /**< The operation is still running */
+    PA_OPERATION_DONE,
+    /**< The operation has completed */
+    PA_OPERATION_CANCELLED
+    /**< The operation has been cancelled. Operations may get cancelled by the
+     * application, or as a result of the context getting disconneted while the
+     * operation is pending. */
+} pa_operation_state_t;
+
+/** \cond fulldocs */
+#define PA_OPERATION_RUNNING PA_OPERATION_RUNNING
+#define PA_OPERATION_DONE PA_OPERATION_DONE
+#define PA_OPERATION_CANCELED PA_OPERATION_CANCELLED
+#define PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
+/** \endcond */
+
+/** An invalid index */
+#define PA_INVALID_INDEX ((uint32_t) -1)
+
+/** Some special flags for contexts. */
+typedef enum pa_context_flags {
+    PA_CONTEXT_NOFLAGS = 0x0000U,
+    /**< Flag to pass when no specific options are needed (used to avoid casting)  \since 0.9.19 */
+    PA_CONTEXT_NOAUTOSPAWN = 0x0001U,
+    /**< Disabled autospawning of the PulseAudio daemon if required */
+    PA_CONTEXT_NOFAIL = 0x0002U
+    /**< Don't fail if the daemon is not available when pa_context_connect() is called, instead enter PA_CONTEXT_CONNECTING state and wait for the daemon to appear.  \since 0.9.15 */
+} pa_context_flags_t;
+
+/** \cond fulldocs */
+/* Allow clients to check with #ifdef for those flags */
+#define PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
+#define PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
+/** \endcond */
+
+/** Direction bitfield - while we currently do not expose anything bidirectional,
+  one should test against the bit instead of the value (e.g.\ if (d & PA_DIRECTION_OUTPUT)),
+  because we might add bidirectional stuff in the future. \since 2.0
+*/
+typedef enum pa_direction {
+    PA_DIRECTION_OUTPUT = 0x0001U,  /**< Output direction */
+    PA_DIRECTION_INPUT = 0x0002U    /**< Input direction */
+} pa_direction_t;
+
+/** \cond fulldocs */
+#define PA_DIRECTION_OUTPUT PA_DIRECTION_OUTPUT
+#define PA_DIRECTION_INPUT PA_DIRECTION_INPUT
+/** \endcond */
+
+/** The type of device we are dealing with */
+typedef enum pa_device_type {
+    PA_DEVICE_TYPE_SINK,     /**< Playback device */
+    PA_DEVICE_TYPE_SOURCE    /**< Recording device */
+} pa_device_type_t;
+
+/** \cond fulldocs */
+#define PA_DEVICE_TYPE_SINK PA_DEVICE_TYPE_SINK
+#define PA_DEVICE_TYPE_SOURCE PA_DEVICE_TYPE_SOURCE
+/** \endcond */
+
+/** The direction of a pa_stream object */
+typedef enum pa_stream_direction {
+    PA_STREAM_NODIRECTION,   /**< Invalid direction */
+    PA_STREAM_PLAYBACK,      /**< Playback stream */
+    PA_STREAM_RECORD,        /**< Record stream */
+    PA_STREAM_UPLOAD         /**< Sample upload stream */
+} pa_stream_direction_t;
+
+/** \cond fulldocs */
+#define PA_STREAM_NODIRECTION PA_STREAM_NODIRECTION
+#define PA_STREAM_PLAYBACK PA_STREAM_PLAYBACK
+#define PA_STREAM_RECORD PA_STREAM_RECORD
+#define PA_STREAM_UPLOAD PA_STREAM_UPLOAD
+/** \endcond */
+
+/** Some special flags for stream connections. */
+typedef enum pa_stream_flags {
+
+    PA_STREAM_NOFLAGS = 0x0000U,
+    /**< Flag to pass when no specific options are needed (used to avoid casting)  \since 0.9.19 */
+
+    PA_STREAM_START_CORKED = 0x0001U,
+    /**< Create the stream corked, requiring an explicit
+     * pa_stream_cork() call to uncork it. */
+
+    PA_STREAM_INTERPOLATE_TIMING = 0x0002U,
+    /**< Interpolate the latency for this stream. When enabled,
+     * pa_stream_get_latency() and pa_stream_get_time() will try to
+     * estimate the current record/playback time based on the local
+     * time that passed since the last timing info update.  Using this
+     * option has the advantage of not requiring a whole roundtrip
+     * when the current playback/recording time is needed. Consider
+     * using this option when requesting latency information
+     * frequently. This is especially useful on long latency network
+     * connections. It makes a lot of sense to combine this option
+     * with PA_STREAM_AUTO_TIMING_UPDATE. */
+
+    PA_STREAM_NOT_MONOTONIC = 0x0004U,
+    /**< Don't force the time to increase monotonically. If this
+     * option is enabled, pa_stream_get_time() will not necessarily
+     * return always monotonically increasing time values on each
+     * call. This may confuse applications which cannot deal with time
+     * going 'backwards', but has the advantage that bad transport
+     * latency estimations that caused the time to jump ahead can
+     * be corrected quickly, without the need to wait. (Please note
+     * that this flag was named PA_STREAM_NOT_MONOTONOUS in releases
+     * prior to 0.9.11. The old name is still defined too, for
+     * compatibility reasons. */
+
+    PA_STREAM_AUTO_TIMING_UPDATE = 0x0008U,
+    /**< If set timing update requests are issued periodically
+     * automatically. Combined with PA_STREAM_INTERPOLATE_TIMING you
+     * will be able to query the current time and latency with
+     * pa_stream_get_time() and pa_stream_get_latency() at all times
+     * without a packet round trip.*/
+
+    PA_STREAM_NO_REMAP_CHANNELS = 0x0010U,
+    /**< Don't remap channels by their name, instead map them simply
+     * by their index. Implies PA_STREAM_NO_REMIX_CHANNELS. Only
+     * supported when the server is at least PA 0.9.8. It is ignored
+     * on older servers.\since 0.9.8 */
+
+    PA_STREAM_NO_REMIX_CHANNELS = 0x0020U,
+    /**< When remapping channels by name, don't upmix or downmix them
+     * to related channels. Copy them into matching channels of the
+     * device 1:1. Only supported when the server is at least PA
+     * 0.9.8. It is ignored on older servers. \since 0.9.8 */
+
+    PA_STREAM_FIX_FORMAT = 0x0040U,
+    /**< Use the sample format of the sink/device this stream is being
+     * connected to, and possibly ignore the format the sample spec
+     * contains -- but you still have to pass a valid value in it as a
+     * hint to PulseAudio what would suit your stream best. If this is
+     * used you should query the used sample format after creating the
+     * stream by using pa_stream_get_sample_spec(). Also, if you
+     * specified manual buffer metrics it is recommended to update
+     * them with pa_stream_set_buffer_attr() to compensate for the
+     * changed frame sizes. Only supported when the server is at least
+     * PA 0.9.8. It is ignored on older servers.
+     *
+     * When creating streams with pa_stream_new_extended(), this flag has no
+     * effect. If you specify a format with PCM encoding, and you want the
+     * server to choose the sample format, then you should leave the sample
+     * format unspecified in the pa_format_info object. This also means that
+     * you can't use pa_format_info_from_sample_spec(), because that function
+     * always sets the sample format.
+     *
+     * \since 0.9.8 */
+
+    PA_STREAM_FIX_RATE = 0x0080U,
+    /**< Use the sample rate of the sink, and possibly ignore the rate
+     * the sample spec contains. Usage similar to
+     * PA_STREAM_FIX_FORMAT. Only supported when the server is at least
+     * PA 0.9.8. It is ignored on older servers.
+     *
+     * When creating streams with pa_stream_new_extended(), this flag has no
+     * effect. If you specify a format with PCM encoding, and you want the
+     * server to choose the sample rate, then you should leave the rate
+     * unspecified in the pa_format_info object. This also means that you can't
+     * use pa_format_info_from_sample_spec(), because that function always sets
+     * the sample rate.
+     *
+     * \since 0.9.8 */
+
+    PA_STREAM_FIX_CHANNELS = 0x0100,
+    /**< Use the number of channels and the channel map of the sink,
+     * and possibly ignore the number of channels and the map the
+     * sample spec and the passed channel map contains. Usage similar
+     * to PA_STREAM_FIX_FORMAT. Only supported when the server is at
+     * least PA 0.9.8. It is ignored on older servers.
+     *
+     * When creating streams with pa_stream_new_extended(), this flag has no
+     * effect. If you specify a format with PCM encoding, and you want the
+     * server to choose the channel count and/or channel map, then you should
+     * leave the channels and/or the channel map unspecified in the
+     * pa_format_info object. This also means that you can't use
+     * pa_format_info_from_sample_spec(), because that function always sets
+     * the channel count (but if you only want to leave the channel map
+     * unspecified, then pa_format_info_from_sample_spec() works, because it
+     * accepts a NULL channel map).
+     *
+     * \since 0.9.8 */
+
+    PA_STREAM_DONT_MOVE = 0x0200U,
+    /**< Don't allow moving of this stream to another
+     * sink/device. Useful if you use any of the PA_STREAM_FIX_ flags
+     * and want to make sure that resampling never takes place --
+     * which might happen if the stream is moved to another
+     * sink/source with a different sample spec/channel map. Only
+     * supported when the server is at least PA 0.9.8. It is ignored
+     * on older servers. \since 0.9.8 */
+
+    PA_STREAM_VARIABLE_RATE = 0x0400U,
+    /**< Allow dynamic changing of the sampling rate during playback
+     * with pa_stream_update_sample_rate(). Only supported when the
+     * server is at least PA 0.9.8. It is ignored on older
+     * servers. \since 0.9.8 */
+
+    PA_STREAM_PEAK_DETECT = 0x0800U,
+    /**< Find peaks instead of resampling. \since 0.9.11 */
+
+    PA_STREAM_START_MUTED = 0x1000U,
+    /**< Create in muted state. If neither PA_STREAM_START_UNMUTED nor
+     * PA_STREAM_START_MUTED it is left to the server to decide
+     * whether to create the stream in muted or in unmuted
+     * state. \since 0.9.11 */
+
+    PA_STREAM_ADJUST_LATENCY = 0x2000U,
+    /**< Try to adjust the latency of the sink/source based on the
+     * requested buffer metrics and adjust buffer metrics
+     * accordingly. Also see pa_buffer_attr. This option may not be
+     * specified at the same time as PA_STREAM_EARLY_REQUESTS. \since
+     * 0.9.11 */
+
+    PA_STREAM_EARLY_REQUESTS = 0x4000U,
+    /**< Enable compatibility mode for legacy clients that rely on a
+     * "classic" hardware device fragment-style playback model. If
+     * this option is set, the minreq value of the buffer metrics gets
+     * a new meaning: instead of just specifying that no requests
+     * asking for less new data than this value will be made to the
+     * client it will also guarantee that requests are generated as
+     * early as this limit is reached. This flag should only be set in
+     * very few situations where compatibility with a fragment-based
+     * playback model needs to be kept and the client applications
+     * cannot deal with data requests that are delayed to the latest
+     * moment possible. (Usually these are programs that use usleep()
+     * or a similar call in their playback loops instead of sleeping
+     * on the device itself.) Also see pa_buffer_attr. This option may
+     * not be specified at the same time as
+     * PA_STREAM_ADJUST_LATENCY. \since 0.9.12 */
+
+    PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND = 0x8000U,
+    /**< If set this stream won't be taken into account when it is
+     * checked whether the device this stream is connected to should
+     * auto-suspend. \since 0.9.15 */
+
+    PA_STREAM_START_UNMUTED = 0x10000U,
+    /**< Create in unmuted state. If neither PA_STREAM_START_UNMUTED
+     * nor PA_STREAM_START_MUTED it is left to the server to decide
+     * whether to create the stream in muted or in unmuted
+     * state. \since 0.9.15 */
+
+    PA_STREAM_FAIL_ON_SUSPEND = 0x20000U,
+    /**< If the sink/source this stream is connected to is suspended
+     * during the creation of this stream, cause it to fail. If the
+     * sink/source is being suspended during creation of this stream,
+     * make sure this stream is terminated. \since 0.9.15 */
+
+    PA_STREAM_RELATIVE_VOLUME = 0x40000U,
+    /**< If a volume is passed when this stream is created, consider
+     * it relative to the sink's current volume, never as absolute
+     * device volume. If this is not specified the volume will be
+     * consider absolute when the sink is in flat volume mode,
+     * relative otherwise. \since 0.9.20 */
+
+    PA_STREAM_PASSTHROUGH = 0x80000U
+    /**< Used to tag content that will be rendered by passthrough sinks.
+     * The data will be left as is and not reformatted, resampled.
+     * \since 1.0 */
+
+} pa_stream_flags_t;
+
+/** \cond fulldocs */
+
+/* English is an evil language */
+#define PA_STREAM_NOT_MONOTONOUS PA_STREAM_NOT_MONOTONIC
+
+/* Allow clients to check with #ifdef for those flags */
+#define PA_STREAM_START_CORKED PA_STREAM_START_CORKED
+#define PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
+#define PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
+#define PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
+#define PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
+#define PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
+#define PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
+#define PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
+#define PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
+#define PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
+#define PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
+#define PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
+#define PA_STREAM_START_MUTED PA_STREAM_START_MUTED
+#define PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
+#define PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
+#define PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
+#define PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
+#define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
+#define PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
+#define PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
+
+/** \endcond */
+
+/** Playback and record buffer metrics */
+typedef struct pa_buffer_attr {
+    uint32_t maxlength;
+    /**< Maximum length of the buffer in bytes. Setting this to (uint32_t) -1
+     * will initialize this to the maximum value supported by server,
+     * which is recommended.
+     *
+     * In strict low-latency playback scenarios you might want to set this to
+     * a lower value, likely together with the PA_STREAM_ADJUST_LATENCY flag.
+     * If you do so, you ensure that the latency doesn't grow beyond what is
+     * acceptable for the use case, at the cost of getting more underruns if
+     * the latency is lower than what the server can reliably handle. */
+
+    uint32_t tlength;
+    /**< Playback only: target length of the buffer. The server tries
+     * to assure that at least tlength bytes are always available in
+     * the per-stream server-side playback buffer. It is recommended
+     * to set this to (uint32_t) -1, which will initialize this to a
+     * value that is deemed sensible by the server. However, this
+     * value will default to something like 2s, i.e. for applications
+     * that have specific latency requirements this value should be
+     * set to the maximum latency that the application can deal
+     * with. When PA_STREAM_ADJUST_LATENCY is not set this value will
+     * influence only the per-stream playback buffer size. When
+     * PA_STREAM_ADJUST_LATENCY is set the overall latency of the sink
+     * plus the playback buffer size is configured to this value. Set
+     * PA_STREAM_ADJUST_LATENCY if you are interested in adjusting the
+     * overall latency. Don't set it if you are interested in
+     * configuring the server-side per-stream playback buffer
+     * size. */
+
+    uint32_t prebuf;
+    /**< Playback only: pre-buffering. The server does not start with
+     * playback before at least prebuf bytes are available in the
+     * buffer. It is recommended to set this to (uint32_t) -1, which
+     * will initialize this to the same value as tlength, whatever
+     * that may be. Initialize to 0 to enable manual start/stop
+     * control of the stream. This means that playback will not stop
+     * on underrun and playback will not start automatically. Instead
+     * pa_stream_cork() needs to be called explicitly. If you set
+     * this value to 0 you should also set PA_STREAM_START_CORKED. */
+
+    uint32_t minreq;
+    /**< Playback only: minimum request. The server does not request
+     * less than minreq bytes from the client, instead waits until the
+     * buffer is free enough to request more bytes at once. It is
+     * recommended to set this to (uint32_t) -1, which will initialize
+     * this to a value that is deemed sensible by the server. This
+     * should be set to a value that gives PulseAudio enough time to
+     * move the data from the per-stream playback buffer into the
+     * hardware playback buffer. */
+
+    uint32_t fragsize;
+    /**< Recording only: fragment size. The server sends data in
+     * blocks of fragsize bytes size. Large values diminish
+     * interactivity with other operations on the connection context
+     * but decrease control overhead. It is recommended to set this to
+     * (uint32_t) -1, which will initialize this to a value that is
+     * deemed sensible by the server. However, this value will default
+     * to something like 2s, i.e. for applications that have specific
+     * latency requirements this value should be set to the maximum
+     * latency that the application can deal with. If
+     * PA_STREAM_ADJUST_LATENCY is set the overall source latency will
+     * be adjusted according to this value. If it is not set the
+     * source latency is left unmodified. */
+
+} pa_buffer_attr;
+
+/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */
+typedef enum pa_error_code {
+    PA_OK = 0,                     /**< No error */
+    PA_ERR_ACCESS,                 /**< Access failure */
+    PA_ERR_COMMAND,                /**< Unknown command */
+    PA_ERR_INVALID,                /**< Invalid argument */
+    PA_ERR_EXIST,                  /**< Entity exists */
+    PA_ERR_NOENTITY,               /**< No such entity */
+    PA_ERR_CONNECTIONREFUSED,      /**< Connection refused */
+    PA_ERR_PROTOCOL,               /**< Protocol error */
+    PA_ERR_TIMEOUT,                /**< Timeout */
+    PA_ERR_AUTHKEY,                /**< No authentication key */
+    PA_ERR_INTERNAL,               /**< Internal error */
+    PA_ERR_CONNECTIONTERMINATED,   /**< Connection terminated */
+    PA_ERR_KILLED,                 /**< Entity killed */
+    PA_ERR_INVALIDSERVER,          /**< Invalid server */
+    PA_ERR_MODINITFAILED,          /**< Module initialization failed */
+    PA_ERR_BADSTATE,               /**< Bad state */
+    PA_ERR_NODATA,                 /**< No data */
+    PA_ERR_VERSION,                /**< Incompatible protocol version */
+    PA_ERR_TOOLARGE,               /**< Data too large */
+    PA_ERR_NOTSUPPORTED,           /**< Operation not supported \since 0.9.5 */
+    PA_ERR_UNKNOWN,                /**< The error code was unknown to the client */
+    PA_ERR_NOEXTENSION,            /**< Extension does not exist. \since 0.9.12 */
+    PA_ERR_OBSOLETE,               /**< Obsolete functionality. \since 0.9.15 */
+    PA_ERR_NOTIMPLEMENTED,         /**< Missing implementation. \since 0.9.15 */
+    PA_ERR_FORKED,                 /**< The caller forked without calling execve() and tried to reuse the context. \since 0.9.15 */
+    PA_ERR_IO,                     /**< An IO error happened. \since 0.9.16 */
+    PA_ERR_BUSY,                   /**< Device or resource busy. \since 0.9.17 */
+    PA_ERR_MAX                     /**< Not really an error but the first invalid error code */
+} pa_error_code_t;
+
+/** \cond fulldocs */
+#define PA_OK PA_OK
+#define PA_ERR_ACCESS PA_ERR_ACCESS
+#define PA_ERR_COMMAND PA_ERR_COMMAND
+#define PA_ERR_INVALID PA_ERR_INVALID
+#define PA_ERR_EXIST PA_ERR_EXIST
+#define PA_ERR_NOENTITY PA_ERR_NOENTITY
+#define PA_ERR_CONNECTIONREFUSED PA_ERR_CONNECTIONREFUSED
+#define PA_ERR_PROTOCOL PA_ERR_PROTOCOL
+#define PA_ERR_TIMEOUT PA_ERR_TIMEOUT
+#define PA_ERR_AUTHKEY PA_ERR_AUTHKEY
+#define PA_ERR_INTERNAL PA_ERR_INTERNAL
+#define PA_ERR_CONNECTIONTERMINATED PA_ERR_CONNECTIONTERMINATED
+#define PA_ERR_KILLED PA_ERR_KILLED
+#define PA_ERR_INVALIDSERVER PA_ERR_INVALIDSERVER
+#define PA_ERR_MODINITFAILED PA_ERR_MODINITFAILED
+#define PA_ERR_BADSTATE PA_ERR_BADSTATE
+#define PA_ERR_NODATA PA_ERR_NODATA
+#define PA_ERR_VERSION PA_ERR_VERSION
+#define PA_ERR_TOOLARGE PA_ERR_TOOLARGE
+#define PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED
+#define PA_ERR_UNKNOWN PA_ERR_UNKNOWN
+#define PA_ERR_NOEXTENSION PA_ERR_NOEXTENSION
+#define PA_ERR_OBSOLETE PA_ERR_OBSOLETE
+#define PA_ERR_NOTIMPLEMENTED PA_ERR_NOTIMPLEMENTED
+#define PA_ERR_FORKED PA_ERR_FORKED
+#define PA_ERR_MAX PA_ERR_MAX
+/** \endcond */
+
+/** Subscription event mask, as used by pa_context_subscribe() */
+typedef enum pa_subscription_mask {
+    PA_SUBSCRIPTION_MASK_NULL = 0x0000U,
+    /**< No events */
+
+    PA_SUBSCRIPTION_MASK_SINK = 0x0001U,
+    /**< Sink events */
+
+    PA_SUBSCRIPTION_MASK_SOURCE = 0x0002U,
+    /**< Source events */
+
+    PA_SUBSCRIPTION_MASK_SINK_INPUT = 0x0004U,
+    /**< Sink input events */
+
+    PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 0x0008U,
+    /**< Source output events */
+
+    PA_SUBSCRIPTION_MASK_MODULE = 0x0010U,
+    /**< Module events */
+
+    PA_SUBSCRIPTION_MASK_CLIENT = 0x0020U,
+    /**< Client events */
+
+    PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 0x0040U,
+    /**< Sample cache events */
+
+    PA_SUBSCRIPTION_MASK_SERVER = 0x0080U,
+    /**< Other global server changes. */
+
+/** \cond fulldocs */
+    PA_SUBSCRIPTION_MASK_AUTOLOAD = 0x0100U,
+    /**< \deprecated Autoload table events. */
+/** \endcond */
+
+    PA_SUBSCRIPTION_MASK_CARD = 0x0200U,
+    /**< Card events. \since 0.9.15 */
+
+    PA_SUBSCRIPTION_MASK_ALL = 0x02ffU
+    /**< Catch all events */
+} pa_subscription_mask_t;
+
+/** Subscription event types, as used by pa_context_subscribe() */
+typedef enum pa_subscription_event_type {
+    PA_SUBSCRIPTION_EVENT_SINK = 0x0000U,
+    /**< Event type: Sink */
+
+    PA_SUBSCRIPTION_EVENT_SOURCE = 0x0001U,
+    /**< Event type: Source */
+
+    PA_SUBSCRIPTION_EVENT_SINK_INPUT = 0x0002U,
+    /**< Event type: Sink input */
+
+    PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 0x0003U,
+    /**< Event type: Source output */
+
+    PA_SUBSCRIPTION_EVENT_MODULE = 0x0004U,
+    /**< Event type: Module */
+
+    PA_SUBSCRIPTION_EVENT_CLIENT = 0x0005U,
+    /**< Event type: Client */
+
+    PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 0x0006U,
+    /**< Event type: Sample cache item */
+
+    PA_SUBSCRIPTION_EVENT_SERVER = 0x0007U,
+    /**< Event type: Global server change, only occurring with PA_SUBSCRIPTION_EVENT_CHANGE. */
+
+/** \cond fulldocs */
+    PA_SUBSCRIPTION_EVENT_AUTOLOAD = 0x0008U,
+    /**< \deprecated Event type: Autoload table changes. */
+/** \endcond */
+
+    PA_SUBSCRIPTION_EVENT_CARD = 0x0009U,
+    /**< Event type: Card \since 0.9.15 */
+
+    PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 0x000FU,
+    /**< A mask to extract the event type from an event value */
+
+    PA_SUBSCRIPTION_EVENT_NEW = 0x0000U,
+    /**< A new object was created */
+
+    PA_SUBSCRIPTION_EVENT_CHANGE = 0x0010U,
+    /**< A property of the object was modified */
+
+    PA_SUBSCRIPTION_EVENT_REMOVE = 0x0020U,
+    /**< An object was removed */
+
+    PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U
+    /**< A mask to extract the event operation from an event value */
+
+} pa_subscription_event_type_t;
+
+/** Return one if an event type t matches an event mask bitfield */
+#define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))))
+
+/** \cond fulldocs */
+#define PA_SUBSCRIPTION_MASK_NULL PA_SUBSCRIPTION_MASK_NULL
+#define PA_SUBSCRIPTION_MASK_SINK PA_SUBSCRIPTION_MASK_SINK
+#define PA_SUBSCRIPTION_MASK_SOURCE PA_SUBSCRIPTION_MASK_SOURCE
+#define PA_SUBSCRIPTION_MASK_SINK_INPUT PA_SUBSCRIPTION_MASK_SINK_INPUT
+#define PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT
+#define PA_SUBSCRIPTION_MASK_MODULE PA_SUBSCRIPTION_MASK_MODULE
+#define PA_SUBSCRIPTION_MASK_CLIENT PA_SUBSCRIPTION_MASK_CLIENT
+#define PA_SUBSCRIPTION_MASK_SAMPLE_CACHE PA_SUBSCRIPTION_MASK_SAMPLE_CACHE
+#define PA_SUBSCRIPTION_MASK_SERVER PA_SUBSCRIPTION_MASK_SERVER
+#define PA_SUBSCRIPTION_MASK_AUTOLOAD PA_SUBSCRIPTION_MASK_AUTOLOAD
+#define PA_SUBSCRIPTION_MASK_CARD PA_SUBSCRIPTION_MASK_CARD
+#define PA_SUBSCRIPTION_MASK_ALL PA_SUBSCRIPTION_MASK_ALL
+#define PA_SUBSCRIPTION_EVENT_SINK PA_SUBSCRIPTION_EVENT_SINK
+#define PA_SUBSCRIPTION_EVENT_SOURCE PA_SUBSCRIPTION_EVENT_SOURCE
+#define PA_SUBSCRIPTION_EVENT_SINK_INPUT PA_SUBSCRIPTION_EVENT_SINK_INPUT
+#define PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
+#define PA_SUBSCRIPTION_EVENT_MODULE PA_SUBSCRIPTION_EVENT_MODULE
+#define PA_SUBSCRIPTION_EVENT_CLIENT PA_SUBSCRIPTION_EVENT_CLIENT
+#define PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE
+#define PA_SUBSCRIPTION_EVENT_SERVER PA_SUBSCRIPTION_EVENT_SERVER
+#define PA_SUBSCRIPTION_EVENT_AUTOLOAD PA_SUBSCRIPTION_EVENT_AUTOLOAD
+#define PA_SUBSCRIPTION_EVENT_CARD PA_SUBSCRIPTION_EVENT_CARD
+#define PA_SUBSCRIPTION_EVENT_FACILITY_MASK PA_SUBSCRIPTION_EVENT_FACILITY_MASK
+#define PA_SUBSCRIPTION_EVENT_NEW PA_SUBSCRIPTION_EVENT_NEW
+#define PA_SUBSCRIPTION_EVENT_CHANGE PA_SUBSCRIPTION_EVENT_CHANGE
+#define PA_SUBSCRIPTION_EVENT_REMOVE PA_SUBSCRIPTION_EVENT_REMOVE
+#define PA_SUBSCRIPTION_EVENT_TYPE_MASK PA_SUBSCRIPTION_EVENT_TYPE_MASK
+/** \endcond */
+
+/** A structure for all kinds of timing information of a stream. See
+ * pa_stream_update_timing_info() and pa_stream_get_timing_info(). The
+ * total output latency a sample that is written with
+ * pa_stream_write() takes to be played may be estimated by
+ * sink_usec+buffer_usec+transport_usec. (where buffer_usec is defined
+ * as pa_bytes_to_usec(write_index-read_index)) The output buffer
+ * which buffer_usec relates to may be manipulated freely (with
+ * pa_stream_write()'s seek argument, pa_stream_flush() and friends),
+ * the buffers sink_usec and source_usec relate to are first-in
+ * first-out (FIFO) buffers which cannot be flushed or manipulated in
+ * any way. The total input latency a sample that is recorded takes to
+ * be delivered to the application is:
+ * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of
+ * sign issues!) When connected to a monitor source sink_usec contains
+ * the latency of the owning sink. The two latency estimations
+ * described here are implemented in pa_stream_get_latency(). Please
+ * note that this structure can be extended as part of evolutionary
+ * API updates at any time in any new release.*/
+typedef struct pa_timing_info {
+    struct timeval timestamp;
+    /**< The time when this timing info structure was current */
+
+    int synchronized_clocks;
+    /**< Non-zero if the local and the remote machine have
+     * synchronized clocks. If synchronized clocks are detected
+     * transport_usec becomes much more reliable. However, the code
+     * that detects synchronized clocks is very limited and unreliable
+     * itself. */
+
+    pa_usec_t sink_usec;
+    /**< Time in usecs a sample takes to be played on the sink. For
+     * playback streams and record streams connected to a monitor
+     * source. */
+
+    pa_usec_t source_usec;
+    /**< Time in usecs a sample takes from being recorded to being
+     * delivered to the application. Only for record streams. */
+
+    pa_usec_t transport_usec;
+    /**< Estimated time in usecs a sample takes to be transferred
+     * to/from the daemon. For both playback and record streams. */
+
+    int playing;
+    /**< Non-zero when the stream is currently not underrun and data
+     * is being passed on to the device. Only for playback
+     * streams. This field does not say whether the data is actually
+     * already being played. To determine this check whether
+     * since_underrun (converted to usec) is larger than sink_usec.*/
+
+    int write_index_corrupt;
+    /**< Non-zero if write_index is not up-to-date because a local
+     * write command that corrupted it has been issued in the time
+     * since this latency info was current . Only write commands with
+     * SEEK_RELATIVE_ON_READ and SEEK_RELATIVE_END can corrupt
+     * write_index. */
+
+    int64_t write_index;
+    /**< Current write index into the playback buffer in bytes. Think
+     * twice before using this for seeking purposes: it might be out
+     * of date a the time you want to use it. Consider using
+     * PA_SEEK_RELATIVE instead. */
+
+    int read_index_corrupt;
+    /**< Non-zero if read_index is not up-to-date because a local
+     * pause or flush request that corrupted it has been issued in the
+     * time since this latency info was current. */
+
+    int64_t read_index;
+    /**< Current read index into the playback buffer in bytes. Think
+     * twice before using this for seeking purposes: it might be out
+     * of date a the time you want to use it. Consider using
+     * PA_SEEK_RELATIVE_ON_READ instead. */
+
+    pa_usec_t configured_sink_usec;
+    /**< The configured latency for the sink. \since 0.9.11 */
+
+    pa_usec_t configured_source_usec;
+    /**< The configured latency for the source. \since 0.9.11 */
+
+    int64_t since_underrun;
+    /**< Bytes that were handed to the sink since the last underrun
+     * happened, or since playback started again after the last
+     * underrun. playing will tell you which case it is. \since
+     * 0.9.11 */
+
+} pa_timing_info;
+
+/** A structure for the spawn api. This may be used to integrate auto
+ * spawned daemons into your application. For more information see
+ * pa_context_connect(). When spawning a new child process the
+ * waitpid() is used on the child's PID. The spawn routine will not
+ * block or ignore SIGCHLD signals, since this cannot be done in a
+ * thread compatible way. You might have to do this in
+ * prefork/postfork. */
+typedef struct pa_spawn_api {
+    void (*prefork)(void);
+    /**< Is called just before the fork in the parent process. May be
+     * NULL. */
+
+    void (*postfork)(void);
+    /**< Is called immediately after the fork in the parent
+     * process. May be NULL.*/
+
+    void (*atfork)(void);
+    /**< Is called immediately after the fork in the child
+     * process. May be NULL. It is not safe to close all file
+     * descriptors in this function unconditionally, since a UNIX
+     * socket (created using socketpair()) is passed to the new
+     * process. */
+} pa_spawn_api;
+
+/** Seek type for pa_stream_write(). */
+typedef enum pa_seek_mode {
+    PA_SEEK_RELATIVE = 0,
+    /**< Seek relatively to the write index */
+
+    PA_SEEK_ABSOLUTE = 1,
+    /**< Seek relatively to the start of the buffer queue */
+
+    PA_SEEK_RELATIVE_ON_READ = 2,
+    /**< Seek relatively to the read index.  */
+
+    PA_SEEK_RELATIVE_END = 3
+    /**< Seek relatively to the current end of the buffer queue. */
+} pa_seek_mode_t;
+
+/** \cond fulldocs */
+#define PA_SEEK_RELATIVE PA_SEEK_RELATIVE
+#define PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
+#define PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
+#define PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
+/** \endcond */
+
+/** Special sink flags. */
+typedef enum pa_sink_flags {
+    PA_SINK_NOFLAGS = 0x0000U,
+    /**< Flag to pass when no specific options are needed (used to avoid casting)  \since 0.9.19 */
+
+    PA_SINK_HW_VOLUME_CTRL = 0x0001U,
+    /**< Supports hardware volume control. This is a dynamic flag and may
+     * change at runtime after the sink has initialized */
+
+    PA_SINK_LATENCY = 0x0002U,
+    /**< Supports latency querying */
+
+    PA_SINK_HARDWARE = 0x0004U,
+    /**< Is a hardware sink of some kind, in contrast to
+     * "virtual"/software sinks \since 0.9.3 */
+
+    PA_SINK_NETWORK = 0x0008U,
+    /**< Is a networked sink of some kind. \since 0.9.7 */
+
+    PA_SINK_HW_MUTE_CTRL = 0x0010U,
+    /**< Supports hardware mute control. This is a dynamic flag and may
+     * change at runtime after the sink has initialized \since 0.9.11 */
+
+    PA_SINK_DECIBEL_VOLUME = 0x0020U,
+    /**< Volume can be translated to dB with pa_sw_volume_to_dB(). This is a
+     * dynamic flag and may change at runtime after the sink has initialized
+     * \since 0.9.11 */
+
+    PA_SINK_FLAT_VOLUME = 0x0040U,
+    /**< This sink is in flat volume mode, i.e.\ always the maximum of
+     * the volume of all connected inputs. \since 0.9.15 */
+
+    PA_SINK_DYNAMIC_LATENCY = 0x0080U,
+    /**< The latency can be adjusted dynamically depending on the
+     * needs of the connected streams. \since 0.9.15 */
+
+    PA_SINK_SET_FORMATS = 0x0100U,
+    /**< The sink allows setting what formats are supported by the connected
+     * hardware. The actual functionality to do this might be provided by an
+     * extension. \since 1.0 */
+
+#ifdef __INCLUDED_FROM_PULSE_AUDIO
+/** \cond fulldocs */
+    /* PRIVATE: Server-side values -- do not try to use these at client-side.
+     * The server will filter out these flags anyway, so you should never see
+     * these flags in sinks. */
+
+    PA_SINK_SHARE_VOLUME_WITH_MASTER = 0x1000000U,
+    /**< This sink shares the volume with the master sink (used by some filter
+     * sinks). */
+
+    PA_SINK_DEFERRED_VOLUME = 0x2000000U,
+    /**< The HW volume changes are syncronized with SW volume. */
+/** \endcond */
+#endif
+
+} pa_sink_flags_t;
+
+/** \cond fulldocs */
+#define PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
+#define PA_SINK_LATENCY PA_SINK_LATENCY
+#define PA_SINK_HARDWARE PA_SINK_HARDWARE
+#define PA_SINK_NETWORK PA_SINK_NETWORK
+#define PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
+#define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
+#define PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
+#define PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
+#define PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
+#ifdef __INCLUDED_FROM_PULSE_AUDIO
+#define PA_SINK_CLIENT_FLAGS_MASK 0xFFFFFF
+#endif
+
+/** \endcond */
+
+/** Sink state. \since 0.9.15 */
+typedef enum pa_sink_state { /* enum serialized in u8 */
+    PA_SINK_INVALID_STATE = -1,
+    /**< This state is used when the server does not support sink state introspection \since 0.9.15 */
+
+    PA_SINK_RUNNING = 0,
+    /**< Running, sink is playing and used by at least one non-corked sink-input \since 0.9.15 */
+
+    PA_SINK_IDLE = 1,
+    /**< When idle, the sink is playing but there is no non-corked sink-input attached to it \since 0.9.15 */
+
+    PA_SINK_SUSPENDED = 2,
+    /**< When suspended, actual sink access can be closed, for instance \since 0.9.15 */
+
+/** \cond fulldocs */
+    /* PRIVATE: Server-side values -- DO NOT USE THIS ON THE CLIENT
+     * SIDE! These values are *not* considered part of the official PA
+     * API/ABI. If you use them your application might break when PA
+     * is upgraded. Also, please note that these values are not useful
+     * on the client side anyway. */
+
+    PA_SINK_INIT = -2,
+    /**< Initialization state */
+
+    PA_SINK_UNLINKED = -3
+    /**< The state when the sink is getting unregistered and removed from client access */
+/** \endcond */
+
+} pa_sink_state_t;
+
+/** Returns non-zero if sink is playing: running or idle. \since 0.9.15 */
+static inline int PA_SINK_IS_OPENED(pa_sink_state_t x) {
+    return x == PA_SINK_RUNNING || x == PA_SINK_IDLE;
+}
+
+/** Returns non-zero if sink is running. \since 1.0 */
+static inline int PA_SINK_IS_RUNNING(pa_sink_state_t x) {
+    return x == PA_SINK_RUNNING;
+}
+
+/** \cond fulldocs */
+#define PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
+#define PA_SINK_RUNNING PA_SINK_RUNNING
+#define PA_SINK_IDLE PA_SINK_IDLE
+#define PA_SINK_SUSPENDED PA_SINK_SUSPENDED
+#define PA_SINK_INIT PA_SINK_INIT
+#define PA_SINK_UNLINKED PA_SINK_UNLINKED
+#define PA_SINK_IS_OPENED PA_SINK_IS_OPENED
+/** \endcond */
+
+/** Special source flags.  */
+typedef enum pa_source_flags {
+    PA_SOURCE_NOFLAGS = 0x0000U,
+    /**< Flag to pass when no specific options are needed (used to avoid casting)  \since 0.9.19 */
+
+    PA_SOURCE_HW_VOLUME_CTRL = 0x0001U,
+    /**< Supports hardware volume control. This is a dynamic flag and may
+     * change at runtime after the source has initialized */
+
+    PA_SOURCE_LATENCY = 0x0002U,
+    /**< Supports latency querying */
+
+    PA_SOURCE_HARDWARE = 0x0004U,
+    /**< Is a hardware source of some kind, in contrast to
+     * "virtual"/software source \since 0.9.3 */
+
+    PA_SOURCE_NETWORK = 0x0008U,
+    /**< Is a networked source of some kind. \since 0.9.7 */
+
+    PA_SOURCE_HW_MUTE_CTRL = 0x0010U,
+    /**< Supports hardware mute control. This is a dynamic flag and may
+     * change at runtime after the source has initialized \since 0.9.11 */
+
+    PA_SOURCE_DECIBEL_VOLUME = 0x0020U,
+    /**< Volume can be translated to dB with pa_sw_volume_to_dB(). This is a
+     * dynamic flag and may change at runtime after the source has initialized
+     * \since 0.9.11 */
+
+    PA_SOURCE_DYNAMIC_LATENCY = 0x0040U,
+    /**< The latency can be adjusted dynamically depending on the
+     * needs of the connected streams. \since 0.9.15 */
+
+    PA_SOURCE_FLAT_VOLUME = 0x0080U,
+    /**< This source is in flat volume mode, i.e.\ always the maximum of
+     * the volume of all connected outputs. \since 1.0 */
+
+#ifdef __INCLUDED_FROM_PULSE_AUDIO
+/** \cond fulldocs */
+    /* PRIVATE: Server-side values -- do not try to use these at client-side.
+     * The server will filter out these flags anyway, so you should never see
+     * these flags in sources. */
+
+    PA_SOURCE_SHARE_VOLUME_WITH_MASTER = 0x1000000U,
+    /**< This source shares the volume with the master source (used by some filter
+     * sources). */
+
+    PA_SOURCE_DEFERRED_VOLUME = 0x2000000U,
+    /**< The HW volume changes are syncronized with SW volume. */
+#endif
+} pa_source_flags_t;
+
+/** \cond fulldocs */
+#define PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
+#define PA_SOURCE_LATENCY PA_SOURCE_LATENCY
+#define PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
+#define PA_SOURCE_NETWORK PA_SOURCE_NETWORK
+#define PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
+#define PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
+#define PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
+#define PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
+#ifdef __INCLUDED_FROM_PULSE_AUDIO
+#define PA_SOURCE_CLIENT_FLAGS_MASK 0xFFFFFF
+#endif
+
+/** \endcond */
+
+/** Source state. \since 0.9.15 */
+typedef enum pa_source_state {
+    PA_SOURCE_INVALID_STATE = -1,
+    /**< This state is used when the server does not support source state introspection \since 0.9.15 */
+
+    PA_SOURCE_RUNNING = 0,
+    /**< Running, source is recording and used by at least one non-corked source-output \since 0.9.15 */
+
+    PA_SOURCE_IDLE = 1,
+    /**< When idle, the source is still recording but there is no non-corked source-output \since 0.9.15 */
+
+    PA_SOURCE_SUSPENDED = 2,
+    /**< When suspended, actual source access can be closed, for instance \since 0.9.15 */
+
+/** \cond fulldocs */
+    /* PRIVATE: Server-side values -- DO NOT USE THIS ON THE CLIENT
+     * SIDE! These values are *not* considered part of the official PA
+     * API/ABI. If you use them your application might break when PA
+     * is upgraded. Also, please note that these values are not useful
+     * on the client side anyway. */
+
+    PA_SOURCE_INIT = -2,
+    /**< Initialization state */
+
+    PA_SOURCE_UNLINKED = -3
+    /**< The state when the source is getting unregistered and removed from client access */
+/** \endcond */
+
+} pa_source_state_t;
+
+/** Returns non-zero if source is recording: running or idle. \since 0.9.15 */
+static inline int PA_SOURCE_IS_OPENED(pa_source_state_t x) {
+    return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE;
+}
+
+/** Returns non-zero if source is running \since 1.0 */
+static inline int PA_SOURCE_IS_RUNNING(pa_source_state_t x) {
+    return x == PA_SOURCE_RUNNING;
+}
+
+/** \cond fulldocs */
+#define PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
+#define PA_SOURCE_RUNNING PA_SOURCE_RUNNING
+#define PA_SOURCE_IDLE PA_SOURCE_IDLE
+#define PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
+#define PA_SOURCE_INIT PA_SOURCE_INIT
+#define PA_SOURCE_UNLINKED PA_SOURCE_UNLINKED
+#define PA_SOURCE_IS_OPENED PA_SOURCE_IS_OPENED
+/** \endcond */
+
+/** A generic free() like callback prototype */
+typedef void (*pa_free_cb_t)(void *p);
+
+/** A stream policy/meta event requesting that an application should
+ * cork a specific stream. See pa_stream_event_cb_t for more
+ * information. \since 0.9.15 */
+#define PA_STREAM_EVENT_REQUEST_CORK "request-cork"
+
+/** A stream policy/meta event requesting that an application should
+ * cork a specific stream. See pa_stream_event_cb_t for more
+ * information, \since 0.9.15 */
+#define PA_STREAM_EVENT_REQUEST_UNCORK "request-uncork"
+
+/** A stream event notifying that the stream is going to be
+ * disconnected because the underlying sink changed and no longer
+ * supports the format that was originally negotiated. Clients need
+ * to connect a new stream to renegotiate a format and continue
+ * playback. \since 1.0 */
+#define PA_STREAM_EVENT_FORMAT_LOST "format-lost"
+
+#ifndef __INCLUDED_FROM_PULSE_AUDIO
+/** Port availability / jack detection status
+ * \since 2.0 */
+typedef enum pa_port_available {
+    PA_PORT_AVAILABLE_UNKNOWN = 0, /**< This port does not support jack detection \since 2.0 */
+    PA_PORT_AVAILABLE_NO = 1,      /**< This port is not available, likely because the jack is not plugged in. \since 2.0 */
+    PA_PORT_AVAILABLE_YES = 2,     /**< This port is available, likely because the jack is plugged in. \since 2.0 */
+} pa_port_available_t;
+
+/** \cond fulldocs */
+#define PA_PORT_AVAILABLE_UNKNOWN PA_PORT_AVAILABLE_UNKNOWN
+#define PA_PORT_AVAILABLE_NO PA_PORT_AVAILABLE_NO
+#define PA_PORT_AVAILABLE_YES PA_PORT_AVAILABLE_YES
+
+/** \endcond */
+#endif
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/direction.c b/src/pulse/direction.c
new file mode 100644 (file)
index 0000000..ab64f49
--- /dev/null
@@ -0,0 +1,44 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 Intel Corporation
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "direction.h"
+
+#include <pulsecore/i18n.h>
+
+int pa_direction_valid(pa_direction_t direction) {
+    if (direction != PA_DIRECTION_INPUT
+            && direction != PA_DIRECTION_OUTPUT
+            && direction != (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT))
+        return 0;
+
+    return 1;
+}
+
+const char *pa_direction_to_string(pa_direction_t direction) {
+    pa_init_i18n();
+
+    if (direction == PA_DIRECTION_INPUT)
+        return _("input");
+    if (direction == PA_DIRECTION_OUTPUT)
+        return _("output");
+    if (direction == (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT))
+        return _("bidirectional");
+
+    return _("invalid");
+}
diff --git a/src/pulse/direction.h b/src/pulse/direction.h
new file mode 100644 (file)
index 0000000..65ece69
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef foodirectionhfoo
+#define foodirectionhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 Intel Corporation
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/def.h>
+
+/** \file
+ * Utility functions for \ref pa_direction_t. */
+
+/** Return non-zero if the given value is a valid direction (either input,
+ * output or bidirectional). \since 6.0 */
+int pa_direction_valid(pa_direction_t direction) PA_GCC_CONST;
+
+/** Return a textual representation of the direction. \since 6.0 */
+const char *pa_direction_to_string(pa_direction_t direction);
+
+#endif
diff --git a/src/pulse/error.c b/src/pulse/error.c
new file mode 100644 (file)
index 0000000..2760928
--- /dev/null
@@ -0,0 +1,76 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/def.h>
+
+#include <pulsecore/i18n.h>
+
+#include "error.h"
+
+const char*pa_strerror(int error) {
+
+    static const char* const errortab[PA_ERR_MAX] = {
+        [PA_OK] = N_("OK"),
+        [PA_ERR_ACCESS] = N_("Access denied"),
+        [PA_ERR_COMMAND] = N_("Unknown command"),
+        [PA_ERR_INVALID] = N_("Invalid argument"),
+        [PA_ERR_EXIST] = N_("Entity exists"),
+        [PA_ERR_NOENTITY] = N_("No such entity"),
+        [PA_ERR_CONNECTIONREFUSED] = N_("Connection refused"),
+        [PA_ERR_PROTOCOL] = N_("Protocol error"),
+        [PA_ERR_TIMEOUT] = N_("Timeout"),
+        [PA_ERR_AUTHKEY] = N_("No authentication key"),
+        [PA_ERR_INTERNAL] = N_("Internal error"),
+        [PA_ERR_CONNECTIONTERMINATED] = N_("Connection terminated"),
+        [PA_ERR_KILLED] = N_("Entity killed"),
+        [PA_ERR_INVALIDSERVER] = N_("Invalid server"),
+        [PA_ERR_MODINITFAILED] = N_("Module initialization failed"),
+        [PA_ERR_BADSTATE] = N_("Bad state"),
+        [PA_ERR_NODATA] = N_("No data"),
+        [PA_ERR_VERSION] = N_("Incompatible protocol version"),
+        [PA_ERR_TOOLARGE] = N_("Too large"),
+        [PA_ERR_NOTSUPPORTED] = N_("Not supported"),
+        [PA_ERR_UNKNOWN] = N_("Unknown error code"),
+        [PA_ERR_NOEXTENSION] = N_("No such extension"),
+        [PA_ERR_OBSOLETE] = N_("Obsolete functionality"),
+        [PA_ERR_NOTIMPLEMENTED] = N_("Missing implementation"),
+        [PA_ERR_FORKED] = N_("Client forked"),
+        [PA_ERR_IO] = N_("Input/Output error"),
+        [PA_ERR_BUSY] = N_("Device or resource busy")
+    };
+
+    pa_init_i18n();
+
+    if (error < 0)
+        error = -error;
+
+    if (error >= PA_ERR_MAX)
+        return NULL;
+
+    return _(errortab[error]);
+}
diff --git a/src/pulse/error.h b/src/pulse/error.h
new file mode 100644 (file)
index 0000000..7b9b84a
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef fooerrorhfoo
+#define fooerrorhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/version.h>
+
+/** \file
+ * Error management */
+
+PA_C_DECL_BEGIN
+
+/** Return a human readable error message for the specified numeric error code */
+const char* pa_strerror(int error);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c
new file mode 100644 (file)
index 0000000..38f96da
--- /dev/null
@@ -0,0 +1,434 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+  Copyright 2009 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/context.h>
+#include <pulse/xmalloc.h>
+#include <pulse/fork-detect.h>
+#include <pulse/operation.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+#include "ext-device-manager.h"
+
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_READ,
+    SUBCOMMAND_RENAME,
+    SUBCOMMAND_DELETE,
+    SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
+    SUBCOMMAND_REORDER,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT
+};
+
+static void ext_device_manager_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    uint32_t version = PA_INVALID_INDEX;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+    } else if (pa_tagstruct_getu32(t, &version) < 0 ||
+               !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_ext_device_manager_test_cb_t cb = (pa_ext_device_manager_test_cb_t) o->callback;
+        cb(o->context, version, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_device_manager_test(
+        pa_context *c,
+        pa_ext_device_manager_test_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_manager_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+static void ext_device_manager_read_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_ext_device_manager_info i;
+
+            memset(&i, 0, sizeof(i));
+
+            if (pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_gets(t, &i.description) < 0 ||
+                pa_tagstruct_gets(t, &i.icon) < 0 ||
+                pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_getu32(t, &i.n_role_priorities) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (i.n_role_priorities > 0) {
+                uint32_t j;
+                i.role_priorities = pa_xnew0(pa_ext_device_manager_role_priority_info, i.n_role_priorities+1);
+
+                for (j = 0; j < i.n_role_priorities; j++) {
+
+                    if (pa_tagstruct_gets(t, &i.role_priorities[j].role) < 0 ||
+                        pa_tagstruct_getu32(t, &i.role_priorities[j].priority) < 0) {
+
+                        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                        pa_xfree(i.role_priorities);
+                        goto finish;
+                    }
+                }
+
+                /* Terminate with an extra NULL entry, just to make sure */
+                i.role_priorities[j].role = NULL;
+                i.role_priorities[j].priority = 0;
+            }
+
+            if (o->callback) {
+                pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_xfree(i.role_priorities);
+        }
+    }
+
+    if (o->callback) {
+        pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_device_manager_read(
+        pa_context *c,
+        pa_ext_device_manager_read_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_READ);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_manager_read_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_manager_set_device_description(
+        pa_context *c,
+        const char* device,
+        const char* description,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(device);
+    pa_assert(description);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, *description, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_RENAME);
+
+    pa_tagstruct_puts(t, device);
+    pa_tagstruct_puts(t, description);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_manager_delete(
+        pa_context *c,
+        const char *const s[],
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+    const char *const *k;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(s);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_DELETE);
+
+    for (k = s; *k; k++) {
+        if (!*k || !**k)
+            goto fail;
+
+        pa_tagstruct_puts(t, *k);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+
+fail:
+    if (o) {
+        pa_operation_cancel(o);
+        pa_operation_unref(o);
+    }
+
+    if (t)
+        pa_tagstruct_free(t);
+
+    pa_context_set_error(c, PA_ERR_INVALID);
+    return NULL;
+}
+
+pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING);
+    pa_tagstruct_put_boolean(t, !!enable);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_manager_reorder_devices_for_role(
+        pa_context *c,
+        const char* role,
+        const char** devices,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag, i;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    pa_assert(role);
+    pa_assert(devices);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_REORDER);
+    pa_tagstruct_puts(t, role);
+
+    i = 0; while (devices[i]) i++;
+    pa_tagstruct_putu32(t, i);
+
+    i = 0;
+    while (devices[i])
+        pa_tagstruct_puts(t, devices[i++]);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_manager_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE);
+    pa_tagstruct_put_boolean(t, enable);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+void pa_ext_device_manager_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_device_manager_subscribe_cb_t cb,
+        void *userdata) {
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    c->ext_device_manager.callback = cb;
+    c->ext_device_manager.userdata = userdata;
+}
+
+void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
+    uint32_t subcommand;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
+        !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (subcommand != SUBCOMMAND_EVENT) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (c->ext_device_manager.callback)
+        c->ext_device_manager.callback(c, c->ext_device_manager.userdata);
+}
diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h
new file mode 100644 (file)
index 0000000..8c05e1c
--- /dev/null
@@ -0,0 +1,130 @@
+#ifndef foopulseextdevicemanagerhfoo
+#define foopulseextdevicemanagerhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+  Copyright 2009 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/context.h>
+#include <pulse/version.h>
+
+/** \file
+ *
+ * Routines for controlling module-device-manager
+ */
+
+PA_C_DECL_BEGIN
+
+/* Don't extend this struct! It will break binary compatibility, because
+ * pa_ext_device_manager_info.role_priorities points to an array of structs
+ * instead of an array of pointers to structs. */
+typedef struct pa_ext_device_manager_role_priority_info {
+    const char *role;
+    uint32_t priority;
+} pa_ext_device_manager_role_priority_info;
+
+/** Stores information about one device in the device database that is
+ * maintained by module-device-manager. \since 0.9.21 */
+typedef struct pa_ext_device_manager_info {
+    const char *name;            /**< Identifier string of the device. A string like "sink:" or similar followed by the name of the device. */
+    const char *description;     /**< The description of the device when it was last seen, if applicable and saved */
+    const char *icon;            /**< The icon given to the device */
+    uint32_t index;              /**< The device index if it is currently available or PA_INVALID_INDEX */
+    uint32_t n_role_priorities;  /**< How many role priorities do we have? */
+    pa_ext_device_manager_role_priority_info *role_priorities; /**< An array of role priority structures or NULL */
+} pa_ext_device_manager_info;
+
+/** Callback prototype for pa_ext_device_manager_test(). \since 0.9.21 */
+typedef void (*pa_ext_device_manager_test_cb_t)(
+        pa_context *c,
+        uint32_t version,
+        void *userdata);
+
+/** Test if this extension module is available in the server. \since 0.9.21 */
+pa_operation *pa_ext_device_manager_test(
+        pa_context *c,
+        pa_ext_device_manager_test_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_device_manager_read(). \since 0.9.21 */
+typedef void (*pa_ext_device_manager_read_cb_t)(
+        pa_context *c,
+        const pa_ext_device_manager_info *info,
+        int eol,
+        void *userdata);
+
+/** Read all entries from the device database. \since 0.9.21 */
+pa_operation *pa_ext_device_manager_read(
+        pa_context *c,
+        pa_ext_device_manager_read_cb_t cb,
+        void *userdata);
+
+/** Sets the description for a device. \since 0.9.21 */
+pa_operation *pa_ext_device_manager_set_device_description(
+        pa_context *c,
+        const char* device,
+        const char* description,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Delete entries from the device database. \since 0.9.21 */
+pa_operation *pa_ext_device_manager_delete(
+        pa_context *c,
+        const char *const s[],
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Enable the role-based device-priority routing mode. \since 0.9.21 */
+pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Prefer a given device in the priority list. \since 0.9.21 */
+pa_operation *pa_ext_device_manager_reorder_devices_for_role(
+        pa_context *c,
+        const char* role,
+        const char** devices,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Subscribe to changes in the device database. \since 0.9.21 */
+pa_operation *pa_ext_device_manager_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_device_manager_set_subscribe_cb(). \since 0.9.21 */
+typedef void (*pa_ext_device_manager_subscribe_cb_t)(
+        pa_context *c,
+        void *userdata);
+
+/** Set the subscription callback that is called when
+ * pa_ext_device_manager_subscribe() was called. \since 0.9.21 */
+void pa_ext_device_manager_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_device_manager_subscribe_cb_t cb,
+        void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/ext-device-restore.c b/src/pulse/ext-device-restore.c
new file mode 100644 (file)
index 0000000..5c6d88b
--- /dev/null
@@ -0,0 +1,373 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+  Copyright 2011 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/context.h>
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+#include <pulse/fork-detect.h>
+#include <pulse/operation.h>
+#include <pulse/format.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+#include "ext-device-restore.h"
+
+/* Protocol extension commands */
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT,
+    SUBCOMMAND_READ_FORMATS_ALL,
+    SUBCOMMAND_READ_FORMATS,
+    SUBCOMMAND_SAVE_FORMATS
+};
+
+static void ext_device_restore_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    uint32_t version = PA_INVALID_INDEX;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+    } else if (pa_tagstruct_getu32(t, &version) < 0 ||
+               !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_ext_device_restore_test_cb_t cb = (pa_ext_device_restore_test_cb_t) o->callback;
+        cb(o->context, version, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_device_restore_test(
+        pa_context *c,
+        pa_ext_device_restore_test_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_restore_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE);
+    pa_tagstruct_put_boolean(t, enable);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+void pa_ext_device_restore_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_device_restore_subscribe_cb_t cb,
+        void *userdata) {
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    c->ext_device_restore.callback = cb;
+    c->ext_device_restore.userdata = userdata;
+}
+
+static void ext_device_restore_read_device_formats_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+        uint8_t j;
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_ext_device_restore_info i;
+            pa_zero(i);
+
+            if (pa_tagstruct_getu32(t, &i.type) < 0 ||
+                pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_getu8(t, &i.n_formats) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (PA_DEVICE_TYPE_SINK != i.type && PA_DEVICE_TYPE_SOURCE != i.type) {
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (i.index == PA_INVALID_INDEX) {
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (i.n_formats > 0) {
+                i.formats = pa_xnew0(pa_format_info*, i.n_formats);
+
+                for (j = 0; j < i.n_formats; j++) {
+
+                    pa_format_info *f = i.formats[j] = pa_format_info_new();
+                    if (pa_tagstruct_get_format_info(t, f) < 0) {
+                        uint8_t k;
+
+                        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                        for (k = 0; k < j+1; k++)
+                            pa_format_info_free(i.formats[k]);
+                        pa_xfree(i.formats);
+                        goto finish;
+                    }
+                }
+            }
+
+            if (o->callback) {
+                pa_ext_device_restore_read_device_formats_cb_t cb = (pa_ext_device_restore_read_device_formats_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            for (j = 0; j < i.n_formats; j++)
+                pa_format_info_free(i.formats[j]);
+            pa_xfree(i.formats);
+        }
+    }
+
+    if (o->callback) {
+        pa_ext_device_restore_read_device_formats_cb_t cb = (pa_ext_device_restore_read_device_formats_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_device_restore_read_formats_all(
+        pa_context *c,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_READ_FORMATS_ALL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_read_device_formats_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_restore_read_formats(
+        pa_context *c,
+        pa_device_type_t type,
+        uint32_t idx,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(idx != PA_INVALID_INDEX);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_READ_FORMATS);
+    pa_tagstruct_putu32(t, type);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_read_device_formats_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_restore_save_formats(
+        pa_context *c,
+        pa_device_type_t type,
+        uint32_t idx,
+        uint8_t n_formats,
+        pa_format_info **formats,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint8_t j;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(idx != PA_INVALID_INDEX);
+    pa_assert(n_formats > 0);
+    pa_assert(formats && *formats);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_SAVE_FORMATS);
+
+    pa_tagstruct_putu32(t, type);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_putu8(t, n_formats);
+    for (j = 0; j < n_formats; j++)
+        pa_tagstruct_put_format_info(t, formats[j]);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+/* Command function defined in internal.h */
+void pa_ext_device_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
+    uint32_t subcommand;
+    pa_device_type_t type;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
+        pa_tagstruct_getu32(t, &type) < 0 ||
+        pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (subcommand != SUBCOMMAND_EVENT) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (PA_DEVICE_TYPE_SINK != type && PA_DEVICE_TYPE_SOURCE != type) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (idx == PA_INVALID_INDEX) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (c->ext_device_restore.callback)
+        c->ext_device_restore.callback(c, type, idx, c->ext_device_restore.userdata);
+}
diff --git a/src/pulse/ext-device-restore.h b/src/pulse/ext-device-restore.h
new file mode 100644 (file)
index 0000000..246b060
--- /dev/null
@@ -0,0 +1,110 @@
+#ifndef foopulseextdevicerestorehfoo
+#define foopulseextdevicerestorehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+  Copyright 2011 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/context.h>
+#include <pulse/format.h>
+#include <pulse/version.h>
+
+/** \file
+ *
+ * Routines for controlling module-device-restore
+ */
+
+PA_C_DECL_BEGIN
+
+/** Stores information about one device in the device database that is
+ * maintained by module-device-manager. \since 1.0 */
+typedef struct pa_ext_device_restore_info {
+    pa_device_type_t type;       /**< Device type sink or source? */
+    uint32_t index;              /**< The device index */
+    uint8_t n_formats;           /**< How many formats do we have? */
+    pa_format_info **formats;    /**< An array of formats (may be NULL if n_formats == 0) */
+} pa_ext_device_restore_info;
+
+/** Callback prototype for pa_ext_device_restore_test(). \since 1.0 */
+typedef void (*pa_ext_device_restore_test_cb_t)(
+        pa_context *c,
+        uint32_t version,
+        void *userdata);
+
+/** Test if this extension module is available in the server. \since 1.0 */
+pa_operation *pa_ext_device_restore_test(
+        pa_context *c,
+        pa_ext_device_restore_test_cb_t cb,
+        void *userdata);
+
+/** Subscribe to changes in the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_device_restore_set_subscribe_cb(). \since 1.0 */
+typedef void (*pa_ext_device_restore_subscribe_cb_t)(
+        pa_context *c,
+        pa_device_type_t type,
+        uint32_t idx,
+        void *userdata);
+
+/** Set the subscription callback that is called when
+ * pa_ext_device_restore_subscribe() was called. \since 1.0 */
+void pa_ext_device_restore_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_device_restore_subscribe_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_device_restore_read_formats(). \since 1.0 */
+typedef void (*pa_ext_device_restore_read_device_formats_cb_t)(
+        pa_context *c,
+        const pa_ext_device_restore_info *info,
+        int eol,
+        void *userdata);
+
+/** Read the formats for all present devices from the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_read_formats_all(
+        pa_context *c,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata);
+
+/** Read an entry from the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_read_formats(
+        pa_context *c,
+        pa_device_type_t type,
+        uint32_t idx,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata);
+
+/** Read an entry from the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_save_formats(
+        pa_context *c,
+        pa_device_type_t type,
+        uint32_t idx,
+        uint8_t n_formats,
+        pa_format_info **formats,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/ext-stream-restore.c b/src/pulse/ext-stream-restore.c
new file mode 100644 (file)
index 0000000..a250d24
--- /dev/null
@@ -0,0 +1,361 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/context.h>
+#include <pulse/fork-detect.h>
+#include <pulse/operation.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+#include "ext-stream-restore.h"
+
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_READ,
+    SUBCOMMAND_WRITE,
+    SUBCOMMAND_DELETE,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT
+};
+
+static void ext_stream_restore_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    uint32_t version = PA_INVALID_INDEX;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+    } else if (pa_tagstruct_getu32(t, &version) < 0 ||
+               !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_ext_stream_restore_test_cb_t cb = (pa_ext_stream_restore_test_cb_t) o->callback;
+        cb(o->context, version, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_stream_restore_test(
+        pa_context *c,
+        pa_ext_stream_restore_test_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-stream-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_stream_restore_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+static void ext_stream_restore_read_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_ext_stream_restore_info i;
+            bool mute = false;
+
+            memset(&i, 0, sizeof(i));
+
+            if (pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                pa_tagstruct_gets(t, &i.device) < 0 ||
+                pa_tagstruct_get_boolean(t, &mute) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            i.mute = (int) mute;
+
+            if (o->callback) {
+                pa_ext_stream_restore_read_cb_t cb = (pa_ext_stream_restore_read_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+        }
+    }
+
+    if (o->callback) {
+        pa_ext_stream_restore_read_cb_t cb = (pa_ext_stream_restore_read_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_stream_restore_read(
+        pa_context *c,
+        pa_ext_stream_restore_read_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-stream-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_READ);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_stream_restore_read_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_stream_restore_write(
+        pa_context *c,
+        pa_update_mode_t mode,
+        const pa_ext_stream_restore_info data[],
+        unsigned n,
+        int apply_immediately,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE || mode == PA_UPDATE_SET);
+    pa_assert(data);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-stream-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_WRITE);
+
+    pa_tagstruct_putu32(t, mode);
+    pa_tagstruct_put_boolean(t, apply_immediately);
+
+    for (; n > 0; n--, data++) {
+        if (!data->name || !*data->name)
+            goto fail;
+
+        pa_tagstruct_puts(t, data->name);
+
+        if (data->volume.channels > 0 &&
+            !pa_cvolume_compatible_with_channel_map(&data->volume, &data->channel_map))
+            goto fail;
+
+        pa_tagstruct_put_channel_map(t, &data->channel_map);
+        pa_tagstruct_put_cvolume(t, &data->volume);
+        pa_tagstruct_puts(t, data->device);
+        pa_tagstruct_put_boolean(t, data->mute);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+
+fail:
+    pa_operation_cancel(o);
+    pa_operation_unref(o);
+
+    pa_tagstruct_free(t);
+
+    pa_context_set_error(c, PA_ERR_INVALID);
+    return NULL;
+}
+
+pa_operation *pa_ext_stream_restore_delete(
+        pa_context *c,
+        const char *const s[],
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+    const char *const *k;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(s);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-stream-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_DELETE);
+
+    for (k = s; *k; k++) {
+        if (!*k || !**k)
+            goto fail;
+
+        pa_tagstruct_puts(t, *k);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+
+fail:
+    pa_operation_cancel(o);
+    pa_operation_unref(o);
+
+    pa_tagstruct_free(t);
+
+    pa_context_set_error(c, PA_ERR_INVALID);
+    return NULL;
+}
+
+pa_operation *pa_ext_stream_restore_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-stream-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE);
+    pa_tagstruct_put_boolean(t, enable);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+void pa_ext_stream_restore_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_stream_restore_subscribe_cb_t cb,
+        void *userdata) {
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    c->ext_stream_restore.callback = cb;
+    c->ext_stream_restore.userdata = userdata;
+}
+
+void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
+    uint32_t subcommand;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
+        !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (subcommand != SUBCOMMAND_EVENT) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (c->ext_stream_restore.callback)
+        c->ext_stream_restore.callback(c, c->ext_stream_restore.userdata);
+}
diff --git a/src/pulse/ext-stream-restore.h b/src/pulse/ext-stream-restore.h
new file mode 100644 (file)
index 0000000..dd7f4ae
--- /dev/null
@@ -0,0 +1,109 @@
+#ifndef foopulseextstreamrestorehfoo
+#define foopulseextstreamrestorehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/context.h>
+#include <pulse/version.h>
+#include <pulse/volume.h>
+#include <pulse/channelmap.h>
+
+/** \file
+ *
+ * Routines for controlling module-stream-restore
+ */
+
+PA_C_DECL_BEGIN
+
+/** Stores information about one entry in the stream database that is
+ * maintained by module-stream-restore. \since 0.9.12 */
+typedef struct pa_ext_stream_restore_info {
+    const char *name;            /**< Identifier string of the stream. A string like "sink-input-by-role:" or similar followed by some arbitrary property value. */
+    pa_channel_map channel_map;  /**< The channel map for the volume field, if applicable */
+    pa_cvolume volume;           /**< The volume of the stream when it was seen last, if applicable and saved */
+    const char *device;          /**< The sink/source of the stream when it was last seen, if applicable and saved */
+    int mute;                    /**< The boolean mute state of the stream when it was last seen, if applicable and saved */
+} pa_ext_stream_restore_info;
+
+/** Callback prototype for pa_ext_stream_restore_test(). \since 0.9.12 */
+typedef void (*pa_ext_stream_restore_test_cb_t)(
+        pa_context *c,
+        uint32_t version,
+        void *userdata);
+
+/** Test if this extension module is available in the server. \since 0.9.12 */
+pa_operation *pa_ext_stream_restore_test(
+        pa_context *c,
+        pa_ext_stream_restore_test_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_stream_restore_read(). \since 0.9.12 */
+typedef void (*pa_ext_stream_restore_read_cb_t)(
+        pa_context *c,
+        const pa_ext_stream_restore_info *info,
+        int eol,
+        void *userdata);
+
+/** Read all entries from the stream database. \since 0.9.12 */
+pa_operation *pa_ext_stream_restore_read(
+        pa_context *c,
+        pa_ext_stream_restore_read_cb_t cb,
+        void *userdata);
+
+/** Store entries in the stream database. \since 0.9.12 */
+pa_operation *pa_ext_stream_restore_write(
+        pa_context *c,
+        pa_update_mode_t mode,
+        const pa_ext_stream_restore_info data[],
+        unsigned n,
+        int apply_immediately,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Delete entries from the stream database. \since 0.9.12 */
+pa_operation *pa_ext_stream_restore_delete(
+        pa_context *c,
+        const char *const s[],
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Subscribe to changes in the stream database. \since 0.9.12 */
+pa_operation *pa_ext_stream_restore_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_stream_restore_set_subscribe_cb(). \since 0.9.12 */
+typedef void (*pa_ext_stream_restore_subscribe_cb_t)(
+        pa_context *c,
+        void *userdata);
+
+/** Set the subscription callback that is called when
+ * pa_ext_stream_restore_subscribe() was called. \since 0.9.12 */
+void pa_ext_stream_restore_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_stream_restore_subscribe_cb_t cb,
+        void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/fork-detect.c b/src/pulse/fork-detect.c
new file mode 100644 (file)
index 0000000..9299304
--- /dev/null
@@ -0,0 +1,57 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/macro.h>
+
+#include "fork-detect.h"
+
+int pa_detect_fork(void) {
+    static pa_atomic_t pid = PA_ATOMIC_INIT((int) -1);
+
+    /* Some really stupid applications (Hey, vim, that means you!)
+     * love to fork after initializing
+     * gtk/libcanberra/pulseaudio. This is really bad style. We
+     * however have to deal with this cleanly, so we try to detect the
+     * forks making sure all our calls fail cleanly after the fork. */
+
+    pa_assert_cc(sizeof(pa_atomic_t) >= sizeof(pid_t));
+
+    for (;;) {
+        pid_t stored_pid = (pid_t) pa_atomic_load(&pid);
+
+        /* First let's check whether the current pid matches the stored one */
+        if (stored_pid == getpid())
+            return false;
+
+        /* Does it contain a different PID than ours? Then the process got forked. */
+        if ((int) stored_pid != (int) -1)
+            return true;
+
+        /* Ok, it still contains no PID, then store it */
+        if (pa_atomic_cmpxchg(&pid, (int) -1, (int) getpid()))
+            return false;
+    }
+}
diff --git a/src/pulse/fork-detect.h b/src/pulse/fork-detect.h
new file mode 100644 (file)
index 0000000..d455e5d
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef fooforkdetecthfoo
+#define fooforkdetecthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+int pa_detect_fork(void);
+
+#endif
diff --git a/src/pulse/format.c b/src/pulse/format.c
new file mode 100644 (file)
index 0000000..8474978
--- /dev/null
@@ -0,0 +1,683 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2011 Intel Corporation
+  Copyright 2011 Collabora Multimedia
+  Copyright 2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/json.h>
+#include <pulse/internal.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-format.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
+
+#include "format.h"
+
+#define PA_JSON_MIN_KEY "min"
+#define PA_JSON_MAX_KEY "max"
+
+static int pa_format_info_prop_compatible(const char *one, const char *two);
+
+static const char* const _encoding_str_table[]= {
+    [PA_ENCODING_PCM] = "pcm",
+    [PA_ENCODING_AC3_IEC61937] = "ac3-iec61937",
+    [PA_ENCODING_EAC3_IEC61937] = "eac3-iec61937",
+    [PA_ENCODING_MPEG_IEC61937] = "mpeg-iec61937",
+    [PA_ENCODING_DTS_IEC61937] = "dts-iec61937",
+    [PA_ENCODING_MPEG2_AAC_IEC61937] = "mpeg2-aac-iec61937",
+    [PA_ENCODING_ANY] = "any",
+};
+
+const char *pa_encoding_to_string(pa_encoding_t e) {
+    if (e < 0 || e >= PA_ENCODING_MAX)
+        return NULL;
+
+    return _encoding_str_table[e];
+}
+
+pa_encoding_t pa_encoding_from_string(const char *encoding) {
+    pa_encoding_t e;
+
+    for (e = PA_ENCODING_ANY; e < PA_ENCODING_MAX; e++)
+        if (pa_streq(_encoding_str_table[e], encoding))
+            return e;
+
+    return PA_ENCODING_INVALID;
+}
+
+pa_format_info* pa_format_info_new(void) {
+    pa_format_info *f = pa_xnew(pa_format_info, 1);
+
+    f->encoding = PA_ENCODING_INVALID;
+    f->plist = pa_proplist_new();
+
+    return f;
+}
+
+pa_format_info* pa_format_info_copy(const pa_format_info *src) {
+    pa_format_info *dest;
+
+    pa_assert(src);
+
+    dest = pa_xnew(pa_format_info, 1);
+
+    dest->encoding = src->encoding;
+
+    if (src->plist)
+        dest->plist = pa_proplist_copy(src->plist);
+    else
+        dest->plist = NULL;
+
+    return dest;
+}
+
+void pa_format_info_free(pa_format_info *f) {
+    pa_assert(f);
+
+    pa_proplist_free(f->plist);
+    pa_xfree(f);
+}
+
+int pa_format_info_valid(const pa_format_info *f) {
+    return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL);
+}
+
+int pa_format_info_is_pcm(const pa_format_info *f) {
+    return f->encoding == PA_ENCODING_PCM;
+}
+
+char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) {
+    char *tmp;
+
+    pa_assert(s);
+    pa_assert(l > 0);
+    pa_assert(f);
+
+    pa_init_i18n();
+
+    if (!pa_format_info_valid(f))
+        pa_snprintf(s, l, _("(invalid)"));
+    else {
+        tmp = pa_proplist_to_string_sep(f->plist, "  ");
+        if (tmp[0])
+            pa_snprintf(s, l, "%s, %s", pa_encoding_to_string(f->encoding), tmp);
+        else
+            pa_snprintf(s, l, "%s", pa_encoding_to_string(f->encoding));
+        pa_xfree(tmp);
+    }
+
+    return s;
+}
+
+pa_format_info* pa_format_info_from_string(const char *str) {
+    pa_format_info *f = pa_format_info_new();
+    char *encoding = NULL, *properties = NULL;
+    size_t pos;
+
+    pos = strcspn(str, ",");
+
+    encoding = pa_xstrndup(str, pos);
+    f->encoding = pa_encoding_from_string(pa_strip(encoding));
+    if (f->encoding == PA_ENCODING_INVALID)
+        goto error;
+
+    if (pos != strlen(str)) {
+        pa_proplist *plist;
+
+        properties = pa_xstrdup(&str[pos+1]);
+        plist = pa_proplist_from_string(properties);
+
+        if (!plist)
+            goto error;
+
+        pa_proplist_free(f->plist);
+        f->plist = plist;
+    }
+
+out:
+    if (encoding)
+        pa_xfree(encoding);
+    if (properties)
+        pa_xfree(properties);
+    return f;
+
+error:
+    pa_format_info_free(f);
+    f = NULL;
+    goto out;
+}
+
+int pa_format_info_is_compatible(const pa_format_info *first, const pa_format_info *second) {
+    const char *key;
+    void *state = NULL;
+
+    pa_assert(first);
+    pa_assert(second);
+
+    if (first->encoding != second->encoding)
+        return false;
+
+    while ((key = pa_proplist_iterate(first->plist, &state))) {
+        const char *value_one, *value_two;
+
+        value_one = pa_proplist_gets(first->plist, key);
+        value_two = pa_proplist_gets(second->plist, key);
+
+        if (!value_two || !pa_format_info_prop_compatible(value_one, value_two))
+            return false;
+    }
+
+    return true;
+}
+
+pa_format_info* pa_format_info_from_sample_spec(const pa_sample_spec *ss, const pa_channel_map *map) {
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    pa_format_info *f;
+
+    pa_assert(ss && pa_sample_spec_valid(ss));
+    pa_assert(!map || pa_channel_map_valid(map));
+
+    f = pa_format_info_new();
+    f->encoding = PA_ENCODING_PCM;
+
+    pa_format_info_set_sample_format(f, ss->format);
+    pa_format_info_set_rate(f, ss->rate);
+    pa_format_info_set_channels(f, ss->channels);
+
+    if (map) {
+        pa_channel_map_snprint(cm, sizeof(cm), map);
+        pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm);
+    }
+
+    return f;
+}
+
+/* For PCM streams */
+int pa_format_info_to_sample_spec(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
+    pa_assert(f);
+    pa_assert(ss);
+
+    if (!pa_format_info_is_pcm(f))
+        return pa_format_info_to_sample_spec_fake(f, ss, map);
+
+    if (pa_format_info_get_sample_format(f, &ss->format) < 0)
+        return -PA_ERR_INVALID;
+    if (pa_format_info_get_rate(f, &ss->rate) < 0)
+        return -PA_ERR_INVALID;
+    if (pa_format_info_get_channels(f, &ss->channels) < 0)
+        return -PA_ERR_INVALID;
+    if (map && pa_format_info_get_channel_map(f, map) < 0)
+        return -PA_ERR_INVALID;
+
+    return 0;
+}
+
+pa_prop_type_t pa_format_info_get_prop_type(const pa_format_info *f, const char *key) {
+    const char *str;
+    pa_json_object *o;
+    const pa_json_object *o1;
+    pa_prop_type_t type;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    str = pa_proplist_gets(f->plist, key);
+    if (!str)
+        return PA_PROP_TYPE_INVALID;
+
+    o = pa_json_parse(str);
+    if (!o)
+        return PA_PROP_TYPE_INVALID;
+
+    switch (pa_json_object_get_type(o)) {
+        case PA_JSON_TYPE_INT:
+            type = PA_PROP_TYPE_INT;
+            break;
+
+        case PA_JSON_TYPE_STRING:
+            type = PA_PROP_TYPE_STRING;
+            break;
+
+        case PA_JSON_TYPE_ARRAY:
+            if (pa_json_object_get_array_length(o) == 0) {
+                /* Unlikely, but let's account for this anyway. We need at
+                 * least one element to figure out the array type. */
+                type = PA_PROP_TYPE_INVALID;
+                break;
+            }
+
+            o1 = pa_json_object_get_array_member(o, 0);
+
+            if (pa_json_object_get_type(o1) == PA_JSON_TYPE_INT)
+                type = PA_PROP_TYPE_INT_ARRAY;
+            else if (pa_json_object_get_type(o1) == PA_JSON_TYPE_STRING)
+                type = PA_PROP_TYPE_STRING_ARRAY;
+            else
+                type = PA_PROP_TYPE_INVALID;
+
+            break;
+
+        case PA_JSON_TYPE_OBJECT:
+            /* We actually know at this point that it's a int range, but let's
+             * confirm. */
+            if (!pa_json_object_get_object_member(o, PA_JSON_MIN_KEY)) {
+                type = PA_PROP_TYPE_INVALID;
+                break;
+            }
+
+            if (!pa_json_object_get_object_member(o, PA_JSON_MAX_KEY)) {
+                type = PA_PROP_TYPE_INVALID;
+                break;
+            }
+
+            type = PA_PROP_TYPE_INT_RANGE;
+            break;
+
+        default:
+            type = PA_PROP_TYPE_INVALID;
+            break;
+    }
+
+    pa_json_object_free(o);
+    return type;
+}
+
+int pa_format_info_get_prop_int(const pa_format_info *f, const char *key, int *v) {
+    const char *str;
+    pa_json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+    pa_assert(v);
+
+    str = pa_proplist_gets(f->plist, key);
+    if (!str)
+        return -PA_ERR_NOENTITY;
+
+    o = pa_json_parse(str);
+    if (!o) {
+        pa_log_debug("Failed to parse format info property '%s'.", key);
+        return -PA_ERR_INVALID;
+    }
+
+    if (pa_json_object_get_type(o) != PA_JSON_TYPE_INT) {
+        pa_log_debug("Format info property '%s' type is not int.", key);
+        pa_json_object_free(o);
+        return -PA_ERR_INVALID;
+    }
+
+    *v = pa_json_object_get_int(o);
+    pa_json_object_free(o);
+
+    return 0;
+}
+
+int pa_format_info_get_prop_int_range(const pa_format_info *f, const char *key, int *min, int *max) {
+    const char *str;
+    pa_json_object *o;
+    const pa_json_object *o1;
+    int ret = -PA_ERR_INVALID;
+
+    pa_assert(f);
+    pa_assert(key);
+    pa_assert(min);
+    pa_assert(max);
+
+    str = pa_proplist_gets(f->plist, key);
+    if (!str)
+        return -PA_ERR_NOENTITY;
+
+    o = pa_json_parse(str);
+    if (!o) {
+        pa_log_debug("Failed to parse format info property '%s'.", key);
+        return -PA_ERR_INVALID;
+    }
+
+    if (pa_json_object_get_type(o) != PA_JSON_TYPE_OBJECT)
+        goto out;
+
+    if (!(o1 = pa_json_object_get_object_member(o, PA_JSON_MIN_KEY)) ||
+            (pa_json_object_get_type(o1) != PA_JSON_TYPE_INT))
+        goto out;
+
+    *min = pa_json_object_get_int(o1);
+
+    if (!(o1 = pa_json_object_get_object_member(o, PA_JSON_MAX_KEY)) ||
+            (pa_json_object_get_type(o1) != PA_JSON_TYPE_INT))
+        goto out;
+
+    *max = pa_json_object_get_int(o1);
+
+    ret = 0;
+
+out:
+    if (ret < 0)
+        pa_log_debug("Format info property '%s' is not a valid int range.", key);
+
+    pa_json_object_free(o);
+    return ret;
+}
+
+int pa_format_info_get_prop_int_array(const pa_format_info *f, const char *key, int **values, int *n_values) {
+    const char *str;
+    pa_json_object *o;
+    const pa_json_object *o1;
+    int i, ret = -PA_ERR_INVALID;
+
+    pa_assert(f);
+    pa_assert(key);
+    pa_assert(values);
+    pa_assert(n_values);
+
+    str = pa_proplist_gets(f->plist, key);
+    if (!str)
+        return -PA_ERR_NOENTITY;
+
+    o = pa_json_parse(str);
+    if (!o) {
+        pa_log_debug("Failed to parse format info property '%s'.", key);
+        return -PA_ERR_INVALID;
+    }
+
+    if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY)
+        goto out;
+
+    *n_values = pa_json_object_get_array_length(o);
+    *values = pa_xnew(int, *n_values);
+
+    for (i = 0; i < *n_values; i++) {
+        o1 = pa_json_object_get_array_member(o, i);
+
+        if (pa_json_object_get_type(o1) != PA_JSON_TYPE_INT) {
+            goto out;
+        }
+
+        (*values)[i] = pa_json_object_get_int(o1);
+    }
+
+    ret = 0;
+
+out:
+    if (ret < 0)
+        pa_log_debug("Format info property '%s' is not a valid int array.", key);
+
+    pa_json_object_free(o);
+    return ret;
+}
+
+int pa_format_info_get_prop_string(const pa_format_info *f, const char *key, char **v) {
+    const char *str = NULL;
+    pa_json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+    pa_assert(v);
+
+    str = pa_proplist_gets(f->plist, key);
+    if (!str)
+        return -PA_ERR_NOENTITY;
+
+    o = pa_json_parse(str);
+    if (!o) {
+        pa_log_debug("Failed to parse format info property '%s'.", key);
+        return -PA_ERR_INVALID;
+    }
+
+    if (pa_json_object_get_type(o) != PA_JSON_TYPE_STRING) {
+        pa_log_debug("Format info property '%s' type is not string.", key);
+        pa_json_object_free(o);
+        return -PA_ERR_INVALID;
+    }
+
+    *v = pa_xstrdup(pa_json_object_get_string(o));
+    pa_json_object_free(o);
+
+    return 0;
+}
+
+int pa_format_info_get_prop_string_array(const pa_format_info *f, const char *key, char ***values, int *n_values) {
+    const char *str;
+    pa_json_object *o;
+    const pa_json_object *o1;
+    int i, ret = -PA_ERR_INVALID;
+
+    pa_assert(f);
+    pa_assert(key);
+    pa_assert(values);
+    pa_assert(n_values);
+
+    str = pa_proplist_gets(f->plist, key);
+    if (!str)
+        return -PA_ERR_NOENTITY;
+
+    o = pa_json_parse(str);
+    if (!o) {
+        pa_log_debug("Failed to parse format info property '%s'.", key);
+        return -PA_ERR_INVALID;
+    }
+
+    if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY)
+        goto out;
+
+    *n_values = pa_json_object_get_array_length(o);
+    *values = pa_xnew(char *, *n_values);
+
+    for (i = 0; i < *n_values; i++) {
+        o1 = pa_json_object_get_array_member(o, i);
+
+        if (pa_json_object_get_type(o1) != PA_JSON_TYPE_STRING) {
+            goto out;
+        }
+
+        (*values)[i] = pa_xstrdup(pa_json_object_get_string(o1));
+    }
+
+    ret = 0;
+
+out:
+    if (ret < 0)
+        pa_log_debug("Format info property '%s' is not a valid string array.", key);
+
+    pa_json_object_free(o);
+    return ret;
+}
+
+void pa_format_info_free_string_array(char **values, int n_values) {
+    int i;
+
+    for (i = 0; i < n_values; i++)
+        pa_xfree(values[i]);
+
+    pa_xfree(values);
+}
+
+void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf) {
+    pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(sf));
+}
+
+void pa_format_info_set_rate(pa_format_info *f, int rate) {
+    pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, rate);
+}
+
+void pa_format_info_set_channels(pa_format_info *f, int channels) {
+    pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, channels);
+}
+
+void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map) {
+    char map_str[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+    pa_channel_map_snprint(map_str, sizeof(map_str), map);
+
+    pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, map_str);
+}
+
+void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) {
+    pa_assert(f);
+    pa_assert(key);
+
+    pa_proplist_setf(f->plist, key, "%d", value);
+}
+
+void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) {
+    pa_strbuf *buf;
+    char *str;
+    int i;
+
+    pa_assert(f);
+    pa_assert(key);
+    pa_assert(n_values > 0);
+
+    buf = pa_strbuf_new();
+
+    pa_strbuf_printf(buf, "[ %d", values[0]);
+
+    for (i = 1; i < n_values; i++)
+        pa_strbuf_printf(buf, ", %d", values[i]);
+
+    pa_strbuf_printf(buf, " ]");
+    str = pa_strbuf_to_string_free(buf);
+
+    pa_proplist_sets(f->plist, key, str);
+    pa_xfree (str);
+}
+
+void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) {
+    pa_assert(f);
+    pa_assert(key);
+
+    pa_proplist_setf(f->plist, key, "{ \"" PA_JSON_MIN_KEY "\": %d, \"" PA_JSON_MAX_KEY "\": %d }",
+            min, max);
+}
+
+void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) {
+    pa_assert(f);
+    pa_assert(key);
+
+    pa_proplist_setf(f->plist, key, "\"%s\"", value);
+}
+
+void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) {
+    pa_strbuf *buf;
+    char *str;
+    int i;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    buf = pa_strbuf_new();
+
+    pa_strbuf_printf(buf, "[ \"%s\"", values[0]);
+
+    for (i = 1; i < n_values; i++)
+        pa_strbuf_printf(buf, ", \"%s\"", values[i]);
+
+    pa_strbuf_printf(buf, " ]");
+    str = pa_strbuf_to_string_free(buf);
+
+    pa_proplist_sets(f->plist, key, str);
+    pa_xfree (str);
+}
+
+static bool pa_json_is_fixed_type(pa_json_object *o) {
+    switch(pa_json_object_get_type(o)) {
+        case PA_JSON_TYPE_OBJECT:
+        case PA_JSON_TYPE_ARRAY:
+            return false;
+
+        default:
+            return true;
+    }
+}
+
+static int pa_format_info_prop_compatible(const char *one, const char *two) {
+    pa_json_object *o1 = NULL, *o2 = NULL;
+    int i, ret = 0;
+
+    o1 = pa_json_parse(one);
+    if (!o1)
+        goto out;
+
+    o2 = pa_json_parse(two);
+    if (!o2)
+        goto out;
+
+    /* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */
+    pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), false);
+
+    if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) {
+        ret = pa_json_object_equal(o1, o2);
+        goto out;
+    }
+
+    if (pa_json_is_fixed_type(o1)) {
+        pa_json_object *tmp = o2;
+        o2 = o1;
+        o1 = tmp;
+    }
+
+    /* o2 is now a fixed type, and o1 is not */
+
+    if (pa_json_object_get_type(o1) == PA_JSON_TYPE_ARRAY) {
+        for (i = 0; i < pa_json_object_get_array_length(o1); i++) {
+            if (pa_json_object_equal(pa_json_object_get_array_member(o1, i), o2)) {
+                ret = 1;
+                break;
+            }
+        }
+    } else if (pa_json_object_get_type(o1) == PA_JSON_TYPE_OBJECT) {
+        /* o1 should be a range type */
+        int min, max, v;
+        const pa_json_object *o_min = NULL, *o_max = NULL;
+
+        if (pa_json_object_get_type(o2) != PA_JSON_TYPE_INT) {
+            /* We don't support non-integer ranges */
+            goto out;
+        }
+
+        if (!(o_min = pa_json_object_get_object_member(o1, PA_JSON_MIN_KEY)) ||
+            pa_json_object_get_type(o_min) != PA_JSON_TYPE_INT)
+            goto out;
+
+        if (!(o_max = pa_json_object_get_object_member(o1, PA_JSON_MAX_KEY)) ||
+            pa_json_object_get_type(o_max) != PA_JSON_TYPE_INT)
+            goto out;
+
+        v = pa_json_object_get_int(o2);
+        min = pa_json_object_get_int(o_min);
+        max = pa_json_object_get_int(o_max);
+
+        ret = v >= min && v <= max;
+    } else {
+        pa_log_warn("Got a format type that we don't support");
+    }
+
+out:
+    if (o1)
+        pa_json_object_free(o1);
+    if (o2)
+        pa_json_object_free(o2);
+
+    return ret;
+}
diff --git a/src/pulse/format.h b/src/pulse/format.h
new file mode 100644 (file)
index 0000000..f606b3b
--- /dev/null
@@ -0,0 +1,262 @@
+#ifndef fooformathfoo
+#define fooformathfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2011 Intel Corporation
+  Copyright 2011 Collabora Multimedia
+  Copyright 2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+#include <pulse/proplist.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+
+/** \file
+ * Utility functions for handling a stream or sink format. */
+
+PA_C_DECL_BEGIN
+
+/** Represents the type of encoding used in a stream or accepted by a sink. \since 1.0 */
+typedef enum pa_encoding {
+    PA_ENCODING_ANY,
+    /**< Any encoding format, PCM or compressed */
+
+    PA_ENCODING_PCM,
+    /**< Any PCM format */
+
+    PA_ENCODING_AC3_IEC61937,
+    /**< AC3 data encapsulated in IEC 61937 header/padding */
+
+    PA_ENCODING_EAC3_IEC61937,
+    /**< EAC3 data encapsulated in IEC 61937 header/padding */
+
+    PA_ENCODING_MPEG_IEC61937,
+    /**< MPEG-1 or MPEG-2 (Part 3, not AAC) data encapsulated in IEC 61937 header/padding */
+
+    PA_ENCODING_DTS_IEC61937,
+    /**< DTS data encapsulated in IEC 61937 header/padding */
+
+    PA_ENCODING_MPEG2_AAC_IEC61937,
+    /**< MPEG-2 AAC data encapsulated in IEC 61937 header/padding. \since 4.0 */
+
+    PA_ENCODING_MAX,
+    /**< Valid encoding types must be less than this value */
+
+    PA_ENCODING_INVALID = -1,
+    /**< Represents an invalid encoding */
+} pa_encoding_t;
+
+/** \cond fulldocs */
+#define PA_ENCODING_ANY PA_ENCODING_ANY
+#define PA_ENCODING_PCM PA_ENCODING_PCM
+#define PA_ENCODING_AC3_IEC61937 PA_ENCODING_AC3_IEC61937
+#define PA_ENCODING_EAC3_IEC61937 PA_ENCODING_EAC3_IEC61937
+#define PA_ENCODING_MPEG_IEC61937 PA_ENCODING_MPEG_IEC61937
+#define PA_ENCODING_DTS_IEC61937 PA_ENCODING_DTS_IEC61937
+#define PA_ENCODING_MPEG2_AAC_IEC61937 PA_ENCODING_MPEG2_AAC_IEC61937
+#define PA_ENCODING_MAX PA_ENCODING_MAX
+#define PA_ENCODING_INVALID PA_ENCODING_INVALID
+/** \endcond */
+
+/** Returns a printable string representing the given encoding type. \since 1.0 */
+const char *pa_encoding_to_string(pa_encoding_t e) PA_GCC_CONST;
+
+/** Converts a string of the form returned by \a pa_encoding_to_string() back to a \a pa_encoding_t. \since 1.0 */
+pa_encoding_t pa_encoding_from_string(const char *encoding);
+
+/** Represents the format of data provided in a stream or processed by a sink. \since 1.0 */
+typedef struct pa_format_info {
+    pa_encoding_t encoding;
+    /**< The encoding used for the format */
+
+    pa_proplist *plist;
+    /**< Additional encoding-specific properties such as sample rate, bitrate, etc. */
+} pa_format_info;
+
+/** Allocates a new \a pa_format_info structure. Clients must initialise at least the encoding field themselves. \since 1.0 */
+pa_format_info* pa_format_info_new(void);
+
+/** Returns a new \a pa_format_info struct and representing the same format as \a src. \since 1.0 */
+pa_format_info* pa_format_info_copy(const pa_format_info *src);
+
+/** Frees a \a pa_format_info structure. \since 1.0 */
+void pa_format_info_free(pa_format_info *f);
+
+/** Returns non-zero when the format info structure is valid. \since 1.0 */
+int pa_format_info_valid(const pa_format_info *f);
+
+/** Returns non-zero when the format info structure represents a PCM (i.e.\ uncompressed data) format. \since 1.0 */
+int pa_format_info_is_pcm(const pa_format_info *f);
+
+/** Returns non-zero if the format represented by \a first is a subset of
+ * the format represented by \a second. This means that \a second must
+ * have all the fields that \a first does, but the reverse need not
+ * be true. This is typically expected to be used to check if a
+ * stream's format is compatible with a given sink. In such a case,
+ * \a first would be the sink's format and \a second would be the
+ * stream's. \since 1.0 */
+int pa_format_info_is_compatible(const pa_format_info *first, const pa_format_info *second);
+
+/** Maximum required string length for
+ * pa_format_info_snprint(). Please note that this value can change
+ * with any release without warning and without being considered API
+ * or ABI breakage. You should not use this definition anywhere where
+ * it might become part of an ABI. \since 1.0 */
+#define PA_FORMAT_INFO_SNPRINT_MAX 256
+
+/** Return a human-readable string representing the given format. \since 1.0 */
+char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f);
+
+/** Parse a human-readable string of the form generated by
+ * \a pa_format_info_snprint() into a pa_format_info structure. \since 1.0 */
+pa_format_info* pa_format_info_from_string(const char *str);
+
+/** Utility function to take a \a pa_sample_spec and generate the corresponding
+ * \a pa_format_info.
+ *
+ * Note that if you want the server to choose some of the stream parameters,
+ * for example the sample rate, so that they match the device parameters, then
+ * you shouldn't use this function. In order to allow the server to choose
+ * a parameter value, that parameter must be left unspecified in the
+ * pa_format_info object, and this function always specifies all parameters. An
+ * exception is the channel map: if you pass NULL for the channel map, then the
+ * channel map will be left unspecified, allowing the server to choose it.
+ *
+ * \since 2.0 */
+pa_format_info* pa_format_info_from_sample_spec(const pa_sample_spec *ss, const pa_channel_map *map);
+
+/** Utility function to generate a \a pa_sample_spec and \a pa_channel_map corresponding to a given \a pa_format_info. The
+ * conversion for PCM formats is straight-forward. For non-PCM formats, if there is a fixed size-time conversion (i.e. all
+ * IEC61937-encapsulated formats), a "fake" sample spec whose size-time conversion corresponds to this format is provided and
+ * the channel map argument is ignored. For formats with variable size-time conversion, this function will fail. Returns a
+ * negative integer if conversion failed and 0 on success. \since 2.0 */
+int pa_format_info_to_sample_spec(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
+
+/** Represents the type of value type of a property on a \ref pa_format_info. \since 2.0 */
+typedef enum pa_prop_type_t {
+    PA_PROP_TYPE_INT,
+    /**< Integer property */
+
+    PA_PROP_TYPE_INT_RANGE,
+    /**< Integer range property */
+
+    PA_PROP_TYPE_INT_ARRAY,
+    /**< Integer array property */
+
+    PA_PROP_TYPE_STRING,
+    /**< String property */
+
+    PA_PROP_TYPE_STRING_ARRAY,
+    /**< String array property */
+
+    PA_PROP_TYPE_INVALID = -1,
+    /**< Represents an invalid type */
+} pa_prop_type_t;
+
+/** \cond fulldocs */
+#define PA_PROP_TYPE_INT PA_PROP_TYPE_INT
+#define PA_PROP_TYPE_INT_RANGE PA_PROP_TYPE_INT_RANGE
+#define PA_PROP_TYPE_INT_ARRAY PA_PROP_TYPE_INT_ARRAY
+#define PA_PROP_TYPE_STRING PA_PROP_TYPE_STRING
+#define PA_PROP_TYPE_STRING_ARRAY PA_PROP_TYPE_STRING_ARRAY
+#define PA_PROP_TYPE_INVALID PA_PROP_TYPE_INVALID
+/** \endcond */
+
+/** Gets the type of property \a key in a given \ref pa_format_info. \since 2.0 */
+pa_prop_type_t pa_format_info_get_prop_type(const pa_format_info *f, const char *key);
+
+/** Gets an integer property from the given format info. Returns 0 on success and a negative integer on failure. \since 2.0 */
+int pa_format_info_get_prop_int(const pa_format_info *f, const char *key, int *v);
+/** Gets an integer range property from the given format info. Returns 0 on success and a negative integer on failure.
+ * \since 2.0 */
+int pa_format_info_get_prop_int_range(const pa_format_info *f, const char *key, int *min, int *max);
+/** Gets an integer array property from the given format info. \a values contains the values and \a n_values contains the
+ * number of elements. The caller must free \a values using \ref pa_xfree. Returns 0 on success and a negative integer on
+ * failure. \since 2.0 */
+int pa_format_info_get_prop_int_array(const pa_format_info *f, const char *key, int **values, int *n_values);
+/** Gets a string property from the given format info.  The caller must free the returned string using \ref pa_xfree. Returns
+ * 0 on success and a negative integer on failure. \since 2.0 */
+int pa_format_info_get_prop_string(const pa_format_info *f, const char *key, char **v);
+/** Gets a string array property from the given format info. \a values contains the values and \a n_values contains
+ * the number of elements. The caller must free \a values using \ref pa_format_info_free_string_array. Returns 0 on success and
+ * a negative integer on failure. \since 2.0 */
+int pa_format_info_get_prop_string_array(const pa_format_info *f, const char *key, char ***values, int *n_values);
+
+/** Frees a string array returned by \ref pa_format_info_get_prop_string_array. \since 2.0 */
+void pa_format_info_free_string_array(char **values, int n_values);
+
+/** Sets an integer property on the given format info. \since 1.0 */
+void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value);
+/** Sets a property with a list of integer values on the given format info. \since 1.0 */
+void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values);
+/** Sets a property which can have any value in a given integer range on the given format info. \since 1.0 */
+void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max);
+/** Sets a string property on the given format info. \since 1.0 */
+void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value);
+/** Sets a property with a list of string values on the given format info. \since 1.0 */
+void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values);
+
+/** Convenience method to set the sample format as a property on the given
+ * format.
+ *
+ * Note for PCM: If the sample format is left unspecified in the pa_format_info
+ * object, then the server will select the stream sample format. In that case
+ * the stream sample format will most likely match the device sample format,
+ * meaning that sample format conversion will be avoided.
+ *
+ * \since 1.0 */
+void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf);
+
+/** Convenience method to set the sampling rate as a property on the given
+ * format.
+ *
+ * Note for PCM: If the sample rate is left unspecified in the pa_format_info
+ * object, then the server will select the stream sample rate. In that case the
+ * stream sample rate will most likely match the device sample rate, meaning
+ * that sample rate conversion will be avoided.
+ *
+ * \since 1.0 */
+void pa_format_info_set_rate(pa_format_info *f, int rate);
+
+/** Convenience method to set the number of channels as a property on the given
+ * format.
+ *
+ * Note for PCM: If the channel count is left unspecified in the pa_format_info
+ * object, then the server will select the stream channel count. In that case
+ * the stream channel count will most likely match the device channel count,
+ * meaning that up/downmixing will be avoided.
+ *
+ * \since 1.0 */
+void pa_format_info_set_channels(pa_format_info *f, int channels);
+
+/** Convenience method to set the channel map as a property on the given
+ * format.
+ *
+ * Note for PCM: If the channel map is left unspecified in the pa_format_info
+ * object, then the server will select the stream channel map. In that case the
+ * stream channel map will most likely match the device channel map, meaning
+ * that remixing will be avoided.
+ *
+ * \since 1.0 */
+void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/gccmacro.h b/src/pulse/gccmacro.h
new file mode 100644 (file)
index 0000000..e5ba5bd
--- /dev/null
@@ -0,0 +1,132 @@
+#ifndef foopulsegccmacrohfoo
+#define foopulsegccmacrohfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/** \file
+ * GCC attribute macros */
+
+#if defined(__GNUC__)
+#ifdef __MINGW32__
+/* libintl overrides printf with a #define. As this breaks this attribute,
+ * it has a workaround. However the workaround isn't enabled for MINGW
+ * builds (only cygwin) */
+#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (__printf__, a, b)))
+#else
+#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
+#endif
+#else
+/** If we're in GNU C, use some magic for detecting invalid format strings */
+#define PA_GCC_PRINTF_ATTR(a,b)
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ >= 4)
+#define PA_GCC_SENTINEL __attribute__ ((sentinel))
+#else
+/** Macro for usage of GCC's sentinel compilation warnings */
+#define PA_GCC_SENTINEL
+#endif
+
+#ifdef __GNUC__
+#define PA_GCC_NORETURN __attribute__((noreturn))
+#else
+/** Macro for no-return functions */
+#define PA_GCC_NORETURN
+#endif
+
+#ifdef __GNUC__
+#define PA_GCC_UNUSED __attribute__ ((unused))
+#else
+/** Macro for not used function, variable or parameter */
+#define PA_GCC_UNUSED
+#endif
+
+#ifdef __GNUC__
+#define PA_GCC_DESTRUCTOR __attribute__ ((destructor))
+#else
+/** Call this function when process terminates */
+#define PA_GCC_DESTRUCTOR
+#endif
+
+#ifndef PA_GCC_PURE
+#ifdef __GNUC__
+#define PA_GCC_PURE __attribute__ ((pure))
+#else
+/** This function's return value depends only the arguments list and global state **/
+#define PA_GCC_PURE
+#endif
+#endif
+
+#ifndef PA_GCC_CONST
+#ifdef __GNUC__
+#define PA_GCC_CONST __attribute__ ((const))
+#else
+/** This function's return value depends only the arguments list (stricter version of PA_GCC_PURE) **/
+#define PA_GCC_CONST
+#endif
+#endif
+
+#ifndef PA_GCC_DEPRECATED
+#ifdef __GNUC__
+#define PA_GCC_DEPRECATED __attribute__ ((deprecated))
+#else
+/** This function is deprecated **/
+#define PA_GCC_DEPRECATED
+#endif
+#endif
+
+#ifndef PA_GCC_PACKED
+#ifdef __GNUC__
+#define PA_GCC_PACKED __attribute__ ((packed))
+#else
+/** Structure shall be packed in memory **/
+#define PA_GCC_PACKED
+#endif
+#endif
+
+#ifndef PA_GCC_ALLOC_SIZE
+#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
+#define PA_GCC_ALLOC_SIZE(x) __attribute__ ((__alloc_size__(x)))
+#define PA_GCC_ALLOC_SIZE2(x,y) __attribute__ ((__alloc_size__(x,y)))
+#else
+/** Macro for usage of GCC's alloc_size attribute */
+#define PA_GCC_ALLOC_SIZE(x)
+/** Macro for usage of GCC's alloc_size attribute */
+#define PA_GCC_ALLOC_SIZE2(x,y)
+#endif
+#endif
+
+#ifndef PA_GCC_MALLOC
+#ifdef __GNUC__
+#define PA_GCC_MALLOC __attribute__ ((malloc))
+#else
+/** Macro for usage of GCC's malloc attribute */
+#define PA_GCC_MALLOC
+#endif
+#endif
+
+#ifndef PA_GCC_WEAKREF
+#if defined(__GNUC__) && defined(__ELF__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ > 1)) || (__GNUC__ > 4))
+/** Macro for usage of GCC's weakref attribute */
+#define PA_GCC_WEAKREF(x) __attribute__((weakref(#x)))
+#endif
+#endif
+
+#endif
diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c
new file mode 100644 (file)
index 0000000..1ce3cd3
--- /dev/null
@@ -0,0 +1,662 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/llist.h>
+
+#include <glib.h>
+#include "glib-mainloop.h"
+
+struct pa_io_event {
+    pa_glib_mainloop *mainloop;
+    int dead;
+
+    GPollFD poll_fd;
+    int poll_fd_added;
+
+    pa_io_event_cb_t callback;
+    void *userdata;
+    pa_io_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_io_event);
+};
+
+struct pa_time_event {
+    pa_glib_mainloop *mainloop;
+    int dead;
+
+    int enabled;
+    struct timeval timeval;
+
+    pa_time_event_cb_t callback;
+    void *userdata;
+    pa_time_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_time_event);
+};
+
+struct pa_defer_event {
+    pa_glib_mainloop *mainloop;
+    int dead;
+
+    int enabled;
+
+    pa_defer_event_cb_t callback;
+    void *userdata;
+    pa_defer_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_defer_event);
+};
+
+struct pa_glib_mainloop {
+    GSource source;
+
+    pa_mainloop_api api;
+    GMainContext *context;
+
+    PA_LLIST_HEAD(pa_io_event, io_events);
+    PA_LLIST_HEAD(pa_time_event, time_events);
+    PA_LLIST_HEAD(pa_defer_event, defer_events);
+
+    int n_enabled_defer_events, n_enabled_time_events;
+    int io_events_please_scan, time_events_please_scan, defer_events_please_scan;
+
+    pa_time_event *cached_next_time_event;
+};
+
+static void cleanup_io_events(pa_glib_mainloop *g, int force) {
+    pa_io_event *e;
+
+    e = g->io_events;
+    while (e) {
+        pa_io_event *n = e->next;
+
+        if (!force && g->io_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_io_event, g->io_events, e);
+
+            if (e->dead) {
+                g_assert(g->io_events_please_scan > 0);
+                g->io_events_please_scan--;
+            }
+
+            if (e->poll_fd_added)
+                g_source_remove_poll(&g->source, &e->poll_fd);
+
+            if (e->destroy_callback)
+                e->destroy_callback(&g->api, e, e->userdata);
+
+            pa_xfree(e);
+        }
+
+        e = n;
+    }
+
+    g_assert(g->io_events_please_scan == 0);
+}
+
+static void cleanup_time_events(pa_glib_mainloop *g, int force) {
+    pa_time_event *e;
+
+    e = g->time_events;
+    while (e) {
+        pa_time_event *n = e->next;
+
+        if (!force && g->time_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_time_event, g->time_events, e);
+
+            if (e->dead) {
+                g_assert(g->time_events_please_scan > 0);
+                g->time_events_please_scan--;
+            }
+
+            if (!e->dead && e->enabled) {
+                g_assert(g->n_enabled_time_events > 0);
+                g->n_enabled_time_events--;
+            }
+
+            if (e->destroy_callback)
+                e->destroy_callback(&g->api, e, e->userdata);
+
+            pa_xfree(e);
+        }
+
+        e = n;
+    }
+
+    g_assert(g->time_events_please_scan == 0);
+}
+
+static void cleanup_defer_events(pa_glib_mainloop *g, int force) {
+    pa_defer_event *e;
+
+    e = g->defer_events;
+    while (e) {
+        pa_defer_event *n = e->next;
+
+        if (!force && g->defer_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_defer_event, g->defer_events, e);
+
+            if (e->dead) {
+                g_assert(g->defer_events_please_scan > 0);
+                g->defer_events_please_scan--;
+            }
+
+            if (!e->dead && e->enabled) {
+                g_assert(g->n_enabled_defer_events > 0);
+                g->n_enabled_defer_events--;
+            }
+
+            if (e->destroy_callback)
+                e->destroy_callback(&g->api, e, e->userdata);
+
+            pa_xfree(e);
+        }
+
+        e = n;
+    }
+
+    g_assert(g->defer_events_please_scan == 0);
+}
+
+static gushort map_flags_to_glib(pa_io_event_flags_t flags) {
+    return (gushort)
+        ((flags & PA_IO_EVENT_INPUT ? G_IO_IN : 0) |
+         (flags & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0) |
+         (flags & PA_IO_EVENT_ERROR ? G_IO_ERR : 0) |
+         (flags & PA_IO_EVENT_HANGUP ? G_IO_HUP : 0));
+}
+
+static pa_io_event_flags_t map_flags_from_glib(gushort flags) {
+    return
+        (flags & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
+        (flags & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
+        (flags & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
+        (flags & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
+}
+
+static pa_io_event* glib_io_new(
+        pa_mainloop_api*m,
+        int fd,
+        pa_io_event_flags_t f,
+        pa_io_event_cb_t cb,
+        void *userdata) {
+
+    pa_io_event *e;
+    pa_glib_mainloop *g;
+
+    g_assert(m);
+    g_assert(m->userdata);
+    g_assert(fd >= 0);
+    g_assert(cb);
+
+    g = m->userdata;
+
+    e = pa_xnew(pa_io_event, 1);
+    e->mainloop = g;
+    e->dead = 0;
+
+    e->poll_fd.fd = fd;
+    e->poll_fd.events = map_flags_to_glib(f);
+    e->poll_fd.revents = 0;
+
+    e->callback = cb;
+    e->userdata = userdata;
+    e->destroy_callback = NULL;
+
+    PA_LLIST_PREPEND(pa_io_event, g->io_events, e);
+
+    g_source_add_poll(&g->source, &e->poll_fd);
+    e->poll_fd_added = 1;
+
+    return e;
+}
+
+static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->poll_fd.events = map_flags_to_glib(f);
+}
+
+static void glib_io_free(pa_io_event*e) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->dead = 1;
+    e->mainloop->io_events_please_scan++;
+
+    if (e->poll_fd_added) {
+        g_source_remove_poll(&e->mainloop->source, &e->poll_fd);
+        e->poll_fd_added = 0;
+    }
+}
+
+static void glib_io_set_destroy(pa_io_event*e, pa_io_event_destroy_cb_t cb) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->destroy_callback = cb;
+}
+
+/* Time sources */
+
+static pa_time_event* glib_time_new(
+        pa_mainloop_api*m,
+        const struct timeval *tv,
+        pa_time_event_cb_t cb,
+        void *userdata) {
+
+    pa_glib_mainloop *g;
+    pa_time_event *e;
+
+    g_assert(m);
+    g_assert(m->userdata);
+    g_assert(cb);
+
+    g = m->userdata;
+
+    e = pa_xnew(pa_time_event, 1);
+    e->mainloop = g;
+    e->dead = 0;
+
+    if ((e->enabled = !!tv)) {
+        e->timeval = *tv;
+        g->n_enabled_time_events++;
+
+        if (g->cached_next_time_event) {
+            g_assert(g->cached_next_time_event->enabled);
+
+            if (pa_timeval_cmp(tv, &g->cached_next_time_event->timeval) < 0)
+                g->cached_next_time_event = e;
+        }
+    }
+
+    e->callback = cb;
+    e->userdata = userdata;
+    e->destroy_callback = NULL;
+
+    PA_LLIST_PREPEND(pa_time_event, g->time_events, e);
+
+    return e;
+}
+
+static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    if (e->enabled && !tv) {
+        g_assert(e->mainloop->n_enabled_time_events > 0);
+        e->mainloop->n_enabled_time_events--;
+    } else if (!e->enabled && tv)
+        e->mainloop->n_enabled_time_events++;
+
+    if ((e->enabled = !!tv))
+        e->timeval = *tv;
+
+    if (e->mainloop->cached_next_time_event == e)
+        e->mainloop->cached_next_time_event = NULL;
+
+    if (e->mainloop->cached_next_time_event && e->enabled) {
+        g_assert(e->mainloop->cached_next_time_event->enabled);
+
+        if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0)
+            e->mainloop->cached_next_time_event = e;
+    }
+}
+
+static void glib_time_free(pa_time_event *e) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->dead = 1;
+    e->mainloop->time_events_please_scan++;
+
+    if (e->enabled)
+        e->mainloop->n_enabled_time_events--;
+
+    if (e->mainloop->cached_next_time_event == e)
+        e->mainloop->cached_next_time_event = NULL;
+}
+
+static void glib_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->destroy_callback = cb;
+}
+
+/* Deferred sources */
+
+static pa_defer_event* glib_defer_new(
+        pa_mainloop_api*m,
+        pa_defer_event_cb_t cb,
+        void *userdata) {
+
+    pa_defer_event *e;
+    pa_glib_mainloop *g;
+
+    g_assert(m);
+    g_assert(m->userdata);
+    g_assert(cb);
+
+    g = m->userdata;
+
+    e = pa_xnew(pa_defer_event, 1);
+    e->mainloop = g;
+    e->dead = 0;
+
+    e->enabled = 1;
+    g->n_enabled_defer_events++;
+
+    e->callback = cb;
+    e->userdata = userdata;
+    e->destroy_callback = NULL;
+
+    PA_LLIST_PREPEND(pa_defer_event, g->defer_events, e);
+    return e;
+}
+
+static void glib_defer_enable(pa_defer_event *e, int b) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    if (e->enabled && !b) {
+        g_assert(e->mainloop->n_enabled_defer_events > 0);
+        e->mainloop->n_enabled_defer_events--;
+    } else if (!e->enabled && b)
+        e->mainloop->n_enabled_defer_events++;
+
+    e->enabled = b;
+}
+
+static void glib_defer_free(pa_defer_event *e) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->dead = 1;
+    e->mainloop->defer_events_please_scan++;
+
+    if (e->enabled) {
+        g_assert(e->mainloop->n_enabled_defer_events > 0);
+        e->mainloop->n_enabled_defer_events--;
+    }
+}
+
+static void glib_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->destroy_callback = cb;
+}
+
+/* quit() */
+
+static void glib_quit(pa_mainloop_api*a, int retval) {
+
+    g_warning("quit() ignored");
+
+    /* NOOP */
+}
+
+static pa_time_event* find_next_time_event(pa_glib_mainloop *g) {
+    pa_time_event *t, *n = NULL;
+    g_assert(g);
+
+    if (g->cached_next_time_event)
+        return g->cached_next_time_event;
+
+    for (t = g->time_events; t; t = t->next) {
+
+        if (t->dead || !t->enabled)
+            continue;
+
+        if (!n || pa_timeval_cmp(&t->timeval, &n->timeval) < 0) {
+            n = t;
+
+            /* Shortcut for tv = { 0, 0 } */
+            if (n->timeval.tv_sec <= 0)
+                break;
+        }
+    }
+
+    g->cached_next_time_event = n;
+    return n;
+}
+
+static void scan_dead(pa_glib_mainloop *g) {
+    g_assert(g);
+
+    if (g->io_events_please_scan)
+        cleanup_io_events(g, 0);
+
+    if (g->time_events_please_scan)
+        cleanup_time_events(g, 0);
+
+    if (g->defer_events_please_scan)
+        cleanup_defer_events(g, 0);
+}
+
+static gboolean prepare_func(GSource *source, gint *timeout) {
+    pa_glib_mainloop *g = (pa_glib_mainloop*) source;
+
+    g_assert(g);
+    g_assert(timeout);
+
+    scan_dead(g);
+
+    if (g->n_enabled_defer_events) {
+        *timeout = 0;
+        return TRUE;
+    } else if (g->n_enabled_time_events) {
+        pa_time_event *t;
+        GTimeVal now;
+        struct timeval tvnow;
+        pa_usec_t usec;
+
+        t = find_next_time_event(g);
+        g_assert(t);
+
+        g_get_current_time(&now);
+        tvnow.tv_sec = now.tv_sec;
+        tvnow.tv_usec = now.tv_usec;
+
+        if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) {
+            *timeout = 0;
+            return TRUE;
+        }
+        usec = pa_timeval_diff(&t->timeval, &tvnow);
+        *timeout = (gint) (usec / 1000);
+    } else
+        *timeout = -1;
+
+    return FALSE;
+}
+static gboolean check_func(GSource *source) {
+    pa_glib_mainloop *g = (pa_glib_mainloop*) source;
+    pa_io_event *e;
+
+    g_assert(g);
+
+    if (g->n_enabled_defer_events)
+        return TRUE;
+    else if (g->n_enabled_time_events) {
+        pa_time_event *t;
+        GTimeVal now;
+        struct timeval tvnow;
+
+        t = find_next_time_event(g);
+        g_assert(t);
+
+        g_get_current_time(&now);
+        tvnow.tv_sec = now.tv_sec;
+        tvnow.tv_usec = now.tv_usec;
+
+        if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0)
+            return TRUE;
+    }
+
+    for (e = g->io_events; e; e = e->next)
+        if (!e->dead && e->poll_fd.revents != 0)
+            return TRUE;
+
+    return FALSE;
+}
+
+static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer userdata) {
+    pa_glib_mainloop *g = (pa_glib_mainloop*) source;
+    pa_io_event *e;
+
+    g_assert(g);
+
+    if (g->n_enabled_defer_events) {
+        pa_defer_event *d;
+
+        for (d = g->defer_events; d; d = d->next) {
+            if (d->dead || !d->enabled)
+                continue;
+
+            break;
+        }
+
+        g_assert(d);
+
+        d->callback(&g->api, d, d->userdata);
+        return TRUE;
+    }
+
+    if (g->n_enabled_time_events) {
+        GTimeVal now;
+        struct timeval tvnow;
+        pa_time_event *t;
+
+        t = find_next_time_event(g);
+        g_assert(t);
+
+        g_get_current_time(&now);
+        tvnow.tv_sec = now.tv_sec;
+        tvnow.tv_usec = now.tv_usec;
+
+        if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) {
+
+            /* Disable time event */
+            glib_time_restart(t, NULL);
+
+            t->callback(&g->api, t, &t->timeval, t->userdata);
+            return TRUE;
+        }
+    }
+
+    for (e = g->io_events; e; e = e->next)
+        if (!e->dead && e->poll_fd.revents != 0) {
+            e->callback(&g->api, e, e->poll_fd.fd, map_flags_from_glib(e->poll_fd.revents), e->userdata);
+            e->poll_fd.revents = 0;
+            return TRUE;
+        }
+
+    return FALSE;
+}
+
+static const pa_mainloop_api vtable = {
+    .userdata = NULL,
+
+    .io_new = glib_io_new,
+    .io_enable = glib_io_enable,
+    .io_free = glib_io_free,
+    .io_set_destroy = glib_io_set_destroy,
+
+    .time_new = glib_time_new,
+    .time_restart = glib_time_restart,
+    .time_free = glib_time_free,
+    .time_set_destroy = glib_time_set_destroy,
+
+    .defer_new = glib_defer_new,
+    .defer_enable = glib_defer_enable,
+    .defer_free = glib_defer_free,
+    .defer_set_destroy = glib_defer_set_destroy,
+
+    .quit = glib_quit,
+};
+
+pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) {
+    pa_glib_mainloop *g;
+
+    static GSourceFuncs source_funcs = {
+        prepare_func,
+        check_func,
+        dispatch_func,
+        NULL,
+        NULL,
+        NULL
+    };
+
+    g = (pa_glib_mainloop*) g_source_new(&source_funcs, sizeof(pa_glib_mainloop));
+    g_main_context_ref(g->context = c ? c : g_main_context_default());
+
+    g->api = vtable;
+    g->api.userdata = g;
+
+    PA_LLIST_HEAD_INIT(pa_io_event, g->io_events);
+    PA_LLIST_HEAD_INIT(pa_time_event, g->time_events);
+    PA_LLIST_HEAD_INIT(pa_defer_event, g->defer_events);
+
+    g->n_enabled_defer_events = g->n_enabled_time_events = 0;
+    g->io_events_please_scan = g->time_events_please_scan = g->defer_events_please_scan = 0;
+
+    g->cached_next_time_event = NULL;
+
+    g_source_attach(&g->source, g->context);
+    g_source_set_can_recurse(&g->source, FALSE);
+
+    return g;
+}
+
+void pa_glib_mainloop_free(pa_glib_mainloop* g) {
+    g_assert(g);
+
+    cleanup_io_events(g, 1);
+    cleanup_defer_events(g, 1);
+    cleanup_time_events(g, 1);
+
+    g_main_context_unref(g->context);
+    g_source_destroy(&g->source);
+    g_source_unref(&g->source);
+}
+
+pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
+    g_assert(g);
+
+    return &g->api;
+}
diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h
new file mode 100644 (file)
index 0000000..52d9a75
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef fooglibmainloophfoo
+#define fooglibmainloophfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <glib.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+#include <pulse/version.h>
+
+/** \page glib-mainloop GLIB Main Loop Bindings
+ *
+ * \section overv_sec Overview
+ *
+ * The GLIB main loop bindings are extremely easy to use. All that is
+ * required is to create a pa_glib_mainloop object using
+ * pa_glib_mainloop_new(). When the main loop abstraction is needed, it is
+ * provided by pa_glib_mainloop_get_api().
+ *
+ */
+
+/** \file
+ * GLIB main loop support
+ *
+ * See also \subpage glib-mainloop
+ */
+
+PA_C_DECL_BEGIN
+
+/** An opaque GLIB main loop object */
+typedef struct pa_glib_mainloop pa_glib_mainloop;
+
+/** Create a new GLIB main loop object for the specified GLIB main
+ * loop context. Takes an argument c for the
+ * GMainContext to use. If c is NULL the default context is used. */
+pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c);
+
+/** Free the GLIB main loop object */
+void pa_glib_mainloop_free(pa_glib_mainloop* g);
+
+/** Return the abstract main loop API vtable for the GLIB main loop
+    object. No need to free the API as it is owned by the loop
+    and is destroyed when the loop is freed. */
+pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
new file mode 100644 (file)
index 0000000..01d2b6e
--- /dev/null
@@ -0,0 +1,319 @@
+#ifndef foointernalhfoo
+#define foointernalhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/context.h>
+#include <pulse/stream.h>
+#include <pulse/operation.h>
+#include <pulse/subscribe.h>
+#include <pulse/ext-device-manager.h>
+#include <pulse/ext-device-restore.h>
+#include <pulse/ext-stream-restore.h>
+
+#include <pulsecore/socket-client.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/mcalign.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/time-smoother.h>
+#ifdef HAVE_DBUS
+#include <pulsecore/dbus-util.h>
+#endif
+
+#include "client-conf.h"
+
+#define DEFAULT_TIMEOUT (30)
+
+#define PA_PROTOCOL_FLAG_MASK 0xFFFF0000U
+#define PA_PROTOCOL_VERSION_MASK 0x0000FFFFU
+
+#define PA_PROTOCOL_FLAG_SHM 0x80000000U
+#define PA_PROTOCOL_FLAG_MEMFD 0x40000000U
+
+struct pa_context {
+    PA_REFCNT_DECLARE;
+
+#ifdef HAVE_DBUS
+    pa_dbus_wrap_connection *system_bus;
+    pa_dbus_wrap_connection *session_bus;
+#endif
+
+    pa_proplist *proplist;
+    pa_mainloop_api* mainloop;
+
+    pa_socket_client *client;
+    pa_pstream *pstream;
+    pa_pdispatch *pdispatch;
+
+    pa_srbchannel_template srb_template;
+    uint32_t srb_setup_tag;
+
+    pa_hashmap *record_streams, *playback_streams;
+    PA_LLIST_HEAD(pa_stream, streams);
+    PA_LLIST_HEAD(pa_operation, operations);
+
+    uint32_t version;
+    uint32_t ctag;
+    uint32_t csyncid;
+    int error;
+    pa_context_state_t state;
+
+    pa_context_notify_cb_t state_callback;
+    void *state_userdata;
+    pa_context_subscribe_cb_t subscribe_callback;
+    void *subscribe_userdata;
+    pa_context_event_cb_t event_callback;
+    void *event_userdata;
+
+    pa_mempool *mempool;
+
+    bool is_local:1;
+    bool do_shm:1;
+    bool memfd_on_local:1;
+    bool server_specified:1;
+    bool no_fail:1;
+    bool do_autospawn:1;
+    bool use_rtclock:1;
+    bool filter_added:1;
+    pa_spawn_api spawn_api;
+
+    pa_mem_type_t shm_type;
+
+    pa_strlist *server_list;
+
+    char *server;
+
+    pa_client_conf *conf;
+
+    uint32_t client_index;
+
+    /* Extension specific data */
+    struct {
+        pa_ext_device_manager_subscribe_cb_t callback;
+        void *userdata;
+    } ext_device_manager;
+    struct {
+        pa_ext_device_restore_subscribe_cb_t callback;
+        void *userdata;
+    } ext_device_restore;
+    struct {
+        pa_ext_stream_restore_subscribe_cb_t callback;
+        void *userdata;
+    } ext_stream_restore;
+};
+
+#define PA_MAX_WRITE_INDEX_CORRECTIONS 32
+
+typedef struct pa_index_correction {
+    uint32_t tag;
+    int64_t value;
+    bool valid:1;
+    bool absolute:1;
+    bool corrupt:1;
+} pa_index_correction;
+
+#define PA_MAX_FORMATS (PA_ENCODING_MAX)
+
+struct pa_stream {
+    PA_REFCNT_DECLARE;
+    PA_LLIST_FIELDS(pa_stream);
+
+    pa_context *context;
+    pa_mainloop_api *mainloop;
+
+    uint32_t direct_on_input;
+
+    pa_stream_direction_t direction;
+    pa_stream_state_t state;
+    pa_stream_flags_t flags;
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    uint8_t n_formats;
+    pa_format_info *req_formats[PA_MAX_FORMATS];
+    pa_format_info *format;
+
+    pa_proplist *proplist;
+
+    bool channel_valid:1;
+    bool suspended:1;
+    bool corked:1;
+    bool timing_info_valid:1;
+    bool auto_timing_update_requested:1;
+
+    uint32_t channel;
+    uint32_t syncid;
+    uint32_t stream_index;
+
+    int64_t requested_bytes;
+    pa_buffer_attr buffer_attr;
+
+    uint32_t device_index;
+    char *device_name;
+
+    /* playback */
+    pa_memblock *write_memblock;
+    void *write_data;
+    int64_t latest_underrun_at_index;
+
+    /* recording */
+    pa_memchunk peek_memchunk;
+    void *peek_data;
+    pa_memblockq *record_memblockq;
+
+    /* Store latest latency info */
+    pa_timing_info timing_info;
+
+    /* Use to make sure that time advances monotonically */
+    pa_usec_t previous_time;
+
+    /* time updates with tags older than these are invalid */
+    uint32_t write_index_not_before;
+    uint32_t read_index_not_before;
+
+    /* Data about individual timing update corrections */
+    pa_index_correction write_index_corrections[PA_MAX_WRITE_INDEX_CORRECTIONS];
+    int current_write_index_correction;
+
+    /* Latency interpolation stuff */
+    pa_time_event *auto_timing_update_event;
+    pa_usec_t auto_timing_interval_usec;
+
+    pa_smoother *smoother;
+
+    /* Callbacks */
+    pa_stream_notify_cb_t state_callback;
+    void *state_userdata;
+    pa_stream_request_cb_t read_callback;
+    void *read_userdata;
+    pa_stream_request_cb_t write_callback;
+    void *write_userdata;
+    pa_stream_notify_cb_t overflow_callback;
+    void *overflow_userdata;
+    pa_stream_notify_cb_t underflow_callback;
+    void *underflow_userdata;
+    pa_stream_notify_cb_t latency_update_callback;
+    void *latency_update_userdata;
+    pa_stream_notify_cb_t moved_callback;
+    void *moved_userdata;
+    pa_stream_notify_cb_t suspended_callback;
+    void *suspended_userdata;
+    pa_stream_notify_cb_t started_callback;
+    void *started_userdata;
+    pa_stream_event_cb_t event_callback;
+    void *event_userdata;
+    pa_stream_notify_cb_t buffer_attr_callback;
+    void *buffer_attr_userdata;
+};
+
+typedef void (*pa_operation_cb_t)(void);
+
+struct pa_operation {
+    PA_REFCNT_DECLARE;
+
+    pa_context *context;
+    pa_stream *stream;
+
+    PA_LLIST_FIELDS(pa_operation);
+
+    pa_operation_state_t state;
+    void *userdata;
+    pa_operation_cb_t callback;
+    void *state_userdata;
+    pa_operation_notify_cb_t state_callback;
+
+    void *private; /* some operations might need this */
+};
+
+void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
+void pa_operation_done(pa_operation *o);
+
+void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+void pa_context_fail(pa_context *c, int error);
+int pa_context_set_error(pa_context *c, int error);
+void pa_context_set_state(pa_context *c, pa_context_state_t st);
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, bool fail);
+pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
+
+void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
+
+pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag);
+
+#define PA_CHECK_VALIDITY(context, expression, error)         \
+    do {                                                      \
+        if (!(expression))                                    \
+            return -pa_context_set_error((context), (error)); \
+    } while(false)
+
+#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) \
+    do {                                                                \
+        if (!(expression)) {                                            \
+            pa_context_set_error((context), (error));                   \
+            return value;                                               \
+        }                                                               \
+    } while(false)
+
+#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error)       \
+    PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
+
+#define PA_FAIL(context, error)                                 \
+    do {                                                        \
+        return -pa_context_set_error((context), (error));       \
+    } while(false)
+
+#define PA_FAIL_RETURN_ANY(context, error, value)      \
+    do {                                               \
+        pa_context_set_error((context), (error));      \
+        return value;                                  \
+    } while(false)
+
+#define PA_FAIL_RETURN_NULL(context, error)     \
+    PA_FAIL_RETURN_ANY(context, error, NULL)
+
+void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
+void pa_ext_device_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
+void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
+
+bool pa_mainloop_is_our_api(pa_mainloop_api*m);
+
+#endif
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
new file mode 100644 (file)
index 0000000..510d784
--- /dev/null
@@ -0,0 +1,2186 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/context.h>
+#include <pulse/direction.h>
+#include <pulse/xmalloc.h>
+#include <pulse/fork-detect.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+#include "introspect.h"
+
+/*** Statistics ***/
+
+static void context_stat_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    pa_stat_info i, *p = &i;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    pa_zero(i);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        p = NULL;
+    } else if (pa_tagstruct_getu32(t, &i.memblock_total) < 0 ||
+               pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 ||
+               pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 ||
+               pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 ||
+               pa_tagstruct_getu32(t, &i.scache_size) < 0 ||
+               !pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_stat_info_cb_t cb = (pa_stat_info_cb_t) o->callback;
+        cb(o->context, p, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_STAT, context_stat_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Server Info ***/
+
+static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    pa_server_info i, *p = &i;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    pa_zero(i);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        p = NULL;
+    } else if (pa_tagstruct_gets(t, &i.server_name) < 0 ||
+               pa_tagstruct_gets(t, &i.server_version) < 0 ||
+               pa_tagstruct_gets(t, &i.user_name) < 0 ||
+               pa_tagstruct_gets(t, &i.host_name) < 0 ||
+               pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+               pa_tagstruct_gets(t, &i.default_sink_name) < 0 ||
+               pa_tagstruct_gets(t, &i.default_source_name) < 0 ||
+               pa_tagstruct_getu32(t, &i.cookie) < 0 ||
+               (o->context->version >= 15 &&
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0) ||
+               !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (p && o->context->version < 15)
+        pa_channel_map_init_extend(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+
+    if (o->callback) {
+        pa_server_info_cb_t cb = (pa_server_info_cb_t) o->callback;
+        cb(o->context, p, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SERVER_INFO, context_get_server_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Sink Info ***/
+
+static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+    pa_sink_info i;
+    uint32_t j;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    /* For safety in case someone use fail: outside the while loop below */
+    pa_zero(i);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            bool mute;
+            uint32_t flags;
+            uint32_t state;
+            const char *ap = NULL;
+
+            pa_zero(i);
+            i.proplist = pa_proplist_new();
+            i.base_volume = PA_VOLUME_NORM;
+            i.n_volume_steps = PA_VOLUME_NORM+1;
+            mute = false;
+            state = PA_SINK_INVALID_STATE;
+            i.card = PA_INVALID_INDEX;
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_gets(t, &i.description) < 0 ||
+                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                pa_tagstruct_get_boolean(t, &mute) < 0 ||
+                pa_tagstruct_getu32(t, &i.monitor_source) < 0 ||
+                pa_tagstruct_gets(t, &i.monitor_source_name) < 0 ||
+                pa_tagstruct_get_usec(t, &i.latency) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                pa_tagstruct_getu32(t, &flags) < 0 ||
+                (o->context->version >= 13 &&
+                 (pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
+                  pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) ||
+                (o->context->version >= 15 &&
+                 (pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
+                  pa_tagstruct_getu32(t, &state) < 0 ||
+                  pa_tagstruct_getu32(t, &i.n_volume_steps) < 0 ||
+                  pa_tagstruct_getu32(t, &i.card) < 0)) ||
+                (o->context->version >= 16 &&
+                 (pa_tagstruct_getu32(t, &i.n_ports)))) {
+
+                goto fail;
+            }
+
+            if (o->context->version >= 16) {
+                if (i.n_ports > 0) {
+                    i.ports = pa_xnew(pa_sink_port_info*, i.n_ports+1);
+                    i.ports[0] = pa_xnew(pa_sink_port_info, i.n_ports);
+
+                    for (j = 0; j < i.n_ports; j++) {
+                        i.ports[j] = &i.ports[0][j];
+
+                        if (pa_tagstruct_gets(t, &i.ports[j]->name) < 0 ||
+                            pa_tagstruct_gets(t, &i.ports[j]->description) < 0 ||
+                            pa_tagstruct_getu32(t, &i.ports[j]->priority) < 0) {
+
+                            goto fail;
+                        }
+
+                        i.ports[j]->available = PA_PORT_AVAILABLE_UNKNOWN;
+                        if (o->context->version >= 24) {
+                            uint32_t av;
+                            if (pa_tagstruct_getu32(t, &av) < 0 || av > PA_PORT_AVAILABLE_YES)
+                                goto fail;
+                            i.ports[j]->available = av;
+                        }
+                    }
+
+                    i.ports[j] = NULL;
+                }
+
+                if (pa_tagstruct_gets(t, &ap) < 0)
+                    goto fail;
+
+                if (ap) {
+                    for (j = 0; j < i.n_ports; j++)
+                        if (pa_streq(i.ports[j]->name, ap)) {
+                            i.active_port = i.ports[j];
+                            break;
+                        }
+                }
+            }
+
+            if (o->context->version >= 21) {
+                uint8_t n_formats;
+                if (pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1)
+                    goto fail;
+
+                i.formats = pa_xnew0(pa_format_info*, n_formats);
+
+                for (j = 0; j < n_formats; j++) {
+                    i.n_formats++;
+                    i.formats[j] = pa_format_info_new();
+
+                    if (pa_tagstruct_get_format_info(t, i.formats[j]) < 0)
+                        goto fail;
+                }
+            }
+
+            i.mute = (int) mute;
+            i.flags = (pa_sink_flags_t) flags;
+            i.state = (pa_sink_state_t) state;
+
+            if (o->callback) {
+                pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            if (i.formats) {
+                for (j = 0; j < i.n_formats; j++)
+                    pa_format_info_free(i.formats[j]);
+                pa_xfree(i.formats);
+            }
+            if (i.ports) {
+                pa_xfree(i.ports[0]);
+                pa_xfree(i.ports);
+            }
+            pa_proplist_free(i.proplist);
+        }
+    }
+
+    if (o->callback) {
+        pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+    return;
+
+fail:
+    pa_assert(i.proplist);
+
+    pa_context_fail(o->context, PA_ERR_PROTOCOL);
+
+    if (i.formats) {
+        for (j = 0; j < i.n_formats; j++)
+            pa_format_info_free(i.formats[j]);
+        pa_xfree(i.formats);
+    }
+    if (i.ports) {
+        pa_xfree(i.ports[0]);
+        pa_xfree(i.ports);
+    }
+    pa_proplist_free(i.proplist);
+
+    goto finish;
+}
+
+pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INFO_LIST, context_get_sink_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INFO, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_PORT, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_puts(t, port);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char *name, const char*port, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_PORT, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_puts(t, port);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+/*** Source info ***/
+
+static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+    pa_source_info i;
+    uint32_t j;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    /* For safety in case someone use fail: outside the while loop below */
+    pa_zero(i);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            bool mute;
+            uint32_t flags;
+            uint32_t state;
+            const char *ap;
+
+            pa_zero(i);
+            i.proplist = pa_proplist_new();
+            i.base_volume = PA_VOLUME_NORM;
+            i.n_volume_steps = PA_VOLUME_NORM+1;
+            mute = false;
+            state = PA_SOURCE_INVALID_STATE;
+            i.card = PA_INVALID_INDEX;
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_gets(t, &i.description) < 0 ||
+                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                pa_tagstruct_get_boolean(t, &mute) < 0 ||
+                pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 ||
+                pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 ||
+                pa_tagstruct_get_usec(t, &i.latency) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                pa_tagstruct_getu32(t, &flags) < 0 ||
+                (o->context->version >= 13 &&
+                 (pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
+                  pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) ||
+                (o->context->version >= 15 &&
+                 (pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
+                  pa_tagstruct_getu32(t, &state) < 0 ||
+                  pa_tagstruct_getu32(t, &i.n_volume_steps) < 0 ||
+                  pa_tagstruct_getu32(t, &i.card) < 0)) ||
+                (o->context->version >= 16 &&
+                 (pa_tagstruct_getu32(t, &i.n_ports)))) {
+
+                goto fail;
+            }
+
+            if (o->context->version >= 16) {
+                if (i.n_ports > 0) {
+                    i.ports = pa_xnew(pa_source_port_info*, i.n_ports+1);
+                    i.ports[0] = pa_xnew(pa_source_port_info, i.n_ports);
+
+                    for (j = 0; j < i.n_ports; j++) {
+                        i.ports[j] = &i.ports[0][j];
+
+                        if (pa_tagstruct_gets(t, &i.ports[j]->name) < 0 ||
+                            pa_tagstruct_gets(t, &i.ports[j]->description) < 0 ||
+                            pa_tagstruct_getu32(t, &i.ports[j]->priority) < 0) {
+
+                            goto fail;
+                        }
+
+                        i.ports[j]->available = PA_PORT_AVAILABLE_UNKNOWN;
+                        if (o->context->version >= 24) {
+                            uint32_t av;
+                            if (pa_tagstruct_getu32(t, &av) < 0 || av > PA_PORT_AVAILABLE_YES)
+                                goto fail;
+                            i.ports[j]->available = av;
+                        }
+                    }
+
+                    i.ports[j] = NULL;
+                }
+
+                if (pa_tagstruct_gets(t, &ap) < 0)
+                    goto fail;
+
+                if (ap) {
+                    for (j = 0; j < i.n_ports; j++)
+                        if (pa_streq(i.ports[j]->name, ap)) {
+                            i.active_port = i.ports[j];
+                            break;
+                        }
+                }
+            }
+
+            if (o->context->version >= 22) {
+                uint8_t n_formats;
+                if (pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1)
+                    goto fail;
+
+                i.formats = pa_xnew0(pa_format_info*, n_formats);
+
+                for (j = 0; j < n_formats; j++) {
+                    i.n_formats++;
+                    i.formats[j] = pa_format_info_new();
+
+                    if (pa_tagstruct_get_format_info(t, i.formats[j]) < 0)
+                        goto fail;
+                }
+            }
+
+            i.mute = (int) mute;
+            i.flags = (pa_source_flags_t) flags;
+            i.state = (pa_source_state_t) state;
+
+            if (o->callback) {
+                pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            if (i.formats) {
+                for (j = 0; j < i.n_formats; j++)
+                    pa_format_info_free(i.formats[j]);
+                pa_xfree(i.formats);
+            }
+            if (i.ports) {
+                pa_xfree(i.ports[0]);
+                pa_xfree(i.ports);
+            }
+            pa_proplist_free(i.proplist);
+        }
+    }
+
+    if (o->callback) {
+        pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+    return;
+
+fail:
+    pa_assert(i.proplist);
+
+    pa_context_fail(o->context, PA_ERR_PROTOCOL);
+
+    if (i.formats) {
+        for (j = 0; j < i.n_formats; j++)
+            pa_format_info_free(i.formats[j]);
+        pa_xfree(i.formats);
+    }
+    if (i.ports) {
+        pa_xfree(i.ports[0]);
+        pa_xfree(i.ports);
+    }
+    pa_proplist_free(i.proplist);
+
+    goto finish;
+}
+
+pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_INFO_LIST, context_get_source_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_INFO, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_PORT, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_puts(t, port);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char *name, const char*port, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_PORT, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_puts(t, port);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+/*** Client info ***/
+
+static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_client_info i;
+
+            pa_zero(i);
+            i.proplist = pa_proplist_new();
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                pa_proplist_free(i.proplist);
+                goto finish;
+            }
+
+            if (o->callback) {
+                pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+        }
+    }
+
+    if (o->callback) {
+        pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_CLIENT_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_client_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Card info ***/
+
+static void card_info_free(pa_card_info* i) {
+    if (i->proplist)
+        pa_proplist_free(i->proplist);
+
+    pa_xfree(i->profiles);
+
+    if (i->n_profiles) {
+        uint32_t j;
+
+        for (j = 0; j < i->n_profiles; j++)
+             pa_xfree(i->profiles2[j]);
+
+        pa_xfree(i->profiles2);
+    }
+
+    if (i->ports) {
+        uint32_t j;
+
+        for (j = 0; j < i->n_ports; j++) {
+            if (i->ports[j]) {
+                if (i->ports[j]->profiles)
+                    pa_xfree(i->ports[j]->profiles);
+                if (i->ports[j]->profiles2)
+                    pa_xfree(i->ports[j]->profiles2);
+                if (i->ports[j]->proplist)
+                    pa_proplist_free(i->ports[j]->proplist);
+            }
+        }
+
+        pa_xfree(i->ports[0]);
+        pa_xfree(i->ports);
+    }
+}
+
+static int fill_card_port_info(pa_context *context, pa_tagstruct* t, pa_card_info* i) {
+    uint32_t j, k, l;
+
+    if (pa_tagstruct_getu32(t, &i->n_ports) < 0)
+        return -PA_ERR_PROTOCOL;
+
+    if (i->n_ports == 0) {
+        i->ports = NULL;
+        return 0;
+    }
+
+    i->ports = pa_xnew0(pa_card_port_info*, i->n_ports+1);
+    i->ports[0] = pa_xnew0(pa_card_port_info, i->n_ports);
+
+    for (j = 0; j < i->n_ports; j++) {
+        uint8_t direction;
+        uint32_t available;
+        pa_card_port_info* port = i->ports[j] = &i->ports[0][j];
+
+        port->proplist = pa_proplist_new();
+
+        if (pa_tagstruct_gets(t, &port->name) < 0 ||
+            pa_tagstruct_gets(t, &port->description) < 0 ||
+            pa_tagstruct_getu32(t, &port->priority) < 0 ||
+            pa_tagstruct_getu32(t, &available) < 0 ||
+            pa_tagstruct_getu8(t, &direction) < 0 ||
+            !pa_direction_valid(direction) ||
+            pa_tagstruct_get_proplist(t, port->proplist) < 0 ||
+            pa_tagstruct_getu32(t, &port->n_profiles) < 0) {
+
+            return -PA_ERR_PROTOCOL;
+        }
+
+        if (available > PA_PORT_AVAILABLE_YES ) {
+            return -PA_ERR_PROTOCOL;
+        }
+
+        port->direction = direction;
+        port->available = available;
+
+        if (port->n_profiles > 0) {
+            port->profiles = pa_xnew0(pa_card_profile_info*, i->n_profiles+1);
+            port->profiles2 = pa_xnew0(pa_card_profile_info2*, i->n_profiles+1);
+
+            for (k = 0; k < port->n_profiles; k++) {
+                const char* profilename;
+
+                if (pa_tagstruct_gets(t, &profilename) < 0)
+                    return -PA_ERR_PROTOCOL;
+
+                for (l = 0; l < i->n_profiles; l++) {
+                    if (pa_streq(i->profiles[l].name, profilename)) {
+                        port->profiles[k] = &i->profiles[l];
+                        port->profiles2[k] = i->profiles2[l];
+                        break;
+                    }
+                }
+
+                if (l >= i->n_profiles)
+                    return -PA_ERR_PROTOCOL;
+            }
+        }
+        if (context->version >= 27) {
+            if (pa_tagstruct_gets64(t, &port->latency_offset) < 0)
+                return -PA_ERR_PROTOCOL;
+        } else
+            port->latency_offset = 0;
+    }
+
+    return 0;
+}
+
+static int fill_card_profile_info(pa_context *context, pa_tagstruct* t, pa_card_info* i) {
+    uint32_t j;
+
+    i->profiles = pa_xnew0(pa_card_profile_info, i->n_profiles+1);
+    i->profiles2 = pa_xnew0(pa_card_profile_info2*, i->n_profiles+1);
+
+    for (j = 0; j < i->n_profiles; j++) {
+        if (pa_tagstruct_gets(t, &i->profiles[j].name) < 0 ||
+            pa_tagstruct_gets(t, &i->profiles[j].description) < 0 ||
+            pa_tagstruct_getu32(t, &i->profiles[j].n_sinks) < 0 ||
+            pa_tagstruct_getu32(t, &i->profiles[j].n_sources) < 0 ||
+            pa_tagstruct_getu32(t, &i->profiles[j].priority) < 0)
+                return -PA_ERR_PROTOCOL;
+
+        i->profiles2[j] = pa_xnew0(pa_card_profile_info2, 1);
+        i->profiles2[j]->name = i->profiles[j].name;
+        i->profiles2[j]->description = i->profiles[j].description;
+        i->profiles2[j]->n_sinks = i->profiles[j].n_sinks;
+        i->profiles2[j]->n_sources = i->profiles[j].n_sources;
+        i->profiles2[j]->priority = i->profiles[j].priority;
+        i->profiles2[j]->available = 1;
+
+        if (context->version >= 29) {
+            uint32_t av;
+
+            if (pa_tagstruct_getu32(t, &av) < 0)
+                return -PA_ERR_PROTOCOL;
+
+            i->profiles2[j]->available = av;
+        }
+    }
+
+    return 0;
+}
+
+static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+    pa_card_info i;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            uint32_t j;
+            const char*ap;
+
+            pa_zero(i);
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                pa_tagstruct_getu32(t, &i.n_profiles) < 0)
+                    goto fail;
+
+            if (i.n_profiles > 0) {
+                if (fill_card_profile_info(o->context, t, &i) < 0)
+                    goto fail;
+            }
+
+            i.proplist = pa_proplist_new();
+
+            if (pa_tagstruct_gets(t, &ap) < 0 ||
+                pa_tagstruct_get_proplist(t, i.proplist) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                card_info_free(&i);
+                goto finish;
+            }
+
+            if (ap) {
+                for (j = 0; j < i.n_profiles; j++)
+                    if (pa_streq(i.profiles[j].name, ap)) {
+                        i.active_profile = &i.profiles[j];
+                        i.active_profile2 = i.profiles2[j];
+                        break;
+                    }
+            }
+
+            if (o->context->version >= 26) {
+                if (fill_card_port_info(o->context, t, &i) < 0)
+                    goto fail;
+            }
+
+            if (o->callback) {
+                pa_card_info_cb_t cb = (pa_card_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            card_info_free(&i);
+        }
+    }
+
+    if (o->callback) {
+        pa_card_info_cb_t cb = (pa_card_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+    return;
+
+fail:
+    pa_context_fail(o->context, PA_ERR_PROTOCOL);
+    card_info_free(&i);
+    goto finish;
+}
+
+pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_CARD_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_card_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char*name, pa_card_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_CARD_INFO, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_card_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata) {
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_CARD_INFO_LIST, context_get_card_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_CARD_PROFILE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_puts(t, profile);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char *name, const char*profile, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_CARD_PROFILE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_puts(t, profile);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+/*** Module info ***/
+
+static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_module_info i;
+            bool auto_unload = false;
+
+            pa_zero(i);
+            i.proplist = pa_proplist_new();
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_gets(t, &i.argument) < 0 ||
+                pa_tagstruct_getu32(t, &i.n_used) < 0 ||
+                (o->context->version < 15 && pa_tagstruct_get_boolean(t, &auto_unload) < 0) ||
+                (o->context->version >= 15 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            i.auto_unload = (int) auto_unload;
+
+            if (o->callback) {
+                pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+        }
+    }
+
+    if (o->callback) {
+        pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_MODULE_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_module_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_MODULE_INFO_LIST, context_get_module_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Sink input info ***/
+
+static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_sink_input_info i;
+            bool mute = false, corked = false, has_volume = false, volume_writable = true;
+
+            pa_zero(i);
+            i.proplist = pa_proplist_new();
+            i.format = pa_format_info_new();
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_getu32(t, &i.client) < 0 ||
+                pa_tagstruct_getu32(t, &i.sink) < 0 ||
+                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
+                pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
+                pa_tagstruct_gets(t, &i.resample_method) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                (o->context->version >= 11 && pa_tagstruct_get_boolean(t, &mute) < 0) ||
+                (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0) ||
+                (o->context->version >= 19 && pa_tagstruct_get_boolean(t, &corked) < 0) ||
+                (o->context->version >= 20 && (pa_tagstruct_get_boolean(t, &has_volume) < 0 ||
+                                               pa_tagstruct_get_boolean(t, &volume_writable) < 0)) ||
+                (o->context->version >= 21 && pa_tagstruct_get_format_info(t, i.format) < 0)) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                pa_proplist_free(i.proplist);
+                pa_format_info_free(i.format);
+                goto finish;
+            }
+
+            i.mute = (int) mute;
+            i.corked = (int) corked;
+            i.has_volume = (int) has_volume;
+            i.volume_writable = (int) volume_writable;
+
+            if (o->callback) {
+                pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+            pa_format_info_free(i.format);
+        }
+    }
+
+    if (o->callback) {
+        pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INPUT_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_input_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INPUT_INFO_LIST, context_get_sink_input_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Source output info ***/
+
+static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_source_output_info i;
+            bool mute = false, corked = false, has_volume = false, volume_writable = true;
+
+            pa_zero(i);
+            i.proplist = pa_proplist_new();
+            i.format = pa_format_info_new();
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_getu32(t, &i.client) < 0 ||
+                pa_tagstruct_getu32(t, &i.source) < 0 ||
+                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
+                pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
+                pa_tagstruct_gets(t, &i.resample_method) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0) ||
+                (o->context->version >= 19 && pa_tagstruct_get_boolean(t, &corked) < 0) ||
+                (o->context->version >= 22 && (pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                                               pa_tagstruct_get_boolean(t, &mute) < 0 ||
+                                               pa_tagstruct_get_boolean(t, &has_volume) < 0 ||
+                                               pa_tagstruct_get_boolean(t, &volume_writable) < 0 ||
+                                               pa_tagstruct_get_format_info(t, i.format) < 0))) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                pa_proplist_free(i.proplist);
+                pa_format_info_free(i.format);
+                goto finish;
+            }
+
+            i.mute = (int) mute;
+            i.corked = (int) corked;
+            i.has_volume = (int) has_volume;
+            i.volume_writable = (int) volume_writable;
+
+            if (o->callback) {
+                pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+            pa_format_info_free(i.format);
+        }
+    }
+
+    if (o->callback) {
+        pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_output_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_source_output_info_list(pa_context *c,  pa_source_output_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, context_get_source_output_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Volume manipulation ***/
+
+pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(name);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_MUTE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(name);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_MUTE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_VOLUME, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_MUTE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_VOLUME, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(name);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_VOLUME, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_MUTE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(name);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_MUTE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 22, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 22, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_OUTPUT_MUTE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+/** Sample Cache **/
+
+static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_sample_info i;
+            bool lazy = false;
+
+            pa_zero(i);
+            i.proplist = pa_proplist_new();
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                pa_tagstruct_get_usec(t, &i.duration) < 0 ||
+                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_getu32(t, &i.bytes) < 0 ||
+                pa_tagstruct_get_boolean(t, &lazy) < 0 ||
+                pa_tagstruct_gets(t, &i.filename) < 0 ||
+                (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            i.lazy = (int) lazy;
+
+            if (o->callback) {
+                pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+        }
+    }
+
+    if (o->callback) {
+        pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SAMPLE_INFO, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SAMPLE_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, command, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+    return command_kill(c, PA_COMMAND_KILL_CLIENT, idx, cb, userdata);
+}
+
+pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+    return command_kill(c, PA_COMMAND_KILL_SINK_INPUT, idx, cb, userdata);
+}
+
+pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+    return command_kill(c, PA_COMMAND_KILL_SOURCE_OUTPUT, idx, cb, userdata);
+}
+
+static void context_index_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    uint32_t idx;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        idx = PA_INVALID_INDEX;
+    } else if (pa_tagstruct_getu32(t, &idx) ||
+               !pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_context_index_cb_t cb = (pa_context_index_cb_t) o->callback;
+        cb(o->context, idx, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_LOAD_MODULE, &tag);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_puts(t, argument);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_index_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+    return command_kill(c, PA_COMMAND_UNLOAD_MODULE, idx, cb, userdata);
+}
+
+pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card_name, const char *port_name, int64_t offset, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, card_name && *card_name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, port_name && *port_name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 27, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_PORT_LATENCY_OFFSET, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, card_name);
+    pa_tagstruct_puts(t, port_name);
+    pa_tagstruct_puts64(t, offset);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+/*** Autoload stuff ***/
+
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Module auto-loading no longer supported.");
+
+pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) {
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
+}
+
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Module auto-loading no longer supported.");
+
+pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
+}
+
+PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Module auto-loading no longer supported.");
+
+pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
+}
+
+PA_WARN_REFERENCE(pa_context_add_autoload, "Module auto-loading no longer supported.");
+
+pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
+}
+
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Module auto-loading no longer supported.");
+
+pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
+}
+
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Module auto-loading no longer supported.");
+
+pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
+}
+
+pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, sink_name && *sink_name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SINK_INPUT, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, sink_name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, sink_idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SINK_INPUT, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_putu32(t, sink_idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, source_name && *source_name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SOURCE_OUTPUT, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, source_name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, source_idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SOURCE_OUTPUT, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_putu32(t, source_idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !sink_name || *sink_name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SINK, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, sink_name);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SINK, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, idx == PA_INVALID_INDEX ? "" : NULL);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !source_name || *source_name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SOURCE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, source_name);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SOURCE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, idx == PA_INVALID_INDEX ? "" : NULL);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
new file mode 100644 (file)
index 0000000..43389b7
--- /dev/null
@@ -0,0 +1,760 @@
+#ifndef foointrospecthfoo
+#define foointrospecthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/operation.h>
+#include <pulse/context.h>
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+#include <pulse/proplist.h>
+#include <pulse/format.h>
+#include <pulse/version.h>
+
+/** \page introspect Server Query and Control
+ *
+ * \section overv_sec Overview
+ *
+ * Sometimes it is necessary to query and modify global settings in the
+ * server. For this, PulseAudio has the introspection API. It can list sinks,
+ * sources, samples and other aspects of the server. It can also modify the
+ * attributes of the server that will affect operations on a global level,
+ * and not just the application's context.
+ *
+ * \section query_sec Querying
+ *
+ * All querying is done through callbacks. This approach is necessary to
+ * maintain an asynchronous design. The client will request the information
+ * and some time later, the server will respond with the desired data.
+ *
+ * Some objects can have multiple instances on the server. When requesting all
+ * of these at once, the callback will be called multiple times, once for
+ * each object. When the list has been exhausted, the callback will be called
+ * without an information structure and the eol parameter set to a positive
+ * value.
+ *
+ * Note that even if a single object is requested, and not the entire list,
+ * the terminating call will still be made.
+ *
+ * If an error occurs, the callback will be invoked without an information
+ * structure and eol set to a negative value..
+ *
+ * Data members in the information structures are only valid during the
+ * duration of the callback. If they are required after the callback is
+ * finished, a deep copy of the information structure must be performed.
+ *
+ * \subsection server_subsec Server Information
+ *
+ * The server can be queried about its name, the environment it's running on
+ * and the currently active global defaults. Calling
+ * pa_context_get_server_info() provides access to a pa_server_info structure
+ * containing all of these.
+ *
+ * \subsection memstat_subsec Memory Usage
+ *
+ * Statistics about memory usage can be fetched using pa_context_stat(),
+ * giving a pa_stat_info structure.
+ *
+ * \subsection sinksrc_subsec Sinks and Sources
+ *
+ * The server can have an arbitrary number of sinks and sources. Each sink
+ * and source have both an index and a name associated with it. As such,
+ * there are three ways to get access to them:
+ *
+ * \li By index - pa_context_get_sink_info_by_index() /
+ *                pa_context_get_source_info_by_index()
+ * \li By name - pa_context_get_sink_info_by_name() /
+ *               pa_context_get_source_info_by_name()
+ * \li All - pa_context_get_sink_info_list() /
+ *           pa_context_get_source_info_list()
+ *
+ * All three method use the same callback and will provide a pa_sink_info or
+ * pa_source_info structure.
+ *
+ * \subsection siso_subsec Sink Inputs and Source Outputs
+ *
+ * Sink inputs and source outputs are the representations of the client ends
+ * of streams inside the server. I.e. they connect a client stream to one of
+ * the global sinks or sources.
+ *
+ * Sink inputs and source outputs only have an index to identify them. As
+ * such, there are only two ways to get information about them:
+ *
+ * \li By index - pa_context_get_sink_input_info() /
+ *                pa_context_get_source_output_info()
+ * \li All - pa_context_get_sink_input_info_list() /
+ *           pa_context_get_source_output_info_list()
+ *
+ * The structure returned is the pa_sink_input_info or pa_source_output_info
+ * structure.
+ *
+ * \subsection samples_subsec Samples
+ *
+ * The list of cached samples can be retrieved from the server. Three methods
+ * exist for querying the sample cache list:
+ *
+ * \li By index - pa_context_get_sample_info_by_index()
+ * \li By name - pa_context_get_sample_info_by_name()
+ * \li All - pa_context_get_sample_info_list()
+ *
+ * Note that this only retrieves information about the sample, not the sample
+ * data itself.
+ *
+ * \subsection module_subsec Driver Modules
+ *
+ * PulseAudio driver modules are identified by index and are retrieved using either
+ * pa_context_get_module_info() or pa_context_get_module_info_list(). The
+ * information structure is called pa_module_info.
+ *
+ * \subsection client_subsec Clients
+ *
+ * PulseAudio clients are also identified by index and are retrieved using
+ * either pa_context_get_client_info() or pa_context_get_client_info_list().
+ * The information structure is called pa_client_info.
+ *
+ * \section ctrl_sec Control
+ *
+ * Some parts of the server are only possible to read, but most can also be
+ * modified in different ways. Note that these changes will affect all
+ * connected clients and not just the one issuing the request.
+ *
+ * \subsection sinksrc_subsec Sinks and Sources
+ *
+ * The most common change one would want to apply to sinks and sources is to
+ * modify the volume of the audio. Identically to how sinks and sources can
+ * be queried, there are two ways of identifying them:
+ *
+ * \li By index - pa_context_set_sink_volume_by_index() /
+ *                pa_context_set_source_volume_by_index()
+ * \li By name - pa_context_set_sink_volume_by_name() /
+ *               pa_context_set_source_volume_by_name()
+ *
+ * It is also possible to mute a sink or source:
+ *
+ * \li By index - pa_context_set_sink_mute_by_index() /
+ *                pa_context_set_source_mute_by_index()
+ * \li By name - pa_context_set_sink_mute_by_name() /
+ *               pa_context_set_source_mute_by_name()
+ *
+ * \subsection siso_subsec Sink Inputs and Source Outputs
+ *
+ * If an application desires to modify the volume of just a single stream
+ * (commonly one of its own streams), this can be done by setting the volume
+ * of its associated sink input or source output, using
+ * pa_context_set_sink_input_volume() or pa_context_set_source_output_volume().
+ *
+ * It is also possible to remove sink inputs and source outputs, terminating
+ * the streams associated with them:
+ *
+ * \li Sink input - pa_context_kill_sink_input()
+ * \li Source output - pa_context_kill_source_output()
+ *
+ * It is strongly recommended that all volume changes are done as a direct
+ * result of user input. With automated requests, such as those resulting
+ * from misguided attempts of crossfading, PulseAudio can store the stream
+ * volume at an inappropriate moment and restore it later. Besides, such
+ * attempts lead to OSD popups in some desktop environments.
+ *
+ * As a special case of the general rule above, it is recommended that your
+ * application leaves the task of saving and restoring the volume of its
+ * streams to PulseAudio and does not attempt to do it by itself. PulseAudio
+ * really knows better about events such as stream moving or headphone
+ * plugging that would make the volume stored by the application inapplicable
+ * to the new configuration.
+ *
+ * Another important case where setting a sink input volume may be a bad idea
+ * is related to interpreters that interpret potentially untrusted scripts.
+ * PulseAudio relies on your application not making malicious requests (such
+ * as repeatedly setting the volume to 100%). Thus, script interpreters that
+ * represent a security boundary must sandbox volume-changing requests coming
+ * from their scripts. In the worst case, it may be necessary to apply the
+ * script-requested volume to the script-produced sounds by altering the
+ * samples in the script interpreter and not touching the sink or sink input
+ * volume as seen by PulseAudio.
+ *
+ * If an application changes any volume, it should also listen to changes of
+ * the same volume originating from outside the application (e.g., from the
+ * system mixer application) and update its user interface accordingly. Use
+ * \ref subscribe to get such notifications.
+ *
+ * \subsection module_subsec Modules
+ *
+ * Server modules can be remotely loaded and unloaded using
+ * pa_context_load_module() and pa_context_unload_module().
+ *
+ * \subsection client_subsec Clients
+ *
+ * The only operation supported on clients is the possibility of kicking
+ * them off the server using pa_context_kill_client().
+ */
+
+/** \file
+ *
+ * Routines for daemon introspection.
+ *
+ * See also \subpage introspect
+ */
+
+PA_C_DECL_BEGIN
+
+/** @{ \name Sinks */
+
+/** Stores information about a specific port of a sink.  Please
+ * note that this structure can be extended as part of evolutionary
+ * API updates at any time in any new release. \since 0.9.16 */
+typedef struct pa_sink_port_info {
+    const char *name;                   /**< Name of this port */
+    const char *description;            /**< Description of this port */
+    uint32_t priority;                  /**< The higher this value is, the more useful this port is as a default. */
+    int available;                      /**< A flags (see #pa_port_available), indicating availability status of this port. \since 2.0 */
+} pa_sink_port_info;
+
+/** Stores information about sinks. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_sink_info {
+    const char *name;                  /**< Name of the sink */
+    uint32_t index;                    /**< Index of the sink */
+    const char *description;           /**< Description of this sink */
+    pa_sample_spec sample_spec;        /**< Sample spec of this sink */
+    pa_channel_map channel_map;        /**< Channel map */
+    uint32_t owner_module;             /**< Index of the owning module of this sink, or PA_INVALID_INDEX. */
+    pa_cvolume volume;                 /**< Volume of the sink */
+    int mute;                          /**< Mute switch of the sink */
+    uint32_t monitor_source;           /**< Index of the monitor source connected to this sink. */
+    const char *monitor_source_name;   /**< The name of the monitor source. */
+    pa_usec_t latency;                 /**< Length of queued audio in the output buffer. */
+    const char *driver;                /**< Driver name */
+    pa_sink_flags_t flags;             /**< Flags */
+    pa_proplist *proplist;             /**< Property list \since 0.9.11 */
+    pa_usec_t configured_latency;      /**< The latency this device has been configured to. \since 0.9.11 */
+    pa_volume_t base_volume;           /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.15 */
+    pa_sink_state_t state;             /**< State \since 0.9.15 */
+    uint32_t n_volume_steps;           /**< Number of volume steps for sinks which do not support arbitrary volumes. \since 0.9.15 */
+    uint32_t card;                     /**< Card index, or PA_INVALID_INDEX. \since 0.9.15 */
+    uint32_t n_ports;                  /**< Number of entries in port array \since 0.9.16 */
+    pa_sink_port_info** ports;         /**< Array of available ports, or NULL. Array is terminated by an entry set to NULL. The number of entries is stored in n_ports. \since 0.9.16 */
+    pa_sink_port_info* active_port;    /**< Pointer to active port in the array, or NULL. \since 0.9.16 */
+    uint8_t n_formats;                 /**< Number of formats supported by the sink. \since 1.0 */
+    pa_format_info **formats;          /**< Array of formats supported by the sink. \since 1.0 */
+} pa_sink_info;
+
+/** Callback prototype for pa_context_get_sink_info_by_name() and friends */
+typedef void (*pa_sink_info_cb_t)(pa_context *c, const pa_sink_info *i, int eol, void *userdata);
+
+/** Get information about a sink by its name */
+pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata);
+
+/** Get information about a sink by its index */
+pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata);
+
+/** Get the complete sink list */
+pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata);
+
+/** Set the volume of a sink device specified by its index */
+pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the volume of a sink device specified by its name */
+pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink device specified by its index */
+pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink device specified by its name */
+pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Suspend/Resume a sink. \since 0.9.7 */
+pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata);
+
+/** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */
+pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend,  pa_context_success_cb_t cb, void* userdata);
+
+/** Change the profile of a sink. \since 0.9.16 */
+pa_operation* pa_context_set_sink_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata);
+
+/** Change the profile of a sink. \since 0.9.15 */
+pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Sources */
+
+/** Stores information about a specific port of a source.  Please
+ * note that this structure can be extended as part of evolutionary
+ * API updates at any time in any new release. \since 0.9.16 */
+typedef struct pa_source_port_info {
+    const char *name;                   /**< Name of this port */
+    const char *description;            /**< Description of this port */
+    uint32_t priority;                  /**< The higher this value is, the more useful this port is as a default. */
+    int available;                      /**< A flags (see #pa_port_available), indicating availability status of this port. \since 2.0 */
+} pa_source_port_info;
+
+/** Stores information about sources. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_source_info {
+    const char *name;                   /**< Name of the source */
+    uint32_t index;                     /**< Index of the source */
+    const char *description;            /**< Description of this source */
+    pa_sample_spec sample_spec;         /**< Sample spec of this source */
+    pa_channel_map channel_map;         /**< Channel map */
+    uint32_t owner_module;              /**< Owning module index, or PA_INVALID_INDEX. */
+    pa_cvolume volume;                  /**< Volume of the source */
+    int mute;                           /**< Mute switch of the sink */
+    uint32_t monitor_of_sink;           /**< If this is a monitor source, the index of the owning sink, otherwise PA_INVALID_INDEX. */
+    const char *monitor_of_sink_name;   /**< Name of the owning sink, or NULL. */
+    pa_usec_t latency;                  /**< Length of filled record buffer of this source. */
+    const char *driver;                 /**< Driver name */
+    pa_source_flags_t flags;            /**< Flags */
+    pa_proplist *proplist;              /**< Property list \since 0.9.11 */
+    pa_usec_t configured_latency;       /**< The latency this device has been configured to. \since 0.9.11 */
+    pa_volume_t base_volume;            /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.15 */
+    pa_source_state_t state;            /**< State \since 0.9.15 */
+    uint32_t n_volume_steps;            /**< Number of volume steps for sources which do not support arbitrary volumes. \since 0.9.15 */
+    uint32_t card;                      /**< Card index, or PA_INVALID_INDEX. \since 0.9.15 */
+    uint32_t n_ports;                   /**< Number of entries in port array \since 0.9.16 */
+    pa_source_port_info** ports;        /**< Array of available ports, or NULL. Array is terminated by an entry set to NULL. The number of entries is stored in n_ports. \since 0.9.16  */
+    pa_source_port_info* active_port;   /**< Pointer to active port in the array, or NULL. \since 0.9.16  */
+    uint8_t n_formats;                  /**< Number of formats supported by the source. \since 1.0 */
+    pa_format_info **formats;           /**< Array of formats supported by the source. \since 1.0 */
+} pa_source_info;
+
+/** Callback prototype for pa_context_get_source_info_by_name() and friends */
+typedef void (*pa_source_info_cb_t)(pa_context *c, const pa_source_info *i, int eol, void *userdata);
+
+/** Get information about a source by its name */
+pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata);
+
+/** Get information about a source by its index */
+pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata);
+
+/** Get the complete source list */
+pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata);
+
+/** Set the volume of a source device specified by its index */
+pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the volume of a source device specified by its name */
+pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source device specified by its index */
+pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source device specified by its name */
+pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Suspend/Resume a source. \since 0.9.7 */
+pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata);
+
+/** Suspend/Resume a source. If idx is PA_INVALID_INDEX, all sources will be suspended. \since 0.9.7 */
+pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
+
+/** Change the profile of a source. \since 0.9.16 */
+pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata);
+
+/** Change the profile of a source. \since 0.9.15 */
+pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Server */
+
+/** Server information. Please note that this structure can be
+ * extended as part of evolutionary API updates at any time in any new
+ * release. */
+typedef struct pa_server_info {
+    const char *user_name;              /**< User name of the daemon process */
+    const char *host_name;              /**< Host name the daemon is running on */
+    const char *server_version;         /**< Version string of the daemon */
+    const char *server_name;            /**< Server package name (usually "pulseaudio") */
+    pa_sample_spec sample_spec;         /**< Default sample specification */
+    const char *default_sink_name;      /**< Name of default sink. */
+    const char *default_source_name;    /**< Name of default source. */
+    uint32_t cookie;                    /**< A random cookie for identifying this instance of PulseAudio. */
+    pa_channel_map channel_map;         /**< Default channel map. \since 0.9.15 */
+} pa_server_info;
+
+/** Callback prototype for pa_context_get_server_info() */
+typedef void (*pa_server_info_cb_t) (pa_context *c, const pa_server_info*i, void *userdata);
+
+/** Get some information about the server */
+pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Modules */
+
+/** Stores information about modules. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_module_info {
+    uint32_t index;                     /**< Index of the module */
+    const char*name,                    /**< Name of the module */
+        *argument;                      /**< Argument string of the module */
+    uint32_t n_used;                    /**< Usage counter or PA_INVALID_INDEX */
+/** \cond fulldocs */
+    int auto_unload;                    /**< \deprecated Non-zero if this is an autoloaded module. */
+/** \endcond */
+    pa_proplist *proplist;              /**< Property list \since 0.9.15 */
+} pa_module_info;
+
+/** Callback prototype for pa_context_get_module_info() and friends */
+typedef void (*pa_module_info_cb_t) (pa_context *c, const pa_module_info*i, int eol, void *userdata);
+
+/** Get some information about a module by its index */
+pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata);
+
+/** Get the complete list of currently loaded modules */
+pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata);
+
+/** Callback prototype for pa_context_load_module() */
+typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+
+/** Load a module. */
+pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata);
+
+/** Unload a module. */
+pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Clients */
+
+/** Stores information about clients. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_client_info {
+    uint32_t index;                      /**< Index of this client */
+    const char *name;                    /**< Name of this client */
+    uint32_t owner_module;               /**< Index of the owning module, or PA_INVALID_INDEX. */
+    const char *driver;                  /**< Driver name */
+    pa_proplist *proplist;               /**< Property list \since 0.9.11 */
+} pa_client_info;
+
+/** Callback prototype for pa_context_get_client_info() and friends */
+typedef void (*pa_client_info_cb_t) (pa_context *c, const pa_client_info*i, int eol, void *userdata);
+
+/** Get information about a client by its index */
+pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata);
+
+/** Get the complete client list */
+pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata);
+
+/** Kill a client. */
+pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Cards */
+
+/** \deprecated Superseded by pa_card_profile_info2 \since 0.9.15 */
+typedef struct pa_card_profile_info {
+    const char *name;                   /**< Name of this profile */
+    const char *description;            /**< Description of this profile */
+    uint32_t n_sinks;                   /**< Number of sinks this profile would create */
+    uint32_t n_sources;                 /**< Number of sources this profile would create */
+    uint32_t priority;                  /**< The higher this value is, the more useful this profile is as a default. */
+} pa_card_profile_info;
+
+/** Stores information about a specific profile of a card. Please
+ * note that this structure can be extended as part of evolutionary
+ * API updates at any time in any new release. \since 5.0 */
+typedef struct pa_card_profile_info2 {
+    const char *name;                   /**< Name of this profile */
+    const char *description;            /**< Description of this profile */
+    uint32_t n_sinks;                   /**< Number of sinks this profile would create */
+    uint32_t n_sources;                 /**< Number of sources this profile would create */
+    uint32_t priority;                  /**< The higher this value is, the more useful this profile is as a default. */
+    int available;
+    /**< Is this profile available? If this is zero, meaning "unavailable",
+     * then it makes no sense to try to activate this profile. If this is
+     * non-zero, it's still not a guarantee that activating the profile will
+     * result in anything useful, it just means that the server isn't aware of
+     * any reason why the profile would definitely be useless. \since 5.0 */
+} pa_card_profile_info2;
+
+/** Stores information about a specific port of a card.  Please
+ * note that this structure can be extended as part of evolutionary
+ * API updates at any time in any new release. \since 2.0 */
+typedef struct pa_card_port_info {
+    const char *name;                   /**< Name of this port */
+    const char *description;            /**< Description of this port */
+    uint32_t priority;                  /**< The higher this value is, the more useful this port is as a default. */
+    int available;                      /**< A #pa_port_available enum, indicating availability status of this port. */
+    int direction;                      /**< A #pa_direction enum, indicating the direction of this port. */
+    uint32_t n_profiles;                /**< Number of entries in profile array */
+    pa_card_profile_info** profiles;    /**< \deprecated Superseded by profiles2 */
+    pa_proplist *proplist;              /**< Property list */
+    int64_t latency_offset;             /**< Latency offset of the port that gets added to the sink/source latency when the port is active. \since 3.0 */
+    pa_card_profile_info2** profiles2;  /**< Array of pointers to available profiles, or NULL. Array is terminated by an entry set to NULL. \since 5.0 */
+} pa_card_port_info;
+
+/** Stores information about cards. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release.  \since 0.9.15 */
+typedef struct pa_card_info {
+    uint32_t index;                      /**< Index of this card */
+    const char *name;                    /**< Name of this card */
+    uint32_t owner_module;               /**< Index of the owning module, or PA_INVALID_INDEX. */
+    const char *driver;                  /**< Driver name */
+    uint32_t n_profiles;                 /**< Number of entries in profile array */
+    pa_card_profile_info* profiles;      /**< \deprecated Superseded by profiles2 */
+    pa_card_profile_info* active_profile; /**< \deprecated Superseded by active_profile2 */
+    pa_proplist *proplist;               /**< Property list */
+    uint32_t n_ports;                    /**< Number of entries in port array */
+    pa_card_port_info **ports;           /**< Array of pointers to ports, or NULL. Array is terminated by an entry set to NULL. */
+    pa_card_profile_info2** profiles2;    /**< Array of pointers to available profiles, or NULL. Array is terminated by an entry set to NULL. \since 5.0 */
+    pa_card_profile_info2* active_profile2; /**< Pointer to active profile in the array, or NULL. \since 5.0 */
+} pa_card_info;
+
+/** Callback prototype for pa_context_get_card_info_...() \since 0.9.15 */
+typedef void (*pa_card_info_cb_t) (pa_context *c, const pa_card_info*i, int eol, void *userdata);
+
+/** Get information about a card by its index \since 0.9.15 */
+pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata);
+
+/** Get information about a card by its name \since 0.9.15 */
+pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, pa_card_info_cb_t cb, void *userdata);
+
+/** Get the complete card list \since 0.9.15 */
+pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata);
+
+/** Change the profile of a card. \since 0.9.15 */
+pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata);
+
+/** Change the profile of a card. \since 0.9.15 */
+pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name, const char*profile, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the latency offset of a port. \since 3.0 */
+pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card_name, const char *port_name, int64_t offset, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Sink Inputs */
+
+/** Stores information about sink inputs. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_sink_input_info {
+    uint32_t index;                      /**< Index of the sink input */
+    const char *name;                    /**< Name of the sink input */
+    uint32_t owner_module;               /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module. */
+    uint32_t client;                     /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client. */
+    uint32_t sink;                       /**< Index of the connected sink */
+    pa_sample_spec sample_spec;          /**< The sample specification of the sink input. */
+    pa_channel_map channel_map;          /**< Channel map */
+    pa_cvolume volume;                   /**< The volume of this sink input. */
+    pa_usec_t buffer_usec;               /**< Latency due to buffering in sink input, see pa_timing_info for details. */
+    pa_usec_t sink_usec;                 /**< Latency of the sink device, see pa_timing_info for details. */
+    const char *resample_method;         /**< The resampling method used by this sink input. */
+    const char *driver;                  /**< Driver name */
+    int mute;                            /**< Stream muted \since 0.9.7 */
+    pa_proplist *proplist;               /**< Property list \since 0.9.11 */
+    int corked;                          /**< Stream corked \since 1.0 */
+    int has_volume;                      /**< Stream has volume. If not set, then the meaning of this struct's volume member is unspecified. \since 1.0 */
+    int volume_writable;                 /**< The volume can be set. If not set, the volume can still change even though clients can't control the volume. \since 1.0 */
+    pa_format_info *format;              /**< Stream format information. \since 1.0 */
+} pa_sink_input_info;
+
+/** Callback prototype for pa_context_get_sink_input_info() and friends */
+typedef void (*pa_sink_input_info_cb_t) (pa_context *c, const pa_sink_input_info *i, int eol, void *userdata);
+
+/** Get some information about a sink input by its index */
+pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata);
+
+/** Get the complete sink input list */
+pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata);
+
+/** Move the specified sink input to a different sink. \since 0.9.5 */
+pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata);
+
+/** Move the specified sink input to a different sink. \since 0.9.5 */
+pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata);
+
+/** Set the volume of a sink input stream */
+pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink input stream \since 0.9.7 */
+pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Kill a sink input. */
+pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Source Outputs */
+
+/** Stores information about source outputs. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_source_output_info {
+    uint32_t index;                      /**< Index of the source output */
+    const char *name;                    /**< Name of the source output */
+    uint32_t owner_module;               /**< Index of the module this source output belongs to, or PA_INVALID_INDEX when it does not belong to any module. */
+    uint32_t client;                     /**< Index of the client this source output belongs to, or PA_INVALID_INDEX when it does not belong to any client. */
+    uint32_t source;                     /**< Index of the connected source */
+    pa_sample_spec sample_spec;          /**< The sample specification of the source output */
+    pa_channel_map channel_map;          /**< Channel map */
+    pa_usec_t buffer_usec;               /**< Latency due to buffering in the source output, see pa_timing_info for details. */
+    pa_usec_t source_usec;               /**< Latency of the source device, see pa_timing_info for details. */
+    const char *resample_method;         /**< The resampling method used by this source output. */
+    const char *driver;                  /**< Driver name */
+    pa_proplist *proplist;               /**< Property list \since 0.9.11 */
+    int corked;                          /**< Stream corked \since 1.0 */
+    pa_cvolume volume;                   /**< The volume of this source output \since 1.0 */
+    int mute;                            /**< Stream muted \since 1.0 */
+    int has_volume;                      /**< Stream has volume. If not set, then the meaning of this struct's volume member is unspecified. \since 1.0 */
+    int volume_writable;                 /**< The volume can be set. If not set, the volume can still change even though clients can't control the volume. \since 1.0 */
+    pa_format_info *format;              /**< Stream format information. \since 1.0 */
+} pa_source_output_info;
+
+/** Callback prototype for pa_context_get_source_output_info() and friends */
+typedef void (*pa_source_output_info_cb_t) (pa_context *c, const pa_source_output_info *i, int eol, void *userdata);
+
+/** Get information about a source output by its index */
+pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata);
+
+/** Get the complete list of source outputs */
+pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata);
+
+/** Move the specified source output to a different source. \since 0.9.5 */
+pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata);
+
+/** Move the specified source output to a different source. \since 0.9.5 */
+pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata);
+
+/** Set the volume of a source output stream \since 1.0 */
+pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source output stream \since 1.0 */
+pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Kill a source output. */
+pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Statistics */
+
+/** Memory block statistics. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_stat_info {
+    uint32_t memblock_total;           /**< Currently allocated memory blocks */
+    uint32_t memblock_total_size;      /**< Current total size of allocated memory blocks */
+    uint32_t memblock_allocated;       /**< Allocated memory blocks during the whole lifetime of the daemon. */
+    uint32_t memblock_allocated_size;  /**< Total size of all memory blocks allocated during the whole lifetime of the daemon. */
+    uint32_t scache_size;              /**< Total size of all sample cache entries. */
+} pa_stat_info;
+
+/** Callback prototype for pa_context_stat() */
+typedef void (*pa_stat_info_cb_t) (pa_context *c, const pa_stat_info *i, void *userdata);
+
+/** Get daemon memory block statistics */
+pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Cached Samples */
+
+/** Stores information about sample cache entries. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_sample_info {
+    uint32_t index;                       /**< Index of this entry */
+    const char *name;                     /**< Name of this entry */
+    pa_cvolume volume;                    /**< Default volume of this entry */
+    pa_sample_spec sample_spec;           /**< Sample specification of the sample */
+    pa_channel_map channel_map;           /**< The channel map */
+    pa_usec_t duration;                   /**< Duration of this entry */
+    uint32_t bytes;                       /**< Length of this sample in bytes. */
+    int lazy;                             /**< Non-zero when this is a lazy cache entry. */
+    const char *filename;                 /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. */
+    pa_proplist *proplist;                /**< Property list for this sample. \since 0.9.11 */
+} pa_sample_info;
+
+/** Callback prototype for pa_context_get_sample_info_by_name() and friends */
+typedef void (*pa_sample_info_cb_t)(pa_context *c, const pa_sample_info *i, int eol, void *userdata);
+
+/** Get information about a sample by its name */
+pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata);
+
+/** Get information about a sample by its index */
+pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata);
+
+/** Get the complete list of samples stored in the daemon. */
+pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata);
+
+/** @} */
+
+/** \cond fulldocs */
+
+/** @{ \name Autoload Entries */
+
+/** \deprecated Type of an autoload entry. */
+typedef enum pa_autoload_type {
+    PA_AUTOLOAD_SINK = 0,
+    PA_AUTOLOAD_SOURCE = 1
+} pa_autoload_type_t;
+
+/** \deprecated Stores information about autoload entries. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_autoload_info {
+    uint32_t index;               /**< Index of this autoload entry */
+    const char *name;             /**< Name of the sink or source */
+    pa_autoload_type_t type;      /**< Type of the autoload entry */
+    const char *module;           /**< Module name to load */
+    const char *argument;         /**< Argument string for module */
+} pa_autoload_info;
+
+/** \deprecated Callback prototype for pa_context_get_autoload_info_by_name() and friends */
+typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata);
+
+/** \deprecated Get info about a specific autoload entry. */
+pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
+
+/** \deprecated Get info about a specific autoload entry. */
+pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
+
+/** \deprecated Get the complete list of autoload entries. */
+pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
+
+/** \deprecated Add a new autoload entry. */
+pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata) PA_GCC_DEPRECATED;
+
+/** \deprecated Remove an autoload entry. */
+pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
+
+/** \deprecated Remove an autoload entry. */
+pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
+
+/** @} */
+
+/** \endcond */
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/json.c b/src/pulse/json.c
new file mode 100644 (file)
index 0000000..d126712
--- /dev/null
@@ -0,0 +1,614 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2016 Arun Raghavan <mail@arunraghavan.net>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+
+#include <pulse/json.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/strbuf.h>
+
+#define MAX_NESTING_DEPTH 20 /* Arbitrary number to make sure we don't have a stack overflow */
+
+struct pa_json_object {
+    pa_json_type type;
+
+    union {
+        int int_value;
+        double double_value;
+        bool bool_value;
+        char *string_value;
+        pa_hashmap *object_values; /* name -> object */
+        pa_idxset *array_values; /* objects */
+    };
+};
+
+static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth);
+
+static pa_json_object* json_object_new(void) {
+    pa_json_object *obj;
+
+    obj = pa_xnew0(pa_json_object, 1);
+
+    return obj;
+}
+
+static bool is_whitespace(char c) {
+    return c == '\t' || c == '\n' || c == '\r' || c == ' ';
+}
+
+static bool is_digit(char c) {
+    return c >= '0' && c <= '9';
+}
+
+static bool is_end(const char c, const char *end) {
+    if (!end)
+        return c == '\0';
+    else  {
+        while (*end) {
+            if (c == *end)
+                return true;
+            end++;
+        }
+    }
+
+    return false;
+}
+
+static const char* consume_string(const char *str, const char *expect) {
+    while (*expect) {
+        if (*str != *expect)
+            return NULL;
+
+        str++;
+        expect++;
+    }
+
+    return str;
+}
+
+static const char* parse_null(const char *str, pa_json_object *obj) {
+    str = consume_string(str, "null");
+
+    if (str)
+        obj->type = PA_JSON_TYPE_NULL;
+
+    return str;
+}
+
+static const char* parse_boolean(const char *str, pa_json_object *obj) {
+    const char *tmp;
+
+    tmp = consume_string(str, "true");
+
+    if (tmp) {
+        obj->type = PA_JSON_TYPE_BOOL;
+        obj->bool_value = true;
+    } else {
+        tmp = consume_string(str, "false");
+
+        if (str) {
+            obj->type = PA_JSON_TYPE_BOOL;
+            obj->bool_value = false;
+        }
+    }
+
+    return tmp;
+}
+
+static const char* parse_string(const char *str, pa_json_object *obj) {
+    pa_strbuf *buf = pa_strbuf_new();
+
+    str++; /* Consume leading '"' */
+
+    while (*str && *str != '"') {
+        if (*str != '\\') {
+            /* We only accept ASCII printable characters. */
+            if (*str < 0x20 || *str > 0x7E) {
+                pa_log("Invalid non-ASCII character: 0x%x", (unsigned int) *str);
+                goto error;
+            }
+
+            /* Normal character, juts consume */
+            pa_strbuf_putc(buf, *str);
+        } else {
+            /* Need to unescape */
+            str++;
+
+            switch (*str) {
+                case '"':
+                case '\\':
+                case '/':
+                    pa_strbuf_putc(buf, *str);
+                    break;
+
+                case 'b':
+                    pa_strbuf_putc(buf, '\b' /* backspace */);
+                    break;
+
+                case 'f':
+                    pa_strbuf_putc(buf, '\f' /* form feed */);
+                    break;
+
+                case 'n':
+                    pa_strbuf_putc(buf, '\n' /* new line */);
+                    break;
+
+                case 'r':
+                    pa_strbuf_putc(buf, '\r' /* carriage return */);
+                    break;
+
+                case 't':
+                    pa_strbuf_putc(buf, '\t' /* horizontal tab */);
+                    break;
+
+                case 'u':
+                    pa_log("Unicode code points are currently unsupported");
+                    goto error;
+
+                default:
+                    pa_log("Unexepcted escape value: %c", *str);
+                    goto error;
+            }
+        }
+
+        str++;
+    }
+
+    if (*str != '"') {
+        pa_log("Failed to parse remainder of string: %s", str);
+        goto error;
+    }
+
+    str++;
+
+    obj->type = PA_JSON_TYPE_STRING;
+    obj->string_value = pa_strbuf_to_string_free(buf);
+
+    return str;
+
+error:
+    pa_strbuf_free(buf);
+    return NULL;
+}
+
+static const char* parse_number(const char *str, pa_json_object *obj) {
+    bool negative = false, has_fraction = false, has_exponent = false, valid = false;
+    unsigned int integer = 0;
+    unsigned int fraction = 0;
+    unsigned int fraction_digits = 0;
+    int exponent = 0;
+
+    if (*str == '-') {
+        negative = true;
+        str++;
+    }
+
+    if (*str == '0') {
+        valid = true;
+        str++;
+        goto fraction;
+    }
+
+    while (is_digit(*str)) {
+        valid = true;
+
+        if (integer > ((negative ? INT_MAX : UINT_MAX) / 10)) {
+            pa_log("Integer overflow while parsing number");
+            goto error;
+        }
+
+        integer = (integer * 10) + (*str - '0');
+        str++;
+    }
+
+fraction:
+
+    if (!valid) {
+        pa_log("Missing digits while parsing number");
+        goto error;
+    }
+
+    if (*str == '.') {
+        has_fraction = true;
+        str++;
+        valid = false;
+
+        while (is_digit(*str)) {
+            valid = true;
+
+            if (fraction > (UINT_MAX / 10)) {
+                pa_log("Integer overflow while parsing fractional part of number");
+                goto error;
+            }
+
+            fraction = (fraction * 10) + (*str - '0');
+            fraction_digits++;
+            str++;
+        }
+
+        if (!valid) {
+            pa_log("No digit after '.' while parsing fraction");
+            goto error;
+        }
+    }
+
+    if (*str == 'e' || *str == 'E') {
+        bool exponent_negative = false;
+
+        has_exponent = true;
+        str++;
+        valid = false;
+
+        if (*str == '-') {
+            exponent_negative = true;
+            str++;
+        } else if (*str == '+')
+            str++;
+
+        while (is_digit(*str)) {
+            valid = true;
+
+            if (exponent > (INT_MAX / 10)) {
+                pa_log("Integer overflow while parsing exponent part of number");
+                goto error;
+            }
+
+            exponent = (exponent * 10) + (*str - '0');
+            str++;
+        }
+
+        if (!valid) {
+            pa_log("No digit in exponent while parsing fraction");
+            goto error;
+        }
+
+        if (exponent_negative)
+            exponent *= -1;
+    }
+
+    if (has_fraction || has_exponent) {
+        obj->type = PA_JSON_TYPE_DOUBLE;
+        obj->double_value =
+            (negative ? -1.0 : 1.0) * (integer + (double) fraction / pow(10, fraction_digits)) * pow(10, exponent);
+    } else {
+        obj->type = PA_JSON_TYPE_INT;
+        obj->int_value = (negative ? -1 : 1) * integer;
+    }
+
+    return str;
+
+error:
+    return NULL;
+}
+
+static const char *parse_object(const char *str, pa_json_object *obj, unsigned int depth) {
+    pa_json_object *name = NULL, *value = NULL;
+
+    obj->object_values = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
+                                             pa_xfree, (pa_free_cb_t) pa_json_object_free);
+
+    while (*str != '}') {
+        str++; /* Consume leading '{' or ',' */
+
+        str = parse_value(str, ":", &name, depth + 1);
+        if (!str || pa_json_object_get_type(name) != PA_JSON_TYPE_STRING) {
+            pa_log("Could not parse key for object");
+            goto error;
+        }
+
+        /* Consume the ':' */
+        str++;
+
+        str = parse_value(str, ",}", &value, depth + 1);
+        if (!str) {
+            pa_log("Could not parse value for object");
+            goto error;
+        }
+
+        pa_hashmap_put(obj->object_values, pa_xstrdup(pa_json_object_get_string(name)), value);
+        pa_json_object_free(name);
+
+        name = NULL;
+        value = NULL;
+    }
+
+    /* Drop trailing '}' */
+    str++;
+
+    /* We now know the value was correctly parsed */
+    obj->type = PA_JSON_TYPE_OBJECT;
+
+    return str;
+
+error:
+    pa_hashmap_free(obj->object_values);
+    obj->object_values = NULL;
+
+    if (name)
+        pa_json_object_free(name);
+    if (value)
+        pa_json_object_free(value);
+
+    return NULL;
+}
+
+static const char *parse_array(const char *str, pa_json_object *obj, unsigned int depth) {
+    pa_json_object *value;
+
+    obj->array_values = pa_idxset_new(NULL, NULL);
+
+    while (*str != ']') {
+        str++; /* Consume leading '[' or ',' */
+
+        /* Need to chew up whitespaces as a special case to deal with the
+         * possibility of an empty array */
+        while (is_whitespace(*str))
+            str++;
+
+        if (*str == ']')
+            break;
+
+        str = parse_value(str, ",]", &value, depth + 1);
+        if (!str) {
+            pa_log("Could not parse value for array");
+            goto error;
+        }
+
+        pa_idxset_put(obj->array_values, value, NULL);
+    }
+
+    /* Drop trailing ']' */
+    str++;
+
+    /* We now know the value was correctly parsed */
+    obj->type = PA_JSON_TYPE_ARRAY;
+
+    return str;
+
+error:
+    pa_idxset_free(obj->array_values, (pa_free_cb_t) pa_json_object_free);
+    obj->array_values = NULL;
+    return NULL;
+}
+
+typedef enum {
+    JSON_PARSER_STATE_INIT,
+    JSON_PARSER_STATE_FINISH,
+} json_parser_state;
+
+static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth) {
+    json_parser_state state = JSON_PARSER_STATE_INIT;
+    pa_json_object *o;
+
+    pa_assert(str != NULL);
+
+    o = json_object_new();
+
+    if (depth > MAX_NESTING_DEPTH) {
+        pa_log("Exceeded maximum permitted nesting depth of objects (%u)", MAX_NESTING_DEPTH);
+        goto error;
+    }
+
+    while (!is_end(*str, end)) {
+        switch (state) {
+            case JSON_PARSER_STATE_INIT:
+                if (is_whitespace(*str)) {
+                    str++;
+                } else if (*str == 'n') {
+                    str = parse_null(str, o);
+                    state = JSON_PARSER_STATE_FINISH;
+                } else if (*str == 't' || *str == 'f') {
+                    str = parse_boolean(str, o);
+                    state = JSON_PARSER_STATE_FINISH;
+                } else if (*str == '"') {
+                    str = parse_string(str, o);
+                    state = JSON_PARSER_STATE_FINISH;
+                } else if (is_digit(*str) || *str == '-') {
+                    str = parse_number(str, o);
+                    state = JSON_PARSER_STATE_FINISH;
+                } else if (*str == '{') {
+                    str = parse_object(str, o, depth);
+                    state = JSON_PARSER_STATE_FINISH;
+                } else if (*str == '[') {
+                    str = parse_array(str, o, depth);
+                    state = JSON_PARSER_STATE_FINISH;
+                } else {
+                    pa_log("Invalid JSON string: %s", str);
+                    goto error;
+                }
+
+                if (!str)
+                    goto error;
+
+                break;
+
+            case JSON_PARSER_STATE_FINISH:
+                /* Consume trailing whitespaces */
+                if (is_whitespace(*str)) {
+                    str++;
+                } else {
+                    goto error;
+                }
+        }
+    }
+
+    if (pa_json_object_get_type(o) == PA_JSON_TYPE_INIT) {
+        /* We didn't actually get any data */
+        pa_log("No data while parsing json string: '%s' till '%s'", str, pa_strnull(end));
+        goto error;
+    }
+
+    *obj = o;
+
+    return str;
+
+error:
+    pa_json_object_free(o);
+    return NULL;
+}
+
+
+pa_json_object* pa_json_parse(const char *str) {
+    pa_json_object *obj;
+
+    str = parse_value(str, NULL, &obj, 0);
+
+    if (!str) {
+        pa_log("JSON parsing failed");
+        return NULL;
+    }
+
+    if (*str != '\0') {
+        pa_log("Unable to parse complete JSON string, remainder is: %s", str);
+        pa_json_object_free(obj);
+        return NULL;
+    }
+
+    return obj;
+}
+
+pa_json_type pa_json_object_get_type(const pa_json_object *obj) {
+    return obj->type;
+}
+
+void pa_json_object_free(pa_json_object *obj) {
+
+    switch (pa_json_object_get_type(obj)) {
+        case PA_JSON_TYPE_INIT:
+        case PA_JSON_TYPE_INT:
+        case PA_JSON_TYPE_DOUBLE:
+        case PA_JSON_TYPE_BOOL:
+        case PA_JSON_TYPE_NULL:
+            break;
+
+        case PA_JSON_TYPE_STRING:
+            pa_xfree(obj->string_value);
+            break;
+
+        case PA_JSON_TYPE_OBJECT:
+            pa_hashmap_free(obj->object_values);
+            break;
+
+        case PA_JSON_TYPE_ARRAY:
+            pa_idxset_free(obj->array_values, (pa_free_cb_t) pa_json_object_free);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_xfree(obj);
+}
+
+int pa_json_object_get_int(const pa_json_object *o) {
+    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT);
+    return o->int_value;
+}
+
+double pa_json_object_get_double(const pa_json_object *o) {
+    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_DOUBLE);
+    return o->double_value;
+}
+
+bool pa_json_object_get_bool(const pa_json_object *o) {
+    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_BOOL);
+    return o->bool_value;
+}
+
+const char* pa_json_object_get_string(const pa_json_object *o) {
+    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_STRING);
+    return o->string_value;
+}
+
+const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name) {
+    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+    return pa_hashmap_get(o->object_values, name);
+}
+
+int pa_json_object_get_array_length(const pa_json_object *o) {
+    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    return pa_idxset_size(o->array_values);
+}
+
+const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index) {
+    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    return pa_idxset_get_by_index(o->array_values, index);
+}
+
+bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2) {
+    int i;
+
+    if (pa_json_object_get_type(o1) != pa_json_object_get_type(o2))
+        return false;
+
+    switch (pa_json_object_get_type(o1)) {
+        case PA_JSON_TYPE_NULL:
+            return true;
+
+        case PA_JSON_TYPE_BOOL:
+            return o1->bool_value == o2->bool_value;
+
+        case PA_JSON_TYPE_INT:
+            return o1->int_value == o2->int_value;
+
+        case PA_JSON_TYPE_DOUBLE:
+            return PA_DOUBLE_IS_EQUAL(o1->double_value, o2->double_value);
+
+        case PA_JSON_TYPE_STRING:
+            return pa_streq(o1->string_value, o2->string_value);
+
+        case PA_JSON_TYPE_ARRAY:
+            if (pa_json_object_get_array_length(o1) != pa_json_object_get_array_length(o2))
+                return false;
+
+            for (i = 0; i < pa_json_object_get_array_length(o1); i++) {
+                if (!pa_json_object_equal(pa_json_object_get_array_member(o1, i),
+                            pa_json_object_get_array_member(o2, i)))
+                    return false;
+            }
+
+            return true;
+
+        case PA_JSON_TYPE_OBJECT: {
+            void *state;
+            const char *key;
+            const pa_json_object *v1, *v2;
+
+            if (pa_hashmap_size(o1->object_values) != pa_hashmap_size(o2->object_values))
+                return false;
+
+            PA_HASHMAP_FOREACH_KV(key, v1, o1->object_values, state) {
+                v2 = pa_json_object_get_object_member(o2, key);
+                if (!v2 || !pa_json_object_equal(v1, v2))
+                    return false;
+            }
+
+            return true;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+}
diff --git a/src/pulse/json.h b/src/pulse/json.h
new file mode 100644 (file)
index 0000000..7759bf2
--- /dev/null
@@ -0,0 +1,53 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2016 Arun Raghavan <mail@arunraghavan.net>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#define PA_DOUBLE_IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001)
+
+typedef enum {
+    PA_JSON_TYPE_INIT = 0,
+    PA_JSON_TYPE_NULL,
+    PA_JSON_TYPE_INT,
+    PA_JSON_TYPE_DOUBLE,
+    PA_JSON_TYPE_BOOL,
+    PA_JSON_TYPE_STRING,
+    PA_JSON_TYPE_ARRAY,
+    PA_JSON_TYPE_OBJECT,
+} pa_json_type;
+
+typedef struct pa_json_object pa_json_object;
+
+pa_json_object* pa_json_parse(const char *str);
+pa_json_type pa_json_object_get_type(const pa_json_object *obj);
+void pa_json_object_free(pa_json_object *obj);
+
+/* All pointer members that are returned are valid while the corresponding object is valid */
+
+int pa_json_object_get_int(const pa_json_object *o);
+double pa_json_object_get_double(const pa_json_object *o);
+bool pa_json_object_get_bool(const pa_json_object *o);
+const char* pa_json_object_get_string(const pa_json_object *o);
+
+const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name);
+
+int pa_json_object_get_array_length(const pa_json_object *o);
+const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index);
+
+bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2);
diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c
new file mode 100644 (file)
index 0000000..4ea0061
--- /dev/null
@@ -0,0 +1,75 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+
+#include "mainloop-api.h"
+
+struct once_info {
+    void (*callback)(pa_mainloop_api*m, void *userdata);
+    void *userdata;
+};
+
+static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+    struct once_info *i = userdata;
+
+    pa_assert(m);
+    pa_assert(i);
+
+    pa_assert(i->callback);
+    i->callback(m, i->userdata);
+
+    pa_assert(m->defer_free);
+    m->defer_free(e);
+}
+
+static void free_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+    struct once_info *i = userdata;
+
+    pa_assert(m);
+    pa_assert(i);
+    pa_xfree(i);
+}
+
+void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) {
+    struct once_info *i;
+    pa_defer_event *e;
+
+    pa_assert(m);
+    pa_assert(callback);
+
+    pa_init_i18n();
+
+    i = pa_xnew(struct once_info, 1);
+    i->callback = callback;
+    i->userdata = userdata;
+
+    pa_assert(m->defer_new);
+    pa_assert_se(e = m->defer_new(m, once_callback, i));
+    m->defer_set_destroy(e, free_callback);
+}
diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h
new file mode 100644 (file)
index 0000000..4d84731
--- /dev/null
@@ -0,0 +1,124 @@
+#ifndef foomainloopapihfoo
+#define foomainloopapihfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/time.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/version.h>
+
+/** \file
+ *
+ * Main loop abstraction layer. Both the PulseAudio core and the
+ * PulseAudio client library use a main loop abstraction layer. Due to
+ * this it is possible to embed PulseAudio into other
+ * applications easily. Two main loop implementations are
+ * currently available:
+ * \li A minimal implementation based on the C library's poll() function (See \ref mainloop.h)
+ * \li A wrapper around the GLIB main loop. Use this to embed PulseAudio into your GLIB/GTK+/GNOME programs (See \ref glib-mainloop.h)
+ *
+ * The structure pa_mainloop_api is used as vtable for the main loop abstraction.
+ *
+ * This mainloop abstraction layer has no direct support for UNIX signals. Generic, mainloop implementation agnostic support is available through \ref mainloop-signal.h.
+ * */
+
+PA_C_DECL_BEGIN
+
+/** An abstract mainloop API vtable */
+typedef struct pa_mainloop_api pa_mainloop_api;
+
+/** A bitmask for IO events */
+typedef enum pa_io_event_flags {
+    PA_IO_EVENT_NULL = 0,     /**< No event */
+    PA_IO_EVENT_INPUT = 1,    /**< Input event */
+    PA_IO_EVENT_OUTPUT = 2,   /**< Output event */
+    PA_IO_EVENT_HANGUP = 4,   /**< Hangup event */
+    PA_IO_EVENT_ERROR = 8     /**< Error event */
+} pa_io_event_flags_t;
+
+/** An opaque IO event source object */
+typedef struct pa_io_event pa_io_event;
+/** An IO event callback prototype \since 0.9.3 */
+typedef void (*pa_io_event_cb_t)(pa_mainloop_api*ea, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata);
+/** A IO event destroy callback prototype \since 0.9.3 */
+typedef void (*pa_io_event_destroy_cb_t)(pa_mainloop_api*a, pa_io_event *e, void *userdata);
+
+/** An opaque timer event source object */
+typedef struct pa_time_event pa_time_event;
+/** A time event callback prototype \since 0.9.3 */
+typedef void (*pa_time_event_cb_t)(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata);
+/** A time event destroy callback prototype \since 0.9.3 */
+typedef void (*pa_time_event_destroy_cb_t)(pa_mainloop_api*a, pa_time_event *e, void *userdata);
+
+/** An opaque deferred event source object. Events of this type are triggered once in every main loop iteration */
+typedef struct pa_defer_event pa_defer_event;
+/** A defer event callback prototype \since 0.9.3 */
+typedef void (*pa_defer_event_cb_t)(pa_mainloop_api*a, pa_defer_event* e, void *userdata);
+/** A defer event destroy callback prototype \since 0.9.3 */
+typedef void (*pa_defer_event_destroy_cb_t)(pa_mainloop_api*a, pa_defer_event *e, void *userdata);
+
+/** An abstract mainloop API vtable */
+struct pa_mainloop_api {
+    /** A pointer to some private, arbitrary data of the main loop implementation */
+    void *userdata;
+
+    /** Create a new IO event source object */
+    pa_io_event* (*io_new)(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata);
+    /** Enable or disable IO events on this object */
+    void (*io_enable)(pa_io_event* e, pa_io_event_flags_t events);
+    /** Free a IO event source object */
+    void (*io_free)(pa_io_event* e);
+    /** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */
+    void (*io_set_destroy)(pa_io_event *e, pa_io_event_destroy_cb_t cb);
+
+    /** Create a new timer event source object for the specified Unix time */
+    pa_time_event* (*time_new)(pa_mainloop_api*a, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata);
+    /** Restart a running or expired timer event source with a new Unix time */
+    void (*time_restart)(pa_time_event* e, const struct timeval *tv);
+    /** Free a deferred timer event source object */
+    void (*time_free)(pa_time_event* e);
+    /** Set a function that is called when the timer event source is destroyed. Use this to free the userdata argument if required */
+    void (*time_set_destroy)(pa_time_event *e, pa_time_event_destroy_cb_t cb);
+
+    /** Create a new deferred event source object */
+    pa_defer_event* (*defer_new)(pa_mainloop_api*a, pa_defer_event_cb_t cb, void *userdata);
+    /** Enable or disable a deferred event source temporarily */
+    void (*defer_enable)(pa_defer_event* e, int b);
+    /** Free a deferred event source object */
+    void (*defer_free)(pa_defer_event* e);
+    /** Set a function that is called when the deferred event source is destroyed. Use this to free the userdata argument if required */
+    void (*defer_set_destroy)(pa_defer_event *e, pa_defer_event_destroy_cb_t cb);
+
+    /** Exit the main loop and return the specified retval*/
+    void (*quit)(pa_mainloop_api*a, int retval);
+};
+
+/** Run the specified callback function once from the main loop using an
+ * anonymous defer event. If the mainloop runs in a different thread, you need
+ * to follow the mainloop implementation's rules regarding how to safely create
+ * defer events. In particular, if you're using \ref pa_threaded_mainloop, you
+ * must lock the mainloop before calling this function. */
+void pa_mainloop_api_once(pa_mainloop_api*m, void (*callback)(pa_mainloop_api*m, void *userdata), void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c
new file mode 100644 (file)
index 0000000..f196ad3
--- /dev/null
@@ -0,0 +1,224 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "mainloop-signal.h"
+
+struct pa_signal_event {
+    int sig;
+#ifdef HAVE_SIGACTION
+    struct sigaction saved_sigaction;
+#else
+    void (*saved_handler)(int sig);
+#endif
+    void *userdata;
+    pa_signal_cb_t callback;
+    pa_signal_destroy_cb_t destroy_callback;
+    pa_signal_event *previous, *next;
+};
+
+static pa_mainloop_api *api = NULL;
+static int signal_pipe[2] = { -1, -1 };
+static pa_io_event* io_event = NULL;
+static pa_signal_event *signals = NULL;
+
+static void signal_handler(int sig) {
+    int saved_errno;
+
+    saved_errno = errno;
+
+#ifndef HAVE_SIGACTION
+    signal(sig, signal_handler);
+#endif
+
+    /* XXX: If writing fails, there's nothing we can do? */
+    (void) pa_write(signal_pipe[1], &sig, sizeof(sig), NULL);
+
+    errno = saved_errno;
+}
+
+static void dispatch(pa_mainloop_api*a, int sig) {
+    pa_signal_event *s;
+
+    for (s = signals; s; s = s->next)
+        if (s->sig == sig) {
+            pa_assert(s->callback);
+            s->callback(a, s, sig, s->userdata);
+            break;
+        }
+}
+
+static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata) {
+    ssize_t r;
+    int sig;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(f == PA_IO_EVENT_INPUT);
+    pa_assert(e == io_event);
+    pa_assert(fd == signal_pipe[0]);
+
+    if ((r = pa_read(signal_pipe[0], &sig, sizeof(sig), NULL)) < 0) {
+        if (errno == EAGAIN)
+            return;
+
+        pa_log("read(): %s", pa_cstrerror(errno));
+        return;
+    }
+
+    if (r != sizeof(sig)) {
+        pa_log("short read()");
+        return;
+    }
+
+    dispatch(a, sig);
+}
+
+int pa_signal_init(pa_mainloop_api *a) {
+
+    pa_assert(a);
+    pa_assert(!api);
+    pa_assert(signal_pipe[0] == -1);
+    pa_assert(signal_pipe[1] == -1);
+    pa_assert(!io_event);
+
+    if (pa_pipe_cloexec(signal_pipe) < 0) {
+        pa_log("pipe(): %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_make_fd_nonblock(signal_pipe[0]);
+    pa_make_fd_nonblock(signal_pipe[1]);
+
+    api = a;
+
+    pa_assert_se(io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL));
+
+    return 0;
+}
+
+void pa_signal_done(void) {
+    while (signals)
+        pa_signal_free(signals);
+
+    if (io_event) {
+        pa_assert(api);
+        api->io_free(io_event);
+        io_event = NULL;
+    }
+
+    pa_close_pipe(signal_pipe);
+
+    api = NULL;
+}
+
+pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t _callback, void *userdata) {
+    pa_signal_event *e = NULL;
+
+#ifdef HAVE_SIGACTION
+    struct sigaction sa;
+#endif
+
+    pa_assert(sig > 0);
+    pa_assert(_callback);
+
+    pa_init_i18n();
+
+    for (e = signals; e; e = e->next)
+        if (e->sig == sig)
+            return NULL;
+
+    e = pa_xnew(pa_signal_event, 1);
+    e->sig = sig;
+    e->callback = _callback;
+    e->userdata = userdata;
+    e->destroy_callback = NULL;
+
+#ifdef HAVE_SIGACTION
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = signal_handler;
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = SA_RESTART;
+
+    if (sigaction(sig, &sa, &e->saved_sigaction) < 0)
+#else
+    if ((e->saved_handler = signal(sig, signal_handler)) == SIG_ERR)
+#endif
+        goto fail;
+
+    e->previous = NULL;
+    e->next = signals;
+    signals = e;
+
+    return e;
+fail:
+    pa_xfree(e);
+    return NULL;
+}
+
+void pa_signal_free(pa_signal_event *e) {
+    pa_assert(e);
+
+    if (e->next)
+        e->next->previous = e->previous;
+    if (e->previous)
+        e->previous->next = e->next;
+    else
+        signals = e->next;
+
+#ifdef HAVE_SIGACTION
+    pa_assert_se(sigaction(e->sig, &e->saved_sigaction, NULL) == 0);
+#else
+    pa_assert_se(signal(e->sig, e->saved_handler) == signal_handler);
+#endif
+
+    if (e->destroy_callback)
+        e->destroy_callback(api, e, e->userdata);
+
+    pa_xfree(e);
+}
+
+void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t _callback) {
+    pa_assert(e);
+
+    e->destroy_callback = _callback;
+}
diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h
new file mode 100644 (file)
index 0000000..0a7bdeb
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef foomainloopsignalhfoo
+#define foomainloopsignalhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+
+PA_C_DECL_BEGIN
+
+/** \file
+ * UNIX signal support for main loops. In contrast to other
+ * main loop event sources such as timer and IO events, UNIX signal
+ * support requires modification of the global process
+ * environment. Due to this the generic main loop abstraction layer as
+ * defined in \ref mainloop-api.h doesn't have direct support for UNIX
+ * signals. However, you may hook signal support into an abstract main loop via the routines defined herein.
+ */
+
+/** An opaque UNIX signal event source object */
+typedef struct pa_signal_event pa_signal_event;
+
+/** Callback prototype for signal events */
+typedef void (*pa_signal_cb_t) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata);
+
+/** Destroy callback prototype for signal events */
+typedef void (*pa_signal_destroy_cb_t) (pa_mainloop_api *api, pa_signal_event*e, void *userdata);
+
+/** Initialize the UNIX signal subsystem and bind it to the specified main loop */
+int pa_signal_init(pa_mainloop_api *api);
+
+/** Cleanup the signal subsystem */
+void pa_signal_done(void);
+
+/** Create a new UNIX signal event source object */
+pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t callback, void *userdata);
+
+/** Free a UNIX signal event source object */
+void pa_signal_free(pa_signal_event *e);
+
+/** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */
+void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t callback);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c
new file mode 100644 (file)
index 0000000..aeb55d9
--- /dev/null
@@ -0,0 +1,978 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifndef HAVE_PIPE
+#include <pulsecore/pipe.h>
+#endif
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/poll.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/macro.h>
+
+#include "mainloop.h"
+#include "internal.h"
+
+struct pa_io_event {
+    pa_mainloop *mainloop;
+    bool dead:1;
+
+    int fd;
+    pa_io_event_flags_t events;
+    struct pollfd *pollfd;
+
+    pa_io_event_cb_t callback;
+    void *userdata;
+    pa_io_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_io_event);
+};
+
+struct pa_time_event {
+    pa_mainloop *mainloop;
+    bool dead:1;
+
+    bool enabled:1;
+    bool use_rtclock:1;
+    pa_usec_t time;
+
+    pa_time_event_cb_t callback;
+    void *userdata;
+    pa_time_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_time_event);
+};
+
+struct pa_defer_event {
+    pa_mainloop *mainloop;
+    bool dead:1;
+
+    bool enabled:1;
+
+    pa_defer_event_cb_t callback;
+    void *userdata;
+    pa_defer_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_defer_event);
+};
+
+struct pa_mainloop {
+    PA_LLIST_HEAD(pa_io_event, io_events);
+    PA_LLIST_HEAD(pa_time_event, time_events);
+    PA_LLIST_HEAD(pa_defer_event, defer_events);
+
+    unsigned n_enabled_defer_events, n_enabled_time_events, n_io_events;
+    unsigned io_events_please_scan, time_events_please_scan, defer_events_please_scan;
+
+    bool rebuild_pollfds:1;
+    struct pollfd *pollfds;
+    unsigned max_pollfds, n_pollfds;
+
+    pa_usec_t prepared_timeout;
+    pa_time_event *cached_next_time_event;
+
+    pa_mainloop_api api;
+
+    int retval;
+    bool quit:1;
+
+    int wakeup_pipe[2];
+    int wakeup_pipe_type;
+
+    enum {
+        STATE_PASSIVE,
+        STATE_PREPARED,
+        STATE_POLLING,
+        STATE_POLLED,
+        STATE_QUIT
+    } state;
+
+    pa_poll_func poll_func;
+    void *poll_func_userdata;
+    int poll_func_ret;
+};
+
+static short map_flags_to_libc(pa_io_event_flags_t flags) {
+    return (short)
+        ((flags & PA_IO_EVENT_INPUT ? POLLIN : 0) |
+         (flags & PA_IO_EVENT_OUTPUT ? POLLOUT : 0) |
+         (flags & PA_IO_EVENT_ERROR ? POLLERR : 0) |
+         (flags & PA_IO_EVENT_HANGUP ? POLLHUP : 0));
+}
+
+static pa_io_event_flags_t map_flags_from_libc(short flags) {
+    return
+        (flags & POLLIN ? PA_IO_EVENT_INPUT : 0) |
+        (flags & POLLOUT ? PA_IO_EVENT_OUTPUT : 0) |
+        (flags & POLLERR ? PA_IO_EVENT_ERROR : 0) |
+        (flags & POLLHUP ? PA_IO_EVENT_HANGUP : 0);
+}
+
+/* IO events */
+static pa_io_event* mainloop_io_new(
+        pa_mainloop_api *a,
+        int fd,
+        pa_io_event_flags_t events,
+        pa_io_event_cb_t callback,
+        void *userdata) {
+
+    pa_mainloop *m;
+    pa_io_event *e;
+
+    pa_assert(a);
+    pa_assert(a->userdata);
+    pa_assert(fd >= 0);
+    pa_assert(callback);
+
+    m = a->userdata;
+    pa_assert(a == &m->api);
+
+    e = pa_xnew0(pa_io_event, 1);
+    e->mainloop = m;
+
+    e->fd = fd;
+    e->events = events;
+
+    e->callback = callback;
+    e->userdata = userdata;
+
+    PA_LLIST_PREPEND(pa_io_event, m->io_events, e);
+    m->rebuild_pollfds = true;
+    m->n_io_events ++;
+
+    pa_mainloop_wakeup(m);
+
+    return e;
+}
+
+static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    if (e->events == events)
+        return;
+
+    e->events = events;
+
+    if (e->pollfd)
+        e->pollfd->events = map_flags_to_libc(events);
+    else
+        e->mainloop->rebuild_pollfds = true;
+
+    pa_mainloop_wakeup(e->mainloop);
+}
+
+static void mainloop_io_free(pa_io_event *e) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    e->dead = true;
+    e->mainloop->io_events_please_scan ++;
+
+    e->mainloop->n_io_events --;
+    e->mainloop->rebuild_pollfds = true;
+
+    pa_mainloop_wakeup(e->mainloop);
+}
+
+static void mainloop_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t callback) {
+    pa_assert(e);
+
+    e->destroy_callback = callback;
+}
+
+/* Defer events */
+static pa_defer_event* mainloop_defer_new(
+        pa_mainloop_api *a,
+        pa_defer_event_cb_t callback,
+        void *userdata) {
+
+    pa_mainloop *m;
+    pa_defer_event *e;
+
+    pa_assert(a);
+    pa_assert(a->userdata);
+    pa_assert(callback);
+
+    m = a->userdata;
+    pa_assert(a == &m->api);
+
+    e = pa_xnew0(pa_defer_event, 1);
+    e->mainloop = m;
+
+    e->enabled = true;
+    m->n_enabled_defer_events++;
+
+    e->callback = callback;
+    e->userdata = userdata;
+
+    PA_LLIST_PREPEND(pa_defer_event, m->defer_events, e);
+
+    pa_mainloop_wakeup(e->mainloop);
+
+    return e;
+}
+
+static void mainloop_defer_enable(pa_defer_event *e, int b) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    if (e->enabled && !b) {
+        pa_assert(e->mainloop->n_enabled_defer_events > 0);
+        e->mainloop->n_enabled_defer_events--;
+    } else if (!e->enabled && b) {
+        e->mainloop->n_enabled_defer_events++;
+        pa_mainloop_wakeup(e->mainloop);
+    }
+
+    e->enabled = b;
+}
+
+static void mainloop_defer_free(pa_defer_event *e) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    e->dead = true;
+    e->mainloop->defer_events_please_scan ++;
+
+    if (e->enabled) {
+        pa_assert(e->mainloop->n_enabled_defer_events > 0);
+        e->mainloop->n_enabled_defer_events--;
+        e->enabled = false;
+    }
+}
+
+static void mainloop_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t callback) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    e->destroy_callback = callback;
+}
+
+/* Time events */
+static pa_usec_t make_rt(const struct timeval *tv, bool *use_rtclock) {
+    struct timeval ttv;
+
+    if (!tv) {
+        *use_rtclock = false;
+        return PA_USEC_INVALID;
+    }
+
+    ttv = *tv;
+    *use_rtclock = !!(ttv.tv_usec & PA_TIMEVAL_RTCLOCK);
+
+    if (*use_rtclock)
+        ttv.tv_usec &= ~PA_TIMEVAL_RTCLOCK;
+    else
+        pa_rtclock_from_wallclock(&ttv);
+
+    return pa_timeval_load(&ttv);
+}
+
+static pa_time_event* mainloop_time_new(
+        pa_mainloop_api *a,
+        const struct timeval *tv,
+        pa_time_event_cb_t callback,
+        void *userdata) {
+
+    pa_mainloop *m;
+    pa_time_event *e;
+    pa_usec_t t;
+    bool use_rtclock = false;
+
+    pa_assert(a);
+    pa_assert(a->userdata);
+    pa_assert(callback);
+
+    t = make_rt(tv, &use_rtclock);
+
+    m = a->userdata;
+    pa_assert(a == &m->api);
+
+    e = pa_xnew0(pa_time_event, 1);
+    e->mainloop = m;
+
+    if ((e->enabled = (t != PA_USEC_INVALID))) {
+        e->time = t;
+        e->use_rtclock = use_rtclock;
+
+        m->n_enabled_time_events++;
+
+        if (m->cached_next_time_event) {
+            pa_assert(m->cached_next_time_event->enabled);
+
+            if (t < m->cached_next_time_event->time)
+                m->cached_next_time_event = e;
+        }
+    }
+
+    e->callback = callback;
+    e->userdata = userdata;
+
+    PA_LLIST_PREPEND(pa_time_event, m->time_events, e);
+
+    if (e->enabled)
+        pa_mainloop_wakeup(m);
+
+    return e;
+}
+
+static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) {
+    bool valid;
+    pa_usec_t t;
+    bool use_rtclock = false;
+
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    t = make_rt(tv, &use_rtclock);
+
+    valid = (t != PA_USEC_INVALID);
+    if (e->enabled && !valid) {
+        pa_assert(e->mainloop->n_enabled_time_events > 0);
+        e->mainloop->n_enabled_time_events--;
+    } else if (!e->enabled && valid)
+        e->mainloop->n_enabled_time_events++;
+
+    if ((e->enabled = valid)) {
+        e->time = t;
+        e->use_rtclock = use_rtclock;
+        pa_mainloop_wakeup(e->mainloop);
+    }
+
+    if (e->mainloop->cached_next_time_event == e)
+        e->mainloop->cached_next_time_event = NULL;
+
+    if (e->mainloop->cached_next_time_event && e->enabled) {
+        pa_assert(e->mainloop->cached_next_time_event->enabled);
+
+        if (t < e->mainloop->cached_next_time_event->time)
+            e->mainloop->cached_next_time_event = e;
+    }
+}
+
+static void mainloop_time_free(pa_time_event *e) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    e->dead = true;
+    e->mainloop->time_events_please_scan ++;
+
+    if (e->enabled) {
+        pa_assert(e->mainloop->n_enabled_time_events > 0);
+        e->mainloop->n_enabled_time_events--;
+        e->enabled = false;
+    }
+
+    if (e->mainloop->cached_next_time_event == e)
+        e->mainloop->cached_next_time_event = NULL;
+
+    /* no wakeup needed here. Think about it! */
+}
+
+static void mainloop_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t callback) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    e->destroy_callback = callback;
+}
+
+/* quit() */
+
+static void mainloop_quit(pa_mainloop_api *a, int retval) {
+    pa_mainloop *m;
+
+    pa_assert(a);
+    pa_assert(a->userdata);
+    m = a->userdata;
+    pa_assert(a == &m->api);
+
+    pa_mainloop_quit(m, retval);
+}
+
+static const pa_mainloop_api vtable = {
+    .userdata = NULL,
+
+    .io_new = mainloop_io_new,
+    .io_enable = mainloop_io_enable,
+    .io_free = mainloop_io_free,
+    .io_set_destroy = mainloop_io_set_destroy,
+
+    .time_new = mainloop_time_new,
+    .time_restart = mainloop_time_restart,
+    .time_free = mainloop_time_free,
+    .time_set_destroy = mainloop_time_set_destroy,
+
+    .defer_new = mainloop_defer_new,
+    .defer_enable = mainloop_defer_enable,
+    .defer_free = mainloop_defer_free,
+    .defer_set_destroy = mainloop_defer_set_destroy,
+
+    .quit = mainloop_quit,
+};
+
+pa_mainloop *pa_mainloop_new(void) {
+    pa_mainloop *m;
+
+    pa_init_i18n();
+
+    m = pa_xnew0(pa_mainloop, 1);
+
+    if (pa_pipe_cloexec(m->wakeup_pipe) < 0) {
+        pa_log_error("ERROR: cannot create wakeup pipe");
+        pa_xfree(m);
+        return NULL;
+    }
+
+    pa_make_fd_nonblock(m->wakeup_pipe[0]);
+    pa_make_fd_nonblock(m->wakeup_pipe[1]);
+
+    m->rebuild_pollfds = true;
+
+    m->api = vtable;
+    m->api.userdata = m;
+
+    m->state = STATE_PASSIVE;
+
+    m->poll_func_ret = -1;
+
+    return m;
+}
+
+static void cleanup_io_events(pa_mainloop *m, bool force) {
+    pa_io_event *e, *n;
+
+    PA_LLIST_FOREACH_SAFE(e, n, m->io_events) {
+
+        if (!force && m->io_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_io_event, m->io_events, e);
+
+            if (e->dead) {
+                pa_assert(m->io_events_please_scan > 0);
+                m->io_events_please_scan--;
+            }
+
+            if (e->destroy_callback)
+                e->destroy_callback(&m->api, e, e->userdata);
+
+            pa_xfree(e);
+
+            m->rebuild_pollfds = true;
+        }
+    }
+
+    pa_assert(m->io_events_please_scan == 0);
+}
+
+static void cleanup_time_events(pa_mainloop *m, bool force) {
+    pa_time_event *e, *n;
+
+    PA_LLIST_FOREACH_SAFE(e, n, m->time_events) {
+
+        if (!force && m->time_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_time_event, m->time_events, e);
+
+            if (e->dead) {
+                pa_assert(m->time_events_please_scan > 0);
+                m->time_events_please_scan--;
+            }
+
+            if (!e->dead && e->enabled) {
+                pa_assert(m->n_enabled_time_events > 0);
+                m->n_enabled_time_events--;
+                e->enabled = false;
+            }
+
+            if (e->destroy_callback)
+                e->destroy_callback(&m->api, e, e->userdata);
+
+            pa_xfree(e);
+        }
+    }
+
+    pa_assert(m->time_events_please_scan == 0);
+}
+
+static void cleanup_defer_events(pa_mainloop *m, bool force) {
+    pa_defer_event *e, *n;
+
+    PA_LLIST_FOREACH_SAFE(e, n, m->defer_events) {
+
+        if (!force && m->defer_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_defer_event, m->defer_events, e);
+
+            if (e->dead) {
+                pa_assert(m->defer_events_please_scan > 0);
+                m->defer_events_please_scan--;
+            }
+
+            if (!e->dead && e->enabled) {
+                pa_assert(m->n_enabled_defer_events > 0);
+                m->n_enabled_defer_events--;
+                e->enabled = false;
+            }
+
+            if (e->destroy_callback)
+                e->destroy_callback(&m->api, e, e->userdata);
+
+            pa_xfree(e);
+        }
+    }
+
+    pa_assert(m->defer_events_please_scan == 0);
+}
+
+void pa_mainloop_free(pa_mainloop *m) {
+    pa_assert(m);
+
+    cleanup_io_events(m, true);
+    cleanup_defer_events(m, true);
+    cleanup_time_events(m, true);
+
+    pa_xfree(m->pollfds);
+
+    pa_close_pipe(m->wakeup_pipe);
+
+    pa_xfree(m);
+}
+
+static void scan_dead(pa_mainloop *m) {
+    pa_assert(m);
+
+    if (m->io_events_please_scan)
+        cleanup_io_events(m, false);
+
+    if (m->time_events_please_scan)
+        cleanup_time_events(m, false);
+
+    if (m->defer_events_please_scan)
+        cleanup_defer_events(m, false);
+}
+
+static void rebuild_pollfds(pa_mainloop *m) {
+    pa_io_event*e;
+    struct pollfd *p;
+    unsigned l;
+
+    l = m->n_io_events + 1;
+    if (m->max_pollfds < l) {
+        l *= 2;
+        m->pollfds = pa_xrealloc(m->pollfds, sizeof(struct pollfd)*l);
+        m->max_pollfds = l;
+    }
+
+    m->n_pollfds = 0;
+    p = m->pollfds;
+
+    m->pollfds[0].fd = m->wakeup_pipe[0];
+    m->pollfds[0].events = POLLIN;
+    m->pollfds[0].revents = 0;
+    p++;
+    m->n_pollfds++;
+
+    PA_LLIST_FOREACH(e, m->io_events) {
+        if (e->dead) {
+            e->pollfd = NULL;
+            continue;
+        }
+
+        e->pollfd = p;
+        p->fd = e->fd;
+        p->events = map_flags_to_libc(e->events);
+        p->revents = 0;
+
+        p++;
+        m->n_pollfds++;
+    }
+
+    m->rebuild_pollfds = false;
+}
+
+static unsigned dispatch_pollfds(pa_mainloop *m) {
+    pa_io_event *e;
+    unsigned r = 0, k;
+
+    pa_assert(m->poll_func_ret > 0);
+
+    k = m->poll_func_ret;
+
+    PA_LLIST_FOREACH(e, m->io_events) {
+
+        if (k <= 0 || m->quit)
+            break;
+
+        if (e->dead || !e->pollfd || !e->pollfd->revents)
+            continue;
+
+        pa_assert(e->pollfd->fd == e->fd);
+        pa_assert(e->callback);
+
+        e->callback(&m->api, e, e->fd, map_flags_from_libc(e->pollfd->revents), e->userdata);
+        e->pollfd->revents = 0;
+        r++;
+        k--;
+    }
+
+    return r;
+}
+
+static unsigned dispatch_defer(pa_mainloop *m) {
+    pa_defer_event *e;
+    unsigned r = 0;
+
+    if (m->n_enabled_defer_events <= 0)
+        return 0;
+
+    PA_LLIST_FOREACH(e, m->defer_events) {
+
+        if (m->quit)
+            break;
+
+        if (e->dead || !e->enabled)
+            continue;
+
+        pa_assert(e->callback);
+        e->callback(&m->api, e, e->userdata);
+        r++;
+    }
+
+    return r;
+}
+
+static pa_time_event* find_next_time_event(pa_mainloop *m) {
+    pa_time_event *t, *n = NULL;
+    pa_assert(m);
+
+    if (m->cached_next_time_event)
+        return m->cached_next_time_event;
+
+    PA_LLIST_FOREACH(t, m->time_events) {
+
+        if (t->dead || !t->enabled)
+            continue;
+
+        if (!n || t->time < n->time) {
+            n = t;
+
+            /* Shortcut for time == 0 */
+            if (n->time == 0)
+                break;
+        }
+    }
+
+    m->cached_next_time_event = n;
+    return n;
+}
+
+static pa_usec_t calc_next_timeout(pa_mainloop *m) {
+    pa_time_event *t;
+    pa_usec_t clock_now;
+
+    if (m->n_enabled_time_events <= 0)
+        return PA_USEC_INVALID;
+
+    pa_assert_se(t = find_next_time_event(m));
+
+    if (t->time <= 0)
+        return 0;
+
+    clock_now = pa_rtclock_now();
+
+    if (t->time <= clock_now)
+        return 0;
+
+    return t->time - clock_now;
+}
+
+static unsigned dispatch_timeout(pa_mainloop *m) {
+    pa_time_event *e;
+    pa_usec_t now;
+    unsigned r = 0;
+    pa_assert(m);
+
+    if (m->n_enabled_time_events <= 0)
+        return 0;
+
+    now = pa_rtclock_now();
+
+    PA_LLIST_FOREACH(e, m->time_events) {
+
+        if (m->quit)
+            break;
+
+        if (e->dead || !e->enabled)
+            continue;
+
+        if (e->time <= now) {
+            struct timeval tv;
+            pa_assert(e->callback);
+
+            /* Disable time event */
+            mainloop_time_restart(e, NULL);
+
+            e->callback(&m->api, e, pa_timeval_rtstore(&tv, e->time, e->use_rtclock), e->userdata);
+
+            r++;
+        }
+    }
+
+    return r;
+}
+
+void pa_mainloop_wakeup(pa_mainloop *m) {
+    char c = 'W';
+    pa_assert(m);
+
+    if (pa_write(m->wakeup_pipe[1], &c, sizeof(c), &m->wakeup_pipe_type) < 0)
+        /* Not many options for recovering from the error. Let's at least log something. */
+        pa_log("pa_write() failed while trying to wake up the mainloop: %s", pa_cstrerror(errno));
+}
+
+static void clear_wakeup(pa_mainloop *m) {
+    char c[10];
+
+    pa_assert(m);
+
+    while (pa_read(m->wakeup_pipe[0], &c, sizeof(c), &m->wakeup_pipe_type) == sizeof(c))
+        ;
+}
+
+int pa_mainloop_prepare(pa_mainloop *m, int timeout) {
+    pa_assert(m);
+    pa_assert(m->state == STATE_PASSIVE);
+
+    clear_wakeup(m);
+    scan_dead(m);
+
+    if (m->quit)
+        goto quit;
+
+    if (m->n_enabled_defer_events <= 0) {
+
+        if (m->rebuild_pollfds)
+            rebuild_pollfds(m);
+
+        m->prepared_timeout = calc_next_timeout(m);
+        if (timeout >= 0) {
+            uint64_t u = (uint64_t) timeout * PA_USEC_PER_MSEC;
+
+            if (u < m->prepared_timeout || m->prepared_timeout == PA_USEC_INVALID)
+                m->prepared_timeout = timeout;
+        }
+    }
+
+    m->state = STATE_PREPARED;
+    return 0;
+
+quit:
+    m->state = STATE_QUIT;
+    return -2;
+}
+
+static int usec_to_timeout(pa_usec_t u) {
+    int timeout;
+
+    if (u == PA_USEC_INVALID)
+        return -1;
+
+    timeout = (u + PA_USEC_PER_MSEC - 1) / PA_USEC_PER_MSEC;
+    pa_assert(timeout >= 0);
+
+    return timeout;
+}
+
+int pa_mainloop_poll(pa_mainloop *m) {
+    pa_assert(m);
+    pa_assert(m->state == STATE_PREPARED);
+
+    if (m->quit)
+        goto quit;
+
+    m->state = STATE_POLLING;
+
+    if (m->n_enabled_defer_events)
+        m->poll_func_ret = 0;
+    else {
+        pa_assert(!m->rebuild_pollfds);
+
+        if (m->poll_func)
+            m->poll_func_ret = m->poll_func(
+                    m->pollfds, m->n_pollfds,
+                    usec_to_timeout(m->prepared_timeout),
+                    m->poll_func_userdata);
+        else {
+#ifdef HAVE_PPOLL
+            struct timespec ts;
+
+            m->poll_func_ret = ppoll(
+                    m->pollfds, m->n_pollfds,
+                    m->prepared_timeout == PA_USEC_INVALID ? NULL : pa_timespec_store(&ts, m->prepared_timeout),
+                    NULL);
+#else
+            m->poll_func_ret = pa_poll(
+                    m->pollfds, m->n_pollfds,
+                    usec_to_timeout(m->prepared_timeout));
+#endif
+        }
+
+        if (m->poll_func_ret < 0) {
+            if (errno == EINTR)
+                m->poll_func_ret = 0;
+            else
+                pa_log("poll(): %s", pa_cstrerror(errno));
+        }
+    }
+
+    m->state = m->poll_func_ret < 0 ? STATE_PASSIVE : STATE_POLLED;
+    return m->poll_func_ret;
+
+quit:
+    m->state = STATE_QUIT;
+    return -2;
+}
+
+int pa_mainloop_dispatch(pa_mainloop *m) {
+    unsigned dispatched = 0;
+
+    pa_assert(m);
+    pa_assert(m->state == STATE_POLLED);
+
+    if (m->quit)
+        goto quit;
+
+    if (m->n_enabled_defer_events)
+        dispatched += dispatch_defer(m);
+    else {
+        if (m->n_enabled_time_events)
+            dispatched += dispatch_timeout(m);
+
+        if (m->quit)
+            goto quit;
+
+        if (m->poll_func_ret > 0)
+            dispatched += dispatch_pollfds(m);
+    }
+
+    if (m->quit)
+        goto quit;
+
+    m->state = STATE_PASSIVE;
+
+    return (int) dispatched;
+
+quit:
+    m->state = STATE_QUIT;
+    return -2;
+}
+
+int pa_mainloop_get_retval(pa_mainloop *m) {
+    pa_assert(m);
+
+    return m->retval;
+}
+
+int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {
+    int r;
+    pa_assert(m);
+
+    if ((r = pa_mainloop_prepare(m, block ? -1 : 0)) < 0)
+        goto quit;
+
+    if ((r = pa_mainloop_poll(m)) < 0)
+        goto quit;
+
+    if ((r = pa_mainloop_dispatch(m)) < 0)
+        goto quit;
+
+    return r;
+
+quit:
+
+    if ((r == -2) && retval)
+        *retval = pa_mainloop_get_retval(m);
+    return r;
+}
+
+int pa_mainloop_run(pa_mainloop *m, int *retval) {
+    int r;
+
+    while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0)
+        ;
+
+    if (r == -2)
+        return 1;
+    else
+        return -1;
+}
+
+void pa_mainloop_quit(pa_mainloop *m, int retval) {
+    pa_assert(m);
+
+    m->quit = true;
+    m->retval = retval;
+    pa_mainloop_wakeup(m);
+}
+
+pa_mainloop_api* pa_mainloop_get_api(pa_mainloop *m) {
+    pa_assert(m);
+
+    return &m->api;
+}
+
+void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) {
+    pa_assert(m);
+
+    m->poll_func = poll_func;
+    m->poll_func_userdata = userdata;
+}
+
+bool pa_mainloop_is_our_api(pa_mainloop_api *m) {
+    pa_assert(m);
+
+    return m->io_new == mainloop_io_new;
+}
diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h
new file mode 100644 (file)
index 0000000..6e2ca5f
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef foomainloophfoo
+#define foomainloophfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+
+PA_C_DECL_BEGIN
+
+struct pollfd;
+
+/** \page mainloop Main Loop
+ *
+ * \section overv_sec Overview
+ *
+ * The built-in main loop implementation is based on the poll() system call.
+ * It supports the functions defined in the main loop abstraction and very
+ * little else.
+ *
+ * The main loop is created using pa_mainloop_new() and destroyed using
+ * pa_mainloop_free(). To get access to the main loop abstraction,
+ * pa_mainloop_get_api() is used.
+ *
+ * \section iter_sec Iteration
+ *
+ * The main loop is designed around the concept of iterations. Each iteration
+ * consists of three steps that repeat during the application's entire
+ * lifetime:
+ *
+ * -# Prepare - Build a list of file descriptors
+ *               that need to be monitored and calculate the next timeout.
+ * -# Poll - Execute the actual poll() system call.
+ * -# Dispatch - Dispatch any events that have fired.
+ *
+ * When using the main loop, the application can either execute each
+ * iteration, one at a time, using pa_mainloop_iterate(), or let the library
+ * iterate automatically using pa_mainloop_run().
+ *
+ * \section thread_sec Threads
+ *
+ * The main loop functions are designed to be thread safe, but the objects
+ * are not. What this means is that multiple main loops can be used, but only
+ * one object per thread.
+ *
+ */
+
+/** \file
+ *
+ * A minimal main loop implementation based on the C library's poll()
+ * function. Using the routines defined herein you may create a simple
+ * main loop supporting the generic main loop abstraction layer as
+ * defined in \ref mainloop-api.h. This implementation is thread safe
+ * as long as you access the main loop object from a single thread only.
+ *
+ * See also \subpage mainloop
+ */
+
+/** An opaque main loop object */
+typedef struct pa_mainloop pa_mainloop;
+
+/** Allocate a new main loop object */
+pa_mainloop *pa_mainloop_new(void);
+
+/** Free a main loop object */
+void pa_mainloop_free(pa_mainloop* m);
+
+/** Prepare for a single iteration of the main loop. Returns a negative value
+on error or exit request. timeout specifies a maximum timeout for the subsequent
+poll, or -1 for blocking behaviour. .*/
+int pa_mainloop_prepare(pa_mainloop *m, int timeout);
+
+/** Execute the previously prepared poll. Returns a negative value on error.*/
+int pa_mainloop_poll(pa_mainloop *m);
+
+/** Dispatch timeout, io and deferred events from the previously executed poll. Returns
+a negative value on error. On success returns the number of source dispatched. */
+int pa_mainloop_dispatch(pa_mainloop *m);
+
+/** Return the return value as specified with the main loop's quit() routine. */
+int pa_mainloop_get_retval(pa_mainloop *m);
+
+/** Run a single iteration of the main loop. This is a convenience function
+for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch().
+Returns a negative value on error or exit request. If block is nonzero,
+block for events if none are queued. Optionally return the return value as
+specified with the main loop's quit() routine in the integer variable retval points
+to. On success returns the number of sources dispatched in this iteration. */
+int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval);
+
+/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
+int pa_mainloop_run(pa_mainloop *m, int *retval);
+
+/** Return the abstract main loop abstraction layer vtable for this
+    main loop. No need to free the API as it is owned by the loop
+    and is destroyed when the loop is freed. */
+pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m);
+
+/** Shutdown the main loop with the specified return value */
+void pa_mainloop_quit(pa_mainloop *m, int retval);
+
+/** Interrupt a running poll (for threaded systems) */
+void pa_mainloop_wakeup(pa_mainloop *m);
+
+/** Generic prototype of a poll() like function */
+typedef int (*pa_poll_func)(struct pollfd *ufds, unsigned long nfds, int timeout, void*userdata);
+
+/** Change the poll() implementation */
+void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/operation.c b/src/pulse/operation.c
new file mode 100644 (file)
index 0000000..3ed3143
--- /dev/null
@@ -0,0 +1,151 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+#include <pulse/fork-detect.h>
+
+#include "internal.h"
+#include "operation.h"
+
+PA_STATIC_FLIST_DECLARE(operations, 0, pa_xfree);
+
+pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_assert(c);
+
+    if (!(o = pa_flist_pop(PA_STATIC_FLIST_GET(operations))))
+        o = pa_xnew(pa_operation, 1);
+
+    pa_zero(*o);
+
+    PA_REFCNT_INIT(o);
+    o->context = c;
+    o->stream = s;
+
+    o->state = PA_OPERATION_RUNNING;
+    o->callback = cb;
+    o->userdata = userdata;
+
+    /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
+    PA_LLIST_PREPEND(pa_operation, c->operations, o);
+    pa_operation_ref(o);
+
+    return o;
+}
+
+pa_operation *pa_operation_ref(pa_operation *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    PA_REFCNT_INC(o);
+    return o;
+}
+
+void pa_operation_unref(pa_operation *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (PA_REFCNT_DEC(o) <= 0) {
+        pa_assert(!o->context);
+        pa_assert(!o->stream);
+
+        if (pa_flist_push(PA_STATIC_FLIST_GET(operations), o) < 0)
+            pa_xfree(o);
+    }
+}
+
+static void operation_unlink(pa_operation *o) {
+    pa_assert(o);
+
+    if (o->context) {
+        pa_assert(PA_REFCNT_VALUE(o) >= 2);
+
+        PA_LLIST_REMOVE(pa_operation, o->context->operations, o);
+        pa_operation_unref(o);
+
+        o->context = NULL;
+    }
+
+    o->stream = NULL;
+    o->callback = NULL;
+    o->userdata = NULL;
+    o->state_callback = NULL;
+    o->state_userdata = NULL;
+}
+
+static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (st == o->state)
+        return;
+
+    pa_operation_ref(o);
+
+    o->state = st;
+
+    if (o->state_callback)
+        o->state_callback(o, o->state_userdata);
+
+    if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED))
+        operation_unlink(o);
+
+    pa_operation_unref(o);
+}
+
+void pa_operation_cancel(pa_operation *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    operation_set_state(o, PA_OPERATION_CANCELED);
+}
+
+void pa_operation_done(pa_operation *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    operation_set_state(o, PA_OPERATION_DONE);
+}
+
+pa_operation_state_t pa_operation_get_state(pa_operation *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    return o->state;
+}
+
+void pa_operation_set_state_callback(pa_operation *o, pa_operation_notify_cb_t cb, void *userdata) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (o->state == PA_OPERATION_DONE || o->state == PA_OPERATION_CANCELED)
+        return;
+
+    o->state_callback = cb;
+    o->state_userdata = userdata;
+}
diff --git a/src/pulse/operation.h b/src/pulse/operation.h
new file mode 100644 (file)
index 0000000..edd7d76
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef foooperationhfoo
+#define foooperationhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/def.h>
+#include <pulse/version.h>
+
+/** \file
+ * Asynchronous operations */
+
+PA_C_DECL_BEGIN
+
+/** An asynchronous operation object */
+typedef struct pa_operation pa_operation;
+
+/** A callback for operation state changes */
+typedef void (*pa_operation_notify_cb_t) (pa_operation *o, void *userdata);
+
+/** Increase the reference count by one */
+pa_operation *pa_operation_ref(pa_operation *o);
+
+/** Decrease the reference count by one */
+void pa_operation_unref(pa_operation *o);
+
+/** Cancel the operation. Beware! This will not necessarily cancel the
+ * execution of the operation on the server side. However it will make
+ * sure that the callback associated with this operation will not be
+ * called anymore, effectively disabling the operation from the client
+ * side's view. */
+void pa_operation_cancel(pa_operation *o);
+
+/** Return the current status of the operation */
+pa_operation_state_t pa_operation_get_state(pa_operation *o);
+
+/** Set the callback function that is called when the operation state
+ * changes. Usually this is not necessary, since the functions that
+ * create pa_operation objects already take a callback that is called
+ * when the operation finishes. Registering a state change callback is
+ * mainly useful, if you want to get called back also if the operation
+ * gets cancelled. \since 4.0 */
+void pa_operation_set_state_callback(pa_operation *o, pa_operation_notify_cb_t cb, void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
new file mode 100644 (file)
index 0000000..d8c6404
--- /dev/null
@@ -0,0 +1,705 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <ctype.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/hashmap.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core-util.h>
+
+#include "proplist.h"
+
+struct property {
+    char *key;
+    void *value;
+    size_t nbytes;
+};
+
+#define MAKE_HASHMAP(p) ((pa_hashmap*) (p))
+#define MAKE_PROPLIST(p) ((pa_proplist*) (p))
+
+int pa_proplist_key_valid(const char *key) {
+
+    if (!pa_ascii_valid(key))
+        return 0;
+
+    if (strlen(key) <= 0)
+        return 0;
+
+    return 1;
+}
+
+static void property_free(struct property *prop) {
+    pa_assert(prop);
+
+    pa_xfree(prop->key);
+    pa_xfree(prop->value);
+    pa_xfree(prop);
+}
+
+pa_proplist* pa_proplist_new(void) {
+    return MAKE_PROPLIST(pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) property_free));
+}
+
+void pa_proplist_free(pa_proplist* p) {
+    pa_assert(p);
+
+    pa_hashmap_free(MAKE_HASHMAP(p));
+}
+
+/** Will accept only valid UTF-8 */
+int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
+    struct property *prop;
+    bool add = false;
+
+    pa_assert(p);
+    pa_assert(key);
+    pa_assert(value);
+
+    if (!pa_proplist_key_valid(key) || !pa_utf8_valid(value))
+        return -1;
+
+    if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+        prop = pa_xnew(struct property, 1);
+        prop->key = pa_xstrdup(key);
+        add = true;
+    } else
+        pa_xfree(prop->value);
+
+    prop->value = pa_xstrdup(value);
+    prop->nbytes = strlen(value)+1;
+
+    if (add)
+        pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+    return 0;
+}
+
+/** Will accept only valid UTF-8 */
+static int proplist_setn(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) {
+    struct property *prop;
+    bool add = false;
+    char *k, *v;
+
+    pa_assert(p);
+    pa_assert(key);
+    pa_assert(value);
+
+    k = pa_xstrndup(key, key_length);
+    v = pa_xstrndup(value, value_length);
+
+    if (!pa_proplist_key_valid(k) || !pa_utf8_valid(v)) {
+        pa_xfree(k);
+        pa_xfree(v);
+        return -1;
+    }
+
+    if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), k))) {
+        prop = pa_xnew(struct property, 1);
+        prop->key = k;
+        add = true;
+    } else {
+        pa_xfree(prop->value);
+        pa_xfree(k);
+    }
+
+    prop->value = v;
+    prop->nbytes = strlen(v)+1;
+
+    if (add)
+        pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+    return 0;
+}
+
+/** Will accept only valid UTF-8 */
+int pa_proplist_setp(pa_proplist *p, const char *pair) {
+    const char *t;
+
+    pa_assert(p);
+    pa_assert(pair);
+
+    if (!(t = strchr(pair, '=')))
+        return -1;
+
+    return proplist_setn(p,
+                         pair, t - pair,
+                         t + 1, strchr(pair, 0) - t - 1);
+}
+
+static int proplist_sethex(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) {
+    struct property *prop;
+    bool add = false;
+    char *k, *v;
+    uint8_t *d;
+    size_t dn;
+
+    pa_assert(p);
+    pa_assert(key);
+    pa_assert(value);
+
+    k = pa_xstrndup(key, key_length);
+
+    if (!pa_proplist_key_valid(k)) {
+        pa_xfree(k);
+        return -1;
+    }
+
+    v = pa_xstrndup(value, value_length);
+    d = pa_xmalloc(value_length*2+1);
+
+    if ((dn = pa_parsehex(v, d, value_length*2)) == (size_t) -1) {
+        pa_xfree(k);
+        pa_xfree(v);
+        pa_xfree(d);
+        return -1;
+    }
+
+    pa_xfree(v);
+
+    if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), k))) {
+        prop = pa_xnew(struct property, 1);
+        prop->key = k;
+        add = true;
+    } else {
+        pa_xfree(prop->value);
+        pa_xfree(k);
+    }
+
+    d[dn] = 0;
+    prop->value = d;
+    prop->nbytes = dn;
+
+    if (add)
+        pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+    return 0;
+}
+
+/** Will accept only valid UTF-8 */
+int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) {
+    struct property *prop;
+    bool add = false;
+    va_list ap;
+    char *v;
+
+    pa_assert(p);
+    pa_assert(key);
+    pa_assert(format);
+
+    if (!pa_proplist_key_valid(key) || !pa_utf8_valid(format))
+        return -1;
+
+    va_start(ap, format);
+    v = pa_vsprintf_malloc(format, ap);
+    va_end(ap);
+
+    if (!pa_utf8_valid(v))
+        goto fail;
+
+    if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+        prop = pa_xnew(struct property, 1);
+        prop->key = pa_xstrdup(key);
+        add = true;
+    } else
+        pa_xfree(prop->value);
+
+    prop->value = v;
+    prop->nbytes = strlen(v)+1;
+
+    if (add)
+        pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+    return 0;
+
+fail:
+    pa_xfree(v);
+    return -1;
+}
+
+int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) {
+    struct property *prop;
+    bool add = false;
+
+    pa_assert(p);
+    pa_assert(key);
+    pa_assert(data || nbytes == 0);
+
+    if (!pa_proplist_key_valid(key))
+        return -1;
+
+    if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+        prop = pa_xnew(struct property, 1);
+        prop->key = pa_xstrdup(key);
+        add = true;
+    } else
+        pa_xfree(prop->value);
+
+    prop->value = pa_xmalloc(nbytes+1);
+    if (nbytes > 0)
+        memcpy(prop->value, data, nbytes);
+    ((char*) prop->value)[nbytes] = 0;
+    prop->nbytes = nbytes;
+
+    if (add)
+        pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+    return 0;
+}
+
+const char *pa_proplist_gets(pa_proplist *p, const char *key) {
+    struct property *prop;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!pa_proplist_key_valid(key))
+        return NULL;
+
+    if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key)))
+        return NULL;
+
+    if (prop->nbytes <= 0)
+        return NULL;
+
+    if (((char*) prop->value)[prop->nbytes-1] != 0)
+        return NULL;
+
+    if (strlen((char*) prop->value) != prop->nbytes-1)
+        return NULL;
+
+    if (!pa_utf8_valid((char*) prop->value))
+        return NULL;
+
+    return (char*) prop->value;
+}
+
+int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes) {
+    struct property *prop;
+
+    pa_assert(p);
+    pa_assert(key);
+    pa_assert(data);
+    pa_assert(nbytes);
+
+    if (!pa_proplist_key_valid(key))
+        return -1;
+
+    if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key)))
+        return -1;
+
+    *data = prop->value;
+    *nbytes = prop->nbytes;
+
+    return 0;
+}
+
+void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other) {
+    struct property *prop;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE);
+    pa_assert(other);
+
+    if (mode == PA_UPDATE_SET)
+        pa_proplist_clear(p);
+
+    /* MAKE_HASHMAP turns the const pointer into a non-const pointer, but
+     * that's ok, because we don't modify the hashmap contents. */
+    while ((prop = pa_hashmap_iterate(MAKE_HASHMAP(other), &state, NULL))) {
+
+        if (mode == PA_UPDATE_MERGE && pa_proplist_contains(p, prop->key))
+            continue;
+
+        pa_assert_se(pa_proplist_set(p, prop->key, prop->value, prop->nbytes) == 0);
+    }
+}
+
+int pa_proplist_unset(pa_proplist *p, const char *key) {
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!pa_proplist_key_valid(key))
+        return -1;
+
+    if (pa_hashmap_remove_and_free(MAKE_HASHMAP(p), key) < 0)
+        return -2;
+
+    return 0;
+}
+
+int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) {
+    const char * const * k;
+    int n = 0;
+
+    pa_assert(p);
+    pa_assert(keys);
+
+    for (k = keys; *k; k++)
+        if (!pa_proplist_key_valid(*k))
+            return -1;
+
+    for (k = keys; *k; k++)
+        if (pa_proplist_unset(p, *k) >= 0)
+            n++;
+
+    return n;
+}
+
+const char *pa_proplist_iterate(pa_proplist *p, void **state) {
+    struct property *prop;
+
+    if (!(prop = pa_hashmap_iterate(MAKE_HASHMAP(p), state, NULL)))
+        return NULL;
+
+    return prop->key;
+}
+
+char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) {
+    const char *key;
+    void *state = NULL;
+    pa_strbuf *buf;
+
+    pa_assert(p);
+    pa_assert(sep);
+
+    buf = pa_strbuf_new();
+
+    while ((key = pa_proplist_iterate(p, &state))) {
+        const char *v;
+
+        if (!pa_strbuf_isempty(buf))
+            pa_strbuf_puts(buf, sep);
+
+        if ((v = pa_proplist_gets(p, key))) {
+            const char *t;
+
+            pa_strbuf_printf(buf, "%s = \"", key);
+
+            for (t = v;;) {
+                size_t h;
+
+                h = strcspn(t, "\"");
+
+                if (h > 0)
+                    pa_strbuf_putsn(buf, t, h);
+
+                t += h;
+
+                if (*t == 0)
+                    break;
+
+                pa_assert(*t == '"');
+                pa_strbuf_puts(buf, "\\\"");
+
+                t++;
+            }
+
+            pa_strbuf_puts(buf, "\"");
+        } else {
+            const void *value;
+            size_t nbytes;
+            char *c;
+
+            pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0);
+            c = pa_xmalloc(nbytes*2+1);
+            pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
+
+            pa_strbuf_printf(buf, "%s = hex:%s", key, c);
+            pa_xfree(c);
+        }
+    }
+
+    return pa_strbuf_to_string_free(buf);
+}
+
+char *pa_proplist_to_string(pa_proplist *p) {
+    char *s, *t;
+
+    s = pa_proplist_to_string_sep(p, "\n");
+    t = pa_sprintf_malloc("%s\n", s);
+    pa_xfree(s);
+
+    return t;
+}
+
+pa_proplist *pa_proplist_from_string(const char *s) {
+    enum {
+        WHITESPACE,
+        KEY,
+        AFTER_KEY,
+        VALUE_START,
+        VALUE_SIMPLE,
+        VALUE_DOUBLE_QUOTES,
+        VALUE_DOUBLE_QUOTES_ESCAPE,
+        VALUE_TICKS,
+        VALUE_TICKS_ESCAPED,
+        VALUE_HEX
+    } state;
+
+    pa_proplist *pl;
+    const char *p, *key = NULL, *value = NULL;
+    size_t key_len = 0, value_len = 0;
+
+    pa_assert(s);
+
+    pl = pa_proplist_new();
+
+    state = WHITESPACE;
+
+    for (p = s;; p++) {
+        switch (state) {
+
+            case WHITESPACE:
+                if (*p == 0)
+                    goto success;
+                else if (*p == '=')
+                    goto fail;
+                else if (!isspace((unsigned char)*p)) {
+                    key = p;
+                    state = KEY;
+                    key_len = 1;
+                }
+                break;
+
+            case KEY:
+                if (*p == 0)
+                    goto fail;
+                else if (*p == '=')
+                    state = VALUE_START;
+                else if (isspace((unsigned char)*p))
+                    state = AFTER_KEY;
+                else
+                    key_len++;
+                break;
+
+            case AFTER_KEY:
+                if (*p == 0)
+                    goto fail;
+                else if (*p == '=')
+                    state = VALUE_START;
+                else if (!isspace((unsigned char)*p))
+                    goto fail;
+                break;
+
+            case VALUE_START:
+                if (*p == 0)
+                    goto fail;
+                else if (strncmp(p, "hex:", 4) == 0) {
+                    state = VALUE_HEX;
+                    value = p+4;
+                    value_len = 0;
+                    p += 3;
+                } else 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((unsigned char)*p)) {
+                    state = VALUE_SIMPLE;
+                    value = p;
+                    value_len = 1;
+                }
+                break;
+
+            case VALUE_SIMPLE:
+                if (*p == 0 || isspace((unsigned char)*p)) {
+                    if (proplist_setn(pl, key, key_len, value, value_len) < 0)
+                        goto fail;
+
+                    if (*p == 0)
+                        goto success;
+
+                    state = WHITESPACE;
+                } else
+                    value_len++;
+                break;
+
+            case VALUE_DOUBLE_QUOTES:
+                if (*p == 0)
+                    goto fail;
+                else if (*p == '"') {
+                    char *v;
+
+                    v = pa_unescape(pa_xstrndup(value, value_len));
+
+                    if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) {
+                        pa_xfree(v);
+                        goto fail;
+                    }
+
+                    pa_xfree(v);
+                    state = WHITESPACE;
+                } else if (*p == '\\') {
+                    state = VALUE_DOUBLE_QUOTES_ESCAPE;
+                    value_len++;
+                } else
+                    value_len++;
+                break;
+
+            case VALUE_DOUBLE_QUOTES_ESCAPE:
+                if (*p == 0)
+                    goto fail;
+                else {
+                    state = VALUE_DOUBLE_QUOTES;
+                    value_len++;
+                }
+                break;
+
+            case VALUE_TICKS:
+                if (*p == 0)
+                    goto fail;
+                else if (*p == '\'') {
+                    char *v;
+
+                    v = pa_unescape(pa_xstrndup(value, value_len));
+
+                    if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) {
+                        pa_xfree(v);
+                        goto fail;
+                    }
+
+                    pa_xfree(v);
+                    state = WHITESPACE;
+                } else if (*p == '\\') {
+                    state = VALUE_TICKS_ESCAPED;
+                    value_len++;
+                } else
+                    value_len++;
+                break;
+
+            case VALUE_TICKS_ESCAPED:
+                if (*p == 0)
+                    goto fail;
+                else {
+                    state = VALUE_TICKS;
+                    value_len++;
+                }
+                break;
+
+            case VALUE_HEX:
+                if ((*p >= '0' && *p <= '9') ||
+                    (*p >= 'A' && *p <= 'F') ||
+                    (*p >= 'a' && *p <= 'f')) {
+                    value_len++;
+                } else if (*p == 0 || isspace((unsigned char)*p)) {
+
+                    if (proplist_sethex(pl, key, key_len, value, value_len) < 0)
+                        goto fail;
+
+                    if (*p == 0)
+                        goto success;
+
+                    state = WHITESPACE;
+                } else
+                    goto fail;
+                break;
+        }
+    }
+
+success:
+    return MAKE_PROPLIST(pl);
+
+fail:
+    pa_proplist_free(pl);
+    return NULL;
+}
+
+int pa_proplist_contains(pa_proplist *p, const char *key) {
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!pa_proplist_key_valid(key))
+        return -1;
+
+    if (!(pa_hashmap_get(MAKE_HASHMAP(p), key)))
+        return 0;
+
+    return 1;
+}
+
+void pa_proplist_clear(pa_proplist *p) {
+    pa_assert(p);
+
+    pa_hashmap_remove_all(MAKE_HASHMAP(p));
+}
+
+pa_proplist* pa_proplist_copy(const pa_proplist *p) {
+    pa_proplist *copy;
+
+    pa_assert_se(copy = pa_proplist_new());
+
+    if (p)
+        pa_proplist_update(copy, PA_UPDATE_REPLACE, p);
+
+    return copy;
+}
+
+unsigned pa_proplist_size(pa_proplist *p) {
+    pa_assert(p);
+
+    return pa_hashmap_size(MAKE_HASHMAP(p));
+}
+
+int pa_proplist_isempty(pa_proplist *p) {
+    pa_assert(p);
+
+    return pa_hashmap_isempty(MAKE_HASHMAP(p));
+}
+
+int pa_proplist_equal(pa_proplist *a, pa_proplist *b) {
+    const void *key = NULL;
+    struct property *a_prop = NULL;
+    struct property *b_prop = NULL;
+    void *state = NULL;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    if (a == b)
+        return 1;
+
+    if (pa_proplist_size(a) != pa_proplist_size(b))
+        return 0;
+
+    while ((a_prop = pa_hashmap_iterate(MAKE_HASHMAP(a), &state, &key))) {
+        if (!(b_prop = pa_hashmap_get(MAKE_HASHMAP(b), key)))
+            return 0;
+
+        if (a_prop->nbytes != b_prop->nbytes)
+            return 0;
+
+        if (memcmp(a_prop->value, b_prop->value, a_prop->nbytes) != 0)
+            return 0;
+    }
+
+    return 1;
+}
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
new file mode 100644 (file)
index 0000000..bc9e8f8
--- /dev/null
@@ -0,0 +1,409 @@
+#ifndef foopulseproplisthfoo
+#define foopulseproplisthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+#include <pulse/version.h>
+
+/** \file
+ * Property list constants and functions */
+
+PA_C_DECL_BEGIN
+
+/** For streams: localized media name, formatted as UTF-8. E.g. "Guns'N'Roses: Civil War".*/
+#define PA_PROP_MEDIA_NAME                     "media.name"
+
+/** For streams: localized media title if applicable, formatted as UTF-8. E.g. "Civil War" */
+#define PA_PROP_MEDIA_TITLE                    "media.title"
+
+/** For streams: localized media artist if applicable, formatted as UTF-8. E.g. "Guns'N'Roses" */
+#define PA_PROP_MEDIA_ARTIST                   "media.artist"
+
+/** For streams: localized media copyright string if applicable, formatted as UTF-8. E.g. "Evil Record Corp." */
+#define PA_PROP_MEDIA_COPYRIGHT                "media.copyright"
+
+/** For streams: localized media generator software string if applicable, formatted as UTF-8. E.g. "Foocrop AudioFrobnicator" */
+#define PA_PROP_MEDIA_SOFTWARE                 "media.software"
+
+/** For streams: media language if applicable, in standard POSIX format. E.g. "de_DE" */
+#define PA_PROP_MEDIA_LANGUAGE                 "media.language"
+
+/** For streams: source filename if applicable, in URI format or local path. E.g. "/home/lennart/music/foobar.ogg" */
+#define PA_PROP_MEDIA_FILENAME                 "media.filename"
+
+/** \cond fulldocs */
+/** For streams: icon for the media. A binary blob containing PNG image data */
+#define PA_PROP_MEDIA_ICON                     "media.icon"
+/** \endcond */
+
+/** For streams: an XDG icon name for the media. E.g. "audio-x-mp3" */
+#define PA_PROP_MEDIA_ICON_NAME                "media.icon_name"
+
+/** For streams: logic role of this media. One of the strings "video", "music", "game", "event", "phone", "animation", "production", "a11y", "test" */
+#define PA_PROP_MEDIA_ROLE                     "media.role"
+
+/** For streams: the name of a filter that is desired, e.g.\ "echo-cancel" or "equalizer-sink". PulseAudio may choose to not apply the filter if it does not make sense (for example, applying echo-cancellation on a Bluetooth headset probably does not make sense. \since 1.0 */
+#define PA_PROP_FILTER_WANT                    "filter.want"
+
+/** For streams: the name of a filter that is desired, e.g.\ "echo-cancel" or "equalizer-sink". Differs from PA_PROP_FILTER_WANT in that it forces PulseAudio to apply the filter, regardless of whether PulseAudio thinks it makes sense to do so or not. If this is set, PA_PROP_FILTER_WANT is ignored. In other words, you almost certainly do not want to use this. \since 1.0 */
+#define PA_PROP_FILTER_APPLY                   "filter.apply"
+
+/** For streams: the name of a filter that should specifically suppressed (i.e.\ overrides PA_PROP_FILTER_WANT). Useful for the times that PA_PROP_FILTER_WANT is automatically added (e.g. echo-cancellation for phone streams when $VOIP_APP does its own, internal AEC) \since 1.0 */
+#define PA_PROP_FILTER_SUPPRESS                "filter.suppress"
+
+/** For event sound streams: XDG event sound name. e.g.\ "message-new-email" (Event sound streams are those with media.role set to "event") */
+#define PA_PROP_EVENT_ID                       "event.id"
+
+/** For event sound streams: localized human readable one-line description of the event, formatted as UTF-8. E.g. "Email from lennart@example.com received." */
+#define PA_PROP_EVENT_DESCRIPTION              "event.description"
+
+/** For event sound streams: absolute horizontal mouse position on the screen if the event sound was triggered by a mouse click, integer formatted as text string. E.g. "865" */
+#define PA_PROP_EVENT_MOUSE_X                  "event.mouse.x"
+
+/** For event sound streams: absolute vertical mouse position on the screen if the event sound was triggered by a mouse click, integer formatted as text string. E.g. "432" */
+#define PA_PROP_EVENT_MOUSE_Y                  "event.mouse.y"
+
+/** For event sound streams: relative horizontal mouse position on the screen if the event sound was triggered by a mouse click, float formatted as text string, ranging from 0.0 (left side of the screen) to 1.0 (right side of the screen). E.g. "0.65" */
+#define PA_PROP_EVENT_MOUSE_HPOS               "event.mouse.hpos"
+
+/** For event sound streams: relative vertical mouse position on the screen if the event sound was triggered by a mouse click, float formatted as text string, ranging from 0.0 (top of the screen) to 1.0 (bottom of the screen). E.g. "0.43" */
+#define PA_PROP_EVENT_MOUSE_VPOS               "event.mouse.vpos"
+
+/** For event sound streams: mouse button that triggered the event if applicable, integer formatted as string with 0=left, 1=middle, 2=right. E.g. "0" */
+#define PA_PROP_EVENT_MOUSE_BUTTON             "event.mouse.button"
+
+/** For streams that belong to a window on the screen: localized window title. E.g. "Totem Music Player" */
+#define PA_PROP_WINDOW_NAME                    "window.name"
+
+/** For streams that belong to a window on the screen: a textual id for identifying a window logically. E.g. "org.gnome.Totem.MainWindow" */
+#define PA_PROP_WINDOW_ID                      "window.id"
+
+/** \cond fulldocs */
+/** For streams that belong to a window on the screen: window icon. A binary blob containing PNG image data */
+#define PA_PROP_WINDOW_ICON                    "window.icon"
+/** \endcond */
+
+/** For streams that belong to a window on the screen: an XDG icon name for the window. E.g. "totem" */
+#define PA_PROP_WINDOW_ICON_NAME               "window.icon_name"
+
+/** For streams that belong to a window on the screen: absolute horizontal window position on the screen, integer formatted as text string. E.g. "865". \since 0.9.17 */
+#define PA_PROP_WINDOW_X                       "window.x"
+
+/** For streams that belong to a window on the screen: absolute vertical window position on the screen, integer formatted as text string. E.g. "343". \since 0.9.17 */
+#define PA_PROP_WINDOW_Y                       "window.y"
+
+/** For streams that belong to a window on the screen: window width on the screen, integer formatted as text string. e.g. "365". \since 0.9.17 */
+#define PA_PROP_WINDOW_WIDTH                   "window.width"
+
+/** For streams that belong to a window on the screen: window height on the screen, integer formatted as text string. E.g. "643". \since 0.9.17 */
+#define PA_PROP_WINDOW_HEIGHT                  "window.height"
+
+/** For streams that belong to a window on the screen: relative position of the window center on the screen, float formatted as text string, ranging from 0.0 (left side of the screen) to 1.0 (right side of the screen). E.g. "0.65". \since 0.9.17 */
+#define PA_PROP_WINDOW_HPOS                    "window.hpos"
+
+/** For streams that belong to a window on the screen: relative position of the window center on the screen, float formatted as text string, ranging from 0.0 (top of the screen) to 1.0 (bottom of the screen). E.g. "0.43". \since 0.9.17 */
+#define PA_PROP_WINDOW_VPOS                    "window.vpos"
+
+/** For streams that belong to a window on the screen: if the windowing system supports multiple desktops, a comma separated list of indexes of the desktops this window is visible on. If this property is an empty string, it is visible on all desktops (i.e. 'sticky'). The first desktop is 0. E.g. "0,2,3" \since 0.9.18 */
+#define PA_PROP_WINDOW_DESKTOP                 "window.desktop"
+
+/** For streams that belong to an X11 window on the screen: the X11 display string. E.g. ":0.0" */
+#define PA_PROP_WINDOW_X11_DISPLAY             "window.x11.display"
+
+/** For streams that belong to an X11 window on the screen: the X11 screen the window is on, an integer formatted as string. E.g. "0" */
+#define PA_PROP_WINDOW_X11_SCREEN              "window.x11.screen"
+
+/** For streams that belong to an X11 window on the screen: the X11 monitor the window is on, an integer formatted as string. E.g. "0" */
+#define PA_PROP_WINDOW_X11_MONITOR             "window.x11.monitor"
+
+/** For streams that belong to an X11 window on the screen: the window XID, an integer formatted as string. E.g. "25632" */
+#define PA_PROP_WINDOW_X11_XID                 "window.x11.xid"
+
+/** For clients/streams: localized human readable application name. E.g. "Totem Music Player" */
+#define PA_PROP_APPLICATION_NAME               "application.name"
+
+/** For clients/streams: a textual id for identifying an application logically. E.g. "org.gnome.Totem" */
+#define PA_PROP_APPLICATION_ID                 "application.id"
+
+/** For clients/streams: a version string, e.g.\ "0.6.88" */
+#define PA_PROP_APPLICATION_VERSION            "application.version"
+
+/** \cond fulldocs */
+/** For clients/streams: application icon. A binary blob containing PNG image data */
+#define PA_PROP_APPLICATION_ICON               "application.icon"
+/** \endcond */
+
+/** For clients/streams: an XDG icon name for the application. E.g. "totem" */
+#define PA_PROP_APPLICATION_ICON_NAME          "application.icon_name"
+
+/** For clients/streams: application language if applicable, in standard POSIX format. E.g. "de_DE" */
+#define PA_PROP_APPLICATION_LANGUAGE           "application.language"
+
+/** For clients/streams on UNIX: application process PID, an integer formatted as string. E.g. "4711" */
+#define PA_PROP_APPLICATION_PROCESS_ID         "application.process.id"
+
+/** For clients/streams: application process name. E.g. "totem" */
+#define PA_PROP_APPLICATION_PROCESS_BINARY     "application.process.binary"
+
+/** For clients/streams: application user name. E.g. "lennart" */
+#define PA_PROP_APPLICATION_PROCESS_USER       "application.process.user"
+
+/** For clients/streams: host name the application runs on. E.g. "omega" */
+#define PA_PROP_APPLICATION_PROCESS_HOST       "application.process.host"
+
+/** For clients/streams: the D-Bus host id the application runs on. E.g. "543679e7b01393ed3e3e650047d78f6e" */
+#define PA_PROP_APPLICATION_PROCESS_MACHINE_ID "application.process.machine_id"
+
+/** For clients/streams: an id for the login session the application runs in. On Unix the value of $XDG_SESSION_ID. E.g. "5" */
+#define PA_PROP_APPLICATION_PROCESS_SESSION_ID "application.process.session_id"
+
+/** For devices: device string in the underlying audio layer's format. E.g. "surround51:0" */
+#define PA_PROP_DEVICE_STRING                  "device.string"
+
+/** For devices: API this device is access with. E.g. "alsa" */
+#define PA_PROP_DEVICE_API                     "device.api"
+
+/** For devices: localized human readable device one-line description. E.g. "Foobar Industries USB Headset 2000+ Ultra" */
+#define PA_PROP_DEVICE_DESCRIPTION             "device.description"
+
+/** For devices: bus path to the device in the OS' format. E.g. "/sys/bus/pci/devices/0000:00:1f.2" */
+#define PA_PROP_DEVICE_BUS_PATH                "device.bus_path"
+
+/** For devices: serial number if applicable. E.g. "4711-0815-1234" */
+#define PA_PROP_DEVICE_SERIAL                  "device.serial"
+
+/** For devices: vendor ID if applicable. E.g. 1274 */
+#define PA_PROP_DEVICE_VENDOR_ID               "device.vendor.id"
+
+/** For devices: vendor name if applicable. E.g. "Foocorp Heavy Industries" */
+#define PA_PROP_DEVICE_VENDOR_NAME             "device.vendor.name"
+
+/** For devices: product ID if applicable. E.g. 4565 */
+#define PA_PROP_DEVICE_PRODUCT_ID              "device.product.id"
+
+/** For devices: product name if applicable. E.g. "SuperSpeakers 2000 Pro" */
+#define PA_PROP_DEVICE_PRODUCT_NAME            "device.product.name"
+
+/** For devices: device class. One of "sound", "modem", "monitor", "filter" */
+#define PA_PROP_DEVICE_CLASS                   "device.class"
+
+/** For devices: form factor if applicable. One of "internal", "speaker", "handset", "tv", "webcam", "microphone", "headset", "headphone", "hands-free", "car", "hifi", "computer", "portable" */
+#define PA_PROP_DEVICE_FORM_FACTOR             "device.form_factor"
+
+/** For devices: bus of the device if applicable. One of "isa", "pci", "usb", "firewire", "bluetooth" */
+#define PA_PROP_DEVICE_BUS                     "device.bus"
+
+/** \cond fulldocs */
+/** For devices: icon for the device. A binary blob containing PNG image data */
+#define PA_PROP_DEVICE_ICON                    "device.icon"
+/** \endcond */
+
+/** For devices: an XDG icon name for the device. E.g. "sound-card-speakers-usb" */
+#define PA_PROP_DEVICE_ICON_NAME               "device.icon_name"
+
+/** For devices: access mode of the device if applicable. One of "mmap", "mmap_rewrite", "serial" */
+#define PA_PROP_DEVICE_ACCESS_MODE             "device.access_mode"
+
+/** For filter devices: master device id if applicable. */
+#define PA_PROP_DEVICE_MASTER_DEVICE           "device.master_device"
+
+/** For devices: buffer size in bytes, integer formatted as string. */
+#define PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE   "device.buffering.buffer_size"
+
+/** For devices: fragment size in bytes, integer formatted as string. */
+#define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size"
+
+/** For devices: profile identifier for the profile this devices is in. E.g. "analog-stereo", "analog-surround-40", "iec958-stereo", ...*/
+#define PA_PROP_DEVICE_PROFILE_NAME            "device.profile.name"
+
+/** For devices: intended use. A space separated list of roles (see PA_PROP_MEDIA_ROLE) this device is particularly well suited for, due to latency, quality or form factor. \since 0.9.16 */
+#define PA_PROP_DEVICE_INTENDED_ROLES          "device.intended_roles"
+
+/** For devices: human readable one-line description of the profile this device is in. E.g. "Analog Stereo", ... */
+#define PA_PROP_DEVICE_PROFILE_DESCRIPTION     "device.profile.description"
+
+/** For modules: the author's name, formatted as UTF-8 string. E.g. "Lennart Poettering" */
+#define PA_PROP_MODULE_AUTHOR                  "module.author"
+
+/** For modules: a human readable one-line description of the module's purpose formatted as UTF-8. E.g. "Frobnicate sounds with a flux compensator" */
+#define PA_PROP_MODULE_DESCRIPTION             "module.description"
+
+/** For modules: a human readable usage description of the module's arguments formatted as UTF-8. */
+#define PA_PROP_MODULE_USAGE                   "module.usage"
+
+/** For modules: a version string for the module. E.g. "0.9.15" */
+#define PA_PROP_MODULE_VERSION                 "module.version"
+
+/** For PCM formats: the sample format used as returned by pa_sample_format_to_string() \since 1.0 */
+#define PA_PROP_FORMAT_SAMPLE_FORMAT           "format.sample_format"
+
+/** For all formats: the sample rate (unsigned integer) \since 1.0 */
+#define PA_PROP_FORMAT_RATE                    "format.rate"
+
+/** For all formats: the number of channels (unsigned integer) \since 1.0 */
+#define PA_PROP_FORMAT_CHANNELS                "format.channels"
+
+/** For PCM formats: the channel map of the stream as returned by pa_channel_map_snprint() \since 1.0 */
+#define PA_PROP_FORMAT_CHANNEL_MAP             "format.channel_map"
+
+/** A property list object. Basically a dictionary with ASCII strings
+ * as keys and arbitrary data as values. \since 0.9.11 */
+typedef struct pa_proplist pa_proplist;
+
+/** Allocate a property list. \since 0.9.11 */
+pa_proplist* pa_proplist_new(void);
+
+/** Free the property list. \since 0.9.11 */
+void pa_proplist_free(pa_proplist* p);
+
+/** Returns a non-zero value if the key is valid. \since 3.0 */
+int pa_proplist_key_valid(const char *key);
+
+/** Append a new string entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. Will accept only valid
+ * UTF-8. \since 0.9.11 */
+int pa_proplist_sets(pa_proplist *p, const char *key, const char *value);
+
+/** Append a new string entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. Will accept only valid
+ * UTF-8. The string passed in must contain a '='. Left hand side of
+ * the '=' is used as key name, the right hand side as string
+ * data. \since 0.9.16 */
+int pa_proplist_setp(pa_proplist *p, const char *pair);
+
+/** Append a new string entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. Will accept only valid
+ * UTF-8. The data can be passed as printf()-style format string with
+ * arguments. \since 0.9.11 */
+int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) PA_GCC_PRINTF_ATTR(3,4);
+
+/** Append a new arbitrary data entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. \since 0.9.11 */
+int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes);
+
+/** Return a string entry for the specified key. Will return NULL if
+ * the data is not valid UTF-8. Will return a NUL-terminated string in
+ * an internally allocated buffer. The caller should make a copy of
+ * the data before accessing the property list again. \since 0.9.11 */
+const char *pa_proplist_gets(pa_proplist *p, const char *key);
+
+/** Store the value for the specified key in \a data. Will store a
+ * NUL-terminated string for string entries. The \a data pointer returned will
+ * point to an internally allocated buffer. The caller should make a
+ * copy of the data before the property list is accessed again. \since
+ * 0.9.11 */
+int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes);
+
+/** Update mode enum for pa_proplist_update(). \since 0.9.11 */
+typedef enum pa_update_mode {
+    PA_UPDATE_SET
+    /**< Replace the entire property list with the new one. Don't keep
+     *  any of the old data around. */,
+
+    PA_UPDATE_MERGE
+    /**< Merge new property list into the existing one, not replacing
+     *  any old entries if they share a common key with the new
+     *  property list. */,
+
+    PA_UPDATE_REPLACE
+    /**< Merge new property list into the existing one, replacing all
+     *  old entries that share a common key with the new property
+     *  list. */
+} pa_update_mode_t;
+
+/** \cond fulldocs */
+#define PA_UPDATE_SET PA_UPDATE_SET
+#define PA_UPDATE_MERGE PA_UPDATE_MERGE
+#define PA_UPDATE_REPLACE PA_UPDATE_REPLACE
+/** \endcond */
+
+/** Merge property list "other" into "p", adhering the merge mode as
+ * specified in "mode". \since 0.9.11 */
+void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other);
+
+/** Removes a single entry from the property list, identified be the
+ * specified key name. \since 0.9.11 */
+int pa_proplist_unset(pa_proplist *p, const char *key);
+
+/** Similar to pa_proplist_unset() but takes an array of keys to
+ * remove. The array should be terminated by a NULL pointer. Returns -1
+ * on failure, otherwise the number of entries actually removed (which
+ * might even be 0, if there were no matching entries to
+ * remove). \since 0.9.11 */
+int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]);
+
+/** Iterate through the property list. The user should allocate a
+ * state variable of type void* and initialize it with NULL. A pointer
+ * to this variable should then be passed to pa_proplist_iterate()
+ * which should be called in a loop until it returns NULL which
+ * signifies EOL. The property list should not be modified during
+ * iteration through the list -- with the exception of deleting the
+ * current entry. On each invocation this function will return the
+ * key string for the next entry. The keys in the property list do not
+ * have any particular order. \since 0.9.11 */
+const char *pa_proplist_iterate(pa_proplist *p, void **state);
+
+/** Format the property list nicely as a human readable string. This
+ * works very much like pa_proplist_to_string_sep() and uses a newline
+ * as separator and appends one final one. Call pa_xfree() on the
+ * result. \since 0.9.11 */
+char *pa_proplist_to_string(pa_proplist *p);
+
+/** Format the property list nicely as a human readable string and
+ * choose the separator. Call pa_xfree() on the result. \since
+ * 0.9.15 */
+char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep);
+
+/** Allocate a new property list and assign key/value from a human
+ * readable string. \since 0.9.15 */
+pa_proplist *pa_proplist_from_string(const char *str);
+
+/** Returns 1 if an entry for the specified key exists in the
+ * property list. \since 0.9.11 */
+int pa_proplist_contains(pa_proplist *p, const char *key);
+
+/** Remove all entries from the property list object. \since 0.9.11 */
+void pa_proplist_clear(pa_proplist *p);
+
+/** Allocate a new property list and copy over every single entry from
+ * the specified list. \since 0.9.11 */
+pa_proplist* pa_proplist_copy(const pa_proplist *p);
+
+/** Return the number of entries in the property list. \since 0.9.15 */
+unsigned pa_proplist_size(pa_proplist *p);
+
+/** Returns 0 when the proplist is empty, positive otherwise \since 0.9.15 */
+int pa_proplist_isempty(pa_proplist *p);
+
+/** Return non-zero when a and b have the same keys and values.
+ * \since 0.9.16 */
+int pa_proplist_equal(pa_proplist *a, pa_proplist *b);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h
new file mode 100644 (file)
index 0000000..063d5e2
--- /dev/null
@@ -0,0 +1,180 @@
+#ifndef foopulseaudiohfoo
+#define foopulseaudiohfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/direction.h>
+#include <pulse/mainloop-api.h>
+#include <pulse/sample.h>
+#include <pulse/format.h>
+#include <pulse/def.h>
+#include <pulse/context.h>
+#include <pulse/stream.h>
+#include <pulse/introspect.h>
+#include <pulse/subscribe.h>
+#include <pulse/scache.h>
+#include <pulse/version.h>
+#include <pulse/error.h>
+#include <pulse/operation.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+#include <pulse/thread-mainloop.h>
+#include <pulse/mainloop.h>
+#include <pulse/mainloop-signal.h>
+#include <pulse/util.h>
+#include <pulse/timeval.h>
+#include <pulse/proplist.h>
+#include <pulse/rtclock.h>
+
+/** \file
+ * Include all libpulse header files at once. The following files are
+ * included: \ref direction.h, \ref mainloop-api.h, \ref sample.h, \ref def.h,
+ * \ref context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h, \ref
+ * scache.h, \ref version.h, \ref error.h, \ref channelmap.h, \ref
+ * operation.h,\ref volume.h, \ref xmalloc.h, \ref utf8.h, \ref
+ * thread-mainloop.h, \ref mainloop.h, \ref util.h, \ref proplist.h,
+ * \ref timeval.h, \ref rtclock.h and \ref mainloop-signal.h at
+ * once */
+
+/** \mainpage
+ *
+ * \section intro_sec Introduction
+ *
+ * This document describes the client API for the PulseAudio sound
+ * server. The API comes in two flavours to accommodate different styles
+ * of applications and different needs in complexity:
+ *
+ * \li The complete but somewhat complicated to use asynchronous API
+ * \li The simplified, easy to use, but limited synchronous API
+ *
+ * All strings in PulseAudio are in the UTF-8 encoding, regardless of current
+ * locale. Some functions will filter invalid sequences from the string, some
+ * will simply fail. To ensure reliable behaviour, make sure everything you
+ * pass to the API is already in UTF-8.
+
+ * \section simple_sec Simple API
+ *
+ * Use this if you develop your program in synchronous style and just
+ * need a way to play or record data on the sound server. See
+ * \subpage simple for more details.
+ *
+ * \section async_sec Asynchronous API
+ *
+ * Use this if you develop your programs in asynchronous, event loop
+ * based style or if you want to use the advanced features of the
+ * PulseAudio API. A guide can be found in \subpage async.
+ *
+ * By using the built-in threaded main loop, it is possible to achieve a
+ * pseudo-synchronous API, which can be useful in synchronous applications
+ * where the simple API is insufficient. See the \ref async page for
+ * details.
+ *
+ * \section thread_sec Threads
+ *
+ * The PulseAudio client libraries are not designed to be directly
+ * thread-safe. They are however designed to be reentrant and
+ * threads-aware.
+ *
+ * To use the libraries in a threaded environment, you must assure that
+ * all objects are only used in one thread at a time. Normally, this means
+ * that all objects belonging to a single context must be accessed from the
+ * same thread.
+ *
+ * The included main loop implementation is also not thread safe. Take care
+ * to make sure event objects are not manipulated when any other code is
+ * using the main loop.
+ *
+ * \section error_sec Error Handling
+ *
+ * Every function should explicitly document how errors are reported to
+ * the caller. Unfortunately, currently a lot of that documentation is
+ * missing. Here is an overview of the general conventions used.
+ *
+ * The PulseAudio API indicates error conditions by returning a negative
+ * integer value or a NULL pointer. On success, zero or a positive integer
+ * value or a valid pointer is returned.
+ *
+ * Functions of the \ref simple generally return -1 or NULL on failure and
+ * can optionally store an error code (see ::pa_error_code) using a pointer
+ * argument.
+ *
+ * Functions of the \ref async return an negative error code or NULL on
+ * failure (see ::pa_error_code). In the later case, pa_context_errno()
+ * can be used to obtain the error code of the last failed operation.
+ *
+ * An error code can be turned into a human readable message using
+ * pa_strerror().
+ *
+ * \section logging_sec Logging
+ *
+ * You can configure different logging parameters for the PulseAudio client
+ * libraries. The following environment variables are recognized:
+ *
+ *  - `PULSE_LOG`: Maximum log level required. Bigger values result in a
+ *     more verbose logging output. The following values are recognized:
+ *     + `0`: Error messages
+ *     + `1`: Warning messages
+ *     + `2`: Notice messages
+ *     + `3`: Info messages
+ *     + `4`: Debug messages
+ *  - `PULSE_LOG_SYSLOG`: If defined, force all client libraries to log
+ *     their output using the syslog(3) mechanism. Default behavior is to
+ *     log all output to stderr.
+ *  - `PULSE_LOG_JOURNAL`: If defined, force all client libraries to log
+ *     their output using the systemd journal. If both `PULSE_LOG_JOURNAL`
+ *     and `PULSE_LOG_SYSLOG` are defined, logging to the systemd journal
+ *     takes a higher precedence. Each message originating library file name
+ *     and function are included by default through the journal fields
+ *     `CODE_FILE`, `CODE_FUNC`, and `CODE_LINE`. Any backtrace attached to
+ *     the logging message is sent through the PulseAudio-specific journal
+ *     field `PULSE_BACKTRACE`. This environment variable has no effect if
+ *     PulseAudio was compiled without systemd journal support.
+ *  - `PULSE_LOG_COLORS`: If defined, enables colored logging output.
+ *  - `PULSE_LOG_TIME`: If defined, include timestamps with each message.
+ *  - `PULSE_LOG_FILE`: If defined, include each message originating file
+ *     name.
+ *  - `PULSE_LOG_META`: If defined, include each message originating file
+ *     name and path relative to the PulseAudio source tree root.
+ *  - `PULSE_LOG_LEVEL`: If defined, include a log level prefix with each
+ *     message. Respectively, the prefixes "E", "W", "N", "I", "D" stands
+ *     for Error, Warning, Notice, Info, and Debugging.
+ *  - `PULSE_LOG_BACKTRACE`: Number of functions to display in the backtrace.
+ *     If this variable is not defined, or has a value of zero, no backtrace
+ *     is shown.
+ *  - `PULSE_LOG_BACKTRACE_SKIP`: Number of backtrace levels to skip, from
+ *     the function printing the log message downwards.
+ *  - `PULSE_LOG_NO_RATE_LIMIT`: If defined, do not rate limit the logging
+ *     output. Rate limiting skips certain log messages when their frequency
+ *     is considered too high.
+ *
+ * \section pkgconfig pkg-config
+ *
+ * The PulseAudio libraries provide pkg-config snippets for the different
+ * modules:
+ *
+ * \li libpulse - The asynchronous API and the internal main loop implementation.
+ * \li libpulse-mainloop-glib - GLIB 2.x main loop bindings.
+ * \li libpulse-simple - The simple PulseAudio API.
+ */
+
+#endif
diff --git a/src/pulse/rtclock.c b/src/pulse/rtclock.c
new file mode 100644 (file)
index 0000000..56cacf7
--- /dev/null
@@ -0,0 +1,36 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/time.h>
+
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-rtclock.h>
+
+#include "rtclock.h"
+
+pa_usec_t pa_rtclock_now(void) {
+    struct timeval tv;
+
+    return pa_timeval_load(pa_rtclock_get(&tv));
+}
diff --git a/src/pulse/rtclock.h b/src/pulse/rtclock.h
new file mode 100644 (file)
index 0000000..da65076
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef foortclockfoo
+#define foortclockfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/sample.h>
+
+/** \file
+ *  Monotonic clock utilities. */
+
+PA_C_DECL_BEGIN
+
+/** Return the current monotonic system time in usec, if such a clock
+ * is available.  If it is not available this will return the
+ * wallclock time instead.  \since 0.9.16 */
+pa_usec_t pa_rtclock_now(void);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
new file mode 100644 (file)
index 0000000..ff77985
--- /dev/null
@@ -0,0 +1,285 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+
+#include "sample.h"
+
+static const size_t size_table[] = {
+    [PA_SAMPLE_U8] = 1,
+    [PA_SAMPLE_ULAW] = 1,
+    [PA_SAMPLE_ALAW] = 1,
+    [PA_SAMPLE_S16LE] = 2,
+    [PA_SAMPLE_S16BE] = 2,
+    [PA_SAMPLE_FLOAT32LE] = 4,
+    [PA_SAMPLE_FLOAT32BE] = 4,
+    [PA_SAMPLE_S32LE] = 4,
+    [PA_SAMPLE_S32BE] = 4,
+    [PA_SAMPLE_S24LE] = 3,
+    [PA_SAMPLE_S24BE] = 3,
+    [PA_SAMPLE_S24_32LE] = 4,
+    [PA_SAMPLE_S24_32BE] = 4
+};
+
+size_t pa_sample_size_of_format(pa_sample_format_t f) {
+    pa_assert(pa_sample_format_valid(f));
+
+    return size_table[f];
+}
+
+size_t pa_sample_size(const pa_sample_spec *spec) {
+    pa_assert(spec);
+    pa_assert(pa_sample_spec_valid(spec));
+
+    return size_table[spec->format];
+}
+
+size_t pa_frame_size(const pa_sample_spec *spec) {
+    pa_assert(spec);
+    pa_assert(pa_sample_spec_valid(spec));
+
+    return size_table[spec->format] * spec->channels;
+}
+
+size_t pa_bytes_per_second(const pa_sample_spec *spec) {
+    pa_assert(spec);
+    pa_assert(pa_sample_spec_valid(spec));
+
+    return spec->rate * size_table[spec->format] * spec->channels;
+}
+
+pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
+    pa_assert(spec);
+    pa_assert(pa_sample_spec_valid(spec));
+
+    return (((pa_usec_t) (length / (size_table[spec->format] * spec->channels)) * PA_USEC_PER_SEC) / spec->rate);
+}
+
+size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) {
+    pa_assert(spec);
+    pa_assert(pa_sample_spec_valid(spec));
+
+    return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * (size_table[spec->format] * spec->channels);
+}
+
+pa_sample_spec* pa_sample_spec_init(pa_sample_spec *spec) {
+    pa_assert(spec);
+
+    spec->format = PA_SAMPLE_INVALID;
+    spec->rate = 0;
+    spec->channels = 0;
+
+    return spec;
+}
+
+int pa_sample_format_valid(unsigned format) {
+    return format < PA_SAMPLE_MAX;
+}
+
+int pa_sample_rate_valid(uint32_t rate) {
+    /* The extra 1% is due to module-loopback: it temporarily sets
+     * a higher-than-nominal rate to get rid of excessive buffer
+     * latency */
+    return rate > 0 && rate <= PA_RATE_MAX * 101 / 100;
+}
+
+int pa_channels_valid(uint8_t channels) {
+    return channels > 0 && channels <= PA_CHANNELS_MAX;
+}
+
+int pa_sample_spec_valid(const pa_sample_spec *spec) {
+    pa_assert(spec);
+
+    if (PA_UNLIKELY(!pa_sample_rate_valid(spec->rate) ||
+        !pa_channels_valid(spec->channels) ||
+        !pa_sample_format_valid(spec->format)))
+        return 0;
+
+    return 1;
+}
+
+int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    pa_return_val_if_fail(pa_sample_spec_valid(a), 0);
+
+    if (PA_UNLIKELY(a == b))
+        return 1;
+
+    pa_return_val_if_fail(pa_sample_spec_valid(b), 0);
+
+    return
+        (a->format == b->format) &&
+        (a->rate == b->rate) &&
+        (a->channels == b->channels);
+}
+
+const char *pa_sample_format_to_string(pa_sample_format_t f) {
+    static const char* const table[]= {
+        [PA_SAMPLE_U8] = "u8",
+        [PA_SAMPLE_ALAW] = "aLaw",
+        [PA_SAMPLE_ULAW] = "uLaw",
+        [PA_SAMPLE_S16LE] = "s16le",
+        [PA_SAMPLE_S16BE] = "s16be",
+        [PA_SAMPLE_FLOAT32LE] = "float32le",
+        [PA_SAMPLE_FLOAT32BE] = "float32be",
+        [PA_SAMPLE_S32LE] = "s32le",
+        [PA_SAMPLE_S32BE] = "s32be",
+        [PA_SAMPLE_S24LE] = "s24le",
+        [PA_SAMPLE_S24BE] = "s24be",
+        [PA_SAMPLE_S24_32LE] = "s24-32le",
+        [PA_SAMPLE_S24_32BE] = "s24-32be",
+    };
+
+    if (!pa_sample_format_valid(f))
+        return NULL;
+
+    return table[f];
+}
+
+char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
+    pa_assert(s);
+    pa_assert(l > 0);
+    pa_assert(spec);
+
+    pa_init_i18n();
+
+    if (!pa_sample_spec_valid(spec))
+        pa_snprintf(s, l, _("(invalid)"));
+    else
+        pa_snprintf(s, l, _("%s %uch %uHz"), pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
+
+    return s;
+}
+
+char* pa_bytes_snprint(char *s, size_t l, unsigned v) {
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    pa_init_i18n();
+
+    if (v >= ((unsigned) 1024)*1024*1024)
+        pa_snprintf(s, l, _("%0.1f GiB"), ((double) v)/1024/1024/1024);
+    else if (v >= ((unsigned) 1024)*1024)
+        pa_snprintf(s, l, _("%0.1f MiB"), ((double) v)/1024/1024);
+    else if (v >= (unsigned) 1024)
+        pa_snprintf(s, l, _("%0.1f KiB"), ((double) v)/1024);
+    else
+        pa_snprintf(s, l, _("%u B"), (unsigned) v);
+
+    return s;
+}
+
+pa_sample_format_t pa_parse_sample_format(const char *format) {
+    pa_assert(format);
+
+    if (strcasecmp(format, "s16le") == 0)
+        return PA_SAMPLE_S16LE;
+    else if (strcasecmp(format, "s16be") == 0)
+        return PA_SAMPLE_S16BE;
+    else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0)
+        return PA_SAMPLE_S16NE;
+    else if (strcasecmp(format, "s16re") == 0)
+        return PA_SAMPLE_S16RE;
+    else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0)
+        return PA_SAMPLE_U8;
+    else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0 || strcasecmp(format, "float") == 0)
+        return PA_SAMPLE_FLOAT32NE;
+    else if (strcasecmp(format, "float32re") == 0)
+        return PA_SAMPLE_FLOAT32RE;
+    else if (strcasecmp(format, "float32le") == 0)
+        return PA_SAMPLE_FLOAT32LE;
+    else if (strcasecmp(format, "float32be") == 0)
+        return PA_SAMPLE_FLOAT32BE;
+    else if (strcasecmp(format, "ulaw") == 0 || strcasecmp(format, "mulaw") == 0)
+        return PA_SAMPLE_ULAW;
+    else if (strcasecmp(format, "alaw") == 0)
+        return PA_SAMPLE_ALAW;
+    else if (strcasecmp(format, "s32le") == 0)
+        return PA_SAMPLE_S32LE;
+    else if (strcasecmp(format, "s32be") == 0)
+        return PA_SAMPLE_S32BE;
+    else if (strcasecmp(format, "s32ne") == 0 || strcasecmp(format, "s32") == 0 || strcasecmp(format, "32") == 0)
+        return PA_SAMPLE_S32NE;
+    else if (strcasecmp(format, "s32re") == 0)
+        return PA_SAMPLE_S24RE;
+    else if (strcasecmp(format, "s24le") == 0)
+        return PA_SAMPLE_S24LE;
+    else if (strcasecmp(format, "s24be") == 0)
+        return PA_SAMPLE_S24BE;
+    else if (strcasecmp(format, "s24ne") == 0 || strcasecmp(format, "s24") == 0 || strcasecmp(format, "24") == 0)
+        return PA_SAMPLE_S24NE;
+    else if (strcasecmp(format, "s24re") == 0)
+        return PA_SAMPLE_S24RE;
+    else if (strcasecmp(format, "s24-32le") == 0)
+        return PA_SAMPLE_S24_32LE;
+    else if (strcasecmp(format, "s24-32be") == 0)
+        return PA_SAMPLE_S24_32BE;
+    else if (strcasecmp(format, "s24-32ne") == 0 || strcasecmp(format, "s24-32") == 0)
+        return PA_SAMPLE_S24_32NE;
+    else if (strcasecmp(format, "s24-32re") == 0)
+        return PA_SAMPLE_S24_32RE;
+
+    return PA_SAMPLE_INVALID;
+}
+
+int pa_sample_format_is_le(pa_sample_format_t f) {
+    pa_assert(pa_sample_format_valid(f));
+
+    switch (f) {
+        case PA_SAMPLE_S16LE:
+        case PA_SAMPLE_S24LE:
+        case PA_SAMPLE_S32LE:
+        case PA_SAMPLE_S24_32LE:
+        case PA_SAMPLE_FLOAT32LE:
+            return 1;
+
+        case PA_SAMPLE_S16BE:
+        case PA_SAMPLE_S24BE:
+        case PA_SAMPLE_S32BE:
+        case PA_SAMPLE_S24_32BE:
+        case PA_SAMPLE_FLOAT32BE:
+            return 0;
+
+        default:
+            return -1;
+    }
+}
+
+int pa_sample_format_is_be(pa_sample_format_t f) {
+    int r;
+
+    if ((r = pa_sample_format_is_le(f)) < 0)
+        return r;
+
+    return !r;
+}
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
new file mode 100644 (file)
index 0000000..4299eec
--- /dev/null
@@ -0,0 +1,354 @@
+#ifndef foosamplehfoo
+#define foosamplehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <pulse/gccmacro.h>
+#include <pulse/cdecl.h>
+#include <pulse/version.h>
+
+/** \page sample Sample Format Specifications
+ *
+ * \section overv_sec Overview
+ *
+ * PulseAudio is capable of handling a multitude of sample formats, rates
+ * and channels, transparently converting and mixing them as needed.
+ *
+ * \section format_sec Sample Format
+ *
+ * PulseAudio supports the following sample formats:
+ *
+ * \li PA_SAMPLE_U8 - Unsigned 8 bit integer PCM.
+ * \li PA_SAMPLE_S16LE - Signed 16 integer bit PCM, little endian.
+ * \li PA_SAMPLE_S16BE - Signed 16 integer bit PCM, big endian.
+ * \li PA_SAMPLE_FLOAT32LE - 32 bit IEEE floating point PCM, little endian.
+ * \li PA_SAMPLE_FLOAT32BE - 32 bit IEEE floating point PCM, big endian.
+ * \li PA_SAMPLE_ALAW - 8 bit a-Law.
+ * \li PA_SAMPLE_ULAW - 8 bit mu-Law.
+ * \li PA_SAMPLE_S32LE - Signed 32 bit integer PCM, little endian.
+ * \li PA_SAMPLE_S32BE - Signed 32 bit integer PCM, big endian.
+ * \li PA_SAMPLE_S24LE - Signed 24 bit integer PCM packed, little endian.
+ * \li PA_SAMPLE_S24BE - Signed 24 bit integer PCM packed, big endian.
+ * \li PA_SAMPLE_S24_32LE - Signed 24 bit integer PCM in LSB of 32 bit words, little endian.
+ * \li PA_SAMPLE_S24_32BE - Signed 24 bit integer PCM in LSB of 32 bit words, big endian.
+ *
+ * The floating point sample formats have the range from -1.0 to 1.0.
+ *
+ * The sample formats that are sensitive to endianness have convenience
+ * macros for native endian (NE), and reverse endian (RE).
+ *
+ * \section rate_sec Sample Rates
+ *
+ * PulseAudio supports any sample rate between 1 Hz and 192000 Hz. There is no
+ * point trying to exceed the sample rate of the output device though as the
+ * signal will only get downsampled, consuming CPU on the machine running the
+ * server.
+ *
+ * \section chan_sec Channels
+ *
+ * PulseAudio supports up to 32 individual channels. The order of the
+ * channels is up to the application, but they must be continuous. To map
+ * channels to speakers, see \ref channelmap.
+ *
+ * \section calc_sec Calculations
+ *
+ * The PulseAudio library contains a number of convenience functions to do
+ * calculations on sample formats:
+ *
+ * \li pa_bytes_per_second() - The number of bytes one second of audio will
+ *                             take given a sample format.
+ * \li pa_frame_size() - The size, in bytes, of one frame (i.e. one set of
+ *                       samples, one for each channel).
+ * \li pa_sample_size() - The size, in bytes, of one sample.
+ * \li pa_bytes_to_usec() - Calculate the time it would take to play a buffer
+ *                          of a certain size.
+ *
+ * \section util_sec Convenience Functions
+ *
+ * The library also contains a couple of other convenience functions:
+ *
+ * \li pa_sample_spec_valid() - Tests if a sample format specification is
+ *                              valid.
+ * \li pa_sample_spec_equal() - Tests if the sample format specifications are
+ *                              identical.
+ * \li pa_sample_format_to_string() - Return a textual description of a
+ *                                    sample format.
+ * \li pa_parse_sample_format() - Parse a text string into a sample format.
+ * \li pa_sample_spec_snprint() - Create a textual description of a complete
+ *                                 sample format specification.
+ * \li pa_bytes_snprint() - Pretty print a byte value (e.g. 2.5 MiB).
+ */
+
+/** \file
+ * Constants and routines for sample type handling
+ *
+ * See also \subpage sample
+ */
+
+PA_C_DECL_BEGIN
+
+#if !defined(WORDS_BIGENDIAN)
+
+#if defined(__BYTE_ORDER)
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#endif
+
+/* On Sparc, WORDS_BIGENDIAN needs to be set if _BIG_ENDIAN is defined. */
+#if defined(__sparc__) && defined(_BIG_ENDIAN)
+#define WORDS_BIGENDIAN
+#endif
+
+#endif
+
+/** Maximum number of allowed channels */
+#define PA_CHANNELS_MAX 32U
+
+/** Maximum allowed sample rate */
+#define PA_RATE_MAX (48000U*8U)
+
+/** Sample format */
+typedef enum pa_sample_format {
+    PA_SAMPLE_U8,
+    /**< Unsigned 8 Bit PCM */
+
+    PA_SAMPLE_ALAW,
+    /**< 8 Bit a-Law */
+
+    PA_SAMPLE_ULAW,
+    /**< 8 Bit mu-Law */
+
+    PA_SAMPLE_S16LE,
+    /**< Signed 16 Bit PCM, little endian (PC) */
+
+    PA_SAMPLE_S16BE,
+    /**< Signed 16 Bit PCM, big endian */
+
+    PA_SAMPLE_FLOAT32LE,
+    /**< 32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0 */
+
+    PA_SAMPLE_FLOAT32BE,
+    /**< 32 Bit IEEE floating point, big endian, range -1.0 to 1.0 */
+
+    PA_SAMPLE_S32LE,
+    /**< Signed 32 Bit PCM, little endian (PC) */
+
+    PA_SAMPLE_S32BE,
+    /**< Signed 32 Bit PCM, big endian */
+
+    PA_SAMPLE_S24LE,
+    /**< Signed 24 Bit PCM packed, little endian (PC). \since 0.9.15 */
+
+    PA_SAMPLE_S24BE,
+    /**< Signed 24 Bit PCM packed, big endian. \since 0.9.15 */
+
+    PA_SAMPLE_S24_32LE,
+    /**< Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC). \since 0.9.15 */
+
+    PA_SAMPLE_S24_32BE,
+    /**< Signed 24 Bit PCM in LSB of 32 Bit words, big endian. \since 0.9.15 */
+
+    PA_SAMPLE_MAX,
+    /**< Upper limit of valid sample types */
+
+    PA_SAMPLE_INVALID = -1
+    /**< An invalid value */
+} pa_sample_format_t;
+
+#ifdef WORDS_BIGENDIAN
+/** Signed 16 Bit PCM, native endian */
+#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
+/** 32 Bit IEEE floating point, native endian */
+#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
+/** Signed 32 Bit PCM, native endian */
+#define PA_SAMPLE_S32NE PA_SAMPLE_S32BE
+/** Signed 24 Bit PCM packed, native endian. \since 0.9.15 */
+#define PA_SAMPLE_S24NE PA_SAMPLE_S24BE
+/** Signed 24 Bit PCM in LSB of 32 Bit words, native endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32NE PA_SAMPLE_S24_32BE
+
+/** Signed 16 Bit PCM reverse endian */
+#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE
+/** 32 Bit IEEE floating point, reverse endian */
+#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE
+/** Signed 32 Bit PCM, reverse endian */
+#define PA_SAMPLE_S32RE PA_SAMPLE_S32LE
+/** Signed 24 Bit PCM, packed reverse endian. \since 0.9.15 */
+#define PA_SAMPLE_S24RE PA_SAMPLE_S24LE
+/** Signed 24 Bit PCM, in LSB of 32 Bit words, reverse endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32RE PA_SAMPLE_S24_32LE
+#else
+/** Signed 16 Bit PCM, native endian */
+#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
+/** 32 Bit IEEE floating point, native endian */
+#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
+/** Signed 32 Bit PCM, native endian */
+#define PA_SAMPLE_S32NE PA_SAMPLE_S32LE
+/** Signed 24 Bit PCM packed, native endian. \since 0.9.15 */
+#define PA_SAMPLE_S24NE PA_SAMPLE_S24LE
+/** Signed 24 Bit PCM in LSB of 32 Bit words, native endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32NE PA_SAMPLE_S24_32LE
+
+/** Signed 16 Bit PCM, reverse endian */
+#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE
+/** 32 Bit IEEE floating point, reverse endian */
+#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE
+/** Signed 32 Bit PCM, reverse endian */
+#define PA_SAMPLE_S32RE PA_SAMPLE_S32BE
+/** Signed 24 Bit PCM, packed reverse endian. \since 0.9.15 */
+#define PA_SAMPLE_S24RE PA_SAMPLE_S24BE
+/** Signed 24 Bit PCM, in LSB of 32 Bit words, reverse endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32RE PA_SAMPLE_S24_32BE
+#endif
+
+/** A Shortcut for PA_SAMPLE_FLOAT32NE */
+#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE
+
+/** \cond fulldocs */
+/* Allow clients to check with #ifdef for these sample formats */
+#define PA_SAMPLE_U8 PA_SAMPLE_U8
+#define PA_SAMPLE_ALAW PA_SAMPLE_ALAW
+#define PA_SAMPLE_ULAW PA_SAMPLE_ULAW
+#define PA_SAMPLE_S16LE PA_SAMPLE_S16LE
+#define PA_SAMPLE_S16BE PA_SAMPLE_S16BE
+#define PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
+#define PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
+#define PA_SAMPLE_S32LE PA_SAMPLE_S32LE
+#define PA_SAMPLE_S32BE PA_SAMPLE_S32BE
+#define PA_SAMPLE_S24LE PA_SAMPLE_S24LE
+#define PA_SAMPLE_S24BE PA_SAMPLE_S24BE
+#define PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
+#define PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
+/** \endcond */
+
+/** A sample format and attribute specification */
+typedef struct pa_sample_spec {
+    pa_sample_format_t format;
+    /**< The sample format */
+
+    uint32_t rate;
+    /**< The sample rate. (e.g. 44100) */
+
+    uint8_t channels;
+    /**< Audio channels. (1 for mono, 2 for stereo, ...) */
+} pa_sample_spec;
+
+/** Type for usec specifications (unsigned). Always 64 bit. */
+typedef uint64_t pa_usec_t;
+
+/** Return the amount of bytes playback of a second of audio with the specified sample type takes */
+size_t pa_bytes_per_second(const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Return the size of a frame with the specific sample type */
+size_t pa_frame_size(const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Return the size of a sample with the specific sample type */
+size_t pa_sample_size(const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Similar to pa_sample_size() but take a sample format instead of a
+ * full sample spec. \since 0.9.15 */
+size_t pa_sample_size_of_format(pa_sample_format_t f) PA_GCC_PURE;
+
+/** Calculate the time the specified bytes take to play with the
+ * specified sample type. The return value will always be rounded
+ * down for non-integral return values. */
+pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Calculates the number of bytes that are required for the specified
+ * time. The return value will always be rounded down for non-integral
+ * return values. \since 0.9 */
+size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Initialize the specified sample spec and return a pointer to
+ * it. The sample spec will have a defined state but
+ * pa_sample_spec_valid() will fail for it. \since 0.9.13 */
+pa_sample_spec* pa_sample_spec_init(pa_sample_spec *spec);
+
+/** Return non-zero if the given integer is a valid sample format. \since 5.0 */
+int pa_sample_format_valid(unsigned format) PA_GCC_PURE;
+
+/** Return non-zero if the rate is within the supported range. \since 5.0 */
+int pa_sample_rate_valid(uint32_t rate) PA_GCC_PURE;
+
+/** Return non-zero if the channel count is within the supported range.
+ * \since 5.0 */
+int pa_channels_valid(uint8_t channels) PA_GCC_PURE;
+
+/** Return non-zero when the sample type specification is valid */
+int pa_sample_spec_valid(const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Return non-zero when the two sample type specifications match */
+int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) PA_GCC_PURE;
+
+/** Return a descriptive string for the specified sample format. \since 0.8 */
+const char *pa_sample_format_to_string(pa_sample_format_t f) PA_GCC_PURE;
+
+/** Parse a sample format text. Inverse of pa_sample_format_to_string() */
+pa_sample_format_t pa_parse_sample_format(const char *format) PA_GCC_PURE;
+
+/** Maximum required string length for
+ * pa_sample_spec_snprint(). Please note that this value can change
+ * with any release without warning and without being considered API
+ * or ABI breakage. You should not use this definition anywhere where
+ * it might become part of an ABI. */
+#define PA_SAMPLE_SPEC_SNPRINT_MAX 32
+
+/** Pretty print a sample type specification to a string */
+char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec);
+
+/** Maximum required string length for pa_bytes_snprint(). Please note
+ * that this value can change with any release without warning and
+ * without being considered API or ABI breakage. You should not use
+ * this definition anywhere where it might become part of an
+ * ABI. \since 0.9.16 */
+#define PA_BYTES_SNPRINT_MAX 11
+
+/** Pretty print a byte size value (i.e.\ "2.5 MiB") */
+char* pa_bytes_snprint(char *s, size_t l, unsigned v);
+
+/** Return 1 when the specified format is little endian, return -1
+ * when endianness does not apply to this format. \since 0.9.16 */
+int pa_sample_format_is_le(pa_sample_format_t f) PA_GCC_PURE;
+
+/** Return 1 when the specified format is big endian, return -1 when
+ * endianness does not apply to this format. \since 0.9.16 */
+int pa_sample_format_is_be(pa_sample_format_t f) PA_GCC_PURE;
+
+#ifdef WORDS_BIGENDIAN
+#define pa_sample_format_is_ne(f) pa_sample_format_is_be(f)
+#define pa_sample_format_is_re(f) pa_sample_format_is_le(f)
+#else
+/** Return 1 when the specified format is native endian, return -1
+ * when endianness does not apply to this format. \since 0.9.16 */
+#define pa_sample_format_is_ne(f) pa_sample_format_is_le(f)
+/** Return 1 when the specified format is reverse endian, return -1
+ * when endianness does not apply to this format. \since 0.9.16 */
+#define pa_sample_format_is_re(f) pa_sample_format_is_be(f)
+#endif
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
new file mode 100644 (file)
index 0000000..de7b411
--- /dev/null
@@ -0,0 +1,272 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <pulse/utf8.h>
+#include <pulse/fork-detect.h>
+
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/proplist-util.h>
+
+#include "internal.h"
+#include "scache.h"
+
+int pa_stream_connect_upload(pa_stream *s, size_t length) {
+    pa_tagstruct *t;
+    uint32_t tag;
+    const char *name;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, length > 0, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, length == (size_t) (uint32_t) length, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    if (!(name = pa_proplist_gets(s->proplist, PA_PROP_EVENT_ID)))
+        name = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME);
+
+    PA_CHECK_VALIDITY(s->context, name && *name && pa_utf8_valid(name), PA_ERR_INVALID);
+
+    pa_stream_ref(s);
+
+    s->direction = PA_STREAM_UPLOAD;
+    s->flags = 0;
+
+    t = pa_tagstruct_command(s->context, PA_COMMAND_CREATE_UPLOAD_STREAM, &tag);
+
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+    pa_tagstruct_put_channel_map(t, &s->channel_map);
+    pa_tagstruct_putu32(t, (uint32_t) length);
+
+    if (s->context->version >= 13)
+        pa_tagstruct_put_proplist(t, s->proplist);
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
+
+    pa_stream_set_state(s, PA_STREAM_CREATING);
+
+    pa_stream_unref(s);
+    return 0;
+}
+
+int pa_stream_finish_upload(pa_stream *s) {
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    pa_stream_ref(s);
+
+    t = pa_tagstruct_command(s->context, PA_COMMAND_FINISH_UPLOAD_STREAM, &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s, NULL);
+
+    pa_stream_unref(s);
+    return 0;
+}
+
+static void play_sample_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int success = 1;
+    uint32_t idx = PA_INVALID_INDEX;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        success = 0;
+    } else if ((o->context->version >= 13 && pa_tagstruct_getu32(t, &idx) < 0) ||
+               !pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    } else if (o->context->version >= 13 && idx == PA_INVALID_INDEX)
+        success = 0;
+
+    if (o->callback) {
+        pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback;
+        cb(o->context, success, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+static void play_sample_with_proplist_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    uint32_t idx;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        idx = PA_INVALID_INDEX;
+    } else if (pa_tagstruct_getu32(t, &idx) < 0 ||
+               !pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_context_play_sample_cb_t cb = (pa_context_play_sample_cb_t) o->callback;
+        cb(o->context, idx, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    if (!dev)
+        dev = c->conf->default_sink;
+
+    t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, dev);
+
+    if (!PA_VOLUME_IS_VALID(volume) && c->version < 15)
+        volume = PA_VOLUME_NORM;
+
+    pa_tagstruct_putu32(t, volume);
+    pa_tagstruct_puts(t, name);
+
+    if (c->version >= 13) {
+        pa_proplist *p = pa_proplist_new();
+        pa_tagstruct_put_proplist(t, p);
+        pa_proplist_free(p);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_proplist *p, pa_context_play_sample_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    if (!dev)
+        dev = c->conf->default_sink;
+
+    t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, dev);
+
+    if (!PA_VOLUME_IS_VALID(volume) && c->version < 15)
+        volume = PA_VOLUME_NORM;
+
+    pa_tagstruct_putu32(t, volume);
+    pa_tagstruct_puts(t, name);
+
+    if (p)
+        pa_tagstruct_put_proplist(t, p);
+    else {
+        p = pa_proplist_new();
+        pa_tagstruct_put_proplist(t, p);
+        pa_proplist_free(p);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_with_proplist_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_SAMPLE, &tag);
+    pa_tagstruct_puts(t, name);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
diff --git a/src/pulse/scache.h b/src/pulse/scache.h
new file mode 100644 (file)
index 0000000..e799b1d
--- /dev/null
@@ -0,0 +1,124 @@
+#ifndef fooscachehfoo
+#define fooscachehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/context.h>
+#include <pulse/stream.h>
+#include <pulse/cdecl.h>
+#include <pulse/version.h>
+
+/** \page scache Sample Cache
+ *
+ * \section overv_sec Overview
+ *
+ * The sample cache provides a simple way of overcoming high network latencies
+ * and reducing bandwidth. Instead of streaming a sound precisely when it
+ * should be played, it is stored on the server and only the command to start
+ * playing it needs to be sent.
+ *
+ * \section create_sec Creation
+ *
+ * To create a sample, the normal stream API is used (see \ref streams). The
+ * function pa_stream_connect_upload() will make sure the stream is stored as
+ * a sample on the server.
+ *
+ * To complete the upload, pa_stream_finish_upload() is called and the sample
+ * will receive the same name as the stream. If the upload should be aborted,
+ * simply call pa_stream_disconnect().
+ *
+ * \section play_sec Playing samples
+ *
+ * To play back a sample, simply call pa_context_play_sample():
+ *
+ * \code
+ * pa_operation *o;
+ *
+ * o = pa_context_play_sample(my_context,
+ *                            "sample2",       // Name of my sample
+ *                            NULL,            // Use default sink
+ *                            PA_VOLUME_NORM,  // Full volume
+ *                            NULL,            // Don't need a callback
+ *                            NULL
+ *                            );
+ * if (o)
+ *     pa_operation_unref(o);
+ * \endcode
+ *
+ * \section rem_sec Removing samples
+ *
+ * When a sample is no longer needed, it should be removed on the server to
+ * save resources. The sample is deleted using pa_context_remove_sample().
+ */
+
+/** \file
+ * All sample cache related routines
+ *
+ * See also \subpage scache
+ */
+
+PA_C_DECL_BEGIN
+
+/** Callback prototype for pa_context_play_sample_with_proplist(). The
+ * idx value is the index of the sink input object, or
+ * PA_INVALID_INDEX on failure. \since 0.9.11 */
+typedef void (*pa_context_play_sample_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+
+/** Make this stream a sample upload stream */
+int pa_stream_connect_upload(pa_stream *s, size_t length);
+
+/** Finish the sample upload, the stream name will become the sample
+ * name. You cancel a sample upload by issuing
+ * pa_stream_disconnect() */
+int pa_stream_finish_upload(pa_stream *s);
+
+/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
+pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Play a sample from the sample cache to the specified device. If
+ * the latter is NULL use the default sink. Returns an operation
+ * object */
+pa_operation* pa_context_play_sample(
+        pa_context *c               /**< Context */,
+        const char *name            /**< Name of the sample to play */,
+        const char *dev             /**< Sink to play this sample on */,
+        pa_volume_t volume          /**< Volume to play this sample with. Starting with 0.9.15 you may pass here PA_VOLUME_INVALID which will leave the decision about the volume to the server side which is a good idea. */ ,
+        pa_context_success_cb_t cb  /**< Call this function after successfully starting playback, or NULL */,
+        void *userdata              /**< Userdata to pass to the callback */);
+
+/** Play a sample from the sample cache to the specified device,
+ * allowing specification of a property list for the playback
+ * stream. If the latter is NULL use the default sink. Returns an
+ * operation object. \since 0.9.11 */
+pa_operation* pa_context_play_sample_with_proplist(
+        pa_context *c                   /**< Context */,
+        const char *name                /**< Name of the sample to play */,
+        const char *dev                 /**< Sink to play this sample on */,
+        pa_volume_t volume              /**< Volume to play this sample with. Starting with 0.9.15 you may pass here PA_VOLUME_INVALID which will leave the decision about the volume to the server side which is a good idea.  */ ,
+        pa_proplist *proplist           /**< Property list for this sound. The property list of the cached entry will be merged into this property list */,
+        pa_context_play_sample_cb_t cb  /**< Call this function after successfully starting playback, or NULL */,
+        void *userdata                  /**< Userdata to pass to the callback */);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/simple.c b/src/pulse/simple.c
new file mode 100644 (file)
index 0000000..7b66f62
--- /dev/null
@@ -0,0 +1,512 @@
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/thread-mainloop.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "simple.h"
+
+struct pa_simple {
+    pa_threaded_mainloop *mainloop;
+    pa_context *context;
+    pa_stream *stream;
+    pa_stream_direction_t direction;
+
+    const void *read_data;
+    size_t read_index, read_length;
+
+    int operation_success;
+};
+
+#define CHECK_VALIDITY_RETURN_ANY(rerror, expression, error, ret)       \
+    do {                                                                \
+        if (!(expression)) {                                            \
+            if (rerror)                                                 \
+                *(rerror) = error;                                      \
+            return (ret);                                               \
+        }                                                               \
+    } while(false);
+
+#define CHECK_SUCCESS_GOTO(p, rerror, expression, label)        \
+    do {                                                        \
+        if (!(expression)) {                                    \
+            if (rerror)                                         \
+                *(rerror) = pa_context_errno((p)->context);     \
+            goto label;                                         \
+        }                                                       \
+    } while(false);
+
+#define CHECK_DEAD_GOTO(p, rerror, label)                               \
+    do {                                                                \
+        if (!(p)->context || !PA_CONTEXT_IS_GOOD(pa_context_get_state((p)->context)) || \
+            !(p)->stream || !PA_STREAM_IS_GOOD(pa_stream_get_state((p)->stream))) { \
+            if (((p)->context && pa_context_get_state((p)->context) == PA_CONTEXT_FAILED) || \
+                ((p)->stream && pa_stream_get_state((p)->stream) == PA_STREAM_FAILED)) { \
+                if (rerror)                                             \
+                    *(rerror) = pa_context_errno((p)->context);         \
+            } else                                                      \
+                if (rerror)                                             \
+                    *(rerror) = PA_ERR_BADSTATE;                        \
+            goto label;                                                 \
+        }                                                               \
+    } while(false);
+
+static void context_state_cb(pa_context *c, void *userdata) {
+    pa_simple *p = userdata;
+    pa_assert(c);
+    pa_assert(p);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_READY:
+        case PA_CONTEXT_TERMINATED:
+        case PA_CONTEXT_FAILED:
+            pa_threaded_mainloop_signal(p->mainloop, 0);
+            break;
+
+        case PA_CONTEXT_UNCONNECTED:
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+    }
+}
+
+static void stream_state_cb(pa_stream *s, void * userdata) {
+    pa_simple *p = userdata;
+    pa_assert(s);
+    pa_assert(p);
+
+    switch (pa_stream_get_state(s)) {
+
+        case PA_STREAM_READY:
+        case PA_STREAM_FAILED:
+        case PA_STREAM_TERMINATED:
+            pa_threaded_mainloop_signal(p->mainloop, 0);
+            break;
+
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+            break;
+    }
+}
+
+static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
+    pa_simple *p = userdata;
+    pa_assert(p);
+
+    pa_threaded_mainloop_signal(p->mainloop, 0);
+}
+
+static void stream_latency_update_cb(pa_stream *s, void *userdata) {
+    pa_simple *p = userdata;
+
+    pa_assert(p);
+
+    pa_threaded_mainloop_signal(p->mainloop, 0);
+}
+
+pa_simple* pa_simple_new(
+        const char *server,
+        const char *name,
+        pa_stream_direction_t dir,
+        const char *dev,
+        const char *stream_name,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        const pa_buffer_attr *attr,
+        int *rerror) {
+
+    pa_simple *p;
+    int error = PA_ERR_INTERNAL, r;
+
+    CHECK_VALIDITY_RETURN_ANY(rerror, !server || *server, PA_ERR_INVALID, NULL);
+    CHECK_VALIDITY_RETURN_ANY(rerror, dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD, PA_ERR_INVALID, NULL);
+    CHECK_VALIDITY_RETURN_ANY(rerror, !dev || *dev, PA_ERR_INVALID, NULL);
+    CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
+    CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)
+
+    p = pa_xnew0(pa_simple, 1);
+    p->direction = dir;
+
+    if (!(p->mainloop = pa_threaded_mainloop_new()))
+        goto fail;
+
+    if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name)))
+        goto fail;
+
+    pa_context_set_state_callback(p->context, context_state_cb, p);
+
+    if (pa_context_connect(p->context, server, 0, NULL) < 0) {
+        error = pa_context_errno(p->context);
+        goto fail;
+    }
+
+    pa_threaded_mainloop_lock(p->mainloop);
+
+    if (pa_threaded_mainloop_start(p->mainloop) < 0)
+        goto unlock_and_fail;
+
+    for (;;) {
+        pa_context_state_t state;
+
+        state = pa_context_get_state(p->context);
+
+        if (state == PA_CONTEXT_READY)
+            break;
+
+        if (!PA_CONTEXT_IS_GOOD(state)) {
+            error = pa_context_errno(p->context);
+            goto unlock_and_fail;
+        }
+
+        /* Wait until the context is ready */
+        pa_threaded_mainloop_wait(p->mainloop);
+    }
+
+    if (!(p->stream = pa_stream_new(p->context, stream_name, ss, map))) {
+        error = pa_context_errno(p->context);
+        goto unlock_and_fail;
+    }
+
+    pa_stream_set_state_callback(p->stream, stream_state_cb, p);
+    pa_stream_set_read_callback(p->stream, stream_request_cb, p);
+    pa_stream_set_write_callback(p->stream, stream_request_cb, p);
+    pa_stream_set_latency_update_callback(p->stream, stream_latency_update_cb, p);
+
+    if (dir == PA_STREAM_PLAYBACK)
+        r = pa_stream_connect_playback(p->stream, dev, attr,
+                                       PA_STREAM_INTERPOLATE_TIMING
+                                       |PA_STREAM_ADJUST_LATENCY
+                                       |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
+    else
+        r = pa_stream_connect_record(p->stream, dev, attr,
+                                     PA_STREAM_INTERPOLATE_TIMING
+                                     |PA_STREAM_ADJUST_LATENCY
+                                     |PA_STREAM_AUTO_TIMING_UPDATE);
+
+    if (r < 0) {
+        error = pa_context_errno(p->context);
+        goto unlock_and_fail;
+    }
+
+    for (;;) {
+        pa_stream_state_t state;
+
+        state = pa_stream_get_state(p->stream);
+
+        if (state == PA_STREAM_READY)
+            break;
+
+        if (!PA_STREAM_IS_GOOD(state)) {
+            error = pa_context_errno(p->context);
+            goto unlock_and_fail;
+        }
+
+        /* Wait until the stream is ready */
+        pa_threaded_mainloop_wait(p->mainloop);
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+
+    return p;
+
+unlock_and_fail:
+    pa_threaded_mainloop_unlock(p->mainloop);
+
+fail:
+    if (rerror)
+        *rerror = error;
+    pa_simple_free(p);
+    return NULL;
+}
+
+void pa_simple_free(pa_simple *s) {
+    pa_assert(s);
+
+    if (s->mainloop)
+        pa_threaded_mainloop_stop(s->mainloop);
+
+    if (s->stream)
+        pa_stream_unref(s->stream);
+
+    if (s->context) {
+        pa_context_disconnect(s->context);
+        pa_context_unref(s->context);
+    }
+
+    if (s->mainloop)
+        pa_threaded_mainloop_free(s->mainloop);
+
+    pa_xfree(s);
+}
+
+int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {
+    pa_assert(p);
+
+    CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
+    CHECK_VALIDITY_RETURN_ANY(rerror, data, PA_ERR_INVALID, -1);
+    CHECK_VALIDITY_RETURN_ANY(rerror, length > 0, PA_ERR_INVALID, -1);
+
+    pa_threaded_mainloop_lock(p->mainloop);
+
+    CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+    while (length > 0) {
+        size_t l;
+        int r;
+
+        while (!(l = pa_stream_writable_size(p->stream))) {
+            pa_threaded_mainloop_wait(p->mainloop);
+            CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+        }
+
+        CHECK_SUCCESS_GOTO(p, rerror, l != (size_t) -1, unlock_and_fail);
+
+        if (l > length)
+            l = length;
+
+        r = pa_stream_write(p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
+        CHECK_SUCCESS_GOTO(p, rerror, r >= 0, unlock_and_fail);
+
+        data = (const uint8_t*) data + l;
+        length -= l;
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return 0;
+
+unlock_and_fail:
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return -1;
+}
+
+int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
+    pa_assert(p);
+
+    CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1);
+    CHECK_VALIDITY_RETURN_ANY(rerror, data, PA_ERR_INVALID, -1);
+    CHECK_VALIDITY_RETURN_ANY(rerror, length > 0, PA_ERR_INVALID, -1);
+
+    pa_threaded_mainloop_lock(p->mainloop);
+
+    CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+    while (length > 0) {
+        size_t l;
+
+        while (!p->read_data) {
+            int r;
+
+            r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
+            CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail);
+
+            if (p->read_length <= 0) {
+                pa_threaded_mainloop_wait(p->mainloop);
+                CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+            } else if (!p->read_data) {
+                /* There's a hole in the stream, skip it. We could generate
+                 * silence, but that wouldn't work for compressed streams. */
+                r = pa_stream_drop(p->stream);
+                CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail);
+            } else
+                p->read_index = 0;
+        }
+
+        l = p->read_length < length ? p->read_length : length;
+        memcpy(data, (const uint8_t*) p->read_data+p->read_index, l);
+
+        data = (uint8_t*) data + l;
+        length -= l;
+
+        p->read_index += l;
+        p->read_length -= l;
+
+        if (!p->read_length) {
+            int r;
+
+            r = pa_stream_drop(p->stream);
+            p->read_data = NULL;
+            p->read_length = 0;
+            p->read_index = 0;
+
+            CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail);
+        }
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return 0;
+
+unlock_and_fail:
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return -1;
+}
+
+static void success_cb(pa_stream *s, int success, void *userdata) {
+    pa_simple *p = userdata;
+
+    pa_assert(s);
+    pa_assert(p);
+
+    p->operation_success = success;
+    pa_threaded_mainloop_signal(p->mainloop, 0);
+}
+
+int pa_simple_drain(pa_simple *p, int *rerror) {
+    pa_operation *o = NULL;
+
+    pa_assert(p);
+
+    CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
+
+    pa_threaded_mainloop_lock(p->mainloop);
+    CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+    o = pa_stream_drain(p->stream, success_cb, p);
+    CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
+
+    p->operation_success = 0;
+    while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
+        pa_threaded_mainloop_wait(p->mainloop);
+        CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+    }
+    CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
+
+    pa_operation_unref(o);
+    pa_threaded_mainloop_unlock(p->mainloop);
+
+    return 0;
+
+unlock_and_fail:
+
+    if (o) {
+        pa_operation_cancel(o);
+        pa_operation_unref(o);
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return -1;
+}
+
+int pa_simple_flush(pa_simple *p, int *rerror) {
+    pa_operation *o = NULL;
+
+    pa_assert(p);
+
+    pa_threaded_mainloop_lock(p->mainloop);
+    CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+    o = pa_stream_flush(p->stream, success_cb, p);
+    CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
+
+    p->operation_success = 0;
+    while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
+        pa_threaded_mainloop_wait(p->mainloop);
+        CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+    }
+    CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
+
+    pa_operation_unref(o);
+    pa_threaded_mainloop_unlock(p->mainloop);
+
+    return 0;
+
+unlock_and_fail:
+
+    if (o) {
+        pa_operation_cancel(o);
+        pa_operation_unref(o);
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return -1;
+}
+
+pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) {
+    pa_usec_t t;
+
+    pa_assert(p);
+
+    pa_threaded_mainloop_lock(p->mainloop);
+
+    for (;;) {
+        int negative;
+
+        CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+        if (pa_stream_get_latency(p->stream, &t, &negative) >= 0) {
+            if (p->direction == PA_STREAM_RECORD) {
+                pa_usec_t already_read;
+
+                /* pa_simple_read() calls pa_stream_peek() to get the next
+                 * chunk of audio. If the next chunk is larger than what the
+                 * pa_simple_read() caller wanted, the leftover data is stored
+                 * in p->read_data until pa_simple_read() is called again.
+                 * pa_stream_drop() won't be called until the whole chunk has
+                 * been consumed, which means that pa_stream_get_latency() will
+                 * return too large values, because the whole size of the
+                 * partially read chunk is included in the latency. Therefore,
+                 * we need to substract the already-read amount from the
+                 * latency. */
+                already_read = pa_bytes_to_usec(p->read_index, pa_stream_get_sample_spec(p->stream));
+
+                if (!negative) {
+                    if (t > already_read)
+                        t -= already_read;
+                    else
+                        t = 0;
+                }
+            }
+
+            /* We don't have a way to report negative latencies from
+             * pa_simple_get_latency(). If the latency is negative, let's
+             * report zero. */
+            if (negative)
+                t = 0;
+
+            break;
+        }
+
+        CHECK_SUCCESS_GOTO(p, rerror, pa_context_errno(p->context) == PA_ERR_NODATA, unlock_and_fail);
+
+        /* Wait until latency data is available again */
+        pa_threaded_mainloop_wait(p->mainloop);
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+
+    return t;
+
+unlock_and_fail:
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return (pa_usec_t) -1;
+}
diff --git a/src/pulse/simple.h b/src/pulse/simple.h
new file mode 100644 (file)
index 0000000..7b84f71
--- /dev/null
@@ -0,0 +1,159 @@
+#ifndef foosimplehfoo
+#define foosimplehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/def.h>
+#include <pulse/cdecl.h>
+#include <pulse/version.h>
+
+/** \page simple Simple API
+ *
+ * \section overv_sec Overview
+ *
+ * The simple API is designed for applications with very basic sound
+ * playback or capture needs. It can only support a single stream per
+ * connection and has no support for handling of complex features like
+ * events, channel mappings and volume control. It is, however, very simple
+ * to use and quite sufficient for many programs.
+ *
+ * \section conn_sec Connecting
+ *
+ * The first step before using the sound system is to connect to the
+ * server. This is normally done this way:
+ *
+ * \code
+ * pa_simple *s;
+ * pa_sample_spec ss;
+ *
+ * ss.format = PA_SAMPLE_S16NE;
+ * ss.channels = 2;
+ * ss.rate = 44100;
+ *
+ * s = pa_simple_new(NULL,               // Use the default server.
+ *                   "Fooapp",           // Our application's name.
+ *                   PA_STREAM_PLAYBACK,
+ *                   NULL,               // Use the default device.
+ *                   "Music",            // Description of our stream.
+ *                   &ss,                // Our sample format.
+ *                   NULL,               // Use default channel map
+ *                   NULL,               // Use default buffering attributes.
+ *                   NULL,               // Ignore error code.
+ *                   );
+ * \endcode
+ *
+ * At this point a connected object is returned, or NULL if there was a
+ * problem connecting.
+ *
+ * \section transfer_sec Transferring data
+ *
+ * Once the connection is established to the server, data can start flowing.
+ * Using the connection is very similar to the normal read() and write()
+ * system calls. The main difference is that they're called pa_simple_read()
+ * and pa_simple_write(). Note that these operations always block.
+ *
+ * \section ctrl_sec Buffer control
+ *
+ * \li pa_simple_get_latency() - Will return the total latency of
+ *                               the playback or record pipeline, respectively.
+ * \li pa_simple_flush() - Will throw away all data currently in buffers.
+ *
+ * If a playback stream is used then the following operation is available:
+ *
+ * \li pa_simple_drain() - Will wait for all sent data to finish playing.
+ *
+ * \section cleanup_sec Cleanup
+ *
+ * Once playback or capture is complete, the connection should be closed
+ * and resources freed. This is done through:
+ *
+ * \code
+ * pa_simple_free(s);
+ * \endcode
+ */
+
+/** \file
+ * A simple but limited synchronous playback and recording
+ * API. This is a synchronous, simplified wrapper around the standard
+ * asynchronous API.
+ *
+ * See also \subpage simple
+ */
+
+/** \example pacat-simple.c
+ * A simple playback tool using the simple API */
+
+/** \example parec-simple.c
+ * A simple recording tool using the simple API */
+
+PA_C_DECL_BEGIN
+
+/** \struct pa_simple
+ * An opaque simple connection object */
+typedef struct pa_simple pa_simple;
+
+/** Create a new connection to the server. */
+pa_simple* pa_simple_new(
+    const char *server,                 /**< Server name, or NULL for default */
+    const char *name,                   /**< A descriptive name for this client (application name, ...) */
+    pa_stream_direction_t dir,          /**< Open this stream for recording or playback? */
+    const char *dev,                    /**< Sink (resp. source) name, or NULL for default */
+    const char *stream_name,            /**< A descriptive name for this stream (application name, song title, ...) */
+    const pa_sample_spec *ss,           /**< The sample type to use */
+    const pa_channel_map *map,          /**< The channel map to use, or NULL for default */
+    const pa_buffer_attr *attr,         /**< Buffering attributes, or NULL for default */
+    int *error                          /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */
+    );
+
+/** Close and free the connection to the server. The connection object becomes invalid when this is called. */
+void pa_simple_free(pa_simple *s);
+
+/** Write some data to the server. */
+int pa_simple_write(pa_simple *s, const void *data, size_t bytes, int *error);
+
+/** Wait until all data already written is played by the daemon. */
+int pa_simple_drain(pa_simple *s, int *error);
+
+/** Read some data from the server. This function blocks until \a bytes amount
+ * of data has been received from the server, or until an error occurs.
+ * Returns a negative value on failure. */
+int pa_simple_read(
+    pa_simple *s, /**< The connection object. */
+    void *data,   /**< A pointer to a buffer. */
+    size_t bytes, /**< The number of bytes to read. */
+    int *error
+    /**< A pointer where the error code is stored when the function returns
+     * a negative value. It is OK to pass NULL here. */
+    );
+
+/** Return the playback or record latency. */
+pa_usec_t pa_simple_get_latency(pa_simple *s, int *error);
+
+/** Flush the playback or record buffer. This discards any audio in the buffer. */
+int pa_simple_flush(pa_simple *s, int *error);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
new file mode 100644 (file)
index 0000000..ee95757
--- /dev/null
@@ -0,0 +1,2927 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pulse/def.h>
+#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
+#include <pulse/xmalloc.h>
+#include <pulse/fork-detect.h>
+
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+
+#include "internal.h"
+#include "stream.h"
+
+/* #define STREAM_DEBUG */
+
+#define AUTO_TIMING_INTERVAL_START_USEC (10*PA_USEC_PER_MSEC)
+#define AUTO_TIMING_INTERVAL_END_USEC (1500*PA_USEC_PER_MSEC)
+
+#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
+#define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC)
+#define SMOOTHER_MIN_HISTORY (4)
+
+pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
+    return pa_stream_new_with_proplist(c, name, ss, map, NULL);
+}
+
+static void reset_callbacks(pa_stream *s) {
+    s->read_callback = NULL;
+    s->read_userdata = NULL;
+    s->write_callback = NULL;
+    s->write_userdata = NULL;
+    s->state_callback = NULL;
+    s->state_userdata = NULL;
+    s->overflow_callback = NULL;
+    s->overflow_userdata = NULL;
+    s->underflow_callback = NULL;
+    s->underflow_userdata = NULL;
+    s->latency_update_callback = NULL;
+    s->latency_update_userdata = NULL;
+    s->moved_callback = NULL;
+    s->moved_userdata = NULL;
+    s->suspended_callback = NULL;
+    s->suspended_userdata = NULL;
+    s->started_callback = NULL;
+    s->started_userdata = NULL;
+    s->event_callback = NULL;
+    s->event_userdata = NULL;
+    s->buffer_attr_callback = NULL;
+    s->buffer_attr_userdata = NULL;
+}
+
+static pa_stream *pa_stream_new_with_proplist_internal(
+        pa_context *c,
+        const char *name,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_format_info * const *formats,
+        unsigned int n_formats,
+        pa_proplist *p) {
+
+    pa_stream *s;
+    unsigned int i;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert((ss == NULL && map == NULL) || (formats == NULL && n_formats == 0));
+    pa_assert(n_formats < PA_MAX_FORMATS);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
+
+    s = pa_xnew(pa_stream, 1);
+    PA_REFCNT_INIT(s);
+    s->context = c;
+    s->mainloop = c->mainloop;
+
+    s->direction = PA_STREAM_NODIRECTION;
+    s->state = PA_STREAM_UNCONNECTED;
+    s->flags = 0;
+
+    if (ss)
+        s->sample_spec = *ss;
+    else
+        pa_sample_spec_init(&s->sample_spec);
+
+    if (map)
+        s->channel_map = *map;
+    else
+        pa_channel_map_init(&s->channel_map);
+
+    s->n_formats = 0;
+    if (formats) {
+        s->n_formats = n_formats;
+        for (i = 0; i < n_formats; i++)
+            s->req_formats[i] = pa_format_info_copy(formats[i]);
+    }
+
+    /* We'll get the final negotiated format after connecting */
+    s->format = NULL;
+
+    s->direct_on_input = PA_INVALID_INDEX;
+
+    s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
+    if (name)
+        pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name);
+
+    s->channel = 0;
+    s->channel_valid = false;
+    s->syncid = c->csyncid++;
+    s->stream_index = PA_INVALID_INDEX;
+
+    s->requested_bytes = 0;
+    memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
+
+    /* We initialize the target length here, so that if the user
+     * passes no explicit buffering metrics the default is similar to
+     * what older PA versions provided. */
+
+    s->buffer_attr.maxlength = (uint32_t) -1;
+    if (ss)
+        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
+    else {
+        /* FIXME: We assume a worst-case compressed format corresponding to
+         * 48000 Hz, 2 ch, S16 PCM, but this can very well be incorrect */
+        pa_sample_spec tmp_ss = {
+            .format   = PA_SAMPLE_S16NE,
+            .rate     = 48000,
+            .channels = 2,
+        };
+        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &tmp_ss); /* 250ms of buffering */
+    }
+    s->buffer_attr.minreq = (uint32_t) -1;
+    s->buffer_attr.prebuf = (uint32_t) -1;
+    s->buffer_attr.fragsize = (uint32_t) -1;
+
+    s->device_index = PA_INVALID_INDEX;
+    s->device_name = NULL;
+    s->suspended = false;
+    s->corked = false;
+
+    s->write_memblock = NULL;
+    s->write_data = NULL;
+
+    pa_memchunk_reset(&s->peek_memchunk);
+    s->peek_data = NULL;
+    s->record_memblockq = NULL;
+
+    memset(&s->timing_info, 0, sizeof(s->timing_info));
+    s->timing_info_valid = false;
+
+    s->previous_time = 0;
+    s->latest_underrun_at_index = -1;
+
+    s->read_index_not_before = 0;
+    s->write_index_not_before = 0;
+    for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++)
+        s->write_index_corrections[i].valid = 0;
+    s->current_write_index_correction = 0;
+
+    s->auto_timing_update_event = NULL;
+    s->auto_timing_update_requested = false;
+    s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
+
+    reset_callbacks(s);
+
+    s->smoother = NULL;
+
+    /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
+    PA_LLIST_PREPEND(pa_stream, c->streams, s);
+    pa_stream_ref(s);
+
+    return s;
+}
+
+pa_stream *pa_stream_new_with_proplist(
+        pa_context *c,
+        const char *name,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_proplist *p) {
+
+    pa_channel_map tmap;
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
+
+    if (!map)
+        PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
+
+    return pa_stream_new_with_proplist_internal(c, name, ss, map, NULL, 0, p);
+}
+
+pa_stream *pa_stream_new_extended(
+        pa_context *c,
+        const char *name,
+        pa_format_info * const *formats,
+        unsigned int n_formats,
+        pa_proplist *p) {
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 21, PA_ERR_NOTSUPPORTED);
+
+    return pa_stream_new_with_proplist_internal(c, name, NULL, NULL, formats, n_formats, p);
+}
+
+static void stream_unlink(pa_stream *s) {
+    pa_operation *o, *n;
+    pa_assert(s);
+
+    if (!s->context)
+        return;
+
+    /* Detach from context */
+
+    /* Unref all operation objects that point to us */
+    for (o = s->context->operations; o; o = n) {
+        n = o->next;
+
+        if (o->stream == s)
+            pa_operation_cancel(o);
+    }
+
+    /* Drop all outstanding replies for this stream */
+    if (s->context->pdispatch)
+        pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+
+    if (s->channel_valid) {
+        pa_hashmap_remove((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, PA_UINT32_TO_PTR(s->channel));
+        s->channel = 0;
+        s->channel_valid = false;
+    }
+
+    PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
+    pa_stream_unref(s);
+
+    s->context = NULL;
+
+    if (s->auto_timing_update_event) {
+        pa_assert(s->mainloop);
+        s->mainloop->time_free(s->auto_timing_update_event);
+    }
+
+    reset_callbacks(s);
+}
+
+static void stream_free(pa_stream *s) {
+    unsigned int i;
+
+    pa_assert(s);
+
+    stream_unlink(s);
+
+    if (s->write_memblock) {
+        if (s->write_data)
+            pa_memblock_release(s->write_memblock);
+        pa_memblock_unref(s->write_memblock);
+    }
+
+    if (s->peek_memchunk.memblock) {
+        if (s->peek_data)
+            pa_memblock_release(s->peek_memchunk.memblock);
+        pa_memblock_unref(s->peek_memchunk.memblock);
+    }
+
+    if (s->record_memblockq)
+        pa_memblockq_free(s->record_memblockq);
+
+    if (s->proplist)
+        pa_proplist_free(s->proplist);
+
+    if (s->smoother)
+        pa_smoother_free(s->smoother);
+
+    for (i = 0; i < s->n_formats; i++)
+        pa_format_info_free(s->req_formats[i]);
+
+    if (s->format)
+        pa_format_info_free(s->format);
+
+    pa_xfree(s->device_name);
+    pa_xfree(s);
+}
+
+void pa_stream_unref(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (PA_REFCNT_DEC(s) <= 0)
+        stream_free(s);
+}
+
+pa_stream* pa_stream_ref(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_REFCNT_INC(s);
+    return s;
+}
+
+pa_stream_state_t pa_stream_get_state(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    return s->state;
+}
+
+pa_context* pa_stream_get_context(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    return s->context;
+}
+
+uint32_t pa_stream_get_index(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, !pa_detect_fork(), PA_ERR_FORKED, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+
+    return s->stream_index;
+}
+
+void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == st)
+        return;
+
+    pa_stream_ref(s);
+
+    s->state = st;
+
+    if (s->state_callback)
+        s->state_callback(s, s->state_userdata);
+
+    if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED))
+        stream_unlink(s);
+
+    pa_stream_unref(s);
+}
+
+static void request_auto_timing_update(pa_stream *s, bool force) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
+        return;
+
+    if (s->state == PA_STREAM_READY &&
+        (force || !s->auto_timing_update_requested)) {
+        pa_operation *o;
+
+#ifdef STREAM_DEBUG
+        pa_log_debug("Automatically requesting new timing data");
+#endif
+
+        if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
+            pa_operation_unref(o);
+            s->auto_timing_update_requested = true;
+        }
+    }
+
+    if (s->auto_timing_update_event) {
+        if (s->suspended && !force) {
+            pa_assert(s->mainloop);
+            s->mainloop->time_free(s->auto_timing_update_event);
+            s->auto_timing_update_event = NULL;
+        } else {
+            if (force)
+                s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
+
+            pa_context_rttime_restart(s->context, s->auto_timing_update_event, pa_rtclock_now() + s->auto_timing_interval_usec);
+
+            s->auto_timing_interval_usec = PA_MIN(AUTO_TIMING_INTERVAL_END_USEC, s->auto_timing_interval_usec*2);
+        }
+    }
+}
+
+void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_hashmap_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, PA_UINT32_TO_PTR(channel))))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    pa_context_set_error(c, PA_ERR_KILLED);
+    pa_stream_set_state(s, PA_STREAM_FAILED);
+
+finish:
+    pa_context_unref(c);
+}
+
+static void check_smoother_status(pa_stream *s, bool aposteriori, bool force_start, bool force_stop) {
+    pa_usec_t x;
+
+    pa_assert(s);
+    pa_assert(!force_start || !force_stop);
+
+    if (!s->smoother)
+        return;
+
+    x = pa_rtclock_now();
+
+    if (s->timing_info_valid) {
+        if (aposteriori)
+            x -= s->timing_info.transport_usec;
+        else
+            x += s->timing_info.transport_usec;
+    }
+
+    if (s->suspended || s->corked || force_stop)
+        pa_smoother_pause(s->smoother, x);
+    else if (force_start || s->buffer_attr.prebuf == 0) {
+
+        if (!s->timing_info_valid &&
+            !aposteriori &&
+            !force_start &&
+            !force_stop &&
+            s->context->version >= 13) {
+
+            /* If the server supports STARTED events we take them as
+             * indications when audio really starts/stops playing, if
+             * we don't have any timing info yet -- instead of trying
+             * to be smart and guessing the server time. Otherwise the
+             * unknown transport delay adds too much noise to our time
+             * calculations. */
+
+            return;
+        }
+
+        pa_smoother_resume(s->smoother, x, true);
+    }
+
+    /* Please note that we have no idea if playback actually started
+     * if prebuf is non-zero! */
+}
+
+static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata);
+
+void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+    const char *dn;
+    bool suspended;
+    uint32_t di;
+    pa_usec_t usec = 0;
+    uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 12) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_getu32(t, &di) < 0 ||
+        pa_tagstruct_gets(t, &dn) < 0 ||
+        pa_tagstruct_get_boolean(t, &suspended) < 0) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (c->version >= 13) {
+
+        if (command == PA_COMMAND_RECORD_STREAM_MOVED) {
+            if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
+                pa_tagstruct_getu32(t, &fragsize) < 0 ||
+                pa_tagstruct_get_usec(t, &usec) < 0) {
+                pa_context_fail(c, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        } else {
+            if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
+                pa_tagstruct_getu32(t, &tlength) < 0 ||
+                pa_tagstruct_getu32(t, &prebuf) < 0 ||
+                pa_tagstruct_getu32(t, &minreq) < 0 ||
+                pa_tagstruct_get_usec(t, &usec) < 0) {
+                pa_context_fail(c, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!dn || di == PA_INVALID_INDEX) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_hashmap_get(command == PA_COMMAND_PLAYBACK_STREAM_MOVED ? c->playback_streams : c->record_streams, PA_UINT32_TO_PTR(channel))))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    if (c->version >= 13) {
+        if (s->direction == PA_STREAM_RECORD)
+            s->timing_info.configured_source_usec = usec;
+        else
+            s->timing_info.configured_sink_usec = usec;
+
+        s->buffer_attr.maxlength = maxlength;
+        s->buffer_attr.fragsize = fragsize;
+        s->buffer_attr.tlength = tlength;
+        s->buffer_attr.prebuf = prebuf;
+        s->buffer_attr.minreq = minreq;
+    }
+
+    pa_xfree(s->device_name);
+    s->device_name = pa_xstrdup(dn);
+    s->device_index = di;
+
+    s->suspended = suspended;
+
+    if ((s->flags & PA_STREAM_AUTO_TIMING_UPDATE) && !suspended && !s->auto_timing_update_event) {
+        s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
+        s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
+        request_auto_timing_update(s, true);
+    }
+
+    check_smoother_status(s, true, false, false);
+    request_auto_timing_update(s, true);
+
+    if (s->moved_callback)
+        s->moved_callback(s, s->moved_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+void pa_command_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+    pa_usec_t usec = 0;
+    uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED || command == PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 15) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &channel) < 0) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (command == PA_COMMAND_RECORD_STREAM_MOVED) {
+        if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
+            pa_tagstruct_getu32(t, &fragsize) < 0 ||
+            pa_tagstruct_get_usec(t, &usec) < 0) {
+            pa_context_fail(c, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    } else {
+        if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
+            pa_tagstruct_getu32(t, &tlength) < 0 ||
+            pa_tagstruct_getu32(t, &prebuf) < 0 ||
+            pa_tagstruct_getu32(t, &minreq) < 0 ||
+            pa_tagstruct_get_usec(t, &usec) < 0) {
+            pa_context_fail(c, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_hashmap_get(command == PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED ? c->playback_streams : c->record_streams, PA_UINT32_TO_PTR(channel))))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    if (s->direction == PA_STREAM_RECORD)
+        s->timing_info.configured_source_usec = usec;
+    else
+        s->timing_info.configured_sink_usec = usec;
+
+    s->buffer_attr.maxlength = maxlength;
+    s->buffer_attr.fragsize = fragsize;
+    s->buffer_attr.tlength = tlength;
+    s->buffer_attr.prebuf = prebuf;
+    s->buffer_attr.minreq = minreq;
+
+    request_auto_timing_update(s, true);
+
+    if (s->buffer_attr_callback)
+        s->buffer_attr_callback(s, s->buffer_attr_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+    bool suspended;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED || command == PA_COMMAND_RECORD_STREAM_SUSPENDED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 12) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_get_boolean(t, &suspended) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_hashmap_get(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED ? c->playback_streams : c->record_streams, PA_UINT32_TO_PTR(channel))))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    s->suspended = suspended;
+
+    if ((s->flags & PA_STREAM_AUTO_TIMING_UPDATE) && !suspended && !s->auto_timing_update_event) {
+        s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
+        s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
+        request_auto_timing_update(s, true);
+    }
+
+    check_smoother_status(s, true, false, false);
+    request_auto_timing_update(s, true);
+
+    if (s->suspended_callback)
+        s->suspended_callback(s, s->suspended_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_STARTED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 13) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(channel))))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    check_smoother_status(s, true, true, false);
+    request_auto_timing_update(s, true);
+
+    if (s->started_callback)
+        s->started_callback(s, s->started_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+    pa_proplist *pl = NULL;
+    const char *event;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_EVENT || command == PA_COMMAND_RECORD_STREAM_EVENT);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 15) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    pl = pa_proplist_new();
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_gets(t, &event) < 0 ||
+        pa_tagstruct_get_proplist(t, pl) < 0 ||
+        !pa_tagstruct_eof(t) || !event) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_hashmap_get(command == PA_COMMAND_PLAYBACK_STREAM_EVENT ? c->playback_streams : c->record_streams, PA_UINT32_TO_PTR(channel))))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    if (pa_streq(event, PA_STREAM_EVENT_FORMAT_LOST)) {
+        /* Let client know what the running time was when the stream had to be killed  */
+        pa_usec_t stream_time;
+        if (pa_stream_get_time(s, &stream_time) == 0)
+            pa_proplist_setf(pl, "stream-time", "%llu", (unsigned long long) stream_time);
+    }
+
+    if (s->event_callback)
+        s->event_callback(s, event, pl, s->event_userdata);
+
+finish:
+    pa_context_unref(c);
+
+    if (pl)
+        pa_proplist_free(pl);
+}
+
+void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_stream *s;
+    pa_context *c = userdata;
+    uint32_t bytes, channel;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_REQUEST);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_getu32(t, &bytes) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(channel))))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    s->requested_bytes += bytes;
+
+#ifdef STREAM_DEBUG
+    pa_log_debug("got request for %lli, now at %lli", (long long) bytes, (long long) s->requested_bytes);
+#endif
+
+    if (s->requested_bytes > 0 && s->write_callback)
+        s->write_callback(s, (size_t) s->requested_bytes, s->write_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+int64_t pa_stream_get_underflow_index(pa_stream *p) {
+    pa_assert(p);
+    return p->latest_underrun_at_index;
+}
+
+void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_stream *s;
+    pa_context *c = userdata;
+    uint32_t channel;
+    int64_t offset = -1;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (c->version >= 23 && command == PA_COMMAND_UNDERFLOW) {
+        if (pa_tagstruct_gets64(t, &offset) < 0) {
+            pa_context_fail(c, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(channel))))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    if (offset != -1)
+        s->latest_underrun_at_index = offset;
+
+    if (s->buffer_attr.prebuf > 0)
+        check_smoother_status(s, true, false, true);
+
+    request_auto_timing_update(s, true);
+
+    if (command == PA_COMMAND_OVERFLOW) {
+        if (s->overflow_callback)
+            s->overflow_callback(s, s->overflow_userdata);
+    } else if (command == PA_COMMAND_UNDERFLOW) {
+        if (s->underflow_callback)
+            s->underflow_callback(s, s->underflow_userdata);
+    }
+
+finish:
+    pa_context_unref(c);
+}
+
+static void invalidate_indexes(pa_stream *s, bool r, bool w) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+#ifdef STREAM_DEBUG
+    pa_log_debug("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag);
+#endif
+
+    if (s->state != PA_STREAM_READY)
+        return;
+
+    if (w) {
+        s->write_index_not_before = s->context->ctag;
+
+        if (s->timing_info_valid)
+            s->timing_info.write_index_corrupt = true;
+
+#ifdef STREAM_DEBUG
+        pa_log_debug("write_index invalidated");
+#endif
+    }
+
+    if (r) {
+        s->read_index_not_before = s->context->ctag;
+
+        if (s->timing_info_valid)
+            s->timing_info.read_index_corrupt = true;
+
+#ifdef STREAM_DEBUG
+        pa_log_debug("read_index invalidated");
+#endif
+    }
+
+    request_auto_timing_update(s, true);
+}
+
+static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
+    pa_stream *s = userdata;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    pa_stream_ref(s);
+    request_auto_timing_update(s, false);
+    pa_stream_unref(s);
+}
+
+static void create_stream_complete(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(s->state == PA_STREAM_CREATING);
+
+    pa_stream_set_state(s, PA_STREAM_READY);
+
+    if (s->requested_bytes > 0 && s->write_callback)
+        s->write_callback(s, (size_t) s->requested_bytes, s->write_userdata);
+
+    if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {
+        s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
+        pa_assert(!s->auto_timing_update_event);
+        s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
+
+        request_auto_timing_update(s, true);
+    }
+
+    check_smoother_status(s, true, false, false);
+}
+
+static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flags_t *flags) {
+    const char *e;
+
+    pa_assert(s);
+    pa_assert(attr);
+
+    if ((e = getenv("PULSE_LATENCY_MSEC"))) {
+        uint32_t ms;
+        pa_sample_spec ss;
+
+        pa_sample_spec_init(&ss);
+
+        if (pa_sample_spec_valid(&s->sample_spec))
+            ss = s->sample_spec;
+        else if (s->n_formats == 1)
+            pa_format_info_to_sample_spec(s->req_formats[0], &ss, NULL);
+
+        if (pa_atou(e, &ms) < 0 || ms <= 0)
+            pa_log_debug("Failed to parse $PULSE_LATENCY_MSEC: %s", e);
+        else if (!pa_sample_spec_valid(&s->sample_spec))
+            pa_log_debug("Ignoring $PULSE_LATENCY_MSEC: %s (invalid sample spec)", e);
+        else {
+            attr->maxlength = (uint32_t) -1;
+            attr->tlength = pa_usec_to_bytes(ms * PA_USEC_PER_MSEC, &ss);
+            attr->minreq = (uint32_t) -1;
+            attr->prebuf = (uint32_t) -1;
+            attr->fragsize = attr->tlength;
+
+            if (flags)
+                *flags |= PA_STREAM_ADJUST_LATENCY;
+        }
+    }
+
+    if (s->context->version >= 13)
+        return;
+
+    /* Version older than 0.9.10 didn't do server side buffer_attr
+     * selection, hence we have to fake it on the client side. */
+
+    /* We choose fairly conservative values here, to not confuse
+     * old clients with extremely large playback buffers */
+
+    if (attr->maxlength == (uint32_t) -1)
+        attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */
+
+    if (attr->tlength == (uint32_t) -1)
+        attr->tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &s->sample_spec); /* 250ms of buffering */
+
+    if (attr->minreq == (uint32_t) -1)
+        attr->minreq = (attr->tlength)/5; /* Ask for more data when there are only 200ms left in the playback buffer */
+
+    if (attr->prebuf == (uint32_t) -1)
+        attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */
+
+    if (attr->fragsize == (uint32_t) -1)
+        attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */
+}
+
+void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_stream *s = userdata;
+    uint32_t requested_bytes = 0;
+
+    pa_assert(pd);
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(s->state == PA_STREAM_CREATING);
+
+    pa_stream_ref(s);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(s->context, command, t, false) < 0)
+            goto finish;
+
+        pa_stream_set_state(s, PA_STREAM_FAILED);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
+        s->channel == PA_INVALID_INDEX ||
+        ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 || s->stream_index == PA_INVALID_INDEX)) ||
+        ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &requested_bytes) < 0)) {
+        pa_context_fail(s->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    s->requested_bytes = (int64_t) requested_bytes;
+
+    if (s->context->version >= 9) {
+        if (s->direction == PA_STREAM_PLAYBACK) {
+            if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
+                pa_tagstruct_getu32(t, &s->buffer_attr.tlength) < 0 ||
+                pa_tagstruct_getu32(t, &s->buffer_attr.prebuf) < 0 ||
+                pa_tagstruct_getu32(t, &s->buffer_attr.minreq) < 0) {
+                pa_context_fail(s->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        } else if (s->direction == PA_STREAM_RECORD) {
+            if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
+                pa_tagstruct_getu32(t, &s->buffer_attr.fragsize) < 0) {
+                pa_context_fail(s->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        }
+    }
+
+    if (s->context->version >= 12 && s->direction != PA_STREAM_UPLOAD) {
+        pa_sample_spec ss;
+        pa_channel_map cm;
+        const char *dn = NULL;
+        bool suspended;
+
+        if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+            pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+            pa_tagstruct_getu32(t, &s->device_index) < 0 ||
+            pa_tagstruct_gets(t, &dn) < 0 ||
+            pa_tagstruct_get_boolean(t, &suspended) < 0) {
+            pa_context_fail(s->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        if (!dn || s->device_index == PA_INVALID_INDEX ||
+            ss.channels != cm.channels ||
+            !pa_channel_map_valid(&cm) ||
+            !pa_sample_spec_valid(&ss) ||
+            (s->n_formats == 0 && (
+                (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
+                (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
+                (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))))) {
+            pa_context_fail(s->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        pa_xfree(s->device_name);
+        s->device_name = pa_xstrdup(dn);
+        s->suspended = suspended;
+
+        s->channel_map = cm;
+        s->sample_spec = ss;
+    }
+
+    if (s->context->version >= 13 && s->direction != PA_STREAM_UPLOAD) {
+        pa_usec_t usec;
+
+        if (pa_tagstruct_get_usec(t, &usec) < 0) {
+            pa_context_fail(s->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        if (s->direction == PA_STREAM_RECORD)
+            s->timing_info.configured_source_usec = usec;
+        else
+            s->timing_info.configured_sink_usec = usec;
+    }
+
+    if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK)
+        || s->context->version >= 22) {
+
+        pa_format_info *f = pa_format_info_new();
+
+        if (pa_tagstruct_get_format_info(t, f) < 0 || !pa_format_info_valid(f)) {
+            pa_format_info_free(f);
+            if (s->n_formats > 0) {
+                /* We used the extended API, so we should have got back a proper format */
+                pa_context_fail(s->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        } else
+            s->format = f;
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(s->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (s->direction == PA_STREAM_RECORD) {
+        pa_assert(!s->record_memblockq);
+
+        s->record_memblockq = pa_memblockq_new(
+                "client side record memblockq",
+                0,
+                s->buffer_attr.maxlength,
+                0,
+                &s->sample_spec,
+                1,
+                0,
+                0,
+                NULL);
+    }
+
+    s->channel_valid = true;
+    pa_hashmap_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, PA_UINT32_TO_PTR(s->channel), s);
+
+    create_stream_complete(s);
+
+finish:
+    pa_stream_unref(s);
+}
+
+static int create_stream(
+        pa_stream_direction_t direction,
+        pa_stream *s,
+        const char *dev,
+        const pa_buffer_attr *attr,
+        pa_stream_flags_t flags,
+        const pa_cvolume *volume,
+        pa_stream *sync_stream) {
+
+    pa_tagstruct *t;
+    uint32_t tag;
+    bool volume_set = !!volume;
+    pa_cvolume cv;
+    uint32_t i;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(direction == PA_STREAM_PLAYBACK || direction == PA_STREAM_RECORD);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direct_on_input == PA_INVALID_INDEX || direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED|
+                                              PA_STREAM_INTERPOLATE_TIMING|
+                                              PA_STREAM_NOT_MONOTONIC|
+                                              PA_STREAM_AUTO_TIMING_UPDATE|
+                                              PA_STREAM_NO_REMAP_CHANNELS|
+                                              PA_STREAM_NO_REMIX_CHANNELS|
+                                              PA_STREAM_FIX_FORMAT|
+                                              PA_STREAM_FIX_RATE|
+                                              PA_STREAM_FIX_CHANNELS|
+                                              PA_STREAM_DONT_MOVE|
+                                              PA_STREAM_VARIABLE_RATE|
+                                              PA_STREAM_PEAK_DETECT|
+                                              PA_STREAM_START_MUTED|
+                                              PA_STREAM_ADJUST_LATENCY|
+                                              PA_STREAM_EARLY_REQUESTS|
+                                              PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND|
+                                              PA_STREAM_START_UNMUTED|
+                                              PA_STREAM_FAIL_ON_SUSPEND|
+                                              PA_STREAM_RELATIVE_VOLUME|
+                                              PA_STREAM_PASSTHROUGH)), PA_ERR_INVALID);
+
+    PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    /* Although some of the other flags are not supported on older
+     * version, we don't check for them here, because it doesn't hurt
+     * when they are passed but actually not supported. This makes
+     * client development easier */
+
+    PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, (flags & (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS)) != (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS), PA_ERR_INVALID);
+
+    pa_stream_ref(s);
+
+    s->direction = direction;
+
+    if (sync_stream)
+        s->syncid = sync_stream->syncid;
+
+    if (attr)
+        s->buffer_attr = *attr;
+    patch_buffer_attr(s, &s->buffer_attr, &flags);
+
+    s->flags = flags;
+    s->corked = !!(flags & PA_STREAM_START_CORKED);
+
+    if (flags & PA_STREAM_INTERPOLATE_TIMING) {
+        pa_usec_t x;
+
+        x = pa_rtclock_now();
+
+        pa_assert(!s->smoother);
+        s->smoother = pa_smoother_new(
+                SMOOTHER_ADJUST_TIME,
+                SMOOTHER_HISTORY_TIME,
+                !(flags & PA_STREAM_NOT_MONOTONIC),
+                true,
+                SMOOTHER_MIN_HISTORY,
+                x,
+                true);
+    }
+
+    if (!dev)
+        dev = s->direction == PA_STREAM_PLAYBACK ? s->context->conf->default_sink : s->context->conf->default_source;
+
+    t = pa_tagstruct_command(
+            s->context,
+            (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM),
+            &tag);
+
+    if (s->context->version < 13)
+        pa_tagstruct_puts(t, pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME));
+
+    pa_tagstruct_put(
+            t,
+            PA_TAG_SAMPLE_SPEC, &s->sample_spec,
+            PA_TAG_CHANNEL_MAP, &s->channel_map,
+            PA_TAG_U32, PA_INVALID_INDEX,
+            PA_TAG_STRING, dev,
+            PA_TAG_U32, s->buffer_attr.maxlength,
+            PA_TAG_BOOLEAN, s->corked,
+            PA_TAG_INVALID);
+
+    if (!volume) {
+        if (pa_sample_spec_valid(&s->sample_spec))
+            volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
+        else {
+            /* This is not really relevant, since no volume was set, and
+             * the real number of channels is embedded in the format_info
+             * structure */
+            volume = pa_cvolume_reset(&cv, PA_CHANNELS_MAX);
+        }
+    }
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+        pa_tagstruct_put(
+                t,
+                PA_TAG_U32, s->buffer_attr.tlength,
+                PA_TAG_U32, s->buffer_attr.prebuf,
+                PA_TAG_U32, s->buffer_attr.minreq,
+                PA_TAG_U32, s->syncid,
+                PA_TAG_INVALID);
+
+        pa_tagstruct_put_cvolume(t, volume);
+    } else
+        pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
+
+    if (s->context->version >= 12) {
+        pa_tagstruct_put(
+                t,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMAP_CHANNELS,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMIX_CHANNELS,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_FORMAT,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_RATE,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_CHANNELS,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_DONT_MOVE,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_VARIABLE_RATE,
+                PA_TAG_INVALID);
+    }
+
+    if (s->context->version >= 13) {
+
+        if (s->direction == PA_STREAM_PLAYBACK)
+            pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED);
+        else
+            pa_tagstruct_put_boolean(t, flags & PA_STREAM_PEAK_DETECT);
+
+        pa_tagstruct_put(
+                t,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_ADJUST_LATENCY,
+                PA_TAG_PROPLIST, s->proplist,
+                PA_TAG_INVALID);
+
+        if (s->direction == PA_STREAM_RECORD)
+            pa_tagstruct_putu32(t, s->direct_on_input);
+    }
+
+    if (s->context->version >= 14) {
+
+        if (s->direction == PA_STREAM_PLAYBACK)
+            pa_tagstruct_put_boolean(t, volume_set);
+
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_EARLY_REQUESTS);
+    }
+
+    if (s->context->version >= 15) {
+
+        if (s->direction == PA_STREAM_PLAYBACK)
+            pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED));
+
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND);
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_FAIL_ON_SUSPEND);
+    }
+
+    if (s->context->version >= 17 && s->direction == PA_STREAM_PLAYBACK)
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
+
+    if (s->context->version >= 18 && s->direction == PA_STREAM_PLAYBACK)
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
+
+    if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK)
+        || s->context->version >= 22) {
+
+        pa_tagstruct_putu8(t, s->n_formats);
+        for (i = 0; i < s->n_formats; i++)
+            pa_tagstruct_put_format_info(t, s->req_formats[i]);
+    }
+
+    if (s->context->version >= 22 && s->direction == PA_STREAM_RECORD) {
+        pa_tagstruct_put_cvolume(t, volume);
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED);
+        pa_tagstruct_put_boolean(t, volume_set);
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED));
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
+    }
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
+
+    pa_stream_set_state(s, PA_STREAM_CREATING);
+
+    pa_stream_unref(s);
+    return 0;
+}
+
+int pa_stream_connect_playback(
+        pa_stream *s,
+        const char *dev,
+        const pa_buffer_attr *attr,
+        pa_stream_flags_t flags,
+        const pa_cvolume *volume,
+        pa_stream *sync_stream) {
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream);
+}
+
+int pa_stream_connect_record(
+        pa_stream *s,
+        const char *dev,
+        const pa_buffer_attr *attr,
+        pa_stream_flags_t flags) {
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);
+}
+
+int pa_stream_begin_write(
+        pa_stream *s,
+        void **data,
+        size_t *nbytes) {
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, data, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID);
+
+    if (*nbytes != (size_t) -1) {
+        size_t m, fs;
+
+        m = pa_mempool_block_size_max(s->context->mempool);
+        fs = pa_frame_size(&s->sample_spec);
+
+        m = (m / fs) * fs;
+        if (*nbytes > m)
+            *nbytes = m;
+    }
+
+    if (!s->write_memblock) {
+        s->write_memblock = pa_memblock_new(s->context->mempool, *nbytes);
+        s->write_data = pa_memblock_acquire(s->write_memblock);
+    }
+
+    *data = s->write_data;
+    *nbytes = pa_memblock_get_length(s->write_memblock);
+
+    return 0;
+}
+
+int pa_stream_cancel_write(
+        pa_stream *s) {
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->write_memblock, PA_ERR_BADSTATE);
+
+    pa_assert(s->write_data);
+
+    pa_memblock_release(s->write_memblock);
+    pa_memblock_unref(s->write_memblock);
+    s->write_memblock = NULL;
+    s->write_data = NULL;
+
+    return 0;
+}
+
+int pa_stream_write_ext_free(
+        pa_stream *s,
+        const void *data,
+        size_t length,
+        pa_free_cb_t free_cb,
+        void *free_cb_data,
+        int64_t offset,
+        pa_seek_mode_t seek) {
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(data);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, seek <= PA_SEEK_RELATIVE_END, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || (seek == PA_SEEK_RELATIVE && offset == 0), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context,
+                      !s->write_memblock ||
+                      ((data >= s->write_data) &&
+                       ((const char*) data + length <= (const char*) s->write_data + pa_memblock_get_length(s->write_memblock))),
+                      PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, offset % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, length % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, !free_cb || !s->write_memblock, PA_ERR_INVALID);
+
+    if (s->write_memblock) {
+        pa_memchunk chunk;
+
+        /* pa_stream_write_begin() was called before */
+
+        pa_memblock_release(s->write_memblock);
+
+        chunk.memblock = s->write_memblock;
+        chunk.index = (const char *) data - (const char *) s->write_data;
+        chunk.length = length;
+
+        s->write_memblock = NULL;
+        s->write_data = NULL;
+
+        pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk);
+        pa_memblock_unref(chunk.memblock);
+
+    } else {
+        pa_seek_mode_t t_seek = seek;
+        int64_t t_offset = offset;
+        size_t t_length = length;
+        const void *t_data = data;
+
+        /* pa_stream_write_begin() was not called before */
+
+        while (t_length > 0) {
+            pa_memchunk chunk;
+
+            chunk.index = 0;
+
+            if (free_cb && !pa_pstream_get_shm(s->context->pstream)) {
+                chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, free_cb_data, 1);
+                chunk.length = t_length;
+            } else {
+                void *d;
+                size_t blk_size_max;
+
+                /* Break large audio streams into _aligned_ blocks or the
+                 * other endpoint will happily discard them upon arrival. */
+                blk_size_max = pa_frame_align(pa_mempool_block_size_max(s->context->mempool), &s->sample_spec);
+                chunk.length = PA_MIN(t_length, blk_size_max);
+                chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length);
+
+                d = pa_memblock_acquire(chunk.memblock);
+                memcpy(d, t_data, chunk.length);
+                pa_memblock_release(chunk.memblock);
+            }
+
+            pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk);
+
+            t_offset = 0;
+            t_seek = PA_SEEK_RELATIVE;
+
+            t_data = (const uint8_t*) t_data + chunk.length;
+            t_length -= chunk.length;
+
+            pa_memblock_unref(chunk.memblock);
+        }
+
+        if (free_cb && pa_pstream_get_shm(s->context->pstream))
+            free_cb(free_cb_data);
+    }
+
+    /* This is obviously wrong since we ignore the seeking index . But
+     * that's OK, the server side applies the same error */
+    s->requested_bytes -= (seek == PA_SEEK_RELATIVE ? offset : 0) + (int64_t) length;
+
+#ifdef STREAM_DEBUG
+    pa_log_debug("wrote %lli, now at %lli", (long long) length, (long long) s->requested_bytes);
+#endif
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+
+        /* Update latency request correction */
+        if (s->write_index_corrections[s->current_write_index_correction].valid) {
+
+            if (seek == PA_SEEK_ABSOLUTE) {
+                s->write_index_corrections[s->current_write_index_correction].corrupt = false;
+                s->write_index_corrections[s->current_write_index_correction].absolute = true;
+                s->write_index_corrections[s->current_write_index_correction].value = offset + (int64_t) length;
+            } else if (seek == PA_SEEK_RELATIVE) {
+                if (!s->write_index_corrections[s->current_write_index_correction].corrupt)
+                    s->write_index_corrections[s->current_write_index_correction].value += offset + (int64_t) length;
+            } else
+                s->write_index_corrections[s->current_write_index_correction].corrupt = true;
+        }
+
+        /* Update the write index in the already available latency data */
+        if (s->timing_info_valid) {
+
+            if (seek == PA_SEEK_ABSOLUTE) {
+                s->timing_info.write_index_corrupt = false;
+                s->timing_info.write_index = offset + (int64_t) length;
+            } else if (seek == PA_SEEK_RELATIVE) {
+                if (!s->timing_info.write_index_corrupt)
+                    s->timing_info.write_index += offset + (int64_t) length;
+            } else
+                s->timing_info.write_index_corrupt = true;
+        }
+
+        if (!s->timing_info_valid || s->timing_info.write_index_corrupt)
+            request_auto_timing_update(s, true);
+    }
+
+    return 0;
+}
+
+int pa_stream_write(
+        pa_stream *s,
+        const void *data,
+        size_t length,
+        pa_free_cb_t free_cb,
+        int64_t offset,
+        pa_seek_mode_t seek) {
+
+    return pa_stream_write_ext_free(s, data, length, free_cb, (void*) data, offset, seek);
+}
+
+int pa_stream_peek(pa_stream *s, const void **data, size_t *length) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(data);
+    pa_assert(length);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
+
+    if (!s->peek_memchunk.memblock) {
+
+        if (pa_memblockq_peek(s->record_memblockq, &s->peek_memchunk) < 0) {
+            /* record_memblockq is empty. */
+            *data = NULL;
+            *length = 0;
+            return 0;
+
+        } else if (!s->peek_memchunk.memblock) {
+            /* record_memblockq isn't empty, but it doesn't have any data at
+             * the current read index. */
+            *data = NULL;
+            *length = s->peek_memchunk.length;
+            return 0;
+        }
+
+        s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock);
+    }
+
+    pa_assert(s->peek_data);
+    *data = (uint8_t*) s->peek_data + s->peek_memchunk.index;
+    *length = s->peek_memchunk.length;
+    return 0;
+}
+
+int pa_stream_drop(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->peek_memchunk.length > 0, PA_ERR_BADSTATE);
+
+    pa_memblockq_drop(s->record_memblockq, s->peek_memchunk.length);
+
+    /* Fix the simulated local read index */
+    if (s->timing_info_valid && !s->timing_info.read_index_corrupt)
+        s->timing_info.read_index += (int64_t) s->peek_memchunk.length;
+
+    if (s->peek_memchunk.memblock) {
+        pa_assert(s->peek_data);
+        s->peek_data = NULL;
+        pa_memblock_release(s->peek_memchunk.memblock);
+        pa_memblock_unref(s->peek_memchunk.memblock);
+    }
+
+    pa_memchunk_reset(&s->peek_memchunk);
+
+    return 0;
+}
+
+size_t pa_stream_writable_size(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, !pa_detect_fork(), PA_ERR_FORKED, (size_t) -1);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1);
+
+    return s->requested_bytes > 0 ? (size_t) s->requested_bytes : 0;
+}
+
+size_t pa_stream_readable_size(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, !pa_detect_fork(), PA_ERR_FORKED, (size_t) -1);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1);
+
+    return pa_memblockq_get_length(s->record_memblockq);
+}
+
+pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
+
+    /* Ask for a timing update before we cork/uncork to get the best
+     * accuracy for the transport latency suitable for the
+     * check_smoother_status() call in the started callback */
+    request_auto_timing_update(s, true);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(s->context, PA_COMMAND_DRAIN_PLAYBACK_STREAM, &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    /* This might cause the read index to continue again, hence
+     * let's request a timing update */
+    request_auto_timing_update(s, true);
+
+    return o;
+}
+
+static pa_usec_t calc_time(pa_stream *s, bool ignore_transport) {
+    pa_usec_t usec;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(s->state == PA_STREAM_READY);
+    pa_assert(s->direction != PA_STREAM_UPLOAD);
+    pa_assert(s->timing_info_valid);
+    pa_assert(s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt);
+    pa_assert(s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt);
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+        /* The last byte that was written into the output device
+         * had this time value associated */
+        usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
+
+        if (!s->corked && !s->suspended) {
+
+            if (!ignore_transport)
+                /* Because the latency info took a little time to come
+                 * to us, we assume that the real output time is actually
+                 * a little ahead */
+                usec += s->timing_info.transport_usec;
+
+            /* However, the output device usually maintains a buffer
+               too, hence the real sample currently played is a little
+               back  */
+            if (s->timing_info.sink_usec >= usec)
+                usec = 0;
+            else
+                usec -= s->timing_info.sink_usec;
+        }
+
+    } else {
+        pa_assert(s->direction == PA_STREAM_RECORD);
+
+        /* The last byte written into the server side queue had
+         * this time value associated */
+        usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
+
+        if (!s->corked && !s->suspended) {
+
+            if (!ignore_transport)
+                /* Add transport latency */
+                usec += s->timing_info.transport_usec;
+
+            /* Add latency of data in device buffer */
+            usec += s->timing_info.source_usec;
+
+            /* If this is a monitor source, we need to correct the
+             * time by the playback device buffer */
+            if (s->timing_info.sink_usec >= usec)
+                usec = 0;
+            else
+                usec -= s->timing_info.sink_usec;
+        }
+    }
+
+    return usec;
+}
+
+static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    struct timeval local, remote, now;
+    pa_timing_info *i;
+    bool playing = false;
+    uint64_t underrun_for = 0, playing_for = 0;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context || !o->stream)
+        goto finish;
+
+    i = &o->stream->timing_info;
+
+    o->stream->timing_info_valid = false;
+    i->write_index_corrupt = true;
+    i->read_index_corrupt = true;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+    } else {
+
+        if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
+            pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
+            pa_tagstruct_get_boolean(t, &playing) < 0 ||
+            pa_tagstruct_get_timeval(t, &local) < 0 ||
+            pa_tagstruct_get_timeval(t, &remote) < 0 ||
+            pa_tagstruct_gets64(t, &i->write_index) < 0 ||
+            pa_tagstruct_gets64(t, &i->read_index) < 0) {
+
+            pa_context_fail(o->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        if (o->context->version >= 13 &&
+            o->stream->direction == PA_STREAM_PLAYBACK)
+            if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
+                pa_tagstruct_getu64(t, &playing_for) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+        if (!pa_tagstruct_eof(t)) {
+            pa_context_fail(o->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+        o->stream->timing_info_valid = true;
+        i->write_index_corrupt = false;
+        i->read_index_corrupt = false;
+
+        i->playing = (int) playing;
+        i->since_underrun = (int64_t) (playing ? playing_for : underrun_for);
+
+        pa_gettimeofday(&now);
+
+        /* Calculate timestamps */
+        if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) {
+            /* local and remote seem to have synchronized clocks */
+
+            if (o->stream->direction == PA_STREAM_PLAYBACK)
+                i->transport_usec = pa_timeval_diff(&remote, &local);
+            else
+                i->transport_usec = pa_timeval_diff(&now, &remote);
+
+            i->synchronized_clocks = true;
+            i->timestamp = remote;
+        } else {
+            /* clocks are not synchronized, let's estimate latency then */
+            i->transport_usec = pa_timeval_diff(&now, &local)/2;
+            i->synchronized_clocks = false;
+            i->timestamp = local;
+            pa_timeval_add(&i->timestamp, i->transport_usec);
+        }
+
+        /* Invalidate read and write indexes if necessary */
+        if (tag < o->stream->read_index_not_before)
+            i->read_index_corrupt = true;
+
+        if (tag < o->stream->write_index_not_before)
+            i->write_index_corrupt = true;
+
+        if (o->stream->direction == PA_STREAM_PLAYBACK) {
+            /* Write index correction */
+
+            int n, j;
+            uint32_t ctag = tag;
+
+            /* Go through the saved correction values and add up the
+             * total correction.*/
+            for (n = 0, j = o->stream->current_write_index_correction+1;
+                 n < PA_MAX_WRITE_INDEX_CORRECTIONS;
+                 n++, j = (j + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS) {
+
+                /* Step over invalid data or out-of-date data */
+                if (!o->stream->write_index_corrections[j].valid ||
+                    o->stream->write_index_corrections[j].tag < ctag)
+                    continue;
+
+                /* Make sure that everything is in order */
+                ctag = o->stream->write_index_corrections[j].tag+1;
+
+                /* Now fix the write index */
+                if (o->stream->write_index_corrections[j].corrupt) {
+                    /* A corrupting seek was made */
+                    i->write_index_corrupt = true;
+                } else if (o->stream->write_index_corrections[j].absolute) {
+                    /* An absolute seek was made */
+                    i->write_index = o->stream->write_index_corrections[j].value;
+                    i->write_index_corrupt = false;
+                } else if (!i->write_index_corrupt) {
+                    /* A relative seek was made */
+                    i->write_index += o->stream->write_index_corrections[j].value;
+                }
+            }
+
+            /* Clear old correction entries */
+            for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
+                if (!o->stream->write_index_corrections[n].valid)
+                    continue;
+
+                if (o->stream->write_index_corrections[n].tag <= tag)
+                    o->stream->write_index_corrections[n].valid = false;
+            }
+        }
+
+        if (o->stream->direction == PA_STREAM_RECORD) {
+            /* Read index correction */
+
+            if (!i->read_index_corrupt)
+                i->read_index -= (int64_t) pa_memblockq_get_length(o->stream->record_memblockq);
+        }
+
+        /* Update smoother if we're not corked */
+        if (o->stream->smoother && !o->stream->corked) {
+            pa_usec_t u, x;
+
+            u = x = pa_rtclock_now() - i->transport_usec;
+
+            if (o->stream->direction == PA_STREAM_PLAYBACK && o->context->version >= 13) {
+                pa_usec_t su;
+
+                /* If we weren't playing then it will take some time
+                 * until the audio will actually come out through the
+                 * speakers. Since we follow that timing here, we need
+                 * to try to fix this up */
+
+                su = pa_bytes_to_usec((uint64_t) i->since_underrun, &o->stream->sample_spec);
+
+                if (su < i->sink_usec)
+                    x += i->sink_usec - su;
+            }
+
+            if (!i->playing)
+                pa_smoother_pause(o->stream->smoother, x);
+
+            /* Update the smoother */
+            if ((o->stream->direction == PA_STREAM_PLAYBACK && !i->read_index_corrupt) ||
+                (o->stream->direction == PA_STREAM_RECORD && !i->write_index_corrupt))
+                pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, true));
+
+            if (i->playing)
+                pa_smoother_resume(o->stream->smoother, x, true);
+        }
+    }
+
+    o->stream->auto_timing_update_requested = false;
+
+    if (o->stream->latency_update_callback)
+        o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata);
+
+    if (o->callback && o->stream && o->stream->state == PA_STREAM_READY) {
+        pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+        cb(o->stream, o->stream->timing_info_valid, o->userdata);
+    }
+
+finish:
+
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+    struct timeval now;
+    int cidx = 0;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+        /* Find a place to store the write_index correction data for this entry */
+        cidx = (s->current_write_index_correction + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS;
+
+        /* Check if we could allocate a correction slot. If not, there are too many outstanding queries */
+        PA_CHECK_VALIDITY_RETURN_NULL(s->context, !s->write_index_corrections[cidx].valid, PA_ERR_INTERNAL);
+    }
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(
+            s->context,
+            (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY),
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_timing_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+        /* Fill in initial correction data */
+
+        s->current_write_index_correction = cidx;
+
+        s->write_index_corrections[cidx].valid = true;
+        s->write_index_corrections[cidx].absolute = false;
+        s->write_index_corrections[cidx].corrupt = false;
+        s->write_index_corrections[cidx].tag = tag;
+        s->write_index_corrections[cidx].value = 0;
+    }
+
+    return o;
+}
+
+void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_stream *s = userdata;
+
+    pa_assert(pd);
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    pa_stream_ref(s);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(s->context, command, t, false) < 0)
+            goto finish;
+
+        pa_stream_set_state(s, PA_STREAM_FAILED);
+        goto finish;
+    } else if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(s->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    pa_stream_set_state(s, PA_STREAM_TERMINATED);
+
+finish:
+    pa_stream_unref(s);
+}
+
+int pa_stream_disconnect(pa_stream *s) {
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    pa_stream_ref(s);
+
+    t = pa_tagstruct_command(
+            s->context,
+            (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM :
+                        (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM)),
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s, NULL);
+
+    pa_stream_unref(s);
+    return 0;
+}
+
+void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->read_callback = cb;
+    s->read_userdata = userdata;
+}
+
+void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->write_callback = cb;
+    s->write_userdata = userdata;
+}
+
+void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->state_callback = cb;
+    s->state_userdata = userdata;
+}
+
+void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->overflow_callback = cb;
+    s->overflow_userdata = userdata;
+}
+
+void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->underflow_callback = cb;
+    s->underflow_userdata = userdata;
+}
+
+void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->latency_update_callback = cb;
+    s->latency_update_userdata = userdata;
+}
+
+void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->moved_callback = cb;
+    s->moved_userdata = userdata;
+}
+
+void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->suspended_callback = cb;
+    s->suspended_userdata = userdata;
+}
+
+void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->started_callback = cb;
+    s->started_userdata = userdata;
+}
+
+void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->event_callback = cb;
+    s->event_userdata = userdata;
+}
+
+void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->buffer_attr_callback = cb;
+    s->buffer_attr_userdata = userdata;
+}
+
+void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int success = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        success = 0;
+    } else if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+        cb(o->stream, success, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+    /* Ask for a timing update before we cork/uncork to get the best
+     * accuracy for the transport latency suitable for the
+     * check_smoother_status() call in the started callback */
+    request_auto_timing_update(s, true);
+
+    s->corked = b;
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(
+            s->context,
+            (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM),
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_tagstruct_put_boolean(t, !!b);
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    check_smoother_status(s, false, false, false);
+
+    /* This might cause the indexes to hang/start again, hence let's
+     * request a timing update, after the cork/uncork, too */
+    request_auto_timing_update(s, true);
+
+    return o;
+}
+
+static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, pa_stream_success_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(s->context, command, &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+    /* Ask for a timing update *before* the flush, so that the
+     * transport usec is as up to date as possible when we get the
+     * underflow message and update the smoother status*/
+    request_auto_timing_update(s, true);
+
+    if (!(o = stream_send_simple_command(s, (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM), cb, userdata)))
+        return NULL;
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+
+        if (s->write_index_corrections[s->current_write_index_correction].valid)
+            s->write_index_corrections[s->current_write_index_correction].corrupt = true;
+
+        if (s->buffer_attr.prebuf > 0)
+            check_smoother_status(s, false, false, true);
+
+        /* This will change the write index, but leave the
+         * read index untouched. */
+        invalidate_indexes(s, false, true);
+
+    } else
+        /* For record streams this has no influence on the write
+         * index, but the read index might jump. */
+        invalidate_indexes(s, true, false);
+
+    /* Note that we do not update requested_bytes here. This is
+     * because we cannot really know how data actually was dropped
+     * from the write index due to this. This 'error' will be applied
+     * by both client and server and hence we should be fine. */
+
+    return o;
+}
+
+pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
+
+    /* Ask for a timing update before we cork/uncork to get the best
+     * accuracy for the transport latency suitable for the
+     * check_smoother_status() call in the started callback */
+    request_auto_timing_update(s, true);
+
+    if (!(o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata)))
+        return NULL;
+
+    /* This might cause the read index to hang again, hence
+     * let's request a timing update */
+    request_auto_timing_update(s, true);
+
+    return o;
+}
+
+pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
+
+    /* Ask for a timing update before we cork/uncork to get the best
+     * accuracy for the transport latency suitable for the
+     * check_smoother_status() call in the started callback */
+    request_auto_timing_update(s, true);
+
+    if (!(o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata)))
+        return NULL;
+
+    /* This might cause the read index to start moving again, hence
+     * let's request a timing update */
+    request_auto_timing_update(s, true);
+
+    return o;
+}
+
+pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(name);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+    if (s->context->version >= 13) {
+        pa_proplist *p = pa_proplist_new();
+
+        pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+        o = pa_stream_proplist_update(s, PA_UPDATE_REPLACE, p, cb, userdata);
+        pa_proplist_free(p);
+    } else {
+        pa_tagstruct *t;
+        uint32_t tag;
+
+        o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+        t = pa_tagstruct_command(
+                s->context,
+                (uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME),
+                &tag);
+        pa_tagstruct_putu32(t, s->channel);
+        pa_tagstruct_puts(t, name);
+        pa_pstream_send_tagstruct(s->context->pstream, t);
+        pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+    }
+
+    return o;
+}
+
+int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
+    pa_usec_t usec;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
+
+    if (s->smoother)
+        usec = pa_smoother_get(s->smoother, pa_rtclock_now());
+    else
+        usec = calc_time(s, false);
+
+    /* Make sure the time runs monotonically */
+    if (!(s->flags & PA_STREAM_NOT_MONOTONIC)) {
+        if (usec < s->previous_time)
+            usec = s->previous_time;
+        else
+            s->previous_time = usec;
+    }
+
+    if (r_usec)
+        *r_usec = usec;
+
+    return 0;
+}
+
+static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t a, pa_usec_t b, int *negative) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (negative)
+        *negative = 0;
+
+    if (a >= b)
+        return a-b;
+    else {
+        if (negative && s->direction == PA_STREAM_RECORD) {
+            *negative = 1;
+            return b-a;
+        } else
+            return 0;
+    }
+}
+
+int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {
+    pa_usec_t t, c;
+    int r;
+    int64_t cindex;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(r_usec);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
+
+    if ((r = pa_stream_get_time(s, &t)) < 0)
+        return r;
+
+    if (s->direction == PA_STREAM_PLAYBACK)
+        cindex = s->timing_info.write_index;
+    else
+        cindex = s->timing_info.read_index;
+
+    if (cindex < 0)
+        cindex = 0;
+
+    c = pa_bytes_to_usec((uint64_t) cindex, &s->sample_spec);
+
+    if (s->direction == PA_STREAM_PLAYBACK)
+        *r_usec = time_counter_diff(s, c, t, negative);
+    else
+        *r_usec = time_counter_diff(s, t, c, negative);
+
+    return 0;
+}
+
+const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_NODATA);
+
+    return &s->timing_info;
+}
+
+const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+
+    return &s->sample_spec;
+}
+
+const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+
+    return &s->channel_map;
+}
+
+const pa_format_info* pa_stream_get_format_info(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    /* We don't have the format till routing is done */
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+
+    return s->format;
+}
+const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NOTSUPPORTED);
+
+    return &s->buffer_attr;
+}
+
+static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int success = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        success = 0;
+    } else {
+        if (o->stream->direction == PA_STREAM_PLAYBACK) {
+            if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
+                pa_tagstruct_getu32(t, &o->stream->buffer_attr.tlength) < 0 ||
+                pa_tagstruct_getu32(t, &o->stream->buffer_attr.prebuf) < 0 ||
+                pa_tagstruct_getu32(t, &o->stream->buffer_attr.minreq) < 0) {
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        } else if (o->stream->direction == PA_STREAM_RECORD) {
+            if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
+                pa_tagstruct_getu32(t, &o->stream->buffer_attr.fragsize) < 0) {
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        }
+
+        if (o->stream->context->version >= 13) {
+            pa_usec_t usec;
+
+            if (pa_tagstruct_get_usec(t, &usec) < 0) {
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (o->stream->direction == PA_STREAM_RECORD)
+                o->stream->timing_info.configured_source_usec = usec;
+            else
+                o->stream->timing_info.configured_sink_usec = usec;
+        }
+
+        if (!pa_tagstruct_eof(t)) {
+            pa_context_fail(o->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    }
+
+    if (o->callback) {
+        pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+        cb(o->stream, success, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+    pa_buffer_attr copy;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(attr);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+    /* Ask for a timing update before we cork/uncork to get the best
+     * accuracy for the transport latency suitable for the
+     * check_smoother_status() call in the started callback */
+    request_auto_timing_update(s, true);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(
+            s->context,
+            (uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR : PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR),
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+
+    copy = *attr;
+    patch_buffer_attr(s, &copy, NULL);
+    attr = &copy;
+
+    pa_tagstruct_putu32(t, attr->maxlength);
+
+    if (s->direction == PA_STREAM_PLAYBACK)
+        pa_tagstruct_put(
+                t,
+                PA_TAG_U32, attr->tlength,
+                PA_TAG_U32, attr->prebuf,
+                PA_TAG_U32, attr->minreq,
+                PA_TAG_INVALID);
+    else
+        pa_tagstruct_putu32(t, attr->fragsize);
+
+    if (s->context->version >= 13)
+        pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_ADJUST_LATENCY));
+
+    if (s->context->version >= 14)
+        pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_EARLY_REQUESTS));
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_set_buffer_attr_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    /* This might cause changes in the read/write index, hence let's
+     * request a timing update */
+    request_auto_timing_update(s, true);
+
+    return o;
+}
+
+uint32_t pa_stream_get_device_index(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, !pa_detect_fork(), PA_ERR_FORKED, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->device_index != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+
+    return s->device_index;
+}
+
+const char *pa_stream_get_device_name(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->device_name, PA_ERR_BADSTATE);
+
+    return s->device_name;
+}
+
+int pa_stream_is_suspended(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+    return s->suspended;
+}
+
+int pa_stream_is_corked(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+    return s->corked;
+}
+
+static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int success = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        success = 0;
+    } else {
+
+        if (!pa_tagstruct_eof(t)) {
+            pa_context_fail(o->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    }
+
+    o->stream->sample_spec.rate = PA_PTR_TO_UINT(o->private);
+    pa_assert(pa_sample_spec_valid(&o->stream->sample_spec));
+
+    if (o->callback) {
+        pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+        cb(o->stream, success, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, pa_sample_rate_valid(rate), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->flags & PA_STREAM_VARIABLE_RATE, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+    o->private = PA_UINT_TO_PTR(rate);
+
+    t = pa_tagstruct_command(
+            s->context,
+            (uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE : PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE),
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_tagstruct_putu32(t, rate);
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_update_sample_rate_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(
+            s->context,
+            (uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST : PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST),
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_tagstruct_putu32(t, (uint32_t) mode);
+    pa_tagstruct_put_proplist(t, p);
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    /* Please note that we don't update s->proplist here, because we
+     * don't export that field */
+
+    return o;
+}
+
+pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+    const char * const*k;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, keys && keys[0], PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(
+            s->context,
+            (uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST : PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST),
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+
+    for (k = keys; *k; k++)
+        pa_tagstruct_puts(t, *k);
+
+    pa_tagstruct_puts(t, NULL);
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    /* Please note that we don't update s->proplist here, because we
+     * don't export that field */
+
+    return o;
+}
+
+int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, sink_input_idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    s->direct_on_input = sink_input_idx;
+
+    return 0;
+}
+
+uint32_t pa_stream_get_monitor_stream(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, !pa_detect_fork(), PA_ERR_FORKED, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direct_on_input != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+
+    return s->direct_on_input;
+}
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
new file mode 100644 (file)
index 0000000..5dfdee1
--- /dev/null
@@ -0,0 +1,831 @@
+#ifndef foostreamhfoo
+#define foostreamhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/sample.h>
+#include <pulse/format.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+#include <pulse/def.h>
+#include <pulse/cdecl.h>
+#include <pulse/operation.h>
+#include <pulse/context.h>
+#include <pulse/proplist.h>
+
+/** \page streams Audio Streams
+ *
+ * \section overv_sec Overview
+ *
+ * Audio streams form the central functionality of the sound server. Data is
+ * routed, converted and mixed from several sources before it is passed along
+ * to a final output. Currently, there are three forms of audio streams:
+ *
+ * \li Playback streams - Data flows from the client to the server.
+ * \li Record streams - Data flows from the server to the client.
+ * \li Upload streams - Similar to playback streams, but the data is stored in
+ *                      the sample cache. See \ref scache for more information
+ *                      about controlling the sample cache.
+ *
+ * \section create_sec Creating
+ *
+ * To access a stream, a pa_stream object must be created using
+ * pa_stream_new() or pa_stream_new_extended(). pa_stream_new() is for PCM
+ * streams only, while pa_stream_new_extended() can be used for both PCM and
+ * compressed audio streams. At this point the application must specify what
+ * stream format(s) it supports. See \ref sample and \ref channelmap for more
+ * information on the stream format parameters. FIXME: Those references only
+ * talk about PCM parameters, we should also have an overview page for how the
+ * pa_format_info based stream format configuration works. Bug filed:
+ * https://bugs.freedesktop.org/show_bug.cgi?id=72265
+ *
+ * This first step will only create a client-side object, representing the
+ * stream. To use the stream, a server-side object must be created and
+ * associated with the local object. Depending on which type of stream is
+ * desired, a different function is needed:
+ *
+ * \li Playback stream - pa_stream_connect_playback()
+ * \li Record stream - pa_stream_connect_record()
+ * \li Upload stream - pa_stream_connect_upload() (see \ref scache)
+ *
+ * Similar to how connections are done in contexts, connecting a stream will
+ * not generate a pa_operation object. Also like contexts, the application
+ * should register a state change callback, using
+ * pa_stream_set_state_callback(), and wait for the stream to enter an active
+ * state.
+ *
+ * Note: there is a user-controllable slider in mixer applications such as
+ * pavucontrol corresponding to each of the created streams. Multiple
+ * (especially identically named) volume sliders for the same application might
+ * confuse the user. Also, the server supports only a limited number of
+ * simultaneous streams. Because of this, it is not always appropriate to
+ * create multiple streams in one application that needs to output multiple
+ * sounds. The rough guideline is: if there is no use case that would require
+ * separate user-initiated volume changes for each stream, perform the mixing
+ * inside the application.
+ *
+ * \subsection bufattr_subsec Buffer Attributes
+ *
+ * Playback and record streams always have a server-side buffer as
+ * part of the data flow.  The size of this buffer needs to be chosen
+ * in a compromise between low latency and sensitivity for buffer
+ * overflows/underruns.
+ *
+ * The buffer metrics may be controlled by the application. They are
+ * described with a pa_buffer_attr structure which contains a number
+ * of fields:
+ *
+ * \li maxlength - The absolute maximum number of bytes that can be
+ *                 stored in the buffer. If this value is exceeded
+ *                 then data will be lost. It is recommended to pass
+ *                 (uint32_t) -1 here which will cause the server to
+ *                 fill in the maximum possible value.
+ *
+ * \li tlength - The target fill level of the playback buffer. The
+ *               server will only send requests for more data as long
+ *               as the buffer has less than this number of bytes of
+ *               data. If you pass (uint32_t) -1 (which is
+ *               recommended) here the server will choose the longest
+ *               target buffer fill level possible to minimize the
+ *               number of necessary wakeups and maximize drop-out
+ *               safety. This can exceed 2s of buffering. For
+ *               low-latency applications or applications where
+ *               latency matters you should pass a proper value here.
+ *
+ * \li prebuf - Number of bytes that need to be in the buffer before
+ *              playback will commence. Start of playback can be
+ *              forced using pa_stream_trigger() even though the
+ *              prebuffer size hasn't been reached. If a buffer
+ *              underrun occurs, this prebuffering will be again
+ *              enabled. If the playback shall never stop in case of a
+ *              buffer underrun, this value should be set to 0. In
+ *              that case the read index of the output buffer
+ *              overtakes the write index, and hence the fill level of
+ *              the buffer is negative. If you pass (uint32_t) -1 here
+ *              (which is recommended) the server will choose the same
+ *              value as tlength here.
+ *
+ * \li minreq - Minimum number of free bytes in the playback
+ *              buffer before the server will request more data. It is
+ *              recommended to fill in (uint32_t) -1 here. This value
+ *              influences how much time the sound server has to move
+ *              data from the per-stream server-side playback buffer
+ *              to the hardware playback buffer.
+ *
+ * \li fragsize - Maximum number of bytes that the server will push in
+ *                one chunk for record streams. If you pass (uint32_t)
+ *                -1 (which is recommended) here, the server will
+ *                choose the longest fragment setting possible to
+ *                minimize the number of necessary wakeups and
+ *                maximize drop-out safety. This can exceed 2s of
+ *                buffering. For low-latency applications or
+ *                applications where latency matters you should pass a
+ *                proper value here.
+ *
+ * If PA_STREAM_ADJUST_LATENCY is set, then the tlength/fragsize
+ * parameters will be interpreted slightly differently than described
+ * above when passed to pa_stream_connect_record() and
+ * pa_stream_connect_playback(): the overall latency that is comprised
+ * of both the server side playback buffer length, the hardware
+ * playback buffer length and additional latencies will be adjusted in
+ * a way that it matches tlength resp. fragsize. Set
+ * PA_STREAM_ADJUST_LATENCY if you want to control the overall
+ * playback latency for your stream. Unset it if you want to control
+ * only the latency induced by the server-side, rewritable playback
+ * buffer. The server will try to fulfill the client's latency requests
+ * as good as possible. However if the underlying hardware cannot
+ * change the hardware buffer length or only in a limited range, the
+ * actually resulting latency might be different from what the client
+ * requested. Thus, for synchronization clients always need to check
+ * the actual measured latency via pa_stream_get_latency() or a
+ * similar call, and not make any assumptions about the latency
+ * available. The function pa_stream_get_buffer_attr() will always
+ * return the actual size of the server-side per-stream buffer in
+ * tlength/fragsize, regardless whether PA_STREAM_ADJUST_LATENCY is
+ * set or not.
+ *
+ * The server-side per-stream playback buffers are indexed by a write and a read
+ * index. The application writes to the write index and the sound
+ * device reads from the read index. The read index is increased
+ * monotonically, while the write index may be freely controlled by
+ * the application. Subtracting the read index from the write index
+ * will give you the current fill level of the buffer. The read/write
+ * indexes are 64bit values and measured in bytes, they will never
+ * wrap. The current read/write index may be queried using
+ * pa_stream_get_timing_info() (see below for more information). In
+ * case of a buffer underrun the read index is equal or larger than
+ * the write index. Unless the prebuf value is 0, PulseAudio will
+ * temporarily pause playback in such a case, and wait until the
+ * buffer is filled up to prebuf bytes again. If prebuf is 0, the
+ * read index may be larger than the write index, in which case
+ * silence is played. If the application writes data to indexes lower
+ * than the read index, the data is immediately lost.
+ *
+ * \section transfer_sec Transferring Data
+ *
+ * Once the stream is up, data can start flowing between the client and the
+ * server. Two different access models can be used to transfer the data:
+ *
+ * \li Asynchronous - The application register a callback using
+ *                    pa_stream_set_write_callback() and
+ *                    pa_stream_set_read_callback() to receive notifications
+ *                    that data can either be written or read.
+ * \li Polled - Query the library for available data/space using
+ *              pa_stream_writable_size() and pa_stream_readable_size() and
+ *              transfer data as needed. The sizes are stored locally, in the
+ *              client end, so there is no delay when reading them.
+ *
+ * It is also possible to mix the two models freely.
+ *
+ * Once there is data/space available, it can be transferred using either
+ * pa_stream_write() for playback, or pa_stream_peek() / pa_stream_drop() for
+ * record. Make sure you do not overflow the playback buffers as data will be
+ * dropped.
+ *
+ * \section bufctl_sec Buffer Control
+ *
+ * The transfer buffers can be controlled through a number of operations:
+ *
+ * \li pa_stream_cork() - Start or stop the playback or recording.
+ * \li pa_stream_trigger() - Start playback immediately and do not wait for
+ *                           the buffer to fill up to the set trigger level.
+ * \li pa_stream_prebuf() - Reenable the playback trigger level.
+ * \li pa_stream_drain() - Wait for the playback buffer to go empty. Will
+ *                         return a pa_operation object that will indicate when
+ *                         the buffer is completely drained.
+ * \li pa_stream_flush() - Drop all data from the playback or record buffer. Do not
+ *                         wait for it to finish playing.
+ *
+ * \section seek_modes Seeking in the Playback Buffer
+ *
+ * A client application may freely seek in the playback buffer. To
+ * accomplish that the pa_stream_write() function takes a seek mode
+ * and an offset argument. The seek mode is one of:
+ *
+ * \li PA_SEEK_RELATIVE - seek relative to the current write index
+ * \li PA_SEEK_ABSOLUTE - seek relative to the beginning of the playback buffer, (i.e. the first that was ever played in the stream)
+ * \li PA_SEEK_RELATIVE_ON_READ - seek relative to the current read index. Use this to write data to the output buffer that should be played as soon as possible
+ * \li PA_SEEK_RELATIVE_END - seek relative to the last byte ever written.
+ *
+ * If an application just wants to append some data to the output
+ * buffer, PA_SEEK_RELATIVE and an offset of 0 should be used.
+ *
+ * After a call to pa_stream_write() the write index will be left at
+ * the position right after the last byte of the written data.
+ *
+ * \section latency_sec Latency
+ *
+ * A major problem with networked audio is the increased latency caused by
+ * the network. To remedy this, PulseAudio supports an advanced system of
+ * monitoring the current latency.
+ *
+ * To get the raw data needed to calculate latencies, call
+ * pa_stream_get_timing_info(). This will give you a pa_timing_info
+ * structure that contains everything that is known about the server
+ * side buffer transport delays and the backend active in the
+ * server. (Besides other things it contains the write and read index
+ * values mentioned above.)
+ *
+ * This structure is updated every time a
+ * pa_stream_update_timing_info() operation is executed. (i.e. before
+ * the first call to this function the timing information structure is
+ * not available!) Since it is a lot of work to keep this structure
+ * up-to-date manually, PulseAudio can do that automatically for you:
+ * if PA_STREAM_AUTO_TIMING_UPDATE is passed when connecting the
+ * stream PulseAudio will automatically update the structure every
+ * 100ms and every time a function is called that might invalidate the
+ * previously known timing data (such as pa_stream_write() or
+ * pa_stream_flush()). Please note however, that there always is a
+ * short time window when the data in the timing information structure
+ * is out-of-date. PulseAudio tries to mark these situations by
+ * setting the write_index_corrupt and read_index_corrupt fields
+ * accordingly.
+ *
+ * The raw timing data in the pa_timing_info structure is usually hard
+ * to deal with. Therefore a simpler interface is available:
+ * you can call pa_stream_get_time() or pa_stream_get_latency(). The
+ * former will return the current playback time of the hardware since
+ * the stream has been started. The latter returns the overall time a sample
+ * that you write now takes to be played by the hardware. These two
+ * functions base their calculations on the same data that is returned
+ * by pa_stream_get_timing_info(). Hence the same rules for keeping
+ * the timing data up-to-date apply here. In case the write or read
+ * index is corrupted, these two functions will fail with
+ * -PA_ERR_NODATA set.
+ *
+ * Since updating the timing info structure usually requires a full
+ * network round trip and some applications monitor the timing very
+ * often PulseAudio offers a timing interpolation system. If
+ * PA_STREAM_INTERPOLATE_TIMING is passed when connecting the stream,
+ * pa_stream_get_time() and pa_stream_get_latency() will try to
+ * interpolate the current playback time/latency by estimating the
+ * number of samples that have been played back by the hardware since
+ * the last regular timing update. It is especially useful to combine
+ * this option with PA_STREAM_AUTO_TIMING_UPDATE, which will enable
+ * you to monitor the current playback time/latency very precisely and
+ * very frequently without requiring a network round trip every time.
+ *
+ * \section flow_sec Overflow and underflow
+ *
+ * Even with the best precautions, buffers will sometime over - or
+ * underflow.  To handle this gracefully, the application can be
+ * notified when this happens. Callbacks are registered using
+ * pa_stream_set_overflow_callback() and
+ * pa_stream_set_underflow_callback().
+ *
+ * \section sync_streams Synchronizing Multiple Playback Streams
+ *
+ * PulseAudio allows applications to fully synchronize multiple
+ * playback streams that are connected to the same output device. That
+ * means the streams will always be played back sample-by-sample
+ * synchronously. If stream operations like pa_stream_cork() are
+ * issued on one of the synchronized streams, they are simultaneously
+ * issued on the others.
+ *
+ * To synchronize a stream to another, just pass the "master" stream
+ * as last argument to pa_stream_connect_playback(). To make sure that
+ * the freshly created stream doesn't start playback right-away, make
+ * sure to pass PA_STREAM_START_CORKED and -- after all streams have
+ * been created -- uncork them all with a single call to
+ * pa_stream_cork() for the master stream.
+ *
+ * To make sure that a particular stream doesn't stop to play when a
+ * server side buffer underrun happens on it while the other
+ * synchronized streams continue playing and hence deviate, you need to
+ * pass a "prebuf" pa_buffer_attr of 0 when connecting it.
+ *
+ * \section disc_sec Disconnecting
+ *
+ * When a stream has served is purpose it must be disconnected with
+ * pa_stream_disconnect(). If you only unreference it, then it will live on
+ * and eat resources both locally and on the server until you disconnect the
+ * context.
+ *
+ */
+
+/** \file
+ * Audio streams for input, output and sample upload
+ *
+ * See also \subpage streams
+ */
+
+PA_C_DECL_BEGIN
+
+/** An opaque stream for playback or recording */
+typedef struct pa_stream pa_stream;
+
+/** A generic callback for operation completion */
+typedef void (*pa_stream_success_cb_t) (pa_stream*s, int success, void *userdata);
+
+/** A generic request callback */
+typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t nbytes, void *userdata);
+
+/** A generic notification callback */
+typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata);
+
+/** A callback for asynchronous meta/policy event messages. Well known
+ * event names are PA_STREAM_EVENT_REQUEST_CORK and
+ * PA_STREAM_EVENT_REQUEST_UNCORK. The set of defined events can be
+ * extended at any time. Also, server modules may introduce additional
+ * message types so make sure that your callback function ignores messages
+ * it doesn't know. \since 0.9.15 */
+typedef void (*pa_stream_event_cb_t)(pa_stream *p, const char *name, pa_proplist *pl, void *userdata);
+
+/** Create a new, unconnected stream with the specified name and
+ * sample type. It is recommended to use pa_stream_new_with_proplist()
+ * instead and specify some initial properties. */
+pa_stream* pa_stream_new(
+        pa_context *c                     /**< The context to create this stream in */,
+        const char *name                  /**< A name for this stream */,
+        const pa_sample_spec *ss          /**< The desired sample format */,
+        const pa_channel_map *map         /**< The desired channel map, or NULL for default */);
+
+/** Create a new, unconnected stream with the specified name and
+ * sample type, and specify the initial stream property
+ * list. \since 0.9.11 */
+pa_stream* pa_stream_new_with_proplist(
+        pa_context *c                     /**< The context to create this stream in */,
+        const char *name                  /**< A name for this stream */,
+        const pa_sample_spec *ss          /**< The desired sample format */,
+        const pa_channel_map *map         /**< The desired channel map, or NULL for default */,
+        pa_proplist *p                    /**< The initial property list */);
+
+/** Create a new, unconnected stream with the specified name, the set of formats
+ * this client can provide, and an initial list of properties. While
+ * connecting, the server will select the most appropriate format which the
+ * client must then provide. \since 1.0 */
+pa_stream *pa_stream_new_extended(
+        pa_context *c                     /**< The context to create this stream in */,
+        const char *name                  /**< A name for this stream */,
+        pa_format_info * const * formats  /**< The list of formats that can be provided */,
+        unsigned int n_formats            /**< The number of formats being passed in */,
+        pa_proplist *p                    /**< The initial property list */);
+
+/** Decrease the reference counter by one. */
+void pa_stream_unref(pa_stream *s);
+
+/** Increase the reference counter by one. */
+pa_stream *pa_stream_ref(pa_stream *s);
+
+/** Return the current state of the stream. */
+pa_stream_state_t pa_stream_get_state(pa_stream *p);
+
+/** Return the context this stream is attached to. */
+pa_context* pa_stream_get_context(pa_stream *p);
+
+/** Return the sink input resp.\ source output index this stream is
+ * identified in the server with. This is useful with the
+ * introspection functions such as pa_context_get_sink_input_info()
+ * or pa_context_get_source_output_info(). */
+uint32_t pa_stream_get_index(pa_stream *s);
+
+/** Return the index of the sink or source this stream is connected to
+ * in the server. This is useful with the introspection
+ * functions such as pa_context_get_sink_info_by_index() or
+ * pa_context_get_source_info_by_index().
+ *
+ * Please note that streams may be moved between sinks/sources and thus
+ * it is recommended to use pa_stream_set_moved_callback() to be notified
+ * about this. This function will return with -PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+uint32_t pa_stream_get_device_index(pa_stream *s);
+
+/** Return the name of the sink or source this stream is connected to
+ * in the server. This is useful with the introspection
+ * functions such as pa_context_get_sink_info_by_name()
+ * or pa_context_get_source_info_by_name().
+ *
+ * Please note that streams may be moved between sinks/sources and thus
+ * it is recommended to use pa_stream_set_moved_callback() to be notified
+ * about this. This function will return with -PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+const char *pa_stream_get_device_name(pa_stream *s);
+
+/** Return 1 if the sink or source this stream is connected to has
+ * been suspended. This will return 0 if not, and a negative value on
+ * error. This function will return with -PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+int pa_stream_is_suspended(pa_stream *s);
+
+/** Return 1 if the this stream has been corked. This will return 0 if
+ * not, and a negative value on error. \since 0.9.11 */
+int pa_stream_is_corked(pa_stream *s);
+
+/** Connect the stream to a sink. It is strongly recommended to pass
+ * NULL in both \a dev and \a volume and to set neither
+ * PA_STREAM_START_MUTED nor PA_STREAM_START_UNMUTED -- unless these
+ * options are directly dependent on user input or configuration.
+ *
+ * If you follow this rule then the sound server will have the full
+ * flexibility to choose the device, volume and mute status
+ * automatically, based on server-side policies, heuristics and stored
+ * information from previous uses. Also the server may choose to
+ * reconfigure audio devices to make other sinks/sources or
+ * capabilities available to be able to accept the stream.
+ *
+ * Before 0.9.20 it was not defined whether the \a volume parameter was
+ * interpreted relative to the sink's current volume or treated as
+ * an absolute device volume. Since 0.9.20 it is an absolute volume when
+ * the sink is in flat volume mode, and relative otherwise, thus
+ * making sure the volume passed here has always the same semantics as
+ * the volume passed to pa_context_set_sink_input_volume(). It is possible
+ * to figure out whether flat volume mode is in effect for a given sink
+ * by calling pa_context_get_sink_info_by_name().
+ *
+ * Since 5.0, it's possible to specify a single-channel volume even if the
+ * stream has multiple channels. In that case the same volume is applied to all
+ * channels. */
+int pa_stream_connect_playback(
+        pa_stream *s                  /**< The stream to connect to a sink */,
+        const char *dev               /**< Name of the sink to connect to, or NULL for default */ ,
+        const pa_buffer_attr *attr    /**< Buffering attributes, or NULL for default */,
+        pa_stream_flags_t flags       /**< Additional flags, or 0 for default */,
+        const pa_cvolume *volume      /**< Initial volume, or NULL for default */,
+        pa_stream *sync_stream        /**< Synchronize this stream with the specified one, or NULL for a standalone stream */);
+
+/** Connect the stream to a source. */
+int pa_stream_connect_record(
+        pa_stream *s                  /**< The stream to connect to a source */ ,
+        const char *dev               /**< Name of the source to connect to, or NULL for default */,
+        const pa_buffer_attr *attr    /**< Buffer attributes, or NULL for default */,
+        pa_stream_flags_t flags       /**< Additional flags, or 0 for default */);
+
+/** Disconnect a stream from a source/sink. */
+int pa_stream_disconnect(pa_stream *s);
+
+/** Prepare writing data to the server (for playback streams). This
+ * function may be used to optimize the number of memory copies when
+ * doing playback ("zero-copy"). It is recommended to call this
+ * function before each call to pa_stream_write().
+ *
+ * Pass in the address to a pointer and an address of the number of
+ * bytes you want to write. On return the two values will contain a
+ * pointer where you can place the data to write and the maximum number
+ * of bytes you can write. \a *nbytes can be smaller or have the same
+ * value as you passed in. You need to be able to handle both cases.
+ * Accessing memory beyond the returned \a *nbytes value is invalid.
+ * Accessing the memory returned after the following pa_stream_write()
+ * or pa_stream_cancel_write() is invalid.
+ *
+ * On invocation only \a *nbytes needs to be initialized, on return both
+ * *data and *nbytes will be valid. If you place (size_t) -1 in *nbytes
+ * on invocation the memory size will be chosen automatically (which is
+ * recommended to do). After placing your data in the memory area
+ * returned, call pa_stream_write() with \a data set to an address
+ * within this memory area and an \a nbytes value that is smaller or
+ * equal to what was returned by this function to actually execute the
+ * write.
+ *
+ * An invocation of pa_stream_write() should follow "quickly" on
+ * pa_stream_begin_write(). It is not recommended letting an unbounded
+ * amount of time pass after calling pa_stream_begin_write() and
+ * before calling pa_stream_write(). If you want to cancel a
+ * previously called pa_stream_begin_write() without calling
+ * pa_stream_write() use pa_stream_cancel_write(). Calling
+ * pa_stream_begin_write() twice without calling pa_stream_write() or
+ * pa_stream_cancel_write() in between will return exactly the same
+ * \a data pointer and \a nbytes values. \since 0.9.16 */
+int pa_stream_begin_write(
+        pa_stream *p,
+        void **data,
+        size_t *nbytes);
+
+/** Reverses the effect of pa_stream_begin_write() dropping all data
+ * that has already been placed in the memory area returned by
+ * pa_stream_begin_write(). Only valid to call if
+ * pa_stream_begin_write() was called before and neither
+ * pa_stream_cancel_write() nor pa_stream_write() have been called
+ * yet. Accessing the memory previously returned by
+ * pa_stream_begin_write() after this call is invalid. Any further
+ * explicit freeing of the memory area is not necessary. \since
+ * 0.9.16 */
+int pa_stream_cancel_write(
+        pa_stream *p);
+
+/** Write some data to the server (for playback streams).
+ * If \a free_cb is non-NULL this routine is called when all data has
+ * been written out. An internal reference to the specified data is
+ * kept, the data is not copied. If NULL, the data is copied into an
+ * internal buffer.
+ *
+ * The client may freely seek around in the output buffer. For
+ * most applications it is typical to pass 0 and PA_SEEK_RELATIVE
+ * as values for the arguments \a offset and \a seek. After the write
+ * call succeeded the write index will be at the position after where
+ * this chunk of data has been written to.
+ *
+ * As an optimization for avoiding needless memory copies you may call
+ * pa_stream_begin_write() before this call and then place your audio
+ * data directly in the memory area returned by that call. Then, pass
+ * a pointer to that memory area to pa_stream_write(). After the
+ * invocation of pa_stream_write() the memory area may no longer be
+ * accessed. Any further explicit freeing of the memory area is not
+ * necessary. It is OK to write the memory area returned by
+ * pa_stream_begin_write() only partially with this call, skipping
+ * bytes both at the end and at the beginning of the reserved memory
+ * area.*/
+int pa_stream_write(
+        pa_stream *p             /**< The stream to use */,
+        const void *data         /**< The data to write */,
+        size_t nbytes            /**< The length of the data to write in bytes, must be in multiples of the stream's sample spec frame size */,
+        pa_free_cb_t free_cb     /**< A cleanup routine for the data or NULL to request an internal copy */,
+        int64_t offset           /**< Offset for seeking, must be 0 for upload streams, must be in multiples of the stream's sample spec frame size */,
+        pa_seek_mode_t seek      /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
+
+/** Function does exactly the same as pa_stream_write() with the difference
+ *  that free_cb_data is passed to free_cb instead of data. \since 6.0 */
+int pa_stream_write_ext_free(
+        pa_stream *p             /**< The stream to use */,
+        const void *data         /**< The data to write */,
+        size_t nbytes            /**< The length of the data to write in bytes */,
+        pa_free_cb_t free_cb     /**< A cleanup routine for the data or NULL to request an internal copy */,
+        void *free_cb_data       /**< Argument passed to free_cb function */,
+        int64_t offset           /**< Offset for seeking, must be 0 for upload streams */,
+        pa_seek_mode_t seek      /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
+
+/** Read the next fragment from the buffer (for recording streams).
+ * If there is data at the current read index, \a data will point to
+ * the actual data and \a nbytes will contain the size of the data in
+ * bytes (which can be less or more than a complete fragment).
+ *
+ * If there is no data at the current read index, it means that either
+ * the buffer is empty or it contains a hole (that is, the write index
+ * is ahead of the read index but there's no data where the read index
+ * points at). If the buffer is empty, \a data will be NULL and
+ * \a nbytes will be 0. If there is a hole, \a data will be NULL and
+ * \a nbytes will contain the length of the hole.
+ *
+ * Use pa_stream_drop() to actually remove the data from the buffer
+ * and move the read index forward. pa_stream_drop() should not be
+ * called if the buffer is empty, but it should be called if there is
+ * a hole. */
+int pa_stream_peek(
+        pa_stream *p                 /**< The stream to use */,
+        const void **data            /**< Pointer to pointer that will point to data */,
+        size_t *nbytes               /**< The length of the data read in bytes */);
+
+/** Remove the current fragment on record streams. It is invalid to do this without first
+ * calling pa_stream_peek(). */
+int pa_stream_drop(pa_stream *p);
+
+/** Return the number of bytes requested by the server that have not yet
+ * been written.
+ *
+ * It is possible to write more than this amount, up to the stream's
+ * buffer_attr.maxlength bytes. This is usually not desirable, though, as
+ * it would increase stream latency to be higher than requested
+ * (buffer_attr.tlength).
+ */
+size_t pa_stream_writable_size(pa_stream *p);
+
+/** Return the number of bytes that may be read using pa_stream_peek(). */
+size_t pa_stream_readable_size(pa_stream *p);
+
+/** Drain a playback stream.  Use this for notification when the
+ * playback buffer is empty after playing all the audio in the buffer.
+ * Please note that only one drain operation per stream may be issued
+ * at a time. */
+pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Request a timing info structure update for a stream. Use
+ * pa_stream_get_timing_info() to get access to the raw timing data,
+ * or pa_stream_get_time() or pa_stream_get_latency() to get cleaned
+ * up values. */
+pa_operation* pa_stream_update_timing_info(pa_stream *p, pa_stream_success_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever the state of the stream changes. */
+void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when new data may be
+ * written to the stream. */
+void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when new data is available from the stream. */
+void pa_stream_set_read_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) */
+void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Return at what position the latest underflow occurred, or -1 if this information is not
+ * known (e.g.\ if no underflow has occurred, or server is older than 1.0).
+ * Can be used inside the underflow callback to get information about the current underflow.
+ * (Only for playback streams) \since 1.0 */
+int64_t pa_stream_get_underflow_index(pa_stream *p);
+
+/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) */
+void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when a the server starts
+ * playback after an underrun or on initial startup. This only informs
+ * that audio is flowing again, it is no indication that audio started
+ * to reach the speakers already. (Only for playback streams) \since
+ * 0.9.11 */
+void pa_stream_set_started_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever a latency
+ * information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE
+ * streams only. */
+void pa_stream_set_latency_update_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever the stream is
+ * moved to a different sink/source. Use pa_stream_get_device_name() or
+ * pa_stream_get_device_index() to query the new sink/source. This
+ * notification is only generated when the server is at least
+ * 0.9.8. \since 0.9.8 */
+void pa_stream_set_moved_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever the sink/source
+ * this stream is connected to is suspended or resumed. Use
+ * pa_stream_is_suspended() to query the new suspend status. Please
+ * note that the suspend status might also change when the stream is
+ * moved between devices. Thus if you call this function you very
+ * likely want to call pa_stream_set_moved_callback() too. This
+ * notification is only generated when the server is at least
+ * 0.9.8. \since 0.9.8 */
+void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever a meta/policy
+ * control event is received. \since 0.9.15 */
+void pa_stream_set_event_callback(pa_stream *p, pa_stream_event_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever the buffer
+ * attributes on the server side change. Please note that the buffer
+ * attributes can change when moving a stream to a different
+ * sink/source too, hence if you use this callback you should use
+ * pa_stream_set_moved_callback() as well. \since 0.9.15 */
+void pa_stream_set_buffer_attr_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Pause (or resume) playback of this stream temporarily. Available
+ * on both playback and recording streams. If \a b is 1 the stream is
+ * paused. If \a b is 0 the stream is resumed. The pause/resume operation
+ * is executed as quickly as possible. If a cork is very quickly
+ * followed by an uncork or the other way round, this might not
+ * actually have any effect on the stream that is output. You can use
+ * pa_stream_is_corked() to find out whether the stream is currently
+ * paused or not. Normally a stream will be created in uncorked
+ * state. If you pass PA_STREAM_START_CORKED as a flag when connecting
+ * the stream, it will be created in corked state. */
+pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
+
+/** Flush the playback or record buffer of this stream. This discards any audio data
+ * in the buffer.  Most of the time you're better off using the parameter
+ * \a seek of pa_stream_write() instead of this function. */
+pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Reenable prebuffering if specified in the pa_buffer_attr
+ * structure. Available for playback streams only. */
+pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Request immediate start of playback on this stream. This disables
+ * prebuffering temporarily if specified in the pa_buffer_attr structure.
+ * Available for playback streams only. */
+pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Rename the stream. */
+pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
+
+/** Return the current playback/recording time. This is based on the
+ * data in the timing info structure returned by
+ * pa_stream_get_timing_info().
+ *
+ * This function will usually only return new data if a timing info
+ * update has been received. Only if timing interpolation has been
+ * requested (PA_STREAM_INTERPOLATE_TIMING) the data from the last
+ * timing update is used for an estimation of the current
+ * playback/recording time based on the local time that passed since
+ * the timing info structure has been acquired.
+ *
+ * The time value returned by this function is guaranteed to increase
+ * monotonically (the returned value is always greater
+ * or equal to the value returned by the last call). This behaviour
+ * can be disabled by using PA_STREAM_NOT_MONOTONIC. This may be
+ * desirable to better deal with bad estimations of transport
+ * latencies, but may have strange effects if the application is not
+ * able to deal with time going 'backwards'.
+ *
+ * The time interpolator activated by PA_STREAM_INTERPOLATE_TIMING
+ * favours 'smooth' time graphs over accurate ones to improve the
+ * smoothness of UI operations that are tied to the audio clock. If
+ * accuracy is more important to you, you might need to estimate your
+ * timing based on the data from pa_stream_get_timing_info() yourself
+ * or not work with interpolated timing at all and instead always
+ * query the server side for the most up to date timing with
+ * pa_stream_update_timing_info().
+ *
+ * If no timing information has been
+ * received yet this call will return -PA_ERR_NODATA. For more details
+ * see pa_stream_get_timing_info(). */
+int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec);
+
+/** Determine the total stream latency. This function is based on
+ * pa_stream_get_time().
+ *
+ * The latency is stored in \a *r_usec. In case the stream is a
+ * monitoring stream the result can be negative, i.e. the captured
+ * samples are not yet played. In this case \a *negative is set to 1.
+ *
+ * If no timing information has been received yet, this call will
+ * return -PA_ERR_NODATA. On success, it will return 0.
+ *
+ * For more details see pa_stream_get_timing_info() and
+ * pa_stream_get_time(). */
+int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative);
+
+/** Return the latest raw timing data structure. The returned pointer
+ * refers to an internal read-only instance of the timing
+ * structure. The user should make a copy of this structure if he
+ * wants to modify it. An in-place update to this data structure may
+ * be requested using pa_stream_update_timing_info().
+ *
+ * If no timing information has been received before (i.e. by
+ * requesting pa_stream_update_timing_info() or by using
+ * PA_STREAM_AUTO_TIMING_UPDATE), this function will fail with
+ * -PA_ERR_NODATA.
+ *
+ * Please note that the write_index member field (and only this field)
+ * is updated on each pa_stream_write() call, not just when a timing
+ * update has been received. */
+const pa_timing_info* pa_stream_get_timing_info(pa_stream *s);
+
+/** Return a pointer to the stream's sample specification. */
+const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s);
+
+/** Return a pointer to the stream's channel map. */
+const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
+
+/** Return a pointer to the stream's format. \since 1.0 */
+const pa_format_info* pa_stream_get_format_info(pa_stream *s);
+
+/** Return the per-stream server-side buffer metrics of the
+ * stream. Only valid after the stream has been connected successfully
+ * and if the server is at least PulseAudio 0.9. This will return the
+ * actual configured buffering metrics, which may differ from what was
+ * requested during pa_stream_connect_record() or
+ * pa_stream_connect_playback(). This call will always return the
+ * actual per-stream server-side buffer metrics, regardless whether
+ * PA_STREAM_ADJUST_LATENCY is set or not. \since 0.9.0 */
+const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s);
+
+/** Change the buffer metrics of the stream during playback. The
+ * server might have chosen different buffer metrics then
+ * requested. The selected metrics may be queried with
+ * pa_stream_get_buffer_attr() as soon as the callback is called. Only
+ * valid after the stream has been connected successfully and if the
+ * server is at least PulseAudio 0.9.8. Please be aware of the
+ * slightly different semantics of the call depending whether
+ * PA_STREAM_ADJUST_LATENCY is set or not. \since 0.9.8 */
+pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata);
+
+/** Change the stream sampling rate during playback. You need to pass
+ * PA_STREAM_VARIABLE_RATE in the flags parameter of
+ * pa_stream_connect_playback() if you plan to use this function. Only valid
+ * after the stream has been connected successfully and if the server
+ * is at least PulseAudio 0.9.8. \since 0.9.8 */
+pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata);
+
+/** Update the property list of the sink input/source output of this
+ * stream, adding new entries. Please note that it is highly
+ * recommended to set as many properties initially via
+ * pa_stream_new_with_proplist() as possible instead a posteriori with
+ * this function, since that information may be used to route
+ * this stream to the right device. \since 0.9.11 */
+pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata);
+
+/** Update the property list of the sink input/source output of this
+ * stream, remove entries. \since 0.9.11 */
+pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata);
+
+/** For record streams connected to a monitor source: monitor only a
+ * very specific sink input of the sink. This function needs to be
+ * called before pa_stream_connect_record() is called. \since
+ * 0.9.11 */
+int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx);
+
+/** Return the sink input index previously set with
+ * pa_stream_set_monitor_stream().
+ * \since 0.9.11 */
+uint32_t pa_stream_get_monitor_stream(pa_stream *s);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c
new file mode 100644 (file)
index 0000000..e7cce2e
--- /dev/null
@@ -0,0 +1,88 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+#include "subscribe.h"
+
+void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_subscription_event_type_t e;
+    uint32_t idx;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (pa_tagstruct_getu32(t, &e) < 0 ||
+        pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (c->subscribe_callback)
+        c->subscribe_callback(c, e, idx, c->subscribe_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SUBSCRIBE, &tag);
+    pa_tagstruct_putu32(t, m);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+        return;
+
+    c->subscribe_callback = cb;
+    c->subscribe_userdata = userdata;
+}
diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h
new file mode 100644 (file)
index 0000000..b43c8ea
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef foosubscribehfoo
+#define foosubscribehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/def.h>
+#include <pulse/context.h>
+#include <pulse/cdecl.h>
+#include <pulse/version.h>
+
+/** \page subscribe Event Subscription
+ *
+ * \section overv_sec Overview
+ *
+ * The application can be notified, asynchronously, whenever the internal
+ * layout of the server changes. Possible notifications are described in the
+ * \ref pa_subscription_event_type and \ref pa_subscription_mask
+ * enumerations.
+ *
+ * The application sets the notification mask using pa_context_subscribe()
+ * and the function that will be called whenever a notification occurs using
+ * pa_context_set_subscribe_callback().
+ *
+ * The callback will be called with a \ref pa_subscription_event_type_t
+ * representing the event that caused the callback. Clients can examine what
+ * object changed using \ref PA_SUBSCRIPTION_EVENT_FACILITY_MASK. The actual
+ * event type can then be extracted with \ref PA_SUBSCRIPTION_EVENT_TYPE_MASK.
+ * Please note that the masked values are integers, not flags (so you will
+ * check the object/event type using a comparison not a binary AND). For
+ * example, the callback might look something like:
+ *
+@verbatim
+void my_subscription_callback(pa_context *c, pa_subscription_event_type_t t,
+                              uint32_t idx, void *userdata) {
+    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
+        if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+            ... a source was added, let's do stuff! ...
+        }
+    }
+}
+@endverbatim
+ */
+
+/** \file
+ * Daemon introspection event subscription subsystem.
+ *
+ * See also \subpage subscribe
+ */
+
+PA_C_DECL_BEGIN
+
+/** Subscription event callback prototype */
+typedef void (*pa_context_subscribe_cb_t)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
+
+/** Enable event notification */
+pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the context specific call back function that is called whenever the state of the daemon changes */
+void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c
new file mode 100644 (file)
index 0000000..993b7ba
--- /dev/null
@@ -0,0 +1,260 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef OS_IS_WIN32
+#include <pthread.h>
+#endif
+
+#include <signal.h>
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/mainloop.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/poll.h>
+
+#include "thread-mainloop.h"
+
+struct pa_threaded_mainloop {
+    pa_mainloop *real_mainloop;
+    volatile int n_waiting, n_waiting_for_accept;
+
+    pa_thread* thread;
+    pa_mutex* mutex;
+    pa_cond* cond, *accept_cond;
+
+    char *name;
+};
+
+static inline int in_worker(pa_threaded_mainloop *m) {
+    return pa_thread_self() == m->thread;
+}
+
+static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
+    pa_mutex *mutex = userdata;
+    int r;
+
+    pa_assert(mutex);
+
+    /* Before entering poll() we unlock the mutex, so that
+     * avahi_simple_poll_quit() can succeed from another thread. */
+
+    pa_mutex_unlock(mutex);
+    r = pa_poll(ufds, nfds, timeout);
+    pa_mutex_lock(mutex);
+
+    return r;
+}
+
+static void thread(void *userdata) {
+    pa_threaded_mainloop *m = userdata;
+
+#ifndef OS_IS_WIN32
+    sigset_t mask;
+    sigset_t prev_mask;
+    struct sigaction sa;
+
+    sigfillset(&mask);
+
+    /* If SIGSYS is currently unblocked and trapped then keep it unblocked. */
+    if (!pthread_sigmask(SIG_SETMASK, NULL, &prev_mask) &&
+        !sigismember(&prev_mask, SIGSYS) &&
+        !sigaction(SIGSYS, NULL, &sa)
+        && sa.sa_handler != SIG_DFL) {
+        sigdelset(&mask, SIGSYS);
+    }
+
+    /* Make sure that signals are delivered to the main thread.
+     * Use SIG_SETMASK because SIG_BLOCK does an union with current set.*/
+    pthread_sigmask(SIG_SETMASK, &mask, NULL);
+#endif
+
+    pa_mutex_lock(m->mutex);
+
+    (void) pa_mainloop_run(m->real_mainloop, NULL);
+
+    pa_mutex_unlock(m->mutex);
+}
+
+pa_threaded_mainloop *pa_threaded_mainloop_new(void) {
+    pa_threaded_mainloop *m;
+
+    pa_init_i18n();
+
+    m = pa_xnew0(pa_threaded_mainloop, 1);
+
+    if (!(m->real_mainloop = pa_mainloop_new())) {
+        pa_xfree(m);
+        return NULL;
+    }
+
+    m->mutex = pa_mutex_new(true, true);
+    m->cond = pa_cond_new();
+    m->accept_cond = pa_cond_new();
+
+    pa_mainloop_set_poll_func(m->real_mainloop, poll_func, m->mutex);
+
+    return m;
+}
+
+void pa_threaded_mainloop_free(pa_threaded_mainloop* m) {
+    pa_assert(m);
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m));
+
+    pa_threaded_mainloop_stop(m);
+
+    if (m->thread)
+        pa_thread_free(m->thread);
+
+    pa_mainloop_free(m->real_mainloop);
+
+    pa_mutex_free(m->mutex);
+    pa_cond_free(m->cond);
+    pa_cond_free(m->accept_cond);
+
+    pa_xfree(m->name);
+    pa_xfree(m);
+}
+
+int pa_threaded_mainloop_start(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    pa_assert(!m->thread || !pa_thread_is_running(m->thread));
+
+    if (!(m->thread = pa_thread_new(m->name ? m->name : "threaded-ml", thread, m)))
+        return -1;
+
+    return 0;
+}
+
+void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    if (!m->thread || !pa_thread_is_running(m->thread))
+        return;
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert(!in_worker(m));
+
+    pa_mutex_lock(m->mutex);
+    pa_mainloop_quit(m->real_mainloop, 0);
+    pa_mutex_unlock(m->mutex);
+
+    pa_thread_join(m->thread);
+}
+
+void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+
+    pa_mutex_lock(m->mutex);
+}
+
+void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+
+    pa_mutex_unlock(m->mutex);
+}
+
+/* Called with the lock taken */
+void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
+    pa_assert(m);
+
+    pa_cond_signal(m->cond, 1);
+
+    if (wait_for_accept) {
+        m->n_waiting_for_accept ++;
+
+        while (m->n_waiting_for_accept > 0)
+            pa_cond_wait(m->accept_cond, m->mutex);
+    }
+}
+
+/* Called with the lock taken */
+void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+
+    m->n_waiting ++;
+
+    pa_cond_wait(m->cond, m->mutex);
+
+    pa_assert(m->n_waiting > 0);
+    m->n_waiting --;
+}
+
+/* Called with the lock taken */
+void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+
+    pa_assert(m->n_waiting_for_accept > 0);
+    m->n_waiting_for_accept --;
+
+    pa_cond_signal(m->accept_cond, 0);
+}
+
+int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    return pa_mainloop_get_retval(m->real_mainloop);
+}
+
+pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) {
+    pa_assert(m);
+
+    return pa_mainloop_get_api(m->real_mainloop);
+}
+
+int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    return m->thread && pa_thread_self() == m->thread;
+}
+
+void pa_threaded_mainloop_set_name(pa_threaded_mainloop *m, const char *name) {
+    pa_assert(m);
+    pa_assert(name);
+
+    m->name = pa_xstrdup(name);
+
+    if (m->thread)
+        pa_thread_set_name(m->thread, m->name);
+}
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
new file mode 100644 (file)
index 0000000..e69298a
--- /dev/null
@@ -0,0 +1,317 @@
+#ifndef foothreadmainloophfoo
+#define foothreadmainloophfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+#include <pulse/version.h>
+
+PA_C_DECL_BEGIN
+
+/** \page threaded_mainloop Threaded Main Loop
+ *
+ * \section overv_sec Overview
+ *
+ * The threaded main loop implementation is a special version of the primary
+ * main loop implementation (see \ref mainloop). For the basic design, see
+ * its documentation.
+ *
+ * The added feature in the threaded main loop is that it spawns a new thread
+ * that runs the real main loop. This allows a synchronous application to use
+ * the asynchronous API without risking to stall the PulseAudio library.
+ *
+ * \section creat_sec Creation
+ *
+ * A pa_threaded_mainloop object is created using pa_threaded_mainloop_new().
+ * This will only allocate the required structures though, so to use it the
+ * thread must also be started. This is done through
+ * pa_threaded_mainloop_start(), after which you can start using the main loop.
+ *
+ * \section destr_sec Destruction
+ *
+ * When the PulseAudio connection has been terminated, the thread must be
+ * stopped and the resources freed. Stopping the thread is done using
+ * pa_threaded_mainloop_stop(), which must be called without the lock (see
+ * below) held. When that function returns, the thread is stopped and the
+ * pa_threaded_mainloop object can be freed using pa_threaded_mainloop_free().
+ *
+ * \section lock_sec Locking
+ *
+ * Since the PulseAudio API doesn't allow concurrent accesses to objects,
+ * a locking scheme must be used to guarantee safe usage. The threaded main
+ * loop API provides such a scheme through the functions
+ * pa_threaded_mainloop_lock() and pa_threaded_mainloop_unlock().
+ *
+ * The lock is recursive, so it's safe to use it multiple times from the same
+ * thread. Just make sure you call pa_threaded_mainloop_unlock() the same
+ * number of times you called pa_threaded_mainloop_lock().
+ *
+ * The lock needs to be held whenever you call any PulseAudio function that
+ * uses an object associated with this main loop. Make sure you do not hold
+ * on to the lock more than necessary though, as the threaded main loop stops
+ * while the lock is held.
+ *
+ * Example:
+ *
+ * \code
+ * void my_check_stream_func(pa_threaded_mainloop *m, pa_stream *s) {
+ *     pa_stream_state_t state;
+ *
+ *     pa_threaded_mainloop_lock(m);
+ *
+ *     state = pa_stream_get_state(s);
+ *
+ *     pa_threaded_mainloop_unlock(m);
+ *
+ *     if (state == PA_STREAM_READY)
+ *         printf("Stream is ready!");
+ *     else
+ *         printf("Stream is not ready!");
+ * }
+ * \endcode
+ *
+ * \section cb_sec Callbacks
+ *
+ * Callbacks in PulseAudio are asynchronous, so they require extra care when
+ * using them together with a threaded main loop.
+ *
+ * The easiest way to turn the callback based operations into synchronous
+ * ones, is to simply wait for the callback to be called and continue from
+ * there. This is the approach chosen in PulseAudio's threaded API.
+ *
+ * \subsection basic_subsec Basic callbacks
+ *
+ * For the basic case, where all that is required is to wait for the callback
+ * to be invoked, the code should look something like this:
+ *
+ * Example:
+ *
+ * \code
+ * static void my_drain_callback(pa_stream *s, int success, void *userdata) {
+ *     pa_threaded_mainloop *m;
+ *
+ *     m = userdata;
+ *     assert(m);
+ *
+ *     pa_threaded_mainloop_signal(m, 0);
+ * }
+ *
+ * void my_drain_stream_func(pa_threaded_mainloop *m, pa_stream *s) {
+ *     pa_operation *o;
+ *
+ *     pa_threaded_mainloop_lock(m);
+ *
+ *     o = pa_stream_drain(s, my_drain_callback, m);
+ *     assert(o);
+ *
+ *     while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ *         pa_threaded_mainloop_wait(m);
+ *
+ *     pa_operation_unref(o);
+ *
+ *     pa_threaded_mainloop_unlock(m);
+ * }
+ * \endcode
+ *
+ * The main function, my_drain_stream_func(), will wait for the callback to
+ * be called using pa_threaded_mainloop_wait().
+ *
+ * If your application is multi-threaded, then this waiting must be
+ * done inside a while loop. The reason for this is that multiple
+ * threads might be using pa_threaded_mainloop_wait() at the same
+ * time. Each thread must therefore verify that it was its callback
+ * that was invoked. Also the underlying OS synchronization primitives
+ * are usually not free of spurious wake-ups, so a
+ * pa_threaded_mainloop_wait() must be called within a loop even if
+ * you have only one thread waiting.
+ *
+ * The callback, my_drain_callback(), indicates to the main function that it
+ * has been called using pa_threaded_mainloop_signal().
+ *
+ * As you can see, pa_threaded_mainloop_wait() may only be called with
+ * the lock held. The same thing is true for pa_threaded_mainloop_signal(),
+ * but as the lock is held before the callback is invoked, you do not have to
+ * deal with that.
+ *
+ * The functions will not dead lock because the wait function will release
+ * the lock before waiting and then regrab it once it has been signalled.
+ * For those of you familiar with threads, the behaviour is that of a
+ * condition variable.
+ *
+ * \subsection data_subsec Data callbacks
+ *
+ * For many callbacks, simply knowing that they have been called is
+ * insufficient. The callback also receives some data that is desired. To
+ * access this data safely, we must extend our example a bit:
+ *
+ * \code
+ * static int * volatile drain_result = NULL;
+ *
+ * static void my_drain_callback(pa_stream*s, int success, void *userdata) {
+ *     pa_threaded_mainloop *m;
+ *
+ *     m = userdata;
+ *     assert(m);
+ *
+ *     drain_result = &success;
+ *
+ *     pa_threaded_mainloop_signal(m, 1);
+ * }
+ *
+ * void my_drain_stream_func(pa_threaded_mainloop *m, pa_stream *s) {
+ *     pa_operation *o;
+ *
+ *     pa_threaded_mainloop_lock(m);
+ *
+ *     o = pa_stream_drain(s, my_drain_callback, m);
+ *     assert(o);
+ *
+ *     while (drain_result == NULL)
+ *         pa_threaded_mainloop_wait(m);
+ *
+ *     pa_operation_unref(o);
+ *
+ *     if (*drain_result)
+ *         printf("Success!");
+ *     else
+ *         printf("Bitter defeat...");
+ *
+ *     pa_threaded_mainloop_accept(m);
+ *
+ *     pa_threaded_mainloop_unlock(m);
+ * }
+ * \endcode
+ *
+ * The example is a bit silly as it would probably have been easier to just
+ * copy the contents of success, but for larger data structures this can be
+ * wasteful.
+ *
+ * The difference here compared to the basic callback is the value 1 passed
+ * to pa_threaded_mainloop_signal() and the call to
+ * pa_threaded_mainloop_accept(). What will happen is that
+ * pa_threaded_mainloop_signal() will signal the main function and then wait.
+ * The main function is then free to use the data in the callback until
+ * pa_threaded_mainloop_accept() is called, which will allow the callback
+ * to continue.
+ *
+ * Note that pa_threaded_mainloop_accept() must be called some time between
+ * exiting the while loop and unlocking the main loop! Failure to do so will
+ * result in a race condition. I.e. it is not ok to release the lock and
+ * regrab it before calling pa_threaded_mainloop_accept().
+ *
+ * \subsection async_subsec Asynchronous callbacks
+ *
+ * PulseAudio also has callbacks that are completely asynchronous, meaning
+ * that they can be called at any time. The threaded main loop API provides
+ * the locking mechanism to handle concurrent accesses, but nothing else.
+ * Applications will have to handle communication from the callback to the
+ * main program through their own mechanisms.
+ *
+ * The callbacks that are completely asynchronous are:
+ *
+ * \li State callbacks for contexts, streams, etc.
+ * \li Subscription notifications
+ */
+
+/** \file
+ *
+ * A thread based event loop implementation based on pa_mainloop. The
+ * event loop is run in a helper thread in the background. A few
+ * synchronization primitives are available to access the objects
+ * attached to the event loop safely.
+ *
+ * See also \subpage threaded_mainloop
+ */
+
+/** An opaque threaded main loop object */
+typedef struct pa_threaded_mainloop pa_threaded_mainloop;
+
+/** Allocate a new threaded main loop object. You have to call
+ * pa_threaded_mainloop_start() before the event loop thread starts
+ * running. */
+pa_threaded_mainloop *pa_threaded_mainloop_new(void);
+
+/** Free a threaded main loop object. If the event loop thread is
+ * still running, terminate it with pa_threaded_mainloop_stop()
+ * first. */
+void pa_threaded_mainloop_free(pa_threaded_mainloop* m);
+
+/** Start the event loop thread. */
+int pa_threaded_mainloop_start(pa_threaded_mainloop *m);
+
+/** Terminate the event loop thread cleanly. Make sure to unlock the
+ * mainloop object before calling this function. */
+void pa_threaded_mainloop_stop(pa_threaded_mainloop *m);
+
+/** Lock the event loop object, effectively blocking the event loop
+ * thread from processing events. You can use this to enforce
+ * exclusive access to all objects attached to the event loop. This
+ * lock is recursive. This function may not be called inside the event
+ * loop thread. Events that are dispatched from the event loop thread
+ * are executed with this lock held. */
+void pa_threaded_mainloop_lock(pa_threaded_mainloop *m);
+
+/** Unlock the event loop object, inverse of pa_threaded_mainloop_lock(). */
+void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m);
+
+/** Wait for an event to be signalled by the event loop thread. You
+ * can use this to pass data from the event loop thread to the main
+ * thread in a synchronized fashion. This function may not be called
+ * inside the event loop thread. Prior to this call the event loop
+ * object needs to be locked using pa_threaded_mainloop_lock(). While
+ * waiting the lock will be released. Immediately before returning it
+ * will be acquired again. This function may spuriously wake up even
+ * without pa_threaded_mainloop_signal() being called. You need to
+ * make sure to handle that! */
+void pa_threaded_mainloop_wait(pa_threaded_mainloop *m);
+
+/** Signal all threads waiting for a signalling event in
+ * pa_threaded_mainloop_wait(). If wait_for_accept is non-zero, do
+ * not return before the signal was accepted by a
+ * pa_threaded_mainloop_accept() call. While waiting for that condition
+ * the event loop object is unlocked. */
+void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept);
+
+/** Accept a signal from the event thread issued with
+ * pa_threaded_mainloop_signal(). This call should only be used in
+ * conjunction with pa_threaded_mainloop_signal() with a non-zero
+ * wait_for_accept value.  */
+void pa_threaded_mainloop_accept(pa_threaded_mainloop *m);
+
+/** Return the return value as specified with the main loop's
+ * pa_mainloop_quit() routine. */
+int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m);
+
+/** Return the main loop abstraction layer vtable for this main loop.
+ * There is no need to free this object as it is owned by the loop
+ * and is destroyed when the loop is freed. */
+pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m);
+
+/** Returns non-zero when called from within the event loop thread. \since 0.9.7 */
+int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m);
+
+/** Sets the name of the thread. \since 5.0 */
+void pa_threaded_mainloop_set_name(pa_threaded_mainloop *m, const char *name);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c
new file mode 100644 (file)
index 0000000..e991c33
--- /dev/null
@@ -0,0 +1,211 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#include <sys/time.h>
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "timeval.h"
+
+struct timeval *pa_gettimeofday(struct timeval *tv) {
+    pa_assert(tv);
+
+#if defined(OS_IS_WIN32)
+    /*
+     * Copied from implementation by Steven Edwards (LGPL).
+     * Found on wine mailing list.
+     */
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+#define EPOCHFILETIME (116444736000000000i64)
+#else
+#define EPOCHFILETIME (116444736000000000LL)
+#endif
+{
+    FILETIME ft;
+    LARGE_INTEGER li;
+    int64_t t;
+
+    GetSystemTimeAsFileTime(&ft);
+    li.LowPart  = ft.dwLowDateTime;
+    li.HighPart = ft.dwHighDateTime;
+    t  = li.QuadPart;       /* In 100-nanosecond intervals */
+    t -= EPOCHFILETIME;     /* Offset to the Epoch time */
+    t /= 10;                /* In microseconds */
+    tv->tv_sec  = (time_t) (t / PA_USEC_PER_SEC);
+    tv->tv_usec = (suseconds_t) (t % PA_USEC_PER_SEC);
+}
+#elif defined(HAVE_GETTIMEOFDAY)
+    pa_assert_se(gettimeofday(tv, NULL) == 0);
+#else
+#error "Platform lacks gettimeofday() or equivalent function."
+#endif
+
+    return tv;
+}
+
+pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {
+    pa_usec_t r;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    /* Check which is the earlier time and swap the two arguments if required. */
+    if (PA_UNLIKELY(pa_timeval_cmp(a, b) < 0)) {
+        const struct timeval *c;
+        c = a;
+        a = b;
+        b = c;
+    }
+
+    /* Calculate the second difference*/
+    r = ((pa_usec_t) a->tv_sec - (pa_usec_t) b->tv_sec) * PA_USEC_PER_SEC;
+
+    /* Calculate the microsecond difference */
+    if (a->tv_usec > b->tv_usec)
+        r += (pa_usec_t) a->tv_usec - (pa_usec_t) b->tv_usec;
+    else if (a->tv_usec < b->tv_usec)
+        r -= (pa_usec_t) b->tv_usec - (pa_usec_t) a->tv_usec;
+
+    return r;
+}
+
+int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    if (a->tv_sec < b->tv_sec)
+        return -1;
+
+    if (a->tv_sec > b->tv_sec)
+        return 1;
+
+    if (a->tv_usec < b->tv_usec)
+        return -1;
+
+    if (a->tv_usec > b->tv_usec)
+        return 1;
+
+    return 0;
+}
+
+pa_usec_t pa_timeval_age(const struct timeval *tv) {
+    struct timeval now;
+    pa_assert(tv);
+
+    return pa_timeval_diff(pa_gettimeofday(&now), tv);
+}
+
+struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) {
+    time_t secs;
+    pa_assert(tv);
+
+    secs = (time_t) (v/PA_USEC_PER_SEC);
+
+    if (PA_UNLIKELY(tv->tv_sec > PA_INT_TYPE_MAX(time_t) - secs))
+        goto overflow;
+
+    tv->tv_sec += secs;
+    v -= (pa_usec_t) secs * PA_USEC_PER_SEC;
+    tv->tv_usec += (suseconds_t) v;
+
+    /* Normalize */
+    while ((pa_usec_t) tv->tv_usec >= PA_USEC_PER_SEC) {
+
+        if (PA_UNLIKELY(tv->tv_sec >= PA_INT_TYPE_MAX(time_t)))
+            goto overflow;
+
+        tv->tv_sec++;
+        tv->tv_usec -= (suseconds_t) PA_USEC_PER_SEC;
+    }
+
+    return tv;
+
+overflow:
+    tv->tv_sec = PA_INT_TYPE_MAX(time_t);
+    tv->tv_usec = (suseconds_t) (PA_USEC_PER_SEC-1);
+    return tv;
+}
+
+struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v) {
+    time_t secs;
+    pa_assert(tv);
+
+    secs = (time_t) (v/PA_USEC_PER_SEC);
+
+    if (PA_UNLIKELY(tv->tv_sec < secs))
+        goto underflow;
+
+    tv->tv_sec -= secs;
+    v -= (pa_usec_t) secs * PA_USEC_PER_SEC;
+
+    if (tv->tv_usec >= (suseconds_t) v)
+        tv->tv_usec -= (suseconds_t) v;
+    else {
+
+        if (PA_UNLIKELY(tv->tv_sec <= 0))
+            goto underflow;
+
+        tv->tv_sec --;
+        tv->tv_usec += (suseconds_t) (PA_USEC_PER_SEC - v);
+    }
+
+    return tv;
+
+underflow:
+    tv->tv_sec = 0;
+    tv->tv_usec = 0;
+    return tv;
+}
+
+struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) {
+    pa_assert(tv);
+
+    if (PA_UNLIKELY(v == PA_USEC_INVALID)) {
+        tv->tv_sec = PA_INT_TYPE_MAX(time_t);
+        tv->tv_usec = (suseconds_t) (PA_USEC_PER_SEC-1);
+
+        return tv;
+    }
+
+    tv->tv_sec = (time_t) (v / PA_USEC_PER_SEC);
+    tv->tv_usec = (suseconds_t) (v % PA_USEC_PER_SEC);
+
+    return tv;
+}
+
+pa_usec_t pa_timeval_load(const struct timeval *tv) {
+
+    if (PA_UNLIKELY(!tv))
+        return PA_USEC_INVALID;
+
+    return
+        (pa_usec_t) tv->tv_sec * PA_USEC_PER_SEC +
+        (pa_usec_t) tv->tv_usec;
+}
diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h
new file mode 100644 (file)
index 0000000..9ecf791
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef footimevalhfoo
+#define footimevalhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+#include <pulse/sample.h>
+#include <pulse/version.h>
+
+/** \file
+ * Utility functions for handling timeval calculations */
+
+PA_C_DECL_BEGIN
+
+/** The number of milliseconds in a second */
+#define PA_MSEC_PER_SEC ((pa_usec_t) 1000ULL)
+
+/** The number of microseconds in a second */
+#define PA_USEC_PER_SEC ((pa_usec_t) 1000000ULL)
+
+/** The number of nanoseconds in a second */
+#define PA_NSEC_PER_SEC ((unsigned long long) 1000000000ULL)
+
+/** The number of microseconds in a millisecond */
+#define PA_USEC_PER_MSEC ((pa_usec_t) 1000ULL)
+
+/** The number of nanoseconds in a millisecond */
+#define PA_NSEC_PER_MSEC ((unsigned long long) 1000000ULL)
+
+/** The number of nanoseconds in a microsecond */
+#define PA_NSEC_PER_USEC ((unsigned long long) 1000ULL)
+
+/** Invalid time in usec. \since 0.9.15 */
+#define PA_USEC_INVALID ((pa_usec_t) -1)
+
+/** Biggest time in usec. \since 0.9.18 */
+#define PA_USEC_MAX ((pa_usec_t) -2)
+
+struct timeval;
+
+/** Return the current wallclock timestamp, just like UNIX gettimeofday(). */
+struct timeval *pa_gettimeofday(struct timeval *tv);
+
+/** Calculate the difference between the two specified timeval
+ * structs. */
+pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) PA_GCC_PURE;
+
+/** Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwise */
+int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) PA_GCC_PURE;
+
+/** Return the time difference between now and the specified timestamp */
+pa_usec_t pa_timeval_age(const struct timeval *tv);
+
+/** Add the specified time in microseconds to the specified timeval structure */
+struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v);
+
+/** Subtract the specified time in microseconds to the specified timeval structure. \since 0.9.11 */
+struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v);
+
+/** Store the specified usec value in the timeval struct. \since 0.9.7 */
+struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v);
+
+/** Load the specified tv value and return it in usec. \since 0.9.7 */
+pa_usec_t pa_timeval_load(const struct timeval *tv);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c
new file mode 100644 (file)
index 0000000..31a61a0
--- /dev/null
@@ -0,0 +1,287 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This file is based on the GLIB utf8 validation functions. The
+ * original license text follows. */
+
+/* gutf8.c - Operations on UTF-8 strings.
+ *
+ * Copyright (C) 1999 Tom Tromey
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "utf8.h"
+
+#define FILTER_CHAR '_'
+
+static inline bool is_unicode_valid(uint32_t ch) {
+
+    if (ch >= 0x110000) /* End of unicode space */
+        return false;
+    if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
+        return false;
+    if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
+        return false;
+    if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
+        return false;
+
+    return true;
+}
+
+static inline bool is_continuation_char(uint8_t ch) {
+    if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
+        return false;
+    return true;
+}
+
+static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
+    *u_ch <<= 6;
+    *u_ch |= ch & 0x3f;
+}
+
+static char* utf8_validate(const char *str, char *output) {
+    uint32_t val = 0;
+    uint32_t min = 0;
+    const uint8_t *p, *last;
+    int size;
+    uint8_t *o;
+
+    pa_assert(str);
+
+    o = (uint8_t*) output;
+    for (p = (const uint8_t*) str; *p; p++) {
+        if (*p < 128) {
+            if (o)
+                *o = *p;
+        } else {
+            last = p;
+
+            if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
+                size = 2;
+                min = 128;
+                val = (uint32_t) (*p & 0x1e);
+                goto ONE_REMAINING;
+            } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
+                size = 3;
+                min = (1 << 11);
+                val = (uint32_t) (*p & 0x0f);
+                goto TWO_REMAINING;
+            } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
+                size = 4;
+                min = (1 << 16);
+                val = (uint32_t) (*p & 0x07);
+            } else
+                goto error;
+
+            p++;
+            if (!is_continuation_char(*p))
+                goto error;
+            merge_continuation_char(&val, *p);
+
+TWO_REMAINING:
+            p++;
+            if (!is_continuation_char(*p))
+                goto error;
+            merge_continuation_char(&val, *p);
+
+ONE_REMAINING:
+            p++;
+            if (!is_continuation_char(*p))
+                goto error;
+            merge_continuation_char(&val, *p);
+
+            if (val < min)
+                goto error;
+
+            if (!is_unicode_valid(val))
+                goto error;
+
+            if (o) {
+                memcpy(o, last, (size_t) size);
+                o += size;
+            }
+
+            continue;
+
+error:
+            if (o) {
+                *o = FILTER_CHAR;
+                p = last; /* We retry at the next character */
+            } else
+                goto failure;
+        }
+
+        if (o)
+            o++;
+    }
+
+    if (o) {
+        *o = '\0';
+        return output;
+    }
+
+    return (char*) str;
+
+failure:
+    return NULL;
+}
+
+char* pa_utf8_valid (const char *str) {
+    return utf8_validate(str, NULL);
+}
+
+char* pa_utf8_filter (const char *str) {
+    char *new_str;
+
+    pa_assert(str);
+    new_str = pa_xmalloc(strlen(str) + 1);
+    return utf8_validate(str, new_str);
+}
+
+#ifdef HAVE_ICONV
+
+static char* iconv_simple(const char *str, const char *to, const char *from) {
+    char *new_str;
+    size_t len, inlen;
+    iconv_t cd;
+    ICONV_CONST char *inbuf;
+    char *outbuf;
+    size_t res, inbytes, outbytes;
+
+    pa_assert(str);
+    pa_assert(to);
+    pa_assert(from);
+
+    cd = iconv_open(to, from);
+    if (cd == (iconv_t)-1)
+        return NULL;
+
+    inlen = len = strlen(str) + 1;
+    new_str = pa_xmalloc(len);
+
+    for (;;) {
+        inbuf = (ICONV_CONST char*) str; /* Brain dead prototype for iconv() */
+        inbytes = inlen;
+        outbuf = new_str;
+        outbytes = len;
+
+        res = iconv(cd, &inbuf, &inbytes, &outbuf, &outbytes);
+
+        if (res != (size_t)-1)
+            break;
+
+        if (errno != E2BIG) {
+            pa_xfree(new_str);
+            new_str = NULL;
+            break;
+        }
+
+        pa_assert(inbytes != 0);
+
+        len += inbytes;
+        new_str = pa_xrealloc(new_str, len);
+    }
+
+    iconv_close(cd);
+
+    return new_str;
+}
+
+char* pa_utf8_to_locale (const char *str) {
+    return iconv_simple(str, "", "UTF-8");
+}
+
+char* pa_locale_to_utf8 (const char *str) {
+    return iconv_simple(str, "UTF-8", "");
+}
+
+#else
+
+char* pa_utf8_to_locale (const char *str) {
+    pa_assert(str);
+
+    return pa_ascii_filter(str);
+}
+
+char* pa_locale_to_utf8 (const char *str) {
+    pa_assert(str);
+
+    if (pa_utf8_valid(str))
+        return pa_xstrdup(str);
+
+    return NULL;
+}
+
+#endif
+
+char *pa_ascii_valid(const char *str) {
+    const char *p;
+    pa_assert(str);
+
+    for (p = str; *p; p++)
+        if ((unsigned char) *p >= 128)
+            return NULL;
+
+    return (char*) str;
+}
+
+char *pa_ascii_filter(const char *str) {
+    char *r, *s, *d;
+    pa_assert(str);
+
+    r = pa_xstrdup(str);
+
+    for (s = r, d = r; *s; s++)
+        if ((unsigned char) *s < 128)
+            *(d++) = *s;
+
+    *d = 0;
+
+    return r;
+}
diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h
new file mode 100644 (file)
index 0000000..a72097a
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef fooutf8hfoo
+#define fooutf8hfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+#include <pulse/version.h>
+
+/** \file
+ * UTF-8 validation functions
+ */
+
+PA_C_DECL_BEGIN
+
+/** Test if the specified strings qualifies as valid UTF8. Return the string if so, otherwise NULL */
+char *pa_utf8_valid(const char *str) PA_GCC_PURE;
+
+/** Test if the specified strings qualifies as valid 7-bit ASCII. Return the string if so, otherwise NULL. \since 0.9.15 */
+char *pa_ascii_valid(const char *str) PA_GCC_PURE;
+
+/** Filter all invalid UTF8 characters from the specified string, returning a new fully UTF8 valid string. Don't forget to free the returned string with pa_xfree() */
+char *pa_utf8_filter(const char *str);
+
+/** Filter all invalid ASCII characters from the specified string, returning a new fully ASCII valid string. Don't forget to free the returned string with pa_xfree(). \since 0.9.15 */
+char *pa_ascii_filter(const char *str);
+
+/** Convert a UTF-8 string to the current locale. Free the string using pa_xfree(). */
+char* pa_utf8_to_locale (const char *str);
+
+/** Convert a string in the current locale to UTF-8. Free the string using pa_xfree(). */
+char* pa_locale_to_utf8 (const char *str);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/util.c b/src/pulse/util.c
new file mode 100644 (file)
index 0000000..54fe7a2
--- /dev/null
@@ -0,0 +1,344 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+#ifdef OS_IS_DARWIN
+#include <libgen.h>
+#include <sys/sysctl.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/socket.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/usergroup.h>
+
+#include "util.h"
+
+#if defined(HAVE_DLADDR) && defined(PA_GCC_WEAKREF)
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+#include <dlfcn.h>
+
+static int _main() PA_GCC_WEAKREF(main);
+#endif
+
+char *pa_get_user_name(char *s, size_t l) {
+    const char *p;
+    char *name = NULL;
+#ifdef OS_IS_WIN32
+    char buf[1024];
+#endif
+
+#ifdef HAVE_PWD_H
+    struct passwd *r;
+#endif
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    p = NULL;
+#ifdef HAVE_GETUID
+    p = getuid() == 0 ? "root" : NULL;
+#endif
+    if (!p) p = getenv("USER");
+    if (!p) p = getenv("LOGNAME");
+    if (!p) p = getenv("USERNAME");
+
+    if (p) {
+        name = pa_strlcpy(s, p, l);
+    } else {
+#ifdef HAVE_PWD_H
+
+        if ((r = pa_getpwuid_malloc(getuid())) == NULL) {
+            pa_snprintf(s, l, "%lu", (unsigned long) getuid());
+            return s;
+        }
+
+        name = pa_strlcpy(s, r->pw_name, l);
+        pa_getpwuid_free(r);
+
+#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */
+        DWORD size = sizeof(buf);
+
+        if (!GetUserName(buf, &size)) {
+            errno = ENOENT;
+            return NULL;
+        }
+
+        name = pa_strlcpy(s, buf, l);
+
+#else /* HAVE_PWD_H */
+
+        return NULL;
+#endif /* HAVE_PWD_H */
+    }
+
+    return name;
+}
+
+char *pa_get_host_name(char *s, size_t l) {
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    if (gethostname(s, l) < 0)
+        return NULL;
+
+    s[l-1] = 0;
+    return s;
+}
+
+char *pa_get_home_dir(char *s, size_t l) {
+    char *e;
+    char *dir;
+#ifdef HAVE_PWD_H
+    struct passwd *r;
+#endif
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    if ((e = getenv("HOME"))) {
+        dir = pa_strlcpy(s, e, l);
+        goto finish;
+    }
+
+    if ((e = getenv("USERPROFILE"))) {
+        dir = pa_strlcpy(s, e, l);
+        goto finish;
+    }
+
+#ifdef HAVE_PWD_H
+    errno = 0;
+    if ((r = pa_getpwuid_malloc(getuid())) == NULL) {
+        if (!errno)
+            errno = ENOENT;
+
+        return NULL;
+    }
+
+    dir = pa_strlcpy(s, r->pw_dir, l);
+
+    pa_getpwuid_free(r);
+#endif /* HAVE_PWD_H */
+
+finish:
+    if (!dir) {
+        errno = ENOENT;
+        return NULL;
+    }
+
+    if (!pa_is_path_absolute(dir)) {
+        pa_log("Failed to get the home directory, not an absolute path: %s", dir);
+        errno = ENOENT;
+        return NULL;
+    }
+
+    return dir;
+}
+
+char *pa_get_binary_name(char *s, size_t l) {
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+#if defined(OS_IS_WIN32)
+    {
+        char path[PATH_MAX];
+
+        if (GetModuleFileName(NULL, path, PATH_MAX))
+            return pa_strlcpy(s, pa_path_get_filename(path), l);
+    }
+#endif
+
+#if defined(__linux__) || defined(__FreeBSD_kernel__)
+    {
+        char *rp;
+        /* This works on Linux and Debian/kFreeBSD */
+
+        if ((rp = pa_readlink("/proc/self/exe"))) {
+            pa_strlcpy(s, pa_path_get_filename(rp), l);
+            pa_xfree(rp);
+            return s;
+        }
+    }
+#endif
+
+#ifdef __FreeBSD__
+    {
+        char *rp;
+
+        if ((rp = pa_readlink("/proc/curproc/file"))) {
+            pa_strlcpy(s, pa_path_get_filename(rp), l);
+            pa_xfree(rp);
+            return s;
+        }
+    }
+#endif
+
+#if defined(HAVE_DLADDR) && defined(PA_GCC_WEAKREF)
+    {
+        Dl_info info;
+        if(_main) {
+            int err = dladdr(&_main, &info);
+            if (err != 0) {
+                char *p = pa_realpath(info.dli_fname);
+                if (p)
+                    return p;
+            }
+        }
+    }
+#endif
+
+#if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_NAME)
+    {
+
+        #ifndef TASK_COMM_LEN
+        /* Actually defined in linux/sched.h */
+        #define TASK_COMM_LEN 16
+        #endif
+
+        char tcomm[TASK_COMM_LEN+1];
+        memset(tcomm, 0, sizeof(tcomm));
+
+        /* This works on Linux only */
+        if (prctl(PR_GET_NAME, (unsigned long) tcomm, 0, 0, 0) == 0)
+            return pa_strlcpy(s, tcomm, l);
+
+    }
+#endif
+
+#ifdef OS_IS_DARWIN
+    {
+        int mib[] = { CTL_KERN, KERN_PROCARGS, getpid(), 0 };
+        size_t len, nmib = (sizeof(mib) / sizeof(mib[0])) - 1;
+        char *buf;
+
+        sysctl(mib, nmib, NULL, &len, NULL, 0);
+        buf = (char *) pa_xmalloc(len);
+
+        if (sysctl(mib, nmib, buf, &len, NULL, 0) == 0) {
+            pa_strlcpy(s, basename(buf), l);
+            pa_xfree(buf);
+            return s;
+        }
+
+        pa_xfree(buf);
+
+        /* fall thru */
+    }
+#endif /* OS_IS_DARWIN */
+
+    errno = ENOENT;
+    return NULL;
+}
+
+char *pa_path_get_filename(const char *p) {
+    char *fn;
+
+    if (!p)
+        return NULL;
+
+    if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))
+        return fn+1;
+
+    return (char*) p;
+}
+
+char *pa_get_fqdn(char *s, size_t l) {
+    char hn[256];
+#ifdef HAVE_GETADDRINFO
+    struct addrinfo *a = NULL, hints;
+#endif
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    if (!pa_get_host_name(hn, sizeof(hn)))
+        return NULL;
+
+#ifdef HAVE_GETADDRINFO
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_flags = AI_CANONNAME;
+
+    if (getaddrinfo(hn, NULL, &hints, &a))
+        return pa_strlcpy(s, hn, l);
+
+    if (!a->ai_canonname || !*a->ai_canonname) {
+        freeaddrinfo(a);
+        return pa_strlcpy(s, hn, l);
+    }
+
+    pa_strlcpy(s, a->ai_canonname, l);
+    freeaddrinfo(a);
+    return s;
+#else
+    return pa_strlcpy(s, hn, l);
+#endif
+}
+
+int pa_msleep(unsigned long t) {
+#ifdef OS_IS_WIN32
+    Sleep(t);
+    return 0;
+#elif defined(HAVE_NANOSLEEP)
+    struct timespec ts;
+
+    ts.tv_sec = (time_t) (t / PA_MSEC_PER_SEC);
+    ts.tv_nsec = (long) ((t % PA_MSEC_PER_SEC) * PA_NSEC_PER_MSEC);
+
+    return nanosleep(&ts, NULL);
+#else
+#error "Platform lacks a sleep function."
+#endif
+}
diff --git a/src/pulse/util.h b/src/pulse/util.h
new file mode 100644 (file)
index 0000000..e4a62da
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stddef.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/version.h>
+
+/** \file
+ * Assorted utility functions */
+
+PA_C_DECL_BEGIN
+
+/** Return the current username in the specified string buffer. */
+char *pa_get_user_name(char *s, size_t l);
+
+/** Return the current hostname in the specified buffer. */
+char *pa_get_host_name(char *s, size_t l);
+
+/** Return the fully qualified domain name in s */
+char *pa_get_fqdn(char *s, size_t l);
+
+/** Return the home directory of the current user */
+char *pa_get_home_dir(char *s, size_t l);
+
+/** Return the binary file name of the current process. This is not
+ * supported on all architectures, in which case NULL is returned. */
+char *pa_get_binary_name(char *s, size_t l);
+
+/** Return a pointer to the filename inside a path (which is the last
+ * component). If passed NULL will return NULL. */
+char *pa_path_get_filename(const char *p);
+
+/** Wait t milliseconds */
+int pa_msleep(unsigned long t);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in
new file mode 100644 (file)
index 0000000..e8c1e6d
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef fooversionhfoo /*-*-C-*-*/
+#define fooversionhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* WARNING: Make sure to edit the real source file version.h.in! */
+
+#include <pulse/cdecl.h>
+
+/** \file
+ * Define header version */
+
+PA_C_DECL_BEGIN
+
+/** Return the version of the header files. Keep in mind that this is
+a macro and not a function, so it is impossible to get the pointer of
+it. */
+#define pa_get_headers_version() ("@PA_MAJOR@.@PA_MINOR@.0")
+
+/** Return the version of the library the current application is
+ * linked to. */
+const char* pa_get_library_version(void);
+
+/** The current API version. Version 6 relates to Polypaudio
+ * 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have
+ * PA_API_VERSION undefined. Please note that this is only ever
+ * increased on incompatible API changes!  */
+#define PA_API_VERSION @PA_API_VERSION@
+
+/** The current protocol version. Version 8 relates to Polypaudio
+ * 0.8/PulseAudio 0.9. */
+#define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@
+
+/** The major version of PA. \since 0.9.15 */
+#define PA_MAJOR @PA_MAJOR@
+
+/** The minor version of PA. \since 0.9.15 */
+#define PA_MINOR @PA_MINOR@
+
+/** The micro version of PA (will always be 0 from v1.0 onwards). \since 0.9.15 */
+#define PA_MICRO 0
+
+/** Evaluates to TRUE if the PulseAudio library version is equal or
+ * newer than the specified. \since 0.9.16 */
+#define PA_CHECK_VERSION(major,minor,micro)                             \
+    ((PA_MAJOR > (major)) ||                                            \
+     (PA_MAJOR == (major) && PA_MINOR > (minor)) ||                     \
+     (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro)))
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
new file mode 100644 (file)
index 0000000..ffd42ec
--- /dev/null
@@ -0,0 +1,991 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/sample-util.h>
+
+#include "volume.h"
+
+int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
+    int i;
+    pa_assert(a);
+    pa_assert(b);
+
+    pa_return_val_if_fail(pa_cvolume_valid(a), 0);
+
+    if (PA_UNLIKELY(a == b))
+        return 1;
+
+    pa_return_val_if_fail(pa_cvolume_valid(b), 0);
+
+    if (a->channels != b->channels)
+        return 0;
+
+    for (i = 0; i < a->channels; i++)
+        if (a->values[i] != b->values[i])
+            return 0;
+
+    return 1;
+}
+
+pa_cvolume* pa_cvolume_init(pa_cvolume *a) {
+    unsigned c;
+
+    pa_assert(a);
+
+    a->channels = 0;
+
+    for (c = 0; c < PA_CHANNELS_MAX; c++)
+        a->values[c] = PA_VOLUME_INVALID;
+
+    return a;
+}
+
+pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {
+    int i;
+
+    pa_assert(a);
+    pa_assert(pa_channels_valid(channels));
+
+    a->channels = (uint8_t) channels;
+
+    for (i = 0; i < a->channels; i++)
+        /* Clamp in case there is stale data that exceeds the current
+         * PA_VOLUME_MAX */
+        a->values[i] = PA_CLAMP_VOLUME(v);
+
+    return a;
+}
+
+pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
+    uint64_t sum = 0;
+    unsigned c;
+
+    pa_assert(a);
+    pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
+
+    for (c = 0; c < a->channels; c++)
+        sum += a->values[c];
+
+    sum /= a->channels;
+
+    return (pa_volume_t) sum;
+}
+
+pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) {
+    uint64_t sum = 0;
+    unsigned c, n;
+
+    pa_assert(a);
+
+    if (!cm)
+        return pa_cvolume_avg(a);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
+
+    for (c = n = 0; c < a->channels; c++) {
+
+        if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
+            continue;
+
+        sum += a->values[c];
+        n ++;
+    }
+
+    if (n > 0)
+        sum /= n;
+
+    return (pa_volume_t) sum;
+}
+
+pa_volume_t pa_cvolume_max(const pa_cvolume *a) {
+    pa_volume_t m = PA_VOLUME_MUTED;
+    unsigned c;
+
+    pa_assert(a);
+    pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
+
+    for (c = 0; c < a->channels; c++)
+        if (a->values[c] > m)
+            m = a->values[c];
+
+    return m;
+}
+
+pa_volume_t pa_cvolume_min(const pa_cvolume *a) {
+    pa_volume_t m = PA_VOLUME_MAX;
+    unsigned c;
+
+    pa_assert(a);
+    pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
+
+    for (c = 0; c < a->channels; c++)
+        if (a->values[c] < m)
+            m = a->values[c];
+
+    return m;
+}
+
+pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) {
+    pa_volume_t m = PA_VOLUME_MUTED;
+    unsigned c;
+
+    pa_assert(a);
+
+    if (!cm)
+        return pa_cvolume_max(a);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
+
+    for (c = 0; c < a->channels; c++) {
+
+        if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
+            continue;
+
+        if (a->values[c] > m)
+            m = a->values[c];
+    }
+
+    return m;
+}
+
+pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) {
+    pa_volume_t m = PA_VOLUME_MAX;
+    unsigned c;
+
+    pa_assert(a);
+
+    if (!cm)
+        return pa_cvolume_min(a);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
+
+    for (c = 0; c < a->channels; c++) {
+
+        if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
+            continue;
+
+        if (a->values[c] < m)
+            m = a->values[c];
+    }
+
+    return m;
+}
+
+pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) {
+    uint64_t result;
+
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID);
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID);
+
+    /* cbrt((a/PA_VOLUME_NORM)^3*(b/PA_VOLUME_NORM)^3)*PA_VOLUME_NORM = a*b/PA_VOLUME_NORM */
+
+    result = ((uint64_t) a * (uint64_t) b + (uint64_t) PA_VOLUME_NORM / 2ULL) / (uint64_t) PA_VOLUME_NORM;
+
+    if (result > (uint64_t)PA_VOLUME_MAX)
+        pa_log_warn("pa_sw_volume_multiply: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings.");
+
+    return (pa_volume_t) PA_CLAMP_VOLUME(result);
+}
+
+pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) {
+    uint64_t result;
+
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID);
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID);
+
+    if (b <= PA_VOLUME_MUTED)
+        return 0;
+
+    result = ((uint64_t) a * (uint64_t) PA_VOLUME_NORM + (uint64_t) b / 2ULL) / (uint64_t) b;
+
+    if (result > (uint64_t)PA_VOLUME_MAX)
+        pa_log_warn("pa_sw_volume_divide: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings.");
+
+    return (pa_volume_t) PA_CLAMP_VOLUME(result);
+}
+
+/* Amplitude, not power */
+static double linear_to_dB(double v) {
+    return 20.0 * log10(v);
+}
+
+static double dB_to_linear(double v) {
+    return pow(10.0, v / 20.0);
+}
+
+pa_volume_t pa_sw_volume_from_dB(double dB) {
+    if (isinf(dB) < 0 || dB <= PA_DECIBEL_MININFTY)
+        return PA_VOLUME_MUTED;
+
+    return pa_sw_volume_from_linear(dB_to_linear(dB));
+}
+
+double pa_sw_volume_to_dB(pa_volume_t v) {
+
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), PA_DECIBEL_MININFTY);
+
+    if (v <= PA_VOLUME_MUTED)
+        return PA_DECIBEL_MININFTY;
+
+    return linear_to_dB(pa_sw_volume_to_linear(v));
+}
+
+pa_volume_t pa_sw_volume_from_linear(double v) {
+
+    if (v <= 0.0)
+        return PA_VOLUME_MUTED;
+
+    /*
+     * We use a cubic mapping here, as suggested and discussed here:
+     *
+     * http://www.robotplanet.dk/audio/audio_gui_design/
+     * http://lists.linuxaudio.org/pipermail/linux-audio-dev/2009-May/thread.html#23151
+     *
+     * We make sure that the conversion to linear and back yields the
+     * same volume value! That's why we need the lround() below!
+     */
+
+    return (pa_volume_t) PA_CLAMP_VOLUME((uint64_t) lround(cbrt(v) * PA_VOLUME_NORM));
+}
+
+double pa_sw_volume_to_linear(pa_volume_t v) {
+    double f;
+
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0.0);
+
+    if (v <= PA_VOLUME_MUTED)
+        return 0.0;
+
+    if (v == PA_VOLUME_NORM)
+        return 1.0;
+
+    f = ((double) v / PA_VOLUME_NORM);
+
+    return f*f*f;
+}
+
+char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {
+    unsigned channel;
+    bool first = true;
+    char *e;
+
+    pa_assert(s);
+    pa_assert(l > 0);
+    pa_assert(c);
+
+    pa_init_i18n();
+
+    if (!pa_cvolume_valid(c)) {
+        pa_snprintf(s, l, _("(invalid)"));
+        return s;
+    }
+
+    *(e = s) = 0;
+
+    for (channel = 0; channel < c->channels && l > 1; channel++) {
+        l -= pa_snprintf(e, l, "%s%u: %3u%%",
+                      first ? "" : " ",
+                      channel,
+                      (unsigned)(((uint64_t)c->values[channel] * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM));
+
+        e = strchr(e, 0);
+        first = false;
+    }
+
+    return s;
+}
+
+char *pa_volume_snprint(char *s, size_t l, pa_volume_t v) {
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    pa_init_i18n();
+
+    if (!PA_VOLUME_IS_VALID(v)) {
+        pa_snprintf(s, l, _("(invalid)"));
+        return s;
+    }
+
+    pa_snprintf(s, l, "%3u%%", (unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM));
+    return s;
+}
+
+char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c) {
+    unsigned channel;
+    bool first = true;
+    char *e;
+
+    pa_assert(s);
+    pa_assert(l > 0);
+    pa_assert(c);
+
+    pa_init_i18n();
+
+    if (!pa_cvolume_valid(c)) {
+        pa_snprintf(s, l, _("(invalid)"));
+        return s;
+    }
+
+    *(e = s) = 0;
+
+    for (channel = 0; channel < c->channels && l > 1; channel++) {
+        double f = pa_sw_volume_to_dB(c->values[channel]);
+
+        l -= pa_snprintf(e, l, "%s%u: %0.2f dB",
+                         first ? "" : " ",
+                         channel,
+                         isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f);
+
+        e = strchr(e, 0);
+        first = false;
+    }
+
+    return s;
+}
+
+char *pa_cvolume_snprint_verbose(char *s, size_t l, const pa_cvolume *c, const pa_channel_map *map, int print_dB) {
+    char *current = s;
+    bool first = true;
+
+    pa_assert(s);
+    pa_assert(l > 0);
+    pa_assert(c);
+
+    pa_init_i18n();
+
+    if (!pa_cvolume_valid(c)) {
+        pa_snprintf(s, l, _("(invalid)"));
+        return s;
+    }
+
+    pa_assert(!map || (map->channels == c->channels));
+    pa_assert(!map || pa_channel_map_valid(map));
+
+    current[0] = 0;
+
+    for (unsigned channel = 0; channel < c->channels && l > 1; channel++) {
+        char channel_position[32];
+        size_t bytes_printed;
+        char buf[PA_VOLUME_SNPRINT_VERBOSE_MAX];
+
+        if (map)
+            pa_snprintf(channel_position, sizeof(channel_position), "%s", pa_channel_position_to_string(map->map[channel]));
+        else
+            pa_snprintf(channel_position, sizeof(channel_position), "%u", channel);
+
+        bytes_printed = pa_snprintf(current, l, "%s%s: %s",
+                                    first ? "" : ",   ",
+                                    channel_position,
+                                    pa_volume_snprint_verbose(buf, sizeof(buf), c->values[channel], print_dB));
+        l -= bytes_printed;
+        current += bytes_printed;
+        first = false;
+    }
+
+    return s;
+}
+
+char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) {
+    double f;
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    pa_init_i18n();
+
+    if (!PA_VOLUME_IS_VALID(v)) {
+        pa_snprintf(s, l, _("(invalid)"));
+        return s;
+    }
+
+    f = pa_sw_volume_to_dB(v);
+    pa_snprintf(s, l, "%0.2f dB", isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f);
+
+    return s;
+}
+
+char *pa_volume_snprint_verbose(char *s, size_t l, pa_volume_t v, int print_dB) {
+    char dB[PA_SW_VOLUME_SNPRINT_DB_MAX];
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    pa_init_i18n();
+
+    if (!PA_VOLUME_IS_VALID(v)) {
+        pa_snprintf(s, l, _("(invalid)"));
+        return s;
+    }
+
+    pa_snprintf(s, l, "%" PRIu32 " / %3u%%%s%s",
+                v,
+                (unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM),
+                print_dB ? " / " : "",
+                print_dB ? pa_sw_volume_snprint_dB(dB, sizeof(dB), v) : "");
+
+    return s;
+}
+
+int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
+    unsigned c;
+    pa_assert(a);
+
+    pa_return_val_if_fail(pa_cvolume_valid(a), 0);
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0);
+
+    for (c = 0; c < a->channels; c++)
+        if (a->values[c] != v)
+            return 0;
+
+    return 1;
+}
+
+pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
+    unsigned i;
+
+    pa_assert(dest);
+    pa_assert(a);
+    pa_assert(b);
+
+    pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+    pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
+
+    for (i = 0; i < a->channels && i < b->channels; i++)
+        dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]);
+
+    dest->channels = (uint8_t) i;
+
+    return dest;
+}
+
+pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) {
+    unsigned i;
+
+    pa_assert(dest);
+    pa_assert(a);
+
+    pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL);
+
+    for (i = 0; i < a->channels; i++)
+        dest->values[i] = pa_sw_volume_multiply(a->values[i], b);
+
+    dest->channels = (uint8_t) i;
+
+    return dest;
+}
+
+pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
+    unsigned i;
+
+    pa_assert(dest);
+    pa_assert(a);
+    pa_assert(b);
+
+    pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+    pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
+
+    for (i = 0; i < a->channels && i < b->channels; i++)
+        dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]);
+
+    dest->channels = (uint8_t) i;
+
+    return dest;
+}
+
+pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) {
+    unsigned i;
+
+    pa_assert(dest);
+    pa_assert(a);
+
+    pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL);
+
+    for (i = 0; i < a->channels; i++)
+        dest->values[i] = pa_sw_volume_divide(a->values[i], b);
+
+    dest->channels = (uint8_t) i;
+
+    return dest;
+}
+
+int pa_cvolume_valid(const pa_cvolume *v) {
+    unsigned c;
+
+    pa_assert(v);
+
+    if (!pa_channels_valid(v->channels))
+        return 0;
+
+    for (c = 0; c < v->channels; c++)
+        if (!PA_VOLUME_IS_VALID(v->values[c]))
+            return 0;
+
+    return 1;
+}
+
+static bool on_left(pa_channel_position_t p) {
+    return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LEFT);
+}
+
+static bool on_right(pa_channel_position_t p) {
+    return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_RIGHT);
+}
+
+static bool on_center(pa_channel_position_t p) {
+    return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_CENTER);
+}
+
+static bool on_hfe(pa_channel_position_t p) {
+    return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_HFE);
+}
+
+static bool on_lfe(pa_channel_position_t p) {
+    return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LFE);
+}
+
+static bool on_front(pa_channel_position_t p) {
+    return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_FRONT);
+}
+
+static bool on_rear(pa_channel_position_t p) {
+    return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_REAR);
+}
+
+pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) {
+    int a, b;
+    pa_cvolume result;
+
+    pa_assert(v);
+    pa_assert(from);
+    pa_assert(to);
+
+    pa_return_val_if_fail(pa_channel_map_valid(to), NULL);
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL);
+
+    if (pa_channel_map_equal(from, to))
+        return v;
+
+    result.channels = to->channels;
+
+    for (b = 0; b < to->channels; b++) {
+        pa_volume_t k = 0;
+        int n = 0;
+
+        for (a = 0; a < from->channels; a++)
+            if (from->map[a] == to->map[b]) {
+                k += v->values[a];
+                n ++;
+            }
+
+        if (n <= 0) {
+            for (a = 0; a < from->channels; a++)
+                if ((on_left(from->map[a]) && on_left(to->map[b])) ||
+                    (on_right(from->map[a]) && on_right(to->map[b])) ||
+                    (on_center(from->map[a]) && on_center(to->map[b])) ||
+                    (on_lfe(from->map[a]) && on_lfe(to->map[b]))) {
+
+                    k += v->values[a];
+                    n ++;
+                }
+        }
+
+        if (n <= 0)
+            k = pa_cvolume_avg(v);
+        else
+            k /= n;
+
+        result.values[b] = k;
+    }
+
+    *v = result;
+    return v;
+}
+
+int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) {
+
+    pa_assert(v);
+    pa_assert(ss);
+
+    pa_return_val_if_fail(pa_cvolume_valid(v), 0);
+    pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
+
+    return v->channels == ss->channels;
+}
+
+int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) {
+    pa_assert(v);
+    pa_assert(cm);
+
+    pa_return_val_if_fail(pa_cvolume_valid(v), 0);
+    pa_return_val_if_fail(pa_channel_map_valid(cm), 0);
+
+    return v->channels == cm->channels;
+}
+
+/*
+ * Returns the average volume of l and r, where l and r are two disjoint sets of channels
+ * (e g left and right, or front and rear).
+ */
+static void get_avg(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r,
+                    bool (*on_l)(pa_channel_position_t), bool (*on_r)(pa_channel_position_t)) {
+    int c;
+    pa_volume_t left = 0, right = 0;
+    unsigned n_left = 0, n_right = 0;
+
+    pa_assert(v);
+    pa_assert(map);
+    pa_assert(map->channels == v->channels);
+    pa_assert(l);
+    pa_assert(r);
+
+    for (c = 0; c < map->channels; c++) {
+        if (on_l(map->map[c])) {
+            left += v->values[c];
+            n_left++;
+        } else if (on_r(map->map[c])) {
+            right += v->values[c];
+            n_right++;
+        }
+    }
+
+    if (n_left <= 0)
+        *l = PA_VOLUME_NORM;
+    else
+        *l = left / n_left;
+
+    if (n_right <= 0)
+        *r = PA_VOLUME_NORM;
+    else
+        *r = right / n_right;
+}
+
+float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) {
+    pa_volume_t left, right;
+
+    pa_assert(v);
+    pa_assert(map);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
+
+    if (!pa_channel_map_can_balance(map))
+        return 0.0f;
+
+    get_avg(map, v, &left, &right, on_left, on_right);
+
+    if (left == right)
+        return 0.0f;
+
+    /*   1.0,  0.0  =>  -1.0
+         0.0,  1.0  =>   1.0
+         0.0,  0.0  =>   0.0
+         0.5,  0.5  =>   0.0
+         1.0,  0.5  =>  -0.5
+         1.0,  0.25 => -0.75
+         0.75, 0.25 => -0.66
+         0.5,  0.25 => -0.5   */
+
+    if (left > right)
+        return -1.0f + ((float) right / (float) left);
+    else
+        return 1.0f - ((float) left / (float) right);
+}
+
+static pa_cvolume* set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance,
+                               bool (*on_l)(pa_channel_position_t), bool (*on_r)(pa_channel_position_t)) {
+
+    pa_volume_t left, nleft, right, nright, m;
+    unsigned c;
+
+    get_avg(map, v, &left, &right, on_l, on_r);
+
+    m = PA_MAX(left, right);
+
+    if (new_balance <= 0) {
+        nright = (new_balance + 1.0f) * m;
+        nleft = m;
+    } else {
+        nleft = (1.0f - new_balance) * m;
+        nright = m;
+    }
+
+    for (c = 0; c < map->channels; c++) {
+        if (on_l(map->map[c])) {
+            if (left == 0)
+                v->values[c] = nleft;
+            else
+                v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left);
+        } else if (on_r(map->map[c])) {
+            if (right == 0)
+                v->values[c] = nright;
+            else
+                v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right);
+        }
+    }
+
+    return v;
+}
+
+
+pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) {
+    pa_assert(map);
+    pa_assert(v);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
+    pa_return_val_if_fail(new_balance >= -1.0f, NULL);
+    pa_return_val_if_fail(new_balance <= 1.0f, NULL);
+
+    if (!pa_channel_map_can_balance(map))
+        return v;
+
+    return set_balance(v, map, new_balance, on_left, on_right);
+}
+
+pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) {
+    unsigned c;
+    pa_volume_t t = 0;
+
+    pa_assert(v);
+
+    pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL);
+
+    t = pa_cvolume_max(v);
+
+    if (t <= PA_VOLUME_MUTED)
+        return pa_cvolume_set(v, v->channels, max);
+
+    for (c = 0; c < v->channels; c++)
+        v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t);
+
+    return v;
+}
+
+pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map *cm, pa_channel_position_mask_t mask) {
+    unsigned c;
+    pa_volume_t t = 0;
+
+    pa_assert(v);
+
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL);
+
+    if (!cm)
+        return pa_cvolume_scale(v, max);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, cm), NULL);
+
+    t = pa_cvolume_max_mask(v, cm, mask);
+
+    if (t <= PA_VOLUME_MUTED)
+        return pa_cvolume_set(v, v->channels, max);
+
+    for (c = 0; c < v->channels; c++)
+        v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t);
+
+    return v;
+}
+
+float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) {
+    pa_volume_t rear, front;
+
+    pa_assert(v);
+    pa_assert(map);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
+
+    if (!pa_channel_map_can_fade(map))
+        return 0.0f;
+
+    get_avg(map, v, &rear, &front, on_rear, on_front);
+
+    if (front == rear)
+        return 0.0f;
+
+    if (rear > front)
+        return -1.0f + ((float) front / (float) rear);
+    else
+        return 1.0f - ((float) rear / (float) front);
+}
+
+pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float new_fade) {
+    pa_assert(map);
+    pa_assert(v);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
+    pa_return_val_if_fail(new_fade >= -1.0f, NULL);
+    pa_return_val_if_fail(new_fade <= 1.0f, NULL);
+
+    if (!pa_channel_map_can_fade(map))
+        return v;
+
+    return set_balance(v, map, new_fade, on_rear, on_front);
+}
+
+float pa_cvolume_get_lfe_balance(const pa_cvolume *v, const pa_channel_map *map) {
+    pa_volume_t hfe, lfe;
+
+    pa_assert(v);
+    pa_assert(map);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
+
+    if (!pa_channel_map_can_lfe_balance(map))
+        return 0.0f;
+
+    get_avg(map, v, &hfe, &lfe, on_hfe, on_lfe);
+
+    if (hfe == lfe)
+        return 0.0f;
+
+    if (hfe > lfe)
+        return -1.0f + ((float) lfe / (float) hfe);
+    else
+        return 1.0f - ((float) hfe / (float) lfe);
+}
+
+pa_cvolume* pa_cvolume_set_lfe_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) {
+    pa_assert(map);
+    pa_assert(v);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
+    pa_return_val_if_fail(new_balance >= -1.0f, NULL);
+    pa_return_val_if_fail(new_balance <= 1.0f, NULL);
+
+    if (!pa_channel_map_can_lfe_balance(map))
+        return v;
+
+    return set_balance(v, map, new_balance, on_hfe, on_lfe);
+}
+
+pa_cvolume* pa_cvolume_set_position(
+        pa_cvolume *cv,
+        const pa_channel_map *map,
+        pa_channel_position_t t,
+        pa_volume_t v) {
+
+    unsigned c;
+    bool good = false;
+
+    pa_assert(cv);
+    pa_assert(map);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), NULL);
+    pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, NULL);
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), NULL);
+
+    for (c = 0; c < map->channels; c++)
+        if (map->map[c] == t) {
+            cv->values[c] = v;
+            good = true;
+        }
+
+    return good ? cv : NULL;
+}
+
+pa_volume_t pa_cvolume_get_position(
+        pa_cvolume *cv,
+        const pa_channel_map *map,
+        pa_channel_position_t t) {
+
+    unsigned c;
+    pa_volume_t v = PA_VOLUME_MUTED;
+
+    pa_assert(cv);
+    pa_assert(map);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), PA_VOLUME_MUTED);
+    pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, PA_VOLUME_MUTED);
+
+    for (c = 0; c < map->channels; c++)
+        if (map->map[c] == t)
+            if (cv->values[c] > v)
+                v = cv->values[c];
+
+    return v;
+}
+
+pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
+    unsigned i;
+
+    pa_assert(dest);
+    pa_assert(a);
+    pa_assert(b);
+
+    pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+    pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
+
+    for (i = 0; i < a->channels && i < b->channels; i++)
+        dest->values[i] = PA_MAX(a->values[i], b->values[i]);
+
+    dest->channels = (uint8_t) i;
+
+    return dest;
+}
+
+pa_cvolume* pa_cvolume_inc_clamp(pa_cvolume *v, pa_volume_t inc, pa_volume_t limit) {
+    pa_volume_t m;
+
+    pa_assert(v);
+
+    pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(inc), NULL);
+
+    m = pa_cvolume_max(v);
+
+    if (m >= limit - inc)
+        m = limit;
+    else
+        m += inc;
+
+    return pa_cvolume_scale(v, m);
+}
+
+pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc) {
+    return pa_cvolume_inc_clamp(v, inc, PA_VOLUME_MAX);
+}
+
+pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) {
+    pa_volume_t m;
+
+    pa_assert(v);
+
+    pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+    pa_return_val_if_fail(PA_VOLUME_IS_VALID(dec), NULL);
+
+    m = pa_cvolume_max(v);
+
+    if (m <= PA_VOLUME_MUTED + dec)
+        m = PA_VOLUME_MUTED;
+    else
+        m -= dec;
+
+    return pa_cvolume_scale(v, m);
+}
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
new file mode 100644 (file)
index 0000000..8cf4fa4
--- /dev/null
@@ -0,0 +1,436 @@
+#ifndef foovolumehfoo
+#define foovolumehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <limits.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/version.h>
+
+/** \page volume Volume Control
+ *
+ * \section overv_sec Overview
+ *
+ * Sinks, sources, sink inputs and samples can all have their own volumes.
+ * To deal with these, The PulseAudio library contains a number of functions
+ * that ease handling.
+ *
+ * The basic volume type in PulseAudio is the \ref pa_volume_t type. Most of
+ * the time, applications will use the aggregated pa_cvolume structure that
+ * can store the volume of all channels at once.
+ *
+ * Volumes commonly span between muted (0%), and normal (100%). It is possible
+ * to set volumes to higher than 100%, but clipping might occur.
+ *
+ * There is no single well-defined meaning attached to the 100% volume for a
+ * sink input. In fact, it depends on the server configuration. With flat
+ * volumes enabled (the default in most Linux distributions), it means the
+ * maximum volume that the sound hardware is capable of, which is usually so
+ * high that you absolutely must not set sink input volume to 100% unless the
+ * the user explicitly requests that (note that usually you shouldn't set the
+ * volume anyway if the user doesn't explicitly request it, instead, let
+ * PulseAudio decide the volume for the sink input). With flat volumes disabled
+ * (the default in Ubuntu), the sink input volume is relative to the sink
+ * volume, so 100% sink input volume means that the sink input is played at the
+ * current sink volume level. In this case 100% is often a good default volume
+ * for a sink input, although you still should let PulseAudio decide the
+ * default volume. It is possible to figure out whether flat volume mode is in
+ * effect for a given sink by calling pa_context_get_sink_info_by_name().
+ *
+ * \section calc_sec Calculations
+ *
+ * The volumes in PulseAudio are logarithmic in nature and applications
+ * shouldn't perform calculations with them directly. Instead, they should
+ * be converted to and from either dB or a linear scale:
+ *
+ * \li dB - pa_sw_volume_from_dB() / pa_sw_volume_to_dB()
+ * \li Linear - pa_sw_volume_from_linear() / pa_sw_volume_to_linear()
+ *
+ * For simple multiplication, pa_sw_volume_multiply() and
+ * pa_sw_cvolume_multiply() can be used.
+ *
+ * Calculations can only be reliably performed on software volumes
+ * as it is commonly unknown what scale hardware volumes relate to.
+ *
+ * The functions described above are only valid when used with
+ * software volumes. Hence it is usually a better idea to treat all
+ * volume values as opaque with a range from PA_VOLUME_MUTED (0%) to
+ * PA_VOLUME_NORM (100%) and to refrain from any calculations with
+ * them.
+ *
+ * \section conv_sec Convenience Functions
+ *
+ * To handle the pa_cvolume structure, the PulseAudio library provides a
+ * number of convenience functions:
+ *
+ * \li pa_cvolume_valid() - Tests if a pa_cvolume structure is valid.
+ * \li pa_cvolume_equal() - Tests if two pa_cvolume structures are identical.
+ * \li pa_cvolume_channels_equal_to() - Tests if all channels of a pa_cvolume
+ *                             structure have a given volume.
+ * \li pa_cvolume_is_muted() - Tests if all channels of a pa_cvolume
+ *                             structure are muted.
+ * \li pa_cvolume_is_norm() - Tests if all channels of a pa_cvolume structure
+ *                            are at a normal volume.
+ * \li pa_cvolume_set() - Set the first n channels of a pa_cvolume structure to
+ *                        a certain volume.
+ * \li pa_cvolume_reset() - Set the first n channels of a pa_cvolume structure
+ *                          to a normal volume.
+ * \li pa_cvolume_mute() - Set the first n channels of a pa_cvolume structure
+ *                         to a muted volume.
+ * \li pa_cvolume_avg() - Return the average volume of all channels.
+ * \li pa_cvolume_snprint() - Pretty print a pa_cvolume structure.
+ */
+
+/** \file
+ * Constants and routines for volume handling
+ *
+ * See also \subpage volume
+ */
+
+PA_C_DECL_BEGIN
+
+/** Volume specification:
+ *  PA_VOLUME_MUTED: silence;
+ * < PA_VOLUME_NORM: decreased volume;
+ *   PA_VOLUME_NORM: normal volume;
+ * > PA_VOLUME_NORM: increased volume */
+typedef uint32_t pa_volume_t;
+
+/** Normal volume (100%, 0 dB) */
+#define PA_VOLUME_NORM ((pa_volume_t) 0x10000U)
+
+/** Muted (minimal valid) volume (0%, -inf dB) */
+#define PA_VOLUME_MUTED ((pa_volume_t) 0U)
+
+/** Maximum valid volume we can store. \since 0.9.15 */
+#define PA_VOLUME_MAX ((pa_volume_t) UINT32_MAX/2)
+
+/** Recommended maximum volume to show in user facing UIs.
+ * Note: UIs should deal gracefully with volumes greater than this value
+ * and not cause feedback loops etc. - i.e. if the volume is more than
+ * this, the UI should not limit it and push the limited value back to
+ * the server. \since 0.9.23 */
+#define PA_VOLUME_UI_MAX (pa_sw_volume_from_dB(+11.0))
+
+/** Special 'invalid' volume. \since 0.9.16 */
+#define PA_VOLUME_INVALID ((pa_volume_t) UINT32_MAX)
+
+/** Check if volume is valid. \since 1.0 */
+#define PA_VOLUME_IS_VALID(v) ((v) <= PA_VOLUME_MAX)
+
+/** Clamp volume to the permitted range. \since 1.0 */
+#define PA_CLAMP_VOLUME(v) (PA_CLAMP_UNLIKELY((v), PA_VOLUME_MUTED, PA_VOLUME_MAX))
+
+/** A structure encapsulating a per-channel volume */
+typedef struct pa_cvolume {
+    uint8_t channels;                     /**< Number of channels */
+    pa_volume_t values[PA_CHANNELS_MAX];  /**< Per-channel volume */
+} pa_cvolume;
+
+/** Return non-zero when *a == *b */
+int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE;
+
+/** Initialize the specified volume and return a pointer to
+ * it. The sample spec will have a defined state but
+ * pa_cvolume_valid() will fail for it. \since 0.9.13 */
+pa_cvolume* pa_cvolume_init(pa_cvolume *a);
+
+/** Set the volume of the first n channels to PA_VOLUME_NORM */
+#define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM)
+
+/** Set the volume of the first n channels to PA_VOLUME_MUTED */
+#define pa_cvolume_mute(a, n) pa_cvolume_set((a), (n), PA_VOLUME_MUTED)
+
+/** Set the volume of the specified number of channels to the volume v */
+pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v);
+
+/** Maximum length of the strings returned by
+ * pa_cvolume_snprint(). Please note that this value can change with
+ * any release without warning and without being considered API or ABI
+ * breakage. You should not use this definition anywhere where it
+ * might become part of an ABI.*/
+#define PA_CVOLUME_SNPRINT_MAX 320
+
+/** Pretty print a volume structure */
+char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c);
+
+/** Maximum length of the strings returned by
+ * pa_sw_cvolume_snprint_dB(). Please note that this value can change with
+ * any release without warning and without being considered API or ABI
+ * breakage. You should not use this definition anywhere where it
+ * might become part of an ABI. \since 0.9.13 */
+#define PA_SW_CVOLUME_SNPRINT_DB_MAX 448
+
+/** Pretty print a volume structure but show dB values. \since 0.9.13 */
+char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c);
+
+/** Maximum length of the strings returned by pa_cvolume_snprint_verbose().
+ * Please note that this value can change with any release without warning and
+ * without being considered API or ABI breakage. You should not use this
+ * definition anywhere where it might become part of an ABI. \since 5.0 */
+#define PA_CVOLUME_SNPRINT_VERBOSE_MAX 1984
+
+/** Pretty print a volume structure in a verbose way. The volume for each
+ * channel is printed in several formats: the raw pa_volume_t value,
+ * percentage, and if print_dB is non-zero, also the dB value. If map is not
+ * NULL, the channel names will be printed. \since 5.0 */
+char *pa_cvolume_snprint_verbose(char *s, size_t l, const pa_cvolume *c, const pa_channel_map *map, int print_dB);
+
+/** Maximum length of the strings returned by
+ * pa_volume_snprint(). Please note that this value can change with
+ * any release without warning and without being considered API or ABI
+ * breakage. You should not use this definition anywhere where it
+ * might become part of an ABI. \since 0.9.15 */
+#define PA_VOLUME_SNPRINT_MAX 10
+
+/** Pretty print a volume \since 0.9.15 */
+char *pa_volume_snprint(char *s, size_t l, pa_volume_t v);
+
+/** Maximum length of the strings returned by
+ * pa_sw_volume_snprint_dB(). Please note that this value can change with
+ * any release without warning and without being considered API or ABI
+ * breakage. You should not use this definition anywhere where it
+ * might become part of an ABI. \since 0.9.15 */
+#define PA_SW_VOLUME_SNPRINT_DB_MAX 11
+
+/** Pretty print a volume but show dB values. \since 0.9.15 */
+char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v);
+
+/** Maximum length of the strings returned by pa_volume_snprint_verbose().
+ * Please note that this value can change with any release without warning and
+ * withou being considered API or ABI breakage. You should not use this
+ * definition anywhere where it might become part of an ABI. \since 5.0 */
+#define PA_VOLUME_SNPRINT_VERBOSE_MAX 35
+
+/** Pretty print a volume in a verbose way. The volume is printed in several
+ * formats: the raw pa_volume_t value, percentage, and if print_dB is non-zero,
+ * also the dB value. \since 5.0 */
+char *pa_volume_snprint_verbose(char *s, size_t l, pa_volume_t v, int print_dB);
+
+/** Return the average volume of all channels */
+pa_volume_t pa_cvolume_avg(const pa_cvolume *a) PA_GCC_PURE;
+
+/** Return the average volume of all channels that are included in the
+ * specified channel map with the specified channel position mask. If
+ * cm is NULL this call is identical to pa_cvolume_avg(). If no
+ * channel is selected the returned value will be
+ * PA_VOLUME_MUTED. \since 0.9.16 */
+pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) PA_GCC_PURE;
+
+/** Return the maximum volume of all channels. \since 0.9.12 */
+pa_volume_t pa_cvolume_max(const pa_cvolume *a) PA_GCC_PURE;
+
+/** Return the maximum volume of all channels that are included in the
+ * specified channel map with the specified channel position mask. If
+ * cm is NULL this call is identical to pa_cvolume_max(). If no
+ * channel is selected the returned value will be PA_VOLUME_MUTED.
+ * \since 0.9.16 */
+pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) PA_GCC_PURE;
+
+/** Return the minimum volume of all channels. \since 0.9.16 */
+pa_volume_t pa_cvolume_min(const pa_cvolume *a) PA_GCC_PURE;
+
+/** Return the minimum volume of all channels that are included in the
+ * specified channel map with the specified channel position mask. If
+ * cm is NULL this call is identical to pa_cvolume_min(). If no
+ * channel is selected the returned value will be PA_VOLUME_MUTED.
+ * \since 0.9.16 */
+pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) PA_GCC_PURE;
+
+/** Return non-zero when the passed cvolume structure is valid */
+int pa_cvolume_valid(const pa_cvolume *v) PA_GCC_PURE;
+
+/** Return non-zero if the volume of all channels is equal to the specified value */
+int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) PA_GCC_PURE;
+
+/** Return 1 if the specified volume has all channels muted */
+#define pa_cvolume_is_muted(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_MUTED)
+
+/** Return 1 if the specified volume has all channels on normal level */
+#define pa_cvolume_is_norm(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_NORM)
+
+/** Multiply two volume specifications, return the result. This uses
+ * PA_VOLUME_NORM as neutral element of multiplication. This is only
+ * valid for software volumes! */
+pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST;
+
+/** Multiply two per-channel volumes and return the result in
+ * *dest. This is only valid for software volumes! a, b and dest may
+ * point to the same structure. */
+pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
+
+/** Multiply a per-channel volume with a scalar volume and return the
+ * result in *dest. This is only valid for software volumes! a
+ * and dest may point to the same structure. \since
+ * 0.9.16 */
+pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b);
+
+/** Divide two volume specifications, return the result. This uses
+ * PA_VOLUME_NORM as neutral element of division. This is only valid
+ * for software volumes! If a division by zero is tried the result
+ * will be 0. \since 0.9.13 */
+pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) PA_GCC_CONST;
+
+/** Divide two per-channel volumes and return the result in
+ * *dest. This is only valid for software volumes! a, b
+ * and dest may point to the same structure. \since 0.9.13 */
+pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
+
+/** Divide a per-channel volume by a scalar volume and return the
+ * result in *dest. This is only valid for software volumes! a
+ * and dest may point to the same structure. \since
+ * 0.9.16 */
+pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b);
+
+/** Convert a decibel value to a volume (amplitude, not power). This is only valid for software volumes! */
+pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST;
+
+/** Convert a volume to a decibel value (amplitude, not power). This is only valid for software volumes! */
+double pa_sw_volume_to_dB(pa_volume_t v) PA_GCC_CONST;
+
+/** Convert a linear factor to a volume.  0.0 and less is muted while
+ * 1.0 is PA_VOLUME_NORM.  This is only valid for software volumes! */
+pa_volume_t pa_sw_volume_from_linear(double v) PA_GCC_CONST;
+
+/** Convert a volume to a linear factor. This is only valid for software volumes! */
+double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;
+
+#ifdef INFINITY
+#define PA_DECIBEL_MININFTY ((double) -INFINITY)
+#else
+/** This floor value is used as minus infinity when using pa_sw_volume_to_dB() / pa_sw_volume_from_dB(). */
+#define PA_DECIBEL_MININFTY ((double) -200.0)
+#endif
+
+/** Remap a volume from one channel mapping to a different channel mapping. \since 0.9.12 */
+pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to);
+
+/** Return non-zero if the specified volume is compatible with the
+ * specified sample spec. \since 0.9.13 */
+int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) PA_GCC_PURE;
+
+/** Return non-zero if the specified volume is compatible with the
+ * specified sample spec. \since 0.9.15 */
+int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) PA_GCC_PURE;
+
+/** Calculate a 'balance' value for the specified volume with the
+ * specified channel map. The return value will range from -1.0f
+ * (left) to +1.0f (right). If no balance value is applicable to this
+ * channel map the return value will always be 0.0f. See
+ * pa_channel_map_can_balance(). \since 0.9.15 */
+float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) PA_GCC_PURE;
+
+/** Adjust the 'balance' value for the specified volume with the
+ * specified channel map. v will be modified in place and
+ * returned. The balance is a value between -1.0f and +1.0f. This
+ * operation might not be reversible! Also, after this call
+ * pa_cvolume_get_balance() is not guaranteed to actually return the
+ * requested balance value (e.g. when the input volume was zero anyway for
+ * all channels). If no balance value is applicable to
+ * this channel map the volume will not be modified. See
+ * pa_channel_map_can_balance(). \since 0.9.15 */
+pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance);
+
+/** Calculate a 'fade' value (i.e.\ 'balance' between front and rear)
+ * for the specified volume with the specified channel map. The return
+ * value will range from -1.0f (rear) to +1.0f (left). If no fade
+ * value is applicable to this channel map the return value will
+ * always be 0.0f. See pa_channel_map_can_fade(). \since 0.9.15 */
+float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) PA_GCC_PURE;
+
+/** Adjust the 'fade' value (i.e.\ 'balance' between front and rear)
+ * for the specified volume with the specified channel map. v will be
+ * modified in place and returned. The balance is a value between
+ * -1.0f and +1.0f. This operation might not be reversible! Also,
+ * after this call pa_cvolume_get_fade() is not guaranteed to actually
+ * return the requested fade value (e.g. when the input volume was
+ * zero anyway for all channels). If no fade value is applicable to
+ * this channel map the volume will not be modified. See
+ * pa_channel_map_can_fade(). \since 0.9.15 */
+pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float new_fade);
+
+/** Calculate a 'lfe balance' value for the specified volume with
+ * the specified channel map. The return value will range from
+ * -1.0f (no lfe) to +1.0f (only lfe), where 0.0f is balanced.
+ * If no value is applicable to this channel map the return value
+ * will always be 0.0f. See pa_channel_map_can_lfe_balance(). \since 8.0 */
+float pa_cvolume_get_lfe_balance(const pa_cvolume *v, const pa_channel_map *map) PA_GCC_PURE;
+
+/** Adjust the 'lfe balance' value for the specified volume with
+ * the specified channel map. v will be modified in place and returned.
+ * The balance is a value between -1.0f (no lfe) and +1.0f (only lfe).
+ * This operation might not be reversible! Also, after this call
+ * pa_cvolume_get_lfe_balance() is not guaranteed to actually
+ * return the requested value (e.g. when the input volume was
+ * zero anyway for all channels). If no lfe balance value is applicable to
+ * this channel map the volume will not be modified. See
+ * pa_channel_map_can_lfe_balance(). \since 8.0 */
+pa_cvolume* pa_cvolume_set_lfe_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance);
+
+/** Scale the passed pa_cvolume structure so that the maximum volume
+ * of all channels equals max. The proportions between the channel
+ * volumes are kept. \since 0.9.15 */
+pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max);
+
+/** Scale the passed pa_cvolume structure so that the maximum volume
+ * of all channels selected via cm/mask equals max. This also modifies
+ * the volume of those channels that are unmasked. The proportions
+ * between the channel volumes are kept. \since 0.9.16 */
+pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map *cm, pa_channel_position_mask_t mask);
+
+/** Set the passed volume to all channels at the specified channel
+ * position. Will return the updated volume struct, or NULL if there
+ * is no channel at the position specified. You can check if a channel
+ * map includes a specific position by calling
+ * pa_channel_map_has_position(). \since 0.9.16 */
+pa_cvolume* pa_cvolume_set_position(pa_cvolume *cv, const pa_channel_map *map, pa_channel_position_t t, pa_volume_t v);
+
+/** Get the maximum volume of all channels at the specified channel
+ * position. Will return 0 if there is no channel at the position
+ * specified. You can check if a channel map includes a specific
+ * position by calling pa_channel_map_has_position(). \since 0.9.16 */
+pa_volume_t pa_cvolume_get_position(pa_cvolume *cv, const pa_channel_map *map, pa_channel_position_t t) PA_GCC_PURE;
+
+/** This goes through all channels in a and b and sets the
+ * corresponding channel in dest to the greater volume of both. a, b
+ * and dest may point to the same structure. \since 0.9.16 */
+pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
+
+/** Increase the volume passed in by 'inc', but not exceeding 'limit'.
+ * The proportions between the channels are kept. \since 0.9.19 */
+pa_cvolume* pa_cvolume_inc_clamp(pa_cvolume *v, pa_volume_t inc, pa_volume_t limit);
+
+/** Increase the volume passed in by 'inc'. The proportions between
+ * the channels are kept. \since 0.9.16 */
+pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc);
+
+/** Decrease the volume passed in by 'dec'. The proportions between
+ * the channels are kept. \since 0.9.16 */
+pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c
new file mode 100644 (file)
index 0000000..1a535b3
--- /dev/null
@@ -0,0 +1,131 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pulse/gccmacro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "xmalloc.h"
+
+/* Make sure not to allocate more than this much memory. */
+#define MAX_ALLOC_SIZE (1024*1024*96) /* 96MB */
+
+/* #undef malloc */
+/* #undef free */
+/* #undef realloc */
+/* #undef strndup */
+/* #undef strdup */
+
+static void oom(void) PA_GCC_NORETURN;
+
+/* called in case of an OOM situation. Prints an error message and
+ * exits */
+static void oom(void) {
+    static const char e[] = "Not enough memory\n";
+    pa_loop_write(STDERR_FILENO, e, sizeof(e)-1, NULL);
+#ifdef SIGQUIT
+    raise(SIGQUIT);
+#endif
+    _exit(1);
+}
+
+void* pa_xmalloc(size_t size) {
+    void *p;
+    pa_assert(size > 0);
+    pa_assert(size < MAX_ALLOC_SIZE);
+
+    if (!(p = malloc(size)))
+        oom();
+
+    return p;
+}
+
+void* pa_xmalloc0(size_t size) {
+    void *p;
+    pa_assert(size > 0);
+    pa_assert(size < MAX_ALLOC_SIZE);
+
+    if (!(p = calloc(1, size)))
+        oom();
+
+    return p;
+}
+
+void *pa_xrealloc(void *ptr, size_t size) {
+    void *p;
+    pa_assert(size > 0);
+    pa_assert(size < MAX_ALLOC_SIZE);
+
+    if (!(p = realloc(ptr, size)))
+        oom();
+    return p;
+}
+
+void* pa_xmemdup(const void *p, size_t l) {
+    if (!p)
+        return NULL;
+    else {
+        char *r = pa_xmalloc(l);
+        memcpy(r, p, l);
+        return r;
+    }
+}
+
+char *pa_xstrdup(const char *s) {
+    if (!s)
+        return NULL;
+
+    return pa_xmemdup(s, strlen(s)+1);
+}
+
+char *pa_xstrndup(const char *s, size_t l) {
+    char *e, *r;
+
+    if (!s)
+        return NULL;
+
+    if ((e = memchr(s, 0, l)))
+        return pa_xmemdup(s, (size_t) (e-s+1));
+
+    r = pa_xmalloc(l+1);
+    memcpy(r, s, l);
+    r[l] = 0;
+    return r;
+}
+
+void pa_xfree(void *p) {
+    int saved_errno;
+
+    if (!p)
+        return;
+
+    saved_errno = errno;
+    free(p);
+    errno = saved_errno;
+}
diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h
new file mode 100644 (file)
index 0000000..d1d6968
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef foomemoryhfoo
+#define foomemoryhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+#include <pulse/version.h>
+
+/** \file
+ * Memory allocation functions.
+ */
+
+PA_C_DECL_BEGIN
+
+/** Allocate the specified number of bytes, just like malloc() does. However, in case of OOM, terminate */
+void* pa_xmalloc(size_t l) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE(1);
+
+/** Same as pa_xmalloc(), but initialize allocated memory to 0 */
+void *pa_xmalloc0(size_t l) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE(1);
+
+/**  The combination of pa_xmalloc() and realloc() */
+void *pa_xrealloc(void *ptr, size_t size) PA_GCC_ALLOC_SIZE(2);
+
+/** Free allocated memory */
+void pa_xfree(void *p);
+
+/** Duplicate the specified string, allocating memory with pa_xmalloc() */
+char *pa_xstrdup(const char *s) PA_GCC_MALLOC;
+
+/** Duplicate the specified string, but truncate after l characters */
+char *pa_xstrndup(const char *s, size_t l) PA_GCC_MALLOC;
+
+/** Duplicate the specified memory block */
+void* pa_xmemdup(const void *p, size_t l) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE(2);
+
+/** Internal helper for pa_xnew() */
+static void* _pa_xnew_internal(size_t n, size_t k) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE2(1,2);
+
+static inline void* _pa_xnew_internal(size_t n, size_t k) {
+    assert(n < INT_MAX/k);
+    return pa_xmalloc(n*k);
+}
+
+/** Allocate n new structures of the specified type. */
+#define pa_xnew(type, n) ((type*) _pa_xnew_internal((n), sizeof(type)))
+
+/** Internal helper for pa_xnew0() */
+static void* _pa_xnew0_internal(size_t n, size_t k) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE2(1,2);
+
+static inline void* _pa_xnew0_internal(size_t n, size_t k) {
+    assert(n < INT_MAX/k);
+    return pa_xmalloc0(n*k);
+}
+
+/** Same as pa_xnew() but set the memory to zero */
+#define pa_xnew0(type, n) ((type*) _pa_xnew0_internal((n), sizeof(type)))
+
+/** Internal helper for pa_xnew0() */
+static void* _pa_xnewdup_internal(const void *p, size_t n, size_t k) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE2(2,3);
+
+static inline void* _pa_xnewdup_internal(const void *p, size_t n, size_t k) {
+    assert(n < INT_MAX/k);
+    return pa_xmemdup(p, n*k);
+}
+
+/** Same as pa_xnew() but duplicate the specified data */
+#define pa_xnewdup(type, p, n) ((type*) _pa_xnewdup_internal((p), (n), sizeof(type)))
+
+/** Internal helper for pa_xrenew() */
+static void* _pa_xrenew_internal(void *p, size_t n, size_t k) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE2(2,3);
+
+static inline void* _pa_xrenew_internal(void *p, size_t n, size_t k) {
+    assert(n < INT_MAX/k);
+    return pa_xrealloc(p, n*k);
+}
+
+/** Reallocate n new structures of the specified type. */
+#define pa_xrenew(type, p, n) ((type*) _pa_xrenew_internal(p, (n), sizeof(type)))
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulsecore/Makefile b/src/pulsecore/Makefile
new file mode 120000 (symlink)
index 0000000..f5b1dba
--- /dev/null
@@ -0,0 +1 @@
+../modules/Makefile
\ No newline at end of file
diff --git a/src/pulsecore/arpa-inet.c b/src/pulsecore/arpa-inet.c
new file mode 100644 (file)
index 0000000..afea397
--- /dev/null
@@ -0,0 +1,106 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if !defined(HAVE_ARPA_INET_H) && defined(OS_IS_WIN32)
+
+#include <errno.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/core-util.h>
+
+#include "arpa-inet.h"
+
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
+    struct in_addr *in = (struct in_addr*)src;
+#ifdef HAVE_IPV6
+    struct in6_addr *in6 = (struct in6_addr*)src;
+#endif
+
+    pa_assert(src);
+    pa_assert(dst);
+
+    switch (af) {
+    case AF_INET:
+        pa_snprintf(dst, cnt, "%d.%d.%d.%d",
+#ifdef WORDS_BIGENDIAN
+            (int)(in->s_addr >> 24) & 0xff,
+            (int)(in->s_addr >> 16) & 0xff,
+            (int)(in->s_addr >>  8) & 0xff,
+            (int)(in->s_addr >>  0) & 0xff);
+#else
+            (int)(in->s_addr >>  0) & 0xff,
+            (int)(in->s_addr >>  8) & 0xff,
+            (int)(in->s_addr >> 16) & 0xff,
+            (int)(in->s_addr >> 24) & 0xff);
+#endif
+        break;
+#ifdef HAVE_IPV6
+    case AF_INET6:
+        pa_snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
+            in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1],
+            in6->s6_addr[ 2] << 8 | in6->s6_addr[ 3],
+            in6->s6_addr[ 4] << 8 | in6->s6_addr[ 5],
+            in6->s6_addr[ 6] << 8 | in6->s6_addr[ 7],
+            in6->s6_addr[ 8] << 8 | in6->s6_addr[ 9],
+            in6->s6_addr[10] << 8 | in6->s6_addr[11],
+            in6->s6_addr[12] << 8 | in6->s6_addr[13],
+            in6->s6_addr[14] << 8 | in6->s6_addr[15]);
+        break;
+#endif
+    default:
+        errno = EAFNOSUPPORT;
+        return NULL;
+    }
+
+    return dst;
+}
+
+int inet_pton(int af, const char *src, void *dst) {
+    struct in_addr *in = (struct in_addr*)dst;
+#ifdef HAVE_IPV6
+    struct in6_addr *in6 = (struct in6_addr*)dst;
+#endif
+
+    pa_assert(src);
+    pa_assert(dst);
+
+    switch (af) {
+    case AF_INET:
+        in->s_addr = inet_addr(src);
+        if (in->s_addr == INADDR_NONE)
+            return 0;
+        break;
+#ifdef HAVE_IPV6
+    case AF_INET6:
+        /* FIXME */
+#endif
+    default:
+        errno = EAFNOSUPPORT;
+        return -1;
+    }
+
+    return 1;
+}
+
+#endif
diff --git a/src/pulsecore/arpa-inet.h b/src/pulsecore/arpa-inet.h
new file mode 100644 (file)
index 0000000..d940f70
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef fooarpa_inethfoo
+#define fooarpa_inethfoo
+
+#if defined(HAVE_ARPA_INET_H)
+
+#include <arpa/inet.h>
+
+#elif defined(OS_IS_WIN32)
+
+/* On Windows winsock2.h (here included via pulsecore/socket.h) provides most of the functionality of arpa/inet.h, except for
+ * the inet_ntop and inet_pton functions, which are implemented here. */
+
+#include <pulsecore/socket.h>
+
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
+
+int inet_pton(int af, const char *src, void *dst);
+
+#endif
+
+#endif
diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c
new file mode 100644 (file)
index 0000000..47371ae
--- /dev/null
@@ -0,0 +1,358 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/flist.h>
+
+#include "asyncmsgq.h"
+
+PA_STATIC_FLIST_DECLARE(asyncmsgq, 0, pa_xfree);
+PA_STATIC_FLIST_DECLARE(semaphores, 0, (void(*)(void*)) pa_semaphore_free);
+
+struct asyncmsgq_item {
+    int code;
+    pa_msgobject *object;
+    void *userdata;
+    pa_free_cb_t free_cb;
+    int64_t offset;
+    pa_memchunk memchunk;
+    pa_semaphore *semaphore;
+    int ret;
+};
+
+struct pa_asyncmsgq {
+    PA_REFCNT_DECLARE;
+    pa_asyncq *asyncq;
+    pa_mutex *mutex; /* only for the writer side */
+
+    struct asyncmsgq_item *current;
+};
+
+pa_asyncmsgq *pa_asyncmsgq_new(unsigned size) {
+    pa_asyncq *asyncq;
+    pa_asyncmsgq *a;
+
+    asyncq = pa_asyncq_new(size);
+    if (!asyncq)
+        return NULL;
+
+    a = pa_xnew(pa_asyncmsgq, 1);
+
+    PA_REFCNT_INIT(a);
+    a->asyncq = asyncq;
+    pa_assert_se(a->mutex = pa_mutex_new(false, true));
+    a->current = NULL;
+
+    return a;
+}
+
+static void asyncmsgq_free(pa_asyncmsgq *a) {
+    struct asyncmsgq_item *i;
+    pa_assert(a);
+
+    while ((i = pa_asyncq_pop(a->asyncq, false))) {
+
+        pa_assert(!i->semaphore);
+
+        if (i->object)
+            pa_msgobject_unref(i->object);
+
+        if (i->memchunk.memblock)
+            pa_memblock_unref(i->memchunk.memblock);
+
+        if (i->free_cb)
+            i->free_cb(i->userdata);
+
+        if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), i) < 0)
+            pa_xfree(i);
+    }
+
+    pa_asyncq_free(a->asyncq, NULL);
+    pa_mutex_free(a->mutex);
+    pa_xfree(a);
+}
+
+pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q) {
+    pa_assert(PA_REFCNT_VALUE(q) > 0);
+
+    PA_REFCNT_INC(q);
+    return q;
+}
+
+void pa_asyncmsgq_unref(pa_asyncmsgq* q) {
+    pa_assert(PA_REFCNT_VALUE(q) > 0);
+
+    if (PA_REFCNT_DEC(q) <= 0)
+        asyncmsgq_free(q);
+}
+
+void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *chunk, pa_free_cb_t free_cb) {
+    struct asyncmsgq_item *i;
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(asyncmsgq))))
+        i = pa_xnew(struct asyncmsgq_item, 1);
+
+    i->code = code;
+    i->object = object ? pa_msgobject_ref(object) : NULL;
+    i->userdata = (void*) userdata;
+    i->free_cb = free_cb;
+    i->offset = offset;
+    if (chunk) {
+        pa_assert(chunk->memblock);
+        i->memchunk = *chunk;
+        pa_memblock_ref(i->memchunk.memblock);
+    } else
+        pa_memchunk_reset(&i->memchunk);
+    i->semaphore = NULL;
+
+    /* This mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
+    pa_mutex_lock(a->mutex);
+    pa_asyncq_post(a->asyncq, i);
+    pa_mutex_unlock(a->mutex);
+}
+
+int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *chunk) {
+    struct asyncmsgq_item i;
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    i.code = code;
+    i.object = object;
+    i.userdata = (void*) userdata;
+    i.free_cb = NULL;
+    i.ret = -1;
+    i.offset = offset;
+    if (chunk) {
+        pa_assert(chunk->memblock);
+        i.memchunk = *chunk;
+    } else
+        pa_memchunk_reset(&i.memchunk);
+
+    if (!(i.semaphore = pa_flist_pop(PA_STATIC_FLIST_GET(semaphores))))
+        i.semaphore = pa_semaphore_new(0);
+
+    /* This mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
+    pa_mutex_lock(a->mutex);
+    pa_assert_se(pa_asyncq_push(a->asyncq, &i, true) == 0);
+    pa_mutex_unlock(a->mutex);
+
+    pa_semaphore_wait(i.semaphore);
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(semaphores), i.semaphore) < 0)
+        pa_semaphore_free(i.semaphore);
+
+    return i.ret;
+}
+
+int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, bool wait_op) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+    pa_assert(!a->current);
+
+    if (!(a->current = pa_asyncq_pop(a->asyncq, wait_op))) {
+/*         pa_log("failure"); */
+        return -1;
+    }
+
+/*     pa_log("success"); */
+
+    if (code)
+        *code = a->current->code;
+    if (userdata)
+        *userdata = a->current->userdata;
+    if (offset)
+        *offset = a->current->offset;
+    if (object) {
+        if ((*object = a->current->object))
+            pa_msgobject_assert_ref(*object);
+    }
+    if (chunk)
+        *chunk = a->current->memchunk;
+
+/*     pa_log_debug("Get q=%p object=%p (%s) code=%i data=%p chunk.length=%lu", */
+/*                  (void*) a, */
+/*                  (void*) a->current->object, */
+/*                  a->current->object ? a->current->object->parent.type_name : NULL, */
+/*                  a->current->code, */
+/*                  (void*) a->current->userdata, */
+/*                  (unsigned long) a->current->memchunk.length); */
+
+    return 0;
+}
+
+void pa_asyncmsgq_done(pa_asyncmsgq *a, int ret) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+    pa_assert(a);
+    pa_assert(a->current);
+
+    if (a->current->semaphore) {
+        a->current->ret = ret;
+        pa_semaphore_post(a->current->semaphore);
+    } else {
+
+        if (a->current->free_cb)
+            a->current->free_cb(a->current->userdata);
+
+        if (a->current->object)
+            pa_msgobject_unref(a->current->object);
+
+        if (a->current->memchunk.memblock)
+            pa_memblock_unref(a->current->memchunk.memblock);
+
+        if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), a->current) < 0)
+            pa_xfree(a->current);
+    }
+
+    a->current = NULL;
+}
+
+int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code) {
+    int c;
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    pa_asyncmsgq_ref(a);
+
+    do {
+        pa_msgobject *o;
+        void *data;
+        int64_t offset;
+        pa_memchunk chunk;
+        int ret;
+
+        if (pa_asyncmsgq_get(a, &o, &c, &data, &offset, &chunk, true) < 0)
+            return -1;
+
+        ret = pa_asyncmsgq_dispatch(o, c, data, offset, &chunk);
+        pa_asyncmsgq_done(a, ret);
+
+    } while (c != code);
+
+    pa_asyncmsgq_unref(a);
+
+    return 0;
+}
+
+int pa_asyncmsgq_process_one(pa_asyncmsgq *a) {
+    pa_msgobject *object;
+    int code;
+    void *data;
+    pa_memchunk chunk;
+    int64_t offset;
+    int ret;
+
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, false) < 0)
+        return 0;
+
+    pa_asyncmsgq_ref(a);
+    ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+    pa_asyncmsgq_done(a, ret);
+    pa_asyncmsgq_unref(a);
+
+    return 1;
+}
+
+int pa_asyncmsgq_read_fd(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    return pa_asyncq_read_fd(a->asyncq);
+}
+
+int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    return pa_asyncq_read_before_poll(a->asyncq);
+}
+
+void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    pa_asyncq_read_after_poll(a->asyncq);
+}
+
+int pa_asyncmsgq_write_fd(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    return pa_asyncq_write_fd(a->asyncq);
+}
+
+void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    pa_asyncq_write_before_poll(a->asyncq);
+}
+
+void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    pa_asyncq_write_after_poll(a->asyncq);
+}
+
+int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk) {
+
+    if (object)
+        return object->process_msg(object, code, userdata, offset, pa_memchunk_isset(memchunk) ? memchunk : NULL);
+
+    return 0;
+}
+
+void pa_asyncmsgq_flush(pa_asyncmsgq *a, bool run) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    for (;;) {
+        pa_msgobject *object;
+        int code;
+        void *data;
+        int64_t offset;
+        pa_memchunk chunk;
+        int ret;
+
+        if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, false) < 0)
+            return;
+
+        if (!run) {
+            pa_asyncmsgq_done(a, -1);
+            continue;
+        }
+
+        pa_asyncmsgq_ref(a);
+        ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+        pa_asyncmsgq_done(a, ret);
+        pa_asyncmsgq_unref(a);
+    }
+}
+
+bool pa_asyncmsgq_dispatching(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    return !!a->current;
+}
diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h
new file mode 100644 (file)
index 0000000..367ccac
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef foopulseasyncmsgqhfoo
+#define foopulseasyncmsgqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/asyncq.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/msgobject.h>
+
+/* A simple asynchronous message queue, based on pa_asyncq. In
+ * contrast to pa_asyncq this one is multiple-writer safe, though
+ * still not multiple-reader safe. This queue is intended to be used
+ * for controlling real-time threads from normal-priority
+ * threads. Multiple-writer-safety is accomplished by using a mutex on
+ * the writer side. This queue is thus not useful for communication
+ * between several real-time threads.
+ *
+ * The queue takes messages consisting of:
+ *    "Object" for which this messages is intended (may be NULL)
+ *    A numeric message code
+ *    Arbitrary userdata pointer (may be NULL)
+ *    A memchunk (may be NULL)
+ *
+ * There are two functions for submitting messages: _post and
+ * _send. The former just enqueues the message asynchronously, the
+ * latter waits for completion, synchronously. */
+
+enum {
+    PA_MESSAGE_SHUTDOWN = -1/* A generic message to inform the handler of this queue to quit */
+};
+
+typedef struct pa_asyncmsgq pa_asyncmsgq;
+
+pa_asyncmsgq* pa_asyncmsgq_new(unsigned size);
+pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q);
+
+void pa_asyncmsgq_unref(pa_asyncmsgq* q);
+
+void pa_asyncmsgq_post(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk, pa_free_cb_t userdata_free_cb);
+int pa_asyncmsgq_send(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk);
+
+int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, bool wait);
+int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk);
+void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret);
+int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code);
+int pa_asyncmsgq_process_one(pa_asyncmsgq *a);
+
+void pa_asyncmsgq_flush(pa_asyncmsgq *a, bool run);
+
+/* For the reading side */
+int pa_asyncmsgq_read_fd(pa_asyncmsgq *q);
+int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a);
+void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a);
+
+/* For the write side */
+int pa_asyncmsgq_write_fd(pa_asyncmsgq *q);
+void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a);
+void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a);
+
+bool pa_asyncmsgq_dispatching(pa_asyncmsgq *a);
+
+#endif
diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c
new file mode 100644 (file)
index 0000000..53bfe4f
--- /dev/null
@@ -0,0 +1,320 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/fdsem.h>
+
+#include "asyncq.h"
+
+#define ASYNCQ_SIZE 256
+
+/* For debugging purposes we can define _Y to put an extra thread
+ * yield between each operation. */
+
+/* #define PROFILE */
+
+#ifdef PROFILE
+#define _Y pa_thread_yield()
+#else
+#define _Y do { } while(0)
+#endif
+
+struct localq {
+    void *data;
+    PA_LLIST_FIELDS(struct localq);
+};
+
+struct pa_asyncq {
+    unsigned size;
+    unsigned read_idx;
+    unsigned write_idx;
+    pa_fdsem *read_fdsem, *write_fdsem;
+
+    PA_LLIST_HEAD(struct localq, localq);
+    struct localq *last_localq;
+    bool waiting_for_post;
+};
+
+PA_STATIC_FLIST_DECLARE(localq, 0, pa_xfree);
+
+#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
+
+static unsigned reduce(pa_asyncq *l, unsigned value) {
+    return value & (unsigned) (l->size - 1);
+}
+
+pa_asyncq *pa_asyncq_new(unsigned size) {
+    pa_asyncq *l;
+
+    if (!size)
+        size = ASYNCQ_SIZE;
+
+    pa_assert(pa_is_power_of_two(size));
+
+    l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size));
+
+    l->size = size;
+
+    PA_LLIST_HEAD_INIT(struct localq, l->localq);
+    l->last_localq = NULL;
+    l->waiting_for_post = false;
+
+    if (!(l->read_fdsem = pa_fdsem_new())) {
+        pa_xfree(l);
+        return NULL;
+    }
+
+    if (!(l->write_fdsem = pa_fdsem_new())) {
+        pa_fdsem_free(l->read_fdsem);
+        pa_xfree(l);
+        return NULL;
+    }
+
+    return l;
+}
+
+void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
+    struct localq *q;
+    pa_assert(l);
+
+    if (free_cb) {
+        void *p;
+
+        while ((p = pa_asyncq_pop(l, 0)))
+            free_cb(p);
+    }
+
+    while ((q = l->localq)) {
+        if (free_cb)
+            free_cb(q->data);
+
+        PA_LLIST_REMOVE(struct localq, l->localq, q);
+
+        if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
+            pa_xfree(q);
+    }
+
+    pa_fdsem_free(l->read_fdsem);
+    pa_fdsem_free(l->write_fdsem);
+    pa_xfree(l);
+}
+
+static int push(pa_asyncq*l, void *p, bool wait_op) {
+    unsigned idx;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+    pa_assert(p);
+
+    cells = PA_ASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->write_idx);
+
+    if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) {
+
+        if (!wait_op)
+            return -1;
+
+/*         pa_log("sleeping on push"); */
+
+        do {
+            pa_fdsem_wait(l->read_fdsem);
+        } while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p));
+    }
+
+    _Y;
+    l->write_idx++;
+
+    pa_fdsem_post(l->write_fdsem);
+
+    return 0;
+}
+
+static bool flush_postq(pa_asyncq *l, bool wait_op) {
+    struct localq *q;
+
+    pa_assert(l);
+
+    while ((q = l->last_localq)) {
+
+        if (push(l, q->data, wait_op) < 0)
+            return false;
+
+        l->last_localq = q->prev;
+
+        PA_LLIST_REMOVE(struct localq, l->localq, q);
+
+        if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
+            pa_xfree(q);
+    }
+
+    return true;
+}
+
+int pa_asyncq_push(pa_asyncq*l, void *p, bool wait_op) {
+    pa_assert(l);
+
+    if (!flush_postq(l, wait_op))
+        return -1;
+
+    return push(l, p, wait_op);
+}
+
+void pa_asyncq_post(pa_asyncq*l, void *p) {
+    struct localq *q;
+
+    pa_assert(l);
+    pa_assert(p);
+
+    if (flush_postq(l, false))
+        if (pa_asyncq_push(l, p, false) >= 0)
+            return;
+
+    /* OK, we couldn't push anything in the queue. So let's queue it
+     * locally and push it later */
+
+    if (pa_log_ratelimit(PA_LOG_WARN))
+        pa_log_warn("q overrun, queuing locally");
+
+    if (!(q = pa_flist_pop(PA_STATIC_FLIST_GET(localq))))
+        q = pa_xnew(struct localq, 1);
+
+    q->data = p;
+    PA_LLIST_PREPEND(struct localq, l->localq, q);
+
+    if (!l->last_localq)
+        l->last_localq = q;
+
+    return;
+}
+
+void* pa_asyncq_pop(pa_asyncq*l, bool wait_op) {
+    unsigned idx;
+    void *ret;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+
+    cells = PA_ASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->read_idx);
+
+    if (!(ret = pa_atomic_ptr_load(&cells[idx]))) {
+
+        if (!wait_op)
+            return NULL;
+
+/*         pa_log("sleeping on pop"); */
+
+        do {
+            pa_fdsem_wait(l->write_fdsem);
+        } while (!(ret = pa_atomic_ptr_load(&cells[idx])));
+    }
+
+    pa_assert(ret);
+
+    /* Guaranteed to succeed if we only have a single reader */
+    pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL));
+
+    _Y;
+    l->read_idx++;
+
+    pa_fdsem_post(l->read_fdsem);
+
+    return ret;
+}
+
+int pa_asyncq_read_fd(pa_asyncq *q) {
+    pa_assert(q);
+
+    return pa_fdsem_get(q->write_fdsem);
+}
+
+int pa_asyncq_read_before_poll(pa_asyncq *l) {
+    unsigned idx;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+
+    cells = PA_ASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->read_idx);
+
+    for (;;) {
+        if (pa_atomic_ptr_load(&cells[idx]))
+            return -1;
+
+        if (pa_fdsem_before_poll(l->write_fdsem) >= 0)
+            return 0;
+    }
+}
+
+void pa_asyncq_read_after_poll(pa_asyncq *l) {
+    pa_assert(l);
+
+    pa_fdsem_after_poll(l->write_fdsem);
+}
+
+int pa_asyncq_write_fd(pa_asyncq *q) {
+    pa_assert(q);
+
+    return pa_fdsem_get(q->read_fdsem);
+}
+
+void pa_asyncq_write_before_poll(pa_asyncq *l) {
+    pa_assert(l);
+
+    for (;;) {
+
+        if (flush_postq(l, false))
+            break;
+
+        if (pa_fdsem_before_poll(l->read_fdsem) >= 0) {
+            l->waiting_for_post = true;
+            break;
+        }
+    }
+}
+
+void pa_asyncq_write_after_poll(pa_asyncq *l) {
+    pa_assert(l);
+
+    if (l->waiting_for_post) {
+        pa_fdsem_after_poll(l->read_fdsem);
+        l->waiting_for_post = false;
+    }
+}
diff --git a/src/pulsecore/asyncq.h b/src/pulsecore/asyncq.h
new file mode 100644 (file)
index 0000000..8c86762
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef foopulseasyncqhfoo
+#define foopulseasyncqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <pulse/def.h>
+#include <pulsecore/macro.h>
+
+/* A simple, asynchronous, lock-free (if requested also wait-free)
+ * queue. Not multiple-reader/multiple-writer safe. If that is
+ * required both sides can be protected by a mutex each. --- Which is
+ * not a bad thing in most cases, since this queue is intended for
+ * communication between a normal thread and a single real-time
+ * thread. Only the real-time side needs to be lock-free/wait-free.
+ *
+ * If the queue is full and another entry shall be pushed, or when the
+ * queue is empty and another entry shall be popped and the "wait"
+ * argument is non-zero, the queue will block on a UNIX FIFO object --
+ * that will probably require locking on the kernel side -- which
+ * however is probably not problematic, because we do it only on
+ * starvation or overload in which case we have to block anyway.  */
+
+typedef struct pa_asyncq pa_asyncq;
+
+pa_asyncq* pa_asyncq_new(unsigned size);
+void pa_asyncq_free(pa_asyncq* q, pa_free_cb_t free_cb);
+
+void* pa_asyncq_pop(pa_asyncq *q, bool wait);
+int pa_asyncq_push(pa_asyncq *q, void *p, bool wait);
+
+/* Similar to pa_asyncq_push(), but if the queue is full, postpone the
+ * appending of the item locally and delay until
+ * pa_asyncq_before_poll_post() is called. */
+void pa_asyncq_post(pa_asyncq*l, void *p);
+
+/* For the reading side */
+int pa_asyncq_read_fd(pa_asyncq *q);
+int pa_asyncq_read_before_poll(pa_asyncq *a);
+void pa_asyncq_read_after_poll(pa_asyncq *a);
+
+/* For the writing side */
+int pa_asyncq_write_fd(pa_asyncq *q);
+void pa_asyncq_write_before_poll(pa_asyncq *a);
+void pa_asyncq_write_after_poll(pa_asyncq *a);
+
+#endif
diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h
new file mode 100644 (file)
index 0000000..7657144
--- /dev/null
@@ -0,0 +1,650 @@
+#ifndef foopulseatomichfoo
+#define foopulseatomichfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+  Copyright 2008 Nokia Corporation
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/macro.h>
+
+/*
+ * atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*).  It is
+ * not guaranteed however, that sizeof(AO_t) == sizeof(size_t).
+ * however very likely.
+ *
+ * For now we do only full memory barriers. Eventually we might want
+ * to support more elaborate memory barriers, in which case we will add
+ * suffixes to the function names.
+ *
+ * On gcc >= 4.1 we use the builtin atomic functions. otherwise we use
+ * libatomic_ops
+ */
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifdef HAVE_ATOMIC_BUILTINS
+
+/* __sync based implementation */
+
+typedef struct pa_atomic {
+    volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    __sync_synchronize();
+    return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    a->value = i;
+    __sync_synchronize();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    return __sync_fetch_and_add(&a->value, i);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    return __sync_fetch_and_sub(&a->value, i);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    return pa_atomic_add(a, 1);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    return pa_atomic_sub(a, 1);
+}
+
+/* Returns true when the operation was successful. */
+static inline bool pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    return __sync_bool_compare_and_swap(&a->value, old_i, new_i);
+}
+
+typedef struct pa_atomic_ptr {
+    volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    __sync_synchronize();
+    return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    a->value = (unsigned long) p;
+    __sync_synchronize();
+}
+
+static inline bool pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    return __sync_bool_compare_and_swap(&a->value, (long) old_p, (long) new_p);
+}
+
+#elif defined(__NetBSD__) && defined(HAVE_SYS_ATOMIC_H)
+
+/* NetBSD 5.0+ atomic_ops(3) implementation */
+
+#include <sys/atomic.h>
+
+typedef struct pa_atomic {
+    volatile unsigned int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (unsigned int) (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    membar_sync();
+    return (int) a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    a->value = (unsigned int) i;
+    membar_sync();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    int nv = (int) atomic_add_int_nv(&a->value, i);
+    return nv - i;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    int nv = (int) atomic_add_int_nv(&a->value, -i);
+    return nv + i;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    int nv = (int) atomic_inc_uint_nv(&a->value);
+    return nv - 1;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    int nv = (int) atomic_dec_uint_nv(&a->value);
+    return nv + 1;
+}
+
+/* Returns true when the operation was successful. */
+static inline bool pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    unsigned int r = atomic_cas_uint(&a->value, (unsigned int) old_i, (unsigned int) new_i);
+    return (int) r == old_i;
+}
+
+typedef struct pa_atomic_ptr {
+    volatile void *value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    membar_sync();
+    return (void *) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    a->value = p;
+    membar_sync();
+}
+
+static inline bool pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    void *r = atomic_cas_ptr(&a->value, old_p, new_p);
+    return r == old_p;
+}
+
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <machine/atomic.h>
+
+#if __FreeBSD_version < 600000
+#if defined(__i386__) || defined(__amd64__)
+#if defined(__amd64__)
+#define atomic_load_acq_64      atomic_load_acq_long
+#endif
+static inline u_int atomic_fetchadd_int(volatile u_int *p, u_int v) {
+    __asm __volatile(
+            "   " __XSTRING(MPLOCKED) "         "
+            "   xaddl   %0, %1 ;        "
+            "# atomic_fetchadd_int"
+            : "+r" (v),
+            "=m" (*p)
+            : "m" (*p));
+
+    return (v);
+}
+#elif defined(__sparc__) && defined(__arch64__)
+#define atomic_load_acq_64      atomic_load_acq_long
+#define atomic_fetchadd_int     atomic_add_int
+#elif defined(__ia64__)
+#define atomic_load_acq_64      atomic_load_acq_long
+static inline uint32_t
+atomic_fetchadd_int(volatile uint32_t *p, uint32_t v) {
+    uint32_t value;
+
+    do {
+        value = *p;
+    } while (!atomic_cmpset_32(p, value, value + v));
+    return (value);
+}
+#endif
+#endif
+
+typedef struct pa_atomic {
+    volatile unsigned long value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    return (int) atomic_load_acq_int((unsigned int *) &a->value);
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    atomic_store_rel_int((unsigned int *) &a->value, i);
+}
+
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    return atomic_fetchadd_int((unsigned int *) &a->value, i);
+}
+
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    return atomic_fetchadd_int((unsigned int *) &a->value, -(i));
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    return atomic_fetchadd_int((unsigned int *) &a->value, 1);
+}
+
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    return atomic_fetchadd_int((unsigned int *) &a->value, -1);
+}
+
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    return atomic_cmpset_int((unsigned int *) &a->value, old_i, new_i);
+}
+
+typedef struct pa_atomic_ptr {
+    volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (unsigned long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+#ifdef atomic_load_acq_64
+    return (void*) atomic_load_acq_ptr((unsigned long *) &a->value);
+#else
+    return (void*) atomic_load_acq_ptr((unsigned int *) &a->value);
+#endif
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+#ifdef atomic_load_acq_64
+    atomic_store_rel_ptr(&a->value, (unsigned long) p);
+#else
+    atomic_store_rel_ptr((unsigned int *) &a->value, (unsigned int) p);
+#endif
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+#ifdef atomic_load_acq_64
+    return atomic_cmpset_ptr(&a->value, (unsigned long) old_p, (unsigned long) new_p);
+#else
+    return atomic_cmpset_ptr((unsigned int *) &a->value, (unsigned int) old_p, (unsigned int) new_p);
+#endif
+}
+
+#elif defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__))
+
+#warn "The native atomic operations implementation for AMD64 has not been tested thoroughly. libatomic_ops is known to not work properly on AMD64 and your gcc version is too old for the gcc-builtin atomic ops support. You have three options now: test the native atomic operations implementation for AMD64, fix libatomic_ops, or upgrade your GCC."
+
+/* Adapted from glibc */
+
+typedef struct pa_atomic {
+    volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    a->value = i;
+}
+
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    int result;
+
+    __asm __volatile ("lock; xaddl %0, %1"
+                      : "=r" (result), "=m" (a->value)
+                      : "0" (i), "m" (a->value));
+
+    return result;
+}
+
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    return pa_atomic_add(a, -i);
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    return pa_atomic_add(a, 1);
+}
+
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    return pa_atomic_sub(a, 1);
+}
+
+static inline bool pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    int result;
+
+    __asm__ __volatile__ ("lock; cmpxchgl %2, %1"
+                          : "=a" (result), "=m" (a->value)
+                          : "r" (new_i), "m" (a->value), "0" (old_i));
+
+    return result == old_i;
+}
+
+typedef struct pa_atomic_ptr {
+    volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    a->value = (unsigned long) p;
+}
+
+static inline bool pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    void *result;
+
+    __asm__ __volatile__ ("lock; cmpxchgq %q2, %1"
+                          : "=a" (result), "=m" (a->value)
+                          : "r" (new_p), "m" (a->value), "0" (old_p));
+
+    return result == old_p;
+}
+
+#elif defined(ATOMIC_ARM_INLINE_ASM)
+
+/*
+   These should only be enabled if we have ARMv6 or better.
+*/
+
+typedef struct pa_atomic {
+    volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline void pa_memory_barrier(void) {
+#ifdef ATOMIC_ARM_MEMORY_BARRIER_ENABLED
+    asm volatile ("mcr  p15, 0, r0, c7, c10, 5  @ dmb");
+#endif
+}
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    pa_memory_barrier();
+    return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    a->value = i;
+    pa_memory_barrier();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    unsigned long not_exclusive;
+    int new_val, old_val;
+
+    pa_memory_barrier();
+    do {
+        asm volatile ("ldrex    %0, [%3]\n"
+                      "add      %2, %0, %4\n"
+                      "strex    %1, %2, [%3]\n"
+                      : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val)
+                      : "r" (&a->value), "Ir" (i)
+                      : "cc");
+    } while(not_exclusive);
+    pa_memory_barrier();
+
+    return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    unsigned long not_exclusive;
+    int new_val, old_val;
+
+    pa_memory_barrier();
+    do {
+        asm volatile ("ldrex    %0, [%3]\n"
+                      "sub      %2, %0, %4\n"
+                      "strex    %1, %2, [%3]\n"
+                      : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val)
+                      : "r" (&a->value), "Ir" (i)
+                      : "cc");
+    } while(not_exclusive);
+    pa_memory_barrier();
+
+    return old_val;
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    return pa_atomic_add(a, 1);
+}
+
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    return pa_atomic_sub(a, 1);
+}
+
+static inline bool pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    unsigned long not_equal, not_exclusive;
+
+    pa_memory_barrier();
+    do {
+        asm volatile ("ldrex    %0, [%2]\n"
+                      "subs     %0, %0, %3\n"
+                      "mov      %1, %0\n"
+                      "strexeq %0, %4, [%2]\n"
+                      : "=&r" (not_exclusive), "=&r" (not_equal)
+                      : "r" (&a->value), "Ir" (old_i), "r" (new_i)
+                      : "cc");
+    } while(not_exclusive && !not_equal);
+    pa_memory_barrier();
+
+    return !not_equal;
+}
+
+typedef struct pa_atomic_ptr {
+    volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    pa_memory_barrier();
+    return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    a->value = (unsigned long) p;
+    pa_memory_barrier();
+}
+
+static inline bool pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    unsigned long not_equal, not_exclusive;
+
+    pa_memory_barrier();
+    do {
+        asm volatile ("ldrex    %0, [%2]\n"
+                      "subs     %0, %0, %3\n"
+                      "mov      %1, %0\n"
+                      "strexeq %0, %4, [%2]\n"
+                      : "=&r" (not_exclusive), "=&r" (not_equal)
+                      : "r" (&a->value), "Ir" (old_p), "r" (new_p)
+                      : "cc");
+    } while(not_exclusive && !not_equal);
+    pa_memory_barrier();
+
+    return !not_equal;
+}
+
+#elif defined(ATOMIC_ARM_LINUX_HELPERS)
+
+/* See file arch/arm/kernel/entry-armv.S in your kernel sources for more
+   information about these functions. The arm kernel helper functions first
+   appeared in 2.6.16.
+   Apply --disable-atomic-arm-linux-helpers flag to configure if you prefer
+   inline asm implementation or you have an obsolete Linux kernel.
+*/
+/* Memory barrier */
+typedef void (__kernel_dmb_t)(void);
+#define __kernel_dmb (*(__kernel_dmb_t *)0xffff0fa0)
+
+static inline void pa_memory_barrier(void) {
+#ifndef ATOMIC_ARM_MEMORY_BARRIER_ENABLED
+    __kernel_dmb();
+#endif
+}
+
+/* Atomic exchange (__kernel_cmpxchg_t contains memory barriers if needed) */
+typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
+#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)
+
+/* This is just to get rid of all warnings */
+typedef int (__kernel_cmpxchg_u_t)(unsigned long oldval, unsigned long newval, volatile unsigned long *ptr);
+#define __kernel_cmpxchg_u (*(__kernel_cmpxchg_u_t *)0xffff0fc0)
+
+typedef struct pa_atomic {
+    volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    pa_memory_barrier();
+    return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    a->value = i;
+    pa_memory_barrier();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    int old_val;
+    do {
+        old_val = a->value;
+    } while(__kernel_cmpxchg(old_val, old_val + i, &a->value));
+    return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    int old_val;
+    do {
+        old_val = a->value;
+    } while(__kernel_cmpxchg(old_val, old_val - i, &a->value));
+    return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    return pa_atomic_add(a, 1);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    return pa_atomic_sub(a, 1);
+}
+
+/* Returns true when the operation was successful. */
+static inline bool pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    bool failed;
+    do {
+      failed = !!__kernel_cmpxchg(old_i, new_i, &a->value);
+    } while(failed && a->value == old_i);
+    return !failed;
+}
+
+typedef struct pa_atomic_ptr {
+    volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (unsigned long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    pa_memory_barrier();
+    return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    a->value = (unsigned long) p;
+    pa_memory_barrier();
+}
+
+static inline bool pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    bool failed;
+    do {
+        failed = !!__kernel_cmpxchg_u((unsigned long) old_p, (unsigned long) new_p, &a->value);
+    } while(failed && a->value == (unsigned long) old_p);
+    return !failed;
+}
+
+#else
+
+/* libatomic_ops based implementation */
+
+#include <atomic_ops.h>
+
+typedef struct pa_atomic {
+    volatile AO_t value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (AO_t) (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    return (int) AO_load_full((AO_t*) &a->value);
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    AO_store_full(&a->value, (AO_t) i);
+}
+
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    return (int) AO_fetch_and_add_full(&a->value, (AO_t) i);
+}
+
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    return (int) AO_fetch_and_add_full(&a->value, (AO_t) -i);
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    return (int) AO_fetch_and_add1_full(&a->value);
+}
+
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    return (int) AO_fetch_and_sub1_full(&a->value);
+}
+
+static inline bool pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    return AO_compare_and_swap_full(&a->value, (unsigned long) old_i, (unsigned long) new_i);
+}
+
+typedef struct pa_atomic_ptr {
+    volatile AO_t value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (AO_t) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    return (void*) AO_load_full((AO_t*) &a->value);
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    AO_store_full(&a->value, (AO_t) p);
+}
+
+static inline bool pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    return AO_compare_and_swap_full(&a->value, (AO_t) old_p, (AO_t) new_p);
+}
+
+#endif
+
+#endif
diff --git a/src/pulsecore/aupdate.c b/src/pulsecore/aupdate.c
new file mode 100644 (file)
index 0000000..04d5a57
--- /dev/null
@@ -0,0 +1,135 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+
+#include "aupdate.h"
+
+#define MSB (1U << (sizeof(unsigned)*8U-1))
+#define WHICH(n) (!!((n) & MSB))
+#define COUNTER(n) ((n) & ~MSB)
+
+struct pa_aupdate {
+    pa_atomic_t read_lock;
+    pa_mutex *write_lock;
+    pa_semaphore *semaphore;
+    bool swapped;
+};
+
+pa_aupdate *pa_aupdate_new(void) {
+    pa_aupdate *a;
+
+    a = pa_xnew(pa_aupdate, 1);
+    pa_atomic_store(&a->read_lock, 0);
+    a->write_lock = pa_mutex_new(false, false);
+    a->semaphore = pa_semaphore_new(0);
+
+    return a;
+}
+
+void pa_aupdate_free(pa_aupdate *a) {
+    pa_assert(a);
+
+    pa_mutex_free(a->write_lock);
+    pa_semaphore_free(a->semaphore);
+
+    pa_xfree(a);
+}
+
+unsigned pa_aupdate_read_begin(pa_aupdate *a) {
+    unsigned n;
+
+    pa_assert(a);
+
+    /* Increase the lock counter */
+    n = (unsigned) pa_atomic_inc(&a->read_lock);
+
+    /* When n is 0 we have about 2^31 threads running that all try to
+     * access the data at the same time, oh my! */
+    pa_assert(COUNTER(n)+1 > 0);
+
+    /* The uppermost bit tells us which data to look at */
+    return WHICH(n);
+}
+
+void pa_aupdate_read_end(pa_aupdate *a) {
+    unsigned PA_UNUSED n;
+
+    pa_assert(a);
+
+    /* Decrease the lock counter */
+    n = (unsigned) pa_atomic_dec(&a->read_lock);
+
+    /* Make sure the counter was valid */
+    pa_assert(COUNTER(n) > 0);
+
+    /* Post the semaphore */
+    pa_semaphore_post(a->semaphore);
+}
+
+unsigned pa_aupdate_write_begin(pa_aupdate *a) {
+    unsigned n;
+
+    pa_assert(a);
+
+    pa_mutex_lock(a->write_lock);
+
+    n = (unsigned) pa_atomic_load(&a->read_lock);
+
+    a->swapped = false;
+
+    return !WHICH(n);
+}
+
+unsigned pa_aupdate_write_swap(pa_aupdate *a) {
+    unsigned n;
+
+    pa_assert(a);
+
+    for (;;) {
+        n = (unsigned) pa_atomic_load(&a->read_lock);
+
+        /* If the read counter is > 0 wait; if it is 0 try to swap the lists */
+        if (COUNTER(n) > 0)
+            pa_semaphore_wait(a->semaphore);
+        else if (pa_atomic_cmpxchg(&a->read_lock, (int) n, (int) (n ^ MSB)))
+            break;
+    }
+
+    a->swapped = true;
+
+    return WHICH(n);
+}
+
+void pa_aupdate_write_end(pa_aupdate *a) {
+    pa_assert(a);
+
+    if (!a->swapped)
+        pa_aupdate_write_swap(a);
+
+    pa_mutex_unlock(a->write_lock);
+}
diff --git a/src/pulsecore/aupdate.h b/src/pulsecore/aupdate.h
new file mode 100644 (file)
index 0000000..347b853
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef foopulsecoreaupdatehfoo
+#define foopulsecoreaupdatehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct pa_aupdate pa_aupdate;
+
+pa_aupdate *pa_aupdate_new(void);
+void pa_aupdate_free(pa_aupdate *a);
+
+/* Will return 0, or 1, depending on which copy of the data the caller
+ * should look at */
+unsigned pa_aupdate_read_begin(pa_aupdate *a);
+void pa_aupdate_read_end(pa_aupdate *a);
+
+/* Will return 0, or 1, depending which copy of the data the caller
+ * should modify */
+unsigned pa_aupdate_write_begin(pa_aupdate *a);
+void pa_aupdate_write_end(pa_aupdate *a);
+
+/* Will return 0, or 1, depending which copy of the data the caller
+ * should modify. Each time called this will return the opposite of
+ * the previous pa_aupdate_write_begin() / pa_aupdate_write_swap()
+ * call. Should only be called between pa_aupdate_write_begin() and
+ * pa_aupdate_write_end() */
+unsigned pa_aupdate_write_swap(pa_aupdate *a);
+
+/*
+ * This infrastructure allows lock-free updates of arbitrary data
+ * structures in an rcu'ish way: two copies of the data structure
+ * should be existing. One side ('the reader') has read access to one
+ * of the two data structure at a time. It does not have to lock it,
+ * however it needs to signal that it is using it/stopped using
+ * it. The other side ('the writer') modifies the second data structure,
+ * and then atomically swaps the two data structures, followed by a
+ * modification of the other one.
+ *
+ * This is intended to be used for cases where the reader side needs
+ * to be fast while the writer side can be slow.
+ *
+ * The reader side is signal handler safe.
+ *
+ * The writer side lock is not recursive. The reader side is.
+ *
+ * There may be multiple readers and multiple writers at the same
+ * time.
+ *
+ * Usage is like this:
+ *
+ * static struct foo bar[2];
+ * static pa_aupdate *a;
+ *
+ * reader() {
+ *     unsigned j;
+ *
+ *     j = pa_update_read_begin(a);
+ *
+ *     ... read the data structure bar[j] ...
+ *
+ *     pa_update_read_end(a);
+ * }
+ *
+ * writer() {
+ *    unsigned j;
+ *
+ *    j = pa_update_write_begin(a);
+ *
+ *    ... update the data structure bar[j] ...
+ *
+ *    j = pa_update_write_swap(a);
+ *
+ *    ... update the data structure bar[j], the same way as above ...
+ *
+ *    pa_update_write_end(a)
+ * }
+ *
+ * In some cases keeping both structures up-to-date might not be
+ * necessary, since they are fully rebuilt on each iteration
+ * anyway. In that case you may leave the _write_swap() call out, it
+ * will then be done implicitly in the _write_end() invocation.
+ */
+
+#endif
diff --git a/src/pulsecore/auth-cookie.c b/src/pulsecore/auth-cookie.c
new file mode 100644 (file)
index 0000000..248d9bf
--- /dev/null
@@ -0,0 +1,139 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/refcnt.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/authkey.h>
+
+#include "auth-cookie.h"
+
+struct pa_auth_cookie {
+    PA_REFCNT_DECLARE;
+    pa_core *core;
+    char *name;
+    size_t size;
+};
+
+pa_auth_cookie* pa_auth_cookie_get(pa_core *core, const char *cn, bool create, size_t size) {
+    pa_auth_cookie *c;
+    char *t;
+
+    pa_assert(core);
+    pa_assert(size > 0);
+
+    t = pa_sprintf_malloc("auth-cookie%s%s", cn ? "@" : "", cn ? cn : "");
+
+    if ((c = pa_shared_get(core, t))) {
+
+        pa_xfree(t);
+
+        if (c->size != size)
+            return NULL;
+
+        return pa_auth_cookie_ref(c);
+    }
+
+    c = pa_xmalloc(PA_ALIGN(sizeof(pa_auth_cookie)) + size);
+    PA_REFCNT_INIT(c);
+    c->core = core;
+    c->name = t;
+    c->size = size;
+
+    pa_assert_se(pa_shared_set(core, t, c) >= 0);
+
+    if (pa_authkey_load(cn, create, (uint8_t*) c + PA_ALIGN(sizeof(pa_auth_cookie)), size) < 0) {
+        pa_auth_cookie_unref(c);
+        return NULL;
+    }
+
+    return c;
+}
+
+pa_auth_cookie *pa_auth_cookie_create(pa_core *core, const void *data, size_t size) {
+    pa_auth_cookie *c;
+    char *t;
+
+    pa_assert(core);
+    pa_assert(data);
+    pa_assert(size > 0);
+
+    t = pa_xstrdup("auth-cookie");
+
+    if ((c = pa_shared_get(core, t))) {
+
+        pa_xfree(t);
+
+        if (c->size != size)
+            return NULL;
+
+        return pa_auth_cookie_ref(c);
+    }
+
+    c = pa_xmalloc(PA_ALIGN(sizeof(pa_auth_cookie)) + size);
+    PA_REFCNT_INIT(c);
+    c->core = core;
+    c->name = t;
+    c->size = size;
+
+    pa_assert_se(pa_shared_set(core, t, c) >= 0);
+
+    memcpy((uint8_t *) c + PA_ALIGN(sizeof(pa_auth_cookie)), data, size);
+
+    return c;
+}
+
+pa_auth_cookie* pa_auth_cookie_ref(pa_auth_cookie *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_REFCNT_INC(c);
+
+    return c;
+}
+
+void pa_auth_cookie_unref(pa_auth_cookie *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (PA_REFCNT_DEC(c) > 0)
+        return;
+
+    pa_assert_se(pa_shared_remove(c->core, c->name) >= 0);
+
+    pa_xfree(c->name);
+    pa_xfree(c);
+}
+
+const uint8_t* pa_auth_cookie_read(pa_auth_cookie *c, size_t size) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(c->size == size);
+
+    return (const uint8_t*) c + PA_ALIGN(sizeof(pa_auth_cookie));
+}
diff --git a/src/pulsecore/auth-cookie.h b/src/pulsecore/auth-cookie.h
new file mode 100644 (file)
index 0000000..01f5244
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef fooauthcookiehfoo
+#define fooauthcookiehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+
+typedef struct pa_auth_cookie pa_auth_cookie;
+
+pa_auth_cookie* pa_auth_cookie_get(pa_core *c, const char *cn, bool create, size_t size);
+pa_auth_cookie* pa_auth_cookie_create(pa_core *c, const void *data, size_t size);
+pa_auth_cookie* pa_auth_cookie_ref(pa_auth_cookie *c);
+void pa_auth_cookie_unref(pa_auth_cookie *c);
+
+const uint8_t* pa_auth_cookie_read(pa_auth_cookie *, size_t size);
+
+#endif
diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c
new file mode 100644 (file)
index 0000000..71a9833
--- /dev/null
@@ -0,0 +1,208 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+
+#include "authkey.h"
+
+/* Generate a new authentication key, store it in file fd and return it in *data  */
+static int generate(int fd, void *ret_data, size_t length) {
+    ssize_t r;
+
+    pa_assert(fd >= 0);
+    pa_assert(ret_data);
+    pa_assert(length > 0);
+
+    pa_random(ret_data, length);
+
+    lseek(fd, (off_t) 0, SEEK_SET);
+    if (ftruncate(fd, (off_t) 0) < 0) {
+        pa_log("Failed to truncate cookie file: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    if ((r = pa_loop_write(fd, ret_data, length, NULL)) < 0 || (size_t) r != length) {
+        pa_log("Failed to write cookie file: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* Load an authentication cookie from file fn and store it in data. If
+ * the cookie file doesn't exist, create it */
+static int load(const char *fn, bool create, void *data, size_t length) {
+    int fd = -1;
+    int writable = 1;
+    int unlock = 0, ret = -1;
+    ssize_t r;
+
+    pa_assert(fn);
+    pa_assert(data);
+    pa_assert(length > 0);
+
+    if (create)
+        pa_make_secure_parent_dir(fn, pa_in_system_mode() ? 0755U : 0700U, -1, -1, false);
+
+    if ((fd = pa_open_cloexec(fn, (create ? O_RDWR|O_CREAT : O_RDONLY)|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
+
+        if (!create || errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY)) < 0) {
+            pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
+            goto finish;
+        } else
+            writable = 0;
+    }
+
+    unlock = pa_lock_fd(fd, 1) >= 0;
+
+    if ((r = pa_loop_read(fd, data, length, NULL)) < 0) {
+        pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
+        goto finish;
+    }
+
+    if ((size_t) r != length) {
+        pa_log_debug("Got %d bytes from cookie file '%s', expected %d", (int) r, fn, (int) length);
+
+        if (!writable) {
+            pa_log_warn("Unable to write cookie to read-only file");
+            goto finish;
+        }
+
+        if (generate(fd, data, length) < 0)
+            goto finish;
+    }
+
+    ret = 0;
+
+finish:
+
+    if (fd >= 0) {
+
+        if (unlock)
+            pa_lock_fd(fd, 0);
+
+        if (pa_close(fd) < 0) {
+            pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
+            ret = -1;
+        }
+    }
+
+    return ret;
+}
+
+/* If the specified file path starts with / return it, otherwise
+ * return path prepended with the config home directory. */
+static int normalize_path(const char *fn, char **_r) {
+    pa_assert(fn);
+    pa_assert(_r);
+
+    if (!pa_is_path_absolute(fn))
+        return pa_append_to_config_home_dir(fn, _r);
+
+    *_r = pa_xstrdup(fn);
+    return 0;
+}
+
+int pa_authkey_load(const char *fn, bool create, void *data, size_t length) {
+    char *p;
+    int ret;
+
+    pa_assert(fn);
+    pa_assert(data);
+    pa_assert(length > 0);
+
+    if ((ret = normalize_path(fn, &p)) < 0)
+        return ret;
+
+    if ((ret = load(p, create, data, length)) < 0)
+        pa_log_warn("Failed to load authentication key '%s': %s", p, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");
+
+    pa_xfree(p);
+
+    return ret;
+}
+
+/* Store the specified cookie in the specified cookie file */
+int pa_authkey_save(const char *fn, const void *data, size_t length) {
+    int fd = -1;
+    int unlock = 0, ret;
+    ssize_t r;
+    char *p;
+
+    pa_assert(fn);
+    pa_assert(data);
+    pa_assert(length > 0);
+
+    if ((ret = normalize_path(fn, &p)) < 0)
+        return ret;
+
+    if ((fd = pa_open_cloexec(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
+        pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
+        ret = -1;
+        goto finish;
+    }
+
+    unlock = pa_lock_fd(fd, 1) >= 0;
+
+    if ((r = pa_loop_write(fd, data, length, NULL)) < 0 || (size_t) r != length) {
+        pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
+        ret = -1;
+        goto finish;
+    }
+
+finish:
+
+    if (fd >= 0) {
+
+        if (unlock)
+            pa_lock_fd(fd, 0);
+
+        if (pa_close(fd) < 0) {
+            pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
+            ret = -1;
+        }
+    }
+
+    pa_xfree(p);
+
+    return ret;
+}
diff --git a/src/pulsecore/authkey.h b/src/pulsecore/authkey.h
new file mode 100644 (file)
index 0000000..7af9253
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef fooauthkeyhfoo
+#define fooauthkeyhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+int pa_authkey_load(const char *fn, bool create, void *data, size_t length);
+
+int pa_authkey_save(const char *path, const void *data, size_t length);
+
+#endif
diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c
new file mode 100644 (file)
index 0000000..dc586cb
--- /dev/null
@@ -0,0 +1,193 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+
+#include "avahi-wrap.h"
+
+typedef struct {
+    AvahiPoll api;
+    pa_mainloop_api *mainloop;
+} pa_avahi_poll;
+
+struct AvahiWatch {
+    pa_io_event *io_event;
+    pa_avahi_poll *avahi_poll;
+    AvahiWatchEvent current_event;
+    AvahiWatchCallback callback;
+    void *userdata;
+};
+
+static AvahiWatchEvent translate_io_flags_back(pa_io_event_flags_t e) {
+    return
+        (e & PA_IO_EVENT_INPUT ? AVAHI_WATCH_IN : 0) |
+        (e & PA_IO_EVENT_OUTPUT ? AVAHI_WATCH_OUT : 0) |
+        (e & PA_IO_EVENT_ERROR ? AVAHI_WATCH_ERR : 0) |
+        (e & PA_IO_EVENT_HANGUP ? AVAHI_WATCH_HUP : 0);
+}
+
+static pa_io_event_flags_t translate_io_flags(AvahiWatchEvent e) {
+    return
+        (e & AVAHI_WATCH_IN ? PA_IO_EVENT_INPUT : 0) |
+        (e & AVAHI_WATCH_OUT ? PA_IO_EVENT_OUTPUT : 0) |
+        (e & AVAHI_WATCH_ERR ? PA_IO_EVENT_ERROR : 0) |
+        (e & AVAHI_WATCH_HUP ? PA_IO_EVENT_HANGUP : 0);
+}
+
+static void watch_callback(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
+    AvahiWatch *w = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(w);
+
+    w->current_event = translate_io_flags_back(events);
+    w->callback(w, fd, w->current_event, w->userdata);
+    w->current_event = 0;
+}
+
+static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata) {
+    pa_avahi_poll *p;
+    AvahiWatch *w;
+
+    pa_assert(api);
+    pa_assert(fd >= 0);
+    pa_assert(callback);
+    pa_assert_se(p = api->userdata);
+
+    w = pa_xnew(AvahiWatch, 1);
+    w->avahi_poll = p;
+    w->current_event = 0;
+    w->callback = callback;
+    w->userdata = userdata;
+    w->io_event = p->mainloop->io_new(p->mainloop, fd, translate_io_flags(event), watch_callback, w);
+
+    return w;
+}
+
+static void watch_update(AvahiWatch *w, AvahiWatchEvent event) {
+    pa_assert(w);
+
+    w->avahi_poll->mainloop->io_enable(w->io_event, translate_io_flags(event));
+}
+
+static AvahiWatchEvent watch_get_events(AvahiWatch *w) {
+    pa_assert(w);
+
+    return w->current_event;
+}
+
+static void watch_free(AvahiWatch *w) {
+    pa_assert(w);
+
+    w->avahi_poll->mainloop->io_free(w->io_event);
+    pa_xfree(w);
+}
+
+struct AvahiTimeout {
+    pa_time_event *time_event;
+    pa_avahi_poll *avahi_poll;
+    AvahiTimeoutCallback callback;
+    void *userdata;
+};
+
+static void timeout_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
+    AvahiTimeout *to = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+
+    to->callback(to, to->userdata);
+}
+
+static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) {
+    pa_avahi_poll *p;
+    AvahiTimeout *t;
+
+    pa_assert(api);
+    pa_assert(callback);
+    pa_assert_se(p = api->userdata);
+
+    t = pa_xnew(AvahiTimeout, 1);
+    t->avahi_poll = p;
+    t->callback = callback;
+    t->userdata = userdata;
+
+    t->time_event = tv ? p->mainloop->time_new(p->mainloop, tv, timeout_callback, t) : NULL;
+
+    return t;
+}
+
+static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
+
+    pa_assert(t);
+
+    if (t->time_event && tv)
+        t->avahi_poll->mainloop->time_restart(t->time_event, tv);
+    else if (!t->time_event && tv)
+        t->time_event = t->avahi_poll->mainloop->time_new(t->avahi_poll->mainloop, tv, timeout_callback, t);
+    else if (t->time_event && !tv) {
+        t->avahi_poll->mainloop->time_free(t->time_event);
+        t->time_event = NULL;
+    }
+}
+
+static void timeout_free(AvahiTimeout *t) {
+    pa_assert(t);
+
+    if (t->time_event)
+        t->avahi_poll->mainloop->time_free(t->time_event);
+    pa_xfree(t);
+}
+
+AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *m) {
+    pa_avahi_poll *p;
+
+    pa_assert(m);
+
+    p = pa_xnew(pa_avahi_poll, 1);
+
+    p->api.userdata = p;
+    p->api.watch_new = watch_new;
+    p->api.watch_update = watch_update;
+    p->api.watch_get_events = watch_get_events;
+    p->api.watch_free = watch_free;
+    p->api.timeout_new = timeout_new;
+    p->api.timeout_update = timeout_update;
+    p->api.timeout_free = timeout_free;
+    p->mainloop = m;
+
+    return &p->api;
+}
+
+void pa_avahi_poll_free(AvahiPoll *api) {
+    pa_avahi_poll *p;
+    pa_assert(api);
+    pa_assert_se(p = api->userdata);
+
+    pa_xfree(p);
+}
+
diff --git a/src/pulsecore/avahi-wrap.h b/src/pulsecore/avahi-wrap.h
new file mode 100644 (file)
index 0000000..c7a9719
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef fooavahiwrapperhfoo
+#define fooavahiwrapperhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <avahi-client/client.h>
+
+#include <pulse/mainloop-api.h>
+
+AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *api);
+void pa_avahi_poll_free(AvahiPoll *p);
+
+#endif
diff --git a/src/pulsecore/bitset.c b/src/pulsecore/bitset.c
new file mode 100644 (file)
index 0000000..5c146ca
--- /dev/null
@@ -0,0 +1,65 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include "bitset.h"
+
+void pa_bitset_set(pa_bitset_t *b, unsigned k, bool v) {
+    pa_assert(b);
+
+    if (v)
+        b[k >> 5] |= 1 << (k & 31);
+    else
+        b[k >> 5] &= ~((uint32_t) (1 << (k & 31)));
+}
+
+bool pa_bitset_get(const pa_bitset_t *b, unsigned k) {
+    return !!(b[k >> 5] & (1 << (k & 31)));
+}
+
+bool pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...) {
+    va_list ap;
+    pa_bitset_t *a;
+    bool equal;
+
+    a = pa_xnew0(pa_bitset_t, PA_BITSET_ELEMENTS(n));
+
+    va_start(ap, n);
+    for (;;) {
+        int j = va_arg(ap, int);
+
+        if (j < 0)
+            break;
+
+        pa_bitset_set(a, j, true);
+    }
+    va_end(ap);
+
+    equal = memcmp(a, b, PA_BITSET_SIZE(n)) == 0;
+    pa_xfree(a);
+
+    return equal;
+}
diff --git a/src/pulsecore/bitset.h b/src/pulsecore/bitset.h
new file mode 100644 (file)
index 0000000..32fa82d
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef foopulsecorebitsethfoo
+#define foopulsecorebitsethfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <pulsecore/macro.h>
+
+#define PA_BITSET_ELEMENTS(n) (((n)+31)/32)
+#define PA_BITSET_SIZE(n) (PA_BITSET_ELEMENTS(n)*4)
+
+typedef uint32_t pa_bitset_t;
+
+void pa_bitset_set(pa_bitset_t *b, unsigned k, bool v);
+bool pa_bitset_get(const pa_bitset_t *b, unsigned k);
+bool pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...);
+
+#endif
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
new file mode 100644 (file)
index 0000000..bc5b75b
--- /dev/null
@@ -0,0 +1,408 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/device-port.h>
+
+#include "card.h"
+
+pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra) {
+    pa_card_profile *c;
+
+    pa_assert(name);
+
+    c = pa_xmalloc0(PA_ALIGN(sizeof(pa_card_profile)) + extra);
+    c->name = pa_xstrdup(name);
+    c->description = pa_xstrdup(description);
+    c->available = PA_AVAILABLE_UNKNOWN;
+
+    return c;
+}
+
+void pa_card_profile_free(pa_card_profile *c) {
+    pa_assert(c);
+
+    pa_xfree(c->input_name);
+    pa_xfree(c->output_name);
+    pa_xfree(c->name);
+    pa_xfree(c->description);
+    pa_xfree(c);
+}
+
+void pa_card_profile_set_available(pa_card_profile *c, pa_available_t available) {
+    pa_core *core;
+
+    pa_assert(c);
+    pa_assert(c->card); /* Modify member variable directly during creation instead of using this function */
+
+    if (c->available == available)
+        return;
+
+    c->available = available;
+    pa_log_debug("Setting card %s profile %s to availability status %s", c->card->name, c->name,
+                 available == PA_AVAILABLE_YES ? "yes" : available == PA_AVAILABLE_NO ? "no" : "unknown");
+
+    /* Post subscriptions to the card which owns us */
+    pa_assert_se(core = c->card->core);
+    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->card->index);
+
+    pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED], c);
+}
+
+pa_card_new_data* pa_card_new_data_init(pa_card_new_data *data) {
+    pa_assert(data);
+
+    memset(data, 0, sizeof(*data));
+    data->proplist = pa_proplist_new();
+    data->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_card_profile_free);
+    data->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_device_port_unref);
+    return data;
+}
+
+void pa_card_new_data_set_name(pa_card_new_data *data, const char *name) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    data->name = pa_xstrdup(name);
+}
+
+void pa_card_new_data_set_preferred_port(pa_card_new_data *data, pa_direction_t direction, pa_device_port *port) {
+    pa_assert(data);
+
+    if (direction == PA_DIRECTION_INPUT)
+        data->preferred_input_port = port;
+    else
+        data->preferred_output_port = port;
+}
+
+void pa_card_new_data_done(pa_card_new_data *data) {
+
+    pa_assert(data);
+
+    pa_proplist_free(data->proplist);
+
+    if (data->profiles)
+        pa_hashmap_free(data->profiles);
+
+    if (data->ports)
+        pa_hashmap_free(data->ports);
+
+    pa_xfree(data->name);
+}
+
+pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
+    pa_card *c;
+    const char *name;
+    void *state;
+    pa_card_profile *profile;
+    pa_device_port *port;
+
+    pa_core_assert_ref(core);
+    pa_assert(data);
+    pa_assert(data->name);
+    pa_assert(data->profiles);
+    pa_assert(!pa_hashmap_isempty(data->profiles));
+
+    c = pa_xnew0(pa_card, 1);
+
+    if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_CARD, c, data->namereg_fail))) {
+        pa_xfree(c);
+        return NULL;
+    }
+
+    pa_card_new_data_set_name(data, name);
+    pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_NEW], data);
+
+    c->core = core;
+    c->name = pa_xstrdup(data->name);
+    c->proplist = pa_proplist_copy(data->proplist);
+    c->driver = pa_xstrdup(pa_path_get_filename(data->driver));
+    c->module = data->module;
+
+    c->sinks = pa_idxset_new(NULL, NULL);
+    c->sources = pa_idxset_new(NULL, NULL);
+
+    /* As a minor optimization we just steal the list instead of
+     * copying it here */
+    pa_assert_se(c->profiles = data->profiles);
+    data->profiles = NULL;
+    pa_assert_se(c->ports = data->ports);
+    data->ports = NULL;
+
+    PA_HASHMAP_FOREACH(profile, c->profiles, state)
+        profile->card = c;
+
+    PA_HASHMAP_FOREACH(port, c->ports, state)
+        port->card = c;
+
+    c->preferred_input_port = data->preferred_input_port;
+    c->preferred_output_port = data->preferred_output_port;
+
+    pa_device_init_description(c->proplist, c);
+    pa_device_init_icon(c->proplist, true);
+    pa_device_init_intended_roles(c->proplist);
+
+    return c;
+}
+
+void pa_card_choose_initial_profile(pa_card *card) {
+    pa_card_profile *profile;
+    void *state;
+    pa_card_profile *best = NULL;
+
+    pa_assert(card);
+
+    /* By default, pick the highest priority profile that is not unavailable,
+     * or if all profiles are unavailable, pick the profile with the highest
+     * priority regardless of its availability. */
+
+    PA_HASHMAP_FOREACH(profile, card->profiles, state) {
+        if (profile->available == PA_AVAILABLE_NO)
+            continue;
+
+        if (!best || profile->priority > best->priority)
+            best = profile;
+    }
+
+    if (!best) {
+        PA_HASHMAP_FOREACH(profile, card->profiles, state) {
+            if (!best || profile->priority > best->priority)
+                best = profile;
+        }
+    }
+    pa_assert(best);
+
+    card->active_profile = best;
+    card->save_profile = false;
+
+    /* Let policy modules override the default. */
+    pa_hook_fire(&card->core->hooks[PA_CORE_HOOK_CARD_CHOOSE_INITIAL_PROFILE], card);
+}
+
+void pa_card_put(pa_card *card) {
+    pa_assert(card);
+
+    pa_assert_se(pa_idxset_put(card->core->cards, card, &card->index) >= 0);
+    card->linked = true;
+
+    pa_log_info("Created %u \"%s\"", card->index, card->name);
+    pa_hook_fire(&card->core->hooks[PA_CORE_HOOK_CARD_PUT], card);
+    pa_subscription_post(card->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW, card->index);
+}
+
+void pa_card_free(pa_card *c) {
+    pa_core *core;
+
+    pa_assert(c);
+    pa_assert(c->core);
+
+    core = c->core;
+
+    if (c->linked) {
+        pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_UNLINK], c);
+
+        pa_idxset_remove_by_data(c->core->cards, c, NULL);
+        pa_log_info("Freed %u \"%s\"", c->index, c->name);
+        pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
+    }
+
+    pa_namereg_unregister(core, c->name);
+
+    pa_assert(pa_idxset_isempty(c->sinks));
+    pa_idxset_free(c->sinks, NULL);
+    pa_assert(pa_idxset_isempty(c->sources));
+    pa_idxset_free(c->sources, NULL);
+
+    pa_hashmap_free(c->ports);
+
+    if (c->profiles)
+        pa_hashmap_free(c->profiles);
+
+    pa_proplist_free(c->proplist);
+    pa_xfree(c->driver);
+    pa_xfree(c->name);
+    pa_xfree(c);
+}
+
+void pa_card_add_profile(pa_card *c, pa_card_profile *profile) {
+    pa_assert(c);
+    pa_assert(profile);
+
+    /* take ownership of the profile */
+    pa_assert_se(pa_hashmap_put(c->profiles, profile->name, profile) >= 0);
+    profile->card = c;
+
+    pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
+
+    pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PROFILE_ADDED], profile);
+}
+
+static const char* profile_name_for_dir(pa_card_profile *cp, pa_direction_t dir) {
+    if (dir == PA_DIRECTION_OUTPUT && cp->output_name)
+        return cp->output_name;
+    if (dir == PA_DIRECTION_INPUT && cp->input_name)
+        return cp->input_name;
+    return cp->name;
+}
+
+static void update_port_preferred_profile(pa_card *c) {
+    pa_sink *sink;
+    pa_source *source;
+    uint32_t state;
+
+    PA_IDXSET_FOREACH(sink, c->sinks, state)
+        if (sink->active_port)
+            pa_device_port_set_preferred_profile(sink->active_port, profile_name_for_dir(c->active_profile, PA_DIRECTION_OUTPUT));
+    PA_IDXSET_FOREACH(source, c->sources, state)
+        if (source->active_port)
+            pa_device_port_set_preferred_profile(source->active_port, profile_name_for_dir(c->active_profile, PA_DIRECTION_INPUT));
+}
+
+int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save) {
+    int r;
+
+    pa_assert(c);
+    pa_assert(profile);
+    pa_assert(profile->card == c);
+
+    if (!c->set_profile) {
+        pa_log_debug("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name);
+        return -PA_ERR_NOTIMPLEMENTED;
+    }
+
+    if (c->active_profile == profile) {
+        if (save && !c->save_profile) {
+            update_port_preferred_profile(c);
+            c->save_profile = true;
+        }
+        return 0;
+    }
+
+    /* If we're setting the initial profile, we shouldn't call set_profile(),
+     * because the implementations don't expect that (for historical reasons).
+     * We should just set c->active_profile, and the implementations will
+     * properly set up that profile after pa_card_put() has returned. It would
+     * be probably good to change this so that also the initial profile can be
+     * set up in set_profile(), but if set_profile() fails, that would need
+     * some better handling than what we do here currently. */
+    if (c->linked && (r = c->set_profile(c, profile)) < 0)
+        return r;
+
+    c->active_profile = profile;
+    c->save_profile = save;
+
+    if (save)
+        update_port_preferred_profile(c);
+
+    if (c->linked) {
+        pa_log_info("Changed profile of card %u \"%s\" to %s", c->index, c->name, profile->name);
+        pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], c);
+        pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
+    }
+
+    return 0;
+}
+
+void pa_card_set_preferred_port(pa_card *c, pa_direction_t direction, pa_device_port *port) {
+    pa_device_port *old_port;
+    const char *old_port_str;
+    const char *new_port_str;
+    pa_card_preferred_port_changed_hook_data data;
+
+    pa_assert(c);
+
+    if (direction == PA_DIRECTION_INPUT) {
+        old_port = c->preferred_input_port;
+        old_port_str = c->preferred_input_port ? c->preferred_input_port->name : "(unset)";
+    } else {
+        old_port = c->preferred_output_port;
+        old_port_str = c->preferred_output_port ? c->preferred_output_port->name : "(unset)";
+    }
+
+    if (port == old_port)
+        return;
+
+    new_port_str = port ? port->name : "(unset)";
+
+    if (direction == PA_DIRECTION_INPUT) {
+        c->preferred_input_port = port;
+        pa_log_debug("%s: preferred_input_port: %s -> %s", c->name, old_port_str, new_port_str);
+    } else {
+        c->preferred_output_port = port;
+        pa_log_debug("%s: preferred_output_port: %s -> %s", c->name, old_port_str, new_port_str);
+    }
+
+    data.card = c;
+    data.direction = direction;
+    pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED], &data);
+}
+
+int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause) {
+    pa_sink *sink;
+    pa_source *source;
+    pa_suspend_cause_t suspend_cause;
+    uint32_t idx;
+    int ret = 0;
+
+    pa_assert(c);
+    pa_assert(cause != 0);
+
+    suspend_cause = c->suspend_cause;
+
+    if (suspend)
+        suspend_cause |= cause;
+    else
+        suspend_cause &= ~cause;
+
+    if (c->suspend_cause != suspend_cause) {
+        pa_log_debug("Card suspend causes/state changed");
+        c->suspend_cause = suspend_cause;
+        pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_SUSPEND_CHANGED], c);
+    }
+
+    PA_IDXSET_FOREACH(sink, c->sinks, idx) {
+        int r;
+
+        if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
+            ret = r;
+    }
+
+    PA_IDXSET_FOREACH(source, c->sources, idx) {
+        int r;
+
+        if ((r = pa_source_suspend(source, suspend, cause)) < 0)
+            ret = r;
+    }
+
+    return ret;
+}
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
new file mode 100644 (file)
index 0000000..5699475
--- /dev/null
@@ -0,0 +1,145 @@
+#ifndef foopulsecardhfoo
+#define foopulsecardhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/typedefs.h>
+#include <pulse/proplist.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/idxset.h>
+
+/* This enum replaces pa_port_available_t (defined in pulse/def.h) for
+ * internal use, so make sure both enum types stay in sync. */
+typedef enum pa_available {
+    PA_AVAILABLE_UNKNOWN = 0,
+    PA_AVAILABLE_NO = 1,
+    PA_AVAILABLE_YES = 2,
+} pa_available_t;
+
+struct pa_card_profile {
+    pa_card *card;
+    char *name;
+    char *description;
+
+    /* Identifiers for the profile's input and output parts, i e, if two different profiles
+       have the same input_name string, they have the same source(s).
+       Same for output_name and sink(s).
+       Can be NULL (and in case of an input- or output- only profile, the other direction
+       will be NULL). */
+    char *input_name;
+    char *output_name;
+
+    unsigned priority;
+    pa_available_t available; /* PA_AVAILABLE_UNKNOWN, PA_AVAILABLE_NO or PA_AVAILABLE_YES */
+
+    /* We probably want to have different properties later on here */
+    unsigned n_sinks;
+    unsigned n_sources;
+
+    unsigned max_sink_channels;
+    unsigned max_source_channels;
+
+    /* .. followed by some implementation specific data */
+};
+
+#define PA_CARD_PROFILE_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_card_profile))))
+
+struct pa_card {
+    uint32_t index;
+    pa_core *core;
+
+    char *name;
+
+    pa_proplist *proplist;
+    pa_module *module;
+    char *driver;
+
+    pa_idxset *sinks;
+    pa_idxset *sources;
+
+    pa_hashmap *profiles;
+    pa_card_profile *active_profile;
+
+    pa_hashmap *ports;
+    pa_device_port *preferred_input_port;
+    pa_device_port *preferred_output_port;
+
+    bool save_profile:1;
+
+    pa_suspend_cause_t suspend_cause;
+
+    bool linked;
+
+    void *userdata;
+
+    int (*set_profile)(pa_card *c, pa_card_profile *profile);
+};
+
+typedef struct pa_card_new_data {
+    char *name;
+    pa_proplist *proplist;
+
+    const char *driver;
+    pa_module *module;
+
+    pa_hashmap *profiles;
+    pa_hashmap *ports;
+    pa_device_port *preferred_input_port;
+    pa_device_port *preferred_output_port;
+
+    bool namereg_fail:1;
+} pa_card_new_data;
+
+typedef struct {
+    pa_card *card;
+    pa_direction_t direction;
+} pa_card_preferred_port_changed_hook_data;
+
+pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra);
+void pa_card_profile_free(pa_card_profile *c);
+
+/* The profile's available status has changed */
+void pa_card_profile_set_available(pa_card_profile *c, pa_available_t available);
+
+pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data);
+void pa_card_new_data_set_name(pa_card_new_data *data, const char *name);
+void pa_card_new_data_set_preferred_port(pa_card_new_data *data, pa_direction_t direction, pa_device_port *port);
+void pa_card_new_data_done(pa_card_new_data *data);
+
+pa_card *pa_card_new(pa_core *c, pa_card_new_data *data);
+
+/* Select the initial card profile according to the configured policies. This
+ * must be called between pa_card_new() and pa_card_put(), after the port and
+ * profile availablities have been initialized. */
+void pa_card_choose_initial_profile(pa_card *card);
+
+void pa_card_put(pa_card *c);
+void pa_card_free(pa_card *c);
+
+void pa_card_add_profile(pa_card *c, pa_card_profile *profile);
+
+int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save);
+
+void pa_card_set_preferred_port(pa_card *c, pa_direction_t direction, pa_device_port *port);
+
+int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause);
+
+#endif
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
new file mode 100644 (file)
index 0000000..01fea47
--- /dev/null
@@ -0,0 +1,2238 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ltdl.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <time.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/error.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/client.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/tokenizer.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/cli-text.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/sound-file.h>
+#include <pulsecore/play-memchunk.h>
+#include <pulsecore/sound-file-stream.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/modinfo.h>
+#include <pulsecore/dynarray.h>
+
+#include "cli-command.h"
+
+struct command {
+    const char *name;
+    int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, bool *fail);
+    const char *help;
+    unsigned args;
+};
+
+#define META_INCLUDE ".include"
+#define META_FAIL ".fail"
+#define META_NOFAIL ".nofail"
+#define META_IFEXISTS ".ifexists"
+#define META_ELSE ".else"
+#define META_ENDIF ".endif"
+
+enum {
+    IFSTATE_NONE = -1,
+    IFSTATE_FALSE = 0,
+    IFSTATE_TRUE = 1,
+};
+
+/* Prototypes for all available commands */
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_source_output_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_source_output_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_log_target(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_port_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+
+/* A method table for all available commands */
+
+static const struct command commands[] = {
+    { "help",                    pa_cli_command_help,               "Show this help",               1 },
+    { "list-modules",            pa_cli_command_modules,            "List loaded modules",          1 },
+    { "list-cards",              pa_cli_command_cards,              "List cards",                   1 },
+    { "list-sinks",              pa_cli_command_sinks,              "List loaded sinks",            1 },
+    { "list-sources",            pa_cli_command_sources,            "List loaded sources",          1 },
+    { "list-clients",            pa_cli_command_clients,            "List loaded clients",          1 },
+    { "list-sink-inputs",        pa_cli_command_sink_inputs,        "List sink inputs",             1 },
+    { "list-source-outputs",     pa_cli_command_source_outputs,     "List source outputs",          1 },
+    { "stat",                    pa_cli_command_stat,               "Show memory block statistics", 1 },
+    { "info",                    pa_cli_command_info,               "Show comprehensive status",    1 },
+    { "ls",                      pa_cli_command_info,               NULL,                           1 },
+    { "list",                    pa_cli_command_info,               NULL,                           1 },
+    { "load-module",             pa_cli_command_load,               "Load a module (args: name, arguments)", 3},
+    { "unload-module",           pa_cli_command_unload,             "Unload a module (args: index|name)", 2},
+    { "describe-module",         pa_cli_command_describe,           "Describe a module (arg: name)", 2},
+    { "set-sink-volume",         pa_cli_command_sink_volume,        "Set the volume of a sink (args: index|name, volume)", 3},
+    { "set-source-volume",       pa_cli_command_source_volume,      "Set the volume of a source (args: index|name, volume)", 3},
+    { "set-sink-mute",           pa_cli_command_sink_mute,          "Set the mute switch of a sink (args: index|name, bool)", 3},
+    { "set-source-mute",         pa_cli_command_source_mute,        "Set the mute switch of a source (args: index|name, bool)", 3},
+    { "set-sink-input-volume",   pa_cli_command_sink_input_volume,  "Set the volume of a sink input (args: index, volume)", 3},
+    { "set-source-output-volume",pa_cli_command_source_output_volume,"Set the volume of a source output (args: index, volume)", 3},
+    { "set-sink-input-mute",     pa_cli_command_sink_input_mute,    "Set the mute switch of a sink input (args: index, bool)", 3},
+    { "set-source-output-mute",  pa_cli_command_source_output_mute, "Set the mute switch of a source output (args: index, bool)", 3},
+    { "set-default-sink",        pa_cli_command_sink_default,       "Set the default sink (args: index|name)", 2},
+    { "set-default-source",      pa_cli_command_source_default,     "Set the default source (args: index|name)", 2},
+    { "set-card-profile",        pa_cli_command_card_profile,       "Change the profile of a card (args: index|name, profile-name)", 3},
+    { "set-sink-port",           pa_cli_command_sink_port,          "Change the port of a sink (args: index|name, port-name)", 3},
+    { "set-source-port",         pa_cli_command_source_port,        "Change the port of a source (args: index|name, port-name)", 3},
+    { "set-port-latency-offset", pa_cli_command_port_offset,        "Change the latency of a port (args: card-index|card-name, port-name, latency-offset)", 4},
+    { "suspend-sink",            pa_cli_command_suspend_sink,       "Suspend sink (args: index|name, bool)", 3},
+    { "suspend-source",          pa_cli_command_suspend_source,     "Suspend source (args: index|name, bool)", 3},
+    { "suspend",                 pa_cli_command_suspend,            "Suspend all sinks and all sources (args: bool)", 2},
+    { "move-sink-input",         pa_cli_command_move_sink_input,    "Move sink input to another sink (args: index, sink)", 3},
+    { "move-source-output",      pa_cli_command_move_source_output, "Move source output to another source (args: index, source)", 3},
+    { "update-sink-proplist",    pa_cli_command_update_sink_proplist, "Update the properties of a sink (args: index|name, properties)", 3},
+    { "update-source-proplist",  pa_cli_command_update_source_proplist, "Update the properties of a source (args: index|name, properties)", 3},
+    { "update-sink-input-proplist", pa_cli_command_update_sink_input_proplist, "Update the properties of a sink input (args: index, properties)", 3},
+    { "update-source-output-proplist", pa_cli_command_update_source_output_proplist, "Update the properties of a source output (args: index, properties)", 3},
+    { "list-samples",            pa_cli_command_scache_list,        "List all entries in the sample cache", 1},
+    { "play-sample",             pa_cli_command_scache_play,        "Play a sample from the sample cache (args: name, sink|index)", 3},
+    { "remove-sample",           pa_cli_command_scache_remove,      "Remove a sample from the sample cache (args: name)", 2},
+    { "load-sample",             pa_cli_command_scache_load,        "Load a sound file into the sample cache (args: name, filename)", 3},
+    { "load-sample-lazy",        pa_cli_command_scache_load,        "Lazily load a sound file into the sample cache (args: name, filename)", 3},
+    { "load-sample-dir-lazy",    pa_cli_command_scache_load_dir,    "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
+    { "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},
+    { "set-log-target",          pa_cli_command_log_target,         "Change the log target (args: null|auto|syslog|stderr|file:PATH|newfile:PATH)", 2},
+    { "set-log-level",           pa_cli_command_log_level,          "Change the log level (args: numeric level)", 2},
+    { "set-log-meta",            pa_cli_command_log_meta,           "Show source code location in log messages (args: bool)", 2},
+    { "set-log-time",            pa_cli_command_log_time,           "Show timestamps in log messages (args: bool)", 2},
+    { "set-log-backtrace",       pa_cli_command_log_backtrace,      "Show backtrace in log messages (args: frames)", 2},
+    { "play-file",               pa_cli_command_play_file,          "Play a sound file (args: filename, sink|index)", 3},
+    { "dump",                    pa_cli_command_dump,               "Dump daemon configuration", 1},
+    { "dump-volumes",            pa_cli_command_dump_volumes,       "Debug: Show the state of all volumes", 1 },
+    { "shared",                  pa_cli_command_list_shared_props,  "Debug: Show shared properties", 1},
+    { "exit",                    pa_cli_command_exit,               "Terminate the daemon",         1 },
+    { "vacuum",                  pa_cli_command_vacuum,             NULL, 1},
+    { NULL, NULL, NULL, 0 }
+};
+
+static const char whitespace[] = " \t\n\r";
+static const char linebreak[] = "\n\r";
+
+static uint32_t parse_index(const char *n) {
+    uint32_t idx;
+
+    if (pa_atou(n, &idx) < 0)
+        return (uint32_t) PA_IDXSET_INVALID;
+
+    return idx;
+}
+
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (pa_core_exit(c, false, 0) < 0)
+        pa_strbuf_puts(buf, "Not allowed to terminate daemon.\n");
+
+    return 0;
+}
+
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const struct command*command;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_strbuf_puts(buf, "Available commands:\n");
+
+    for (command = commands; command->name; command++)
+        if (command->help)
+            pa_strbuf_printf(buf, "    %-25s %s\n", command->name, command->help);
+    return 0;
+}
+
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_module_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_client_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_card_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_sink_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_source_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_sink_input_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_source_output_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char bytes[PA_BYTES_SNPRINT_MAX];
+    const pa_mempool_stat *mstat;
+    unsigned k;
+
+    static const char* const type_table[PA_MEMBLOCK_TYPE_MAX] = {
+        [PA_MEMBLOCK_POOL] = "POOL",
+        [PA_MEMBLOCK_POOL_EXTERNAL] = "POOL_EXTERNAL",
+        [PA_MEMBLOCK_APPENDED] = "APPENDED",
+        [PA_MEMBLOCK_USER] = "USER",
+        [PA_MEMBLOCK_FIXED] = "FIXED",
+        [PA_MEMBLOCK_IMPORTED] = "IMPORTED",
+    };
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    mstat = pa_mempool_get_stat(c->mempool);
+
+    pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
+                     (unsigned) pa_atomic_load(&mstat->n_allocated),
+                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->allocated_size)));
+
+    pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
+                     (unsigned) pa_atomic_load(&mstat->n_accumulated),
+                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->accumulated_size)));
+
+    pa_strbuf_printf(buf, "Memory blocks imported from other processes: %u, size: %s.\n",
+                     (unsigned) pa_atomic_load(&mstat->n_imported),
+                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->imported_size)));
+
+    pa_strbuf_printf(buf, "Memory blocks exported to other processes: %u, size: %s.\n",
+                     (unsigned) pa_atomic_load(&mstat->n_exported),
+                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->exported_size)));
+
+    pa_strbuf_printf(buf, "Total sample cache size: %s.\n",
+                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_scache_total_size(c)));
+
+    pa_strbuf_printf(buf, "Default sample spec: %s\n",
+                     pa_sample_spec_snprint(ss, sizeof(ss), &c->default_sample_spec));
+
+    pa_strbuf_printf(buf, "Default channel map: %s\n",
+                     pa_channel_map_snprint(cm, sizeof(cm), &c->default_channel_map));
+
+    pa_strbuf_printf(buf, "Default sink name: %s\n"
+                     "Default source name: %s\n",
+                     c->default_sink ? c->default_sink->name : "none",
+                     c->default_source ? c->default_source->name : "none");
+
+    for (k = 0; k < PA_MEMBLOCK_TYPE_MAX; k++)
+        pa_strbuf_printf(buf,
+                         "Memory blocks of type %s: %u allocated/%u accumulated.\n",
+                         type_table[k],
+                         (unsigned) pa_atomic_load(&mstat->n_allocated_by_type[k]),
+                         (unsigned) pa_atomic_load(&mstat->n_accumulated_by_type[k]));
+
+    return 0;
+}
+
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_cli_command_stat(c, t, buf, fail);
+    pa_cli_command_modules(c, t, buf, fail);
+    pa_cli_command_sinks(c, t, buf, fail);
+    pa_cli_command_sources(c, t, buf, fail);
+    pa_cli_command_clients(c, t, buf, fail);
+    pa_cli_command_cards(c, t, buf, fail);
+    pa_cli_command_sink_inputs(c, t, buf, fail);
+    pa_cli_command_source_outputs(c, t, buf, fail);
+    pa_cli_command_scache_list(c, t, buf, fail);
+    return 0;
+}
+
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *name;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(name = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n");
+        return -1;
+    }
+
+    if (!pa_module_load(c, name,  pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "Module load failed.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    pa_module *m;
+    uint32_t idx;
+    const char *i;
+    bool unloaded = false;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(i = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify the module index or name.\n");
+        return -1;
+    }
+
+    if (pa_atou(i, &idx) >= 0) {
+        if (!(m = pa_idxset_get_by_index(c->modules, idx))) {
+            pa_strbuf_puts(buf, "Invalid module index.\n");
+            return -1;
+        }
+
+        pa_module_unload_request(m, false);
+
+    } else {
+        PA_IDXSET_FOREACH(m, c->modules, idx)
+            if (pa_streq(i, m->name)) {
+                unloaded = true;
+                pa_module_unload_request(m, false);
+            }
+
+        if (unloaded == false) {
+            pa_strbuf_printf(buf, "Module %s not loaded.\n", i);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *name;
+    pa_modinfo *i;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(name = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify the module name.\n");
+        return -1;
+    }
+
+    if ((i = pa_modinfo_get_by_name(name))) {
+
+        pa_strbuf_printf(buf, "Name: %s\n", name);
+
+        if (!i->description && !i->version && !i->author && !i->usage)
+            pa_strbuf_printf(buf, "No module information available\n");
+        else {
+            if (i->version)
+                pa_strbuf_printf(buf, "Version: %s\n", i->version);
+            if (i->description)
+                pa_strbuf_printf(buf, "Description: %s\n", i->description);
+            if (i->author)
+                pa_strbuf_printf(buf, "Author: %s\n", i->author);
+            if (i->usage)
+                pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
+            pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
+            if (i->deprecated)
+                pa_strbuf_printf(buf, "Warning, deprecated: %s\n", i->deprecated);
+        }
+
+        pa_modinfo_free(i);
+    } else
+        pa_strbuf_puts(buf, "Failed to open module.\n");
+
+    return 0;
+}
+
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *v;
+    pa_sink *sink;
+    uint32_t volume;
+    pa_cvolume cvolume;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(v = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
+        return -1;
+    }
+
+    if (pa_atou(v, &volume) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse volume.\n");
+        return -1;
+    }
+
+    if (!PA_VOLUME_IS_VALID(volume)) {
+        pa_strbuf_puts(buf, "Volume outside permissible range.\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;
+    }
+
+    pa_cvolume_set(&cvolume, 1, volume);
+    pa_sink_set_volume(sink, &cvolume, true, true);
+    return 0;
+}
+
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *v;
+    pa_sink_input *si;
+    pa_volume_t volume;
+    pa_cvolume cvolume;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(v = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
+        return -1;
+    }
+
+    if (pa_atou(v, &volume) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse volume.\n");
+        return -1;
+    }
+
+    if (!PA_VOLUME_IS_VALID(volume)) {
+        pa_strbuf_puts(buf, "Volume outside permissible range.\n");
+        return -1;
+    }
+
+    if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) {
+        pa_strbuf_puts(buf, "No sink input found with this index.\n");
+        return -1;
+    }
+
+    if (!si->volume_writable) {
+        pa_strbuf_puts(buf, "This sink input's volume can't be changed.\n");
+        return -1;
+    }
+
+    pa_cvolume_set(&cvolume, 1, volume);
+    pa_sink_input_set_volume(si, &cvolume, true, true);
+    return 0;
+}
+
+static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *v;
+    pa_source *source;
+    uint32_t volume;
+    pa_cvolume cvolume;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(v = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
+        return -1;
+    }
+
+    if (pa_atou(v, &volume) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse volume.\n");
+        return -1;
+    }
+
+    if (!PA_VOLUME_IS_VALID(volume)) {
+        pa_strbuf_puts(buf, "Volume outside permissible range.\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;
+    }
+
+    pa_cvolume_set(&cvolume, 1, volume);
+    pa_source_set_volume(source, &cvolume, true, true);
+    return 0;
+}
+
+static int pa_cli_command_source_output_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *v;
+    pa_source_output *so;
+    pa_volume_t volume;
+    pa_cvolume cvolume;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(v = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
+        return -1;
+    }
+
+    if (pa_atou(v, &volume) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse volume.\n");
+        return -1;
+    }
+
+    if (!PA_VOLUME_IS_VALID(volume)) {
+        pa_strbuf_puts(buf, "Volume outside permissible range.\n");
+        return -1;
+    }
+
+    if (!(so = pa_idxset_get_by_index(c->source_outputs, idx))) {
+        pa_strbuf_puts(buf, "No source output found with this index.\n");
+        return -1;
+    }
+
+    if (!so->volume_writable) {
+        pa_strbuf_puts(buf, "This source output's volume can't be changed.\n");
+        return -1;
+    }
+
+    pa_cvolume_set(&cvolume, 1, volume);
+    pa_source_output_set_volume(so, &cvolume, true, true);
+    return 0;
+}
+
+static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *m;
+    pa_sink *sink;
+    int mute;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(m = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((mute = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+        return -1;
+    }
+
+    pa_sink_set_mute(sink, mute, true);
+    return 0;
+}
+
+static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *m;
+    pa_source *source;
+    int mute;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(m = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((mute = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
+        return -1;
+    }
+
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+        return -1;
+    }
+
+    pa_source_set_mute(source, mute, true);
+    return 0;
+}
+
+static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *s;
+    pa_sink *sink;
+    pa_proplist *p;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(s = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\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;
+    }
+
+    if (!(p = pa_proplist_from_string(s))) {
+        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+        return -1;
+    }
+
+    pa_sink_update_proplist(sink, PA_UPDATE_REPLACE, p);
+
+    pa_proplist_free(p);
+
+    return 0;
+}
+
+static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *s;
+    pa_source *source;
+    pa_proplist *p;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(s = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\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;
+    }
+
+    if (!(p = pa_proplist_from_string(s))) {
+        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+        return -1;
+    }
+
+    pa_source_update_proplist(source, PA_UPDATE_REPLACE, p);
+
+    pa_proplist_free(p);
+
+    return 0;
+}
+
+static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *s;
+    pa_sink_input *si;
+    uint32_t idx;
+    pa_proplist *p;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink input either by index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(s = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
+        return -1;
+    }
+
+    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No sink input found with this index.\n");
+        return -1;
+    }
+
+    if (!(p = pa_proplist_from_string(s))) {
+        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+        return -1;
+    }
+
+    pa_sink_input_update_proplist(si, PA_UPDATE_REPLACE, p);
+
+    pa_proplist_free(p);
+
+    return 0;
+}
+
+static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *s;
+    pa_source_output *so;
+    uint32_t idx;
+    pa_proplist *p;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(s = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
+        return -1;
+    }
+
+    if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No source output found with this index.\n");
+        return -1;
+    }
+
+    if (!(p = pa_proplist_from_string(s))) {
+        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+        return -1;
+    }
+
+    pa_source_output_update_proplist(so, PA_UPDATE_REPLACE, p);
+
+    pa_proplist_free(p);
+
+    return 0;
+}
+
+static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *v;
+    pa_sink_input *si;
+    uint32_t idx;
+    int mute;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(v = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((mute = pa_parse_boolean(v)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
+        return -1;
+    }
+
+    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No sink input found with this index.\n");
+        return -1;
+    }
+
+    pa_sink_input_set_mute(si, mute, true);
+    return 0;
+}
+
+static int pa_cli_command_source_output_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *v;
+    pa_source_output *so;
+    uint32_t idx;
+    int mute;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(v = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((mute = pa_parse_boolean(v)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
+        return -1;
+    }
+
+    if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No source output found with this index.\n");
+        return -1;
+    }
+
+    pa_source_output_set_mute(so, mute, true);
+    return 0;
+}
+
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n;
+    pa_sink *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if ((s = pa_namereg_get(c, n, PA_NAMEREG_SINK)))
+        pa_core_set_configured_default_sink(c, s->name);
+    else
+        pa_strbuf_printf(buf, "Sink %s does not exist.\n", n);
+
+    return 0;
+}
+
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n;
+    pa_source *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+        return -1;
+    }
+
+    if ((s = pa_namereg_get(c, n, PA_NAMEREG_SOURCE)))
+        pa_core_set_configured_default_source(c, s->name);
+    else
+        pa_strbuf_printf(buf, "Source %s does not exist.\n", n);
+    return 0;
+}
+
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n;
+    pa_client *client;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a client by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(client = pa_idxset_get_by_index(c->clients, idx))) {
+        pa_strbuf_puts(buf, "No client found by this index.\n");
+        return -1;
+    }
+
+    pa_client_kill(client);
+    return 0;
+}
+
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n;
+    pa_sink_input *sink_input;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) {
+        pa_strbuf_puts(buf, "No sink input found by this index.\n");
+        return -1;
+    }
+
+    pa_sink_input_kill(sink_input);
+    return 0;
+}
+
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n;
+    pa_source_output *source_output;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) {
+        pa_strbuf_puts(buf, "No source output found by this index.\n");
+        return -1;
+    }
+
+    pa_source_output_kill(source_output);
+    return 0;
+}
+
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_scache_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+
+    return 0;
+}
+
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *sink_name;
+    pa_sink *sink;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) {
+        pa_strbuf_puts(buf, "No sink by that name.\n");
+        return -1;
+    }
+
+    if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM, NULL, &idx) < 0) {
+        pa_strbuf_puts(buf, "Failed to play sample.\n");
+        return -1;
+    }
+
+    pa_strbuf_printf(buf, "Playing on sink input #%i\n", idx);
+
+    return 0;
+}
+
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sample name.\n");
+        return -1;
+    }
+
+    if (pa_scache_remove_item(c, n) < 0) {
+        pa_strbuf_puts(buf, "Failed to remove sample.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *fname, *n;
+    int r;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
+        return -1;
+    }
+
+    if (strstr(pa_tokenizer_get(t, 0), "lazy"))
+        r = pa_scache_add_file_lazy(c, n, fname, NULL);
+    else
+        r = pa_scache_add_file(c, n, fname, NULL);
+
+    if (r < 0)
+        pa_strbuf_puts(buf, "Failed to load sound file.\n");
+
+    return 0;
+}
+
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *pname;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(pname = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a path name.\n");
+        return -1;
+    }
+
+    if (pa_scache_add_directory_lazy(c, pname) < 0) {
+        pa_strbuf_puts(buf, "Failed to load directory.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *fname, *sink_name;
+    pa_sink *sink;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) {
+        pa_strbuf_puts(buf, "No sink by that name.\n");
+        return -1;
+    }
+
+    return pa_play_file(sink, fname, NULL);
+}
+
+static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_shared_dump(c, buf);
+    return 0;
+}
+
+static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_mempool_vacuum(c->mempool);
+
+    return 0;
+}
+
+static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *k;
+    pa_sink_input *si;
+    pa_sink *sink;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(k = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a sink.\n");
+        return -1;
+    }
+
+    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No sink input found with this index.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, k, PA_NAMEREG_SINK))) {
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+        return -1;
+    }
+
+    if (pa_sink_input_move_to(si, sink, true) < 0) {
+        pa_strbuf_puts(buf, "Moved failed.\n");
+        return -1;
+    }
+    return 0;
+}
+
+static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *k;
+    pa_source_output *so;
+    pa_source *source;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(k = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a source.\n");
+        return -1;
+    }
+
+    if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No source output found with this index.\n");
+        return -1;
+    }
+
+    if (!(source = pa_namereg_get(c, k, PA_NAMEREG_SOURCE))) {
+        pa_strbuf_puts(buf, "No source found by this name or index.\n");
+        return -1;
+    }
+
+    if (pa_source_output_move_to(so, source, true) < 0) {
+        pa_strbuf_puts(buf, "Moved failed.\n");
+        return -1;
+    }
+    return 0;
+}
+
+static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *m;
+    pa_sink *sink;
+    int suspend, r;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(m = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((suspend = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+        return -1;
+    }
+
+    pa_log_debug("%s of sink %s requested via CLI.", suspend ? "Suspending" : "Resuming", sink->name);
+
+    if ((r = pa_sink_suspend(sink, suspend, PA_SUSPEND_USER)) < 0)
+        pa_strbuf_printf(buf, "Failed to resume/suspend sink: %s\n", pa_strerror(r));
+
+    return 0;
+}
+
+static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *m;
+    pa_source *source;
+    int suspend, r;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(m = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((suspend = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+        return -1;
+    }
+
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
+        pa_strbuf_puts(buf, "No source found by this name or index.\n");
+        return -1;
+    }
+
+    pa_log_debug("%s of source %s requested via CLI.", suspend ? "Suspending" : "Resuming", source->name);
+
+    if ((r = pa_source_suspend(source, suspend, PA_SUSPEND_USER)) < 0)
+        pa_strbuf_printf(buf, "Failed to resume/suspend source: %s\n", pa_strerror(r));
+
+    return 0;
+}
+
+static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *m;
+    int suspend, r;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(m = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((suspend = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+        return -1;
+    }
+
+    pa_log_debug("%s of all sinks and sources requested via CLI.", suspend ? "Suspending" : "Resuming");
+
+    if ((r = pa_sink_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0)
+        pa_strbuf_printf(buf, "Failed to resume/suspend all sinks: %s\n", pa_strerror(r));
+
+    if ((r = pa_source_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0)
+        pa_strbuf_printf(buf, "Failed to resume/suspend all sources: %s\n", pa_strerror(r));
+
+    return 0;
+}
+
+static int pa_cli_command_log_target(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *m;
+    pa_log_target *log_target = NULL;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(m = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a log target (null|auto|syslog|stderr|file:PATH|newfile:PATH).\n");
+        return -1;
+    }
+
+    /* 'auto' is actually the effect with 'stderr' */
+    if (pa_streq(m, "auto"))
+        log_target = pa_log_target_new(PA_LOG_STDERR, NULL);
+    else {
+        log_target = pa_log_parse_target(m);
+
+        if (!log_target) {
+            pa_strbuf_puts(buf, "Invalid log target.\n");
+            return -1;
+        }
+    }
+
+    if (pa_log_set_target(log_target) < 0) {
+        pa_strbuf_puts(buf, "Failed to set log target.\n");
+        pa_log_target_free(log_target);
+        return -1;
+    }
+
+    pa_log_target_free(log_target);
+
+    return 0;
+}
+
+static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *m;
+    uint32_t level;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(m = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a log level (0..4).\n");
+        return -1;
+    }
+
+    if (pa_atou(m, &level) < 0 || level >= PA_LOG_LEVEL_MAX) {
+        pa_strbuf_puts(buf, "Failed to parse log level.\n");
+        return -1;
+    }
+
+    pa_log_set_level(level);
+
+    return 0;
+}
+
+static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *m;
+    int b;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(m = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a boolean.\n");
+        return -1;
+    }
+
+    if ((b = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse log meta switch.\n");
+        return -1;
+    }
+
+    pa_log_set_flags(PA_LOG_PRINT_META, b ? PA_LOG_SET : PA_LOG_UNSET);
+
+    return 0;
+}
+
+static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *m;
+    int b;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(m = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a boolean.\n");
+        return -1;
+    }
+
+    if ((b = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse log meta switch.\n");
+        return -1;
+    }
+
+    pa_log_set_flags(PA_LOG_PRINT_TIME, b ? PA_LOG_SET : PA_LOG_UNSET);
+
+    return 0;
+}
+
+static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *m;
+    uint32_t nframes;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(m = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a backtrace level.\n");
+        return -1;
+    }
+
+    if (pa_atou(m, &nframes) < 0 || nframes >= 1000) {
+        pa_strbuf_puts(buf, "Failed to parse backtrace level.\n");
+        return -1;
+    }
+
+    pa_log_set_show_backtrace(nframes);
+
+    return 0;
+}
+
+static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *p;
+    pa_card *card;
+    pa_card_profile *profile;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a card either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(p = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
+        return -1;
+    }
+
+    if (!(card = pa_namereg_get(c, n, PA_NAMEREG_CARD))) {
+        pa_strbuf_puts(buf, "No card found by this name or index.\n");
+        return -1;
+    }
+
+    if (!(profile = pa_hashmap_get(card->profiles, p))) {
+        pa_strbuf_printf(buf, "No such profile: %s\n", p);
+        return -1;
+    }
+
+    if (pa_card_set_profile(card, profile, true) < 0) {
+        pa_strbuf_printf(buf, "Failed to set card profile to '%s'.\n", p);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *p;
+    pa_sink *sink;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(p = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a profile by its name.\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;
+    }
+
+    if (pa_sink_set_port(sink, p, true) < 0) {
+        pa_strbuf_printf(buf, "Failed to set sink port to '%s'.\n", p);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *p;
+    pa_source *source;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(p = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a profile by its name.\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;
+    }
+
+    if (pa_source_set_port(source, p, true) < 0) {
+        pa_strbuf_printf(buf, "Failed to set source port to '%s'.\n", p);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_port_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *n, *p, *l;
+    pa_device_port *port;
+    pa_card *card;
+    int32_t offset;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a card either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(p = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a port by its name.\n");
+        return -1;
+    }
+
+    if (!(l = pa_tokenizer_get(t, 3))) {
+        pa_strbuf_puts(buf, "You need to specify a latency offset.\n");
+        return -1;
+    }
+
+    if (pa_atoi(l, &offset) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse the latency offset.\n");
+        return -1;
+    }
+
+    if (!(card = pa_namereg_get(c, n, PA_NAMEREG_CARD))) {
+        pa_strbuf_puts(buf, "No card found by this name or index.\n");
+        return -1;
+    }
+
+    if (!(port = pa_hashmap_get(card->ports, p))) {
+        pa_strbuf_puts(buf, "No port found by this name.\n");
+        return -1;
+    }
+
+    pa_device_port_set_latency_offset(port, offset);
+
+    return 0;
+}
+
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    pa_module *m;
+    pa_sink *sink;
+    pa_source *source;
+    pa_card *card;
+    bool nl;
+    uint32_t idx;
+    time_t now;
+#ifdef HAVE_CTIME_R
+    char txt[256];
+#endif
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    time(&now);
+
+#ifdef HAVE_CTIME_R
+    pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt));
+#else
+    pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
+#endif
+
+    PA_IDXSET_FOREACH(m, c->modules, idx) {
+
+        pa_strbuf_printf(buf, "load-module %s", m->name);
+
+        if (m->argument)
+            pa_strbuf_printf(buf, " %s", m->argument);
+
+        pa_strbuf_puts(buf, "\n");
+    }
+
+    nl = false;
+    PA_IDXSET_FOREACH(sink, c->sinks, idx) {
+
+        if (!nl) {
+            pa_strbuf_puts(buf, "\n");
+            nl = true;
+        }
+
+        pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_max(pa_sink_get_volume(sink, false)));
+        pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, false)));
+        pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
+    }
+
+    nl = false;
+    PA_IDXSET_FOREACH(source, c->sources, idx) {
+
+        if (!nl) {
+            pa_strbuf_puts(buf, "\n");
+            nl = true;
+        }
+
+        pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_max(pa_source_get_volume(source, false)));
+        pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source, false)));
+        pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
+    }
+
+    nl = false;
+    PA_IDXSET_FOREACH(card, c->cards, idx) {
+
+        if (!nl) {
+            pa_strbuf_puts(buf, "\n");
+            nl = true;
+        }
+
+        pa_strbuf_printf(buf, "set-card-profile %s %s\n", card->name, card->active_profile->name);
+    }
+
+    nl = false;
+    if (c->default_sink) {
+        if (!nl) {
+            pa_strbuf_puts(buf, "\n");
+            nl = true;
+        }
+
+        pa_strbuf_printf(buf, "set-default-sink %s\n", c->default_sink->name);
+    }
+
+    if (c->default_source) {
+        if (!nl)
+            pa_strbuf_puts(buf, "\n");
+
+        pa_strbuf_printf(buf, "set-default-source %s\n", c->default_source->name);
+    }
+
+    pa_strbuf_puts(buf, "\n### EOF\n");
+
+    return 0;
+}
+
+static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    pa_sink *s;
+    pa_source *so;
+    pa_sink_input *i;
+    pa_source_output *o;
+    uint32_t s_idx, i_idx;
+    char v_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    pa_channel_map *map;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    PA_IDXSET_FOREACH(s, c->sinks, s_idx) {
+        map = &s->channel_map;
+        pa_strbuf_printf(buf, "Sink %d: ", s_idx);
+        pa_strbuf_printf(buf,
+                         "reference = %s, ",
+                         pa_cvolume_snprint_verbose(v_str,
+                                                    sizeof(v_str),
+                                                    &s->reference_volume,
+                                                    map,
+                                                    s->flags & PA_SINK_DECIBEL_VOLUME));
+        pa_strbuf_printf(buf,
+                         "real = %s, ",
+                         pa_cvolume_snprint_verbose(v_str,
+                                                    sizeof(v_str),
+                                                    &s->real_volume,
+                                                    &s->channel_map,
+                                                    s->flags & PA_SINK_DECIBEL_VOLUME));
+        pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &s->soft_volume, map, true));
+        pa_strbuf_printf(buf,
+                         "current_hw = %s, ",
+                         pa_cvolume_snprint_verbose(v_str,
+                                                    sizeof(v_str),
+                                                    &s->thread_info.current_hw_volume,
+                                                    map,
+                                                    s->flags & PA_SINK_DECIBEL_VOLUME));
+        pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(s->save_volume));
+
+        PA_IDXSET_FOREACH(i, s->inputs, i_idx) {
+            map = &i->channel_map;
+            pa_strbuf_printf(buf, "\tInput %d: ", i_idx);
+            pa_strbuf_printf(buf, "volume = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &i->volume, map, true));
+            pa_strbuf_printf(buf,
+                             "reference_ratio = %s, ",
+                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &i->reference_ratio, map, true));
+            pa_strbuf_printf(buf,
+                             "real_ratio = %s, ",
+                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &i->real_ratio, map, true));
+            pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &i->soft_volume, map, true));
+            pa_strbuf_printf(buf,
+                             "volume_factor = %s, ",
+                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &i->volume_factor, map, true));
+            pa_strbuf_printf(buf,
+                             "volume_factor_sink = %s, ",
+                             pa_cvolume_snprint_verbose(v_str,
+                                                        sizeof(v_str),
+                                                        &i->volume_factor_sink,
+                                                        &i->sink->channel_map,
+                                                        true));
+            pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(i->save_volume));
+        }
+    }
+
+    PA_IDXSET_FOREACH(so, c->sources, s_idx) {
+        map = &so->channel_map;
+        pa_strbuf_printf(buf, "Source %d: ", s_idx);
+        pa_strbuf_printf(buf,
+                         "reference = %s, ",
+                         pa_cvolume_snprint_verbose(v_str,
+                                                    sizeof(v_str),
+                                                    &so->reference_volume,
+                                                    map,
+                                                    so->flags & PA_SOURCE_DECIBEL_VOLUME));
+        pa_strbuf_printf(buf,
+                         "real = %s, ",
+                         pa_cvolume_snprint_verbose(v_str,
+                                                    sizeof(v_str),
+                                                    &so->real_volume,
+                                                    map,
+                                                    so->flags & PA_SOURCE_DECIBEL_VOLUME));
+        pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &so->soft_volume, map, true));
+        pa_strbuf_printf(buf,
+                         "current_hw = %s, ",
+                         pa_cvolume_snprint_verbose(v_str,
+                                                    sizeof(v_str),
+                                                    &so->thread_info.current_hw_volume,
+                                                    map,
+                                                    so->flags & PA_SOURCE_DECIBEL_VOLUME));
+        pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(so->save_volume));
+
+        PA_IDXSET_FOREACH(o, so->outputs, i_idx) {
+            map = &o->channel_map;
+            pa_strbuf_printf(buf, "\tOutput %d: ", i_idx);
+            pa_strbuf_printf(buf, "volume = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &o->volume, map, true));
+            pa_strbuf_printf(buf,
+                             "reference_ratio = %s, ",
+                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &o->reference_ratio, map, true));
+            pa_strbuf_printf(buf,
+                             "real_ratio = %s, ",
+                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &o->real_ratio, map, true));
+            pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &o->soft_volume, map, true));
+            pa_strbuf_printf(buf,
+                             "volume_factor = %s, ",
+                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &o->volume_factor, map, true));
+            pa_strbuf_printf(buf,
+                             "volume_factor_source = %s, ",
+                             pa_cvolume_snprint_verbose(v_str,
+                                                        sizeof(v_str),
+                                                        &o->volume_factor_source,
+                                                        &o->source->channel_map,
+                                                        true));
+            pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(o->save_volume));
+        }
+    }
+
+    return 0;
+}
+
+int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, bool *fail, int *ifstate) {
+    const char *cs;
+
+    pa_assert(c);
+    pa_assert(s);
+    pa_assert(buf);
+
+    cs = s+strspn(s, whitespace);
+
+    if (*cs == '#' || !*cs)
+        return 0;
+    else if (*cs == '.') {
+        if (!strcmp(cs, META_ELSE)) {
+            if (!ifstate || *ifstate == IFSTATE_NONE) {
+                pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
+                return -1;
+            } else if (*ifstate == IFSTATE_TRUE)
+                *ifstate = IFSTATE_FALSE;
+            else
+                *ifstate = IFSTATE_TRUE;
+            return 0;
+        } else if (!strcmp(cs, META_ENDIF)) {
+            if (!ifstate || *ifstate == IFSTATE_NONE) {
+                pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
+                return -1;
+            } else
+                *ifstate = IFSTATE_NONE;
+            return 0;
+        }
+        if (ifstate && *ifstate == IFSTATE_FALSE)
+            return 0;
+        if (!strcmp(cs, META_FAIL))
+            *fail = true;
+        else if (!strcmp(cs, META_NOFAIL))
+            *fail = false;
+        else {
+            size_t l;
+            l = strcspn(cs, whitespace);
+
+            if (l == sizeof(META_INCLUDE)-1 && !strncmp(cs, META_INCLUDE, l)) {
+                struct stat st;
+                const char *filename = cs+l+strspn(cs+l, whitespace);
+
+                if (stat(filename, &st) < 0) {
+                    pa_log_warn("stat('%s'): %s", filename, pa_cstrerror(errno));
+                    if (*fail)
+                        return -1;
+                } else {
+                    if (S_ISDIR(st.st_mode)) {
+                        DIR *d;
+
+                        if (!(d = opendir(filename))) {
+                            pa_log_warn("Failed to read '%s': %s", filename, pa_cstrerror(errno));
+                            if (*fail)
+                                return -1;
+                        } else {
+                            unsigned i, count;
+                            char **sorted_files;
+                            struct dirent *de;
+                            bool failed = false;
+                            pa_dynarray *files = pa_dynarray_new(NULL);
+
+                            while ((de = readdir(d))) {
+                                char *extn;
+                                size_t flen = strlen(de->d_name);
+
+                                if (flen < 4)
+                                    continue;
+
+                                extn = &de->d_name[flen-3];
+                                if (strncmp(extn, ".pa", 3) == 0)
+                                    pa_dynarray_append(files, pa_sprintf_malloc("%s" PA_PATH_SEP "%s", filename, de->d_name));
+                            }
+
+                            closedir(d);
+
+                            count = pa_dynarray_size(files);
+                            sorted_files = pa_xnew(char*, count);
+                            for (i = 0; i < count; ++i)
+                                sorted_files[i] = pa_dynarray_get(files, i);
+                            pa_dynarray_free(files);
+
+                            for (i = 0; i < count; ++i) {
+                                for (unsigned j = 0; j < count; ++j) {
+                                    if (strcmp(sorted_files[i], sorted_files[j]) < 0) {
+                                        char *tmp = sorted_files[i];
+                                        sorted_files[i] = sorted_files[j];
+                                        sorted_files[j] = tmp;
+                                    }
+                                }
+                            }
+
+                            for (i = 0; i < count; ++i) {
+                                if (!failed) {
+                                    if (pa_cli_command_execute_file(c, sorted_files[i], buf, fail) < 0 && *fail)
+                                        failed = true;
+                                }
+
+                                pa_xfree(sorted_files[i]);
+                            }
+                            pa_xfree(sorted_files);
+                            if (failed)
+                                return -1;
+                        }
+                    } else if (pa_cli_command_execute_file(c, filename, buf, fail) < 0 && *fail) {
+                        return -1;
+                    }
+                }
+            } else if (l == sizeof(META_IFEXISTS)-1 && !strncmp(cs, META_IFEXISTS, l)) {
+                if (!ifstate) {
+                    pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
+                    return -1;
+                } else if (*ifstate != IFSTATE_NONE) {
+                    pa_strbuf_printf(buf, "Nested %s commands not supported\n", cs);
+                    return -1;
+                } else {
+                    const char *filename = cs+l+strspn(cs+l, whitespace);
+                    *ifstate = pa_module_exists(filename) ? IFSTATE_TRUE : IFSTATE_FALSE;
+                }
+            } else {
+                pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
+                if (*fail) return -1;
+            }
+        }
+    } else {
+        const struct command*command;
+        int unknown = 1;
+        size_t l;
+
+        if (ifstate && *ifstate == IFSTATE_FALSE)
+            return 0;
+
+        l = strcspn(cs, whitespace);
+
+        for (command = commands; command->name; command++)
+            if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
+                int ret;
+                pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
+                pa_assert(t);
+                ret = command->proc(c, t, buf, fail);
+                pa_tokenizer_free(t);
+                unknown = 0;
+
+                if (ret < 0 && *fail)
+                    return -1;
+
+                break;
+            }
+
+        if (unknown) {
+            pa_strbuf_printf(buf, "Unknown command: %s\n", cs);
+            if (*fail)
+                return -1;
+        }
+    }
+
+    return 0;
+}
+
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, bool *fail) {
+    return pa_cli_command_execute_line_stateful(c, s, buf, fail, NULL);
+}
+
+int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, bool *fail) {
+    char line[2048];
+    int ifstate = IFSTATE_NONE;
+    int ret = -1;
+    bool _fail = true;
+
+    pa_assert(c);
+    pa_assert(f);
+    pa_assert(buf);
+
+    if (!fail)
+        fail = &_fail;
+
+    while (fgets(line, sizeof(line), f)) {
+        pa_strip_nl(line);
+
+        if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
+            goto fail;
+    }
+
+    ret = 0;
+
+fail:
+
+    return ret;
+}
+
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, bool *fail) {
+    FILE *f = NULL;
+    int ret = -1;
+    bool _fail = true;
+
+    pa_assert(c);
+    pa_assert(fn);
+    pa_assert(buf);
+
+    if (!fail)
+        fail = &_fail;
+
+    if (!(f = pa_fopen_cloexec(fn, "r"))) {
+        pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
+        if (!*fail)
+            ret = 0;
+        goto fail;
+    }
+
+    pa_log_debug("Parsing script '%s'", fn);
+    ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
+
+fail:
+    if (f)
+        fclose(f);
+
+    return ret;
+}
+
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, bool *fail) {
+    const char *p;
+    int ifstate = IFSTATE_NONE;
+    bool _fail = true;
+
+    pa_assert(c);
+    pa_assert(s);
+    pa_assert(buf);
+
+    if (!fail)
+        fail = &_fail;
+
+    p = s;
+    while (*p) {
+        size_t l = strcspn(p, linebreak);
+        char *line = pa_xstrndup(p, l);
+
+        if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail) {
+            pa_xfree(line);
+            return -1;
+        }
+        pa_xfree(line);
+
+        p += l;
+        p += strspn(p, linebreak);
+    }
+
+    return 0;
+}
diff --git a/src/pulsecore/cli-command.h b/src/pulsecore/cli-command.h
new file mode 100644 (file)
index 0000000..3513341
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef fooclicommandhfoo
+#define fooclicommandhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core.h>
+
+/* Execute a single CLI command. Write the results to the string
+ * buffer *buf. If *fail is non-zero the function will return -1 when
+ * one or more of the executed commands failed. *fail
+ * may be modified by the function call. */
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, bool *fail);
+
+/* Execute a whole file of CLI commands */
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, bool *fail);
+
+/* Execute a whole file of CLI commands */
+int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, bool *fail);
+
+/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, bool *fail);
+
+/* Same as pa_cli_command_execute_line() but also take ifstate var. */
+int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, bool *fail, int *ifstate);
+
+#endif
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
new file mode 100644 (file)
index 0000000..ded82f6
--- /dev/null
@@ -0,0 +1,755 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/client.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/namereg.h>
+
+#include "cli-text.h"
+
+char *pa_module_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_module *m;
+    uint32_t idx = PA_IDXSET_INVALID;
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules));
+
+    PA_IDXSET_FOREACH(m, c->modules, idx) {
+        char *t;
+
+        pa_strbuf_printf(s, "    index: %u\n"
+                         "\tname: <%s>\n"
+                         "\targument: <%s>\n"
+                         "\tused: %i\n"
+                         "\tload once: %s\n",
+                         m->index,
+                         m->name,
+                         pa_strempty(m->argument),
+                         pa_module_get_n_used(m),
+                         pa_yes_no(m->load_once));
+
+        t = pa_proplist_to_string_sep(m->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_to_string_free(s);
+}
+
+char *pa_client_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_client *client;
+    uint32_t idx = PA_IDXSET_INVALID;
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients));
+
+    PA_IDXSET_FOREACH(client, c->clients, idx) {
+        char *t;
+        pa_strbuf_printf(
+                s,
+                "    index: %u\n"
+                "\tdriver: <%s>\n",
+                client->index,
+                client->driver);
+
+        if (client->module)
+            pa_strbuf_printf(s, "\towner module: %u\n", client->module->index);
+
+        t = pa_proplist_to_string_sep(client->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_to_string_free(s);
+}
+
+static const char *available_to_string(pa_available_t a) {
+    switch (a) {
+        case PA_AVAILABLE_UNKNOWN:
+            return "unknown";
+        case PA_AVAILABLE_NO:
+            return "no";
+        case PA_AVAILABLE_YES:
+            return "yes";
+        default:
+            return "invalid"; /* Should never happen! */
+    }
+}
+
+static void append_port_list(pa_strbuf *s, pa_hashmap *ports) {
+    pa_device_port *p;
+    void *state;
+
+    pa_assert(ports);
+
+    if (pa_hashmap_isempty(ports))
+        return;
+
+    pa_strbuf_puts(s, "\tports:\n");
+    PA_HASHMAP_FOREACH(p, ports, state) {
+        char *t = pa_proplist_to_string_sep(p->proplist, "\n\t\t\t\t");
+        pa_strbuf_printf(s, "\t\t%s: %s (priority %u, latency offset %" PRId64 " usec, available: %s)\n",
+            p->name, p->description, p->priority, p->latency_offset,
+            available_to_string(p->available));
+        pa_strbuf_printf(s, "\t\t\tproperties:\n\t\t\t\t%s\n", t);
+        pa_xfree(t);
+    }
+}
+
+char *pa_card_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_card *card;
+    uint32_t idx = PA_IDXSET_INVALID;
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u card(s) available.\n", pa_idxset_size(c->cards));
+
+    PA_IDXSET_FOREACH(card, c->cards, idx) {
+        char *t;
+        pa_sink *sink;
+        pa_source *source;
+        uint32_t sidx;
+        pa_card_profile *profile;
+        void *state;
+
+        pa_strbuf_printf(
+                s,
+                "    index: %u\n"
+                "\tname: <%s>\n"
+                "\tdriver: <%s>\n",
+                card->index,
+                card->name,
+                card->driver);
+
+        if (card->module)
+            pa_strbuf_printf(s, "\towner module: %u\n", card->module->index);
+
+        t = pa_proplist_to_string_sep(card->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
+        pa_xfree(t);
+
+        pa_strbuf_puts(s, "\tprofiles:\n");
+        PA_HASHMAP_FOREACH(profile, card->profiles, state)
+            pa_strbuf_printf(s, "\t\t%s: %s (priority %u, available: %s)\n", profile->name, profile->description,
+                             profile->priority, available_to_string(profile->available));
+
+        pa_strbuf_printf(
+                s,
+                "\tactive profile: <%s>\n",
+                card->active_profile->name);
+
+        if (!pa_idxset_isempty(card->sinks)) {
+            pa_strbuf_puts(s, "\tsinks:\n");
+            PA_IDXSET_FOREACH(sink, card->sinks, sidx)
+                pa_strbuf_printf(s, "\t\t%s/#%u: %s\n", sink->name, sink->index, pa_strna(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+        }
+
+        if (!pa_idxset_isempty(card->sources)) {
+            pa_strbuf_puts(s, "\tsources:\n");
+            PA_IDXSET_FOREACH(source, card->sources, sidx)
+                pa_strbuf_printf(s, "\t\t%s/#%u: %s\n", source->name, source->index, pa_strna(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+        }
+
+        append_port_list(s, card->ports);
+    }
+
+    return pa_strbuf_to_string_free(s);
+}
+
+static const char *sink_state_to_string(pa_sink_state_t state) {
+    switch (state) {
+        case PA_SINK_INIT:
+            return "INIT";
+        case PA_SINK_RUNNING:
+            return "RUNNING";
+        case PA_SINK_SUSPENDED:
+            return "SUSPENDED";
+        case PA_SINK_IDLE:
+            return "IDLE";
+        case PA_SINK_UNLINKED:
+            return "UNLINKED";
+        default:
+            return "INVALID";
+    }
+}
+
+static const char *source_state_to_string(pa_source_state_t state) {
+    switch (state) {
+        case PA_SOURCE_INIT:
+            return "INIT";
+        case PA_SOURCE_RUNNING:
+            return "RUNNING";
+        case PA_SOURCE_SUSPENDED:
+            return "SUSPENDED";
+        case PA_SOURCE_IDLE:
+            return "IDLE";
+        case PA_SOURCE_UNLINKED:
+            return "UNLINKED";
+        default:
+            return "INVALID";
+    }
+}
+
+char *pa_sink_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_sink *sink;
+    uint32_t idx = PA_IDXSET_INVALID;
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));
+
+    PA_IDXSET_FOREACH(sink, c->sinks, idx) {
+        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX],
+            cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX],
+            v[PA_VOLUME_SNPRINT_VERBOSE_MAX],
+            cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
+        const char *cmn;
+
+        cmn = pa_channel_map_to_pretty_name(&sink->channel_map);
+
+        pa_strbuf_printf(
+            s,
+            "  %c index: %u\n"
+            "\tname: <%s>\n"
+            "\tdriver: <%s>\n"
+            "\tflags: %s%s%s%s%s%s%s%s\n"
+            "\tstate: %s\n"
+            "\tsuspend cause: %s%s%s%s\n"
+            "\tpriority: %u\n"
+            "\tvolume: %s\n"
+            "\t        balance %0.2f\n"
+            "\tbase volume: %s\n"
+            "\tvolume steps: %u\n"
+            "\tmuted: %s\n"
+            "\tcurrent latency: %0.2f ms\n"
+            "\tmax request: %lu KiB\n"
+            "\tmax rewind: %lu KiB\n"
+            "\tmonitor source: %u\n"
+            "\tsample spec: %s\n"
+            "\tchannel map: %s%s%s\n"
+            "\tused by: %u\n"
+            "\tlinked by: %u\n",
+            sink == c->default_sink ? '*' : ' ',
+            sink->index,
+            sink->name,
+            sink->driver,
+            sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
+            sink->flags & PA_SINK_NETWORK ? "NETWORK " : "",
+            sink->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
+            sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+            sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
+            sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
+            sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME " : "",
+            sink->flags & PA_SINK_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "",
+            sink_state_to_string(pa_sink_get_state(sink)),
+            sink->suspend_cause & PA_SUSPEND_USER ? "USER " : "",
+            sink->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "",
+            sink->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "",
+            sink->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "",
+            sink->priority,
+            pa_cvolume_snprint_verbose(cv,
+                                       sizeof(cv),
+                                       pa_sink_get_volume(sink, false),
+                                       &sink->channel_map,
+                                       sink->flags & PA_SINK_DECIBEL_VOLUME),
+            pa_cvolume_get_balance(pa_sink_get_volume(sink, false), &sink->channel_map),
+            pa_volume_snprint_verbose(v, sizeof(v), sink->base_volume, sink->flags & PA_SINK_DECIBEL_VOLUME),
+            sink->n_volume_steps,
+            pa_yes_no(pa_sink_get_mute(sink, false)),
+            (double) pa_sink_get_latency(sink) / (double) PA_USEC_PER_MSEC,
+            (unsigned long) pa_sink_get_max_request(sink) / 1024,
+            (unsigned long) pa_sink_get_max_rewind(sink) / 1024,
+            sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
+            pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
+            pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
+            cmn ? "\n\t             " : "",
+            cmn ? cmn : "",
+            pa_sink_used_by(sink),
+            pa_sink_linked_by(sink));
+
+        if (sink->flags & PA_SINK_DYNAMIC_LATENCY) {
+            pa_usec_t min_latency, max_latency;
+            pa_sink_get_latency_range(sink, &min_latency, &max_latency);
+
+            pa_strbuf_printf(
+                    s,
+                    "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n",
+                    (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC,
+                    (double) min_latency / PA_USEC_PER_MSEC,
+                    (double) max_latency / PA_USEC_PER_MSEC);
+        } else
+            pa_strbuf_printf(
+                    s,
+                    "\tfixed latency: %0.2f ms\n",
+                    (double) pa_sink_get_fixed_latency(sink) / PA_USEC_PER_MSEC);
+
+        if (sink->card)
+            pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name);
+        if (sink->module)
+            pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index);
+
+        t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
+        pa_xfree(t);
+
+        append_port_list(s, sink->ports);
+
+        if (sink->active_port)
+            pa_strbuf_printf(
+                    s,
+                    "\tactive port: <%s>\n",
+                    sink->active_port->name);
+    }
+
+    return pa_strbuf_to_string_free(s);
+}
+
+char *pa_source_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_source *source;
+    uint32_t idx = PA_IDXSET_INVALID;
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources));
+
+    PA_IDXSET_FOREACH(source, c->sources, idx) {
+        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX],
+            cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX],
+            v[PA_VOLUME_SNPRINT_VERBOSE_MAX],
+            cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
+        const char *cmn;
+
+        cmn = pa_channel_map_to_pretty_name(&source->channel_map);
+
+        pa_strbuf_printf(
+            s,
+            "  %c index: %u\n"
+            "\tname: <%s>\n"
+            "\tdriver: <%s>\n"
+            "\tflags: %s%s%s%s%s%s%s\n"
+            "\tstate: %s\n"
+            "\tsuspend cause: %s%s%s%s\n"
+            "\tpriority: %u\n"
+            "\tvolume: %s\n"
+            "\t        balance %0.2f\n"
+            "\tbase volume: %s\n"
+            "\tvolume steps: %u\n"
+            "\tmuted: %s\n"
+            "\tcurrent latency: %0.2f ms\n"
+            "\tmax rewind: %lu KiB\n"
+            "\tsample spec: %s\n"
+            "\tchannel map: %s%s%s\n"
+            "\tused by: %u\n"
+            "\tlinked by: %u\n",
+            source == c->default_source ? '*' : ' ',
+            source->index,
+            source->name,
+            source->driver,
+            source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
+            source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
+            source->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
+            source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+            source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
+            source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
+            source->flags & PA_SOURCE_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "",
+            source_state_to_string(pa_source_get_state(source)),
+            source->suspend_cause & PA_SUSPEND_USER ? "USER " : "",
+            source->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "",
+            source->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "",
+            source->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "",
+            source->priority,
+            pa_cvolume_snprint_verbose(cv,
+                                       sizeof(cv),
+                                       pa_source_get_volume(source, false),
+                                       &source->channel_map,
+                                       source->flags & PA_SOURCE_DECIBEL_VOLUME),
+            pa_cvolume_get_balance(pa_source_get_volume(source, false), &source->channel_map),
+            pa_volume_snprint_verbose(v, sizeof(v), source->base_volume, source->flags & PA_SOURCE_DECIBEL_VOLUME),
+            source->n_volume_steps,
+            pa_yes_no(pa_source_get_mute(source, false)),
+            (double) pa_source_get_latency(source) / PA_USEC_PER_MSEC,
+            (unsigned long) pa_source_get_max_rewind(source) / 1024,
+            pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
+            pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
+            cmn ? "\n\t             " : "",
+            cmn ? cmn : "",
+            pa_source_used_by(source),
+            pa_source_linked_by(source));
+
+        if (source->flags & PA_SOURCE_DYNAMIC_LATENCY) {
+            pa_usec_t min_latency, max_latency;
+            pa_source_get_latency_range(source, &min_latency, &max_latency);
+
+            pa_strbuf_printf(
+                    s,
+                    "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n",
+                    (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC,
+                    (double) min_latency / PA_USEC_PER_MSEC,
+                    (double) max_latency / PA_USEC_PER_MSEC);
+        } else
+            pa_strbuf_printf(
+                    s,
+                    "\tfixed latency: %0.2f ms\n",
+                    (double) pa_source_get_fixed_latency(source) / PA_USEC_PER_MSEC);
+
+        if (source->monitor_of)
+            pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
+        if (source->card)
+            pa_strbuf_printf(s, "\tcard: %u <%s>\n", source->card->index, source->card->name);
+        if (source->module)
+            pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index);
+
+        t = pa_proplist_to_string_sep(source->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
+        pa_xfree(t);
+
+        append_port_list(s, source->ports);
+
+        if (source->active_port)
+            pa_strbuf_printf(
+                    s,
+                    "\tactive port: <%s>\n",
+                    source->active_port->name);
+    }
+
+    return pa_strbuf_to_string_free(s);
+}
+
+char *pa_source_output_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_source_output *o;
+    uint32_t idx = PA_IDXSET_INVALID;
+    static const char* const state_table[] = {
+        [PA_SOURCE_OUTPUT_INIT] = "INIT",
+        [PA_SOURCE_OUTPUT_RUNNING] = "RUNNING",
+        [PA_SOURCE_OUTPUT_CORKED] = "CORKED",
+        [PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED"
+    };
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u source output(s) available.\n", pa_idxset_size(c->source_outputs));
+
+    PA_IDXSET_FOREACH(o, c->source_outputs, idx) {
+        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+        pa_usec_t cl;
+        const char *cmn;
+        pa_cvolume v;
+        char *volume_str = NULL;
+
+        cmn = pa_channel_map_to_pretty_name(&o->channel_map);
+
+        if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1)
+            pa_snprintf(clt, sizeof(clt), "n/a");
+        else
+            pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
+
+        pa_assert(o->source);
+
+        if (pa_source_output_is_volume_readable(o)) {
+            pa_source_output_get_volume(o, &v, true);
+            volume_str = pa_sprintf_malloc("%s\n\t        balance %0.2f",
+                                           pa_cvolume_snprint_verbose(cv, sizeof(cv), &v, &o->channel_map, true),
+                                           pa_cvolume_get_balance(&v, &o->channel_map));
+        } else
+            volume_str = pa_xstrdup("n/a");
+
+        pa_strbuf_printf(
+            s,
+            "    index: %u\n"
+            "\tdriver: <%s>\n"
+            "\tflags: %s%s%s%s%s%s%s%s%s%s%s%s\n"
+            "\tstate: %s\n"
+            "\tsource: %u <%s>\n"
+            "\tvolume: %s\n"
+            "\tmuted: %s\n"
+            "\tcurrent latency: %0.2f ms\n"
+            "\trequested latency: %s\n"
+            "\tsample spec: %s\n"
+            "\tchannel map: %s%s%s\n"
+            "\tresample method: %s\n",
+            o->index,
+            o->driver,
+            o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
+            o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "",
+            o->flags & PA_SOURCE_OUTPUT_START_CORKED ? "START_CORKED " : "",
+            o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "",
+            o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "",
+            o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+            o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "",
+            o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
+            o->flags & PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND ? "DONT_INHIBIT_AUTO_SUSPEND " : "",
+            o->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND ? "NO_CREATE_ON_SUSPEND " : "",
+            o->flags & PA_SOURCE_OUTPUT_KILL_ON_SUSPEND ? "KILL_ON_SUSPEND " : "",
+            o->flags & PA_SOURCE_OUTPUT_PASSTHROUGH ? "PASSTHROUGH " : "",
+            state_table[pa_source_output_get_state(o)],
+            o->source->index, o->source->name,
+            volume_str,
+            pa_yes_no(o->muted),
+            (double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
+            clt,
+            pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
+            pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
+            cmn ? "\n\t             " : "",
+            cmn ? cmn : "",
+            pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
+
+        pa_xfree(volume_str);
+
+        if (o->module)
+            pa_strbuf_printf(s, "\towner module: %u\n", o->module->index);
+        if (o->client)
+            pa_strbuf_printf(s, "\tclient: %u <%s>\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME)));
+        if (o->direct_on_input)
+            pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index);
+
+        t = pa_proplist_to_string_sep(o->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_to_string_free(s);
+}
+
+char *pa_sink_input_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_sink_input *i;
+    uint32_t idx = PA_IDXSET_INVALID;
+    static const char* const state_table[] = {
+        [PA_SINK_INPUT_INIT] = "INIT",
+        [PA_SINK_INPUT_RUNNING] = "RUNNING",
+        [PA_SINK_INPUT_DRAINED] = "DRAINED",
+        [PA_SINK_INPUT_CORKED] = "CORKED",
+        [PA_SINK_INPUT_UNLINKED] = "UNLINKED"
+    };
+
+    pa_assert(c);
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));
+
+    PA_IDXSET_FOREACH(i, c->sink_inputs, idx) {
+        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+        pa_usec_t cl;
+        const char *cmn;
+        pa_cvolume v;
+        char *volume_str = NULL;
+
+        cmn = pa_channel_map_to_pretty_name(&i->channel_map);
+
+        if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1)
+            pa_snprintf(clt, sizeof(clt), "n/a");
+        else
+            pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
+
+        pa_assert(i->sink);
+
+        if (pa_sink_input_is_volume_readable(i)) {
+            pa_sink_input_get_volume(i, &v, true);
+            volume_str = pa_sprintf_malloc("%s\n\t        balance %0.2f",
+                                           pa_cvolume_snprint_verbose(cv, sizeof(cv), &v, &i->channel_map, true),
+                                           pa_cvolume_get_balance(&v, &i->channel_map));
+        } else
+            volume_str = pa_xstrdup("n/a");
+
+        pa_strbuf_printf(
+            s,
+            "    index: %u\n"
+            "\tdriver: <%s>\n"
+            "\tflags: %s%s%s%s%s%s%s%s%s%s%s%s\n"
+            "\tstate: %s\n"
+            "\tsink: %u <%s>\n"
+            "\tvolume: %s\n"
+            "\tmuted: %s\n"
+            "\tcurrent latency: %0.2f ms\n"
+            "\trequested latency: %s\n"
+            "\tsample spec: %s\n"
+            "\tchannel map: %s%s%s\n"
+            "\tresample method: %s\n",
+            i->index,
+            i->driver,
+            i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
+            i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "",
+            i->flags & PA_SINK_INPUT_START_CORKED ? "START_CORKED " : "",
+            i->flags & PA_SINK_INPUT_NO_REMAP ? "NO_REMAP " : "",
+            i->flags & PA_SINK_INPUT_NO_REMIX ? "NO_REMIX " : "",
+            i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+            i->flags & PA_SINK_INPUT_FIX_RATE ? "FIX_RATE " : "",
+            i->flags & PA_SINK_INPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
+            i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND ? "DONT_INHIBIT_AUTO_SUSPEND " : "",
+            i->flags & PA_SINK_INPUT_NO_CREATE_ON_SUSPEND ? "NO_CREATE_SUSPEND " : "",
+            i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND ? "KILL_ON_SUSPEND " : "",
+            i->flags & PA_SINK_INPUT_PASSTHROUGH ? "PASSTHROUGH " : "",
+            state_table[pa_sink_input_get_state(i)],
+            i->sink->index, i->sink->name,
+            volume_str,
+            pa_yes_no(i->muted),
+            (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
+            clt,
+            pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
+            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+            cmn ? "\n\t             " : "",
+            cmn ? cmn : "",
+            pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
+
+        pa_xfree(volume_str);
+
+        if (i->module)
+            pa_strbuf_printf(s, "\tmodule: %u\n", i->module->index);
+        if (i->client)
+            pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME)));
+
+        t = pa_proplist_to_string_sep(i->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_to_string_free(s);
+}
+
+char *pa_scache_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u cache entrie(s) available.\n", c->scache ? pa_idxset_size(c->scache) : 0);
+
+    if (c->scache) {
+        pa_scache_entry *e;
+        uint32_t idx = PA_IDXSET_INVALID;
+
+        PA_IDXSET_FOREACH(e, c->scache, idx) {
+            double l = 0;
+            char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
+            const char *cmn;
+
+            cmn = pa_channel_map_to_pretty_name(&e->channel_map);
+
+            if (e->memchunk.memblock) {
+                pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
+                pa_channel_map_snprint(cm, sizeof(cm), &e->channel_map);
+                l = (double) e->memchunk.length / (double) pa_bytes_per_second(&e->sample_spec);
+            }
+
+            pa_strbuf_printf(
+                s,
+                "    name: <%s>\n"
+                "\tindex: %u\n"
+                "\tsample spec: %s\n"
+                "\tchannel map: %s%s%s\n"
+                "\tlength: %lu\n"
+                "\tduration: %0.1f s\n"
+                "\tvolume: %s\n"
+                "\t        balance %0.2f\n"
+                "\tlazy: %s\n"
+                "\tfilename: <%s>\n",
+                e->name,
+                e->index,
+                ss,
+                cm,
+                cmn ? "\n\t             " : "",
+                cmn ? cmn : "",
+                (long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
+                l,
+                e->volume_is_set ? pa_cvolume_snprint_verbose(cv, sizeof(cv), &e->volume, &e->channel_map, true) : "n/a",
+                (e->memchunk.memblock && e->volume_is_set) ? pa_cvolume_get_balance(&e->volume, &e->channel_map) : 0.0f,
+                pa_yes_no(e->lazy),
+                e->filename ? e->filename : "n/a");
+
+            t = pa_proplist_to_string_sep(e->proplist, "\n\t\t");
+            pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
+            pa_xfree(t);
+        }
+    }
+
+    return pa_strbuf_to_string_free(s);
+}
+
+char *pa_full_status_string(pa_core *c) {
+    pa_strbuf *s;
+    int i;
+
+    s = pa_strbuf_new();
+
+    for (i = 0; i < 8; i++) {
+        char *t = NULL;
+
+        switch (i) {
+            case 0:
+                t = pa_sink_list_to_string(c);
+                break;
+            case 1:
+                t = pa_source_list_to_string(c);
+                break;
+            case 2:
+                t = pa_sink_input_list_to_string(c);
+                break;
+            case 3:
+                t = pa_source_output_list_to_string(c);
+                break;
+            case 4:
+                t = pa_client_list_to_string(c);
+                break;
+            case 5:
+                t = pa_card_list_to_string(c);
+                break;
+            case 6:
+                t = pa_module_list_to_string(c);
+                break;
+            case 7:
+                t = pa_scache_list_to_string(c);
+                break;
+        }
+
+        pa_strbuf_puts(s, t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_to_string_free(s);
+}
diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h
new file mode 100644 (file)
index 0000000..b89be50
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef fooclitexthfoo
+#define fooclitexthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+
+/* Some functions to generate pretty formatted listings of
+ * entities. The returned strings have to be freed manually. */
+
+char *pa_sink_input_list_to_string(pa_core *c);
+char *pa_source_output_list_to_string(pa_core *c);
+char *pa_sink_list_to_string(pa_core *core);
+char *pa_source_list_to_string(pa_core *c);
+char *pa_card_list_to_string(pa_core *c);
+char *pa_client_list_to_string(pa_core *c);
+char *pa_module_list_to_string(pa_core *c);
+char *pa_scache_list_to_string(pa_core *c);
+
+char *pa_full_status_string(pa_core *c);
+
+#endif
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
new file mode 100644 (file)
index 0000000..f942629
--- /dev/null
@@ -0,0 +1,176 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/ioline.h>
+#include <pulsecore/module.h>
+#include <pulsecore/client.h>
+#include <pulsecore/tokenizer.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/cli-text.h>
+#include <pulsecore/cli-command.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "cli.h"
+
+#define PROMPT ">>> "
+
+struct pa_cli {
+    pa_core *core;
+    pa_ioline *line;
+
+    pa_cli_eof_cb_t eof_callback;
+    void *userdata;
+
+    pa_client *client;
+
+    bool fail, kill_requested;
+    int defer_kill;
+
+    bool interactive;
+    char *last_line;
+};
+
+static void line_callback(pa_ioline *line, const char *s, void *userdata);
+static void client_kill(pa_client *c);
+
+pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
+    char cname[256];
+    pa_cli *c;
+    pa_client_new_data data;
+    pa_client *client;
+
+    pa_assert(io);
+
+    pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
+
+    pa_client_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_proplist_sets(data.proplist, PA_PROP_APPLICATION_NAME, cname);
+    client = pa_client_new(core, &data);
+    pa_client_new_data_done(&data);
+
+    if (!client)
+        return NULL;
+
+    c = pa_xnew0(pa_cli, 1);
+    c->core = core;
+    c->client = client;
+    pa_assert_se(c->line = pa_ioline_new(io));
+
+    c->client->kill = client_kill;
+    c->client->userdata = c;
+
+    pa_ioline_set_callback(c->line, line_callback, c);
+
+    return c;
+}
+
+void pa_cli_free(pa_cli *c) {
+    pa_assert(c);
+
+    pa_ioline_close(c->line);
+    pa_ioline_unref(c->line);
+    pa_client_free(c->client);
+    pa_xfree(c->last_line);
+    pa_xfree(c);
+}
+
+static void client_kill(pa_client *client) {
+    pa_cli *c;
+
+    pa_assert(client);
+    pa_assert_se(c = client->userdata);
+
+    pa_log_debug("CLI client killed.");
+
+    if (c->defer_kill)
+        c->kill_requested = true;
+    else if (c->eof_callback)
+        c->eof_callback(c, c->userdata);
+}
+
+static void line_callback(pa_ioline *line, const char *s, void *userdata) {
+    pa_strbuf *buf;
+    pa_cli *c = userdata;
+    char *p;
+
+    pa_assert(line);
+    pa_assert(c);
+
+    if (!s) {
+        pa_log_debug("CLI got EOF from user.");
+
+        if (c->eof_callback)
+            c->eof_callback(c, c->userdata);
+
+        return;
+    }
+
+    /* Magic command, like they had in AT Hayes Modems! Those were the good days! */
+    if (pa_streq(s, "/"))
+        s = c->last_line;
+    else if (s[0]) {
+        pa_xfree(c->last_line);
+        c->last_line = pa_xstrdup(s);
+    }
+
+    pa_assert_se(buf = pa_strbuf_new());
+    c->defer_kill++;
+    if (pa_streq(s, "hello")) {
+        pa_strbuf_printf(buf, "Welcome to PulseAudio %s! "
+            "Use \"help\" for usage information.\n", PACKAGE_VERSION);
+        c->interactive = true;
+    }
+    else
+        pa_cli_command_execute_line(c->core, s, buf, &c->fail);
+    c->defer_kill--;
+    pa_ioline_puts(line, p = pa_strbuf_to_string_free(buf));
+    pa_xfree(p);
+
+    if (c->kill_requested) {
+        if (c->eof_callback)
+            c->eof_callback(c, c->userdata);
+    } else if (c->interactive)
+        pa_ioline_puts(line, PROMPT);
+}
+
+void pa_cli_set_eof_callback(pa_cli *c, pa_cli_eof_cb_t cb, void *userdata) {
+    pa_assert(c);
+
+    c->eof_callback = cb;
+    c->userdata = userdata;
+}
+
+pa_module *pa_cli_get_module(pa_cli *c) {
+    pa_assert(c);
+
+    return c->client->module;
+}
diff --git a/src/pulsecore/cli.h b/src/pulsecore/cli.h
new file mode 100644 (file)
index 0000000..37d5ff9
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef fooclihfoo
+#define fooclihfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/iochannel.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+
+typedef struct pa_cli pa_cli;
+
+typedef void (*pa_cli_eof_cb_t)(pa_cli *c, void *userdata);
+
+/* Create a new command line session on the specified io channel owned by the specified module */
+pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m);
+void pa_cli_free(pa_cli *cli);
+
+/* Set a callback function that is called whenever the command line session is terminated */
+void pa_cli_set_eof_callback(pa_cli *cli, pa_cli_eof_cb_t cb, void *userdata);
+
+pa_module *pa_cli_get_module(pa_cli *c);
+
+#endif
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
new file mode 100644 (file)
index 0000000..2e6af47
--- /dev/null
@@ -0,0 +1,168 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "client.h"
+
+pa_client_new_data* pa_client_new_data_init(pa_client_new_data *data) {
+    pa_assert(data);
+
+    memset(data, 0, sizeof(*data));
+    data->proplist = pa_proplist_new();
+
+    return data;
+}
+
+void pa_client_new_data_done(pa_client_new_data *data) {
+    pa_assert(data);
+
+    pa_proplist_free(data->proplist);
+}
+
+pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) {
+    pa_client *c;
+
+    pa_core_assert_ref(core);
+    pa_assert(data);
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_NEW], data) < 0)
+        return NULL;
+
+    c = pa_xnew0(pa_client, 1);
+    c->core = core;
+    c->proplist = pa_proplist_copy(data->proplist);
+    c->driver = pa_xstrdup(pa_path_get_filename(data->driver));
+    c->module = data->module;
+
+    c->sink_inputs = pa_idxset_new(NULL, NULL);
+    c->source_outputs = pa_idxset_new(NULL, NULL);
+
+    pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
+
+    pa_log_info("Created %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
+    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
+
+    pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_PUT], c);
+
+    pa_core_check_idle(core);
+
+    return c;
+}
+
+void pa_client_free(pa_client *c) {
+    pa_core *core;
+
+    pa_assert(c);
+    pa_assert(c->core);
+
+    core = c->core;
+
+    pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_UNLINK], c);
+
+    pa_idxset_remove_by_data(c->core->clients, c, NULL);
+
+    pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
+    pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
+
+    pa_assert(pa_idxset_isempty(c->sink_inputs));
+    pa_idxset_free(c->sink_inputs, NULL);
+    pa_assert(pa_idxset_isempty(c->source_outputs));
+    pa_idxset_free(c->source_outputs, NULL);
+
+    pa_proplist_free(c->proplist);
+    pa_xfree(c->driver);
+    pa_xfree(c);
+
+    pa_core_check_idle(core);
+}
+
+void pa_client_kill(pa_client *c) {
+    pa_assert(c);
+
+    if (!c->kill) {
+        pa_log_warn("kill() operation not implemented for client %u", c->index);
+        return;
+    }
+
+    c->kill(c);
+}
+
+void pa_client_set_name(pa_client *c, const char *name) {
+    pa_assert(c);
+    pa_assert(name);
+
+    pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)), name);
+    pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+
+    pa_client_update_proplist(c, 0, NULL);
+}
+
+void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist *p) {
+    pa_assert(c);
+
+    if (p)
+        pa_proplist_update(c->proplist, mode, p);
+
+    pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c);
+    pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
+}
+
+void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data) {
+    pa_proplist *pl = NULL;
+    pa_client_send_event_hook_data hook_data;
+
+    pa_assert(c);
+    pa_assert(event);
+
+    if (!c->send_event)
+        return;
+
+    if (!data)
+        data = pl = pa_proplist_new();
+
+    hook_data.client = c;
+    hook_data.data = data;
+    hook_data.event = event;
+
+    if (pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_SEND_EVENT], &hook_data) < 0)
+        goto finish;
+
+    c->send_event(c, event, data);
+
+finish:
+
+    if (pl)
+        pa_proplist_free(pl);
+}
diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h
new file mode 100644 (file)
index 0000000..eb8173d
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef foopulseclienthfoo
+#define foopulseclienthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include <pulsecore/typedefs.h>
+#include <pulse/proplist.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+
+/* Every connection to the server should have a pa_client
+ * attached. That way the user may generate a listing of all connected
+ * clients easily and kill them if he wants.*/
+
+struct pa_client {
+    uint32_t index;
+    pa_core *core;
+
+    pa_proplist *proplist;
+    pa_module *module;
+    char *driver;
+
+    pa_idxset *sink_inputs;
+    pa_idxset *source_outputs;
+
+    void *userdata;
+
+    void (*kill)(pa_client *c);
+
+    void (*send_event)(pa_client *c, const char *name, pa_proplist *data);
+};
+
+typedef struct pa_client_new_data {
+    pa_proplist *proplist;
+    const char *driver;
+    pa_module *module;
+} pa_client_new_data;
+
+pa_client_new_data *pa_client_new_data_init(pa_client_new_data *data);
+void pa_client_new_data_done(pa_client_new_data *data);
+
+pa_client *pa_client_new(pa_core *c, pa_client_new_data *data);
+
+/* This function should be called only by the code that created the client */
+void pa_client_free(pa_client *c);
+
+/* Code that didn't create the client should call this function to
+ * request destruction of the client */
+void pa_client_kill(pa_client *c);
+
+/* Rename the client */
+void pa_client_set_name(pa_client *c, const char *name);
+
+void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist *p);
+
+void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data);
+
+typedef struct pa_client_send_event_hook_data {
+    pa_client *client;
+    const char *event;
+    pa_proplist *data;
+} pa_client_send_event_hook_data;
+
+#endif
diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c
new file mode 100644 (file)
index 0000000..73b7061
--- /dev/null
@@ -0,0 +1,394 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "conf-parser.h"
+
+#define WHITESPACE " \t\n"
+#define COMMENTS "#;\n"
+
+/* Run the user supplied parser for an assignment */
+static int normal_assignment(pa_config_parser_state *state) {
+    const pa_config_item *item;
+
+    pa_assert(state);
+
+    for (item = state->item_table; item->parse; item++) {
+
+        if (item->lvalue && !pa_streq(state->lvalue, item->lvalue))
+            continue;
+
+        if (item->section && !state->section)
+            continue;
+
+        if (item->section && !pa_streq(state->section, item->section))
+            continue;
+
+        state->data = item->data;
+
+        return item->parse(state);
+    }
+
+    pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", state->filename, state->lineno, state->lvalue, pa_strna(state->section));
+
+    return -1;
+}
+
+/* Parse a proplist entry. */
+static int proplist_assignment(pa_config_parser_state *state) {
+    pa_assert(state);
+    pa_assert(state->proplist);
+
+    if (pa_proplist_sets(state->proplist, state->lvalue, state->rvalue) < 0) {
+        pa_log("[%s:%u] Failed to parse a proplist entry: %s = %s", state->filename, state->lineno, state->lvalue, state->rvalue);
+        return -1;
+    }
+
+    return 0;
+}
+
+/* Parse a variable assignment line */
+static int parse_line(pa_config_parser_state *state) {
+    char *c;
+
+    state->lvalue = state->buf + strspn(state->buf, WHITESPACE);
+
+    if ((c = strpbrk(state->lvalue, COMMENTS)))
+        *c = 0;
+
+    if (!*state->lvalue)
+        return 0;
+
+    if (pa_startswith(state->lvalue, ".include ")) {
+        char *path = NULL, *fn;
+        int r;
+
+        fn = pa_strip(state->lvalue + 9);
+        if (!pa_is_path_absolute(fn)) {
+            const char *k;
+            if ((k = strrchr(state->filename, '/'))) {
+                char *dir = pa_xstrndup(state->filename, k - state->filename);
+                fn = path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", dir, fn);
+                pa_xfree(dir);
+            }
+        }
+
+        r = pa_config_parse(fn, NULL, state->item_table, state->proplist, false, state->userdata);
+        pa_xfree(path);
+        return r;
+    }
+
+    if (*state->lvalue == '[') {
+        size_t k;
+
+        k = strlen(state->lvalue);
+        pa_assert(k > 0);
+
+        if (state->lvalue[k-1] != ']') {
+            pa_log("[%s:%u] Invalid section header.", state->filename, state->lineno);
+            return -1;
+        }
+
+        pa_xfree(state->section);
+        state->section = pa_xstrndup(state->lvalue + 1, k-2);
+
+        if (pa_streq(state->section, "Properties")) {
+            if (!state->proplist) {
+                pa_log("[%s:%u] \"Properties\" section is not allowed in this file.", state->filename, state->lineno);
+                return -1;
+            }
+
+            state->in_proplist = true;
+        } else
+            state->in_proplist = false;
+
+        return 0;
+    }
+
+    if (!(state->rvalue = strchr(state->lvalue, '='))) {
+        pa_log("[%s:%u] Missing '='.", state->filename, state->lineno);
+        return -1;
+    }
+
+    *state->rvalue = 0;
+    state->rvalue++;
+
+    state->lvalue = pa_strip(state->lvalue);
+    state->rvalue = pa_strip(state->rvalue);
+
+    if (state->in_proplist)
+        return proplist_assignment(state);
+    else
+        return normal_assignment(state);
+}
+
+#ifndef OS_IS_WIN32
+static int conf_filter(const struct dirent *entry) {
+    return pa_endswith(entry->d_name, ".conf");
+}
+#endif
+
+/* Go through the file and parse each line */
+int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, pa_proplist *proplist, bool use_dot_d,
+                    void *userdata) {
+    int r = -1;
+    bool do_close = !f;
+    pa_config_parser_state state;
+
+    pa_assert(filename);
+    pa_assert(t);
+
+    pa_zero(state);
+
+    if (!f && !(f = pa_fopen_cloexec(filename, "r"))) {
+        if (errno == ENOENT) {
+            pa_log_debug("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno));
+            r = 0;
+            goto finish;
+        }
+
+        pa_log_warn("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno));
+        goto finish;
+    }
+    pa_log_debug("Parsing configuration file '%s'", filename);
+
+    state.filename = filename;
+    state.item_table = t;
+    state.userdata = userdata;
+
+    if (proplist)
+        state.proplist = pa_proplist_new();
+
+    while (!feof(f)) {
+        if (!fgets(state.buf, sizeof(state.buf), f)) {
+            if (feof(f))
+                break;
+
+            pa_log_warn("Failed to read configuration file '%s': %s", filename, pa_cstrerror(errno));
+            goto finish;
+        }
+
+        state.lineno++;
+
+        if (parse_line(&state) < 0)
+            goto finish;
+    }
+
+    if (proplist)
+        pa_proplist_update(proplist, PA_UPDATE_REPLACE, state.proplist);
+
+    r = 0;
+
+finish:
+    if (state.proplist)
+        pa_proplist_free(state.proplist);
+
+    pa_xfree(state.section);
+
+    if (do_close && f)
+        fclose(f);
+
+    if (use_dot_d) {
+#ifdef OS_IS_WIN32
+        char *dir_name = pa_sprintf_malloc("%s.d", filename);
+        char *pattern = pa_sprintf_malloc("%s\\*.conf", dir_name);
+        HANDLE fh;
+        WIN32_FIND_DATA wfd;
+
+        fh = FindFirstFile(pattern, &wfd);
+        if (fh != INVALID_HANDLE_VALUE) {
+            do {
+                if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+                    char *filename2 = pa_sprintf_malloc("%s\\%s", dir_name, wfd.cFileName);
+                    pa_config_parse(filename2, NULL, t, proplist, false, userdata);
+                    pa_xfree(filename2);
+                }
+            } while (FindNextFile(fh, &wfd));
+            FindClose(fh);
+        } else {
+            DWORD err = GetLastError();
+
+            if (err == ERROR_PATH_NOT_FOUND) {
+                pa_log_debug("Pattern %s did not match any files, ignoring.", pattern);
+            } else {
+                LPVOID msgbuf;
+                DWORD fret = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                                           NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&msgbuf, 0, NULL);
+
+                if (fret != 0) {
+                    pa_log_warn("FindFirstFile(%s) failed with error %ld (%s), ignoring.", pattern, err, (char*)msgbuf);
+                    LocalFree(msgbuf);
+                } else {
+                    pa_log_warn("FindFirstFile(%s) failed with error %ld, ignoring.", pattern, err);
+                    pa_log_warn("FormatMessage failed with error %ld", GetLastError());
+                }
+            }
+        }
+
+        pa_xfree(pattern);
+        pa_xfree(dir_name);
+#else
+        char *dir_name;
+        int n;
+        struct dirent **entries = NULL;
+
+        dir_name = pa_sprintf_malloc("%s.d", filename);
+
+        n = scandir(dir_name, &entries, conf_filter, alphasort);
+        if (n >= 0) {
+            int i;
+
+            for (i = 0; i < n; i++) {
+                char *filename2;
+
+                filename2 = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", dir_name, entries[i]->d_name);
+                pa_config_parse(filename2, NULL, t, proplist, false, userdata);
+                pa_xfree(filename2);
+
+                free(entries[i]);
+            }
+
+            free(entries);
+        } else {
+            if (errno == ENOENT)
+                pa_log_debug("%s does not exist, ignoring.", dir_name);
+            else
+                pa_log_warn("scandir(\"%s\") failed: %s", dir_name, pa_cstrerror(errno));
+        }
+
+        pa_xfree(dir_name);
+#endif
+    }
+
+    return r;
+}
+
+int pa_config_parse_int(pa_config_parser_state *state) {
+    int *i;
+    int32_t k;
+
+    pa_assert(state);
+
+    i = state->data;
+
+    if (pa_atoi(state->rvalue, &k) < 0) {
+        pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    *i = (int) k;
+    return 0;
+}
+
+int pa_config_parse_unsigned(pa_config_parser_state *state) {
+    unsigned *u;
+    uint32_t k;
+
+    pa_assert(state);
+
+    u = state->data;
+
+    if (pa_atou(state->rvalue, &k) < 0) {
+        pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    *u = (unsigned) k;
+    return 0;
+}
+
+int pa_config_parse_size(pa_config_parser_state *state) {
+    size_t *i;
+    uint32_t k;
+
+    pa_assert(state);
+
+    i = state->data;
+
+    if (pa_atou(state->rvalue, &k) < 0) {
+        pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    *i = (size_t) k;
+    return 0;
+}
+
+int pa_config_parse_bool(pa_config_parser_state *state) {
+    int k;
+    bool *b;
+
+    pa_assert(state);
+
+    b = state->data;
+
+    if ((k = pa_parse_boolean(state->rvalue)) < 0) {
+        pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    *b = !!k;
+
+    return 0;
+}
+
+int pa_config_parse_not_bool(pa_config_parser_state *state) {
+    int k;
+    bool *b;
+
+    pa_assert(state);
+
+    b = state->data;
+
+    if ((k = pa_parse_boolean(state->rvalue)) < 0) {
+        pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue);
+        return -1;
+    }
+
+    *b = !k;
+
+    return 0;
+}
+
+int pa_config_parse_string(pa_config_parser_state *state) {
+    char **s;
+
+    pa_assert(state);
+
+    s = state->data;
+
+    pa_xfree(*s);
+    *s = *state->rvalue ? pa_xstrdup(state->rvalue) : NULL;
+    return 0;
+}
diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h
new file mode 100644 (file)
index 0000000..7dc0ff9
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef fooconfparserhfoo
+#define fooconfparserhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+
+#include <pulse/proplist.h>
+
+/* An abstract parser for simple, line based, shallow configuration
+ * files consisting of variable assignments only. */
+
+typedef struct pa_config_parser_state pa_config_parser_state;
+
+typedef int (*pa_config_parser_cb_t)(pa_config_parser_state *state);
+
+/* Wraps info for parsing a specific configuration variable */
+typedef struct pa_config_item {
+    const char *lvalue; /* name of the variable */
+    pa_config_parser_cb_t parse; /* Function that is called to parse the variable's value */
+    void *data; /* Where to store the variable's data */
+    const char *section;
+} pa_config_item;
+
+struct pa_config_parser_state {
+    const char *filename;
+    unsigned lineno;
+    char *section;
+    char *lvalue;
+    char *rvalue;
+    void *data; /* The data pointer of the current pa_config_item. */
+    void *userdata; /* The pointer that was given to pa_config_parse(). */
+
+    /* Private data to be used only by conf-parser.c. */
+    const pa_config_item *item_table;
+    char buf[4096];
+    pa_proplist *proplist;
+    bool in_proplist;
+};
+
+/* The configuration file parsing routine. Expects a table of
+ * pa_config_items in *t that is terminated by an item where lvalue is
+ * NULL.
+ *
+ * If use_dot_d is true, then after parsing the file named by the filename
+ * argument, the function will parse all files ending with ".conf" in
+ * alphabetical order from a directory whose name is filename + ".d", if such
+ * directory exists.
+ *
+ * Some configuration files may contain a Properties section, which
+ * is a bit special. Normally all accepted lvalues must be predefined
+ * in the pa_config_item table, but in the Properties section the
+ * pa_config_item table is ignored, and all lvalues are accepted (as
+ * long as they are valid proplist keys). If the proplist pointer is
+ * non-NULL, the parser will parse any section named "Properties" as
+ * properties, and those properties will be merged into the given
+ * proplist. If proplist is NULL, then sections named "Properties"
+ * are not allowed at all in the configuration file. */
+int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, pa_proplist *proplist, bool use_dot_d,
+                    void *userdata);
+
+/* Generic parsers for integers, size_t, booleans and strings */
+int pa_config_parse_int(pa_config_parser_state *state);
+int pa_config_parse_unsigned(pa_config_parser_state *state);
+int pa_config_parse_size(pa_config_parser_state *state);
+int pa_config_parse_bool(pa_config_parser_state *state);
+int pa_config_parse_not_bool(pa_config_parser_state *state);
+int pa_config_parse_string(pa_config_parser_state *state);
+
+#endif
diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c
new file mode 100644 (file)
index 0000000..446de6c
--- /dev/null
@@ -0,0 +1,78 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+
+#include "core-error.h"
+
+PA_STATIC_TLS_DECLARE(cstrerror, pa_xfree);
+
+const char* pa_cstrerror(int errnum) {
+    const char *original = NULL;
+    char *translated, *t;
+    char errbuf[128];
+
+    if (errnum < 0)
+        errnum = -errnum;
+
+    if ((t = PA_STATIC_TLS_GET(cstrerror)))
+        pa_xfree(t);
+
+#if defined(HAVE_STRERROR_R) && defined(__GLIBC__)
+    original = strerror_r(errnum, errbuf, sizeof(errbuf));
+#elif defined(HAVE_STRERROR_R)
+    if (strerror_r(errnum, errbuf, sizeof(errbuf)) == 0) {
+        errbuf[sizeof(errbuf) - 1] = 0;
+        original = errbuf;
+    }
+#else
+    /* This might not be thread safe, but we hope for the best */
+    original = strerror(errnum);
+#endif
+
+    /* The second condition is a Windows-ism */
+    if (!original || !strcasecmp(original, "Unknown error")) {
+        pa_snprintf(errbuf, sizeof(errbuf), "Unknown error %d", errnum);
+        original = errbuf;
+    }
+
+    if (!(translated = pa_locale_to_utf8(original))) {
+        pa_log_warn("Unable to convert error string to locale, filtering.");
+        translated = pa_utf8_filter(original);
+    }
+
+    PA_STATIC_TLS_SET(cstrerror, translated);
+
+    return translated;
+}
diff --git a/src/pulsecore/core-error.h b/src/pulsecore/core-error.h
new file mode 100644 (file)
index 0000000..f4af5d5
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef foocoreerrorhfoo
+#define foocoreerrorhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/cdecl.h>
+
+/** \file
+ * Error management */
+
+PA_C_DECL_BEGIN
+
+/** A wrapper around the standard strerror() function that converts the
+ * string to UTF-8. The function is thread safe but the returned string is
+ * only guaranteed to exist until the thread exits or pa_cstrerror() is
+ * called again from the same thread. */
+const char* pa_cstrerror(int errnum);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulsecore/core-format.c b/src/pulsecore/core-format.c
new file mode 100644 (file)
index 0000000..9d3c8d6
--- /dev/null
@@ -0,0 +1,241 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "core-format.h"
+
+#include <pulse/def.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+
+int pa_format_info_get_sample_format(const pa_format_info *f, pa_sample_format_t *sf) {
+    int r;
+    char *sf_str;
+    pa_sample_format_t sf_local;
+
+    pa_assert(f);
+    pa_assert(sf);
+
+    r = pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf_str);
+    if (r < 0)
+        return r;
+
+    sf_local = pa_parse_sample_format(sf_str);
+    pa_xfree(sf_str);
+
+    if (!pa_sample_format_valid(sf_local)) {
+        pa_log_debug("Invalid sample format.");
+        return -PA_ERR_INVALID;
+    }
+
+    *sf = sf_local;
+
+    return 0;
+}
+
+int pa_format_info_get_rate(const pa_format_info *f, uint32_t *rate) {
+    int r;
+    int rate_local;
+
+    pa_assert(f);
+    pa_assert(rate);
+
+    r = pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate_local);
+    if (r < 0)
+        return r;
+
+    if (!pa_sample_rate_valid(rate_local)) {
+        pa_log_debug("Invalid sample rate: %i", rate_local);
+        return -PA_ERR_INVALID;
+    }
+
+    *rate = rate_local;
+
+    return 0;
+}
+
+int pa_format_info_get_channels(const pa_format_info *f, uint8_t *channels) {
+    int r;
+    int channels_local;
+
+    pa_assert(f);
+    pa_assert(channels);
+
+    r = pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels_local);
+    if (r < 0)
+        return r;
+
+    if (!pa_channels_valid(channels_local)) {
+        pa_log_debug("Invalid channel count: %i", channels_local);
+        return -PA_ERR_INVALID;
+    }
+
+    *channels = channels_local;
+
+    return 0;
+}
+
+int pa_format_info_get_channel_map(const pa_format_info *f, pa_channel_map *map) {
+    int r;
+    char *map_str;
+
+    pa_assert(f);
+    pa_assert(map);
+
+    r = pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &map_str);
+    if (r < 0)
+        return r;
+
+    map = pa_channel_map_parse(map, map_str);
+    pa_xfree(map_str);
+
+    if (!map) {
+        pa_log_debug("Failed to parse channel map.");
+        return -PA_ERR_INVALID;
+    }
+
+    return 0;
+}
+
+pa_format_info *pa_format_info_from_sample_spec2(const pa_sample_spec *ss, const pa_channel_map *map, bool set_format,
+                                                 bool set_rate, bool set_channels) {
+    pa_format_info *format = NULL;
+
+    pa_assert(ss);
+
+    format = pa_format_info_new();
+    format->encoding = PA_ENCODING_PCM;
+
+    if (set_format)
+        pa_format_info_set_sample_format(format, ss->format);
+
+    if (set_rate)
+        pa_format_info_set_rate(format, ss->rate);
+
+    if (set_channels) {
+        pa_format_info_set_channels(format, ss->channels);
+
+        if (map) {
+            if (map->channels != ss->channels) {
+                pa_log_debug("Channel map is incompatible with the sample spec.");
+                goto fail;
+            }
+
+            pa_format_info_set_channel_map(format, map);
+        }
+    }
+
+    return format;
+
+fail:
+    if (format)
+        pa_format_info_free(format);
+
+    return NULL;
+}
+
+int pa_format_info_to_sample_spec2(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map,
+                                   const pa_sample_spec *fallback_ss, const pa_channel_map *fallback_map) {
+    int r, r2;
+    pa_sample_spec ss_local;
+    pa_channel_map map_local;
+
+    pa_assert(f);
+    pa_assert(ss);
+    pa_assert(map);
+    pa_assert(fallback_ss);
+    pa_assert(fallback_map);
+
+    if (!pa_format_info_is_pcm(f))
+        return pa_format_info_to_sample_spec_fake(f, ss, map);
+
+    r = pa_format_info_get_sample_format(f, &ss_local.format);
+    if (r == -PA_ERR_NOENTITY)
+        ss_local.format = fallback_ss->format;
+    else if (r < 0)
+        return r;
+
+    pa_assert(pa_sample_format_valid(ss_local.format));
+
+    r = pa_format_info_get_rate(f, &ss_local.rate);
+    if (r == -PA_ERR_NOENTITY)
+        ss_local.rate = fallback_ss->rate;
+    else if (r < 0)
+        return r;
+
+    pa_assert(pa_sample_rate_valid(ss_local.rate));
+
+    r = pa_format_info_get_channels(f, &ss_local.channels);
+    r2 = pa_format_info_get_channel_map(f, &map_local);
+    if (r == -PA_ERR_NOENTITY && r2 >= 0)
+        ss_local.channels = map_local.channels;
+    else if (r == -PA_ERR_NOENTITY)
+        ss_local.channels = fallback_ss->channels;
+    else if (r < 0)
+        return r;
+
+    pa_assert(pa_channels_valid(ss_local.channels));
+
+    if (r2 >= 0 && map_local.channels != ss_local.channels) {
+        pa_log_debug("Channel map is not compatible with the sample spec.");
+        return -PA_ERR_INVALID;
+    }
+
+    if (r2 == -PA_ERR_NOENTITY) {
+        if (fallback_map->channels == ss_local.channels)
+            map_local = *fallback_map;
+        else
+            pa_channel_map_init_extend(&map_local, ss_local.channels, PA_CHANNEL_MAP_DEFAULT);
+    } else if (r2 < 0)
+        return r2;
+
+    pa_assert(pa_channel_map_valid(&map_local));
+    pa_assert(ss_local.channels == map_local.channels);
+
+    *ss = ss_local;
+    *map = map_local;
+
+    return 0;
+}
+
+int pa_format_info_to_sample_spec_fake(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
+    int rate;
+
+    pa_assert(f);
+    pa_assert(ss);
+
+    /* Note: When we add support for non-IEC61937 encapsulated compressed
+     * formats, this function should return a non-zero values for these. */
+
+    ss->format = PA_SAMPLE_S16LE;
+    ss->channels = 2;
+
+    if (map)
+        pa_channel_map_init_stereo(map);
+
+    pa_return_val_if_fail(pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate) == 0, -PA_ERR_INVALID);
+    ss->rate = (uint32_t) rate;
+
+    if (f->encoding == PA_ENCODING_EAC3_IEC61937)
+        ss->rate *= 4;
+
+    return 0;
+}
diff --git a/src/pulsecore/core-format.h b/src/pulsecore/core-format.h
new file mode 100644 (file)
index 0000000..3750304
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef foocoreformathfoo
+#define foocoreformathfoo
+
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/format.h>
+
+#include <stdbool.h>
+
+/* Gets the sample format stored in the format info. Returns a negative error
+ * code on failure. If the sample format property is not set at all, returns
+ * -PA_ERR_NOENTITY. */
+int pa_format_info_get_sample_format(const pa_format_info *f, pa_sample_format_t *sf);
+
+/* Gets the sample rate stored in the format info. Returns a negative error
+ * code on failure. If the sample rate property is not set at all, returns
+ * -PA_ERR_NOENTITY. */
+int pa_format_info_get_rate(const pa_format_info *f, uint32_t *rate);
+
+/* Gets the channel count stored in the format info. Returns a negative error
+ * code on failure. If the channels property is not set at all, returns
+ * -PA_ERR_NOENTITY. */
+int pa_format_info_get_channels(const pa_format_info *f, uint8_t *channels);
+
+/* Gets the channel map stored in the format info. Returns a negative error
+ * code on failure. If the channel map property is not set at all, returns
+ * -PA_ERR_NOENTITY. */
+int pa_format_info_get_channel_map(const pa_format_info *f, pa_channel_map *map);
+
+/* Convert a sample spec and an optional channel map to a new PCM format info
+ * object (remember to free it). If map is NULL, then the channel map will be
+ * left unspecified. If some fields of the sample spec should be ignored, pass
+ * false for set_format, set_rate and set_channels as appropriate, then those
+ * fields will be left unspecified. This function returns NULL if the input is
+ * invalid (for example, setting the sample rate was requested, but the rate
+ * in ss is invalid).
+ *
+ * pa_format_info_from_sample_spec() exists too. This "version 2" was created,
+ * because the original function doesn't provide the possibility of ignoring
+ * some of the sample spec fields. That functionality can't be added to the
+ * original function, because the function is a part of the public API and
+ * adding parameters to it would break the API. */
+pa_format_info *pa_format_info_from_sample_spec2(const pa_sample_spec *ss, const pa_channel_map *map, bool set_format,
+                                                 bool set_rate, bool set_channels);
+
+/* Convert the format info into a sample spec and a channel map. If the format
+ * info doesn't contain some information, the fallback sample spec and channel
+ * map are used to populate the output.
+ *
+ * pa_format_info_to_sample_spec() exists too. This "version 2" was created,
+ * because the original function doesn't provide the possibility of specifying
+ * a fallback sample spec and channel map. That functionality can't be added to
+ * the original function, because the function is part of the public API and
+ * adding parameters to it would break the API. */
+int pa_format_info_to_sample_spec2(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map,
+                                   const pa_sample_spec *fallback_ss, const pa_channel_map *fallback_map);
+
+/* For compressed formats. Converts the format info into a sample spec and a
+ * channel map that an ALSA device can use as its configuration parameters when
+ * playing back the compressed data. That is, the returned sample spec doesn't
+ * describe the audio content, but the device parameters. */
+int pa_format_info_to_sample_spec_fake(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
+
+#endif
diff --git a/src/pulsecore/core-rtclock.c b/src/pulsecore/core-rtclock.c
new file mode 100644 (file)
index 0000000..2c2e286
--- /dev/null
@@ -0,0 +1,267 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef OS_IS_DARWIN
+#define _POSIX_C_SOURCE 1
+#endif
+
+#include <stddef.h>
+#include <time.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+#ifdef OS_IS_DARWIN
+#include <CoreServices/CoreServices.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <pulse/timeval.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
+
+#include "core-rtclock.h"
+
+#ifdef OS_IS_WIN32
+static int64_t counter_freq = 0;
+#endif
+
+pa_usec_t pa_rtclock_age(const struct timeval *tv) {
+    struct timeval now;
+    pa_assert(tv);
+
+    return pa_timeval_diff(pa_rtclock_get(&now), tv);
+}
+
+struct timeval *pa_rtclock_get(struct timeval *tv) {
+
+#if defined(OS_IS_DARWIN)
+    uint64_t val, abs_time = mach_absolute_time();
+    Nanoseconds nanos;
+
+    nanos = AbsoluteToNanoseconds(*(AbsoluteTime *) &abs_time);
+    val = *(uint64_t *) &nanos;
+
+    tv->tv_sec = val / PA_NSEC_PER_SEC;
+    tv->tv_usec = (val % PA_NSEC_PER_SEC) / PA_NSEC_PER_USEC;
+
+    return tv;
+
+#elif defined(HAVE_CLOCK_GETTIME)
+    struct timespec ts;
+
+#ifdef CLOCK_MONOTONIC
+    /* No locking or atomic ops for no_monotonic here */
+    static bool no_monotonic = false;
+
+    if (!no_monotonic)
+        if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
+            no_monotonic = true;
+
+    if (no_monotonic)
+#endif /* CLOCK_MONOTONIC */
+        pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+
+    pa_assert(tv);
+
+    tv->tv_sec = ts.tv_sec;
+    tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC;
+
+    return tv;
+#elif defined(OS_IS_WIN32)
+    if (counter_freq > 0) {
+        LARGE_INTEGER count;
+
+        pa_assert_se(QueryPerformanceCounter(&count));
+
+        tv->tv_sec = count.QuadPart / counter_freq;
+        tv->tv_usec = (count.QuadPart % counter_freq) * PA_USEC_PER_SEC / counter_freq;
+
+        return tv;
+    }
+#endif /* HAVE_CLOCK_GETTIME */
+
+    return pa_gettimeofday(tv);
+}
+
+bool pa_rtclock_hrtimer(void) {
+
+#if defined (OS_IS_DARWIN)
+    mach_timebase_info_data_t tbi;
+    uint64_t time_nsec;
+
+    mach_timebase_info(&tbi);
+
+    /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */
+    time_nsec = tbi.numer / tbi.denom;
+    return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
+
+#elif defined(HAVE_CLOCK_GETTIME)
+    struct timespec ts;
+
+#ifdef CLOCK_MONOTONIC
+
+    if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0)
+        return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
+
+#endif /* CLOCK_MONOTONIC */
+
+    pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0);
+    return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
+
+#elif defined(OS_IS_WIN32)
+
+    if (counter_freq > 0)
+        return counter_freq >= (int64_t) (PA_USEC_PER_SEC/PA_HRTIMER_THRESHOLD_USEC);
+
+#endif /* HAVE_CLOCK_GETTIME */
+
+    return false;
+}
+
+#define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC))
+
+void pa_rtclock_hrtimer_enable(void) {
+
+#ifdef PR_SET_TIMERSLACK
+    int slack_ns;
+
+    if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) {
+        pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported.");
+        return;
+    }
+
+    pa_log_debug("Timer slack is set to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
+
+    if (slack_ns > TIMER_SLACK_NS) {
+        slack_ns = TIMER_SLACK_NS;
+
+        pa_log_debug("Setting timer slack to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
+
+        if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) {
+            pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno));
+            return;
+        }
+    }
+
+#elif defined(OS_IS_WIN32)
+    LARGE_INTEGER freq;
+
+    pa_assert_se(QueryPerformanceFrequency(&freq));
+    counter_freq = freq.QuadPart;
+
+#endif
+}
+
+struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
+    struct timeval wc_now, rt_now;
+
+    pa_assert(tv);
+
+    pa_gettimeofday(&wc_now);
+    pa_rtclock_get(&rt_now);
+
+    /* pa_timeval_sub() saturates on underflow! */
+
+    if (pa_timeval_cmp(&wc_now, tv) < 0)
+        pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
+    else
+        pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv));
+
+    *tv = rt_now;
+
+    return tv;
+}
+
+#ifdef HAVE_CLOCK_GETTIME
+pa_usec_t pa_timespec_load(const struct timespec *ts) {
+
+    if (PA_UNLIKELY(!ts))
+        return PA_USEC_INVALID;
+
+    return
+        (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC +
+        (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC;
+}
+
+struct timespec* pa_timespec_store(struct timespec *ts, pa_usec_t v) {
+    pa_assert(ts);
+
+    if (PA_UNLIKELY(v == PA_USEC_INVALID)) {
+        ts->tv_sec = PA_INT_TYPE_MAX(time_t);
+        ts->tv_nsec = (long) (PA_NSEC_PER_SEC-1);
+        return NULL;
+    }
+
+    ts->tv_sec = (time_t) (v / PA_USEC_PER_SEC);
+    ts->tv_nsec = (long) ((v % PA_USEC_PER_SEC) * PA_NSEC_PER_USEC);
+
+    return ts;
+}
+#endif
+
+static struct timeval* wallclock_from_rtclock(struct timeval *tv) {
+    struct timeval wc_now, rt_now;
+
+    pa_assert(tv);
+
+    pa_gettimeofday(&wc_now);
+    pa_rtclock_get(&rt_now);
+
+    /* pa_timeval_sub() saturates on underflow! */
+
+    if (pa_timeval_cmp(&rt_now, tv) < 0)
+        pa_timeval_add(&wc_now, pa_timeval_diff(tv, &rt_now));
+    else
+        pa_timeval_sub(&wc_now, pa_timeval_diff(&rt_now, tv));
+
+    *tv = wc_now;
+
+    return tv;
+}
+
+struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, bool rtclock) {
+    pa_assert(tv);
+
+    if (v == PA_USEC_INVALID)
+        return NULL;
+
+    pa_timeval_store(tv, v);
+
+    if (rtclock)
+        tv->tv_usec |= PA_TIMEVAL_RTCLOCK;
+    else
+        wallclock_from_rtclock(tv);
+
+    return tv;
+}
diff --git a/src/pulsecore/core-rtclock.h b/src/pulsecore/core-rtclock.h
new file mode 100644 (file)
index 0000000..89ad237
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef foopulsertclockhfoo
+#define foopulsertclockhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/macro.h>
+#include <pulse/sample.h>
+
+struct timeval;
+
+/* Something like pulse/timeval.h but based on CLOCK_MONOTONIC */
+
+struct timeval *pa_rtclock_get(struct timeval *ts);
+
+pa_usec_t pa_rtclock_age(const struct timeval *tv);
+bool pa_rtclock_hrtimer(void);
+void pa_rtclock_hrtimer_enable(void);
+
+/* timer with a resolution better than this are considered high-resolution */
+#define PA_HRTIMER_THRESHOLD_USEC 10
+
+/* bit to set in tv.tv_usec to mark that the timeval is in monotonic time */
+#define PA_TIMEVAL_RTCLOCK ((time_t) (1LU << 30))
+
+struct timeval* pa_rtclock_from_wallclock(struct timeval *tv);
+
+#ifdef HAVE_CLOCK_GETTIME
+struct timespec;
+
+pa_usec_t pa_timespec_load(const struct timespec *ts);
+struct timespec* pa_timespec_store(struct timespec *ts, pa_usec_t v);
+#endif
+
+struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, bool rtclock);
+
+#endif
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
new file mode 100644 (file)
index 0000000..026eeca
--- /dev/null
@@ -0,0 +1,521 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+#include <time.h>
+
+#ifdef HAVE_GLOB_H
+#include <glob.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <pulse/mainloop.h>
+#include <pulse/channelmap.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/play-memchunk.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sound-file.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/macro.h>
+
+#include "core-scache.h"
+
+#define UNLOAD_POLL_TIME (60 * PA_USEC_PER_SEC)
+
+static void timeout_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
+    pa_core *c = userdata;
+
+    pa_assert(c);
+    pa_assert(c->mainloop == m);
+    pa_assert(c->scache_auto_unload_event == e);
+
+    pa_scache_unload_unused(c);
+
+    pa_core_rttime_restart(c, e, pa_rtclock_now() + UNLOAD_POLL_TIME);
+}
+
+static void free_entry(pa_scache_entry *e) {
+    pa_assert(e);
+
+    pa_namereg_unregister(e->core, e->name);
+    pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index);
+    pa_hook_fire(&e->core->hooks[PA_CORE_HOOK_SAMPLE_CACHE_UNLINK], e);
+    pa_xfree(e->name);
+    pa_xfree(e->filename);
+    if (e->memchunk.memblock)
+        pa_memblock_unref(e->memchunk.memblock);
+    if (e->proplist)
+        pa_proplist_free(e->proplist);
+    pa_xfree(e);
+}
+
+static pa_scache_entry* scache_add_item(pa_core *c, const char *name, bool *new_sample) {
+    pa_scache_entry *e;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(new_sample);
+
+    if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) {
+        if (e->memchunk.memblock)
+            pa_memblock_unref(e->memchunk.memblock);
+
+        pa_xfree(e->filename);
+        pa_proplist_clear(e->proplist);
+
+        pa_assert(e->core == c);
+
+        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
+        *new_sample = false;
+    } else {
+        e = pa_xnew(pa_scache_entry, 1);
+
+        if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, true)) {
+            pa_xfree(e);
+            return NULL;
+        }
+
+        e->name = pa_xstrdup(name);
+        e->core = c;
+        e->proplist = pa_proplist_new();
+
+        pa_idxset_put(c->scache, e, &e->index);
+
+        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index);
+        *new_sample = true;
+    }
+
+    e->last_used_time = 0;
+    pa_memchunk_reset(&e->memchunk);
+    e->filename = NULL;
+    e->lazy = false;
+    e->last_used_time = 0;
+
+    pa_sample_spec_init(&e->sample_spec);
+    pa_channel_map_init(&e->channel_map);
+    pa_cvolume_init(&e->volume);
+    e->volume_is_set = false;
+
+    pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event");
+
+    return e;
+}
+
+int pa_scache_add_item(
+        pa_core *c,
+        const char *name,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        const pa_memchunk *chunk,
+        pa_proplist *p,
+        uint32_t *idx) {
+
+    pa_scache_entry *e;
+    char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
+    pa_channel_map tmap;
+    bool new_sample;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(!ss || pa_sample_spec_valid(ss));
+    pa_assert(!map || (pa_channel_map_valid(map) && ss && pa_channel_map_compatible(map, ss)));
+
+    if (ss && !map) {
+        pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+        map = &tmap;
+    }
+
+    if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
+        return -1;
+
+    if (!(e = scache_add_item(c, name, &new_sample)))
+        return -1;
+
+    pa_sample_spec_init(&e->sample_spec);
+    pa_channel_map_init(&e->channel_map);
+    pa_cvolume_init(&e->volume);
+    e->volume_is_set = false;
+
+    if (ss) {
+        e->sample_spec = *ss;
+        pa_cvolume_reset(&e->volume, ss->channels);
+    }
+
+    if (map)
+        e->channel_map = *map;
+
+    if (chunk) {
+        e->memchunk = *chunk;
+        pa_memblock_ref(e->memchunk.memblock);
+    }
+
+    if (p)
+        pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p);
+
+    if (idx)
+        *idx = e->index;
+
+    pa_log_debug("Created sample \"%s\" (#%d), %lu bytes with sample spec %s",
+                 name, e->index, (unsigned long) e->memchunk.length,
+                 pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec));
+
+    pa_hook_fire(&e->core->hooks[new_sample ? PA_CORE_HOOK_SAMPLE_CACHE_NEW : PA_CORE_HOOK_SAMPLE_CACHE_CHANGED], e);
+
+    return 0;
+}
+
+int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_memchunk chunk;
+    int r;
+    pa_proplist *p;
+
+#ifdef OS_IS_WIN32
+    char buf[MAX_PATH];
+
+    if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
+        filename = buf;
+#endif
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(filename);
+
+    p = pa_proplist_new();
+    pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename);
+
+    if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk, p) < 0) {
+        pa_proplist_free(p);
+        return -1;
+    }
+
+    r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx);
+    pa_memblock_unref(chunk.memblock);
+    pa_proplist_free(p);
+
+    return r;
+}
+
+int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
+    pa_scache_entry *e;
+    bool new_sample;
+
+#ifdef OS_IS_WIN32
+    char buf[MAX_PATH];
+
+    if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
+        filename = buf;
+#endif
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(filename);
+
+    if (!(e = scache_add_item(c, name, &new_sample)))
+        return -1;
+
+    e->lazy = true;
+    e->filename = pa_xstrdup(filename);
+
+    pa_proplist_sets(e->proplist, PA_PROP_MEDIA_FILENAME, filename);
+
+    if (!c->scache_auto_unload_event)
+        c->scache_auto_unload_event = pa_core_rttime_new(c, pa_rtclock_now() + UNLOAD_POLL_TIME, timeout_callback, c);
+
+    if (idx)
+        *idx = e->index;
+
+    pa_hook_fire(&e->core->hooks[new_sample ? PA_CORE_HOOK_SAMPLE_CACHE_NEW : PA_CORE_HOOK_SAMPLE_CACHE_CHANGED], e);
+
+    return 0;
+}
+
+int pa_scache_remove_item(pa_core *c, const char *name) {
+    pa_scache_entry *e;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
+        return -1;
+
+    pa_assert_se(pa_idxset_remove_by_data(c->scache, e, NULL) == e);
+
+    pa_log_debug("Removed sample \"%s\"", name);
+
+    free_entry(e);
+
+    return 0;
+}
+
+void pa_scache_free_all(pa_core *c) {
+    pa_assert(c);
+
+    pa_idxset_remove_all(c->scache, (pa_free_cb_t) free_entry);
+
+    if (c->scache_auto_unload_event) {
+        c->mainloop->time_free(c->scache_auto_unload_event);
+        c->scache_auto_unload_event = NULL;
+    }
+}
+
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
+    pa_scache_entry *e;
+    pa_cvolume r;
+    pa_proplist *merged;
+    bool pass_volume;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(sink);
+
+    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
+        return -1;
+
+    merged = pa_proplist_new();
+    pa_proplist_sets(merged, PA_PROP_MEDIA_NAME, name);
+    pa_proplist_sets(merged, PA_PROP_EVENT_ID, name);
+
+    if (e->lazy && !e->memchunk.memblock) {
+        pa_channel_map old_channel_map = e->channel_map;
+
+        if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk, merged) < 0)
+            goto fail;
+
+        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
+
+        if (e->volume_is_set) {
+            if (pa_cvolume_valid(&e->volume))
+                pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map);
+            else
+                pa_cvolume_reset(&e->volume, e->sample_spec.channels);
+        }
+    }
+
+    if (!e->memchunk.memblock)
+        goto fail;
+
+    pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
+
+    pass_volume = true;
+
+    if (e->volume_is_set && PA_VOLUME_IS_VALID(volume)) {
+        pa_cvolume_set(&r, e->sample_spec.channels, volume);
+        pa_sw_cvolume_multiply(&r, &r, &e->volume);
+    } else if (e->volume_is_set)
+        r = e->volume;
+    else if (PA_VOLUME_IS_VALID(volume))
+        pa_cvolume_set(&r, e->sample_spec.channels, volume);
+    else
+        pass_volume = false;
+
+    pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist);
+
+    if (p)
+        pa_proplist_update(merged, PA_UPDATE_REPLACE, p);
+
+    if (pa_play_memchunk(sink,
+                         &e->sample_spec, &e->channel_map,
+                         &e->memchunk,
+                         pass_volume ? &r : NULL,
+                         merged,
+                         PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND, sink_input_idx) < 0)
+        goto fail;
+
+    pa_proplist_free(merged);
+
+    if (e->lazy)
+        time(&e->last_used_time);
+
+    return 0;
+
+fail:
+    pa_proplist_free(merged);
+    return -1;
+}
+
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
+    pa_sink *sink;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK)))
+        return -1;
+
+    return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx);
+}
+
+const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
+    pa_scache_entry *e;
+
+    pa_assert(c);
+    pa_assert(id != PA_IDXSET_INVALID);
+
+    if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id)))
+        return NULL;
+
+    return e->name;
+}
+
+uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
+    pa_scache_entry *e;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
+        return PA_IDXSET_INVALID;
+
+    return e->index;
+}
+
+size_t pa_scache_total_size(pa_core *c) {
+    pa_scache_entry *e;
+    uint32_t idx;
+    size_t sum = 0;
+
+    pa_assert(c);
+
+    if (!c->scache || !pa_idxset_size(c->scache))
+        return 0;
+
+    PA_IDXSET_FOREACH(e, c->scache, idx)
+        if (e->memchunk.memblock)
+            sum += e->memchunk.length;
+
+    return sum;
+}
+
+void pa_scache_unload_unused(pa_core *c) {
+    pa_scache_entry *e;
+    time_t now;
+    uint32_t idx;
+
+    pa_assert(c);
+
+    if (!c->scache || !pa_idxset_size(c->scache))
+        return;
+
+    time(&now);
+
+    PA_IDXSET_FOREACH(e, c->scache, idx) {
+
+        if (!e->lazy || !e->memchunk.memblock)
+            continue;
+
+        if (e->last_used_time + c->scache_idle_time > now)
+            continue;
+
+        pa_memblock_unref(e->memchunk.memblock);
+        pa_memchunk_reset(&e->memchunk);
+
+        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
+    }
+}
+
+static void add_file(pa_core *c, const char *pathname) {
+    struct stat st;
+    const char *e;
+
+    pa_core_assert_ref(c);
+    pa_assert(pathname);
+
+    e = pa_path_get_filename(pathname);
+
+    if (stat(pathname, &st) < 0) {
+        pa_log("stat('%s'): %s", pathname, pa_cstrerror(errno));
+        return;
+    }
+
+#if defined(S_ISREG) && defined(S_ISLNK)
+    if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
+#endif
+        pa_scache_add_file_lazy(c, e, pathname, NULL);
+}
+
+int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
+    DIR *dir;
+
+    pa_core_assert_ref(c);
+    pa_assert(pathname);
+
+    /* First try to open this as directory */
+    if (!(dir = opendir(pathname))) {
+#ifdef HAVE_GLOB_H
+        glob_t p;
+        unsigned int i;
+        /* If that fails, try to open it as shell glob */
+
+        if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) {
+            pa_log("failed to open directory '%s': %s", pathname, pa_cstrerror(errno));
+            return -1;
+        }
+
+        for (i = 0; i < p.gl_pathc; i++)
+            add_file(c, p.gl_pathv[i]);
+
+        globfree(&p);
+#else
+        return -1;
+#endif
+    } else {
+        struct dirent *e;
+
+        while ((e = readdir(dir))) {
+            char *p;
+
+            if (e->d_name[0] == '.')
+                continue;
+
+            p = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", pathname, e->d_name);
+            add_file(c, p);
+            pa_xfree(p);
+        }
+
+        closedir(dir);
+    }
+
+    return 0;
+}
diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h
new file mode 100644 (file)
index 0000000..f0eacaa
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef foocorescachehfoo
+#define foocorescachehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+
+#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*16)
+
+typedef struct pa_scache_entry {
+    uint32_t index;
+    pa_core *core;
+
+    char *name;
+
+    pa_cvolume volume;
+    bool volume_is_set;
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    pa_memchunk memchunk;
+
+    char *filename;
+
+    bool lazy;
+    time_t last_used_time;
+
+    pa_proplist *proplist;
+} pa_scache_entry;
+
+int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, pa_proplist *p, uint32_t *idx);
+int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx);
+int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx);
+
+int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
+
+int pa_scache_remove_item(pa_core *c, const char *name);
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
+void pa_scache_free_all(pa_core *c);
+
+const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
+uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name);
+
+size_t pa_scache_total_size(pa_core *c);
+
+void pa_scache_unload_unused(pa_core *c);
+
+#endif
diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c
new file mode 100644 (file)
index 0000000..61c779b
--- /dev/null
@@ -0,0 +1,261 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "core-subscribe.h"
+
+/* The subscription subsystem may be used to be notified whenever an
+ * entity (sink, source, ...) is created or deleted. Modules may
+ * register a callback function that is called whenever an event
+ * matching a subscription mask happens. The execution of the callback
+ * function is postponed to the next main loop iteration, i.e. is not
+ * called from within the stack frame the entity was created in. */
+
+struct pa_subscription {
+    pa_core *core;
+    bool dead;
+
+    pa_subscription_cb_t callback;
+    void *userdata;
+    pa_subscription_mask_t mask;
+
+    PA_LLIST_FIELDS(pa_subscription);
+};
+
+struct pa_subscription_event {
+    pa_core *core;
+
+    pa_subscription_event_type_t type;
+    uint32_t index;
+
+    PA_LLIST_FIELDS(pa_subscription_event);
+};
+
+static void sched_event(pa_core *c);
+
+/* Allocate a new subscription object for the given subscription mask. Use the specified callback function and user data */
+pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_subscription_cb_t callback, void *userdata) {
+    pa_subscription *s;
+
+    pa_assert(c);
+    pa_assert(m);
+    pa_assert(callback);
+
+    s = pa_xnew(pa_subscription, 1);
+    s->core = c;
+    s->dead = false;
+    s->callback = callback;
+    s->userdata = userdata;
+    s->mask = m;
+
+    PA_LLIST_PREPEND(pa_subscription, c->subscriptions, s);
+    return s;
+}
+
+/* Free a subscription object, effectively marking it for deletion */
+void pa_subscription_free(pa_subscription*s) {
+    pa_assert(s);
+    pa_assert(!s->dead);
+
+    s->dead = true;
+    sched_event(s->core);
+}
+
+static void free_subscription(pa_subscription *s) {
+    pa_assert(s);
+    pa_assert(s->core);
+
+    PA_LLIST_REMOVE(pa_subscription, s->core->subscriptions, s);
+    pa_xfree(s);
+}
+
+static void free_event(pa_subscription_event *s) {
+    pa_assert(s);
+    pa_assert(s->core);
+
+    if (!s->next)
+        s->core->subscription_event_last = s->prev;
+
+    PA_LLIST_REMOVE(pa_subscription_event, s->core->subscription_event_queue, s);
+    pa_xfree(s);
+}
+
+/* Free all subscription objects */
+void pa_subscription_free_all(pa_core *c) {
+    pa_assert(c);
+
+    while (c->subscriptions)
+        free_subscription(c->subscriptions);
+
+    while (c->subscription_event_queue)
+        free_event(c->subscription_event_queue);
+
+    if (c->subscription_defer_event) {
+        c->mainloop->defer_free(c->subscription_defer_event);
+        c->subscription_defer_event = NULL;
+    }
+}
+
+#ifdef DEBUG
+static void dump_event(const char * prefix, pa_subscription_event*e) {
+    const char * const fac_table[] = {
+        [PA_SUBSCRIPTION_EVENT_SINK] = "SINK",
+        [PA_SUBSCRIPTION_EVENT_SOURCE] = "SOURCE",
+        [PA_SUBSCRIPTION_EVENT_SINK_INPUT] = "SINK_INPUT",
+        [PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT] = "SOURCE_OUTPUT",
+        [PA_SUBSCRIPTION_EVENT_MODULE] = "MODULE",
+        [PA_SUBSCRIPTION_EVENT_CLIENT] = "CLIENT",
+        [PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE] = "SAMPLE_CACHE",
+        [PA_SUBSCRIPTION_EVENT_SERVER] = "SERVER",
+        [PA_SUBSCRIPTION_EVENT_AUTOLOAD] = "AUTOLOAD"
+    };
+
+    const char * const type_table[] = {
+        [PA_SUBSCRIPTION_EVENT_NEW] = "NEW",
+        [PA_SUBSCRIPTION_EVENT_CHANGE] = "CHANGE",
+        [PA_SUBSCRIPTION_EVENT_REMOVE] = "REMOVE"
+    };
+
+    pa_log_debug("%s event (%s|%s|%u)",
+           prefix,
+           fac_table[e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK],
+           type_table[e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK],
+           e->index);
+}
+#endif
+
+/* Deferred callback for dispatching subscription events */
+static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
+    pa_core *c = userdata;
+    pa_subscription *s;
+
+    pa_assert(c->mainloop == m);
+    pa_assert(c);
+    pa_assert(c->subscription_defer_event == de);
+
+    c->mainloop->defer_enable(c->subscription_defer_event, 0);
+
+    /* Dispatch queued events */
+
+    while (c->subscription_event_queue) {
+        pa_subscription_event *e = c->subscription_event_queue;
+
+        for (s = c->subscriptions; s; s = s->next) {
+
+            if (!s->dead && pa_subscription_match_flags(s->mask, e->type))
+                s->callback(c, e->type, e->index, s->userdata);
+        }
+
+#ifdef DEBUG
+        dump_event("Dispatched", e);
+#endif
+        free_event(e);
+    }
+
+    /* Remove dead subscriptions */
+
+    s = c->subscriptions;
+    while (s) {
+        pa_subscription *n = s->next;
+        if (s->dead)
+            free_subscription(s);
+        s = n;
+    }
+}
+
+/* Schedule an mainloop event so that a pending subscription event is dispatched */
+static void sched_event(pa_core *c) {
+    pa_assert(c);
+
+    if (!c->subscription_defer_event) {
+        c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c);
+        pa_assert(c->subscription_defer_event);
+    }
+
+    c->mainloop->defer_enable(c->subscription_defer_event, 1);
+}
+
+/* Append a new subscription event to the subscription event queue and schedule a main loop event */
+void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx) {
+    pa_subscription_event *e;
+    pa_assert(c);
+
+    /* No need for queuing subscriptions of no one is listening */
+    if (!c->subscriptions)
+        return;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_NEW) {
+        pa_subscription_event *i, *n;
+
+        /* Check for duplicates */
+        for (i = c->subscription_event_last; i; i = n) {
+            n = i->prev;
+
+            /* not the same object type */
+            if (((t ^ i->type) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))
+                continue;
+
+            /* not the same object */
+            if (i->index != idx)
+                continue;
+
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                /* This object is being removed, hence there is no
+                 * point in keeping the old events regarding this
+                 * entry in the queue. */
+
+                free_event(i);
+                pa_log_debug("Dropped redundant event due to remove event.");
+                continue;
+            }
+
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
+                /* This object has changed. If a "new" or "change" event for
+                 * this object is still in the queue we can exit. */
+
+                pa_log_debug("Dropped redundant event due to change event.");
+                return;
+            }
+        }
+    }
+
+    e = pa_xnew(pa_subscription_event, 1);
+    e->core = c;
+    e->type = t;
+    e->index = idx;
+
+    PA_LLIST_INSERT_AFTER(pa_subscription_event, c->subscription_event_queue, c->subscription_event_last, e);
+    c->subscription_event_last = e;
+
+#ifdef DEBUG
+    dump_event("Queued", e);
+#endif
+
+    sched_event(c);
+}
diff --git a/src/pulsecore/core-subscribe.h b/src/pulsecore/core-subscribe.h
new file mode 100644 (file)
index 0000000..6032dc3
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef foocoresubscribehfoo
+#define foocoresubscribehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct pa_subscription pa_subscription;
+typedef struct pa_subscription_event pa_subscription_event;
+
+#include <pulsecore/core.h>
+#include <pulsecore/native-common.h>
+
+typedef void (*pa_subscription_cb_t)(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
+
+pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m,  pa_subscription_cb_t cb, void *userdata);
+void pa_subscription_free(pa_subscription*s);
+void pa_subscription_free_all(pa_core *c);
+
+void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx);
+
+#endif
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
new file mode 100644 (file)
index 0000000..d4cfa20
--- /dev/null
@@ -0,0 +1,3749 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2004 Joe Marcus Clarke
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+
+#ifdef HAVE_UNAME
+#include <sys/utsname.h>
+#endif
+
+#if defined(HAVE_REGEX_H)
+#include <regex.h>
+#elif defined(HAVE_PCREPOSIX_H)
+#include <pcreposix.h>
+#endif
+
+#ifdef HAVE_STRTOD_L
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#ifdef HAVE_XLOCALE_H
+#include <xlocale.h>
+#endif
+#endif
+
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+
+#if defined(__linux__) && !defined(SCHED_RESET_ON_FORK)
+#define SCHED_RESET_ON_FORK 0x40000000
+#endif
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#ifndef ENOTSUP
+#define ENOTSUP   135
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_LIBSAMPLERATE
+#include <samplerate.h>
+#endif
+
+#ifdef __APPLE__
+#include <mach/mach_init.h>
+#include <mach/thread_act.h>
+#include <mach/thread_policy.h>
+#include <sys/sysctl.h>
+#endif
+
+#ifdef HAVE_DBUS
+#include "rtkit.h"
+#endif
+
+#if defined(__linux__) && !defined(__ANDROID__)
+#include <sys/personality.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/usergroup.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/cpu-x86.h>
+#include <pulsecore/pipe.h>
+#include <pulsecore/once.h>
+
+#include "core-util.h"
+
+/* Not all platforms have this */
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+#define NEWLINE "\r\n"
+#define WHITESPACE "\n\r \t"
+
+static pa_strlist *recorded_env = NULL;
+
+#ifdef OS_IS_WIN32
+static fd_set nonblocking_fds;
+#endif
+
+#ifdef OS_IS_WIN32
+
+/* Returns the directory of the current DLL, with '/bin/' removed if it is the last component */
+char *pa_win32_get_toplevel(HANDLE handle) {
+    static char *toplevel = NULL;
+
+    if (!toplevel) {
+        char library_path[MAX_PATH];
+        char *p;
+
+        if (!GetModuleFileName(handle, library_path, MAX_PATH))
+            return NULL;
+
+        toplevel = pa_xstrdup(library_path);
+
+        p = strrchr(toplevel, PA_PATH_SEP_CHAR);
+        if (p)
+            *p = '\0';
+
+        p = strrchr(toplevel, PA_PATH_SEP_CHAR);
+        if (p && pa_streq(p + 1, "bin"))
+            *p = '\0';
+    }
+
+    return toplevel;
+}
+
+#endif
+
+static void set_nonblock(int fd, bool nonblock) {
+
+#ifdef O_NONBLOCK
+    int v, nv;
+    pa_assert(fd >= 0);
+
+    pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0);
+
+    if (nonblock)
+        nv = v | O_NONBLOCK;
+    else
+        nv = v & ~O_NONBLOCK;
+
+    if (v != nv)
+        pa_assert_se(fcntl(fd, F_SETFL, v|O_NONBLOCK) >= 0);
+
+#elif defined(OS_IS_WIN32)
+    u_long arg;
+
+    if (nonblock)
+        arg = 1;
+    else
+        arg = 0;
+
+    if (ioctlsocket(fd, FIONBIO, &arg) < 0) {
+        pa_assert_se(WSAGetLastError() == WSAENOTSOCK);
+        pa_log_warn("Only sockets can be made non-blocking!");
+        return;
+    }
+
+    /* There is no method to query status, so we remember all fds */
+    if (nonblock)
+        FD_SET(fd, &nonblocking_fds);
+    else
+        FD_CLR(fd, &nonblocking_fds);
+#else
+    pa_log_warn("Non-blocking I/O not supported.!");
+#endif
+
+}
+
+/** Make a file descriptor nonblock. Doesn't do any error checking */
+void pa_make_fd_nonblock(int fd) {
+    set_nonblock(fd, true);
+}
+
+/** Make a file descriptor blocking. Doesn't do any error checking */
+void pa_make_fd_block(int fd) {
+    set_nonblock(fd, false);
+}
+
+/** Query if a file descriptor is non-blocking */
+bool pa_is_fd_nonblock(int fd) {
+
+#ifdef O_NONBLOCK
+    int v;
+    pa_assert(fd >= 0);
+
+    pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0);
+
+    return !!(v & O_NONBLOCK);
+
+#elif defined(OS_IS_WIN32)
+    return !!FD_ISSET(fd, &nonblocking_fds);
+#else
+    return false;
+#endif
+
+}
+
+/* Set the FD_CLOEXEC flag for a fd */
+void pa_make_fd_cloexec(int fd) {
+
+#ifdef FD_CLOEXEC
+    int v;
+    pa_assert(fd >= 0);
+
+    pa_assert_se((v = fcntl(fd, F_GETFD, 0)) >= 0);
+
+    if (!(v & FD_CLOEXEC))
+        pa_assert_se(fcntl(fd, F_SETFD, v|FD_CLOEXEC) >= 0);
+#endif
+
+}
+
+/** Creates a directory securely. Will create parent directories recursively if
+ * required. This will not update permissions on parent directories if they
+ * already exist, however. */
+int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid, bool update_perms) {
+    struct stat st;
+    int r, saved_errno;
+    bool retry = true;
+
+    pa_assert(dir);
+
+again:
+#ifdef OS_IS_WIN32
+    r = mkdir(dir);
+#else
+{
+    mode_t u;
+    u = umask((~m) & 0777);
+    r = mkdir(dir, m);
+    umask(u);
+}
+#endif
+
+    if (r < 0 && errno == ENOENT && retry) {
+        /* If a parent directory in the path doesn't exist, try to create that
+         * first, then try again. */
+        pa_make_secure_parent_dir(dir, m, uid, gid, false);
+        retry = false;
+        goto again;
+    }
+
+    if (r < 0 && errno != EEXIST)
+        return -1;
+
+#if defined(HAVE_FSTAT) && !defined(OS_IS_WIN32)
+{
+    int fd;
+    if ((fd = open(dir,
+#ifdef O_CLOEXEC
+                   O_CLOEXEC|
+#endif
+#ifdef O_NOCTTY
+                   O_NOCTTY|
+#endif
+#ifdef O_NOFOLLOW
+                   O_NOFOLLOW|
+#endif
+                   O_RDONLY)) < 0)
+        goto fail;
+
+    if (fstat(fd, &st) < 0) {
+        pa_assert_se(pa_close(fd) >= 0);
+        goto fail;
+    }
+
+    if (!S_ISDIR(st.st_mode)) {
+        pa_assert_se(pa_close(fd) >= 0);
+        errno = EEXIST;
+        goto fail;
+    }
+
+    if (!update_perms) {
+        pa_assert_se(pa_close(fd) >= 0);
+        return 0;
+    }
+
+#ifdef HAVE_FCHOWN
+    if (uid == (uid_t) -1)
+        uid = getuid();
+    if (gid == (gid_t) -1)
+        gid = getgid();
+    if (((st.st_uid != uid) || (st.st_gid != gid)) && fchown(fd, uid, gid) < 0) {
+        pa_assert_se(pa_close(fd) >= 0);
+        goto fail;
+    }
+#endif
+
+#ifdef HAVE_FCHMOD
+    if ((st.st_mode & 07777) != m && fchmod(fd, m) < 0) {
+        pa_assert_se(pa_close(fd) >= 0);
+        goto fail;
+    };
+#endif
+
+    pa_assert_se(pa_close(fd) >= 0);
+}
+#else
+    pa_log_warn("Secure directory creation not supported on this platform.");
+#endif
+
+    return 0;
+
+fail:
+    saved_errno = errno;
+    rmdir(dir);
+    errno = saved_errno;
+
+    return -1;
+}
+
+/* Return a newly allocated sting containing the parent directory of the specified file */
+char *pa_parent_dir(const char *fn) {
+    char *slash, *dir = pa_xstrdup(fn);
+
+    if ((slash = (char*) pa_path_get_filename(dir)) == dir) {
+        pa_xfree(dir);
+        errno = ENOENT;
+        return NULL;
+    }
+
+    *(slash-1) = 0;
+    return dir;
+}
+
+/* Creates a the parent directory of the specified path securely */
+int pa_make_secure_parent_dir(const char *fn, mode_t m, uid_t uid, gid_t gid, bool update_perms) {
+    int ret = -1;
+    char *dir;
+
+    if (!(dir = pa_parent_dir(fn)))
+        goto finish;
+
+    if (pa_make_secure_dir(dir, m, uid, gid, update_perms) < 0)
+        goto finish;
+
+    ret = 0;
+
+finish:
+    pa_xfree(dir);
+    return ret;
+}
+
+/** Platform independent read function. Necessary since not all
+ * systems treat all file descriptors equal. If type is
+ * non-NULL it is used to cache the type of the fd. This is
+ * useful for making sure that only a single syscall is executed per
+ * function call. The variable pointed to should be initialized to 0
+ * by the caller. */
+ssize_t pa_read(int fd, void *buf, size_t count, int *type) {
+
+#ifdef OS_IS_WIN32
+
+    if (!type || *type == 0) {
+        ssize_t r;
+
+        if ((r = recv(fd, buf, count, 0)) >= 0)
+            return r;
+
+        if (WSAGetLastError() != WSAENOTSOCK) {
+            errno = WSAGetLastError();
+            return r;
+        }
+
+        if (type)
+            *type = 1;
+    }
+
+#endif
+
+    for (;;) {
+        ssize_t r;
+
+        if ((r = read(fd, buf, count)) < 0)
+            if (errno == EINTR)
+                continue;
+
+        return r;
+    }
+}
+
+/** Similar to pa_read(), but handles writes */
+ssize_t pa_write(int fd, const void *buf, size_t count, int *type) {
+
+    if (!type || *type == 0) {
+        ssize_t r;
+
+        for (;;) {
+            if ((r = send(fd, buf, count, MSG_NOSIGNAL)) < 0) {
+
+                if (errno == EINTR)
+                    continue;
+
+                break;
+            }
+
+            return r;
+        }
+
+#ifdef OS_IS_WIN32
+        if (WSAGetLastError() != WSAENOTSOCK) {
+            errno = WSAGetLastError();
+            return r;
+        }
+#else
+        if (errno != ENOTSOCK)
+            return r;
+#endif
+
+        if (type)
+            *type = 1;
+    }
+
+    for (;;) {
+        ssize_t r;
+
+        if ((r = write(fd, buf, count)) < 0)
+            if (errno == EINTR)
+                continue;
+
+        return r;
+    }
+}
+
+/** Calls read() in a loop. Makes sure that as much as 'size' bytes,
+ * unless EOF is reached or an error occurred */
+ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) {
+    ssize_t ret = 0;
+    int _type;
+
+    pa_assert(fd >= 0);
+    pa_assert(data);
+    pa_assert(size);
+
+    if (!type) {
+        _type = 0;
+        type = &_type;
+    }
+
+    while (size > 0) {
+        ssize_t r;
+
+        if ((r = pa_read(fd, data, size, type)) < 0)
+            return r;
+
+        if (r == 0)
+            break;
+
+        ret += r;
+        data = (uint8_t*) data + r;
+        size -= (size_t) r;
+    }
+
+    return ret;
+}
+
+/** Similar to pa_loop_read(), but wraps write() */
+ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {
+    ssize_t ret = 0;
+    int _type;
+
+    pa_assert(fd >= 0);
+    pa_assert(data);
+    pa_assert(size);
+
+    if (!type) {
+        _type = 0;
+        type = &_type;
+    }
+
+    while (size > 0) {
+        ssize_t r;
+
+        if ((r = pa_write(fd, data, size, type)) < 0)
+            return r;
+
+        if (r == 0)
+            break;
+
+        ret += r;
+        data = (const uint8_t*) data + r;
+        size -= (size_t) r;
+    }
+
+    return ret;
+}
+
+/** Platform independent close function. Necessary since not all
+ * systems treat all file descriptors equal. */
+int pa_close(int fd) {
+
+#ifdef OS_IS_WIN32
+    int ret;
+
+    FD_CLR(fd, &nonblocking_fds);
+
+    if ((ret = closesocket(fd)) == 0)
+        return 0;
+
+    if (WSAGetLastError() != WSAENOTSOCK) {
+        errno = WSAGetLastError();
+        return ret;
+    }
+#endif
+
+    for (;;) {
+        int r;
+
+        if ((r = close(fd)) < 0)
+            if (errno == EINTR)
+                continue;
+
+        return r;
+    }
+}
+
+/* Print a warning messages in case that the given signal is not
+ * blocked or trapped */
+void pa_check_signal_is_blocked(int sig) {
+#ifdef HAVE_SIGACTION
+    struct sigaction sa;
+    sigset_t set;
+
+    /* If POSIX threads are supported use thread-aware
+     * pthread_sigmask() function, to check if the signal is
+     * blocked. Otherwise fall back to sigprocmask() */
+
+#ifdef HAVE_PTHREAD
+    if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) {
+#endif
+        if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) {
+            pa_log("sigprocmask(): %s", pa_cstrerror(errno));
+            return;
+        }
+#ifdef HAVE_PTHREAD
+    }
+#endif
+
+    if (sigismember(&set, sig))
+        return;
+
+    /* Check whether the signal is trapped */
+
+    if (sigaction(sig, NULL, &sa) < 0) {
+        pa_log("sigaction(): %s", pa_cstrerror(errno));
+        return;
+    }
+
+    if (sa.sa_handler != SIG_DFL)
+        return;
+
+    pa_log_warn("%s is not trapped. This might cause malfunction!", pa_sig2str(sig));
+#else /* HAVE_SIGACTION */
+    pa_log_warn("%s might not be trapped. This might cause malfunction!", pa_sig2str(sig));
+#endif
+}
+
+/* The following function is based on an example from the GNU libc
+ * documentation. This function is similar to GNU's asprintf(). */
+char *pa_sprintf_malloc(const char *format, ...) {
+    size_t size = 100;
+    char *c = NULL;
+
+    pa_assert(format);
+
+    for(;;) {
+        int r;
+        va_list ap;
+
+        c = pa_xrealloc(c, size);
+
+        va_start(ap, format);
+        r = vsnprintf(c, size, format, ap);
+        va_end(ap);
+
+        c[size-1] = 0;
+
+        if (r > -1 && (size_t) r < size)
+            return c;
+
+        if (r > -1)    /* glibc 2.1 */
+            size = (size_t) r+1;
+        else           /* glibc 2.0 */
+            size *= 2;
+    }
+}
+
+/* Same as the previous function, but use a va_list instead of an
+ * ellipsis */
+char *pa_vsprintf_malloc(const char *format, va_list ap) {
+    size_t size = 100;
+    char *c = NULL;
+
+    pa_assert(format);
+
+    for(;;) {
+        int r;
+        va_list aq;
+
+        c = pa_xrealloc(c, size);
+
+        va_copy(aq, ap);
+        r = vsnprintf(c, size, format, aq);
+        va_end(aq);
+
+        c[size-1] = 0;
+
+        if (r > -1 && (size_t) r < size)
+            return c;
+
+        if (r > -1)    /* glibc 2.1 */
+            size = (size_t) r+1;
+        else           /* glibc 2.0 */
+            size *= 2;
+    }
+}
+
+/* Similar to OpenBSD's strlcpy() function */
+char *pa_strlcpy(char *b, const char *s, size_t l) {
+    size_t k;
+
+    pa_assert(b);
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    k = strlen(s);
+
+    if (k > l-1)
+        k = l-1;
+
+    memcpy(b, s, k);
+    b[k] = 0;
+
+    return b;
+}
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+static int set_scheduler(int rtprio) {
+#ifdef HAVE_SCHED_H
+    struct sched_param sp;
+#ifdef HAVE_DBUS
+    int r;
+    long long rttime;
+#ifdef RLIMIT_RTTIME
+    struct rlimit rl;
+#endif
+    DBusError error;
+    DBusConnection *bus;
+
+    dbus_error_init(&error);
+#endif
+
+    pa_zero(sp);
+    sp.sched_priority = rtprio;
+
+#ifdef SCHED_RESET_ON_FORK
+    if (pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp) == 0) {
+        pa_log_debug("SCHED_RR|SCHED_RESET_ON_FORK worked.");
+        return 0;
+    }
+#endif
+
+    if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == 0) {
+        pa_log_debug("SCHED_RR worked.");
+        return 0;
+    }
+#endif  /* HAVE_SCHED_H */
+
+#ifdef HAVE_DBUS
+    /* Try to talk to RealtimeKit */
+
+    if (!(bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) {
+        pa_log("Failed to connect to system bus: %s", error.message);
+        dbus_error_free(&error);
+        errno = -EIO;
+        return -1;
+    }
+
+    /* We need to disable exit on disconnect because otherwise
+     * dbus_shutdown will kill us. See
+     * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */
+    dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
+    rttime = rtkit_get_rttime_usec_max(bus);
+    if (rttime >= 0) {
+#ifdef RLIMIT_RTTIME
+        r = getrlimit(RLIMIT_RTTIME, &rl);
+
+        if (r >= 0 && (long long) rl.rlim_max > rttime) {
+            pa_log_info("Clamping rlimit-rttime to %lld for RealtimeKit", rttime);
+            rl.rlim_cur = rl.rlim_max = rttime;
+            r = setrlimit(RLIMIT_RTTIME, &rl);
+
+            if (r < 0)
+                pa_log("setrlimit() failed: %s", pa_cstrerror(errno));
+        }
+#endif
+        r = rtkit_make_realtime(bus, 0, rtprio);
+        dbus_connection_close(bus);
+        dbus_connection_unref(bus);
+
+        if (r >= 0) {
+            pa_log_debug("RealtimeKit worked.");
+            return 0;
+        }
+
+        errno = -r;
+    } else {
+        dbus_connection_close(bus);
+        dbus_connection_unref(bus);
+        errno = -rttime;
+    }
+
+#else
+    errno = 0;
+#endif
+
+    return -1;
+}
+#endif
+
+/* Make the current thread a realtime thread, and acquire the highest
+ * rtprio we can get that is less or equal the specified parameter. If
+ * the thread is already realtime, don't do anything. */
+int pa_make_realtime(int rtprio) {
+
+#if defined(OS_IS_DARWIN)
+    struct thread_time_constraint_policy ttcpolicy;
+    uint64_t freq = 0;
+    size_t size = sizeof(freq);
+    int ret;
+
+    ret = sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0);
+    if (ret < 0) {
+        pa_log_info("Unable to read CPU frequency, acquisition of real-time scheduling failed.");
+        return -1;
+    }
+
+    pa_log_debug("sysctl for hw.cpufrequency: %llu", freq);
+
+    /* See http://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/KernelProgramming/scheduler/scheduler.html */
+    ttcpolicy.period = freq / 160;
+    ttcpolicy.computation = freq / 3300;
+    ttcpolicy.constraint = freq / 2200;
+    ttcpolicy.preemptible = 1;
+
+    ret = thread_policy_set(mach_thread_self(),
+                            THREAD_TIME_CONSTRAINT_POLICY,
+                            (thread_policy_t) &ttcpolicy,
+                            THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+    if (ret) {
+        pa_log_info("Unable to set real-time thread priority (%08x).", ret);
+        return -1;
+    }
+
+    pa_log_info("Successfully acquired real-time thread priority.");
+    return 0;
+
+#elif defined(_POSIX_PRIORITY_SCHEDULING)
+    int p;
+
+    if (set_scheduler(rtprio) >= 0) {
+        pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i.", rtprio);
+        return 0;
+    }
+
+    for (p = rtprio-1; p >= 1; p--)
+        if (set_scheduler(p) >= 0) {
+            pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", p, rtprio);
+            return 0;
+        }
+#elif defined(OS_IS_WIN32)
+    /* Windows only allows realtime scheduling to be set on a per process basis.
+     * Therefore, instead of making the thread realtime, just give it the highest non-realtime priority. */
+    if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) {
+        pa_log_info("Successfully enabled THREAD_PRIORITY_TIME_CRITICAL scheduling for thread.");
+        return 0;
+    }
+
+    pa_log_warn("SetThreadPriority() failed: 0x%08X", GetLastError());
+    errno = EPERM;
+#else
+    errno = ENOTSUP;
+#endif
+    pa_log_info("Failed to acquire real-time scheduling: %s", pa_cstrerror(errno));
+    return -1;
+}
+
+#ifdef HAVE_SYS_RESOURCE_H
+static int set_nice(int nice_level) {
+#ifdef HAVE_DBUS
+    DBusError error;
+    DBusConnection *bus;
+    int r;
+
+    dbus_error_init(&error);
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+    if (setpriority(PRIO_PROCESS, 0, nice_level) >= 0) {
+        pa_log_debug("setpriority() worked.");
+        return 0;
+    }
+#endif
+
+#ifdef HAVE_DBUS
+    /* Try to talk to RealtimeKit */
+
+    if (!(bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) {
+        pa_log("Failed to connect to system bus: %s", error.message);
+        dbus_error_free(&error);
+        errno = -EIO;
+        return -1;
+    }
+
+    /* We need to disable exit on disconnect because otherwise
+     * dbus_shutdown will kill us. See
+     * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */
+    dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
+    r = rtkit_make_high_priority(bus, 0, nice_level);
+    dbus_connection_close(bus);
+    dbus_connection_unref(bus);
+
+    if (r >= 0) {
+        pa_log_debug("RealtimeKit worked.");
+        return 0;
+    }
+
+    errno = -r;
+#endif
+
+    return -1;
+}
+#endif
+
+/* Raise the priority of the current process as much as possible that
+ * is <= the specified nice level..*/
+int pa_raise_priority(int nice_level) {
+
+#ifdef HAVE_SYS_RESOURCE_H
+    int n;
+
+    if (set_nice(nice_level) >= 0) {
+        pa_log_info("Successfully gained nice level %i.", nice_level);
+        return 0;
+    }
+
+    for (n = nice_level+1; n < 0; n++)
+        if (set_nice(n) >= 0) {
+            pa_log_info("Successfully acquired nice level %i, which is lower than the requested %i.", n, nice_level);
+            return 0;
+        }
+
+    pa_log_info("Failed to acquire high-priority scheduling: %s", pa_cstrerror(errno));
+    return -1;
+#endif
+
+#ifdef OS_IS_WIN32
+    if (nice_level < 0) {
+        if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
+            pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
+            errno = EPERM;
+            return -1;
+        }
+
+        pa_log_info("Successfully gained high priority class.");
+    }
+#endif
+
+    return 0;
+}
+
+/* Reset the priority to normal, inverting the changes made by
+ * pa_raise_priority() and pa_make_realtime()*/
+void pa_reset_priority(void) {
+#ifdef HAVE_SYS_RESOURCE_H
+    struct sched_param sp;
+
+    setpriority(PRIO_PROCESS, 0, 0);
+
+    pa_zero(sp);
+    pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp);
+#endif
+
+#ifdef OS_IS_WIN32
+    SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
+#endif
+}
+
+int pa_match(const char *expr, const char *v) {
+#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
+    int k;
+    regex_t re;
+    int r;
+
+    if (regcomp(&re, expr, REG_NOSUB|REG_EXTENDED) != 0) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    if ((k = regexec(&re, v, 0, NULL, 0)) == 0)
+        r = 1;
+    else if (k == REG_NOMATCH)
+        r = 0;
+    else
+        r = -1;
+
+    regfree(&re);
+
+    if (r < 0)
+        errno = EINVAL;
+
+    return r;
+#else
+    errno = ENOSYS;
+    return -1;
+#endif
+}
+
+/* Try to parse a boolean string value.*/
+int pa_parse_boolean(const char *v) {
+    pa_assert(v);
+
+    /* First we check language independent */
+    if (pa_streq(v, "1") || !strcasecmp(v, "y") || !strcasecmp(v, "t")
+            || !strcasecmp(v, "yes") || !strcasecmp(v, "true") || !strcasecmp(v, "on"))
+        return 1;
+    else if (pa_streq(v, "0") || !strcasecmp(v, "n") || !strcasecmp(v, "f")
+                 || !strcasecmp(v, "no") || !strcasecmp(v, "false") || !strcasecmp(v, "off"))
+        return 0;
+
+#ifdef HAVE_LANGINFO_H
+{
+    const char *expr;
+    /* And then we check language dependent */
+    if ((expr = nl_langinfo(YESEXPR)))
+        if (expr[0])
+            if (pa_match(expr, v) > 0)
+                return 1;
+
+    if ((expr = nl_langinfo(NOEXPR)))
+        if (expr[0])
+            if (pa_match(expr, v) > 0)
+                return 0;
+}
+#endif
+
+    errno = EINVAL;
+    return -1;
+}
+
+/* Try to parse a volume string to pa_volume_t. The allowed formats are:
+ * db, % and unsigned integer */
+int pa_parse_volume(const char *v, pa_volume_t *volume) {
+    int len;
+    uint32_t i;
+    double d;
+    char str[64];
+
+    pa_assert(v);
+    pa_assert(volume);
+
+    len = strlen(v);
+
+    if (len >= 64)
+        return -1;
+
+    memcpy(str, v, len + 1);
+
+    if (str[len - 1] == '%') {
+        str[len - 1] = '\0';
+        if (pa_atod(str, &d) < 0)
+            return -1;
+
+        d = d / 100 * PA_VOLUME_NORM;
+
+        if (d < 0 || d > PA_VOLUME_MAX)
+            return -1;
+
+        *volume = d;
+        return 0;
+    }
+
+    if (len > 2 && (str[len - 1] == 'b' || str[len - 1] == 'B') &&
+               (str[len - 2] == 'd' || str[len - 2] == 'D')) {
+        str[len - 2] = '\0';
+        if (pa_atod(str, &d) < 0)
+            return -1;
+
+        if (d > pa_sw_volume_to_dB(PA_VOLUME_MAX))
+            return -1;
+
+        *volume = pa_sw_volume_from_dB(d);
+        return 0;
+    }
+
+    if (pa_atou(v, &i) < 0 || !PA_VOLUME_IS_VALID(i))
+        return -1;
+
+    *volume = i;
+    return 0;
+}
+
+/* Split the specified string wherever one of the characters in delimiter
+ * occurs. Each time it is called returns a newly allocated string
+ * with pa_xmalloc(). The variable state points to, should be
+ * initialized to NULL before the first call. */
+char *pa_split(const char *c, const char *delimiter, const char**state) {
+    const char *current = *state ? *state : c;
+    size_t l;
+
+    if (!*current)
+        return NULL;
+
+    l = strcspn(current, delimiter);
+    *state = current+l;
+
+    if (**state)
+        (*state)++;
+
+    return pa_xstrndup(current, l);
+}
+
+/* Split the specified string wherever one of the characters in delimiter
+ * occurs. Each time it is called returns a pointer to the substring within the
+ * string and the length in 'n'. Note that the resultant string cannot be used
+ * as-is without the length parameter, since it is merely pointing to a point
+ * within the original string. The variable state points to, should be
+ * initialized to NULL before the first call. */
+const char *pa_split_in_place(const char *c, const char *delimiter, int *n, const char**state) {
+    const char *current = *state ? *state : c;
+    size_t l;
+
+    if (!*current)
+        return NULL;
+
+    l = strcspn(current, delimiter);
+    *state = current+l;
+
+    if (**state)
+        (*state)++;
+
+    *n = l;
+    return current;
+}
+
+/* Split a string into words. Otherwise similar to pa_split(). */
+char *pa_split_spaces(const char *c, const char **state) {
+    const char *current = *state ? *state : c;
+    size_t l;
+
+    if (!*current || *c == 0)
+        return NULL;
+
+    current += strspn(current, WHITESPACE);
+    l = strcspn(current, WHITESPACE);
+
+    *state = current+l;
+
+    return pa_xstrndup(current, l);
+}
+
+/* Similar to pa_split_spaces, except this returns a string in-place.
+   Returned string is generally not NULL-terminated.
+   See pa_split_in_place(). */
+const char *pa_split_spaces_in_place(const char *c, int *n, const char **state) {
+    const char *current = *state ? *state : c;
+    size_t l;
+
+    if (!*current || *c == 0)
+        return NULL;
+
+    current += strspn(current, WHITESPACE);
+    l = strcspn(current, WHITESPACE);
+
+    *state = current+l;
+
+    *n = l;
+    return current;
+}
+
+PA_STATIC_TLS_DECLARE(signame, pa_xfree);
+
+/* Return the name of an UNIX signal. Similar to Solaris sig2str() */
+const char *pa_sig2str(int sig) {
+    char *t;
+
+    if (sig <= 0)
+        goto fail;
+
+#ifdef NSIG
+    if (sig >= NSIG)
+        goto fail;
+#endif
+
+#ifdef HAVE_SIG2STR
+    {
+        char buf[SIG2STR_MAX];
+
+        if (sig2str(sig, buf) == 0) {
+            pa_xfree(PA_STATIC_TLS_GET(signame));
+            t = pa_sprintf_malloc("SIG%s", buf);
+            PA_STATIC_TLS_SET(signame, t);
+            return t;
+        }
+    }
+#else
+
+    switch (sig) {
+#ifdef SIGHUP
+        case SIGHUP:    return "SIGHUP";
+#endif
+        case SIGINT:    return "SIGINT";
+#ifdef SIGQUIT
+        case SIGQUIT:   return "SIGQUIT";
+#endif
+        case SIGILL:    return "SIGULL";
+#ifdef SIGTRAP
+        case SIGTRAP:   return "SIGTRAP";
+#endif
+        case SIGABRT:   return "SIGABRT";
+#ifdef SIGBUS
+        case SIGBUS:    return "SIGBUS";
+#endif
+        case SIGFPE:    return "SIGFPE";
+#ifdef SIGKILL
+        case SIGKILL:   return "SIGKILL";
+#endif
+#ifdef SIGUSR1
+        case SIGUSR1:   return "SIGUSR1";
+#endif
+        case SIGSEGV:   return "SIGSEGV";
+#ifdef SIGUSR2
+        case SIGUSR2:   return "SIGUSR2";
+#endif
+#ifdef SIGPIPE
+        case SIGPIPE:   return "SIGPIPE";
+#endif
+#ifdef SIGALRM
+        case SIGALRM:   return "SIGALRM";
+#endif
+        case SIGTERM:   return "SIGTERM";
+#ifdef SIGSTKFLT
+        case SIGSTKFLT: return "SIGSTKFLT";
+#endif
+#ifdef SIGCHLD
+        case SIGCHLD:   return "SIGCHLD";
+#endif
+#ifdef SIGCONT
+        case SIGCONT:   return "SIGCONT";
+#endif
+#ifdef SIGSTOP
+        case SIGSTOP:   return "SIGSTOP";
+#endif
+#ifdef SIGTSTP
+        case SIGTSTP:   return "SIGTSTP";
+#endif
+#ifdef SIGTTIN
+        case SIGTTIN:   return "SIGTTIN";
+#endif
+#ifdef SIGTTOU
+        case SIGTTOU:   return "SIGTTOU";
+#endif
+#ifdef SIGURG
+        case SIGURG:    return "SIGURG";
+#endif
+#ifdef SIGXCPU
+        case SIGXCPU:   return "SIGXCPU";
+#endif
+#ifdef SIGXFSZ
+        case SIGXFSZ:   return "SIGXFSZ";
+#endif
+#ifdef SIGVTALRM
+        case SIGVTALRM: return "SIGVTALRM";
+#endif
+#ifdef SIGPROF
+        case SIGPROF:   return "SIGPROF";
+#endif
+#ifdef SIGWINCH
+        case SIGWINCH:  return "SIGWINCH";
+#endif
+#ifdef SIGIO
+        case SIGIO:     return "SIGIO";
+#endif
+#ifdef SIGPWR
+        case SIGPWR:    return "SIGPWR";
+#endif
+#ifdef SIGSYS
+        case SIGSYS:    return "SIGSYS";
+#endif
+    }
+
+#ifdef SIGRTMIN
+    if (sig >= SIGRTMIN && sig <= SIGRTMAX) {
+        pa_xfree(PA_STATIC_TLS_GET(signame));
+        t = pa_sprintf_malloc("SIGRTMIN+%i", sig - SIGRTMIN);
+        PA_STATIC_TLS_SET(signame, t);
+        return t;
+    }
+#endif
+
+#endif
+
+fail:
+
+    pa_xfree(PA_STATIC_TLS_GET(signame));
+    t = pa_sprintf_malloc("SIG%i", sig);
+    PA_STATIC_TLS_SET(signame, t);
+    return t;
+}
+
+#ifdef HAVE_GRP_H
+
+/* Check whether the specified GID and the group name match */
+static int is_group(gid_t gid, const char *name) {
+    struct group *group = NULL;
+    int r = -1;
+
+    errno = 0;
+    if (!(group = pa_getgrgid_malloc(gid))) {
+        if (!errno)
+            errno = ENOENT;
+
+        pa_log("pa_getgrgid_malloc(%u): %s", gid, pa_cstrerror(errno));
+
+        goto finish;
+    }
+
+    r = pa_streq(name, group->gr_name);
+
+finish:
+    pa_getgrgid_free(group);
+
+    return r;
+}
+
+/* Check the current user is member of the specified group */
+int pa_own_uid_in_group(const char *name, gid_t *gid) {
+    GETGROUPS_T *gids, tgid;
+    long n = sysconf(_SC_NGROUPS_MAX);
+    int r = -1, i, k;
+
+    pa_assert(n > 0);
+
+    gids = pa_xmalloc(sizeof(GETGROUPS_T) * (size_t) n);
+
+    if ((n = getgroups((int) n, gids)) < 0) {
+        pa_log("getgroups(): %s", pa_cstrerror(errno));
+        goto finish;
+    }
+
+    for (i = 0; i < n; i++) {
+
+        if ((k = is_group(gids[i], name)) < 0)
+            goto finish;
+        else if (k > 0) {
+            *gid = gids[i];
+            r = 1;
+            goto finish;
+        }
+    }
+
+    if ((k = is_group(tgid = getgid(), name)) < 0)
+        goto finish;
+    else if (k > 0) {
+        *gid = tgid;
+        r = 1;
+        goto finish;
+    }
+
+    r = 0;
+
+finish:
+
+    pa_xfree(gids);
+    return r;
+}
+
+/* Check whether the specific user id is a member of the specified group */
+int pa_uid_in_group(uid_t uid, const char *name) {
+    struct group *group = NULL;
+    char **i;
+    int r = -1;
+
+    errno = 0;
+    if (!(group = pa_getgrnam_malloc(name))) {
+        if (!errno)
+            errno = ENOENT;
+        goto finish;
+    }
+
+    r = 0;
+    for (i = group->gr_mem; *i; i++) {
+        struct passwd *pw = NULL;
+
+        errno = 0;
+        if (!(pw = pa_getpwnam_malloc(*i)))
+            continue;
+
+        if (pw->pw_uid == uid)
+            r = 1;
+
+        pa_getpwnam_free(pw);
+
+        if (r == 1)
+            break;
+    }
+
+finish:
+    pa_getgrnam_free(group);
+
+    return r;
+}
+
+/* Get the GID of a given group, return (gid_t) -1 on failure. */
+gid_t pa_get_gid_of_group(const char *name) {
+    gid_t ret = (gid_t) -1;
+    struct group *gr = NULL;
+
+    errno = 0;
+    if (!(gr = pa_getgrnam_malloc(name))) {
+        if (!errno)
+            errno = ENOENT;
+        goto finish;
+    }
+
+    ret = gr->gr_gid;
+
+finish:
+    pa_getgrnam_free(gr);
+    return ret;
+}
+
+int pa_check_in_group(gid_t g) {
+    gid_t gids[NGROUPS_MAX];
+    int r;
+
+    if ((r = getgroups(NGROUPS_MAX, gids)) < 0)
+        return -1;
+
+    for (; r > 0; r--)
+        if (gids[r-1] == g)
+            return 1;
+
+    return 0;
+}
+
+#else /* HAVE_GRP_H */
+
+int pa_own_uid_in_group(const char *name, gid_t *gid) {
+    errno = ENOTSUP;
+    return -1;
+
+}
+
+int pa_uid_in_group(uid_t uid, const char *name) {
+    errno = ENOTSUP;
+    return -1;
+}
+
+gid_t pa_get_gid_of_group(const char *name) {
+    errno = ENOTSUP;
+    return (gid_t) -1;
+}
+
+int pa_check_in_group(gid_t g) {
+    errno = ENOTSUP;
+    return -1;
+}
+
+#endif
+
+/* Lock or unlock a file entirely.
+  (advisory on UNIX, mandatory on Windows) */
+int pa_lock_fd(int fd, int b) {
+#ifdef F_SETLKW
+    struct flock f_lock;
+
+    /* Try a R/W lock first */
+
+    f_lock.l_type = (short) (b ? F_WRLCK : F_UNLCK);
+    f_lock.l_whence = SEEK_SET;
+    f_lock.l_start = 0;
+    f_lock.l_len = 0;
+
+    if (fcntl(fd, F_SETLKW, &f_lock) >= 0)
+        return 0;
+
+    /* Perhaps the file descriptor was opened for read only, than try again with a read lock. */
+    if (b && errno == EBADF) {
+        f_lock.l_type = F_RDLCK;
+        if (fcntl(fd, F_SETLKW, &f_lock) >= 0)
+            return 0;
+    }
+
+    pa_log("%slock: %s", !b ? "un" : "", pa_cstrerror(errno));
+#endif
+
+#ifdef OS_IS_WIN32
+    HANDLE h = (HANDLE) _get_osfhandle(fd);
+
+    if (b && LockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
+        return 0;
+    if (!b && UnlockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
+        return 0;
+
+    pa_log("%slock failed: 0x%08X", !b ? "un" : "", GetLastError());
+
+    /* FIXME: Needs to set errno! */
+#endif
+
+    return -1;
+}
+
+/* Remove trailing newlines from a string */
+char* pa_strip_nl(char *s) {
+    pa_assert(s);
+
+    s[strcspn(s, NEWLINE)] = 0;
+    return s;
+}
+
+char *pa_strip(char *s) {
+    char *e, *l = NULL;
+
+    /* Drops trailing whitespace. Modifies the string in
+     * place. Returns pointer to first non-space character */
+
+    s += strspn(s, WHITESPACE);
+
+    for (e = s; *e; e++)
+        if (!strchr(WHITESPACE, *e))
+            l = e;
+
+    if (l)
+        *(l+1) = 0;
+    else
+        *s = 0;
+
+    return s;
+}
+
+/* Create a temporary lock file and lock it. */
+int pa_lock_lockfile(const char *fn) {
+    int fd;
+    pa_assert(fn);
+
+    for (;;) {
+        struct stat st;
+
+        if ((fd = pa_open_cloexec(fn, O_CREAT|O_RDWR
+#ifdef O_NOFOLLOW
+                       |O_NOFOLLOW
+#endif
+                       , S_IRUSR|S_IWUSR)) < 0) {
+            pa_log_warn("Failed to create lock file '%s': %s", fn, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        if (pa_lock_fd(fd, 1) < 0) {
+            pa_log_warn("Failed to lock file '%s'.", fn);
+            goto fail;
+        }
+
+        if (fstat(fd, &st) < 0) {
+            pa_log_warn("Failed to fstat() file '%s': %s", fn, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        /* Check whether the file has been removed meanwhile. When yes,
+         * restart this loop, otherwise, we're done */
+        if (st.st_nlink >= 1)
+            break;
+
+        if (pa_lock_fd(fd, 0) < 0) {
+            pa_log_warn("Failed to unlock file '%s'.", fn);
+            goto fail;
+        }
+
+        if (pa_close(fd) < 0) {
+            pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno));
+            fd = -1;
+            goto fail;
+        }
+    }
+
+    return fd;
+
+fail:
+
+    if (fd >= 0) {
+        int saved_errno = errno;
+        pa_close(fd);
+        errno = saved_errno;
+    }
+
+    return -1;
+}
+
+/* Unlock a temporary lock file */
+int pa_unlock_lockfile(const char *fn, int fd) {
+    int r = 0;
+    pa_assert(fd >= 0);
+
+    if (fn) {
+        if (unlink(fn) < 0) {
+            pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno));
+            r = -1;
+        }
+    }
+
+    if (pa_lock_fd(fd, 0) < 0) {
+        pa_log_warn("Failed to unlock file '%s'.", fn);
+        r = -1;
+    }
+
+    if (pa_close(fd) < 0) {
+        pa_log_warn("Failed to close '%s': %s", fn, pa_cstrerror(errno));
+        r = -1;
+    }
+
+    return r;
+}
+
+static int check_ours(const char *p) {
+    struct stat st;
+
+    pa_assert(p);
+
+    if (stat(p, &st) < 0)
+        return -errno;
+
+#ifdef HAVE_GETUID
+    if (st.st_uid != getuid())
+        return -EACCES;
+#endif
+
+    return 0;
+}
+
+static char *get_pulse_home(void) {
+    char *h, *ret;
+    int t;
+
+    h = pa_get_home_dir_malloc();
+    if (!h) {
+        pa_log_error("Failed to get home directory.");
+        return NULL;
+    }
+
+    t = check_ours(h);
+    if (t < 0 && t != -ENOENT) {
+        pa_log_error("Home directory not accessible: %s", pa_cstrerror(-t));
+        pa_xfree(h);
+        return NULL;
+    }
+
+    /* If the old directory exists, use it. */
+    ret = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
+    pa_xfree(h);
+    if (access(ret, F_OK) >= 0)
+        return ret;
+    free(ret);
+
+    /* Otherwise go for the XDG compliant directory. */
+    if (pa_get_config_home_dir(&ret) < 0)
+        return NULL;
+
+    return ret;
+}
+
+char *pa_get_state_dir(void) {
+    char *d;
+
+    /* The state directory shall contain dynamic data that should be
+     * kept across reboots, and is private to this user */
+
+    if (!(d = pa_xstrdup(getenv("PULSE_STATE_PATH"))))
+        if (!(d = get_pulse_home()))
+            return NULL;
+
+    /* If PULSE_STATE_PATH and PULSE_RUNTIME_PATH point to the same
+     * dir then this will break. */
+
+    if (pa_make_secure_dir(d, 0700U, (uid_t) -1, (gid_t) -1, true) < 0) {
+        pa_log_error("Failed to create secure directory (%s): %s", d, pa_cstrerror(errno));
+        pa_xfree(d);
+        return NULL;
+    }
+
+    return d;
+}
+
+char *pa_get_home_dir_malloc(void) {
+    char *homedir;
+    size_t allocated = 128;
+
+    for (;;) {
+        homedir = pa_xmalloc(allocated);
+
+        if (!pa_get_home_dir(homedir, allocated)) {
+            pa_xfree(homedir);
+            return NULL;
+        }
+
+        if (strlen(homedir) < allocated - 1)
+            break;
+
+        pa_xfree(homedir);
+        allocated *= 2;
+    }
+
+    return homedir;
+}
+
+int pa_append_to_home_dir(const char *path, char **_r) {
+    char *home_dir;
+
+    pa_assert(path);
+    pa_assert(_r);
+
+    home_dir = pa_get_home_dir_malloc();
+    if (!home_dir) {
+        pa_log("Failed to get home directory.");
+        return -PA_ERR_NOENTITY;
+    }
+
+    *_r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", home_dir, path);
+    pa_xfree(home_dir);
+    return 0;
+}
+
+int pa_get_config_home_dir(char **_r) {
+    const char *e;
+    char *home_dir;
+
+    pa_assert(_r);
+
+    e = getenv("XDG_CONFIG_HOME");
+    if (e && *e) {
+        *_r = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse", e);
+        return 0;
+    }
+
+    home_dir = pa_get_home_dir_malloc();
+    if (!home_dir)
+        return -PA_ERR_NOENTITY;
+
+    *_r = pa_sprintf_malloc("%s" PA_PATH_SEP ".config" PA_PATH_SEP "pulse", home_dir);
+    pa_xfree(home_dir);
+    return 0;
+}
+
+int pa_append_to_config_home_dir(const char *path, char **_r) {
+    int r;
+    char *config_home_dir;
+
+    pa_assert(path);
+    pa_assert(_r);
+
+    r = pa_get_config_home_dir(&config_home_dir);
+    if (r < 0)
+        return r;
+
+    *_r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", config_home_dir, path);
+    pa_xfree(config_home_dir);
+    return 0;
+}
+
+char *pa_get_binary_name_malloc(void) {
+    char *t;
+    size_t allocated = 128;
+
+    for (;;) {
+        t = pa_xmalloc(allocated);
+
+        if (!pa_get_binary_name(t, allocated)) {
+            pa_xfree(t);
+            return NULL;
+        }
+
+        if (strlen(t) < allocated - 1)
+            break;
+
+        pa_xfree(t);
+        allocated *= 2;
+    }
+
+    return t;
+}
+
+static char* make_random_dir(mode_t m) {
+    static const char table[] =
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789";
+
+    char *fn;
+    size_t pathlen;
+
+    fn = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse-XXXXXXXXXXXX", pa_get_temp_dir());
+    pathlen = strlen(fn);
+
+    for (;;) {
+        size_t i;
+        int r;
+        mode_t u;
+        int saved_errno;
+
+        for (i = pathlen - 12; i < pathlen; i++)
+            fn[i] = table[rand() % (sizeof(table)-1)];
+
+        u = umask((~m) & 0777);
+#ifndef OS_IS_WIN32
+        r = mkdir(fn, m);
+#else
+        r = mkdir(fn);
+#endif
+
+        saved_errno = errno;
+        umask(u);
+        errno = saved_errno;
+
+        if (r >= 0)
+            return fn;
+
+        if (errno != EEXIST) {
+            pa_log_error("Failed to create random directory %s: %s", fn, pa_cstrerror(errno));
+            pa_xfree(fn);
+            return NULL;
+        }
+    }
+}
+
+static int make_random_dir_and_link(mode_t m, const char *k) {
+    char *p;
+
+    if (!(p = make_random_dir(m)))
+        return -1;
+
+#ifdef HAVE_SYMLINK
+    if (symlink(p, k) < 0) {
+        int saved_errno = errno;
+
+        if (errno != EEXIST)
+            pa_log_error("Failed to symlink %s to %s: %s", k, p, pa_cstrerror(errno));
+
+        rmdir(p);
+        pa_xfree(p);
+
+        errno = saved_errno;
+        return -1;
+    }
+#else
+    pa_xfree(p);
+    return -1;
+#endif
+
+    pa_xfree(p);
+    return 0;
+}
+
+char *pa_get_runtime_dir(void) {
+    char *d, *k = NULL, *p = NULL, *t = NULL, *mid;
+    mode_t m;
+
+    /* The runtime directory shall contain dynamic data that needs NOT
+     * to be kept across reboots and is usually private to the user,
+     * except in system mode, where it might be accessible by other
+     * users, too. Since we need POSIX locking and UNIX sockets in
+     * this directory, we try XDG_RUNTIME_DIR first, and if that isn't
+     * set create a directory in $HOME and link it to a random subdir
+     * in /tmp, if it was not explicitly configured. */
+
+    m = pa_in_system_mode() ? 0755U : 0700U;
+
+    /* Use the explicitly configured value if it is set */
+    d = getenv("PULSE_RUNTIME_PATH");
+    if (d) {
+
+        if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1, true) < 0) {
+            pa_log_error("Failed to create secure directory (%s): %s", d, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        return pa_xstrdup(d);
+    }
+
+    /* Use the XDG standard for the runtime directory. */
+    d = getenv("XDG_RUNTIME_DIR");
+    if (d) {
+#ifdef HAVE_GETUID
+        struct stat st;
+        if (stat(d, &st) == 0 && st.st_uid != getuid()) {
+            pa_log(_("XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! "
+                   "(This could e g happen if you try to connect to a non-root PulseAudio as a root user, over the native protocol. Don't do that.)"),
+                   d, getuid(), st.st_uid);
+            goto fail;
+        }
+#endif
+
+        k = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse", d);
+
+        if (pa_make_secure_dir(k, m, (uid_t) -1, (gid_t) -1, true) < 0) {
+            pa_log_error("Failed to create secure directory (%s): %s", k, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        return k;
+    }
+
+    /* XDG_RUNTIME_DIR wasn't set, use the old legacy fallback */
+    d = get_pulse_home();
+    if (!d)
+        goto fail;
+
+    if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1, true) < 0) {
+        pa_log_error("Failed to create secure directory (%s): %s", d, pa_cstrerror(errno));
+        pa_xfree(d);
+        goto fail;
+    }
+
+    mid = pa_machine_id();
+    if (!mid) {
+        pa_xfree(d);
+        goto fail;
+    }
+
+    k = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-runtime", d, mid);
+    pa_xfree(d);
+    pa_xfree(mid);
+
+    for (;;) {
+        /* OK, first let's check if the "runtime" symlink already exists */
+
+        p = pa_readlink(k);
+        if (!p) {
+
+            if (errno != ENOENT) {
+                pa_log_error("Failed to stat runtime directory %s: %s", k, pa_cstrerror(errno));
+                goto fail;
+            }
+
+#ifdef HAVE_SYMLINK
+            /* Hmm, so the runtime directory didn't exist yet, so let's
+             * create one in /tmp and symlink that to it */
+
+            if (make_random_dir_and_link(0700, k) < 0) {
+
+                /* Mhmm, maybe another process was quicker than us,
+                 * let's check if that was valid */
+                if (errno == EEXIST)
+                    continue;
+
+                goto fail;
+            }
+#else
+            /* No symlink possible, so let's just create the runtime directly
+             * Do not check again if it exists since it cannot be a symlink */
+            if (mkdir(k) < 0 && errno != EEXIST)
+                goto fail;
+#endif
+
+            return k;
+        }
+
+        /* Make sure that this actually makes sense */
+        if (!pa_is_path_absolute(p)) {
+            pa_log_error("Path %s in link %s is not absolute.", p, k);
+            errno = ENOENT;
+            goto fail;
+        }
+
+        /* Hmm, so this symlink is still around, make sure nobody fools us */
+#ifdef HAVE_LSTAT
+{
+        struct stat st;
+        if (lstat(p, &st) < 0) {
+
+            if (errno != ENOENT) {
+                pa_log_error("Failed to stat runtime directory %s: %s", p, pa_cstrerror(errno));
+                goto fail;
+            }
+
+        } else {
+
+            if (S_ISDIR(st.st_mode) &&
+                (st.st_uid == getuid()) &&
+                ((st.st_mode & 0777) == 0700)) {
+
+                pa_xfree(p);
+                return k;
+            }
+
+            pa_log_info("Hmm, runtime path exists, but points to an invalid directory. Changing runtime directory.");
+        }
+}
+#endif
+
+        pa_xfree(p);
+        p = NULL;
+
+        /* Hmm, so the link points to some nonexisting or invalid
+         * dir. Let's replace it by a new link. We first create a
+         * temporary link and then rename that to allow concurrent
+         * execution of this function. */
+
+        t = pa_sprintf_malloc("%s.tmp", k);
+
+        if (make_random_dir_and_link(0700, t) < 0) {
+
+            if (errno != EEXIST) {
+                pa_log_error("Failed to symlink %s: %s", t, pa_cstrerror(errno));
+                goto fail;
+            }
+
+            pa_xfree(t);
+            t = NULL;
+
+            /* Hmm, someone else was quicker then us. Let's give
+             * him some time to finish, and retry. */
+            pa_msleep(10);
+            continue;
+        }
+
+        /* OK, we succeeded in creating the temporary symlink, so
+         * let's rename it */
+        if (rename(t, k) < 0) {
+            pa_log_error("Failed to rename %s to %s: %s", t, k, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        pa_xfree(t);
+        return k;
+    }
+
+fail:
+    pa_xfree(p);
+    pa_xfree(k);
+    pa_xfree(t);
+
+    return NULL;
+}
+
+/* Try to open a configuration file. If "env" is specified, open the
+ * value of the specified environment variable. Otherwise look for a
+ * file "local" in the home directory or a file "global" in global
+ * file system. If "result" is non-NULL, a pointer to a newly
+ * allocated buffer containing the used configuration file is
+ * stored there.*/
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
+    const char *fn;
+    FILE *f;
+
+    if (env && (fn = getenv(env))) {
+        if ((f = pa_fopen_cloexec(fn, "r"))) {
+            if (result)
+                *result = pa_xstrdup(fn);
+
+            return f;
+        }
+
+        pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+        return NULL;
+    }
+
+    if (local) {
+        const char *e;
+        char *lfn;
+        char *h;
+
+        if ((e = getenv("PULSE_CONFIG_PATH"))) {
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
+            f = pa_fopen_cloexec(fn, "r");
+        } else if ((h = pa_get_home_dir_malloc())) {
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
+            f = pa_fopen_cloexec(fn, "r");
+            if (!f) {
+                free(lfn);
+                fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".config/pulse" PA_PATH_SEP "%s", h, local);
+                f = pa_fopen_cloexec(fn, "r");
+            }
+            pa_xfree(h);
+        } else
+            return NULL;
+
+        if (f) {
+            if (result)
+                *result = pa_xstrdup(fn);
+
+            pa_xfree(lfn);
+            return f;
+        }
+
+        if (errno != ENOENT) {
+            pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+            pa_xfree(lfn);
+            return NULL;
+        }
+
+        pa_xfree(lfn);
+    }
+
+    if (global) {
+        char *gfn;
+
+#ifdef OS_IS_WIN32
+        if (strncmp(global, PA_DEFAULT_CONFIG_DIR, strlen(PA_DEFAULT_CONFIG_DIR)) == 0)
+            gfn = pa_sprintf_malloc("%s" PA_PATH_SEP "etc" PA_PATH_SEP "pulse%s",
+                                    pa_win32_get_toplevel(NULL),
+                                    global + strlen(PA_DEFAULT_CONFIG_DIR));
+        else
+#endif
+        gfn = pa_xstrdup(global);
+
+        if ((f = pa_fopen_cloexec(gfn, "r"))) {
+            if (result)
+                *result = gfn;
+            else
+                pa_xfree(gfn);
+
+            return f;
+        }
+        pa_xfree(gfn);
+    }
+
+    errno = ENOENT;
+    return NULL;
+}
+
+char *pa_find_config_file(const char *global, const char *local, const char *env) {
+    const char *fn;
+
+    if (env && (fn = getenv(env))) {
+        if (access(fn, R_OK) == 0)
+            return pa_xstrdup(fn);
+
+        pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
+        return NULL;
+    }
+
+    if (local) {
+        const char *e;
+        char *lfn;
+        char *h;
+
+        if ((e = getenv("PULSE_CONFIG_PATH")))
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
+        else if ((h = pa_get_home_dir_malloc())) {
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
+            pa_xfree(h);
+        } else
+            return NULL;
+
+        if (access(fn, R_OK) == 0) {
+            char *r = pa_xstrdup(fn);
+            pa_xfree(lfn);
+            return r;
+        }
+
+        if (errno != ENOENT) {
+            pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
+            pa_xfree(lfn);
+            return NULL;
+        }
+
+        pa_xfree(lfn);
+    }
+
+    if (global) {
+        char *gfn;
+
+#ifdef OS_IS_WIN32
+        if (strncmp(global, PA_DEFAULT_CONFIG_DIR, strlen(PA_DEFAULT_CONFIG_DIR)) == 0)
+            gfn = pa_sprintf_malloc("%s" PA_PATH_SEP "etc" PA_PATH_SEP "pulse%s",
+                                    pa_win32_get_toplevel(NULL),
+                                    global + strlen(PA_DEFAULT_CONFIG_DIR));
+        else
+#endif
+        gfn = pa_xstrdup(global);
+
+        if (access(gfn, R_OK) == 0)
+            return gfn;
+        pa_xfree(gfn);
+    }
+
+    errno = ENOENT;
+
+    return NULL;
+}
+
+/* Format the specified data as a hexademical string */
+char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) {
+    size_t i = 0, j = 0;
+    const char hex[] = "0123456789abcdef";
+
+    pa_assert(d);
+    pa_assert(s);
+    pa_assert(slength > 0);
+
+    while (j+2 < slength && i < dlength) {
+        s[j++] = hex[*d >> 4];
+        s[j++] = hex[*d & 0xF];
+
+        d++;
+        i++;
+    }
+
+    s[j < slength ? j : slength] = 0;
+    return s;
+}
+
+/* Convert a hexadecimal digit to a number or -1 if invalid */
+static int hexc(char c) {
+    if (c >= '0' && c <= '9')
+        return c - '0';
+
+    if (c >= 'A' && c <= 'F')
+        return c - 'A' + 10;
+
+    if (c >= 'a' && c <= 'f')
+        return c - 'a' + 10;
+
+    errno = EINVAL;
+    return -1;
+}
+
+/* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */
+size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
+    size_t j = 0;
+
+    pa_assert(p);
+    pa_assert(d);
+
+    while (j < dlength && *p) {
+        int b;
+
+        if ((b = hexc(*(p++))) < 0)
+            return (size_t) -1;
+
+        d[j] = (uint8_t) (b << 4);
+
+        if (!*p)
+            return (size_t) -1;
+
+        if ((b = hexc(*(p++))) < 0)
+            return (size_t) -1;
+
+        d[j] |= (uint8_t) b;
+        j++;
+    }
+
+    return j;
+}
+
+/* Returns nonzero when *s starts with *pfx */
+bool pa_startswith(const char *s, const char *pfx) {
+    size_t l;
+
+    pa_assert(s);
+    pa_assert(pfx);
+
+    l = strlen(pfx);
+
+    return strlen(s) >= l && strncmp(s, pfx, l) == 0;
+}
+
+/* Returns nonzero when *s ends with *sfx */
+bool pa_endswith(const char *s, const char *sfx) {
+    size_t l1, l2;
+
+    pa_assert(s);
+    pa_assert(sfx);
+
+    l1 = strlen(s);
+    l2 = strlen(sfx);
+
+    return l1 >= l2 && pa_streq(s + l1 - l2, sfx);
+}
+
+bool pa_is_path_absolute(const char *fn) {
+    pa_assert(fn);
+
+#ifndef OS_IS_WIN32
+    return *fn == '/';
+#else
+    return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\';
+#endif
+}
+
+char *pa_make_path_absolute(const char *p) {
+    char *r;
+    char *cwd;
+
+    pa_assert(p);
+
+    if (pa_is_path_absolute(p))
+        return pa_xstrdup(p);
+
+    if (!(cwd = pa_getcwd()))
+        return pa_xstrdup(p);
+
+    r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p);
+    pa_xfree(cwd);
+    return r;
+}
+
+/* If fn is NULL, return the PulseAudio runtime or state dir (depending on the
+ * rt parameter). If fn is non-NULL and starts with /, return fn. Otherwise,
+ * append fn to the runtime/state dir and return it. */
+static char *get_path(const char *fn, bool prependmid, bool rt) {
+    char *rtp;
+
+    rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir();
+
+    if (fn) {
+        char *r, *canonical_rtp;
+
+        if (pa_is_path_absolute(fn)) {
+            pa_xfree(rtp);
+            return pa_xstrdup(fn);
+        }
+
+        if (!rtp)
+            return NULL;
+
+        /* Hopefully make the path smaller to avoid 108 char limit (fdo#44680) */
+        if ((canonical_rtp = pa_realpath(rtp))) {
+            if (strlen(rtp) >= strlen(canonical_rtp))
+                pa_xfree(rtp);
+            else {
+                pa_xfree(canonical_rtp);
+                canonical_rtp = rtp;
+            }
+        } else
+            canonical_rtp = rtp;
+
+        if (prependmid) {
+            char *mid;
+
+            if (!(mid = pa_machine_id())) {
+                pa_xfree(canonical_rtp);
+                return NULL;
+            }
+
+            r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-%s", canonical_rtp, mid, fn);
+            pa_xfree(mid);
+        } else
+            r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", canonical_rtp, fn);
+
+        pa_xfree(canonical_rtp);
+        return r;
+    } else
+        return rtp;
+}
+
+char *pa_runtime_path(const char *fn) {
+    return get_path(fn, false, true);
+}
+
+char *pa_state_path(const char *fn, bool appendmid) {
+    return get_path(fn, appendmid, false);
+}
+
+/* Convert the string s to a signed integer in *ret_i */
+int pa_atoi(const char *s, int32_t *ret_i) {
+    long l;
+
+    pa_assert(s);
+    pa_assert(ret_i);
+
+    if (pa_atol(s, &l) < 0)
+        return -1;
+
+    if ((int32_t) l != l) {
+        errno = ERANGE;
+        return -1;
+    }
+
+    *ret_i = (int32_t) l;
+
+    return 0;
+}
+
+/* Convert the string s to an unsigned integer in *ret_u */
+int pa_atou(const char *s, uint32_t *ret_u) {
+    char *x = NULL;
+    unsigned long l;
+
+    pa_assert(s);
+    pa_assert(ret_u);
+
+    /* strtoul() ignores leading spaces. We don't. */
+    if (isspace((unsigned char)*s)) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    /* strtoul() accepts strings that start with a minus sign. In that case the
+     * original negative number gets negated, and strtoul() returns the negated
+     * result. We don't want that kind of behaviour. strtoul() also allows a
+     * leading plus sign, which is also a thing that we don't want. */
+    if (*s == '-' || *s == '+') {
+        errno = EINVAL;
+        return -1;
+    }
+
+    errno = 0;
+    l = strtoul(s, &x, 0);
+
+    /* If x doesn't point to the end of s, there was some trailing garbage in
+     * the string. If x points to s, no conversion was done (empty string). */
+    if (!x || *x || x == s || errno) {
+        if (!errno)
+            errno = EINVAL;
+        return -1;
+    }
+
+    if ((uint32_t) l != l) {
+        errno = ERANGE;
+        return -1;
+    }
+
+    *ret_u = (uint32_t) l;
+
+    return 0;
+}
+
+/* Convert the string s to a signed long integer in *ret_l. */
+int pa_atol(const char *s, long *ret_l) {
+    char *x = NULL;
+    long l;
+
+    pa_assert(s);
+    pa_assert(ret_l);
+
+    /* strtol() ignores leading spaces. We don't. */
+    if (isspace((unsigned char)*s)) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    /* strtol() accepts leading plus signs, but that's ugly, so we don't allow
+     * that. */
+    if (*s == '+') {
+        errno = EINVAL;
+        return -1;
+    }
+
+    errno = 0;
+    l = strtol(s, &x, 0);
+
+    /* If x doesn't point to the end of s, there was some trailing garbage in
+     * the string. If x points to s, no conversion was done (at least an empty
+     * string can trigger this). */
+    if (!x || *x || x == s || errno) {
+        if (!errno)
+            errno = EINVAL;
+        return -1;
+    }
+
+    *ret_l = l;
+
+    return 0;
+}
+
+#ifdef HAVE_STRTOD_L
+static locale_t c_locale = NULL;
+
+static void c_locale_destroy(void) {
+    freelocale(c_locale);
+}
+#endif
+
+int pa_atod(const char *s, double *ret_d) {
+    char *x = NULL;
+    double f;
+
+    pa_assert(s);
+    pa_assert(ret_d);
+
+    /* strtod() ignores leading spaces. We don't. */
+    if (isspace((unsigned char)*s)) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    /* strtod() accepts leading plus signs, but that's ugly, so we don't allow
+     * that. */
+    if (*s == '+') {
+        errno = EINVAL;
+        return -1;
+    }
+
+    /* This should be locale independent */
+
+#ifdef HAVE_STRTOD_L
+
+    PA_ONCE_BEGIN {
+
+        if ((c_locale = newlocale(LC_ALL_MASK, "C", NULL)))
+            atexit(c_locale_destroy);
+
+    } PA_ONCE_END;
+
+    if (c_locale) {
+        errno = 0;
+        f = strtod_l(s, &x, c_locale);
+    } else
+#endif
+    {
+        errno = 0;
+        f = strtod(s, &x);
+    }
+
+    /* If x doesn't point to the end of s, there was some trailing garbage in
+     * the string. If x points to s, no conversion was done (at least an empty
+     * string can trigger this). */
+    if (!x || *x || x == s || errno) {
+        if (!errno)
+            errno = EINVAL;
+        return -1;
+    }
+
+    if (isnan(f)) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    *ret_d = f;
+
+    return 0;
+}
+
+/* Same as snprintf, but guarantees NUL-termination on every platform */
+size_t pa_snprintf(char *str, size_t size, const char *format, ...) {
+    size_t ret;
+    va_list ap;
+
+    pa_assert(str);
+    pa_assert(size > 0);
+    pa_assert(format);
+
+    va_start(ap, format);
+    ret = pa_vsnprintf(str, size, format, ap);
+    va_end(ap);
+
+    return ret;
+}
+
+/* Same as vsnprintf, but guarantees NUL-termination on every platform */
+size_t pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+    int ret;
+
+    pa_assert(str);
+    pa_assert(size > 0);
+    pa_assert(format);
+
+    ret = vsnprintf(str, size, format, ap);
+
+    str[size-1] = 0;
+
+    if (ret < 0)
+        return strlen(str);
+
+    if ((size_t) ret > size-1)
+        return size-1;
+
+    return (size_t) ret;
+}
+
+/* Truncate the specified string, but guarantee that the string
+ * returned still validates as UTF8 */
+char *pa_truncate_utf8(char *c, size_t l) {
+    pa_assert(c);
+    pa_assert(pa_utf8_valid(c));
+
+    if (strlen(c) <= l)
+        return c;
+
+    c[l] = 0;
+
+    while (l > 0 && !pa_utf8_valid(c))
+        c[--l] = 0;
+
+    return c;
+}
+
+char *pa_getcwd(void) {
+    size_t l = 128;
+
+    for (;;) {
+        char *p = pa_xmalloc(l);
+        if (getcwd(p, l))
+            return p;
+
+        if (errno != ERANGE) {
+            pa_xfree(p);
+            return NULL;
+        }
+
+        pa_xfree(p);
+        l *= 2;
+    }
+}
+
+void *pa_will_need(const void *p, size_t l) {
+#ifdef RLIMIT_MEMLOCK
+    struct rlimit rlim;
+#endif
+    const void *a;
+    size_t size;
+    int r = ENOTSUP;
+    size_t bs;
+    const size_t page_size = pa_page_size();
+
+    pa_assert(p);
+    pa_assert(l > 0);
+
+    a = PA_PAGE_ALIGN_PTR(p);
+    size = (size_t) ((const uint8_t*) p + l - (const uint8_t*) a);
+
+#ifdef HAVE_POSIX_MADVISE
+    if ((r = posix_madvise((void*) a, size, POSIX_MADV_WILLNEED)) == 0) {
+        pa_log_debug("posix_madvise() worked fine!");
+        return (void*) p;
+    }
+#endif
+
+    /* Most likely the memory was not mmap()ed from a file and thus
+     * madvise() didn't work, so let's misuse mlock() do page this
+     * stuff back into RAM. Yeah, let's fuck with the MM!  It's so
+     * inviting, the man page of mlock() tells us: "All pages that
+     * contain a part of the specified address range are guaranteed to
+     * be resident in RAM when the call returns successfully." */
+
+#ifdef RLIMIT_MEMLOCK
+    pa_assert_se(getrlimit(RLIMIT_MEMLOCK, &rlim) == 0);
+
+    if (rlim.rlim_cur < page_size) {
+        pa_log_debug("posix_madvise() failed (or doesn't exist), resource limits don't allow mlock(), can't page in data: %s", pa_cstrerror(r));
+        errno = EPERM;
+        return (void*) p;
+    }
+
+    bs = PA_PAGE_ALIGN((size_t) rlim.rlim_cur);
+#else
+    bs = page_size*4;
+#endif
+
+    pa_log_debug("posix_madvise() failed (or doesn't exist), trying mlock(): %s", pa_cstrerror(r));
+
+#ifdef HAVE_MLOCK
+    while (size > 0 && bs > 0) {
+
+        if (bs > size)
+            bs = size;
+
+        if (mlock(a, bs) < 0) {
+            bs = PA_PAGE_ALIGN(bs / 2);
+            continue;
+        }
+
+        pa_assert_se(munlock(a, bs) == 0);
+
+        a = (const uint8_t*) a + bs;
+        size -= bs;
+    }
+#endif
+
+    if (bs <= 0)
+        pa_log_debug("mlock() failed too (or doesn't exist), giving up: %s", pa_cstrerror(errno));
+    else
+        pa_log_debug("mlock() worked fine!");
+
+    return (void*) p;
+}
+
+void pa_close_pipe(int fds[2]) {
+    pa_assert(fds);
+
+    if (fds[0] >= 0)
+        pa_assert_se(pa_close(fds[0]) == 0);
+
+    if (fds[1] >= 0)
+        pa_assert_se(pa_close(fds[1]) == 0);
+
+    fds[0] = fds[1] = -1;
+}
+
+char *pa_readlink(const char *p) {
+#ifdef HAVE_READLINK
+    size_t l = 100;
+
+    for (;;) {
+        char *c;
+        ssize_t n;
+
+        c = pa_xmalloc(l);
+
+        if ((n = readlink(p, c, l-1)) < 0) {
+            pa_xfree(c);
+            return NULL;
+        }
+
+        if ((size_t) n < l-1) {
+            c[n] = 0;
+            return c;
+        }
+
+        pa_xfree(c);
+        l *= 2;
+    }
+#else
+    return NULL;
+#endif
+}
+
+int pa_close_all(int except_fd, ...) {
+    va_list ap;
+    unsigned n = 0, i;
+    int r, *p;
+
+    va_start(ap, except_fd);
+
+    if (except_fd >= 0)
+        for (n = 1; va_arg(ap, int) >= 0; n++)
+            ;
+
+    va_end(ap);
+
+    p = pa_xnew(int, n+1);
+
+    va_start(ap, except_fd);
+
+    i = 0;
+    if (except_fd >= 0) {
+        int fd;
+        p[i++] = except_fd;
+
+        while ((fd = va_arg(ap, int)) >= 0)
+            p[i++] = fd;
+    }
+    p[i] = -1;
+
+    va_end(ap);
+
+    r = pa_close_allv(p);
+    pa_xfree(p);
+
+    return r;
+}
+
+int pa_close_allv(const int except_fds[]) {
+#ifndef OS_IS_WIN32
+    struct rlimit rl;
+    int maxfd, fd;
+
+#ifdef __linux__
+    int saved_errno;
+    DIR *d;
+
+    if ((d = opendir("/proc/self/fd"))) {
+
+        struct dirent *de;
+
+        while ((de = readdir(d))) {
+            bool found;
+            long l;
+            char *e = NULL;
+            int i;
+
+            if (de->d_name[0] == '.')
+                continue;
+
+            errno = 0;
+            l = strtol(de->d_name, &e, 10);
+            if (errno != 0 || !e || *e) {
+                closedir(d);
+                errno = EINVAL;
+                return -1;
+            }
+
+            fd = (int) l;
+
+            if ((long) fd != l) {
+                closedir(d);
+                errno = EINVAL;
+                return -1;
+            }
+
+            if (fd < 3)
+                continue;
+
+            if (fd == dirfd(d))
+                continue;
+
+            found = false;
+            for (i = 0; except_fds[i] >= 0; i++)
+                if (except_fds[i] == fd) {
+                    found = true;
+                    break;
+                }
+
+            if (found)
+                continue;
+
+            if (pa_close(fd) < 0) {
+                saved_errno = errno;
+                closedir(d);
+                errno = saved_errno;
+
+                return -1;
+            }
+        }
+
+        closedir(d);
+        return 0;
+    }
+
+#endif
+
+    if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
+        maxfd = (int) rl.rlim_max;
+    else
+        maxfd = sysconf(_SC_OPEN_MAX);
+
+    for (fd = 3; fd < maxfd; fd++) {
+        int i;
+        bool found;
+
+        found = false;
+        for (i = 0; except_fds[i] >= 0; i++)
+            if (except_fds[i] == fd) {
+                found = true;
+                break;
+            }
+
+        if (found)
+            continue;
+
+        if (pa_close(fd) < 0 && errno != EBADF)
+            return -1;
+    }
+#endif  /* !OS_IS_WIN32 */
+
+    return 0;
+}
+
+int pa_unblock_sigs(int except, ...) {
+    va_list ap;
+    unsigned n = 0, i;
+    int r, *p;
+
+    va_start(ap, except);
+
+    if (except >= 1)
+        for (n = 1; va_arg(ap, int) >= 0; n++)
+            ;
+
+    va_end(ap);
+
+    p = pa_xnew(int, n+1);
+
+    va_start(ap, except);
+
+    i = 0;
+    if (except >= 1) {
+        int sig;
+        p[i++] = except;
+
+        while ((sig = va_arg(ap, int)) >= 0)
+            p[i++] = sig;
+    }
+    p[i] = -1;
+
+    va_end(ap);
+
+    r = pa_unblock_sigsv(p);
+    pa_xfree(p);
+
+    return r;
+}
+
+int pa_unblock_sigsv(const int except[]) {
+#ifndef OS_IS_WIN32
+    int i;
+    sigset_t ss;
+
+    if (sigemptyset(&ss) < 0)
+        return -1;
+
+    for (i = 0; except[i] > 0; i++)
+        if (sigaddset(&ss, except[i]) < 0)
+            return -1;
+
+    return sigprocmask(SIG_SETMASK, &ss, NULL);
+#else
+    return 0;
+#endif
+}
+
+int pa_reset_sigs(int except, ...) {
+    va_list ap;
+    unsigned n = 0, i;
+    int *p, r;
+
+    va_start(ap, except);
+
+    if (except >= 1)
+        for (n = 1; va_arg(ap, int) >= 0; n++)
+            ;
+
+    va_end(ap);
+
+    p = pa_xnew(int, n+1);
+
+    va_start(ap, except);
+
+    i = 0;
+    if (except >= 1) {
+        int sig;
+        p[i++] = except;
+
+        while ((sig = va_arg(ap, int)) >= 0)
+            p[i++] = sig;
+    }
+    p[i] = -1;
+
+    va_end(ap);
+
+    r = pa_reset_sigsv(p);
+    pa_xfree(p);
+
+    return r;
+}
+
+int pa_reset_sigsv(const int except[]) {
+#ifndef OS_IS_WIN32
+    int sig;
+
+    for (sig = 1; sig < NSIG; sig++) {
+        bool reset = true;
+
+        switch (sig) {
+            case SIGKILL:
+            case SIGSTOP:
+                reset = false;
+                break;
+
+            default: {
+                int i;
+
+                for (i = 0; except[i] > 0; i++) {
+                    if (sig == except[i]) {
+                        reset = false;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (reset) {
+            struct sigaction sa;
+
+            memset(&sa, 0, sizeof(sa));
+            sa.sa_handler = SIG_DFL;
+
+            /* On Linux the first two RT signals are reserved by
+             * glibc, and sigaction() will return EINVAL for them. */
+            if ((sigaction(sig, &sa, NULL) < 0))
+                if (errno != EINVAL)
+                    return -1;
+        }
+    }
+#endif
+
+    return 0;
+}
+
+void pa_set_env(const char *key, const char *value) {
+    pa_assert(key);
+    pa_assert(value);
+
+    /* This is not thread-safe */
+
+#ifdef OS_IS_WIN32
+    SetEnvironmentVariable(key, value);
+#else
+    setenv(key, value, 1);
+#endif
+}
+
+void pa_unset_env(const char *key) {
+    pa_assert(key);
+
+    /* This is not thread-safe */
+
+#ifdef OS_IS_WIN32
+    SetEnvironmentVariable(key, NULL);
+#else
+    unsetenv(key);
+#endif
+}
+
+void pa_set_env_and_record(const char *key, const char *value) {
+    pa_assert(key);
+    pa_assert(value);
+
+    /* This is not thread-safe */
+
+    pa_set_env(key, value);
+    recorded_env = pa_strlist_prepend(recorded_env, key);
+}
+
+void pa_unset_env_recorded(void) {
+
+    /* This is not thread-safe */
+
+    for (;;) {
+        char *s;
+
+        recorded_env = pa_strlist_pop(recorded_env, &s);
+
+        if (!s)
+            break;
+
+        pa_unset_env(s);
+        pa_xfree(s);
+    }
+}
+
+bool pa_in_system_mode(void) {
+    const char *e;
+
+    if (!(e = getenv("PULSE_SYSTEM")))
+        return false;
+
+    return !!atoi(e);
+}
+
+/* Checks a delimiters-separated list of words in haystack for needle */
+bool pa_str_in_list(const char *haystack, const char *delimiters, const char *needle) {
+    char *s;
+    const char *state = NULL;
+
+    if (!haystack || !needle)
+        return false;
+
+    while ((s = pa_split(haystack, delimiters, &state))) {
+        if (pa_streq(needle, s)) {
+            pa_xfree(s);
+            return true;
+        }
+
+        pa_xfree(s);
+    }
+
+    return false;
+}
+
+/* Checks a whitespace-separated list of words in haystack for needle */
+bool pa_str_in_list_spaces(const char *haystack, const char *needle) {
+    const char *s;
+    int n;
+    const char *state = NULL;
+
+    if (!haystack || !needle)
+        return false;
+
+    while ((s = pa_split_spaces_in_place(haystack, &n, &state))) {
+        if (pa_strneq(needle, s, n))
+            return true;
+    }
+
+    return false;
+}
+
+char *pa_get_user_name_malloc(void) {
+    ssize_t k;
+    char *u;
+
+#ifdef _SC_LOGIN_NAME_MAX
+    k = (ssize_t) sysconf(_SC_LOGIN_NAME_MAX);
+
+    if (k <= 0)
+#endif
+        k = 32;
+
+    u = pa_xnew(char, k+1);
+
+    if (!(pa_get_user_name(u, k))) {
+        pa_xfree(u);
+        return NULL;
+    }
+
+    return u;
+}
+
+char *pa_get_host_name_malloc(void) {
+    size_t l;
+
+    l = 100;
+    for (;;) {
+        char *c;
+
+        c = pa_xmalloc(l);
+
+        if (!pa_get_host_name(c, l)) {
+
+            if (errno != EINVAL && errno != ENAMETOOLONG)
+                break;
+
+        } else if (strlen(c) < l-1) {
+            char *u;
+
+            if (*c == 0) {
+                pa_xfree(c);
+                break;
+            }
+
+            u = pa_utf8_filter(c);
+            pa_xfree(c);
+            return u;
+        }
+
+        /* Hmm, the hostname is as long the space we offered the
+         * function, we cannot know if it fully fit in, so let's play
+         * safe and retry. */
+
+        pa_xfree(c);
+        l *= 2;
+    }
+
+    return NULL;
+}
+
+char *pa_machine_id(void) {
+    FILE *f;
+    char *h;
+
+    /* The returned value is supposed be some kind of ascii identifier
+     * that is unique and stable across reboots. First we try if the machine-id
+     * file is available. If it's available, that's great, since it provides an
+     * identifier that suits our needs perfectly. If it's not, we fall back to
+     * the hostname, which is not as good, since it can change over time. */
+
+    /* We search for the machine-id file from four locations. The first two are
+     * relative to the configured installation prefix, but if we're installed
+     * under /usr/local, for example, it's likely that the machine-id won't be
+     * found there, so we also try the hardcoded paths.
+     *
+     * PA_MACHINE_ID or PA_MACHINE_ID_FALLBACK might exist on a Windows system,
+     * but the last two hardcoded paths certainly don't, hence we don't try
+     * them on Windows. */
+    if ((f = pa_fopen_cloexec(PA_MACHINE_ID, "r")) ||
+        (f = pa_fopen_cloexec(PA_MACHINE_ID_FALLBACK, "r")) ||
+#if !defined(OS_IS_WIN32)
+        (f = pa_fopen_cloexec("/etc/machine-id", "r")) ||
+        (f = pa_fopen_cloexec("/var/lib/dbus/machine-id", "r"))
+#else
+        false
+#endif
+        ) {
+        char ln[34] = "", *r;
+
+        r = fgets(ln, sizeof(ln)-1, f);
+        fclose(f);
+
+        pa_strip_nl(ln);
+
+        if (r && ln[0])
+            return pa_utf8_filter(ln);
+    }
+
+    if ((h = pa_get_host_name_malloc()))
+        return h;
+
+#if !defined(OS_IS_WIN32) && !defined(__ANDROID__)
+    /* If no hostname was set we use the POSIX hostid. It's usually
+     * the IPv4 address.  Might not be that stable. */
+    return pa_sprintf_malloc("%08lx", (unsigned long) gethostid());
+#else
+    return NULL;
+#endif
+}
+
+char *pa_session_id(void) {
+    const char *e;
+
+    e = getenv("XDG_SESSION_ID");
+    if (!e)
+        return NULL;
+
+    return pa_utf8_filter(e);
+}
+
+char *pa_uname_string(void) {
+#ifdef HAVE_UNAME
+    struct utsname u;
+
+    pa_assert_se(uname(&u) >= 0);
+
+    return pa_sprintf_malloc("%s %s %s %s", u.sysname, u.machine, u.release, u.version);
+#endif
+#ifdef OS_IS_WIN32
+    OSVERSIONINFO i;
+
+    pa_zero(i);
+    i.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+    pa_assert_se(GetVersionEx(&i));
+
+    return pa_sprintf_malloc("Windows %d.%d (%d) %s", i.dwMajorVersion, i.dwMinorVersion, i.dwBuildNumber, i.szCSDVersion);
+#endif
+}
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+bool pa_in_valgrind(void) {
+    static int b = 0;
+
+    /* To make heisenbugs a bit simpler to find we check for $VALGRIND
+     * here instead of really checking whether we run in valgrind or
+     * not. */
+
+    if (b < 1)
+        b = getenv("VALGRIND") ? 2 : 1;
+
+    return b > 1;
+}
+#endif
+
+unsigned pa_gcd(unsigned a, unsigned b) {
+
+    while (b > 0) {
+        unsigned t = b;
+        b = a % b;
+        a = t;
+    }
+
+    return a;
+}
+
+void pa_reduce(unsigned *num, unsigned *den) {
+
+    unsigned gcd = pa_gcd(*num, *den);
+
+    if (gcd <= 0)
+        return;
+
+    *num /= gcd;
+    *den /= gcd;
+
+    pa_assert(pa_gcd(*num, *den) == 1);
+}
+
+unsigned pa_ncpus(void) {
+    long ncpus;
+
+#ifdef _SC_NPROCESSORS_ONLN
+    ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    ncpus = 1;
+#endif
+
+    return ncpus <= 0 ? 1 : (unsigned) ncpus;
+}
+
+char *pa_replace(const char*s, const char*a, const char *b) {
+    pa_strbuf *sb;
+    size_t an;
+
+    pa_assert(s);
+    pa_assert(a);
+    pa_assert(*a);
+    pa_assert(b);
+
+    an = strlen(a);
+    sb = pa_strbuf_new();
+
+    for (;;) {
+        const char *p;
+
+        if (!(p = strstr(s, a)))
+            break;
+
+        pa_strbuf_putsn(sb, s, p-s);
+        pa_strbuf_puts(sb, b);
+        s = p + an;
+    }
+
+    pa_strbuf_puts(sb, s);
+
+    return pa_strbuf_to_string_free(sb);
+}
+
+char *pa_escape(const char *p, const char *chars) {
+    const char *s;
+    const char *c;
+    pa_strbuf *buf = pa_strbuf_new();
+
+    for (s = p; *s; ++s) {
+        if (*s == '\\')
+            pa_strbuf_putc(buf, '\\');
+        else if (chars) {
+            for (c = chars; *c; ++c) {
+                if (*s == *c) {
+                    pa_strbuf_putc(buf, '\\');
+                    break;
+                }
+            }
+        }
+        pa_strbuf_putc(buf, *s);
+    }
+
+    return pa_strbuf_to_string_free(buf);
+}
+
+char *pa_unescape(char *p) {
+    char *s, *d;
+    bool escaped = false;
+
+    for (s = p, d = p; *s; s++) {
+        if (!escaped && *s == '\\') {
+            escaped = true;
+            continue;
+        }
+
+        *(d++) = *s;
+        escaped = false;
+    }
+
+    *d = 0;
+
+    return p;
+}
+
+char *pa_realpath(const char *path) {
+    char *t;
+    pa_assert(path);
+
+    /* We want only absolute paths */
+    if (path[0] != '/') {
+        errno = EINVAL;
+        return NULL;
+    }
+
+#if defined(__GLIBC__)
+    {
+        char *r;
+
+        if (!(r = realpath(path, NULL)))
+            return NULL;
+
+        /* We copy this here in case our pa_xmalloc() is not
+         * implemented on top of libc malloc() */
+        t = pa_xstrdup(r);
+        pa_xfree(r);
+    }
+#elif defined(PATH_MAX)
+    {
+        char *path_buf;
+        path_buf = pa_xmalloc(PATH_MAX);
+
+#if defined(OS_IS_WIN32)
+        if (!(t = _fullpath(path_buf, path, _MAX_PATH))) {
+            pa_xfree(path_buf);
+            return NULL;
+        }
+#else
+        if (!(t = realpath(path, path_buf))) {
+            pa_xfree(path_buf);
+            return NULL;
+        }
+#endif
+    }
+#else
+#error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here."
+#endif
+
+    return t;
+}
+
+void pa_disable_sigpipe(void) {
+
+#ifdef SIGPIPE
+    struct sigaction sa;
+
+    pa_zero(sa);
+
+    if (sigaction(SIGPIPE, NULL, &sa) < 0) {
+        pa_log("sigaction(): %s", pa_cstrerror(errno));
+        return;
+    }
+
+    sa.sa_handler = SIG_IGN;
+
+    if (sigaction(SIGPIPE, &sa, NULL) < 0) {
+        pa_log("sigaction(): %s", pa_cstrerror(errno));
+        return;
+    }
+#endif
+}
+
+void pa_xfreev(void**a) {
+    void **p;
+
+    if (!a)
+        return;
+
+    for (p = a; *p; p++)
+        pa_xfree(*p);
+
+    pa_xfree(a);
+}
+
+char **pa_split_spaces_strv(const char *s) {
+    char **t, *e;
+    unsigned i = 0, n = 8;
+    const char *state = NULL;
+
+    t = pa_xnew(char*, n);
+    while ((e = pa_split_spaces(s, &state))) {
+        t[i++] = e;
+
+        if (i >= n) {
+            n *= 2;
+            t = pa_xrenew(char*, t, n);
+        }
+    }
+
+    if (i <= 0) {
+        pa_xfree(t);
+        return NULL;
+    }
+
+    t[i] = NULL;
+    return t;
+}
+
+char* pa_maybe_prefix_path(const char *path, const char *prefix) {
+    pa_assert(path);
+
+    if (pa_is_path_absolute(path))
+        return pa_xstrdup(path);
+
+    return pa_sprintf_malloc("%s" PA_PATH_SEP "%s", prefix, path);
+}
+
+size_t pa_pipe_buf(int fd) {
+
+#ifdef _PC_PIPE_BUF
+    long n;
+
+    if ((n = fpathconf(fd, _PC_PIPE_BUF)) >= 0)
+        return (size_t) n;
+#endif
+
+#ifdef PIPE_BUF
+    return PIPE_BUF;
+#else
+    return 4096;
+#endif
+}
+
+void pa_reset_personality(void) {
+
+#if defined(__linux__) && !defined(__ANDROID__)
+    if (personality(PER_LINUX) < 0)
+        pa_log_warn("Uh, personality() failed: %s", pa_cstrerror(errno));
+#endif
+
+}
+
+bool pa_run_from_build_tree(void) {
+    char *rp;
+    static bool b = false;
+
+    PA_ONCE_BEGIN {
+        if ((rp = pa_readlink("/proc/self/exe"))) {
+            b = pa_startswith(rp, PA_BUILDDIR);
+            pa_xfree(rp);
+        }
+    } PA_ONCE_END;
+
+    return b;
+}
+
+const char *pa_get_temp_dir(void) {
+    const char *t;
+
+    if ((t = getenv("TMPDIR")) &&
+        pa_is_path_absolute(t))
+        return t;
+
+    if ((t = getenv("TMP")) &&
+        pa_is_path_absolute(t))
+        return t;
+
+    if ((t = getenv("TEMP")) &&
+        pa_is_path_absolute(t))
+        return t;
+
+    if ((t = getenv("TEMPDIR")) &&
+        pa_is_path_absolute(t))
+        return t;
+
+    return "/tmp";
+}
+
+int pa_open_cloexec(const char *fn, int flags, mode_t mode) {
+    int fd;
+
+#ifdef O_NOCTTY
+    flags |= O_NOCTTY;
+#endif
+
+#ifdef O_CLOEXEC
+    if ((fd = open(fn, flags|O_CLOEXEC, mode)) >= 0)
+        goto finish;
+
+    if (errno != EINVAL)
+        return fd;
+#endif
+
+    if ((fd = open(fn, flags, mode)) >= 0)
+        goto finish;
+
+    /* return error */
+    return fd;
+
+finish:
+    /* Some implementations might simply ignore O_CLOEXEC if it is not
+     * understood, make sure FD_CLOEXEC is enabled anyway */
+
+    pa_make_fd_cloexec(fd);
+    return fd;
+}
+
+int pa_socket_cloexec(int domain, int type, int protocol) {
+    int fd;
+
+#ifdef SOCK_CLOEXEC
+    if ((fd = socket(domain, type | SOCK_CLOEXEC, protocol)) >= 0)
+        goto finish;
+
+    if (errno != EINVAL)
+        return fd;
+#endif
+
+    if ((fd = socket(domain, type, protocol)) >= 0)
+        goto finish;
+
+    /* return error */
+    return fd;
+
+finish:
+    /* Some implementations might simply ignore SOCK_CLOEXEC if it is
+     * not understood, make sure FD_CLOEXEC is enabled anyway */
+
+    pa_make_fd_cloexec(fd);
+    return fd;
+}
+
+int pa_pipe_cloexec(int pipefd[2]) {
+    int r;
+
+#ifdef HAVE_PIPE2
+    if ((r = pipe2(pipefd, O_CLOEXEC)) >= 0)
+        goto finish;
+
+    if (errno == EMFILE) {
+        pa_log_error("The per-process limit on the number of open file descriptors has been reached.");
+        return r;
+    }
+
+    if (errno == ENFILE) {
+        pa_log_error("The system-wide limit on the total number of open files has been reached.");
+        return r;
+    }
+
+    if (errno != EINVAL && errno != ENOSYS)
+        return r;
+
+#endif
+
+    if ((r = pipe(pipefd)) >= 0)
+        goto finish;
+
+    if (errno == EMFILE) {
+        pa_log_error("The per-process limit on the number of open file descriptors has been reached.");
+        return r;
+    }
+
+    if (errno == ENFILE) {
+        pa_log_error("The system-wide limit on the total number of open files has been reached.");
+        return r;
+    }
+
+    /* return error */
+    return r;
+
+finish:
+    pa_make_fd_cloexec(pipefd[0]);
+    pa_make_fd_cloexec(pipefd[1]);
+
+    return 0;
+}
+
+int pa_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
+    int fd;
+
+    errno = 0;
+
+#ifdef HAVE_ACCEPT4
+    if ((fd = accept4(sockfd, addr, addrlen, SOCK_CLOEXEC)) >= 0)
+        goto finish;
+
+    if (errno != EINVAL && errno != ENOSYS)
+        return fd;
+
+#endif
+
+#ifdef HAVE_PACCEPT
+    if ((fd = paccept(sockfd, addr, addrlen, NULL, SOCK_CLOEXEC)) >= 0)
+        goto finish;
+#endif
+
+    if ((fd = accept(sockfd, addr, addrlen)) >= 0)
+        goto finish;
+
+    /* return error */
+    return fd;
+
+finish:
+    pa_make_fd_cloexec(fd);
+    return fd;
+}
+
+FILE* pa_fopen_cloexec(const char *path, const char *mode) {
+    FILE *f;
+    char *m;
+
+    m = pa_sprintf_malloc("%se", mode);
+
+    errno = 0;
+    if ((f = fopen(path, m))) {
+        pa_xfree(m);
+        goto finish;
+    }
+
+    pa_xfree(m);
+
+    if (errno != EINVAL)
+        return NULL;
+
+    if (!(f = fopen(path, mode)))
+        return NULL;
+
+finish:
+    pa_make_fd_cloexec(fileno(f));
+    return f;
+}
+
+void pa_nullify_stdfds(void) {
+
+#ifndef OS_IS_WIN32
+        pa_close(STDIN_FILENO);
+        pa_close(STDOUT_FILENO);
+        pa_close(STDERR_FILENO);
+
+        pa_assert_se(open("/dev/null", O_RDONLY) == STDIN_FILENO);
+        pa_assert_se(open("/dev/null", O_WRONLY) == STDOUT_FILENO);
+        pa_assert_se(open("/dev/null", O_WRONLY) == STDERR_FILENO);
+#else
+        FreeConsole();
+#endif
+
+}
+
+char *pa_read_line_from_file(const char *fn) {
+    FILE *f;
+    char ln[256] = "", *r;
+
+    if (!(f = pa_fopen_cloexec(fn, "r")))
+        return NULL;
+
+    r = fgets(ln, sizeof(ln)-1, f);
+    fclose(f);
+
+    if (!r) {
+        errno = EIO;
+        return NULL;
+    }
+
+    pa_strip_nl(ln);
+    return pa_xstrdup(ln);
+}
+
+bool pa_running_in_vm(void) {
+
+#if defined(__i386__) || defined(__x86_64__)
+
+    /* Both CPUID and DMI are x86 specific interfaces... */
+
+    uint32_t eax = 0x40000000;
+    union {
+        uint32_t sig32[3];
+        char text[13];
+    } sig;
+
+#ifdef __linux__
+    const char *const dmi_vendors[] = {
+        "/sys/class/dmi/id/sys_vendor",
+        "/sys/class/dmi/id/board_vendor",
+        "/sys/class/dmi/id/bios_vendor"
+    };
+
+    unsigned i;
+
+    for (i = 0; i < PA_ELEMENTSOF(dmi_vendors); i++) {
+        char *s;
+
+        if ((s = pa_read_line_from_file(dmi_vendors[i]))) {
+
+            if (pa_startswith(s, "QEMU") ||
+                /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+                pa_startswith(s, "VMware") ||
+                pa_startswith(s, "VMW") ||
+                pa_startswith(s, "Microsoft Corporation") ||
+                pa_startswith(s, "innotek GmbH") ||
+                pa_startswith(s, "Xen")) {
+
+                pa_xfree(s);
+                return true;
+            }
+
+            pa_xfree(s);
+        }
+    }
+
+#endif
+
+    /* http://lwn.net/Articles/301888/ */
+    pa_zero(sig);
+
+    __asm__ __volatile__ (
+        /* ebx/rbx is being used for PIC! */
+        "  push %%"PA_REG_b"         \n\t"
+        "  cpuid                     \n\t"
+        "  mov %%ebx, %1             \n\t"
+        "  pop %%"PA_REG_b"          \n\t"
+
+        : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
+        : "0" (eax)
+    );
+
+    if (pa_streq(sig.text, "XenVMMXenVMM") ||
+        pa_streq(sig.text, "KVMKVMKVM") ||
+        /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+        pa_streq(sig.text, "VMwareVMware") ||
+        /* http://msdn.microsoft.com/en-us/library/bb969719.aspx */
+        pa_streq(sig.text, "Microsoft Hv"))
+        return true;
+
+#endif
+
+    return false;
+}
+
+size_t pa_page_size(void) {
+#if defined(PAGE_SIZE)
+    return PAGE_SIZE;
+#elif defined(PAGESIZE)
+    return PAGESIZE;
+#elif defined(HAVE_SYSCONF)
+    static size_t page_size = 4096; /* Let's hope it's like x86. */
+
+    PA_ONCE_BEGIN {
+        long ret = sysconf(_SC_PAGE_SIZE);
+        if (ret > 0)
+            page_size = ret;
+    } PA_ONCE_END;
+
+    return page_size;
+#else
+    return 4096;
+#endif
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
new file mode 100644 (file)
index 0000000..e28b6aa
--- /dev/null
@@ -0,0 +1,321 @@
+#ifndef foocoreutilhfoo
+#define foocoreutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <pulse/gccmacro.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/socket.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+struct timeval;
+
+/* These resource limits are pretty new on Linux, let's define them
+ * here manually, in case the kernel is newer than the glibc */
+#if !defined(RLIMIT_NICE) && defined(__linux__)
+#define RLIMIT_NICE 13
+#endif
+#if !defined(RLIMIT_RTPRIO) && defined(__linux__)
+#define RLIMIT_RTPRIO 14
+#endif
+#if !defined(RLIMIT_RTTIME) && defined(__linux__)
+#define RLIMIT_RTTIME 15
+#endif
+
+void pa_make_fd_nonblock(int fd);
+void pa_make_fd_block(int fd);
+bool pa_is_fd_nonblock(int fd);
+
+void pa_make_fd_cloexec(int fd);
+
+int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid, bool update_perms);
+int pa_make_secure_parent_dir(const char *fn, mode_t, uid_t uid, gid_t gid, bool update_perms);
+
+ssize_t pa_read(int fd, void *buf, size_t count, int *type);
+ssize_t pa_write(int fd, const void *buf, size_t count, int *type);
+ssize_t pa_loop_read(int fd, void*data, size_t size, int *type);
+ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type);
+
+int pa_close(int fd);
+
+void pa_check_signal_is_blocked(int sig);
+
+char *pa_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
+char *pa_vsprintf_malloc(const char *format, va_list ap);
+
+char *pa_strlcpy(char *b, const char *s, size_t l);
+
+char *pa_parent_dir(const char *fn);
+
+int pa_make_realtime(int rtprio);
+int pa_raise_priority(int nice_level);
+void pa_reset_priority(void);
+
+int pa_parse_boolean(const char *s) PA_GCC_PURE;
+
+int pa_parse_volume(const char *s, pa_volume_t *volume);
+
+static inline const char *pa_yes_no(bool b) {
+    return b ? "yes" : "no";
+}
+
+static inline const char *pa_yes_no_localised(bool b) {
+    return b ? _("yes") : _("no");
+}
+
+static inline const char *pa_strnull(const char *x) {
+    return x ? x : "(null)";
+}
+
+static inline const char *pa_strempty(const char *x) {
+    return x ? x : "";
+}
+
+static inline const char *pa_strna(const char *x) {
+    return x ? x : "n/a";
+}
+
+char *pa_split(const char *c, const char *delimiters, const char **state);
+const char *pa_split_in_place(const char *c, const char *delimiters, int *n, const char **state);
+char *pa_split_spaces(const char *c, const char **state);
+const char *pa_split_spaces_in_place(const char *c, int *n, const char **state);
+
+char *pa_strip_nl(char *s);
+char *pa_strip(char *s);
+
+const char *pa_sig2str(int sig) PA_GCC_PURE;
+
+int pa_own_uid_in_group(const char *name, gid_t *gid);
+int pa_uid_in_group(uid_t uid, const char *name);
+gid_t pa_get_gid_of_group(const char *name);
+int pa_check_in_group(gid_t g);
+
+int pa_lock_fd(int fd, int b);
+
+int pa_lock_lockfile(const char *fn);
+int pa_unlock_lockfile(const char *fn, int fd);
+
+char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);
+size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength);
+
+bool pa_startswith(const char *s, const char *pfx) PA_GCC_PURE;
+bool pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
+
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result);
+char* pa_find_config_file(const char *global, const char *local, const char *env);
+
+char *pa_get_runtime_dir(void);
+char *pa_get_state_dir(void);
+char *pa_get_home_dir_malloc(void);
+int pa_append_to_home_dir(const char *path, char **_r);
+int pa_get_config_home_dir(char **_r);
+int pa_append_to_config_home_dir(const char *path, char **_r);
+char *pa_get_binary_name_malloc(void);
+char *pa_runtime_path(const char *fn);
+char *pa_state_path(const char *fn, bool prepend_machine_id);
+
+int pa_atoi(const char *s, int32_t *ret_i);
+int pa_atou(const char *s, uint32_t *ret_u);
+int pa_atol(const char *s, long *ret_l);
+int pa_atod(const char *s, double *ret_d);
+
+size_t pa_snprintf(char *str, size_t size, const char *format, ...);
+size_t pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);
+
+char *pa_truncate_utf8(char *c, size_t l);
+
+int pa_match(const char *expr, const char *v);
+
+char *pa_getcwd(void);
+char *pa_make_path_absolute(const char *p);
+bool pa_is_path_absolute(const char *p);
+
+void *pa_will_need(const void *p, size_t l);
+
+static inline int pa_is_power_of_two(unsigned n) {
+    return !(n & (n - 1));
+}
+
+static inline unsigned pa_ulog2(unsigned n) {
+
+    if (n <= 1)
+        return 0;
+
+#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+    return 8U * (unsigned) sizeof(unsigned) - (unsigned) __builtin_clz(n) - 1;
+#else
+{
+    unsigned r = 0;
+
+    for (;;) {
+        n = n >> 1;
+
+        if (!n)
+            return r;
+
+        r++;
+    }
+}
+#endif
+}
+
+static inline unsigned pa_make_power_of_two(unsigned n) {
+
+    if (pa_is_power_of_two(n))
+        return n;
+
+    return 1U << (pa_ulog2(n) + 1);
+}
+
+void pa_close_pipe(int fds[2]);
+
+char *pa_readlink(const char *p);
+
+int pa_close_all(int except_fd, ...);
+int pa_close_allv(const int except_fds[]);
+int pa_unblock_sigs(int except, ...);
+int pa_unblock_sigsv(const int except[]);
+int pa_reset_sigs(int except, ...);
+int pa_reset_sigsv(const int except[]);
+
+void pa_set_env(const char *key, const char *value);
+void pa_unset_env(const char *key);
+void pa_set_env_and_record(const char *key, const char *value);
+void pa_unset_env_recorded(void);
+
+bool pa_in_system_mode(void);
+
+#define pa_streq(a,b) (!strcmp((a),(b)))
+#define pa_strneq(a,b,n) (!strncmp((a),(b),(n)))
+
+/* Like pa_streq, but does not blow up on NULL pointers. */
+static inline bool pa_safe_streq(const char *a, const char *b) {
+    if (a == NULL || b == NULL)
+        return a == b;
+    return pa_streq(a, b);
+}
+
+bool pa_str_in_list_spaces(const char *needle, const char *haystack);
+bool pa_str_in_list(const char *haystack, const char *delimiters, const char *needle);
+
+char *pa_get_host_name_malloc(void);
+char *pa_get_user_name_malloc(void);
+
+char *pa_machine_id(void);
+char *pa_session_id(void);
+char *pa_uname_string(void);
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+bool pa_in_valgrind(void);
+#else
+static inline bool pa_in_valgrind(void) {
+    return false;
+}
+#endif
+
+unsigned pa_gcd(unsigned a, unsigned b);
+void pa_reduce(unsigned *num, unsigned *den);
+
+unsigned pa_ncpus(void);
+
+/* Replaces all occurrences of `a' in `s' with `b'. The caller has to free the
+ * returned string. All parameters must be non-NULL and additionally `a' must
+ * not be a zero-length string.
+ */
+char *pa_replace(const char*s, const char*a, const char *b);
+
+/* Escapes p by inserting backslashes in front of backslashes. chars is a
+ * regular (i.e. NULL-terminated) string containing additional characters that
+ * should be escaped. chars can be NULL. The caller has to free the returned
+ * string. */
+char *pa_escape(const char *p, const char *chars);
+
+/* Does regular backslash unescaping. Returns the argument p. */
+char *pa_unescape(char *p);
+
+char *pa_realpath(const char *path);
+
+void pa_disable_sigpipe(void);
+
+void pa_xfreev(void**a);
+
+static inline void pa_xstrfreev(char **a) {
+    pa_xfreev((void**) a);
+}
+
+char **pa_split_spaces_strv(const char *s);
+
+char* pa_maybe_prefix_path(const char *path, const char *prefix);
+
+/* Returns size of the specified pipe or 4096 on failure */
+size_t pa_pipe_buf(int fd);
+
+void pa_reset_personality(void);
+
+bool pa_run_from_build_tree(void) PA_GCC_CONST;
+
+const char *pa_get_temp_dir(void);
+
+int pa_open_cloexec(const char *fn, int flags, mode_t mode);
+int pa_socket_cloexec(int domain, int type, int protocol);
+int pa_pipe_cloexec(int pipefd[2]);
+int pa_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
+FILE* pa_fopen_cloexec(const char *path, const char *mode);
+
+void pa_nullify_stdfds(void);
+
+char *pa_read_line_from_file(const char *fn);
+bool pa_running_in_vm(void);
+
+#ifdef OS_IS_WIN32
+char *pa_win32_get_toplevel(HANDLE handle);
+#endif
+
+size_t pa_page_size(void);
+
+/* Rounds down */
+static inline void* PA_PAGE_ALIGN_PTR(const void *p) {
+    return (void*) (((size_t) p) & ~(pa_page_size() - 1));
+}
+
+/* Rounds up */
+static inline size_t PA_PAGE_ALIGN(size_t l) {
+    size_t page_size = pa_page_size();
+    return (l + page_size - 1) & ~(page_size - 1);
+}
+
+#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
new file mode 100644 (file)
index 0000000..454c566
--- /dev/null
@@ -0,0 +1,504 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/random.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "core.h"
+
+PA_DEFINE_PUBLIC_CLASS(pa_core, pa_msgobject);
+
+static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_core *c = PA_CORE(o);
+
+    pa_core_assert_ref(c);
+
+    switch (code) {
+
+        case PA_CORE_MESSAGE_UNLOAD_MODULE:
+            pa_module_unload(userdata, true);
+            return 0;
+
+        default:
+            return -1;
+    }
+}
+
+static void core_free(pa_object *o);
+
+pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size) {
+    pa_core* c;
+    pa_mempool *pool;
+    pa_mem_type_t type;
+    int j;
+
+    pa_assert(m);
+
+    if (shared) {
+        type = (enable_memfd) ? PA_MEM_TYPE_SHARED_MEMFD : PA_MEM_TYPE_SHARED_POSIX;
+        if (!(pool = pa_mempool_new(type, shm_size, false))) {
+            pa_log_warn("Failed to allocate %s memory pool. Falling back to a normal memory pool.",
+                        pa_mem_type_to_string(type));
+            shared = false;
+        }
+    }
+
+    if (!shared) {
+        if (!(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, shm_size, false))) {
+            pa_log("pa_mempool_new() failed.");
+            return NULL;
+        }
+    }
+
+    c = pa_msgobject_new(pa_core);
+    c->parent.parent.free = core_free;
+    c->parent.process_msg = core_process_msg;
+
+    c->state = PA_CORE_STARTUP;
+    c->mainloop = m;
+
+    c->clients = pa_idxset_new(NULL, NULL);
+    c->cards = pa_idxset_new(NULL, NULL);
+    c->sinks = pa_idxset_new(NULL, NULL);
+    c->sources = pa_idxset_new(NULL, NULL);
+    c->sink_inputs = pa_idxset_new(NULL, NULL);
+    c->source_outputs = pa_idxset_new(NULL, NULL);
+    c->modules = pa_idxset_new(NULL, NULL);
+    c->scache = pa_idxset_new(NULL, NULL);
+
+    c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    c->default_source = NULL;
+    c->default_sink = NULL;
+
+    c->default_sample_spec.format = PA_SAMPLE_S16NE;
+    c->default_sample_spec.rate = 44100;
+    c->default_sample_spec.channels = 2;
+    pa_channel_map_init_extend(&c->default_channel_map, c->default_sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+    c->default_n_fragments = 4;
+    c->default_fragment_size_msec = 25;
+
+    c->deferred_volume_safety_margin_usec = 8000;
+    c->deferred_volume_extra_delay_usec = 0;
+
+    c->module_defer_unload_event = NULL;
+    c->modules_pending_unload = pa_hashmap_new(NULL, NULL);
+
+    c->subscription_defer_event = NULL;
+    PA_LLIST_HEAD_INIT(pa_subscription, c->subscriptions);
+    PA_LLIST_HEAD_INIT(pa_subscription_event, c->subscription_event_queue);
+    c->subscription_event_last = NULL;
+
+    c->mempool = pool;
+    c->shm_size = shm_size;
+    pa_silence_cache_init(&c->silence_cache);
+
+    c->exit_event = NULL;
+    c->scache_auto_unload_event = NULL;
+
+    c->exit_idle_time = -1;
+    c->scache_idle_time = 20;
+
+    c->flat_volumes = true;
+    c->disallow_module_loading = false;
+    c->disallow_exit = false;
+    c->running_as_daemon = false;
+    c->realtime_scheduling = false;
+    c->realtime_priority = 5;
+    c->disable_remixing = false;
+    c->remixing_use_all_sink_channels = true;
+    c->disable_lfe_remixing = true;
+    c->lfe_crossover_freq = 0;
+    c->deferred_volume = true;
+    c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1;
+
+    for (j = 0; j < PA_CORE_HOOK_MAX; j++)
+        pa_hook_init(&c->hooks[j], c);
+
+    pa_random(&c->cookie, sizeof(c->cookie));
+
+#ifdef SIGPIPE
+    pa_check_signal_is_blocked(SIGPIPE);
+#endif
+
+    pa_core_check_idle(c);
+
+    c->state = PA_CORE_RUNNING;
+
+    return c;
+}
+
+static void core_free(pa_object *o) {
+    pa_core *c = PA_CORE(o);
+    int j;
+    pa_assert(c);
+
+    c->state = PA_CORE_SHUTDOWN;
+
+    /* Note: All modules and samples in the cache should be unloaded before
+     * we get here */
+
+    pa_assert(pa_idxset_isempty(c->scache));
+    pa_idxset_free(c->scache, NULL);
+
+    pa_assert(pa_idxset_isempty(c->modules));
+    pa_idxset_free(c->modules, NULL);
+
+    pa_assert(pa_idxset_isempty(c->clients));
+    pa_idxset_free(c->clients, NULL);
+
+    pa_assert(pa_idxset_isempty(c->cards));
+    pa_idxset_free(c->cards, NULL);
+
+    pa_assert(pa_idxset_isempty(c->sinks));
+    pa_idxset_free(c->sinks, NULL);
+
+    pa_assert(pa_idxset_isempty(c->sources));
+    pa_idxset_free(c->sources, NULL);
+
+    pa_assert(pa_idxset_isempty(c->source_outputs));
+    pa_idxset_free(c->source_outputs, NULL);
+
+    pa_assert(pa_idxset_isempty(c->sink_inputs));
+    pa_idxset_free(c->sink_inputs, NULL);
+
+    pa_assert(pa_hashmap_isempty(c->namereg));
+    pa_hashmap_free(c->namereg);
+
+    pa_assert(pa_hashmap_isempty(c->shared));
+    pa_hashmap_free(c->shared);
+
+    pa_assert(pa_hashmap_isempty(c->modules_pending_unload));
+    pa_hashmap_free(c->modules_pending_unload);
+
+    pa_subscription_free_all(c);
+
+    if (c->exit_event)
+        c->mainloop->time_free(c->exit_event);
+
+    pa_assert(!c->default_source);
+    pa_assert(!c->default_sink);
+    pa_xfree(c->configured_default_source);
+    pa_xfree(c->configured_default_sink);
+
+    pa_silence_cache_done(&c->silence_cache);
+    pa_mempool_unref(c->mempool);
+
+    for (j = 0; j < PA_CORE_HOOK_MAX; j++)
+        pa_hook_done(&c->hooks[j]);
+
+    pa_xfree(c);
+}
+
+void pa_core_set_configured_default_sink(pa_core *core, const char *sink) {
+    char *old_sink;
+
+    pa_assert(core);
+
+    old_sink = pa_xstrdup(core->configured_default_sink);
+
+    if (pa_safe_streq(sink, old_sink))
+        goto finish;
+
+    pa_xfree(core->configured_default_sink);
+    core->configured_default_sink = pa_xstrdup(sink);
+    pa_log_info("configured_default_sink: %s -> %s",
+                old_sink ? old_sink : "(unset)", sink ? sink : "(unset)");
+
+    pa_core_update_default_sink(core);
+
+finish:
+    pa_xfree(old_sink);
+}
+
+void pa_core_set_configured_default_source(pa_core *core, const char *source) {
+    char *old_source;
+
+    pa_assert(core);
+
+    old_source = pa_xstrdup(core->configured_default_source);
+
+    if (pa_safe_streq(source, old_source))
+        goto finish;
+
+    pa_xfree(core->configured_default_source);
+    core->configured_default_source = pa_xstrdup(source);
+    pa_log_info("configured_default_source: %s -> %s",
+                old_source ? old_source : "(unset)", source ? source : "(unset)");
+
+    pa_core_update_default_source(core);
+
+finish:
+    pa_xfree(old_source);
+}
+
+/* a  < b  ->  return -1
+ * a == b  ->  return  0
+ * a  > b  ->  return  1 */
+static int compare_sinks(pa_sink *a, pa_sink *b) {
+    pa_core *core;
+
+    core = a->core;
+
+    /* Available sinks always beat unavailable sinks. */
+    if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
+            && (!b->active_port || b->active_port->available != PA_AVAILABLE_NO))
+        return -1;
+    if (b->active_port && b->active_port->available == PA_AVAILABLE_NO
+            && (!a->active_port || a->active_port->available != PA_AVAILABLE_NO))
+        return 1;
+
+    /* The configured default sink is preferred over any other sink. */
+    if (pa_safe_streq(b->name, core->configured_default_sink))
+        return -1;
+    if (pa_safe_streq(a->name, core->configured_default_sink))
+        return 1;
+
+    if (a->priority < b->priority)
+        return -1;
+    if (a->priority > b->priority)
+        return 1;
+
+    /* It's hard to find any difference between these sinks, but maybe one of
+     * them is already the default sink? If so, it's best to keep it as the
+     * default to avoid changing the routing for no good reason. */
+    if (b == core->default_sink)
+        return -1;
+    if (a == core->default_sink)
+        return 1;
+
+    return 0;
+}
+
+void pa_core_update_default_sink(pa_core *core) {
+    pa_sink *best = NULL;
+    pa_sink *sink;
+    uint32_t idx;
+    pa_sink *old_default_sink;
+
+    pa_assert(core);
+
+    PA_IDXSET_FOREACH(sink, core->sinks, idx) {
+        if (!PA_SINK_IS_LINKED(sink->state))
+            continue;
+
+        if (!best) {
+            best = sink;
+            continue;
+        }
+
+        if (compare_sinks(sink, best) > 0)
+            best = sink;
+    }
+
+    old_default_sink = core->default_sink;
+
+    if (best == old_default_sink)
+        return;
+
+    core->default_sink = best;
+    pa_log_info("default_sink: %s -> %s",
+                old_default_sink ? old_default_sink->name : "(unset)", best ? best->name : "(unset)");
+
+    /* If the default sink changed, it may be that the default source has to be
+     * changed too, because monitor sources are prioritized partly based on the
+     * priorities of the monitored sinks. */
+    pa_core_update_default_source(core);
+
+    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
+    pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], core->default_sink);
+}
+
+/* a  < b  ->  return -1
+ * a == b  ->  return  0
+ * a  > b  ->  return  1 */
+static int compare_sources(pa_source *a, pa_source *b) {
+    pa_core *core;
+
+    core = a->core;
+
+    /* Available sources always beat unavailable sources. */
+    if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
+            && (!b->active_port || b->active_port->available != PA_AVAILABLE_NO))
+        return -1;
+    if (b->active_port && b->active_port->available == PA_AVAILABLE_NO
+            && (!a->active_port || a->active_port->available != PA_AVAILABLE_NO))
+        return 1;
+
+    /* The configured default source is preferred over any other source. */
+    if (pa_safe_streq(b->name, core->configured_default_source))
+        return -1;
+    if (pa_safe_streq(a->name, core->configured_default_source))
+        return 1;
+
+    /* Monitor sources lose to non-monitor sources. */
+    if (a->monitor_of && !b->monitor_of)
+        return -1;
+    if (!a->monitor_of && b->monitor_of)
+        return 1;
+
+    if (a->priority < b->priority)
+        return -1;
+    if (a->priority > b->priority)
+        return 1;
+
+    /* If the sources are monitors, we can compare the monitored sinks. */
+    if (a->monitor_of)
+        return compare_sinks(a->monitor_of, b->monitor_of);
+
+    /* It's hard to find any difference between these sources, but maybe one of
+     * them is already the default source? If so, it's best to keep it as the
+     * default to avoid changing the routing for no good reason. */
+    if (b == core->default_source)
+        return -1;
+    if (a == core->default_source)
+        return 1;
+
+    return 0;
+}
+
+void pa_core_update_default_source(pa_core *core) {
+    pa_source *best = NULL;
+    pa_source *source;
+    uint32_t idx;
+    pa_source *old_default_source;
+
+    pa_assert(core);
+
+    PA_IDXSET_FOREACH(source, core->sources, idx) {
+        if (!PA_SOURCE_IS_LINKED(source->state))
+            continue;
+
+        if (!best) {
+            best = source;
+            continue;
+        }
+
+        if (compare_sources(source, best) > 0)
+            best = source;
+    }
+
+    old_default_source = core->default_source;
+
+    if (best == old_default_source)
+        return;
+
+    core->default_source = best;
+    pa_log_info("default_source: %s -> %s",
+                old_default_source ? old_default_source->name : "(unset)", best ? best->name : "(unset)");
+    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
+    pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED], core->default_source);
+}
+
+static void exit_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
+    pa_core *c = userdata;
+    pa_assert(c->exit_event == e);
+
+    pa_log_info("We are idle, quitting...");
+    pa_core_exit(c, true, 0);
+}
+
+void pa_core_check_idle(pa_core *c) {
+    pa_assert(c);
+
+    if (!c->exit_event &&
+        c->exit_idle_time >= 0 &&
+        pa_idxset_size(c->clients) == 0) {
+
+        c->exit_event = pa_core_rttime_new(c, pa_rtclock_now() + c->exit_idle_time * PA_USEC_PER_SEC, exit_callback, c);
+
+    } else if (c->exit_event && pa_idxset_size(c->clients) > 0) {
+        c->mainloop->time_free(c->exit_event);
+        c->exit_event = NULL;
+    }
+}
+
+int pa_core_exit(pa_core *c, bool force, int retval) {
+    pa_assert(c);
+
+    if (c->disallow_exit && !force)
+        return -1;
+
+    c->mainloop->quit(c->mainloop, retval);
+    return 0;
+}
+
+void pa_core_maybe_vacuum(pa_core *c) {
+    pa_assert(c);
+
+    if (pa_idxset_isempty(c->sink_inputs) && pa_idxset_isempty(c->source_outputs)) {
+        pa_log_debug("Hmm, no streams around, trying to vacuum.");
+    } else {
+        pa_sink *si;
+        pa_source *so;
+        uint32_t idx;
+
+        idx = 0;
+        PA_IDXSET_FOREACH(si, c->sinks, idx)
+            if (pa_sink_get_state(si) != PA_SINK_SUSPENDED)
+                return;
+
+        idx = 0;
+        PA_IDXSET_FOREACH(so, c->sources, idx)
+            if (pa_source_get_state(so) != PA_SOURCE_SUSPENDED)
+                return;
+
+        pa_log_info("All sinks and sources are suspended, vacuuming memory");
+    }
+
+    pa_mempool_vacuum(c->mempool);
+}
+
+pa_time_event* pa_core_rttime_new(pa_core *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) {
+    struct timeval tv;
+
+    pa_assert(c);
+    pa_assert(c->mainloop);
+
+    return c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, usec, true), cb, userdata);
+}
+
+void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec) {
+    struct timeval tv;
+
+    pa_assert(c);
+    pa_assert(c->mainloop);
+
+    c->mainloop->time_restart(e, pa_timeval_rtstore(&tv, usec, true));
+}
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
new file mode 100644 (file)
index 0000000..79a095d
--- /dev/null
@@ -0,0 +1,268 @@
+#ifndef foocorehfoo
+#define foocorehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/typedefs.h>
+#include <pulse/mainloop-api.h>
+#include <pulse/sample.h>
+#include <pulsecore/cpu.h>
+
+/* This is a bitmask that encodes the cause why a sink/source is
+ * suspended. */
+typedef enum pa_suspend_cause {
+    PA_SUSPEND_USER = 1,         /* Exposed to the user via some protocol */
+    PA_SUSPEND_APPLICATION = 2,  /* Used by the device reservation logic */
+    PA_SUSPEND_IDLE = 4,         /* Used by module-suspend-on-idle */
+    PA_SUSPEND_SESSION = 8,      /* Used by module-hal for mark inactive sessions */
+    PA_SUSPEND_PASSTHROUGH = 16, /* Used to suspend monitor sources when the sink is in passthrough mode */
+    PA_SUSPEND_INTERNAL = 32,    /* This is used for short period server-internal suspends, such as for sample rate updates */
+    PA_SUSPEND_ALL = 0xFFFF      /* Magic cause that can be used to resume forcibly */
+} pa_suspend_cause_t;
+
+#include <pulsecore/idxset.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/hook-list.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/msgobject.h>
+
+typedef enum pa_server_type {
+    PA_SERVER_TYPE_UNSET,
+    PA_SERVER_TYPE_USER,
+    PA_SERVER_TYPE_SYSTEM,
+    PA_SERVER_TYPE_NONE
+} pa_server_type_t;
+
+typedef enum pa_core_state {
+    PA_CORE_STARTUP,
+    PA_CORE_RUNNING,
+    PA_CORE_SHUTDOWN
+} pa_core_state_t;
+
+typedef enum pa_core_hook {
+    PA_CORE_HOOK_SINK_NEW,
+    PA_CORE_HOOK_SINK_FIXATE,
+    PA_CORE_HOOK_SINK_PUT,
+    PA_CORE_HOOK_SINK_UNLINK,
+    PA_CORE_HOOK_SINK_UNLINK_POST,
+    PA_CORE_HOOK_SINK_STATE_CHANGED,
+    PA_CORE_HOOK_SINK_PROPLIST_CHANGED,
+    PA_CORE_HOOK_SINK_PORT_CHANGED,
+    PA_CORE_HOOK_SINK_FLAGS_CHANGED,
+    PA_CORE_HOOK_SINK_VOLUME_CHANGED,
+    PA_CORE_HOOK_SINK_MUTE_CHANGED,
+    PA_CORE_HOOK_SINK_PORT_LATENCY_OFFSET_CHANGED,
+    PA_CORE_HOOK_SOURCE_NEW,
+    PA_CORE_HOOK_SOURCE_FIXATE,
+    PA_CORE_HOOK_SOURCE_PUT,
+    PA_CORE_HOOK_SOURCE_UNLINK,
+    PA_CORE_HOOK_SOURCE_UNLINK_POST,
+    PA_CORE_HOOK_SOURCE_STATE_CHANGED,
+    PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED,
+    PA_CORE_HOOK_SOURCE_PORT_CHANGED,
+    PA_CORE_HOOK_SOURCE_FLAGS_CHANGED,
+    PA_CORE_HOOK_SOURCE_VOLUME_CHANGED,
+    PA_CORE_HOOK_SOURCE_MUTE_CHANGED,
+    PA_CORE_HOOK_SOURCE_PORT_LATENCY_OFFSET_CHANGED,
+    PA_CORE_HOOK_SINK_INPUT_NEW,
+    PA_CORE_HOOK_SINK_INPUT_FIXATE,
+    PA_CORE_HOOK_SINK_INPUT_PUT,
+    PA_CORE_HOOK_SINK_INPUT_UNLINK,
+    PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
+    PA_CORE_HOOK_SINK_INPUT_MOVE_START,
+    PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH,
+    PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL,
+    PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
+    PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED,
+    PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED,
+    PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
+    PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
+    PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
+    PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
+    PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK,
+    PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
+    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START,
+    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH,
+    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL,
+    PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
+    PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED,
+    PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED,
+    PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
+    PA_CORE_HOOK_CLIENT_NEW,
+    PA_CORE_HOOK_CLIENT_PUT,
+    PA_CORE_HOOK_CLIENT_UNLINK,
+    PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_CLIENT_SEND_EVENT,
+    PA_CORE_HOOK_CARD_NEW,
+    PA_CORE_HOOK_CARD_CHOOSE_INITIAL_PROFILE,
+    PA_CORE_HOOK_CARD_PUT,
+    PA_CORE_HOOK_CARD_UNLINK,
+    PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED,
+    PA_CORE_HOOK_CARD_PROFILE_CHANGED,
+    PA_CORE_HOOK_CARD_PROFILE_ADDED,
+    PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED,
+    PA_CORE_HOOK_CARD_SUSPEND_CHANGED,
+    PA_CORE_HOOK_PORT_AVAILABLE_CHANGED,
+    PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED,
+    PA_CORE_HOOK_DEFAULT_SINK_CHANGED,
+    PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED,
+    PA_CORE_HOOK_MODULE_NEW,
+    PA_CORE_HOOK_MODULE_PROPLIST_CHANGED,
+    PA_CORE_HOOK_MODULE_UNLINK,
+    PA_CORE_HOOK_SAMPLE_CACHE_NEW,
+    PA_CORE_HOOK_SAMPLE_CACHE_CHANGED,
+    PA_CORE_HOOK_SAMPLE_CACHE_UNLINK,
+    PA_CORE_HOOK_MAX
+} pa_core_hook_t;
+
+/* The core structure of PulseAudio. Every PulseAudio daemon contains
+ * exactly one of these. It is used for storing kind of global
+ * variables for the daemon. */
+
+struct pa_core {
+    pa_msgobject parent;
+
+    pa_core_state_t state;
+
+    /* A random value which may be used to identify this instance of
+     * PulseAudio. Not cryptographically secure in any way. */
+    uint32_t cookie;
+
+    pa_mainloop_api *mainloop;
+
+    /* idxset of all kinds of entities */
+    pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache;
+
+    /* Some hashmaps for all sorts of entities */
+    pa_hashmap *namereg, *shared;
+
+    /* The default sink/source as configured by the user. If the user hasn't
+     * explicitly configured anything, these are set to NULL. These are strings
+     * instead of sink/source pointers, because that allows us to reference
+     * devices that don't currently exist. That's useful for remembering that
+     * a hotplugged USB sink was previously set as the default sink. */
+    char *configured_default_sink;
+    char *configured_default_source;
+
+    /* The effective default sink/source. If no sink or source is explicitly
+     * configured as the default, we pick the device that ranks highest
+     * according to the compare_sinks() and compare_sources() functions in
+     * core.c. pa_core_update_default_sink/source() has to be called whenever
+     * anything changes that might change the comparison results. */
+    pa_sink *default_sink;
+    pa_source *default_source;
+
+    pa_channel_map default_channel_map;
+    pa_sample_spec default_sample_spec;
+    uint32_t alternate_sample_rate;
+    unsigned default_n_fragments, default_fragment_size_msec;
+    unsigned deferred_volume_safety_margin_usec;
+    int deferred_volume_extra_delay_usec;
+    unsigned lfe_crossover_freq;
+
+    pa_defer_event *module_defer_unload_event;
+    pa_hashmap *modules_pending_unload; /* pa_module -> pa_module (hashmap-as-a-set) */
+
+    pa_defer_event *subscription_defer_event;
+    PA_LLIST_HEAD(pa_subscription, subscriptions);
+    PA_LLIST_HEAD(pa_subscription_event, subscription_event_queue);
+    pa_subscription_event *subscription_event_last;
+
+    /* The mempool is used for data we write to, it's readonly for the client. */
+    pa_mempool *mempool;
+
+    /* Shared memory size, as specified either by daemon configuration
+     * or PA daemon defaults (~ 64 MiB). */
+    size_t shm_size;
+
+    pa_silence_cache silence_cache;
+
+    pa_time_event *exit_event;
+    pa_time_event *scache_auto_unload_event;
+
+    int exit_idle_time, scache_idle_time;
+
+    bool flat_volumes:1;
+    bool disallow_module_loading:1;
+    bool disallow_exit:1;
+    bool running_as_daemon:1;
+    bool realtime_scheduling:1;
+    bool avoid_resampling:1;
+    bool disable_remixing:1;
+    bool remixing_use_all_sink_channels:1;
+    bool disable_lfe_remixing:1;
+    bool deferred_volume:1;
+
+    pa_resample_method_t resample_method;
+    int realtime_priority;
+
+    pa_server_type_t server_type;
+    pa_cpu_info cpu_info;
+
+    /* hooks */
+    pa_hook hooks[PA_CORE_HOOK_MAX];
+};
+
+PA_DECLARE_PUBLIC_CLASS(pa_core);
+#define PA_CORE(o) pa_core_cast(o)
+
+enum {
+    PA_CORE_MESSAGE_UNLOAD_MODULE,
+    PA_CORE_MESSAGE_MAX
+};
+
+pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size);
+
+void pa_core_set_configured_default_sink(pa_core *core, const char *sink);
+void pa_core_set_configured_default_source(pa_core *core, const char *source);
+
+/* These should be called whenever something changes that may affect the
+ * default sink or source choice.
+ *
+ * If the default source choice happens between two monitor sources, the
+ * monitored sinks are compared, so if the default sink changes, the default
+ * source may change too. However, pa_core_update_default_sink() calls
+ * pa_core_update_default_source() internally, so it's sufficient to only call
+ * pa_core_update_default_sink() when something happens that affects the sink
+ * ordering. */
+void pa_core_update_default_sink(pa_core *core);
+void pa_core_update_default_source(pa_core *core);
+
+/* Check whether no one is connected to this core */
+void pa_core_check_idle(pa_core *c);
+
+int pa_core_exit(pa_core *c, bool force, int retval);
+
+void pa_core_maybe_vacuum(pa_core *c);
+
+/* wrapper for c->mainloop->time_*() RT time events */
+pa_time_event* pa_core_rttime_new(pa_core *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata);
+void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec);
+
+#endif
diff --git a/src/pulsecore/cpu-arm.c b/src/pulsecore/cpu-arm.c
new file mode 100644 (file)
index 0000000..122a72b
--- /dev/null
@@ -0,0 +1,170 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "cpu-arm.h"
+
+#if defined (__arm__) && defined (__linux__)
+
+#define MAX_BUFFER 4096
+static char *
+get_cpuinfo_line(char *cpuinfo, const char *tag) {
+    char *line, *end, *colon;
+
+    if (!(line = strstr(cpuinfo, tag)))
+        return NULL;
+
+    if (!(end = strchr(line, '\n')))
+        return NULL;
+
+    if (!(colon = strchr(line, ':')))
+        return NULL;
+
+    if (++colon >= end)
+        return NULL;
+
+    return pa_xstrndup(colon, end - colon);
+}
+
+static char *get_cpuinfo(void) {
+    char *cpuinfo;
+    int n, fd;
+
+    cpuinfo = pa_xmalloc(MAX_BUFFER);
+
+    if ((fd = pa_open_cloexec("/proc/cpuinfo", O_RDONLY, 0)) < 0) {
+        pa_xfree(cpuinfo);
+        return NULL;
+    }
+
+    if ((n = pa_read(fd, cpuinfo, MAX_BUFFER-1, NULL)) < 0) {
+        pa_xfree(cpuinfo);
+        pa_close(fd);
+        return NULL;
+    }
+    cpuinfo[n] = 0;
+    pa_close(fd);
+
+    return cpuinfo;
+}
+#endif /* defined (__arm__) && defined (__linux__) */
+
+void pa_cpu_get_arm_flags(pa_cpu_arm_flag_t *flags) {
+#if defined (__arm__) && defined (__linux__)
+    char *cpuinfo, *line;
+    int arch, part;
+
+    /* We need to read the CPU flags from /proc/cpuinfo because there is no user
+     * space support to get the CPU features. This only works on linux AFAIK. */
+    if (!(cpuinfo = get_cpuinfo())) {
+        pa_log("Can't read cpuinfo");
+        return;
+    }
+
+    *flags = 0;
+
+    /* get the CPU architecture */
+    if ((line = get_cpuinfo_line(cpuinfo, "CPU architecture"))) {
+        arch = strtoul(line, NULL, 0);
+        if (arch >= 6)
+            *flags |= PA_CPU_ARM_V6;
+        if (arch >= 7)
+            *flags |= PA_CPU_ARM_V7;
+
+        pa_xfree(line);
+    }
+
+    /* get the CPU features */
+    if ((line = get_cpuinfo_line(cpuinfo, "Features"))) {
+        const char *state = NULL;
+        char *current;
+
+        while ((current = pa_split_spaces(line, &state))) {
+            if (pa_streq(current, "vfp"))
+                *flags |= PA_CPU_ARM_VFP;
+            else if (pa_streq(current, "edsp"))
+                *flags |= PA_CPU_ARM_EDSP;
+            else if (pa_streq(current, "neon"))
+                *flags |= PA_CPU_ARM_NEON;
+            else if (pa_streq(current, "vfpv3"))
+                *flags |= PA_CPU_ARM_VFPV3;
+
+            pa_xfree(current);
+        }
+        pa_xfree(line);
+    }
+
+    /* get the CPU part number */
+    if ((line = get_cpuinfo_line(cpuinfo, "CPU part"))) {
+        part = strtoul(line, NULL, 0);
+        if (part == 0xc08)
+            *flags |= PA_CPU_ARM_CORTEX_A8;
+        pa_xfree(line);
+    }
+    pa_xfree(cpuinfo);
+
+    pa_log_info("CPU flags: %s%s%s%s%s%s%s",
+          (*flags & PA_CPU_ARM_V6) ? "V6 " : "",
+          (*flags & PA_CPU_ARM_V7) ? "V7 " : "",
+          (*flags & PA_CPU_ARM_VFP) ? "VFP " : "",
+          (*flags & PA_CPU_ARM_EDSP) ? "EDSP " : "",
+          (*flags & PA_CPU_ARM_NEON) ? "NEON " : "",
+          (*flags & PA_CPU_ARM_VFPV3) ? "VFPV3 " : "",
+          (*flags & PA_CPU_ARM_CORTEX_A8) ? "Cortex-A8 " : "");
+#endif
+}
+
+bool pa_cpu_init_arm(pa_cpu_arm_flag_t *flags) {
+#if defined (__arm__)
+#if defined (__linux__)
+    pa_cpu_get_arm_flags(flags);
+
+    if (*flags & PA_CPU_ARM_V6)
+        pa_volume_func_init_arm(*flags);
+
+#ifdef HAVE_NEON
+    if (*flags & PA_CPU_ARM_NEON) {
+        pa_convert_func_init_neon(*flags);
+        pa_mix_func_init_neon(*flags);
+        pa_remap_func_init_neon(*flags);
+    }
+#endif
+
+    return true;
+
+#else /* defined (__linux__) */
+    pa_log("Reading ARM CPU features not yet supported on this OS");
+#endif /* defined (__linux__) */
+
+#else /* defined (__arm__) */
+    return false;
+#endif /* defined (__arm__) */
+}
diff --git a/src/pulsecore/cpu-arm.h b/src/pulsecore/cpu-arm.h
new file mode 100644 (file)
index 0000000..dfdda32
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef foocpuarmhfoo
+#define foocpuarmhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdint.h>
+#include <pulsecore/macro.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+typedef enum pa_cpu_arm_flag {
+    PA_CPU_ARM_V6       = (1 << 0),
+    PA_CPU_ARM_V7       = (1 << 1),
+    PA_CPU_ARM_VFP      = (1 << 2),
+    PA_CPU_ARM_EDSP     = (1 << 3),
+    PA_CPU_ARM_NEON     = (1 << 4),
+    PA_CPU_ARM_VFPV3    = (1 << 5),
+    PA_CPU_ARM_CORTEX_A8 = (1 << 6),
+} pa_cpu_arm_flag_t;
+
+void pa_cpu_get_arm_flags(pa_cpu_arm_flag_t *flags);
+bool pa_cpu_init_arm(pa_cpu_arm_flag_t *flags);
+
+/* some optimized functions */
+void pa_volume_func_init_arm(pa_cpu_arm_flag_t flags);
+
+#ifdef HAVE_NEON
+void pa_convert_func_init_neon(pa_cpu_arm_flag_t flags);
+void pa_mix_func_init_neon(pa_cpu_arm_flag_t flags);
+void pa_remap_func_init_neon(pa_cpu_arm_flag_t flags);
+#endif
+
+#endif /* foocpuarmhfoo */
diff --git a/src/pulsecore/cpu-orc.c b/src/pulsecore/cpu-orc.c
new file mode 100644 (file)
index 0000000..5caf6b6
--- /dev/null
@@ -0,0 +1,39 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2010 Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "cpu-orc.h"
+
+bool pa_cpu_init_orc(pa_cpu_info cpu_info) {
+#ifndef DISABLE_ORC
+    /* Update these as we test on more architectures */
+    pa_cpu_x86_flag_t x86_want_flags = PA_CPU_X86_MMX | PA_CPU_X86_SSE | PA_CPU_X86_SSE2 | PA_CPU_X86_SSE3 | PA_CPU_X86_SSSE3 | PA_CPU_X86_SSE4_1 | PA_CPU_X86_SSE4_2;
+
+    /* Enable Orc svolume optimizations */
+    if ((cpu_info.cpu_type == PA_CPU_X86) && (cpu_info.flags.x86 & x86_want_flags)) {
+        pa_volume_func_init_orc();
+        return true;
+    }
+#endif
+
+    return false;
+}
diff --git a/src/pulsecore/cpu-orc.h b/src/pulsecore/cpu-orc.h
new file mode 100644 (file)
index 0000000..edfacea
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef foocpuorchfoo
+#define foocpuorchfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2010 Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/cpu.h>
+
+/* Orc-optimised bits */
+
+bool pa_cpu_init_orc(pa_cpu_info cpu_info);
+
+void pa_volume_func_init_orc(void);
+
+#endif /* foocpuorchfoo */
diff --git a/src/pulsecore/cpu-x86.c b/src/pulsecore/cpu-x86.c
new file mode 100644 (file)
index 0000000..a86c26d
--- /dev/null
@@ -0,0 +1,135 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+
+#include <pulsecore/log.h>
+
+#include "cpu-x86.h"
+
+#if defined (__i386__) || defined (__amd64__)
+static void get_cpuid(uint32_t op, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) {
+    __asm__ __volatile__ (
+        "  push %%"PA_REG_b"   \n\t"
+        "  cpuid               \n\t"
+        "  mov %%ebx, %%esi    \n\t"
+        "  pop %%"PA_REG_b"    \n\t"
+
+        : "=a" (*a), "=S" (*b), "=c" (*c), "=d" (*d)
+        : "0" (op)
+    );
+}
+#endif
+
+void pa_cpu_get_x86_flags(pa_cpu_x86_flag_t *flags) {
+#if defined (__i386__) || defined (__amd64__)
+    uint32_t eax, ebx, ecx, edx;
+    uint32_t level;
+
+    *flags = 0;
+
+    /* get standard level */
+    get_cpuid(0x00000000, &level, &ebx, &ecx, &edx);
+    if (level >= 1) {
+        get_cpuid(0x00000001, &eax, &ebx, &ecx, &edx);
+
+        if (edx & (1<<15))
+          *flags |= PA_CPU_X86_CMOV;
+
+        if (edx & (1<<23))
+          *flags |= PA_CPU_X86_MMX;
+
+        if (edx & (1<<25))
+          *flags |= PA_CPU_X86_SSE;
+
+        if (edx & (1<<26))
+          *flags |= PA_CPU_X86_SSE2;
+
+        if (ecx & (1<<0))
+          *flags |= PA_CPU_X86_SSE3;
+
+        if (ecx & (1<<9))
+          *flags |= PA_CPU_X86_SSSE3;
+
+        if (ecx & (1<<19))
+          *flags |= PA_CPU_X86_SSE4_1;
+
+        if (ecx & (1<<20))
+          *flags |= PA_CPU_X86_SSE4_2;
+    }
+
+    /* get extended level */
+    get_cpuid(0x80000000, &level, &ebx, &ecx, &edx);
+    if (level >= 0x80000001) {
+        get_cpuid(0x80000001, &eax, &ebx, &ecx, &edx);
+
+        if (edx & (1<<22))
+          *flags |= PA_CPU_X86_MMXEXT;
+
+        if (edx & (1<<23))
+          *flags |= PA_CPU_X86_MMX;
+
+        if (edx & (1<<30))
+          *flags |= PA_CPU_X86_3DNOWEXT;
+
+        if (edx & (1<<31))
+          *flags |= PA_CPU_X86_3DNOW;
+    }
+
+    pa_log_info("CPU flags: %s%s%s%s%s%s%s%s%s%s%s",
+    (*flags & PA_CPU_X86_CMOV) ? "CMOV " : "",
+    (*flags & PA_CPU_X86_MMX) ? "MMX " : "",
+    (*flags & PA_CPU_X86_SSE) ? "SSE " : "",
+    (*flags & PA_CPU_X86_SSE2) ? "SSE2 " : "",
+    (*flags & PA_CPU_X86_SSE3) ? "SSE3 " : "",
+    (*flags & PA_CPU_X86_SSSE3) ? "SSSE3 " : "",
+    (*flags & PA_CPU_X86_SSE4_1) ? "SSE4_1 " : "",
+    (*flags & PA_CPU_X86_SSE4_2) ? "SSE4_2 " : "",
+    (*flags & PA_CPU_X86_MMXEXT) ? "MMXEXT " : "",
+    (*flags & PA_CPU_X86_3DNOW) ? "3DNOW " : "",
+    (*flags & PA_CPU_X86_3DNOWEXT) ? "3DNOWEXT " : "");
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
+
+bool pa_cpu_init_x86(pa_cpu_x86_flag_t *flags) {
+#if defined (__i386__) || defined (__amd64__)
+    pa_cpu_get_x86_flags(flags);
+
+    /* activate various optimisations */
+    if (*flags & PA_CPU_X86_MMX) {
+        pa_volume_func_init_mmx(*flags);
+        pa_remap_func_init_mmx(*flags);
+    }
+
+    if (*flags & (PA_CPU_X86_SSE | PA_CPU_X86_SSE2)) {
+        pa_volume_func_init_sse(*flags);
+        pa_remap_func_init_sse(*flags);
+        pa_convert_func_init_sse(*flags);
+    }
+
+    return true;
+#else /* defined (__i386__) || defined (__amd64__) */
+    return false;
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/cpu-x86.h b/src/pulsecore/cpu-x86.h
new file mode 100644 (file)
index 0000000..30bbc80
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef foocpux86hfoo
+#define foocpux86hfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdint.h>
+#include <pulsecore/macro.h>
+
+typedef enum pa_cpu_x86_flag {
+    PA_CPU_X86_MMX       = (1 << 0),
+    PA_CPU_X86_MMXEXT    = (1 << 1),
+    PA_CPU_X86_SSE       = (1 << 2),
+    PA_CPU_X86_SSE2      = (1 << 3),
+    PA_CPU_X86_SSE3      = (1 << 4),
+    PA_CPU_X86_SSSE3     = (1 << 5),
+    PA_CPU_X86_SSE4_1    = (1 << 6),
+    PA_CPU_X86_SSE4_2    = (1 << 7),
+    PA_CPU_X86_3DNOW     = (1 << 8),
+    PA_CPU_X86_3DNOWEXT  = (1 << 9),
+    PA_CPU_X86_CMOV      = (1 << 10)
+} pa_cpu_x86_flag_t;
+
+void pa_cpu_get_x86_flags(pa_cpu_x86_flag_t *flags);
+bool pa_cpu_init_x86 (pa_cpu_x86_flag_t *flags);
+
+#if defined (__i386__)
+typedef int32_t pa_reg_x86;
+#define PA_REG_a "eax"
+#define PA_REG_b "ebx"
+#define PA_REG_c "ecx"
+#define PA_REG_d "edx"
+#define PA_REG_D "edi"
+#define PA_REG_S "esi"
+#elif defined (__amd64__)
+typedef int64_t pa_reg_x86;
+#define PA_REG_a "rax"
+#define PA_REG_b "rbx"
+#define PA_REG_c "rcx"
+#define PA_REG_d "rdx"
+#define PA_REG_D "rdi"
+#define PA_REG_S "rsi"
+#endif
+
+/* some optimized functions */
+void pa_volume_func_init_mmx(pa_cpu_x86_flag_t flags);
+void pa_volume_func_init_sse(pa_cpu_x86_flag_t flags);
+
+void pa_remap_func_init_mmx(pa_cpu_x86_flag_t flags);
+void pa_remap_func_init_sse(pa_cpu_x86_flag_t flags);
+
+void pa_convert_func_init_sse (pa_cpu_x86_flag_t flags);
+
+#endif /* foocpux86hfoo */
diff --git a/src/pulsecore/cpu.c b/src/pulsecore/cpu.c
new file mode 100644 (file)
index 0000000..e0c110e
--- /dev/null
@@ -0,0 +1,38 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "cpu.h"
+#include "cpu-orc.h"
+
+void pa_cpu_init(pa_cpu_info *cpu_info) {
+    cpu_info->cpu_type = PA_CPU_UNDEFINED;
+    /* don't force generic code, used for testing only */
+    cpu_info->force_generic_code = false;
+    if (!getenv("PULSE_NO_SIMD")) {
+        if (pa_cpu_init_x86(&cpu_info->flags.x86))
+            cpu_info->cpu_type = PA_CPU_X86;
+        else if (pa_cpu_init_arm(&cpu_info->flags.arm))
+            cpu_info->cpu_type = PA_CPU_ARM;
+        pa_cpu_init_orc(*cpu_info);
+    }
+
+    pa_remap_func_init(cpu_info);
+    pa_mix_func_init(cpu_info);
+}
diff --git a/src/pulsecore/cpu.h b/src/pulsecore/cpu.h
new file mode 100644 (file)
index 0000000..e65c4fb
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef foocpuhfoo
+#define foocpuhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2010 Arun Raghavan
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/cpu-x86.h>
+#include <pulsecore/cpu-arm.h>
+
+typedef enum {
+    PA_CPU_UNDEFINED = 0,
+    PA_CPU_X86,
+    PA_CPU_ARM,
+} pa_cpu_type_t;
+
+typedef struct pa_cpu_info pa_cpu_info;
+
+struct pa_cpu_info {
+    pa_cpu_type_t cpu_type;
+
+    union {
+        pa_cpu_x86_flag_t x86;
+        pa_cpu_arm_flag_t arm;
+    } flags;
+    bool force_generic_code;
+};
+
+void pa_cpu_init(pa_cpu_info *cpu_info);
+
+void pa_remap_func_init(const pa_cpu_info *cpu_info);
+void pa_mix_func_init(const pa_cpu_info *cpu_info);
+
+#endif /* foocpuhfoo */
diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h
new file mode 100644 (file)
index 0000000..9fdbb4f
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef foocredshfoo
+#define foocredshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#include <pulsecore/socket.h>
+#include <stdbool.h>
+
+#define MAX_ANCIL_DATA_FDS 2
+
+typedef struct pa_creds pa_creds;
+typedef struct pa_cmsg_ancil_data pa_cmsg_ancil_data;
+
+#if defined(SCM_CREDENTIALS)
+
+#define HAVE_CREDS 1
+
+struct pa_creds {
+    gid_t gid;
+    uid_t uid;
+};
+
+/* Struct for handling ancillary data, i e, extra data that can be sent together with a message
+ * over unix pipes. Supports sending and receiving credentials and file descriptors. */
+struct pa_cmsg_ancil_data {
+    pa_creds creds;
+    bool creds_valid;
+    int nfd;
+
+    /* Don't close these fds by your own. Check pa_cmsg_ancil_data_close_fds() */
+    int fds[MAX_ANCIL_DATA_FDS];
+    bool close_fds_on_cleanup;
+};
+
+void pa_cmsg_ancil_data_close_fds(struct pa_cmsg_ancil_data *ancil);
+
+#else
+#undef HAVE_CREDS
+#endif
+
+#endif
diff --git a/src/pulsecore/database-gdbm.c b/src/pulsecore/database-gdbm.c
new file mode 100644 (file)
index 0000000..b1da9df
--- /dev/null
@@ -0,0 +1,247 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <gdbm.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "database.h"
+
+#define MAKE_GDBM_FILE(x) ((GDBM_FILE) (x))
+
+static inline datum* datum_to_gdbm(datum *to, const pa_datum *from) {
+    pa_assert(from);
+    pa_assert(to);
+
+    to->dptr = from->data;
+    to->dsize = from->size;
+
+    return to;
+}
+
+static inline pa_datum* datum_from_gdbm(pa_datum *to, const datum *from) {
+    pa_assert(from);
+    pa_assert(to);
+
+    to->data = from->dptr;
+    to->size = from->dsize;
+
+    return to;
+}
+
+void pa_datum_free(pa_datum *d) {
+    pa_assert(d);
+
+    free(d->data); /* gdbm uses raw malloc/free hence we should do that here, too */
+    pa_zero(d);
+}
+
+pa_database* pa_database_open(const char *fn, bool for_write) {
+    GDBM_FILE f;
+    int gdbm_cache_size;
+    char *path;
+
+    pa_assert(fn);
+
+    /* We include the host identifier in the file name because gdbm
+     * files are CPU dependent, and we don't want things to go wrong
+     * if we are on a multiarch system. */
+    path = pa_sprintf_malloc("%s."CANONICAL_HOST".gdbm", fn);
+    errno = 0;
+
+    /* We need to set the block size explicitly here, since otherwise
+     * gdbm takes the native block size of the underlying file system
+     * which might be incredibly large. */
+    f = gdbm_open((char*) path, 1024, GDBM_NOLOCK | (for_write ? GDBM_WRCREAT : GDBM_READER), 0644, NULL);
+
+    if (f)
+        pa_log_debug("Opened GDBM database '%s'", path);
+
+    pa_xfree(path);
+
+    if (!f) {
+        if (errno == 0)
+            errno = EIO;
+        return NULL;
+    }
+
+    /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */
+    gdbm_cache_size = 10;
+    gdbm_setopt(f, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size));
+
+    return (pa_database*) f;
+}
+
+void pa_database_close(pa_database *db) {
+    pa_assert(db);
+
+    gdbm_close(MAKE_GDBM_FILE(db));
+}
+
+pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data) {
+    datum gdbm_key, gdbm_data;
+
+    pa_assert(db);
+    pa_assert(key);
+    pa_assert(data);
+
+    gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key));
+
+    return gdbm_data.dptr ?
+        datum_from_gdbm(data, &gdbm_data) :
+        NULL;
+}
+
+int pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, bool overwrite) {
+    datum gdbm_key, gdbm_data;
+
+    pa_assert(db);
+    pa_assert(key);
+    pa_assert(data);
+
+    return gdbm_store(MAKE_GDBM_FILE(db),
+                      *datum_to_gdbm(&gdbm_key, key),
+                      *datum_to_gdbm(&gdbm_data, data),
+                      overwrite ? GDBM_REPLACE : GDBM_INSERT) != 0 ? -1 : 0;
+}
+
+int pa_database_unset(pa_database *db, const pa_datum *key) {
+    datum gdbm_key;
+
+    pa_assert(db);
+    pa_assert(key);
+
+    return gdbm_delete(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key)) != 0 ? -1 : 0;
+}
+
+int pa_database_clear(pa_database *db) {
+    datum gdbm_key;
+
+    pa_assert(db);
+
+    gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db));
+
+    while (gdbm_key.dptr) {
+        datum next;
+
+        next = gdbm_nextkey(MAKE_GDBM_FILE(db), gdbm_key);
+
+        gdbm_delete(MAKE_GDBM_FILE(db), gdbm_key);
+
+        free(gdbm_key.dptr);
+        gdbm_key = next;
+    }
+
+    return gdbm_reorganize(MAKE_GDBM_FILE(db)) == 0 ? 0 : -1;
+}
+
+signed pa_database_size(pa_database *db) {
+    datum gdbm_key;
+    unsigned n = 0;
+
+    pa_assert(db);
+
+    /* This sucks */
+
+    gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db));
+
+    while (gdbm_key.dptr) {
+        datum next;
+
+        n++;
+
+        next = gdbm_nextkey(MAKE_GDBM_FILE(db), gdbm_key);
+        free(gdbm_key.dptr);
+        gdbm_key = next;
+    }
+
+    return (signed) n;
+}
+
+pa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data) {
+    datum gdbm_key, gdbm_data;
+
+    pa_assert(db);
+    pa_assert(key);
+
+    gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db));
+
+    if (!gdbm_key.dptr)
+        return NULL;
+
+    if (data) {
+        gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), gdbm_key);
+
+        if (!gdbm_data.dptr) {
+            free(gdbm_key.dptr);
+            return NULL;
+        }
+
+        datum_from_gdbm(data, &gdbm_data);
+    }
+
+    datum_from_gdbm(key, &gdbm_key);
+
+    return key;
+}
+
+pa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data) {
+    datum gdbm_key, gdbm_data;
+
+    pa_assert(db);
+    pa_assert(key);
+    pa_assert(next);
+
+    if (!key)
+        return pa_database_first(db, next, data);
+
+    gdbm_key = gdbm_nextkey(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key));
+
+    if (!gdbm_key.dptr)
+        return NULL;
+
+    if (data) {
+        gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), gdbm_key);
+
+        if (!gdbm_data.dptr) {
+            free(gdbm_key.dptr);
+            return NULL;
+        }
+
+        datum_from_gdbm(data, &gdbm_data);
+    }
+
+    datum_from_gdbm(next, &gdbm_key);
+
+    return next;
+}
+
+int pa_database_sync(pa_database *db) {
+    pa_assert(db);
+
+    gdbm_sync(MAKE_GDBM_FILE(db));
+    return 0;
+}
diff --git a/src/pulsecore/database-simple.c b/src/pulsecore/database-simple.c
new file mode 100644 (file)
index 0000000..3876487
--- /dev/null
@@ -0,0 +1,495 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Nokia Corporation
+  Contact: Maemo Multimedia <multimedia@maemo.org>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/hashmap.h>
+
+#include "database.h"
+
+typedef struct simple_data {
+    char *filename;
+    char *tmp_filename;
+    pa_hashmap *map;
+    bool read_only;
+} simple_data;
+
+typedef struct entry {
+    pa_datum key;
+    pa_datum data;
+} entry;
+
+void pa_datum_free(pa_datum *d) {
+    pa_assert(d);
+
+    pa_xfree(d->data);
+    d->data = NULL;
+    d->size = 0;
+}
+
+static int compare_func(const void *a, const void *b) {
+    const pa_datum *aa, *bb;
+
+    aa = (const pa_datum*)a;
+    bb = (const pa_datum*)b;
+
+    if (aa->size != bb->size)
+        return aa->size > bb->size ? 1 : -1;
+
+    return memcmp(aa->data, bb->data, aa->size);
+}
+
+/* pa_idxset_string_hash_func modified for our use */
+static unsigned hash_func(const void *p) {
+    const pa_datum *d;
+    unsigned hash = 0;
+    const char *c;
+    unsigned i;
+
+    d = (const pa_datum*)p;
+    c = d->data;
+
+    for (i = 0; i < d->size; i++) {
+        hash = 31 * hash + (unsigned) *c;
+        c++;
+    }
+
+    return hash;
+}
+
+static entry* new_entry(const pa_datum *key, const pa_datum *data) {
+    entry *e;
+
+    pa_assert(key);
+    pa_assert(data);
+
+    e = pa_xnew0(entry, 1);
+    e->key.data = key->size > 0 ? pa_xmemdup(key->data, key->size) : NULL;
+    e->key.size = key->size;
+    e->data.data = data->size > 0 ? pa_xmemdup(data->data, data->size) : NULL;
+    e->data.size = data->size;
+    return e;
+}
+
+static void free_entry(entry *e) {
+    if (e) {
+        if (e->key.data)
+            pa_xfree(e->key.data);
+        if (e->data.data)
+            pa_xfree(e->data.data);
+        pa_xfree(e);
+    }
+}
+
+static int read_uint(FILE *f, uint32_t *res) {
+    size_t items = 0;
+    uint8_t values[4];
+    uint32_t tmp;
+    int i;
+
+    items = fread(&values, sizeof(values), sizeof(uint8_t), f);
+
+    if (feof(f)) /* EOF */
+        return 0;
+
+    if (ferror(f))
+        return -1;
+
+    for (i = 0; i < 4; ++i) {
+        tmp = values[i];
+        *res += (tmp << (i*8));
+    }
+
+    return items;
+}
+
+static int read_data(FILE *f, void **data, ssize_t *length) {
+    size_t items = 0;
+    uint32_t data_len = 0;
+
+    pa_assert(f);
+
+    *data = NULL;
+    *length = 0;
+
+    if ((items = read_uint(f, &data_len)) <= 0)
+        return -1;
+
+    if (data_len > 0) {
+        *data = pa_xmalloc0(data_len);
+        items = fread(*data, data_len, 1, f);
+
+        if (feof(f)) /* EOF */
+            goto reset;
+
+        if (ferror(f))
+            goto reset;
+
+        *length = data_len;
+
+    } else { /* no data? */
+        return -1;
+    }
+
+    return 0;
+
+reset:
+    pa_xfree(*data);
+    *data = NULL;
+    *length = 0;
+    return -1;
+}
+
+static int fill_data(simple_data *db, FILE *f) {
+    pa_datum key;
+    pa_datum data;
+    void *d = NULL;
+    ssize_t l = 0;
+    bool append = false;
+    enum { FIELD_KEY = 0, FIELD_DATA } field = FIELD_KEY;
+
+    pa_assert(db);
+    pa_assert(db->map);
+
+    errno = 0;
+
+    key.size = 0;
+    key.data = NULL;
+
+    while (!read_data(f, &d, &l)) {
+
+        switch (field) {
+            case FIELD_KEY:
+                key.data = d;
+                key.size = l;
+                field = FIELD_DATA;
+                break;
+            case FIELD_DATA:
+                data.data = d;
+                data.size = l;
+                append = true;
+                break;
+        }
+
+        if (append) {
+            entry *e = pa_xnew0(entry, 1);
+            e->key.data = key.data;
+            e->key.size = key.size;
+            e->data.data = data.data;
+            e->data.size = data.size;
+            pa_hashmap_put(db->map, &e->key, e);
+            append = false;
+            field = FIELD_KEY;
+        }
+    }
+
+    if (ferror(f)) {
+        pa_log_warn("read error. %s", pa_cstrerror(errno));
+        pa_database_clear((pa_database*)db);
+    }
+
+    if (field == FIELD_DATA && d)
+        pa_xfree(d);
+
+    return pa_hashmap_size(db->map);
+}
+
+pa_database* pa_database_open(const char *fn, bool for_write) {
+    FILE *f;
+    char *path;
+    simple_data *db;
+
+    pa_assert(fn);
+
+    path = pa_sprintf_malloc("%s."CANONICAL_HOST".simple", fn);
+    errno = 0;
+
+    f = pa_fopen_cloexec(path, "r");
+
+    if (f || errno == ENOENT) { /* file not found is ok */
+        db = pa_xnew0(simple_data, 1);
+        db->map = pa_hashmap_new_full(hash_func, compare_func, NULL, (pa_free_cb_t) free_entry);
+        db->filename = pa_xstrdup(path);
+        db->tmp_filename = pa_sprintf_malloc(".%s.tmp", db->filename);
+        db->read_only = !for_write;
+
+        if (f) {
+            fill_data(db, f);
+            fclose(f);
+        }
+    } else {
+        if (errno == 0)
+            errno = EIO;
+        db = NULL;
+    }
+
+    pa_xfree(path);
+
+    return (pa_database*) db;
+}
+
+void pa_database_close(pa_database *database) {
+    simple_data *db = (simple_data*)database;
+    pa_assert(db);
+
+    pa_database_sync(database);
+    pa_xfree(db->filename);
+    pa_xfree(db->tmp_filename);
+    pa_hashmap_free(db->map);
+    pa_xfree(db);
+}
+
+pa_datum* pa_database_get(pa_database *database, const pa_datum *key, pa_datum* data) {
+    simple_data *db = (simple_data*)database;
+    entry *e;
+
+    pa_assert(db);
+    pa_assert(key);
+    pa_assert(data);
+
+    e = pa_hashmap_get(db->map, key);
+
+    if (!e)
+        return NULL;
+
+    data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
+    data->size = e->data.size;
+
+    return data;
+}
+
+int pa_database_set(pa_database *database, const pa_datum *key, const pa_datum* data, bool overwrite) {
+    simple_data *db = (simple_data*)database;
+    entry *e;
+    int ret = 0;
+
+    pa_assert(db);
+    pa_assert(key);
+    pa_assert(data);
+
+    if (db->read_only)
+        return -1;
+
+    e = new_entry(key, data);
+
+    if (pa_hashmap_put(db->map, &e->key, e) < 0) {
+        /* entry with same key exists in hashmap */
+        entry *r;
+        if (overwrite) {
+            r = pa_hashmap_remove(db->map, key);
+            pa_hashmap_put(db->map, &e->key, e);
+        } else {
+            /* won't overwrite, so clean new entry */
+            r = e;
+            ret = -1;
+        }
+
+        free_entry(r);
+    }
+
+    return ret;
+}
+
+int pa_database_unset(pa_database *database, const pa_datum *key) {
+    simple_data *db = (simple_data*)database;
+
+    pa_assert(db);
+    pa_assert(key);
+
+    return pa_hashmap_remove_and_free(db->map, key);
+}
+
+int pa_database_clear(pa_database *database) {
+    simple_data *db = (simple_data*)database;
+
+    pa_assert(db);
+
+    pa_hashmap_remove_all(db->map);
+
+    return 0;
+}
+
+signed pa_database_size(pa_database *database) {
+    simple_data *db = (simple_data*)database;
+    pa_assert(db);
+
+    return (signed) pa_hashmap_size(db->map);
+}
+
+pa_datum* pa_database_first(pa_database *database, pa_datum *key, pa_datum *data) {
+    simple_data *db = (simple_data*)database;
+    entry *e;
+
+    pa_assert(db);
+    pa_assert(key);
+
+    e = pa_hashmap_first(db->map);
+
+    if (!e)
+        return NULL;
+
+    key->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
+    key->size = e->key.size;
+
+    if (data) {
+        data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
+        data->size = e->data.size;
+    }
+
+    return key;
+}
+
+pa_datum* pa_database_next(pa_database *database, const pa_datum *key, pa_datum *next, pa_datum *data) {
+    simple_data *db = (simple_data*)database;
+    entry *e;
+    entry *search;
+    void *state;
+    bool pick_now;
+
+    pa_assert(db);
+    pa_assert(next);
+
+    if (!key)
+        return pa_database_first(database, next, data);
+
+    search = pa_hashmap_get(db->map, key);
+
+    state = NULL;
+    pick_now = false;
+
+    while ((e = pa_hashmap_iterate(db->map, &state, NULL))) {
+        if (pick_now)
+            break;
+
+        if (search == e)
+            pick_now = true;
+    }
+
+    if (!pick_now || !e)
+        return NULL;
+
+    next->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
+    next->size = e->key.size;
+
+    if (data) {
+        data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
+        data->size = e->data.size;
+    }
+
+    return next;
+}
+
+static int write_uint(FILE *f, const uint32_t num) {
+    size_t items;
+    uint8_t values[4];
+    int i;
+    errno = 0;
+
+    for (i = 0; i < 4; i++)
+        values[i] = (num >> (i*8)) & 0xFF;
+
+    items = fwrite(&values, sizeof(values), sizeof(uint8_t), f);
+
+    if (ferror(f))
+        return -1;
+
+    return items;
+}
+
+static int write_data(FILE *f, void *data, const size_t length) {
+    size_t items;
+    uint32_t len;
+
+    len = length;
+    if ((items = write_uint(f, len)) <= 0)
+        return -1;
+
+    items = fwrite(data, length, 1, f);
+
+    if (ferror(f) || items != 1)
+        return -1;
+
+    return 0;
+}
+
+static int write_entry(FILE *f, const entry *e) {
+    pa_assert(f);
+    pa_assert(e);
+
+    if (write_data(f, e->key.data, e->key.size) < 0)
+        return -1;
+    if (write_data(f, e->data.data, e->data.size) < 0)
+        return -1;
+
+    return 0;
+}
+
+int pa_database_sync(pa_database *database) {
+    simple_data *db = (simple_data*)database;
+    FILE *f;
+    void *state;
+    entry *e;
+
+    pa_assert(db);
+
+    if (db->read_only)
+        return 0;
+
+    errno = 0;
+
+    f = pa_fopen_cloexec(db->tmp_filename, "w");
+
+    if (!f)
+        goto fail;
+
+    state = NULL;
+    while((e = pa_hashmap_iterate(db->map, &state, NULL))) {
+        if (write_entry(f, e) < 0) {
+            pa_log_warn("error while writing to file. %s", pa_cstrerror(errno));
+            goto fail;
+        }
+    }
+
+    fclose(f);
+    f = NULL;
+
+    if (rename(db->tmp_filename, db->filename) < 0) {
+        pa_log_warn("error while renaming file. %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    if (f)
+        fclose(f);
+    return -1;
+}
diff --git a/src/pulsecore/database-tdb.c b/src/pulsecore/database-tdb.c
new file mode 100644 (file)
index 0000000..282f580
--- /dev/null
@@ -0,0 +1,250 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Some versions of tdb lack inclusion of signal.h in the header files but use sigatomic_t */
+#include <signal.h>
+#include <tdb.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "database.h"
+
+#define MAKE_TDB_CONTEXT(x) ((struct tdb_context*) (x))
+
+static inline TDB_DATA* datum_to_tdb(TDB_DATA *to, const pa_datum *from) {
+    pa_assert(from);
+    pa_assert(to);
+
+    to->dptr = from->data;
+    to->dsize = from->size;
+
+    return to;
+}
+
+static inline pa_datum* datum_from_tdb(pa_datum *to, const TDB_DATA *from) {
+    pa_assert(from);
+    pa_assert(to);
+
+    to->data = from->dptr;
+    to->size = from->dsize;
+
+    return to;
+}
+
+void pa_datum_free(pa_datum *d) {
+    pa_assert(d);
+
+    free(d->data); /* tdb uses raw malloc/free hence we should do that here, too */
+    pa_zero(d);
+}
+
+static struct tdb_context *tdb_open_cloexec(
+        const char *name,
+        int hash_size,
+        int tdb_flags,
+        int open_flags,
+        mode_t mode) {
+
+    /* Mimics pa_open_cloexec() */
+
+    struct tdb_context *c;
+
+#ifdef O_NOCTTY
+    open_flags |= O_NOCTTY;
+#endif
+
+#ifdef O_CLOEXEC
+    errno = 0;
+    if ((c = tdb_open(name, hash_size, tdb_flags, open_flags | O_CLOEXEC, mode)))
+        goto finish;
+
+    if (errno != EINVAL)
+        return NULL;
+#endif
+
+    errno = 0;
+    if (!(c = tdb_open(name, hash_size, tdb_flags, open_flags, mode)))
+        return NULL;
+
+finish:
+    pa_make_fd_cloexec(tdb_fd(c));
+    return c;
+}
+
+pa_database* pa_database_open(const char *fn, bool for_write) {
+    struct tdb_context *c;
+    char *path;
+
+    pa_assert(fn);
+
+    path = pa_sprintf_malloc("%s.tdb", fn);
+    if ((c = tdb_open_cloexec(path, 0, TDB_NOSYNC|TDB_NOLOCK, (for_write ? O_RDWR|O_CREAT : O_RDONLY), 0644)))
+        pa_log_debug("Opened TDB database '%s'", path);
+
+    pa_xfree(path);
+
+    if (!c) {
+        if (errno == 0)
+            errno = EIO;
+        return NULL;
+    }
+
+    return (pa_database*) c;
+}
+
+void pa_database_close(pa_database *db) {
+    pa_assert(db);
+
+    tdb_close(MAKE_TDB_CONTEXT(db));
+}
+
+pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data) {
+    TDB_DATA tdb_key, tdb_data;
+
+    pa_assert(db);
+    pa_assert(key);
+    pa_assert(data);
+
+    tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key));
+
+    return tdb_data.dptr ?
+        datum_from_tdb(data, &tdb_data) :
+        NULL;
+}
+
+int pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, bool overwrite) {
+    TDB_DATA tdb_key, tdb_data;
+
+    pa_assert(db);
+    pa_assert(key);
+    pa_assert(data);
+
+    return tdb_store(MAKE_TDB_CONTEXT(db),
+                      *datum_to_tdb(&tdb_key, key),
+                      *datum_to_tdb(&tdb_data, data),
+                     overwrite ? TDB_REPLACE : TDB_INSERT) != 0 ? -1 : 0;
+}
+
+int pa_database_unset(pa_database *db, const pa_datum *key) {
+    TDB_DATA tdb_key;
+
+    pa_assert(db);
+    pa_assert(key);
+
+    return tdb_delete(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key)) != 0 ? -1 : 0;
+}
+
+int pa_database_clear(pa_database *db) {
+    pa_assert(db);
+
+    return tdb_wipe_all(MAKE_TDB_CONTEXT(db)) != 0 ? -1 : 0;
+}
+
+signed pa_database_size(pa_database *db) {
+    TDB_DATA tdb_key;
+    unsigned n = 0;
+
+    pa_assert(db);
+
+    /* This sucks */
+
+    tdb_key = tdb_firstkey(MAKE_TDB_CONTEXT(db));
+
+    while (tdb_key.dptr) {
+        TDB_DATA next;
+
+        n++;
+
+        next = tdb_nextkey(MAKE_TDB_CONTEXT(db), tdb_key);
+        free(tdb_key.dptr);
+        tdb_key = next;
+    }
+
+    return (signed) n;
+}
+
+pa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data) {
+    TDB_DATA tdb_key, tdb_data;
+
+    pa_assert(db);
+    pa_assert(key);
+
+    tdb_key = tdb_firstkey(MAKE_TDB_CONTEXT(db));
+
+    if (!tdb_key.dptr)
+        return NULL;
+
+    if (data) {
+        tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), tdb_key);
+
+        if (!tdb_data.dptr) {
+            free(tdb_key.dptr);
+            return NULL;
+        }
+
+        datum_from_tdb(data, &tdb_data);
+    }
+
+    datum_from_tdb(key, &tdb_key);
+
+    return key;
+}
+
+pa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data) {
+    TDB_DATA tdb_key, tdb_data;
+
+    pa_assert(db);
+    pa_assert(key);
+
+    tdb_key = tdb_nextkey(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key));
+
+    if (!tdb_key.dptr)
+        return NULL;
+
+    if (data) {
+        tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), tdb_key);
+
+        if (!tdb_data.dptr) {
+            free(tdb_key.dptr);
+            return NULL;
+        }
+
+        datum_from_tdb(data, &tdb_data);
+    }
+
+    datum_from_tdb(next, &tdb_key);
+
+    return next;
+}
+
+int pa_database_sync(pa_database *db) {
+    pa_assert(db);
+
+    return 0;
+}
diff --git a/src/pulsecore/database.h b/src/pulsecore/database.h
new file mode 100644 (file)
index 0000000..3a1c7ce
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef foopulsecoredatabasehfoo
+#define foopulsecoredatabasehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/macro.h>
+
+/* A little abstraction over simple databases, such as gdbm, tdb, and
+ * so on. We only make minimal assumptions about the supported
+ * backend: it does not need to support locking, it does not have to
+ * be arch independent. */
+
+typedef struct pa_database pa_database;
+
+typedef struct pa_datum {
+    void *data;
+    size_t size;
+} pa_datum;
+
+void pa_datum_free(pa_datum *d);
+
+/* This will append a suffix to the filename */
+pa_database* pa_database_open(const char *fn, bool for_write);
+void pa_database_close(pa_database *db);
+
+pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data);
+
+int pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, bool overwrite);
+int pa_database_unset(pa_database *db, const pa_datum *key);
+
+int pa_database_clear(pa_database *db);
+
+signed pa_database_size(pa_database *db);
+
+pa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data /* may be NULL */);
+pa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data /* may be NULL */);
+
+int pa_database_sync(pa_database *db);
+
+#endif
diff --git a/src/pulsecore/dbus-shared.c b/src/pulsecore/dbus-shared.c
new file mode 100644 (file)
index 0000000..3422c29
--- /dev/null
@@ -0,0 +1,102 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006, 2009 Lennart Poettering
+  Copyright 2006 Shams E. King
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/shared.h>
+
+#include "dbus-shared.h"
+
+struct pa_dbus_connection {
+    PA_REFCNT_DECLARE;
+
+    pa_dbus_wrap_connection *connection;
+    pa_core *core;
+    const char *property_name;
+};
+
+static pa_dbus_connection* dbus_connection_new(pa_core *c, pa_dbus_wrap_connection *conn, const char *name) {
+    pa_dbus_connection *pconn;
+
+    pconn = pa_xnew(pa_dbus_connection, 1);
+    PA_REFCNT_INIT(pconn);
+    pconn->core = c;
+    pconn->property_name = name;
+    pconn->connection = conn;
+
+    pa_assert_se(pa_shared_set(c, name, pconn) >= 0);
+
+    return pconn;
+}
+
+pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) {
+
+    static const char *const prop_name[] = {
+        [DBUS_BUS_SESSION] = "dbus-connection-session",
+        [DBUS_BUS_SYSTEM] = "dbus-connection-system",
+        [DBUS_BUS_STARTER] = "dbus-connection-starter"
+    };
+    pa_dbus_wrap_connection *conn;
+    pa_dbus_connection *pconn;
+
+    pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
+
+    if ((pconn = pa_shared_get(c, prop_name[type])))
+        return pa_dbus_connection_ref(pconn);
+
+    if (!(conn = pa_dbus_wrap_connection_new(c->mainloop, true, type, error)))
+        return NULL;
+
+    return dbus_connection_new(c, conn, prop_name[type]);
+}
+
+DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) > 0);
+    pa_assert(c->connection);
+
+    return pa_dbus_wrap_connection_get(c->connection);
+}
+
+void pa_dbus_connection_unref(pa_dbus_connection *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) > 0);
+
+    if (PA_REFCNT_DEC(c) > 0)
+        return;
+
+    pa_dbus_wrap_connection_free(c->connection);
+
+    pa_assert_se(pa_shared_remove(c->core, c->property_name) >= 0);
+    pa_xfree(c);
+}
+
+pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) > 0);
+
+    PA_REFCNT_INC(c);
+
+    return c;
+}
diff --git a/src/pulsecore/dbus-shared.h b/src/pulsecore/dbus-shared.h
new file mode 100644 (file)
index 0000000..3d552e9
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef foodbussharedhfoo
+#define foodbussharedhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006, 2009 Lennart Poettering
+  Copyright 2006 Shams E. King
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/dbus-util.h>
+
+typedef struct pa_dbus_connection pa_dbus_connection;
+
+/* return a pa_dbus_connection of the specified type for the given core,
+ * like dbus_bus_get(), but integrates the connection with the pa_core */
+pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error);
+
+DBusConnection* pa_dbus_connection_get(pa_dbus_connection *conn);
+
+pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *conn);
+void pa_dbus_connection_unref(pa_dbus_connection *conn);
+
+#endif
diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
new file mode 100644 (file)
index 0000000..80e2866
--- /dev/null
@@ -0,0 +1,781 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Shams E. King
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdarg.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "dbus-util.h"
+
+struct pa_dbus_wrap_connection {
+    pa_mainloop_api *mainloop;
+    DBusConnection *connection;
+    pa_defer_event* dispatch_event;
+    bool use_rtclock:1;
+};
+
+struct timeout_data {
+    pa_dbus_wrap_connection *connection;
+    DBusTimeout *timeout;
+};
+
+static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
+    DBusConnection *conn = userdata;
+
+    if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE)
+        /* no more data to process, disable the deferred */
+        ea->defer_enable(ev, 0);
+}
+
+/* DBusDispatchStatusFunction callback for the pa mainloop */
+static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) {
+    pa_dbus_wrap_connection *c = userdata;
+
+    pa_assert(c);
+
+    switch(status) {
+
+        case DBUS_DISPATCH_COMPLETE:
+            c->mainloop->defer_enable(c->dispatch_event, 0);
+            break;
+
+        case DBUS_DISPATCH_DATA_REMAINS:
+        case DBUS_DISPATCH_NEED_MEMORY:
+        default:
+            c->mainloop->defer_enable(c->dispatch_event, 1);
+            break;
+    }
+}
+
+static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
+    unsigned int flags;
+    pa_io_event_flags_t events = 0;
+
+    pa_assert(watch);
+
+    flags = dbus_watch_get_flags(watch);
+
+    /* no watch flags for disabled watches */
+    if (!dbus_watch_get_enabled(watch))
+        return PA_IO_EVENT_NULL;
+
+    if (flags & DBUS_WATCH_READABLE)
+        events |= PA_IO_EVENT_INPUT;
+    if (flags & DBUS_WATCH_WRITABLE)
+        events |= PA_IO_EVENT_OUTPUT;
+
+    return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
+}
+
+/* pa_io_event_cb_t IO event handler */
+static void handle_io_event(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    unsigned int flags = 0;
+    DBusWatch *watch = userdata;
+
+#ifdef HAVE_DBUS_WATCH_GET_UNIX_FD
+    pa_assert(fd == dbus_watch_get_unix_fd(watch));
+#else
+    pa_assert(fd == dbus_watch_get_fd(watch));
+#endif
+
+    if (!dbus_watch_get_enabled(watch)) {
+        pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
+        return;
+    }
+
+    if (events & PA_IO_EVENT_INPUT)
+        flags |= DBUS_WATCH_READABLE;
+    if (events & PA_IO_EVENT_OUTPUT)
+        flags |= DBUS_WATCH_WRITABLE;
+    if (events & PA_IO_EVENT_HANGUP)
+        flags |= DBUS_WATCH_HANGUP;
+    if (events & PA_IO_EVENT_ERROR)
+        flags |= DBUS_WATCH_ERROR;
+
+    dbus_watch_handle(watch, flags);
+}
+
+/* pa_time_event_cb_t timer event handler */
+static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *t, void *userdata) {
+    struct timeval tv;
+    struct timeout_data *d = userdata;
+
+    pa_assert(d);
+    pa_assert(d->connection);
+
+    if (dbus_timeout_get_enabled(d->timeout)) {
+        /* Restart it for the next scheduled time. We do this before
+         * calling dbus_timeout_handle() to make sure that the time
+         * event is still around. */
+        ea->time_restart(e, pa_timeval_rtstore(&tv,
+                                               pa_timeval_load(t) + dbus_timeout_get_interval(d->timeout) * PA_USEC_PER_MSEC,
+                                               d->connection->use_rtclock));
+
+        dbus_timeout_handle(d->timeout);
+    }
+}
+
+/* DBusAddWatchFunction callback for pa mainloop */
+static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
+    pa_dbus_wrap_connection *c = data;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(c);
+
+    ev = c->mainloop->io_new(
+            c->mainloop,
+#ifdef HAVE_DBUS_WATCH_GET_UNIX_FD
+            dbus_watch_get_unix_fd(watch),
+#else
+            dbus_watch_get_fd(watch),
+#endif
+            get_watch_flags(watch), handle_io_event, watch);
+
+    dbus_watch_set_data(watch, ev, NULL);
+
+    return TRUE;
+}
+
+/* DBusRemoveWatchFunction callback for pa mainloop */
+static void remove_watch(DBusWatch *watch, void *data) {
+    pa_dbus_wrap_connection *c = data;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(c);
+
+    if ((ev = dbus_watch_get_data(watch)))
+        c->mainloop->io_free(ev);
+}
+
+/* DBusWatchToggledFunction callback for pa mainloop */
+static void toggle_watch(DBusWatch *watch, void *data) {
+    pa_dbus_wrap_connection *c = data;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(c);
+
+    pa_assert_se(ev = dbus_watch_get_data(watch));
+
+    /* get_watch_flags() checks if the watch is enabled */
+    c->mainloop->io_enable(ev, get_watch_flags(watch));
+}
+
+static void time_event_destroy_cb(pa_mainloop_api *a, pa_time_event *e, void *userdata) {
+    pa_xfree(userdata);
+}
+
+/* DBusAddTimeoutFunction callback for pa mainloop */
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
+    pa_dbus_wrap_connection *c = data;
+    pa_time_event *ev;
+    struct timeval tv;
+    struct timeout_data *d;
+
+    pa_assert(timeout);
+    pa_assert(c);
+
+    if (!dbus_timeout_get_enabled(timeout))
+        return FALSE;
+
+    d = pa_xnew(struct timeout_data, 1);
+    d->connection = c;
+    d->timeout = timeout;
+    ev = c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, pa_rtclock_now() + dbus_timeout_get_interval(timeout) * PA_USEC_PER_MSEC, c->use_rtclock), handle_time_event, d);
+    c->mainloop->time_set_destroy(ev, time_event_destroy_cb);
+
+    dbus_timeout_set_data(timeout, ev, NULL);
+
+    return TRUE;
+}
+
+/* DBusRemoveTimeoutFunction callback for pa mainloop */
+static void remove_timeout(DBusTimeout *timeout, void *data) {
+    pa_dbus_wrap_connection *c = data;
+    pa_time_event *ev;
+
+    pa_assert(timeout);
+    pa_assert(c);
+
+    if ((ev = dbus_timeout_get_data(timeout)))
+        c->mainloop->time_free(ev);
+}
+
+/* DBusTimeoutToggledFunction callback for pa mainloop */
+static void toggle_timeout(DBusTimeout *timeout, void *data) {
+    struct timeout_data *d = data;
+    pa_time_event *ev;
+    struct timeval tv;
+
+    pa_assert(d);
+    pa_assert(d->connection);
+    pa_assert(timeout);
+
+    pa_assert_se(ev = dbus_timeout_get_data(timeout));
+
+    if (dbus_timeout_get_enabled(timeout))
+        d->connection->mainloop->time_restart(ev, pa_timeval_rtstore(&tv, pa_rtclock_now() + dbus_timeout_get_interval(timeout) * PA_USEC_PER_MSEC, d->connection->use_rtclock));
+    else
+        d->connection->mainloop->time_restart(ev, pa_timeval_rtstore(&tv, PA_USEC_INVALID, d->connection->use_rtclock));
+}
+
+static void wakeup_main(void *userdata) {
+    pa_dbus_wrap_connection *c = userdata;
+
+    pa_assert(c);
+
+    /* this will wakeup the mainloop and dispatch events, although
+     * it may not be the cleanest way of accomplishing it */
+    c->mainloop->defer_enable(c->dispatch_event, 1);
+}
+
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, bool use_rtclock, DBusBusType type, DBusError *error) {
+    DBusConnection *conn;
+    pa_dbus_wrap_connection *pconn;
+    char *id;
+
+    pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
+
+    if (!(conn = dbus_bus_get_private(type, error)))
+        return NULL;
+
+    pconn = pa_xnew(pa_dbus_wrap_connection, 1);
+    pconn->mainloop = m;
+    pconn->connection = conn;
+    pconn->use_rtclock = use_rtclock;
+
+    dbus_connection_set_exit_on_disconnect(conn, FALSE);
+    dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
+    dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
+    dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
+    dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
+
+    pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
+
+    pa_log_debug("Successfully connected to D-Bus %s bus %s as %s",
+                 type == DBUS_BUS_SYSTEM ? "system" : (type == DBUS_BUS_SESSION ? "session" : "starter"),
+                 pa_strnull((id = dbus_connection_get_server_id(conn))),
+                 pa_strnull(dbus_bus_get_unique_name(conn)));
+
+    dbus_free(id);
+
+    return pconn;
+}
+
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(
+        pa_mainloop_api *m,
+        bool use_rtclock,
+        DBusConnection *conn) {
+    pa_dbus_wrap_connection *pconn;
+
+    pa_assert(m);
+    pa_assert(conn);
+
+    pconn = pa_xnew(pa_dbus_wrap_connection, 1);
+    pconn->mainloop = m;
+    pconn->connection = dbus_connection_ref(conn);
+    pconn->use_rtclock = use_rtclock;
+
+    dbus_connection_set_exit_on_disconnect(conn, FALSE);
+    dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
+    dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
+    dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
+    dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
+
+    pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
+
+    return pconn;
+}
+
+void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
+    pa_assert(c);
+
+    if (dbus_connection_get_is_connected(c->connection)) {
+        dbus_connection_close(c->connection);
+        /* must process remaining messages, bit of a kludge to handle
+         * both unload and shutdown */
+        while (dbus_connection_read_write_dispatch(c->connection, -1))
+            ;
+    }
+
+    c->mainloop->defer_free(c->dispatch_event);
+    dbus_connection_unref(c->connection);
+    pa_xfree(c);
+}
+
+DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *c) {
+  pa_assert(c);
+  pa_assert(c->connection);
+
+  return c->connection;
+}
+
+int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) {
+    const char *t;
+    va_list ap;
+    unsigned k = 0;
+
+    pa_assert(c);
+    pa_assert(error);
+
+    va_start(ap, error);
+    while ((t = va_arg(ap, const char*))) {
+        dbus_bus_add_match(c, t, error);
+
+        if (dbus_error_is_set(error))
+            goto fail;
+
+        k++;
+    }
+    va_end(ap);
+    return 0;
+
+fail:
+
+    va_end(ap);
+    va_start(ap, error);
+    for (; k > 0; k--) {
+        pa_assert_se(t = va_arg(ap, const char*));
+        dbus_bus_remove_match(c, t, NULL);
+    }
+    va_end(ap);
+
+    return -1;
+}
+
+void pa_dbus_remove_matches(DBusConnection *c, ...) {
+    const char *t;
+    va_list ap;
+
+    pa_assert(c);
+
+    va_start(ap, c);
+    while ((t = va_arg(ap, const char*)))
+        dbus_bus_remove_match(c, t, NULL);
+    va_end(ap);
+}
+
+pa_dbus_pending *pa_dbus_pending_new(
+        DBusConnection *c,
+        DBusMessage *m,
+        DBusPendingCall *pending,
+        void *context_data,
+        void *call_data) {
+
+    pa_dbus_pending *p;
+
+    pa_assert(pending);
+
+    p = pa_xnew(pa_dbus_pending, 1);
+    p->connection = c;
+    p->message = m;
+    p->pending = pending;
+    p->context_data = context_data;
+    p->call_data = call_data;
+
+    PA_LLIST_INIT(pa_dbus_pending, p);
+
+    return p;
+}
+
+void pa_dbus_pending_free(pa_dbus_pending *p) {
+    pa_assert(p);
+
+    if (p->pending) {
+        dbus_pending_call_cancel(p->pending);
+        dbus_pending_call_unref(p->pending);
+    }
+
+    if (p->message)
+        dbus_message_unref(p->message);
+
+    pa_xfree(p);
+}
+
+void pa_dbus_sync_pending_list(pa_dbus_pending **p) {
+    pa_assert(p);
+
+    while (*p && dbus_connection_read_write_dispatch((*p)->connection, -1))
+        ;
+}
+
+void pa_dbus_free_pending_list(pa_dbus_pending **p) {
+    pa_dbus_pending *i;
+
+    pa_assert(p);
+
+    while ((i = *p)) {
+        PA_LLIST_REMOVE(pa_dbus_pending, *p, i);
+        pa_dbus_pending_free(i);
+    }
+}
+
+const char *pa_dbus_get_error_message(DBusMessage *m) {
+    const char *message;
+
+    pa_assert(m);
+    pa_assert(dbus_message_get_type(m) == DBUS_MESSAGE_TYPE_ERROR);
+
+    if (dbus_message_get_signature(m)[0] != 's')
+        return "<no explanation>";
+
+    pa_assert_se(dbus_message_get_args(m, NULL, DBUS_TYPE_STRING, &message, DBUS_TYPE_INVALID));
+
+    return message;
+}
+
+void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) {
+    va_list ap;
+    char *message;
+    DBusMessage *reply = NULL;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+    pa_assert(name);
+    pa_assert(format);
+
+    va_start(ap, format);
+    message = pa_vsprintf_malloc(format, ap);
+    va_end(ap);
+    pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message)));
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(message);
+}
+
+void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) {
+    DBusMessage *reply = NULL;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+
+    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
+    DBusMessage *reply = NULL;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+    pa_assert(dbus_type_is_basic(type));
+    pa_assert(data);
+
+    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+    pa_assert_se(dbus_message_append_args(reply, type, data, DBUS_TYPE_INVALID));
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+static const char *signature_from_basic_type(int type) {
+    switch (type) {
+        case DBUS_TYPE_BOOLEAN: return DBUS_TYPE_BOOLEAN_AS_STRING;
+        case DBUS_TYPE_BYTE: return DBUS_TYPE_BYTE_AS_STRING;
+        case DBUS_TYPE_INT16: return DBUS_TYPE_INT16_AS_STRING;
+        case DBUS_TYPE_UINT16: return DBUS_TYPE_UINT16_AS_STRING;
+        case DBUS_TYPE_INT32: return DBUS_TYPE_INT32_AS_STRING;
+        case DBUS_TYPE_UINT32: return DBUS_TYPE_UINT32_AS_STRING;
+        case DBUS_TYPE_INT64: return DBUS_TYPE_INT64_AS_STRING;
+        case DBUS_TYPE_UINT64: return DBUS_TYPE_UINT64_AS_STRING;
+        case DBUS_TYPE_DOUBLE: return DBUS_TYPE_DOUBLE_AS_STRING;
+        case DBUS_TYPE_STRING: return DBUS_TYPE_STRING_AS_STRING;
+        case DBUS_TYPE_OBJECT_PATH: return DBUS_TYPE_OBJECT_PATH_AS_STRING;
+        case DBUS_TYPE_SIGNATURE: return DBUS_TYPE_SIGNATURE_AS_STRING;
+        default: pa_assert_not_reached();
+    }
+}
+
+void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter variant_iter;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+    pa_assert(dbus_type_is_basic(type));
+    pa_assert(data);
+
+    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter,
+                                                  DBUS_TYPE_VARIANT,
+                                                  signature_from_basic_type(type),
+                                                  &variant_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter));
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+/* Note: returns sizeof(char*) for strings, object paths and signatures. */
+static unsigned basic_type_size(int type) {
+    switch (type) {
+        case DBUS_TYPE_BOOLEAN: return sizeof(dbus_bool_t);
+        case DBUS_TYPE_BYTE: return 1;
+        case DBUS_TYPE_INT16: return sizeof(dbus_int16_t);
+        case DBUS_TYPE_UINT16: return sizeof(dbus_uint16_t);
+        case DBUS_TYPE_INT32: return sizeof(dbus_int32_t);
+        case DBUS_TYPE_UINT32: return sizeof(dbus_uint32_t);
+        case DBUS_TYPE_INT64: return sizeof(dbus_int64_t);
+        case DBUS_TYPE_UINT64: return sizeof(dbus_uint64_t);
+        case DBUS_TYPE_DOUBLE: return sizeof(double);
+        case DBUS_TYPE_STRING:
+        case DBUS_TYPE_OBJECT_PATH:
+        case DBUS_TYPE_SIGNATURE: return sizeof(char*);
+        default: pa_assert_not_reached();
+    }
+}
+
+void pa_dbus_send_basic_array_variant_reply(
+        DBusConnection *c,
+        DBusMessage *in_reply_to,
+        int item_type,
+        void *array,
+        unsigned n) {
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+    pa_assert(dbus_type_is_basic(item_type));
+    pa_assert(array || n == 0);
+
+    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_dbus_append_basic_array_variant(&msg_iter, item_type, array, n);
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist) {
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+    pa_assert(proplist);
+
+    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_dbus_append_proplist_variant(&msg_iter, proplist);
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
+    DBusMessageIter array_iter;
+    unsigned i;
+    unsigned item_size;
+
+    pa_assert(iter);
+    pa_assert(dbus_type_is_basic(item_type));
+    pa_assert(array || n == 0);
+
+    item_size = basic_type_size(item_type);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, signature_from_basic_type(item_type), &array_iter));
+
+    for (i = 0; i < n; ++i)
+        pa_assert_se(dbus_message_iter_append_basic(&array_iter, item_type, &((uint8_t*) array)[i * item_size]));
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &array_iter));
+}
+
+void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data) {
+    DBusMessageIter variant_iter;
+
+    pa_assert(iter);
+    pa_assert(dbus_type_is_basic(type));
+    pa_assert(data);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
+    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+}
+
+void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
+    DBusMessageIter variant_iter;
+    char *array_signature;
+
+    pa_assert(iter);
+    pa_assert(dbus_type_is_basic(item_type));
+    pa_assert(array || n == 0);
+
+    array_signature = pa_sprintf_malloc("a%c", *signature_from_basic_type(item_type));
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, array_signature, &variant_iter));
+    pa_dbus_append_basic_array(&variant_iter, item_type, array, n);
+    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+
+    pa_xfree(array_signature);
+}
+
+void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data) {
+    DBusMessageIter dict_entry_iter;
+
+    pa_assert(dict_iter);
+    pa_assert(key);
+    pa_assert(dbus_type_is_basic(type));
+    pa_assert(data);
+
+    pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+    pa_dbus_append_basic_variant(&dict_entry_iter, type, data);
+    pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
+}
+
+void pa_dbus_append_basic_array_variant_dict_entry(
+        DBusMessageIter *dict_iter,
+        const char *key,
+        int item_type,
+        const void *array,
+        unsigned n) {
+    DBusMessageIter dict_entry_iter;
+
+    pa_assert(dict_iter);
+    pa_assert(key);
+    pa_assert(dbus_type_is_basic(item_type));
+    pa_assert(array || n == 0);
+
+    pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+    pa_dbus_append_basic_array_variant(&dict_entry_iter, item_type, array, n);
+    pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
+}
+
+void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist) {
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    DBusMessageIter array_iter;
+    void *state = NULL;
+    const char *key;
+
+    pa_assert(iter);
+    pa_assert(proplist);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{say}", &dict_iter));
+
+    while ((key = pa_proplist_iterate(proplist, &state))) {
+        const void *value = NULL;
+        size_t nbytes;
+
+        pa_assert_se(pa_proplist_get(proplist, key, &value, &nbytes) >= 0);
+
+        pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+
+        pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+
+        pa_assert_se(dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_ARRAY, "y", &array_iter));
+        pa_assert_se(dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, &value, nbytes));
+        pa_assert_se(dbus_message_iter_close_container(&dict_entry_iter, &array_iter));
+
+        pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+    }
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &dict_iter));
+}
+
+void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist) {
+    DBusMessageIter variant_iter;
+
+    pa_assert(iter);
+    pa_assert(proplist);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{say}", &variant_iter));
+    pa_dbus_append_proplist(&variant_iter, proplist);
+    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+}
+
+void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist) {
+    DBusMessageIter dict_entry_iter;
+
+    pa_assert(dict_iter);
+    pa_assert(key);
+    pa_assert(proplist);
+
+    pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+    pa_dbus_append_proplist_variant(&dict_entry_iter, proplist);
+    pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
+}
+
+pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) {
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    pa_proplist *proplist = NULL;
+    const char *key = NULL;
+    const uint8_t *value = NULL;
+    int value_length = 0;
+
+    pa_assert(c);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(pa_streq(dbus_message_iter_get_signature(iter), "a{say}"));
+
+    proplist = pa_proplist_new();
+
+    dbus_message_iter_recurse(iter, &dict_iter);
+
+    while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
+        dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
+
+        dbus_message_iter_get_basic(&dict_entry_iter, &key);
+        dbus_message_iter_next(&dict_entry_iter);
+
+        if (strlen(key) <= 0 || !pa_ascii_valid(key)) {
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key: '%s'.", key);
+            goto fail;
+        }
+
+        dbus_message_iter_get_fixed_array(&dict_entry_iter, &value, &value_length);
+
+        pa_assert(value_length >= 0);
+
+        pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0);
+
+        dbus_message_iter_next(&dict_iter);
+    }
+
+    dbus_message_iter_next(iter);
+
+    return proplist;
+
+fail:
+    if (proplist)
+        pa_proplist_free(proplist);
+
+    return NULL;
+}
diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h
new file mode 100644 (file)
index 0000000..cc3abda
--- /dev/null
@@ -0,0 +1,114 @@
+#ifndef foodbusutilhfoo
+#define foodbusutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Shams E. King
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include <pulse/gccmacro.h>
+#include <pulse/mainloop-api.h>
+#include <pulse/proplist.h>
+
+#include <pulsecore/llist.h>
+
+/* A wrap connection is not shared or refcounted, it is available in client side */
+typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
+
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, bool use_rtclock, DBusBusType type, DBusError *error);
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(
+        pa_mainloop_api *mainloop,
+        bool use_rtclock,
+        DBusConnection *conn);
+void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn);
+
+DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);
+
+int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) PA_GCC_SENTINEL;
+void pa_dbus_remove_matches(DBusConnection *c,  ...) PA_GCC_SENTINEL;
+
+typedef struct pa_dbus_pending pa_dbus_pending;
+
+struct pa_dbus_pending {
+    DBusConnection *connection;
+    DBusMessage *message;
+    DBusPendingCall *pending;
+
+    void *context_data;
+    void *call_data;
+
+    PA_LLIST_FIELDS(pa_dbus_pending);
+};
+
+pa_dbus_pending *pa_dbus_pending_new(DBusConnection *c, DBusMessage *m, DBusPendingCall *pending, void *context_data, void *call_data);
+void pa_dbus_pending_free(pa_dbus_pending *p);
+
+/* Sync up a list of pa_dbus_pending_call objects */
+void pa_dbus_sync_pending_list(pa_dbus_pending **p);
+
+/* Free up a list of pa_dbus_pending_call objects */
+void pa_dbus_free_pending_list(pa_dbus_pending **p);
+
+/* When receiving a DBusMessage with type DBUS_MESSAGE_TYPE_ERROR, the
+ * DBusMessage may or may not contain an error message (a human-readable
+ * explanation of what went wrong). Extracting the error message from the
+ * DBusMessage object is a bit tedious, so here's a helper function that does
+ * that. If the DBusMessage doesn't contain any error message,
+ * "<no explanation>" is returned. */
+const char *pa_dbus_get_error_message(DBusMessage *m);
+
+/* Sends an error message as the reply to the given message. */
+void pa_dbus_send_error(
+        DBusConnection *c,
+        DBusMessage *in_reply_to,
+        const char *name,
+        const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5);
+
+void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to);
+void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
+void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
+void pa_dbus_send_basic_array_variant_reply(
+        DBusConnection *c,
+        DBusMessage *in_reply_to,
+        int item_type,
+        void *array,
+        unsigned n);
+void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist);
+
+void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
+void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
+void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data);
+void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data);
+void pa_dbus_append_basic_array_variant_dict_entry(
+        DBusMessageIter *dict_iter,
+        const char *key,
+        int item_type,
+        const void *array,
+        unsigned n);
+void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist);
+void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist);
+void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist);
+
+/* Returns a new proplist that the caller has to free. If the proplist contains
+ * invalid keys, an error reply is sent and NULL is returned. The iterator must
+ * point to "a{say}" element. This function calls dbus_message_iter_next(iter)
+ * before returning. */
+pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter);
+
+#endif
diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c
new file mode 100644 (file)
index 0000000..76a7e80
--- /dev/null
@@ -0,0 +1,227 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+  Copyright 2011 David Henningsson, Canonical Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "device-port.h"
+#include <pulsecore/card.h>
+#include <pulsecore/core-util.h>
+
+PA_DEFINE_PUBLIC_CLASS(pa_device_port, pa_object);
+
+pa_device_port_new_data *pa_device_port_new_data_init(pa_device_port_new_data *data) {
+    pa_assert(data);
+
+    pa_zero(*data);
+    data->available = PA_AVAILABLE_UNKNOWN;
+    return data;
+}
+
+void pa_device_port_new_data_set_name(pa_device_port_new_data *data, const char *name) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    data->name = pa_xstrdup(name);
+}
+
+void pa_device_port_new_data_set_description(pa_device_port_new_data *data, const char *description) {
+    pa_assert(data);
+
+    pa_xfree(data->description);
+    data->description = pa_xstrdup(description);
+}
+
+void pa_device_port_new_data_set_available(pa_device_port_new_data *data, pa_available_t available) {
+    pa_assert(data);
+
+    data->available = available;
+}
+
+void pa_device_port_new_data_set_direction(pa_device_port_new_data *data, pa_direction_t direction) {
+    pa_assert(data);
+
+    data->direction = direction;
+}
+
+void pa_device_port_new_data_done(pa_device_port_new_data *data) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    pa_xfree(data->description);
+}
+
+void pa_device_port_set_preferred_profile(pa_device_port *p, const char *new_pp) {
+    pa_assert(p);
+
+    if (!pa_safe_streq(p->preferred_profile, new_pp)) {
+        pa_xfree(p->preferred_profile);
+        p->preferred_profile = pa_xstrdup(new_pp);
+    }
+}
+
+void pa_device_port_set_available(pa_device_port *p, pa_available_t status) {
+    pa_assert(p);
+
+    if (p->available == status)
+        return;
+
+/*    pa_assert(status != PA_AVAILABLE_UNKNOWN); */
+
+    p->available = status;
+    pa_log_debug("Setting port %s to status %s", p->name, status == PA_AVAILABLE_YES ? "yes" :
+       status == PA_AVAILABLE_NO ? "no" : "unknown");
+
+    /* Post subscriptions to the card which owns us */
+    /* XXX: We need to check p->card, because this function may be called
+     * before the card object has been created. The card object should probably
+     * be created before port objects, and then p->card could be non-NULL for
+     * the whole lifecycle of pa_device_port. */
+    if (p->card) {
+        /* A sink or source whose active port is unavailable can't be the
+         * default sink/source, so port availability changes may affect the
+         * default sink/source choice. */
+        if (p->direction == PA_DIRECTION_OUTPUT)
+            pa_core_update_default_sink(p->core);
+        else
+            pa_core_update_default_source(p->core);
+
+        pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index);
+        pa_hook_fire(&p->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p);
+    }
+}
+
+static void device_port_free(pa_object *o) {
+    pa_device_port *p = PA_DEVICE_PORT(o);
+
+    pa_assert(p);
+    pa_assert(pa_device_port_refcnt(p) == 0);
+
+    if (p->impl_free)
+        p->impl_free(p);
+
+    if (p->proplist)
+        pa_proplist_free(p->proplist);
+
+    if (p->profiles)
+        pa_hashmap_free(p->profiles);
+
+    pa_xfree(p->preferred_profile);
+    pa_xfree(p->name);
+    pa_xfree(p->description);
+    pa_xfree(p);
+}
+
+pa_device_port *pa_device_port_new(pa_core *c, pa_device_port_new_data *data, size_t extra) {
+    pa_device_port *p;
+
+    pa_assert(data);
+    pa_assert(data->name);
+    pa_assert(data->description);
+    pa_assert(data->direction == PA_DIRECTION_OUTPUT || data->direction == PA_DIRECTION_INPUT);
+
+    p = PA_DEVICE_PORT(pa_object_new_internal(PA_ALIGN(sizeof(pa_device_port)) + extra, pa_device_port_type_id, pa_device_port_check_type));
+    p->parent.free = device_port_free;
+
+    p->name = data->name;
+    data->name = NULL;
+    p->description = data->description;
+    data->description = NULL;
+    p->core = c;
+    p->card = NULL;
+    p->priority = 0;
+    p->available = data->available;
+    p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    p->direction = data->direction;
+
+    p->latency_offset = 0;
+    p->proplist = pa_proplist_new();
+
+    return p;
+}
+
+void pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset) {
+    uint32_t state;
+    pa_core *core;
+
+    pa_assert(p);
+
+    if (offset == p->latency_offset)
+        return;
+
+    p->latency_offset = offset;
+
+    switch (p->direction) {
+        case PA_DIRECTION_OUTPUT: {
+            pa_sink *sink;
+
+            PA_IDXSET_FOREACH(sink, p->core->sinks, state) {
+                if (sink->active_port == p) {
+                    pa_sink_set_port_latency_offset(sink, p->latency_offset);
+                    break;
+                }
+            }
+
+            break;
+        }
+
+        case PA_DIRECTION_INPUT: {
+            pa_source *source;
+
+            PA_IDXSET_FOREACH(source, p->core->sources, state) {
+                if (source->active_port == p) {
+                    pa_source_set_port_latency_offset(source, p->latency_offset);
+                    break;
+                }
+            }
+
+            break;
+        }
+    }
+
+    pa_assert_se(core = p->core);
+    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index);
+    pa_hook_fire(&core->hooks[PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED], p);
+}
+
+pa_device_port *pa_device_port_find_best(pa_hashmap *ports)
+{
+    void *state;
+    pa_device_port *p, *best = NULL;
+
+    if (!ports)
+        return NULL;
+
+    /* First run: skip unavailable ports */
+    PA_HASHMAP_FOREACH(p, ports, state) {
+        if (p->available == PA_AVAILABLE_NO)
+            continue;
+
+        if (!best || p->priority > best->priority)
+            best = p;
+    }
+
+    /* Second run: if only unavailable ports exist, still suggest a port */
+    if (!best) {
+        PA_HASHMAP_FOREACH(p, ports, state)
+            if (!best || p->priority > best->priority)
+                best = p;
+    }
+
+    return best;
+}
diff --git a/src/pulsecore/device-port.h b/src/pulsecore/device-port.h
new file mode 100644 (file)
index 0000000..fbdce1a
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef foopulsedeviceporthfoo
+#define foopulsedeviceporthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+  Copyright 2011 David Henningsson, Canonical Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <inttypes.h>
+
+#include <pulsecore/typedefs.h>
+#include <pulse/def.h>
+#include <pulsecore/object.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/core.h>
+#include <pulsecore/card.h>
+
+struct pa_device_port {
+    pa_object parent; /* Needed for reference counting */
+    pa_core *core;
+    pa_card *card;
+
+    char *name;
+    char *description;
+    char *preferred_profile;
+
+    unsigned priority;
+    pa_available_t available;         /* PA_AVAILABLE_UNKNOWN, PA_AVAILABLE_NO or PA_AVAILABLE_YES */
+
+    pa_proplist *proplist;
+    pa_hashmap *profiles; /* Does not own the profiles */
+    pa_direction_t direction;
+    int64_t latency_offset;
+
+    /* Free the extra implementation specific data. Called before other members are freed. */
+    void (*impl_free)(pa_device_port *port);
+
+    /* .. followed by some implementation specific data */
+};
+
+PA_DECLARE_PUBLIC_CLASS(pa_device_port);
+#define PA_DEVICE_PORT(s) (pa_device_port_cast(s))
+
+#define PA_DEVICE_PORT_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_device_port))))
+
+typedef struct pa_device_port_new_data {
+    char *name;
+    char *description;
+    pa_available_t available;
+    pa_direction_t direction;
+} pa_device_port_new_data;
+
+pa_device_port_new_data *pa_device_port_new_data_init(pa_device_port_new_data *data);
+void pa_device_port_new_data_set_name(pa_device_port_new_data *data, const char *name);
+void pa_device_port_new_data_set_description(pa_device_port_new_data *data, const char *description);
+void pa_device_port_new_data_set_available(pa_device_port_new_data *data, pa_available_t available);
+void pa_device_port_new_data_set_direction(pa_device_port_new_data *data, pa_direction_t direction);
+void pa_device_port_new_data_done(pa_device_port_new_data *data);
+
+pa_device_port *pa_device_port_new(pa_core *c, pa_device_port_new_data *data, size_t extra);
+
+/* The port's available status has changed */
+void pa_device_port_set_available(pa_device_port *p, pa_available_t available);
+
+void pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset);
+void pa_device_port_set_preferred_profile(pa_device_port *p, const char *new_pp);
+
+pa_device_port *pa_device_port_find_best(pa_hashmap *ports);
+
+#endif
diff --git a/src/pulsecore/dllmain.c b/src/pulsecore/dllmain.c
new file mode 100644 (file)
index 0000000..e594ae5
--- /dev/null
@@ -0,0 +1,53 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef OS_IS_WIN32
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <windows.h>
+#include <winsock2.h>
+
+extern char *pa_win32_get_toplevel(HANDLE handle);
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
+    WSADATA data;
+
+    switch (fdwReason) {
+
+    case DLL_PROCESS_ATTACH:
+        if (!pa_win32_get_toplevel(hinstDLL))
+            return FALSE;
+        WSAStartup(MAKEWORD(2, 0), &data);
+        break;
+
+    case DLL_PROCESS_DETACH:
+        WSACleanup();
+        break;
+
+    }
+    return TRUE;
+}
+
+#endif /* OS_IS_WIN32 */
diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c
new file mode 100644 (file)
index 0000000..6a3eb5f
--- /dev/null
@@ -0,0 +1,142 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "dynarray.h"
+
+struct pa_dynarray {
+    void **data;
+    unsigned n_allocated, n_entries;
+    pa_free_cb_t free_cb;
+};
+
+pa_dynarray* pa_dynarray_new(pa_free_cb_t free_cb) {
+    pa_dynarray *array;
+
+    array = pa_xnew0(pa_dynarray, 1);
+    array->free_cb = free_cb;
+
+    return array;
+}
+
+void pa_dynarray_free(pa_dynarray *array) {
+    unsigned i;
+    pa_assert(array);
+
+    if (array->free_cb)
+        for (i = 0; i < array->n_entries; i++)
+            array->free_cb(array->data[i]);
+
+    pa_xfree(array->data);
+    pa_xfree(array);
+}
+
+void pa_dynarray_append(pa_dynarray *array, void *p) {
+    pa_assert(array);
+    pa_assert(p);
+
+    if (array->n_entries == array->n_allocated) {
+        unsigned n = PA_MAX(array->n_allocated * 2, 25U);
+
+        array->data = pa_xrealloc(array->data, sizeof(void *) * n);
+        array->n_allocated = n;
+    }
+
+    array->data[array->n_entries++] = p;
+}
+
+void *pa_dynarray_get(pa_dynarray *array, unsigned i) {
+    pa_assert(array);
+
+    if (i >= array->n_entries)
+        return NULL;
+
+    return array->data[i];
+}
+
+void *pa_dynarray_last(pa_dynarray *array) {
+    pa_assert(array);
+
+    if (array->n_entries == 0)
+        return NULL;
+
+    return array->data[array->n_entries - 1];
+}
+
+int pa_dynarray_remove_by_index(pa_dynarray *array, unsigned i) {
+    void *entry;
+
+    pa_assert(array);
+
+    if (i >= array->n_entries)
+        return -PA_ERR_NOENTITY;
+
+    entry = array->data[i];
+    array->data[i] = array->data[array->n_entries - 1];
+    array->n_entries--;
+
+    if (array->free_cb)
+        array->free_cb(entry);
+
+    return 0;
+}
+
+int pa_dynarray_remove_by_data(pa_dynarray *array, void *p) {
+    unsigned i;
+
+    pa_assert(array);
+    pa_assert(p);
+
+    /* Iterate backwards, with the assumption that recently appended entries
+     * are likely to be removed first. */
+    i = array->n_entries;
+    while (i > 0) {
+        i--;
+        if (array->data[i] == p) {
+            pa_dynarray_remove_by_index(array, i);
+            return 0;
+        }
+    }
+
+    return -PA_ERR_NOENTITY;
+}
+
+void *pa_dynarray_steal_last(pa_dynarray *array) {
+    pa_assert(array);
+
+    if (array->n_entries > 0)
+        return array->data[--array->n_entries];
+    else
+        return NULL;
+}
+
+unsigned pa_dynarray_size(pa_dynarray *array) {
+    pa_assert(array);
+
+    return array->n_entries;
+}
diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h
new file mode 100644 (file)
index 0000000..0b05880
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef foopulsecoredynarrayhfoo
+#define foopulsecoredynarrayhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/def.h>
+
+typedef struct pa_dynarray pa_dynarray;
+
+/* Implementation of a simple dynamically sized array for storing pointers.
+ *
+ * When the array is created, a free callback can be provided, which will be
+ * then used when removing items from the array and when freeing the array. If
+ * the free callback is not provided, the memory management of the stored items
+ * is the responsibility of the array user. If there is need to remove items
+ * from the array without freeing them, while also having the free callback
+ * set, the functions with "steal" in their name can be used.
+ *
+ * Removing items from the middle of the array causes the last item to be
+ * moved to the place of the removed item. That is, array ordering is not
+ * preserved.
+ *
+ * The array doesn't support storing NULL pointers. */
+
+pa_dynarray* pa_dynarray_new(pa_free_cb_t free_cb);
+void pa_dynarray_free(pa_dynarray *array);
+
+void pa_dynarray_append(pa_dynarray *array, void *p);
+
+/* Returns the element at index i, or NULL if i is out of bounds. */
+void *pa_dynarray_get(pa_dynarray *array, unsigned i);
+
+/* Returns the last element, or NULL if the array is empty. */
+void *pa_dynarray_last(pa_dynarray *array);
+
+/* Returns -PA_ERR_NOENTITY if i is out of bounds, and zero otherwise. */
+int pa_dynarray_remove_by_index(pa_dynarray *array, unsigned i);
+
+/* Returns -PA_ERR_NOENTITY if p is not found in the array, and zero
+ * otherwise. If the array contains multiple occurrencies of p, only one of
+ * them is removed (and it's unspecified which one). */
+int pa_dynarray_remove_by_data(pa_dynarray *array, void *p);
+
+/* Returns the removed item, or NULL if the array is empty. */
+void *pa_dynarray_steal_last(pa_dynarray *array);
+
+unsigned pa_dynarray_size(pa_dynarray *array);
+
+#define PA_DYNARRAY_FOREACH(elem, array, idx) \
+    for ((idx) = 0; ((elem) = pa_dynarray_get(array, idx)); (idx)++)
+
+#endif
diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h
new file mode 100644 (file)
index 0000000..c4cb8a2
--- /dev/null
@@ -0,0 +1,160 @@
+#ifndef fooendianmacroshfoo
+#define fooendianmacroshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+
+#ifdef HAVE_BYTESWAP_H
+#define PA_INT16_SWAP(x) ((int16_t) bswap_16((uint16_t) x))
+#define PA_UINT16_SWAP(x) ((uint16_t) bswap_16((uint16_t) x))
+#define PA_INT32_SWAP(x) ((int32_t) bswap_32((uint32_t) x))
+#define PA_UINT32_SWAP(x) ((uint32_t) bswap_32((uint32_t) x))
+#else
+#define PA_INT16_SWAP(x) ( (int16_t) ( ((uint16_t) (x) >> 8) | ((uint16_t) (x) << 8) ) )
+#define PA_UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) (x) >> 8) | ((uint16_t) (x) << 8) ) )
+#define PA_INT32_SWAP(x) ( (int32_t) ( ((uint32_t) (x) >> 24) | ((uint32_t) (x) << 24) | (((uint32_t) (x) & 0xFF00) << 8) | ((((uint32_t) (x)) >> 8) & 0xFF00) ) )
+#define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) (x) >> 24) | ((uint32_t) (x) << 24) | (((uint32_t) (x) & 0xFF00) << 8) | ((((uint32_t) (x)) >> 8) & 0xFF00) ) )
+#endif
+
+static inline uint32_t PA_READ24BE(const uint8_t *p) {
+    return
+        ((uint32_t) p[0] << 16) |
+        ((uint32_t) p[1] << 8) |
+        ((uint32_t) p[2]);
+}
+
+static inline uint32_t PA_READ24LE(const uint8_t *p) {
+    return
+        ((uint32_t) p[2] << 16) |
+        ((uint32_t) p[1] << 8) |
+        ((uint32_t) p[0]);
+}
+
+static inline void PA_WRITE24BE(uint8_t *p, uint32_t u) {
+    p[0] = (uint8_t) (u >> 16);
+    p[1] = (uint8_t) (u >> 8);
+    p[2] = (uint8_t) u;
+}
+
+static inline void PA_WRITE24LE(uint8_t *p, uint32_t u) {
+    p[2] = (uint8_t) (u >> 16);
+    p[1] = (uint8_t) (u >> 8);
+    p[0] = (uint8_t) u;
+}
+
+static inline float PA_READ_FLOAT32RE(const void *p) {
+    union {
+        float f;
+        uint32_t u;
+    } t;
+
+    t.u = PA_UINT32_SWAP(*(uint32_t *) p);
+    return t.f;
+}
+
+static inline void PA_WRITE_FLOAT32RE(void *p, float x) {
+    union {
+        float f;
+        uint32_t u;
+    } t;
+
+    t.f = x;
+    *(uint32_t *) p = PA_UINT32_SWAP(t.u);
+}
+
+#define PA_MAYBE_INT16_SWAP(c,x) ((c) ? PA_INT16_SWAP(x) : (x))
+#define PA_MAYBE_UINT16_SWAP(c,x) ((c) ? PA_UINT16_SWAP(x) : (x))
+
+#define PA_MAYBE_INT32_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : (x))
+#define PA_MAYBE_UINT32_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : (x))
+
+#ifdef WORDS_BIGENDIAN
+ #define PA_INT16_FROM_LE(x) PA_INT16_SWAP(x)
+ #define PA_INT16_FROM_BE(x) ((int16_t)(x))
+
+ #define PA_INT16_TO_LE(x) PA_INT16_SWAP(x)
+ #define PA_INT16_TO_BE(x) ((int16_t)(x))
+
+ #define PA_UINT16_FROM_LE(x) PA_UINT16_SWAP(x)
+ #define PA_UINT16_FROM_BE(x) ((uint16_t)(x))
+
+ #define PA_UINT16_TO_LE(x) PA_UINT16_SWAP(x)
+ #define PA_UINT16_TO_BE(x) ((uint16_t)(x))
+
+ #define PA_INT32_FROM_LE(x) PA_INT32_SWAP(x)
+ #define PA_INT32_FROM_BE(x) ((int32_t)(x))
+
+ #define PA_INT32_TO_LE(x) PA_INT32_SWAP(x)
+ #define PA_INT32_TO_BE(x) ((int32_t)(x))
+
+ #define PA_UINT32_FROM_LE(x) PA_UINT32_SWAP(x)
+ #define PA_UINT32_FROM_BE(x) ((uint32_t)(x))
+
+ #define PA_UINT32_TO_LE(x) PA_UINT32_SWAP(x)
+ #define PA_UINT32_TO_BE(x) ((uint32_t)(x))
+
+ #define PA_READ24NE(x) PA_READ24BE(x)
+ #define PA_WRITE24NE(x,y) PA_WRITE24BE((x),(y))
+
+ #define PA_READ24RE(x) PA_READ24LE(x)
+ #define PA_WRITE24RE(x,y) PA_WRITE24LE((x),(y))
+#else
+ #define PA_INT16_FROM_LE(x) ((int16_t)(x))
+ #define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x)
+
+ #define PA_INT16_TO_LE(x) ((int16_t)(x))
+ #define PA_INT16_TO_BE(x) PA_INT16_SWAP(x)
+
+ #define PA_UINT16_FROM_LE(x) ((uint16_t)(x))
+ #define PA_UINT16_FROM_BE(x) PA_UINT16_SWAP(x)
+
+ #define PA_UINT16_TO_LE(x) ((uint16_t)(x))
+ #define PA_UINT16_TO_BE(x) PA_UINT16_SWAP(x)
+
+ #define PA_INT32_FROM_LE(x) ((int32_t)(x))
+ #define PA_INT32_FROM_BE(x) PA_INT32_SWAP(x)
+
+ #define PA_INT32_TO_LE(x) ((int32_t)(x))
+ #define PA_INT32_TO_BE(x) PA_INT32_SWAP(x)
+
+ #define PA_UINT32_FROM_LE(x) ((uint32_t)(x))
+ #define PA_UINT32_FROM_BE(x) PA_UINT32_SWAP(x)
+
+ #define PA_UINT32_TO_LE(x) ((uint32_t)(x))
+ #define PA_UINT32_TO_BE(x) PA_UINT32_SWAP(x)
+
+ #define PA_READ24NE(x) PA_READ24LE(x)
+ #define PA_WRITE24NE(x,y) PA_WRITE24LE((x),(y))
+
+ #define PA_READ24RE(x) PA_READ24BE(x)
+ #define PA_WRITE24RE(x,y) PA_WRITE24BE((x),(y))
+#endif
+
+#endif
diff --git a/src/pulsecore/esound.h b/src/pulsecore/esound.h
new file mode 100644 (file)
index 0000000..4ca0e04
--- /dev/null
@@ -0,0 +1,204 @@
+#ifndef fooesoundhfoo
+#define fooesoundhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* 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 authentication 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 initial 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 immediately */
+
+    /* 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 complete request */
+    ESD_NEXT_REQUEST,           /* proceed to next request */
+    ESD_CLIENT_STATE_MAX        /* place holder */
+};
+typedef int esd_client_state_t;
+
+/* the endian key is transferred in binary, if it's read into int, */
+/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */
+/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */
+#define ESD_SWAP_ENDIAN_KEY (PA_UINT32_SWAP(ESD_ENDIAN_KEY))
+
+#endif
diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c
new file mode 100644 (file)
index 0000000..a7fbf95
--- /dev/null
@@ -0,0 +1,320 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulse/xmalloc.h>
+
+#ifndef HAVE_PIPE
+#include <pulsecore/pipe.h>
+#endif
+
+#ifdef HAVE_SYS_EVENTFD_H
+#include <sys/eventfd.h>
+#endif
+
+#include "fdsem.h"
+
+struct pa_fdsem {
+    int fds[2];
+#ifdef HAVE_SYS_EVENTFD_H
+    int efd;
+#endif
+    int write_type;
+    pa_fdsem_data *data;
+};
+
+pa_fdsem *pa_fdsem_new(void) {
+    pa_fdsem *f;
+
+    f = pa_xmalloc0(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data)));
+
+#ifdef HAVE_SYS_EVENTFD_H
+    if ((f->efd = eventfd(0, EFD_CLOEXEC)) >= 0)
+        f->fds[0] = f->fds[1] = -1;
+    else
+#endif
+    {
+        if (pa_pipe_cloexec(f->fds) < 0) {
+            pa_xfree(f);
+            return NULL;
+        }
+    }
+
+    f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem)));
+
+    pa_atomic_store(&f->data->waiting, 0);
+    pa_atomic_store(&f->data->signalled, 0);
+    pa_atomic_store(&f->data->in_pipe, 0);
+
+    return f;
+}
+
+pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd) {
+    pa_fdsem *f = NULL;
+
+    pa_assert(data);
+    pa_assert(event_fd >= 0);
+
+#ifdef HAVE_SYS_EVENTFD_H
+    f = pa_xnew0(pa_fdsem, 1);
+
+    f->efd = event_fd;
+    pa_make_fd_cloexec(f->efd);
+    f->fds[0] = f->fds[1] = -1;
+    f->data = data;
+#endif
+
+    return f;
+}
+
+pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data) {
+    pa_fdsem *f = NULL;
+
+    pa_assert(data);
+
+#ifdef HAVE_SYS_EVENTFD_H
+
+    f = pa_xnew0(pa_fdsem, 1);
+
+    if ((f->efd = eventfd(0, EFD_CLOEXEC)) < 0) {
+        pa_xfree(f);
+        return NULL;
+    }
+
+    f->fds[0] = f->fds[1] = -1;
+    f->data = data;
+
+    pa_atomic_store(&f->data->waiting, 0);
+    pa_atomic_store(&f->data->signalled, 0);
+    pa_atomic_store(&f->data->in_pipe, 0);
+
+#endif
+
+    return f;
+}
+
+void pa_fdsem_free(pa_fdsem *f) {
+    pa_assert(f);
+
+#ifdef HAVE_SYS_EVENTFD_H
+    if (f->efd >= 0)
+        pa_close(f->efd);
+#endif
+    pa_close_pipe(f->fds);
+
+    pa_xfree(f);
+}
+
+static void flush(pa_fdsem *f) {
+    ssize_t r;
+    pa_assert(f);
+
+    if (pa_atomic_load(&f->data->in_pipe) <= 0)
+        return;
+
+    do {
+        char x[10];
+
+#ifdef HAVE_SYS_EVENTFD_H
+        if (f->efd >= 0) {
+            uint64_t u;
+
+            if ((r = pa_read(f->efd, &u, sizeof(u), NULL)) != sizeof(u)) {
+
+                if (r >= 0 || errno != EINTR) {
+                    pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+                    pa_assert_not_reached();
+                }
+
+                continue;
+            }
+            r = (ssize_t) u;
+        } else
+#endif
+
+        if ((r = pa_read(f->fds[0], &x, sizeof(x), NULL)) <= 0) {
+
+            if (r >= 0 || errno != EINTR) {
+                pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+                pa_assert_not_reached();
+            }
+
+            continue;
+        }
+
+    } while (pa_atomic_sub(&f->data->in_pipe, (int) r) > (int) r);
+}
+
+void pa_fdsem_post(pa_fdsem *f) {
+    pa_assert(f);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 0, 1)) {
+
+        if (pa_atomic_load(&f->data->waiting)) {
+            ssize_t r;
+            char x = 'x';
+
+            pa_atomic_inc(&f->data->in_pipe);
+
+            for (;;) {
+
+#ifdef HAVE_SYS_EVENTFD_H
+                if (f->efd >= 0) {
+                    uint64_t u = 1;
+
+                    if ((r = pa_write(f->efd, &u, sizeof(u), &f->write_type)) != sizeof(u)) {
+                        if (r >= 0 || errno != EINTR) {
+                            pa_log_error("Invalid write to eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+                            pa_assert_not_reached();
+                        }
+
+                        continue;
+                    }
+                } else
+#endif
+
+                if ((r = pa_write(f->fds[1], &x, 1, &f->write_type)) != 1) {
+                    if (r >= 0 || errno != EINTR) {
+                        pa_log_error("Invalid write to pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+                        pa_assert_not_reached();
+                    }
+
+                    continue;
+                }
+
+                break;
+            }
+        }
+    }
+}
+
+void pa_fdsem_wait(pa_fdsem *f) {
+    pa_assert(f);
+
+    flush(f);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+        return;
+
+    pa_atomic_inc(&f->data->waiting);
+
+    while (!pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
+        char x[10];
+        ssize_t r;
+
+#ifdef HAVE_SYS_EVENTFD_H
+        if (f->efd >= 0) {
+            uint64_t u;
+
+            if ((r = pa_read(f->efd, &u, sizeof(u), NULL)) != sizeof(u)) {
+
+                if (r >= 0 || errno != EINTR) {
+                    pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+                    pa_assert_not_reached();
+                }
+
+                continue;
+            }
+
+            r = (ssize_t) u;
+        } else
+#endif
+
+        if ((r = pa_read(f->fds[0], &x, sizeof(x), NULL)) <= 0) {
+
+            if (r >= 0 || errno != EINTR) {
+                pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+                pa_assert_not_reached();
+            }
+
+            continue;
+        }
+
+        pa_atomic_sub(&f->data->in_pipe, (int) r);
+    }
+
+    pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
+}
+
+int pa_fdsem_try(pa_fdsem *f) {
+    pa_assert(f);
+
+    flush(f);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+        return 1;
+
+    return 0;
+}
+
+int pa_fdsem_get(pa_fdsem *f) {
+    pa_assert(f);
+
+#ifdef HAVE_SYS_EVENTFD_H
+    if (f->efd >= 0)
+        return f->efd;
+#endif
+
+    return f->fds[0];
+}
+
+int pa_fdsem_before_poll(pa_fdsem *f) {
+    pa_assert(f);
+
+    flush(f);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+        return -1;
+
+    pa_atomic_inc(&f->data->waiting);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
+        pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
+        return -1;
+    }
+    return 0;
+}
+
+int pa_fdsem_after_poll(pa_fdsem *f) {
+    pa_assert(f);
+
+    pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
+
+    flush(f);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+        return 1;
+
+    return 0;
+}
diff --git a/src/pulsecore/fdsem.h b/src/pulsecore/fdsem.h
new file mode 100644 (file)
index 0000000..ae1bbe9
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef foopulsefdsemhfoo
+#define foopulsefdsemhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+/* A simple, asynchronous semaphore which uses fds for sleeping. In
+ * the best case all functions are lock-free unless sleeping is
+ * required.  */
+
+#include <pulsecore/atomic.h>
+
+typedef struct pa_fdsem pa_fdsem;
+
+typedef struct pa_fdsem_data {
+    pa_atomic_t waiting;
+    pa_atomic_t signalled;
+    pa_atomic_t in_pipe;
+} pa_fdsem_data;
+
+pa_fdsem *pa_fdsem_new(void);
+pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd);
+pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data);
+void pa_fdsem_free(pa_fdsem *f);
+
+void pa_fdsem_post(pa_fdsem *f);
+void pa_fdsem_wait(pa_fdsem *f);
+int pa_fdsem_try(pa_fdsem *f);
+
+int pa_fdsem_get(pa_fdsem *f);
+
+int pa_fdsem_before_poll(pa_fdsem *f);
+int pa_fdsem_after_poll(pa_fdsem *f);
+
+#endif
diff --git a/src/pulsecore/ffmpeg/avcodec.h b/src/pulsecore/ffmpeg/avcodec.h
new file mode 100644 (file)
index 0000000..079c252
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * copyright (c) 2001 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef AVCODEC_H
+#define AVCODEC_H
+
+/* Just a heavily bastardized version of the original file from
+ * ffmpeg, just enough to get resample2.c to compile without
+ * modification -- Lennart */
+
+#if !defined(PACKAGE) && defined(HAVE_CONFIG_H)
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define av_mallocz(l) calloc(1, (l))
+#define av_malloc(l) malloc(l)
+#define av_realloc(p,l) realloc((p),(l))
+#define av_free(p) free(p)
+
+static inline void av_freep(void *k) {
+    void **p = k;
+
+    if (p) {
+        free(*p);
+        *p = NULL;
+    }
+}
+
+static inline int av_clip(int a, int amin, int amax)
+{
+    if (a < amin)      return amin;
+    else if (a > amax) return amax;
+    else               return a;
+}
+
+#define av_log(a,b,c)
+
+#define FFABS(a) ((a) >= 0 ? (a) : (-(a)))
+#define FFSIGN(a) ((a) > 0 ? 1 : -1)
+
+#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
+#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
+
+struct AVResampleContext;
+struct AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_length, int log2_phase_count, int linear, double cutoff);
+int av_resample(struct AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx);
+void av_resample_compensate(struct AVResampleContext *c, int sample_delta, int compensation_distance);
+void av_resample_close(struct AVResampleContext *c);
+void av_build_filter(int16_t *filter, double factor, int tap_count, int phase_count, int scale, int type);
+
+/*
+ * crude lrintf for non-C99 systems.
+ */
+#ifndef HAVE_LRINTF
+#define lrintf(x) ((long int)(x))
+#endif
+
+#endif /* AVCODEC_H */
diff --git a/src/pulsecore/ffmpeg/dsputil.h b/src/pulsecore/ffmpeg/dsputil.h
new file mode 100644 (file)
index 0000000..8da742d
--- /dev/null
@@ -0,0 +1 @@
+/* empty file, just here to allow us to compile an unmodified resampler2.c */
diff --git a/src/pulsecore/ffmpeg/resample2.c b/src/pulsecore/ffmpeg/resample2.c
new file mode 100644 (file)
index 0000000..a18f96e
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * audio resampling
+ * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @file libavcodec/resample2.c
+ * audio resampling
+ * @author Michael Niedermayer <michaelni@gmx.at>
+ */
+
+#include "avcodec.h"
+#include "dsputil.h"
+
+#ifndef CONFIG_RESAMPLE_HP
+#define FILTER_SHIFT 15
+
+#define FELEM int16_t
+#define FELEM2 int32_t
+#define FELEML int64_t
+#define FELEM_MAX INT16_MAX
+#define FELEM_MIN INT16_MIN
+#define WINDOW_TYPE 9
+#elif !defined(CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE)
+#define FILTER_SHIFT 30
+
+#define FELEM int32_t
+#define FELEM2 int64_t
+#define FELEML int64_t
+#define FELEM_MAX INT32_MAX
+#define FELEM_MIN INT32_MIN
+#define WINDOW_TYPE 12
+#else
+#define FILTER_SHIFT 0
+
+#define FELEM double
+#define FELEM2 double
+#define FELEML double
+#define WINDOW_TYPE 24
+#endif
+
+
+typedef struct AVResampleContext{
+    FELEM *filter_bank;
+    int filter_length;
+    int ideal_dst_incr;
+    int dst_incr;
+    int index;
+    int frac;
+    int src_incr;
+    int compensation_distance;
+    int phase_shift;
+    int phase_mask;
+    int linear;
+}AVResampleContext;
+
+/**
+ * 0th order modified bessel function of the first kind.
+ */
+static double bessel(double x){
+    double v=1;
+    double t=1;
+    int i;
+
+    x= x*x/4;
+    for(i=1; i<50; i++){
+        t *= x/(i*i);
+        v += t;
+    }
+    return v;
+}
+
+/**
+ * builds a polyphase filterbank.
+ * @param factor resampling factor
+ * @param scale wanted sum of coefficients for each filter
+ * @param type 0->cubic, 1->blackman nuttall windowed sinc, 2..16->kaiser windowed sinc beta=2..16
+ */
+void av_build_filter(FELEM *filter, double factor, int tap_count, int phase_count, int scale, int type){
+    int ph, i;
+    double x, y, w, tab[tap_count];
+    const int center= (tap_count-1)/2;
+
+    /* if upsampling, only need to interpolate, no filter */
+    if (factor > 1.0)
+        factor = 1.0;
+
+    for(ph=0;ph<phase_count;ph++) {
+        double norm = 0;
+        for(i=0;i<tap_count;i++) {
+            x = M_PI * ((double)(i - center) - (double)ph / phase_count) * factor;
+            if (x == 0) y = 1.0;
+            else        y = sin(x) / x;
+            switch(type){
+            case 0:{
+                const float d= -0.5; //first order derivative = -0.5
+                x = fabs(((double)(i - center) - (double)ph / phase_count) * factor);
+                if(x<1.0) y= 1 - 3*x*x + 2*x*x*x + d*(            -x*x + x*x*x);
+                else      y=                       d*(-4 + 8*x - 5*x*x + x*x*x);
+                break;}
+            case 1:
+                w = 2.0*x / (factor*tap_count) + M_PI;
+                y *= 0.3635819 - 0.4891775 * cos(w) + 0.1365995 * cos(2*w) - 0.0106411 * cos(3*w);
+                break;
+            default:
+                w = 2.0*x / (factor*tap_count*M_PI);
+                y *= bessel(type*sqrt(FFMAX(1-w*w, 0)));
+                break;
+            }
+
+            tab[i] = y;
+            norm += y;
+        }
+
+        /* normalize so that an uniform color remains the same */
+        for(i=0;i<tap_count;i++) {
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+            filter[ph * tap_count + i] = tab[i] / norm;
+#else
+            filter[ph * tap_count + i] = av_clip(lrintf(tab[i] * scale / norm), FELEM_MIN, FELEM_MAX);
+#endif
+        }
+    }
+#if 0
+    {
+#define LEN 1024
+        int j,k;
+        double sine[LEN + tap_count];
+        double filtered[LEN];
+        double maxff=-2, minff=2, maxsf=-2, minsf=2;
+        for(i=0; i<LEN; i++){
+            double ss=0, sf=0, ff=0;
+            for(j=0; j<LEN+tap_count; j++)
+                sine[j]= cos(i*j*M_PI/LEN);
+            for(j=0; j<LEN; j++){
+                double sum=0;
+                ph=0;
+                for(k=0; k<tap_count; k++)
+                    sum += filter[ph * tap_count + k] * sine[k+j];
+                filtered[j]= sum / (1<<FILTER_SHIFT);
+                ss+= sine[j + center] * sine[j + center];
+                ff+= filtered[j] * filtered[j];
+                sf+= sine[j + center] * filtered[j];
+            }
+            ss= sqrt(2*ss/LEN);
+            ff= sqrt(2*ff/LEN);
+            sf= 2*sf/LEN;
+            maxff= FFMAX(maxff, ff);
+            minff= FFMIN(minff, ff);
+            maxsf= FFMAX(maxsf, sf);
+            minsf= FFMIN(minsf, sf);
+            if(i%11==0){
+                av_log(NULL, AV_LOG_ERROR, "i:%4d ss:%f ff:%13.6e-%13.6e sf:%13.6e-%13.6e\n", i, ss, maxff, minff, maxsf, minsf);
+                minff=minsf= 2;
+                maxff=maxsf= -2;
+            }
+        }
+    }
+#endif
+}
+
+AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff){
+    AVResampleContext *c= av_mallocz(sizeof(AVResampleContext));
+    double factor= FFMIN(out_rate * cutoff / in_rate, 1.0);
+    int phase_count= 1<<phase_shift;
+
+    c->phase_shift= phase_shift;
+    c->phase_mask= phase_count-1;
+    c->linear= linear;
+
+    c->filter_length= FFMAX((int)ceil(filter_size/factor), 1);
+    c->filter_bank= av_mallocz(c->filter_length*(phase_count+1)*sizeof(FELEM));
+    av_build_filter(c->filter_bank, factor, c->filter_length, phase_count, 1<<FILTER_SHIFT, WINDOW_TYPE);
+    memcpy(&c->filter_bank[c->filter_length*phase_count+1], c->filter_bank, (c->filter_length-1)*sizeof(FELEM));
+    c->filter_bank[c->filter_length*phase_count]= c->filter_bank[c->filter_length - 1];
+
+    c->src_incr= out_rate;
+    c->ideal_dst_incr= c->dst_incr= in_rate * phase_count;
+    c->index= -phase_count*((c->filter_length-1)/2);
+
+    return c;
+}
+
+void av_resample_close(AVResampleContext *c){
+    av_freep(&c->filter_bank);
+    av_freep(&c);
+}
+
+void av_resample_compensate(AVResampleContext *c, int sample_delta, int compensation_distance){
+//    sample_delta += (c->ideal_dst_incr - c->dst_incr)*(int64_t)c->compensation_distance / c->ideal_dst_incr;
+    c->compensation_distance= compensation_distance;
+    c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * (int64_t)sample_delta / compensation_distance;
+}
+
+int av_resample(AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx){
+    int dst_index, i;
+    int index= c->index;
+    int frac= c->frac;
+    int dst_incr_frac= c->dst_incr % c->src_incr;
+    int dst_incr=      c->dst_incr / c->src_incr;
+    int compensation_distance= c->compensation_distance;
+
+  if(compensation_distance == 0 && c->filter_length == 1 && c->phase_shift==0){
+        int64_t index2= ((int64_t)index)<<32;
+        int64_t incr= (1LL<<32) * c->dst_incr / c->src_incr;
+        dst_size= FFMIN(dst_size, (src_size-1-index) * (int64_t)c->src_incr / c->dst_incr);
+
+        for(dst_index=0; dst_index < dst_size; dst_index++){
+            dst[dst_index] = src[index2>>32];
+            index2 += incr;
+        }
+        frac += dst_index * dst_incr_frac;
+        index += dst_index * dst_incr;
+        index += frac / c->src_incr;
+        frac %= c->src_incr;
+  }else{
+    for(dst_index=0; dst_index < dst_size; dst_index++){
+        FELEM *filter= c->filter_bank + c->filter_length*(index & c->phase_mask);
+        int sample_index= index >> c->phase_shift;
+        FELEM2 val=0;
+
+        if(sample_index < 0){
+            for(i=0; i<c->filter_length; i++)
+                val += src[FFABS(sample_index + i) % src_size] * filter[i];
+        }else if(sample_index + c->filter_length > src_size){
+            break;
+        }else if(c->linear){
+            FELEM2 v2=0;
+            for(i=0; i<c->filter_length; i++){
+                val += src[sample_index + i] * (FELEM2)filter[i];
+                v2  += src[sample_index + i] * (FELEM2)filter[i + c->filter_length];
+            }
+            val+=(v2-val)*(FELEML)frac / c->src_incr;
+        }else{
+            for(i=0; i<c->filter_length; i++){
+                val += src[sample_index + i] * (FELEM2)filter[i];
+            }
+        }
+
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+        dst[dst_index] = av_clip_int16(lrintf(val));
+#else
+        val = (val + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT;
+        dst[dst_index] = (unsigned)(val + 32768) > 65535 ? (val>>31) ^ 32767 : val;
+#endif
+
+        frac += dst_incr_frac;
+        index += dst_incr;
+        if(frac >= c->src_incr){
+            frac -= c->src_incr;
+            index++;
+        }
+
+        if(dst_index + 1 == compensation_distance){
+            compensation_distance= 0;
+            dst_incr_frac= c->ideal_dst_incr % c->src_incr;
+            dst_incr=      c->ideal_dst_incr / c->src_incr;
+        }
+    }
+  }
+    *consumed= FFMAX(index, 0) >> c->phase_shift;
+    if(index>=0) index &= c->phase_mask;
+
+    if(compensation_distance){
+        compensation_distance -= dst_index;
+        assert(compensation_distance > 0);
+    }
+    if(update_ctx){
+        c->frac= frac;
+        c->index= index;
+        c->dst_incr= dst_incr_frac + c->src_incr*dst_incr;
+        c->compensation_distance= compensation_distance;
+    }
+#if 0
+    if(update_ctx && !c->compensation_distance){
+#undef rand
+        av_resample_compensate(c, rand() % (8000*2) - 8000, 8000*2);
+av_log(NULL, AV_LOG_DEBUG, "%d %d %d\n", c->dst_incr, c->ideal_dst_incr, c->compensation_distance);
+    }
+#endif
+
+    return dst_index;
+}
diff --git a/src/pulsecore/filter/LICENSE.WEBKIT b/src/pulsecore/filter/LICENSE.WEBKIT
new file mode 100644 (file)
index 0000000..2f69d9f
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/src/pulsecore/filter/biquad.c b/src/pulsecore/filter/biquad.c
new file mode 100644 (file)
index 0000000..3205e7c
--- /dev/null
@@ -0,0 +1,117 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Copyright (C) 2010 Google Inc. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE.WEBKIT file.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+
+#include <math.h>
+#include "biquad.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif
+
+static void set_coefficient(struct biquad *bq, double b0, double b1, double b2,
+                           double a0, double a1, double a2)
+{
+       double a0_inv = 1 / a0;
+       bq->b0 = b0 * a0_inv;
+       bq->b1 = b1 * a0_inv;
+       bq->b2 = b2 * a0_inv;
+       bq->a1 = a1 * a0_inv;
+       bq->a2 = a2 * a0_inv;
+}
+
+static void biquad_lowpass(struct biquad *bq, double cutoff)
+{
+       /* Limit cutoff to 0 to 1. */
+       cutoff = PA_MIN(cutoff, 1.0);
+       cutoff = PA_MAX(0.0, cutoff);
+
+       if (cutoff >= 1.0) {
+               /* When cutoff is 1, the z-transform is 1. */
+               set_coefficient(bq, 1, 0, 0, 1, 0, 0);
+       } else if (cutoff > 0) {
+               /* Compute biquad coefficients for lowpass filter */
+               double theta = M_PI * cutoff;
+               double sn = 0.5 * M_SQRT2 * sin(theta);
+               double beta = 0.5 * (1 - sn) / (1 + sn);
+               double gamma_coeff = (0.5 + beta) * cos(theta);
+               double alpha = 0.25 * (0.5 + beta - gamma_coeff);
+
+               double b0 = 2 * alpha;
+               double b1 = 2 * 2 * alpha;
+               double b2 = 2 * alpha;
+               double a1 = 2 * -gamma_coeff;
+               double a2 = 2 * beta;
+
+               set_coefficient(bq, b0, b1, b2, 1, a1, a2);
+       } else {
+               /* When cutoff is zero, nothing gets through the filter, so set
+                * coefficients up correctly.
+                */
+               set_coefficient(bq, 0, 0, 0, 1, 0, 0);
+       }
+}
+
+static void biquad_highpass(struct biquad *bq, double cutoff)
+{
+       /* Limit cutoff to 0 to 1. */
+       cutoff = PA_MIN(cutoff, 1.0);
+       cutoff = PA_MAX(0.0, cutoff);
+
+       if (cutoff >= 1.0) {
+               /* The z-transform is 0. */
+               set_coefficient(bq, 0, 0, 0, 1, 0, 0);
+       } else if (cutoff > 0) {
+               /* Compute biquad coefficients for highpass filter */
+               double theta = M_PI * cutoff;
+               double sn = 0.5 * M_SQRT2 * sin(theta);
+               double beta = 0.5 * (1 - sn) / (1 + sn);
+               double gamma_coeff = (0.5 + beta) * cos(theta);
+               double alpha = 0.25 * (0.5 + beta + gamma_coeff);
+
+               double b0 = 2 * alpha;
+               double b1 = 2 * -2 * alpha;
+               double b2 = 2 * alpha;
+               double a1 = 2 * -gamma_coeff;
+               double a2 = 2 * beta;
+
+               set_coefficient(bq, b0, b1, b2, 1, a1, a2);
+       } else {
+               /* When cutoff is zero, we need to be careful because the above
+                * gives a quadratic divided by the same quadratic, with poles
+                * and zeros on the unit circle in the same place. When cutoff
+                * is zero, the z-transform is 1.
+                */
+               set_coefficient(bq, 1, 0, 0, 1, 0, 0);
+       }
+}
+
+void biquad_set(struct biquad *bq, enum biquad_type type, double freq)
+{
+
+       switch (type) {
+       case BQ_LOWPASS:
+               biquad_lowpass(bq, freq);
+               break;
+       case BQ_HIGHPASS:
+               biquad_highpass(bq, freq);
+               break;
+       }
+}
diff --git a/src/pulsecore/filter/biquad.h b/src/pulsecore/filter/biquad.h
new file mode 100644 (file)
index 0000000..bb8f2fb
--- /dev/null
@@ -0,0 +1,45 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef BIQUAD_H_
+#define BIQUAD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The biquad filter parameters. The transfer function H(z) is (b0 + b1 * z^(-1)
+ * + b2 * z^(-2)) / (1 + a1 * z^(-1) + a2 * z^(-2)).  The previous two inputs
+ * are stored in x1 and x2, and the previous two outputs are stored in y1 and
+ * y2.
+ *
+ * We use double during the coefficients calculation for better accurary, but
+ * float is used during the actual filtering for faster computation.
+ */
+struct biquad {
+       float b0, b1, b2;
+       float a1, a2;
+};
+
+/* The type of the biquad filters */
+enum biquad_type {
+       BQ_LOWPASS,
+       BQ_HIGHPASS,
+};
+
+/* Initialize a biquad filter parameters from its type and parameters.
+ * Args:
+ *    bq - The biquad filter we want to set.
+ *    type - The type of the biquad filter.
+ *    frequency - The value should be in the range [0, 1]. It is relative to
+ *        half of the sampling rate.
+ */
+void biquad_set(struct biquad *bq, enum biquad_type type, double freq);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* BIQUAD_H_ */
diff --git a/src/pulsecore/filter/crossover.c b/src/pulsecore/filter/crossover.c
new file mode 100644 (file)
index 0000000..dab34af
--- /dev/null
@@ -0,0 +1,97 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+
+#include "crossover.h"
+
+void lr4_set(struct lr4 *lr4, enum biquad_type type, float freq)
+{
+       biquad_set(&lr4->bq, type, freq);
+       lr4->x1 = 0;
+       lr4->x2 = 0;
+       lr4->y1 = 0;
+       lr4->y2 = 0;
+       lr4->z1 = 0;
+       lr4->z2 = 0;
+}
+
+void lr4_process_float32(struct lr4 *lr4, int samples, int channels, float *src, float *dest)
+{
+       float lx1 = lr4->x1;
+       float lx2 = lr4->x2;
+       float ly1 = lr4->y1;
+       float ly2 = lr4->y2;
+       float lz1 = lr4->z1;
+       float lz2 = lr4->z2;
+       float lb0 = lr4->bq.b0;
+       float lb1 = lr4->bq.b1;
+       float lb2 = lr4->bq.b2;
+       float la1 = lr4->bq.a1;
+       float la2 = lr4->bq.a2;
+
+       int i;
+       for (i = 0; i < samples * channels; i += channels) {
+               float x, y, z;
+               x = src[i];
+               y = lb0*x + lb1*lx1 + lb2*lx2 - la1*ly1 - la2*ly2;
+               z = lb0*y + lb1*ly1 + lb2*ly2 - la1*lz1 - la2*lz2;
+               lx2 = lx1;
+               lx1 = x;
+               ly2 = ly1;
+               ly1 = y;
+               lz2 = lz1;
+               lz1 = z;
+               dest[i] = z;
+       }
+
+       lr4->x1 = lx1;
+       lr4->x2 = lx2;
+       lr4->y1 = ly1;
+       lr4->y2 = ly2;
+       lr4->z1 = lz1;
+       lr4->z2 = lz2;
+}
+
+void lr4_process_s16(struct lr4 *lr4, int samples, int channels, short *src, short *dest)
+{
+       float lx1 = lr4->x1;
+       float lx2 = lr4->x2;
+       float ly1 = lr4->y1;
+       float ly2 = lr4->y2;
+       float lz1 = lr4->z1;
+       float lz2 = lr4->z2;
+       float lb0 = lr4->bq.b0;
+       float lb1 = lr4->bq.b1;
+       float lb2 = lr4->bq.b2;
+       float la1 = lr4->bq.a1;
+       float la2 = lr4->bq.a2;
+
+       int i;
+       for (i = 0; i < samples * channels; i += channels) {
+               float x, y, z;
+               x = src[i];
+               y = lb0*x + lb1*lx1 + lb2*lx2 - la1*ly1 - la2*ly2;
+               z = lb0*y + lb1*ly1 + lb2*ly2 - la1*lz1 - la2*lz2;
+               lx2 = lx1;
+               lx1 = x;
+               ly2 = ly1;
+               ly1 = y;
+               lz2 = lz1;
+               lz1 = z;
+               dest[i] = PA_CLAMP_UNLIKELY((int) z, -0x8000, 0x7fff);
+       }
+
+       lr4->x1 = lx1;
+       lr4->x2 = lx2;
+       lr4->y1 = ly1;
+       lr4->y2 = ly2;
+       lr4->z1 = lz1;
+       lr4->z2 = lz2;
+}
diff --git a/src/pulsecore/filter/crossover.h b/src/pulsecore/filter/crossover.h
new file mode 100644 (file)
index 0000000..c5c9765
--- /dev/null
@@ -0,0 +1,29 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CROSSOVER_H_
+#define CROSSOVER_H_
+
+#include "biquad.h"
+/* An LR4 filter is two biquads with the same parameters connected in series:
+ *
+ * x -- [BIQUAD] -- y -- [BIQUAD] -- z
+ *
+ * Both biquad filter has the same parameter b[012] and a[12],
+ * The variable [xyz][12] keep the history values.
+ */
+struct lr4 {
+       struct biquad bq;
+       float x1, x2;
+       float y1, y2;
+       float z1, z2;
+};
+
+void lr4_set(struct lr4 *lr4, enum biquad_type type, float freq);
+
+void lr4_process_float32(struct lr4 *lr4, int samples, int channels, float *src, float *dest);
+void lr4_process_s16(struct lr4 *lr4, int samples, int channels, short *src, short *dest);
+
+#endif /* CROSSOVER_H_ */
diff --git a/src/pulsecore/filter/lfe-filter.c b/src/pulsecore/filter/lfe-filter.c
new file mode 100644 (file)
index 0000000..c0b1eb0
--- /dev/null
@@ -0,0 +1,201 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 David Henningsson, Canonical Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "lfe-filter.h"
+#include <pulse/xmalloc.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/filter/biquad.h>
+#include <pulsecore/filter/crossover.h>
+
+struct saved_state {
+    PA_LLIST_FIELDS(struct saved_state);
+    pa_memchunk chunk;
+    int64_t index;
+    struct lr4 lr4[PA_CHANNELS_MAX];
+};
+
+PA_STATIC_FLIST_DECLARE(lfe_state, 0, pa_xfree);
+
+/* An LR4 filter, implemented as a chain of two Butterworth filters.
+
+   Currently the channel map is fixed so that a highpass filter is applied to all
+   channels except for the LFE channel, where a lowpass filter is applied.
+   This works well for e g stereo to 2.1/5.1/7.1 scenarios, where the remap engine
+   has calculated the LFE channel to be the average of all source channels.
+*/
+
+struct pa_lfe_filter {
+    int64_t index;
+    PA_LLIST_HEAD(struct saved_state, saved);
+    float crossover;
+    pa_channel_map cm;
+    pa_sample_spec ss;
+    size_t maxrewind;
+    bool active;
+    struct lr4 lr4[PA_CHANNELS_MAX];
+};
+
+static void remove_state(pa_lfe_filter_t *f, struct saved_state *s) {
+    PA_LLIST_REMOVE(struct saved_state, f->saved, s);
+    pa_memblock_unref(s->chunk.memblock);
+    pa_xfree(s);
+}
+
+pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq, size_t maxrewind) {
+
+    pa_lfe_filter_t *f = pa_xnew0(struct pa_lfe_filter, 1);
+    f->crossover = crossover_freq;
+    f->cm = *cm;
+    f->ss = *ss;
+    f->maxrewind = maxrewind;
+    pa_lfe_filter_update_rate(f, ss->rate);
+    return f;
+}
+
+void pa_lfe_filter_free(pa_lfe_filter_t *f) {
+    while (f->saved)
+        remove_state(f, f->saved);
+
+    pa_xfree(f);
+}
+
+void pa_lfe_filter_reset(pa_lfe_filter_t *f) {
+    pa_lfe_filter_update_rate(f, f->ss.rate);
+}
+
+static void process_block(pa_lfe_filter_t *f, pa_memchunk *buf, bool store_result) {
+    int samples = buf->length / pa_frame_size(&f->ss);
+
+    void *garbage = store_result ? NULL : pa_xmalloc(buf->length);
+
+    if (f->ss.format == PA_SAMPLE_FLOAT32NE) {
+        int i;
+        float *data = pa_memblock_acquire_chunk(buf);
+        for (i = 0; i < f->cm.channels; i++)
+            lr4_process_float32(&f->lr4[i], samples, f->cm.channels, &data[i], garbage ? garbage : &data[i]);
+        pa_memblock_release(buf->memblock);
+    }
+    else if (f->ss.format == PA_SAMPLE_S16NE) {
+        int i;
+        short *data = pa_memblock_acquire_chunk(buf);
+        for (i = 0; i < f->cm.channels; i++)
+            lr4_process_s16(&f->lr4[i], samples, f->cm.channels, &data[i], garbage ? garbage : &data[i]);
+        pa_memblock_release(buf->memblock);
+    }
+    else pa_assert_not_reached();
+
+    pa_xfree(garbage);
+    f->index += samples;
+}
+
+pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *f, pa_memchunk *buf) {
+    pa_mempool *pool;
+    struct saved_state *s, *s2;
+    void *data;
+
+    if (!f->active || !buf->length)
+        return buf;
+
+    /* Remove old states (FIXME: we could do better than searching the entire array here?) */
+    PA_LLIST_FOREACH_SAFE(s, s2, f->saved)
+        if (s->index + (int64_t) (s->chunk.length / pa_frame_size(&f->ss) + f->maxrewind) < f->index)
+            remove_state(f, s);
+
+    /* Insert our existing state into the flist */
+    if ((s = pa_flist_pop(PA_STATIC_FLIST_GET(lfe_state))) == NULL)
+        s = pa_xnew(struct saved_state, 1);
+    PA_LLIST_INIT(struct saved_state, s);
+
+    /* TODO: This actually memcpys the entire chunk into a new allocation, because we need to retain the original
+       in case of rewinding. Investigate whether this can be avoided. */
+    data = pa_memblock_acquire_chunk(buf);
+    pool = pa_memblock_get_pool(buf->memblock);
+    s->chunk.memblock = pa_memblock_new_malloced(pool, pa_xmemdup(data, buf->length), buf->length);
+    s->chunk.length = buf->length;
+    s->chunk.index = 0;
+    pa_memblock_release(buf->memblock);
+    pa_mempool_unref(pool), pool = NULL;
+
+    s->index = f->index;
+    memcpy(s->lr4, f->lr4, sizeof(struct lr4) * f->cm.channels);
+    PA_LLIST_PREPEND(struct saved_state, f->saved, s);
+
+    process_block(f, buf, true);
+    return buf;
+}
+
+void pa_lfe_filter_update_rate(pa_lfe_filter_t *f, uint32_t new_rate) {
+    int i;
+    float biquad_freq = f->crossover / (new_rate / 2);
+
+    while (f->saved)
+        remove_state(f, f->saved);
+
+    f->index = 0;
+    f->ss.rate = new_rate;
+    if (biquad_freq <= 0 || biquad_freq >= 1) {
+        pa_log_warn("Crossover frequency (%f) outside range for sample rate %d", f->crossover, new_rate);
+        f->active = false;
+        return;
+    }
+
+    for (i = 0; i < f->cm.channels; i++)
+        lr4_set(&f->lr4[i], f->cm.map[i] == PA_CHANNEL_POSITION_LFE ? BQ_LOWPASS : BQ_HIGHPASS, biquad_freq);
+
+    f->active = true;
+}
+
+void pa_lfe_filter_rewind(pa_lfe_filter_t *f, size_t amount) {
+    struct saved_state *i, *s = NULL;
+    size_t samples = amount / pa_frame_size(&f->ss);
+    f->index -= samples;
+
+    /* Find the closest saved position */
+    PA_LLIST_FOREACH(i, f->saved) {
+        if (i->index > f->index)
+            continue;
+        if (s == NULL || i->index > s->index)
+            s = i;
+    }
+    if (s == NULL) {
+        pa_log_debug("Rewinding LFE filter %zu samples to position %lli. No saved state found", samples, (long long) f->index);
+        pa_lfe_filter_update_rate(f, f->ss.rate);
+        return;
+    }
+    pa_log_debug("Rewinding LFE filter %zu samples to position %lli. Found saved state at position %lli",
+        samples, (long long) f->index, (long long) s->index);
+    memcpy(f->lr4, s->lr4, sizeof(struct lr4) * f->cm.channels);
+
+    /* now fast forward to the actual position */
+    if (f->index > s->index) {
+        pa_memchunk x = s->chunk;
+        x.length = (f->index - s->index) * pa_frame_size(&f->ss);
+        if (x.length > s->chunk.length) {
+            pa_log_error("Hole in stream, cannot fast forward LFE filter");
+            return;
+        }
+        f->index = s->index;
+        process_block(f, &x, false);
+    }
+}
diff --git a/src/pulsecore/filter/lfe-filter.h b/src/pulsecore/filter/lfe-filter.h
new file mode 100644 (file)
index 0000000..54d695b
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef foolfefilterhfoo
+#define foolfefilterhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 David Henningsson, Canonical Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/memblockq.h>
+
+typedef struct pa_lfe_filter pa_lfe_filter_t;
+
+pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq, size_t maxrewind);
+void pa_lfe_filter_free(pa_lfe_filter_t *);
+void pa_lfe_filter_reset(pa_lfe_filter_t *);
+void pa_lfe_filter_rewind(pa_lfe_filter_t *, size_t amount);
+pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *filter, pa_memchunk *buf);
+void pa_lfe_filter_update_rate(pa_lfe_filter_t *, uint32_t new_rate);
+
+#endif
diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c
new file mode 100644 (file)
index 0000000..8d2e643
--- /dev/null
@@ -0,0 +1,177 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+  Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+  Copyright (C) 2012 Canonical Ltd.
+
+  Contact: Jyri Sarha <Jyri.Sarha@nokia.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "flist.h"
+
+#define FLIST_SIZE 256
+
+/* Atomic table indices contain
+   sign bit = if set, indicates empty/NULL value
+   tag bits (to avoid the ABA problem)
+   actual index bits
+*/
+
+/* Lock free single linked list element. */
+struct pa_flist_elem {
+    pa_atomic_t next;
+    pa_atomic_ptr_t ptr;
+};
+
+typedef struct pa_flist_elem pa_flist_elem;
+
+struct pa_flist {
+    char *name;
+    unsigned size;
+
+    pa_atomic_t current_tag;
+    int index_mask;
+    int tag_shift;
+    int tag_mask;
+
+    /* Stack that contains pointers stored into free list */
+    pa_atomic_t stored;
+    /* Stack that contains empty list elements */
+    pa_atomic_t empty;
+    pa_flist_elem table[];
+};
+
+/* Lock free pop from linked list stack */
+static pa_flist_elem *stack_pop(pa_flist *flist, pa_atomic_t *list) {
+    pa_flist_elem *popped;
+    int idx;
+    pa_assert(list);
+
+    do {
+        idx = pa_atomic_load(list);
+        if (idx < 0)
+            return NULL;
+        popped = &flist->table[idx & flist->index_mask];
+    } while (!pa_atomic_cmpxchg(list, idx, pa_atomic_load(&popped->next)));
+
+    return popped;
+}
+
+/* Lock free push to linked list stack */
+static void stack_push(pa_flist *flist, pa_atomic_t *list, pa_flist_elem *new_elem) {
+    int tag, newindex, next;
+    pa_assert(list);
+
+    tag = pa_atomic_inc(&flist->current_tag);
+    newindex = new_elem - flist->table;
+    pa_assert(newindex >= 0 && newindex < (int) flist->size);
+    newindex |= (tag << flist->tag_shift) & flist->tag_mask;
+
+    do {
+        next = pa_atomic_load(list);
+        pa_atomic_store(&new_elem->next, next);
+    } while (!pa_atomic_cmpxchg(list, next, newindex));
+}
+
+pa_flist *pa_flist_new_with_name(unsigned size, const char *name) {
+    pa_flist *l;
+    unsigned i;
+    pa_assert(name);
+
+    if (!size)
+        size = FLIST_SIZE;
+
+    l = pa_xmalloc0(sizeof(pa_flist) + sizeof(pa_flist_elem) * size);
+
+    l->name = pa_xstrdup(name);
+    l->size = size;
+
+    while (1 << l->tag_shift < (int) size)
+        l->tag_shift++;
+    l->index_mask = (1 << l->tag_shift) - 1;
+    l->tag_mask = INT_MAX - l->index_mask;
+
+    pa_atomic_store(&l->stored, -1);
+    pa_atomic_store(&l->empty, -1);
+    for (i=0; i < size; i++) {
+        stack_push(l, &l->empty, &l->table[i]);
+    }
+    return l;
+}
+
+pa_flist *pa_flist_new(unsigned size) {
+    return pa_flist_new_with_name(size, "unknown");
+}
+
+void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) {
+    pa_assert(l);
+    pa_assert(l->name);
+
+    if (free_cb) {
+        pa_flist_elem *elem;
+        while((elem = stack_pop(l, &l->stored)))
+            free_cb(pa_atomic_ptr_load(&elem->ptr));
+    }
+
+    pa_xfree(l->name);
+    pa_xfree(l);
+}
+
+int pa_flist_push(pa_flist *l, void *p) {
+    pa_flist_elem *elem;
+    pa_assert(l);
+    pa_assert(p);
+
+    elem = stack_pop(l, &l->empty);
+    if (elem == NULL) {
+        if (pa_log_ratelimit(PA_LOG_DEBUG))
+            pa_log_debug("%s flist is full (don't worry)", l->name);
+        return -1;
+    }
+    pa_atomic_ptr_store(&elem->ptr, p);
+    stack_push(l, &l->stored, elem);
+
+    return 0;
+}
+
+void* pa_flist_pop(pa_flist *l) {
+    pa_flist_elem *elem;
+    void *ptr;
+    pa_assert(l);
+
+    elem = stack_pop(l, &l->stored);
+    if (elem == NULL)
+        return NULL;
+
+    ptr = pa_atomic_ptr_load(&elem->ptr);
+
+    stack_push(l, &l->empty, elem);
+
+    return ptr;
+}
diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h
new file mode 100644 (file)
index 0000000..0341208
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef foopulseflisthfoo
+#define foopulseflisthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/def.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/once.h>
+#include <pulsecore/core-util.h>
+
+/* A multiple-reader multipler-write lock-free free list implementation */
+
+typedef struct pa_flist pa_flist;
+
+pa_flist * pa_flist_new(unsigned size);
+/* Name string is copied and added to flist structure. The original is
+ * responsibility of the caller. The name is only used for debug printing. */
+pa_flist * pa_flist_new_with_name(unsigned size, const char *name);
+void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb);
+
+/* Please note that this routine might fail! */
+int pa_flist_push(pa_flist*l, void *p);
+void* pa_flist_pop(pa_flist*l);
+
+/* Please note that the destructor stuff is not really necessary, we do
+ * this just to make valgrind output more useful. */
+
+#define PA_STATIC_FLIST_DECLARE(name, size, free_cb)                    \
+    static struct {                                                     \
+        pa_flist *volatile flist;                                       \
+        pa_once once;                                                   \
+    } name##_flist = { NULL, PA_ONCE_INIT };                            \
+    static void name##_flist_init(void) {                               \
+        name##_flist.flist =                                            \
+            pa_flist_new_with_name(size, __FILE__ ": " #name);          \
+    }                                                                   \
+    static inline pa_flist* name##_flist_get(void) {                    \
+        pa_run_once(&name##_flist.once, name##_flist_init);             \
+        return name##_flist.flist;                                      \
+    }                                                                   \
+    static void name##_flist_destructor(void) PA_GCC_DESTRUCTOR;        \
+    static void name##_flist_destructor(void) {                         \
+        if (!pa_in_valgrind())                                          \
+            return;                                                     \
+        if (name##_flist.flist)                                         \
+            pa_flist_free(name##_flist.flist, (free_cb));               \
+    }                                                                   \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_STATIC_FLIST_GET(name) (name##_flist_get())
+
+#endif
diff --git a/src/pulsecore/g711.c b/src/pulsecore/g711.c
new file mode 100644 (file)
index 0000000..aa2d703
--- /dev/null
@@ -0,0 +1,2531 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+
+/*
+ * December 30, 1994:
+ * Functions linear2alaw, linear2ulaw have been updated to correctly
+ * convert unquantized 16 bit values.
+ * Tables for direct u- to A-law and A- to u-law conversions have been
+ * corrected.
+ * Borge Lindberg, Center for PersonKommunikation, Aalborg University.
+ * bli@cpk.auc.dk
+ *
+ */
+
+#include "g711.h"
+
+#define        SIGN_BIT        (0x80)                /* Sign bit for a A-law byte. */
+#define        QUANT_MASK        (0xf)                /* Quantization field mask. */
+#define        NSEGS                (8)                /* Number of A-law segments. */
+#define        SEG_SHIFT        (4)                /* Left shift for segment number. */
+#define        SEG_MASK        (0x70)                /* Segment field mask. */
+
+#if !defined(FAST_ALAW_CONVERSION) || !defined(FAST_ULAW_CONVERSION)
+static int16_t seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
+                              0x1FF, 0x3FF, 0x7FF, 0xFFF};
+static int16_t seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
+                              0x3FF, 0x7FF, 0xFFF, 0x1FFF};
+
+static int16_t search(
+        int16_t        val,
+        int16_t *table,
+        int size)
+{
+        int i;
+
+        for (i = 0; i < size; i++) {
+                if (val <= *table++)
+                        return (i);
+        }
+        return (size);
+}
+#endif /* !FAST_*_CONVERSION */
+
+#ifndef FAST_ALAW_CONVERSION
+/*
+ * linear2alaw() accepts an 13-bit signed integer and encodes it as A-law data
+ * stored in a unsigned char.  This function should only be called with
+ * the data shifted such that it only contains information in the lower
+ * 13-bits.
+ *
+ *                Linear Input Code        Compressed Code
+ *        ------------------------        ---------------
+ *        0000000wxyza                        000wxyz
+ *        0000001wxyza                        001wxyz
+ *        000001wxyzab                        010wxyz
+ *        00001wxyzabc                        011wxyz
+ *        0001wxyzabcd                        100wxyz
+ *        001wxyzabcde                        101wxyz
+ *        01wxyzabcdef                        110wxyz
+ *        1wxyzabcdefg                        111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char st_13linear2alaw(
+        int16_t                pcm_val)        /* 2's complement (13-bit range) */
+{
+        int16_t                mask;
+        short                seg;
+        unsigned char        aval;
+
+        /* Have calling software do it since its already doing a shift
+         * from 32-bits down to 16-bits.
+         */
+        /* pcm_val = pcm_val >> 3; */
+
+        /* A-law using even bit inversion */
+        if (pcm_val >= 0) {
+                mask = 0xD5;                /* sign (7th) bit = 1 */
+        } else {
+                mask = 0x55;                /* sign bit = 0 */
+                pcm_val = -pcm_val - 1;
+        }
+
+        /* Convert the scaled magnitude to segment number. */
+        seg = search(pcm_val, seg_aend, 8);
+
+        /* Combine the sign, segment, and quantization bits. */
+
+        if (seg >= 8)                /* out of range, return maximum value. */
+                return (unsigned char) (0x7F ^ mask);
+        else {
+                aval = (unsigned char) seg << SEG_SHIFT;
+                if (seg < 2)
+                        aval |= (pcm_val >> 1) & QUANT_MASK;
+                else
+                        aval |= (pcm_val >> seg) & QUANT_MASK;
+                return (aval ^ mask);
+        }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit signed linear PCM
+ *
+ */
+int16_t st_alaw2linear16(
+        unsigned char        a_val)
+{
+        int16_t t;
+        int16_t seg;
+
+        a_val ^= 0x55;
+
+        t = (a_val & QUANT_MASK) << 4;
+        seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+        switch (seg) {
+        case 0:
+                t += 8;
+                break;
+        case 1:
+                t += 0x108;
+                break;
+        default:
+                t += 0x108;
+                t <<= seg - 1;
+        }
+        return ((a_val & SIGN_BIT) ? t : -t);
+}
+#endif /* !FAST_ALAW_CONVERSION */
+
+#define        BIAS                (0x84)                /* Bias for linear code. */
+#define CLIP            8159
+
+#ifndef FAST_ULAW_CONVERSION
+/*
+ * linear2ulaw() accepts a 14-bit signed integer and encodes it as u-law data
+ * stored in a unsigned char.  This function should only be called with
+ * the data shifted such that it only contains information in the lower
+ * 14-bits.
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ *        Biased Linear Input Code        Compressed Code
+ *        ------------------------        ---------------
+ *        00000001wxyza                        000wxyz
+ *        0000001wxyzab                        001wxyz
+ *        000001wxyzabc                        010wxyz
+ *        00001wxyzabcd                        011wxyz
+ *        0001wxyzabcde                        100wxyz
+ *        001wxyzabcdef                        101wxyz
+ *        01wxyzabcdefg                        110wxyz
+ *        1wxyzabcdefgh                        111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz.  * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char st_14linear2ulaw(
+        int16_t                pcm_val)        /* 2's complement (14-bit range) */
+{
+        int16_t                mask;
+        int16_t                seg;
+        unsigned char        uval;
+
+        /* Have calling software do it since its already doing a shift
+         * from 32-bits down to 16-bits.
+         */
+        /* pcm_val = pcm_val >> 2; */
+
+        /* u-law inverts all bits */
+        /* Get the sign and the magnitude of the value. */
+        if (pcm_val < 0) {
+                pcm_val = -pcm_val;
+                mask = 0x7F;
+        } else {
+                mask = 0xFF;
+        }
+        if ( pcm_val > CLIP ) pcm_val = CLIP;                /* clip the magnitude */
+        pcm_val += (BIAS >> 2);
+
+        /* Convert the scaled magnitude to segment number. */
+        seg = search(pcm_val, seg_uend, 8);
+
+        /*
+         * Combine the sign, segment, quantization bits;
+         * and complement the code word.
+         */
+        if (seg >= 8)                /* out of range, return maximum value. */
+                return (unsigned char) (0x7F ^ mask);
+        else {
+                uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
+                return (uval ^ mask);
+        }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+int16_t st_ulaw2linear16(
+        unsigned char        u_val)
+{
+        int16_t                t;
+
+        /* Complement to obtain normal u-law value. */
+        u_val = ~u_val;
+
+        /*
+         * Extract and bias the quantization bits. Then
+         * shift up by the segment number and subtract out the bias.
+         */
+        t = ((u_val & QUANT_MASK) << 3) + BIAS;
+        t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+        return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+#endif /* !FAST_ULAW_CONVERSION */
+
+#ifdef FAST_ALAW_CONVERSION
+
+int16_t _st_alaw2linear16[256] = {
+     -5504,   -5248,   -6016,   -5760,   -4480,   -4224,   -4992,
+     -4736,   -7552,   -7296,   -8064,   -7808,   -6528,   -6272,
+     -7040,   -6784,   -2752,   -2624,   -3008,   -2880,   -2240,
+     -2112,   -2496,   -2368,   -3776,   -3648,   -4032,   -3904,
+     -3264,   -3136,   -3520,   -3392,  -22016,  -20992,  -24064,
+    -23040,  -17920,  -16896,  -19968,  -18944,  -30208,  -29184,
+    -32256,  -31232,  -26112,  -25088,  -28160,  -27136,  -11008,
+    -10496,  -12032,  -11520,   -8960,   -8448,   -9984,   -9472,
+    -15104,  -14592,  -16128,  -15616,  -13056,  -12544,  -14080,
+    -13568,    -344,    -328,    -376,    -360,    -280,    -264,
+      -312,    -296,    -472,    -456,    -504,    -488,    -408,
+      -392,    -440,    -424,     -88,     -72,    -120,    -104,
+       -24,      -8,     -56,     -40,    -216,    -200,    -248,
+      -232,    -152,    -136,    -184,    -168,   -1376,   -1312,
+     -1504,   -1440,   -1120,   -1056,   -1248,   -1184,   -1888,
+     -1824,   -2016,   -1952,   -1632,   -1568,   -1760,   -1696,
+      -688,    -656,    -752,    -720,    -560,    -528,    -624,
+      -592,    -944,    -912,   -1008,    -976,    -816,    -784,
+      -880,    -848,    5504,    5248,    6016,    5760,    4480,
+      4224,    4992,    4736,    7552,    7296,    8064,    7808,
+      6528,    6272,    7040,    6784,    2752,    2624,    3008,
+      2880,    2240,    2112,    2496,    2368,    3776,    3648,
+      4032,    3904,    3264,    3136,    3520,    3392,   22016,
+     20992,   24064,   23040,   17920,   16896,   19968,   18944,
+     30208,   29184,   32256,   31232,   26112,   25088,   28160,
+     27136,   11008,   10496,   12032,   11520,    8960,    8448,
+      9984,    9472,   15104,   14592,   16128,   15616,   13056,
+     12544,   14080,   13568,     344,     328,     376,     360,
+       280,     264,     312,     296,     472,     456,     504,
+       488,     408,     392,     440,     424,      88,      72,
+       120,     104,      24,       8,      56,      40,     216,
+       200,     248,     232,     152,     136,     184,     168,
+      1376,    1312,    1504,    1440,    1120,    1056,    1248,
+      1184,    1888,    1824,    2016,    1952,    1632,    1568,
+      1760,    1696,     688,     656,     752,     720,     560,
+       528,     624,     592,     944,     912,    1008,     976,
+       816,     784,     880,     848
+};
+
+uint8_t _st_13linear2alaw[0x2000] = {
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b,
+   0x6b, 0x6b, 0x6b, 0x6b, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
+   0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6e, 0x6e, 0x6e, 0x6e,
+   0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
+   0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d,
+   0x6d, 0x6d, 0x6d, 0x6d, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62,
+   0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x60, 0x60, 0x60, 0x60,
+   0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+   0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67,
+   0x67, 0x67, 0x67, 0x67, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,
+   0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x7a, 0x7a, 0x7a, 0x7a,
+   0x7b, 0x7b, 0x7b, 0x7b, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
+   0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7c, 0x7c, 0x7c, 0x7c,
+   0x7d, 0x7d, 0x7d, 0x7d, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73,
+   0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, 0x76, 0x76, 0x76, 0x76,
+   0x77, 0x77, 0x77, 0x77, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75,
+   0x4a, 0x4a, 0x4b, 0x4b, 0x48, 0x48, 0x49, 0x49, 0x4e, 0x4e, 0x4f, 0x4f,
+   0x4c, 0x4c, 0x4d, 0x4d, 0x42, 0x42, 0x43, 0x43, 0x40, 0x40, 0x41, 0x41,
+   0x46, 0x46, 0x47, 0x47, 0x44, 0x44, 0x45, 0x45, 0x5a, 0x5a, 0x5b, 0x5b,
+   0x58, 0x58, 0x59, 0x59, 0x5e, 0x5e, 0x5f, 0x5f, 0x5c, 0x5c, 0x5d, 0x5d,
+   0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, 0x56, 0x56, 0x57, 0x57,
+   0x54, 0x54, 0x55, 0x55, 0xd5, 0xd5, 0xd4, 0xd4, 0xd7, 0xd7, 0xd6, 0xd6,
+   0xd1, 0xd1, 0xd0, 0xd0, 0xd3, 0xd3, 0xd2, 0xd2, 0xdd, 0xdd, 0xdc, 0xdc,
+   0xdf, 0xdf, 0xde, 0xde, 0xd9, 0xd9, 0xd8, 0xd8, 0xdb, 0xdb, 0xda, 0xda,
+   0xc5, 0xc5, 0xc4, 0xc4, 0xc7, 0xc7, 0xc6, 0xc6, 0xc1, 0xc1, 0xc0, 0xc0,
+   0xc3, 0xc3, 0xc2, 0xc2, 0xcd, 0xcd, 0xcc, 0xcc, 0xcf, 0xcf, 0xce, 0xce,
+   0xc9, 0xc9, 0xc8, 0xc8, 0xcb, 0xcb, 0xca, 0xca, 0xf5, 0xf5, 0xf5, 0xf5,
+   0xf4, 0xf4, 0xf4, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6,
+   0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf3, 0xf3, 0xf3, 0xf3,
+   0xf2, 0xf2, 0xf2, 0xf2, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc,
+   0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xf9, 0xf9, 0xf9, 0xf9,
+   0xf8, 0xf8, 0xf8, 0xf8, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa,
+   0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4,
+   0xe4, 0xe4, 0xe4, 0xe4, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+   0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe1, 0xe1, 0xe1, 0xe1,
+   0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+   0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2,
+   0xe2, 0xe2, 0xe2, 0xe2, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+   0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xef, 0xef, 0xef, 0xef,
+   0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+   0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8,
+   0xe8, 0xe8, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+   0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+#endif /* FAST_ALAW_CONVERSION */
+
+#ifdef FAST_ULAW_CONVERSION
+
+int16_t _st_ulaw2linear16[256] = {
+    -32124,  -31100,  -30076,  -29052,  -28028,  -27004,  -25980,
+    -24956,  -23932,  -22908,  -21884,  -20860,  -19836,  -18812,
+    -17788,  -16764,  -15996,  -15484,  -14972,  -14460,  -13948,
+    -13436,  -12924,  -12412,  -11900,  -11388,  -10876,  -10364,
+     -9852,   -9340,   -8828,   -8316,   -7932,   -7676,   -7420,
+     -7164,   -6908,   -6652,   -6396,   -6140,   -5884,   -5628,
+     -5372,   -5116,   -4860,   -4604,   -4348,   -4092,   -3900,
+     -3772,   -3644,   -3516,   -3388,   -3260,   -3132,   -3004,
+     -2876,   -2748,   -2620,   -2492,   -2364,   -2236,   -2108,
+     -1980,   -1884,   -1820,   -1756,   -1692,   -1628,   -1564,
+     -1500,   -1436,   -1372,   -1308,   -1244,   -1180,   -1116,
+     -1052,    -988,    -924,    -876,    -844,    -812,    -780,
+      -748,    -716,    -684,    -652,    -620,    -588,    -556,
+      -524,    -492,    -460,    -428,    -396,    -372,    -356,
+      -340,    -324,    -308,    -292,    -276,    -260,    -244,
+      -228,    -212,    -196,    -180,    -164,    -148,    -132,
+      -120,    -112,    -104,     -96,     -88,     -80,     -72,
+       -64,     -56,     -48,     -40,     -32,     -24,     -16,
+        -8,       0,   32124,   31100,   30076,   29052,   28028,
+     27004,   25980,   24956,   23932,   22908,   21884,   20860,
+     19836,   18812,   17788,   16764,   15996,   15484,   14972,
+     14460,   13948,   13436,   12924,   12412,   11900,   11388,
+     10876,   10364,    9852,    9340,    8828,    8316,    7932,
+      7676,    7420,    7164,    6908,    6652,    6396,    6140,
+      5884,    5628,    5372,    5116,    4860,    4604,    4348,
+      4092,    3900,    3772,    3644,    3516,    3388,    3260,
+      3132,    3004,    2876,    2748,    2620,    2492,    2364,
+      2236,    2108,    1980,    1884,    1820,    1756,    1692,
+      1628,    1564,    1500,    1436,    1372,    1308,    1244,
+      1180,    1116,    1052,     988,     924,     876,     844,
+       812,     780,     748,     716,     684,     652,     620,
+       588,     556,     524,     492,     460,     428,     396,
+       372,     356,     340,     324,     308,     292,     276,
+       260,     244,     228,     212,     196,     180,     164,
+       148,     132,     120,     112,     104,      96,      88,
+        80,      72,      64,      56,      48,      40,      32,
+        24,      16,       8,       0
+};
+
+uint8_t _st_14linear2ulaw[0x4000] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40,
+   0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+   0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+   0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+   0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43,
+   0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
+   0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+   0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
+   0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46,
+   0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,
+   0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,
+   0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
+   0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49,
+   0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+   0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+   0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b,
+   0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+   0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+   0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d,
+   0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e,
+   0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f,
+   0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+   0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51,
+   0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52,
+   0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54,
+   0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57,
+   0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+   0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a,
+   0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b,
+   0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d,
+   0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
+   0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
+   0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63,
+   0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66,
+   0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69,
+   0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c,
+   0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
+   0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
+   0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a,
+   0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0xff, 0xfe, 0xfe, 0xfd,
+   0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7,
+   0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1,
+   0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed,
+   0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xea,
+   0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7,
+   0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4,
+   0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1,
+   0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+   0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd,
+   0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+   0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xda,
+   0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+   0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7,
+   0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+   0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4,
+   0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
+   0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1,
+   0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+   0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+   0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xce,
+   0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd,
+   0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+   0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+   0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+   0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca,
+   0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca,
+   0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9,
+   0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
+   0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7,
+   0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+   0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+   0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+   0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4,
+   0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+   0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+   0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
+   0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1,
+   0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
+   0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+   0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80
+};
+
+#endif /* FAST_ULAW_CONVERSION */
+
+/* The following code was used to generate the lookup tables */
+#if 0
+int main()
+{
+    int x, y, find2a = 0;
+
+    y = 0;
+    printf("int16_t _st_alaw2linear16[256] = {\n  ");
+    for (x = 0; x < 256; x++)
+    {
+        printf("%8d,", st_alaw2linear16(x));
+        y++;
+        if (y == 7)
+        {
+            y = 0;
+            printf("\n  ");
+        }
+    }
+
+    printf("\n};\n\nuint8_t _st_13linear2alaw[0x2000] = {\n  ");
+    y = 0;
+    for (x = 0; x < 0x2000; x++)
+    {
+        printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x));
+        y++;
+        if (y == 12)
+        {
+            y = 0;
+            printf("\n  ");
+        }
+    }
+
+    printf("\n};\n\nint16_t _st_ulaw2linear16[256] = {\n  ");
+    y = 0;
+    for (x = 0; x < 256; x++)
+    {
+        printf("%8d,", st_ulaw2linear16(x));
+        y++;
+        if (y == 7)
+        {
+            y = 0;
+            printf("\n  ");
+        }
+    }
+
+    printf("\n};\n\nuint8_t _st_14linear2ulaw[0x4000] = {\n  ");
+    y = 0;
+    for (x = 0; x < 0x4000; x++)
+    {
+        printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x));
+        y++;
+        if (y == 12)
+        {
+            y = 0;
+            printf("\n  ");
+        }
+    }
+    printf("\n};\n");
+
+}
+#endif
+
+/* The following is not used by SoX but kept for reference */
+#if 0
+/* copy from CCITT G.711 specifications */
+unsigned char _u2a[128] = {                        /* u- to A-law conversions */
+        1,        1,        2,        2,        3,        3,        4,        4,
+        5,        5,        6,        6,        7,        7,        8,        8,
+        9,        10,        11,        12,        13,        14,        15,        16,
+        17,        18,        19,        20,        21,        22,        23,        24,
+        25,        27,        29,        31,        33,        34,        35,        36,
+        37,        38,        39,        40,        41,        42,        43,        44,
+        46,        48,        49,        50,        51,        52,        53,        54,
+        55,        56,        57,        58,        59,        60,        61,        62,
+        64,        65,        66,        67,        68,        69,        70,        71,
+        72,        73,        74,        75,        76,        77,        78,        79,
+/* corrected:
+        81,        82,        83,        84,        85,        86,        87,        88,
+   should be: */
+        80,        82,        83,        84,        85,        86,        87,        88,
+        89,        90,        91,        92,        93,        94,        95,        96,
+        97,        98,        99,        100,        101,        102,        103,        104,
+        105,        106,        107,        108,        109,        110,        111,        112,
+        113,        114,        115,        116,        117,        118,        119,        120,
+        121,        122,        123,        124,        125,        126,        127,        128};
+
+unsigned char _a2u[128] = {                        /* A- to u-law conversions */
+        1,        3,        5,        7,        9,        11,        13,        15,
+        16,        17,        18,        19,        20,        21,        22,        23,
+        24,        25,        26,        27,        28,        29,        30,        31,
+        32,        32,        33,        33,        34,        34,        35,        35,
+        36,        37,        38,        39,        40,        41,        42,        43,
+        44,        45,        46,        47,        48,        48,        49,        49,
+        50,        51,        52,        53,        54,        55,        56,        57,
+        58,        59,        60,        61,        62,        63,        64,        64,
+        65,        66,        67,        68,        69,        70,        71,        72,
+/* corrected:
+        73,        74,        75,        76,        77,        78,        79,        79,
+   should be: */
+        73,        74,        75,        76,        77,        78,        79,        80,
+
+        80,        81,        82,        83,        84,        85,        86,        87,
+        88,        89,        90,        91,        92,        93,        94,        95,
+        96,        97,        98,        99,        100,        101,        102,        103,
+        104,        105,        106,        107,        108,        109,        110,        111,
+        112,        113,        114,        115,        116,        117,        118,        119,
+        120,        121,        122,        123,        124,        125,        126,        127};
+
+/* A-law to u-law conversion */
+unsigned char st_alaw2ulaw(
+        unsigned char        aval)
+{
+        aval &= 0xff;
+        return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+            (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char st_ulaw2alaw(
+        unsigned char        uval)
+{
+        uval &= 0xff;
+        return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+            (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+#endif
diff --git a/src/pulsecore/g711.h b/src/pulsecore/g711.h
new file mode 100644 (file)
index 0000000..37ebcf7
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef foog711hfoo
+#define foog711hfoo
+
+/* g711.h - include for G711 u-law and a-law conversion routines
+**
+** Copyright (C) 2001 Chris Bagwell
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+/** Copied from sox -- Lennart Poettering */
+
+#include <inttypes.h>
+
+#ifdef FAST_ALAW_CONVERSION
+extern uint8_t _st_13linear2alaw[0x2000];
+extern int16_t _st_alaw2linear16[256];
+#define st_13linear2alaw(sw) (_st_13linear2alaw[(sw + 0x1000)])
+#define st_alaw2linear16(uc) (_st_alaw2linear16[uc])
+#else
+unsigned char st_13linear2alaw(int16_t pcm_val);
+int16_t st_alaw2linear16(unsigned char);
+#endif
+
+#ifdef FAST_ULAW_CONVERSION
+extern uint8_t _st_14linear2ulaw[0x4000];
+extern int16_t _st_ulaw2linear16[256];
+#define st_14linear2ulaw(sw) (_st_14linear2ulaw[(sw + 0x2000)])
+#define st_ulaw2linear16(uc) (_st_ulaw2linear16[uc])
+#else
+unsigned char st_14linear2ulaw(int16_t pcm_val);
+int16_t st_ulaw2linear16(unsigned char);
+#endif
+
+#endif
diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c
new file mode 100644 (file)
index 0000000..2385c55
--- /dev/null
@@ -0,0 +1,342 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/macro.h>
+
+#include "hashmap.h"
+
+#define NBUCKETS 127
+
+struct hashmap_entry {
+    void *key;
+    void *value;
+
+    struct hashmap_entry *bucket_next, *bucket_previous;
+    struct hashmap_entry *iterate_next, *iterate_previous;
+};
+
+struct pa_hashmap {
+    pa_hash_func_t hash_func;
+    pa_compare_func_t compare_func;
+
+    pa_free_cb_t key_free_func;
+    pa_free_cb_t value_free_func;
+
+    struct hashmap_entry *iterate_list_head, *iterate_list_tail;
+    unsigned n_entries;
+};
+
+#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + PA_ALIGN(sizeof(pa_hashmap))))
+
+PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
+
+pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func, pa_free_cb_t key_free_func, pa_free_cb_t value_free_func) {
+    pa_hashmap *h;
+
+    h = pa_xmalloc0(PA_ALIGN(sizeof(pa_hashmap)) + NBUCKETS*sizeof(struct hashmap_entry*));
+
+    h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
+    h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
+
+    h->key_free_func = key_free_func;
+    h->value_free_func = value_free_func;
+
+    h->n_entries = 0;
+    h->iterate_list_head = h->iterate_list_tail = NULL;
+
+    return h;
+}
+
+pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
+    return pa_hashmap_new_full(hash_func, compare_func, NULL, NULL);
+}
+
+static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) {
+    pa_assert(h);
+    pa_assert(e);
+
+    /* Remove from iteration list */
+    if (e->iterate_next)
+        e->iterate_next->iterate_previous = e->iterate_previous;
+    else
+        h->iterate_list_tail = e->iterate_previous;
+
+    if (e->iterate_previous)
+        e->iterate_previous->iterate_next = e->iterate_next;
+    else
+        h->iterate_list_head = e->iterate_next;
+
+    /* Remove from hash table bucket list */
+    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 {
+        unsigned hash = h->hash_func(e->key) % NBUCKETS;
+        BY_HASH(h)[hash] = e->bucket_next;
+    }
+
+    if (h->key_free_func)
+        h->key_free_func(e->key);
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+        pa_xfree(e);
+
+    pa_assert(h->n_entries >= 1);
+    h->n_entries--;
+}
+
+void pa_hashmap_free(pa_hashmap *h) {
+    pa_assert(h);
+
+    pa_hashmap_remove_all(h);
+    pa_xfree(h);
+}
+
+static struct hashmap_entry *hash_scan(pa_hashmap *h, unsigned hash, const void *key) {
+    struct hashmap_entry *e;
+    pa_assert(h);
+    pa_assert(hash < NBUCKETS);
+
+    for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
+        if (h->compare_func(e->key, key) == 0)
+            return e;
+
+    return NULL;
+}
+
+int pa_hashmap_put(pa_hashmap *h, void *key, void *value) {
+    struct hashmap_entry *e;
+    unsigned hash;
+
+    pa_assert(h);
+
+    hash = h->hash_func(key) % NBUCKETS;
+
+    if (hash_scan(h, hash, key))
+        return -1;
+
+    if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
+        e = pa_xnew(struct hashmap_entry, 1);
+
+    e->key = key;
+    e->value = value;
+
+    /* Insert into hash table */
+    e->bucket_next = BY_HASH(h)[hash];
+    e->bucket_previous = NULL;
+    if (BY_HASH(h)[hash])
+        BY_HASH(h)[hash]->bucket_previous = e;
+    BY_HASH(h)[hash] = e;
+
+    /* Insert into iteration list */
+    e->iterate_previous = h->iterate_list_tail;
+    e->iterate_next = NULL;
+    if (h->iterate_list_tail) {
+        pa_assert(h->iterate_list_head);
+        h->iterate_list_tail->iterate_next = e;
+    } else {
+        pa_assert(!h->iterate_list_head);
+        h->iterate_list_head = e;
+    }
+    h->iterate_list_tail = e;
+
+    h->n_entries++;
+    pa_assert(h->n_entries >= 1);
+
+    return 0;
+}
+
+void* pa_hashmap_get(pa_hashmap *h, const void *key) {
+    unsigned hash;
+    struct hashmap_entry *e;
+
+    pa_assert(h);
+
+    hash = h->hash_func(key) % NBUCKETS;
+
+    if (!(e = hash_scan(h, hash, key)))
+        return NULL;
+
+    return e->value;
+}
+
+void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
+    struct hashmap_entry *e;
+    unsigned hash;
+    void *data;
+
+    pa_assert(h);
+
+    hash = h->hash_func(key) % NBUCKETS;
+
+    if (!(e = hash_scan(h, hash, key)))
+        return NULL;
+
+    data = e->value;
+    remove_entry(h, e);
+
+    return data;
+}
+
+int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key) {
+    void *data;
+
+    pa_assert(h);
+
+    data = pa_hashmap_remove(h, key);
+
+    if (data && h->value_free_func)
+        h->value_free_func(data);
+
+    return data ? 0 : -1;
+}
+
+void pa_hashmap_remove_all(pa_hashmap *h) {
+    pa_assert(h);
+
+    while (h->iterate_list_head) {
+        void *data;
+        data = h->iterate_list_head->value;
+        remove_entry(h, h->iterate_list_head);
+
+        if (h->value_free_func)
+            h->value_free_func(data);
+    }
+}
+
+void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
+    struct hashmap_entry *e;
+
+    pa_assert(h);
+    pa_assert(state);
+
+    if (*state == (void*) -1)
+        goto at_end;
+
+    if (!*state && !h->iterate_list_head)
+        goto at_end;
+
+    e = *state ? *state : h->iterate_list_head;
+
+    if (e->iterate_next)
+        *state = e->iterate_next;
+    else
+        *state = (void*) -1;
+
+    if (key)
+        *key = e->key;
+
+    return e->value;
+
+at_end:
+    *state = (void *) -1;
+
+    if (key)
+        *key = NULL;
+
+    return NULL;
+}
+
+void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void **key) {
+    struct hashmap_entry *e;
+
+    pa_assert(h);
+    pa_assert(state);
+
+    if (*state == (void*) -1)
+        goto at_beginning;
+
+    if (!*state && !h->iterate_list_tail)
+        goto at_beginning;
+
+    e = *state ? *state : h->iterate_list_tail;
+
+    if (e->iterate_previous)
+        *state = e->iterate_previous;
+    else
+        *state = (void*) -1;
+
+    if (key)
+        *key = e->key;
+
+    return e->value;
+
+at_beginning:
+    *state = (void *) -1;
+
+    if (key)
+        *key = NULL;
+
+    return NULL;
+}
+
+void* pa_hashmap_first(pa_hashmap *h) {
+    pa_assert(h);
+
+    if (!h->iterate_list_head)
+        return NULL;
+
+    return h->iterate_list_head->value;
+}
+
+void* pa_hashmap_last(pa_hashmap *h) {
+    pa_assert(h);
+
+    if (!h->iterate_list_tail)
+        return NULL;
+
+    return h->iterate_list_tail->value;
+}
+
+void* pa_hashmap_steal_first(pa_hashmap *h) {
+    void *data;
+
+    pa_assert(h);
+
+    if (!h->iterate_list_head)
+        return NULL;
+
+    data = h->iterate_list_head->value;
+    remove_entry(h, h->iterate_list_head);
+
+    return data;
+}
+
+unsigned pa_hashmap_size(pa_hashmap *h) {
+    pa_assert(h);
+
+    return h->n_entries;
+}
+
+bool pa_hashmap_isempty(pa_hashmap *h) {
+    pa_assert(h);
+
+    return h->n_entries == 0;
+}
diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h
new file mode 100644 (file)
index 0000000..b1027e7
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef foopulsecorehashmaphfoo
+#define foopulsecorehashmaphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/def.h>
+
+#include <pulsecore/idxset.h>
+
+/* Simple Implementation of a hash table. Memory management is the
+ * user's job. It's a good idea to have the key pointer point to a
+ * string in the value data. The insertion order is preserved when
+ * iterating. */
+
+typedef struct pa_hashmap pa_hashmap;
+
+/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */
+pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func);
+
+/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map, and functions to free the key
+ * and value (either or both can be NULL). */
+pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func, pa_free_cb_t key_free_func, pa_free_cb_t value_free_func);
+
+/* Free the hash table. */
+void pa_hashmap_free(pa_hashmap*);
+
+/* Add an entry to the hashmap. Returns non-zero when the entry already exists */
+int pa_hashmap_put(pa_hashmap *h, void *key, void *value);
+
+/* Return an entry from the hashmap */
+void* pa_hashmap_get(pa_hashmap *h, const void *key);
+
+/* Returns the data of the entry while removing */
+void* pa_hashmap_remove(pa_hashmap *h, const void *key);
+
+/* Removes the entry and frees the entry data. Returns a negative value if the
+ * entry is not found. FIXME: This function shouldn't be needed.
+ * pa_hashmap_remove() should free the entry data, and the current semantics of
+ * pa_hashmap_remove() should be implemented by a function called
+ * pa_hashmap_steal(). */
+int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key);
+
+/* Remove all entries but don't free the hashmap */
+void pa_hashmap_remove_all(pa_hashmap *h);
+
+/* Return the current number of entries of the hashmap */
+unsigned pa_hashmap_size(pa_hashmap *h);
+
+/* Return true if the hashmap is empty */
+bool pa_hashmap_isempty(pa_hashmap *h);
+
+/* May be used to iterate through the hashmap. Initially the opaque
+   pointer *state has to be set to NULL. The hashmap may not be
+   modified during iteration -- except for deleting the current entry
+   via pa_hashmap_remove(). The key of the entry is returned in *key,
+   if key is non-NULL. After the last entry in the hashmap NULL is
+   returned. */
+void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
+
+/* Same as pa_hashmap_iterate() but goes backwards */
+void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void**key);
+
+/* Remove the oldest entry in the hashmap and return it */
+void *pa_hashmap_steal_first(pa_hashmap *h);
+
+/* Return the oldest entry in the hashmap */
+void* pa_hashmap_first(pa_hashmap *h);
+
+/* Return the newest entry in the hashmap */
+void* pa_hashmap_last(pa_hashmap *h);
+
+/* A macro to ease iteration through all entries */
+#define PA_HASHMAP_FOREACH(e, h, state) \
+    for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL))
+
+/* A macro to ease itration through all key, value pairs */
+#define PA_HASHMAP_FOREACH_KV(k, e, h, state) \
+    for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k)); (e); (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k)))
+
+/* A macro to ease iteration through all entries, backwards */
+#define PA_HASHMAP_FOREACH_BACKWARDS(e, h, state) \
+    for ((state) = NULL, (e) = pa_hashmap_iterate_backwards((h), &(state), NULL); (e); (e) = pa_hashmap_iterate_backwards((h), &(state), NULL))
+
+#endif
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
new file mode 100644 (file)
index 0000000..e59dcba
--- /dev/null
@@ -0,0 +1,129 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+
+#include "hook-list.h"
+
+void pa_hook_init(pa_hook *hook, void *data) {
+    pa_assert(hook);
+
+    PA_LLIST_HEAD_INIT(pa_hook_slot, hook->slots);
+    hook->n_dead = hook->n_firing = 0;
+    hook->data = data;
+}
+
+static void slot_free(pa_hook *hook, pa_hook_slot *slot) {
+    pa_assert(hook);
+    pa_assert(slot);
+
+    PA_LLIST_REMOVE(pa_hook_slot, hook->slots, slot);
+
+    pa_xfree(slot);
+}
+
+void pa_hook_done(pa_hook *hook) {
+    pa_assert(hook);
+    pa_assert(hook->n_firing == 0);
+
+    while (hook->slots)
+        slot_free(hook, hook->slots);
+
+    pa_hook_init(hook, NULL);
+}
+
+pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) {
+    pa_hook_slot *slot, *where, *prev;
+
+    pa_assert(cb);
+
+    slot = pa_xnew(pa_hook_slot, 1);
+    slot->hook = hook;
+    slot->dead = false;
+    slot->callback = cb;
+    slot->data = data;
+    slot->priority = prio;
+
+    prev = NULL;
+    for (where = hook->slots; where; where = where->next) {
+        if (prio < where->priority)
+            break;
+        prev = where;
+    }
+
+    PA_LLIST_INSERT_AFTER(pa_hook_slot, hook->slots, prev, slot);
+
+    return slot;
+}
+
+void pa_hook_slot_free(pa_hook_slot *slot) {
+    pa_assert(slot);
+    pa_assert(!slot->dead);
+
+    if (slot->hook->n_firing > 0) {
+        slot->dead = true;
+        slot->hook->n_dead++;
+    } else
+        slot_free(slot->hook, slot);
+}
+
+pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
+    pa_hook_slot *slot, *next;
+    pa_hook_result_t result = PA_HOOK_OK;
+
+    pa_assert(hook);
+
+    hook->n_firing ++;
+
+    PA_LLIST_FOREACH(slot, hook->slots) {
+        if (slot->dead)
+            continue;
+
+        if ((result = slot->callback(hook->data, data, slot->data)) != PA_HOOK_OK)
+            break;
+    }
+
+    hook->n_firing --;
+    pa_assert(hook->n_firing >= 0);
+
+    for (slot = hook->slots; hook->n_dead > 0 && slot; slot = next) {
+        next = slot->next;
+
+        if (slot->dead) {
+            slot_free(hook, slot);
+            hook->n_dead--;
+        }
+    }
+
+    pa_assert(hook->n_dead == 0);
+
+    return result;
+}
+
+bool pa_hook_is_firing(pa_hook *hook) {
+    pa_assert(hook);
+
+    return hook->n_firing > 0;
+}
diff --git a/src/pulsecore/hook-list.h b/src/pulsecore/hook-list.h
new file mode 100644 (file)
index 0000000..63037e7
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef foohooklistfoo
+#define foohooklistfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/llist.h>
+
+typedef struct pa_hook_slot pa_hook_slot;
+typedef struct pa_hook pa_hook;
+
+typedef enum pa_hook_result {
+    PA_HOOK_OK = 0,
+    PA_HOOK_STOP = 1,
+    PA_HOOK_CANCEL = -1
+} pa_hook_result_t;
+
+typedef enum pa_hook_priority {
+    PA_HOOK_EARLY = -100,
+    PA_HOOK_NORMAL = 0,
+    PA_HOOK_LATE = 100
+} pa_hook_priority_t;
+
+typedef pa_hook_result_t (*pa_hook_cb_t)(
+        void *hook_data,
+        void *call_data,
+        void *slot_data);
+
+struct pa_hook_slot {
+    bool dead;
+    pa_hook *hook;
+    pa_hook_priority_t priority;
+    pa_hook_cb_t callback;
+    void *data;
+    PA_LLIST_FIELDS(pa_hook_slot);
+};
+
+struct pa_hook {
+    PA_LLIST_HEAD(pa_hook_slot, slots);
+    int n_firing, n_dead;
+
+    void *data;
+};
+
+void pa_hook_init(pa_hook *hook, void *data);
+void pa_hook_done(pa_hook *hook);
+
+pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data);
+void pa_hook_slot_free(pa_hook_slot *slot);
+
+pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data);
+
+bool pa_hook_is_firing(pa_hook *hook);
+
+#endif
diff --git a/src/pulsecore/i18n.c b/src/pulsecore/i18n.c
new file mode 100644 (file)
index 0000000..1cd74c0
--- /dev/null
@@ -0,0 +1,37 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/once.h>
+
+#include "i18n.h"
+
+void pa_init_i18n(void) {
+#ifdef ENABLE_NLS
+    PA_ONCE_BEGIN {
+
+        bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
+        bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+
+    } PA_ONCE_END;
+#endif
+}
diff --git a/src/pulsecore/i18n.h b/src/pulsecore/i18n.h
new file mode 100644 (file)
index 0000000..2423db3
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef foopulsei18nhfoo
+#define foopulsei18nhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/cdecl.h>
+
+PA_C_DECL_BEGIN
+
+#ifdef ENABLE_NLS
+
+#if !defined(GETTEXT_PACKAGE)
+#error "Something is very wrong here, config.h needs to be included first"
+#endif
+
+#include <libintl.h>
+
+#define _(String) dgettext(GETTEXT_PACKAGE, String)
+#ifdef gettext_noop
+#define N_(String) gettext_noop(String)
+#else
+#define N_(String) (String)
+#endif
+
+#else /* NLS is disabled */
+
+#define _(String) (String)
+#define N_(String) (String)
+#define textdomain(String) (String)
+#define gettext(String) (String)
+#define dgettext(Domain,String) (String)
+#define dcgettext(Domain,String,Type) (String)
+#define bindtextdomain(Domain,Directory) (Domain)
+#define bind_textdomain_codeset(Domain,Codeset) (Codeset)
+
+#endif /* ENABLE_NLS */
+
+void pa_init_i18n(void);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c
new file mode 100644 (file)
index 0000000..5175ca2
--- /dev/null
@@ -0,0 +1,471 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/macro.h>
+
+#include "idxset.h"
+
+#define NBUCKETS 127
+
+struct idxset_entry {
+    uint32_t idx;
+    void *data;
+
+    struct idxset_entry *data_next, *data_previous;
+    struct idxset_entry *index_next, *index_previous;
+    struct idxset_entry *iterate_next, *iterate_previous;
+};
+
+struct pa_idxset {
+    pa_hash_func_t hash_func;
+    pa_compare_func_t compare_func;
+
+    uint32_t current_index;
+
+    struct idxset_entry *iterate_list_head, *iterate_list_tail;
+    unsigned n_entries;
+};
+
+#define BY_DATA(i) ((struct idxset_entry**) ((uint8_t*) (i) + PA_ALIGN(sizeof(pa_idxset))))
+#define BY_INDEX(i) (BY_DATA(i) + NBUCKETS)
+
+PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
+
+unsigned pa_idxset_string_hash_func(const void *p) {
+    unsigned hash = 0;
+    const char *c;
+
+    for (c = p; *c; c++)
+        hash = 31 * hash + (unsigned) *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 PA_PTR_TO_UINT(p);
+}
+
+int pa_idxset_trivial_compare_func(const void *a, const void *b) {
+    return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
+    pa_idxset *s;
+
+    s = pa_xmalloc0(PA_ALIGN(sizeof(pa_idxset)) + NBUCKETS*2*sizeof(struct idxset_entry*));
+
+    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->current_index = 0;
+    s->n_entries = 0;
+    s->iterate_list_head = s->iterate_list_tail = NULL;
+
+    return s;
+}
+
+static void remove_entry(pa_idxset *s, struct idxset_entry *e) {
+    pa_assert(s);
+    pa_assert(e);
+
+    /* Remove from iteration linked list */
+    if (e->iterate_next)
+        e->iterate_next->iterate_previous = e->iterate_previous;
+    else
+        s->iterate_list_tail = e->iterate_previous;
+
+    if (e->iterate_previous)
+        e->iterate_previous->iterate_next = e->iterate_next;
+    else
+        s->iterate_list_head = e->iterate_next;
+
+    /* Remove from data hash table */
+    if (e->data_next)
+        e->data_next->data_previous = e->data_previous;
+
+    if (e->data_previous)
+        e->data_previous->data_next = e->data_next;
+    else {
+        unsigned hash = s->hash_func(e->data) % NBUCKETS;
+        BY_DATA(s)[hash] = e->data_next;
+    }
+
+    /* Remove from index hash table */
+    if (e->index_next)
+        e->index_next->index_previous = e->index_previous;
+
+    if (e->index_previous)
+        e->index_previous->index_next = e->index_next;
+    else
+        BY_INDEX(s)[e->idx % NBUCKETS] = e->index_next;
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+        pa_xfree(e);
+
+    pa_assert(s->n_entries >= 1);
+    s->n_entries--;
+}
+
+void pa_idxset_free(pa_idxset *s, pa_free_cb_t free_cb) {
+    pa_assert(s);
+
+    pa_idxset_remove_all(s, free_cb);
+    pa_xfree(s);
+}
+
+static struct idxset_entry* data_scan(pa_idxset *s, unsigned hash, const void *p) {
+    struct idxset_entry *e;
+    pa_assert(s);
+    pa_assert(hash < NBUCKETS);
+    pa_assert(p);
+
+    for (e = BY_DATA(s)[hash]; e; e = e->data_next)
+        if (s->compare_func(e->data, p) == 0)
+            return e;
+
+    return NULL;
+}
+
+static struct idxset_entry* index_scan(pa_idxset *s, unsigned hash, uint32_t idx) {
+    struct idxset_entry *e;
+    pa_assert(s);
+    pa_assert(hash < NBUCKETS);
+
+    for (e = BY_INDEX(s)[hash]; e; e = e->index_next)
+        if (e->idx == idx)
+            return e;
+
+    return NULL;
+}
+
+int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
+    unsigned hash;
+    struct idxset_entry *e;
+
+    pa_assert(s);
+
+    hash = s->hash_func(p) % NBUCKETS;
+
+    if ((e = data_scan(s, hash, p))) {
+        if (idx)
+            *idx = e->idx;
+
+        return -1;
+    }
+
+    if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
+        e = pa_xnew(struct idxset_entry, 1);
+
+    e->data = p;
+    e->idx = s->current_index++;
+
+    /* Insert into data hash table */
+    e->data_next = BY_DATA(s)[hash];
+    e->data_previous = NULL;
+    if (BY_DATA(s)[hash])
+        BY_DATA(s)[hash]->data_previous = e;
+    BY_DATA(s)[hash] = e;
+
+    hash = e->idx % NBUCKETS;
+
+    /* Insert into index hash table */
+    e->index_next = BY_INDEX(s)[hash];
+    e->index_previous = NULL;
+    if (BY_INDEX(s)[hash])
+        BY_INDEX(s)[hash]->index_previous = e;
+    BY_INDEX(s)[hash] = e;
+
+    /* Insert into iteration list */
+    e->iterate_previous = s->iterate_list_tail;
+    e->iterate_next = NULL;
+    if (s->iterate_list_tail) {
+        pa_assert(s->iterate_list_head);
+        s->iterate_list_tail->iterate_next = e;
+    } else {
+        pa_assert(!s->iterate_list_head);
+        s->iterate_list_head = e;
+    }
+    s->iterate_list_tail = e;
+
+    s->n_entries++;
+    pa_assert(s->n_entries >= 1);
+
+    if (idx)
+        *idx = e->idx;
+
+    return 0;
+}
+
+void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
+    unsigned hash;
+    struct idxset_entry *e;
+
+    pa_assert(s);
+
+    hash = idx % NBUCKETS;
+
+    if (!(e = index_scan(s, hash, idx)))
+        return NULL;
+
+    return e->data;
+}
+
+void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
+    unsigned hash;
+    struct idxset_entry *e;
+
+    pa_assert(s);
+
+    hash = s->hash_func(p) % NBUCKETS;
+
+    if (!(e = data_scan(s, hash, p)))
+        return NULL;
+
+    if (idx)
+        *idx = e->idx;
+
+    return e->data;
+}
+
+void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
+    struct idxset_entry *e;
+    unsigned hash;
+    void *data;
+
+    pa_assert(s);
+
+    hash = idx % NBUCKETS;
+
+    if (!(e = index_scan(s, hash, idx)))
+        return NULL;
+
+    data = e->data;
+    remove_entry(s, e);
+
+    return data;
+}
+
+void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
+    struct idxset_entry *e;
+    unsigned hash;
+    void *r;
+
+    pa_assert(s);
+
+    hash = s->hash_func(data) % NBUCKETS;
+
+    if (!(e = data_scan(s, hash, data)))
+        return NULL;
+
+    r = e->data;
+
+    if (idx)
+        *idx = e->idx;
+
+    remove_entry(s, e);
+
+    return r;
+}
+
+void pa_idxset_remove_all(pa_idxset *s, pa_free_cb_t free_cb) {
+    pa_assert(s);
+
+    while (s->iterate_list_head) {
+        void *data = s->iterate_list_head->data;
+
+        remove_entry(s, s->iterate_list_head);
+
+        if (free_cb)
+            free_cb(data);
+    }
+}
+
+void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
+    unsigned hash;
+    struct idxset_entry *e;
+
+    pa_assert(s);
+    pa_assert(idx);
+
+    hash = *idx % NBUCKETS;
+
+    e = index_scan(s, hash, *idx);
+
+    if (e && e->iterate_next)
+        e = e->iterate_next;
+    else
+        e = s->iterate_list_head;
+
+    if (!e)
+        return NULL;
+
+    *idx = e->idx;
+    return e->data;
+}
+
+void *pa_idxset_iterate(pa_idxset *s, void **state, uint32_t *idx) {
+    struct idxset_entry *e;
+
+    pa_assert(s);
+    pa_assert(state);
+
+    if (*state == (void*) -1)
+        goto at_end;
+
+    if ((!*state && !s->iterate_list_head))
+        goto at_end;
+
+    e = *state ? *state : s->iterate_list_head;
+
+    if (e->iterate_next)
+        *state = e->iterate_next;
+    else
+        *state = (void*) -1;
+
+    if (idx)
+        *idx = e->idx;
+
+    return e->data;
+
+at_end:
+    *state = (void *) -1;
+
+    if (idx)
+        *idx = PA_IDXSET_INVALID;
+
+    return NULL;
+}
+
+void* pa_idxset_steal_first(pa_idxset *s, uint32_t *idx) {
+    void *data;
+
+    pa_assert(s);
+
+    if (!s->iterate_list_head)
+        return NULL;
+
+    data = s->iterate_list_head->data;
+
+    if (idx)
+        *idx = s->iterate_list_head->idx;
+
+    remove_entry(s, s->iterate_list_head);
+
+    return data;
+}
+
+void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
+    pa_assert(s);
+
+    if (!s->iterate_list_head) {
+        if (idx)
+            *idx = PA_IDXSET_INVALID;
+        return NULL;
+    }
+
+    if (idx)
+        *idx = s->iterate_list_head->idx;
+
+    return s->iterate_list_head->data;
+}
+
+void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
+    struct idxset_entry *e;
+    unsigned hash;
+
+    pa_assert(s);
+    pa_assert(idx);
+
+    if (*idx == PA_IDXSET_INVALID)
+        return NULL;
+
+    hash = *idx % NBUCKETS;
+
+    if ((e = index_scan(s, hash, *idx))) {
+
+        e = e->iterate_next;
+
+        if (e) {
+            *idx = e->idx;
+            return e->data;
+        } else {
+            *idx = PA_IDXSET_INVALID;
+            return NULL;
+        }
+
+    } else {
+
+        /* If the entry passed doesn't exist anymore we try to find
+         * the next following */
+
+        for ((*idx)++; *idx < s->current_index; (*idx)++) {
+
+            hash = *idx % NBUCKETS;
+
+            if ((e = index_scan(s, hash, *idx))) {
+                *idx = e->idx;
+                return e->data;
+            }
+        }
+
+        *idx = PA_IDXSET_INVALID;
+        return NULL;
+    }
+}
+
+unsigned pa_idxset_size(pa_idxset*s) {
+    pa_assert(s);
+
+    return s->n_entries;
+}
+
+bool pa_idxset_isempty(pa_idxset *s) {
+    pa_assert(s);
+
+    return s->n_entries == 0;
+}
+
+pa_idxset *pa_idxset_copy(pa_idxset *s, pa_copy_func_t copy_func) {
+    pa_idxset *copy;
+    struct idxset_entry *i;
+
+    pa_assert(s);
+
+    copy = pa_idxset_new(s->hash_func, s->compare_func);
+
+    for (i = s->iterate_list_head; i; i = i->iterate_next)
+        pa_idxset_put(copy, copy_func ? copy_func(i->data) : i->data, NULL);
+
+    return copy;
+}
diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h
new file mode 100644 (file)
index 0000000..7acb202
--- /dev/null
@@ -0,0 +1,116 @@
+#ifndef foopulsecoreidxsethfoo
+#define foopulsecoreidxsethfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/def.h>
+
+#include <pulsecore/macro.h>
+
+/* A combination of a set and a dynamic array. Entries are indexable
+ * both through an automatically generated numeric index and the
+ * entry's data pointer. As usual, memory management is the user's
+ * job. */
+
+/* A special index value denoting the invalid index. */
+#define PA_IDXSET_INVALID ((uint32_t) -1)
+
+/* Generic implementations for hash and comparison functions. Just
+ * compares the pointer or calculates the hash value directly from the
+ * pointer value. */
+unsigned pa_idxset_trivial_hash_func(const void *p);
+int pa_idxset_trivial_compare_func(const void *a, const void *b);
+
+/* Generic implementations for hash and comparison functions for strings. */
+unsigned pa_idxset_string_hash_func(const void *p);
+int pa_idxset_string_compare_func(const void *a, const void *b);
+
+typedef unsigned (*pa_hash_func_t)(const void *p);
+typedef int (*pa_compare_func_t)(const void *a, const void *b);
+typedef void *(*pa_copy_func_t)(const void *p);
+
+typedef struct pa_idxset pa_idxset;
+
+/* Instantiate a new idxset with the specified hash and comparison functions */
+pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func);
+
+/* Free the idxset. When the idxset is not empty the specified function is called for every entry contained */
+void pa_idxset_free(pa_idxset *s, pa_free_cb_t free_cb);
+
+/* Store a new item in the idxset. The index of the item is returned in *idx */
+int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx);
+
+/* Get the entry by its idx */
+void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx);
+
+/* Get the entry by its data. The index is returned in *idx */
+void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx);
+
+/* Similar to pa_idxset_get_by_index(), but removes the entry from the idxset. */
+void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx);
+
+/* Similar to pa_idxset_get_by_data(), but removes the entry from the idxset */
+void* pa_idxset_remove_by_data(pa_idxset*s, const void *p, uint32_t *idx);
+
+/* If free_cb is not NULL, it's called for each entry. */
+void pa_idxset_remove_all(pa_idxset *s, pa_free_cb_t free_cb);
+
+/* This may be used to iterate through all entries. When called with
+   an invalid index value it returns the first entry, otherwise the
+   next following. The function is best called with *idx =
+   PA_IDXSET_VALID first. It is safe to manipulate the idxset between
+   the calls. It is not guaranteed that all entries have already been
+   returned before the an entry is returned the second time.*/
+void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx);
+
+/* Iterate through the idxset. At first iteration state should be NULL */
+void *pa_idxset_iterate(pa_idxset *s, void **state, uint32_t *idx);
+
+/* Return the oldest entry in the idxset and remove it. If idx is not NULL fill in its index in *idx */
+void* pa_idxset_steal_first(pa_idxset *s, uint32_t *idx);
+
+/* Return the oldest entry in the idxset. Fill in its index in *idx. */
+void* pa_idxset_first(pa_idxset *s, uint32_t *idx);
+
+/* Return the entry following the entry indexed by *idx.  After the
+ * call *index contains the index of the returned
+ * object. pa_idxset_first() and pa_idxset_next() may be used to
+ * iterate through the set.*/
+void *pa_idxset_next(pa_idxset *s, uint32_t *idx);
+
+/* Return the current number of entries in the idxset */
+unsigned pa_idxset_size(pa_idxset*s);
+
+/* Return true of the idxset is empty */
+bool pa_idxset_isempty(pa_idxset *s);
+
+/* Duplicate the idxset. This will not copy the actual indexes. If copy_func is
+ * set, each entry is copied using the provided function, otherwise a shallow
+ * copy will be made. */
+pa_idxset *pa_idxset_copy(pa_idxset *s, pa_copy_func_t copy_func);
+
+/* A macro to ease iteration through all entries */
+#define PA_IDXSET_FOREACH(e, s, idx) \
+    for ((e) = pa_idxset_first((s), &(idx)); (e); (e) = pa_idxset_next((s), &(idx)))
+
+#endif
diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
new file mode 100644 (file)
index 0000000..8973375
--- /dev/null
@@ -0,0 +1,541 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "iochannel.h"
+
+struct pa_iochannel {
+    int ifd, ofd;
+    int ifd_type, ofd_type;
+    pa_mainloop_api* mainloop;
+
+    pa_iochannel_cb_t callback;
+    void*userdata;
+
+    bool readable:1;
+    bool writable:1;
+    bool hungup:1;
+    bool no_close:1;
+
+    pa_io_event* input_event, *output_event;
+};
+
+static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
+
+static void delete_events(pa_iochannel *io) {
+    pa_assert(io);
+
+    if (io->input_event)
+        io->mainloop->io_free(io->input_event);
+
+    if (io->output_event && io->output_event != io->input_event)
+        io->mainloop->io_free(io->output_event);
+
+    io->input_event = io->output_event = NULL;
+}
+
+static void enable_events(pa_iochannel *io) {
+    pa_assert(io);
+
+    if (io->hungup) {
+        delete_events(io);
+        return;
+    }
+
+    if (io->ifd == io->ofd && io->ifd >= 0) {
+        pa_io_event_flags_t f = PA_IO_EVENT_NULL;
+
+        if (!io->readable)
+            f |= PA_IO_EVENT_INPUT;
+        if (!io->writable)
+            f |= PA_IO_EVENT_OUTPUT;
+
+        pa_assert(io->input_event == io->output_event);
+
+        if (f != PA_IO_EVENT_NULL) {
+            if (io->input_event)
+                io->mainloop->io_enable(io->input_event, f);
+            else
+                io->input_event = io->output_event = io->mainloop->io_new(io->mainloop, io->ifd, f, callback, io);
+        } else
+            delete_events(io);
+
+    } else {
+
+        if (io->ifd >= 0) {
+            if (!io->readable) {
+                if (io->input_event)
+                    io->mainloop->io_enable(io->input_event, PA_IO_EVENT_INPUT);
+                else
+                    io->input_event = io->mainloop->io_new(io->mainloop, io->ifd, PA_IO_EVENT_INPUT, callback, io);
+            } else if (io->input_event) {
+                io->mainloop->io_free(io->input_event);
+                io->input_event = NULL;
+            }
+        }
+
+        if (io->ofd >= 0) {
+            if (!io->writable) {
+                if (io->output_event)
+                    io->mainloop->io_enable(io->output_event, PA_IO_EVENT_OUTPUT);
+                else
+                    io->output_event = io->mainloop->io_new(io->mainloop, io->ofd, PA_IO_EVENT_OUTPUT, callback, io);
+            } else if (io->output_event) {
+                io->mainloop->io_free(io->output_event);
+                io->output_event = NULL;
+            }
+        }
+    }
+}
+
+static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    pa_iochannel *io = userdata;
+    bool changed = false;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(fd >= 0);
+    pa_assert(userdata);
+
+    if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
+        io->hungup = true;
+        changed = true;
+    }
+
+    if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
+        io->readable = true;
+        changed = true;
+        pa_assert(e == io->input_event);
+    }
+
+    if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
+        io->writable = true;
+        changed = true;
+        pa_assert(e == io->output_event);
+    }
+
+    if (changed) {
+        enable_events(io);
+
+        if (io->callback)
+            io->callback(io, io->userdata);
+    }
+}
+
+pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
+    pa_iochannel *io;
+
+    pa_assert(m);
+    pa_assert(ifd >= 0 || ofd >= 0);
+
+    io = pa_xnew0(pa_iochannel, 1);
+    io->ifd = ifd;
+    io->ofd = ofd;
+    io->mainloop = m;
+
+    if (io->ifd >= 0)
+        pa_make_fd_nonblock(io->ifd);
+
+    if (io->ofd >= 0 && io->ofd != io->ifd)
+        pa_make_fd_nonblock(io->ofd);
+
+    enable_events(io);
+    return io;
+}
+
+void pa_iochannel_free(pa_iochannel*io) {
+    pa_assert(io);
+
+    delete_events(io);
+
+    if (!io->no_close) {
+        if (io->ifd >= 0)
+            pa_close(io->ifd);
+        if (io->ofd >= 0 && io->ofd != io->ifd)
+            pa_close(io->ofd);
+    }
+
+    pa_xfree(io);
+}
+
+bool pa_iochannel_is_readable(pa_iochannel*io) {
+    pa_assert(io);
+
+    return io->readable || io->hungup;
+}
+
+bool pa_iochannel_is_writable(pa_iochannel*io) {
+    pa_assert(io);
+
+    return io->writable && !io->hungup;
+}
+
+bool pa_iochannel_is_hungup(pa_iochannel*io) {
+    pa_assert(io);
+
+    return io->hungup;
+}
+
+ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
+    ssize_t r;
+
+    pa_assert(io);
+    pa_assert(data);
+    pa_assert(l);
+    pa_assert(io->ofd >= 0);
+
+    r = pa_write(io->ofd, data, l, &io->ofd_type);
+
+    if ((size_t) r == l)
+        return r; /* Fast path - we almost always successfully write everything */
+
+    if (r < 0) {
+        if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
+            r = 0;
+        else
+            return r;
+    }
+
+    /* Partial write - let's get a notification when we can write more */
+    io->writable = io->hungup = false;
+    enable_events(io);
+
+    return r;
+}
+
+ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
+    ssize_t r;
+
+    pa_assert(io);
+    pa_assert(data);
+    pa_assert(io->ifd >= 0);
+
+    if ((r = pa_read(io->ifd, data, l, &io->ifd_type)) >= 0) {
+
+        /* We also reset the hangup flag here to ensure that another
+         * IO callback is triggered so that we will again call into
+         * user code */
+        io->readable = io->hungup = false;
+        enable_events(io);
+    }
+
+    return r;
+}
+
+#ifdef HAVE_CREDS
+
+bool pa_iochannel_creds_supported(pa_iochannel *io) {
+    struct {
+        struct sockaddr sa;
+#ifdef HAVE_SYS_UN_H
+        struct sockaddr_un un;
+#endif
+        struct sockaddr_storage storage;
+    } sa;
+
+    socklen_t l;
+
+    pa_assert(io);
+    pa_assert(io->ifd >= 0);
+    pa_assert(io->ofd == io->ifd);
+
+    l = sizeof(sa);
+    if (getsockname(io->ifd, &sa.sa, &l) < 0)
+        return false;
+
+    return sa.sa.sa_family == AF_UNIX;
+}
+
+int pa_iochannel_creds_enable(pa_iochannel *io) {
+    int t = 1;
+
+    pa_assert(io);
+    pa_assert(io->ifd >= 0);
+
+    if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) {
+        pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l, const pa_creds *ucred) {
+    ssize_t r;
+    struct msghdr mh;
+    struct iovec iov;
+    union {
+        struct cmsghdr hdr;
+        uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
+    } cmsg;
+    struct ucred *u;
+
+    pa_assert(io);
+    pa_assert(data);
+    pa_assert(l);
+    pa_assert(io->ofd >= 0);
+
+    pa_zero(iov);
+    iov.iov_base = (void*) data;
+    iov.iov_len = l;
+
+    pa_zero(cmsg);
+    cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
+    cmsg.hdr.cmsg_level = SOL_SOCKET;
+    cmsg.hdr.cmsg_type = SCM_CREDENTIALS;
+
+    u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
+
+    u->pid = getpid();
+    if (ucred) {
+        u->uid = ucred->uid;
+        u->gid = ucred->gid;
+    } else {
+        u->uid = getuid();
+        u->gid = getgid();
+    }
+
+    pa_zero(mh);
+    mh.msg_iov = &iov;
+    mh.msg_iovlen = 1;
+    mh.msg_control = &cmsg;
+    mh.msg_controllen = sizeof(cmsg);
+
+    if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
+        io->writable = io->hungup = false;
+        enable_events(io);
+    }
+
+    return r;
+}
+
+/* For more details on FD passing, check the cmsg(3) manpage
+ * and IETF RFC #2292: "Advanced Sockets API for IPv6" */
+ssize_t pa_iochannel_write_with_fds(pa_iochannel*io, const void*data, size_t l, int nfd, const int *fds) {
+    ssize_t r;
+    int *msgdata;
+    struct msghdr mh;
+    struct iovec iov;
+    union {
+        struct cmsghdr hdr;
+        uint8_t data[CMSG_SPACE(sizeof(int) * MAX_ANCIL_DATA_FDS)];
+    } cmsg;
+
+    pa_assert(io);
+    pa_assert(data);
+    pa_assert(l);
+    pa_assert(io->ofd >= 0);
+    pa_assert(fds);
+    pa_assert(nfd > 0);
+    pa_assert(nfd <= MAX_ANCIL_DATA_FDS);
+
+    pa_zero(iov);
+    iov.iov_base = (void*) data;
+    iov.iov_len = l;
+
+    pa_zero(cmsg);
+    cmsg.hdr.cmsg_level = SOL_SOCKET;
+    cmsg.hdr.cmsg_type = SCM_RIGHTS;
+
+    msgdata = (int*) CMSG_DATA(&cmsg.hdr);
+    memcpy(msgdata, fds, nfd * sizeof(int));
+    cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int) * nfd);
+
+    pa_zero(mh);
+    mh.msg_iov = &iov;
+    mh.msg_iovlen = 1;
+    mh.msg_control = &cmsg;
+
+    /* If we followed the example on the cmsg man page, we'd use
+     * sizeof(cmsg.data) here, but if nfd < MAX_ANCIL_DATA_FDS, then the data
+     * buffer is larger than needed, and the kernel doesn't like it if we set
+     * msg_controllen to a larger than necessary value. The commit message for
+     * commit 451d1d6762 contains a longer explanation. */
+    mh.msg_controllen = CMSG_SPACE(sizeof(int) * nfd);
+
+    if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
+        io->writable = io->hungup = false;
+        enable_events(io);
+    }
+    return r;
+}
+
+ssize_t pa_iochannel_read_with_ancil_data(pa_iochannel*io, void*data, size_t l, pa_cmsg_ancil_data *ancil_data) {
+    ssize_t r;
+    struct msghdr mh;
+    struct iovec iov;
+    union {
+        struct cmsghdr hdr;
+        uint8_t data[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int) * MAX_ANCIL_DATA_FDS)];
+    } cmsg;
+
+    pa_assert(io);
+    pa_assert(data);
+    pa_assert(l);
+    pa_assert(io->ifd >= 0);
+    pa_assert(ancil_data);
+
+    if (io->ifd_type > 0) {
+        ancil_data->creds_valid = false;
+        ancil_data->nfd = 0;
+        return pa_iochannel_read(io, data, l);
+    }
+
+    iov.iov_base = data;
+    iov.iov_len = l;
+
+    pa_zero(mh);
+    mh.msg_iov = &iov;
+    mh.msg_iovlen = 1;
+    mh.msg_control = &cmsg;
+    mh.msg_controllen = sizeof(cmsg);
+
+    if ((r = recvmsg(io->ifd, &mh, 0)) >= 0) {
+        struct cmsghdr *cmh;
+
+        ancil_data->creds_valid = false;
+        ancil_data->nfd = 0;
+
+        for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) {
+
+            if (cmh->cmsg_level != SOL_SOCKET)
+                continue;
+
+            if (cmh->cmsg_type == SCM_CREDENTIALS) {
+                struct ucred u;
+                pa_assert(cmh->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
+                memcpy(&u, CMSG_DATA(cmh), sizeof(struct ucred));
+
+                ancil_data->creds.gid = u.gid;
+                ancil_data->creds.uid = u.uid;
+                ancil_data->creds_valid = true;
+            }
+            else if (cmh->cmsg_type == SCM_RIGHTS) {
+                int nfd = (cmh->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+                if (nfd > MAX_ANCIL_DATA_FDS) {
+                    int i;
+                    pa_log("Trying to receive too many file descriptors!");
+                    for (i = 0; i < nfd; i++)
+                        pa_close(((int*) CMSG_DATA(cmh))[i]);
+                    continue;
+                }
+                memcpy(ancil_data->fds, CMSG_DATA(cmh), nfd * sizeof(int));
+                ancil_data->nfd = nfd;
+                ancil_data->close_fds_on_cleanup = true;
+            }
+        }
+
+        io->readable = io->hungup = false;
+        enable_events(io);
+    }
+
+    if (r == -1 && errno == ENOTSOCK) {
+        io->ifd_type = 1;
+        return pa_iochannel_read_with_ancil_data(io, data, l, ancil_data);
+    }
+
+    return r;
+}
+
+#endif /* HAVE_CREDS */
+
+void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) {
+    pa_assert(io);
+
+    io->callback = _callback;
+    io->userdata = userdata;
+}
+
+void pa_iochannel_set_noclose(pa_iochannel*io, bool b) {
+    pa_assert(io);
+
+    io->no_close = b;
+}
+
+void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) {
+    pa_assert(io);
+    pa_assert(s);
+    pa_assert(l);
+
+    pa_socket_peer_to_string(io->ifd, s, l);
+}
+
+int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) {
+    pa_assert(io);
+
+    return pa_socket_set_rcvbuf(io->ifd, l);
+}
+
+int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) {
+    pa_assert(io);
+
+    return pa_socket_set_sndbuf(io->ofd, l);
+}
+
+pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) {
+    pa_assert(io);
+
+    return io->mainloop;
+}
+
+int pa_iochannel_get_recv_fd(pa_iochannel *io) {
+    pa_assert(io);
+
+    return io->ifd;
+}
+
+int pa_iochannel_get_send_fd(pa_iochannel *io) {
+    pa_assert(io);
+
+    return io->ofd;
+}
+
+bool pa_iochannel_socket_is_local(pa_iochannel *io) {
+    pa_assert(io);
+
+    if (pa_socket_is_local(io->ifd))
+        return true;
+
+    if (io->ifd != io->ofd)
+        if (pa_socket_is_local(io->ofd))
+            return true;
+
+    return false;
+}
diff --git a/src/pulsecore/iochannel.h b/src/pulsecore/iochannel.h
new file mode 100644 (file)
index 0000000..c81c5e3
--- /dev/null
@@ -0,0 +1,89 @@
+#ifndef fooiochannelhfoo
+#define fooiochannelhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#include <sys/types.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
+
+/* A wrapper around UNIX file descriptors for attaching them to the a
+   main event loop. Every time new data may be read or be written to
+   the channel a callback function is called. It is safe to destroy
+   the calling iochannel object from the callback */
+
+typedef struct pa_iochannel pa_iochannel;
+
+/* Create a new IO channel for the specified file descriptors for
+input resp. output. It is safe to pass the same file descriptor for
+both parameters (in case of full-duplex channels). For a simplex
+channel specify -1 for the other direction. */
+
+pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd);
+void pa_iochannel_free(pa_iochannel*io);
+
+/* Returns: length written on success, 0 if a retry is needed, negative value
+ * on error. */
+ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l);
+ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l);
+
+#ifdef HAVE_CREDS
+bool pa_iochannel_creds_supported(pa_iochannel *io);
+int pa_iochannel_creds_enable(pa_iochannel *io);
+
+ssize_t pa_iochannel_write_with_fds(pa_iochannel*io, const void*data, size_t l, int nfd, const int *fds);
+ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l, const pa_creds *ucred);
+ssize_t pa_iochannel_read_with_ancil_data(pa_iochannel*io, void*data, size_t l, pa_cmsg_ancil_data *ancil_data);
+#endif
+
+bool pa_iochannel_is_readable(pa_iochannel*io);
+bool pa_iochannel_is_writable(pa_iochannel*io);
+bool pa_iochannel_is_hungup(pa_iochannel*io);
+
+/* Don't close the file descriptors when the io channel is freed. By
+ * default the file descriptors are closed. */
+void pa_iochannel_set_noclose(pa_iochannel*io, bool b);
+
+/* Set the callback function that is called whenever data becomes available for read or write */
+typedef void (*pa_iochannel_cb_t)(pa_iochannel*io, void *userdata);
+void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t callback, void *userdata);
+
+/* In case the file descriptor is a socket, return a pretty-printed string in *s which describes the peer connected */
+void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l);
+
+/* Use setsockopt() to tune the receive and send buffers of TCP sockets */
+int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l);
+int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l);
+
+bool pa_iochannel_socket_is_local(pa_iochannel *io);
+
+pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io);
+
+int pa_iochannel_get_recv_fd(pa_iochannel *io);
+int pa_iochannel_get_send_fd(pa_iochannel *io);
+
+#endif
diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c
new file mode 100644 (file)
index 0000000..dfc5a73
--- /dev/null
@@ -0,0 +1,463 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/socket.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+
+#include "ioline.h"
+
+#define BUFFER_LIMIT (64*1024)
+#define READ_SIZE (1024)
+
+struct pa_ioline {
+    PA_REFCNT_DECLARE;
+
+    pa_iochannel *io;
+    pa_defer_event *defer_event;
+    pa_mainloop_api *mainloop;
+
+    char *wbuf;
+    size_t wbuf_length, wbuf_index, wbuf_valid_length;
+
+    char *rbuf;
+    size_t rbuf_length, rbuf_index, rbuf_valid_length;
+
+    pa_ioline_cb_t callback;
+    void *userdata;
+
+    pa_ioline_drain_cb_t drain_callback;
+    void *drain_userdata;
+
+    bool dead:1;
+    bool defer_close:1;
+};
+
+static void io_callback(pa_iochannel*io, void *userdata);
+static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata);
+
+pa_ioline* pa_ioline_new(pa_iochannel *io) {
+    pa_ioline *l;
+    pa_assert(io);
+
+    l = pa_xnew(pa_ioline, 1);
+    PA_REFCNT_INIT(l);
+    l->io = io;
+
+    l->wbuf = NULL;
+    l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
+
+    l->rbuf = NULL;
+    l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0;
+
+    l->callback = NULL;
+    l->userdata = NULL;
+
+    l->drain_callback = NULL;
+    l->drain_userdata = NULL;
+
+    l->mainloop = pa_iochannel_get_mainloop_api(io);
+
+    l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
+    l->mainloop->defer_enable(l->defer_event, 0);
+
+    l->dead = false;
+    l->defer_close = false;
+
+    pa_iochannel_set_callback(io, io_callback, l);
+
+    return l;
+}
+
+static void ioline_free(pa_ioline *l) {
+    pa_assert(l);
+
+    if (l->io)
+        pa_iochannel_free(l->io);
+
+    if (l->defer_event)
+        l->mainloop->defer_free(l->defer_event);
+
+    pa_xfree(l->wbuf);
+    pa_xfree(l->rbuf);
+    pa_xfree(l);
+}
+
+void pa_ioline_unref(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    if (PA_REFCNT_DEC(l) <= 0)
+        ioline_free(l);
+}
+
+pa_ioline* pa_ioline_ref(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    PA_REFCNT_INC(l);
+    return l;
+}
+
+void pa_ioline_close(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    l->dead = true;
+
+    if (l->io) {
+        pa_iochannel_free(l->io);
+        l->io = NULL;
+    }
+
+    if (l->defer_event) {
+        l->mainloop->defer_free(l->defer_event);
+        l->defer_event = NULL;
+    }
+
+    if (l->callback)
+        l->callback = NULL;
+}
+
+void pa_ioline_puts(pa_ioline *l, const char *c) {
+    size_t len;
+
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+    pa_assert(c);
+
+    if (l->dead)
+        return;
+
+    len = strlen(c);
+    if (len > BUFFER_LIMIT - l->wbuf_valid_length)
+        len = BUFFER_LIMIT - l->wbuf_valid_length;
+
+    if (len) {
+        pa_assert(l->wbuf_length >= l->wbuf_valid_length);
+
+        /* In case the allocated buffer is too small, enlarge it. */
+        if (l->wbuf_valid_length + len > l->wbuf_length) {
+            size_t n = l->wbuf_valid_length+len;
+            char *new = pa_xnew(char, (unsigned) n);
+
+            if (l->wbuf) {
+                memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
+                pa_xfree(l->wbuf);
+            }
+
+            l->wbuf = new;
+            l->wbuf_length = n;
+            l->wbuf_index = 0;
+        } else if (l->wbuf_index + l->wbuf_valid_length + len > l->wbuf_length) {
+
+            /* In case the allocated buffer fits, but the current index is too far from the start, move it to the front. */
+            memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
+            l->wbuf_index = 0;
+        }
+
+        pa_assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);
+
+        /* Append the new string */
+        memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len);
+        l->wbuf_valid_length += len;
+
+        l->mainloop->defer_enable(l->defer_event, 1);
+    }
+}
+
+void pa_ioline_set_callback(pa_ioline*l, pa_ioline_cb_t callback, void *userdata) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    if (l->dead)
+        return;
+
+    l->callback = callback;
+    l->userdata = userdata;
+}
+
+void pa_ioline_set_drain_callback(pa_ioline*l, pa_ioline_drain_cb_t callback, void *userdata) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    if (l->dead)
+        return;
+
+    l->drain_callback = callback;
+    l->drain_userdata = userdata;
+}
+
+static void failure(pa_ioline *l, bool process_leftover) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+    pa_assert(!l->dead);
+
+    if (process_leftover && l->rbuf_valid_length > 0) {
+        /* Pass the last missing bit to the client */
+
+        if (l->callback) {
+            char *p = pa_xstrndup(l->rbuf+l->rbuf_index, l->rbuf_valid_length);
+            l->callback(l, p, l->userdata);
+            pa_xfree(p);
+        }
+    }
+
+    if (l->callback) {
+        l->callback(l, NULL, l->userdata);
+        l->callback = NULL;
+    }
+
+    pa_ioline_close(l);
+}
+
+static void scan_for_lines(pa_ioline *l, size_t skip) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+    pa_assert(skip < l->rbuf_valid_length);
+
+    while (!l->dead && l->rbuf_valid_length > skip) {
+        char *e, *p;
+        size_t m;
+
+        if (!(e = memchr(l->rbuf + l->rbuf_index + skip, '\n', l->rbuf_valid_length - skip)))
+            break;
+
+        *e = 0;
+
+        p = l->rbuf + l->rbuf_index;
+        m = strlen(p);
+
+        l->rbuf_index += m+1;
+        l->rbuf_valid_length -= m+1;
+
+        /* A shortcut for the next time */
+        if (l->rbuf_valid_length == 0)
+            l->rbuf_index = 0;
+
+        if (l->callback)
+            l->callback(l, pa_strip_nl(p), l->userdata);
+
+        skip = 0;
+    }
+
+    /* If the buffer became too large and still no newline was found, drop it. */
+    if (l->rbuf_valid_length >= BUFFER_LIMIT)
+        l->rbuf_index = l->rbuf_valid_length = 0;
+}
+
+static int do_write(pa_ioline *l);
+
+static int do_read(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    while (l->io && !l->dead && pa_iochannel_is_readable(l->io)) {
+        ssize_t r;
+        size_t len;
+
+        len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
+
+        /* Check if we have to enlarge the read buffer */
+        if (len < READ_SIZE) {
+            size_t n = l->rbuf_valid_length+READ_SIZE;
+
+            if (n >= BUFFER_LIMIT)
+                n = BUFFER_LIMIT;
+
+            if (l->rbuf_length >= n) {
+                /* The current buffer is large enough, let's just move the data to the front */
+                if (l->rbuf_valid_length)
+                    memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
+            } else {
+                /* Enlarge the buffer */
+                char *new = pa_xnew(char, (unsigned) n);
+                if (l->rbuf_valid_length)
+                    memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
+                pa_xfree(l->rbuf);
+                l->rbuf = new;
+                l->rbuf_length = n;
+            }
+
+            l->rbuf_index = 0;
+        }
+
+        len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
+
+        pa_assert(len >= READ_SIZE);
+
+        /* Read some data */
+        if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) {
+
+            if (r < 0 && errno == EAGAIN)
+                return 0;
+
+            if (r < 0 && errno != ECONNRESET) {
+                pa_log("read(): %s", pa_cstrerror(errno));
+                failure(l, false);
+            } else
+                failure(l, true);
+
+            return -1;
+        }
+
+        l->rbuf_valid_length += (size_t) r;
+
+        /* Look if a line has been terminated in the newly read data */
+        scan_for_lines(l, l->rbuf_valid_length - (size_t) r);
+    }
+
+    return 0;
+}
+
+/* Try to flush the buffer */
+static int do_write(pa_ioline *l) {
+    ssize_t r;
+
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    while (l->io && !l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length > 0) {
+
+        if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) {
+
+            if (errno != EPIPE)
+                pa_log("write(): %s", pa_cstrerror(errno));
+
+            failure(l, false);
+
+            return -1;
+        }
+
+        l->wbuf_index += (size_t) r;
+        l->wbuf_valid_length -= (size_t) r;
+
+        /* A shortcut for the next time */
+        if (l->wbuf_valid_length == 0)
+            l->wbuf_index = 0;
+    }
+
+    if (l->wbuf_valid_length <= 0 && l->drain_callback)
+        l->drain_callback(l, l->drain_userdata);
+
+    return 0;
+}
+
+/* Try to flush read/write data */
+static void do_work(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    pa_ioline_ref(l);
+
+    l->mainloop->defer_enable(l->defer_event, 0);
+
+    if (!l->dead)
+        do_read(l);
+
+    if (!l->dead)
+        do_write(l);
+
+    if (l->defer_close && !l->wbuf_valid_length)
+        failure(l, true);
+
+    pa_ioline_unref(l);
+}
+
+static void io_callback(pa_iochannel*io, void *userdata) {
+    pa_ioline *l = userdata;
+
+    pa_assert(io);
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    do_work(l);
+}
+
+static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) {
+    pa_ioline *l = userdata;
+
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+    pa_assert(l->mainloop == m);
+    pa_assert(l->defer_event == e);
+
+    do_work(l);
+}
+
+void pa_ioline_defer_close(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    l->defer_close = true;
+
+    if (!l->wbuf_valid_length)
+        l->mainloop->defer_enable(l->defer_event, 1);
+}
+
+void pa_ioline_printf(pa_ioline *l, const char *format, ...) {
+    char *t;
+    va_list ap;
+
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    va_start(ap, format);
+    t = pa_vsprintf_malloc(format, ap);
+    va_end(ap);
+
+    pa_ioline_puts(l, t);
+    pa_xfree(t);
+}
+
+pa_iochannel* pa_ioline_detach_iochannel(pa_ioline *l) {
+    pa_iochannel *r;
+
+    pa_assert(l);
+
+    if (!l->io)
+        return NULL;
+
+    r = l->io;
+    l->io = NULL;
+
+    pa_iochannel_set_callback(r, NULL, NULL);
+
+    return r;
+}
+
+bool pa_ioline_is_drained(pa_ioline *l) {
+    pa_assert(l);
+
+    return l->wbuf_valid_length <= 0;
+}
diff --git a/src/pulsecore/ioline.h b/src/pulsecore/ioline.h
new file mode 100644 (file)
index 0000000..7b6dff3
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef fooiolinehfoo
+#define fooiolinehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/iochannel.h>
+
+/* An ioline wraps an iochannel for line based communication. A
+ * callback function is called whenever a new line has been received
+ * from the client */
+
+typedef struct pa_ioline pa_ioline;
+
+typedef void (*pa_ioline_cb_t)(pa_ioline*io, const char *s, void *userdata);
+typedef void (*pa_ioline_drain_cb_t)(pa_ioline *io, void *userdata);
+
+pa_ioline* pa_ioline_new(pa_iochannel *io);
+void pa_ioline_unref(pa_ioline *l);
+pa_ioline* pa_ioline_ref(pa_ioline *l);
+void pa_ioline_close(pa_ioline *l);
+
+/* Write a string to the channel */
+void pa_ioline_puts(pa_ioline *s, const char *c);
+
+/* Write a string to the channel */
+void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
+
+/* Set the callback function that is called for every received line */
+void pa_ioline_set_callback(pa_ioline*io, pa_ioline_cb_t callback, void *userdata);
+
+/* Set the callback function that is called when everything has been written */
+void pa_ioline_set_drain_callback(pa_ioline*io, pa_ioline_drain_cb_t callback, void *userdata);
+
+/* Make sure to close the ioline object as soon as the send buffer is emptied */
+void pa_ioline_defer_close(pa_ioline *io);
+
+/* Returns true when everything was written */
+bool pa_ioline_is_drained(pa_ioline *io);
+
+/* Detaches from the iochannel and returns it. Data that has already
+ * been read will not be available in the detached iochannel */
+pa_iochannel* pa_ioline_detach_iochannel(pa_ioline *l);
+
+#endif
diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c
new file mode 100644 (file)
index 0000000..e83d8a0
--- /dev/null
@@ -0,0 +1,238 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/types.h>
+#include <string.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/arpa-inet.h>
+
+#include "ipacl.h"
+
+struct acl_entry {
+    PA_LLIST_FIELDS(struct acl_entry);
+    int family;
+    struct in_addr address_ipv4;
+#ifdef HAVE_IPV6
+    struct in6_addr address_ipv6;
+#endif
+    int bits;
+};
+
+struct pa_ip_acl {
+    PA_LLIST_HEAD(struct acl_entry, entries);
+};
+
+pa_ip_acl* pa_ip_acl_new(const char *s) {
+    const char *state = NULL;
+    char *a;
+    pa_ip_acl *acl;
+
+    pa_assert(s);
+
+    acl = pa_xnew(pa_ip_acl, 1);
+    PA_LLIST_HEAD_INIT(struct acl_entry, acl->entries);
+
+    while ((a = pa_split(s, ";", &state))) {
+        char *slash;
+        struct acl_entry e, *n;
+        uint32_t bits;
+
+        if ((slash = strchr(a, '/'))) {
+            *slash = 0;
+            slash++;
+            if (pa_atou(slash, &bits) < 0) {
+                pa_log_warn("Failed to parse number of bits: %s", slash);
+                goto fail;
+            }
+        } else
+            bits = (uint32_t) -1;
+
+        if (inet_pton(AF_INET, a, &e.address_ipv4) > 0) {
+
+            e.bits = bits == (uint32_t) -1 ? 32 : (int) bits;
+
+            if (e.bits > 32) {
+                pa_log_warn("Number of bits out of range: %i", e.bits);
+                goto fail;
+            }
+
+            e.family = AF_INET;
+
+            if (e.bits < 32 && (uint32_t) (ntohl(e.address_ipv4.s_addr) << e.bits) != 0)
+                pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
+
+#ifdef HAVE_IPV6
+        } else if (inet_pton(AF_INET6, a, &e.address_ipv6) > 0) {
+
+            e.bits = bits == (uint32_t) -1 ? 128 : (int) bits;
+
+            if (e.bits > 128) {
+                pa_log_warn("Number of bits out of range: %i", e.bits);
+                goto fail;
+            }
+            e.family = AF_INET6;
+
+            if (e.bits < 128) {
+                int t = 0, i;
+
+                for (i = 0, bits = (uint32_t) e.bits; i < 16; i++) {
+
+                    if (bits >= 8)
+                        bits -= 8;
+                    else {
+                        if ((uint8_t) ((e.address_ipv6.s6_addr[i]) << bits) != 0) {
+                            t = 1;
+                            break;
+                        }
+                        bits = 0;
+                    }
+                }
+
+                if (t)
+                    pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
+            }
+#endif
+
+        } else {
+            pa_log_warn("Failed to parse address: %s", a);
+            goto fail;
+        }
+
+        n = pa_xmemdup(&e, sizeof(struct acl_entry));
+        PA_LLIST_PREPEND(struct acl_entry, acl->entries, n);
+
+        pa_xfree(a);
+    }
+
+    return acl;
+
+fail:
+    pa_xfree(a);
+    pa_ip_acl_free(acl);
+
+    return NULL;
+}
+
+void pa_ip_acl_free(pa_ip_acl *acl) {
+    pa_assert(acl);
+
+    while (acl->entries) {
+        struct acl_entry *e = acl->entries;
+        PA_LLIST_REMOVE(struct acl_entry, acl->entries, e);
+        pa_xfree(e);
+    }
+
+    pa_xfree(acl);
+}
+
+int pa_ip_acl_check(pa_ip_acl *acl, int fd) {
+    struct sockaddr_storage sa;
+    struct acl_entry *e;
+    socklen_t salen;
+
+    pa_assert(acl);
+    pa_assert(fd >= 0);
+
+    salen = sizeof(sa);
+    if (getpeername(fd, (struct sockaddr*) &sa, &salen) < 0)
+        return -1;
+
+#ifdef HAVE_IPV6
+    if (sa.ss_family != AF_INET && sa.ss_family != AF_INET6)
+#else
+    if (sa.ss_family != AF_INET)
+#endif
+        return -1;
+
+    if (sa.ss_family == AF_INET && salen != sizeof(struct sockaddr_in))
+        return -1;
+
+#ifdef HAVE_IPV6
+    if (sa.ss_family == AF_INET6 && salen != sizeof(struct sockaddr_in6))
+        return -1;
+#endif
+
+    for (e = acl->entries; e; e = e->next) {
+
+        if (e->family != sa.ss_family)
+            continue;
+
+        if (e->family == AF_INET) {
+            struct sockaddr_in *sai = (struct sockaddr_in*) &sa;
+
+            if (e->bits == 0 || /* this needs special handling because >> takes the right-hand side modulo 32 */
+                (ntohl(sai->sin_addr.s_addr ^ e->address_ipv4.s_addr) >> (32 - e->bits)) == 0)
+                return 1;
+#ifdef HAVE_IPV6
+        } else if (e->family == AF_INET6) {
+            int i, bits;
+            struct sockaddr_in6 *sai = (struct sockaddr_in6*) &sa;
+
+            if (e->bits == 128)
+                return memcmp(&sai->sin6_addr, &e->address_ipv6, 16) == 0;
+
+            if (e->bits == 0)
+                return 1;
+
+            for (i = 0, bits = e->bits; i < 16; i++) {
+
+                if (bits >= 8) {
+                    if (sai->sin6_addr.s6_addr[i] != e->address_ipv6.s6_addr[i])
+                        break;
+
+                    bits -= 8;
+                } else {
+                    if ((sai->sin6_addr.s6_addr[i] ^ e->address_ipv6.s6_addr[i]) >> (8 - bits) != 0)
+                        break;
+
+                    bits = 0;
+                }
+
+                if (bits == 0)
+                    return 1;
+            }
+#endif
+        }
+    }
+
+    return 0;
+}
diff --git a/src/pulsecore/ipacl.h b/src/pulsecore/ipacl.h
new file mode 100644 (file)
index 0000000..034248a
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef foopulsecoreipaclhfoo
+#define foopulsecoreipaclhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct pa_ip_acl pa_ip_acl;
+
+pa_ip_acl* pa_ip_acl_new(const char *s);
+void pa_ip_acl_free(pa_ip_acl *acl);
+int pa_ip_acl_check(pa_ip_acl *acl, int fd);
+
+#endif
diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h
new file mode 100644 (file)
index 0000000..a9412fa
--- /dev/null
@@ -0,0 +1,111 @@
+#ifndef foollistfoo
+#define foollistfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/macro.h>
+
+/* Some macros for maintaining doubly linked lists */
+
+/* The head of the linked list. Use this in the structure that shall
+ * contain the head of the linked list */
+#define PA_LLIST_HEAD(t,name)                                           \
+    t *name
+
+/* The pointers in the linked list's items. Use this in the item structure */
+#define PA_LLIST_FIELDS(t)                                              \
+    t *next, *prev
+
+/* Initialize the list's head */
+#define PA_LLIST_HEAD_INIT(t,item)                                      \
+    do {                                                                \
+        (item) = (t*) NULL; }                                           \
+    while(0)
+
+/* Initialize a list item */
+#define PA_LLIST_INIT(t,item)                                           \
+    do {                                                                \
+        t *_item = (item);                                              \
+        pa_assert(_item);                                               \
+        _item->prev = _item->next = NULL;                               \
+    } while(0)
+
+/* Prepend an item to the list */
+#define PA_LLIST_PREPEND(t,head,item)                                   \
+    do {                                                                \
+        t **_head = &(head), *_item = (item);                           \
+        pa_assert(_item);                                               \
+        if ((_item->next = *_head))                                     \
+            _item->next->prev = _item;                                  \
+        _item->prev = NULL;                                             \
+        *_head = _item;                                                 \
+    } while (0)
+
+/* Remove an item from the list */
+#define PA_LLIST_REMOVE(t,head,item)                                    \
+    do {                                                                \
+        t **_head = &(head), *_item = (item);                           \
+        pa_assert(_item);                                               \
+        if (_item->next)                                                \
+            _item->next->prev = _item->prev;                            \
+        if (_item->prev)                                                \
+            _item->prev->next = _item->next;                            \
+        else {                                                          \
+            pa_assert(*_head == _item);                                 \
+            *_head = _item->next;                                       \
+        }                                                               \
+        _item->next = _item->prev = NULL;                               \
+    } while(0)
+
+/* Find the head of the list */
+#define PA_LLIST_FIND_HEAD(t,item,head)                                 \
+    do {                                                                \
+        t **_head = (head), *_item = (item);                            \
+        *_head = _item;                                                 \
+        pa_assert(_head);                                               \
+        while ((*_head)->prev)                                          \
+            *_head = (*_head)->prev;                                    \
+    } while (0)
+
+/* Insert an item after another one (a = where, b = what) */
+#define PA_LLIST_INSERT_AFTER(t,head,a,b)                               \
+    do {                                                                \
+        t **_head = &(head), *_a = (a), *_b = (b);                      \
+        pa_assert(_b);                                                  \
+        if (!_a) {                                                      \
+            if ((_b->next = *_head))                                    \
+                _b->next->prev = _b;                                    \
+            _b->prev = NULL;                                            \
+            *_head = _b;                                                \
+        } else {                                                        \
+            if ((_b->next = _a->next))                                  \
+                _b->next->prev = _b;                                    \
+            _b->prev = _a;                                              \
+            _a->next = _b;                                              \
+        }                                                               \
+    } while (0)
+
+#define PA_LLIST_FOREACH(i,head)                                        \
+    for (i = (head); i; i = i->next)
+
+#define PA_LLIST_FOREACH_SAFE(i,n,head)                                 \
+    for (i = (head); i && ((n = i->next), 1); i = n)
+
+#endif
diff --git a/src/pulsecore/lock-autospawn.c b/src/pulsecore/lock-autospawn.c
new file mode 100644 (file)
index 0000000..955fd23
--- /dev/null
@@ -0,0 +1,362 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/poll.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-util.h>
+
+#include "lock-autospawn.h"
+
+/* So, why do we have this complex code here with threads and pipes
+ * and stuff? For two reasons: POSIX file locks are per-process, not
+ * per-file descriptor. That means that two contexts within the same
+ * process that try to create the autospawn lock might end up assuming
+ * they both managed to lock the file. And then, POSIX locking
+ * operations are synchronous. If two contexts run from the same event
+ * loop it must be made sure that they do not block each other, but
+ * that the locking operation can happen asynchronously. */
+
+#define AUTOSPAWN_LOCK "autospawn.lock"
+
+static pa_mutex *mutex;
+
+static unsigned n_ref = 0;
+static int lock_fd = -1;
+static pa_mutex *lock_fd_mutex = NULL;
+static pa_thread *thread = NULL;
+static int pipe_fd[2] = { -1, -1 };
+
+static enum {
+    STATE_IDLE,
+    STATE_OWNING,
+    STATE_TAKEN,
+    STATE_FAILED
+} state = STATE_IDLE;
+
+static void destroy_mutex(void) PA_GCC_DESTRUCTOR;
+
+static int ref(void) {
+
+    if (n_ref > 0) {
+
+        pa_assert(pipe_fd[0] >= 0);
+        pa_assert(pipe_fd[1] >= 0);
+        pa_assert(lock_fd_mutex);
+
+        n_ref++;
+
+        return 0;
+    }
+
+    pa_assert(!lock_fd_mutex);
+    pa_assert(state == STATE_IDLE);
+    pa_assert(lock_fd < 0);
+    pa_assert(!thread);
+    pa_assert(pipe_fd[0] < 0);
+    pa_assert(pipe_fd[1] < 0);
+
+    if (pa_pipe_cloexec(pipe_fd) < 0)
+        return -1;
+
+    pa_make_fd_nonblock(pipe_fd[1]);
+    pa_make_fd_nonblock(pipe_fd[0]);
+
+    lock_fd_mutex = pa_mutex_new(false, false);
+
+    n_ref = 1;
+    return 0;
+}
+
+static void unref(bool after_fork) {
+
+    pa_assert(n_ref > 0);
+    pa_assert(pipe_fd[0] >= 0);
+    pa_assert(pipe_fd[1] >= 0);
+    pa_assert(lock_fd_mutex);
+
+    n_ref--;
+
+    if (n_ref > 0)
+        return;
+
+    /* Join threads only in the process the new thread was created in
+     * to avoid undefined behaviour.
+     * POSIX.1-2008 XSH 2.9.2 Thread IDs: "applications should only assume
+     * that thread IDs are usable and unique within a single process." */
+    if (thread) {
+        if (after_fork)
+            pa_thread_free_nojoin(thread);
+       else
+            pa_thread_free(thread);
+        thread = NULL;
+    }
+
+    pa_mutex_lock(lock_fd_mutex);
+
+    pa_assert(state != STATE_TAKEN);
+
+    if (state == STATE_OWNING) {
+
+        pa_assert(lock_fd >= 0);
+
+        if (after_fork)
+            pa_close(lock_fd);
+        else {
+            char *lf;
+
+            if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
+                pa_log_warn(_("Cannot access autospawn lock."));
+
+            pa_unlock_lockfile(lf, lock_fd);
+            pa_xfree(lf);
+        }
+    }
+
+    lock_fd = -1;
+    state = STATE_IDLE;
+
+    pa_mutex_unlock(lock_fd_mutex);
+
+    pa_mutex_free(lock_fd_mutex);
+    lock_fd_mutex = NULL;
+
+    pa_close(pipe_fd[0]);
+    pa_close(pipe_fd[1]);
+    pipe_fd[0] = pipe_fd[1] = -1;
+}
+
+static void ping(void) {
+    ssize_t s;
+
+    pa_assert(pipe_fd[1] >= 0);
+
+    for (;;) {
+        char x = 'x';
+
+        if ((s = pa_write(pipe_fd[1], &x, 1, NULL)) == 1)
+            break;
+
+        pa_assert(s < 0);
+
+        if (errno == EAGAIN)
+            break;
+
+        pa_assert(errno == EINTR);
+    }
+}
+
+static void wait_for_ping(void) {
+    ssize_t s;
+    char x;
+    struct pollfd pfd;
+    int k;
+
+    pa_assert(pipe_fd[0] >= 0);
+
+    memset(&pfd, 0, sizeof(pfd));
+    pfd.fd = pipe_fd[0];
+    pfd.events = POLLIN;
+
+    if ((k = pa_poll(&pfd, 1, -1)) != 1) {
+        pa_assert(k < 0);
+        pa_assert(errno == EINTR);
+    } else if ((s = pa_read(pipe_fd[0], &x, 1, NULL)) != 1) {
+        pa_assert(s < 0);
+        pa_assert(errno == EAGAIN);
+    }
+}
+
+static void empty_pipe(void) {
+    char x[16];
+    ssize_t s;
+
+    pa_assert(pipe_fd[0] >= 0);
+
+    if ((s = pa_read(pipe_fd[0], &x, sizeof(x), NULL)) < 1) {
+        pa_assert(s < 0);
+        pa_assert(errno == EAGAIN);
+    }
+}
+
+static void thread_func(void *u) {
+    int fd;
+    char *lf;
+
+#ifdef HAVE_PTHREAD
+    sigset_t fullset;
+
+    /* No signals in this thread please */
+    sigfillset(&fullset);
+    pthread_sigmask(SIG_BLOCK, &fullset, NULL);
+#endif
+
+    if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
+        pa_log_warn(_("Cannot access autospawn lock."));
+        goto fail;
+    }
+
+    if ((fd = pa_lock_lockfile(lf)) < 0)
+        goto fail;
+
+    pa_mutex_lock(lock_fd_mutex);
+    pa_assert(state == STATE_IDLE);
+    lock_fd = fd;
+    state = STATE_OWNING;
+    pa_mutex_unlock(lock_fd_mutex);
+
+    goto finish;
+
+fail:
+    pa_mutex_lock(lock_fd_mutex);
+    pa_assert(state == STATE_IDLE);
+    state = STATE_FAILED;
+    pa_mutex_unlock(lock_fd_mutex);
+
+finish:
+    pa_xfree(lf);
+
+    ping();
+}
+
+static int start_thread(void) {
+
+    if (!thread)
+        if (!(thread = pa_thread_new("autospawn", thread_func, NULL)))
+            return -1;
+
+    return 0;
+}
+
+static void create_mutex(void) {
+    PA_ONCE_BEGIN {
+        mutex = pa_mutex_new(false, false);
+    } PA_ONCE_END;
+}
+
+static void destroy_mutex(void) {
+    if (mutex)
+        pa_mutex_free(mutex);
+}
+
+int pa_autospawn_lock_init(void) {
+    int ret = -1;
+
+    create_mutex();
+    pa_mutex_lock(mutex);
+
+    if (ref() < 0)
+        ret = -1;
+    else
+        ret = pipe_fd[0];
+
+    pa_mutex_unlock(mutex);
+
+    return ret;
+}
+
+int pa_autospawn_lock_acquire(bool block) {
+    int ret = -1;
+
+    create_mutex();
+    pa_mutex_lock(mutex);
+    pa_assert(n_ref >= 1);
+
+    pa_mutex_lock(lock_fd_mutex);
+
+    for (;;) {
+
+        empty_pipe();
+
+        if (state == STATE_OWNING) {
+            state = STATE_TAKEN;
+            ret = 1;
+            break;
+        }
+
+        if (state == STATE_FAILED) {
+            ret = -1;
+            break;
+        }
+
+        if (state == STATE_IDLE)
+            if (start_thread() < 0)
+                break;
+
+        if (!block) {
+            ret = 0;
+            break;
+        }
+
+        pa_mutex_unlock(lock_fd_mutex);
+        pa_mutex_unlock(mutex);
+
+        wait_for_ping();
+
+        pa_mutex_lock(mutex);
+        pa_mutex_lock(lock_fd_mutex);
+    }
+
+    pa_mutex_unlock(lock_fd_mutex);
+
+    pa_mutex_unlock(mutex);
+
+    return ret;
+}
+
+void pa_autospawn_lock_release(void) {
+
+    create_mutex();
+    pa_mutex_lock(mutex);
+    pa_assert(n_ref >= 1);
+
+    pa_assert(state == STATE_TAKEN);
+    state = STATE_OWNING;
+
+    ping();
+
+    pa_mutex_unlock(mutex);
+}
+
+void pa_autospawn_lock_done(bool after_fork) {
+
+    create_mutex();
+    pa_mutex_lock(mutex);
+    pa_assert(n_ref >= 1);
+
+    unref(after_fork);
+
+    pa_mutex_unlock(mutex);
+}
diff --git a/src/pulsecore/lock-autospawn.h b/src/pulsecore/lock-autospawn.h
new file mode 100644 (file)
index 0000000..9fc0ab1
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef foopulselockautospawnhfoo
+#define foopulselockautospawnhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/macro.h>
+
+int pa_autospawn_lock_init(void);
+int pa_autospawn_lock_acquire(bool block);
+void pa_autospawn_lock_release(void);
+void pa_autospawn_lock_done(bool after_fork);
+
+#endif
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
new file mode 100644 (file)
index 0000000..b5db818
--- /dev/null
@@ -0,0 +1,685 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#ifdef HAVE_SYSTEMD_JOURNAL
+
+/* sd_journal_send() implicitly add fields for the source file,
+ * function name and code line from where it's invoked. As the
+ * correct code location fields CODE_FILE, CODE_LINE and
+ * CODE_FUNC are already handled by this module, we do not want
+ * the automatic values supplied by the systemd journal API.
+ *
+ * Without suppressing these, both the actual log event source
+ * and the call to sd_journal_send() will be logged. */
+#define SD_JOURNAL_SUPPRESS_LOCATION
+
+#include <systemd/sd-journal.h>
+#endif
+
+#include <pulse/gccmacro.h>
+#include <pulse/rtclock.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/once.h>
+#include <pulsecore/ratelimit.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/i18n.h>
+
+#include "log.h"
+
+#define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG"
+#define ENV_LOG_JOURNAL "PULSE_LOG_JOURNAL"
+#define ENV_LOG_LEVEL "PULSE_LOG"
+#define ENV_LOG_COLORS "PULSE_LOG_COLORS"
+#define ENV_LOG_PRINT_TIME "PULSE_LOG_TIME"
+#define ENV_LOG_PRINT_FILE "PULSE_LOG_FILE"
+#define ENV_LOG_PRINT_META "PULSE_LOG_META"
+#define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
+#define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
+#define ENV_LOG_BACKTRACE_SKIP "PULSE_LOG_BACKTRACE_SKIP"
+#define ENV_LOG_NO_RATELIMIT "PULSE_LOG_NO_RATE_LIMIT"
+#define LOG_MAX_SUFFIX_NUMBER 99
+
+static char *ident = NULL; /* in local charset format */
+static pa_log_target target = { PA_LOG_STDERR, NULL };
+static pa_log_target_type_t target_override;
+static bool target_override_set = false;
+static pa_log_level_t maximum_level = PA_LOG_ERROR, maximum_level_override = PA_LOG_ERROR;
+static unsigned show_backtrace = 0, show_backtrace_override = 0, skip_backtrace = 0;
+static pa_log_flags_t flags = 0, flags_override = 0;
+static bool no_rate_limit = false;
+static int log_fd = -1;
+static int write_type = 0;
+
+#ifdef HAVE_SYSLOG_H
+static const int level_to_syslog[] = {
+    [PA_LOG_ERROR] = LOG_ERR,
+    [PA_LOG_WARN] = LOG_WARNING,
+    [PA_LOG_NOTICE] = LOG_NOTICE,
+    [PA_LOG_INFO] = LOG_INFO,
+    [PA_LOG_DEBUG] = LOG_DEBUG
+};
+#endif
+
+/* These are actually equivalent to the syslog ones
+ * but we don't want to depend on syslog.h */
+#ifdef HAVE_SYSTEMD_JOURNAL
+static const int level_to_journal[] = {
+    [PA_LOG_ERROR]  = 3,
+    [PA_LOG_WARN]   = 4,
+    [PA_LOG_NOTICE] = 5,
+    [PA_LOG_INFO]   = 6,
+    [PA_LOG_DEBUG]  = 7
+};
+#endif
+
+static const char level_to_char[] = {
+    [PA_LOG_ERROR] = 'E',
+    [PA_LOG_WARN] = 'W',
+    [PA_LOG_NOTICE] = 'N',
+    [PA_LOG_INFO] = 'I',
+    [PA_LOG_DEBUG] = 'D'
+};
+
+void pa_log_set_ident(const char *p) {
+    pa_xfree(ident);
+
+    if (!(ident = pa_utf8_to_locale(p)))
+        ident = pa_ascii_filter(p);
+}
+
+/* To make valgrind shut up. */
+static void ident_destructor(void) PA_GCC_DESTRUCTOR;
+static void ident_destructor(void) {
+    if (!pa_in_valgrind())
+        return;
+
+    pa_xfree(ident);
+}
+
+void pa_log_set_level(pa_log_level_t l) {
+    pa_assert(l < PA_LOG_LEVEL_MAX);
+
+    maximum_level = l;
+}
+
+int pa_log_set_target(pa_log_target *t) {
+    int fd = -1;
+    int old_fd;
+
+    pa_assert(t);
+
+    switch (t->type) {
+        case PA_LOG_STDERR:
+        case PA_LOG_SYSLOG:
+#ifdef HAVE_SYSTEMD_JOURNAL
+        case PA_LOG_JOURNAL:
+#endif
+        case PA_LOG_NULL:
+            break;
+        case PA_LOG_FILE:
+            if ((fd = pa_open_cloexec(t->file, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
+                pa_log(_("Failed to open target file '%s'."), t->file);
+                return -1;
+            }
+            break;
+        case PA_LOG_NEWFILE: {
+            char *file_path;
+            char *p;
+            unsigned version;
+
+            file_path = pa_sprintf_malloc("%s.xx", t->file);
+            p = file_path + strlen(t->file);
+
+            for (version = 0; version <= LOG_MAX_SUFFIX_NUMBER; version++) {
+                memset(p, 0, 3); /* Overwrite the ".xx" part in file_path with zero bytes. */
+
+                if (version > 0)
+                    pa_snprintf(p, 4, ".%u", version); /* Why 4? ".xx" + termitating zero byte. */
+
+                if ((fd = pa_open_cloexec(file_path, O_WRONLY | O_TRUNC | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) >= 0)
+                    break;
+            }
+
+            if (version > LOG_MAX_SUFFIX_NUMBER) {
+                pa_log(_("Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."),
+                        t->file, t->file, t->file, t->file, LOG_MAX_SUFFIX_NUMBER);
+                pa_xfree(file_path);
+                return -1;
+            } else
+                pa_log_debug("Opened target file %s\n", file_path);
+
+            pa_xfree(file_path);
+            break;
+        }
+    }
+
+    target.type = t->type;
+    pa_xfree(target.file);
+    target.file = pa_xstrdup(t->file);
+
+    old_fd = log_fd;
+    log_fd = fd;
+
+    if (old_fd >= 0)
+        pa_close(old_fd);
+
+    return 0;
+}
+
+void pa_log_set_flags(pa_log_flags_t _flags, pa_log_merge_t merge) {
+    pa_assert(!(_flags & ~(PA_LOG_COLORS|PA_LOG_PRINT_TIME|PA_LOG_PRINT_FILE|PA_LOG_PRINT_META|PA_LOG_PRINT_LEVEL)));
+
+    if (merge == PA_LOG_SET)
+        flags |= _flags;
+    else if (merge == PA_LOG_UNSET)
+        flags &= ~_flags;
+    else
+        flags = _flags;
+}
+
+void pa_log_set_show_backtrace(unsigned nlevels) {
+    show_backtrace = nlevels;
+}
+
+void pa_log_set_skip_backtrace(unsigned nlevels) {
+    skip_backtrace = nlevels;
+}
+
+#ifdef HAVE_EXECINFO_H
+
+static char* get_backtrace(unsigned show_nframes) {
+    void* trace[32];
+    int n_frames;
+    char **symbols, *e, *r;
+    unsigned j, n, s;
+    size_t a;
+
+    pa_assert(show_nframes > 0);
+
+    n_frames = backtrace(trace, PA_ELEMENTSOF(trace));
+
+    if (n_frames <= 0)
+        return NULL;
+
+    symbols = backtrace_symbols(trace, n_frames);
+
+    if (!symbols)
+        return NULL;
+
+    s = skip_backtrace;
+    n = PA_MIN((unsigned) n_frames, s + show_nframes);
+
+    a = 4;
+
+    for (j = s; j < n; j++) {
+        if (j > s)
+            a += 2;
+        a += strlen(pa_path_get_filename(symbols[j]));
+    }
+
+    r = pa_xnew(char, a);
+
+    strcpy(r, " (");
+    e = r + 2;
+
+    for (j = s; j < n; j++) {
+        const char *sym;
+
+        if (j > s) {
+            strcpy(e, "<<");
+            e += 2;
+        }
+
+        sym = pa_path_get_filename(symbols[j]);
+
+        strcpy(e, sym);
+        e += strlen(sym);
+    }
+
+    strcpy(e, ")");
+
+    free(symbols);
+
+    return r;
+}
+
+#endif
+
+static void init_defaults(void) {
+    PA_ONCE_BEGIN {
+
+        const char *e;
+
+        if (!ident) {
+            char binary[256];
+            if (pa_get_binary_name(binary, sizeof(binary)))
+                pa_log_set_ident(binary);
+        }
+
+        if (getenv(ENV_LOG_SYSLOG)) {
+            target_override = PA_LOG_SYSLOG;
+            target_override_set = true;
+        }
+
+#ifdef HAVE_SYSTEMD_JOURNAL
+        if (getenv(ENV_LOG_JOURNAL)) {
+            target_override = PA_LOG_JOURNAL;
+            target_override_set = true;
+        }
+#endif
+
+        if ((e = getenv(ENV_LOG_LEVEL))) {
+            maximum_level_override = (pa_log_level_t) atoi(e);
+
+            if (maximum_level_override >= PA_LOG_LEVEL_MAX)
+                maximum_level_override = PA_LOG_LEVEL_MAX-1;
+        }
+
+        if (getenv(ENV_LOG_COLORS))
+            flags_override |= PA_LOG_COLORS;
+
+        if (getenv(ENV_LOG_PRINT_TIME))
+            flags_override |= PA_LOG_PRINT_TIME;
+
+        if (getenv(ENV_LOG_PRINT_FILE))
+            flags_override |= PA_LOG_PRINT_FILE;
+
+        if (getenv(ENV_LOG_PRINT_META))
+            flags_override |= PA_LOG_PRINT_META;
+
+        if (getenv(ENV_LOG_PRINT_LEVEL))
+            flags_override |= PA_LOG_PRINT_LEVEL;
+
+        if ((e = getenv(ENV_LOG_BACKTRACE))) {
+            show_backtrace_override = (unsigned) atoi(e);
+
+            if (show_backtrace_override <= 0)
+                show_backtrace_override = 0;
+        }
+
+        if ((e = getenv(ENV_LOG_BACKTRACE_SKIP))) {
+            skip_backtrace = (unsigned) atoi(e);
+
+            if (skip_backtrace <= 0)
+                skip_backtrace = 0;
+        }
+
+        if (getenv(ENV_LOG_NO_RATELIMIT))
+            no_rate_limit = true;
+
+    } PA_ONCE_END;
+}
+
+#ifdef HAVE_SYSLOG_H
+static void log_syslog(pa_log_level_t level, char *t, char *timestamp, char *location, char *bt) {
+    char *local_t;
+
+    openlog(ident, LOG_PID, LOG_USER);
+
+    if ((local_t = pa_utf8_to_locale(t)))
+        t = local_t;
+
+    syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
+    pa_xfree(local_t);
+}
+#endif
+
+void pa_log_levelv_meta(
+        pa_log_level_t level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *format,
+        va_list ap) {
+
+    char *t, *n;
+    int saved_errno = errno;
+    char *bt = NULL;
+    pa_log_target_type_t _target;
+    pa_log_level_t _maximum_level;
+    unsigned _show_backtrace;
+    pa_log_flags_t _flags;
+
+    /* We don't use dynamic memory allocation here to minimize the hit
+     * in RT threads */
+    char text[16*1024], location[128], timestamp[32];
+
+    pa_assert(level < PA_LOG_LEVEL_MAX);
+    pa_assert(format);
+
+    init_defaults();
+
+    _target = target_override_set ? target_override : target.type;
+    _maximum_level = PA_MAX(maximum_level, maximum_level_override);
+    _show_backtrace = PA_MAX(show_backtrace, show_backtrace_override);
+    _flags = flags | flags_override;
+
+    if (PA_LIKELY(level > _maximum_level)) {
+        errno = saved_errno;
+        return;
+    }
+
+    pa_vsnprintf(text, sizeof(text), format, ap);
+
+    if ((_flags & PA_LOG_PRINT_META) && file && line > 0 && func)
+        pa_snprintf(location, sizeof(location), "[%s][%s:%i %s()] ",
+                    pa_strnull(pa_thread_get_name(pa_thread_self())), file, line, func);
+    else if ((_flags & (PA_LOG_PRINT_META|PA_LOG_PRINT_FILE)) && file)
+        pa_snprintf(location, sizeof(location), "[%s] %s: ",
+                    pa_strnull(pa_thread_get_name(pa_thread_self())), pa_path_get_filename(file));
+    else
+        location[0] = 0;
+
+    if (_flags & PA_LOG_PRINT_TIME) {
+        static pa_usec_t start, last;
+        pa_usec_t u, a, r;
+
+        u = pa_rtclock_now();
+
+        PA_ONCE_BEGIN {
+            start = u;
+            last = u;
+        } PA_ONCE_END;
+
+        r = u - last;
+        a = u - start;
+
+        /* This is not thread safe, but this is a debugging tool only
+         * anyway. */
+        last = u;
+
+        pa_snprintf(timestamp, sizeof(timestamp), "(%4llu.%03llu|%4llu.%03llu) ",
+                    (unsigned long long) (a / PA_USEC_PER_SEC),
+                    (unsigned long long) (((a / PA_USEC_PER_MSEC)) % 1000),
+                    (unsigned long long) (r / PA_USEC_PER_SEC),
+                    (unsigned long long) (((r / PA_USEC_PER_MSEC)) % 1000));
+
+    } else
+        timestamp[0] = 0;
+
+#ifdef HAVE_EXECINFO_H
+    if (_show_backtrace > 0)
+        bt = get_backtrace(_show_backtrace);
+#endif
+
+    if (!pa_utf8_valid(text))
+        pa_logl(level, "Invalid UTF-8 string following below:");
+
+    for (t = text; t; t = n) {
+        if ((n = strchr(t, '\n'))) {
+            *n = 0;
+            n++;
+        }
+
+        /* We ignore strings only made out of whitespace */
+        if (t[strspn(t, "\t ")] == 0)
+            continue;
+
+        switch (_target) {
+
+            case PA_LOG_STDERR: {
+                const char *prefix = "", *suffix = "", *grey = "";
+                char *local_t;
+
+#ifndef OS_IS_WIN32
+                /* Yes indeed. Useless, but fun! */
+                if ((_flags & PA_LOG_COLORS) && isatty(STDERR_FILENO)) {
+                    if (level <= PA_LOG_ERROR)
+                        prefix = "\x1B[1;31m";
+                    else if (level <= PA_LOG_WARN)
+                        prefix = "\x1B[1m";
+
+                    if (bt)
+                        grey = "\x1B[2m";
+
+                    if (grey[0] || prefix[0])
+                        suffix = "\x1B[0m";
+                }
+#endif
+
+                /* We shouldn't be using dynamic allocation here to
+                 * minimize the hit in RT threads */
+                if ((local_t = pa_utf8_to_locale(t)))
+                    t = local_t;
+
+                if (_flags & PA_LOG_PRINT_LEVEL)
+                    fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);
+                else
+                    fprintf(stderr, "%s%s%s%s%s%s%s\n", timestamp, location, prefix, t, grey, pa_strempty(bt), suffix);
+#ifdef OS_IS_WIN32
+                fflush(stderr);
+#endif
+
+                pa_xfree(local_t);
+
+                break;
+            }
+
+#ifdef HAVE_SYSLOG_H
+            case PA_LOG_SYSLOG:
+                log_syslog(level, t, timestamp, location, bt);
+                break;
+#endif
+
+#ifdef HAVE_SYSTEMD_JOURNAL
+            case PA_LOG_JOURNAL:
+                if (sd_journal_send("MESSAGE=%s", t,
+                                "PRIORITY=%i", level_to_journal[level],
+                                "CODE_FILE=%s", file,
+                                "CODE_FUNC=%s", func,
+                                "CODE_LINE=%d", line,
+                                "PULSE_BACKTRACE=%s", pa_strempty(bt),
+                                NULL) < 0) {
+#ifdef HAVE_SYSLOG_H
+                    pa_log_target new_target = { .type = PA_LOG_SYSLOG, .file = NULL };
+
+                    syslog(level_to_syslog[PA_LOG_ERROR], "%s%s%s", timestamp, __FILE__,
+                           "Error writing logs to the journal. Redirect log messages to syslog.");
+                    log_syslog(level, t, timestamp, location, bt);
+#else
+                    pa_log_target new_target = { .type = PA_LOG_STDERR, .file = NULL };
+
+                    saved_errno = errno;
+                    fprintf(stderr, "%s\n", "Error writing logs to the journal. Redirect log messages to console.");
+                    fprintf(stderr, "%s %s\n", metadata, t);
+#endif
+                    pa_log_set_target(&new_target);
+                }
+                break;
+#endif
+
+            case PA_LOG_FILE:
+            case PA_LOG_NEWFILE: {
+                char *local_t;
+
+                if ((local_t = pa_utf8_to_locale(t)))
+                    t = local_t;
+
+                if (log_fd >= 0) {
+                    char metadata[256];
+
+                    if (_flags & PA_LOG_PRINT_LEVEL)
+                        pa_snprintf(metadata, sizeof(metadata), "%s%c: %s", timestamp, level_to_char[level], location);
+                    else
+                        pa_snprintf(metadata, sizeof(metadata), "%s%s", timestamp, location);
+
+                    if ((pa_write(log_fd, metadata, strlen(metadata), &write_type) < 0)
+                            || (pa_write(log_fd, t, strlen(t), &write_type) < 0)
+                            || (bt && pa_write(log_fd, bt, strlen(bt), &write_type) < 0)
+                            || (pa_write(log_fd, "\n", 1, &write_type) < 0)) {
+                        pa_log_target new_target = { .type = PA_LOG_STDERR, .file = NULL };
+                        saved_errno = errno;
+                        fprintf(stderr, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
+                        fprintf(stderr, "%s %s\n", metadata, t);
+                        pa_log_set_target(&new_target);
+                    }
+                }
+
+                pa_xfree(local_t);
+
+                break;
+            }
+            case PA_LOG_NULL:
+            default:
+                break;
+        }
+    }
+
+    pa_xfree(bt);
+    errno = saved_errno;
+}
+
+void pa_log_level_meta(
+        pa_log_level_t level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *format, ...) {
+
+    va_list ap;
+    va_start(ap, format);
+    pa_log_levelv_meta(level, file, line, func, format, ap);
+    va_end(ap);
+}
+
+void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
+    pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
+}
+
+void pa_log_level(pa_log_level_t level, const char *format, ...) {
+    va_list ap;
+
+    va_start(ap, format);
+    pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
+    va_end(ap);
+}
+
+bool pa_log_ratelimit(pa_log_level_t level) {
+    /* Not more than 10 messages every 5s */
+    static PA_DEFINE_RATELIMIT(ratelimit, 5 * PA_USEC_PER_SEC, 10);
+
+    init_defaults();
+
+    if (no_rate_limit)
+        return true;
+
+    return pa_ratelimit_test(&ratelimit, level);
+}
+
+pa_log_target *pa_log_target_new(pa_log_target_type_t type, const char *file) {
+    pa_log_target *t = NULL;
+
+    t = pa_xnew(pa_log_target, 1);
+
+    t->type = type;
+    t->file = pa_xstrdup(file);
+
+    return t;
+}
+
+void pa_log_target_free(pa_log_target *t) {
+    pa_assert(t);
+
+    pa_xfree(t->file);
+    pa_xfree(t);
+}
+
+pa_log_target *pa_log_parse_target(const char *string) {
+    pa_log_target *t = NULL;
+
+    pa_assert(string);
+
+    if (pa_streq(string, "stderr"))
+        t = pa_log_target_new(PA_LOG_STDERR, NULL);
+    else if (pa_streq(string, "syslog"))
+        t = pa_log_target_new(PA_LOG_SYSLOG, NULL);
+#ifdef HAVE_SYSTEMD_JOURNAL
+    else if (pa_streq(string, "journal"))
+        t = pa_log_target_new(PA_LOG_JOURNAL, NULL);
+#endif
+    else if (pa_streq(string, "null"))
+        t = pa_log_target_new(PA_LOG_NULL, NULL);
+    else if (pa_startswith(string, "file:"))
+        t = pa_log_target_new(PA_LOG_FILE, string + 5);
+    else if (pa_startswith(string, "newfile:"))
+        t = pa_log_target_new(PA_LOG_NEWFILE, string + 8);
+    else
+        pa_log(_("Invalid log target."));
+
+    return t;
+}
+
+char *pa_log_target_to_string(const pa_log_target *t) {
+    char *string = NULL;
+
+    pa_assert(t);
+
+    switch (t->type) {
+        case PA_LOG_STDERR:
+            string = pa_xstrdup("stderr");
+            break;
+        case PA_LOG_SYSLOG:
+            string = pa_xstrdup("syslog");
+            break;
+#ifdef HAVE_SYSTEMD_JOURNAL
+        case PA_LOG_JOURNAL:
+            string = pa_xstrdup("journal");
+            break;
+#endif
+        case PA_LOG_NULL:
+            string = pa_xstrdup("null");
+            break;
+        case PA_LOG_FILE:
+            string = pa_sprintf_malloc("file:%s", t->file);
+            break;
+        case PA_LOG_NEWFILE:
+            string = pa_sprintf_malloc("newfile:%s", t->file);
+            break;
+    }
+
+    return string;
+}
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
new file mode 100644 (file)
index 0000000..803ed5a
--- /dev/null
@@ -0,0 +1,155 @@
+#ifndef foologhfoo
+#define foologhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include <pulsecore/macro.h>
+#include <pulse/gccmacro.h>
+
+/* A simple logging subsystem */
+
+/* Where to log to */
+typedef enum pa_log_target_type {
+    PA_LOG_STDERR,      /* default */
+    PA_LOG_SYSLOG,
+#ifdef HAVE_SYSTEMD_JOURNAL
+    PA_LOG_JOURNAL,     /* systemd journal */
+#endif
+    PA_LOG_NULL,        /* to /dev/null */
+    PA_LOG_FILE,        /* to a user specified file */
+    PA_LOG_NEWFILE,     /* with an automatic suffix to avoid overwriting anything */
+} pa_log_target_type_t;
+
+typedef enum pa_log_level {
+    PA_LOG_ERROR  = 0,    /* Error messages */
+    PA_LOG_WARN   = 1,    /* Warning messages */
+    PA_LOG_NOTICE = 2,    /* Notice messages */
+    PA_LOG_INFO   = 3,    /* Info messages */
+    PA_LOG_DEBUG  = 4,    /* Debug messages */
+    PA_LOG_LEVEL_MAX
+} pa_log_level_t;
+
+typedef enum pa_log_flags {
+    PA_LOG_COLORS      = 0x01, /* Show colorful output */
+    PA_LOG_PRINT_TIME  = 0x02, /* Show time */
+    PA_LOG_PRINT_FILE  = 0x04, /* Show source file */
+    PA_LOG_PRINT_META  = 0x08, /* Show extended location information */
+    PA_LOG_PRINT_LEVEL = 0x10, /* Show log level prefix */
+} pa_log_flags_t;
+
+typedef enum pa_log_merge {
+    PA_LOG_SET,
+    PA_LOG_UNSET,
+    PA_LOG_RESET
+} pa_log_merge_t;
+
+typedef struct {
+    pa_log_target_type_t type;
+    char *file;
+} pa_log_target;
+
+/* Set an identification for the current daemon. Used when logging to syslog. */
+void pa_log_set_ident(const char *p);
+
+/* Set a log target. */
+int pa_log_set_target(pa_log_target *t);
+
+/* Maximal log level */
+void pa_log_set_level(pa_log_level_t l);
+
+/* Set flags */
+void pa_log_set_flags(pa_log_flags_t flags, pa_log_merge_t merge);
+
+/* Enable backtrace */
+void pa_log_set_show_backtrace(unsigned nlevels);
+
+/* Skip the first backtrace frames */
+void pa_log_set_skip_backtrace(unsigned nlevels);
+
+void pa_log_level_meta(
+        pa_log_level_t level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *format, ...) PA_GCC_PRINTF_ATTR(5,6);
+
+void pa_log_levelv_meta(
+        pa_log_level_t level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *format,
+        va_list ap);
+
+void pa_log_level(
+        pa_log_level_t level,
+        const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
+
+void pa_log_levelv(
+        pa_log_level_t level,
+        const char *format,
+        va_list ap);
+
+pa_log_target *pa_log_target_new(pa_log_target_type_t type, const char *file);
+
+void pa_log_target_free(pa_log_target *t);
+
+pa_log_target *pa_log_parse_target(const char *string);
+
+char *pa_log_target_to_string(const pa_log_target *t);
+
+#if __STDC_VERSION__ >= 199901L
+
+/* ISO varargs available */
+
+#define pa_log_debug(...)  pa_log_level_meta(PA_LOG_DEBUG,  __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_log_info(...)   pa_log_level_meta(PA_LOG_INFO,   __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_log_notice(...) pa_log_level_meta(PA_LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_log_warn(...)   pa_log_level_meta(PA_LOG_WARN,   __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_log_error(...)  pa_log_level_meta(PA_LOG_ERROR,  __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_logl(level, ...)  pa_log_level_meta(level,  __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+#else
+
+#define LOG_FUNC(suffix, level) \
+PA_GCC_UNUSED static void pa_log_##suffix(const char *format, ...) { \
+    va_list ap; \
+    va_start(ap, format); \
+    pa_log_levelv_meta(level, NULL, 0, NULL, format, ap); \
+    va_end(ap); \
+}
+
+LOG_FUNC(debug, PA_LOG_DEBUG)
+LOG_FUNC(info, PA_LOG_INFO)
+LOG_FUNC(notice, PA_LOG_NOTICE)
+LOG_FUNC(warn, PA_LOG_WARN)
+LOG_FUNC(error, PA_LOG_ERROR)
+
+#endif
+
+#define pa_log pa_log_error
+
+bool pa_log_ratelimit(pa_log_level_t level);
+
+#endif
diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c
new file mode 100644 (file)
index 0000000..cfdde26
--- /dev/null
@@ -0,0 +1,63 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "ltdl-helper.h"
+
+pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *symbol) {
+    char *sn, *c;
+    pa_void_func_t f;
+
+    pa_assert(handle);
+    pa_assert(symbol);
+
+    f = (pa_void_func_t) lt_dlsym(handle, symbol);
+
+    if (f)
+        return f;
+
+    if (!module)
+        return NULL;
+
+    /* As the .la files might have been cleansed from the system, we should
+     * try with the ltdl prefix as well. */
+
+    sn = pa_sprintf_malloc("%s_LTX_%s", module, symbol);
+
+    for (c = sn; *c; c++)
+        if (!isalnum((unsigned char)*c))
+            *c = '_';
+
+    f = (pa_void_func_t) lt_dlsym(handle, sn);
+    pa_xfree(sn);
+
+    return f;
+}
diff --git a/src/pulsecore/ltdl-helper.h b/src/pulsecore/ltdl-helper.h
new file mode 100644 (file)
index 0000000..d3219ba
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef foopulsecoreltdlhelperhfoo
+#define foopulsecoreltdlhelperhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <ltdl.h>
+
+typedef void (*pa_void_func_t)(void);
+
+pa_void_func_t pa_load_sym(lt_dlhandle handle, const char*module, const char *symbol);
+
+#endif
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
new file mode 100644 (file)
index 0000000..dbce5cd
--- /dev/null
@@ -0,0 +1,304 @@
+#ifndef foopulsemacrohfoo
+#define foopulsemacrohfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifndef PA_LIKELY
+#ifdef __GNUC__
+#define PA_LIKELY(x) (__builtin_expect(!!(x),1))
+#define PA_UNLIKELY(x) (__builtin_expect(!!(x),0))
+#else
+#define PA_LIKELY(x) (x)
+#define PA_UNLIKELY(x) (x)
+#endif
+#endif
+
+/* Rounds down */
+static inline void* PA_ALIGN_PTR(const void *p) {
+    return (void*) (((size_t) p) & ~(sizeof(void*) - 1));
+}
+
+/* Rounds up */
+static inline size_t PA_ALIGN(size_t l) {
+    return ((l + sizeof(void*) - 1) & ~(sizeof(void*) - 1));
+}
+
+#if defined(__GNUC__)
+    #define PA_UNUSED __attribute__ ((unused))
+#else
+    #define PA_UNUSED
+#endif
+
+#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+#if defined(__GNUC__)
+    #define PA_DECLARE_ALIGNED(n,t,v)      t v __attribute__ ((aligned (n)))
+#else
+    #define PA_DECLARE_ALIGNED(n,t,v)      t v
+#endif
+
+#ifdef __GNUC__
+#define typeof __typeof__
+#endif
+
+/* The users of PA_MIN and PA_MAX, PA_CLAMP, PA_ROUND_UP should be
+ * aware that these macros on non-GCC executed code with side effects
+ * twice. It is thus considered misuse to use code with side effects
+ * as arguments to MIN and MAX. */
+
+#ifdef __GNUC__
+#define PA_MAX(a,b)                             \
+    __extension__ ({                            \
+            typeof(a) _a = (a);                 \
+            typeof(b) _b = (b);                 \
+            _a > _b ? _a : _b;                  \
+        })
+#else
+#define PA_MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_MIN(a,b)                             \
+    __extension__ ({                            \
+            typeof(a) _a = (a);                 \
+            typeof(b) _b = (b);                 \
+            _a < _b ? _a : _b;                  \
+        })
+#else
+#define PA_MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_CLAMP(x, low, high)                                          \
+    __extension__ ({                                                    \
+            typeof(x) _x = (x);                                         \
+            typeof(low) _low = (low);                                   \
+            typeof(high) _high = (high);                                \
+            ((_x > _high) ? _high : ((_x < _low) ? _low : _x));         \
+        })
+#else
+#define PA_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+#endif
+
+#ifdef __GNUC__
+#define PA_CLAMP_UNLIKELY(x, low, high)                                 \
+    __extension__ ({                                                    \
+            typeof(x) _x = (x);                                         \
+            typeof(low) _low = (low);                                   \
+            typeof(high) _high = (high);                                \
+            (PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \
+        })
+#else
+#define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x)))
+#endif
+
+/* We don't define a PA_CLAMP_LIKELY here, because it doesn't really
+ * make sense: we cannot know if it is more likely that the values is
+ * lower or greater than the boundaries.*/
+
+#ifdef __GNUC__
+#define PA_ROUND_UP(a, b)                       \
+    __extension__ ({                            \
+            typeof(a) _a = (a);                 \
+            typeof(b) _b = (b);                 \
+            ((_a + _b - 1) / _b) * _b;          \
+        })
+#else
+#define PA_ROUND_UP(a, b) ((((a) + (b) - 1) / (b)) * (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_ROUND_DOWN(a, b)                     \
+    __extension__ ({                            \
+            typeof(a) _a = (a);                 \
+            typeof(b) _b = (b);                 \
+            (_a / _b) * _b;                     \
+        })
+#else
+#define PA_ROUND_DOWN(a, b) (((a) / (b)) * (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_CLIP_SUB(a, b)                       \
+    __extension__ ({                            \
+            typeof(a) _a = (a);                 \
+            typeof(b) _b = (b);                 \
+            _a > _b ? _a - _b : 0;              \
+        })
+#else
+#define PA_CLIP_SUB(a, b) ((a) > (b) ? (a) - (b) : 0)
+#endif
+
+#ifdef __GNUC__
+#define PA_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#else
+#define PA_PRETTY_FUNCTION ""
+#endif
+
+#define pa_return_if_fail(expr)                                         \
+    do {                                                                \
+        if (PA_UNLIKELY(!(expr))) {                                     \
+            pa_log_debug("Assertion '%s' failed at %s:%u, function %s.", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+            return;                                                     \
+        }                                                               \
+    } while(false)
+
+#define pa_return_val_if_fail(expr, val)                                \
+    do {                                                                \
+        if (PA_UNLIKELY(!(expr))) {                                     \
+            pa_log_debug("Assertion '%s' failed at %s:%u, function %s.", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+            return (val);                                               \
+        }                                                               \
+    } while(false)
+
+#define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL)
+
+/* pa_assert_se() is an assert which guarantees side effects of x,
+ * i.e. is never optimized away, regardless of NDEBUG or FASTPATH. */
+#ifndef __COVERITY__
+#define pa_assert_se(expr)                                              \
+    do {                                                                \
+        if (PA_UNLIKELY(!(expr))) {                                     \
+            pa_log_error("Assertion '%s' failed at %s:%u, function %s(). Aborting.", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+            abort();                                                    \
+        }                                                               \
+    } while (false)
+#else
+#define pa_assert_se(expr)                                              \
+    do {                                                                \
+        int _unique_var = (expr);                                       \
+        if (!_unique_var)                                               \
+            abort();                                                    \
+    } while (false)
+#endif
+
+/* Does exactly nothing */
+#define pa_nop() do {} while (false)
+
+/* pa_assert() is an assert that may be optimized away by defining
+ * NDEBUG. pa_assert_fp() is an assert that may be optimized away by
+ * defining FASTPATH. It is supposed to be used in inner loops. It's
+ * there for extra paranoia checking and should probably be removed in
+ * production builds. */
+#ifdef NDEBUG
+#define pa_assert(expr) pa_nop()
+#define pa_assert_fp(expr) pa_nop()
+#elif defined (FASTPATH)
+#define pa_assert(expr) pa_assert_se(expr)
+#define pa_assert_fp(expr) pa_nop()
+#else
+#define pa_assert(expr) pa_assert_se(expr)
+#define pa_assert_fp(expr) pa_assert_se(expr)
+#endif
+
+#ifdef NDEBUG
+#define pa_assert_not_reached() abort()
+#else
+#define pa_assert_not_reached()                                         \
+    do {                                                                \
+        pa_log_error("Code should not be reached at %s:%u, function %s(). Aborting.", __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+        abort();                                                        \
+    } while (false)
+#endif
+
+/* A compile time assertion */
+#define pa_assert_cc(expr)                         \
+    do {                                           \
+        switch (0) {                               \
+            case 0:                                \
+            case !!(expr):                         \
+                ;                                  \
+        }                                          \
+    } while (false)
+
+#define PA_PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define PA_UINT_TO_PTR(u) ((void*) ((uintptr_t) (u)))
+
+#define PA_PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
+#define PA_UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u)))
+
+#define PA_PTR_TO_INT(p) ((int) ((intptr_t) (p)))
+#define PA_INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
+
+#define PA_PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p)))
+#define PA_INT32_TO_PTR(u) ((void*) ((intptr_t) (u)))
+
+#ifdef OS_IS_WIN32
+#define PA_PATH_SEP "\\"
+#define PA_PATH_SEP_CHAR '\\'
+#else
+#define PA_PATH_SEP "/"
+#define PA_PATH_SEP_CHAR '/'
+#endif
+
+#if defined(__GNUC__) && defined(__ELF__)
+
+#define PA_WARN_REFERENCE(sym, msg)                  \
+    __asm__(".section .gnu.warning." #sym);          \
+    __asm__(".asciz \"" msg "\"");                   \
+    __asm__(".previous")
+
+#else
+
+#define PA_WARN_REFERENCE(sym, msg)
+
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+#define PA_DEBUG_TRAP __asm__("int $3")
+#else
+#define PA_DEBUG_TRAP raise(SIGTRAP)
+#endif
+
+#define pa_memzero(x,l) (memset((x), 0, (l)))
+#define pa_zero(x) (pa_memzero(&(x), sizeof(x)))
+
+#define PA_INT_TYPE_SIGNED(type) (!!((type) 0 > (type) -1))
+
+#define PA_INT_TYPE_HALF(type) ((type) 1 << (sizeof(type)*8 - 2))
+
+#define PA_INT_TYPE_MAX(type)                                          \
+    ((type) (PA_INT_TYPE_SIGNED(type)                                  \
+             ? (PA_INT_TYPE_HALF(type) - 1 + PA_INT_TYPE_HALF(type))   \
+             : (type) -1))
+
+#define PA_INT_TYPE_MIN(type)                                          \
+    ((type) (PA_INT_TYPE_SIGNED(type)                                  \
+             ? (-1 - PA_INT_TYPE_MAX(type))                            \
+             : (type) 0))
+
+/* We include this at the very last place */
+#include <pulsecore/log.h>
+
+#endif
diff --git a/src/pulsecore/mcalign.c b/src/pulsecore/mcalign.c
new file mode 100644 (file)
index 0000000..df885c2
--- /dev/null
@@ -0,0 +1,216 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "mcalign.h"
+
+struct pa_mcalign {
+    size_t base;
+    pa_memchunk leftover, current;
+};
+
+pa_mcalign *pa_mcalign_new(size_t base) {
+    pa_mcalign *m;
+    pa_assert(base);
+
+    m = pa_xnew(pa_mcalign, 1);
+
+    m->base = base;
+    pa_memchunk_reset(&m->leftover);
+    pa_memchunk_reset(&m->current);
+
+    return m;
+}
+
+void pa_mcalign_free(pa_mcalign *m) {
+    pa_assert(m);
+
+    if (m->leftover.memblock)
+        pa_memblock_unref(m->leftover.memblock);
+
+    if (m->current.memblock)
+        pa_memblock_unref(m->current.memblock);
+
+    pa_xfree(m);
+}
+
+void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
+    pa_assert(m);
+    pa_assert(c);
+
+    pa_assert(c->memblock);
+    pa_assert(c->length > 0);
+
+    pa_assert(!m->current.memblock);
+
+    /* Append to the leftover memory block */
+    if (m->leftover.memblock) {
+
+        /* Try to merge */
+        if (m->leftover.memblock == c->memblock &&
+            m->leftover.index + m->leftover.length == c->index) {
+
+            /* Merge */
+            m->leftover.length += c->length;
+
+            /* If the new chunk is larger than m->base, move it to current */
+            if (m->leftover.length >= m->base) {
+                m->current = m->leftover;
+                pa_memchunk_reset(&m->leftover);
+            }
+
+        } else {
+            size_t l;
+            void *lo_data, *m_data;
+
+            /* We have to copy */
+            pa_assert(m->leftover.length < m->base);
+            l = m->base - m->leftover.length;
+
+            if (l > c->length)
+                l = c->length;
+
+            /* Can we use the current block? */
+            pa_memchunk_make_writable(&m->leftover, m->base);
+
+            lo_data = pa_memblock_acquire(m->leftover.memblock);
+            m_data = pa_memblock_acquire(c->memblock);
+            memcpy((uint8_t*) lo_data + m->leftover.index + m->leftover.length, (uint8_t*) m_data + c->index, l);
+            pa_memblock_release(m->leftover.memblock);
+            pa_memblock_release(c->memblock);
+            m->leftover.length += l;
+
+            pa_assert(m->leftover.length <= m->base);
+            pa_assert(m->leftover.length <= pa_memblock_get_length(m->leftover.memblock));
+
+            if (c->length > l) {
+                /* Save the remainder of the memory block */
+                m->current = *c;
+                m->current.index += l;
+                m->current.length -= l;
+                pa_memblock_ref(m->current.memblock);
+            }
+        }
+    } else {
+        /* Nothing to merge or copy, just store it */
+
+        if (c->length >= m->base)
+            m->current = *c;
+        else
+            m->leftover = *c;
+
+        pa_memblock_ref(c->memblock);
+    }
+}
+
+int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
+    pa_assert(m);
+    pa_assert(c);
+
+    /* First test if there's a leftover memory block available */
+    if (m->leftover.memblock) {
+        pa_assert(m->leftover.length > 0);
+        pa_assert(m->leftover.length <= m->base);
+
+        /* The leftover memory block is not yet complete */
+        if (m->leftover.length < m->base)
+            return -1;
+
+        /* Return the leftover memory block */
+        *c = m->leftover;
+        pa_memchunk_reset(&m->leftover);
+
+        /* If the current memblock is too small move it the leftover */
+        if (m->current.memblock && m->current.length < m->base) {
+            m->leftover = m->current;
+            pa_memchunk_reset(&m->current);
+        }
+
+        return 0;
+    }
+
+    /* Now let's see if there is other data available */
+    if (m->current.memblock) {
+        size_t l;
+        pa_assert(m->current.length >= m->base);
+
+        /* The length of the returned memory block */
+        l = m->current.length;
+        l /= m->base;
+        l *= m->base;
+        pa_assert(l > 0);
+
+        /* Prepare the returned block */
+        *c = m->current;
+        pa_memblock_ref(c->memblock);
+        c->length = l;
+
+        /* Drop that from the current memory block */
+        pa_assert(l <= m->current.length);
+        m->current.index += l;
+        m->current.length -= l;
+
+        /* In case the whole block was dropped ... */
+        if (m->current.length == 0)
+            pa_memblock_unref(m->current.memblock);
+        else {
+            /* Move the remainder to leftover */
+            pa_assert(m->current.length < m->base && !m->leftover.memblock);
+
+            m->leftover = m->current;
+        }
+
+        pa_memchunk_reset(&m->current);
+
+        return 0;
+    }
+
+    /* There's simply nothing */
+    return -1;
+}
+
+size_t pa_mcalign_csize(pa_mcalign *m, size_t l) {
+    pa_assert(m);
+    pa_assert(l > 0);
+
+    pa_assert(!m->current.memblock);
+
+    if (m->leftover.memblock)
+        l += m->leftover.length;
+
+    return (l/m->base)*m->base;
+}
+
+void pa_mcalign_flush(pa_mcalign *m) {
+    pa_memchunk chunk;
+    pa_assert(m);
+
+    while (pa_mcalign_pop(m, &chunk) >= 0)
+        pa_memblock_unref(chunk.memblock);
+}
diff --git a/src/pulsecore/mcalign.h b/src/pulsecore/mcalign.h
new file mode 100644 (file)
index 0000000..e29d82e
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef foomcalignhfoo
+#define foomcalignhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
+
+/* An alignment object, used for aligning memchunks to multiples of
+ * the frame size. */
+
+/* Method of operation: the user creates a new mcalign object by
+ * calling pa_mcalign_new() with the appropriate aligning
+ * granularity. After that he may call pa_mcalign_push() for an input
+ * memchunk. After exactly one memchunk the user has to call
+ * pa_mcalign_pop() until it returns -1. If pa_mcalign_pop() returns
+ * 0, the memchunk *c is valid and aligned to the granularity. Some
+ * pseudocode illustrating this:
+ *
+ * pa_mcalign *a = pa_mcalign_new(4, NULL);
+ *
+ * for (;;) {
+ *   pa_memchunk input;
+ *
+ *   ... fill input ...
+ *
+ *   pa_mcalign_push(m, &input);
+ *   pa_memblock_unref(input.memblock);
+ *
+ *   for (;;) {
+ *     pa_memchunk output;
+ *
+ *     if (pa_mcalign_pop(m, &output) < 0)
+ *       break;
+ *
+ *     ... consume output ...
+ *
+ *     pa_memblock_unref(output.memblock);
+ *   }
+ * }
+ *
+ * pa_memchunk_free(a);
+ * */
+
+typedef struct pa_mcalign pa_mcalign;
+
+pa_mcalign *pa_mcalign_new(size_t base);
+void pa_mcalign_free(pa_mcalign *m);
+
+/* Push a new memchunk into the aligner. The caller of this routine
+ * has to free the memchunk by himself. */
+void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c);
+
+/* Pop a new memchunk from the aligner. Returns 0 when successful,
+ * nonzero otherwise. */
+int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c);
+
+/* If we pass l bytes in now, how many bytes would we get out? */
+size_t pa_mcalign_csize(pa_mcalign *m, size_t l);
+
+/* Flush what's still stored in the aligner */
+void pa_mcalign_flush(pa_mcalign *m);
+
+#endif
diff --git a/src/pulsecore/mem.h b/src/pulsecore/mem.h
new file mode 100644 (file)
index 0000000..cba1410
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef foopulsememhfoo
+#define foopulsememhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2016 Ahmed S. Darwish <darwish.07@gmail.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
+
+typedef enum pa_mem_type {
+    PA_MEM_TYPE_SHARED_POSIX,         /* Data is shared and created using POSIX shm_open() */
+    PA_MEM_TYPE_SHARED_MEMFD,         /* Data is shared and created using Linux memfd_create() */
+    PA_MEM_TYPE_PRIVATE,              /* Data is private and created using classic memory allocation
+                                         (posix_memalign(), malloc() or anonymous mmap()) */
+} pa_mem_type_t;
+
+static inline const char *pa_mem_type_to_string(pa_mem_type_t type) {
+    switch (type) {
+    case PA_MEM_TYPE_SHARED_POSIX:
+        return "shared posix-shm";
+    case PA_MEM_TYPE_SHARED_MEMFD:
+        return "shared memfd";
+    case PA_MEM_TYPE_PRIVATE:
+        return "private";
+    }
+
+    pa_assert_not_reached();
+}
+
+static inline bool pa_mem_type_is_shared(pa_mem_type_t t) {
+    return (t == PA_MEM_TYPE_SHARED_POSIX) || (t == PA_MEM_TYPE_SHARED_MEMFD);
+}
+
+static inline bool pa_memfd_is_locally_supported() {
+#if defined(HAVE_CREDS) && defined(HAVE_MEMFD)
+    return true;
+#else
+    return false;
+#endif
+}
+
+#endif
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
new file mode 100644 (file)
index 0000000..fb83dac
--- /dev/null
@@ -0,0 +1,1518 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/def.h>
+
+#include <pulsecore/shm.h>
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/memtrap.h>
+
+#include "memblock.h"
+
+/* We can allocate 64*1024*1024 bytes at maximum. That's 64MB. Please
+ * note that the footprint is usually much smaller, since the data is
+ * stored in SHM and our OS does not commit the memory before we use
+ * it for the first time. */
+#define PA_MEMPOOL_SLOTS_MAX 1024
+#define PA_MEMPOOL_SLOT_SIZE (64*1024)
+
+#define PA_MEMEXPORT_SLOTS_MAX 128
+
+#define PA_MEMIMPORT_SLOTS_MAX 160
+#define PA_MEMIMPORT_SEGMENTS_MAX 16
+
+struct pa_memblock {
+    PA_REFCNT_DECLARE; /* the reference counter */
+    pa_mempool *pool;
+
+    pa_memblock_type_t type;
+
+    bool read_only:1;
+    bool is_silence:1;
+
+    pa_atomic_ptr_t data;
+    size_t length;
+
+    pa_atomic_t n_acquired;
+    pa_atomic_t please_signal;
+
+    union {
+        struct {
+            /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
+            pa_free_cb_t free_cb;
+            /* If type == PA_MEMBLOCK_USER this is passed as free_cb argument */
+            void *free_cb_data;
+        } user;
+
+        struct {
+            uint32_t id;
+            pa_memimport_segment *segment;
+        } imported;
+    } per_type;
+};
+
+struct pa_memimport_segment {
+    pa_memimport *import;
+    pa_shm memory;
+    pa_memtrap *trap;
+    unsigned n_blocks;
+    bool writable;
+};
+
+/*
+ * If true, this segment's lifetime will not be limited by the
+ * number of active blocks (seg->n_blocks) using its shared memory.
+ * Rather, it will exist for the full lifetime of the memimport it
+ * is attached to.
+ *
+ * This is done to support memfd blocks transport.
+ *
+ * To transfer memfd-backed blocks without passing their fd every
+ * time, thus minimizing overhead and avoiding fd leaks, a command
+ * is sent with the memfd fd as ancil data very early on.
+ *
+ * This command has an ID that identifies the memfd region. Further
+ * block references are then exclusively done using this ID. On the
+ * receiving end, such logic is enabled by the memimport's segment
+ * hash and 'permanent' segments below.
+ */
+static bool segment_is_permanent(pa_memimport_segment *seg) {
+    pa_assert(seg);
+    return seg->memory.type == PA_MEM_TYPE_SHARED_MEMFD;
+}
+
+/* A collection of multiple segments */
+struct pa_memimport {
+    pa_mutex *mutex;
+
+    pa_mempool *pool;
+    pa_hashmap *segments;
+    pa_hashmap *blocks;
+
+    /* Called whenever an imported memory block is no longer
+     * needed. */
+    pa_memimport_release_cb_t release_cb;
+    void *userdata;
+
+    PA_LLIST_FIELDS(pa_memimport);
+};
+
+struct memexport_slot {
+    PA_LLIST_FIELDS(struct memexport_slot);
+    pa_memblock *block;
+};
+
+struct pa_memexport {
+    pa_mutex *mutex;
+    pa_mempool *pool;
+
+    struct memexport_slot slots[PA_MEMEXPORT_SLOTS_MAX];
+
+    PA_LLIST_HEAD(struct memexport_slot, free_slots);
+    PA_LLIST_HEAD(struct memexport_slot, used_slots);
+    unsigned n_init;
+    unsigned baseidx;
+
+    /* Called whenever a client from which we imported a memory block
+       which we in turn exported to another client dies and we need to
+       revoke the memory block accordingly */
+    pa_memexport_revoke_cb_t revoke_cb;
+    void *userdata;
+
+    PA_LLIST_FIELDS(pa_memexport);
+};
+
+struct pa_mempool {
+    /* Reference count the mempool
+     *
+     * Any block allocation from the pool itself, or even just imported from
+     * another process through SHM and attached to it (PA_MEMBLOCK_IMPORTED),
+     * shall increase the refcount.
+     *
+     * This is done for per-client mempools: global references to blocks in
+     * the pool, or just to attached ones, can still be lingering around when
+     * the client connection dies and all per-client objects are to be freed.
+     * That is, current PulseAudio design does not guarantee that the client
+     * mempool blocks are referenced only by client-specific objects.
+     *
+     * For further details, please check:
+     * https://lists.freedesktop.org/archives/pulseaudio-discuss/2016-February/025587.html
+     */
+    PA_REFCNT_DECLARE;
+
+    pa_semaphore *semaphore;
+    pa_mutex *mutex;
+
+    pa_shm memory;
+
+    bool global;
+
+    size_t block_size;
+    unsigned n_blocks;
+    bool is_remote_writable;
+
+    pa_atomic_t n_init;
+
+    PA_LLIST_HEAD(pa_memimport, imports);
+    PA_LLIST_HEAD(pa_memexport, exports);
+
+    /* A list of free slots that may be reused */
+    pa_flist *free_slots;
+
+    pa_mempool_stat stat;
+};
+
+static void segment_detach(pa_memimport_segment *seg);
+
+PA_STATIC_FLIST_DECLARE(unused_memblocks, 0, pa_xfree);
+
+/* No lock necessary */
+static void stat_add(pa_memblock*b) {
+    pa_assert(b);
+    pa_assert(b->pool);
+
+    pa_atomic_inc(&b->pool->stat.n_allocated);
+    pa_atomic_add(&b->pool->stat.allocated_size, (int) b->length);
+
+    pa_atomic_inc(&b->pool->stat.n_accumulated);
+    pa_atomic_add(&b->pool->stat.accumulated_size, (int) b->length);
+
+    if (b->type == PA_MEMBLOCK_IMPORTED) {
+        pa_atomic_inc(&b->pool->stat.n_imported);
+        pa_atomic_add(&b->pool->stat.imported_size, (int) b->length);
+    }
+
+    pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
+    pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);
+}
+
+/* No lock necessary */
+static void stat_remove(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(b->pool);
+
+    pa_assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0);
+    pa_assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);
+
+    pa_atomic_dec(&b->pool->stat.n_allocated);
+    pa_atomic_sub(&b->pool->stat.allocated_size, (int) b->length);
+
+    if (b->type == PA_MEMBLOCK_IMPORTED) {
+        pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
+        pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
+
+        pa_atomic_dec(&b->pool->stat.n_imported);
+        pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
+    }
+
+    pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
+}
+
+static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length);
+
+/* No lock necessary */
+pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
+    pa_memblock *b;
+
+    pa_assert(p);
+    pa_assert(length);
+
+    if (!(b = pa_memblock_new_pool(p, length)))
+        b = memblock_new_appended(p, length);
+
+    return b;
+}
+
+/* No lock necessary */
+static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
+    pa_memblock *b;
+
+    pa_assert(p);
+    pa_assert(length);
+
+    /* If -1 is passed as length we choose the size for the caller. */
+
+    if (length == (size_t) -1)
+        length = pa_mempool_block_size_max(p);
+
+    b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length);
+    PA_REFCNT_INIT(b);
+    b->pool = p;
+    pa_mempool_ref(b->pool);
+    b->type = PA_MEMBLOCK_APPENDED;
+    b->read_only = b->is_silence = false;
+    pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
+    b->length = length;
+    pa_atomic_store(&b->n_acquired, 0);
+    pa_atomic_store(&b->please_signal, 0);
+
+    stat_add(b);
+    return b;
+}
+
+/* No lock necessary */
+static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
+    struct mempool_slot *slot;
+    pa_assert(p);
+
+    if (!(slot = pa_flist_pop(p->free_slots))) {
+        int idx;
+
+        /* The free list was empty, we have to allocate a new entry */
+
+        if ((unsigned) (idx = pa_atomic_inc(&p->n_init)) >= p->n_blocks)
+            pa_atomic_dec(&p->n_init);
+        else
+            slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) idx));
+
+        if (!slot) {
+            if (pa_log_ratelimit(PA_LOG_DEBUG))
+                pa_log_debug("Pool full");
+            pa_atomic_inc(&p->stat.n_pool_full);
+            return NULL;
+        }
+    }
+
+/* #ifdef HAVE_VALGRIND_MEMCHECK_H */
+/*     if (PA_UNLIKELY(pa_in_valgrind())) { */
+/*         VALGRIND_MALLOCLIKE_BLOCK(slot, p->block_size, 0, 0); */
+/*     } */
+/* #endif */
+
+    return slot;
+}
+
+/* No lock necessary, totally redundant anyway */
+static inline void* mempool_slot_data(struct mempool_slot *slot) {
+    return slot;
+}
+
+/* No lock necessary */
+static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) {
+    pa_assert(p);
+
+    pa_assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
+    pa_assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
+
+    return (unsigned) ((size_t) ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size);
+}
+
+/* No lock necessary */
+static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) {
+    unsigned idx;
+
+    if ((idx = mempool_slot_idx(p, ptr)) == (unsigned) -1)
+        return NULL;
+
+    return (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (idx * p->block_size));
+}
+
+/* No lock necessary */
+bool pa_mempool_is_remote_writable(pa_mempool *p) {
+    pa_assert(p);
+    return p->is_remote_writable;
+}
+
+/* No lock necessary */
+void pa_mempool_set_is_remote_writable(pa_mempool *p, bool writable) {
+    pa_assert(p);
+    pa_assert(!writable || pa_mempool_is_shared(p));
+    p->is_remote_writable = writable;
+}
+
+/* No lock necessary */
+pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
+    pa_memblock *b = NULL;
+    struct mempool_slot *slot;
+    static int mempool_disable = 0;
+
+    pa_assert(p);
+    pa_assert(length);
+
+    if (mempool_disable == 0)
+        mempool_disable = getenv("PULSE_MEMPOOL_DISABLE") ? 1 : -1;
+
+    if (mempool_disable > 0)
+        return NULL;
+
+    /* If -1 is passed as length we choose the size for the caller: we
+     * take the largest size that fits in one of our slots. */
+
+    if (length == (size_t) -1)
+        length = pa_mempool_block_size_max(p);
+
+    if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) {
+
+        if (!(slot = mempool_allocate_slot(p)))
+            return NULL;
+
+        b = mempool_slot_data(slot);
+        b->type = PA_MEMBLOCK_POOL;
+        pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
+
+    } else if (p->block_size >= length) {
+
+        if (!(slot = mempool_allocate_slot(p)))
+            return NULL;
+
+        if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+            b = pa_xnew(pa_memblock, 1);
+
+        b->type = PA_MEMBLOCK_POOL_EXTERNAL;
+        pa_atomic_ptr_store(&b->data, mempool_slot_data(slot));
+
+    } else {
+        pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size);
+        pa_atomic_inc(&p->stat.n_too_large_for_pool);
+        return NULL;
+    }
+
+    PA_REFCNT_INIT(b);
+    b->pool = p;
+    pa_mempool_ref(b->pool);
+    b->read_only = b->is_silence = false;
+    b->length = length;
+    pa_atomic_store(&b->n_acquired, 0);
+    pa_atomic_store(&b->please_signal, 0);
+
+    stat_add(b);
+    return b;
+}
+
+/* No lock necessary */
+pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, bool read_only) {
+    pa_memblock *b;
+
+    pa_assert(p);
+    pa_assert(d);
+    pa_assert(length != (size_t) -1);
+    pa_assert(length);
+
+    if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+        b = pa_xnew(pa_memblock, 1);
+
+    PA_REFCNT_INIT(b);
+    b->pool = p;
+    pa_mempool_ref(b->pool);
+    b->type = PA_MEMBLOCK_FIXED;
+    b->read_only = read_only;
+    b->is_silence = false;
+    pa_atomic_ptr_store(&b->data, d);
+    b->length = length;
+    pa_atomic_store(&b->n_acquired, 0);
+    pa_atomic_store(&b->please_signal, 0);
+
+    stat_add(b);
+    return b;
+}
+
+/* No lock necessary */
+pa_memblock *pa_memblock_new_user(
+        pa_mempool *p,
+        void *d,
+        size_t length,
+        pa_free_cb_t free_cb,
+        void *free_cb_data,
+        bool read_only) {
+    pa_memblock *b;
+
+    pa_assert(p);
+    pa_assert(d);
+    pa_assert(length);
+    pa_assert(length != (size_t) -1);
+    pa_assert(free_cb);
+
+    if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+        b = pa_xnew(pa_memblock, 1);
+
+    PA_REFCNT_INIT(b);
+    b->pool = p;
+    pa_mempool_ref(b->pool);
+    b->type = PA_MEMBLOCK_USER;
+    b->read_only = read_only;
+    b->is_silence = false;
+    pa_atomic_ptr_store(&b->data, d);
+    b->length = length;
+    pa_atomic_store(&b->n_acquired, 0);
+    pa_atomic_store(&b->please_signal, 0);
+
+    b->per_type.user.free_cb = free_cb;
+    b->per_type.user.free_cb_data = free_cb_data;
+
+    stat_add(b);
+    return b;
+}
+
+/* No lock necessary */
+bool pa_memblock_is_ours(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    return b->type != PA_MEMBLOCK_IMPORTED;
+}
+
+/* No lock necessary */
+bool pa_memblock_is_read_only(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    return b->read_only || PA_REFCNT_VALUE(b) > 1;
+}
+
+/* No lock necessary */
+bool pa_memblock_is_silence(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    return b->is_silence;
+}
+
+/* No lock necessary */
+void pa_memblock_set_is_silence(pa_memblock *b, bool v) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    b->is_silence = v;
+}
+
+/* No lock necessary */
+bool pa_memblock_ref_is_one(pa_memblock *b) {
+    int r;
+    pa_assert(b);
+
+    pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
+
+    return r == 1;
+}
+
+/* No lock necessary */
+void* pa_memblock_acquire(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    pa_atomic_inc(&b->n_acquired);
+
+    return pa_atomic_ptr_load(&b->data);
+}
+
+/* No lock necessary */
+void *pa_memblock_acquire_chunk(const pa_memchunk *c) {
+    pa_assert(c);
+
+    return (uint8_t *) pa_memblock_acquire(c->memblock) + c->index;
+}
+
+/* No lock necessary, in corner cases locks by its own */
+void pa_memblock_release(pa_memblock *b) {
+    int r;
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    r = pa_atomic_dec(&b->n_acquired);
+    pa_assert(r >= 1);
+
+    /* Signal a waiting thread that this memblock is no longer used */
+    if (r == 1 && pa_atomic_load(&b->please_signal))
+        pa_semaphore_post(b->pool->semaphore);
+}
+
+size_t pa_memblock_get_length(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    return b->length;
+}
+
+/* Note! Always unref the returned pool after use */
+pa_mempool* pa_memblock_get_pool(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+    pa_assert(b->pool);
+
+    pa_mempool_ref(b->pool);
+    return b->pool;
+}
+
+/* No lock necessary */
+pa_memblock* pa_memblock_ref(pa_memblock*b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    PA_REFCNT_INC(b);
+    return b;
+}
+
+static void memblock_free(pa_memblock *b) {
+    pa_mempool *pool;
+
+    pa_assert(b);
+    pa_assert(b->pool);
+    pa_assert(pa_atomic_load(&b->n_acquired) == 0);
+
+    pool = b->pool;
+    stat_remove(b);
+
+    switch (b->type) {
+        case PA_MEMBLOCK_USER :
+            pa_assert(b->per_type.user.free_cb);
+            b->per_type.user.free_cb(b->per_type.user.free_cb_data);
+
+            /* Fall through */
+
+        case PA_MEMBLOCK_FIXED:
+            if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+                pa_xfree(b);
+
+            break;
+
+        case PA_MEMBLOCK_APPENDED:
+
+            /* We could attach it to unused_memblocks, but that would
+             * probably waste some considerable amount of memory */
+            pa_xfree(b);
+            break;
+
+        case PA_MEMBLOCK_IMPORTED: {
+            pa_memimport_segment *segment;
+            pa_memimport *import;
+
+            /* FIXME! This should be implemented lock-free */
+
+            pa_assert_se(segment = b->per_type.imported.segment);
+            pa_assert_se(import = segment->import);
+
+            pa_mutex_lock(import->mutex);
+
+            pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)));
+
+            pa_assert(segment->n_blocks >= 1);
+            if (-- segment->n_blocks <= 0)
+                segment_detach(segment);
+
+            pa_mutex_unlock(import->mutex);
+
+            import->release_cb(import, b->per_type.imported.id, import->userdata);
+
+            if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+                pa_xfree(b);
+
+            break;
+        }
+
+        case PA_MEMBLOCK_POOL_EXTERNAL:
+        case PA_MEMBLOCK_POOL: {
+            struct mempool_slot *slot;
+            bool call_free;
+
+            pa_assert_se(slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data)));
+
+            call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL;
+
+/* #ifdef HAVE_VALGRIND_MEMCHECK_H */
+/*             if (PA_UNLIKELY(pa_in_valgrind())) { */
+/*                 VALGRIND_FREELIKE_BLOCK(slot, b->pool->block_size); */
+/*             } */
+/* #endif */
+
+            /* The free list dimensions should easily allow all slots
+             * to fit in, hence try harder if pushing this slot into
+             * the free list fails */
+            while (pa_flist_push(b->pool->free_slots, slot) < 0)
+                ;
+
+            if (call_free)
+                if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+                    pa_xfree(b);
+
+            break;
+        }
+
+        case PA_MEMBLOCK_TYPE_MAX:
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_mempool_unref(pool);
+}
+
+/* No lock necessary */
+void pa_memblock_unref(pa_memblock*b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    if (PA_REFCNT_DEC(b) > 0)
+        return;
+
+    memblock_free(b);
+}
+
+/* Self locked */
+static void memblock_wait(pa_memblock *b) {
+    pa_assert(b);
+
+    if (pa_atomic_load(&b->n_acquired) > 0) {
+        /* We need to wait until all threads gave up access to the
+         * memory block before we can go on. Unfortunately this means
+         * that we have to lock and wait here. Sniff! */
+
+        pa_atomic_inc(&b->please_signal);
+
+        while (pa_atomic_load(&b->n_acquired) > 0)
+            pa_semaphore_wait(b->pool->semaphore);
+
+        pa_atomic_dec(&b->please_signal);
+    }
+}
+
+/* No lock necessary. This function is not multiple caller safe! */
+static void memblock_make_local(pa_memblock *b) {
+    pa_assert(b);
+
+    pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
+
+    if (b->length <= b->pool->block_size) {
+        struct mempool_slot *slot;
+
+        if ((slot = mempool_allocate_slot(b->pool))) {
+            void *new_data;
+            /* We can move it into a local pool, perfect! */
+
+            new_data = mempool_slot_data(slot);
+            memcpy(new_data, pa_atomic_ptr_load(&b->data), b->length);
+            pa_atomic_ptr_store(&b->data, new_data);
+
+            b->type = PA_MEMBLOCK_POOL_EXTERNAL;
+            b->read_only = false;
+
+            goto finish;
+        }
+    }
+
+    /* Humm, not enough space in the pool, so lets allocate the memory with malloc() */
+    b->per_type.user.free_cb = pa_xfree;
+    pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length));
+    b->per_type.user.free_cb_data = pa_atomic_ptr_load(&b->data);
+
+    b->type = PA_MEMBLOCK_USER;
+    b->read_only = false;
+
+finish:
+    pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
+    pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);
+    memblock_wait(b);
+}
+
+/* No lock necessary. This function is not multiple caller safe */
+void pa_memblock_unref_fixed(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+    pa_assert(b->type == PA_MEMBLOCK_FIXED);
+
+    if (PA_REFCNT_VALUE(b) > 1)
+        memblock_make_local(b);
+
+    pa_memblock_unref(b);
+}
+
+/* No lock necessary. */
+pa_memblock *pa_memblock_will_need(pa_memblock *b) {
+    void *p;
+
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    p = pa_memblock_acquire(b);
+    pa_will_need(p, b->length);
+    pa_memblock_release(b);
+
+    return b;
+}
+
+/* Self-locked. This function is not multiple-caller safe */
+static void memblock_replace_import(pa_memblock *b) {
+    pa_memimport_segment *segment;
+    pa_memimport *import;
+
+    pa_assert(b);
+    pa_assert(b->type == PA_MEMBLOCK_IMPORTED);
+
+    pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
+    pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
+    pa_atomic_dec(&b->pool->stat.n_imported);
+    pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
+
+    pa_assert_se(segment = b->per_type.imported.segment);
+    pa_assert_se(import = segment->import);
+
+    pa_mutex_lock(import->mutex);
+
+    pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)));
+
+    memblock_make_local(b);
+
+    pa_assert(segment->n_blocks >= 1);
+    if (-- segment->n_blocks <= 0)
+        segment_detach(segment);
+
+    pa_mutex_unlock(import->mutex);
+}
+
+/*@per_client: This is a security measure. By default this should
+ * be set to true where the created mempool is never shared with more
+ * than one client in the system. Set this to false if a global
+ * mempool, shared with all existing and future clients, is required.
+ *
+ * NOTE-1: Do not create any further global mempools! They allow data
+ * leaks between clients and thus conflict with the xdg-app containers
+ * model. They also complicate the handling of memfd-based pools.
+ *
+ * NOTE-2: Almost all mempools are now created on a per client basis.
+ * The only exception is the pa_core's mempool which is still shared
+ * between all clients of the system.
+ *
+ * Beside security issues, special marking for global mempools is
+ * required for memfd communication. To avoid fd leaks, memfd pools
+ * are registered with the connection pstream to create an ID<->memfd
+ * mapping on both PA endpoints. Such memory regions are then always
+ * referenced by their IDs and never by their fds and thus their fds
+ * can be quickly closed later.
+ *
+ * Unfortunately this scheme cannot work with global pools since the
+ * ID registration mechanism needs to happen for each newly connected
+ * client, and thus the need for a more special handling. That is,
+ * for the pool's fd to be always open :-(
+ *
+ * TODO-1: Transform the global core mempool to a per-client one
+ * TODO-2: Remove global mempools support */
+pa_mempool *pa_mempool_new(pa_mem_type_t type, size_t size, bool per_client) {
+    pa_mempool *p;
+    char t1[PA_BYTES_SNPRINT_MAX], t2[PA_BYTES_SNPRINT_MAX];
+    const size_t page_size = pa_page_size();
+
+    p = pa_xnew0(pa_mempool, 1);
+    PA_REFCNT_INIT(p);
+
+    p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE);
+    if (p->block_size < page_size)
+        p->block_size = page_size;
+
+    if (size <= 0)
+        p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
+    else {
+        p->n_blocks = (unsigned) (size / p->block_size);
+
+        if (p->n_blocks < 2)
+            p->n_blocks = 2;
+    }
+
+    if (pa_shm_create_rw(&p->memory, type, p->n_blocks * p->block_size, 0700) < 0) {
+        pa_xfree(p);
+        return NULL;
+    }
+
+    pa_log_debug("Using %s memory pool with %u slots of size %s each, total size is %s, maximum usable slot size is %lu",
+                 pa_mem_type_to_string(type),
+                 p->n_blocks,
+                 pa_bytes_snprint(t1, sizeof(t1), (unsigned) p->block_size),
+                 pa_bytes_snprint(t2, sizeof(t2), (unsigned) (p->n_blocks * p->block_size)),
+                 (unsigned long) pa_mempool_block_size_max(p));
+
+    p->global = !per_client;
+
+    pa_atomic_store(&p->n_init, 0);
+
+    PA_LLIST_HEAD_INIT(pa_memimport, p->imports);
+    PA_LLIST_HEAD_INIT(pa_memexport, p->exports);
+
+    p->mutex = pa_mutex_new(true, true);
+    p->semaphore = pa_semaphore_new(0);
+
+    p->free_slots = pa_flist_new(p->n_blocks);
+
+    return p;
+}
+
+static void mempool_free(pa_mempool *p) {
+    pa_assert(p);
+
+    pa_mutex_lock(p->mutex);
+
+    while (p->imports)
+        pa_memimport_free(p->imports);
+
+    while (p->exports)
+        pa_memexport_free(p->exports);
+
+    pa_mutex_unlock(p->mutex);
+
+    pa_flist_free(p->free_slots, NULL);
+
+    if (pa_atomic_load(&p->stat.n_allocated) > 0) {
+
+        /* Ouch, somebody is retaining a memory block reference! */
+
+#ifdef DEBUG_REF
+        unsigned i;
+        pa_flist *list;
+
+        /* Let's try to find at least one of those leaked memory blocks */
+
+        list = pa_flist_new(p->n_blocks);
+
+        for (i = 0; i < (unsigned) pa_atomic_load(&p->n_init); i++) {
+            struct mempool_slot *slot;
+            pa_memblock *b, *k;
+
+            slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) i));
+            b = mempool_slot_data(slot);
+
+            while ((k = pa_flist_pop(p->free_slots))) {
+                while (pa_flist_push(list, k) < 0)
+                    ;
+
+                if (b == k)
+                    break;
+            }
+
+            if (!k)
+                pa_log("REF: Leaked memory block %p", b);
+
+            while ((k = pa_flist_pop(list)))
+                while (pa_flist_push(p->free_slots, k) < 0)
+                    ;
+        }
+
+        pa_flist_free(list, NULL);
+
+#endif
+
+        pa_log_error("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
+
+/*         PA_DEBUG_TRAP; */
+    }
+
+    pa_shm_free(&p->memory);
+
+    pa_mutex_free(p->mutex);
+    pa_semaphore_free(p->semaphore);
+
+    pa_xfree(p);
+}
+
+/* No lock necessary */
+const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) {
+    pa_assert(p);
+
+    return &p->stat;
+}
+
+/* No lock necessary */
+size_t pa_mempool_block_size_max(pa_mempool *p) {
+    pa_assert(p);
+
+    return p->block_size - PA_ALIGN(sizeof(pa_memblock));
+}
+
+/* No lock necessary */
+void pa_mempool_vacuum(pa_mempool *p) {
+    struct mempool_slot *slot;
+    pa_flist *list;
+
+    pa_assert(p);
+
+    list = pa_flist_new(p->n_blocks);
+
+    while ((slot = pa_flist_pop(p->free_slots)))
+        while (pa_flist_push(list, slot) < 0)
+            ;
+
+    while ((slot = pa_flist_pop(list))) {
+        pa_shm_punch(&p->memory, (size_t) ((uint8_t*) slot - (uint8_t*) p->memory.ptr), p->block_size);
+
+        while (pa_flist_push(p->free_slots, slot))
+            ;
+    }
+
+    pa_flist_free(list, NULL);
+}
+
+/* No lock necessary */
+bool pa_mempool_is_shared(pa_mempool *p) {
+    pa_assert(p);
+
+    return pa_mem_type_is_shared(p->memory.type);
+}
+
+/* No lock necessary */
+bool pa_mempool_is_memfd_backed(const pa_mempool *p) {
+    pa_assert(p);
+
+    return (p->memory.type == PA_MEM_TYPE_SHARED_MEMFD);
+}
+
+/* No lock necessary */
+int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
+    pa_assert(p);
+
+    if (!pa_mempool_is_shared(p))
+        return -1;
+
+    *id = p->memory.id;
+
+    return 0;
+}
+
+pa_mempool* pa_mempool_ref(pa_mempool *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    PA_REFCNT_INC(p);
+    return p;
+}
+
+void pa_mempool_unref(pa_mempool *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (PA_REFCNT_DEC(p) <= 0)
+        mempool_free(p);
+}
+
+/* No lock necessary
+ * Check pa_mempool_new() for per-client vs. global mempools */
+bool pa_mempool_is_global(pa_mempool *p) {
+    pa_assert(p);
+
+    return p->global;
+}
+
+/* No lock necessary
+ * Check pa_mempool_new() for per-client vs. global mempools */
+bool pa_mempool_is_per_client(pa_mempool *p) {
+    return !pa_mempool_is_global(p);
+}
+
+/* Self-locked
+ *
+ * This is only for per-client mempools!
+ *
+ * After this method's return, the caller owns the file descriptor
+ * and is responsible for closing it in the appropriate time. This
+ * should only be called once during during a mempool's lifetime.
+ *
+ * Check pa_shm->fd and pa_mempool_new() for further context. */
+int pa_mempool_take_memfd_fd(pa_mempool *p) {
+    int memfd_fd;
+
+    pa_assert(p);
+    pa_assert(pa_mempool_is_shared(p));
+    pa_assert(pa_mempool_is_memfd_backed(p));
+    pa_assert(pa_mempool_is_per_client(p));
+
+    pa_mutex_lock(p->mutex);
+
+    memfd_fd = p->memory.fd;
+    p->memory.fd = -1;
+
+    pa_mutex_unlock(p->mutex);
+
+    pa_assert(memfd_fd != -1);
+    return memfd_fd;
+}
+
+/* No lock necessary
+ *
+ * This is only for global mempools!
+ *
+ * Global mempools have their memfd descriptor always open. DO NOT
+ * close the returned descriptor by your own.
+ *
+ * Check pa_mempool_new() for further context. */
+int pa_mempool_get_memfd_fd(pa_mempool *p) {
+    int memfd_fd;
+
+    pa_assert(p);
+    pa_assert(pa_mempool_is_shared(p));
+    pa_assert(pa_mempool_is_memfd_backed(p));
+    pa_assert(pa_mempool_is_global(p));
+
+    memfd_fd = p->memory.fd;
+    pa_assert(memfd_fd != -1);
+
+    return memfd_fd;
+}
+
+/* For receiving blocks from other nodes */
+pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata) {
+    pa_memimport *i;
+
+    pa_assert(p);
+    pa_assert(cb);
+
+    i = pa_xnew(pa_memimport, 1);
+    i->mutex = pa_mutex_new(true, true);
+    i->pool = p;
+    pa_mempool_ref(i->pool);
+    i->segments = pa_hashmap_new(NULL, NULL);
+    i->blocks = pa_hashmap_new(NULL, NULL);
+    i->release_cb = cb;
+    i->userdata = userdata;
+
+    pa_mutex_lock(p->mutex);
+    PA_LLIST_PREPEND(pa_memimport, p->imports, i);
+    pa_mutex_unlock(p->mutex);
+
+    return i;
+}
+
+static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i);
+
+/* Should be called locked
+ * Caller owns passed @memfd_fd and must close it down when appropriate. */
+static pa_memimport_segment* segment_attach(pa_memimport *i, pa_mem_type_t type, uint32_t shm_id,
+                                            int memfd_fd, bool writable) {
+    pa_memimport_segment* seg;
+    pa_assert(pa_mem_type_is_shared(type));
+
+    if (pa_hashmap_size(i->segments) >= PA_MEMIMPORT_SEGMENTS_MAX)
+        return NULL;
+
+    seg = pa_xnew0(pa_memimport_segment, 1);
+
+    if (pa_shm_attach(&seg->memory, type, shm_id, memfd_fd, writable) < 0) {
+        pa_xfree(seg);
+        return NULL;
+    }
+
+    seg->writable = writable;
+    seg->import = i;
+    seg->trap = pa_memtrap_add(seg->memory.ptr, seg->memory.size);
+
+    pa_hashmap_put(i->segments, PA_UINT32_TO_PTR(seg->memory.id), seg);
+    return seg;
+}
+
+/* Should be called locked */
+static void segment_detach(pa_memimport_segment *seg) {
+    pa_assert(seg);
+    pa_assert(seg->n_blocks == (segment_is_permanent(seg) ? 1u : 0u));
+
+    pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id));
+    pa_shm_free(&seg->memory);
+
+    if (seg->trap)
+        pa_memtrap_remove(seg->trap);
+
+    pa_xfree(seg);
+}
+
+/* Self-locked. Not multiple-caller safe */
+void pa_memimport_free(pa_memimport *i) {
+    pa_memexport *e;
+    pa_memblock *b;
+    pa_memimport_segment *seg;
+    void *state = NULL;
+
+    pa_assert(i);
+
+    pa_mutex_lock(i->mutex);
+
+    while ((b = pa_hashmap_first(i->blocks)))
+        memblock_replace_import(b);
+
+    /* Permanent segments exist for the lifetime of the memimport. Now
+     * that we're freeing the memimport itself, clear them all up.
+     *
+     * Careful! segment_detach() internally removes itself from the
+     * memimport's hash; the same hash we're now using for iteration. */
+    PA_HASHMAP_FOREACH(seg, i->segments, state) {
+        if (segment_is_permanent(seg))
+            segment_detach(seg);
+    }
+    pa_assert(pa_hashmap_size(i->segments) == 0);
+
+    pa_mutex_unlock(i->mutex);
+
+    pa_mutex_lock(i->pool->mutex);
+
+    /* If we've exported this block further we need to revoke that export */
+    for (e = i->pool->exports; e; e = e->next)
+        memexport_revoke_blocks(e, i);
+
+    PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i);
+
+    pa_mutex_unlock(i->pool->mutex);
+
+    pa_mempool_unref(i->pool);
+    pa_hashmap_free(i->blocks);
+    pa_hashmap_free(i->segments);
+
+    pa_mutex_free(i->mutex);
+
+    pa_xfree(i);
+}
+
+/* Create a new memimport's memfd segment entry, with passed SHM ID
+ * as key and the newly-created segment (with its mmap()-ed memfd
+ * memory region) as its value.
+ *
+ * Note! check comments at 'pa_shm->fd', 'segment_is_permanent()',
+ * and 'pa_pstream_register_memfd_mempool()' for further details.
+ *
+ * Caller owns passed @memfd_fd and must close it down when appropriate. */
+int pa_memimport_attach_memfd(pa_memimport *i, uint32_t shm_id, int memfd_fd, bool writable) {
+    pa_memimport_segment *seg;
+    int ret = -1;
+
+    pa_assert(i);
+    pa_assert(memfd_fd != -1);
+
+    pa_mutex_lock(i->mutex);
+
+    if (!(seg = segment_attach(i, PA_MEM_TYPE_SHARED_MEMFD, shm_id, memfd_fd, writable)))
+        goto finish;
+
+    /* n_blocks acts as a segment reference count. To avoid the segment
+     * being deleted when receiving silent memchunks, etc., mark our
+     * permanent presence by incrementing that refcount. */
+    seg->n_blocks++;
+
+    pa_assert(segment_is_permanent(seg));
+    ret = 0;
+
+finish:
+    pa_mutex_unlock(i->mutex);
+    return ret;
+}
+
+/* Self-locked */
+pa_memblock* pa_memimport_get(pa_memimport *i, pa_mem_type_t type, uint32_t block_id, uint32_t shm_id,
+                              size_t offset, size_t size, bool writable) {
+    pa_memblock *b = NULL;
+    pa_memimport_segment *seg;
+
+    pa_assert(i);
+    pa_assert(pa_mem_type_is_shared(type));
+
+    pa_mutex_lock(i->mutex);
+
+    if ((b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(block_id)))) {
+        pa_memblock_ref(b);
+        goto finish;
+    }
+
+    if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX)
+        goto finish;
+
+    if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id)))) {
+        if (type == PA_MEM_TYPE_SHARED_MEMFD) {
+            pa_log("Bailing out! No cached memimport segment for memfd ID %u", shm_id);
+            pa_log("Did the other PA endpoint forget registering its memfd pool?");
+            goto finish;
+        }
+
+        pa_assert(type == PA_MEM_TYPE_SHARED_POSIX);
+        if (!(seg = segment_attach(i, type, shm_id, -1, writable)))
+            goto finish;
+    }
+
+    if (writable && !seg->writable) {
+        pa_log("Cannot import cached segment in write mode - previously mapped as read-only");
+        goto finish;
+    }
+
+    if (offset+size > seg->memory.size)
+        goto finish;
+
+    if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+        b = pa_xnew(pa_memblock, 1);
+
+    PA_REFCNT_INIT(b);
+    b->pool = i->pool;
+    pa_mempool_ref(b->pool);
+    b->type = PA_MEMBLOCK_IMPORTED;
+    b->read_only = !writable;
+    b->is_silence = false;
+    pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);
+    b->length = size;
+    pa_atomic_store(&b->n_acquired, 0);
+    pa_atomic_store(&b->please_signal, 0);
+    b->per_type.imported.id = block_id;
+    b->per_type.imported.segment = seg;
+
+    pa_hashmap_put(i->blocks, PA_UINT32_TO_PTR(block_id), b);
+
+    seg->n_blocks++;
+
+    stat_add(b);
+
+finish:
+    pa_mutex_unlock(i->mutex);
+
+    return b;
+}
+
+int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) {
+    pa_memblock *b;
+    int ret = 0;
+    pa_assert(i);
+
+    pa_mutex_lock(i->mutex);
+
+    if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id)))) {
+        ret = -1;
+        goto finish;
+    }
+
+    memblock_replace_import(b);
+
+finish:
+    pa_mutex_unlock(i->mutex);
+
+    return ret;
+}
+
+/* For sending blocks to other nodes */
+pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata) {
+    pa_memexport *e;
+
+    static pa_atomic_t export_baseidx = PA_ATOMIC_INIT(0);
+
+    pa_assert(p);
+    pa_assert(cb);
+
+    if (!pa_mempool_is_shared(p))
+        return NULL;
+
+    e = pa_xnew(pa_memexport, 1);
+    e->mutex = pa_mutex_new(true, true);
+    e->pool = p;
+    pa_mempool_ref(e->pool);
+    PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots);
+    PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots);
+    e->n_init = 0;
+    e->revoke_cb = cb;
+    e->userdata = userdata;
+
+    pa_mutex_lock(p->mutex);
+
+    PA_LLIST_PREPEND(pa_memexport, p->exports, e);
+    e->baseidx = (uint32_t) pa_atomic_add(&export_baseidx, PA_MEMEXPORT_SLOTS_MAX);
+
+    pa_mutex_unlock(p->mutex);
+    return e;
+}
+
+void pa_memexport_free(pa_memexport *e) {
+    pa_assert(e);
+
+    pa_mutex_lock(e->mutex);
+    while (e->used_slots)
+        pa_memexport_process_release(e, (uint32_t) (e->used_slots - e->slots + e->baseidx));
+    pa_mutex_unlock(e->mutex);
+
+    pa_mutex_lock(e->pool->mutex);
+    PA_LLIST_REMOVE(pa_memexport, e->pool->exports, e);
+    pa_mutex_unlock(e->pool->mutex);
+
+    pa_mempool_unref(e->pool);
+    pa_mutex_free(e->mutex);
+    pa_xfree(e);
+}
+
+/* Self-locked */
+int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
+    pa_memblock *b;
+
+    pa_assert(e);
+
+    pa_mutex_lock(e->mutex);
+
+    if (id < e->baseidx)
+        goto fail;
+    id -= e->baseidx;
+
+    if (id >= e->n_init)
+        goto fail;
+
+    if (!e->slots[id].block)
+        goto fail;
+
+    b = e->slots[id].block;
+    e->slots[id].block = NULL;
+
+    PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]);
+    PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]);
+
+    pa_mutex_unlock(e->mutex);
+
+/*     pa_log("Processing release for %u", id); */
+
+    pa_assert(pa_atomic_load(&e->pool->stat.n_exported) > 0);
+    pa_assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length);
+
+    pa_atomic_dec(&e->pool->stat.n_exported);
+    pa_atomic_sub(&e->pool->stat.exported_size, (int) b->length);
+
+    pa_memblock_unref(b);
+
+    return 0;
+
+fail:
+    pa_mutex_unlock(e->mutex);
+
+    return -1;
+}
+
+/* Self-locked */
+static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
+    struct memexport_slot *slot, *next;
+    pa_assert(e);
+    pa_assert(i);
+
+    pa_mutex_lock(e->mutex);
+
+    for (slot = e->used_slots; slot; slot = next) {
+        uint32_t idx;
+        next = slot->next;
+
+        if (slot->block->type != PA_MEMBLOCK_IMPORTED ||
+            slot->block->per_type.imported.segment->import != i)
+            continue;
+
+        idx = (uint32_t) (slot - e->slots + e->baseidx);
+        e->revoke_cb(e, idx, e->userdata);
+        pa_memexport_process_release(e, idx);
+    }
+
+    pa_mutex_unlock(e->mutex);
+}
+
+/* No lock necessary */
+static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) {
+    pa_memblock *n;
+
+    pa_assert(p);
+    pa_assert(b);
+
+    if (b->type == PA_MEMBLOCK_IMPORTED ||
+        b->type == PA_MEMBLOCK_POOL ||
+        b->type == PA_MEMBLOCK_POOL_EXTERNAL) {
+        pa_assert(b->pool == p);
+        return pa_memblock_ref(b);
+    }
+
+    if (!(n = pa_memblock_new_pool(p, b->length)))
+        return NULL;
+
+    memcpy(pa_atomic_ptr_load(&n->data), pa_atomic_ptr_load(&b->data), b->length);
+    return n;
+}
+
+/* Self-locked */
+int pa_memexport_put(pa_memexport *e, pa_memblock *b, pa_mem_type_t *type, uint32_t *block_id,
+                     uint32_t *shm_id, size_t *offset, size_t * size) {
+    pa_shm  *memory;
+    struct memexport_slot *slot;
+    void *data;
+
+    pa_assert(e);
+    pa_assert(b);
+    pa_assert(type);
+    pa_assert(block_id);
+    pa_assert(shm_id);
+    pa_assert(offset);
+    pa_assert(size);
+    pa_assert(b->pool == e->pool);
+
+    if (!(b = memblock_shared_copy(e->pool, b)))
+        return -1;
+
+    pa_mutex_lock(e->mutex);
+
+    if (e->free_slots) {
+        slot = e->free_slots;
+        PA_LLIST_REMOVE(struct memexport_slot, e->free_slots, slot);
+    } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX)
+        slot = &e->slots[e->n_init++];
+    else {
+        pa_mutex_unlock(e->mutex);
+        pa_memblock_unref(b);
+        return -1;
+    }
+
+    PA_LLIST_PREPEND(struct memexport_slot, e->used_slots, slot);
+    slot->block = b;
+    *block_id = (uint32_t) (slot - e->slots + e->baseidx);
+
+    pa_mutex_unlock(e->mutex);
+/*     pa_log("Got block id %u", *block_id); */
+
+    data = pa_memblock_acquire(b);
+
+    if (b->type == PA_MEMBLOCK_IMPORTED) {
+        pa_assert(b->per_type.imported.segment);
+        memory = &b->per_type.imported.segment->memory;
+    } else {
+        pa_assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
+        pa_assert(b->pool);
+        pa_assert(pa_mempool_is_shared(b->pool));
+        memory = &b->pool->memory;
+    }
+
+    pa_assert(data >= memory->ptr);
+    pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);
+
+    *type = memory->type;
+    *shm_id = memory->id;
+    *offset = (size_t) ((uint8_t*) data - (uint8_t*) memory->ptr);
+    *size = b->length;
+
+    pa_memblock_release(b);
+
+    pa_atomic_inc(&e->pool->stat.n_exported);
+    pa_atomic_add(&e->pool->stat.exported_size, (int) b->length);
+
+    return 0;
+}
diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h
new file mode 100644 (file)
index 0000000..57ae4b2
--- /dev/null
@@ -0,0 +1,159 @@
+#ifndef foopulsememblockhfoo
+#define foopulsememblockhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct pa_memblock pa_memblock;
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include <pulse/def.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/mem.h>
+
+/* A pa_memblock is a reference counted memory block. PulseAudio
+ * passes references to pa_memblocks around instead of copying
+ * data. See pa_memchunk for a structure that describes parts of
+ * memory blocks. */
+
+/* The type of memory this block points to */
+typedef enum pa_memblock_type {
+    PA_MEMBLOCK_POOL,             /* Memory is part of the memory pool */
+    PA_MEMBLOCK_POOL_EXTERNAL,    /* Data memory is part of the memory pool but the pa_memblock structure itself is not */
+    PA_MEMBLOCK_APPENDED,         /* The data is appended to the memory block */
+    PA_MEMBLOCK_USER,             /* User supplied memory, to be freed with free_cb */
+    PA_MEMBLOCK_FIXED,            /* Data is a pointer to fixed memory that needs not to be freed */
+    PA_MEMBLOCK_IMPORTED,         /* Memory is imported from another process via shm */
+    PA_MEMBLOCK_TYPE_MAX
+} pa_memblock_type_t;
+
+typedef struct pa_mempool pa_mempool;
+typedef struct pa_mempool_stat pa_mempool_stat;
+typedef struct pa_memimport_segment pa_memimport_segment;
+typedef struct pa_memimport pa_memimport;
+typedef struct pa_memexport pa_memexport;
+
+typedef void (*pa_memimport_release_cb_t)(pa_memimport *i, uint32_t block_id, void *userdata);
+typedef void (*pa_memexport_revoke_cb_t)(pa_memexport *e, uint32_t block_id, void *userdata);
+
+/* Please note that updates to this structure are not locked,
+ * i.e. n_allocated might be updated at a point in time where
+ * n_accumulated is not yet. Take these values with a grain of salt,
+ * they are here for purely statistical reasons.*/
+struct pa_mempool_stat {
+    pa_atomic_t n_allocated;
+    pa_atomic_t n_accumulated;
+    pa_atomic_t n_imported;
+    pa_atomic_t n_exported;
+    pa_atomic_t allocated_size;
+    pa_atomic_t accumulated_size;
+    pa_atomic_t imported_size;
+    pa_atomic_t exported_size;
+
+    pa_atomic_t n_too_large_for_pool;
+    pa_atomic_t n_pool_full;
+
+    pa_atomic_t n_allocated_by_type[PA_MEMBLOCK_TYPE_MAX];
+    pa_atomic_t n_accumulated_by_type[PA_MEMBLOCK_TYPE_MAX];
+};
+
+/* Allocate a new memory block of type PA_MEMBLOCK_MEMPOOL or PA_MEMBLOCK_APPENDED, depending on the size */
+pa_memblock *pa_memblock_new(pa_mempool *, size_t length);
+
+/* Allocate a new memory block of type PA_MEMBLOCK_MEMPOOL. If the requested size is too large, return NULL */
+pa_memblock *pa_memblock_new_pool(pa_mempool *, size_t length);
+
+/* Allocate a new memory block of type PA_MEMBLOCK_USER */
+pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, pa_free_cb_t free_cb, void *free_cb_data, bool read_only);
+
+/* A special case of pa_memblock_new_user: take a memory buffer previously allocated with pa_xmalloc()  */
+static inline pa_memblock *pa_memblock_new_malloced(pa_mempool *p, void *data, size_t length) {
+    return pa_memblock_new_user(p, data, length, pa_xfree, data, 0);
+}
+
+/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */
+pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, bool read_only);
+
+void pa_memblock_unref(pa_memblock*b);
+pa_memblock* pa_memblock_ref(pa_memblock*b);
+
+/* This special unref function has to be called by the owner of the
+memory of a static memory block when he wants to release all
+references to the memory. This causes the memory to be copied and
+converted into a pool of malloc'ed memory block. Please note that this
+function is not multiple caller safe, i.e. needs to be locked
+manually if called from more than one thread at the same time. */
+void pa_memblock_unref_fixed(pa_memblock*b);
+
+bool pa_memblock_is_ours(pa_memblock *b);
+bool pa_memblock_is_read_only(pa_memblock *b);
+bool pa_memblock_is_silence(pa_memblock *b);
+bool pa_memblock_ref_is_one(pa_memblock *b);
+void pa_memblock_set_is_silence(pa_memblock *b, bool v);
+
+void* pa_memblock_acquire(pa_memblock *b);
+void *pa_memblock_acquire_chunk(const pa_memchunk *c);
+void pa_memblock_release(pa_memblock *b);
+
+size_t pa_memblock_get_length(pa_memblock *b);
+
+/* Note! Always unref the returned pool after use */
+pa_mempool * pa_memblock_get_pool(pa_memblock *b);
+
+pa_memblock *pa_memblock_will_need(pa_memblock *b);
+
+/* The memory block manager */
+pa_mempool *pa_mempool_new(pa_mem_type_t type, size_t size, bool per_client);
+void pa_mempool_unref(pa_mempool *p);
+pa_mempool* pa_mempool_ref(pa_mempool *p);
+const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p);
+void pa_mempool_vacuum(pa_mempool *p);
+int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id);
+bool pa_mempool_is_shared(pa_mempool *p);
+bool pa_mempool_is_memfd_backed(const pa_mempool *p);
+bool pa_mempool_is_global(pa_mempool *p);
+bool pa_mempool_is_per_client(pa_mempool *p);
+bool pa_mempool_is_remote_writable(pa_mempool *p);
+void pa_mempool_set_is_remote_writable(pa_mempool *p, bool writable);
+size_t pa_mempool_block_size_max(pa_mempool *p);
+
+int pa_mempool_take_memfd_fd(pa_mempool *p);
+int pa_mempool_get_memfd_fd(pa_mempool *p);
+
+/* For receiving blocks from other nodes */
+pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata);
+void pa_memimport_free(pa_memimport *i);
+int pa_memimport_attach_memfd(pa_memimport *i, uint32_t shm_id, int memfd_fd, bool writable);
+pa_memblock* pa_memimport_get(pa_memimport *i, pa_mem_type_t type, uint32_t block_id,
+                              uint32_t shm_id, size_t offset, size_t size, bool writable);
+int pa_memimport_process_revoke(pa_memimport *i, uint32_t block_id);
+
+/* For sending blocks to other nodes */
+pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata);
+void pa_memexport_free(pa_memexport *e);
+int pa_memexport_put(pa_memexport *e, pa_memblock *b, pa_mem_type_t *type, uint32_t *block_id,
+                     uint32_t *shm_id, size_t *offset, size_t * size);
+int pa_memexport_process_release(pa_memexport *e, uint32_t id);
+
+#endif
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
new file mode 100644 (file)
index 0000000..b132dd3
--- /dev/null
@@ -0,0 +1,1019 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/mcalign.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+
+#include "memblockq.h"
+
+/* #define MEMBLOCKQ_DEBUG */
+
+struct list_item {
+    struct list_item *next, *prev;
+    int64_t index;
+    pa_memchunk chunk;
+};
+
+PA_STATIC_FLIST_DECLARE(list_items, 0, pa_xfree);
+
+struct pa_memblockq {
+    struct list_item *blocks, *blocks_tail;
+    struct list_item *current_read, *current_write;
+    unsigned n_blocks;
+    size_t maxlength, tlength, base, prebuf, minreq, maxrewind;
+    int64_t read_index, write_index;
+    bool in_prebuf;
+    pa_memchunk silence;
+    pa_mcalign *mcalign;
+    int64_t missing, requested;
+    char *name;
+    pa_sample_spec sample_spec;
+};
+
+pa_memblockq* pa_memblockq_new(
+        const char *name,
+        int64_t idx,
+        size_t maxlength,
+        size_t tlength,
+        const pa_sample_spec *sample_spec,
+        size_t prebuf,
+        size_t minreq,
+        size_t maxrewind,
+        pa_memchunk *silence) {
+
+    pa_memblockq* bq;
+
+    pa_assert(sample_spec);
+    pa_assert(name);
+
+    bq = pa_xnew0(pa_memblockq, 1);
+    bq->name = pa_xstrdup(name);
+
+    bq->sample_spec = *sample_spec;
+    bq->base = pa_frame_size(sample_spec);
+    bq->read_index = bq->write_index = idx;
+
+    pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
+                 (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) bq->base, (unsigned long) prebuf, (unsigned long) minreq, (unsigned long) maxrewind);
+
+    bq->in_prebuf = true;
+
+    pa_memblockq_set_maxlength(bq, maxlength);
+    pa_memblockq_set_tlength(bq, tlength);
+    pa_memblockq_set_minreq(bq, minreq);
+    pa_memblockq_set_prebuf(bq, prebuf);
+    pa_memblockq_set_maxrewind(bq, maxrewind);
+
+    pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
+                 (unsigned long) bq->maxlength, (unsigned long) bq->tlength, (unsigned long) bq->base, (unsigned long) bq->prebuf, (unsigned long) bq->minreq, (unsigned long) bq->maxrewind);
+
+    if (silence) {
+        bq->silence = *silence;
+        pa_memblock_ref(bq->silence.memblock);
+    }
+
+    bq->mcalign = pa_mcalign_new(bq->base);
+
+    return bq;
+}
+
+void pa_memblockq_free(pa_memblockq* bq) {
+    pa_assert(bq);
+
+    pa_memblockq_silence(bq);
+
+    if (bq->silence.memblock)
+        pa_memblock_unref(bq->silence.memblock);
+
+    if (bq->mcalign)
+        pa_mcalign_free(bq->mcalign);
+
+    pa_xfree(bq->name);
+    pa_xfree(bq);
+}
+
+static void fix_current_read(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (PA_UNLIKELY(!bq->blocks)) {
+        bq->current_read = NULL;
+        return;
+    }
+
+    if (PA_UNLIKELY(!bq->current_read))
+        bq->current_read = bq->blocks;
+
+    /* Scan left */
+    while (PA_UNLIKELY(bq->current_read->index > bq->read_index))
+
+        if (bq->current_read->prev)
+            bq->current_read = bq->current_read->prev;
+        else
+            break;
+
+    /* Scan right */
+    while (PA_LIKELY(bq->current_read != NULL) && PA_UNLIKELY(bq->current_read->index + (int64_t) bq->current_read->chunk.length <= bq->read_index))
+        bq->current_read = bq->current_read->next;
+
+    /* At this point current_read will either point at or left of the
+       next block to play. It may be NULL in case everything in
+       the queue was already played */
+}
+
+static void fix_current_write(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (PA_UNLIKELY(!bq->blocks)) {
+        bq->current_write = NULL;
+        return;
+    }
+
+    if (PA_UNLIKELY(!bq->current_write))
+        bq->current_write = bq->blocks_tail;
+
+    /* Scan right */
+    while (PA_UNLIKELY(bq->current_write->index + (int64_t) bq->current_write->chunk.length <= bq->write_index))
+
+        if (bq->current_write->next)
+            bq->current_write = bq->current_write->next;
+        else
+            break;
+
+    /* Scan left */
+    while (PA_LIKELY(bq->current_write != NULL) && PA_UNLIKELY(bq->current_write->index > bq->write_index))
+        bq->current_write = bq->current_write->prev;
+
+    /* At this point current_write will either point at or right of
+       the next block to write data to. It may be NULL in case
+       everything in the queue is still to be played */
+}
+
+static void drop_block(pa_memblockq *bq, struct list_item *q) {
+    pa_assert(bq);
+    pa_assert(q);
+
+    pa_assert(bq->n_blocks >= 1);
+
+    if (q->prev)
+        q->prev->next = q->next;
+    else {
+        pa_assert(bq->blocks == q);
+        bq->blocks = q->next;
+    }
+
+    if (q->next)
+        q->next->prev = q->prev;
+    else {
+        pa_assert(bq->blocks_tail == q);
+        bq->blocks_tail = q->prev;
+    }
+
+    if (bq->current_write == q)
+        bq->current_write = q->prev;
+
+    if (bq->current_read == q)
+        bq->current_read = q->next;
+
+    pa_memblock_unref(q->chunk.memblock);
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(list_items), q) < 0)
+        pa_xfree(q);
+
+    bq->n_blocks--;
+}
+
+static void drop_backlog(pa_memblockq *bq) {
+    int64_t boundary;
+    pa_assert(bq);
+
+    boundary = bq->read_index - (int64_t) bq->maxrewind;
+
+    while (bq->blocks && (bq->blocks->index + (int64_t) bq->blocks->chunk.length <= boundary))
+        drop_block(bq, bq->blocks);
+}
+
+static bool can_push(pa_memblockq *bq, size_t l) {
+    int64_t end;
+
+    pa_assert(bq);
+
+    if (bq->read_index > bq->write_index) {
+        int64_t d = bq->read_index - bq->write_index;
+
+        if ((int64_t) l > d)
+            l -= (size_t) d;
+        else
+            return true;
+    }
+
+    end = bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->write_index;
+
+    /* Make sure that the list doesn't get too long */
+    if (bq->write_index + (int64_t) l > end)
+        if (bq->write_index + (int64_t) l - bq->read_index > (int64_t) bq->maxlength)
+            return false;
+
+    return true;
+}
+
+static void write_index_changed(pa_memblockq *bq, int64_t old_write_index, bool account) {
+    int64_t delta;
+
+    pa_assert(bq);
+
+    delta = bq->write_index - old_write_index;
+
+    if (account)
+        bq->requested -= delta;
+    else
+        bq->missing -= delta;
+
+#ifdef MEMBLOCKQ_DEBUG
+     pa_log_debug("[%s] pushed/seeked %lli: requested counter at %lli, account=%i", bq->name, (long long) delta, (long long) bq->requested, account);
+#endif
+}
+
+static void read_index_changed(pa_memblockq *bq, int64_t old_read_index) {
+    int64_t delta;
+
+    pa_assert(bq);
+
+    delta = bq->read_index - old_read_index;
+    bq->missing += delta;
+
+#ifdef MEMBLOCKQ_DEBUG
+    pa_log_debug("[%s] popped %lli: missing counter at %lli", bq->name, (long long) delta, (long long) bq->missing);
+#endif
+}
+
+int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
+    struct list_item *q, *n;
+    pa_memchunk chunk;
+    int64_t old;
+
+    pa_assert(bq);
+    pa_assert(uchunk);
+    pa_assert(uchunk->memblock);
+    pa_assert(uchunk->length > 0);
+    pa_assert(uchunk->index + uchunk->length <= pa_memblock_get_length(uchunk->memblock));
+
+    pa_assert(uchunk->length % bq->base == 0);
+    pa_assert(uchunk->index % bq->base == 0);
+
+    if (!can_push(bq, uchunk->length))
+        return -1;
+
+    old = bq->write_index;
+    chunk = *uchunk;
+
+    fix_current_write(bq);
+    q = bq->current_write;
+
+    /* First we advance the q pointer right of where we want to
+     * write to */
+
+    if (q) {
+        while (bq->write_index + (int64_t) chunk.length > q->index)
+            if (q->next)
+                q = q->next;
+            else
+                break;
+    }
+
+    if (!q)
+        q = bq->blocks_tail;
+
+    /* We go from back to front to look for the right place to add
+     * this new entry. Drop data we will overwrite on the way */
+
+    while (q) {
+
+        if (bq->write_index >= q->index + (int64_t) q->chunk.length)
+            /* We found the entry where we need to place the new entry immediately after */
+            break;
+        else if (bq->write_index + (int64_t) chunk.length <= q->index) {
+            /* This entry isn't touched at all, let's skip it */
+            q = q->prev;
+        } else if (bq->write_index <= q->index &&
+                   bq->write_index + (int64_t) chunk.length >= q->index + (int64_t) q->chunk.length) {
+
+            /* This entry is fully replaced by the new entry, so let's drop it */
+
+            struct list_item *p;
+            p = q;
+            q = q->prev;
+            drop_block(bq, p);
+        } else if (bq->write_index >= q->index) {
+            /* The write index points into this memblock, so let's
+             * truncate or split it */
+
+            if (bq->write_index + (int64_t) chunk.length < q->index + (int64_t) q->chunk.length) {
+
+                /* We need to save the end of this memchunk */
+                struct list_item *p;
+                size_t d;
+
+                /* Create a new list entry for the end of the memchunk */
+                if (!(p = pa_flist_pop(PA_STATIC_FLIST_GET(list_items))))
+                    p = pa_xnew(struct list_item, 1);
+
+                p->chunk = q->chunk;
+                pa_memblock_ref(p->chunk.memblock);
+
+                /* Calculate offset */
+                d = (size_t) (bq->write_index + (int64_t) chunk.length - q->index);
+                pa_assert(d > 0);
+
+                /* Drop it from the new entry */
+                p->index = q->index + (int64_t) d;
+                p->chunk.length -= d;
+
+                /* Add it to the list */
+                p->prev = q;
+                if ((p->next = q->next))
+                    q->next->prev = p;
+                else
+                    bq->blocks_tail = p;
+                q->next = p;
+
+                bq->n_blocks++;
+            }
+
+            /* Truncate the chunk */
+            if (!(q->chunk.length = (size_t) (bq->write_index - q->index))) {
+                struct list_item *p;
+                p = q;
+                q = q->prev;
+                drop_block(bq, p);
+            }
+
+            /* We had to truncate this block, hence we're now at the right position */
+            break;
+        } else {
+            size_t d;
+
+            pa_assert(bq->write_index + (int64_t)chunk.length > q->index &&
+                      bq->write_index + (int64_t)chunk.length < q->index + (int64_t)q->chunk.length &&
+                      bq->write_index < q->index);
+
+            /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
+
+            d = (size_t) (bq->write_index + (int64_t) chunk.length - q->index);
+            q->index += (int64_t) d;
+            q->chunk.index += d;
+            q->chunk.length -= d;
+
+            q = q->prev;
+        }
+    }
+
+    if (q) {
+        pa_assert(bq->write_index >=  q->index + (int64_t)q->chunk.length);
+        pa_assert(!q->next || (bq->write_index + (int64_t)chunk.length <= q->next->index));
+
+        /* Try to merge memory blocks */
+
+        if (q->chunk.memblock == chunk.memblock &&
+            q->chunk.index + q->chunk.length == chunk.index &&
+            bq->write_index == q->index + (int64_t) q->chunk.length) {
+
+            q->chunk.length += chunk.length;
+            bq->write_index += (int64_t) chunk.length;
+            goto finish;
+        }
+    } else
+        pa_assert(!bq->blocks || (bq->write_index + (int64_t)chunk.length <= bq->blocks->index));
+
+    if (!(n = pa_flist_pop(PA_STATIC_FLIST_GET(list_items))))
+        n = pa_xnew(struct list_item, 1);
+
+    n->chunk = chunk;
+    pa_memblock_ref(n->chunk.memblock);
+    n->index = bq->write_index;
+    bq->write_index += (int64_t) n->chunk.length;
+
+    n->next = q ? q->next : bq->blocks;
+    n->prev = q;
+
+    if (n->next)
+        n->next->prev = n;
+    else
+        bq->blocks_tail = n;
+
+    if (n->prev)
+        n->prev->next = n;
+    else
+        bq->blocks = n;
+
+    bq->n_blocks++;
+
+finish:
+
+    write_index_changed(bq, old, true);
+    return 0;
+}
+
+bool pa_memblockq_prebuf_active(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (bq->in_prebuf)
+        return pa_memblockq_get_length(bq) < bq->prebuf;
+    else
+        return bq->prebuf > 0 && bq->read_index >= bq->write_index;
+}
+
+static bool update_prebuf(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (bq->in_prebuf) {
+
+        if (pa_memblockq_get_length(bq) < bq->prebuf)
+            return true;
+
+        bq->in_prebuf = false;
+        return false;
+    } else {
+
+        if (bq->prebuf > 0 && bq->read_index >= bq->write_index) {
+            bq->in_prebuf = true;
+            return true;
+        }
+
+        return false;
+    }
+}
+
+int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
+    int64_t d;
+    pa_assert(bq);
+    pa_assert(chunk);
+
+    /* We need to pre-buffer */
+    if (update_prebuf(bq))
+        return -1;
+
+    fix_current_read(bq);
+
+    /* Do we need to spit out silence? */
+    if (!bq->current_read || bq->current_read->index > bq->read_index) {
+        size_t length;
+
+        /* How much silence shall we return? */
+        if (bq->current_read)
+            length = (size_t) (bq->current_read->index - bq->read_index);
+        else if (bq->write_index > bq->read_index)
+            length = (size_t) (bq->write_index - bq->read_index);
+        else
+            length = 0;
+
+        /* We need to return silence, since no data is yet available */
+        if (bq->silence.memblock) {
+            *chunk = bq->silence;
+            pa_memblock_ref(chunk->memblock);
+
+            if (length > 0 && length < chunk->length)
+                chunk->length = length;
+
+        } else {
+
+            /* If the memblockq is empty, return -1, otherwise return
+             * the time to sleep */
+            if (length <= 0)
+                return -1;
+
+            chunk->memblock = NULL;
+            chunk->length = length;
+        }
+
+        chunk->index = 0;
+        return 0;
+    }
+
+    /* Ok, let's pass real data to the caller */
+    *chunk = bq->current_read->chunk;
+    pa_memblock_ref(chunk->memblock);
+
+    pa_assert(bq->read_index >= bq->current_read->index);
+    d = bq->read_index - bq->current_read->index;
+    chunk->index += (size_t) d;
+    chunk->length -= (size_t) d;
+
+    return 0;
+}
+
+int pa_memblockq_peek_fixed_size(pa_memblockq *bq, size_t block_size, pa_memchunk *chunk) {
+    pa_mempool *pool;
+    pa_memchunk tchunk, rchunk;
+    int64_t ri;
+    struct list_item *item;
+
+    pa_assert(bq);
+    pa_assert(block_size > 0);
+    pa_assert(chunk);
+    pa_assert(bq->silence.memblock);
+
+    if (pa_memblockq_peek(bq, &tchunk) < 0)
+        return -1;
+
+    if (tchunk.length >= block_size) {
+        *chunk = tchunk;
+        chunk->length = block_size;
+        return 0;
+    }
+
+    pool = pa_memblock_get_pool(tchunk.memblock);
+    rchunk.memblock = pa_memblock_new(pool, block_size);
+    rchunk.index = 0;
+    rchunk.length = tchunk.length;
+    pa_mempool_unref(pool), pool = NULL;
+
+    pa_memchunk_memcpy(&rchunk, &tchunk);
+    pa_memblock_unref(tchunk.memblock);
+
+    rchunk.index += tchunk.length;
+
+    /* We don't need to call fix_current_read() here, since
+     * pa_memblock_peek() already did that */
+    item = bq->current_read;
+    ri = bq->read_index + tchunk.length;
+
+    while (rchunk.index < block_size) {
+
+        if (!item || item->index > ri) {
+            /* Do we need to append silence? */
+            tchunk = bq->silence;
+
+            if (item)
+                tchunk.length = PA_MIN(tchunk.length, (size_t) (item->index - ri));
+
+        } else {
+            int64_t d;
+
+            /* We can append real data! */
+            tchunk = item->chunk;
+
+            d = ri - item->index;
+            tchunk.index += (size_t) d;
+            tchunk.length -= (size_t) d;
+
+            /* Go to next item for the next iteration */
+            item = item->next;
+        }
+
+        rchunk.length = tchunk.length = PA_MIN(tchunk.length, block_size - rchunk.index);
+        pa_memchunk_memcpy(&rchunk, &tchunk);
+
+        rchunk.index += rchunk.length;
+        ri += rchunk.length;
+    }
+
+    rchunk.index = 0;
+    rchunk.length = block_size;
+
+    *chunk = rchunk;
+    return 0;
+}
+
+void pa_memblockq_drop(pa_memblockq *bq, size_t length) {
+    int64_t old;
+    pa_assert(bq);
+    pa_assert(length % bq->base == 0);
+
+    old = bq->read_index;
+
+    while (length > 0) {
+
+        /* Do not drop any data when we are in prebuffering mode */
+        if (update_prebuf(bq))
+            break;
+
+        fix_current_read(bq);
+
+        if (bq->current_read) {
+            int64_t p, d;
+
+            /* We go through this piece by piece to make sure we don't
+             * drop more than allowed by prebuf */
+
+            p = bq->current_read->index + (int64_t) bq->current_read->chunk.length;
+            pa_assert(p >= bq->read_index);
+            d = p - bq->read_index;
+
+            if (d > (int64_t) length)
+                d = (int64_t) length;
+
+            bq->read_index += d;
+            length -= (size_t) d;
+
+        } else {
+
+            /* The list is empty, there's nothing we could drop */
+            bq->read_index += (int64_t) length;
+            break;
+        }
+    }
+
+    drop_backlog(bq);
+    read_index_changed(bq, old);
+}
+
+void pa_memblockq_rewind(pa_memblockq *bq, size_t length) {
+    int64_t old;
+    pa_assert(bq);
+    pa_assert(length % bq->base == 0);
+
+    old = bq->read_index;
+
+    /* This is kind of the inverse of pa_memblockq_drop() */
+
+    bq->read_index -= (int64_t) length;
+
+    read_index_changed(bq, old);
+}
+
+bool pa_memblockq_is_readable(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (pa_memblockq_prebuf_active(bq))
+        return false;
+
+    if (pa_memblockq_get_length(bq) <= 0)
+        return false;
+
+    return true;
+}
+
+size_t pa_memblockq_get_length(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (bq->write_index <= bq->read_index)
+        return 0;
+
+    return (size_t) (bq->write_index - bq->read_index);
+}
+
+void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek, bool account) {
+    int64_t old;
+    pa_assert(bq);
+
+    old = bq->write_index;
+
+    switch (seek) {
+        case PA_SEEK_RELATIVE:
+            bq->write_index += offset;
+            break;
+        case PA_SEEK_ABSOLUTE:
+            bq->write_index = offset;
+            break;
+        case PA_SEEK_RELATIVE_ON_READ:
+            bq->write_index = bq->read_index + offset;
+            break;
+        case PA_SEEK_RELATIVE_END:
+            bq->write_index = (bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->read_index) + offset;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    drop_backlog(bq);
+    write_index_changed(bq, old, account);
+}
+
+void pa_memblockq_flush_write(pa_memblockq *bq, bool account) {
+    int64_t old;
+    pa_assert(bq);
+
+    pa_memblockq_silence(bq);
+
+    old = bq->write_index;
+    bq->write_index = bq->read_index;
+
+    pa_memblockq_prebuf_force(bq);
+    write_index_changed(bq, old, account);
+}
+
+void pa_memblockq_flush_read(pa_memblockq *bq) {
+    int64_t old;
+    pa_assert(bq);
+
+    pa_memblockq_silence(bq);
+
+    old = bq->read_index;
+    bq->read_index = bq->write_index;
+
+    pa_memblockq_prebuf_force(bq);
+    read_index_changed(bq, old);
+}
+
+size_t pa_memblockq_get_tlength(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->tlength;
+}
+
+size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->minreq;
+}
+
+size_t pa_memblockq_get_maxrewind(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->maxrewind;
+}
+
+int64_t pa_memblockq_get_read_index(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->read_index;
+}
+
+int64_t pa_memblockq_get_write_index(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->write_index;
+}
+
+int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
+    pa_memchunk rchunk;
+
+    pa_assert(bq);
+    pa_assert(chunk);
+
+    if (bq->base == 1)
+        return pa_memblockq_push(bq, chunk);
+
+    if (!can_push(bq, pa_mcalign_csize(bq->mcalign, chunk->length)))
+        return -1;
+
+    pa_mcalign_push(bq->mcalign, chunk);
+
+    while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
+        int r;
+        r = pa_memblockq_push(bq, &rchunk);
+        pa_memblock_unref(rchunk.memblock);
+
+        if (r < 0) {
+            pa_mcalign_flush(bq->mcalign);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    bq->in_prebuf = false;
+}
+
+void pa_memblockq_prebuf_force(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (bq->prebuf > 0)
+        bq->in_prebuf = true;
+}
+
+size_t pa_memblockq_get_maxlength(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->maxlength;
+}
+
+size_t pa_memblockq_get_prebuf(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->prebuf;
+}
+
+size_t pa_memblockq_pop_missing(pa_memblockq *bq) {
+    size_t l;
+
+    pa_assert(bq);
+
+#ifdef MEMBLOCKQ_DEBUG
+    pa_log_debug("[%s] pop: %lli", bq->name, (long long) bq->missing);
+#endif
+
+    if (bq->missing <= 0)
+        return 0;
+
+    if (((size_t) bq->missing < bq->minreq) &&
+        !pa_memblockq_prebuf_active(bq))
+        return 0;
+
+    l = (size_t) bq->missing;
+
+    bq->requested += bq->missing;
+    bq->missing = 0;
+
+#ifdef MEMBLOCKQ_DEBUG
+    pa_log_debug("[%s] sent %lli: request counter is at %lli", bq->name, (long long) l, (long long) bq->requested);
+#endif
+
+    return l;
+}
+
+void pa_memblockq_set_maxlength(pa_memblockq *bq, size_t maxlength) {
+    pa_assert(bq);
+
+    bq->maxlength = ((maxlength+bq->base-1)/bq->base)*bq->base;
+
+    if (bq->maxlength < bq->base)
+        bq->maxlength = bq->base;
+
+    if (bq->tlength > bq->maxlength)
+        pa_memblockq_set_tlength(bq, bq->maxlength);
+}
+
+void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
+    size_t old_tlength;
+    pa_assert(bq);
+
+    if (tlength <= 0 || tlength == (size_t) -1)
+        tlength = bq->maxlength;
+
+    old_tlength = bq->tlength;
+    bq->tlength = ((tlength+bq->base-1)/bq->base)*bq->base;
+
+    if (bq->tlength > bq->maxlength)
+        bq->tlength = bq->maxlength;
+
+    if (bq->minreq > bq->tlength)
+        pa_memblockq_set_minreq(bq, bq->tlength);
+
+    if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+        pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
+
+    bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength;
+}
+
+void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
+    pa_assert(bq);
+
+    bq->minreq = (minreq/bq->base)*bq->base;
+
+    if (bq->minreq > bq->tlength)
+        bq->minreq = bq->tlength;
+
+    if (bq->minreq < bq->base)
+        bq->minreq = bq->base;
+
+    if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+        pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
+}
+
+void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
+    pa_assert(bq);
+
+    if (prebuf == (size_t) -1)
+        prebuf = bq->tlength+bq->base-bq->minreq;
+
+    bq->prebuf = ((prebuf+bq->base-1)/bq->base)*bq->base;
+
+    if (prebuf > 0 && bq->prebuf < bq->base)
+        bq->prebuf = bq->base;
+
+    if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+        bq->prebuf = bq->tlength+bq->base-bq->minreq;
+
+    if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf)
+        bq->in_prebuf = false;
+}
+
+void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
+    pa_assert(bq);
+
+    bq->maxrewind = (maxrewind/bq->base)*bq->base;
+}
+
+void pa_memblockq_apply_attr(pa_memblockq *bq, const pa_buffer_attr *a) {
+    pa_assert(bq);
+    pa_assert(a);
+
+    pa_memblockq_set_maxlength(bq, a->maxlength);
+    pa_memblockq_set_tlength(bq, a->tlength);
+    pa_memblockq_set_minreq(bq, a->minreq);
+    pa_memblockq_set_prebuf(bq, a->prebuf);
+}
+
+void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a) {
+    pa_assert(bq);
+    pa_assert(a);
+
+    a->maxlength = (uint32_t) pa_memblockq_get_maxlength(bq);
+    a->tlength = (uint32_t) pa_memblockq_get_tlength(bq);
+    a->prebuf = (uint32_t) pa_memblockq_get_prebuf(bq);
+    a->minreq = (uint32_t) pa_memblockq_get_minreq(bq);
+}
+
+int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
+
+    pa_assert(bq);
+    pa_assert(source);
+
+    pa_memblockq_prebuf_disable(bq);
+
+    for (;;) {
+        pa_memchunk chunk;
+
+        if (pa_memblockq_peek(source, &chunk) < 0)
+            return 0;
+
+        pa_assert(chunk.length > 0);
+
+        if (chunk.memblock) {
+
+            if (pa_memblockq_push_align(bq, &chunk) < 0) {
+                pa_memblock_unref(chunk.memblock);
+                return -1;
+            }
+
+            pa_memblock_unref(chunk.memblock);
+        } else
+            pa_memblockq_seek(bq, (int64_t) chunk.length, PA_SEEK_RELATIVE, true);
+
+        pa_memblockq_drop(bq, chunk.length);
+    }
+}
+
+void pa_memblockq_willneed(pa_memblockq *bq) {
+    struct list_item *q;
+
+    pa_assert(bq);
+
+    fix_current_read(bq);
+
+    for (q = bq->current_read; q; q = q->next)
+        pa_memchunk_will_need(&q->chunk);
+}
+
+void pa_memblockq_set_silence(pa_memblockq *bq, pa_memchunk *silence) {
+    pa_assert(bq);
+
+    if (bq->silence.memblock)
+        pa_memblock_unref(bq->silence.memblock);
+
+    if (silence) {
+        bq->silence = *silence;
+        pa_memblock_ref(bq->silence.memblock);
+    } else
+        pa_memchunk_reset(&bq->silence);
+}
+
+bool pa_memblockq_is_empty(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return !bq->blocks;
+}
+
+void pa_memblockq_silence(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    while (bq->blocks)
+        drop_block(bq, bq->blocks);
+
+    pa_assert(bq->n_blocks == 0);
+}
+
+unsigned pa_memblockq_get_nblocks(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->n_blocks;
+}
+
+size_t pa_memblockq_get_base(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->base;
+}
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
new file mode 100644 (file)
index 0000000..69dcebc
--- /dev/null
@@ -0,0 +1,187 @@
+#ifndef foomemblockqhfoo
+#define foomemblockqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
+#include <pulse/def.h>
+
+/* A memblockq is a queue of pa_memchunks (yepp, the name is not
+ * perfect). It is similar to the ring buffers used by most other
+ * audio software. In contrast to a ring buffer this memblockq data
+ * type doesn't need to copy any data around, it just maintains
+ * references to reference counted memory blocks. */
+
+typedef struct pa_memblockq pa_memblockq;
+
+/* Parameters:
+
+   - name:      name for debugging purposes
+
+   - idx:       start value for both read and write index
+
+   - maxlength: maximum length of queue. If more data is pushed into
+                the queue, the operation will fail. Must not be 0.
+
+   - tlength:   the target length of the queue. Pass 0 for the default.
+
+   - ss:        Sample spec describing the queue contents. Only multiples
+                of the frame size as implied by the sample spec are
+                allowed into the queue or can be popped from it.
+
+   - prebuf:    If the queue runs empty wait until this many bytes are in
+                queue again before passing the first byte out. If set
+                to 0 pa_memblockq_pop() will return a silence memblock
+                if no data is in the queue and will never fail. Pass
+                (size_t) -1 for the default.
+
+   - minreq:    pa_memblockq_pop_missing() will only return values greater
+                than this value. Pass 0 for the default.
+
+   - maxrewind: how many bytes of history to keep in the queue
+
+   - silence:   return this memchunk when reading uninitialized data
+*/
+pa_memblockq* pa_memblockq_new(
+        const char *name,
+        int64_t idx,
+        size_t maxlength,
+        size_t tlength,
+        const pa_sample_spec *sample_spec,
+        size_t prebuf,
+        size_t minreq,
+        size_t maxrewind,
+        pa_memchunk *silence);
+
+void pa_memblockq_free(pa_memblockq*bq);
+
+/* Push a new memory chunk into the queue.  */
+int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk);
+
+/* Push a new memory chunk into the queue, but filter it through a
+ * pa_mcalign object. Don't mix this with pa_memblockq_seek() unless
+ * you know what you do. */
+int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk);
+
+/* Manipulate the write pointer */
+void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek, bool account);
+
+/* Return a copy of the next memory chunk in the queue. It is not
+ * removed from the queue. There are two reasons this function might
+ * fail: 1. prebuffering is active, 2. queue is empty and no silence
+ * memblock was passed at initialization. If the queue is not empty,
+ * but we're currently at a hole in the queue and no silence memblock
+ * was passed we return the length of the hole in chunk->length. */
+int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk);
+
+/* Much like pa_memblockq_peek, but guarantees that the returned chunk
+ * will have a length of the block size passed. You must configure a
+ * silence memchunk for this memblockq if you use this call. */
+int pa_memblockq_peek_fixed_size(pa_memblockq *bq, size_t block_size, pa_memchunk *chunk);
+
+/* Drop the specified bytes from the queue. */
+void pa_memblockq_drop(pa_memblockq *bq, size_t length);
+
+/* Rewind the read index. If the history is shorter than the specified length we'll point to silence afterwards. */
+void pa_memblockq_rewind(pa_memblockq *bq, size_t length);
+
+/* Test if the pa_memblockq is currently readable, that is, more data than base */
+bool pa_memblockq_is_readable(pa_memblockq *bq);
+
+/* Return the length of the queue in bytes */
+size_t pa_memblockq_get_length(pa_memblockq *bq);
+
+/* Return the number of bytes that are missing since the last call to
+ * this function, reset the internal counter to 0. */
+size_t pa_memblockq_pop_missing(pa_memblockq *bq);
+
+/* Directly moves the data from the source memblockq into bq */
+int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source);
+
+/* Set the queue to silence, set write index to read index */
+void pa_memblockq_flush_write(pa_memblockq *bq, bool account);
+
+/* Set the queue to silence, set write read index to write index*/
+void pa_memblockq_flush_read(pa_memblockq *bq);
+
+/* Ignore prebuf for now */
+void pa_memblockq_prebuf_disable(pa_memblockq *bq);
+
+/* Force prebuf */
+void pa_memblockq_prebuf_force(pa_memblockq *bq);
+
+/* Return the maximum length of the queue in bytes */
+size_t pa_memblockq_get_maxlength(pa_memblockq *bq);
+
+/* Get Target length */
+size_t pa_memblockq_get_tlength(pa_memblockq *bq);
+
+/* Return the prebuffer length in bytes */
+size_t pa_memblockq_get_prebuf(pa_memblockq *bq);
+
+/* Returns the minimal request value */
+size_t pa_memblockq_get_minreq(pa_memblockq *bq);
+
+/* Returns the maximal rewind value */
+size_t pa_memblockq_get_maxrewind(pa_memblockq *bq);
+
+/* Return the base unit in bytes */
+size_t pa_memblockq_get_base(pa_memblockq *bq);
+
+/* Return the current read index */
+int64_t pa_memblockq_get_read_index(pa_memblockq *bq);
+
+/* Return the current write index */
+int64_t pa_memblockq_get_write_index(pa_memblockq *bq);
+
+/* Change metrics. Always call in order. */
+void pa_memblockq_set_maxlength(pa_memblockq *memblockq, size_t maxlength); /* might modify tlength, prebuf, minreq too */
+void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength); /* might modify minreq, too */
+void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq); /* might modify prebuf, too */
+void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf);
+void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t maxrewind); /* Set the maximum history size */
+void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence);
+
+/* Apply the data from pa_buffer_attr */
+void pa_memblockq_apply_attr(pa_memblockq *memblockq, const pa_buffer_attr *a);
+void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a);
+
+/* Call pa_memchunk_will_need() for every chunk in the queue from the current read pointer to the end */
+void pa_memblockq_willneed(pa_memblockq *bq);
+
+/* Check whether the memblockq is completely empty, i.e. no data
+ * neither left nor right of the read pointer, and hence no buffered
+ * data for the future nor data in the backlog. */
+bool pa_memblockq_is_empty(pa_memblockq *bq);
+
+/* Drop everything in the queue, but don't modify the indexes */
+void pa_memblockq_silence(pa_memblockq *bq);
+
+/* Check whether we currently are in prebuf state */
+bool pa_memblockq_prebuf_active(pa_memblockq *bq);
+
+/* Return how many items are currently stored in the queue */
+unsigned pa_memblockq_get_nblocks(pa_memblockq *bq);
+
+#endif
diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c
new file mode 100644 (file)
index 0000000..8822134
--- /dev/null
@@ -0,0 +1,121 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "memchunk.h"
+
+pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
+    pa_mempool *pool;
+    pa_memblock *n;
+    size_t l;
+    void *tdata, *sdata;
+
+    pa_assert(c);
+    pa_assert(c->memblock);
+
+    if (pa_memblock_ref_is_one(c->memblock) &&
+        !pa_memblock_is_read_only(c->memblock) &&
+        pa_memblock_get_length(c->memblock) >= c->index+min)
+        return c;
+
+    l = PA_MAX(c->length, min);
+
+    pool = pa_memblock_get_pool(c->memblock);
+    n = pa_memblock_new(pool, l);
+    pa_mempool_unref(pool), pool = NULL;
+
+    sdata = pa_memblock_acquire(c->memblock);
+    tdata = pa_memblock_acquire(n);
+
+    memcpy(tdata, (uint8_t*) sdata + c->index, c->length);
+
+    pa_memblock_release(c->memblock);
+    pa_memblock_release(n);
+
+    pa_memblock_unref(c->memblock);
+
+    c->memblock = n;
+    c->index = 0;
+
+    return c;
+}
+
+pa_memchunk* pa_memchunk_reset(pa_memchunk *c) {
+    pa_assert(c);
+
+    memset(c, 0, sizeof(*c));
+
+    return c;
+}
+
+pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c) {
+    void *p;
+
+    pa_assert(c);
+    pa_assert(c->memblock);
+
+    /* A version of pa_memblock_will_need() that works on memchunks
+     * instead of memblocks */
+
+    p = pa_memblock_acquire_chunk(c);
+    pa_will_need(p, c->length);
+    pa_memblock_release(c->memblock);
+
+    return (pa_memchunk*) c;
+}
+
+pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src) {
+    void *p, *q;
+
+    pa_assert(dst);
+    pa_assert(src);
+    pa_assert(dst->length == src->length);
+
+    p = pa_memblock_acquire(dst->memblock);
+    q = pa_memblock_acquire(src->memblock);
+
+    memmove((uint8_t*) p + dst->index,
+            (uint8_t*) q + src->index,
+            dst->length);
+
+    pa_memblock_release(dst->memblock);
+    pa_memblock_release(src->memblock);
+
+    return dst;
+}
+
+bool pa_memchunk_isset(pa_memchunk *chunk) {
+    pa_assert(chunk);
+
+    return
+        chunk->memblock ||
+        chunk->index > 0 ||
+        chunk->length > 0;
+}
diff --git a/src/pulsecore/memchunk.h b/src/pulsecore/memchunk.h
new file mode 100644 (file)
index 0000000..2b19712
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef foomemchunkhfoo
+#define foomemchunkhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct pa_memchunk pa_memchunk;
+
+#include <pulsecore/memblock.h>
+
+/* A memchunk describes a part of a memblock. In contrast to the memblock, a
+ * memchunk is not allocated dynamically or reference counted, instead
+ * it is usually stored on the stack and copied around */
+
+struct pa_memchunk {
+    pa_memblock *memblock;
+    size_t index, length;
+};
+
+/* Make a memchunk writable, i.e. make sure that the caller may have
+ * exclusive access to the memblock and it is not read-only. If needed
+ * the memblock in the structure is replaced by a copy. If min is not
+ * 0 it is made sure that the returned memblock is at least of the
+ * specified size, i.e. is enlarged if necessary. */
+pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min);
+
+/* Invalidate a memchunk. This does not free the containing memblock,
+ * but sets all members to zero. */
+pa_memchunk* pa_memchunk_reset(pa_memchunk *c);
+
+/* Map a memory chunk back into memory if it was swapped out */
+pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c);
+
+/* Copy the data in the src memchunk to the dst memchunk */
+pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src);
+
+/* Return true if any field is set != 0 */
+bool pa_memchunk_isset(pa_memchunk *c);
+
+#endif
diff --git a/src/pulsecore/memfd-wrappers.h b/src/pulsecore/memfd-wrappers.h
new file mode 100644 (file)
index 0000000..3bed9b2
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef foopulsememfdwrappershfoo
+#define foopulsememfdwrappershfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2016 Ahmed S. Darwish <darwish.07@gmail.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_MEMFD
+
+#include <sys/syscall.h>
+#include <fcntl.h>
+
+/*
+ * No glibc wrappers exist for memfd_create(2), so provide our own.
+ *
+ * Also define memfd fcntl sealing macros. While they are already
+ * defined in the kernel header file <linux/fcntl.h>, that file as
+ * a whole conflicts with the original glibc header <fnctl.h>.
+ */
+
+static inline int memfd_create(const char *name, unsigned int flags) {
+    return syscall(SYS_memfd_create, name, flags);
+}
+
+/* memfd_create(2) flags */
+
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC       0x0001U
+#endif
+
+#ifndef MFD_ALLOW_SEALING
+#define MFD_ALLOW_SEALING 0x0002U
+#endif
+
+/* fcntl() seals-related flags */
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
+
+#define F_SEAL_SEAL     0x0001  /* prevent further seals from being set */
+#define F_SEAL_SHRINK   0x0002  /* prevent file from shrinking */
+#define F_SEAL_GROW     0x0004  /* prevent file from growing */
+#define F_SEAL_WRITE    0x0008  /* prevent writes */
+#endif
+
+#endif /* HAVE_MEMFD */
+
+#endif
diff --git a/src/pulsecore/memtrap.c b/src/pulsecore/memtrap.c
new file mode 100644 (file)
index 0000000..e7b511c
--- /dev/null
@@ -0,0 +1,242 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+/* This is deprecated on glibc but is still used by FreeBSD */
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/aupdate.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/once.h>
+#include <pulsecore/mutex.h>
+
+#include "memtrap.h"
+
+struct pa_memtrap {
+    void *start;
+    size_t size;
+    pa_atomic_t bad;
+    pa_memtrap *next[2], *prev[2];
+};
+
+static pa_memtrap *memtraps[2] = { NULL, NULL };
+static pa_aupdate *aupdate;
+static pa_static_mutex mutex = PA_STATIC_MUTEX_INIT; /* only required to serialize access to the write side */
+
+static void allocate_aupdate(void) {
+    PA_ONCE_BEGIN {
+        aupdate = pa_aupdate_new();
+    } PA_ONCE_END;
+}
+
+bool pa_memtrap_is_good(pa_memtrap *m) {
+    pa_assert(m);
+
+    return !pa_atomic_load(&m->bad);
+}
+
+#ifdef HAVE_SIGACTION
+static void sigsafe_error(const char *s) {
+    size_t ret PA_GCC_UNUSED;
+    ret = write(STDERR_FILENO, s, strlen(s));
+}
+
+static void signal_handler(int sig, siginfo_t* si, void *data) {
+    unsigned j;
+    pa_memtrap *m;
+    void *r;
+
+    j = pa_aupdate_read_begin(aupdate);
+
+    for (m = memtraps[j]; m; m = m->next[j])
+        if (si->si_addr >= m->start &&
+            (uint8_t*) si->si_addr < (uint8_t*) m->start + m->size)
+            break;
+
+    if (!m)
+        goto fail;
+
+    pa_atomic_store(&m->bad, 1);
+
+    /* Remap anonymous memory into the bad segment */
+    if ((r = mmap(m->start, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
+        sigsafe_error("mmap() failed.\n");
+        goto fail;
+    }
+
+    pa_assert(r == m->start);
+
+    pa_aupdate_read_end(aupdate);
+    return;
+
+fail:
+    pa_aupdate_read_end(aupdate);
+
+    sigsafe_error("Failed to handle SIGBUS.\n");
+    abort();
+}
+#endif
+
+static void memtrap_link(pa_memtrap *m, unsigned j) {
+    pa_assert(m);
+
+    m->prev[j] = NULL;
+
+    if ((m->next[j] = memtraps[j]))
+        m->next[j]->prev[j] = m;
+
+    memtraps[j] = m;
+}
+
+static void memtrap_unlink(pa_memtrap *m, unsigned j) {
+    pa_assert(m);
+
+    if (m->next[j])
+        m->next[j]->prev[j] = m->prev[j];
+
+    if (m->prev[j])
+        m->prev[j]->next[j] = m->next[j];
+    else
+        memtraps[j] = m->next[j];
+}
+
+pa_memtrap* pa_memtrap_add(const void *start, size_t size) {
+    pa_memtrap *m = NULL;
+    unsigned j;
+    pa_mutex *mx;
+
+    pa_assert(start);
+    pa_assert(size > 0);
+
+    start = PA_PAGE_ALIGN_PTR(start);
+    size = PA_PAGE_ALIGN(size);
+
+    m = pa_xnew(pa_memtrap, 1);
+    m->start = (void*) start;
+    m->size = size;
+    pa_atomic_store(&m->bad, 0);
+
+    allocate_aupdate();
+
+    mx = pa_static_mutex_get(&mutex, false, true);
+    pa_mutex_lock(mx);
+
+    j = pa_aupdate_write_begin(aupdate);
+    memtrap_link(m, j);
+    j = pa_aupdate_write_swap(aupdate);
+    memtrap_link(m, j);
+    pa_aupdate_write_end(aupdate);
+
+    pa_mutex_unlock(mx);
+
+    return m;
+}
+
+void pa_memtrap_remove(pa_memtrap *m) {
+    unsigned j;
+    pa_mutex *mx;
+
+    pa_assert(m);
+
+    allocate_aupdate();
+
+    mx = pa_static_mutex_get(&mutex, false, true);
+    pa_mutex_lock(mx);
+
+    j = pa_aupdate_write_begin(aupdate);
+    memtrap_unlink(m, j);
+    j = pa_aupdate_write_swap(aupdate);
+    memtrap_unlink(m, j);
+    pa_aupdate_write_end(aupdate);
+
+    pa_mutex_unlock(mx);
+
+    pa_xfree(m);
+}
+
+pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size) {
+    unsigned j;
+    pa_mutex *mx;
+
+    pa_assert(m);
+
+    pa_assert(start);
+    pa_assert(size > 0);
+
+    start = PA_PAGE_ALIGN_PTR(start);
+    size = PA_PAGE_ALIGN(size);
+
+    allocate_aupdate();
+
+    mx = pa_static_mutex_get(&mutex, false, true);
+    pa_mutex_lock(mx);
+
+    j = pa_aupdate_write_begin(aupdate);
+
+    if (m->start == start && m->size == size)
+        goto unlock;
+
+    memtrap_unlink(m, j);
+    pa_aupdate_write_swap(aupdate);
+
+    m->start = (void*) start;
+    m->size = size;
+    pa_atomic_store(&m->bad, 0);
+
+    pa_assert_se(pa_aupdate_write_swap(aupdate) == j);
+    memtrap_link(m, j);
+
+unlock:
+    pa_aupdate_write_end(aupdate);
+
+    pa_mutex_unlock(mx);
+
+    return m;
+}
+
+void pa_memtrap_install(void) {
+#ifdef HAVE_SIGACTION
+    struct sigaction sa;
+
+    allocate_aupdate();
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_sigaction = signal_handler;
+    sa.sa_flags = SA_RESTART|SA_SIGINFO;
+
+    pa_assert_se(sigaction(SIGBUS, &sa, NULL) == 0);
+#ifdef __FreeBSD_kernel__
+    pa_assert_se(sigaction(SIGSEGV, &sa, NULL) == 0);
+#endif
+#endif
+}
diff --git a/src/pulsecore/memtrap.h b/src/pulsecore/memtrap.h
new file mode 100644 (file)
index 0000000..cf3e99e
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef foopulsecorememtraphfoo
+#define foopulsecorememtraphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/macro.h>
+
+/* This subsystem will trap SIGBUS on specific memory regions. The
+ * regions will be remapped to anonymous memory (i.e. writable NUL
+ * bytes) on SIGBUS, so that execution of the main program can
+ * continue though with memory having changed beneath its hands. With
+ * pa_memtrap_is_good() it is possible to query if a memory region is
+ * still 'good' i.e. no SIGBUS has happened yet for it.
+ *
+ * Intended usage is to handle memory mapped in which is controlled by
+ * other processes that might execute ftruncate() or when mapping inb
+ * hardware resources that might get invalidated when unplugged. */
+
+typedef struct pa_memtrap pa_memtrap;
+
+pa_memtrap* pa_memtrap_add(const void *start, size_t size);
+pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size);
+
+void pa_memtrap_remove(pa_memtrap *m);
+
+bool pa_memtrap_is_good(pa_memtrap *m);
+
+void pa_memtrap_install(void);
+
+#endif
diff --git a/src/pulsecore/mime-type.c b/src/pulsecore/mime-type.c
new file mode 100644 (file)
index 0000000..b7157b4
--- /dev/null
@@ -0,0 +1,179 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+
+#include "mime-type.h"
+
+bool pa_sample_spec_is_mime(const pa_sample_spec *ss, const pa_channel_map *cm) {
+
+    pa_assert(pa_channel_map_compatible(cm, ss));
+
+    switch (ss->format) {
+        case PA_SAMPLE_S16BE:
+        case PA_SAMPLE_S24BE:
+        case PA_SAMPLE_U8:
+
+            if (ss->rate != 8000 &&
+                ss->rate != 11025 &&
+                ss->rate != 16000 &&
+                ss->rate != 22050 &&
+                ss->rate != 24000 &&
+                ss->rate != 32000 &&
+                ss->rate != 44100 &&
+                ss->rate != 48000)
+                return false;
+
+            if (ss->channels != 1 &&
+                ss->channels != 2)
+                return false;
+
+            if ((cm->channels == 1 && cm->map[0] != PA_CHANNEL_POSITION_MONO) ||
+                (cm->channels == 2 && (cm->map[0] != PA_CHANNEL_POSITION_LEFT || cm->map[1] != PA_CHANNEL_POSITION_RIGHT)))
+                return false;
+
+            return true;
+
+        case PA_SAMPLE_ULAW:
+
+            if (ss->rate != 8000)
+                return false;
+
+            if (ss->channels != 1)
+                return false;
+
+            if (cm->map[0] != PA_CHANNEL_POSITION_MONO)
+                return false;
+
+            return true;
+
+        default:
+            return false;
+    }
+}
+
+void pa_sample_spec_mimefy(pa_sample_spec *ss, pa_channel_map *cm) {
+
+    pa_assert(pa_channel_map_compatible(cm, ss));
+
+    /* Turns the sample type passed in into the next 'better' one that
+     * can be encoded for HTTP. If there is no 'better' one we pick
+     * the 'best' one that is 'worse'. */
+
+    if (ss->channels > 2)
+        ss->channels = 2;
+
+    if (ss->rate > 44100)
+        ss->rate = 48000;
+    else if (ss->rate > 32000)
+        ss->rate = 44100;
+    else if (ss->rate > 24000)
+        ss->rate = 32000;
+    else if (ss->rate > 22050)
+        ss->rate = 24000;
+    else if (ss->rate > 16000)
+        ss->rate = 22050;
+    else if (ss->rate > 11025)
+        ss->rate = 16000;
+    else if (ss->rate > 8000)
+        ss->rate = 11025;
+    else
+        ss->rate = 8000;
+
+    switch (ss->format) {
+        case PA_SAMPLE_S24BE:
+        case PA_SAMPLE_S24LE:
+        case PA_SAMPLE_S24_32LE:
+        case PA_SAMPLE_S24_32BE:
+        case PA_SAMPLE_S32LE:
+        case PA_SAMPLE_S32BE:
+        case PA_SAMPLE_FLOAT32LE:
+        case PA_SAMPLE_FLOAT32BE:
+            ss->format = PA_SAMPLE_S24BE;
+            break;
+
+        case PA_SAMPLE_S16BE:
+        case PA_SAMPLE_S16LE:
+            ss->format = PA_SAMPLE_S16BE;
+            break;
+
+        case PA_SAMPLE_ULAW:
+        case PA_SAMPLE_ALAW:
+
+            if (ss->rate == 8000 && ss->channels == 1)
+                ss->format = PA_SAMPLE_ULAW;
+            else
+                ss->format = PA_SAMPLE_S16BE;
+            break;
+
+        case PA_SAMPLE_U8:
+            ss->format = PA_SAMPLE_U8;
+            break;
+
+        case PA_SAMPLE_MAX:
+        case PA_SAMPLE_INVALID:
+            pa_assert_not_reached();
+    }
+
+    pa_channel_map_init_auto(cm, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+
+    pa_assert(pa_sample_spec_is_mime(ss, cm));
+}
+
+char *pa_sample_spec_to_mime_type(const pa_sample_spec *ss, const pa_channel_map *cm) {
+    pa_assert(pa_channel_map_compatible(cm, ss));
+    pa_assert(pa_sample_spec_valid(ss));
+
+    if (!pa_sample_spec_is_mime(ss, cm))
+        return NULL;
+
+    switch (ss->format) {
+
+        case PA_SAMPLE_S16BE:
+        case PA_SAMPLE_S24BE:
+        case PA_SAMPLE_U8:
+            /* Stupid UPnP implementations (PS3...) choke on spaces in
+             * the mime type, that's why we write only ';' here,
+             * instead of '; '. */
+            return pa_sprintf_malloc("audio/%s;rate=%u;channels=%u",
+                                     ss->format == PA_SAMPLE_S16BE ? "L16" :
+                                     (ss->format == PA_SAMPLE_S24BE ? "L24" : "L8"),
+                                     ss->rate, ss->channels);
+
+        case PA_SAMPLE_ULAW:
+            return pa_xstrdup("audio/basic");
+
+        default:
+            pa_assert_not_reached();
+    }
+}
+
+char *pa_sample_spec_to_mime_type_mimefy(const pa_sample_spec *_ss, const pa_channel_map *_cm) {
+    pa_sample_spec ss = *_ss;
+    pa_channel_map cm = *_cm;
+
+    pa_sample_spec_mimefy(&ss, &cm);
+
+    return pa_sample_spec_to_mime_type(&ss, &cm);
+}
diff --git a/src/pulsecore/mime-type.h b/src/pulsecore/mime-type.h
new file mode 100644 (file)
index 0000000..f07f455
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef foopulsecoremimetypehfoo
+#define foopulsecoremimetypehfoo
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/macro.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+
+bool pa_sample_spec_is_mime(const pa_sample_spec *ss, const pa_channel_map *cm);
+void pa_sample_spec_mimefy(pa_sample_spec *ss, pa_channel_map *cm);
+char *pa_sample_spec_to_mime_type(const pa_sample_spec *ss, const pa_channel_map *cm);
+char *pa_sample_spec_to_mime_type_mimefy(const pa_sample_spec *_ss, const pa_channel_map *_cm);
+
+#endif
diff --git a/src/pulsecore/mix.c b/src/pulsecore/mix.c
new file mode 100644 (file)
index 0000000..59622d7
--- /dev/null
@@ -0,0 +1,726 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+  Copyright 2013 Peter Meerwald <pmeerw@pmeerw.net>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+
+#include <pulsecore/sample-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
+#include <pulsecore/endianmacros.h>
+
+#include "cpu.h"
+#include "mix.h"
+
+#define VOLUME_PADDING 32
+
+static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) {
+    unsigned channel, nchannels, padding;
+
+    pa_assert(linear);
+    pa_assert(volume);
+
+    nchannels = volume->channels;
+
+    for (channel = 0; channel < nchannels; channel++)
+        linear[channel] = (int32_t) lrint(pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+
+    for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
+        linear[channel] = linear[padding];
+}
+
+static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) {
+    unsigned channel, nchannels, padding;
+
+    pa_assert(linear);
+    pa_assert(volume);
+
+    nchannels = volume->channels;
+
+    for (channel = 0; channel < nchannels; channel++)
+        linear[channel] = (float) pa_sw_volume_to_linear(volume->values[channel]);
+
+    for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
+        linear[channel] = linear[padding];
+}
+
+static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) {
+    unsigned k, channel;
+    float linear[PA_CHANNELS_MAX + VOLUME_PADDING];
+
+    pa_assert(streams);
+    pa_assert(spec);
+    pa_assert(volume);
+
+    calc_linear_float_volume(linear, volume);
+
+    for (k = 0; k < nstreams; k++) {
+
+        for (channel = 0; channel < spec->channels; channel++) {
+            pa_mix_info *m = streams + k;
+            m->linear[channel].i = (int32_t) lrint(pa_sw_volume_to_linear(m->volume.values[channel]) * linear[channel] * 0x10000);
+        }
+    }
+}
+
+static void calc_linear_float_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) {
+    unsigned k, channel;
+    float linear[PA_CHANNELS_MAX + VOLUME_PADDING];
+
+    pa_assert(streams);
+    pa_assert(spec);
+    pa_assert(volume);
+
+    calc_linear_float_volume(linear, volume);
+
+    for (k = 0; k < nstreams; k++) {
+
+        for (channel = 0; channel < spec->channels; channel++) {
+            pa_mix_info *m = streams + k;
+            m->linear[channel].f = (float) (pa_sw_volume_to_linear(m->volume.values[channel]) * linear[channel]);
+        }
+    }
+}
+
+typedef void (*pa_calc_stream_volumes_func_t) (pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec);
+
+static const pa_calc_stream_volumes_func_t calc_stream_volumes_table[] = {
+  [PA_SAMPLE_U8]        = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
+  [PA_SAMPLE_ALAW]      = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
+  [PA_SAMPLE_ULAW]      = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
+  [PA_SAMPLE_S16LE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
+  [PA_SAMPLE_S16BE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
+  [PA_SAMPLE_FLOAT32LE] = (pa_calc_stream_volumes_func_t) calc_linear_float_stream_volumes,
+  [PA_SAMPLE_FLOAT32BE] = (pa_calc_stream_volumes_func_t) calc_linear_float_stream_volumes,
+  [PA_SAMPLE_S32LE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
+  [PA_SAMPLE_S32BE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
+  [PA_SAMPLE_S24LE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
+  [PA_SAMPLE_S24BE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
+  [PA_SAMPLE_S24_32LE]  = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
+  [PA_SAMPLE_S24_32BE]  = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes
+};
+
+/* special case: mix 2 s16ne streams, 1 channel each */
+static void pa_mix2_ch1_s16ne(pa_mix_info streams[], int16_t *data, unsigned length) {
+    const int16_t *ptr0 = streams[0].ptr;
+    const int16_t *ptr1 = streams[1].ptr;
+
+    const int32_t cv0 = streams[0].linear[0].i;
+    const int32_t cv1 = streams[1].linear[0].i;
+
+    length /= sizeof(int16_t);
+
+    for (; length > 0; length--) {
+        int32_t sum;
+
+        sum = pa_mult_s16_volume(*ptr0++, cv0);
+        sum += pa_mult_s16_volume(*ptr1++, cv1);
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data++ = sum;
+    }
+}
+
+/* special case: mix 2 s16ne streams, 2 channels each */
+static void pa_mix2_ch2_s16ne(pa_mix_info streams[], int16_t *data, unsigned length) {
+    const int16_t *ptr0 = streams[0].ptr;
+    const int16_t *ptr1 = streams[1].ptr;
+
+    length /= sizeof(int16_t) * 2;
+
+    for (; length > 0; length--) {
+        int32_t sum;
+
+        sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[0].i);
+        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[0].i);
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data++ = sum;
+
+        sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[1].i);
+        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[1].i);
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data++ = sum;
+    }
+}
+
+/* special case: mix 2 s16ne streams */
+static void pa_mix2_s16ne(pa_mix_info streams[], unsigned channels, int16_t *data, unsigned length) {
+    const int16_t *ptr0 = streams[0].ptr;
+    const int16_t *ptr1 = streams[1].ptr;
+    unsigned channel = 0;
+
+    length /= sizeof(int16_t);
+
+    for (; length > 0; length--) {
+        int32_t sum;
+
+        sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[channel].i);
+        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[channel].i);
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data++ = sum;
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+/* special case: mix s16ne streams, 2 channels each */
+static void pa_mix_ch2_s16ne(pa_mix_info streams[], unsigned nstreams, int16_t *data, unsigned length) {
+
+    length /= sizeof(int16_t) * 2;
+
+    for (; length > 0; length--) {
+        int32_t sum0 = 0, sum1 = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv0 = m->linear[0].i;
+            int32_t cv1 = m->linear[1].i;
+
+            sum0 += pa_mult_s16_volume(*((int16_t*) m->ptr), cv0);
+            m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
+
+            sum1 += pa_mult_s16_volume(*((int16_t*) m->ptr), cv1);
+            m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
+        }
+
+        *data++ = PA_CLAMP_UNLIKELY(sum0, -0x8000, 0x7FFF);
+        *data++ = PA_CLAMP_UNLIKELY(sum1, -0x8000, 0x7FFF);
+    }
+}
+
+static void pa_mix_generic_s16ne(pa_mix_info streams[], unsigned nstreams, unsigned channels, int16_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(int16_t);
+
+    for (; length > 0; length--) {
+        int32_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv = m->linear[channel].i;
+
+            if (PA_LIKELY(cv > 0))
+                sum += pa_mult_s16_volume(*((int16_t*) m->ptr), cv);
+            m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data++ = sum;
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_s16ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int16_t *data, unsigned length) {
+    if (nstreams == 2 && channels == 1)
+        pa_mix2_ch1_s16ne(streams, data, length);
+    else if (nstreams == 2 && channels == 2)
+        pa_mix2_ch2_s16ne(streams, data, length);
+    else if (nstreams == 2)
+        pa_mix2_s16ne(streams, channels, data, length);
+    else if (channels == 2)
+        pa_mix_ch2_s16ne(streams, nstreams, data, length);
+    else
+        pa_mix_generic_s16ne(streams, nstreams, channels, data, length);
+}
+
+static void pa_mix_s16re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int16_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(int16_t);
+
+    for (; length > 0; length--, data++) {
+        int32_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv = m->linear[channel].i;
+
+            if (PA_LIKELY(cv > 0))
+                sum += pa_mult_s16_volume(PA_INT16_SWAP(*((int16_t*) m->ptr)), cv);
+            m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data = PA_INT16_SWAP((int16_t) sum);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_s32ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int32_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(int32_t);
+
+    for (; length > 0; length--, data++) {
+        int64_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv = m->linear[channel].i;
+            int64_t v;
+
+            if (PA_LIKELY(cv > 0)) {
+                v = *((int32_t*) m->ptr);
+                v = (v * cv) >> 16;
+                sum += v;
+            }
+            m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+        *data = (int32_t) sum;
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_s32re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int32_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(int32_t);
+
+    for (; length > 0; length--, data++) {
+        int64_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv = m->linear[channel].i;
+            int64_t v;
+
+            if (PA_LIKELY(cv > 0)) {
+                v = PA_INT32_SWAP(*((int32_t*) m->ptr));
+                v = (v * cv) >> 16;
+                sum += v;
+            }
+            m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+        *data = PA_INT32_SWAP((int32_t) sum);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_s24ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    for (; length > 0; length -= 3, data += 3) {
+        int64_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv = m->linear[channel].i;
+            int64_t v;
+
+            if (PA_LIKELY(cv > 0)) {
+                v = (int32_t) (PA_READ24NE(m->ptr) << 8);
+                v = (v * cv) >> 16;
+                sum += v;
+            }
+            m->ptr = (uint8_t*) m->ptr + 3;
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+        PA_WRITE24NE(data, ((uint32_t) sum) >> 8);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_s24re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    for (; length > 0; length -= 3, data += 3) {
+        int64_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv = m->linear[channel].i;
+            int64_t v;
+
+            if (PA_LIKELY(cv > 0)) {
+                v = (int32_t) (PA_READ24RE(m->ptr) << 8);
+                v = (v * cv) >> 16;
+                sum += v;
+            }
+            m->ptr = (uint8_t*) m->ptr + 3;
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+        PA_WRITE24RE(data, ((uint32_t) sum) >> 8);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_s24_32ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint32_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(uint32_t);
+
+    for (; length > 0; length--, data++) {
+        int64_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv = m->linear[channel].i;
+            int64_t v;
+
+            if (PA_LIKELY(cv > 0)) {
+                v = (int32_t) (*((uint32_t*)m->ptr) << 8);
+                v = (v * cv) >> 16;
+                sum += v;
+            }
+            m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+        *data = ((uint32_t) (int32_t) sum) >> 8;
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_s24_32re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint32_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(uint32_t);
+
+    for (; length > 0; length--, data++) {
+        int64_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv = m->linear[channel].i;
+            int64_t v;
+
+            if (PA_LIKELY(cv > 0)) {
+                v = (int32_t) (PA_UINT32_SWAP(*((uint32_t*) m->ptr)) << 8);
+                v = (v * cv) >> 16;
+                sum += v;
+            }
+            m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+        *data = PA_INT32_SWAP(((uint32_t) (int32_t) sum) >> 8);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_u8_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(uint8_t);
+
+    for (; length > 0; length--, data++) {
+        int32_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t v, cv = m->linear[channel].i;
+
+            if (PA_LIKELY(cv > 0)) {
+                v = (int32_t) *((uint8_t*) m->ptr) - 0x80;
+                v = (v * cv) >> 16;
+                sum += v;
+            }
+            m->ptr = (uint8_t*) m->ptr + 1;
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x80, 0x7F);
+        *data = (uint8_t) (sum + 0x80);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_ulaw_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(uint8_t);
+
+    for (; length > 0; length--, data++) {
+        int32_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv = m->linear[channel].i;
+
+            if (PA_LIKELY(cv > 0))
+                sum += pa_mult_s16_volume(st_ulaw2linear16(*((uint8_t*) m->ptr)), cv);
+            m->ptr = (uint8_t*) m->ptr + 1;
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data = (uint8_t) st_14linear2ulaw((int16_t) sum >> 2);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_alaw_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(uint8_t);
+
+    for (; length > 0; length--, data++) {
+        int32_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv = m->linear[channel].i;
+
+            if (PA_LIKELY(cv > 0))
+                sum += pa_mult_s16_volume(st_alaw2linear16(*((uint8_t*) m->ptr)), cv);
+            m->ptr = (uint8_t*) m->ptr + 1;
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data = (uint8_t) st_13linear2alaw((int16_t) sum >> 3);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_float32ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, float *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(float);
+
+    for (; length > 0; length--, data++) {
+        float sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            float v, cv = m->linear[channel].f;
+
+            if (PA_LIKELY(cv > 0)) {
+                v = *((float*) m->ptr);
+                v *= cv;
+                sum += v;
+            }
+            m->ptr = (uint8_t*) m->ptr + sizeof(float);
+        }
+
+        *data = sum;
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_mix_float32re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, float *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(float);
+
+    for (; length > 0; length--, data++) {
+        float sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            float cv = m->linear[channel].f;
+
+            if (PA_LIKELY(cv > 0))
+                sum += PA_READ_FLOAT32RE(m->ptr) * cv;
+            m->ptr = (uint8_t*) m->ptr + sizeof(float);
+        }
+
+        PA_WRITE_FLOAT32RE(data, sum);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static pa_do_mix_func_t do_mix_table[] = {
+    [PA_SAMPLE_U8]        = (pa_do_mix_func_t) pa_mix_u8_c,
+    [PA_SAMPLE_ALAW]      = (pa_do_mix_func_t) pa_mix_alaw_c,
+    [PA_SAMPLE_ULAW]      = (pa_do_mix_func_t) pa_mix_ulaw_c,
+    [PA_SAMPLE_S16NE]     = (pa_do_mix_func_t) pa_mix_s16ne_c,
+    [PA_SAMPLE_S16RE]     = (pa_do_mix_func_t) pa_mix_s16re_c,
+    [PA_SAMPLE_FLOAT32NE] = (pa_do_mix_func_t) pa_mix_float32ne_c,
+    [PA_SAMPLE_FLOAT32RE] = (pa_do_mix_func_t) pa_mix_float32re_c,
+    [PA_SAMPLE_S32NE]     = (pa_do_mix_func_t) pa_mix_s32ne_c,
+    [PA_SAMPLE_S32RE]     = (pa_do_mix_func_t) pa_mix_s32re_c,
+    [PA_SAMPLE_S24NE]     = (pa_do_mix_func_t) pa_mix_s24ne_c,
+    [PA_SAMPLE_S24RE]     = (pa_do_mix_func_t) pa_mix_s24re_c,
+    [PA_SAMPLE_S24_32NE]  = (pa_do_mix_func_t) pa_mix_s24_32ne_c,
+    [PA_SAMPLE_S24_32RE]  = (pa_do_mix_func_t) pa_mix_s24_32re_c
+};
+
+void pa_mix_func_init(const pa_cpu_info *cpu_info) {
+    if (cpu_info->force_generic_code)
+        do_mix_table[PA_SAMPLE_S16NE] = (pa_do_mix_func_t) pa_mix_generic_s16ne;
+    else
+        do_mix_table[PA_SAMPLE_S16NE] = (pa_do_mix_func_t) pa_mix_s16ne_c;
+}
+
+size_t pa_mix(
+        pa_mix_info streams[],
+        unsigned nstreams,
+        void *data,
+        size_t length,
+        const pa_sample_spec *spec,
+        const pa_cvolume *volume,
+        bool mute) {
+
+    pa_cvolume full_volume;
+    unsigned k;
+
+    pa_assert(streams);
+    pa_assert(data);
+    pa_assert(length);
+    pa_assert(spec);
+    pa_assert(nstreams > 1);
+
+    if (!volume)
+        volume = pa_cvolume_reset(&full_volume, spec->channels);
+
+    if (mute || pa_cvolume_is_muted(volume)) {
+        pa_silence_memory(data, length, spec);
+        return length;
+    }
+
+    for (k = 0; k < nstreams; k++) {
+        pa_assert(length <= streams[k].chunk.length);
+        streams[k].ptr = pa_memblock_acquire_chunk(&streams[k].chunk);
+    }
+
+    calc_stream_volumes_table[spec->format](streams, nstreams, volume, spec);
+    do_mix_table[spec->format](streams, nstreams, spec->channels, data, length);
+
+    for (k = 0; k < nstreams; k++)
+        pa_memblock_release(streams[k].chunk.memblock);
+
+    return length;
+}
+
+pa_do_mix_func_t pa_get_mix_func(pa_sample_format_t f) {
+    pa_assert(pa_sample_format_valid(f));
+
+    return do_mix_table[f];
+}
+
+void pa_set_mix_func(pa_sample_format_t f, pa_do_mix_func_t func) {
+    pa_assert(pa_sample_format_valid(f));
+
+    do_mix_table[f] = func;
+}
+
+typedef union {
+  float f;
+  uint32_t i;
+} volume_val;
+
+typedef void (*pa_calc_volume_func_t) (void *volumes, const pa_cvolume *volume);
+
+static const pa_calc_volume_func_t calc_volume_table[] = {
+  [PA_SAMPLE_U8]        = (pa_calc_volume_func_t) calc_linear_integer_volume,
+  [PA_SAMPLE_ALAW]      = (pa_calc_volume_func_t) calc_linear_integer_volume,
+  [PA_SAMPLE_ULAW]      = (pa_calc_volume_func_t) calc_linear_integer_volume,
+  [PA_SAMPLE_S16LE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
+  [PA_SAMPLE_S16BE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
+  [PA_SAMPLE_FLOAT32LE] = (pa_calc_volume_func_t) calc_linear_float_volume,
+  [PA_SAMPLE_FLOAT32BE] = (pa_calc_volume_func_t) calc_linear_float_volume,
+  [PA_SAMPLE_S32LE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
+  [PA_SAMPLE_S32BE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
+  [PA_SAMPLE_S24LE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
+  [PA_SAMPLE_S24BE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
+  [PA_SAMPLE_S24_32LE]  = (pa_calc_volume_func_t) calc_linear_integer_volume,
+  [PA_SAMPLE_S24_32BE]  = (pa_calc_volume_func_t) calc_linear_integer_volume
+};
+
+void pa_volume_memchunk(
+        pa_memchunk*c,
+        const pa_sample_spec *spec,
+        const pa_cvolume *volume) {
+
+    void *ptr;
+    volume_val linear[PA_CHANNELS_MAX + VOLUME_PADDING];
+    pa_do_volume_func_t do_volume;
+
+    pa_assert(c);
+    pa_assert(spec);
+    pa_assert(pa_sample_spec_valid(spec));
+    pa_assert(pa_frame_aligned(c->length, spec));
+    pa_assert(volume);
+
+    if (pa_memblock_is_silence(c->memblock))
+        return;
+
+    if (pa_cvolume_is_norm(volume))
+        return;
+
+    if (pa_cvolume_is_muted(volume)) {
+        pa_silence_memchunk(c, spec);
+        return;
+    }
+
+    do_volume = pa_get_volume_func(spec->format);
+    pa_assert(do_volume);
+
+    calc_volume_table[spec->format] ((void *)linear, volume);
+
+    ptr = pa_memblock_acquire_chunk(c);
+
+    do_volume(ptr, (void *)linear, spec->channels, c->length);
+
+    pa_memblock_release(c->memblock);
+}
diff --git a/src/pulsecore/mix.h b/src/pulsecore/mix.h
new file mode 100644 (file)
index 0000000..8102bcd
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef foomixhfoo
+#define foomixhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+  Copyright 2013 Peter Meerwald <pmeerw@pmeerw.net>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_mix_info {
+    pa_memchunk chunk;
+    pa_cvolume volume;
+    void *userdata;
+
+    /* The following fields are used internally by pa_mix(), should
+     * not be initialised by the caller of pa_mix(). */
+    void *ptr;
+    union {
+        int32_t i;
+        float f;
+    } linear[PA_CHANNELS_MAX];
+} pa_mix_info;
+
+size_t pa_mix(
+    pa_mix_info channels[],
+    unsigned nchannels,
+    void *data,
+    size_t length,
+    const pa_sample_spec *spec,
+    const pa_cvolume *volume,
+    bool mute);
+
+typedef void (*pa_do_mix_func_t) (pa_mix_info streams[], unsigned nstreams, unsigned channels, void *data, unsigned length);
+
+pa_do_mix_func_t pa_get_mix_func(pa_sample_format_t f);
+void pa_set_mix_func(pa_sample_format_t f, pa_do_mix_func_t func);
+
+void pa_volume_memchunk(
+    pa_memchunk*c,
+    const pa_sample_spec *spec,
+    const pa_cvolume *volume);
+
+#endif
diff --git a/src/pulsecore/mix_neon.c b/src/pulsecore/mix_neon.c
new file mode 100644 (file)
index 0000000..eb02d81
--- /dev/null
@@ -0,0 +1,223 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Peter Meerwald <pmeerw@pmeerw.net>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/sample-util.h>
+
+#include "cpu-arm.h"
+#include "mix.h"
+
+#include <arm_neon.h>
+
+static pa_do_mix_func_t fallback;
+
+/* special case: mix s16ne streams, 2 channels each */
+static void pa_mix_ch2_s16ne_neon(pa_mix_info streams[], unsigned nstreams, uint8_t *data, unsigned length) {
+    const unsigned mask = sizeof(int16_t) * 8 - 1;
+    const uint8_t *end = data + (length & ~mask);
+
+    while (data < end) {
+        int32x4_t sum0, sum1;
+        unsigned i;
+
+        __asm__ __volatile__ (
+            "veor.s32 %q[sum0], %q[sum0]     \n\t"
+            "veor.s32 %q[sum1], %q[sum1]     \n\t"
+            : [sum0] "=w" (sum0), [sum1] "=w" (sum1)
+            :
+            : "cc" /* clobber list */
+        );
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv0 = m->linear[0].i;
+            int32_t cv1 = m->linear[1].i;
+
+            __asm__ __volatile__ (
+                "vld2.s16    {d0,d2}, [%[ptr]]!      \n\t"
+                "vmov.s32    d4[0], %[cv0]           \n\t"
+                "vmov.s32    d4[1], %[cv1]           \n\t"
+                "vshll.s16   q0, d0, #15             \n\t"
+                "vshll.s16   q1, d2, #15             \n\t"
+                "vqdmulh.s32 q0, q0, d4[0]           \n\t"
+                "vqdmulh.s32 q1, q1, d4[1]           \n\t"
+                "vqadd.s32   %q[sum0], %q[sum0], q0  \n\t"
+                "vqadd.s32   %q[sum1], %q[sum1], q1  \n\t"
+                : [ptr] "+r" (m->ptr), [sum0] "+w" (sum0), [sum1] "+w" (sum1)
+                : [cv0] "r" (cv0), [cv1] "r" (cv1)
+                : "memory", "cc", "q0", "q1", "d4" /* clobber list */
+            );
+        }
+
+        __asm__ __volatile__ (
+            "vqmovn.s32 d0, %q[sum0]         \n\t"
+            "vqmovn.s32 d1, %q[sum1]         \n\t"
+            "vst2.s16   {d0,d1}, [%[data]]!  \n\t"
+            : [data] "+r" (data)
+            : [sum0] "w" (sum0), [sum1] "w" (sum1)
+            : "memory", "cc", "q0" /* clobber list */
+        );
+    }
+
+    fallback(streams, nstreams, 2, data, length & mask);
+}
+
+/* special case: mix 2 s16ne streams, 1 channel each */
+static void pa_mix2_ch1_s16ne_neon(pa_mix_info streams[], int16_t *data, unsigned length) {
+    const int16_t *ptr0 = streams[0].ptr;
+    const int16_t *ptr1 = streams[1].ptr;
+
+    int32x4_t sv0, sv1;
+    __asm__ __volatile__ (
+        "vdup.s32    %q[sv0], %[lin0]        \n\t"
+        "vdup.s32    %q[sv1], %[lin1]        \n\t"
+        : [sv0] "=w" (sv0), [sv1] "=w" (sv1)
+        : [lin0] "r" (streams[0].linear[0]), [lin1] "r" (streams[1].linear[0])
+        : /* clobber list */
+    );
+
+    length /= sizeof(int16_t);
+    for (; length >= 4; length -= 4) {
+        __asm__ __volatile__ (
+            "vld1.s16    d0, [%[ptr0]]!      \n\t"
+            "vld1.s16    d2, [%[ptr1]]!      \n\t"
+            "vshll.s16   q0, d0, #15         \n\t"
+            "vshll.s16   q1, d2, #15         \n\t"
+            "vqdmulh.s32 q0, q0, %q[sv0]     \n\t"
+            "vqdmulh.s32 q1, q1, %q[sv1]     \n\t"
+            "vqadd.s32   q0, q0, q1          \n\t"
+            "vqmovn.s32  d0, q0              \n\t"
+            "vst1.s16    d0, [%[data]]!      \n\t"
+            : [ptr0] "+r" (ptr0), [ptr1] "+r" (ptr1), [data] "+r" (data)
+            : [sv0] "w" (sv0), [sv1] "w" (sv1)
+            : "memory", "cc", "q0", "q1" /* clobber list */
+        );
+    }
+
+    for (; length > 0; length--) {
+        int32_t sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[0].i);
+        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[0].i);
+        *data++ = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+    }
+}
+
+/* special case: mix 2 s16ne streams, 2 channel each */
+static void pa_mix2_ch2_s16ne_neon(pa_mix_info streams[], int16_t *data, unsigned length) {
+    const int16_t *ptr0 = streams[0].ptr;
+    const int16_t *ptr1 = streams[1].ptr;
+
+    int32x4_t sv0, sv1;
+    __asm__ __volatile__ (
+        "vld1.s32 d0, [%[lin0]]              \n\t"
+        "vmov.s32 d1, d0                     \n\t"
+        "vmov.s32 %q[sv0], q0                \n\t"
+        "vld1.s32 d0, [%[lin1]]              \n\t"
+        "vmov.s32 d1, d0                     \n\t"
+        "vmov.s32 %q[sv1], q0                \n\t"
+        : [sv0] "=w" (sv0), [sv1] "=w" (sv1)
+        : [lin0] "r" (streams[0].linear), [lin1] "r" (streams[1].linear)
+        : "q0" /* clobber list */
+    );
+
+    length /= sizeof(int16_t);
+    for (; length >= 4; length -= 4) {
+        __asm__ __volatile__ (
+            "vld1.s16    d0, [%[ptr0]]!      \n\t"
+            "vld1.s16    d2, [%[ptr1]]!      \n\t"
+            "vshll.s16   q0, d0, #15         \n\t"
+            "vshll.s16   q1, d2, #15         \n\t"
+            "vqdmulh.s32 q0, q0, %q[sv0]     \n\t"
+            "vqdmulh.s32 q1, q1, %q[sv1]     \n\t"
+            "vqadd.s32   q0, q0, q1          \n\t"
+            "vqmovn.s32  d0, q0              \n\t"
+            "vst1.s16    d0, [%[data]]!      \n\t"
+            : [ptr0] "+r" (ptr0), [ptr1] "+r" (ptr1), [data] "+r" (data)
+            : [sv0] "w" (sv0), [sv1] "w" (sv1)
+            : "memory", "cc", "q0", "q1" /* clobber list */
+        );
+    }
+
+    if (length > 0) {
+        int32_t sum;
+
+        sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[0].i);
+        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[0].i);
+        *data++ = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+
+        sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[1].i);
+        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[1].i);
+        *data++ = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+    }
+}
+
+/* special case: mix 2 s16ne streams, 4 channels each */
+static void pa_mix2_ch4_s16ne_neon(pa_mix_info streams[], int16_t *data, unsigned length) {
+    const int16_t *ptr0 = streams[0].ptr;
+    const int16_t *ptr1 = streams[1].ptr;
+
+    int32x4_t sv0, sv1;
+
+    __asm__ __volatile__ (
+        "vld1.s32 %h[sv0], [%[lin0]]         \n\t"
+        "vld1.s32 %h[sv1], [%[lin1]]         \n\t"
+        : [sv0] "=w" (sv0), [sv1] "=w" (sv1)
+        : [lin0] "r" (streams[0].linear), [lin1] "r" (streams[1].linear)
+        : /* clobber list */
+    );
+
+    length /= sizeof(int16_t);
+    for (; length >= 4; length -= 4) {
+        __asm__ __volatile__ (
+            "vld1.s16    d0, [%[ptr0]]!      \n\t"
+            "vld1.s16    d2, [%[ptr1]]!      \n\t"
+            "vshll.s16   q0, d0, #15         \n\t"
+            "vshll.s16   q1, d2, #15         \n\t"
+            "vqdmulh.s32 q0, q0, %q[sv0]     \n\t"
+            "vqdmulh.s32 q1, q1, %q[sv1]     \n\t"
+            "vqadd.s32   q0, q0, q1          \n\t"
+            "vqmovn.s32  d0, q0              \n\t"
+            "vst1.s16    d0, [%[data]]!      \n\t"
+            : [ptr0] "+r" (ptr0), [ptr1] "+r" (ptr1), [data] "+r" (data)
+            : [sv0] "w" (sv0), [sv1] "w" (sv1)
+            : "memory", "cc", "q0", "q1" /* clobber list */
+        );
+    }
+}
+
+static void pa_mix_s16ne_neon(pa_mix_info streams[], unsigned nstreams, unsigned nchannels, void *data, unsigned length) {
+    if (nstreams == 2 && nchannels == 2)
+        pa_mix2_ch2_s16ne_neon(streams, data, length);
+    else if (nstreams == 2 && nchannels == 4)
+        pa_mix2_ch4_s16ne_neon(streams, data, length);
+    else if (nstreams == 2 && nchannels == 1)
+        pa_mix2_ch1_s16ne_neon(streams, data, length);
+    else if (nchannels == 2)
+        pa_mix_ch2_s16ne_neon(streams, nstreams, data, length);
+    else
+        fallback(streams, nstreams, nchannels, data, length);
+}
+
+void pa_mix_func_init_neon(pa_cpu_arm_flag_t flags) {
+    pa_log_info("Initialising ARM NEON optimized mixing functions.");
+
+    fallback = pa_get_mix_func(PA_SAMPLE_S16NE);
+    pa_set_mix_func(PA_SAMPLE_S16NE, (pa_do_mix_func_t) pa_mix_s16ne_neon);
+}
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
new file mode 100644 (file)
index 0000000..bce5891
--- /dev/null
@@ -0,0 +1,546 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/hashmap.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "modargs.h"
+
+struct pa_modargs {
+    pa_hashmap *raw;
+    pa_hashmap *unescaped;
+};
+
+struct entry {
+    char *key, *value;
+};
+
+static int add_key_value(pa_modargs *ma, char *key, char *value, const char* const valid_keys[], bool ignore_dupes) {
+    struct entry *e;
+    char *raw;
+
+    pa_assert(ma);
+    pa_assert(ma->raw);
+    pa_assert(ma->unescaped);
+    pa_assert(key);
+    pa_assert(value);
+
+    if (pa_hashmap_get(ma->unescaped, key)) {
+        pa_xfree(key);
+        pa_xfree(value);
+
+        if (ignore_dupes)
+            return 0;
+        else
+            return -1;
+    }
+
+    if (valid_keys) {
+        const char*const* v;
+        for (v = valid_keys; *v; v++)
+            if (pa_streq(*v, key))
+                break;
+
+        if (!*v) {
+            pa_xfree(key);
+            pa_xfree(value);
+            return -1;
+        }
+    }
+
+    raw = pa_xstrdup(value);
+
+    e = pa_xnew(struct entry, 1);
+    e->key = key;
+    e->value = pa_unescape(value);
+    pa_hashmap_put(ma->unescaped, key, e);
+
+    if (pa_streq(raw, value))
+        pa_xfree(raw);
+    else {
+        e = pa_xnew(struct entry, 1);
+        e->key = pa_xstrdup(key);
+        e->value = raw;
+        pa_hashmap_put(ma->raw, key, e);
+    }
+
+    return 0;
+}
+
+static void free_func(void *p) {
+    struct entry *e = p;
+    pa_assert(e);
+
+    pa_xfree(e->key);
+    pa_xfree(e->value);
+    pa_xfree(e);
+}
+
+static int parse(pa_modargs *ma, const char *args, const char* const* valid_keys, bool ignore_dupes) {
+    enum {
+        WHITESPACE,
+        KEY,
+        VALUE_START,
+        VALUE_SIMPLE,
+        VALUE_SIMPLE_ESCAPED,
+        VALUE_DOUBLE_QUOTES,
+        VALUE_DOUBLE_QUOTES_ESCAPED,
+        VALUE_TICKS,
+        VALUE_TICKS_ESCAPED
+    } state;
+
+    const char *p, *key = NULL, *value = NULL;
+    size_t key_len = 0, value_len = 0;
+
+    state = WHITESPACE;
+
+    for (p = args; *p; p++) {
+        switch (state) {
+
+            case WHITESPACE:
+                if (*p == '=')
+                    goto fail;
+                else if (!isspace((unsigned char)*p)) {
+                    key = p;
+                    state = KEY;
+                    key_len = 1;
+                }
+                break;
+
+            case KEY:
+                if (*p == '=')
+                    state = VALUE_START;
+                else if (isspace((unsigned char)*p))
+                    goto fail;
+                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((unsigned char)*p)) {
+                    if (add_key_value(ma,
+                                      pa_xstrndup(key, key_len),
+                                      pa_xstrdup(""),
+                                      valid_keys,
+                                      ignore_dupes) < 0)
+                        goto fail;
+                    state = WHITESPACE;
+                } else if (*p == '\\') {
+                    state = VALUE_SIMPLE_ESCAPED;
+                    value = p;
+                    value_len = 1;
+                } else {
+                    state = VALUE_SIMPLE;
+                    value = p;
+                    value_len = 1;
+                }
+                break;
+
+            case VALUE_SIMPLE:
+                if (isspace((unsigned char)*p)) {
+                    if (add_key_value(ma,
+                                      pa_xstrndup(key, key_len),
+                                      pa_xstrndup(value, value_len),
+                                      valid_keys,
+                                      ignore_dupes) < 0)
+                        goto fail;
+                    state = WHITESPACE;
+                } else if (*p == '\\') {
+                    state = VALUE_SIMPLE_ESCAPED;
+                    value_len++;
+                } else
+                    value_len++;
+                break;
+
+            case VALUE_SIMPLE_ESCAPED:
+                state = VALUE_SIMPLE;
+                value_len++;
+                break;
+
+            case VALUE_DOUBLE_QUOTES:
+                if (*p == '"') {
+                    if (add_key_value(ma,
+                                      pa_xstrndup(key, key_len),
+                                      pa_xstrndup(value, value_len),
+                                      valid_keys,
+                                      ignore_dupes) < 0)
+                        goto fail;
+                    state = WHITESPACE;
+                } else if (*p == '\\') {
+                    state = VALUE_DOUBLE_QUOTES_ESCAPED;
+                    value_len++;
+                } else
+                    value_len++;
+                break;
+
+            case VALUE_DOUBLE_QUOTES_ESCAPED:
+                state = VALUE_DOUBLE_QUOTES;
+                value_len++;
+                break;
+
+            case VALUE_TICKS:
+                if (*p == '\'') {
+                    if (add_key_value(ma,
+                                      pa_xstrndup(key, key_len),
+                                      pa_xstrndup(value, value_len),
+                                      valid_keys,
+                                      ignore_dupes) < 0)
+                        goto fail;
+                    state = WHITESPACE;
+                } else if (*p == '\\') {
+                    state = VALUE_TICKS_ESCAPED;
+                    value_len++;
+                } else
+                    value_len++;
+                break;
+
+            case VALUE_TICKS_ESCAPED:
+                state = VALUE_TICKS;
+                value_len++;
+                break;
+        }
+    }
+
+    if (state == VALUE_START) {
+        if (add_key_value(ma, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys, ignore_dupes) < 0)
+            goto fail;
+    } else if (state == VALUE_SIMPLE) {
+        if (add_key_value(ma, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys, ignore_dupes) < 0)
+            goto fail;
+    } else if (state != WHITESPACE)
+        goto fail;
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
+    pa_modargs *ma = pa_xnew(pa_modargs, 1);
+
+    ma->raw = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, free_func);
+    ma->unescaped = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, free_func);
+
+    if (args && parse(ma, args, valid_keys, false) < 0)
+        goto fail;
+
+    return ma;
+
+fail:
+    pa_modargs_free(ma);
+    return NULL;
+}
+
+int pa_modargs_append(pa_modargs *ma, const char *args, const char* const* valid_keys) {
+    return parse(ma, args, valid_keys, true);
+}
+
+void pa_modargs_free(pa_modargs*ma) {
+    pa_assert(ma);
+
+    pa_hashmap_free(ma->raw);
+    pa_hashmap_free(ma->unescaped);
+    pa_xfree(ma);
+}
+
+const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def) {
+    struct entry*e;
+
+    pa_assert(ma);
+    pa_assert(key);
+
+    if (!(e = pa_hashmap_get(ma->unescaped, key)))
+        return def;
+
+    return e->value;
+}
+
+static const char *modargs_get_value_raw(pa_modargs *ma, const char *key, const char *def) {
+    struct entry*e;
+
+    pa_assert(ma);
+    pa_assert(key);
+
+    if (!(e = pa_hashmap_get(ma->raw, key)))
+        if (!(e = pa_hashmap_get(ma->unescaped, key)))
+            return def;
+
+    return e->value;
+}
+
+int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) {
+    const char *v;
+
+    pa_assert(value);
+
+    if (!(v = pa_modargs_get_value(ma, key, NULL)))
+        return 0;
+
+    if (pa_atou(v, value) < 0)
+        return -1;
+
+    return 0;
+}
+
+int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {
+    const char *v;
+
+    pa_assert(value);
+
+    if (!(v = pa_modargs_get_value(ma, key, NULL)))
+        return 0;
+
+    if (pa_atoi(v, value) < 0)
+        return -1;
+
+    return 0;
+}
+
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, bool *value) {
+    const char *v;
+    int r;
+
+    pa_assert(value);
+
+    if (!(v = pa_modargs_get_value(ma, key, NULL)))
+        return 0;
+
+    if (!*v)
+        return -1;
+
+    if ((r = pa_parse_boolean(v)) < 0)
+        return -1;
+
+    *value = r;
+    return 0;
+}
+
+int pa_modargs_get_value_double(pa_modargs *ma, const char *key, double *value) {
+    const char *v;
+
+    pa_assert(value);
+
+    if (!(v = pa_modargs_get_value(ma, key, NULL)))
+        return 0;
+
+    if (pa_atod(v, value) < 0)
+        return -1;
+
+    return 0;
+}
+
+int pa_modargs_get_value_volume(pa_modargs *ma, const char *key, pa_volume_t *value) {
+    const char *v;
+
+    pa_assert(value);
+
+    if (!(v = pa_modargs_get_value(ma, key, NULL)))
+        return 0;
+
+    if (pa_parse_volume(v, value) < 0)
+        return -1;
+
+    return 0;
+}
+
+int pa_modargs_get_sample_rate(pa_modargs *ma, uint32_t *rate) {
+    uint32_t rate_local;
+
+    pa_assert(rate);
+
+    rate_local = *rate;
+    if ((pa_modargs_get_value_u32(ma, "rate", &rate_local)) < 0 ||
+        !pa_sample_rate_valid(rate_local))
+        return -1;
+
+    *rate = rate_local;
+
+    return 0;
+}
+
+int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
+    const char *format;
+    uint32_t channels;
+    pa_sample_spec ss;
+
+    pa_assert(rss);
+
+    ss = *rss;
+    if ((pa_modargs_get_sample_rate(ma, &ss.rate)) < 0)
+        return -1;
+
+    channels = ss.channels;
+    if ((pa_modargs_get_value_u32(ma, "channels", &channels)) < 0 ||
+        !pa_channels_valid(channels))
+        return -1;
+    ss.channels = (uint8_t) channels;
+
+    if ((format = pa_modargs_get_value(ma, "format", NULL)))
+        if ((ss.format = pa_parse_sample_format(format)) < 0)
+            return -1;
+
+    if (!pa_sample_spec_valid(&ss))
+        return -1;
+
+    *rss = ss;
+
+    return 0;
+}
+
+int pa_modargs_get_alternate_sample_rate(pa_modargs *ma, uint32_t *alternate_rate) {
+    uint32_t rate_local;
+
+    pa_assert(alternate_rate);
+
+    rate_local = *alternate_rate;
+    if ((pa_modargs_get_value_u32(ma, "alternate_rate", &rate_local)) < 0 ||
+        !pa_sample_rate_valid(*alternate_rate))
+        return -1;
+
+    *alternate_rate = rate_local;
+
+    return 0;
+}
+
+int pa_modargs_get_channel_map(pa_modargs *ma, const char *name, pa_channel_map *rmap) {
+    pa_channel_map map;
+    const char *cm;
+
+    pa_assert(rmap);
+
+    map = *rmap;
+
+    if ((cm = pa_modargs_get_value(ma, name ? name : "channel_map", NULL)))
+        if (!pa_channel_map_parse(&map, cm))
+            return -1;
+
+    if (!pa_channel_map_valid(&map))
+        return -1;
+
+    *rmap = map;
+    return 0;
+}
+
+int pa_modargs_get_resample_method(pa_modargs *ma, pa_resample_method_t *rmethod) {
+    const char *m;
+
+    pa_assert(ma);
+    pa_assert(rmethod);
+
+    if ((m = pa_modargs_get_value(ma, "resample_method", NULL))) {
+        pa_resample_method_t method = pa_parse_resample_method(m);
+
+        if (method == PA_RESAMPLER_INVALID)
+            return -1;
+
+        *rmethod = method;
+    }
+
+    return 0;
+}
+
+int pa_modargs_get_sample_spec_and_channel_map(
+        pa_modargs *ma,
+        pa_sample_spec *rss,
+        pa_channel_map *rmap,
+        pa_channel_map_def_t def) {
+
+    pa_sample_spec ss;
+    pa_channel_map map;
+
+    pa_assert(rss);
+    pa_assert(rmap);
+
+    ss = *rss;
+
+    if (pa_modargs_get_sample_spec(ma, &ss) < 0)
+        return -1;
+
+    map = *rmap;
+
+    if (ss.channels != map.channels)
+        pa_channel_map_init_extend(&map, ss.channels, def);
+
+    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0)
+        return -1;
+
+    if (map.channels != ss.channels) {
+        if (!pa_modargs_get_value(ma, "channels", NULL))
+            ss.channels = map.channels;
+        else
+            return -1;
+    }
+
+    *rmap = map;
+    *rss = ss;
+
+    return 0;
+}
+
+int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m) {
+    const char *v;
+    pa_proplist *n;
+
+    pa_assert(ma);
+    pa_assert(name);
+    pa_assert(p);
+
+    if (!(v = modargs_get_value_raw(ma, name, NULL)))
+        return 0;
+
+    if (!(n = pa_proplist_from_string(v)))
+        return -1;
+
+    pa_proplist_update(p, m, n);
+    pa_proplist_free(n);
+
+    return 0;
+}
+
+const char *pa_modargs_iterate(pa_modargs *ma, void **state) {
+    struct entry *e;
+
+    pa_assert(ma);
+
+    if (!(e = pa_hashmap_iterate(ma->unescaped, state, NULL)))
+        return NULL;
+
+    return e->key;
+}
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
new file mode 100644 (file)
index 0000000..96132a3
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef foomodargshfoo
+#define foomodargshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/proplist.h>
+#include <pulse/volume.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/resampler.h>
+
+typedef struct pa_modargs pa_modargs;
+
+/* A generic parser for module arguments */
+
+/* Parse the string args. The NULL-terminated array keys contains all valid arguments. */
+pa_modargs *pa_modargs_new(const char *args, const char* const keys[]);
+/* Parse the string args, and add any keys that are not already present. */
+int pa_modargs_append(pa_modargs *ma, const char *args, const char* const* valid_keys);
+void pa_modargs_free(pa_modargs*ma);
+
+/* Return the module argument for the specified name as a string. If
+ * the argument was not specified, return def instead.*/
+const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def);
+
+/* Return a module argument as unsigned 32bit value in *value. If the argument
+ * was not specified, *value remains unchanged. */
+int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value);
+int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value);
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, bool *value);
+
+/* Return a module argument as double value in *value. If the argument was not
+ * specified, *value remains unchanged. */
+int pa_modargs_get_value_double(pa_modargs *ma, const char *key, double *value);
+
+/* Return a module argument as pa_volume_t value in *value. If the argument
+ * was not specified, *value remains unchanged. */
+int pa_modargs_get_value_volume(pa_modargs *ma, const char *key, pa_volume_t *value);
+
+/* Return sample rate from the "rate" argument. If the argument was not
+ * specified, *rate remains unchanged. */
+int pa_modargs_get_sample_rate(pa_modargs *ma, uint32_t *rate);
+
+/* Return sample spec data from the three arguments "rate", "format" and
+ * "channels". If the argument was not specified, *ss remains unchanged. */
+int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss);
+
+/* Return channel map data from the argument "channel_map" if name is NULL,
+ * otherwise read from the specified argument. If the argument was not
+ * specified, *map remains unchanged. */
+int pa_modargs_get_channel_map(pa_modargs *ma, const char *name, pa_channel_map *map);
+
+/* Return resample method from the argument "resample_method". If the argument
+ * was not specified, *method remains unchanged. */
+int pa_modargs_get_resample_method(pa_modargs *ma, pa_resample_method_t *method);
+
+/* Combination of pa_modargs_get_sample_spec() and
+pa_modargs_get_channel_map(). Not always suitable, since this routine
+initializes the map parameter based on the channels field of the ss
+structure if no channel_map is found, using pa_channel_map_init_auto() */
+
+int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *ss, pa_channel_map *map, pa_channel_map_def_t def);
+
+/* Return alternate sample rate from "alternate_sample_rate" parameter. If the
+ * argument was not specified, *alternate_rate remains unchanged. */
+int pa_modargs_get_alternate_sample_rate(pa_modargs *ma, uint32_t *alternate_rate);
+
+int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m);
+
+/* Iterate through the module argument list. The user should allocate a
+ * state variable of type void* and initialize it with NULL. A pointer
+ * to this variable should then be passed to pa_modargs_iterate()
+ * which should be called in a loop until it returns NULL which
+ * signifies EOL. On each invocation this function will return the
+ * key string for the next entry. The keys in the argument list do not
+ * have any particular order. */
+const char *pa_modargs_iterate(pa_modargs *ma, void **state);
+
+#endif
diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c
new file mode 100644 (file)
index 0000000..e1a814f
--- /dev/null
@@ -0,0 +1,97 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ltdl.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/ltdl-helper.h>
+
+#include "modinfo.h"
+
+#define PA_SYMBOL_AUTHOR "pa__get_author"
+#define PA_SYMBOL_DESCRIPTION "pa__get_description"
+#define PA_SYMBOL_USAGE "pa__get_usage"
+#define PA_SYMBOL_VERSION "pa__get_version"
+#define PA_SYMBOL_DEPRECATED "pa__get_deprecated"
+#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
+
+pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {
+    pa_modinfo *i;
+    const char* (*func)(void);
+    bool (*func2) (void);
+
+    pa_assert(dl);
+
+    i = pa_xnew0(pa_modinfo, 1);
+
+    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_AUTHOR)))
+        i->author = pa_xstrdup(func());
+
+    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_DESCRIPTION)))
+        i->description = pa_xstrdup(func());
+
+    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_USAGE)))
+        i->usage = pa_xstrdup(func());
+
+    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_VERSION)))
+        i->version = pa_xstrdup(func());
+
+    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_DEPRECATED)))
+        i->deprecated = pa_xstrdup(func());
+
+    if ((func2 = (bool (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_LOAD_ONCE)))
+        i->load_once = func2();
+
+    return i;
+}
+
+pa_modinfo *pa_modinfo_get_by_name(const char *name) {
+    lt_dlhandle dl;
+    pa_modinfo *i;
+
+    pa_assert(name);
+
+    if (!(dl = lt_dlopenext(name))) {
+        pa_log("Failed to open module \"%s\": %s", name, lt_dlerror());
+        return NULL;
+    }
+
+    i = pa_modinfo_get_by_handle(dl, name);
+    lt_dlclose(dl);
+
+    return i;
+}
+
+void pa_modinfo_free(pa_modinfo *i) {
+    pa_assert(i);
+
+    pa_xfree(i->author);
+    pa_xfree(i->description);
+    pa_xfree(i->usage);
+    pa_xfree(i->version);
+    pa_xfree(i->deprecated);
+    pa_xfree(i);
+}
diff --git a/src/pulsecore/modinfo.h b/src/pulsecore/modinfo.h
new file mode 100644 (file)
index 0000000..5b99b77
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef foomodinfohfoo
+#define foomodinfohfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* Some functions for reading module meta data from PulseAudio modules */
+#include <pulsecore/macro.h>
+
+typedef struct pa_modinfo {
+    char *author;
+    char *description;
+    char *usage;
+    char *version;
+    char *deprecated;
+    bool load_once;
+} pa_modinfo;
+
+/* Read meta data from an libtool handle */
+pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name);
+
+/* Read meta data from a module file */
+pa_modinfo *pa_modinfo_get_by_name(const char *name);
+
+/* Free meta data */
+void pa_modinfo_free(pa_modinfo *i);
+
+#endif
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
new file mode 100644 (file)
index 0000000..ac15815
--- /dev/null
@@ -0,0 +1,395 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ltdl.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/proplist.h>
+
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/ltdl-helper.h>
+#include <pulsecore/modinfo.h>
+
+#include "module.h"
+
+#define PA_SYMBOL_INIT "pa__init"
+#define PA_SYMBOL_DONE "pa__done"
+#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
+#define PA_SYMBOL_GET_N_USED "pa__get_n_used"
+#define PA_SYMBOL_GET_DEPRECATE "pa__get_deprecated"
+
+bool pa_module_exists(const char *name) {
+    const char *paths, *state = NULL;
+    char *n, *p, *pathname;
+    bool result;
+
+    pa_assert(name);
+
+    if (name[0] == PA_PATH_SEP_CHAR) {
+        result = access(name, F_OK) == 0 ? true : false;
+        pa_log_debug("Checking for existence of '%s': %s", name, result ? "success" : "failure");
+        if (result)
+            return true;
+    }
+
+    if (!(paths = lt_dlgetsearchpath()))
+        return false;
+
+    /* strip .so from the end of name, if present */
+    n = pa_xstrdup(name);
+    p = strrchr(n, '.');
+    if (p && pa_streq(p, PA_SOEXT))
+        p[0] = 0;
+
+    while ((p = pa_split(paths, ":", &state))) {
+        pathname = pa_sprintf_malloc("%s" PA_PATH_SEP "%s" PA_SOEXT, p, n);
+        result = access(pathname, F_OK) == 0 ? true : false;
+        pa_log_debug("Checking for existence of '%s': %s", pathname, result ? "success" : "failure");
+        pa_xfree(pathname);
+        pa_xfree(p);
+        if (result) {
+            pa_xfree(n);
+            return true;
+        }
+    }
+
+    state = NULL;
+    if (PA_UNLIKELY(pa_run_from_build_tree())) {
+        while ((p = pa_split(paths, ":", &state))) {
+            pathname = pa_sprintf_malloc("%s" PA_PATH_SEP ".libs" PA_PATH_SEP "%s" PA_SOEXT, p, n);
+            result = access(pathname, F_OK) == 0 ? true : false;
+            pa_log_debug("Checking for existence of '%s': %s", pathname, result ? "success" : "failure");
+            pa_xfree(pathname);
+            pa_xfree(p);
+            if (result) {
+                pa_xfree(n);
+                return true;
+            }
+        }
+    }
+
+    pa_xfree(n);
+    return false;
+}
+
+void pa_module_hook_connect(pa_module *m, pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) {
+    pa_assert(m);
+    pa_assert(hook);
+    pa_assert(m->hooks);
+    pa_dynarray_append(m->hooks, pa_hook_connect(hook, prio, cb, data));
+}
+
+pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
+    pa_module *m = NULL;
+    bool (*load_once)(void);
+    const char* (*get_deprecated)(void);
+    pa_modinfo *mi;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    if (c->disallow_module_loading)
+        goto fail;
+
+    m = pa_xnew(pa_module, 1);
+    m->name = pa_xstrdup(name);
+    m->argument = pa_xstrdup(argument);
+    m->load_once = false;
+    m->proplist = pa_proplist_new();
+    m->hooks = pa_dynarray_new((pa_free_cb_t) pa_hook_slot_free);
+    m->index = PA_IDXSET_INVALID;
+
+    if (!(m->dl = lt_dlopenext(name))) {
+        /* We used to print the error that is returned by lt_dlerror(), but
+         * lt_dlerror() is useless. It returns pretty much always "file not
+         * found". That's because if there are any problems with loading the
+         * module with normal loaders, libltdl falls back to the "preload"
+         * loader, which never finds anything, and therefore says "file not
+         * found". */
+        pa_log("Failed to open module \"%s\".", name);
+        goto fail;
+    }
+
+    if ((load_once = (bool (*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_LOAD_ONCE))) {
+
+        m->load_once = load_once();
+
+        if (m->load_once) {
+            pa_module *i;
+            uint32_t idx;
+            /* OK, the module only wants to be loaded once, let's make sure it is */
+
+            PA_IDXSET_FOREACH(i, c->modules, idx) {
+                if (pa_streq(name, i->name)) {
+                    pa_log("Module \"%s\" should be loaded once at most. Refusing to load.", name);
+                    goto fail;
+                }
+            }
+        }
+    }
+
+    if ((get_deprecated = (const char* (*) (void)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_DEPRECATE))) {
+        const char *t;
+
+        if ((t = get_deprecated()))
+            pa_log_warn("%s is deprecated: %s", name, t);
+    }
+
+    if (!(m->init = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_INIT))) {
+        pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);
+        goto fail;
+    }
+
+    m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE);
+    m->get_n_used = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_N_USED);
+    m->userdata = NULL;
+    m->core = c;
+    m->unload_requested = false;
+
+    pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
+    pa_assert(m->index != PA_IDXSET_INVALID);
+
+    if (m->init(m) < 0) {
+        pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
+        goto fail;
+    }
+
+    pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : "");
+
+    pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
+
+    if ((mi = pa_modinfo_get_by_handle(m->dl, name))) {
+
+        if (mi->author && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_AUTHOR))
+            pa_proplist_sets(m->proplist, PA_PROP_MODULE_AUTHOR, mi->author);
+
+        if (mi->description && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_DESCRIPTION))
+            pa_proplist_sets(m->proplist, PA_PROP_MODULE_DESCRIPTION, mi->description);
+
+        if (mi->version && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_VERSION))
+            pa_proplist_sets(m->proplist, PA_PROP_MODULE_VERSION, mi->version);
+
+        pa_modinfo_free(mi);
+    }
+
+    pa_hook_fire(&m->core->hooks[PA_CORE_HOOK_MODULE_NEW], m);
+
+    return m;
+
+fail:
+
+    if (m) {
+        if (m->index != PA_IDXSET_INVALID)
+            pa_idxset_remove_by_index(c->modules, m->index);
+
+        if (m->hooks)
+            pa_dynarray_free(m->hooks);
+
+        if (m->proplist)
+            pa_proplist_free(m->proplist);
+
+        pa_xfree(m->argument);
+        pa_xfree(m->name);
+
+        if (m->dl)
+            lt_dlclose(m->dl);
+
+        pa_xfree(m);
+    }
+
+    return NULL;
+}
+
+static void postponed_dlclose(pa_mainloop_api *api, void *userdata) {
+    lt_dlhandle dl = userdata;
+
+    lt_dlclose(dl);
+}
+
+static void pa_module_free(pa_module *m) {
+    pa_assert(m);
+    pa_assert(m->core);
+
+    pa_log_info("Unloading \"%s\" (index: #%u).", m->name, m->index);
+    pa_hook_fire(&m->core->hooks[PA_CORE_HOOK_MODULE_UNLINK], m);
+
+    if (m->hooks) {
+       pa_dynarray_free(m->hooks);
+       m->hooks = NULL;
+    }
+
+    if (m->done)
+        m->done(m);
+
+    if (m->proplist)
+        pa_proplist_free(m->proplist);
+
+    /* If a module unloads itself with pa_module_unload(), we can't call
+     * lt_dlclose() here, because otherwise pa_module_unload() may return to a
+     * code location that has been removed from memory. Therefore, let's
+     * postpone the lt_dlclose() call a bit.
+     *
+     * Apparently lt_dlclose() doesn't always remove the module from memory,
+     * but it can happen, as can be seen here:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=96831 */
+    pa_mainloop_api_once(m->core->mainloop, postponed_dlclose, m->dl);
+
+    pa_hashmap_remove(m->core->modules_pending_unload, m);
+
+    pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index);
+
+    pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index);
+
+    pa_xfree(m->name);
+    pa_xfree(m->argument);
+    pa_xfree(m);
+}
+
+void pa_module_unload(pa_module *m, bool force) {
+    pa_assert(m);
+
+    if (m->core->disallow_module_loading && !force)
+        return;
+
+    if (!(m = pa_idxset_remove_by_data(m->core->modules, m, NULL)))
+        return;
+
+    pa_module_free(m);
+}
+
+void pa_module_unload_by_index(pa_core *c, uint32_t idx, bool force) {
+    pa_module *m;
+    pa_assert(c);
+    pa_assert(idx != PA_IDXSET_INVALID);
+
+    if (c->disallow_module_loading && !force)
+        return;
+
+    if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
+        return;
+
+    pa_module_free(m);
+}
+
+void pa_module_unload_all(pa_core *c) {
+    pa_module *m;
+    uint32_t *indices;
+    uint32_t state;
+    int i;
+
+    pa_assert(c);
+    pa_assert(c->modules);
+
+    if (pa_idxset_isempty(c->modules))
+        return;
+
+    /* Unload modules in reverse order by default */
+    indices = pa_xnew(uint32_t, pa_idxset_size(c->modules));
+    i = 0;
+    PA_IDXSET_FOREACH(m, c->modules, state)
+        indices[i++] = state;
+    pa_assert(i == (int) pa_idxset_size(c->modules));
+    i--;
+    for (; i >= 0; i--) {
+        m = pa_idxset_remove_by_index(c->modules, indices[i]);
+        if (m)
+            pa_module_free(m);
+    }
+    pa_xfree(indices);
+
+    /* Just in case module unloading caused more modules to load */
+    PA_IDXSET_FOREACH(m, c->modules, state)
+        pa_log_warn("After module unload, module '%s' was still loaded!", m->name);
+    c->disallow_module_loading = 1;
+    pa_idxset_remove_all(c->modules, (pa_free_cb_t) pa_module_free);
+    pa_assert(pa_idxset_isempty(c->modules));
+
+    if (c->module_defer_unload_event) {
+        c->mainloop->defer_free(c->module_defer_unload_event);
+        c->module_defer_unload_event = NULL;
+    }
+    pa_assert(pa_hashmap_isempty(c->modules_pending_unload));
+}
+
+static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
+    pa_core *c = PA_CORE(userdata);
+    pa_module *m;
+
+    pa_core_assert_ref(c);
+    api->defer_enable(e, 0);
+
+    while ((m = pa_hashmap_first(c->modules_pending_unload)))
+        pa_module_unload(m, true);
+}
+
+void pa_module_unload_request(pa_module *m, bool force) {
+    pa_assert(m);
+
+    if (m->core->disallow_module_loading && !force)
+        return;
+
+    m->unload_requested = true;
+    pa_hashmap_put(m->core->modules_pending_unload, m, m);
+
+    if (!m->core->module_defer_unload_event)
+        m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
+
+    m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1);
+}
+
+void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, bool force) {
+    pa_module *m;
+    pa_assert(c);
+
+    if (!(m = pa_idxset_get_by_index(c->modules, idx)))
+        return;
+
+    pa_module_unload_request(m, force);
+}
+
+int pa_module_get_n_used(pa_module*m) {
+    pa_assert(m);
+
+    if (!m->get_n_used)
+        return -1;
+
+    return m->get_n_used(m);
+}
+
+void pa_module_update_proplist(pa_module *m, pa_update_mode_t mode, pa_proplist *p) {
+    pa_assert(m);
+
+    if (p)
+        pa_proplist_update(m->proplist, mode, p);
+
+    pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
+    pa_hook_fire(&m->core->hooks[PA_CORE_HOOK_MODULE_PROPLIST_CHANGED], m);
+}
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
new file mode 100644 (file)
index 0000000..41e2189
--- /dev/null
@@ -0,0 +1,95 @@
+#ifndef foomodulehfoo
+#define foomodulehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <ltdl.h>
+
+typedef struct pa_module pa_module;
+
+#include <pulse/proplist.h>
+#include <pulsecore/dynarray.h>
+
+#include <pulsecore/core.h>
+
+struct pa_module {
+    pa_core *core;
+    char *name, *argument;
+    uint32_t index;
+
+    lt_dlhandle dl;
+
+    int (*init)(pa_module*m);
+    void (*done)(pa_module*m);
+    int (*get_n_used)(pa_module *m);
+
+    void *userdata;
+
+    bool load_once:1;
+    bool unload_requested:1;
+
+    pa_proplist *proplist;
+    pa_dynarray *hooks;
+};
+
+bool pa_module_exists(const char *name);
+
+pa_module* pa_module_load(pa_core *c, const char *name, const char *argument);
+
+void pa_module_unload(pa_module *m, bool force);
+void pa_module_unload_by_index(pa_core *c, uint32_t idx, bool force);
+
+void pa_module_unload_request(pa_module *m, bool force);
+void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, bool force);
+
+void pa_module_unload_all(pa_core *c);
+
+int pa_module_get_n_used(pa_module*m);
+
+void pa_module_update_proplist(pa_module *m, pa_update_mode_t mode, pa_proplist *p);
+
+void pa_module_hook_connect(pa_module *m, pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data);
+
+#define PA_MODULE_AUTHOR(s)                                     \
+    const char *pa__get_author(void) { return s; }              \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_DESCRIPTION(s)                                \
+    const char *pa__get_description(void) { return s; }         \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_USAGE(s)                                      \
+    const char *pa__get_usage(void) { return s; }               \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_VERSION(s)                                    \
+    const char * pa__get_version(void) { return s; }            \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_DEPRECATED(s)                                 \
+    const char * pa__get_deprecated(void) { return s; }         \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_LOAD_ONCE(b)                                  \
+    bool pa__load_once(void) { return b; }                 \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#endif
diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c
new file mode 100644 (file)
index 0000000..e1d0fff
--- /dev/null
@@ -0,0 +1,45 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "msgobject.h"
+
+PA_DEFINE_PUBLIC_CLASS(pa_msgobject, pa_object);
+
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_id, bool (*check_type)(const char *type_name)) {
+    pa_msgobject *o;
+
+    pa_assert(size >= sizeof(pa_msgobject));
+    pa_assert(type_id);
+
+    if (!check_type)
+        check_type = pa_msgobject_check_type;
+
+    pa_assert(check_type(type_id));
+    pa_assert(check_type(pa_object_type_id));
+    pa_assert(check_type(pa_msgobject_type_id));
+
+    o = PA_MSGOBJECT(pa_object_new_internal(size, type_id, check_type));
+    o->process_msg = NULL;
+    return o;
+}
diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h
new file mode 100644 (file)
index 0000000..dfb519d
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef foopulsemsgobjecthfoo
+#define foopulsemsgobjecthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/object.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_msgobject pa_msgobject;
+
+struct pa_msgobject {
+    pa_object parent;
+    int (*process_msg)(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+};
+
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_id, bool (*check_type)(const char *type_name));
+
+#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), type##_type_id, type##_check_type))
+#define pa_msgobject_free ((void (*) (pa_msgobject* o)) pa_object_free)
+
+#define PA_MSGOBJECT(o) pa_msgobject_cast(o)
+
+PA_DECLARE_PUBLIC_CLASS(pa_msgobject);
+
+#endif
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
new file mode 100644 (file)
index 0000000..a835be1
--- /dev/null
@@ -0,0 +1,162 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pthread.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "mutex.h"
+
+struct pa_mutex {
+    pthread_mutex_t mutex;
+};
+
+struct pa_cond {
+    pthread_cond_t cond;
+};
+
+pa_mutex* pa_mutex_new(bool recursive, bool inherit_priority) {
+    pa_mutex *m;
+    pthread_mutexattr_t attr;
+#ifdef HAVE_PTHREAD_PRIO_INHERIT
+    int r;
+#endif
+
+    pa_assert_se(pthread_mutexattr_init(&attr) == 0);
+
+    if (recursive)
+        pa_assert_se(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) == 0);
+
+#ifdef HAVE_PTHREAD_PRIO_INHERIT
+    if (inherit_priority) {
+        r = pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
+        pa_assert(r == 0 || r == ENOTSUP);
+    }
+#endif
+
+    m = pa_xnew(pa_mutex, 1);
+
+#ifndef HAVE_PTHREAD_PRIO_INHERIT
+    pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
+
+#else
+    if ((r = pthread_mutex_init(&m->mutex, &attr))) {
+
+        /* If this failed, then this was probably due to non-available
+         * priority inheritance. In which case we fall back to normal
+         * mutexes. */
+        pa_assert(r == ENOTSUP && inherit_priority);
+
+        pa_assert_se(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_NONE) == 0);
+        pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
+    }
+#endif
+
+    return m;
+}
+
+void pa_mutex_free(pa_mutex *m) {
+    pa_assert(m);
+
+    pa_assert_se(pthread_mutex_destroy(&m->mutex) == 0);
+    pa_xfree(m);
+}
+
+void pa_mutex_lock(pa_mutex *m) {
+    pa_assert(m);
+
+    pa_assert_se(pthread_mutex_lock(&m->mutex) == 0);
+}
+
+bool pa_mutex_try_lock(pa_mutex *m) {
+    int r;
+    pa_assert(m);
+
+    if ((r = pthread_mutex_trylock(&m->mutex)) != 0) {
+        pa_assert(r == EBUSY);
+        return false;
+    }
+
+    return true;
+}
+
+void pa_mutex_unlock(pa_mutex *m) {
+    pa_assert(m);
+
+    pa_assert_se(pthread_mutex_unlock(&m->mutex) == 0);
+}
+
+pa_cond *pa_cond_new(void) {
+    pa_cond *c;
+
+    c = pa_xnew(pa_cond, 1);
+    pa_assert_se(pthread_cond_init(&c->cond, NULL) == 0);
+    return c;
+}
+
+void pa_cond_free(pa_cond *c) {
+    pa_assert(c);
+
+    pa_assert_se(pthread_cond_destroy(&c->cond) == 0);
+    pa_xfree(c);
+}
+
+void pa_cond_signal(pa_cond *c, int broadcast) {
+    pa_assert(c);
+
+    if (broadcast)
+        pa_assert_se(pthread_cond_broadcast(&c->cond) == 0);
+    else
+        pa_assert_se(pthread_cond_signal(&c->cond) == 0);
+}
+
+int pa_cond_wait(pa_cond *c, pa_mutex *m) {
+    pa_assert(c);
+    pa_assert(m);
+
+    return pthread_cond_wait(&c->cond, &m->mutex);
+}
+
+pa_mutex* pa_static_mutex_get(pa_static_mutex *s, bool recursive, bool inherit_priority) {
+    pa_mutex *m;
+
+    pa_assert(s);
+
+    /* First, check if already initialized and short cut */
+    if ((m = pa_atomic_ptr_load(&s->ptr)))
+        return m;
+
+    /* OK, not initialized, so let's allocate, and fill in */
+    m = pa_mutex_new(recursive, inherit_priority);
+    if ((pa_atomic_ptr_cmpxchg(&s->ptr, NULL, m)))
+        return m;
+
+    pa_mutex_free(m);
+
+    /* Him, filling in failed, so someone else must have filled in
+     * already */
+    pa_assert_se(m = pa_atomic_ptr_load(&s->ptr));
+    return m;
+}
diff --git a/src/pulsecore/mutex-win32.c b/src/pulsecore/mutex-win32.c
new file mode 100644 (file)
index 0000000..370f443
--- /dev/null
@@ -0,0 +1,154 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <windows.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/hashmap.h>
+
+#include "mutex.h"
+
+struct pa_mutex {
+    CRITICAL_SECTION mutex;
+};
+
+struct pa_cond {
+    pa_hashmap *wait_events;
+};
+
+pa_mutex* pa_mutex_new(bool recursive, bool inherit_priority) {
+    pa_mutex *m;
+
+    m = pa_xnew(pa_mutex, 1);
+
+    InitializeCriticalSection(&m->mutex);
+
+    return m;
+}
+
+void pa_mutex_free(pa_mutex *m) {
+    assert(m);
+
+    DeleteCriticalSection(&m->mutex);
+    pa_xfree(m);
+}
+
+void pa_mutex_lock(pa_mutex *m) {
+    assert(m);
+
+    EnterCriticalSection(&m->mutex);
+}
+
+void pa_mutex_unlock(pa_mutex *m) {
+    assert(m);
+
+    LeaveCriticalSection(&m->mutex);
+}
+
+pa_cond *pa_cond_new(void) {
+    pa_cond *c;
+
+    c = pa_xnew(pa_cond, 1);
+    c->wait_events = pa_hashmap_new(NULL, NULL);
+    assert(c->wait_events);
+
+    return c;
+}
+
+void pa_cond_free(pa_cond *c) {
+    assert(c);
+
+    pa_hashmap_free(c->wait_events);
+    pa_xfree(c);
+}
+
+void pa_cond_signal(pa_cond *c, int broadcast) {
+    assert(c);
+
+    if (pa_hashmap_size(c->wait_events) == 0)
+        return;
+
+    if (broadcast)
+        SetEvent(pa_hashmap_first(c->wait_events));
+    else {
+        void *iter;
+        const void *key;
+        HANDLE event;
+
+        iter = NULL;
+        while (1) {
+            pa_hashmap_iterate(c->wait_events, &iter, &key);
+            if (key == NULL)
+                break;
+            event = (HANDLE)pa_hashmap_get(c->wait_events, key);
+            SetEvent(event);
+        }
+    }
+}
+
+int pa_cond_wait(pa_cond *c, pa_mutex *m) {
+    HANDLE event;
+
+    assert(c);
+    assert(m);
+
+    event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    assert(event);
+
+    pa_hashmap_put(c->wait_events, event, event);
+
+    pa_mutex_unlock(m);
+
+    WaitForSingleObject(event, INFINITE);
+
+    pa_mutex_lock(m);
+
+    pa_hashmap_remove(c->wait_events, event);
+
+    CloseHandle(event);
+
+    return 0;
+}
+
+/* This is a copy of the function in mutex-posix.c */
+pa_mutex* pa_static_mutex_get(pa_static_mutex *s, bool recursive, bool inherit_priority) {
+    pa_mutex *m;
+
+    pa_assert(s);
+
+    /* First, check if already initialized and short cut */
+    if ((m = pa_atomic_ptr_load(&s->ptr)))
+        return m;
+
+    /* OK, not initialized, so let's allocate, and fill in */
+    m = pa_mutex_new(recursive, inherit_priority);
+    if ((pa_atomic_ptr_cmpxchg(&s->ptr, NULL, m)))
+        return m;
+
+    pa_mutex_free(m);
+
+    /* Him, filling in failed, so someone else must have filled in
+     * already */
+    pa_assert_se(m = pa_atomic_ptr_load(&s->ptr));
+    return m;
+}
diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h
new file mode 100644 (file)
index 0000000..9cdd857
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef foopulsemutexhfoo
+#define foopulsemutexhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
+
+typedef struct pa_mutex pa_mutex;
+
+/* Please think twice before enabling priority inheritance. This is no
+ * magic wand! Use it only when the potentially prioritized threads are
+ * good candidates for it. Don't use this blindly! Also, note that
+ * only very few operating systems actually implement this, hence this
+ * is merely a hint. */
+pa_mutex* pa_mutex_new(bool recursive, bool inherit_priority);
+
+void pa_mutex_free(pa_mutex *m);
+void pa_mutex_lock(pa_mutex *m);
+bool pa_mutex_try_lock(pa_mutex *m);
+void pa_mutex_unlock(pa_mutex *m);
+
+typedef struct pa_cond pa_cond;
+
+pa_cond *pa_cond_new(void);
+void pa_cond_free(pa_cond *c);
+void pa_cond_signal(pa_cond *c, int broadcast);
+int pa_cond_wait(pa_cond *c, pa_mutex *m);
+
+/* Static mutexes are basically just atomically updated pointers to pa_mutex objects */
+
+typedef struct pa_static_mutex {
+    pa_atomic_ptr_t ptr;
+} pa_static_mutex;
+
+#define PA_STATIC_MUTEX_INIT { PA_ATOMIC_PTR_INIT(NULL) }
+
+pa_mutex* pa_static_mutex_get(pa_static_mutex *m, bool recursive, bool inherit_priority);
+
+#endif
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
new file mode 100644 (file)
index 0000000..0b73885
--- /dev/null
@@ -0,0 +1,227 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/source.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "namereg.h"
+
+struct namereg_entry {
+    pa_namereg_type_t type;
+    char *name;
+    void *data;
+};
+
+static bool is_valid_char(char c) {
+    return
+        (c >= 'a' && c <= 'z') ||
+        (c >= 'A' && c <= 'Z') ||
+        (c >= '0' && c <= '9') ||
+        c == '.' ||
+        c == '-' ||
+        c == '_';
+}
+
+bool pa_namereg_is_valid_name(const char *name) {
+    const char *c;
+
+    pa_assert(name);
+
+    if (*name == 0)
+        return false;
+
+    for (c = name; *c && (c-name < PA_NAME_MAX); c++)
+        if (!is_valid_char(*c))
+            return false;
+
+    if (*c)
+        return false;
+
+    return true;
+}
+
+bool pa_namereg_is_valid_name_or_wildcard(const char *name, pa_namereg_type_t type) {
+
+    pa_assert(name);
+
+    if (pa_namereg_is_valid_name(name))
+        return true;
+
+    if (type == PA_NAMEREG_SINK &&
+        pa_streq(name, "@DEFAULT_SINK@"))
+        return true;
+
+    if (type == PA_NAMEREG_SOURCE &&
+        (pa_streq(name, "@DEFAULT_SOURCE@") ||
+         pa_streq(name, "@DEFAULT_MONITOR@")))
+        return true;
+
+    return false;
+}
+
+char* pa_namereg_make_valid_name(const char *name) {
+    const char *a;
+    char *b, *n;
+
+    if (*name == 0)
+        return NULL;
+
+    n = pa_xnew(char, strlen(name)+1);
+
+    for (a = name, b = n; *a && (a-name < PA_NAME_MAX); a++, b++)
+        *b = (char) (is_valid_char(*a) ? *a : '_');
+
+    *b = 0;
+
+    return n;
+}
+
+const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, bool fail) {
+    struct namereg_entry *e;
+    char *n = NULL;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(data);
+
+    if (!*name)
+        return NULL;
+
+    if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE || type == PA_NAMEREG_CARD) &&
+        !pa_namereg_is_valid_name(name)) {
+
+        if (fail)
+            return NULL;
+
+        if (!(name = n = pa_namereg_make_valid_name(name)))
+            return NULL;
+    }
+
+    if ((e = pa_hashmap_get(c->namereg, name)) && fail) {
+        pa_xfree(n);
+        return NULL;
+    }
+
+    if (e) {
+        unsigned i;
+        size_t l = strlen(name);
+        char *k;
+
+        if (l+4 > PA_NAME_MAX) {
+            pa_xfree(n);
+            return NULL;
+        }
+
+        k = pa_xmalloc(l+4);
+
+        for (i = 2; i <= 99; i++) {
+            pa_snprintf(k, l+4, "%s.%u", name, i);
+
+            if (!(e = pa_hashmap_get(c->namereg, k)))
+                break;
+        }
+
+        if (e) {
+            pa_xfree(n);
+            pa_xfree(k);
+            return NULL;
+        }
+
+        pa_xfree(n);
+        n = k;
+    }
+
+    e = pa_xnew(struct namereg_entry, 1);
+    e->type = type;
+    e->name = n ? n : pa_xstrdup(name);
+    e->data = data;
+
+    pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0);
+
+    return e->name;
+}
+
+void pa_namereg_unregister(pa_core *c, const char *name) {
+    struct namereg_entry *e;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    pa_assert_se(e = pa_hashmap_remove(c->namereg, name));
+    pa_xfree(e->name);
+    pa_xfree(e);
+}
+
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
+    struct namereg_entry *e;
+    uint32_t idx;
+    pa_assert(c);
+
+    if (type == PA_NAMEREG_SOURCE && (!name || pa_streq(name, "@DEFAULT_SOURCE@"))) {
+        return c->default_source;
+
+    } else if (type == PA_NAMEREG_SINK && (!name || pa_streq(name, "@DEFAULT_SINK@"))) {
+        return c->default_sink;
+
+    } else if (type == PA_NAMEREG_SOURCE && name && pa_streq(name, "@DEFAULT_MONITOR@")) {
+        if (c->default_sink)
+            return c->default_sink->monitor_source;
+        else
+            return NULL;
+    }
+
+    if (!name)
+        return NULL;
+
+    if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE || type == PA_NAMEREG_CARD) &&
+        !pa_namereg_is_valid_name(name))
+        return NULL;
+
+    if ((e = pa_hashmap_get(c->namereg, name)))
+        if (e->type == type)
+            return e->data;
+
+    if (pa_atou(name, &idx) < 0)
+        return NULL;
+
+    if (type == PA_NAMEREG_SINK)
+        return pa_idxset_get_by_index(c->sinks, idx);
+    else if (type == PA_NAMEREG_SOURCE)
+        return pa_idxset_get_by_index(c->sources, idx);
+    else if (type == PA_NAMEREG_SAMPLE && c->scache)
+        return pa_idxset_get_by_index(c->scache, idx);
+    else if (type == PA_NAMEREG_CARD)
+        return pa_idxset_get_by_index(c->cards, idx);
+
+    return NULL;
+}
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
new file mode 100644 (file)
index 0000000..eb3475a
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef foonamereghfoo
+#define foonamereghfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
+
+#define PA_NAME_MAX 128
+
+typedef enum pa_namereg_type {
+    PA_NAMEREG_SINK,
+    PA_NAMEREG_SOURCE,
+    PA_NAMEREG_SAMPLE,
+    PA_NAMEREG_CARD
+} pa_namereg_type_t;
+
+const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, bool fail);
+void pa_namereg_unregister(pa_core *c, const char *name);
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type);
+
+bool pa_namereg_is_valid_name(const char *name);
+bool pa_namereg_is_valid_name_or_wildcard(const char *name, pa_namereg_type_t type);
+char* pa_namereg_make_valid_name(const char *name);
+
+#endif
diff --git a/src/pulsecore/native-common.c b/src/pulsecore/native-common.c
new file mode 100644 (file)
index 0000000..282a4ed
--- /dev/null
@@ -0,0 +1,78 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2016 Ahmed S. Darwish <darwish.07@gmail.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/tagstruct.h>
+
+#include "native-common.h"
+
+/*
+ * Command handlers shared between client and server
+ */
+
+/* Check pa_pstream_register_memfd_mempool() for further details */
+int pa_common_command_register_memfd_shmid(pa_pstream *p, pa_pdispatch *pd, uint32_t version,
+                                           uint32_t command, pa_tagstruct *t) {
+#if defined(HAVE_CREDS) && defined(HAVE_MEMFD)
+    pa_cmsg_ancil_data *ancil = NULL;
+    unsigned shm_id;
+    int ret = -1;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_REGISTER_MEMFD_SHMID);
+    pa_assert(t);
+
+    ancil = pa_pdispatch_take_ancil_data(pd);
+    if (!ancil)
+        goto finish;
+
+    /* Upon fd leaks and reaching our open fd limit, recvmsg(2)
+     * just strips all passed fds from the ancillary data */
+    if (ancil->nfd == 0) {
+        pa_log("Expected 1 memfd fd to be received over pipe; got 0");
+        pa_log("Did we reach our open file descriptors limit?");
+        goto finish;
+    }
+
+    if (ancil->nfd != 1 || ancil->fds[0] == -1)
+        goto finish;
+
+    if (version < 31 || pa_tagstruct_getu32(t, &shm_id) < 0 || !pa_tagstruct_eof(t))
+        goto finish;
+
+    pa_pstream_attach_memfd_shmid(p, shm_id, ancil->fds[0]);
+
+    ret = 0;
+finish:
+    if (ancil)
+        pa_cmsg_ancil_data_close_fds(ancil);
+
+    return ret;
+#else
+    return -1;
+#endif
+}
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
new file mode 100644 (file)
index 0000000..70338b9
--- /dev/null
@@ -0,0 +1,209 @@
+#ifndef foonativecommonhfoo
+#define foonativecommonhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/def.h>
+
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/tagstruct.h>
+
+PA_C_DECL_BEGIN
+
+enum {
+    /* Generic commands */
+    PA_COMMAND_ERROR,
+    PA_COMMAND_TIMEOUT, /* pseudo command */
+    PA_COMMAND_REPLY,
+
+    /* CLIENT->SERVER */
+    PA_COMMAND_CREATE_PLAYBACK_STREAM,        /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
+    PA_COMMAND_DELETE_PLAYBACK_STREAM,
+    PA_COMMAND_CREATE_RECORD_STREAM,          /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
+    PA_COMMAND_DELETE_RECORD_STREAM,
+    PA_COMMAND_EXIT,
+    PA_COMMAND_AUTH,
+    PA_COMMAND_SET_CLIENT_NAME,
+    PA_COMMAND_LOOKUP_SINK,
+    PA_COMMAND_LOOKUP_SOURCE,
+    PA_COMMAND_DRAIN_PLAYBACK_STREAM,
+    PA_COMMAND_STAT,
+    PA_COMMAND_GET_PLAYBACK_LATENCY,
+    PA_COMMAND_CREATE_UPLOAD_STREAM,
+    PA_COMMAND_DELETE_UPLOAD_STREAM,
+    PA_COMMAND_FINISH_UPLOAD_STREAM,
+    PA_COMMAND_PLAY_SAMPLE,
+    PA_COMMAND_REMOVE_SAMPLE,
+
+    PA_COMMAND_GET_SERVER_INFO,
+    PA_COMMAND_GET_SINK_INFO,
+    PA_COMMAND_GET_SINK_INFO_LIST,
+    PA_COMMAND_GET_SOURCE_INFO,
+    PA_COMMAND_GET_SOURCE_INFO_LIST,
+    PA_COMMAND_GET_MODULE_INFO,
+    PA_COMMAND_GET_MODULE_INFO_LIST,
+    PA_COMMAND_GET_CLIENT_INFO,
+    PA_COMMAND_GET_CLIENT_INFO_LIST,
+    PA_COMMAND_GET_SINK_INPUT_INFO,          /* Payload changed in v11 (0.9.7) */
+    PA_COMMAND_GET_SINK_INPUT_INFO_LIST,     /* Payload changed in v11 (0.9.7) */
+    PA_COMMAND_GET_SOURCE_OUTPUT_INFO,
+    PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST,
+    PA_COMMAND_GET_SAMPLE_INFO,
+    PA_COMMAND_GET_SAMPLE_INFO_LIST,
+    PA_COMMAND_SUBSCRIBE,
+
+    PA_COMMAND_SET_SINK_VOLUME,
+    PA_COMMAND_SET_SINK_INPUT_VOLUME,
+    PA_COMMAND_SET_SOURCE_VOLUME,
+
+    PA_COMMAND_SET_SINK_MUTE,
+    PA_COMMAND_SET_SOURCE_MUTE,
+
+    PA_COMMAND_CORK_PLAYBACK_STREAM,
+    PA_COMMAND_FLUSH_PLAYBACK_STREAM,
+    PA_COMMAND_TRIGGER_PLAYBACK_STREAM,
+
+    PA_COMMAND_SET_DEFAULT_SINK,
+    PA_COMMAND_SET_DEFAULT_SOURCE,
+
+    PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
+    PA_COMMAND_SET_RECORD_STREAM_NAME,
+
+    PA_COMMAND_KILL_CLIENT,
+    PA_COMMAND_KILL_SINK_INPUT,
+    PA_COMMAND_KILL_SOURCE_OUTPUT,
+
+    PA_COMMAND_LOAD_MODULE,
+    PA_COMMAND_UNLOAD_MODULE,
+
+    /* Obsolete */
+    PA_COMMAND_ADD_AUTOLOAD___OBSOLETE,
+    PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE,
+    PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE,
+    PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE,
+
+    PA_COMMAND_GET_RECORD_LATENCY,
+    PA_COMMAND_CORK_RECORD_STREAM,
+    PA_COMMAND_FLUSH_RECORD_STREAM,
+    PA_COMMAND_PREBUF_PLAYBACK_STREAM,
+
+    /* SERVER->CLIENT */
+    PA_COMMAND_REQUEST,
+    PA_COMMAND_OVERFLOW,
+    PA_COMMAND_UNDERFLOW,
+    PA_COMMAND_PLAYBACK_STREAM_KILLED,
+    PA_COMMAND_RECORD_STREAM_KILLED,
+    PA_COMMAND_SUBSCRIBE_EVENT,
+
+    /* A few more client->server commands */
+
+    /* Supported since protocol v10 (0.9.5) */
+    PA_COMMAND_MOVE_SINK_INPUT,
+    PA_COMMAND_MOVE_SOURCE_OUTPUT,
+
+    /* Supported since protocol v11 (0.9.7) */
+    PA_COMMAND_SET_SINK_INPUT_MUTE,
+
+    PA_COMMAND_SUSPEND_SINK,
+    PA_COMMAND_SUSPEND_SOURCE,
+
+    /* Supported since protocol v12 (0.9.8) */
+    PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
+    PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR,
+
+    PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE,
+    PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE,
+
+    /* SERVER->CLIENT */
+    PA_COMMAND_PLAYBACK_STREAM_SUSPENDED,
+    PA_COMMAND_RECORD_STREAM_SUSPENDED,
+    PA_COMMAND_PLAYBACK_STREAM_MOVED,
+    PA_COMMAND_RECORD_STREAM_MOVED,
+
+    /* Supported since protocol v13 (0.9.11) */
+    PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST,
+    PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
+    PA_COMMAND_UPDATE_CLIENT_PROPLIST,
+    PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST,
+    PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
+    PA_COMMAND_REMOVE_CLIENT_PROPLIST,
+
+    /* SERVER->CLIENT */
+    PA_COMMAND_STARTED,
+
+    /* Supported since protocol v14 (0.9.12) */
+    PA_COMMAND_EXTENSION,
+
+    /* Supported since protocol v15 (0.9.15) */
+    PA_COMMAND_GET_CARD_INFO,
+    PA_COMMAND_GET_CARD_INFO_LIST,
+    PA_COMMAND_SET_CARD_PROFILE,
+
+    PA_COMMAND_CLIENT_EVENT,
+    PA_COMMAND_PLAYBACK_STREAM_EVENT,
+    PA_COMMAND_RECORD_STREAM_EVENT,
+
+    /* SERVER->CLIENT */
+    PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED,
+    PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED,
+
+    /* Supported since protocol v16 (0.9.16) */
+    PA_COMMAND_SET_SINK_PORT,
+    PA_COMMAND_SET_SOURCE_PORT,
+
+    /* Supported since protocol v22 (1.0) */
+    PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME,
+    PA_COMMAND_SET_SOURCE_OUTPUT_MUTE,
+
+    /* Supported since protocol v27 (3.0) */
+    PA_COMMAND_SET_PORT_LATENCY_OFFSET,
+
+    /* Supported since protocol v30 (6.0) */
+    /* BOTH DIRECTIONS */
+    PA_COMMAND_ENABLE_SRBCHANNEL,
+    PA_COMMAND_DISABLE_SRBCHANNEL,
+
+    /* Supported since protocol v31 (9.0)
+     * BOTH DIRECTIONS */
+    PA_COMMAND_REGISTER_MEMFD_SHMID,
+
+    PA_COMMAND_MAX
+};
+
+#define PA_NATIVE_COOKIE_LENGTH 256
+#define PA_NATIVE_COOKIE_FILE "cookie"
+#define PA_NATIVE_COOKIE_FILE_FALLBACK ".pulse-cookie"
+
+#define PA_NATIVE_DEFAULT_PORT 4713
+
+#define PA_NATIVE_COOKIE_PROPERTY_NAME "protocol-native-cookie"
+#define PA_NATIVE_SERVER_PROPERTY_NAME "protocol-native-server"
+
+#define PA_NATIVE_DEFAULT_UNIX_SOCKET "native"
+
+int pa_common_command_register_memfd_shmid(pa_pstream *p, pa_pdispatch *pd, uint32_t version,
+                                           uint32_t command, pa_tagstruct *t);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c
new file mode 100644 (file)
index 0000000..cb0328f
--- /dev/null
@@ -0,0 +1,70 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "object.h"
+
+const char pa_object_type_id[] = "pa_object";
+
+pa_object *pa_object_new_internal(size_t size, const char *type_id, bool (*check_type)(const char *type_id)) {
+    pa_object *o;
+
+    pa_assert(size > sizeof(pa_object));
+    pa_assert(type_id);
+
+    if (!check_type)
+        check_type = pa_object_check_type;
+
+    pa_assert(check_type(type_id));
+    pa_assert(check_type(pa_object_type_id));
+
+    o = pa_xmalloc0(size);
+    PA_REFCNT_INIT(o);
+    o->type_id = type_id;
+    o->free = pa_object_free;
+    o->check_type = check_type;
+
+    return o;
+}
+
+pa_object *pa_object_ref(pa_object *o) {
+    pa_object_assert_ref(o);
+
+    PA_REFCNT_INC(o);
+    return o;
+}
+
+void pa_object_unref(pa_object *o) {
+    pa_object_assert_ref(o);
+
+    if (PA_REFCNT_DEC(o) <= 0) {
+        pa_assert(o->free);
+        o->free(o);
+    }
+}
+
+bool pa_object_check_type(const char *type_id) {
+    pa_assert(type_id);
+
+    return type_id == pa_object_type_id;
+}
diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h
new file mode 100644 (file)
index 0000000..b3d500e
--- /dev/null
@@ -0,0 +1,117 @@
+#ifndef foopulseobjecthfoo
+#define foopulseobjecthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/macro.h>
+
+typedef struct pa_object pa_object;
+
+struct pa_object {
+    PA_REFCNT_DECLARE;
+    const char *type_id;
+    void (*free)(pa_object *o);
+    bool (*check_type)(const char *type_name);
+};
+
+pa_object *pa_object_new_internal(size_t size, const char *type_id, bool (*check_type)(const char *type_id));
+#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), type##_type_id, type##_check_type)
+
+#define pa_object_free ((void (*) (pa_object* _obj)) pa_xfree)
+
+bool pa_object_check_type(const char *type_id);
+
+extern const char pa_object_type_id[];
+
+static inline bool pa_object_isinstance(void *o) {
+    pa_object *obj = (pa_object*) o;
+    return obj ? obj->check_type(pa_object_type_id) : true;
+}
+
+pa_object *pa_object_ref(pa_object *o);
+void pa_object_unref(pa_object *o);
+
+static inline int pa_object_refcnt(pa_object *o) {
+    return o ? PA_REFCNT_VALUE(o) : 0;
+}
+
+static inline pa_object* pa_object_cast(void *o) {
+    pa_object *obj = (pa_object*) o;
+    pa_assert(!obj || obj->check_type(pa_object_type_id));
+    return obj;
+}
+
+#define pa_object_assert_ref(o) pa_assert(pa_object_refcnt(o) > 0)
+
+#define PA_OBJECT(o) pa_object_cast(o)
+
+#define PA_DECLARE_CLASS_COMMON(c)                                      \
+    static inline bool c##_isinstance(void *o) {                        \
+        pa_object *obj = (pa_object*) o;                                \
+        return obj ? obj->check_type(c##_type_id) : true;               \
+    }                                                                   \
+    static inline c* c##_cast(void *o) {                                \
+        pa_assert(c##_isinstance(o));                                   \
+        return (c*) o;                                                  \
+    }                                                                   \
+    static inline c* c##_ref(c *o) {                                    \
+        return (c *) ((void *) pa_object_ref(PA_OBJECT(o)));            \
+    }                                                                   \
+    static inline void c##_unref(c* o) {                                \
+        pa_object_unref(PA_OBJECT(o));                                  \
+    }                                                                   \
+    static inline int c##_refcnt(c* o) {                                \
+        return pa_object_refcnt(PA_OBJECT(o));                          \
+    }                                                                   \
+    static inline void c##_assert_ref(c *o) {                           \
+        pa_object_assert_ref(PA_OBJECT(o));                             \
+    }                                                                   \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_DECLARE_PUBLIC_CLASS(c)                                      \
+    extern const char c##_type_id[];                                    \
+    PA_DECLARE_CLASS_COMMON(c);                                         \
+    bool c##_check_type(const char *type_id)
+
+#define PA_DEFINE_PUBLIC_CLASS(c, parent)                               \
+    const char c##_type_id[] = #c;                                      \
+    bool c##_check_type(const char *type_id) {                          \
+        if (type_id == c##_type_id)                                     \
+            return true;                                                \
+        return parent##_check_type(type_id);                            \
+    }                                                                   \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_DEFINE_PRIVATE_CLASS(c, parent)                              \
+    static const char c##_type_id[] = #c;                               \
+    PA_DECLARE_CLASS_COMMON(c);                                         \
+    static bool c##_check_type(const char *type_id) {                   \
+        if (type_id == c##_type_id)                                     \
+            return true;                                                \
+        return parent##_check_type(type_id);                            \
+    }                                                                   \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#endif
diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c
new file mode 100644 (file)
index 0000000..6e52801
--- /dev/null
@@ -0,0 +1,75 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+
+#include "once.h"
+
+/* See http://www.hpl.hp.com/research/linux/atomic_ops/example.php4 for the
+ * reference algorithm used here. */
+
+bool pa_once_begin(pa_once *control) {
+    pa_mutex *m;
+
+    pa_assert(control);
+
+    if (pa_atomic_load(&control->done))
+        return false;
+
+    /* Caveat: We have to make sure that the once func has completed
+     * before returning, even if the once func is not actually
+     * executed by us. Hence the awkward locking. */
+
+    m = pa_static_mutex_get(&control->mutex, false, false);
+    pa_mutex_lock(m);
+
+    if (pa_atomic_load(&control->done)) {
+        pa_mutex_unlock(m);
+        return false;
+    }
+
+    return true;
+}
+
+void pa_once_end(pa_once *control) {
+    pa_mutex *m;
+
+    pa_assert(control);
+
+    pa_assert(!pa_atomic_load(&control->done));
+    pa_atomic_store(&control->done, 1);
+
+    m = pa_static_mutex_get(&control->mutex, false, false);
+    pa_mutex_unlock(m);
+}
+
+/* Not reentrant -- how could it be? */
+void pa_run_once(pa_once *control, pa_once_func_t func) {
+    pa_assert(control);
+    pa_assert(func);
+
+    if (pa_once_begin(control)) {
+        func();
+        pa_once_end(control);
+    }
+}
diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h
new file mode 100644 (file)
index 0000000..8cd7894
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef foopulseoncehfoo
+#define foopulseoncehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/mutex.h>
+
+typedef struct pa_once {
+    pa_static_mutex mutex;
+    pa_atomic_t done;
+} pa_once;
+
+#define PA_ONCE_INIT                                                    \
+    {                                                                   \
+        .mutex = PA_STATIC_MUTEX_INIT,                                  \
+        .done = PA_ATOMIC_INIT(0)                                       \
+    }
+
+/* Not to be called directly, use the macros defined below instead */
+bool pa_once_begin(pa_once *o);
+void pa_once_end(pa_once *o);
+
+#define PA_ONCE_BEGIN                                                   \
+    do {                                                                \
+        static pa_once _once = PA_ONCE_INIT;                            \
+        if (pa_once_begin(&_once)) {{
+
+#define PA_ONCE_END                                                     \
+            }                                                           \
+            pa_once_end(&_once);                                        \
+        }                                                               \
+    } while(0)
+
+/*
+
+  Usage of these macros is like this:
+
+  void foo() {
+
+      PA_ONCE_BEGIN {
+
+          ... stuff to be called just once ...
+
+      } PA_ONCE_END;
+  }
+
+*/
+
+/* Same API but calls a function */
+typedef void (*pa_once_func_t) (void);
+void pa_run_once(pa_once *o, pa_once_func_t f);
+
+#endif
diff --git a/src/pulsecore/packet.c b/src/pulsecore/packet.c
new file mode 100644 (file)
index 0000000..2a61d58
--- /dev/null
@@ -0,0 +1,122 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/flist.h>
+
+#include "packet.h"
+
+#define MAX_APPENDED_SIZE 128
+
+struct pa_packet {
+    PA_REFCNT_DECLARE;
+    enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type;
+    size_t length;
+    uint8_t *data;
+    union {
+        uint8_t appended[MAX_APPENDED_SIZE];
+    } per_type;
+};
+
+PA_STATIC_FLIST_DECLARE(packets, 0, pa_xfree);
+
+pa_packet* pa_packet_new(size_t length) {
+    pa_packet *p;
+
+    pa_assert(length > 0);
+
+    if (!(p = pa_flist_pop(PA_STATIC_FLIST_GET(packets))))
+        p = pa_xnew(pa_packet, 1);
+    PA_REFCNT_INIT(p);
+    p->length = length;
+    if (length > MAX_APPENDED_SIZE) {
+        p->data = pa_xmalloc(length);
+        p->type = PA_PACKET_DYNAMIC;
+    } else {
+        p->data = p->per_type.appended;
+        p->type = PA_PACKET_APPENDED;
+    }
+
+    return p;
+}
+
+pa_packet* pa_packet_new_data(const void* data, size_t length) {
+    pa_packet *p = pa_packet_new(length);
+
+    pa_assert(data);
+    pa_assert(length > 0);
+
+    memcpy(p->data, data, length);
+
+    return p;
+}
+
+pa_packet* pa_packet_new_dynamic(void* data, size_t length) {
+    pa_packet *p;
+
+    pa_assert(data);
+    pa_assert(length > 0);
+
+    if (!(p = pa_flist_pop(PA_STATIC_FLIST_GET(packets))))
+        p = pa_xnew(pa_packet, 1);
+    PA_REFCNT_INIT(p);
+    p->length = length;
+    p->data = data;
+    p->type = PA_PACKET_DYNAMIC;
+
+    return p;
+}
+
+const void* pa_packet_data(pa_packet *p, size_t *l) {
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+    pa_assert(p->data);
+    pa_assert(l);
+
+    *l = p->length;
+
+    return p->data;
+}
+
+pa_packet* pa_packet_ref(pa_packet *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    PA_REFCNT_INC(p);
+    return p;
+}
+
+void pa_packet_unref(pa_packet *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    if (PA_REFCNT_DEC(p) <= 0) {
+        if (p->type == PA_PACKET_DYNAMIC)
+            pa_xfree(p->data);
+        if (pa_flist_push(PA_STATIC_FLIST_GET(packets), p) < 0)
+            pa_xfree(p);
+    }
+}
diff --git a/src/pulsecore/packet.h b/src/pulsecore/packet.h
new file mode 100644 (file)
index 0000000..2060987
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef foopackethfoo
+#define foopackethfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+typedef struct pa_packet pa_packet;
+
+/* create empty packet (either of type appended or dynamic depending
+ * on length) */
+pa_packet* pa_packet_new(size_t length);
+
+/* create packet (either of type appended or dynamic depending on length)
+ * and copy data */
+pa_packet* pa_packet_new_data(const void* data, size_t length);
+
+/* data must have been malloc()ed; the packet takes ownership of the memory,
+ * i.e. memory is free()d with the packet */
+pa_packet* pa_packet_new_dynamic(void* data, size_t length);
+
+const void* pa_packet_data(pa_packet *p, size_t *l);
+
+pa_packet* pa_packet_ref(pa_packet *p);
+void pa_packet_unref(pa_packet *p);
+
+#endif
diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c
new file mode 100644 (file)
index 0000000..b909f52
--- /dev/null
@@ -0,0 +1,156 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/arpa-inet.h>
+
+#include "parseaddr.h"
+
+/* Parse addresses in one of the following forms:
+ *    HOSTNAME
+ *    HOSTNAME:PORT
+ *    [HOSTNAME]
+ *    [HOSTNAME]:PORT
+ *
+ *  Return a newly allocated string of the hostname and fill in *ret_port if specified  */
+
+static char *parse_host(const char *s, uint16_t *ret_port) {
+    pa_assert(s);
+    pa_assert(ret_port);
+
+    if (*s == '[') {
+        char *e;
+        if (!(e = strchr(s+1, ']')))
+            return NULL;
+
+        if (e[1] == ':') {
+            uint32_t p;
+
+            if (pa_atou(e+2, &p) < 0)
+                return NULL;
+
+            *ret_port = (uint16_t) p;
+        } else if (e[1] != 0)
+            return NULL;
+
+        return pa_xstrndup(s+1, (size_t) (e-s-1));
+    } else {
+        char *e;
+        uint32_t p;
+
+        if (!(e = strrchr(s, ':')))
+            return pa_xstrdup(s);
+
+        if (pa_atou(e+1, &p) < 0)
+            return NULL;
+
+        *ret_port = (uint16_t) p;
+        return pa_xstrndup(s, (size_t) (e-s));
+    }
+}
+
+int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
+    const char *p;
+
+    pa_assert(name);
+    pa_assert(ret_p);
+
+    memset(ret_p, 0, sizeof(pa_parsed_address));
+    ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
+
+    if (*name == '{') {
+        char *id, *pfx;
+
+        /* The URL starts with a host id for detecting local connections */
+        if (!(id = pa_machine_id()))
+            return -1;
+
+        pfx = pa_sprintf_malloc("{%s}", id);
+        pa_xfree(id);
+
+        if (!pa_startswith(name, pfx)) {
+            pa_xfree(pfx);
+            /* Not local */
+            return -1;
+        }
+
+        p = name + strlen(pfx);
+        pa_xfree(pfx);
+    } else
+        p = name;
+
+    if (*p == '/')
+        ret_p->type = PA_PARSED_ADDRESS_UNIX;
+    else if (pa_startswith(p, "unix:")) {
+        ret_p->type = PA_PARSED_ADDRESS_UNIX;
+        p += sizeof("unix:")-1;
+    } else if (pa_startswith(p, "tcp:")) {
+        ret_p->type = PA_PARSED_ADDRESS_TCP4;
+        p += sizeof("tcp:")-1;
+    } else if (pa_startswith(p, "tcp4:")) {
+        ret_p->type = PA_PARSED_ADDRESS_TCP4;
+        p += sizeof("tcp4:")-1;
+    } else if (pa_startswith(p, "tcp6:")) {
+        ret_p->type = PA_PARSED_ADDRESS_TCP6;
+        p += sizeof("tcp6:")-1;
+    }
+
+    if (ret_p->type == PA_PARSED_ADDRESS_UNIX)
+        ret_p->path_or_host = pa_xstrdup(p);
+    else
+        if (!(ret_p->path_or_host = parse_host(p, &ret_p->port)))
+            return -1;
+
+    return 0;
+}
+
+bool pa_is_ip_address(const char *a) {
+    char buf[INET6_ADDRSTRLEN];
+
+    pa_assert(a);
+
+    if (inet_pton(AF_INET6, a, buf) >= 1)
+        return true;
+
+    if (inet_pton(AF_INET, a, buf) >= 1)
+        return true;
+
+    return false;
+}
+
+bool pa_is_ip6_address(const char *a) {
+    char buf[INET6_ADDRSTRLEN];
+
+    pa_assert(a);
+
+    if (inet_pton(AF_INET6, a, buf) >= 1)
+        return true;
+
+    return false;
+}
diff --git a/src/pulsecore/parseaddr.h b/src/pulsecore/parseaddr.h
new file mode 100644 (file)
index 0000000..6bb4d85
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef foopulsecoreparseaddrhfoo
+#define foopulsecoreparseaddrhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include <pulsecore/macro.h>
+
+typedef enum pa_parsed_address_type {
+    PA_PARSED_ADDRESS_UNIX,
+    PA_PARSED_ADDRESS_TCP4,
+    PA_PARSED_ADDRESS_TCP6,
+    PA_PARSED_ADDRESS_TCP_AUTO
+} pa_parsed_address_type_t;
+
+typedef struct pa_parsed_address {
+    pa_parsed_address_type_t type;
+    char *path_or_host;
+    uint16_t port;
+} pa_parsed_address;
+
+int pa_parse_address(const char *a, pa_parsed_address *ret_p);
+
+bool pa_is_ip_address(const char *a);
+
+bool pa_is_ip6_address(const char *a);
+
+#endif
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
new file mode 100644 (file)
index 0000000..ab632a5
--- /dev/null
@@ -0,0 +1,475 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/native-common.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/core-rtclock.h>
+
+#include "pdispatch.h"
+
+/* #define DEBUG_OPCODES */
+
+#ifdef DEBUG_OPCODES
+
+static const char *command_names[PA_COMMAND_MAX] = {
+    /* Generic commands */
+    [PA_COMMAND_ERROR] = "ERROR",
+    [PA_COMMAND_TIMEOUT] = "TIMEOUT",
+    [PA_COMMAND_REPLY] = "REPLY",
+
+    /* CLIENT->SERVER */
+    [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_EXIT] = "EXIT",
+    [PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME",
+    [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
+    [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
+    [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
+    [PA_COMMAND_STAT] = "STAT",
+    [PA_COMMAND_GET_PLAYBACK_LATENCY] = "GET_PLAYBACK_LATENCY",
+    [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM",
+    [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM",
+    [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM",
+    [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE",
+    [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE",
+
+    [PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO",
+    [PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO",
+    [PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST",
+    [PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO",
+    [PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST",
+    [PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO",
+    [PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST",
+    [PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO",
+    [PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST",
+    [PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO",
+    [PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST",
+    [PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO",
+    [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST",
+    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO",
+    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST",
+    [PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE",
+
+    [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME",
+    [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME",
+    [PA_COMMAND_SET_SOURCE_VOLUME] = "SET_SOURCE_VOLUME",
+
+    [PA_COMMAND_SET_SINK_MUTE] = "SET_SINK_MUTE",
+    [PA_COMMAND_SET_SOURCE_MUTE] = "SET_SOURCE_MUTE",
+
+    [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM",
+    [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM",
+    [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM",
+
+    [PA_COMMAND_SET_DEFAULT_SINK] = "SET_DEFAULT_SINK",
+    [PA_COMMAND_SET_DEFAULT_SOURCE] = "SET_DEFAULT_SOURCE",
+
+    [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = "SET_PLAYBACK_STREAM_NAME",
+    [PA_COMMAND_SET_RECORD_STREAM_NAME] = "SET_RECORD_STREAM_NAME",
+
+    [PA_COMMAND_KILL_CLIENT] = "KILL_CLIENT",
+    [PA_COMMAND_KILL_SINK_INPUT] = "KILL_SINK_INPUT",
+    [PA_COMMAND_KILL_SOURCE_OUTPUT] = "KILL_SOURCE_OUTPUT",
+
+    [PA_COMMAND_LOAD_MODULE] = "LOAD_MODULE",
+    [PA_COMMAND_UNLOAD_MODULE] = "UNLOAD_MODULE",
+
+    [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = "ADD_AUTOLOAD (obsolete)",
+    [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = "REMOVE_AUTOLOAD (obsolete)",
+    [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = "GET_AUTOLOAD_INFO (obsolete)",
+    [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = "GET_AUTOLOAD_INFO_LIST (obsolete)",
+
+    [PA_COMMAND_GET_RECORD_LATENCY] = "GET_RECORD_LATENCY",
+    [PA_COMMAND_CORK_RECORD_STREAM] = "CORK_RECORD_STREAM",
+    [PA_COMMAND_FLUSH_RECORD_STREAM] = "FLUSH_RECORD_STREAM",
+    [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = "PREBUF_PLAYBACK_STREAM",
+
+    /* SERVER->CLIENT */
+    [PA_COMMAND_REQUEST] = "REQUEST",
+    [PA_COMMAND_OVERFLOW] = "OVERFLOW",
+    [PA_COMMAND_UNDERFLOW] = "UNDERFLOW",
+    [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
+    [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
+    [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT",
+
+    /* A few more client->server commands */
+
+    /* Supported since protocol v10 (0.9.5) */
+    [PA_COMMAND_MOVE_SINK_INPUT] = "MOVE_SINK_INPUT",
+    [PA_COMMAND_MOVE_SOURCE_OUTPUT] = "MOVE_SOURCE_OUTPUT",
+
+    /* Supported since protocol v11 (0.9.7) */
+    [PA_COMMAND_SET_SINK_INPUT_MUTE] = "SET_SINK_INPUT_MUTE",
+
+    [PA_COMMAND_SUSPEND_SINK] = "SUSPEND_SINK",
+    [PA_COMMAND_SUSPEND_SOURCE] = "SUSPEND_SOURCE",
+
+    /* Supported since protocol v12 (0.9.8) */
+    [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = "SET_PLAYBACK_STREAM_BUFFER_ATTR",
+    [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = "SET_RECORD_STREAM_BUFFER_ATTR",
+
+    [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = "UPDATE_PLAYBACK_STREAM_SAMPLE_RATE",
+    [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = "UPDATE_RECORD_STREAM_SAMPLE_RATE",
+
+    /* SERVER->CLIENT */
+    [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = "PLAYBACK_STREAM_SUSPENDED",
+    [PA_COMMAND_RECORD_STREAM_SUSPENDED] = "RECORD_STREAM_SUSPENDED",
+    [PA_COMMAND_PLAYBACK_STREAM_MOVED] = "PLAYBACK_STREAM_MOVED",
+    [PA_COMMAND_RECORD_STREAM_MOVED] = "RECORD_STREAM_MOVED",
+
+    /* Supported since protocol v13 (0.9.11) */
+    [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST] = "UPDATE_RECORD_STREAM_PROPLIST",
+    [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST] = "UPDATE_PLAYBACK_STREAM_PROPLIST",
+    [PA_COMMAND_UPDATE_CLIENT_PROPLIST] = "UPDATE_CLIENT_PROPLIST",
+    [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST] = "REMOVE_RECORD_STREAM_PROPLIST",
+    [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = "REMOVE_PLAYBACK_STREAM_PROPLIST",
+    [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = "REMOVE_CLIENT_PROPLIST",
+
+    /* SERVER->CLIENT */
+    [PA_COMMAND_STARTED] = "STARTED",
+
+    /* Supported since protocol v14 (0.9.12) */
+    [PA_COMMAND_EXTENSION] = "EXTENSION",
+
+    /* Supported since protocol v15 (0.9.15) */
+    [PA_COMMAND_GET_CARD_INFO] = "GET_CARD_INFO",
+    [PA_COMMAND_GET_CARD_INFO_LIST] = "GET_CARD_INFO_LIST",
+    [PA_COMMAND_SET_CARD_PROFILE] = "SET_CARD_PROFILE",
+
+    [PA_COMMAND_CLIENT_EVENT] = "CLIENT_EVENT",
+    [PA_COMMAND_PLAYBACK_STREAM_EVENT] = "PLAYBACK_STREAM_EVENT",
+    [PA_COMMAND_RECORD_STREAM_EVENT] = "RECORD_STREAM_EVENT",
+
+    /* SERVER->CLIENT */
+    [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = "PLAYBACK_BUFFER_ATTR_CHANGED",
+    [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = "RECORD_BUFFER_ATTR_CHANGED",
+
+    /* Supported since protocol v16 (0.9.16) */
+    [PA_COMMAND_SET_SINK_PORT] = "SET_SINK_PORT",
+    [PA_COMMAND_SET_SOURCE_PORT] = "SET_SOURCE_PORT",
+
+    /* Supported since protocol v22 (1.0) */
+    [PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME] = "SET_SOURCE_OUTPUT_VOLUME",
+    [PA_COMMAND_SET_SOURCE_OUTPUT_MUTE] = "SET_SOURCE_OUTPUT_MUTE",
+
+    /* Supported since protocol v27 (3.0) */
+    [PA_COMMAND_SET_PORT_LATENCY_OFFSET] = "SET_PORT_LATENCY_OFFSET",
+
+    /* Supported since protocol v30 (6.0) */
+    /* BOTH DIRECTIONS */
+    [PA_COMMAND_ENABLE_SRBCHANNEL] = "ENABLE_SRBCHANNEL",
+    [PA_COMMAND_DISABLE_SRBCHANNEL] = "DISABLE_SRBCHANNEL",
+
+    /* Supported since protocol v31 (9.0) */
+    /* BOTH DIRECTIONS */
+    [PA_COMMAND_REGISTER_MEMFD_SHMID] = "REGISTER_MEMFD_SHMID",
+};
+
+#endif
+
+PA_STATIC_FLIST_DECLARE(reply_infos, 0, pa_xfree);
+
+struct reply_info {
+    pa_pdispatch *pdispatch;
+    PA_LLIST_FIELDS(struct reply_info);
+    pa_pdispatch_cb_t callback;
+    void *userdata;
+    pa_free_cb_t free_cb;
+    uint32_t tag;
+    pa_time_event *time_event;
+};
+
+struct pa_pdispatch {
+    PA_REFCNT_DECLARE;
+    pa_mainloop_api *mainloop;
+    const pa_pdispatch_cb_t *callback_table;
+    unsigned n_commands;
+    PA_LLIST_HEAD(struct reply_info, replies);
+    pa_pdispatch_drain_cb_t drain_callback;
+    void *drain_userdata;
+    pa_cmsg_ancil_data *ancil_data;
+    bool use_rtclock;
+};
+
+static void reply_info_free(struct reply_info *r) {
+    pa_assert(r);
+    pa_assert(r->pdispatch);
+    pa_assert(r->pdispatch->mainloop);
+
+    if (r->time_event)
+        r->pdispatch->mainloop->time_free(r->time_event);
+
+    PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos), r) < 0)
+        pa_xfree(r);
+}
+
+pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, bool use_rtclock, const pa_pdispatch_cb_t *table, unsigned entries) {
+    pa_pdispatch *pd;
+
+    pa_assert(mainloop);
+    pa_assert((entries && table) || (!entries && !table));
+
+    pd = pa_xnew0(pa_pdispatch, 1);
+    PA_REFCNT_INIT(pd);
+    pd->mainloop = mainloop;
+    pd->callback_table = table;
+    pd->n_commands = entries;
+    PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
+    pd->use_rtclock = use_rtclock;
+
+    return pd;
+}
+
+static void pdispatch_free(pa_pdispatch *pd) {
+    pa_assert(pd);
+
+    while (pd->replies) {
+        if (pd->replies->free_cb)
+            pd->replies->free_cb(pd->replies->userdata);
+
+        reply_info_free(pd->replies);
+    }
+
+    pa_xfree(pd);
+}
+
+static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
+    pa_pdispatch_cb_t callback;
+    void *userdata;
+    uint32_t tag;
+    pa_assert(r);
+
+    pa_pdispatch_ref(pd);
+
+    callback = r->callback;
+    userdata = r->userdata;
+    tag = r->tag;
+
+    reply_info_free(r);
+
+    callback(pd, command, tag, ts, userdata);
+
+    if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
+        pd->drain_callback(pd, pd->drain_userdata);
+
+    pa_pdispatch_unref(pd);
+}
+
+int pa_pdispatch_run(pa_pdispatch *pd, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
+    uint32_t tag, command;
+    pa_tagstruct *ts = NULL;
+    int ret = -1;
+    const void *pdata;
+    size_t plen;
+
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+    pa_assert(packet);
+
+    pa_pdispatch_ref(pd);
+
+    pdata = pa_packet_data(packet, &plen);
+    if (plen <= 8)
+        goto finish;
+
+    ts = pa_tagstruct_new_fixed(pdata, plen);
+
+    if (pa_tagstruct_getu32(ts, &command) < 0 ||
+        pa_tagstruct_getu32(ts, &tag) < 0)
+        goto finish;
+
+#ifdef DEBUG_OPCODES
+{
+    char t[256];
+    char const *p = NULL;
+
+    if (command >= PA_COMMAND_MAX || !(p = command_names[command]))
+        pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
+
+    pa_log("[%p] Received opcode <%s>", pd, p);
+}
+#endif
+
+    pd->ancil_data = ancil_data;
+
+    if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
+        struct reply_info *r;
+
+        PA_LLIST_FOREACH(r, pd->replies)
+            if (r->tag == tag)
+                break;
+
+        if (r)
+            run_action(pd, r, command, ts);
+
+    } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
+        const pa_pdispatch_cb_t *cb = pd->callback_table+command;
+
+        (*cb)(pd, command, tag, ts, userdata);
+    } else {
+        pa_log("Received unsupported command %u", command);
+        goto finish;
+    }
+
+    ret = 0;
+
+finish:
+    pd->ancil_data = NULL;
+
+    if (ts)
+        pa_tagstruct_free(ts);
+
+    pa_pdispatch_unref(pd);
+
+    return ret;
+}
+
+static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *t, void *userdata) {
+    struct reply_info*r = userdata;
+
+    pa_assert(r);
+    pa_assert(r->time_event == e);
+    pa_assert(r->pdispatch);
+    pa_assert(r->pdispatch->mainloop == m);
+    pa_assert(r->callback);
+
+    run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
+}
+
+void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) {
+    struct reply_info *r;
+    struct timeval tv;
+
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+    pa_assert(cb);
+
+    if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
+        r = pa_xnew(struct reply_info, 1);
+
+    r->pdispatch = pd;
+    r->callback = cb;
+    r->userdata = userdata;
+    r->free_cb = free_cb;
+    r->tag = tag;
+
+    pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop,
+                                                        pa_timeval_rtstore(&tv, pa_rtclock_now() + timeout * PA_USEC_PER_SEC, pd->use_rtclock),
+                                                        timeout_callback, r));
+
+    PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
+}
+
+int pa_pdispatch_is_pending(pa_pdispatch *pd) {
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+
+    return !!pd->replies;
+}
+
+void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_cb_t cb, void *userdata) {
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+    pa_assert(!cb || pa_pdispatch_is_pending(pd));
+
+    pd->drain_callback = cb;
+    pd->drain_userdata = userdata;
+}
+
+void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
+    struct reply_info *r, *n;
+
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+
+    PA_LLIST_FOREACH_SAFE(r, n, pd->replies)
+        if (r->userdata == userdata)
+            reply_info_free(r);
+}
+
+void pa_pdispatch_unref(pa_pdispatch *pd) {
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+
+    if (PA_REFCNT_DEC(pd) <= 0)
+        pdispatch_free(pd);
+}
+
+pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+
+    PA_REFCNT_INC(pd);
+    return pd;
+}
+
+#ifdef HAVE_CREDS
+
+const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+
+    if (pd->ancil_data && pd->ancil_data->creds_valid)
+         return &pd->ancil_data->creds;
+    return NULL;
+}
+
+/* Should be called only once during the dispatcher lifetime
+ *
+ * If the returned ancillary data contains any fds, caller maintains sole
+ * responsibility of closing them down using pa_cmsg_ancil_data_close_fds() */
+pa_cmsg_ancil_data *pa_pdispatch_take_ancil_data(pa_pdispatch *pd) {
+    pa_cmsg_ancil_data *ancil;
+
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+
+    ancil = pd->ancil_data;
+
+    /* iochannel guarantees us that nfd will always be capped */
+    if (ancil)
+        pa_assert(ancil->nfd <= MAX_ANCIL_DATA_FDS);
+
+    pd->ancil_data = NULL;
+    return ancil;
+}
+
+#endif
diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h
new file mode 100644 (file)
index 0000000..af76981
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef foopdispatchhfoo
+#define foopdispatchhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulse/def.h>
+
+#include <pulsecore/tagstruct.h>
+#include <pulsecore/packet.h>
+#include <pulsecore/creds.h>
+
+typedef struct pa_pdispatch pa_pdispatch;
+
+typedef void (*pa_pdispatch_cb_t)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+typedef void (*pa_pdispatch_drain_cb_t)(pa_pdispatch *pd, void *userdata);
+
+pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, bool use_rtclock, const pa_pdispatch_cb_t *table, unsigned entries);
+void pa_pdispatch_unref(pa_pdispatch *pd);
+pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd);
+
+int pa_pdispatch_run(pa_pdispatch *pd, pa_packet *p, pa_cmsg_ancil_data *ancil_data, void *userdata);
+
+void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t callback, void *userdata, pa_free_cb_t free_cb);
+
+int pa_pdispatch_is_pending(pa_pdispatch *pd);
+
+void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_cb_t callback, void *userdata);
+
+/* Remove all reply slots with the give userdata parameter */
+void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata);
+
+const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd);
+pa_cmsg_ancil_data *pa_pdispatch_take_ancil_data(pa_pdispatch *pd);
+
+#endif
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
new file mode 100644 (file)
index 0000000..8749b42
--- /dev/null
@@ -0,0 +1,398 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <signal.h>
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "pid.h"
+
+/* Read the PID data from the file descriptor fd, and return it. If no
+ * pid could be read, return 0, on failure (pid_t) -1 */
+static pid_t read_pid(const char *fn, int fd) {
+    ssize_t r;
+    char t[20], *e;
+    uint32_t pid;
+
+    pa_assert(fn);
+    pa_assert(fd >= 0);
+
+    if ((r = pa_loop_read(fd, t, sizeof(t)-1, NULL)) < 0) {
+        pa_log_warn("Failed to read PID file '%s': %s", fn, pa_cstrerror(errno));
+        return (pid_t) -1;
+    }
+
+    if (r == 0)
+        return (pid_t) 0;
+
+    t[r] = 0;
+    if ((e = strchr(t, '\n')))
+        *e = 0;
+
+    if (pa_atou(t, &pid) < 0) {
+        pa_log_warn("Failed to parse PID file '%s'", fn);
+        errno = EINVAL;
+        return (pid_t) -1;
+    }
+
+    return (pid_t) pid;
+}
+
+static int open_pid_file(const char *fn, int mode) {
+    int fd;
+
+    pa_assert(fn);
+
+    for (;;) {
+        struct stat st;
+
+        if ((fd = pa_open_cloexec(fn, mode
+#ifdef O_NOFOLLOW
+                       |O_NOFOLLOW
+#endif
+                       , S_IRUSR|S_IWUSR
+             )) < 0) {
+            if (mode != O_RDONLY || errno != ENOENT)
+                pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        /* Try to lock the file. If that fails, go without */
+        if (pa_lock_fd(fd, 1) < 0)
+            goto fail;
+
+        if (fstat(fd, &st) < 0) {
+            pa_log_warn("Failed to fstat() PID file '%s': %s", fn, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        /* Does the file still exist in the file system? When yes, we're done, otherwise restart */
+        if (st.st_nlink >= 1)
+            break;
+
+        if (pa_lock_fd(fd, 0) < 0)
+            goto fail;
+
+        if (pa_close(fd) < 0) {
+            pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno));
+            fd = -1;
+            goto fail;
+        }
+    }
+
+    return fd;
+
+fail:
+
+    if (fd >= 0) {
+        int saved_errno = errno;
+        pa_lock_fd(fd, 0);
+        pa_close(fd);
+        errno = saved_errno;
+    }
+
+    return -1;
+}
+
+static int proc_name_ours(pid_t pid, const char *procname) {
+#ifdef __linux__
+    char bn[PATH_MAX];
+    FILE *f;
+
+    pa_snprintf(bn, sizeof(bn), "/proc/%lu/stat", (unsigned long) pid);
+
+    if (!(f = pa_fopen_cloexec(bn, "r"))) {
+        pa_log_info("Failed to open %s: %s", bn, pa_cstrerror(errno));
+        return -1;
+    } else {
+        char *expected;
+        bool good;
+        char stored[64];
+
+        if (!(fgets(stored, sizeof(stored), f))) {
+            int saved_errno = feof(f) ? EINVAL : errno;
+            pa_log_info("Failed to read from %s: %s", bn, feof(f) ? "EOF" : pa_cstrerror(errno));
+            fclose(f);
+
+            errno = saved_errno;
+            return -1;
+        }
+
+        fclose(f);
+
+        expected = pa_sprintf_malloc("%lu (%s)", (unsigned long) pid, procname);
+        good = pa_startswith(stored, expected);
+        pa_xfree(expected);
+
+/*#if !defined(__OPTIMIZE__)*/
+        if (!good) {
+            /* libtool likes to rename our binary names ... */
+            expected = pa_sprintf_malloc("%lu (lt-%s)", (unsigned long) pid, procname);
+            good = pa_startswith(stored, expected);
+            pa_xfree(expected);
+        }
+/*#endif*/
+
+        return good;
+    }
+#else
+
+    return 1;
+#endif
+
+}
+
+/* Create a new PID file for the current process. */
+int pa_pid_file_create(const char *procname) {
+    int fd = -1;
+    int ret = -1;
+    char t[20];
+    pid_t pid;
+    size_t l;
+    char *fn;
+
+#ifdef OS_IS_WIN32
+    HANDLE process;
+#endif
+
+    if (!(fn = pa_runtime_path("pid")))
+        goto fail;
+
+    if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
+        goto fail;
+
+    if ((pid = read_pid(fn, fd)) == (pid_t) -1)
+        pa_log_warn("Corrupt PID file, overwriting.");
+    else if (pid > 0) {
+        int ours = 1;
+
+#ifdef OS_IS_WIN32
+        if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid)) != NULL) {
+            CloseHandle(process);
+#else
+        if (kill(pid, 0) >= 0 || errno != ESRCH) {
+#endif
+
+            if (procname)
+                if ((ours = proc_name_ours(pid, procname)) < 0) {
+                    pa_log_warn("Could not check to see if pid %lu is a pulseaudio process. "
+                                "Assuming it is and the daemon is already running.", (unsigned long) pid);
+                    goto fail;
+                }
+
+            if (ours) {
+                pa_log("Daemon already running.");
+                ret = 1;
+                goto fail;
+            }
+        }
+
+        pa_log_warn("Stale PID file, overwriting.");
+    }
+
+    /* Overwrite the current PID file */
+    if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, (off_t) 0) < 0) {
+        pa_log("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
+    l = strlen(t);
+
+    if (pa_loop_write(fd, t, l, NULL) != (ssize_t) l) {
+        pa_log("Failed to write PID file.");
+        goto fail;
+    }
+
+    ret = 0;
+
+fail:
+    if (fd >= 0) {
+        pa_lock_fd(fd, 0);
+
+        if (pa_close(fd) < 0) {
+            pa_log("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno));
+            ret = -1;
+        }
+    }
+
+    pa_xfree(fn);
+
+    return ret;
+}
+
+/* Remove the PID file, if it is ours */
+int pa_pid_file_remove(void) {
+    int fd = -1;
+    char *fn;
+    int ret = -1;
+    pid_t pid;
+
+    if (!(fn = pa_runtime_path("pid")))
+        goto fail;
+
+    if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
+        pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if ((pid = read_pid(fn, fd)) == (pid_t) -1)
+        goto fail;
+
+    if (pid != getpid()) {
+        pa_log("PID file '%s' not mine!", fn);
+        goto fail;
+    }
+
+    if (ftruncate(fd, (off_t) 0) < 0) {
+        pa_log_warn("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
+        goto fail;
+    }
+
+#ifdef OS_IS_WIN32
+    pa_lock_fd(fd, 0);
+    pa_close(fd);
+    fd = -1;
+#endif
+
+    if (unlink(fn) < 0) {
+        pa_log_warn("Failed to remove PID file '%s': %s", fn, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    ret = 0;
+
+fail:
+
+    if (fd >= 0) {
+        pa_lock_fd(fd, 0);
+
+        if (pa_close(fd) < 0) {
+            pa_log_warn("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno));
+            ret = -1;
+        }
+    }
+
+    pa_xfree(fn);
+
+    return ret;
+}
+
+/* Check whether the daemon is currently running, i.e. if a PID file
+ * exists and the PID therein too. Returns 0 on success, -1
+ * otherwise. If pid is non-NULL and a running daemon was found,
+ * return its PID therein */
+int pa_pid_file_check_running(pid_t *pid, const char *procname) {
+    return pa_pid_file_kill(0, pid, procname);
+}
+
+#ifndef OS_IS_WIN32
+
+/* Kill a current running daemon. Return non-zero on success, -1
+ * otherwise. If successful *pid contains the PID of the daemon
+ * process. */
+int pa_pid_file_kill(int sig, pid_t *pid, const char *procname) {
+    int fd = -1;
+    char *fn;
+    int ret = -1;
+    pid_t _pid;
+#ifdef __linux__
+    char *e = NULL;
+#endif
+
+    if (!pid)
+        pid = &_pid;
+
+    if (!(fn = pa_runtime_path("pid")))
+        goto fail;
+
+    if ((fd = open_pid_file(fn, O_RDONLY)) < 0) {
+
+        if (errno == ENOENT)
+            errno = ESRCH;
+
+        goto fail;
+    }
+
+    if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
+        goto fail;
+
+    if (procname) {
+        int ours;
+
+        if ((ours = proc_name_ours(*pid, procname)) < 0)
+            goto fail;
+
+        if (!ours) {
+            errno = ESRCH;
+            goto fail;
+        }
+    }
+
+    ret = kill(*pid, sig);
+
+fail:
+
+    if (fd >= 0) {
+        int saved_errno = errno;
+        pa_lock_fd(fd, 0);
+        pa_close(fd);
+        errno = saved_errno;
+    }
+
+#ifdef __linux__
+    pa_xfree(e);
+#endif
+
+    pa_xfree(fn);
+
+    return ret;
+
+}
+
+#else /* OS_IS_WIN32 */
+
+int pa_pid_file_kill(int sig, pid_t *pid, const char *exe_name) {
+    return -1;
+}
+
+#endif
diff --git a/src/pulsecore/pid.h b/src/pulsecore/pid.h
new file mode 100644 (file)
index 0000000..ff0356b
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef foopidhfoo
+#define foopidhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+int pa_pid_file_create(const char *procname);
+int pa_pid_file_remove(void);
+int pa_pid_file_check_running(pid_t *pid, const char *procname);
+int pa_pid_file_kill(int sig, pid_t *pid, const char *procname);
+
+#endif
diff --git a/src/pulsecore/pipe.c b/src/pulsecore/pipe.c
new file mode 100644 (file)
index 0000000..9f86a48
--- /dev/null
@@ -0,0 +1,155 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+
+#include <pulsecore/socket.h>
+#include <pulsecore/core-util.h>
+
+#include "pipe.h"
+
+#ifndef HAVE_PIPE
+
+static int set_block(int fd, int blocking) {
+#ifdef O_NONBLOCK
+
+    int v;
+
+    pa_assert(fd >= 0);
+
+    if ((v = fcntl(fd, F_GETFL)) < 0)
+        return -1;
+
+    if (blocking)
+        v &= ~O_NONBLOCK;
+    else
+        v |= O_NONBLOCK;
+
+    if (fcntl(fd, F_SETFL, v) < 0)
+        return -1;
+
+    return 0;
+
+#elif defined(OS_IS_WIN32)
+
+    u_long arg;
+
+    arg = !blocking;
+
+    if (ioctlsocket(fd, FIONBIO, &arg) < 0)
+        return -1;
+
+    return 0;
+
+#else
+
+    return -1;
+
+#endif
+}
+
+int pipe(int filedes[2]) {
+    int listener;
+    struct sockaddr_in addr, peer;
+    socklen_t len;
+
+    listener = -1;
+    filedes[0] = -1;
+    filedes[1] = -1;
+
+    listener = socket(PF_INET, SOCK_STREAM, 0);
+    if (listener < 0)
+        goto error;
+
+    filedes[0] = socket(PF_INET, SOCK_STREAM, 0);
+    if (filedes[0] < 0)
+        goto error;
+
+    filedes[1] = socket(PF_INET, SOCK_STREAM, 0);
+    if (filedes[1] < 0)
+        goto error;
+
+    /* Make non-blocking so that connect() won't block */
+    if (set_block(filedes[0], 0) < 0)
+        goto error;
+
+    addr.sin_family = AF_INET;
+    addr.sin_port = 0;
+    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+    if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) < 0)
+        goto error;
+
+    if (listen(listener, 1) < 0)
+        goto error;
+
+    len = sizeof(addr);
+    if (getsockname(listener, (struct sockaddr*)&addr, &len) < 0)
+        goto error;
+
+    if (connect(filedes[0], (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+#ifdef OS_IS_WIN32
+        if (WSAGetLastError() != EWOULDBLOCK)
+#else
+        if (errno != EINPROGRESS)
+#endif
+            goto error;
+    }
+
+    len = sizeof(peer);
+    filedes[1] = accept(listener, (struct sockaddr*)&peer, &len);
+    if (filedes[1] < 0)
+        goto error;
+
+    /* Restore blocking */
+    if (set_block(filedes[0], 1) < 0)
+        goto error;
+
+    len = sizeof(addr);
+    if (getsockname(filedes[0], (struct sockaddr*)&addr, &len) < 0)
+        goto error;
+
+    /* Check that someone else didn't steal the connection */
+    if ((addr.sin_port != peer.sin_port) || (addr.sin_addr.s_addr != peer.sin_addr.s_addr))
+        goto error;
+
+    pa_close(listener);
+
+    return 0;
+
+error:
+        if (listener >= 0)
+                pa_close(listener);
+        if (filedes[0] >= 0)
+                pa_close(filedes[0]);
+        if (filedes[1] >= 0)
+                pa_close(filedes[1]);
+
+        return -1;
+}
+
+#endif /* HAVE_PIPE */
diff --git a/src/pulsecore/pipe.h b/src/pulsecore/pipe.h
new file mode 100644 (file)
index 0000000..54e9819
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef foopipehfoo
+#define foopipehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifndef HAVE_PIPE
+
+int pipe(int filedes[2]);
+
+#endif
+
+#endif
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
new file mode 100644 (file)
index 0000000..c86bd15
--- /dev/null
@@ -0,0 +1,283 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/thread-mq.h>
+
+#include "play-memblockq.h"
+
+typedef struct memblockq_stream {
+    pa_msgobject parent;
+    pa_core *core;
+    pa_sink_input *sink_input;
+    pa_memblockq *memblockq;
+} memblockq_stream;
+
+enum {
+    MEMBLOCKQ_STREAM_MESSAGE_UNLINK,
+};
+
+PA_DEFINE_PRIVATE_CLASS(memblockq_stream, pa_msgobject);
+#define MEMBLOCKQ_STREAM(o) (memblockq_stream_cast(o))
+
+static void memblockq_stream_unlink(memblockq_stream *u) {
+    pa_assert(u);
+
+    if (!u->sink_input)
+        return;
+
+    pa_sink_input_unlink(u->sink_input);
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    memblockq_stream_unref(u);
+}
+
+static void memblockq_stream_free(pa_object *o) {
+    memblockq_stream *u = MEMBLOCKQ_STREAM(o);
+    pa_assert(u);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    pa_xfree(u);
+}
+
+static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    memblockq_stream *u = MEMBLOCKQ_STREAM(o);
+    memblockq_stream_assert_ref(u);
+
+    switch (code) {
+        case MEMBLOCKQ_STREAM_MESSAGE_UNLINK:
+            memblockq_stream_unlink(u);
+            break;
+    }
+
+    return 0;
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    memblockq_stream_unlink(u);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    /* If we are added for the first time, ask for a rewinding so that
+     * we are heard right-away. */
+    if (PA_SINK_INPUT_IS_LINKED(state) &&
+        i->thread_info.state == PA_SINK_INPUT_INIT && i->sink)
+        pa_sink_input_request_rewind(i, 0, false, true, true);
+}
+
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    if (!u->memblockq)
+        return -1;
+
+    if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
+
+        if (pa_sink_input_safe_to_remove(i)) {
+
+            pa_memblockq_free(u->memblockq);
+            u->memblockq = NULL;
+
+            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+        }
+
+        return -1;
+    }
+
+    /* If there's no memblock, there's going to be data in the memblockq after
+     * a gap with length chunk->length. Drop the gap and peek the actual
+     * data. There should always be some data coming - hence the assert. The
+     * gap will occur if the memblockq is rewound beyond index 0.*/
+    if (!chunk->memblock) {
+        pa_memblockq_drop(u->memblockq, chunk->length);
+        pa_assert_se(pa_memblockq_peek(u->memblockq, chunk) >= 0);
+    }
+
+    chunk->length = PA_MIN(chunk->length, nbytes);
+    pa_memblockq_drop(u->memblockq, chunk->length);
+
+    return 0;
+}
+
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    if (!u->memblockq)
+        return;
+
+    pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    if (!u->memblockq)
+        return;
+
+    pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+}
+
+pa_sink_input* pa_memblockq_sink_input_new(
+        pa_sink *sink,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_memblockq *q,
+        pa_cvolume *volume,
+        pa_proplist *p,
+        pa_sink_input_flags_t flags) {
+
+    memblockq_stream *u = NULL;
+    pa_sink_input_new_data data;
+
+    pa_assert(sink);
+    pa_assert(ss);
+
+    /* We allow creating this stream with no q set, so that it can be
+     * filled in later */
+
+    u = pa_msgobject_new(memblockq_stream);
+    u->parent.parent.free = memblockq_stream_free;
+    u->parent.process_msg = memblockq_stream_process_msg;
+    u->core = sink->core;
+    u->sink_input = NULL;
+    u->memblockq = NULL;
+
+    pa_sink_input_new_data_init(&data);
+    pa_sink_input_new_data_set_sink(&data, sink, false);
+    data.driver = __FILE__;
+    pa_sink_input_new_data_set_sample_spec(&data, ss);
+    pa_sink_input_new_data_set_channel_map(&data, map);
+    pa_sink_input_new_data_set_volume(&data, volume);
+    pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+    data.flags |= flags;
+
+    pa_sink_input_new(&u->sink_input, sink->core, &data);
+    pa_sink_input_new_data_done(&data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
+    u->sink_input->userdata = u;
+
+    if (q)
+        pa_memblockq_sink_input_set_queue(u->sink_input, q);
+
+    /* The reference to u is dangling here, because we want
+     * to keep this stream around until it is fully played. */
+
+    /* This sink input is not "put" yet, i.e. pa_sink_input_put() has
+     * not been called! */
+
+    return pa_sink_input_ref(u->sink_input);
+
+fail:
+    if (u)
+        memblockq_stream_unref(u);
+
+    return NULL;
+}
+
+int pa_play_memblockq(
+        pa_sink *sink,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_memblockq *q,
+        pa_cvolume *volume,
+        pa_proplist *p,
+        pa_sink_input_flags_t flags,
+        uint32_t *sink_input_index) {
+
+    pa_sink_input *i;
+
+    pa_assert(sink);
+    pa_assert(ss);
+    pa_assert(q);
+
+    if (!(i = pa_memblockq_sink_input_new(sink, ss, map, q, volume, p, flags)))
+        return -1;
+
+    pa_sink_input_put(i);
+
+    if (sink_input_index)
+        *sink_input_index = i->index;
+
+    pa_sink_input_unref(i);
+
+    return 0;
+}
+
+void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    if ((u->memblockq = q)) {
+        pa_memblockq_set_prebuf(q, 0);
+        pa_memblockq_set_silence(q, NULL);
+        pa_memblockq_willneed(q);
+    }
+}
diff --git a/src/pulsecore/play-memblockq.h b/src/pulsecore/play-memblockq.h
new file mode 100644 (file)
index 0000000..c13aced
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef fooplaymemblockqhfoo
+#define fooplaymemblockqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/sink.h>
+#include <pulsecore/memblockq.h>
+
+pa_sink_input* pa_memblockq_sink_input_new(
+        pa_sink *sink,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_memblockq *q,
+        pa_cvolume *volume,
+        pa_proplist *p,
+        pa_sink_input_flags_t flags);
+
+void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q);
+
+int pa_play_memblockq(
+        pa_sink *sink,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_memblockq *q,
+        pa_cvolume *cvolume,
+        pa_proplist *p,
+        pa_sink_input_flags_t flags,
+        uint32_t *sink_input_index);
+
+#endif
diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c
new file mode 100644 (file)
index 0000000..cc0bc16
--- /dev/null
@@ -0,0 +1,62 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/play-memblockq.h>
+
+#include "play-memchunk.h"
+
+int pa_play_memchunk(
+        pa_sink *sink,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        const pa_memchunk *chunk,
+        pa_cvolume *volume,
+        pa_proplist *p,
+        pa_sink_input_flags_t flags,
+        uint32_t *sink_input_index) {
+
+    pa_memblockq *q;
+    int r;
+    pa_memchunk silence;
+
+    pa_assert(sink);
+    pa_assert(ss);
+    pa_assert(chunk);
+
+    pa_silence_memchunk_get(&sink->core->silence_cache, sink->core->mempool, &silence, ss, 0);
+    q = pa_memblockq_new("pa_play_memchunk() q", 0, chunk->length, 0, ss, 1, 1, 0, &silence);
+    pa_memblock_unref(silence.memblock);
+
+    pa_assert_se(pa_memblockq_push(q, chunk) >= 0);
+
+    if ((r = pa_play_memblockq(sink, ss, map, q, volume, p, flags, sink_input_index)) < 0) {
+        pa_memblockq_free(q);
+        return r;
+    }
+
+    return 0;
+}
diff --git a/src/pulsecore/play-memchunk.h b/src/pulsecore/play-memchunk.h
new file mode 100644 (file)
index 0000000..7c56fe3
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef fooplaychunkhfoo
+#define fooplaychunkhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/sink.h>
+#include <pulsecore/memchunk.h>
+
+int pa_play_memchunk(
+        pa_sink *sink,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        const pa_memchunk *chunk,
+        pa_cvolume *cvolume,
+        pa_proplist *p,
+        pa_sink_input_flags_t flags,
+        uint32_t *sink_input_index);
+
+#endif
diff --git a/src/pulsecore/poll-posix.c b/src/pulsecore/poll-posix.c
new file mode 100644 (file)
index 0000000..e232295
--- /dev/null
@@ -0,0 +1,235 @@
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/***
+   Based on work for the GNU C Library.
+   Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
+***/
+
+/* Poll the file descriptors described by the NFDS structures starting at
+   FDS.  If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
+   an event to occur; if TIMEOUT is -1, block until an event occurs.
+   Returns the number of file descriptors with events, zero if timed out,
+   or -1 for errors.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <pulsecore/socket.h>
+#include <pulsecore/core-util.h>
+#include <pulse/util.h>
+
+#include "poll.h"
+
+/* Mac OSX fails to implement poll() in a working way since 10.4. IOW, for
+ * several years. We need to enable a dirty workaround and emulate that call
+ * with select(), just like for Windows. sic! */
+
+#if !defined(HAVE_POLL_H) || defined(OS_IS_DARWIN)
+
+int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
+    struct timeval tv;
+    fd_set rset, wset, xset;
+    struct pollfd *f;
+    int ready;
+    int maxfd = 0;
+#ifdef OS_IS_WIN32
+    char data[64];
+#endif
+
+    FD_ZERO (&rset);
+    FD_ZERO (&wset);
+    FD_ZERO (&xset);
+
+    if (nfds == 0) {
+        if (timeout >= 0) {
+            pa_msleep(timeout);
+            return 0;
+        }
+
+#ifdef OS_IS_WIN32
+        /*
+         * Windows does not support signals properly so waiting for them would
+         * mean a deadlock.
+         */
+        pa_msleep(100);
+        return 0;
+#else
+        return select(0, NULL, NULL, NULL, NULL);
+#endif
+    }
+
+    for (f = fds; f < &fds[nfds]; ++f) {
+        if (f->fd != -1) {
+            if (f->events & POLLIN)
+                FD_SET (f->fd, &rset);
+            if (f->events & POLLOUT)
+                FD_SET (f->fd, &wset);
+            if (f->events & POLLPRI)
+                FD_SET (f->fd, &xset);
+            if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
+                maxfd = f->fd;
+        }
+    }
+
+    tv.tv_sec = timeout / 1000;
+    tv.tv_usec = (timeout % 1000) * 1000;
+
+    ready = select(maxfd + 1, &rset, &wset, &xset, (timeout == -1 ? NULL : &tv));
+
+    if ((ready == -1) && (errno == EBADF)) {
+        ready = 0;
+        maxfd = -1;
+
+#ifdef OS_IS_WIN32
+        /*
+         * Windows has no fcntl(), so we have to trick around with more
+         * select() calls to find out what went wrong
+         */
+
+        FD_ZERO (&rset);
+        FD_ZERO (&wset);
+        FD_ZERO (&xset);
+
+        for (f = fds; f < &fds[nfds]; ++f) {
+            if (f->fd != -1) {
+                fd_set sngl_rset, sngl_wset, sngl_xset;
+
+                FD_ZERO (&sngl_rset);
+                FD_ZERO (&sngl_wset);
+                FD_ZERO (&sngl_xset);
+
+                if (f->events & POLLIN)
+                    FD_SET (f->fd, &sngl_rset);
+                if (f->events & POLLOUT)
+                    FD_SET (f->fd, &sngl_wset);
+                if (f->events & POLLPRI)
+                    FD_SET (f->fd, &sngl_xset);
+                if (f->events & (POLLIN|POLLOUT|POLLPRI)) {
+                    struct timeval singl_tv;
+
+                    singl_tv.tv_sec = 0;
+                    singl_tv.tv_usec = 0;
+
+                    if (select(f->fd, &rset, &wset, &xset, &singl_tv) != -1) {
+                        if (f->events & POLLIN)
+                            FD_SET (f->fd, &rset);
+                        if (f->events & POLLOUT)
+                            FD_SET (f->fd, &wset);
+                        if (f->events & POLLPRI)
+                            FD_SET (f->fd, &xset);
+                        if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
+                            maxfd = f->fd;
+                        ++ready;
+                    } else if (errno == EBADF)
+                        f->revents |= POLLNVAL;
+                }
+            }
+        }
+
+#else /* !OS_IS_WIN32 */
+
+        for (f = fds; f < &fds[nfds]; f++)
+            if (f->fd != -1) {
+                /* use fcntl() to find out whether the descriptor is valid */
+                if (fcntl(f->fd, F_GETFL) != -1) {
+                    if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) {
+                        maxfd = f->fd;
+                        ready++;
+                    }
+                } else {
+                    FD_CLR(f->fd, &rset);
+                    FD_CLR(f->fd, &wset);
+                    FD_CLR(f->fd, &xset);
+                }
+            }
+
+#endif
+
+        if (ready) {
+        /* Linux alters the tv struct... but it shouldn't matter here ...
+         * as we're going to be a little bit out anyway as we've just eaten
+         * more than a couple of cpu cycles above */
+            ready = select(maxfd + 1, &rset, &wset, &xset, (timeout == -1 ? NULL : &tv));
+        }
+    }
+
+#ifdef OS_IS_WIN32
+    errno = WSAGetLastError();
+#endif
+
+    if (ready > 0) {
+        ready = 0;
+        for (f = fds; f < &fds[nfds]; ++f) {
+            f->revents = 0;
+            if (f->fd != -1) {
+                if (FD_ISSET (f->fd, &rset)) {
+                    /* support for POLLHUP.  An hung up descriptor does not
+                       increase the return value! */
+#ifdef OS_IS_DARWIN
+                    /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
+                     * for some kinds of descriptors.  Detect if this descriptor is a
+                     * connected socket, a server socket, or something else using a
+                     * 0-byte recv, and use ioctl(2) to detect POLLHUP.  */
+                    int r = recv(f->fd, NULL, 0, MSG_PEEK);
+                    if (r == 0 || (r < 0 && errno == ENOTSOCK))
+                        ioctl(f->fd, FIONREAD, &r);
+
+                    if (r == 0)
+                        f->revents |= POLLHUP;
+#else /* !OS_IS_DARWIN */
+                    if (recv (f->fd, data, 64, MSG_PEEK) == -1) {
+                        if (errno == ESHUTDOWN || errno == ECONNRESET ||
+                            errno == ECONNABORTED || errno == ENETRESET) {
+                            fprintf(stderr, "Hangup\n");
+                            f->revents |= POLLHUP;
+                        }
+                    }
+#endif
+
+                    if (f->revents == 0)
+                        f->revents |= POLLIN;
+                }
+                if (FD_ISSET (f->fd, &wset))
+                    f->revents |= POLLOUT;
+                if (FD_ISSET (f->fd, &xset))
+                    f->revents |= POLLPRI;
+            }
+            if (f->revents)
+                ready++;
+        }
+    }
+
+    return ready;
+}
+
+#endif /* HAVE_SYS_POLL_H */
diff --git a/src/pulsecore/poll-win32.c b/src/pulsecore/poll-win32.c
new file mode 100644 (file)
index 0000000..c927e53
--- /dev/null
@@ -0,0 +1,646 @@
+
+/* Emulation for poll(2)
+   Contributed by Paolo Bonzini.
+
+   Copyright 2001-2003, 2006-2012 Free Software Foundation, Inc.
+
+   This file is part of gnulib.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License along
+   with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+/* Tell gcc not to warn about the (nfd < 0) tests, below.  */
+#if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__
+# pragma GCC diagnostic ignored "-Wtype-limits"
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <malloc.h>
+
+#include <sys/types.h>
+
+/* Specification.  */
+#include "poll.h"
+typedef unsigned long nfds_t;
+
+#include <errno.h>
+#include <limits.h>
+#include <assert.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+# define WINDOWS_NATIVE
+# include <winsock2.h>
+# include <windows.h>
+# include <io.h>
+# include <stdio.h>
+# include <conio.h>
+# include <signal.h>
+# if 0
+# include "msvc-nothrow.h"
+# endif
+#else
+# include <sys/time.h>
+# include <sys/socket.h>
+# include <sys/select.h>
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+
+#include <time.h>
+
+#include <pulsecore/core-util.h>
+
+#ifndef INFTIM
+# define INFTIM (-1)
+#endif
+
+/* BeOS does not have MSG_PEEK.  */
+#ifndef MSG_PEEK
+# define MSG_PEEK 0
+#endif
+
+#ifndef POLLRDNORM
+# define POLLRDNORM  0
+# define POLLRDBAND  0
+# define POLLWRNORM  0
+# define POLLWRBAND  0
+#endif
+
+#ifdef WINDOWS_NATIVE
+
+/* Optimized test whether a HANDLE refers to a console.
+   See <http://lists.gnu.org/archive/html/bug-gnulib/2009-08/msg00065.html>.  */
+#define IsConsoleHandle(h) (((intptr_t) (h) & 3) == 3)
+
+static BOOL
+IsSocketHandle (HANDLE h)
+{
+  WSANETWORKEVENTS ev;
+
+  if (IsConsoleHandle (h))
+    return FALSE;
+
+  /* Under Wine, it seems that getsockopt returns 0 for pipes too.
+     WSAEnumNetworkEvents instead distinguishes the two correctly.  */
+  ev.lNetworkEvents = 0xDEADBEEFl;
+  WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
+  return ev.lNetworkEvents != 0xDEADBEEFl;
+}
+
+static HANDLE
+HandleFromFd (int fd)
+{
+  /* since socket() returns a HANDLE already, try that first */
+  if (IsSocketHandle((HANDLE) fd))
+    return ((HANDLE) fd);
+
+  return ((HANDLE) _get_osfhandle(fd));
+}
+
+/* Declare data structures for ntdll functions.  */
+typedef struct _FILE_PIPE_LOCAL_INFORMATION {
+  ULONG NamedPipeType;
+  ULONG NamedPipeConfiguration;
+  ULONG MaximumInstances;
+  ULONG CurrentInstances;
+  ULONG InboundQuota;
+  ULONG ReadDataAvailable;
+  ULONG OutboundQuota;
+  ULONG WriteQuotaAvailable;
+  ULONG NamedPipeState;
+  ULONG NamedPipeEnd;
+} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
+
+typedef struct _IO_STATUS_BLOCK
+{
+  union {
+    DWORD Status;
+    PVOID Pointer;
+  } u;
+  ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+typedef enum _FILE_INFORMATION_CLASS {
+  FilePipeLocalInformation = 24
+} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
+
+typedef DWORD (WINAPI *PNtQueryInformationFile)
+         (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
+
+# ifndef PIPE_BUF
+#  define PIPE_BUF      512
+# endif
+
+/* Compute revents values for file handle H.  If some events cannot happen
+   for the handle, eliminate them from *P_SOUGHT.  */
+
+static int
+windows_compute_revents (HANDLE h, int *p_sought)
+{
+  int i, ret, happened;
+  INPUT_RECORD *irbuffer;
+  DWORD avail, nbuffer;
+  BOOL bRet;
+  IO_STATUS_BLOCK iosb;
+  FILE_PIPE_LOCAL_INFORMATION fpli;
+  static PNtQueryInformationFile NtQueryInformationFile;
+  static BOOL once_only;
+
+  switch (GetFileType (h))
+    {
+    case FILE_TYPE_PIPE:
+      if (!once_only)
+        {
+          NtQueryInformationFile = (PNtQueryInformationFile)
+            GetProcAddress (GetModuleHandle ("ntdll.dll"),
+                            "NtQueryInformationFile");
+          once_only = TRUE;
+        }
+
+      happened = 0;
+      if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
+        {
+          if (avail)
+            happened |= *p_sought & (POLLIN | POLLRDNORM);
+        }
+      else if (GetLastError () == ERROR_BROKEN_PIPE)
+        happened |= POLLHUP;
+
+      else
+        {
+          /* It was the write-end of the pipe.  Check if it is writable.
+             If NtQueryInformationFile fails, optimistically assume the pipe is
+             writable.  This could happen on Windows 9x, where
+             NtQueryInformationFile is not available, or if we inherit a pipe
+             that doesn't permit FILE_READ_ATTRIBUTES access on the write end
+             (I think this should not happen since Windows XP SP2; WINE seems
+             fine too).  Otherwise, ensure that enough space is available for
+             atomic writes.  */
+          memset (&iosb, 0, sizeof (iosb));
+          memset (&fpli, 0, sizeof (fpli));
+
+          if (!NtQueryInformationFile
+              || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
+                                         FilePipeLocalInformation)
+              || fpli.WriteQuotaAvailable >= PIPE_BUF
+              || (fpli.OutboundQuota < PIPE_BUF &&
+                  fpli.WriteQuotaAvailable == fpli.OutboundQuota))
+            happened |= *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
+        }
+      return happened;
+
+    case FILE_TYPE_CHAR:
+      ret = WaitForSingleObject (h, 0);
+      if (!IsConsoleHandle (h))
+        return ret == WAIT_OBJECT_0 ? *p_sought & ~(POLLPRI | POLLRDBAND) : 0;
+
+      nbuffer = avail = 0;
+      bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
+      if (bRet)
+        {
+          /* Input buffer.  */
+          *p_sought &= POLLIN | POLLRDNORM;
+          if (nbuffer == 0)
+            return POLLHUP;
+          if (!*p_sought)
+            return 0;
+
+          irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
+          bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail);
+          if (!bRet || avail == 0)
+            return POLLHUP;
+
+          for (i = 0; i < avail; i++)
+            if (irbuffer[i].EventType == KEY_EVENT)
+              return *p_sought;
+          return 0;
+        }
+      else
+        {
+          /* Screen buffer.  */
+          *p_sought &= POLLOUT | POLLWRNORM | POLLWRBAND;
+          return *p_sought;
+        }
+
+    default:
+      ret = WaitForSingleObject (h, 0);
+      if (ret == WAIT_OBJECT_0)
+        return *p_sought & ~(POLLPRI | POLLRDBAND);
+
+      return *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
+    }
+}
+
+/* Convert fd_sets returned by select into revents values.  */
+
+static int
+windows_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents)
+{
+  int happened = 0;
+
+  if ((lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) == FD_ACCEPT)
+    happened |= (POLLIN | POLLRDNORM) & sought;
+
+  else if (lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))
+    {
+      int r, error;
+
+      char data[64];
+      WSASetLastError (0);
+      r = recv (h, data, sizeof (data), MSG_PEEK);
+      error = WSAGetLastError ();
+      WSASetLastError (0);
+
+      if (r > 0 || error == WSAENOTCONN)
+        happened |= (POLLIN | POLLRDNORM) & sought;
+
+      /* Distinguish hung-up sockets from other errors.  */
+      else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET
+               || error == WSAECONNABORTED || error == WSAENETRESET)
+        happened |= POLLHUP;
+
+      else
+        happened |= POLLERR;
+    }
+
+  if (lNetworkEvents & (FD_WRITE | FD_CONNECT))
+    happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
+
+  if (lNetworkEvents & FD_OOB)
+    happened |= (POLLPRI | POLLRDBAND) & sought;
+
+  return happened;
+}
+
+#else /* !MinGW */
+
+/* Convert select(2) returned fd_sets into poll(2) revents values.  */
+static int
+compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds)
+{
+  int happened = 0;
+  if (FD_ISSET (fd, rfds))
+    {
+      int r;
+      int socket_errno;
+
+# if defined __MACH__ && defined __APPLE__
+      /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
+         for some kinds of descriptors.  Detect if this descriptor is a
+         connected socket, a server socket, or something else using a
+         0-byte recv, and use ioctl(2) to detect POLLHUP.  */
+      r = recv (fd, NULL, 0, MSG_PEEK);
+      socket_errno = (r < 0) ? errno : 0;
+      if (r == 0 || socket_errno == ENOTSOCK)
+        ioctl (fd, FIONREAD, &r);
+# else
+      char data[64];
+      r = recv (fd, data, sizeof (data), MSG_PEEK);
+      socket_errno = (r < 0) ? errno : 0;
+# endif
+      if (r == 0)
+        happened |= POLLHUP;
+
+      /* If the event happened on an unconnected server socket,
+         that's fine. */
+      else if (r > 0 || ( /* (r == -1) && */ socket_errno == ENOTCONN))
+        happened |= (POLLIN | POLLRDNORM) & sought;
+
+      /* Distinguish hung-up sockets from other errors.  */
+      else if (socket_errno == ESHUTDOWN || socket_errno == ECONNRESET
+               || socket_errno == ECONNABORTED || socket_errno == ENETRESET)
+        happened |= POLLHUP;
+
+      /* some systems can't use recv() on non-socket, including HP NonStop */
+      else if (socket_errno == ENOTSOCK)
+        happened |= (POLLIN | POLLRDNORM) & sought;
+
+      else
+        happened |= POLLERR;
+    }
+
+  if (FD_ISSET (fd, wfds))
+    happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
+
+  if (FD_ISSET (fd, efds))
+    happened |= (POLLPRI | POLLRDBAND) & sought;
+
+  return happened;
+}
+#endif /* !MinGW */
+
+int
+pa_poll (struct pollfd *pfd, nfds_t nfd, int timeout)
+{
+  struct timeval tv;
+
+#ifndef WINDOWS_NATIVE
+  struct timeval *ptv;
+  fd_set rfds, wfds, efds;
+  int maxfd, rc;
+  nfds_t i;
+
+# ifdef _SC_OPEN_MAX
+  static int sc_open_max = -1;
+
+  if (nfd < 0
+      || (nfd > sc_open_max
+          && (sc_open_max != -1
+              || nfd > (sc_open_max = sysconf (_SC_OPEN_MAX)))))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+# else /* !_SC_OPEN_MAX */
+#  ifdef OPEN_MAX
+  if (nfd < 0 || nfd > OPEN_MAX)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+#  endif /* OPEN_MAX -- else, no check is needed */
+# endif /* !_SC_OPEN_MAX */
+
+  /* EFAULT is not necessary to implement, but let's do it in the
+     simplest case. */
+  if (!pfd && nfd)
+    {
+      errno = EFAULT;
+      return -1;
+    }
+
+  /* convert timeout number into a timeval structure */
+  if (timeout == 0)
+    {
+      ptv = &tv;
+      ptv->tv_sec = 0;
+      ptv->tv_usec = 0;
+    }
+  else if (timeout > 0)
+    {
+      ptv = &tv;
+      ptv->tv_sec = timeout / 1000;
+      ptv->tv_usec = (timeout % 1000) * 1000;
+    }
+  else if (timeout == INFTIM)
+    /* wait forever */
+    ptv = NULL;
+  else
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  /* create fd sets and determine max fd */
+  maxfd = -1;
+  FD_ZERO (&rfds);
+  FD_ZERO (&wfds);
+  FD_ZERO (&efds);
+  for (i = 0; i < nfd; i++)
+    {
+      if (pfd[i].fd < 0)
+        continue;
+
+      if (pfd[i].events & (POLLIN | POLLRDNORM))
+        FD_SET (pfd[i].fd, &rfds);
+
+      /* see select(2): "the only exceptional condition detectable
+         is out-of-band data received on a socket", hence we push
+         POLLWRBAND events onto wfds instead of efds. */
+      if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND))
+        FD_SET (pfd[i].fd, &wfds);
+      if (pfd[i].events & (POLLPRI | POLLRDBAND))
+        FD_SET (pfd[i].fd, &efds);
+      if (pfd[i].fd >= maxfd
+          && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI
+                               | POLLRDNORM | POLLRDBAND
+                               | POLLWRNORM | POLLWRBAND)))
+        {
+          maxfd = pfd[i].fd;
+          if (maxfd > FD_SETSIZE)
+            {
+              errno = EOVERFLOW;
+              return -1;
+            }
+        }
+    }
+
+  /* examine fd sets */
+  rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
+  if (rc < 0)
+    return rc;
+
+  /* establish results */
+  rc = 0;
+  for (i = 0; i < nfd; i++)
+    if (pfd[i].fd < 0)
+      pfd[i].revents = 0;
+    else
+      {
+        int happened = compute_revents (pfd[i].fd, pfd[i].events,
+                                        &rfds, &wfds, &efds);
+        if (happened)
+          {
+            pfd[i].revents = happened;
+            rc++;
+          }
+      }
+
+  return rc;
+#else /* WINDOWS_NATIVE*/
+  HANDLE hEvent;
+  WSANETWORKEVENTS ev;
+  HANDLE h, handle_array[FD_SETSIZE + 2];
+  DWORD ret, wait_timeout, nhandles;
+  fd_set rfds, wfds, xfds;
+  BOOL poll_again;
+  MSG msg;
+  int rc = 0;
+  nfds_t i;
+
+  hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+
+restart:
+  handle_array[0] = hEvent;
+  nhandles = 1;
+  FD_ZERO (&rfds);
+  FD_ZERO (&wfds);
+  FD_ZERO (&xfds);
+
+  /* Classify socket handles and create fd sets. */
+  for (i = 0; i < nfd; i++)
+    {
+      int sought = pfd[i].events;
+      pfd[i].revents = 0;
+      if (pfd[i].fd < 0)
+        continue;
+      if (!(sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND
+                      | POLLPRI | POLLRDBAND)))
+        continue;
+
+      h = HandleFromFd (pfd[i].fd);
+      assert (h != NULL && h != INVALID_HANDLE_VALUE);
+      if (IsSocketHandle (h))
+        {
+          int requested = FD_CLOSE;
+
+          /* see above; socket handles are mapped onto select.  */
+          if (sought & (POLLIN | POLLRDNORM))
+            {
+              requested |= FD_READ | FD_ACCEPT;
+              FD_SET ((SOCKET) h, &rfds);
+            }
+          if (sought & (POLLOUT | POLLWRNORM | POLLWRBAND))
+            {
+              requested |= FD_WRITE | FD_CONNECT;
+              FD_SET ((SOCKET) h, &wfds);
+            }
+          if (sought & (POLLPRI | POLLRDBAND))
+            {
+              requested |= FD_OOB;
+              FD_SET ((SOCKET) h, &xfds);
+            }
+
+          if (requested)
+            WSAEventSelect ((SOCKET) h, hEvent, requested);
+        }
+      else
+        {
+          /* Poll now.  If we get an event, do not poll again.  Also,
+             screen buffer handles are waitable, and they'll block until
+             a character is available.  windows_compute_revents eliminates
+             bits for the "wrong" direction. */
+          pfd[i].revents = windows_compute_revents (h, &sought);
+          if (sought)
+            handle_array[nhandles++] = h;
+          if (pfd[i].revents)
+            timeout = 0;
+        }
+    }
+
+  /* We poll current status using select(). It cannot be used to check
+     anything but sockets, so we still have to wait in
+     MsgWaitForMultipleObjects(). But that in turn cannot check existing
+     state, so we can't remove this select(). */
+  /* FIXME: MSDN states that we cannot give empty fd_set:s. */
+  tv.tv_sec = tv.tv_usec = 0;
+  if (select (0, &rfds, &wfds, &xfds, &tv) > 0)
+    {
+      /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but
+         no need to call select again.  */
+      poll_again = FALSE;
+      wait_timeout = 0;
+    }
+  else
+    {
+      poll_again = TRUE;
+      if (timeout == INFTIM)
+        wait_timeout = INFINITE;
+      else
+        wait_timeout = timeout;
+    }
+
+  for (;;)
+    {
+      ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
+                                       wait_timeout, QS_ALLINPUT);
+
+      if (ret == WAIT_OBJECT_0 + nhandles)
+        {
+          /* new input of some other kind */
+          BOOL bRet;
+          while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
+            {
+              if (msg.message == WM_QUIT)
+                  raise(SIGTERM);
+              else
+                {
+                  TranslateMessage (&msg);
+                  DispatchMessage (&msg);
+                }
+            }
+        }
+      else
+        break;
+    }
+
+  if (poll_again)
+    select (0, &rfds, &wfds, &xfds, &tv);
+
+  /* Place a sentinel at the end of the array.  */
+  handle_array[nhandles] = NULL;
+  nhandles = 1;
+  for (i = 0; i < nfd; i++)
+    {
+      int happened;
+
+      if (pfd[i].fd < 0)
+        continue;
+      if (!(pfd[i].events & (POLLIN | POLLRDNORM |
+                             POLLOUT | POLLWRNORM | POLLWRBAND)))
+        continue;
+
+      h = (HANDLE) HandleFromFd (pfd[i].fd);
+      if (h != handle_array[nhandles])
+        {
+          /* It's a socket.  */
+          WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
+          WSAEventSelect ((SOCKET) h, 0, 0);
+          /* Have to restore blocking as WSAEventSelect() clears it */
+          if (!pa_is_fd_nonblock(pfd[i].fd))
+            pa_make_fd_block(pfd[i].fd);
+
+          /* If we're lucky, WSAEnumNetworkEvents already provided a way
+             to distinguish FD_READ and FD_ACCEPT; this saves a recv later.  */
+          if (FD_ISSET ((SOCKET) h, &rfds)
+              && !(ev.lNetworkEvents & (FD_READ | FD_ACCEPT)))
+            ev.lNetworkEvents |= FD_READ | FD_ACCEPT;
+          if (FD_ISSET ((SOCKET) h, &wfds))
+            ev.lNetworkEvents |= FD_WRITE | FD_CONNECT;
+          if (FD_ISSET ((SOCKET) h, &xfds))
+            ev.lNetworkEvents |= FD_OOB;
+
+          happened = windows_compute_revents_socket ((SOCKET) h, pfd[i].events,
+                                                     ev.lNetworkEvents);
+        }
+      else
+        {
+          /* Not a socket.  */
+          int sought = pfd[i].events;
+          happened = windows_compute_revents (h, &sought);
+          nhandles++;
+        }
+
+       if ((pfd[i].revents |= happened) != 0)
+        rc++;
+    }
+
+  if (!rc && timeout == INFTIM)
+    {
+      SleepEx (1, TRUE);
+      goto restart;
+    }
+
+  CloseHandle(hEvent);
+
+  return rc;
+#endif
+}
diff --git a/src/pulsecore/poll.h b/src/pulsecore/poll.h
new file mode 100644 (file)
index 0000000..4af1b99
--- /dev/null
@@ -0,0 +1,62 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/***
+   Based on work for the GNU C Library.
+   Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc.
+***/
+
+#if defined(HAVE_POLL_H)
+#include <poll.h>
+#else
+
+/* Event types that can be polled for.  These bits may be set in `events'
+   to indicate the interesting event types; they will appear in `revents'
+   to indicate the status of the file descriptor.  */
+#define POLLIN          0x001           /* There is data to read.  */
+#define POLLPRI         0x002           /* There is urgent data to read.  */
+#define POLLOUT         0x004           /* Writing now will not block.  */
+
+/* Event types always implicitly polled for.  These bits need not be set in
+   `events', but they will appear in `revents' to indicate the status of
+   the file descriptor.  */
+#define POLLERR         0x008           /* Error condition.  */
+#define POLLHUP         0x010           /* Hung up.  */
+#define POLLNVAL        0x020           /* Invalid polling request.  */
+
+/* Data structure describing a polling request.  */
+struct pollfd {
+    int fd;                     /* File descriptor to poll.  */
+    short int events;           /* Types of events poller cares about.  */
+    short int revents;          /* Types of events that actually occurred.  */
+};
+
+/* Poll the file descriptors described by the NFDS structures starting at
+   FDS.  If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
+   an event to occur; if TIMEOUT is -1, block until an event occurs.
+   Returns the number of file descriptors with events, zero if timed out,
+   or -1 for errors.  */
+
+#endif /* HAVE_POLL_H */
+
+#if defined(HAVE_POLL_H) && !defined(OS_IS_DARWIN)
+#define pa_poll(fds,nfds,timeout) poll((fds),(nfds),(timeout))
+#else
+int pa_poll(struct pollfd *fds, unsigned long nfds, int timeout);
+#endif
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
new file mode 100644 (file)
index 0000000..baf683d
--- /dev/null
@@ -0,0 +1,274 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <locale.h>
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#endif
+
+#ifdef __APPLE__
+#include <crt_externs.h>
+#define environ (*_NSGetEnviron())
+#elif !HAVE_DECL_ENVIRON
+extern char **environ;
+#endif
+
+#include <pulse/gccmacro.h>
+#include <pulse/proplist.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+
+#if defined(HAVE_GLIB) && defined(PA_GCC_WEAKREF)
+#include <glib.h>
+static G_CONST_RETURN gchar* _g_get_application_name(void) PA_GCC_WEAKREF(g_get_application_name);
+#endif
+
+#if defined(HAVE_GTK) && defined(PA_GCC_WEAKREF)
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+static G_CONST_RETURN gchar* _gtk_window_get_default_icon_name(void) PA_GCC_WEAKREF(gtk_window_get_default_icon_name);
+static Display *_gdk_display PA_GCC_WEAKREF(gdk_display);
+#endif
+
+#include "proplist-util.h"
+
+static void add_glib_properties(pa_proplist *p) {
+
+#if defined(HAVE_GLIB) && defined(PA_GCC_WEAKREF)
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_NAME))
+        if (_g_get_application_name) {
+            const gchar *t;
+
+            /* We ignore the tiny race condition here. */
+
+            if ((t = _g_get_application_name()))
+                pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, t);
+        }
+
+#endif
+}
+
+static void add_gtk_properties(pa_proplist *p) {
+
+#if defined(HAVE_GTK) && defined(PA_GCC_WEAKREF)
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_ICON_NAME))
+        if (_gtk_window_get_default_icon_name) {
+            const gchar *t;
+
+            /* We ignore the tiny race condition here. */
+
+            if ((t = _gtk_window_get_default_icon_name()))
+                pa_proplist_sets(p, PA_PROP_APPLICATION_ICON_NAME, t);
+        }
+
+    if (!pa_proplist_contains(p, PA_PROP_WINDOW_X11_DISPLAY))
+        if (&_gdk_display && _gdk_display) {
+            const char *t;
+
+            /* We ignore the tiny race condition here. */
+
+            if ((t = DisplayString(_gdk_display)))
+                pa_proplist_sets(p, PA_PROP_WINDOW_X11_DISPLAY, t);
+        }
+
+#endif
+}
+
+void pa_init_proplist(pa_proplist *p) {
+    char **e;
+    const char *pp;
+
+    pa_assert(p);
+
+    if (environ) {
+
+        /* Some applications seem to reset environ to NULL for various
+         * reasons, hence we need to check for this explicitly. See
+         * rhbz #473080 */
+
+        for (e = environ; *e; e++) {
+
+            if (pa_startswith(*e, "PULSE_PROP_")) {
+                size_t kl, skip;
+                char *k;
+                bool override;
+
+                if (pa_startswith(*e, "PULSE_PROP_OVERRIDE_")) {
+                    skip = 20;
+                    override = true;
+                } else {
+                    skip = 11;
+                    override = false;
+                }
+
+                kl = strcspn(*e+skip, "=");
+
+                if ((*e)[skip+kl] != '=')
+                    continue;
+
+                k = pa_xstrndup(*e+skip, kl);
+
+                if (!pa_streq(k, "OVERRIDE"))
+                    if (override || !pa_proplist_contains(p, k))
+                        pa_proplist_sets(p, k, *e+skip+kl+1);
+                pa_xfree(k);
+            }
+        }
+    }
+
+    if ((pp = getenv("PULSE_PROP"))) {
+        pa_proplist *t;
+
+        if ((t = pa_proplist_from_string(pp))) {
+            pa_proplist_update(p, PA_UPDATE_MERGE, t);
+            pa_proplist_free(t);
+        }
+    }
+
+    if ((pp = getenv("PULSE_PROP_OVERRIDE"))) {
+        pa_proplist *t;
+
+        if ((t = pa_proplist_from_string(pp))) {
+            pa_proplist_update(p, PA_UPDATE_REPLACE, t);
+            pa_proplist_free(t);
+        }
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_ID)) {
+        char t[32];
+        pa_snprintf(t, sizeof(t), "%lu", (unsigned long) getpid());
+        pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_ID, t);
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_USER)) {
+        char *u;
+
+        if ((u = pa_get_user_name_malloc())) {
+            pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_USER, u);
+            pa_xfree(u);
+        }
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_HOST)) {
+        char *h;
+
+        if ((h = pa_get_host_name_malloc())) {
+            pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_HOST, h);
+            pa_xfree(h);
+        }
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY)) {
+        char *t;
+
+        if ((t = pa_get_binary_name_malloc())) {
+            char *c = pa_utf8_filter(t);
+            pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_BINARY, c);
+            pa_xfree(t);
+            pa_xfree(c);
+        }
+    }
+
+    add_glib_properties(p);
+    add_gtk_properties(p);
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_NAME)) {
+        const char *t;
+
+        if ((t = pa_proplist_gets(p, PA_PROP_APPLICATION_PROCESS_BINARY)))
+            pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, t);
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_LANGUAGE)) {
+        const char *l;
+
+        if ((l = setlocale(LC_MESSAGES, NULL)))
+            pa_proplist_sets(p, PA_PROP_APPLICATION_LANGUAGE, l);
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_WINDOW_X11_DISPLAY)) {
+        const char *t;
+
+        if ((t = getenv("DISPLAY"))) {
+            char *c = pa_utf8_filter(t);
+            pa_proplist_sets(p, PA_PROP_WINDOW_X11_DISPLAY, c);
+            pa_xfree(c);
+        }
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_MACHINE_ID)) {
+        char *m;
+
+        if ((m = pa_machine_id())) {
+            pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_MACHINE_ID, m);
+            pa_xfree(m);
+        }
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_SESSION_ID)) {
+        char *s;
+
+        if ((s = pa_session_id())) {
+            pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_SESSION_ID, s);
+            pa_xfree(s);
+        }
+    }
+}
+
+char *pa_proplist_get_stream_group(pa_proplist *p, const char *prefix, const char *cache) {
+    const char *r;
+    char *t;
+
+    if (!p)
+        return NULL;
+
+    if (cache && (r = pa_proplist_gets(p, cache)))
+        return pa_xstrdup(r);
+
+    if (!prefix)
+        prefix = "stream";
+
+    if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_ROLE)))
+        t = pa_sprintf_malloc("%s-by-media-role:%s", prefix, r);
+    else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_ID)))
+        t = pa_sprintf_malloc("%s-by-application-id:%s", prefix, r);
+    else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_NAME)))
+        t = pa_sprintf_malloc("%s-by-application-name:%s", prefix, r);
+    else if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_NAME)))
+        t = pa_sprintf_malloc("%s-by-media-name:%s", prefix, r);
+    else
+        t = pa_sprintf_malloc("%s-fallback:%s", prefix, r);
+
+    if (cache)
+        pa_proplist_sets(p, cache, t);
+
+    return t;
+}
diff --git a/src/pulsecore/proplist-util.h b/src/pulsecore/proplist-util.h
new file mode 100644 (file)
index 0000000..17f231f
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef fooproplistutilutilhfoo
+#define fooproplistutilutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/proplist.h>
+
+void pa_init_proplist(pa_proplist *p);
+char *pa_proplist_get_stream_group(pa_proplist *pl, const char *prefix, const char *cache);
+
+#endif
diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c
new file mode 100644 (file)
index 0000000..522a829
--- /dev/null
@@ -0,0 +1,140 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/cli.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/shared.h>
+
+#include "protocol-cli.h"
+
+/* Don't allow more than this many concurrent connections */
+#define MAX_CONNECTIONS 25
+
+struct pa_cli_protocol {
+    PA_REFCNT_DECLARE;
+
+    pa_core *core;
+    pa_idxset *connections;
+};
+
+static void cli_unlink(pa_cli_protocol *p, pa_cli *c) {
+    pa_assert(p);
+    pa_assert(c);
+
+    pa_idxset_remove_by_data(p->connections, c, NULL);
+    pa_cli_free(c);
+}
+
+static void cli_eof_cb(pa_cli*c, void*userdata) {
+    pa_cli_protocol *p = userdata;
+    pa_assert(p);
+
+    cli_unlink(p, c);
+}
+
+void pa_cli_protocol_connect(pa_cli_protocol *p, pa_iochannel *io, pa_module *m) {
+    pa_cli *c;
+
+    pa_assert(p);
+    pa_assert(io);
+    pa_assert(m);
+
+    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
+        pa_iochannel_free(io);
+        return;
+    }
+
+    c = pa_cli_new(p->core, io, m);
+    pa_cli_set_eof_callback(c, cli_eof_cb, p);
+
+    pa_idxset_put(p->connections, c, NULL);
+}
+
+void pa_cli_protocol_disconnect(pa_cli_protocol *p, pa_module *m) {
+    pa_cli *c;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(m);
+
+    while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+        if (pa_cli_get_module(c) == m)
+            cli_unlink(p, c);
+}
+
+static pa_cli_protocol* cli_protocol_new(pa_core *c) {
+    pa_cli_protocol *p;
+
+    pa_assert(c);
+
+    p = pa_xnew(pa_cli_protocol, 1);
+    PA_REFCNT_INIT(p);
+    p->core = c;
+    p->connections = pa_idxset_new(NULL, NULL);
+
+    pa_assert_se(pa_shared_set(c, "cli-protocol", p) >= 0);
+
+    return p;
+}
+
+pa_cli_protocol* pa_cli_protocol_get(pa_core *c) {
+    pa_cli_protocol *p;
+
+    if ((p = pa_shared_get(c, "cli-protocol")))
+        return pa_cli_protocol_ref(p);
+
+    return cli_protocol_new(c);
+}
+
+pa_cli_protocol* pa_cli_protocol_ref(pa_cli_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    PA_REFCNT_INC(p);
+
+    return p;
+}
+
+void pa_cli_protocol_unref(pa_cli_protocol *p) {
+    pa_cli *c;
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    if (PA_REFCNT_DEC(p) > 0)
+        return;
+
+    while ((c = pa_idxset_first(p->connections, NULL)))
+        cli_unlink(p, c);
+
+    pa_idxset_free(p->connections, NULL);
+
+    pa_assert_se(pa_shared_remove(p->core, "cli-protocol") >= 0);
+
+    pa_xfree(p);
+}
diff --git a/src/pulsecore/protocol-cli.h b/src/pulsecore/protocol-cli.h
new file mode 100644 (file)
index 0000000..fde6e38
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef fooprotocolclihfoo
+#define fooprotocolclihfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/socket-server.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+
+typedef struct pa_cli_protocol pa_cli_protocol;
+
+pa_cli_protocol* pa_cli_protocol_get(pa_core *core);
+pa_cli_protocol* pa_cli_protocol_ref(pa_cli_protocol *p);
+void pa_cli_protocol_unref(pa_cli_protocol *p);
+void pa_cli_protocol_connect(pa_cli_protocol *p, pa_iochannel *io, pa_module *m);
+void pa_cli_protocol_disconnect(pa_cli_protocol *o, pa_module *m);
+
+#endif
diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
new file mode 100644 (file)
index 0000000..59afc1a
--- /dev/null
@@ -0,0 +1,1136 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/strbuf.h>
+
+#include "protocol-dbus.h"
+
+struct pa_dbus_protocol {
+    PA_REFCNT_DECLARE;
+
+    pa_core *core;
+    pa_hashmap *objects; /* Object path -> struct object_entry */
+    pa_hashmap *connections; /* DBusConnection -> struct connection_entry */
+    pa_idxset *extensions; /* Strings */
+
+    pa_hook hooks[PA_DBUS_PROTOCOL_HOOK_MAX];
+};
+
+struct object_entry {
+    char *path;
+    pa_hashmap *interfaces; /* Interface name -> struct interface_entry */
+    char *introspection;
+};
+
+struct connection_entry {
+    DBusConnection *connection;
+    pa_client *client;
+
+    bool listening_for_all_signals;
+
+    /* Contains object paths. If this is empty, then signals from all objects
+     * are accepted. Only used when listening_for_all_signals == true. */
+    pa_idxset *all_signals_objects;
+
+    /* Signal name -> signal paths entry. The entries contain object paths. If
+     * a path set is empty, then that signal is accepted from all objects. This
+     * variable is only used when listening_for_all_signals == false. */
+    pa_hashmap *listening_signals;
+};
+
+/* Only used in connection entries' listening_signals hashmap. */
+struct signal_paths_entry {
+    char *signal;
+    pa_idxset *paths;
+};
+
+struct interface_entry {
+    char *name;
+    pa_hashmap *method_handlers;
+    pa_hashmap *method_signatures; /* Derived from method_handlers. Contains only "in" arguments. */
+    pa_hashmap *property_handlers;
+    pa_dbus_receive_cb_t get_all_properties_cb;
+    pa_dbus_signal_info *signals;
+    unsigned n_signals;
+    void *userdata;
+};
+
+char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
+    char *address = NULL;
+    char *runtime_path = NULL;
+    char *escaped_path = NULL;
+
+    switch (server_type) {
+        case PA_SERVER_TYPE_USER:
+            pa_assert_se((runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME)));
+            pa_assert_se((escaped_path = dbus_address_escape_value(runtime_path)));
+            address = pa_sprintf_malloc("unix:path=%s", escaped_path);
+            break;
+
+        case PA_SERVER_TYPE_SYSTEM:
+            pa_assert_se((escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH)));
+            address = pa_sprintf_malloc("unix:path=%s", escaped_path);
+            break;
+
+        case PA_SERVER_TYPE_NONE:
+            address = pa_xnew0(char, 1);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_xfree(runtime_path);
+    dbus_free(escaped_path);
+
+    return address;
+}
+
+static pa_dbus_protocol *dbus_protocol_new(pa_core *c) {
+    pa_dbus_protocol *p;
+    unsigned i;
+
+    pa_assert(c);
+
+    p = pa_xnew(pa_dbus_protocol, 1);
+    PA_REFCNT_INIT(p);
+    p->core = c;
+    p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
+        pa_hook_init(&p->hooks[i], p);
+
+    pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0);
+
+    return p;
+}
+
+pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c) {
+    pa_dbus_protocol *p;
+
+    if ((p = pa_shared_get(c, "dbus-protocol")))
+        return pa_dbus_protocol_ref(p);
+
+    return dbus_protocol_new(c);
+}
+
+pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    PA_REFCNT_INC(p);
+
+    return p;
+}
+
+void pa_dbus_protocol_unref(pa_dbus_protocol *p) {
+    unsigned i;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    if (PA_REFCNT_DEC(p) > 0)
+        return;
+
+    pa_assert(pa_hashmap_isempty(p->objects));
+    pa_assert(pa_hashmap_isempty(p->connections));
+    pa_assert(pa_idxset_isempty(p->extensions));
+
+    pa_hashmap_free(p->objects);
+    pa_hashmap_free(p->connections);
+    pa_idxset_free(p->extensions, NULL);
+
+    for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
+        pa_hook_done(&p->hooks[i]);
+
+    pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0);
+
+    pa_xfree(p);
+}
+
+static void update_introspection(struct object_entry *oe) {
+    pa_strbuf *buf;
+    void *interfaces_state = NULL;
+    struct interface_entry *iface_entry = NULL;
+
+    pa_assert(oe);
+
+    buf = pa_strbuf_new();
+    pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+    pa_strbuf_puts(buf, "<node>\n");
+
+    PA_HASHMAP_FOREACH(iface_entry, oe->interfaces, interfaces_state) {
+        pa_dbus_method_handler *method_handler;
+        pa_dbus_property_handler *property_handler;
+        void *handlers_state = NULL;
+        unsigned i;
+        unsigned j;
+
+        pa_strbuf_printf(buf, " <interface name=\"%s\">\n", iface_entry->name);
+
+        PA_HASHMAP_FOREACH(method_handler, iface_entry->method_handlers, handlers_state) {
+            pa_strbuf_printf(buf, "  <method name=\"%s\">\n", method_handler->method_name);
+
+            for (i = 0; i < method_handler->n_arguments; ++i)
+                pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n",
+                                 method_handler->arguments[i].name,
+                                 method_handler->arguments[i].type,
+                                 method_handler->arguments[i].direction);
+
+            pa_strbuf_puts(buf, "  </method>\n");
+        }
+
+        handlers_state = NULL;
+
+        PA_HASHMAP_FOREACH(property_handler, iface_entry->property_handlers, handlers_state)
+            pa_strbuf_printf(buf, "  <property name=\"%s\" type=\"%s\" access=\"%s\"/>\n",
+                             property_handler->property_name,
+                             property_handler->type,
+                             property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write");
+
+        for (i = 0; i < iface_entry->n_signals; ++i) {
+            pa_strbuf_printf(buf, "  <signal name=\"%s\">\n", iface_entry->signals[i].name);
+
+            for (j = 0; j < iface_entry->signals[i].n_arguments; ++j)
+                pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\"/>\n", iface_entry->signals[i].arguments[j].name,
+                                                                             iface_entry->signals[i].arguments[j].type);
+
+            pa_strbuf_puts(buf, "  </signal>\n");
+        }
+
+        pa_strbuf_puts(buf, " </interface>\n");
+    }
+
+    pa_strbuf_puts(buf, " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
+                        "  <method name=\"Introspect\">\n"
+                        "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
+                        "  </method>\n"
+                        " </interface>\n"
+                        " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
+                        "  <method name=\"Get\">\n"
+                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
+                        "  </method>\n"
+                        "  <method name=\"Set\">\n"
+                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
+                        "  </method>\n"
+                        "  <method name=\"GetAll\">\n"
+                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
+                        "  </method>\n"
+                        " </interface>\n");
+
+    pa_strbuf_puts(buf, "</node>\n");
+
+    pa_xfree(oe->introspection);
+    oe->introspection = pa_strbuf_to_string_free(buf);
+}
+
+/* Return value of find_handler() and its subfunctions. */
+enum find_result_t {
+    /* The received message is a valid .Get call. */
+    FOUND_GET_PROPERTY,
+
+    /* The received message is a valid .Set call. */
+    FOUND_SET_PROPERTY,
+
+    /* The received message is a valid .GetAll call. */
+    FOUND_GET_ALL,
+
+    /* The received message is a valid method call. */
+    FOUND_METHOD,
+
+    /* The interface of the received message hasn't been registered for the
+     * destination object. */
+    NO_SUCH_INTERFACE,
+
+    /* No property handler was found for the received .Get or .Set call. */
+    NO_SUCH_PROPERTY,
+
+    /* The interface argument of a property call didn't match any registered
+     * interface. */
+    NO_SUCH_PROPERTY_INTERFACE,
+
+    /* The received message called .Get or .Set for a property whose access
+     * mode doesn't match the call. */
+    PROPERTY_ACCESS_DENIED,
+
+    /* The new value signature of a .Set call didn't match the expected
+     * signature. */
+    INVALID_PROPERTY_SIG,
+
+    /* No method handler was found for the received message. */
+    NO_SUCH_METHOD,
+
+    /* The signature of the received message didn't match the expected
+     * signature. Despite the name, this can also be returned for a property
+     * call if its message signature is invalid. */
+    INVALID_METHOD_SIG
+};
+
+/* Data for resolving the correct reaction to a received message. */
+struct call_info {
+    DBusMessage *message; /* The received message. */
+    struct object_entry *obj_entry;
+    const char *interface; /* Destination interface name (extracted from the message). */
+    struct interface_entry *iface_entry;
+
+    const char *property; /* Property name (extracted from the message). */
+    const char *property_interface; /* The interface argument of a property call is stored here. */
+    pa_dbus_property_handler *property_handler;
+    const char *expected_property_sig; /* Property signature from the introspection data. */
+    const char *property_sig; /* The signature of the new value in the received .Set message. */
+    DBusMessageIter variant_iter; /* Iterator pointing to the beginning of the new value variant of a .Set call. */
+
+    const char *method; /* Method name (extracted from the message). */
+    pa_dbus_method_handler *method_handler;
+    const char *expected_method_sig; /* Method signature from the introspection data. */
+    const char *method_sig; /* The signature of the received message. */
+};
+
+/* Called when call_info->property has been set and the property interface has
+ * not been given. In case of a Set call, call_info->property_sig is also set,
+ * which is checked against the expected value in this function. */
+static enum find_result_t find_handler_by_property(struct call_info *call_info) {
+    void *state = NULL;
+
+    pa_assert(call_info);
+
+    PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
+        if ((call_info->property_handler = pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
+            if (pa_streq(call_info->method, "Get"))
+                return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
+
+            else if (pa_streq(call_info->method, "Set")) {
+                call_info->expected_property_sig = call_info->property_handler->type;
+
+                if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
+                    return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
+                else
+                    return INVALID_PROPERTY_SIG;
+
+            } else
+                pa_assert_not_reached();
+        }
+    }
+
+    return NO_SUCH_PROPERTY;
+}
+
+static enum find_result_t find_handler_by_method(struct call_info *call_info) {
+    void *state = NULL;
+
+    pa_assert(call_info);
+
+    PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
+        if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) {
+            pa_assert_se(call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method));
+
+            if (pa_streq(call_info->method_sig, call_info->expected_method_sig))
+                return FOUND_METHOD;
+            else
+                return INVALID_METHOD_SIG;
+        }
+    }
+
+    return NO_SUCH_METHOD;
+}
+
+static enum find_result_t find_handler_from_properties_call(struct call_info *call_info) {
+    pa_assert(call_info);
+
+    if (pa_streq(call_info->method, "GetAll")) {
+        call_info->expected_method_sig = "s";
+        if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
+            return INVALID_METHOD_SIG;
+
+        pa_assert_se(dbus_message_get_args(call_info->message, NULL,
+                                           DBUS_TYPE_STRING, &call_info->property_interface,
+                                           DBUS_TYPE_INVALID));
+
+        if (*call_info->property_interface) {
+            if ((call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
+                return FOUND_GET_ALL;
+            else
+                return NO_SUCH_PROPERTY_INTERFACE;
+
+        } else {
+            pa_assert_se(call_info->iface_entry = pa_hashmap_first(call_info->obj_entry->interfaces));
+            return FOUND_GET_ALL;
+        }
+
+    } else if (pa_streq(call_info->method, "Get")) {
+        call_info->expected_method_sig = "ss";
+        if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
+            return INVALID_METHOD_SIG;
+
+        pa_assert_se(dbus_message_get_args(call_info->message, NULL,
+                                           DBUS_TYPE_STRING, &call_info->property_interface,
+                                           DBUS_TYPE_STRING, &call_info->property,
+                                           DBUS_TYPE_INVALID));
+
+        if (*call_info->property_interface) {
+            if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
+                return NO_SUCH_PROPERTY_INTERFACE;
+            else if ((call_info->property_handler =
+                        pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property)))
+                return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
+            else
+                return NO_SUCH_PROPERTY;
+
+        } else
+            return find_handler_by_property(call_info);
+
+    } else if (pa_streq(call_info->method, "Set")) {
+        DBusMessageIter msg_iter;
+
+        call_info->expected_method_sig = "ssv";
+        if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
+            return INVALID_METHOD_SIG;
+
+        pa_assert_se(dbus_message_iter_init(call_info->message, &msg_iter));
+
+        dbus_message_iter_get_basic(&msg_iter, &call_info->property_interface);
+        pa_assert_se(dbus_message_iter_next(&msg_iter));
+        dbus_message_iter_get_basic(&msg_iter, &call_info->property);
+        pa_assert_se(dbus_message_iter_next(&msg_iter));
+
+        dbus_message_iter_recurse(&msg_iter, &call_info->variant_iter);
+
+        call_info->property_sig = dbus_message_iter_get_signature(&call_info->variant_iter);
+
+        if (*call_info->property_interface) {
+            if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
+                return NO_SUCH_PROPERTY_INTERFACE;
+
+            else if ((call_info->property_handler =
+                        pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
+                call_info->expected_property_sig = call_info->property_handler->type;
+
+                if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
+                    return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
+                else
+                    return INVALID_PROPERTY_SIG;
+
+            } else
+                return NO_SUCH_PROPERTY;
+
+        } else
+            return find_handler_by_property(call_info);
+
+    } else
+        pa_assert_not_reached();
+}
+
+static enum find_result_t find_handler(struct call_info *call_info) {
+    pa_assert(call_info);
+
+    if (call_info->interface) {
+        if (pa_streq(call_info->interface, DBUS_INTERFACE_PROPERTIES))
+            return find_handler_from_properties_call(call_info);
+
+        else if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->interface)))
+            return NO_SUCH_INTERFACE;
+
+        else if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) {
+            pa_assert_se(call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method));
+
+            if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
+                return INVALID_METHOD_SIG;
+
+            return FOUND_METHOD;
+
+        } else
+            return NO_SUCH_METHOD;
+
+    } else { /* The method call doesn't contain an interface. */
+        if (pa_streq(call_info->method, "Get") || pa_streq(call_info->method, "Set") || pa_streq(call_info->method, "GetAll")) {
+            if (find_handler_by_method(call_info) == FOUND_METHOD)
+                /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */
+                return FOUND_METHOD;
+            else
+                /* Assume this is a .Properties call. */
+                return find_handler_from_properties_call(call_info);
+
+        } else /* This is not a .Properties call. */
+            return find_handler_by_method(call_info);
+    }
+}
+
+static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
+    pa_dbus_protocol *p = user_data;
+    struct call_info call_info;
+
+    pa_assert(connection);
+    pa_assert(message);
+    pa_assert(p);
+    pa_assert(p->objects);
+
+    if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    pa_log_debug("Received message: destination = %s, interface = %s, member = %s",
+                 dbus_message_get_path(message),
+                 dbus_message_get_interface(message),
+                 dbus_message_get_member(message));
+
+    call_info.message = message;
+    pa_assert_se(call_info.obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)));
+    call_info.interface = dbus_message_get_interface(message);
+    pa_assert_se(call_info.method = dbus_message_get_member(message));
+    pa_assert_se(call_info.method_sig = dbus_message_get_signature(message));
+
+    if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") ||
+        (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) {
+        pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &call_info.obj_entry->introspection);
+        goto finish;
+    }
+
+    switch (find_handler(&call_info)) {
+        case FOUND_GET_PROPERTY:
+            call_info.property_handler->get_cb(connection, message, call_info.iface_entry->userdata);
+            break;
+
+        case FOUND_SET_PROPERTY:
+            call_info.property_handler->set_cb(connection, message, &call_info.variant_iter, call_info.iface_entry->userdata);
+            break;
+
+        case FOUND_METHOD:
+            call_info.method_handler->receive_cb(connection, message, call_info.iface_entry->userdata);
+            break;
+
+        case FOUND_GET_ALL:
+            if (call_info.iface_entry->get_all_properties_cb)
+                call_info.iface_entry->get_all_properties_cb(connection, message, call_info.iface_entry->userdata);
+            else {
+                DBusMessage *dummy_reply = NULL;
+                DBusMessageIter msg_iter;
+                DBusMessageIter dict_iter;
+
+                pa_assert_se(dummy_reply = dbus_message_new_method_return(message));
+                dbus_message_iter_init_append(dummy_reply, &msg_iter);
+                pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+                pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+                pa_assert_se(dbus_connection_send(connection, dummy_reply, NULL));
+                dbus_message_unref(dummy_reply);
+            }
+            break;
+
+        case PROPERTY_ACCESS_DENIED:
+            pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED,
+                               "%s access denied for property %s", call_info.method, call_info.property);
+            break;
+
+        case NO_SUCH_METHOD:
+            pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "No such method: %s", call_info.method);
+            break;
+
+        case NO_SUCH_INTERFACE:
+            pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_INTERFACE, "No such interface: %s", call_info.interface);
+            break;
+
+        case NO_SUCH_PROPERTY:
+            pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property: %s", call_info.property);
+            break;
+
+        case NO_SUCH_PROPERTY_INTERFACE:
+            pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_INTERFACE, "No such property interface: %s", call_info.property_interface);
+            break;
+
+        case INVALID_METHOD_SIG:
+            pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
+                               "Invalid signature for method %s: '%s'. Expected '%s'.",
+                               call_info.method, call_info.method_sig, call_info.expected_method_sig);
+            break;
+
+        case INVALID_PROPERTY_SIG:
+            pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
+                               "Invalid signature for property %s: '%s'. Expected '%s'.",
+                               call_info.property, call_info.property_sig, call_info.expected_property_sig);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+finish:
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusObjectPathVTable vtable = {
+    .unregister_function = NULL,
+    .message_function = handle_message_cb,
+    .dbus_internal_pad1 = NULL,
+    .dbus_internal_pad2 = NULL,
+    .dbus_internal_pad3 = NULL,
+    .dbus_internal_pad4 = NULL
+};
+
+static void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
+    struct connection_entry *conn_entry;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(obj_entry);
+
+    PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
+        pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p));
+}
+
+static pa_dbus_arg_info *copy_args(const pa_dbus_arg_info *src, unsigned n) {
+    pa_dbus_arg_info *dst;
+    unsigned i;
+
+    if (n == 0)
+        return NULL;
+
+    pa_assert(src);
+
+    dst = pa_xnew0(pa_dbus_arg_info, n);
+
+    for (i = 0; i < n; ++i) {
+        dst[i].name = pa_xstrdup(src[i].name);
+        dst[i].type = pa_xstrdup(src[i].type);
+        dst[i].direction = pa_xstrdup(src[i].direction);
+    }
+
+    return dst;
+}
+
+static void method_handler_free(pa_dbus_method_handler *h) {
+    unsigned i;
+
+    pa_assert(h);
+
+    pa_xfree((char *) h->method_name);
+
+    for (i = 0; i < h->n_arguments; ++i) {
+        pa_xfree((char *) h->arguments[i].name);
+        pa_xfree((char *) h->arguments[i].type);
+        pa_xfree((char *) h->arguments[i].direction);
+    }
+
+    pa_xfree((pa_dbus_arg_info *) h->arguments);
+    pa_xfree(h);
+}
+
+static pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) {
+    pa_hashmap *handlers;
+    unsigned i;
+
+    pa_assert(info);
+    pa_assert(info->method_handlers || info->n_method_handlers == 0);
+
+    handlers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) method_handler_free);
+
+    for (i = 0; i < info->n_method_handlers; ++i) {
+        pa_dbus_method_handler *h = pa_xnew(pa_dbus_method_handler, 1);
+        h->method_name = pa_xstrdup(info->method_handlers[i].method_name);
+        h->arguments = copy_args(info->method_handlers[i].arguments, info->method_handlers[i].n_arguments);
+        h->n_arguments = info->method_handlers[i].n_arguments;
+        h->receive_cb = info->method_handlers[i].receive_cb;
+
+        pa_hashmap_put(handlers, (char *) h->method_name, h);
+    }
+
+    return handlers;
+}
+
+static pa_hashmap *extract_method_signatures(pa_hashmap *method_handlers) {
+    pa_hashmap *signatures = NULL;
+    pa_dbus_method_handler *handler = NULL;
+    void *state = NULL;
+    pa_strbuf *sig_buf = NULL;
+    unsigned i = 0;
+
+    pa_assert(method_handlers);
+
+    signatures = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
+
+    PA_HASHMAP_FOREACH(handler, method_handlers, state) {
+        sig_buf = pa_strbuf_new();
+
+        for (i = 0; i < handler->n_arguments; ++i) {
+            if (pa_streq(handler->arguments[i].direction, "in"))
+                pa_strbuf_puts(sig_buf, handler->arguments[i].type);
+        }
+
+        pa_hashmap_put(signatures, (char *) handler->method_name, pa_strbuf_to_string_free(sig_buf));
+    }
+
+    return signatures;
+}
+
+static void property_handler_free(pa_dbus_property_handler *h) {
+    pa_assert(h);
+
+    pa_xfree((char *) h->property_name);
+    pa_xfree((char *) h->type);
+
+    pa_xfree(h);
+}
+
+static pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) {
+    pa_hashmap *handlers;
+    unsigned i = 0;
+
+    pa_assert(info);
+    pa_assert(info->property_handlers || info->n_property_handlers == 0);
+
+    handlers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) property_handler_free);
+
+    for (i = 0; i < info->n_property_handlers; ++i) {
+        pa_dbus_property_handler *h = pa_xnew(pa_dbus_property_handler, 1);
+        h->property_name = pa_xstrdup(info->property_handlers[i].property_name);
+        h->type = pa_xstrdup(info->property_handlers[i].type);
+        h->get_cb = info->property_handlers[i].get_cb;
+        h->set_cb = info->property_handlers[i].set_cb;
+
+        pa_hashmap_put(handlers, (char *) h->property_name, h);
+    }
+
+    return handlers;
+}
+
+static pa_dbus_signal_info *copy_signals(const pa_dbus_interface_info *info) {
+    pa_dbus_signal_info *dst;
+    unsigned i;
+
+    pa_assert(info);
+
+    if (info->n_signals == 0)
+        return NULL;
+
+    pa_assert(info->signals);
+
+    dst = pa_xnew(pa_dbus_signal_info, info->n_signals);
+
+    for (i = 0; i < info->n_signals; ++i) {
+        dst[i].name = pa_xstrdup(info->signals[i].name);
+        dst[i].arguments = copy_args(info->signals[i].arguments, info->signals[i].n_arguments);
+        dst[i].n_arguments = info->signals[i].n_arguments;
+    }
+
+    return dst;
+}
+
+int pa_dbus_protocol_add_interface(pa_dbus_protocol *p,
+                                   const char *path,
+                                   const pa_dbus_interface_info *info,
+                                   void *userdata) {
+    struct object_entry *obj_entry;
+    struct interface_entry *iface_entry;
+    bool obj_entry_created = false;
+
+    pa_assert(p);
+    pa_assert(path);
+    pa_assert(info);
+    pa_assert(info->name);
+    pa_assert(info->method_handlers || info->n_method_handlers == 0);
+    pa_assert(info->property_handlers || info->n_property_handlers == 0);
+    pa_assert(info->get_all_properties_cb || info->n_property_handlers == 0);
+    pa_assert(info->signals || info->n_signals == 0);
+
+    if (!(obj_entry = pa_hashmap_get(p->objects, path))) {
+        obj_entry = pa_xnew(struct object_entry, 1);
+        obj_entry->path = pa_xstrdup(path);
+        obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+        obj_entry->introspection = NULL;
+
+        pa_hashmap_put(p->objects, obj_entry->path, obj_entry);
+        obj_entry_created = true;
+    }
+
+    if (pa_hashmap_get(obj_entry->interfaces, info->name) != NULL)
+        goto fail; /* The interface was already registered. */
+
+    iface_entry = pa_xnew(struct interface_entry, 1);
+    iface_entry->name = pa_xstrdup(info->name);
+    iface_entry->method_handlers = create_method_handlers(info);
+    iface_entry->method_signatures = extract_method_signatures(iface_entry->method_handlers);
+    iface_entry->property_handlers = create_property_handlers(info);
+    iface_entry->get_all_properties_cb = info->get_all_properties_cb;
+    iface_entry->signals = copy_signals(info);
+    iface_entry->n_signals = info->n_signals;
+    iface_entry->userdata = userdata;
+    pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry);
+
+    update_introspection(obj_entry);
+
+    if (obj_entry_created)
+        register_object(p, obj_entry);
+
+    pa_log_debug("Interface %s added for object %s", iface_entry->name, obj_entry->path);
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+static void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
+    struct connection_entry *conn_entry;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(obj_entry);
+
+    PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
+        pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path));
+}
+
+int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) {
+    struct object_entry *obj_entry;
+    struct interface_entry *iface_entry;
+    unsigned i;
+
+    pa_assert(p);
+    pa_assert(path);
+    pa_assert(interface);
+
+    if (!(obj_entry = pa_hashmap_get(p->objects, path)))
+        return -1;
+
+    if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface)))
+        return -1;
+
+    update_introspection(obj_entry);
+
+    pa_log_debug("Interface %s removed from object %s", iface_entry->name, obj_entry->path);
+
+    pa_xfree(iface_entry->name);
+    pa_hashmap_free(iface_entry->method_signatures);
+    pa_hashmap_free(iface_entry->method_handlers);
+    pa_hashmap_free(iface_entry->property_handlers);
+
+    for (i = 0; i < iface_entry->n_signals; ++i) {
+        unsigned j;
+
+        pa_xfree((char *) iface_entry->signals[i].name);
+
+        for (j = 0; j < iface_entry->signals[i].n_arguments; ++j) {
+            pa_xfree((char *) iface_entry->signals[i].arguments[j].name);
+            pa_xfree((char *) iface_entry->signals[i].arguments[j].type);
+            pa_assert(iface_entry->signals[i].arguments[j].direction == NULL);
+        }
+
+        pa_xfree((pa_dbus_arg_info *) iface_entry->signals[i].arguments);
+    }
+
+    pa_xfree(iface_entry->signals);
+    pa_xfree(iface_entry);
+
+    if (pa_hashmap_isempty(obj_entry->interfaces)) {
+        unregister_object(p, obj_entry);
+
+        pa_hashmap_remove(p->objects, path);
+        pa_xfree(obj_entry->path);
+        pa_hashmap_free(obj_entry->interfaces);
+        pa_xfree(obj_entry->introspection);
+        pa_xfree(obj_entry);
+    }
+
+    return 0;
+}
+
+static void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
+    struct object_entry *obj_entry;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(conn);
+
+    PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
+        pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p));
+}
+
+static void signal_paths_entry_free(struct signal_paths_entry *e) {
+    pa_assert(e);
+
+    pa_xfree(e->signal);
+    pa_idxset_free(e->paths, pa_xfree);
+    pa_xfree(e);
+}
+
+int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client) {
+    struct connection_entry *conn_entry;
+
+    pa_assert(p);
+    pa_assert(conn);
+    pa_assert(client);
+
+    if (pa_hashmap_get(p->connections, conn))
+        return -1; /* The connection was already registered. */
+
+    register_all_objects(p, conn);
+
+    conn_entry = pa_xnew(struct connection_entry, 1);
+    conn_entry->connection = dbus_connection_ref(conn);
+    conn_entry->client = client;
+    conn_entry->listening_for_all_signals = false;
+    conn_entry->all_signals_objects = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    conn_entry->listening_signals = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
+                                                        (pa_free_cb_t) signal_paths_entry_free);
+
+    pa_hashmap_put(p->connections, conn, conn_entry);
+
+    return 0;
+}
+
+static void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
+    struct object_entry *obj_entry;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(conn);
+
+    PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
+        pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path));
+}
+
+static struct signal_paths_entry *signal_paths_entry_new(const char *signal_name) {
+    struct signal_paths_entry *e = NULL;
+
+    pa_assert(signal_name);
+
+    e = pa_xnew0(struct signal_paths_entry, 1);
+    e->signal = pa_xstrdup(signal_name);
+    e->paths = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    return e;
+}
+
+int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn) {
+    struct connection_entry *conn_entry = NULL;
+
+    pa_assert(p);
+    pa_assert(conn);
+
+    if (!(conn_entry = pa_hashmap_remove(p->connections, conn)))
+        return -1;
+
+    unregister_all_objects(p, conn);
+
+    dbus_connection_unref(conn_entry->connection);
+    pa_idxset_free(conn_entry->all_signals_objects, pa_xfree);
+    pa_hashmap_free(conn_entry->listening_signals);
+    pa_xfree(conn_entry);
+
+    return 0;
+}
+
+pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) {
+    struct connection_entry *conn_entry;
+
+    pa_assert(p);
+    pa_assert(conn);
+
+    if (!(conn_entry = pa_hashmap_get(p->connections, conn)))
+        return NULL;
+
+    return conn_entry->client;
+}
+
+void pa_dbus_protocol_add_signal_listener(
+        pa_dbus_protocol *p,
+        DBusConnection *conn,
+        const char *signal_name,
+        char **objects,
+        unsigned n_objects) {
+    struct connection_entry *conn_entry = NULL;
+    struct signal_paths_entry *signal_paths_entry = NULL;
+    unsigned i = 0;
+
+    pa_assert(p);
+    pa_assert(conn);
+    pa_assert(objects || n_objects == 0);
+
+    pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
+
+    /* all_signals_objects will either be emptied or replaced with new objects,
+     * so we empty it here unconditionally. If listening_for_all_signals is
+     * currently false, the idxset is empty already so this does nothing. */
+    pa_idxset_remove_all(conn_entry->all_signals_objects, pa_xfree);
+
+    if (signal_name) {
+        conn_entry->listening_for_all_signals = false;
+
+        /* Replace the old signal paths entry for this signal with a new
+         * one. */
+        pa_hashmap_remove_and_free(conn_entry->listening_signals, signal_name);
+        signal_paths_entry = signal_paths_entry_new(signal_name);
+
+        for (i = 0; i < n_objects; ++i)
+            pa_idxset_put(signal_paths_entry->paths, pa_xstrdup(objects[i]), NULL);
+
+        pa_hashmap_put(conn_entry->listening_signals, signal_paths_entry->signal, signal_paths_entry);
+
+    } else {
+        conn_entry->listening_for_all_signals = true;
+
+        /* We're not interested in individual signals anymore, so let's empty
+         * listening_signals. */
+        pa_hashmap_remove_all(conn_entry->listening_signals);
+
+        for (i = 0; i < n_objects; ++i)
+            pa_idxset_put(conn_entry->all_signals_objects, pa_xstrdup(objects[i]), NULL);
+    }
+}
+
+void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal_name) {
+    struct connection_entry *conn_entry = NULL;
+    struct signal_paths_entry *signal_paths_entry = NULL;
+
+    pa_assert(p);
+    pa_assert(conn);
+
+    pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
+
+    if (signal_name) {
+        if ((signal_paths_entry = pa_hashmap_remove(conn_entry->listening_signals, signal_name)))
+            signal_paths_entry_free(signal_paths_entry);
+
+    } else {
+        conn_entry->listening_for_all_signals = false;
+        pa_idxset_remove_all(conn_entry->all_signals_objects, pa_xfree);
+        pa_hashmap_remove_all(conn_entry->listening_signals);
+    }
+}
+
+void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal_msg) {
+    struct connection_entry *conn_entry;
+    struct signal_paths_entry *signal_paths_entry;
+    void *state = NULL;
+    DBusMessage *signal_copy;
+    char *signal_string;
+
+    pa_assert(p);
+    pa_assert(signal_msg);
+    pa_assert(dbus_message_get_type(signal_msg) == DBUS_MESSAGE_TYPE_SIGNAL);
+    pa_assert(dbus_message_get_path(signal_msg));
+    pa_assert(dbus_message_get_interface(signal_msg));
+    pa_assert(dbus_message_get_member(signal_msg));
+
+    signal_string = pa_sprintf_malloc("%s.%s", dbus_message_get_interface(signal_msg), dbus_message_get_member(signal_msg));
+
+    PA_HASHMAP_FOREACH(conn_entry, p->connections, state) {
+        if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */
+             && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal_msg), NULL)
+                 || pa_idxset_isempty(conn_entry->all_signals_objects)))
+
+            || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */
+                && (signal_paths_entry = pa_hashmap_get(conn_entry->listening_signals, signal_string))
+                && (pa_idxset_get_by_data(signal_paths_entry->paths, dbus_message_get_path(signal_msg), NULL)
+                    || pa_idxset_isempty(signal_paths_entry->paths)))) {
+
+            pa_assert_se(signal_copy = dbus_message_copy(signal_msg));
+            pa_assert_se(dbus_connection_send(conn_entry->connection, signal_copy, NULL));
+            dbus_message_unref(signal_copy);
+        }
+    }
+
+    pa_xfree(signal_string);
+}
+
+const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) {
+    const char **extensions;
+    const char *ext_name;
+    void *state = NULL;
+    unsigned i = 0;
+
+    pa_assert(p);
+    pa_assert(n);
+
+    *n = pa_idxset_size(p->extensions);
+
+    if (*n <= 0)
+        return NULL;
+
+    extensions = pa_xnew(const char *, *n);
+
+    while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL)))
+        extensions[i++] = ext_name;
+
+    return extensions;
+}
+
+int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name) {
+    char *internal_name;
+
+    pa_assert(p);
+    pa_assert(name);
+
+    internal_name = pa_xstrdup(name);
+
+    if (pa_idxset_put(p->extensions, internal_name, NULL) < 0) {
+        pa_xfree(internal_name);
+        return -1;
+    }
+
+    pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED], internal_name);
+
+    return 0;
+}
+
+int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) {
+    char *internal_name;
+
+    pa_assert(p);
+    pa_assert(name);
+
+    if (!(internal_name = pa_idxset_remove_by_data(p->extensions, name, NULL)))
+        return -1;
+
+    pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED], internal_name);
+
+    pa_xfree(internal_name);
+
+    return 0;
+}
+
+pa_hook_slot *pa_dbus_protocol_hook_connect(
+        pa_dbus_protocol *p,
+        pa_dbus_protocol_hook_t hook,
+        pa_hook_priority_t prio,
+        pa_hook_cb_t cb,
+        void *data) {
+    pa_assert(p);
+    pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX);
+    pa_assert(cb);
+
+    return pa_hook_connect(&p->hooks[hook], prio, cb, data);
+}
diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h
new file mode 100644 (file)
index 0000000..fdc1ae9
--- /dev/null
@@ -0,0 +1,215 @@
+#ifndef fooprotocoldbushfoo
+#define fooprotocoldbushfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
+
+#define PA_DBUS_DEFAULT_PORT 24883
+#define PA_DBUS_SOCKET_NAME "dbus-socket"
+
+#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME
+
+#define PA_DBUS_CORE_INTERFACE "org.PulseAudio.Core1"
+#define PA_DBUS_CORE_OBJECT_PATH "/org/pulseaudio/core1"
+
+#define PA_DBUS_ERROR_NO_SUCH_INTERFACE PA_DBUS_CORE_INTERFACE ".NoSuchInterfaceError"
+#define PA_DBUS_ERROR_NO_SUCH_PROPERTY PA_DBUS_CORE_INTERFACE ".NoSuchPropertyError"
+#define PA_DBUS_ERROR_NOT_FOUND PA_DBUS_CORE_INTERFACE ".NotFoundError"
+
+/* Returns the default address of the server type in the escaped form. For
+ * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the
+ * string. */
+char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type);
+
+typedef struct pa_dbus_protocol pa_dbus_protocol;
+
+/* This function either creates a new pa_dbus_protocol object, or if one
+ * already exists, increases the reference count. */
+pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c);
+
+pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p);
+void pa_dbus_protocol_unref(pa_dbus_protocol *p);
+
+/* Called when a received message needs handling. Completely ignoring the
+ * message isn't a good idea; if you can't handle the message, reply with an
+ * error.
+ *
+ * The message signature is already checked against the introspection data, so
+ * you don't have to do that yourself.
+ *
+ * All messages are method calls. */
+typedef void (*pa_dbus_receive_cb_t)(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+/* A specialized version of pa_dbus_receive_cb_t: the additional iterator
+ * argument points to the element inside the new value variant.
+ *
+ * The new value signature is checked against the introspection data, so you
+ * don't have to do that yourself. */
+typedef void (*pa_dbus_set_property_cb_t)(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+
+typedef struct pa_dbus_arg_info {
+    const char *name;
+    const char *type;
+    const char *direction; /* NULL for signal arguments. */
+} pa_dbus_arg_info;
+
+typedef struct pa_dbus_signal_info {
+    const char *name;
+    const pa_dbus_arg_info *arguments; /* NULL, if the signal has no args. */
+    unsigned n_arguments;
+} pa_dbus_signal_info;
+
+typedef struct pa_dbus_method_handler {
+    const char *method_name;
+    const pa_dbus_arg_info *arguments; /* NULL, if the method has no args. */
+    unsigned n_arguments;
+    pa_dbus_receive_cb_t receive_cb;
+} pa_dbus_method_handler;
+
+typedef struct pa_dbus_property_handler {
+    const char *property_name;
+    const char *type;
+
+    /* The access mode for the property is determined by checking whether
+     * get_cb or set_cb is NULL. */
+    pa_dbus_receive_cb_t get_cb;
+    pa_dbus_set_property_cb_t set_cb;
+} pa_dbus_property_handler;
+
+typedef struct pa_dbus_interface_info {
+    const char* name;
+    const pa_dbus_method_handler *method_handlers; /* NULL, if the interface has no methods. */
+    unsigned n_method_handlers;
+    const pa_dbus_property_handler *property_handlers; /* NULL, if the interface has no properties. */
+    unsigned n_property_handlers;
+    const pa_dbus_receive_cb_t get_all_properties_cb; /* May be NULL, in which case GetAll returns an error. */
+    const pa_dbus_signal_info *signals; /* NULL, if the interface has no signals. */
+    unsigned n_signals;
+} pa_dbus_interface_info;
+
+/* The following functions may only be called from the main thread. */
+
+/* Registers the given interface to the given object path. It doesn't matter
+ * whether or not the object has already been registered; if it is, then its
+ * interface set is extended.
+ *
+ * Introspection requests are handled automatically.
+ *
+ * Userdata is passed to all the callbacks.
+ *
+ * Fails and returns a negative number if the object already has the interface
+ * registered. */
+int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, const char *path, const pa_dbus_interface_info *info, void *userdata);
+
+/* Returns a negative number if the given object doesn't have the given
+ * interface registered. */
+int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface);
+
+/* Fails and returns a negative number if the connection is already
+ * registered. */
+int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client);
+
+/* Returns a negative number if the connection isn't registered. */
+int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn);
+
+/* Returns NULL if the connection isn't registered. */
+pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn);
+
+/* Enables signal receiving for the given connection. The connection must have
+ * been registered earlier. The signal string must contain both the signal
+ * interface and the signal name, concatenated using a period as the separator.
+ *
+ * If the signal argument is NULL, all signals will be sent to the connection,
+ * otherwise calling this function only adds the given signal to the list of
+ * signals that will be delivered to the connection.
+ *
+ * The objects argument is a list of object paths. If the list is not empty,
+ * only signals from the given objects are delivered. If this function is
+ * called multiple time for the same connection and signal, the latest call
+ * always replaces the previous object list. */
+void pa_dbus_protocol_add_signal_listener(
+        pa_dbus_protocol *p,
+        DBusConnection *conn,
+        const char *signal,
+        char **objects,
+        unsigned n_objects);
+
+/* Disables the delivery of the signal for the given connection. The connection
+ * must have been registered. If signal is NULL, all signals are disabled. If
+ * signal is non-NULL and _add_signal_listener() was previously called with
+ * NULL signal (causing all signals to be enabled), this function doesn't do
+ * anything. Also, if the signal wasn't enabled before, this function doesn't
+ * do anything in that case either. */
+void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal);
+
+/* Sends the given signal to all interested clients. By default no signals are
+ * sent - clients have to explicitly to request signals by calling
+ * .Core1.ListenForSignal. That method's handler then calls
+ * pa_dbus_protocol_add_signal_listener(). */
+void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal);
+
+/* Returns an array of extension identifier strings. The strings pointers point
+ * to the internal copies, so don't free the strings. The caller must free the
+ * array, however. Also, do not save the returned pointer or any of the string
+ * pointers, because the contained strings may be freed at any time. If you
+ * need to save the array, copy it. */
+const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n);
+
+/* Modules that want to provide a D-Bus interface for clients should register
+ * an identifier that the clients can use to check whether the additional
+ * functionality is available.
+ *
+ * This function registers the extension with the given name. It is recommended
+ * that the name follows the D-Bus interface naming convention, so that the
+ * names remain unique in case there will be at some point in the future
+ * extensions that aren't included with the main PulseAudio source tree. For
+ * in-tree extensions the convention is to use the org.PulseAudio.Ext
+ * namespace.
+ *
+ * It is suggested that the name contains a version number, and whenever the
+ * extension interface is modified in non-backwards compatible way, the version
+ * number is incremented.
+ *
+ * Fails and returns a negative number if the extension is already registered.
+ */
+int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name);
+
+/* Returns a negative number if the extension isn't registered. */
+int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name);
+
+/* All hooks have the pa_dbus_protocol object as hook data. */
+typedef enum pa_dbus_protocol_hook {
+    PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, /* Extension name as call data. */
+    PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, /* Extension name as call data. */
+    PA_DBUS_PROTOCOL_HOOK_MAX
+} pa_dbus_protocol_hook_t;
+
+pa_hook_slot *pa_dbus_protocol_hook_connect(
+        pa_dbus_protocol *p,
+        pa_dbus_protocol_hook_t hook,
+        pa_hook_priority_t prio,
+        pa_hook_cb_t cb,
+        void *data);
+
+#endif
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
new file mode 100644 (file)
index 0000000..4f83ac4
--- /dev/null
@@ -0,0 +1,1734 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/sample.h>
+#include <pulse/timeval.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/proplist.h>
+
+#include <pulsecore/esound.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/client.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/source.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/ipacl.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/endianmacros.h>
+
+#include "protocol-esound.h"
+
+/* Don't accept more connection than this */
+#define MAX_CONNECTIONS 64
+
+/* Kick a client if it doesn't authenticate within this time */
+#define AUTH_TIMEOUT (5*PA_USEC_PER_SEC)
+
+#define DEFAULT_COOKIE_FILE ".esd_auth"
+
+#define PLAYBACK_BUFFER_SECONDS (.25)
+#define PLAYBACK_BUFFER_FRAGMENTS (10)
+#define RECORD_BUFFER_SECONDS (5)
+
+#define MAX_CACHE_SAMPLE_SIZE (2048000)
+
+#define DEFAULT_SINK_LATENCY (150*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (150*PA_USEC_PER_MSEC)
+
+#define SCACHE_PREFIX "esound."
+
+/* This is heavily based on esound's code */
+
+typedef struct connection {
+    pa_msgobject parent;
+
+    uint32_t index;
+    bool dead;
+    pa_esound_protocol *protocol;
+    pa_esound_options *options;
+    pa_iochannel *io;
+    pa_client *client;
+    bool authorized, swap_byte_order;
+    void *write_data;
+    size_t write_data_alloc, write_data_index, write_data_length;
+    void *read_data;
+    size_t read_data_alloc, read_data_length;
+    esd_proto_t request;
+    esd_client_state_t state;
+    pa_sink_input *sink_input;
+    pa_source_output *source_output;
+    pa_memblockq *input_memblockq, *output_memblockq;
+    pa_defer_event *defer_event;
+
+    char *original_name;
+
+    struct {
+        pa_memblock *current_memblock;
+        size_t memblock_index;
+        pa_atomic_t missing;
+        bool underrun;
+    } playback;
+
+    struct {
+        pa_memchunk memchunk;
+        char *name;
+        pa_sample_spec sample_spec;
+    } scache;
+
+    pa_time_event *auth_timeout_event;
+} connection;
+
+PA_DEFINE_PRIVATE_CLASS(connection, pa_msgobject);
+#define CONNECTION(o) (connection_cast(o))
+
+struct pa_esound_protocol {
+    PA_REFCNT_DECLARE;
+
+    pa_core *core;
+    pa_idxset *connections;
+    unsigned n_player;
+};
+
+enum {
+    SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+    SINK_INPUT_MESSAGE_DISABLE_PREBUF
+};
+
+enum {
+    CONNECTION_MESSAGE_REQUEST_DATA,
+    CONNECTION_MESSAGE_POST_DATA,
+    CONNECTION_MESSAGE_UNLINK_CONNECTION
+};
+
+typedef struct proto_handler {
+    size_t data_length;
+    int (*proc)(connection *c, esd_proto_t request, const void *data, size_t length);
+    const char *description;
+} esd_proto_handler_info_t;
+
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_kill_cb(pa_sink_input *i);
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
+static void source_output_kill_cb(pa_source_output *o);
+
+static int esd_proto_connect(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_play(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_record(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_get_latency(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_pan(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_get_id(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_standby_mode(connection *c, esd_proto_t request, const void *data, size_t length);
+
+/* the big map of protocol handler info */
+static struct proto_handler proto_map[ESD_PROTO_MAX] = {
+    { ESD_KEY_LEN + sizeof(int),      esd_proto_connect, "connect" },
+    { ESD_KEY_LEN + sizeof(int),      NULL, "lock" },
+    { ESD_KEY_LEN + sizeof(int),      NULL, "unlock" },
+
+    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_play, "stream play" },
+    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" },
+    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" },
+
+    { ESD_NAME_MAX + 3 * sizeof(int), esd_proto_sample_cache, "sample cache" },                      /* 6 */
+    { sizeof(int),                    esd_proto_sample_free_or_play, "sample free" },
+    { sizeof(int),                    esd_proto_sample_free_or_play, "sample play" },                /* 8 */
+    { sizeof(int),                    NULL, "sample loop" },
+    { sizeof(int),                    NULL, "sample stop" },
+    { (size_t) -1,                    NULL, "TODO: sample kill" },
+
+    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "standby" },
+    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "resume" },                       /* 13 */
+
+    { ESD_NAME_MAX,                   esd_proto_sample_get_id, "sample getid" },                     /* 14 */
+    { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },
+
+    { sizeof(int),                    esd_proto_server_info, "server info" },
+    { sizeof(int),                    esd_proto_all_info, "all info" },
+    { (size_t) -1,                    NULL, "TODO: subscribe" },
+    { (size_t) -1,                    NULL, "TODO: unsubscribe" },
+
+    { 3 * sizeof(int),                esd_proto_stream_pan, "stream pan"},
+    { 3 * sizeof(int),                esd_proto_sample_pan, "sample pan" },
+
+    { sizeof(int),                    esd_proto_standby_mode, "standby mode" },
+    { 0,                              esd_proto_get_latency, "get latency" }
+};
+
+static void connection_unlink(connection *c) {
+    pa_assert(c);
+
+    if (!c->protocol)
+        return;
+
+    if (c->options) {
+        pa_esound_options_unref(c->options);
+        c->options = NULL;
+    }
+
+    if (c->sink_input) {
+        pa_sink_input_unlink(c->sink_input);
+        pa_sink_input_unref(c->sink_input);
+        c->sink_input = NULL;
+    }
+
+    if (c->source_output) {
+        pa_source_output_unlink(c->source_output);
+        pa_source_output_unref(c->source_output);
+        c->source_output = NULL;
+    }
+
+    if (c->client) {
+        pa_client_free(c->client);
+        c->client = NULL;
+    }
+
+    if (c->state == ESD_STREAMING_DATA)
+        c->protocol->n_player--;
+
+    if (c->io) {
+        pa_iochannel_free(c->io);
+        c->io = NULL;
+    }
+
+    if (c->defer_event) {
+        c->protocol->core->mainloop->defer_free(c->defer_event);
+        c->defer_event = NULL;
+    }
+
+    if (c->auth_timeout_event) {
+        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+        c->auth_timeout_event = NULL;
+    }
+
+    pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+    c->protocol = NULL;
+    connection_unref(c);
+}
+
+static void connection_free(pa_object *obj) {
+    connection *c = CONNECTION(obj);
+    pa_assert(c);
+
+    if (c->input_memblockq)
+        pa_memblockq_free(c->input_memblockq);
+    if (c->output_memblockq)
+        pa_memblockq_free(c->output_memblockq);
+
+    if (c->playback.current_memblock)
+        pa_memblock_unref(c->playback.current_memblock);
+
+    pa_xfree(c->read_data);
+    pa_xfree(c->write_data);
+
+    if (c->scache.memchunk.memblock)
+        pa_memblock_unref(c->scache.memchunk.memblock);
+    pa_xfree(c->scache.name);
+
+    pa_xfree(c->original_name);
+    pa_xfree(c);
+}
+
+static void connection_write_prepare(connection *c, size_t length) {
+    size_t t;
+    pa_assert(c);
+
+    t = c->write_data_length+length;
+
+    if (c->write_data_alloc < t)
+        c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t);
+
+    pa_assert(c->write_data);
+}
+
+static void connection_write(connection *c, const void *data, size_t length) {
+    size_t i;
+    pa_assert(c);
+
+    c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+
+    connection_write_prepare(c, length);
+
+    pa_assert(c->write_data);
+
+    i = c->write_data_length;
+    c->write_data_length += length;
+
+    memcpy((uint8_t*) c->write_data + i, data, length);
+}
+
+static void format_esd2native(int format, bool swap_bytes, pa_sample_spec *ss) {
+    pa_assert(ss);
+
+    ss->channels = (uint8_t) (((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1);
+    if ((format & ESD_MASK_BITS) == ESD_BITS16)
+        ss->format = swap_bytes ? PA_SAMPLE_S16RE : PA_SAMPLE_S16NE;
+    else
+        ss->format = PA_SAMPLE_U8;
+}
+
+static int format_native2esd(pa_sample_spec *ss) {
+    int format = 0;
+
+    format = (ss->format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
+    format |= (ss->channels >= 2) ? ESD_STEREO : ESD_MONO;
+
+    return format;
+}
+
+#define CHECK_VALIDITY(expression, ...) do {            \
+        if (PA_UNLIKELY(!(expression))) {               \
+            pa_log_warn(__FILE__ ": " __VA_ARGS__);     \
+            return -1;                                  \
+        }                                               \
+    } while(0);
+
+/*** esound commands ***/
+
+static int esd_proto_connect(connection *c, esd_proto_t request, const void *data, size_t length) {
+    uint32_t ekey;
+    int ok;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
+
+    if (!c->authorized && c->options->auth_cookie) {
+        const uint8_t*key;
+
+        if ((key = pa_auth_cookie_read(c->options->auth_cookie, ESD_KEY_LEN)))
+            if (memcmp(data, key, ESD_KEY_LEN) == 0)
+                c->authorized = true;
+    }
+
+    if (!c->authorized) {
+        pa_log("Kicked client with invalid authentication key.");
+        return -1;
+    }
+
+    if (c->auth_timeout_event) {
+        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+        c->auth_timeout_event = NULL;
+    }
+
+    data = (const char*)data + ESD_KEY_LEN;
+
+    memcpy(&ekey, data, sizeof(uint32_t));
+    if (ekey == ESD_ENDIAN_KEY)
+        c->swap_byte_order = false;
+    else if (ekey == ESD_SWAP_ENDIAN_KEY)
+        c->swap_byte_order = true;
+    else {
+        pa_log_warn("Client sent invalid endian key");
+        return -1;
+    }
+
+    pa_proplist_sets(c->client->proplist, "esound.byte_order", c->swap_byte_order ? "reverse" : "native");
+
+    ok = 1;
+    connection_write(c, &ok, sizeof(int));
+    return 0;
+}
+
+static int esd_proto_stream_play(connection *c, esd_proto_t request, const void *data, size_t length) {
+    char name[ESD_NAME_MAX], *utf8_name;
+    int32_t format, rate;
+    pa_sample_spec ss;
+    size_t l;
+    pa_sink *sink = NULL;
+    pa_sink_input_new_data sdata;
+    pa_memchunk silence;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));
+
+    memcpy(&format, data, sizeof(int32_t));
+    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+    data = (const char*) data + sizeof(int32_t);
+
+    memcpy(&rate, data, sizeof(int32_t));
+    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+    data = (const char*) data + sizeof(int32_t);
+
+    ss.rate = (uint32_t) rate;
+    format_esd2native(format, c->swap_byte_order, &ss);
+
+    CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification");
+
+    if (c->options->default_sink) {
+        sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK);
+        CHECK_VALIDITY(sink, "No such sink: %s", pa_strnull(c->options->default_sink));
+    }
+
+    pa_strlcpy(name, data, sizeof(name));
+
+    utf8_name = pa_utf8_filter(name);
+    pa_client_set_name(c->client, utf8_name);
+    pa_xfree(utf8_name);
+
+    c->original_name = pa_xstrdup(name);
+
+    pa_assert(!c->sink_input && !c->input_memblockq);
+
+    pa_sink_input_new_data_init(&sdata);
+    sdata.driver = __FILE__;
+    sdata.module = c->options->module;
+    sdata.client = c->client;
+    if (sink)
+        pa_sink_input_new_data_set_sink(&sdata, sink, false);
+    pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
+
+    pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata);
+    pa_sink_input_new_data_done(&sdata);
+
+    CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
+
+    l = (size_t) ((double) pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
+    pa_sink_input_get_silence(c->sink_input, &silence);
+    c->input_memblockq = pa_memblockq_new(
+            "esound protocol connection input_memblockq",
+            0,
+            l,
+            l,
+            &ss,
+            (size_t) -1,
+            l/PLAYBACK_BUFFER_FRAGMENTS,
+            0,
+            &silence);
+    pa_memblock_unref(silence.memblock);
+    pa_iochannel_socket_set_rcvbuf(c->io, l);
+
+    c->sink_input->parent.process_msg = sink_input_process_msg;
+    c->sink_input->pop = sink_input_pop_cb;
+    c->sink_input->process_rewind = sink_input_process_rewind_cb;
+    c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    c->sink_input->kill = sink_input_kill_cb;
+    c->sink_input->userdata = c;
+
+    pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
+    c->state = ESD_STREAMING_DATA;
+
+    c->protocol->n_player++;
+
+    pa_atomic_store(&c->playback.missing, (int) pa_memblockq_pop_missing(c->input_memblockq));
+
+    pa_sink_input_put(c->sink_input);
+
+    return 0;
+}
+
+static int esd_proto_stream_record(connection *c, esd_proto_t request, const void *data, size_t length) {
+    char name[ESD_NAME_MAX], *utf8_name;
+    int32_t format, rate;
+    pa_source *source = NULL;
+    pa_sample_spec ss;
+    size_t l;
+    pa_source_output_new_data sdata;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));
+
+    memcpy(&format, data, sizeof(int32_t));
+    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+    data = (const char*) data + sizeof(int32_t);
+
+    memcpy(&rate, data, sizeof(int32_t));
+    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+    data = (const char*) data + sizeof(int32_t);
+
+    ss.rate = (uint32_t) rate;
+    format_esd2native(format, c->swap_byte_order, &ss);
+
+    CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
+
+    if (request == ESD_PROTO_STREAM_MON) {
+        pa_sink* sink;
+
+        sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK);
+        CHECK_VALIDITY(sink, "No such sink: %s", pa_strnull(c->options->default_sink));
+
+        source = sink->monitor_source;
+        CHECK_VALIDITY(source, "No such source.");
+    } else {
+        pa_assert(request == ESD_PROTO_STREAM_REC);
+
+        if (c->options->default_source) {
+            source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE);
+            CHECK_VALIDITY(source, "No such source: %s", pa_strnull(c->options->default_source));
+        }
+    }
+
+    pa_strlcpy(name, data, sizeof(name));
+
+    utf8_name = pa_utf8_filter(name);
+    pa_client_set_name(c->client, utf8_name);
+    pa_xfree(utf8_name);
+
+    c->original_name = pa_xstrdup(name);
+
+    pa_assert(!c->output_memblockq && !c->source_output);
+
+    pa_source_output_new_data_init(&sdata);
+    sdata.driver = __FILE__;
+    sdata.module = c->options->module;
+    sdata.client = c->client;
+    if (source)
+        pa_source_output_new_data_set_source(&sdata, source, false);
+    pa_source_output_new_data_set_sample_spec(&sdata, &ss);
+
+    pa_source_output_new(&c->source_output, c->protocol->core, &sdata);
+    pa_source_output_new_data_done(&sdata);
+
+    CHECK_VALIDITY(c->source_output, "Failed to create source output.");
+
+    l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
+    c->output_memblockq = pa_memblockq_new(
+            "esound protocol connection output_memblockq",
+            0,
+            l,
+            l,
+            &ss,
+            1,
+            0,
+            0,
+            NULL);
+    pa_iochannel_socket_set_sndbuf(c->io, l);
+
+    c->source_output->push = source_output_push_cb;
+    c->source_output->kill = source_output_kill_cb;
+    c->source_output->get_latency = source_output_get_latency_cb;
+    c->source_output->userdata = c;
+
+    pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
+    c->state = ESD_STREAMING_DATA;
+
+    c->protocol->n_player++;
+
+    pa_source_output_put(c->source_output);
+
+    return 0;
+}
+
+static int esd_proto_get_latency(connection *c, esd_proto_t request, const void *data, size_t length) {
+    pa_sink *sink;
+    int32_t latency;
+
+    connection_assert_ref(c);
+    pa_assert(!data);
+    pa_assert(length == 0);
+
+    if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
+        latency = 0;
+    else {
+        double usec = (double) pa_sink_get_requested_latency(sink);
+        latency = (int) ((usec*44100)/1000000);
+    }
+
+    latency = PA_MAYBE_INT32_SWAP(c->swap_byte_order, latency);
+    connection_write(c, &latency, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length) {
+    int32_t rate = 44100, format = ESD_STEREO|ESD_BITS16;
+    int32_t response;
+    pa_sink *sink;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == sizeof(int32_t));
+
+    if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK))) {
+        rate = (int32_t) sink->sample_spec.rate;
+        format = format_native2esd(&sink->sample_spec);
+    }
+
+    connection_write_prepare(c, sizeof(int32_t) * 3);
+
+    response = 0;
+    connection_write(c, &response, sizeof(int32_t));
+    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+    connection_write(c, &rate, sizeof(int32_t));
+    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+    connection_write(c, &format, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length) {
+    size_t t, k, s;
+    connection *conn;
+    uint32_t idx = PA_IDXSET_INVALID;
+    unsigned nsamples;
+    char terminator[sizeof(int32_t)*6+ESD_NAME_MAX];
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == sizeof(int32_t));
+
+    if (esd_proto_server_info(c, request, data, length) < 0)
+        return -1;
+
+    k = sizeof(int32_t)*5+ESD_NAME_MAX;
+    s = sizeof(int32_t)*6+ESD_NAME_MAX;
+    nsamples = pa_idxset_size(c->protocol->core->scache);
+    t = s*(nsamples+1) + k*(c->protocol->n_player+1);
+
+    connection_write_prepare(c, t);
+
+    memset(terminator, 0, sizeof(terminator));
+
+    PA_IDXSET_FOREACH(conn, c->protocol->connections, idx) {
+        int32_t id, format = ESD_BITS16 | ESD_STEREO, rate = 44100, lvolume = ESD_VOLUME_BASE, rvolume = ESD_VOLUME_BASE;
+        char name[ESD_NAME_MAX];
+
+        if (conn->state != ESD_STREAMING_DATA)
+            continue;
+
+        pa_assert(t >= k*2+s);
+
+        if (conn->sink_input) {
+            pa_cvolume volume;
+            pa_sink_input_get_volume(conn->sink_input, &volume, true);
+            rate = (int32_t) conn->sink_input->sample_spec.rate;
+            lvolume = (int32_t) ((volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+            rvolume = (int32_t) ((volume.values[volume.channels == 2 ? 1 : 0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+            format = format_native2esd(&conn->sink_input->sample_spec);
+        }
+
+        /* id */
+        id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) (conn->index+1));
+        connection_write(c, &id, sizeof(int32_t));
+
+        /* name */
+        memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
+        if (conn->original_name)
+            strncpy(name, conn->original_name, ESD_NAME_MAX);
+        else if (conn->client && pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME))
+            strncpy(name, pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME), ESD_NAME_MAX);
+        connection_write(c, name, ESD_NAME_MAX);
+
+        /* rate */
+        rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+        connection_write(c, &rate, sizeof(int32_t));
+
+        /* left */
+        lvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, lvolume);
+        connection_write(c, &lvolume, sizeof(int32_t));
+
+        /*right*/
+        rvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rvolume);
+        connection_write(c, &rvolume, sizeof(int32_t));
+
+        /*format*/
+        format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+        connection_write(c, &format, sizeof(int32_t));
+
+        t -= k;
+    }
+
+    pa_assert(t == s*(nsamples+1)+k);
+    t -= k;
+
+    connection_write(c, terminator, k);
+
+    if (nsamples) {
+        pa_scache_entry *ce;
+
+        idx = PA_IDXSET_INVALID;
+
+        PA_IDXSET_FOREACH(ce, c->protocol->core->scache, idx) {
+            int32_t id, rate, lvolume, rvolume, format, len;
+            char name[ESD_NAME_MAX];
+            pa_channel_map stereo = { .channels = 2, .map = { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } };
+            pa_cvolume volume;
+            pa_sample_spec ss;
+
+            pa_assert(t >= s*2);
+
+            if (ce->volume_is_set) {
+                volume = ce->volume;
+                pa_cvolume_remap(&volume, &ce->channel_map, &stereo);
+            } else
+                pa_cvolume_reset(&volume, 2);
+
+            if (ce->memchunk.memblock)
+                ss = ce->sample_spec;
+            else {
+                ss.format = PA_SAMPLE_S16NE;
+                ss.rate = 44100;
+                ss.channels = 2;
+            }
+
+            /* id */
+            id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
+            connection_write(c, &id, sizeof(int32_t));
+
+            /* name */
+            memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
+            if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0)
+                strncpy(name, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX);
+            else
+                pa_snprintf(name, ESD_NAME_MAX, "native.%s", ce->name);
+            connection_write(c, name, ESD_NAME_MAX);
+
+            /* rate */
+            rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ss.rate);
+            connection_write(c, &rate, sizeof(int32_t));
+
+            /* left */
+            lvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ((volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM));
+            connection_write(c, &lvolume, sizeof(int32_t));
+
+            /*right*/
+            rvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ((volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM));
+            connection_write(c, &rvolume, sizeof(int32_t));
+
+            /*format*/
+            format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ss));
+            connection_write(c, &format, sizeof(int32_t));
+
+            /*length*/
+            len = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length);
+            connection_write(c, &len, sizeof(int32_t));
+
+            t -= s;
+        }
+    }
+
+    pa_assert(t == s);
+
+    connection_write(c, terminator, s);
+
+    return 0;
+}
+
+static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *data, size_t length) {
+    int32_t ok;
+    uint32_t idx, lvolume, rvolume;
+    connection *conn;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == sizeof(int32_t)*3);
+
+    memcpy(&idx, data, sizeof(uint32_t));
+    idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
+    data = (const char*)data + sizeof(uint32_t);
+
+    memcpy(&lvolume, data, sizeof(uint32_t));
+    lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);
+    data = (const char*)data + sizeof(uint32_t);
+
+    memcpy(&rvolume, data, sizeof(uint32_t));
+    rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
+
+    if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) {
+        pa_cvolume volume;
+        volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+        volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+        volume.channels = conn->sink_input->sample_spec.channels;
+
+        pa_sink_input_set_volume(conn->sink_input, &volume, true, true);
+        ok = 1;
+    } else
+        ok = 0;
+
+    connection_write(c, &ok, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_sample_pan(connection *c, esd_proto_t request, const void *data, size_t length) {
+    int32_t ok = 0;
+    uint32_t idx, lvolume, rvolume;
+    pa_cvolume volume;
+    pa_scache_entry *ce;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == sizeof(int32_t)*3);
+
+    memcpy(&idx, data, sizeof(uint32_t));
+    idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
+    data = (const char*)data + sizeof(uint32_t);
+
+    memcpy(&lvolume, data, sizeof(uint32_t));
+    lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);
+    data = (const char*)data + sizeof(uint32_t);
+
+    memcpy(&rvolume, data, sizeof(uint32_t));
+    rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
+
+    volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+    volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+    volume.channels = 2;
+
+    if ((ce = pa_idxset_get_by_index(c->protocol->core->scache, idx))) {
+        pa_channel_map stereo = { .channels = 2, .map = { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } };
+
+        pa_cvolume_remap(&volume, &stereo, &ce->channel_map);
+        ce->volume = volume;
+        ce->volume_is_set = true;
+        ok = 1;
+    }
+
+    connection_write(c, &ok, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length) {
+    pa_sample_spec ss;
+    int32_t format, rate, sc_length;
+    uint32_t idx;
+    char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == (ESD_NAME_MAX+3*sizeof(int32_t)));
+
+    memcpy(&format, data, sizeof(int32_t));
+    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+    data = (const char*)data + sizeof(int32_t);
+
+    memcpy(&rate, data, sizeof(int32_t));
+    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+    data = (const char*)data + sizeof(int32_t);
+
+    ss.rate = (uint32_t) rate;
+    format_esd2native(format, c->swap_byte_order, &ss);
+
+    CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
+
+    memcpy(&sc_length, data, sizeof(int32_t));
+    sc_length = PA_MAYBE_INT32_SWAP(c->swap_byte_order, sc_length);
+    data = (const char*)data + sizeof(int32_t);
+
+    CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length);
+
+    strcpy(name, SCACHE_PREFIX);
+    pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
+
+    CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
+
+    pa_assert(!c->scache.memchunk.memblock);
+    c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) sc_length);
+    c->scache.memchunk.index = 0;
+    c->scache.memchunk.length = (size_t) sc_length;
+    c->scache.sample_spec = ss;
+    pa_assert(!c->scache.name);
+    c->scache.name = pa_xstrdup(name);
+
+    c->state = ESD_CACHING_SAMPLE;
+
+    pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, c->client->proplist, &idx);
+
+    idx += 1;
+    connection_write(c, &idx, sizeof(uint32_t));
+
+    return 0;
+}
+
+static int esd_proto_sample_get_id(connection *c, esd_proto_t request, const void *data, size_t length) {
+    int32_t ok;
+    uint32_t idx;
+    char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == ESD_NAME_MAX);
+
+    strcpy(name, SCACHE_PREFIX);
+    pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
+
+    CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
+
+    ok = -1;
+    if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID)
+        ok = (int32_t) idx + 1;
+
+    connection_write(c, &ok, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length) {
+    int32_t ok;
+    const char *name;
+    uint32_t idx;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == sizeof(int32_t));
+
+    memcpy(&idx, data, sizeof(uint32_t));
+    idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
+
+    ok = 0;
+
+    if ((name = pa_scache_get_name_by_id(c->protocol->core, idx))) {
+        if (request == ESD_PROTO_SAMPLE_PLAY) {
+            pa_sink *sink;
+
+            if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
+                if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
+                    ok = (int32_t) idx + 1;
+        } else {
+            pa_assert(request == ESD_PROTO_SAMPLE_FREE);
+
+            if (pa_scache_remove_item(c->protocol->core, name) >= 0)
+                ok = (int32_t) idx + 1;
+        }
+    }
+
+    connection_write(c, &ok, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length) {
+    int32_t ok = 1;
+
+    connection_assert_ref(c);
+
+    connection_write_prepare(c, sizeof(int32_t) * 2);
+    connection_write(c, &ok, sizeof(int32_t));
+
+    pa_log_debug("%s of all sinks and sources requested by client %" PRIu32 ".",
+                 request == ESD_PROTO_STANDBY ? "Suspending" : "Resuming", c->client->index);
+
+    if (request == ESD_PROTO_STANDBY) {
+        ok = pa_sink_suspend_all(c->protocol->core, true, PA_SUSPEND_USER) >= 0;
+        ok &= pa_source_suspend_all(c->protocol->core, true, PA_SUSPEND_USER) >= 0;
+    } else {
+        pa_assert(request == ESD_PROTO_RESUME);
+        ok = pa_sink_suspend_all(c->protocol->core, false, PA_SUSPEND_USER) >= 0;
+        ok &= pa_source_suspend_all(c->protocol->core, false, PA_SUSPEND_USER) >= 0;
+    }
+
+    connection_write(c, &ok, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_standby_mode(connection *c, esd_proto_t request, const void *data, size_t length) {
+    int32_t mode;
+    pa_sink *sink, *source;
+
+    connection_assert_ref(c);
+
+    mode = ESM_RUNNING;
+
+    if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
+        if (pa_sink_get_state(sink) == PA_SINK_SUSPENDED)
+            mode = ESM_ON_STANDBY;
+
+    if ((source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE)))
+        if (pa_source_get_state(source) == PA_SOURCE_SUSPENDED)
+            mode = ESM_ON_STANDBY;
+
+    mode = PA_MAYBE_INT32_SWAP(c->swap_byte_order, mode);
+
+    connection_write(c, &mode, sizeof(mode));
+    return 0;
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(pa_client *c) {
+    pa_assert(c);
+
+    connection_unlink(CONNECTION(c->userdata));
+}
+
+/*** pa_iochannel callbacks ***/
+
+static int do_read(connection *c) {
+    connection_assert_ref(c);
+
+/*     pa_log("READ"); */
+
+    if (c->state == ESD_NEXT_REQUEST) {
+        ssize_t r;
+        pa_assert(c->read_data_length < sizeof(c->request));
+
+        if ((r = pa_iochannel_read(c->io,
+                                   ((uint8_t*) &c->request) + c->read_data_length,
+                                   sizeof(c->request) - c->read_data_length)) <= 0) {
+
+            if (r < 0 && (errno == EINTR || errno == EAGAIN))
+                return 0;
+
+            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            return -1;
+        }
+
+        c->read_data_length += (size_t) r;
+
+        if (c->read_data_length >= sizeof(c->request)) {
+            struct proto_handler *handler;
+
+            c->request = PA_MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
+
+            if (c->request < ESD_PROTO_CONNECT || c->request >= ESD_PROTO_MAX) {
+                pa_log("received invalid request.");
+                return -1;
+            }
+
+            handler = proto_map+c->request;
+
+/*             pa_log("executing request #%u", c->request); */
+
+            if (!handler->proc) {
+                pa_log("received unimplemented request #%u.", c->request);
+                return -1;
+            }
+
+            if (handler->data_length == 0) {
+                c->read_data_length = 0;
+
+                if (handler->proc(c, c->request, NULL, 0) < 0)
+                    return -1;
+
+            } else {
+                if (c->read_data_alloc < handler->data_length)
+                    c->read_data = pa_xrealloc(c->read_data, c->read_data_alloc = handler->data_length);
+                pa_assert(c->read_data);
+
+                c->state = ESD_NEEDS_REQDATA;
+                c->read_data_length = 0;
+            }
+        }
+
+    } else if (c->state == ESD_NEEDS_REQDATA) {
+        ssize_t r;
+        struct proto_handler *handler = proto_map+c->request;
+
+        pa_assert(handler->proc);
+
+        pa_assert(c->read_data && c->read_data_length < handler->data_length);
+
+        if ((r = pa_iochannel_read(c->io,
+                                   (uint8_t*) c->read_data + c->read_data_length,
+                                   handler->data_length - c->read_data_length)) <= 0) {
+
+            if (r < 0 && (errno == EINTR || errno == EAGAIN))
+                return 0;
+
+            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            return -1;
+        }
+
+        c->read_data_length += (size_t) r;
+        if (c->read_data_length >= handler->data_length) {
+            size_t l = c->read_data_length;
+            pa_assert(handler->proc);
+
+            c->state = ESD_NEXT_REQUEST;
+            c->read_data_length = 0;
+
+            if (handler->proc(c, c->request, c->read_data, l) < 0)
+                return -1;
+        }
+    } else if (c->state == ESD_CACHING_SAMPLE) {
+        ssize_t r;
+        void *p;
+
+        pa_assert(c->scache.memchunk.memblock);
+        pa_assert(c->scache.name);
+        pa_assert(c->scache.memchunk.index < c->scache.memchunk.length);
+
+        p = pa_memblock_acquire(c->scache.memchunk.memblock);
+        r = pa_iochannel_read(c->io, (uint8_t*) p+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index);
+        pa_memblock_release(c->scache.memchunk.memblock);
+
+        if (r <= 0) {
+            if (r < 0 && (errno == EINTR || errno == EAGAIN))
+                return 0;
+
+            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            return -1;
+        }
+
+        c->scache.memchunk.index += (size_t) r;
+        pa_assert(c->scache.memchunk.index <= c->scache.memchunk.length);
+
+        if (c->scache.memchunk.index == c->scache.memchunk.length) {
+            uint32_t idx;
+
+            c->scache.memchunk.index = 0;
+            pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, c->client->proplist, &idx);
+
+            pa_memblock_unref(c->scache.memchunk.memblock);
+            pa_memchunk_reset(&c->scache.memchunk);
+
+            pa_xfree(c->scache.name);
+            c->scache.name = NULL;
+
+            c->state = ESD_NEXT_REQUEST;
+
+            idx += 1;
+            connection_write(c, &idx, sizeof(uint32_t));
+        }
+
+    } else if (c->state == ESD_STREAMING_DATA && c->sink_input) {
+        pa_memchunk chunk;
+        ssize_t r;
+        size_t l;
+        void *p;
+        size_t space = 0;
+
+        pa_assert(c->input_memblockq);
+
+/*         pa_log("STREAMING_DATA"); */
+
+        if ((l = (size_t) pa_atomic_load(&c->playback.missing)) <= 0)
+            return 0;
+
+        if (c->playback.current_memblock) {
+
+            space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
+
+            if (space <= 0) {
+                pa_memblock_unref(c->playback.current_memblock);
+                c->playback.current_memblock = NULL;
+            }
+        }
+
+        if (!c->playback.current_memblock) {
+            pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1));
+            c->playback.memblock_index = 0;
+
+            space = pa_memblock_get_length(c->playback.current_memblock);
+        }
+
+        if (l > space)
+            l = space;
+
+        p = pa_memblock_acquire(c->playback.current_memblock);
+        r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l);
+        pa_memblock_release(c->playback.current_memblock);
+
+        if (r <= 0) {
+
+            if (r < 0 && (errno == EINTR || errno == EAGAIN))
+                return 0;
+
+            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            return -1;
+        }
+
+        chunk.memblock = c->playback.current_memblock;
+        chunk.index = c->playback.memblock_index;
+        chunk.length = (size_t) r;
+
+        c->playback.memblock_index += (size_t) r;
+
+        pa_atomic_sub(&c->playback.missing, (int) r);
+        pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
+    }
+
+    return 0;
+}
+
+static int do_write(connection *c) {
+    connection_assert_ref(c);
+
+/*     pa_log("WRITE"); */
+
+    if (c->write_data_length) {
+        ssize_t r;
+
+        pa_assert(c->write_data_index < c->write_data_length);
+        if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
+            pa_log("write(): %s", pa_cstrerror(errno));
+            return -1;
+        }
+
+        c->write_data_index += (size_t) r;
+        if (c->write_data_index >= c->write_data_length)
+            c->write_data_length = c->write_data_index = 0;
+
+        return 1;
+
+    } else if (c->state == ESD_STREAMING_DATA && c->source_output) {
+        pa_memchunk chunk;
+        ssize_t r;
+        void *p;
+
+        if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
+            return 0;
+
+        pa_assert(chunk.memblock);
+        pa_assert(chunk.length);
+
+        p = pa_memblock_acquire(chunk.memblock);
+        r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
+        pa_memblock_release(chunk.memblock);
+
+        pa_memblock_unref(chunk.memblock);
+
+        if (r < 0) {
+            pa_log("write(): %s", pa_cstrerror(errno));
+            return -1;
+        }
+
+        pa_memblockq_drop(c->output_memblockq, (size_t) r);
+        return 1;
+    }
+
+    return 0;
+}
+
+static void do_work(connection *c) {
+    connection_assert_ref(c);
+
+    c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
+
+    if (c->dead)
+        return;
+
+    if (pa_iochannel_is_readable(c->io))
+        if (do_read(c) < 0)
+            goto fail;
+
+    if (c->state == ESD_STREAMING_DATA && !c->sink_input && pa_iochannel_is_hungup(c->io))
+        /* In case we are in capture mode we will never call read()
+         * on the socket, hence we need to detect the hangup manually
+         * here, instead of simply waiting for read() to return 0. */
+        goto fail;
+
+    while (pa_iochannel_is_writable(c->io)) {
+        int r = do_write(c);
+        if (r < 0)
+            goto fail;
+        if (r == 0)
+            break;
+    }
+
+    return;
+
+fail:
+
+    if (c->state == ESD_STREAMING_DATA && c->sink_input) {
+        c->dead = true;
+
+        pa_iochannel_free(c->io);
+        c->io = NULL;
+
+        pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL);
+    } else
+        connection_unlink(c);
+}
+
+static void io_callback(pa_iochannel*io, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    connection_assert_ref(c);
+    pa_assert(io);
+
+    do_work(c);
+}
+
+static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    connection_assert_ref(c);
+    pa_assert(e);
+
+    do_work(c);
+}
+
+static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    connection *c = CONNECTION(o);
+    connection_assert_ref(c);
+
+    if (!c->protocol)
+        return -1;
+
+    switch (code) {
+        case CONNECTION_MESSAGE_REQUEST_DATA:
+            do_work(c);
+            break;
+
+        case CONNECTION_MESSAGE_POST_DATA:
+/*             pa_log("got data %u", chunk->length); */
+            pa_memblockq_push_align(c->output_memblockq, chunk);
+            do_work(c);
+            break;
+
+        case CONNECTION_MESSAGE_UNLINK_CONNECTION:
+            connection_unlink(c);
+            break;
+    }
+
+    return 0;
+}
+
+/*** sink_input callbacks ***/
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_sink_input *i = PA_SINK_INPUT(o);
+    connection*c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    switch (code) {
+
+        case SINK_INPUT_MESSAGE_POST_DATA: {
+            pa_assert(chunk);
+
+            /* New data from the main loop */
+            pa_memblockq_push_align(c->input_memblockq, chunk);
+
+            if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
+                pa_log_debug("Requesting rewind due to end of underrun.");
+                pa_sink_input_request_rewind(c->sink_input, 0, false, true, false);
+            }
+
+/*             pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
+
+            return 0;
+        }
+
+        case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
+            pa_memblockq_prebuf_disable(c->input_memblockq);
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+        }
+
+        default:
+            return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
+    }
+}
+
+/* Called from thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+    connection*c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+    pa_assert(chunk);
+
+    if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
+
+        c->playback.underrun = true;
+
+        if (c->dead && pa_sink_input_safe_to_remove(i))
+            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+
+        return -1;
+    } else {
+        size_t m;
+
+        c->playback.underrun = false;
+
+        chunk->length = PA_MIN(length, chunk->length);
+        pa_memblockq_drop(c->input_memblockq, chunk->length);
+        m = pa_memblockq_pop_missing(c->input_memblockq);
+
+        if (m > 0)
+            if (pa_atomic_add(&c->playback.missing, (int) m) <= 0)
+                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
+
+        return 0;
+    }
+}
+
+/* Called from thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    /* If we are in an underrun, then we don't rewind */
+    if (i->thread_info.underrun_for > 0)
+        return;
+
+    pa_memblockq_rewind(c->input_memblockq, nbytes);
+}
+
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+
+    connection_unlink(CONNECTION(i->userdata));
+}
+
+/*** source_output callbacks ***/
+
+/* Called from thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    connection *c;
+
+    pa_source_output_assert_ref(o);
+    c = CONNECTION(o->userdata);
+    pa_assert(c);
+    pa_assert(chunk);
+
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+}
+
+static void source_output_kill_cb(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+
+    connection_unlink(CONNECTION(o->userdata));
+}
+
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+    connection*c;
+
+    pa_source_output_assert_ref(o);
+    c = CONNECTION(o->userdata);
+    pa_assert(c);
+
+    return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
+}
+
+/*** entry points ***/
+
+static void auth_timeout(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    pa_assert(m);
+    connection_assert_ref(c);
+    pa_assert(c->auth_timeout_event == e);
+
+    if (!c->authorized)
+        connection_unlink(c);
+}
+
+void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esound_options *o) {
+    connection *c;
+    char pname[128];
+    pa_client_new_data data;
+    pa_client *client;
+
+    pa_assert(p);
+    pa_assert(io);
+    pa_assert(o);
+
+    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
+        pa_iochannel_free(io);
+        return;
+    }
+
+    pa_client_new_data_init(&data);
+    data.module = o->module;
+    data.driver = __FILE__;
+    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "EsounD client (%s)", pname);
+    pa_proplist_sets(data.proplist, "esound-protocol.peer", pname);
+    client = pa_client_new(p->core, &data);
+    pa_client_new_data_done(&data);
+
+    if (!client)
+        return;
+
+    c = pa_msgobject_new(connection);
+    c->parent.parent.free = connection_free;
+    c->parent.process_msg = connection_process_msg;
+    c->protocol = p;
+    c->io = io;
+    pa_iochannel_set_callback(c->io, io_callback, c);
+
+    c->client = client;
+    c->client->kill = client_kill_cb;
+    c->client->userdata = c;
+
+    c->options = pa_esound_options_ref(o);
+    c->authorized = false;
+    c->swap_byte_order = false;
+    c->dead = false;
+
+    c->read_data_length = 0;
+    c->read_data = pa_xmalloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length);
+
+    c->write_data_length = c->write_data_index = c->write_data_alloc = 0;
+    c->write_data = NULL;
+
+    c->state = ESD_NEEDS_REQDATA;
+    c->request = ESD_PROTO_CONNECT;
+
+    c->sink_input = NULL;
+    c->input_memblockq = NULL;
+
+    c->source_output = NULL;
+    c->output_memblockq = NULL;
+
+    c->playback.current_memblock = NULL;
+    c->playback.memblock_index = 0;
+    c->playback.underrun = true;
+    pa_atomic_store(&c->playback.missing, 0);
+
+    pa_memchunk_reset(&c->scache.memchunk);
+    c->scache.name = NULL;
+
+    c->original_name = NULL;
+
+    if (o->auth_anonymous) {
+        pa_log_info("Client authenticated anonymously.");
+        c->authorized = true;
+    }
+
+    if (!c->authorized &&
+        o->auth_ip_acl &&
+        pa_ip_acl_check(o->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
+
+        pa_log_info("Client authenticated by IP ACL.");
+        c->authorized = true;
+    }
+
+    if (!c->authorized)
+        c->auth_timeout_event = pa_core_rttime_new(p->core, pa_rtclock_now() + AUTH_TIMEOUT, auth_timeout, c);
+    else
+        c->auth_timeout_event = NULL;
+
+    c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
+    p->core->mainloop->defer_enable(c->defer_event, 0);
+
+    pa_idxset_put(p->connections, c, &c->index);
+}
+
+void pa_esound_protocol_disconnect(pa_esound_protocol *p, pa_module *m) {
+    connection *c;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(m);
+
+    while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+        if (c->options->module == m)
+            connection_unlink(c);
+}
+
+static pa_esound_protocol* esound_protocol_new(pa_core *c) {
+    pa_esound_protocol *p;
+
+    pa_assert(c);
+
+    p = pa_xnew(pa_esound_protocol, 1);
+    PA_REFCNT_INIT(p);
+    p->core = c;
+    p->connections = pa_idxset_new(NULL, NULL);
+    p->n_player = 0;
+
+    pa_assert_se(pa_shared_set(c, "esound-protocol", p) >= 0);
+
+    return p;
+}
+
+pa_esound_protocol* pa_esound_protocol_get(pa_core *c) {
+    pa_esound_protocol *p;
+
+    if ((p = pa_shared_get(c, "esound-protocol")))
+        return pa_esound_protocol_ref(p);
+
+    return esound_protocol_new(c);
+}
+
+pa_esound_protocol* pa_esound_protocol_ref(pa_esound_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    PA_REFCNT_INC(p);
+
+    return p;
+}
+
+void pa_esound_protocol_unref(pa_esound_protocol *p) {
+    connection *c;
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    if (PA_REFCNT_DEC(p) > 0)
+        return;
+
+    while ((c = pa_idxset_first(p->connections, NULL)))
+        connection_unlink(c);
+
+    pa_idxset_free(p->connections, NULL);
+
+    pa_assert_se(pa_shared_remove(p->core, "esound-protocol") >= 0);
+
+    pa_xfree(p);
+}
+
+pa_esound_options* pa_esound_options_new(void) {
+    pa_esound_options *o;
+
+    o = pa_xnew0(pa_esound_options, 1);
+    PA_REFCNT_INIT(o);
+
+    return o;
+}
+
+pa_esound_options* pa_esound_options_ref(pa_esound_options *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    PA_REFCNT_INC(o);
+
+    return o;
+}
+
+void pa_esound_options_unref(pa_esound_options *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (PA_REFCNT_DEC(o) > 0)
+        return;
+
+    if (o->auth_ip_acl)
+        pa_ip_acl_free(o->auth_ip_acl);
+
+    if (o->auth_cookie)
+        pa_auth_cookie_unref(o->auth_cookie);
+
+    pa_xfree(o->default_sink);
+    pa_xfree(o->default_source);
+
+    pa_xfree(o);
+}
+
+int pa_esound_options_parse(pa_esound_options *o, pa_core *c, pa_modargs *ma) {
+    bool enabled;
+    const char *acl;
+
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+    pa_assert(ma);
+
+    if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &o->auth_anonymous) < 0) {
+        pa_log("auth-anonymous= expects a boolean argument.");
+        return -1;
+    }
+
+    if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
+        pa_ip_acl *ipa;
+
+        if (!(ipa = pa_ip_acl_new(acl))) {
+            pa_log("Failed to parse IP ACL '%s'", acl);
+            return -1;
+        }
+
+        if (o->auth_ip_acl)
+            pa_ip_acl_free(o->auth_ip_acl);
+
+        o->auth_ip_acl = ipa;
+    }
+
+    enabled = true;
+    if (pa_modargs_get_value_boolean(ma, "auth-cookie-enabled", &enabled) < 0) {
+        pa_log("auth-cookie-enabled= expects a boolean argument.");
+        return -1;
+    }
+
+    if (o->auth_cookie)
+        pa_auth_cookie_unref(o->auth_cookie);
+
+    if (enabled) {
+        char *cn;
+
+        /* The new name for this is 'auth-cookie', for compat reasons
+         * we check the old name too */
+        if (!(cn = pa_xstrdup(pa_modargs_get_value(ma, "auth-cookie", NULL)))) {
+            if (!(cn = pa_xstrdup(pa_modargs_get_value(ma, "cookie", NULL)))) {
+                if (pa_append_to_home_dir(DEFAULT_COOKIE_FILE, &cn) < 0)
+                    return -1;
+            }
+        }
+
+        o->auth_cookie = pa_auth_cookie_get(c, cn, true, ESD_KEY_LEN);
+        pa_xfree(cn);
+        if (!o->auth_cookie)
+            return -1;
+
+    } else
+        o->auth_cookie = NULL;
+
+    pa_xfree(o->default_sink);
+    o->default_sink = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+
+    pa_xfree(o->default_source);
+    o->default_source = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
+
+    return 0;
+}
diff --git a/src/pulsecore/protocol-esound.h b/src/pulsecore/protocol-esound.h
new file mode 100644 (file)
index 0000000..6208640
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef fooprotocolesoundhfoo
+#define fooprotocolesoundhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/ipacl.h>
+#include <pulsecore/auth-cookie.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+
+typedef struct pa_esound_protocol pa_esound_protocol;
+
+typedef struct pa_esound_options {
+    PA_REFCNT_DECLARE;
+
+    pa_module *module;
+
+    bool auth_anonymous;
+    pa_ip_acl *auth_ip_acl;
+    pa_auth_cookie *auth_cookie;
+
+    char *default_sink, *default_source;
+} pa_esound_options;
+
+pa_esound_protocol* pa_esound_protocol_get(pa_core*core);
+pa_esound_protocol* pa_esound_protocol_ref(pa_esound_protocol *p);
+void pa_esound_protocol_unref(pa_esound_protocol *p);
+void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esound_options *o);
+void pa_esound_protocol_disconnect(pa_esound_protocol *p, pa_module *m);
+
+pa_esound_options* pa_esound_options_new(void);
+pa_esound_options* pa_esound_options_ref(pa_esound_options *o);
+void pa_esound_options_unref(pa_esound_options *o);
+int pa_esound_options_parse(pa_esound_options *o, pa_core *c, pa_modargs *ma);
+
+#endif
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
new file mode 100644 (file)
index 0000000..25a2cd0
--- /dev/null
@@ -0,0 +1,817 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/ioline.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/cli-text.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/mime-type.h>
+
+#include "protocol-http.h"
+
+/* Don't allow more than this many concurrent connections */
+#define MAX_CONNECTIONS 10
+
+#define URL_ROOT "/"
+#define URL_CSS "/style"
+#define URL_STATUS "/status"
+#define URL_LISTEN "/listen"
+#define URL_LISTEN_SOURCE "/listen/source/"
+
+#define MIME_HTML "text/html; charset=utf-8"
+#define MIME_TEXT "text/plain; charset=utf-8"
+#define MIME_CSS "text/css"
+
+#define HTML_HEADER(t)                                                  \
+    "<?xml version=\"1.0\"?>\n"                                         \
+    "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" \
+    "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"                   \
+    "        <head>\n"                                                  \
+    "                <title>"t"</title>\n"                              \
+    "                <link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/>\n" \
+    "        </head>\n"                                                 \
+    "        <body>\n"
+
+#define HTML_FOOTER                                                     \
+    "        </body>\n"                                                 \
+    "</html>\n"
+
+#define RECORD_BUFFER_SECONDS (5)
+#define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
+
+enum state {
+    STATE_REQUEST_LINE,
+    STATE_MIME_HEADER,
+    STATE_DATA
+};
+
+enum method {
+    METHOD_GET,
+    METHOD_HEAD
+};
+
+struct connection {
+    pa_http_protocol *protocol;
+    pa_iochannel *io;
+    pa_ioline *line;
+    pa_memblockq *output_memblockq;
+    pa_source_output *source_output;
+    pa_client *client;
+    enum state state;
+    char *url;
+    enum method method;
+    pa_module *module;
+};
+
+struct pa_http_protocol {
+    PA_REFCNT_DECLARE;
+
+    pa_core *core;
+    pa_idxset *connections;
+
+    pa_strlist *servers;
+};
+
+enum {
+    SOURCE_OUTPUT_MESSAGE_POST_DATA = PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
+/* Called from main context */
+static void connection_unlink(struct connection *c) {
+    pa_assert(c);
+
+    if (c->source_output) {
+        pa_source_output_unlink(c->source_output);
+        c->source_output->userdata = NULL;
+        pa_source_output_unref(c->source_output);
+    }
+
+    if (c->client)
+        pa_client_free(c->client);
+
+    pa_xfree(c->url);
+
+    if (c->line)
+        pa_ioline_unref(c->line);
+
+    if (c->io)
+        pa_iochannel_free(c->io);
+
+    if (c->output_memblockq)
+        pa_memblockq_free(c->output_memblockq);
+
+    pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+
+    pa_xfree(c);
+}
+
+/* Called from main context */
+static int do_write(struct connection *c) {
+    pa_memchunk chunk;
+    ssize_t r;
+    void *p;
+
+    pa_assert(c);
+
+    if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
+        return 0;
+
+    pa_assert(chunk.memblock);
+    pa_assert(chunk.length > 0);
+
+    p = pa_memblock_acquire(chunk.memblock);
+    r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
+    pa_memblock_release(chunk.memblock);
+
+    pa_memblock_unref(chunk.memblock);
+
+    if (r < 0) {
+        pa_log("write(): %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_memblockq_drop(c->output_memblockq, (size_t) r);
+
+    return 1;
+}
+
+/* Called from main context */
+static void do_work(struct connection *c) {
+    pa_assert(c);
+
+    if (pa_iochannel_is_hungup(c->io))
+        goto fail;
+
+    while (pa_iochannel_is_writable(c->io)) {
+        int r = do_write(c);
+        if (r < 0)
+            goto fail;
+        if (r == 0)
+            break;
+    }
+
+    return;
+
+fail:
+    connection_unlink(c);
+}
+
+/* Called from thread context, except when it is not */
+static int source_output_process_msg(pa_msgobject *m, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_source_output *o = PA_SOURCE_OUTPUT(m);
+    struct connection *c;
+
+    pa_source_output_assert_ref(o);
+
+    if (!(c = o->userdata))
+        return -1;
+
+    switch (code) {
+
+        case SOURCE_OUTPUT_MESSAGE_POST_DATA:
+            /* While this function is usually called from IO thread
+             * context, this specific command is not! */
+            pa_memblockq_push_align(c->output_memblockq, chunk);
+            do_work(c);
+            break;
+
+        default:
+            return pa_source_output_process_msg(m, code, userdata, offset, chunk);
+    }
+
+    return 0;
+}
+
+/* Called from thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    struct connection *c;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_se(c = o->userdata);
+    pa_assert(chunk);
+
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(o), SOURCE_OUTPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+}
+
+/* Called from main context */
+static void source_output_kill_cb(pa_source_output *o) {
+    struct connection*c;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_se(c = o->userdata);
+
+    connection_unlink(c);
+}
+
+/* Called from main context */
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+    struct connection*c;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_se(c = o->userdata);
+
+    return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
+}
+
+/*** client callbacks ***/
+static void client_kill_cb(pa_client *client) {
+    struct connection*c;
+
+    pa_assert(client);
+    pa_assert_se(c = client->userdata);
+
+    connection_unlink(c);
+}
+
+/*** pa_iochannel callbacks ***/
+static void io_callback(pa_iochannel*io, void *userdata) {
+    struct connection *c = userdata;
+
+    pa_assert(c);
+    pa_assert(io);
+
+    do_work(c);
+}
+
+static char *escape_html(const char *t) {
+    pa_strbuf *sb;
+    const char *p, *e;
+
+    sb = pa_strbuf_new();
+
+    for (e = p = t; *p; p++) {
+
+        if (*p == '>' || *p == '<' || *p == '&') {
+
+            if (p > e) {
+                pa_strbuf_putsn(sb, e, p-e);
+                e = p + 1;
+            }
+
+            if (*p == '>')
+                pa_strbuf_puts(sb, "&gt;");
+            else if (*p == '<')
+                pa_strbuf_puts(sb, "&lt;");
+            else
+                pa_strbuf_puts(sb, "&amp;");
+        }
+    }
+
+    if (p > e)
+        pa_strbuf_putsn(sb, e, p-e);
+
+    return pa_strbuf_to_string_free(sb);
+}
+
+static void http_response(
+        struct connection *c,
+        int code,
+        const char *msg,
+        const char *mime) {
+
+    char *s;
+
+    pa_assert(c);
+    pa_assert(msg);
+    pa_assert(mime);
+
+    s = pa_sprintf_malloc(
+            "HTTP/1.0 %i %s\n"
+            "Connection: close\n"
+            "Content-Type: %s\n"
+            "Cache-Control: no-cache\n"
+            "Expires: 0\n"
+            "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
+            "\n", code, msg, mime);
+    pa_ioline_puts(c->line, s);
+    pa_xfree(s);
+}
+
+static void html_response(
+        struct connection *c,
+        int code,
+        const char *msg,
+        const char *text) {
+
+    char *s;
+    pa_assert(c);
+
+    http_response(c, code, msg, MIME_HTML);
+
+    if (c->method == METHOD_HEAD) {
+        pa_ioline_defer_close(c->line);
+        return;
+    }
+
+    if (!text)
+        text = msg;
+
+    s = pa_sprintf_malloc(
+            HTML_HEADER("%s")
+            "%s"
+            HTML_FOOTER,
+            text, text);
+
+    pa_ioline_puts(c->line, s);
+    pa_xfree(s);
+
+    pa_ioline_defer_close(c->line);
+}
+
+static void html_print_field(pa_ioline *line, const char *left, const char *right) {
+    char *eleft, *eright;
+
+    eleft = escape_html(left);
+    eright = escape_html(right);
+
+    pa_ioline_printf(line,
+                     "<tr><td><b>%s</b></td>"
+                     "<td>%s</td></tr>\n", eleft, eright);
+
+    pa_xfree(eleft);
+    pa_xfree(eright);
+}
+
+static void handle_root(struct connection *c) {
+    char *t;
+
+    pa_assert(c);
+
+    http_response(c, 200, "OK", MIME_HTML);
+
+    if (c->method == METHOD_HEAD) {
+        pa_ioline_defer_close(c->line);
+        return;
+    }
+
+    pa_ioline_puts(c->line,
+                   HTML_HEADER(PACKAGE_NAME" "PACKAGE_VERSION)
+                   "<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
+                   "<table>\n");
+
+    t = pa_get_user_name_malloc();
+    html_print_field(c->line, "User Name:", t);
+    pa_xfree(t);
+
+    t = pa_get_host_name_malloc();
+    html_print_field(c->line, "Host name:", t);
+    pa_xfree(t);
+
+    t = pa_machine_id();
+    html_print_field(c->line, "Machine ID:", t);
+    pa_xfree(t);
+
+    t = pa_uname_string();
+    html_print_field(c->line, "System:", t);
+    pa_xfree(t);
+
+    t = pa_sprintf_malloc("%lu", (unsigned long) getpid());
+    html_print_field(c->line, "Process ID:", t);
+    pa_xfree(t);
+
+    pa_ioline_puts(c->line,
+                   "</table>\n"
+                   "<p><a href=\"" URL_STATUS "\">Show an extensive server status report</a></p>\n"
+                   "<p><a href=\"" URL_LISTEN "\">Monitor sinks and sources</a></p>\n"
+                   HTML_FOOTER);
+
+    pa_ioline_defer_close(c->line);
+}
+
+static void handle_css(struct connection *c) {
+    pa_assert(c);
+
+    http_response(c, 200, "OK", MIME_CSS);
+
+    if (c->method == METHOD_HEAD) {
+        pa_ioline_defer_close(c->line);
+        return;
+    }
+
+    pa_ioline_puts(c->line,
+                   "body { color: black; background-color: white; }\n"
+                   "a:link, a:visited { color: #900000; }\n"
+                   "div.news-date { font-size: 80%; font-style: italic; }\n"
+                   "pre { background-color: #f0f0f0; padding: 0.4cm; }\n"
+                   ".grey { color: #8f8f8f; font-size: 80%; }"
+                   "table {  margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
+                   "td { padding-left:10px; padding-right:10px; }\n");
+
+    pa_ioline_defer_close(c->line);
+}
+
+static void handle_status(struct connection *c) {
+    char *r;
+
+    pa_assert(c);
+
+    http_response(c, 200, "OK", MIME_TEXT);
+
+    if (c->method == METHOD_HEAD) {
+        pa_ioline_defer_close(c->line);
+        return;
+    }
+
+    r = pa_full_status_string(c->protocol->core);
+    pa_ioline_puts(c->line, r);
+    pa_xfree(r);
+
+    pa_ioline_defer_close(c->line);
+}
+
+static void handle_listen(struct connection *c) {
+    pa_source *source;
+    pa_sink *sink;
+    uint32_t idx;
+
+    http_response(c, 200, "OK", MIME_HTML);
+
+    pa_ioline_puts(c->line,
+                   HTML_HEADER("Listen")
+                   "<h2>Sinks</h2>\n"
+                   "<p>\n");
+
+    if (c->method == METHOD_HEAD) {
+        pa_ioline_defer_close(c->line);
+        return;
+    }
+
+    PA_IDXSET_FOREACH(sink, c->protocol->core->sinks, idx) {
+        char *t, *m;
+
+        t = escape_html(pa_strna(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+        m = pa_sample_spec_to_mime_type_mimefy(&sink->sample_spec, &sink->channel_map);
+
+        pa_ioline_printf(c->line,
+                         "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
+                         sink->monitor_source->name, m, t);
+
+        pa_xfree(t);
+        pa_xfree(m);
+    }
+
+    pa_ioline_puts(c->line,
+                   "</p>\n"
+                   "<h2>Sources</h2>\n"
+                   "<p>\n");
+
+    PA_IDXSET_FOREACH(source, c->protocol->core->sources, idx) {
+        char *t, *m;
+
+        if (source->monitor_of)
+            continue;
+
+        t = escape_html(pa_strna(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+        m = pa_sample_spec_to_mime_type_mimefy(&source->sample_spec, &source->channel_map);
+
+        pa_ioline_printf(c->line,
+                         "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
+                         source->name, m, t);
+
+        pa_xfree(m);
+        pa_xfree(t);
+
+    }
+
+    pa_ioline_puts(c->line,
+                   "</p>\n"
+                   HTML_FOOTER);
+
+    pa_ioline_defer_close(c->line);
+}
+
+static void line_drain_callback(pa_ioline *l, void *userdata) {
+    struct connection *c;
+
+    pa_assert(l);
+    pa_assert_se(c = userdata);
+
+    /* We don't need the line reader anymore, instead we need a real
+     * binary io channel */
+    pa_assert_se(c->io = pa_ioline_detach_iochannel(c->line));
+    pa_iochannel_set_callback(c->io, io_callback, c);
+
+    pa_iochannel_socket_set_sndbuf(c->io, pa_memblockq_get_length(c->output_memblockq));
+
+    pa_ioline_unref(c->line);
+    c->line = NULL;
+}
+
+static void handle_listen_prefix(struct connection *c, const char *source_name) {
+    pa_source *source;
+    pa_source_output_new_data data;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    char *t;
+    size_t l;
+
+    pa_assert(c);
+    pa_assert(source_name);
+
+    pa_assert(c->line);
+    pa_assert(!c->io);
+
+    if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
+        html_response(c, 404, "Source not found", NULL);
+        return;
+    }
+
+    ss = source->sample_spec;
+    cm = source->channel_map;
+
+    pa_sample_spec_mimefy(&ss, &cm);
+
+    pa_source_output_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = c->module;
+    data.client = c->client;
+    pa_source_output_new_data_set_source(&data, source, false);
+    pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+    pa_source_output_new_data_set_sample_spec(&data, &ss);
+    pa_source_output_new_data_set_channel_map(&data, &cm);
+
+    pa_source_output_new(&c->source_output, c->protocol->core, &data);
+    pa_source_output_new_data_done(&data);
+
+    if (!c->source_output) {
+        html_response(c, 403, "Cannot create source output", NULL);
+        return;
+    }
+
+    c->source_output->parent.process_msg = source_output_process_msg;
+    c->source_output->push = source_output_push_cb;
+    c->source_output->kill = source_output_kill_cb;
+    c->source_output->get_latency = source_output_get_latency_cb;
+    c->source_output->userdata = c;
+
+    pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
+    l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
+    c->output_memblockq = pa_memblockq_new(
+            "http protocol connection output_memblockq",
+            0,
+            l,
+            0,
+            &ss,
+            1,
+            0,
+            0,
+            NULL);
+
+    pa_source_output_put(c->source_output);
+
+    t = pa_sample_spec_to_mime_type(&ss, &cm);
+    http_response(c, 200, "OK", t);
+    pa_xfree(t);
+
+    if (c->method == METHOD_HEAD) {
+        connection_unlink(c);
+        return;
+    }
+    pa_ioline_set_callback(c->line, NULL, NULL);
+
+    if (pa_ioline_is_drained(c->line))
+        line_drain_callback(c->line, c);
+    else
+        pa_ioline_set_drain_callback(c->line, line_drain_callback, c);
+}
+
+static void handle_url(struct connection *c) {
+    pa_assert(c);
+
+    pa_log_debug("Request for %s", c->url);
+
+    if (pa_streq(c->url, URL_ROOT))
+        handle_root(c);
+    else if (pa_streq(c->url, URL_CSS))
+        handle_css(c);
+    else if (pa_streq(c->url, URL_STATUS))
+        handle_status(c);
+    else if (pa_streq(c->url, URL_LISTEN))
+        handle_listen(c);
+    else if (pa_startswith(c->url, URL_LISTEN_SOURCE))
+        handle_listen_prefix(c, c->url + sizeof(URL_LISTEN_SOURCE)-1);
+    else
+        html_response(c, 404, "Not Found", NULL);
+}
+
+static void line_callback(pa_ioline *line, const char *s, void *userdata) {
+    struct connection *c = userdata;
+    pa_assert(line);
+    pa_assert(c);
+
+    if (!s) {
+        /* EOF */
+        connection_unlink(c);
+        return;
+    }
+
+    switch (c->state) {
+        case STATE_REQUEST_LINE: {
+            if (pa_startswith(s, "GET ")) {
+                c->method = METHOD_GET;
+                s +=4;
+            } else if (pa_startswith(s, "HEAD ")) {
+                c->method = METHOD_HEAD;
+                s +=5;
+            } else {
+                goto fail;
+            }
+
+            c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?"));
+            c->state = STATE_MIME_HEADER;
+            break;
+        }
+
+        case STATE_MIME_HEADER: {
+
+            /* Ignore MIME headers */
+            if (strcspn(s, " \r\n") != 0)
+                break;
+
+            /* We're done */
+            c->state = STATE_DATA;
+
+            handle_url(c);
+            break;
+        }
+
+        default:
+            ;
+    }
+
+    return;
+
+fail:
+    html_response(c, 500, "Internal Server Error", NULL);
+}
+
+void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m) {
+    struct connection *c;
+    pa_client_new_data client_data;
+    char pname[128];
+
+    pa_assert(p);
+    pa_assert(io);
+    pa_assert(m);
+
+    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
+        pa_iochannel_free(io);
+        return;
+    }
+
+    c = pa_xnew0(struct connection, 1);
+    c->protocol = p;
+    c->state = STATE_REQUEST_LINE;
+    c->module = m;
+
+    c->line = pa_ioline_new(io);
+    pa_ioline_set_callback(c->line, line_callback, c);
+
+    pa_client_new_data_init(&client_data);
+    client_data.module = c->module;
+    client_data.driver = __FILE__;
+    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+    pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "HTTP client (%s)", pname);
+    pa_proplist_sets(client_data.proplist, "http-protocol.peer", pname);
+    c->client = pa_client_new(p->core, &client_data);
+    pa_client_new_data_done(&client_data);
+
+    if (!c->client)
+        goto fail;
+
+    c->client->kill = client_kill_cb;
+    c->client->userdata = c;
+
+    pa_idxset_put(p->connections, c, NULL);
+
+    return;
+
+fail:
+    if (c)
+        connection_unlink(c);
+}
+
+void pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m) {
+    struct connection *c;
+    uint32_t idx;
+
+    pa_assert(p);
+    pa_assert(m);
+
+    PA_IDXSET_FOREACH(c, p->connections, idx)
+        if (c->module == m)
+            connection_unlink(c);
+}
+
+static pa_http_protocol* http_protocol_new(pa_core *c) {
+    pa_http_protocol *p;
+
+    pa_assert(c);
+
+    p = pa_xnew0(pa_http_protocol, 1);
+    PA_REFCNT_INIT(p);
+    p->core = c;
+    p->connections = pa_idxset_new(NULL, NULL);
+
+    pa_assert_se(pa_shared_set(c, "http-protocol", p) >= 0);
+
+    return p;
+}
+
+pa_http_protocol* pa_http_protocol_get(pa_core *c) {
+    pa_http_protocol *p;
+
+    if ((p = pa_shared_get(c, "http-protocol")))
+        return pa_http_protocol_ref(p);
+
+    return http_protocol_new(c);
+}
+
+pa_http_protocol* pa_http_protocol_ref(pa_http_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    PA_REFCNT_INC(p);
+
+    return p;
+}
+
+void pa_http_protocol_unref(pa_http_protocol *p) {
+    struct connection *c;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    if (PA_REFCNT_DEC(p) > 0)
+        return;
+
+    while ((c = pa_idxset_first(p->connections, NULL)))
+        connection_unlink(c);
+
+    pa_idxset_free(p->connections, NULL);
+
+    pa_strlist_free(p->servers);
+
+    pa_assert_se(pa_shared_remove(p->core, "http-protocol") >= 0);
+
+    pa_xfree(p);
+}
+
+void pa_http_protocol_add_server_string(pa_http_protocol *p, const char *name) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+    pa_assert(name);
+
+    p->servers = pa_strlist_prepend(p->servers, name);
+}
+
+void pa_http_protocol_remove_server_string(pa_http_protocol *p, const char *name) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+    pa_assert(name);
+
+    p->servers = pa_strlist_remove(p->servers, name);
+}
+
+pa_strlist *pa_http_protocol_servers(pa_http_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    return p->servers;
+}
diff --git a/src/pulsecore/protocol-http.h b/src/pulsecore/protocol-http.h
new file mode 100644 (file)
index 0000000..89a6518
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef fooprotocolhttphfoo
+#define fooprotocolhttphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/strlist.h>
+
+typedef struct pa_http_protocol pa_http_protocol;
+
+pa_http_protocol* pa_http_protocol_get(pa_core *core);
+pa_http_protocol* pa_http_protocol_ref(pa_http_protocol *p);
+void pa_http_protocol_unref(pa_http_protocol *p);
+void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m);
+void pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m);
+
+void pa_http_protocol_add_server_string(pa_http_protocol *p, const char *name);
+void pa_http_protocol_remove_server_string(pa_http_protocol *p, const char *name);
+pa_strlist *pa_http_protocol_servers(pa_http_protocol *p);
+
+#endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
new file mode 100644 (file)
index 0000000..866e2c6
--- /dev/null
@@ -0,0 +1,5478 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/version.h>
+#include <pulse/utf8.h>
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+#include <pulse/internal.h>
+
+#include <pulsecore/native-common.h>
+#include <pulsecore/packet.h>
+#include <pulsecore/client.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/tagstruct.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/mem.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/ipacl.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/mem.h>
+
+#include "protocol-native.h"
+
+/* #define PROTOCOL_NATIVE_DEBUG */
+
+/* Kick a client if it doesn't authenticate within this time */
+#define AUTH_TIMEOUT (60 * PA_USEC_PER_SEC)
+
+/* Don't accept more connection than this */
+#define MAX_CONNECTIONS 64
+
+#define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */
+#define DEFAULT_TLENGTH_MSEC 2000 /* 2s */
+#define DEFAULT_PROCESS_MSEC 20   /* 20ms */
+#define DEFAULT_FRAGSIZE_MSEC DEFAULT_TLENGTH_MSEC
+
+struct pa_native_protocol;
+
+typedef struct record_stream {
+    pa_msgobject parent;
+
+    pa_native_connection *connection;
+    uint32_t index;
+
+    pa_source_output *source_output;
+    pa_memblockq *memblockq;
+
+    bool adjust_latency:1;
+    bool early_requests:1;
+
+    /* Requested buffer attributes */
+    pa_buffer_attr buffer_attr_req;
+    /* Fixed-up and adjusted buffer attributes */
+    pa_buffer_attr buffer_attr;
+
+    pa_atomic_t on_the_fly;
+    pa_usec_t configured_source_latency;
+    size_t drop_initial;
+
+    /* Only updated after SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY */
+    size_t on_the_fly_snapshot;
+    pa_usec_t current_monitor_latency;
+    pa_usec_t current_source_latency;
+} record_stream;
+
+#define RECORD_STREAM(o) (record_stream_cast(o))
+PA_DEFINE_PRIVATE_CLASS(record_stream, pa_msgobject);
+
+typedef struct output_stream {
+    pa_msgobject parent;
+} output_stream;
+
+#define OUTPUT_STREAM(o) (output_stream_cast(o))
+PA_DEFINE_PRIVATE_CLASS(output_stream, pa_msgobject);
+
+typedef struct playback_stream {
+    output_stream parent;
+
+    pa_native_connection *connection;
+    uint32_t index;
+
+    pa_sink_input *sink_input;
+    pa_memblockq *memblockq;
+
+    bool adjust_latency:1;
+    bool early_requests:1;
+
+    bool is_underrun:1;
+    bool drain_request:1;
+    uint32_t drain_tag;
+    uint32_t syncid;
+
+    /* Optimization to avoid too many rewinds with a lot of small blocks */
+    pa_atomic_t seek_or_post_in_queue;
+    int64_t seek_windex;
+
+    pa_atomic_t missing;
+    pa_usec_t configured_sink_latency;
+    /* Requested buffer attributes */
+    pa_buffer_attr buffer_attr_req;
+    /* Fixed-up and adjusted buffer attributes */
+    pa_buffer_attr buffer_attr;
+
+    /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
+    int64_t read_index, write_index;
+    size_t render_memblockq_length;
+    pa_usec_t current_sink_latency;
+    uint64_t playing_for, underrun_for;
+} playback_stream;
+
+#define PLAYBACK_STREAM(o) (playback_stream_cast(o))
+PA_DEFINE_PRIVATE_CLASS(playback_stream, output_stream);
+
+typedef struct upload_stream {
+    output_stream parent;
+
+    pa_native_connection *connection;
+    uint32_t index;
+
+    pa_memchunk memchunk;
+    size_t length;
+    char *name;
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    pa_proplist *proplist;
+} upload_stream;
+
+#define UPLOAD_STREAM(o) (upload_stream_cast(o))
+PA_DEFINE_PRIVATE_CLASS(upload_stream, output_stream);
+
+struct pa_native_connection {
+    pa_msgobject parent;
+    pa_native_protocol *protocol;
+    pa_native_options *options;
+    bool authorized:1;
+    bool is_local:1;
+    uint32_t version;
+    pa_client *client;
+    /* R/W mempool, one per client connection, for srbchannel transport.
+     * Both server and client can write to this shm area.
+     *
+     * Note: This will be NULL if our connection with the client does
+     * not support srbchannels */
+    pa_mempool *rw_mempool;
+    pa_pstream *pstream;
+    pa_pdispatch *pdispatch;
+    pa_idxset *record_streams, *output_streams;
+    uint32_t rrobin_index;
+    pa_subscription *subscription;
+    pa_time_event *auth_timeout_event;
+    pa_srbchannel *srbpending;
+};
+
+#define PA_NATIVE_CONNECTION(o) (pa_native_connection_cast(o))
+PA_DEFINE_PRIVATE_CLASS(pa_native_connection, pa_msgobject);
+
+struct pa_native_protocol {
+    PA_REFCNT_DECLARE;
+
+    pa_core *core;
+    pa_idxset *connections;
+
+    pa_strlist *servers;
+    pa_hook hooks[PA_NATIVE_HOOK_MAX];
+
+    pa_hashmap *extensions;
+};
+
+enum {
+    SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY = PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
+enum {
+    SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+    SINK_INPUT_MESSAGE_DRAIN, /* disabled prebuf, get playback started. */
+    SINK_INPUT_MESSAGE_FLUSH,
+    SINK_INPUT_MESSAGE_TRIGGER,
+    SINK_INPUT_MESSAGE_SEEK,
+    SINK_INPUT_MESSAGE_PREBUF_FORCE,
+    SINK_INPUT_MESSAGE_UPDATE_LATENCY,
+    SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR
+};
+
+enum {
+    PLAYBACK_STREAM_MESSAGE_REQUEST_DATA,      /* data requested from sink input from the main loop */
+    PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
+    PLAYBACK_STREAM_MESSAGE_OVERFLOW,
+    PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
+    PLAYBACK_STREAM_MESSAGE_STARTED,
+    PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH
+};
+
+enum {
+    RECORD_STREAM_MESSAGE_POST_DATA         /* data from source output to main loop */
+};
+
+enum {
+    CONNECTION_MESSAGE_RELEASE,
+    CONNECTION_MESSAGE_REVOKE
+};
+
+static bool sink_input_process_underrun_cb(pa_sink_input *i);
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
+static void sink_input_kill_cb(pa_sink_input *i);
+static void sink_input_suspend_cb(pa_sink_input *i, bool suspend);
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest);
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl);
+
+static void native_connection_send_memblock(pa_native_connection *c);
+static void playback_stream_request_bytes(struct playback_stream*s);
+
+static void source_output_kill_cb(pa_source_output *o);
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
+static void source_output_suspend_cb(pa_source_output *o, bool suspend);
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest);
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
+
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+static int source_output_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
+/* structure management */
+
+/* Called from main context */
+static void upload_stream_unlink(upload_stream *s) {
+    pa_assert(s);
+
+    if (!s->connection)
+        return;
+
+    pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
+    s->connection = NULL;
+    upload_stream_unref(s);
+}
+
+/* Called from main context */
+static void upload_stream_free(pa_object *o) {
+    upload_stream *s = UPLOAD_STREAM(o);
+    pa_assert(s);
+
+    upload_stream_unlink(s);
+
+    pa_xfree(s->name);
+
+    if (s->proplist)
+        pa_proplist_free(s->proplist);
+
+    if (s->memchunk.memblock)
+        pa_memblock_unref(s->memchunk.memblock);
+
+    pa_xfree(s);
+}
+
+/* Called from main context */
+static upload_stream* upload_stream_new(
+        pa_native_connection *c,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        const char *name,
+        size_t length,
+        pa_proplist *p) {
+
+    upload_stream *s;
+
+    pa_assert(c);
+    pa_assert(ss);
+    pa_assert(name);
+    pa_assert(length > 0);
+    pa_assert(p);
+
+    s = pa_msgobject_new(upload_stream);
+    s->parent.parent.parent.free = upload_stream_free;
+    s->connection = c;
+    s->sample_spec = *ss;
+    s->channel_map = *map;
+    s->name = pa_xstrdup(name);
+    pa_memchunk_reset(&s->memchunk);
+    s->length = length;
+    s->proplist = pa_proplist_copy(p);
+    pa_proplist_update(s->proplist, PA_UPDATE_MERGE, c->client->proplist);
+
+    pa_idxset_put(c->output_streams, s, &s->index);
+
+    return s;
+}
+
+/* Called from main context */
+static void record_stream_unlink(record_stream *s) {
+    pa_assert(s);
+
+    if (!s->connection)
+        return;
+
+    if (s->source_output) {
+        pa_source_output_unlink(s->source_output);
+        pa_source_output_unref(s->source_output);
+        s->source_output = NULL;
+    }
+
+    pa_assert_se(pa_idxset_remove_by_data(s->connection->record_streams, s, NULL) == s);
+    s->connection = NULL;
+    record_stream_unref(s);
+}
+
+/* Called from main context */
+static void record_stream_free(pa_object *o) {
+    record_stream *s = RECORD_STREAM(o);
+    pa_assert(s);
+
+    record_stream_unlink(s);
+
+    pa_memblockq_free(s->memblockq);
+    pa_xfree(s);
+}
+
+/* Called from main context */
+static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    record_stream *s = RECORD_STREAM(o);
+    record_stream_assert_ref(s);
+
+    if (!s->connection)
+        return -1;
+
+    switch (code) {
+
+        case RECORD_STREAM_MESSAGE_POST_DATA:
+
+            /* We try to keep up to date with how many bytes are
+             * currently on the fly */
+            pa_atomic_sub(&s->on_the_fly, chunk->length);
+
+            if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
+/*                 pa_log_warn("Failed to push data into output queue."); */
+                return -1;
+            }
+
+            if (!pa_pstream_is_pending(s->connection->pstream))
+                native_connection_send_memblock(s->connection);
+
+            break;
+    }
+
+    return 0;
+}
+
+/* Called from main context */
+static void fix_record_buffer_attr_pre(record_stream *s) {
+
+    size_t frame_size;
+    pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec;
+
+    pa_assert(s);
+
+    /* This function will be called from the main thread, before as
+     * well as after the source output has been activated using
+     * pa_source_output_put()! That means it may not touch any
+     * ->thread_info data! */
+
+    frame_size = pa_frame_size(&s->source_output->sample_spec);
+    s->buffer_attr = s->buffer_attr_req;
+
+    if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
+        s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
+    if (s->buffer_attr.maxlength <= 0)
+        s->buffer_attr.maxlength = (uint32_t) frame_size;
+
+    if (s->buffer_attr.fragsize == (uint32_t) -1)
+        s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
+    if (s->buffer_attr.fragsize <= 0)
+        s->buffer_attr.fragsize = (uint32_t) frame_size;
+
+    orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(s->buffer_attr.fragsize, &s->source_output->sample_spec);
+
+    if (s->early_requests) {
+
+        /* In early request mode we need to emulate the classic
+         * fragment-based playback model. Unfortunately we have no
+         * mechanism to tell the source how often we want it to send us
+         * data. The next best thing we can do is to set the source's
+         * total buffer (i.e. its latency) to the fragment size. That
+         * way it will have to send data at least that often. */
+
+        source_usec = fragsize_usec;
+
+    } else if (s->adjust_latency) {
+
+        /* So, the user asked us to adjust the latency according to
+         * what the source can provide. We set the source to whatever
+         * latency it can provide that is closest to what we want, and
+         * let the client buffer be equally large. This does NOT mean
+         * that we are doing (2 * fragsize) bytes of buffering, since
+         * the client-side buffer is only data that is on the way to
+         * the client. */
+
+        source_usec = fragsize_usec;
+
+    } else {
+
+        /* Ok, the user didn't ask us to adjust the latency, hence we
+         * don't */
+
+        source_usec = (pa_usec_t) -1;
+    }
+
+    if (source_usec != (pa_usec_t) -1)
+        s->configured_source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
+    else
+        s->configured_source_latency = 0;
+
+    if (s->early_requests) {
+
+        /* Ok, we didn't necessarily get what we were asking for. We
+         * might still get the proper fragment interval, we just can't
+         * guarantee it. */
+
+        if (fragsize_usec != s->configured_source_latency)
+            pa_log_debug("Could not configure a sufficiently low latency. Early requests might not be satisifed.");
+
+    } else if (s->adjust_latency) {
+
+        /* We keep the client buffer large enough to transfer one
+         * hardware-buffer-sized chunk at a time to the client. */
+
+        fragsize_usec = s->configured_source_latency;
+    }
+
+    if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) !=
+        pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec))
+
+        s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
+
+    if (s->buffer_attr.fragsize <= 0)
+        s->buffer_attr.fragsize = (uint32_t) frame_size;
+}
+
+/* Called from main context */
+static void fix_record_buffer_attr_post(record_stream *s) {
+    size_t base;
+
+    pa_assert(s);
+
+    /* This function will be called from the main thread, before as
+     * well as after the source output has been activated using
+     * pa_source_output_put()! That means it may not touch and
+     * ->thread_info data! */
+
+    base = pa_frame_size(&s->source_output->sample_spec);
+
+    s->buffer_attr.fragsize = (s->buffer_attr.fragsize/base)*base;
+    if (s->buffer_attr.fragsize <= 0)
+        s->buffer_attr.fragsize = base;
+
+    if (s->buffer_attr.fragsize > s->buffer_attr.maxlength)
+        s->buffer_attr.fragsize = s->buffer_attr.maxlength;
+}
+
+/* Called from main context */
+static record_stream* record_stream_new(
+        pa_native_connection *c,
+        pa_source *source,
+        pa_sample_spec *ss,
+        pa_channel_map *map,
+        pa_idxset *formats,
+        pa_buffer_attr *attr,
+        pa_cvolume *volume,
+        bool muted,
+        bool muted_set,
+        pa_source_output_flags_t flags,
+        pa_proplist *p,
+        bool adjust_latency,
+        bool early_requests,
+        bool relative_volume,
+        bool peak_detect,
+        pa_sink_input *direct_on_input,
+        int *ret) {
+
+    record_stream *s;
+    pa_source_output *source_output = NULL;
+    pa_source_output_new_data data;
+    char *memblockq_name;
+
+    pa_assert(c);
+    pa_assert(ss);
+    pa_assert(p);
+    pa_assert(ret);
+
+    pa_source_output_new_data_init(&data);
+
+    pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+    data.driver = __FILE__;
+    data.module = c->options->module;
+    data.client = c->client;
+    if (source)
+        pa_source_output_new_data_set_source(&data, source, false);
+    if (pa_sample_spec_valid(ss))
+        pa_source_output_new_data_set_sample_spec(&data, ss);
+    if (pa_channel_map_valid(map))
+        pa_source_output_new_data_set_channel_map(&data, map);
+    if (formats)
+        pa_source_output_new_data_set_formats(&data, formats);
+    data.direct_on_input = direct_on_input;
+    if (volume) {
+        pa_source_output_new_data_set_volume(&data, volume);
+        data.volume_is_absolute = !relative_volume;
+        data.save_volume = false;
+    }
+    if (muted_set) {
+        pa_source_output_new_data_set_muted(&data, muted);
+        data.save_muted = false;
+    }
+    if (peak_detect)
+        data.resample_method = PA_RESAMPLER_PEAKS;
+    data.flags = flags;
+
+    *ret = -pa_source_output_new(&source_output, c->protocol->core, &data);
+
+    pa_source_output_new_data_done(&data);
+
+    if (!source_output)
+        return NULL;
+
+    s = pa_msgobject_new(record_stream);
+    s->parent.parent.free = record_stream_free;
+    s->parent.process_msg = record_stream_process_msg;
+    s->connection = c;
+    s->source_output = source_output;
+    s->buffer_attr_req = *attr;
+    s->adjust_latency = adjust_latency;
+    s->early_requests = early_requests;
+    pa_atomic_store(&s->on_the_fly, 0);
+
+    s->source_output->parent.process_msg = source_output_process_msg;
+    s->source_output->push = source_output_push_cb;
+    s->source_output->kill = source_output_kill_cb;
+    s->source_output->get_latency = source_output_get_latency_cb;
+    s->source_output->moving = source_output_moving_cb;
+    s->source_output->suspend = source_output_suspend_cb;
+    s->source_output->send_event = source_output_send_event_cb;
+    s->source_output->userdata = s;
+
+    fix_record_buffer_attr_pre(s);
+
+    memblockq_name = pa_sprintf_malloc("native protocol record stream memblockq [%u]", s->source_output->index);
+    s->memblockq = pa_memblockq_new(
+            memblockq_name,
+            0,
+            s->buffer_attr.maxlength,
+            0,
+            &source_output->sample_spec,
+            1,
+            0,
+            0,
+            NULL);
+    pa_xfree(memblockq_name);
+
+    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+    fix_record_buffer_attr_post(s);
+
+    *ss = s->source_output->sample_spec;
+    *map = s->source_output->channel_map;
+
+    pa_idxset_put(c->record_streams, s, &s->index);
+
+    pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms",
+                ((double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) + (double) s->configured_source_latency) / PA_USEC_PER_MSEC,
+                (double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) / PA_USEC_PER_MSEC,
+                (double) s->configured_source_latency / PA_USEC_PER_MSEC);
+
+    pa_source_output_put(s->source_output);
+    return s;
+}
+
+/* Called from main context */
+static void record_stream_send_killed(record_stream *r) {
+    pa_tagstruct *t;
+    record_stream_assert_ref(r);
+
+    t = pa_tagstruct_new();
+    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);
+}
+
+/* Called from main context */
+static void playback_stream_unlink(playback_stream *s) {
+    pa_assert(s);
+
+    if (!s->connection)
+        return;
+
+    if (s->sink_input) {
+        pa_sink_input_unlink(s->sink_input);
+        pa_sink_input_unref(s->sink_input);
+        s->sink_input = NULL;
+    }
+
+    if (s->drain_request)
+        pa_pstream_send_error(s->connection->pstream, s->drain_tag, PA_ERR_NOENTITY);
+
+    pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
+    s->connection = NULL;
+    playback_stream_unref(s);
+}
+
+/* Called from main context */
+static void playback_stream_free(pa_object* o) {
+    playback_stream *s = PLAYBACK_STREAM(o);
+    pa_assert(s);
+
+    playback_stream_unlink(s);
+
+    pa_memblockq_free(s->memblockq);
+    pa_xfree(s);
+}
+
+/* Called from main context */
+static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    playback_stream *s = PLAYBACK_STREAM(o);
+    playback_stream_assert_ref(s);
+
+    if (!s->connection)
+        return -1;
+
+    switch (code) {
+
+        case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
+            pa_tagstruct *t;
+            int l = 0;
+
+            for (;;) {
+                if ((l = pa_atomic_load(&s->missing)) <= 0)
+                    return 0;
+
+                if (pa_atomic_cmpxchg(&s->missing, l, 0))
+                    break;
+            }
+
+            t = pa_tagstruct_new();
+            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, (uint32_t) l);
+            pa_pstream_send_tagstruct(s->connection->pstream, t);
+
+#ifdef PROTOCOL_NATIVE_DEBUG
+            pa_log("Requesting %lu bytes", (unsigned long) l);
+#endif
+            break;
+        }
+
+        case PLAYBACK_STREAM_MESSAGE_UNDERFLOW: {
+            pa_tagstruct *t;
+
+#ifdef PROTOCOL_NATIVE_DEBUG
+            pa_log("signalling underflow");
+#endif
+
+            /* Report that we're empty */
+            t = pa_tagstruct_new();
+            pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW);
+            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+            pa_tagstruct_putu32(t, s->index);
+            if (s->connection->version >= 23)
+                pa_tagstruct_puts64(t, offset);
+            pa_pstream_send_tagstruct(s->connection->pstream, t);
+            break;
+        }
+
+        case PLAYBACK_STREAM_MESSAGE_OVERFLOW: {
+            pa_tagstruct *t;
+
+            /* Notify the user we're overflowed*/
+            t = pa_tagstruct_new();
+            pa_tagstruct_putu32(t, PA_COMMAND_OVERFLOW);
+            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+            pa_tagstruct_putu32(t, s->index);
+            pa_pstream_send_tagstruct(s->connection->pstream, t);
+            break;
+        }
+
+        case PLAYBACK_STREAM_MESSAGE_STARTED:
+
+            if (s->connection->version >= 13) {
+                pa_tagstruct *t;
+
+                /* Notify the user we started playback */
+                t = pa_tagstruct_new();
+                pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
+                pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+                pa_tagstruct_putu32(t, s->index);
+                pa_pstream_send_tagstruct(s->connection->pstream, t);
+            }
+
+            break;
+
+        case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
+            pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
+            break;
+
+        case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH:
+
+            s->buffer_attr.tlength = (uint32_t) offset;
+
+            if (s->connection->version >= 15) {
+                pa_tagstruct *t;
+
+                t = pa_tagstruct_new();
+                pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED);
+                pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+                pa_tagstruct_putu32(t, s->index);
+                pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+                pa_tagstruct_putu32(t, s->buffer_attr.tlength);
+                pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
+                pa_tagstruct_putu32(t, s->buffer_attr.minreq);
+                pa_tagstruct_put_usec(t, s->configured_sink_latency);
+                pa_pstream_send_tagstruct(s->connection->pstream, t);
+            }
+
+            break;
+    }
+
+    return 0;
+}
+
+/* Called from main context */
+static void fix_playback_buffer_attr(playback_stream *s) {
+    size_t frame_size, max_prebuf;
+    pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;
+
+    pa_assert(s);
+
+#ifdef PROTOCOL_NATIVE_DEBUG
+    pa_log("Client requested: maxlength=%li bytes tlength=%li bytes minreq=%li bytes prebuf=%li bytes",
+           (long) s->buffer_attr_req.maxlength,
+           (long) s->buffer_attr_req.tlength,
+           (long) s->buffer_attr_req.minreq,
+           (long) s->buffer_attr_req.prebuf);
+
+    pa_log("Client requested: maxlength=%lu ms tlength=%lu ms minreq=%lu ms prebuf=%lu ms",
+           (unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.maxlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
+           (unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.tlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
+           (unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.minreq, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
+           (unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.prebuf, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC));
+#endif
+
+    /* This function will be called from the main thread, before as
+     * well as after the sink input has been activated using
+     * pa_sink_input_put()! That means it may not touch any
+     * ->thread_info data, such as the memblockq! */
+
+    frame_size = pa_frame_size(&s->sink_input->sample_spec);
+    s->buffer_attr = s->buffer_attr_req;
+
+    if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
+        s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
+    if (s->buffer_attr.maxlength <= 0)
+        s->buffer_attr.maxlength = (uint32_t) frame_size;
+
+    if (s->buffer_attr.tlength == (uint32_t) -1)
+        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+    if (s->buffer_attr.tlength <= 0)
+        s->buffer_attr.tlength = (uint32_t) frame_size;
+    if (s->buffer_attr.tlength > s->buffer_attr.maxlength)
+        s->buffer_attr.tlength = s->buffer_attr.maxlength;
+
+    if (s->buffer_attr.minreq == (uint32_t) -1) {
+        uint32_t process = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+        /* With low-latency, tlength/4 gives a decent default in all of traditional, adjust latency and early request modes. */
+        uint32_t m = s->buffer_attr.tlength / 4;
+        if (frame_size)
+            m -= m % frame_size;
+        s->buffer_attr.minreq = PA_MIN(process, m);
+    }
+    if (s->buffer_attr.minreq <= 0)
+        s->buffer_attr.minreq = (uint32_t) frame_size;
+
+    if (s->buffer_attr.tlength < s->buffer_attr.minreq+frame_size)
+        s->buffer_attr.tlength = s->buffer_attr.minreq+(uint32_t) frame_size;
+
+    orig_tlength_usec = tlength_usec = pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec);
+    orig_minreq_usec = minreq_usec = pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec);
+
+    pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
+                (double) tlength_usec / PA_USEC_PER_MSEC,
+                (double) minreq_usec / PA_USEC_PER_MSEC);
+
+    if (s->early_requests) {
+
+        /* In early request mode we need to emulate the classic
+         * fragment-based playback model. Unfortunately we have no
+         * mechanism to tell the sink how often we want to be queried
+         * for data. The next best thing we can do is to set the sink's
+         * total buffer (i.e. its latency) to the fragment size. That
+         * way it will have to query us at least that often. */
+
+        sink_usec = minreq_usec;
+        pa_log_debug("Early requests mode enabled, configuring sink latency to minreq.");
+
+    } else if (s->adjust_latency) {
+
+        /* So, the user asked us to adjust the latency of the stream
+         * buffer according to the what the sink can provide. The
+         * tlength passed in shall be the overall latency. Roughly
+         * half the latency will be spent on the hw buffer, the other
+         * half of it in the async buffer queue we maintain for each
+         * client. In between we'll have a safety space of size
+         * 2*minreq. Why the 2*minreq? When the hw buffer is completely
+         * empty and needs to be filled, then our buffer must have
+         * enough data to fulfill this request immediately and thus
+         * have at least the same tlength as the size of the hw
+         * buffer. It additionally needs space for 2 times minreq
+         * because if the buffer ran empty and a partial fillup
+         * happens immediately on the next iteration we need to be
+         * able to fulfill it and give the application also minreq
+         * time to fill it up again for the next request Makes 2 times
+         * minreq in plus.. */
+
+        if (tlength_usec > minreq_usec*2)
+            sink_usec = (tlength_usec - minreq_usec*2)/2;
+        else
+            sink_usec = 0;
+
+        pa_log_debug("Adjust latency mode enabled, configuring sink latency to half of overall latency.");
+
+    } else {
+
+        /* Ok, the user didn't ask us to adjust the latency, but we
+         * still need to make sure that the parameters from the user
+         * do make sense. */
+
+        if (tlength_usec > minreq_usec*2)
+            sink_usec = (tlength_usec - minreq_usec*2);
+        else
+            sink_usec = 0;
+
+        pa_log_debug("Traditional mode enabled, modifying sink usec only for compat with minreq.");
+    }
+
+    s->configured_sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
+
+    if (s->early_requests) {
+
+        /* Ok, we didn't necessarily get what we were asking for. We
+         * might still get the proper fragment interval, we just can't
+         * guarantee it. */
+
+        if (minreq_usec != s->configured_sink_latency)
+            pa_log_debug("Could not configure a sufficiently low latency. Early requests might not be satisifed.");
+
+    } else if (s->adjust_latency) {
+
+        /* Ok, we didn't necessarily get what we were asking for, so
+         * let's subtract from what we asked for for the remaining
+         * buffer space */
+
+        if (tlength_usec >= s->configured_sink_latency)
+            tlength_usec -= s->configured_sink_latency;
+    }
+
+    pa_log_debug("Requested latency=%0.2f ms, Received latency=%0.2f ms",
+                 (double) sink_usec / PA_USEC_PER_MSEC,
+                 (double) s->configured_sink_latency / PA_USEC_PER_MSEC);
+
+    /* FIXME: This is actually larger than necessary, since not all of
+     * the sink latency is actually rewritable. */
+    if (tlength_usec < s->configured_sink_latency + 2*minreq_usec)
+        tlength_usec = s->configured_sink_latency + 2*minreq_usec;
+
+    if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) !=
+        pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec))
+        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec);
+
+    if (pa_usec_to_bytes(orig_minreq_usec, &s->sink_input->sample_spec) !=
+        pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec))
+        s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
+
+    if (s->buffer_attr.minreq <= 0) {
+        s->buffer_attr.minreq = (uint32_t) frame_size;
+        s->buffer_attr.tlength += (uint32_t) frame_size*2;
+    }
+
+    if (s->buffer_attr.tlength <= s->buffer_attr.minreq)
+        s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size;
+
+    max_prebuf = s->buffer_attr.tlength + (uint32_t)frame_size - s->buffer_attr.minreq;
+
+    if (s->buffer_attr.prebuf == (uint32_t) -1 ||
+        s->buffer_attr.prebuf > max_prebuf)
+        s->buffer_attr.prebuf = max_prebuf;
+
+#ifdef PROTOCOL_NATIVE_DEBUG
+    pa_log("Client accepted: maxlength=%lu ms tlength=%lu ms minreq=%lu ms prebuf=%lu ms",
+           (unsigned long) (pa_bytes_to_usec(s->buffer_attr.maxlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
+           (unsigned long) (pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
+           (unsigned long) (pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
+           (unsigned long) (pa_bytes_to_usec(s->buffer_attr.prebuf, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC));
+#endif
+}
+
+/* Called from main context */
+static playback_stream* playback_stream_new(
+        pa_native_connection *c,
+        pa_sink *sink,
+        pa_sample_spec *ss,
+        pa_channel_map *map,
+        pa_idxset *formats,
+        pa_buffer_attr *a,
+        pa_cvolume *volume,
+        bool muted,
+        bool muted_set,
+        pa_sink_input_flags_t flags,
+        pa_proplist *p,
+        bool adjust_latency,
+        bool early_requests,
+        bool relative_volume,
+        uint32_t syncid,
+        uint32_t *missing,
+        int *ret) {
+
+    /* Note: This function takes ownership of the 'formats' param, so we need
+     * to take extra care to not leak it */
+
+    playback_stream *ssync;
+    playback_stream *s = NULL;
+    pa_sink_input *sink_input = NULL;
+    pa_memchunk silence;
+    uint32_t idx;
+    int64_t start_index;
+    pa_sink_input_new_data data;
+    char *memblockq_name;
+
+    pa_assert(c);
+    pa_assert(ss);
+    pa_assert(missing);
+    pa_assert(p);
+    pa_assert(ret);
+
+    /* Find syncid group */
+    PA_IDXSET_FOREACH(ssync, c->output_streams, idx) {
+
+        if (!playback_stream_isinstance(ssync))
+            continue;
+
+        if (ssync->syncid == syncid)
+            break;
+    }
+
+    /* Synced streams must connect to the same sink */
+    if (ssync) {
+
+        if (!sink)
+            sink = ssync->sink_input->sink;
+        else if (sink != ssync->sink_input->sink) {
+            *ret = PA_ERR_INVALID;
+            goto out;
+        }
+    }
+
+    pa_sink_input_new_data_init(&data);
+
+    pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+    data.driver = __FILE__;
+    data.module = c->options->module;
+    data.client = c->client;
+    if (sink)
+        pa_sink_input_new_data_set_sink(&data, sink, false);
+    if (pa_sample_spec_valid(ss))
+        pa_sink_input_new_data_set_sample_spec(&data, ss);
+    if (pa_channel_map_valid(map))
+        pa_sink_input_new_data_set_channel_map(&data, map);
+    if (formats) {
+        pa_sink_input_new_data_set_formats(&data, formats);
+        /* Ownership transferred to new_data, so we don't free it ourselves */
+        formats = NULL;
+    }
+    if (volume) {
+        pa_sink_input_new_data_set_volume(&data, volume);
+        data.volume_is_absolute = !relative_volume;
+        data.save_volume = false;
+    }
+    if (muted_set) {
+        pa_sink_input_new_data_set_muted(&data, muted);
+        data.save_muted = false;
+    }
+    data.sync_base = ssync ? ssync->sink_input : NULL;
+    data.flags = flags;
+
+    *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data);
+
+    pa_sink_input_new_data_done(&data);
+
+    if (!sink_input)
+        goto out;
+
+    s = pa_msgobject_new(playback_stream);
+    s->parent.parent.parent.free = playback_stream_free;
+    s->parent.parent.process_msg = playback_stream_process_msg;
+    s->connection = c;
+    s->syncid = syncid;
+    s->sink_input = sink_input;
+    s->is_underrun = true;
+    s->drain_request = false;
+    pa_atomic_store(&s->missing, 0);
+    s->buffer_attr_req = *a;
+    s->adjust_latency = adjust_latency;
+    s->early_requests = early_requests;
+    pa_atomic_store(&s->seek_or_post_in_queue, 0);
+    s->seek_windex = -1;
+
+    s->sink_input->parent.process_msg = sink_input_process_msg;
+    s->sink_input->pop = sink_input_pop_cb;
+    s->sink_input->process_underrun = sink_input_process_underrun_cb;
+    s->sink_input->process_rewind = sink_input_process_rewind_cb;
+    s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    s->sink_input->update_max_request = sink_input_update_max_request_cb;
+    s->sink_input->kill = sink_input_kill_cb;
+    s->sink_input->moving = sink_input_moving_cb;
+    s->sink_input->suspend = sink_input_suspend_cb;
+    s->sink_input->send_event = sink_input_send_event_cb;
+    s->sink_input->userdata = s;
+
+    start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
+
+    fix_playback_buffer_attr(s);
+
+    pa_sink_input_get_silence(sink_input, &silence);
+    memblockq_name = pa_sprintf_malloc("native protocol playback stream memblockq [%u]", s->sink_input->index);
+    s->memblockq = pa_memblockq_new(
+            memblockq_name,
+            start_index,
+            s->buffer_attr.maxlength,
+            s->buffer_attr.tlength,
+            &sink_input->sample_spec,
+            s->buffer_attr.prebuf,
+            s->buffer_attr.minreq,
+            0,
+            &silence);
+    pa_xfree(memblockq_name);
+    pa_memblock_unref(silence.memblock);
+
+    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+
+    *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
+
+#ifdef PROTOCOL_NATIVE_DEBUG
+    pa_log("missing original: %li", (long int) *missing);
+#endif
+
+    *ss = s->sink_input->sample_spec;
+    *map = s->sink_input->channel_map;
+
+    pa_idxset_put(c->output_streams, s, &s->index);
+
+    pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms",
+                ((double) pa_bytes_to_usec(s->buffer_attr.tlength, &sink_input->sample_spec) + (double) s->configured_sink_latency) / PA_USEC_PER_MSEC,
+                (double) pa_bytes_to_usec(s->buffer_attr.tlength-s->buffer_attr.minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+                (double) pa_bytes_to_usec(s->buffer_attr.minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+                (double) s->configured_sink_latency / PA_USEC_PER_MSEC);
+
+    pa_sink_input_put(s->sink_input);
+
+out:
+    if (formats)
+        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+
+    return s;
+}
+
+/* Called from IO context */
+static void playback_stream_request_bytes(playback_stream *s) {
+    size_t m;
+
+    playback_stream_assert_ref(s);
+
+    m = pa_memblockq_pop_missing(s->memblockq);
+
+    /* pa_log("request_bytes(%lu) (tlength=%lu minreq=%lu length=%lu really missing=%lli)", */
+    /*        (unsigned long) m, */
+    /*        pa_memblockq_get_tlength(s->memblockq), */
+    /*        pa_memblockq_get_minreq(s->memblockq), */
+    /*        pa_memblockq_get_length(s->memblockq), */
+    /*        (long long) pa_memblockq_get_tlength(s->memblockq) - (long long) pa_memblockq_get_length(s->memblockq)); */
+
+    if (m <= 0)
+        return;
+
+#ifdef PROTOCOL_NATIVE_DEBUG
+    pa_log("request_bytes(%lu)", (unsigned long) m);
+#endif
+
+    if (pa_atomic_add(&s->missing, (int) m) <= 0)
+        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
+}
+
+/* Called from main context */
+static void playback_stream_send_killed(playback_stream *p) {
+    pa_tagstruct *t;
+    playback_stream_assert_ref(p);
+
+    t = pa_tagstruct_new();
+    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);
+}
+
+/* Called from main context */
+static int native_connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(o);
+    pa_native_connection_assert_ref(c);
+
+    if (!c->protocol)
+        return -1;
+
+    switch (code) {
+
+        case CONNECTION_MESSAGE_REVOKE:
+            pa_pstream_send_revoke(c->pstream, PA_PTR_TO_UINT(userdata));
+            break;
+
+        case CONNECTION_MESSAGE_RELEASE:
+            pa_pstream_send_release(c->pstream, PA_PTR_TO_UINT(userdata));
+            break;
+    }
+
+    return 0;
+}
+
+/* Called from main context */
+static void native_connection_unlink(pa_native_connection *c) {
+    record_stream *r;
+    output_stream *o;
+
+    pa_assert(c);
+
+    if (!c->protocol)
+        return;
+
+    pa_hook_fire(&c->protocol->hooks[PA_NATIVE_HOOK_CONNECTION_UNLINK], c);
+
+    if (c->options)
+        pa_native_options_unref(c->options);
+
+    if (c->srbpending)
+        pa_srbchannel_free(c->srbpending);
+
+    while ((r = pa_idxset_first(c->record_streams, NULL)))
+        record_stream_unlink(r);
+
+    while ((o = pa_idxset_first(c->output_streams, NULL)))
+        if (playback_stream_isinstance(o))
+            playback_stream_unlink(PLAYBACK_STREAM(o));
+        else
+            upload_stream_unlink(UPLOAD_STREAM(o));
+
+    if (c->subscription)
+        pa_subscription_free(c->subscription);
+
+    if (c->pstream)
+        pa_pstream_unlink(c->pstream);
+
+    if (c->auth_timeout_event) {
+        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+        c->auth_timeout_event = NULL;
+    }
+
+    pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+    c->protocol = NULL;
+    pa_native_connection_unref(c);
+}
+
+/* Called from main context */
+static void native_connection_free(pa_object *o) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(o);
+
+    pa_assert(c);
+
+    native_connection_unlink(c);
+
+    pa_idxset_free(c->record_streams, NULL);
+    pa_idxset_free(c->output_streams, NULL);
+
+    pa_pdispatch_unref(c->pdispatch);
+    pa_pstream_unref(c->pstream);
+    if (c->rw_mempool)
+        pa_mempool_unref(c->rw_mempool);
+
+    pa_client_free(c->client);
+
+    pa_xfree(c);
+}
+
+/* Called from main context */
+static void native_connection_send_memblock(pa_native_connection *c) {
+    uint32_t start;
+    record_stream *r;
+
+    start = PA_IDXSET_INVALID;
+    for (;;) {
+        pa_memchunk chunk;
+
+        if (!(r = RECORD_STREAM(pa_idxset_rrobin(c->record_streams, &c->rrobin_index))))
+            return;
+
+        if (start == PA_IDXSET_INVALID)
+            start = c->rrobin_index;
+        else if (start == c->rrobin_index)
+            return;
+
+        if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) {
+            pa_memchunk schunk = chunk;
+
+            if (schunk.length > r->buffer_attr.fragsize)
+                schunk.length = r->buffer_attr.fragsize;
+
+            pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk);
+
+            pa_memblockq_drop(r->memblockq, schunk.length);
+            pa_memblock_unref(schunk.memblock);
+
+            return;
+        }
+    }
+}
+
+/*** sink input callbacks ***/
+
+/* Called from thread context */
+static void handle_seek(playback_stream *s, int64_t indexw) {
+    playback_stream_assert_ref(s);
+
+/*     pa_log("handle_seek: %llu -- %i", (unsigned long long) s->sink_input->thread_info.underrun_for, pa_memblockq_is_readable(s->memblockq)); */
+
+    if (s->sink_input->thread_info.underrun_for > 0) {
+
+/*         pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */
+
+        if (pa_memblockq_is_readable(s->memblockq)) {
+
+            /* We just ended an underrun, let's ask the sink
+             * for a complete rewind rewrite */
+
+            pa_log_debug("Requesting rewind due to end of underrun.");
+            pa_sink_input_request_rewind(s->sink_input,
+                                         (size_t) (s->sink_input->thread_info.underrun_for == (uint64_t) -1 ? 0 :
+                                                   s->sink_input->thread_info.underrun_for),
+                                         false, true, false);
+        }
+
+    } else {
+        int64_t indexr;
+
+        indexr = pa_memblockq_get_read_index(s->memblockq);
+
+        if (indexw < indexr) {
+            /* OK, the sink already asked for this data, so
+             * let's have it ask us again */
+
+            pa_log_debug("Requesting rewind due to rewrite.");
+            pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), true, false, false);
+        }
+    }
+
+    playback_stream_request_bytes(s);
+}
+
+static void flush_write_no_account(pa_memblockq *q) {
+    pa_memblockq_flush_write(q, false);
+}
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_sink_input *i = PA_SINK_INPUT(o);
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    switch (code) {
+
+        case SINK_INPUT_MESSAGE_SEEK:
+        case SINK_INPUT_MESSAGE_POST_DATA: {
+            int64_t windex = pa_memblockq_get_write_index(s->memblockq);
+
+            if (code == SINK_INPUT_MESSAGE_SEEK) {
+                /* The client side is incapable of accounting correctly
+                 * for seeks of a type != PA_SEEK_RELATIVE. We need to be
+                 * able to deal with that. */
+
+                pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata), PA_PTR_TO_UINT(userdata) == PA_SEEK_RELATIVE);
+                windex = PA_MIN(windex, pa_memblockq_get_write_index(s->memblockq));
+            }
+
+            if (chunk && pa_memblockq_push_align(s->memblockq, chunk) < 0) {
+                if (pa_log_ratelimit(PA_LOG_WARN))
+                    pa_log_warn("Failed to push data into queue");
+                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
+                pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, true);
+            }
+
+            /* If more data is in queue, we rewind later instead. */
+            if (s->seek_windex != -1)
+                windex = PA_MIN(windex, s->seek_windex);
+            if (pa_atomic_dec(&s->seek_or_post_in_queue) > 1)
+                s->seek_windex = windex;
+            else {
+                s->seek_windex = -1;
+                handle_seek(s, windex);
+            }
+            return 0;
+        }
+
+        case SINK_INPUT_MESSAGE_DRAIN:
+        case SINK_INPUT_MESSAGE_FLUSH:
+        case SINK_INPUT_MESSAGE_PREBUF_FORCE:
+        case SINK_INPUT_MESSAGE_TRIGGER: {
+
+            int64_t windex;
+            pa_sink_input *isync;
+            void (*func)(pa_memblockq *bq);
+
+            switch (code) {
+                case SINK_INPUT_MESSAGE_FLUSH:
+                    func = flush_write_no_account;
+                    break;
+
+                case SINK_INPUT_MESSAGE_PREBUF_FORCE:
+                    func = pa_memblockq_prebuf_force;
+                    break;
+
+                case SINK_INPUT_MESSAGE_DRAIN:
+                case SINK_INPUT_MESSAGE_TRIGGER:
+                    func = pa_memblockq_prebuf_disable;
+                    break;
+
+                default:
+                    pa_assert_not_reached();
+            }
+
+            windex = pa_memblockq_get_write_index(s->memblockq);
+            func(s->memblockq);
+            handle_seek(s, windex);
+
+            /* Do the same for all other members in the sync group */
+            for (isync = i->sync_prev; isync; isync = isync->sync_prev) {
+                playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+                windex = pa_memblockq_get_write_index(ssync->memblockq);
+                func(ssync->memblockq);
+                handle_seek(ssync, windex);
+            }
+
+            for (isync = i->sync_next; isync; isync = isync->sync_next) {
+                playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+                windex = pa_memblockq_get_write_index(ssync->memblockq);
+                func(ssync->memblockq);
+                handle_seek(ssync, windex);
+            }
+
+            if (code == SINK_INPUT_MESSAGE_DRAIN) {
+                if (!pa_memblockq_is_readable(s->memblockq))
+                    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL);
+                else {
+                    s->drain_tag = PA_PTR_TO_UINT(userdata);
+                    s->drain_request = true;
+                }
+            }
+
+            return 0;
+        }
+
+        case SINK_INPUT_MESSAGE_UPDATE_LATENCY:
+            /* Atomically get a snapshot of all timing parameters... */
+            s->read_index = pa_memblockq_get_read_index(s->memblockq);
+            s->write_index = pa_memblockq_get_write_index(s->memblockq);
+            s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
+            s->current_sink_latency = pa_sink_get_latency_within_thread(s->sink_input->sink, false);
+            s->underrun_for = s->sink_input->thread_info.underrun_for;
+            s->playing_for = s->sink_input->thread_info.playing_for;
+
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_SET_STATE: {
+            int64_t windex;
+
+            windex = pa_memblockq_get_write_index(s->memblockq);
+
+            /* We enable prebuffering so that after CORKED -> RUNNING
+             * transitions we don't have trouble with underruns in case the
+             * buffer has too little data. This must not be done when draining
+             * has been requested, however, otherwise the buffered audio would
+             * never play. */
+            if (!s->drain_request)
+                pa_memblockq_prebuf_force(s->memblockq);
+
+            handle_seek(s, windex);
+
+            /* Fall through to the default handler */
+            break;
+        }
+
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            *r = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &i->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+            break;
+        }
+
+        case SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR: {
+            pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
+            pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+            return 0;
+        }
+    }
+
+    return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
+}
+
+static bool handle_input_underrun(playback_stream *s, bool force) {
+    bool send_drain;
+
+    if (pa_memblockq_is_readable(s->memblockq))
+        return false;
+
+    if (!s->is_underrun)
+        pa_log_debug("%s %s of '%s'", force ? "Actual" : "Implicit",
+            s->drain_request ? "drain" : "underrun", pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME)));
+
+    send_drain = s->drain_request && (force || pa_sink_input_safe_to_remove(s->sink_input));
+
+    if (send_drain) {
+         s->drain_request = false;
+         pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
+         pa_log_debug("Drain acknowledged of '%s'", pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME)));
+    } else if (!s->is_underrun) {
+         pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, pa_memblockq_get_read_index(s->memblockq), NULL, NULL);
+    }
+    s->is_underrun = true;
+    playback_stream_request_bytes(s);
+    return true;
+}
+
+/* Called from thread context */
+static bool sink_input_process_underrun_cb(pa_sink_input *i) {
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    return handle_input_underrun(s, true);
+}
+
+/* Called from thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+    pa_assert(chunk);
+
+#ifdef PROTOCOL_NATIVE_DEBUG
+    pa_log("%s, pop(): %lu", pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME), (unsigned long) pa_memblockq_get_length(s->memblockq));
+#endif
+
+    if (!handle_input_underrun(s, false))
+        s->is_underrun = false;
+
+    /* This call will not fail with prebuf=0, hence we check for
+       underrun explicitly in handle_input_underrun */
+    if (pa_memblockq_peek(s->memblockq, chunk) < 0)
+        return -1;
+
+    chunk->length = PA_MIN(nbytes, chunk->length);
+
+    if (i->thread_info.underrun_for > 0)
+        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL);
+
+    pa_memblockq_drop(s->memblockq, chunk->length);
+    playback_stream_request_bytes(s);
+
+    return 0;
+}
+
+/* Called from thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    /* If we are in an underrun, then we don't rewind */
+    if (i->thread_info.underrun_for > 0)
+        return;
+
+    pa_memblockq_rewind(s->memblockq, nbytes);
+}
+
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    pa_memblockq_set_maxrewind(s->memblockq, nbytes);
+}
+
+/* Called from thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    playback_stream *s;
+    size_t new_tlength, old_tlength;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    old_tlength = pa_memblockq_get_tlength(s->memblockq);
+    new_tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq);
+
+    if (old_tlength < new_tlength) {
+        pa_log_debug("max_request changed, trying to update from %zu to %zu.", old_tlength, new_tlength);
+        pa_memblockq_set_tlength(s->memblockq, new_tlength);
+        new_tlength = pa_memblockq_get_tlength(s->memblockq);
+
+        if (new_tlength == old_tlength)
+            pa_log_debug("Failed to increase tlength");
+        else {
+            pa_log_debug("Notifying client about increased tlength");
+            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH, NULL, pa_memblockq_get_tlength(s->memblockq), NULL, NULL);
+        }
+    }
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    playback_stream_send_killed(s);
+    playback_stream_unlink(s);
+}
+
+/* Called from main context */
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl) {
+    playback_stream *s;
+    pa_tagstruct *t;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    if (s->connection->version < 15)
+      return;
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void sink_input_suspend_cb(pa_sink_input *i, bool suspend) {
+    playback_stream *s;
+    pa_tagstruct *t;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    if (s->connection->version < 12)
+      return;
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_SUSPENDED);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
+    playback_stream *s;
+    pa_tagstruct *t;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    if (!dest)
+        return;
+
+    fix_playback_buffer_attr(s);
+    pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
+    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+
+    if (s->connection->version < 12)
+      return;
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_putu32(t, dest->index);
+    pa_tagstruct_puts(t, dest->name);
+    pa_tagstruct_put_boolean(t, pa_sink_get_state(dest) == PA_SINK_SUSPENDED);
+
+    if (s->connection->version >= 13) {
+        pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(t, s->buffer_attr.tlength);
+        pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
+        pa_tagstruct_putu32(t, s->buffer_attr.minreq);
+        pa_tagstruct_put_usec(t, s->configured_sink_latency);
+    }
+
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/*** source_output callbacks ***/
+
+/* Called from thread context */
+static int source_output_process_msg(pa_msgobject *_o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_source_output *o = PA_SOURCE_OUTPUT(_o);
+    record_stream *s;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    switch (code) {
+        case SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY:
+            /* Atomically get a snapshot of all timing parameters... */
+            s->current_monitor_latency = o->source->monitor_of ? pa_sink_get_latency_within_thread(o->source->monitor_of, false) : 0;
+            s->current_source_latency = pa_source_get_latency_within_thread(o->source, false);
+            s->on_the_fly_snapshot = pa_atomic_load(&s->on_the_fly);
+            return 0;
+    }
+
+    return pa_source_output_process_msg(_o, code, userdata, offset, chunk);
+}
+
+/* Called from thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    record_stream *s;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+    pa_assert(chunk);
+
+    pa_atomic_add(&s->on_the_fly, chunk->length);
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+}
+
+static void source_output_kill_cb(pa_source_output *o) {
+    record_stream *s;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    record_stream_send_killed(s);
+    record_stream_unlink(s);
+}
+
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+    record_stream *s;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    /*pa_log("get_latency: %u", pa_memblockq_get_length(s->memblockq));*/
+
+    return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
+}
+
+/* Called from main context */
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl) {
+    record_stream *s;
+    pa_tagstruct *t;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    if (s->connection->version < 15)
+      return;
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void source_output_suspend_cb(pa_source_output *o, bool suspend) {
+    record_stream *s;
+    pa_tagstruct *t;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    if (s->connection->version < 12)
+      return;
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_SUSPENDED);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
+    record_stream *s;
+    pa_tagstruct *t;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    if (!dest)
+        return;
+
+    fix_record_buffer_attr_pre(s);
+    pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
+    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+    fix_record_buffer_attr_post(s);
+
+    if (s->connection->version < 12)
+      return;
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_putu32(t, dest->index);
+    pa_tagstruct_puts(t, dest->name);
+    pa_tagstruct_put_boolean(t, pa_source_get_state(dest) == PA_SOURCE_SUSPENDED);
+
+    if (s->connection->version >= 13) {
+        pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
+        pa_tagstruct_put_usec(t, s->configured_source_latency);
+    }
+
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/*** pdispatch callbacks ***/
+
+static void protocol_error(pa_native_connection *c) {
+    pa_log("protocol error, kicking client");
+    native_connection_unlink(c);
+}
+
+#define CHECK_VALIDITY(pstream, expression, tag, error) do { \
+if (!(expression)) { \
+    pa_pstream_send_error((pstream), (tag), (error)); \
+    return; \
+} \
+} while(0);
+
+#define CHECK_VALIDITY_GOTO(pstream, expression, tag, error, label) do { \
+if (!(expression)) { \
+    pa_pstream_send_error((pstream), (tag), (error)); \
+    goto label; \
+} \
+} while(0);
+
+static pa_tagstruct *reply_new(uint32_t tag) {
+    pa_tagstruct *reply;
+
+    reply = pa_tagstruct_new();
+    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+    pa_tagstruct_putu32(reply, tag);
+    return reply;
+}
+
+static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    playback_stream *s;
+    uint32_t sink_index, syncid, missing = 0;
+    pa_buffer_attr attr;
+    const char *name = NULL, *sink_name;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_tagstruct *reply;
+    pa_sink *sink = NULL;
+    pa_cvolume volume;
+    bool
+        corked = false,
+        no_remap = false,
+        no_remix = false,
+        fix_format = false,
+        fix_rate = false,
+        fix_channels = false,
+        no_move = false,
+        variable_rate = false,
+        muted = false,
+        adjust_latency = false,
+        early_requests = false,
+        dont_inhibit_auto_suspend = false,
+        volume_set = true,
+        muted_set = false,
+        fail_on_suspend = false,
+        relative_volume = false,
+        passthrough = false;
+
+    pa_sink_input_flags_t flags = 0;
+    pa_proplist *p = NULL;
+    int ret = PA_ERR_INVALID;
+    uint8_t n_formats = 0;
+    pa_format_info *format;
+    pa_idxset *formats = NULL;
+    uint32_t i;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+    memset(&attr, 0, sizeof(attr));
+
+    if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
+        pa_tagstruct_get(
+                t,
+                PA_TAG_SAMPLE_SPEC, &ss,
+                PA_TAG_CHANNEL_MAP, &map,
+                PA_TAG_U32, &sink_index,
+                PA_TAG_STRING, &sink_name,
+                PA_TAG_U32, &attr.maxlength,
+                PA_TAG_BOOLEAN, &corked,
+                PA_TAG_U32, &attr.tlength,
+                PA_TAG_U32, &attr.prebuf,
+                PA_TAG_U32, &attr.minreq,
+                PA_TAG_U32, &syncid,
+                PA_TAG_CVOLUME, &volume,
+                PA_TAG_INVALID) < 0) {
+
+        protocol_error(c);
+        goto finish;
+    }
+
+    CHECK_VALIDITY_GOTO(c->pstream, c->authorized, tag, PA_ERR_ACCESS, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID, finish);
+
+    p = pa_proplist_new();
+
+    if (name)
+        pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+
+    if (c->version >= 12) {
+        /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+        if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+            pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+            pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+            pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+
+            protocol_error(c);
+            goto finish;
+        }
+    }
+
+    if (c->version >= 13) {
+
+        if (pa_tagstruct_get_boolean(t, &muted) < 0 ||
+            pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
+            pa_tagstruct_get_proplist(t, p) < 0) {
+
+            protocol_error(c);
+            goto finish;
+        }
+    }
+
+    if (c->version >= 14) {
+
+        if (pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
+            pa_tagstruct_get_boolean(t, &early_requests) < 0) {
+
+            protocol_error(c);
+            goto finish;
+        }
+    }
+
+    if (c->version >= 15) {
+
+        if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
+            pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
+            pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
+
+            protocol_error(c);
+            goto finish;
+        }
+    }
+
+    if (c->version >= 17) {
+
+        if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) {
+
+            protocol_error(c);
+            goto finish;
+        }
+    }
+
+    if (c->version >= 18) {
+
+        if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) {
+            protocol_error(c);
+            goto finish;
+        }
+    }
+
+    if (c->version >= 21) {
+
+        if (pa_tagstruct_getu8(t, &n_formats) < 0) {
+            protocol_error(c);
+            goto finish;
+        }
+
+        if (n_formats)
+            formats = pa_idxset_new(NULL, NULL);
+
+        for (i = 0; i < n_formats; i++) {
+            format = pa_format_info_new();
+            if (pa_tagstruct_get_format_info(t, format) < 0) {
+                protocol_error(c);
+                goto finish;
+            }
+            pa_idxset_put(formats, format, NULL);
+        }
+    }
+
+    if (n_formats == 0) {
+        CHECK_VALIDITY_GOTO(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID, finish);
+        CHECK_VALIDITY_GOTO(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID, finish);
+        CHECK_VALIDITY_GOTO(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID, finish);
+    } else {
+        PA_IDXSET_FOREACH(format, formats, i) {
+            CHECK_VALIDITY_GOTO(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID, finish);
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        goto finish;
+    }
+
+    if (sink_index != PA_INVALID_INDEX) {
+
+        if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+            goto finish;
+        }
+
+    } else if (sink_name) {
+
+        if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+            goto finish;
+        }
+    }
+
+    flags =
+        (corked ? PA_SINK_INPUT_START_CORKED : 0) |
+        (no_remap ? PA_SINK_INPUT_NO_REMAP : 0) |
+        (no_remix ? PA_SINK_INPUT_NO_REMIX : 0) |
+        (fix_format ? PA_SINK_INPUT_FIX_FORMAT : 0) |
+        (fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) |
+        (fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) |
+        (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
+        (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) |
+        (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
+        (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0) |
+        (passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0);
+
+    /* Only since protocol version 15 there's a separate muted_set
+     * flag. For older versions we synthesize it here */
+    muted_set = muted_set || muted;
+
+    s = playback_stream_new(c, sink, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, flags, p, adjust_latency, early_requests, relative_volume, syncid, &missing, &ret);
+    /* We no longer own the formats idxset */
+    formats = NULL;
+
+    CHECK_VALIDITY_GOTO(c->pstream, s, tag, ret, finish);
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, s->index);
+    pa_assert(s->sink_input);
+    pa_tagstruct_putu32(reply, s->sink_input->index);
+    pa_tagstruct_putu32(reply, missing);
+
+#ifdef PROTOCOL_NATIVE_DEBUG
+    pa_log("initial request is %u", missing);
+#endif
+
+    if (c->version >= 9) {
+        /* Since 0.9.0 we support sending the buffer metrics back to the client */
+
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.tlength);
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.prebuf);
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.minreq);
+    }
+
+    if (c->version >= 12) {
+        /* Since 0.9.8 we support sending the chosen sample
+         * spec/channel map/device/suspend status back to the
+         * client */
+
+        pa_tagstruct_put_sample_spec(reply, &ss);
+        pa_tagstruct_put_channel_map(reply, &map);
+
+        pa_tagstruct_putu32(reply, s->sink_input->sink->index);
+        pa_tagstruct_puts(reply, s->sink_input->sink->name);
+
+        pa_tagstruct_put_boolean(reply, pa_sink_get_state(s->sink_input->sink) == PA_SINK_SUSPENDED);
+    }
+
+    if (c->version >= 13)
+        pa_tagstruct_put_usec(reply, s->configured_sink_latency);
+
+    if (c->version >= 21) {
+        /* Send back the format we negotiated */
+        if (s->sink_input->format)
+            pa_tagstruct_put_format_info(reply, s->sink_input->format);
+        else {
+            pa_format_info *f = pa_format_info_new();
+            pa_tagstruct_put_format_info(reply, f);
+            pa_format_info_free(f);
+        }
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+
+finish:
+    if (p)
+        pa_proplist_free(p);
+    if (formats)
+        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+}
+
+static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t channel;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    switch (command) {
+
+        case PA_COMMAND_DELETE_PLAYBACK_STREAM: {
+            playback_stream *s;
+            if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !playback_stream_isinstance(s)) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+                return;
+            }
+
+            playback_stream_unlink(s);
+            break;
+        }
+
+        case PA_COMMAND_DELETE_RECORD_STREAM: {
+            record_stream *s;
+            if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+                return;
+            }
+
+            record_stream_unlink(s);
+            break;
+        }
+
+        case PA_COMMAND_DELETE_UPLOAD_STREAM: {
+            upload_stream *s;
+
+            if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !upload_stream_isinstance(s)) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+                return;
+            }
+
+            upload_stream_unlink(s);
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    record_stream *s;
+    pa_buffer_attr attr;
+    uint32_t source_index;
+    const char *name = NULL, *source_name;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_tagstruct *reply;
+    pa_source *source = NULL;
+    pa_cvolume volume;
+    bool
+        corked = false,
+        no_remap = false,
+        no_remix = false,
+        fix_format = false,
+        fix_rate = false,
+        fix_channels = false,
+        no_move = false,
+        variable_rate = false,
+        muted = false,
+        adjust_latency = false,
+        peak_detect = false,
+        early_requests = false,
+        dont_inhibit_auto_suspend = false,
+        volume_set = false,
+        muted_set = false,
+        fail_on_suspend = false,
+        relative_volume = false,
+        passthrough = false;
+
+    pa_source_output_flags_t flags = 0;
+    pa_proplist *p = NULL;
+    uint32_t direct_on_input_idx = PA_INVALID_INDEX;
+    pa_sink_input *direct_on_input = NULL;
+    int ret = PA_ERR_INVALID;
+    uint8_t n_formats = 0;
+    pa_format_info *format;
+    pa_idxset *formats = NULL;
+    uint32_t i;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    memset(&attr, 0, sizeof(attr));
+
+    if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_get_channel_map(t, &map) < 0 ||
+        pa_tagstruct_getu32(t, &source_index) < 0 ||
+        pa_tagstruct_gets(t, &source_name) < 0 ||
+        pa_tagstruct_getu32(t, &attr.maxlength) < 0 ||
+        pa_tagstruct_get_boolean(t, &corked) < 0 ||
+        pa_tagstruct_getu32(t, &attr.fragsize) < 0) {
+
+        protocol_error(c);
+        goto finish;
+    }
+
+    CHECK_VALIDITY_GOTO(c->pstream, c->authorized, tag, PA_ERR_ACCESS, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, !source_name || pa_namereg_is_valid_name_or_wildcard(source_name, PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID, finish);
+
+    p = pa_proplist_new();
+
+    if (name)
+        pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+
+    if (c->version >= 12) {
+        /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+        if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+            pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+            pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+            pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+
+            protocol_error(c);
+            goto finish;
+        }
+    }
+
+    if (c->version >= 13) {
+
+        if (pa_tagstruct_get_boolean(t, &peak_detect) < 0 ||
+            pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
+            pa_tagstruct_get_proplist(t, p) < 0 ||
+            pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) {
+
+            protocol_error(c);
+            goto finish;
+        }
+    }
+
+    if (c->version >= 14) {
+
+        if (pa_tagstruct_get_boolean(t, &early_requests) < 0) {
+            protocol_error(c);
+            goto finish;
+        }
+    }
+
+    if (c->version >= 15) {
+
+        if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
+            pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
+
+            protocol_error(c);
+            goto finish;
+        }
+    }
+
+    if (c->version >= 22) {
+        /* For newer client versions (with per-source-output volumes), we try
+         * to make the behaviour for playback and record streams the same. */
+        volume_set = true;
+
+        if (pa_tagstruct_getu8(t, &n_formats) < 0) {
+            protocol_error(c);
+            goto finish;
+        }
+
+        if (n_formats)
+            formats = pa_idxset_new(NULL, NULL);
+
+        for (i = 0; i < n_formats; i++) {
+            format = pa_format_info_new();
+            if (pa_tagstruct_get_format_info(t, format) < 0) {
+                protocol_error(c);
+                goto finish;
+            }
+            pa_idxset_put(formats, format, NULL);
+        }
+
+        if (pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+            pa_tagstruct_get_boolean(t, &muted) < 0 ||
+            pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
+            pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
+            pa_tagstruct_get_boolean(t, &relative_volume) < 0 ||
+            pa_tagstruct_get_boolean(t, &passthrough) < 0) {
+
+            protocol_error(c);
+            goto finish;
+        }
+
+        CHECK_VALIDITY_GOTO(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID, finish);
+    }
+
+    if (n_formats == 0) {
+        CHECK_VALIDITY_GOTO(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID, finish);
+        CHECK_VALIDITY_GOTO(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID, finish);
+        CHECK_VALIDITY_GOTO(c->pstream, c->version < 22 || (volume.channels == ss.channels), tag, PA_ERR_INVALID, finish);
+        CHECK_VALIDITY_GOTO(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID, finish);
+    } else {
+        PA_IDXSET_FOREACH(format, formats, i) {
+            CHECK_VALIDITY_GOTO(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID, finish);
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        goto finish;
+    }
+
+    if (source_index != PA_INVALID_INDEX) {
+
+        if (!(source = pa_idxset_get_by_index(c->protocol->core->sources, source_index))) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+            goto finish;
+        }
+
+    } else if (source_name) {
+
+        if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+            goto finish;
+        }
+    }
+
+    if (direct_on_input_idx != PA_INVALID_INDEX) {
+
+        if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+            goto finish;
+        }
+    }
+
+    flags =
+        (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) |
+        (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) |
+        (no_remix ? PA_SOURCE_OUTPUT_NO_REMIX : 0) |
+        (fix_format ? PA_SOURCE_OUTPUT_FIX_FORMAT : 0) |
+        (fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) |
+        (fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
+        (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
+        (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) |
+        (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
+        (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0) |
+        (passthrough ? PA_SOURCE_OUTPUT_PASSTHROUGH : 0);
+
+    s = record_stream_new(c, source, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, flags, p, adjust_latency, early_requests, relative_volume, peak_detect, direct_on_input, &ret);
+
+    CHECK_VALIDITY_GOTO(c->pstream, s, tag, ret, finish);
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, s->index);
+    pa_assert(s->source_output);
+    pa_tagstruct_putu32(reply, s->source_output->index);
+
+    if (c->version >= 9) {
+        /* Since 0.9 we support sending the buffer metrics back to the client */
+
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.fragsize);
+    }
+
+    if (c->version >= 12) {
+        /* Since 0.9.8 we support sending the chosen sample
+         * spec/channel map/device/suspend status back to the
+         * client */
+
+        pa_tagstruct_put_sample_spec(reply, &ss);
+        pa_tagstruct_put_channel_map(reply, &map);
+
+        pa_tagstruct_putu32(reply, s->source_output->source->index);
+        pa_tagstruct_puts(reply, s->source_output->source->name);
+
+        pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_SUSPENDED);
+    }
+
+    if (c->version >= 13)
+        pa_tagstruct_put_usec(reply, s->configured_source_latency);
+
+    if (c->version >= 22) {
+        /* Send back the format we negotiated */
+        if (s->source_output->format)
+            pa_tagstruct_put_format_info(reply, s->source_output->format);
+        else {
+            pa_format_info *f = pa_format_info_new();
+            pa_tagstruct_put_format_info(reply, f);
+            pa_format_info_free(f);
+        }
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+
+finish:
+    if (p)
+        pa_proplist_free(p);
+    if (formats)
+        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+}
+
+static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    int ret;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    ret = pa_core_exit(c->protocol->core, false, 0);
+    CHECK_VALIDITY(c->pstream, ret >= 0, tag, PA_ERR_ACCESS);
+
+    pa_log_debug("Client %s asks us to terminate.", pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)));
+
+    pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */
+}
+
+static void setup_srbchannel(pa_native_connection *c, pa_mem_type_t shm_type) {
+    pa_srbchannel_template srbt;
+    pa_srbchannel *srb;
+    pa_memchunk mc;
+    pa_tagstruct *t;
+    int fdlist[2];
+
+#ifndef HAVE_CREDS
+    pa_log_debug("Disabling srbchannel, reason: No fd passing support");
+    return;
+#endif
+
+    if (!c->options->srbchannel) {
+        pa_log_debug("Disabling srbchannel, reason: Must be enabled by module parameter");
+        return;
+    }
+
+    if (c->version < 30) {
+        pa_log_debug("Disabling srbchannel, reason: Protocol too old");
+        return;
+    }
+
+    if (!pa_pstream_get_shm(c->pstream)) {
+        pa_log_debug("Disabling srbchannel, reason: No SHM support");
+        return;
+    }
+
+    if (c->rw_mempool) {
+        pa_log_debug("Ignoring srbchannel setup, reason: received COMMAND_AUTH "
+                     "more than once");
+        return;
+    }
+
+    if (!(c->rw_mempool = pa_mempool_new(shm_type, c->protocol->core->shm_size, true))) {
+        pa_log_warn("Disabling srbchannel, reason: Failed to allocate shared "
+                    "writable memory pool.");
+        return;
+    }
+
+    if (shm_type == PA_MEM_TYPE_SHARED_MEMFD) {
+        const char *reason;
+        if (pa_pstream_register_memfd_mempool(c->pstream, c->rw_mempool, &reason)) {
+            pa_log_warn("Disabling srbchannel, reason: Failed to register memfd mempool: %s", reason);
+            goto fail;
+        }
+    }
+    pa_mempool_set_is_remote_writable(c->rw_mempool, true);
+
+    srb = pa_srbchannel_new(c->protocol->core->mainloop, c->rw_mempool);
+    if (!srb) {
+        pa_log_debug("Failed to create srbchannel");
+        goto fail;
+    }
+    pa_log_debug("Enabling srbchannel...");
+    pa_srbchannel_export(srb, &srbt);
+
+    /* Send enable command to client */
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_ENABLE_SRBCHANNEL);
+    pa_tagstruct_putu32(t, (size_t) srb); /* tag */
+    fdlist[0] = srbt.readfd;
+    fdlist[1] = srbt.writefd;
+    pa_pstream_send_tagstruct_with_fds(c->pstream, t, 2, fdlist, false);
+
+    /* Send ringbuffer memblock to client */
+    mc.memblock = srbt.memblock;
+    mc.index = 0;
+    mc.length = pa_memblock_get_length(srbt.memblock);
+    pa_pstream_send_memblock(c->pstream, 0, 0, 0, &mc);
+
+    c->srbpending = srb;
+    return;
+
+fail:
+    if (c->rw_mempool) {
+        pa_mempool_unref(c->rw_mempool);
+        c->rw_mempool = NULL;
+    }
+}
+
+static void command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+
+    if (tag != (uint32_t) (size_t) c->srbpending) {
+        protocol_error(c);
+        return;
+    }
+
+    pa_log_debug("Client enabled srbchannel.");
+    pa_pstream_set_srbchannel(c->pstream, c->srbpending);
+    c->srbpending = NULL;
+}
+
+static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    const void*cookie;
+    bool memfd_on_remote = false, do_memfd = false;
+    pa_tagstruct *reply;
+    pa_mem_type_t shm_type;
+    bool shm_on_remote = false, do_shm;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &c->version) < 0 ||
+        pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    /* Minimum supported version */
+    if (c->version < 8) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_VERSION);
+        return;
+    }
+
+    /* Starting with protocol version 13 the MSB of the version tag
+       reflects if shm is available for this pa_native_connection or
+       not. */
+    if ((c->version & PA_PROTOCOL_VERSION_MASK) >= 13) {
+        shm_on_remote = !!(c->version & PA_PROTOCOL_FLAG_SHM);
+
+        /* Starting with protocol version 31, the second MSB of the version
+         * tag reflects whether memfd is supported on the other PA end. */
+        if ((c->version & PA_PROTOCOL_VERSION_MASK) >= 31)
+            memfd_on_remote = !!(c->version & PA_PROTOCOL_FLAG_MEMFD);
+
+        /* Reserve the two most-significant _bytes_ of the version tag
+         * for flags. */
+        c->version &= PA_PROTOCOL_VERSION_MASK;
+    }
+
+    pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
+
+    pa_proplist_setf(c->client->proplist, "native-protocol.version", "%u", c->version);
+
+    if (!c->authorized) {
+        bool success = false;
+
+#ifdef HAVE_CREDS
+        const pa_creds *creds;
+
+        if ((creds = pa_pdispatch_creds(pd))) {
+            if (creds->uid == getuid())
+                success = true;
+            else if (c->options->auth_group) {
+                int r;
+                gid_t gid;
+
+                if ((gid = pa_get_gid_of_group(c->options->auth_group)) == (gid_t) -1)
+                    pa_log_warn("Failed to get GID of group '%s'", c->options->auth_group);
+                else if (gid == creds->gid)
+                    success = true;
+
+                if (!success) {
+                    if ((r = pa_uid_in_group(creds->uid, c->options->auth_group)) < 0)
+                        pa_log_warn("Failed to check group membership.");
+                    else if (r > 0)
+                        success = true;
+                }
+            }
+
+            pa_log_info("Got credentials: uid=%lu gid=%lu success=%i",
+                        (unsigned long) creds->uid,
+                        (unsigned long) creds->gid,
+                        (int) success);
+        }
+#endif
+
+        if (!success && c->options->auth_cookie) {
+            const uint8_t *ac;
+
+            if ((ac = pa_auth_cookie_read(c->options->auth_cookie, PA_NATIVE_COOKIE_LENGTH)))
+                if (memcmp(ac, cookie, PA_NATIVE_COOKIE_LENGTH) == 0)
+                    success = true;
+        }
+
+        if (!success) {
+            pa_log_warn("Denied access to client with invalid authentication data.");
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_ACCESS);
+            return;
+        }
+
+        c->authorized = true;
+        if (c->auth_timeout_event) {
+            c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+            c->auth_timeout_event = NULL;
+        }
+    }
+
+    /* Enable shared memory and memfd support if possible */
+    do_shm =
+        pa_mempool_is_shared(c->protocol->core->mempool) &&
+        c->is_local;
+
+    pa_log_debug("SHM possible: %s", pa_yes_no(do_shm));
+
+    if (do_shm)
+        if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
+            do_shm = false;
+
+#ifdef HAVE_CREDS
+    if (do_shm) {
+        /* Only enable SHM if both sides are owned by the same
+         * user. This is a security measure because otherwise data
+         * private to the user might leak. */
+
+        const pa_creds *creds;
+        if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
+            do_shm = false;
+    }
+#endif
+
+    pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));
+    pa_pstream_enable_shm(c->pstream, do_shm);
+
+    /* Do not declare memfd support for 9.0 client libraries (protocol v31).
+     *
+     * Although they support memfd transport, such 9.0 clients has an iochannel
+     * bug that would break memfd audio if they're run in 32-bit mode over a
+     * 64-bit kernel. Thus influence them to use the POSIX shared memory model
+     * instead. Check commit 451d1d676237c81 for further details. */
+    do_memfd =
+        c->version >= 32 && do_shm && pa_mempool_is_memfd_backed(c->protocol->core->mempool);
+
+    shm_type = PA_MEM_TYPE_PRIVATE;
+    if (do_shm) {
+        if (do_memfd && memfd_on_remote) {
+            pa_pstream_enable_memfd(c->pstream);
+            shm_type = PA_MEM_TYPE_SHARED_MEMFD;
+        } else
+            shm_type = PA_MEM_TYPE_SHARED_POSIX;
+
+        pa_log_debug("Memfd possible: %s", pa_yes_no(pa_memfd_is_locally_supported()));
+        pa_log_debug("Negotiated SHM type: %s", pa_mem_type_to_string(shm_type));
+    }
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0) |
+                        (do_memfd ? 0x40000000 : 0));
+
+#ifdef HAVE_CREDS
+{
+    /* SHM support is only enabled after both sides made sure they are the same user. */
+
+    pa_creds ucred;
+
+    ucred.uid = getuid();
+    ucred.gid = getgid();
+
+    pa_pstream_send_tagstruct_with_creds(c->pstream, reply, &ucred);
+}
+#else
+    pa_pstream_send_tagstruct(c->pstream, reply);
+#endif
+
+    /* The client enables memfd transport on its pstream only after
+     * inspecting our version flags to see if we support memfds too.
+     *
+     * Thus register any pools after sending the server's version
+     * flags and _never_ before it. */
+    if (shm_type == PA_MEM_TYPE_SHARED_MEMFD) {
+        const char *reason;
+
+        if (pa_pstream_register_memfd_mempool(c->pstream, c->protocol->core->mempool, &reason))
+            pa_log("Failed to register memfd mempool. Reason: %s", reason);
+    }
+
+    setup_srbchannel(c, shm_type);
+}
+
+static void command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_common_command_register_memfd_shmid(c->pstream, pd, c->version, command, t))
+        protocol_error(c);
+}
+
+static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    const char *name = NULL;
+    pa_proplist *p;
+    pa_tagstruct *reply;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    p = pa_proplist_new();
+
+    if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) ||
+        (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
+        !pa_tagstruct_eof(t)) {
+
+        protocol_error(c);
+        pa_proplist_free(p);
+        return;
+    }
+
+    if (name)
+        if (pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name) < 0) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+            pa_proplist_free(p);
+            return;
+        }
+
+    pa_client_update_proplist(c->client, PA_UPDATE_REPLACE, p);
+    pa_proplist_free(p);
+
+    reply = reply_new(tag);
+
+    if (c->version >= 13)
+        pa_tagstruct_putu32(reply, c->client->index);
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    const char *name;
+    uint32_t idx = PA_IDXSET_INVALID;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &name) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_LOOKUP_SINK ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_LOOKUP_SINK) {
+        pa_sink *sink;
+        if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK)))
+            idx = sink->index;
+    } else {
+        pa_source *source;
+        pa_assert(command == PA_COMMAND_LOOKUP_SOURCE);
+        if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE)))
+            idx = source->index;
+    }
+
+    if (idx == PA_IDXSET_INVALID)
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+    else {
+        pa_tagstruct *reply;
+        reply = reply_new(tag);
+        pa_tagstruct_putu32(reply, idx);
+        pa_pstream_send_tagstruct(c->pstream, reply);
+    }
+}
+
+static void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    playback_stream *s;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    s = pa_idxset_get_by_index(c->output_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+    pa_asyncmsgq_post(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_DRAIN, PA_UINT_TO_PTR(tag), 0, NULL, NULL);
+}
+
+static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    pa_tagstruct *reply;
+    const pa_mempool_stat *stat;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    stat = pa_mempool_get_stat(c->protocol->core->mempool);
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_allocated));
+    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->allocated_size));
+    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_accumulated));
+    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->accumulated_size));
+    pa_tagstruct_putu32(reply, (uint32_t) pa_scache_total_size(c->protocol->core));
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    pa_tagstruct *reply;
+    playback_stream *s;
+    struct timeval tv, now;
+    uint32_t idx;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_get_timeval(t, &tv) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    s = pa_idxset_get_by_index(c->output_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+    /* Get an atomic snapshot of all timing parameters */
+    pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
+
+    reply = reply_new(tag);
+    pa_tagstruct_put_usec(reply,
+                          s->current_sink_latency +
+                          pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sink->sample_spec));
+    pa_tagstruct_put_usec(reply, 0);
+    pa_tagstruct_put_boolean(reply,
+                             s->playing_for > 0 &&
+                             pa_sink_get_state(s->sink_input->sink) == PA_SINK_RUNNING &&
+                             pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);
+    pa_tagstruct_put_timeval(reply, &tv);
+    pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
+    pa_tagstruct_puts64(reply, s->write_index);
+    pa_tagstruct_puts64(reply, s->read_index);
+
+    if (c->version >= 13) {
+        pa_tagstruct_putu64(reply, s->underrun_for);
+        pa_tagstruct_putu64(reply, s->playing_for);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    pa_tagstruct *reply;
+    record_stream *s;
+    struct timeval tv, now;
+    uint32_t idx;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_get_timeval(t, &tv) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    s = pa_idxset_get_by_index(c->record_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+    /* Get an atomic snapshot of all timing parameters */
+    pa_assert_se(pa_asyncmsgq_send(s->source_output->source->asyncmsgq, PA_MSGOBJECT(s->source_output), SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
+
+    reply = reply_new(tag);
+    pa_tagstruct_put_usec(reply, s->current_monitor_latency);
+    pa_tagstruct_put_usec(reply,
+                          s->current_source_latency +
+                          pa_bytes_to_usec(s->on_the_fly_snapshot, &s->source_output->sample_spec));
+    pa_tagstruct_put_boolean(reply,
+                             pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING &&
+                             pa_source_output_get_state(s->source_output) == PA_SOURCE_OUTPUT_RUNNING);
+    pa_tagstruct_put_timeval(reply, &tv);
+    pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
+    pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
+    pa_tagstruct_puts64(reply, pa_memblockq_get_read_index(s->memblockq));
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    upload_stream *s;
+    uint32_t length;
+    const char *name = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_tagstruct *reply;
+    pa_proplist *p;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_get_channel_map(t, &map) < 0 ||
+        pa_tagstruct_getu32(t, &length) < 0) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (length % pa_frame_size(&ss)) == 0 && length > 0, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, length <= PA_SCACHE_ENTRY_SIZE_MAX, tag, PA_ERR_TOOLARGE);
+
+    p = pa_proplist_new();
+
+    if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
+        !pa_tagstruct_eof(t)) {
+
+        protocol_error(c);
+        pa_proplist_free(p);
+        return;
+    }
+
+    if (c->version < 13)
+        pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+    else if (!name)
+        if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
+            name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
+
+    if (!name || !pa_namereg_is_valid_name(name)) {
+        pa_proplist_free(p);
+        CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_INVALID);
+    }
+
+    s = upload_stream_new(c, &ss, &map, name, length, p);
+    pa_proplist_free(p);
+
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, s->index);
+    pa_tagstruct_putu32(reply, length);
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_finish_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t channel;
+    upload_stream *s;
+    uint32_t idx;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    s = pa_idxset_get_by_index(c->output_streams, channel);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, upload_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+    if (!s->memchunk.memblock)
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_TOOLARGE);
+    else if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);
+    else
+        pa_pstream_send_simple_ack(c->pstream, tag);
+
+    upload_stream_unlink(s);
+}
+
+static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t sink_index;
+    pa_volume_t volume;
+    pa_sink *sink;
+    const char *name, *sink_name;
+    uint32_t idx;
+    pa_proplist *p;
+    pa_tagstruct *reply;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
+        pa_tagstruct_gets(t, &sink_name) < 0 ||
+        pa_tagstruct_getu32(t, &volume) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+
+    if (sink_index != PA_INVALID_INDEX)
+        sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
+    else
+        sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK);
+
+    CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+    p = pa_proplist_new();
+
+    if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        pa_proplist_free(p);
+        return;
+    }
+
+    pa_proplist_update(p, PA_UPDATE_MERGE, c->client->proplist);
+
+    if (pa_scache_play_item(c->protocol->core, name, sink, volume, p, &idx) < 0) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+        pa_proplist_free(p);
+        return;
+    }
+
+    pa_proplist_free(p);
+
+    reply = reply_new(tag);
+
+    if (c->version >= 13)
+        pa_tagstruct_putu32(reply, idx);
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_remove_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    const char *name;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &name) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+
+    if (pa_scache_remove_item(c->protocol->core, name) < 0) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+        return;
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void fixup_sample_spec(pa_native_connection *c, pa_sample_spec *fixed, const pa_sample_spec *original) {
+    pa_assert(c);
+    pa_assert(fixed);
+    pa_assert(original);
+
+    *fixed = *original;
+
+    if (c->version < 12) {
+        /* Before protocol version 12 we didn't support S32 samples,
+         * so we need to lie about this to the client */
+
+        if (fixed->format == PA_SAMPLE_S32LE)
+            fixed->format = PA_SAMPLE_FLOAT32LE;
+        if (fixed->format == PA_SAMPLE_S32BE)
+            fixed->format = PA_SAMPLE_FLOAT32BE;
+    }
+
+    if (c->version < 15) {
+        if (fixed->format == PA_SAMPLE_S24LE || fixed->format == PA_SAMPLE_S24_32LE)
+            fixed->format = PA_SAMPLE_FLOAT32LE;
+        if (fixed->format == PA_SAMPLE_S24BE || fixed->format == PA_SAMPLE_S24_32BE)
+            fixed->format = PA_SAMPLE_FLOAT32BE;
+    }
+}
+
+static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink *sink) {
+    pa_sample_spec fixed_ss;
+
+    pa_assert(t);
+    pa_sink_assert_ref(sink);
+
+    fixup_sample_spec(c, &fixed_ss, &sink->sample_spec);
+
+    pa_tagstruct_put(
+        t,
+        PA_TAG_U32, sink->index,
+        PA_TAG_STRING, sink->name,
+        PA_TAG_STRING, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)),
+        PA_TAG_SAMPLE_SPEC, &fixed_ss,
+        PA_TAG_CHANNEL_MAP, &sink->channel_map,
+        PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX,
+        PA_TAG_CVOLUME, pa_sink_get_volume(sink, false),
+        PA_TAG_BOOLEAN, pa_sink_get_mute(sink, false),
+        PA_TAG_U32, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
+        PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL,
+        PA_TAG_USEC, pa_sink_get_latency(sink),
+        PA_TAG_STRING, sink->driver,
+        PA_TAG_U32, sink->flags & PA_SINK_CLIENT_FLAGS_MASK,
+        PA_TAG_INVALID);
+
+    if (c->version >= 13) {
+        pa_tagstruct_put_proplist(t, sink->proplist);
+        pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink));
+    }
+
+    if (c->version >= 15) {
+        pa_tagstruct_put_volume(t, sink->base_volume);
+        if (PA_UNLIKELY(pa_sink_get_state(sink) == PA_SINK_INVALID_STATE))
+            pa_log_error("Internal sink state is invalid.");
+        pa_tagstruct_putu32(t, pa_sink_get_state(sink));
+        pa_tagstruct_putu32(t, sink->n_volume_steps);
+        pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX);
+    }
+
+    if (c->version >= 16) {
+        void *state;
+        pa_device_port *p;
+
+        pa_tagstruct_putu32(t, pa_hashmap_size(sink->ports));
+
+        PA_HASHMAP_FOREACH(p, sink->ports, state) {
+            pa_tagstruct_puts(t, p->name);
+            pa_tagstruct_puts(t, p->description);
+            pa_tagstruct_putu32(t, p->priority);
+            if (c->version >= 24)
+                pa_tagstruct_putu32(t, p->available);
+        }
+
+        pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL);
+    }
+
+    if (c->version >= 21) {
+        uint32_t i;
+        pa_format_info *f;
+        pa_idxset *formats = pa_sink_get_formats(sink);
+
+        pa_tagstruct_putu8(t, (uint8_t) pa_idxset_size(formats));
+        PA_IDXSET_FOREACH(f, formats, i) {
+            pa_tagstruct_put_format_info(t, f);
+        }
+
+        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+    }
+}
+
+static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
+    pa_sample_spec fixed_ss;
+
+    pa_assert(t);
+    pa_source_assert_ref(source);
+
+    fixup_sample_spec(c, &fixed_ss, &source->sample_spec);
+
+    pa_tagstruct_put(
+        t,
+        PA_TAG_U32, source->index,
+        PA_TAG_STRING, source->name,
+        PA_TAG_STRING, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)),
+        PA_TAG_SAMPLE_SPEC, &fixed_ss,
+        PA_TAG_CHANNEL_MAP, &source->channel_map,
+        PA_TAG_U32, source->module ? source->module->index : PA_INVALID_INDEX,
+        PA_TAG_CVOLUME, pa_source_get_volume(source, false),
+        PA_TAG_BOOLEAN, pa_source_get_mute(source, false),
+        PA_TAG_U32, source->monitor_of ? source->monitor_of->index : PA_INVALID_INDEX,
+        PA_TAG_STRING, source->monitor_of ? source->monitor_of->name : NULL,
+        PA_TAG_USEC, pa_source_get_latency(source),
+        PA_TAG_STRING, source->driver,
+        PA_TAG_U32, source->flags & PA_SOURCE_CLIENT_FLAGS_MASK,
+        PA_TAG_INVALID);
+
+    if (c->version >= 13) {
+        pa_tagstruct_put_proplist(t, source->proplist);
+        pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source));
+    }
+
+    if (c->version >= 15) {
+        pa_tagstruct_put_volume(t, source->base_volume);
+        if (PA_UNLIKELY(pa_source_get_state(source) == PA_SOURCE_INVALID_STATE))
+            pa_log_error("Internal source state is invalid.");
+        pa_tagstruct_putu32(t, pa_source_get_state(source));
+        pa_tagstruct_putu32(t, source->n_volume_steps);
+        pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX);
+    }
+
+    if (c->version >= 16) {
+        void *state;
+        pa_device_port *p;
+
+        pa_tagstruct_putu32(t, pa_hashmap_size(source->ports));
+
+        PA_HASHMAP_FOREACH(p, source->ports, state) {
+            pa_tagstruct_puts(t, p->name);
+            pa_tagstruct_puts(t, p->description);
+            pa_tagstruct_putu32(t, p->priority);
+            if (c->version >= 24)
+                pa_tagstruct_putu32(t, p->available);
+        }
+
+        pa_tagstruct_puts(t, source->active_port ? source->active_port->name : NULL);
+    }
+
+    if (c->version >= 22) {
+        uint32_t i;
+        pa_format_info *f;
+        pa_idxset *formats = pa_source_get_formats(source);
+
+        pa_tagstruct_putu8(t, (uint8_t) pa_idxset_size(formats));
+        PA_IDXSET_FOREACH(f, formats, i) {
+            pa_tagstruct_put_format_info(t, f);
+        }
+
+        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+    }
+}
+
+static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
+    pa_assert(t);
+    pa_assert(client);
+
+    pa_tagstruct_putu32(t, client->index);
+    pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)));
+    pa_tagstruct_putu32(t, client->module ? client->module->index : PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, client->driver);
+
+    if (c->version >= 13)
+        pa_tagstruct_put_proplist(t, client->proplist);
+}
+
+static void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_card *card) {
+    void *state = NULL;
+    pa_card_profile *p;
+    pa_device_port *port;
+
+    pa_assert(t);
+    pa_assert(card);
+
+    pa_tagstruct_putu32(t, card->index);
+    pa_tagstruct_puts(t, card->name);
+    pa_tagstruct_putu32(t, card->module ? card->module->index : PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, card->driver);
+
+    pa_tagstruct_putu32(t, pa_hashmap_size(card->profiles));
+
+    PA_HASHMAP_FOREACH(p, card->profiles, state) {
+        pa_tagstruct_puts(t, p->name);
+        pa_tagstruct_puts(t, p->description);
+        pa_tagstruct_putu32(t, p->n_sinks);
+        pa_tagstruct_putu32(t, p->n_sources);
+        pa_tagstruct_putu32(t, p->priority);
+
+        if (c->version >= 29)
+            pa_tagstruct_putu32(t, (p->available != PA_AVAILABLE_NO));
+    }
+
+    pa_tagstruct_puts(t, card->active_profile->name);
+    pa_tagstruct_put_proplist(t, card->proplist);
+
+    if (c->version < 26)
+        return;
+
+    pa_tagstruct_putu32(t, pa_hashmap_size(card->ports));
+
+    PA_HASHMAP_FOREACH(port, card->ports, state) {
+        void *state2;
+
+        pa_tagstruct_puts(t, port->name);
+        pa_tagstruct_puts(t, port->description);
+        pa_tagstruct_putu32(t, port->priority);
+        pa_tagstruct_putu32(t, port->available);
+        pa_tagstruct_putu8(t, port->direction);
+        pa_tagstruct_put_proplist(t, port->proplist);
+
+        pa_tagstruct_putu32(t, pa_hashmap_size(port->profiles));
+
+        PA_HASHMAP_FOREACH(p, port->profiles, state2)
+            pa_tagstruct_puts(t, p->name);
+
+        if (c->version >= 27)
+            pa_tagstruct_puts64(t, port->latency_offset);
+    }
+}
+
+static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) {
+    pa_assert(t);
+    pa_assert(module);
+
+    pa_tagstruct_putu32(t, module->index);
+    pa_tagstruct_puts(t, module->name);
+    pa_tagstruct_puts(t, module->argument);
+    pa_tagstruct_putu32(t, (uint32_t) pa_module_get_n_used(module));
+
+    if (c->version < 15)
+        pa_tagstruct_put_boolean(t, false); /* autoload is obsolete */
+
+    if (c->version >= 15)
+        pa_tagstruct_put_proplist(t, module->proplist);
+}
+
+static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
+    pa_sample_spec fixed_ss;
+    pa_usec_t sink_latency;
+    pa_cvolume v;
+    bool has_volume = false;
+
+    pa_assert(t);
+    pa_sink_input_assert_ref(s);
+
+    fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
+
+    has_volume = pa_sink_input_is_volume_readable(s);
+    if (has_volume)
+        pa_sink_input_get_volume(s, &v, true);
+    else
+        pa_cvolume_reset(&v, fixed_ss.channels);
+
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
+    pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
+    pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
+    pa_tagstruct_putu32(t, s->sink->index);
+    pa_tagstruct_put_sample_spec(t, &fixed_ss);
+    pa_tagstruct_put_channel_map(t, &s->channel_map);
+    pa_tagstruct_put_cvolume(t, &v);
+    pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
+    pa_tagstruct_put_usec(t, sink_latency);
+    pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
+    pa_tagstruct_puts(t, s->driver);
+    if (c->version >= 11)
+        pa_tagstruct_put_boolean(t, s->muted);
+    if (c->version >= 13)
+        pa_tagstruct_put_proplist(t, s->proplist);
+    if (c->version >= 19)
+        pa_tagstruct_put_boolean(t, (pa_sink_input_get_state(s) == PA_SINK_INPUT_CORKED));
+    if (c->version >= 20) {
+        pa_tagstruct_put_boolean(t, has_volume);
+        pa_tagstruct_put_boolean(t, s->volume_writable);
+    }
+    if (c->version >= 21)
+        pa_tagstruct_put_format_info(t, s->format);
+}
+
+static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) {
+    pa_sample_spec fixed_ss;
+    pa_usec_t source_latency;
+    pa_cvolume v;
+    bool has_volume = false;
+
+    pa_assert(t);
+    pa_source_output_assert_ref(s);
+
+    fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
+
+    has_volume = pa_source_output_is_volume_readable(s);
+    if (has_volume)
+        pa_source_output_get_volume(s, &v, true);
+    else
+        pa_cvolume_reset(&v, fixed_ss.channels);
+
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
+    pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
+    pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
+    pa_tagstruct_putu32(t, s->source->index);
+    pa_tagstruct_put_sample_spec(t, &fixed_ss);
+    pa_tagstruct_put_channel_map(t, &s->channel_map);
+    pa_tagstruct_put_usec(t, pa_source_output_get_latency(s, &source_latency));
+    pa_tagstruct_put_usec(t, source_latency);
+    pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s)));
+    pa_tagstruct_puts(t, s->driver);
+    if (c->version >= 13)
+        pa_tagstruct_put_proplist(t, s->proplist);
+    if (c->version >= 19)
+        pa_tagstruct_put_boolean(t, (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_CORKED));
+    if (c->version >= 22) {
+        pa_tagstruct_put_cvolume(t, &v);
+        pa_tagstruct_put_boolean(t, s->muted);
+        pa_tagstruct_put_boolean(t, has_volume);
+        pa_tagstruct_put_boolean(t, s->volume_writable);
+        pa_tagstruct_put_format_info(t, s->format);
+    }
+}
+
+static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) {
+    pa_sample_spec fixed_ss;
+    pa_cvolume v;
+
+    pa_assert(t);
+    pa_assert(e);
+
+    if (e->memchunk.memblock)
+        fixup_sample_spec(c, &fixed_ss, &e->sample_spec);
+    else
+        memset(&fixed_ss, 0, sizeof(fixed_ss));
+
+    pa_tagstruct_putu32(t, e->index);
+    pa_tagstruct_puts(t, e->name);
+
+    if (e->volume_is_set)
+        v = e->volume;
+    else
+        pa_cvolume_init(&v);
+
+    pa_tagstruct_put_cvolume(t, &v);
+    pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
+    pa_tagstruct_put_sample_spec(t, &fixed_ss);
+    pa_tagstruct_put_channel_map(t, &e->channel_map);
+    pa_tagstruct_putu32(t, (uint32_t) e->memchunk.length);
+    pa_tagstruct_put_boolean(t, e->lazy);
+    pa_tagstruct_puts(t, e->filename);
+
+    if (c->version >= 13)
+        pa_tagstruct_put_proplist(t, e->proplist);
+}
+
+static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    pa_sink *sink = NULL;
+    pa_source *source = NULL;
+    pa_client *client = NULL;
+    pa_card *card = NULL;
+    pa_module *module = NULL;
+    pa_sink_input *si = NULL;
+    pa_source_output *so = NULL;
+    pa_scache_entry *sce = NULL;
+    const char *name = NULL;
+    pa_tagstruct *reply;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        (command != PA_COMMAND_GET_CLIENT_INFO &&
+         command != PA_COMMAND_GET_MODULE_INFO &&
+         command != PA_COMMAND_GET_SINK_INPUT_INFO &&
+         command != PA_COMMAND_GET_SOURCE_OUTPUT_INFO &&
+         pa_tagstruct_gets(t, &name) < 0) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name ||
+                   (command == PA_COMMAND_GET_SINK_INFO &&
+                    pa_namereg_is_valid_name_or_wildcard(name, PA_NAMEREG_SINK)) ||
+                   (command == PA_COMMAND_GET_SOURCE_INFO &&
+                    pa_namereg_is_valid_name_or_wildcard(name, PA_NAMEREG_SOURCE)) ||
+                   pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, command == PA_COMMAND_GET_SINK_INFO ||
+                   command == PA_COMMAND_GET_SOURCE_INFO ||
+                   (idx != PA_INVALID_INDEX || name), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_GET_SINK_INFO) {
+        if (idx != PA_INVALID_INDEX)
+            sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+        else
+            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
+    } else if (command == PA_COMMAND_GET_SOURCE_INFO) {
+        if (idx != PA_INVALID_INDEX)
+            source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+        else
+            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+    } else if (command == PA_COMMAND_GET_CARD_INFO) {
+        if (idx != PA_INVALID_INDEX)
+            card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
+        else
+            card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
+    } else if (command == PA_COMMAND_GET_CLIENT_INFO)
+        client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
+    else if (command == PA_COMMAND_GET_MODULE_INFO)
+        module = pa_idxset_get_by_index(c->protocol->core->modules, idx);
+    else if (command == PA_COMMAND_GET_SINK_INPUT_INFO)
+        si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+    else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO)
+        so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
+    else {
+        pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO);
+        if (idx != PA_INVALID_INDEX)
+            sce = pa_idxset_get_by_index(c->protocol->core->scache, idx);
+        else
+            sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE);
+    }
+
+    if (!sink && !source && !client && !card && !module && !si && !so && !sce) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+        return;
+    }
+
+    reply = reply_new(tag);
+    if (sink)
+        sink_fill_tagstruct(c, reply, sink);
+    else if (source)
+        source_fill_tagstruct(c, reply, source);
+    else if (client)
+        client_fill_tagstruct(c, reply, client);
+    else if (card)
+        card_fill_tagstruct(c, reply, card);
+    else if (module)
+        module_fill_tagstruct(c, reply, module);
+    else if (si)
+        sink_input_fill_tagstruct(c, reply, si);
+    else if (so)
+        source_output_fill_tagstruct(c, reply, so);
+    else
+        scache_fill_tagstruct(c, reply, sce);
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    pa_idxset *i;
+    uint32_t idx;
+    void *p;
+    pa_tagstruct *reply;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    reply = reply_new(tag);
+
+    if (command == PA_COMMAND_GET_SINK_INFO_LIST)
+        i = c->protocol->core->sinks;
+    else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
+        i = c->protocol->core->sources;
+    else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
+        i = c->protocol->core->clients;
+    else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
+        i = c->protocol->core->cards;
+    else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
+        i = c->protocol->core->modules;
+    else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
+        i = c->protocol->core->sink_inputs;
+    else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
+        i = c->protocol->core->source_outputs;
+    else {
+        pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+        i = c->protocol->core->scache;
+    }
+
+    if (i) {
+        PA_IDXSET_FOREACH(p, i, idx) {
+            if (command == PA_COMMAND_GET_SINK_INFO_LIST)
+                sink_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
+                source_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
+                client_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
+                card_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
+                module_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
+                sink_input_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
+                source_output_fill_tagstruct(c, reply, p);
+            else {
+                pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+                scache_fill_tagstruct(c, reply, p);
+            }
+        }
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    pa_tagstruct *reply;
+    pa_sample_spec fixed_ss;
+    char *h, *u;
+    pa_core *core;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    reply = reply_new(tag);
+    pa_tagstruct_puts(reply, PACKAGE_NAME);
+    pa_tagstruct_puts(reply, PACKAGE_VERSION);
+
+    u = pa_get_user_name_malloc();
+    pa_tagstruct_puts(reply, u);
+    pa_xfree(u);
+
+    h = pa_get_host_name_malloc();
+    pa_tagstruct_puts(reply, h);
+    pa_xfree(h);
+
+    core = c->protocol->core;
+
+    fixup_sample_spec(c, &fixed_ss, &core->default_sample_spec);
+    pa_tagstruct_put_sample_spec(reply, &fixed_ss);
+
+    pa_tagstruct_puts(reply, core->default_sink ? core->default_sink->name : NULL);
+    pa_tagstruct_puts(reply, core->default_source ? core->default_source->name : NULL);
+
+    pa_tagstruct_putu32(reply, c->protocol->core->cookie);
+
+    if (c->version >= 15)
+        pa_tagstruct_put_channel_map(reply, &core->default_channel_map);
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint32_t idx, void *userdata) {
+    pa_tagstruct *t;
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+
+    pa_native_connection_assert_ref(c);
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1);
+    pa_tagstruct_putu32(t, e);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+}
+
+static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    pa_subscription_mask_t m;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &m) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, (m & ~PA_SUBSCRIPTION_MASK_ALL) == 0, tag, PA_ERR_INVALID);
+
+    if (c->subscription)
+        pa_subscription_free(c->subscription);
+
+    if (m != 0) {
+        c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c);
+        pa_assert(c->subscription);
+    } else
+        c->subscription = NULL;
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_volume(
+        pa_pdispatch *pd,
+        uint32_t command,
+        uint32_t tag,
+        pa_tagstruct *t,
+        void *userdata) {
+
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    pa_cvolume volume;
+    pa_sink *sink = NULL;
+    pa_source *source = NULL;
+    pa_sink_input *si = NULL;
+    pa_source_output *so = NULL;
+    const char *name = NULL;
+    const char *client_name;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        (command == PA_COMMAND_SET_SINK_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
+        (command == PA_COMMAND_SET_SOURCE_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
+        pa_tagstruct_get_cvolume(t, &volume) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_VOLUME ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
+
+    switch (command) {
+
+        case PA_COMMAND_SET_SINK_VOLUME:
+            if (idx != PA_INVALID_INDEX)
+                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+            else
+                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
+            break;
+
+        case PA_COMMAND_SET_SOURCE_VOLUME:
+            if (idx != PA_INVALID_INDEX)
+                source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+            else
+                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+            break;
+
+        case PA_COMMAND_SET_SINK_INPUT_VOLUME:
+            si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+            break;
+
+        case PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME:
+            so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    CHECK_VALIDITY(c->pstream, si || so || sink || source, tag, PA_ERR_NOENTITY);
+
+    client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
+
+    if (sink) {
+        CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &sink->sample_spec), tag, PA_ERR_INVALID);
+
+        pa_log_debug("Client %s changes volume of sink %s.", client_name, sink->name);
+        pa_sink_set_volume(sink, &volume, true, true);
+    } else if (source) {
+        CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &source->sample_spec), tag, PA_ERR_INVALID);
+
+        pa_log_debug("Client %s changes volume of source %s.", client_name, source->name);
+        pa_source_set_volume(source, &volume, true, true);
+    } else if (si) {
+        CHECK_VALIDITY(c->pstream, si->volume_writable, tag, PA_ERR_BADSTATE);
+        CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &si->sample_spec), tag, PA_ERR_INVALID);
+
+        pa_log_debug("Client %s changes volume of sink input %s.",
+                     client_name,
+                     pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
+        pa_sink_input_set_volume(si, &volume, true, true);
+    } else if (so) {
+        CHECK_VALIDITY(c->pstream, so->volume_writable, tag, PA_ERR_BADSTATE);
+        CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &so->sample_spec), tag, PA_ERR_INVALID);
+
+        pa_log_debug("Client %s changes volume of source output %s.",
+                     client_name,
+                     pa_strnull(pa_proplist_gets(so->proplist, PA_PROP_MEDIA_NAME)));
+        pa_source_output_set_volume(so, &volume, true, true);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_mute(
+        pa_pdispatch *pd,
+        uint32_t command,
+        uint32_t tag,
+        pa_tagstruct *t,
+        void *userdata) {
+
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    bool mute;
+    pa_sink *sink = NULL;
+    pa_source *source = NULL;
+    pa_sink_input *si = NULL;
+    pa_source_output *so = NULL;
+    const char *name = NULL, *client_name;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        (command == PA_COMMAND_SET_SINK_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
+        (command == PA_COMMAND_SET_SOURCE_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
+        pa_tagstruct_get_boolean(t, &mute) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_MUTE ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
+
+    switch (command) {
+
+        case PA_COMMAND_SET_SINK_MUTE:
+            if (idx != PA_INVALID_INDEX)
+                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+            else
+                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
+
+            break;
+
+        case PA_COMMAND_SET_SOURCE_MUTE:
+            if (idx != PA_INVALID_INDEX)
+                source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+            else
+                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+
+            break;
+
+        case PA_COMMAND_SET_SINK_INPUT_MUTE:
+            si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+            break;
+
+        case PA_COMMAND_SET_SOURCE_OUTPUT_MUTE:
+            so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    CHECK_VALIDITY(c->pstream, si || so || sink || source, tag, PA_ERR_NOENTITY);
+
+    client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
+
+    if (sink) {
+        pa_log_debug("Client %s changes mute of sink %s.", client_name, sink->name);
+        pa_sink_set_mute(sink, mute, true);
+    } else if (source) {
+        pa_log_debug("Client %s changes mute of source %s.", client_name, source->name);
+        pa_source_set_mute(source, mute, true);
+    } else if (si) {
+        pa_log_debug("Client %s changes mute of sink input %s.",
+                     client_name,
+                     pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
+        pa_sink_input_set_mute(si, mute, true);
+    } else if (so) {
+        pa_log_debug("Client %s changes mute of source output %s.",
+                     client_name,
+                     pa_strnull(pa_proplist_gets(so->proplist, PA_PROP_MEDIA_NAME)));
+        pa_source_output_set_mute(so, mute, true);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_cork_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    bool b;
+    playback_stream *s;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_get_boolean(t, &b) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+    s = pa_idxset_get_by_index(c->output_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+    pa_sink_input_cork(s->sink_input, b);
+
+    if (b)
+        s->is_underrun = true;
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_trigger_or_flush_or_prebuf_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    playback_stream *s;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+    s = pa_idxset_get_by_index(c->output_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+    switch (command) {
+        case PA_COMMAND_FLUSH_PLAYBACK_STREAM:
+            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_FLUSH, NULL, 0, NULL);
+            break;
+
+        case PA_COMMAND_PREBUF_PLAYBACK_STREAM:
+            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_PREBUF_FORCE, NULL, 0, NULL);
+            break;
+
+        case PA_COMMAND_TRIGGER_PLAYBACK_STREAM:
+            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_TRIGGER, NULL, 0, NULL);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    record_stream *s;
+    bool b;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_get_boolean(t, &b) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    s = pa_idxset_get_by_index(c->record_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+    pa_source_output_cork(s->source_output, b);
+    pa_memblockq_prebuf_force(s->memblockq);
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    record_stream *s;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    s = pa_idxset_get_by_index(c->record_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+    pa_memblockq_flush_read(s->memblockq);
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    pa_buffer_attr a;
+    pa_tagstruct *reply;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    memset(&a, 0, sizeof(a));
+
+    if (pa_tagstruct_getu32(t, &idx) < 0) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {
+        playback_stream *s;
+        bool adjust_latency = false, early_requests = false;
+
+        s = pa_idxset_get_by_index(c->output_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+        if (pa_tagstruct_get(
+                    t,
+                    PA_TAG_U32, &a.maxlength,
+                    PA_TAG_U32, &a.tlength,
+                    PA_TAG_U32, &a.prebuf,
+                    PA_TAG_U32, &a.minreq,
+                    PA_TAG_INVALID) < 0 ||
+            (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+            (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
+            !pa_tagstruct_eof(t)) {
+            protocol_error(c);
+            return;
+        }
+
+        s->adjust_latency = adjust_latency;
+        s->early_requests = early_requests;
+        s->buffer_attr_req = a;
+
+        fix_playback_buffer_attr(s);
+        pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR, NULL, 0, NULL) == 0);
+
+        reply = reply_new(tag);
+        pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(reply, s->buffer_attr.tlength);
+        pa_tagstruct_putu32(reply, s->buffer_attr.prebuf);
+        pa_tagstruct_putu32(reply, s->buffer_attr.minreq);
+
+        if (c->version >= 13)
+            pa_tagstruct_put_usec(reply, s->configured_sink_latency);
+
+    } else {
+        record_stream *s;
+        bool adjust_latency = false, early_requests = false;
+        pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);
+
+        s = pa_idxset_get_by_index(c->record_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        if (pa_tagstruct_get(
+                    t,
+                    PA_TAG_U32, &a.maxlength,
+                    PA_TAG_U32, &a.fragsize,
+                    PA_TAG_INVALID) < 0 ||
+            (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+            (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
+            !pa_tagstruct_eof(t)) {
+            protocol_error(c);
+            return;
+        }
+
+        s->adjust_latency = adjust_latency;
+        s->early_requests = early_requests;
+        s->buffer_attr_req = a;
+
+        fix_record_buffer_attr_pre(s);
+        pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
+        pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+        fix_record_buffer_attr_post(s);
+
+        reply = reply_new(tag);
+        pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(reply, s->buffer_attr.fragsize);
+
+        if (c->version >= 13)
+            pa_tagstruct_put_usec(reply, s->configured_source_latency);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    uint32_t rate;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_getu32(t, &rate) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, pa_sample_rate_valid(rate), tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE) {
+        playback_stream *s;
+
+        s = pa_idxset_get_by_index(c->output_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+        pa_sink_input_set_rate(s->sink_input, rate);
+
+    } else {
+        record_stream *s;
+        pa_assert(command == PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE);
+
+        s = pa_idxset_get_by_index(c->record_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        pa_source_output_set_rate(s->source_output, rate);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    uint32_t mode;
+    pa_proplist *p;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    p = pa_proplist_new();
+
+    if (command == PA_COMMAND_UPDATE_CLIENT_PROPLIST) {
+
+        if (pa_tagstruct_getu32(t, &mode) < 0 ||
+            pa_tagstruct_get_proplist(t, p) < 0 ||
+            !pa_tagstruct_eof(t)) {
+            protocol_error(c);
+            pa_proplist_free(p);
+            return;
+        }
+
+    } else {
+
+        if (pa_tagstruct_getu32(t, &idx) < 0 ||
+            pa_tagstruct_getu32(t, &mode) < 0 ||
+            pa_tagstruct_get_proplist(t, p) < 0 ||
+            !pa_tagstruct_eof(t)) {
+            protocol_error(c);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
+    if (!(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE)) {
+        pa_proplist_free(p);
+        CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_INVALID);
+    }
+
+    if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) {
+        playback_stream *s;
+
+        s = pa_idxset_get_by_index(c->output_streams, idx);
+        if (!s || !playback_stream_isinstance(s)) {
+            pa_proplist_free(p);
+            CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_NOENTITY);
+        }
+        pa_sink_input_update_proplist(s->sink_input, mode, p);
+
+    } else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
+        record_stream *s;
+
+        if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) {
+            pa_proplist_free(p);
+            CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_NOENTITY);
+        }
+        pa_source_output_update_proplist(s->source_output, mode, p);
+
+    } else {
+        pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
+
+        pa_client_update_proplist(c->client, mode, p);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+    pa_proplist_free(p);
+}
+
+static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    unsigned changed = 0;
+    pa_proplist *p;
+    pa_strlist *l = NULL;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    if (command != PA_COMMAND_REMOVE_CLIENT_PROPLIST) {
+
+        if (pa_tagstruct_getu32(t, &idx) < 0) {
+            protocol_error(c);
+            return;
+        }
+    }
+
+    if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
+        playback_stream *s;
+
+        s = pa_idxset_get_by_index(c->output_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+        p = s->sink_input->proplist;
+
+    } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
+        record_stream *s;
+
+        s = pa_idxset_get_by_index(c->record_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        p = s->source_output->proplist;
+    } else {
+        pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
+
+        p = c->client->proplist;
+    }
+
+    for (;;) {
+        const char *k;
+
+        if (pa_tagstruct_gets(t, &k) < 0) {
+            protocol_error(c);
+            pa_strlist_free(l);
+            return;
+        }
+
+        if (!k)
+            break;
+
+        l = pa_strlist_prepend(l, k);
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        pa_strlist_free(l);
+        return;
+    }
+
+    for (;;) {
+        char *z;
+
+        l = pa_strlist_pop(l, &z);
+
+        if (!z)
+            break;
+
+        changed += (unsigned) (pa_proplist_unset(p, z) >= 0);
+        pa_xfree(z);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+
+    if (changed) {
+        if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
+            playback_stream *s;
+
+            s = pa_idxset_get_by_index(c->output_streams, idx);
+            pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+
+        } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
+            record_stream *s;
+
+            s = pa_idxset_get_by_index(c->record_streams, idx);
+            pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
+
+        } else {
+            pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
+            pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+        }
+    }
+}
+
+static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    const char *s;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &s) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !s || pa_namereg_is_valid_name(s), tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_SET_DEFAULT_SOURCE) {
+        pa_source *source;
+
+        source = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SOURCE);
+        CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+        pa_core_set_configured_default_source(c->protocol->core, source->name);
+    } else {
+        pa_sink *sink;
+        pa_assert(command == PA_COMMAND_SET_DEFAULT_SINK);
+
+        sink = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SINK);
+        CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+        pa_core_set_configured_default_sink(c->protocol->core, sink->name);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    const char *name;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_SET_PLAYBACK_STREAM_NAME) {
+        playback_stream *s;
+
+        s = pa_idxset_get_by_index(c->output_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+        pa_sink_input_set_property(s->sink_input, PA_PROP_MEDIA_NAME, name);
+
+    } else {
+        record_stream *s;
+        pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_NAME);
+
+        s = pa_idxset_get_by_index(c->record_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        pa_source_output_set_property(s->source_output, PA_PROP_MEDIA_NAME, name);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    if (command == PA_COMMAND_KILL_CLIENT) {
+        pa_client *client;
+
+        client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
+        CHECK_VALIDITY(c->pstream, client, tag, PA_ERR_NOENTITY);
+
+        pa_native_connection_ref(c);
+        pa_client_kill(client);
+
+    } else if (command == PA_COMMAND_KILL_SINK_INPUT) {
+        pa_sink_input *s;
+
+        s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        pa_native_connection_ref(c);
+        pa_sink_input_kill(s);
+    } else {
+        pa_source_output *s;
+
+        pa_assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT);
+
+        s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        pa_native_connection_ref(c);
+        pa_source_output_kill(s);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+    pa_native_connection_unref(c);
+}
+
+static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    pa_module *m;
+    const char *name, *argument;
+    pa_tagstruct *reply;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &argument) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name) && !strchr(name, '/'), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, !argument || pa_utf8_valid(argument), tag, PA_ERR_INVALID);
+
+    if (!(m = pa_module_load(c->protocol->core, name, argument))) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_MODINITFAILED);
+        return;
+    }
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, m->index);
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx;
+    pa_module *m;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
+    CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOENTITY);
+
+    pa_module_unload_request(m, false);
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX;
+    const char *name_device = NULL;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_getu32(t, &idx_device) < 0 ||
+        pa_tagstruct_gets(t, &name_device) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+
+    CHECK_VALIDITY(c->pstream, !name_device || pa_namereg_is_valid_name_or_wildcard(name_device, command == PA_COMMAND_MOVE_SINK_INPUT ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (idx_device != PA_INVALID_INDEX) ^ (name_device != NULL), tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_MOVE_SINK_INPUT) {
+        pa_sink_input *si = NULL;
+        pa_sink *sink = NULL;
+
+        si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+
+        if (idx_device != PA_INVALID_INDEX)
+            sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx_device);
+        else
+            sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK);
+
+        CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
+
+        if (pa_sink_input_move_to(si, sink, true) < 0) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+            return;
+        }
+    } else {
+        pa_source_output *so = NULL;
+        pa_source *source;
+
+        pa_assert(command == PA_COMMAND_MOVE_SOURCE_OUTPUT);
+
+        so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
+
+        if (idx_device != PA_INVALID_INDEX)
+            source = pa_idxset_get_by_index(c->protocol->core->sources, idx_device);
+        else
+            source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE);
+
+        CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY);
+
+        if (pa_source_output_move_to(so, source, true) < 0) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+            return;
+        }
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx = PA_INVALID_INDEX;
+    const char *name = NULL;
+    bool b;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_get_boolean(t, &b) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SUSPEND_SINK ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) || *name == 0, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_SUSPEND_SINK) {
+
+        if (idx == PA_INVALID_INDEX && name && !*name) {
+
+            pa_log_debug("%s all sinks", b ? "Suspending" : "Resuming");
+
+            if (pa_sink_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+                return;
+            }
+        } else {
+            pa_sink *sink = NULL;
+
+            if (idx != PA_INVALID_INDEX)
+                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+            else
+                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
+
+            CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+            pa_log_debug("%s of sink %s requested by client %" PRIu32 ".",
+                         b ? "Suspending" : "Resuming", sink->name, c->client->index);
+
+            if (pa_sink_suspend(sink, b, PA_SUSPEND_USER) < 0) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+                return;
+            }
+        }
+    } else {
+
+        pa_assert(command == PA_COMMAND_SUSPEND_SOURCE);
+
+        if (idx == PA_INVALID_INDEX && name && !*name) {
+
+            pa_log_debug("%s all sources", b ? "Suspending" : "Resuming");
+
+            if (pa_source_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+                return;
+            }
+
+        } else {
+            pa_source *source;
+
+            if (idx != PA_INVALID_INDEX)
+                source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+            else
+                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+
+            CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+            pa_log_debug("%s of source %s requested by client %" PRIu32 ".",
+                         b ? "Suspending" : "Resuming", source->name, c->client->index);
+
+            if (pa_source_suspend(source, b, PA_SUSPEND_USER) < 0) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+                return;
+            }
+        }
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx = PA_INVALID_INDEX;
+    const char *name = NULL;
+    pa_module *m;
+    pa_native_protocol_ext_cb_t cb;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name || pa_utf8_valid(name), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
+
+    if (idx != PA_INVALID_INDEX)
+        m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
+    else
+        PA_IDXSET_FOREACH(m, c->protocol->core->modules, idx)
+            if (pa_streq(name, m->name))
+                break;
+
+    CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
+    CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+
+    cb = (pa_native_protocol_ext_cb_t) (unsigned long) pa_hashmap_get(c->protocol->extensions, m);
+    CHECK_VALIDITY(c->pstream, cb, tag, PA_ERR_NOEXTENSION);
+
+    if (cb(c->protocol, m, c, tag, t) < 0)
+        protocol_error(c);
+}
+
+static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx = PA_INVALID_INDEX;
+    const char *name = NULL, *profile_name = NULL;
+    pa_card *card = NULL;
+    pa_card_profile *profile;
+    int ret;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &profile_name) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, profile_name, tag, PA_ERR_INVALID);
+
+    if (idx != PA_INVALID_INDEX)
+        card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
+    else
+        card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
+
+    CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
+
+    profile = pa_hashmap_get(card->profiles, profile_name);
+
+    CHECK_VALIDITY(c->pstream, profile, tag, PA_ERR_NOENTITY);
+
+    pa_log_info("Application \"%s\" requests card profile change. card = %s, profile = %s",
+                pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_NAME)),
+                card->name,
+                profile->name);
+
+    if ((ret = pa_card_set_profile(card, profile, true)) < 0) {
+        pa_pstream_send_error(c->pstream, tag, -ret);
+        return;
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx = PA_INVALID_INDEX;
+    const char *name = NULL, *port = NULL;
+    int ret;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &port) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_PORT ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, port, tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_SET_SINK_PORT) {
+        pa_sink *sink;
+
+        if (idx != PA_INVALID_INDEX)
+            sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+        else
+            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
+
+        CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+        if ((ret = pa_sink_set_port(sink, port, true)) < 0) {
+            pa_pstream_send_error(c->pstream, tag, -ret);
+            return;
+        }
+    } else {
+        pa_source *source;
+
+        pa_assert(command == PA_COMMAND_SET_SOURCE_PORT);
+
+        if (idx != PA_INVALID_INDEX)
+            source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+        else
+            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+
+        CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+        if ((ret = pa_source_set_port(source, port, true)) < 0) {
+            pa_pstream_send_error(c->pstream, tag, -ret);
+            return;
+        }
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    const char *port_name, *card_name;
+    uint32_t idx = PA_INVALID_INDEX;
+    int64_t offset;
+    pa_card *card = NULL;
+    pa_device_port *port = NULL;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &card_name) < 0 ||
+        pa_tagstruct_gets(t, &port_name) < 0 ||
+        pa_tagstruct_gets64(t, &offset) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !card_name || pa_namereg_is_valid_name(card_name), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (card_name != NULL), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, port_name, tag, PA_ERR_INVALID);
+
+    if (idx != PA_INVALID_INDEX)
+        card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
+    else
+        card = pa_namereg_get(c->protocol->core, card_name, PA_NAMEREG_CARD);
+
+    CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
+
+    port = pa_hashmap_get(card->ports, port_name);
+    CHECK_VALIDITY(c->pstream, port, tag, PA_ERR_NOENTITY);
+
+    pa_device_port_set_latency_offset(port, offset);
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
+    [PA_COMMAND_ERROR] = NULL,
+    [PA_COMMAND_TIMEOUT] = NULL,
+    [PA_COMMAND_REPLY] = NULL,
+    [PA_COMMAND_CREATE_PLAYBACK_STREAM] = command_create_playback_stream,
+    [PA_COMMAND_DELETE_PLAYBACK_STREAM] = command_delete_stream,
+    [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = command_drain_playback_stream,
+    [PA_COMMAND_CREATE_RECORD_STREAM] = command_create_record_stream,
+    [PA_COMMAND_DELETE_RECORD_STREAM] = command_delete_stream,
+    [PA_COMMAND_AUTH] = command_auth,
+    [PA_COMMAND_REQUEST] = NULL,
+    [PA_COMMAND_EXIT] = command_exit,
+    [PA_COMMAND_SET_CLIENT_NAME] = command_set_client_name,
+    [PA_COMMAND_LOOKUP_SINK] = command_lookup,
+    [PA_COMMAND_LOOKUP_SOURCE] = command_lookup,
+    [PA_COMMAND_STAT] = command_stat,
+    [PA_COMMAND_GET_PLAYBACK_LATENCY] = command_get_playback_latency,
+    [PA_COMMAND_GET_RECORD_LATENCY] = command_get_record_latency,
+    [PA_COMMAND_CREATE_UPLOAD_STREAM] = command_create_upload_stream,
+    [PA_COMMAND_DELETE_UPLOAD_STREAM] = command_delete_stream,
+    [PA_COMMAND_FINISH_UPLOAD_STREAM] = command_finish_upload_stream,
+    [PA_COMMAND_PLAY_SAMPLE] = command_play_sample,
+    [PA_COMMAND_REMOVE_SAMPLE] = command_remove_sample,
+    [PA_COMMAND_GET_SINK_INFO] = command_get_info,
+    [PA_COMMAND_GET_SOURCE_INFO] = command_get_info,
+    [PA_COMMAND_GET_CLIENT_INFO] = command_get_info,
+    [PA_COMMAND_GET_CARD_INFO] = command_get_info,
+    [PA_COMMAND_GET_MODULE_INFO] = command_get_info,
+    [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info,
+    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info,
+    [PA_COMMAND_GET_SAMPLE_INFO] = command_get_info,
+    [PA_COMMAND_GET_SINK_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_CARD_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_SERVER_INFO] = command_get_server_info,
+    [PA_COMMAND_SUBSCRIBE] = command_subscribe,
+
+    [PA_COMMAND_SET_SINK_VOLUME] = command_set_volume,
+    [PA_COMMAND_SET_SINK_INPUT_VOLUME] = command_set_volume,
+    [PA_COMMAND_SET_SOURCE_VOLUME] = command_set_volume,
+    [PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME] = command_set_volume,
+
+    [PA_COMMAND_SET_SINK_MUTE] = command_set_mute,
+    [PA_COMMAND_SET_SINK_INPUT_MUTE] = command_set_mute,
+    [PA_COMMAND_SET_SOURCE_MUTE] = command_set_mute,
+    [PA_COMMAND_SET_SOURCE_OUTPUT_MUTE] = command_set_mute,
+
+    [PA_COMMAND_SUSPEND_SINK] = command_suspend,
+    [PA_COMMAND_SUSPEND_SOURCE] = command_suspend,
+
+    [PA_COMMAND_CORK_PLAYBACK_STREAM] = command_cork_playback_stream,
+    [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
+    [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
+    [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
+
+    [PA_COMMAND_CORK_RECORD_STREAM] = command_cork_record_stream,
+    [PA_COMMAND_FLUSH_RECORD_STREAM] = command_flush_record_stream,
+
+    [PA_COMMAND_SET_DEFAULT_SINK] = command_set_default_sink_or_source,
+    [PA_COMMAND_SET_DEFAULT_SOURCE] = command_set_default_sink_or_source,
+    [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = command_set_stream_name,
+    [PA_COMMAND_SET_RECORD_STREAM_NAME] = command_set_stream_name,
+    [PA_COMMAND_KILL_CLIENT] = command_kill,
+    [PA_COMMAND_KILL_SINK_INPUT] = command_kill,
+    [PA_COMMAND_KILL_SOURCE_OUTPUT] = command_kill,
+    [PA_COMMAND_LOAD_MODULE] = command_load_module,
+    [PA_COMMAND_UNLOAD_MODULE] = command_unload_module,
+
+    [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = NULL,
+    [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = NULL,
+    [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = NULL,
+    [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = NULL,
+
+    [PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream,
+    [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream,
+
+    [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+    [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+
+    [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
+    [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
+
+    [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST] = command_update_proplist,
+    [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST] = command_update_proplist,
+    [PA_COMMAND_UPDATE_CLIENT_PROPLIST] = command_update_proplist,
+
+    [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST] = command_remove_proplist,
+    [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist,
+    [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist,
+
+    [PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile,
+
+    [PA_COMMAND_SET_SINK_PORT] = command_set_sink_or_source_port,
+    [PA_COMMAND_SET_SOURCE_PORT] = command_set_sink_or_source_port,
+
+    [PA_COMMAND_SET_PORT_LATENCY_OFFSET] = command_set_port_latency_offset,
+
+    [PA_COMMAND_ENABLE_SRBCHANNEL] = command_enable_srbchannel,
+
+    [PA_COMMAND_REGISTER_MEMFD_SHMID] = command_register_memfd_shmid,
+
+    [PA_COMMAND_EXTENSION] = command_extension
+};
+
+/*** pstream callbacks ***/
+
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+
+    pa_assert(p);
+    pa_assert(packet);
+    pa_native_connection_assert_ref(c);
+
+    if (pa_pdispatch_run(c->pdispatch, packet, ancil_data, c) < 0) {
+        pa_log("invalid packet.");
+        native_connection_unlink(c);
+    }
+}
+
+static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    output_stream *stream;
+
+    pa_assert(p);
+    pa_assert(chunk);
+    pa_native_connection_assert_ref(c);
+
+    if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
+        pa_log_debug("Client sent block for invalid stream.");
+        /* Ignoring */
+        return;
+    }
+
+#ifdef PROTOCOL_NATIVE_DEBUG
+    pa_log("got %lu bytes from client", (unsigned long) chunk->length);
+#endif
+
+    if (playback_stream_isinstance(stream)) {
+        playback_stream *ps = PLAYBACK_STREAM(stream);
+
+        size_t frame_size = pa_frame_size(&ps->sink_input->sample_spec);
+        if (chunk->index % frame_size != 0 || chunk->length % frame_size != 0) {
+            pa_log_warn("Client sent non-aligned memblock: index %d, length %d, frame size: %d",
+                        (int) chunk->index, (int) chunk->length, (int) frame_size);
+            return;
+        }
+
+        pa_atomic_inc(&ps->seek_or_post_in_queue);
+        if (chunk->memblock) {
+            if (seek != PA_SEEK_RELATIVE || offset != 0)
+                pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, chunk, NULL);
+            else
+                pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+        } else
+            pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset+chunk->length, NULL, NULL);
+
+    } else {
+        upload_stream *u = UPLOAD_STREAM(stream);
+        size_t l;
+
+        if (!u->memchunk.memblock) {
+            if (u->length == chunk->length && chunk->memblock) {
+                u->memchunk = *chunk;
+                pa_memblock_ref(u->memchunk.memblock);
+                u->length = 0;
+            } else {
+                u->memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, u->length);
+                u->memchunk.index = u->memchunk.length = 0;
+            }
+        }
+
+        pa_assert(u->memchunk.memblock);
+
+        l = u->length;
+        if (l > chunk->length)
+            l = chunk->length;
+
+        if (l > 0) {
+            void *dst;
+            dst = pa_memblock_acquire(u->memchunk.memblock);
+
+            if (chunk->memblock) {
+                void *src;
+                src = pa_memblock_acquire(chunk->memblock);
+
+                memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
+                       (uint8_t*) src + chunk->index, l);
+
+                pa_memblock_release(chunk->memblock);
+            } else
+                pa_silence_memory((uint8_t*) dst + u->memchunk.index + u->memchunk.length, l, &u->sample_spec);
+
+            pa_memblock_release(u->memchunk.memblock);
+
+            u->memchunk.length += l;
+            u->length -= l;
+        }
+    }
+}
+
+static void pstream_die_callback(pa_pstream *p, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+
+    pa_assert(p);
+    pa_native_connection_assert_ref(c);
+
+    native_connection_unlink(c);
+    pa_log_info("Connection died.");
+}
+
+static void pstream_drain_callback(pa_pstream *p, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+
+    pa_assert(p);
+    pa_native_connection_assert_ref(c);
+
+    native_connection_send_memblock(c);
+}
+
+static void pstream_revoke_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
+    pa_thread_mq *q;
+
+    if (!(q = pa_thread_mq_get()))
+        pa_pstream_send_revoke(p, block_id);
+    else
+        pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_REVOKE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
+}
+
+static void pstream_release_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
+    pa_thread_mq *q;
+
+    if (!(q = pa_thread_mq_get()))
+        pa_pstream_send_release(p, block_id);
+    else
+        pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_RELEASE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(pa_client *c) {
+    pa_assert(c);
+
+    native_connection_unlink(PA_NATIVE_CONNECTION(c->userdata));
+    pa_log_info("Connection killed.");
+}
+
+static void client_send_event_cb(pa_client *client, const char*event, pa_proplist *pl) {
+    pa_tagstruct *t;
+    pa_native_connection *c;
+
+    pa_assert(client);
+    c = PA_NATIVE_CONNECTION(client->userdata);
+    pa_native_connection_assert_ref(c);
+
+    if (c->version < 15)
+      return;
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_CLIENT_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(c->pstream, t);
+}
+
+/*** module entry points ***/
+
+static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+
+    pa_assert(m);
+    pa_native_connection_assert_ref(c);
+    pa_assert(c->auth_timeout_event == e);
+
+    if (!c->authorized) {
+        native_connection_unlink(c);
+        pa_log_info("Connection terminated due to authentication timeout.");
+    }
+}
+
+void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *o) {
+    pa_native_connection *c;
+    char pname[128];
+    pa_client *client;
+    pa_client_new_data data;
+
+    pa_assert(p);
+    pa_assert(io);
+    pa_assert(o);
+
+    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
+        pa_iochannel_free(io);
+        return;
+    }
+
+    pa_client_new_data_init(&data);
+    data.module = o->module;
+    data.driver = __FILE__;
+    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "Native client (%s)", pname);
+    pa_proplist_sets(data.proplist, "native-protocol.peer", pname);
+    client = pa_client_new(p->core, &data);
+    pa_client_new_data_done(&data);
+
+    if (!client)
+        return;
+
+    c = pa_msgobject_new(pa_native_connection);
+    c->parent.parent.free = native_connection_free;
+    c->parent.process_msg = native_connection_process_msg;
+    c->protocol = p;
+    c->options = pa_native_options_ref(o);
+    c->authorized = false;
+    c->srbpending = NULL;
+
+    if (o->auth_anonymous) {
+        pa_log_info("Client authenticated anonymously.");
+        c->authorized = true;
+    }
+
+    if (!c->authorized &&
+        o->auth_ip_acl &&
+        pa_ip_acl_check(o->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
+
+        pa_log_info("Client authenticated by IP ACL.");
+        c->authorized = true;
+    }
+
+    if (!c->authorized)
+        c->auth_timeout_event = pa_core_rttime_new(p->core, pa_rtclock_now() + AUTH_TIMEOUT, auth_timeout, c);
+    else
+        c->auth_timeout_event = NULL;
+
+    c->is_local = pa_iochannel_socket_is_local(io);
+    c->version = 8;
+
+    c->client = client;
+    c->client->kill = client_kill_cb;
+    c->client->send_event = client_send_event_cb;
+    c->client->userdata = c;
+
+    c->rw_mempool = NULL;
+
+    c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
+    pa_pstream_set_receive_packet_callback(c->pstream, pstream_packet_callback, c);
+    pa_pstream_set_receive_memblock_callback(c->pstream, pstream_memblock_callback, c);
+    pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
+    pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c);
+    pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c);
+    pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c);
+
+    c->pdispatch = pa_pdispatch_new(p->core->mainloop, true, command_table, PA_COMMAND_MAX);
+
+    c->record_streams = pa_idxset_new(NULL, NULL);
+    c->output_streams = pa_idxset_new(NULL, NULL);
+
+    c->rrobin_index = PA_IDXSET_INVALID;
+    c->subscription = NULL;
+
+    pa_idxset_put(p->connections, c, NULL);
+
+#ifdef HAVE_CREDS
+    if (pa_iochannel_creds_supported(io))
+        pa_iochannel_creds_enable(io);
+#endif
+
+    pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_CONNECTION_PUT], c);
+}
+
+void pa_native_protocol_disconnect(pa_native_protocol *p, pa_module *m) {
+    pa_native_connection *c;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(m);
+
+    while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+        if (c->options->module == m)
+            native_connection_unlink(c);
+}
+
+static pa_native_protocol* native_protocol_new(pa_core *c) {
+    pa_native_protocol *p;
+    pa_native_hook_t h;
+
+    pa_assert(c);
+
+    p = pa_xnew(pa_native_protocol, 1);
+    PA_REFCNT_INIT(p);
+    p->core = c;
+    p->connections = pa_idxset_new(NULL, NULL);
+
+    p->servers = NULL;
+
+    p->extensions = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    for (h = 0; h < PA_NATIVE_HOOK_MAX; h++)
+        pa_hook_init(&p->hooks[h], p);
+
+    pa_assert_se(pa_shared_set(c, "native-protocol", p) >= 0);
+
+    return p;
+}
+
+pa_native_protocol* pa_native_protocol_get(pa_core *c) {
+    pa_native_protocol *p;
+
+    if ((p = pa_shared_get(c, "native-protocol")))
+        return pa_native_protocol_ref(p);
+
+    return native_protocol_new(c);
+}
+
+pa_native_protocol* pa_native_protocol_ref(pa_native_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    PA_REFCNT_INC(p);
+
+    return p;
+}
+
+void pa_native_protocol_unref(pa_native_protocol *p) {
+    pa_native_connection *c;
+    pa_native_hook_t h;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    if (PA_REFCNT_DEC(p) > 0)
+        return;
+
+    while ((c = pa_idxset_first(p->connections, NULL)))
+        native_connection_unlink(c);
+
+    pa_idxset_free(p->connections, NULL);
+
+    pa_strlist_free(p->servers);
+
+    for (h = 0; h < PA_NATIVE_HOOK_MAX; h++)
+        pa_hook_done(&p->hooks[h]);
+
+    pa_hashmap_free(p->extensions);
+
+    pa_assert_se(pa_shared_remove(p->core, "native-protocol") >= 0);
+
+    pa_xfree(p);
+}
+
+void pa_native_protocol_add_server_string(pa_native_protocol *p, const char *name) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+    pa_assert(name);
+
+    p->servers = pa_strlist_prepend(p->servers, name);
+
+    pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_SERVERS_CHANGED], p->servers);
+}
+
+void pa_native_protocol_remove_server_string(pa_native_protocol *p, const char *name) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+    pa_assert(name);
+
+    p->servers = pa_strlist_remove(p->servers, name);
+
+    pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_SERVERS_CHANGED], p->servers);
+}
+
+pa_hook *pa_native_protocol_hooks(pa_native_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    return p->hooks;
+}
+
+pa_strlist *pa_native_protocol_servers(pa_native_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    return p->servers;
+}
+
+int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_native_protocol_ext_cb_t cb) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+    pa_assert(m);
+    pa_assert(cb);
+    pa_assert(!pa_hashmap_get(p->extensions, m));
+
+    pa_assert_se(pa_hashmap_put(p->extensions, m, (void*) (unsigned long) cb) == 0);
+    return 0;
+}
+
+void pa_native_protocol_remove_ext(pa_native_protocol *p, pa_module *m) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+    pa_assert(m);
+
+    pa_assert_se(pa_hashmap_remove(p->extensions, m));
+}
+
+pa_native_options* pa_native_options_new(void) {
+    pa_native_options *o;
+
+    o = pa_xnew0(pa_native_options, 1);
+    PA_REFCNT_INIT(o);
+
+    return o;
+}
+
+pa_native_options* pa_native_options_ref(pa_native_options *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    PA_REFCNT_INC(o);
+
+    return o;
+}
+
+void pa_native_options_unref(pa_native_options *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (PA_REFCNT_DEC(o) > 0)
+        return;
+
+    pa_xfree(o->auth_group);
+
+    if (o->auth_ip_acl)
+        pa_ip_acl_free(o->auth_ip_acl);
+
+    if (o->auth_cookie)
+        pa_auth_cookie_unref(o->auth_cookie);
+
+    pa_xfree(o);
+}
+
+int pa_native_options_parse(pa_native_options *o, pa_core *c, pa_modargs *ma) {
+    bool enabled;
+    const char *acl;
+
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+    pa_assert(ma);
+
+    o->srbchannel = true;
+    if (pa_modargs_get_value_boolean(ma, "srbchannel", &o->srbchannel) < 0) {
+        pa_log("srbchannel= expects a boolean argument.");
+        return -1;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &o->auth_anonymous) < 0) {
+        pa_log("auth-anonymous= expects a boolean argument.");
+        return -1;
+    }
+
+    enabled = true;
+    if (pa_modargs_get_value_boolean(ma, "auth-group-enable", &enabled) < 0) {
+        pa_log("auth-group-enable= expects a boolean argument.");
+        return -1;
+    }
+
+    pa_xfree(o->auth_group);
+    o->auth_group = enabled ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", pa_in_system_mode() ? PA_ACCESS_GROUP : NULL)) : NULL;
+
+#ifndef HAVE_CREDS
+    if (o->auth_group)
+        pa_log_warn("Authentication group configured, but not available on local system. Ignoring.");
+#endif
+
+    if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
+        pa_ip_acl *ipa;
+
+        if (!(ipa = pa_ip_acl_new(acl))) {
+            pa_log("Failed to parse IP ACL '%s'", acl);
+            return -1;
+        }
+
+        if (o->auth_ip_acl)
+            pa_ip_acl_free(o->auth_ip_acl);
+
+        o->auth_ip_acl = ipa;
+    }
+
+    enabled = true;
+    if (pa_modargs_get_value_boolean(ma, "auth-cookie-enabled", &enabled) < 0) {
+        pa_log("auth-cookie-enabled= expects a boolean argument.");
+        return -1;
+    }
+
+    if (o->auth_cookie)
+        pa_auth_cookie_unref(o->auth_cookie);
+
+    if (enabled) {
+        const char *cn;
+
+        /* The new name for this is 'auth-cookie', for compat reasons
+         * we check the old name too */
+        cn = pa_modargs_get_value(ma, "auth-cookie", NULL);
+        if (!cn)
+            cn = pa_modargs_get_value(ma, "cookie", NULL);
+
+        if (cn)
+            o->auth_cookie = pa_auth_cookie_get(c, cn, true, PA_NATIVE_COOKIE_LENGTH);
+        else {
+            o->auth_cookie = pa_auth_cookie_get(c, PA_NATIVE_COOKIE_FILE, false, PA_NATIVE_COOKIE_LENGTH);
+            if (!o->auth_cookie) {
+                char *fallback_path;
+
+                if (pa_append_to_home_dir(PA_NATIVE_COOKIE_FILE_FALLBACK, &fallback_path) >= 0) {
+                    o->auth_cookie = pa_auth_cookie_get(c, fallback_path, false, PA_NATIVE_COOKIE_LENGTH);
+                    pa_xfree(fallback_path);
+                }
+
+                if (!o->auth_cookie)
+                    o->auth_cookie = pa_auth_cookie_get(c, PA_NATIVE_COOKIE_FILE, true, PA_NATIVE_COOKIE_LENGTH);
+            }
+        }
+
+        if (!o->auth_cookie)
+            return -1;
+
+    } else
+          o->auth_cookie = NULL;
+
+    return 0;
+}
+
+pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c) {
+    pa_native_connection_assert_ref(c);
+
+    return c->pstream;
+}
+
+pa_client* pa_native_connection_get_client(pa_native_connection *c) {
+    pa_native_connection_assert_ref(c);
+
+    return c->client;
+}
diff --git a/src/pulsecore/protocol-native.h b/src/pulsecore/protocol-native.h
new file mode 100644 (file)
index 0000000..0347fdf
--- /dev/null
@@ -0,0 +1,88 @@
+#ifndef fooprotocolnativehfoo
+#define fooprotocolnativehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/ipacl.h>
+#include <pulsecore/auth-cookie.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/hook-list.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/tagstruct.h>
+
+typedef struct pa_native_protocol pa_native_protocol;
+
+typedef struct pa_native_connection pa_native_connection;
+
+typedef struct pa_native_options {
+    PA_REFCNT_DECLARE;
+
+    pa_module *module;
+
+    bool auth_anonymous;
+    bool srbchannel;
+    char *auth_group;
+    pa_ip_acl *auth_ip_acl;
+    pa_auth_cookie *auth_cookie;
+} pa_native_options;
+
+typedef enum pa_native_hook {
+    PA_NATIVE_HOOK_SERVERS_CHANGED,
+    PA_NATIVE_HOOK_CONNECTION_PUT,
+    PA_NATIVE_HOOK_CONNECTION_UNLINK,
+    PA_NATIVE_HOOK_MAX
+} pa_native_hook_t;
+
+pa_native_protocol* pa_native_protocol_get(pa_core *core);
+pa_native_protocol* pa_native_protocol_ref(pa_native_protocol *p);
+void pa_native_protocol_unref(pa_native_protocol *p);
+void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *a);
+void pa_native_protocol_disconnect(pa_native_protocol *p, pa_module *m);
+
+pa_hook *pa_native_protocol_hooks(pa_native_protocol *p);
+
+void pa_native_protocol_add_server_string(pa_native_protocol *p, const char *name);
+void pa_native_protocol_remove_server_string(pa_native_protocol *p, const char *name);
+pa_strlist *pa_native_protocol_servers(pa_native_protocol *p);
+
+typedef int (*pa_native_protocol_ext_cb_t)(
+        pa_native_protocol *p,
+        pa_module *m,
+        pa_native_connection *c,
+        uint32_t tag,
+        pa_tagstruct *t);
+
+int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_native_protocol_ext_cb_t cb);
+void pa_native_protocol_remove_ext(pa_native_protocol *p, pa_module *m);
+
+pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c);
+pa_client* pa_native_connection_get_client(pa_native_connection *c);
+
+pa_native_options* pa_native_options_new(void);
+pa_native_options* pa_native_options_ref(pa_native_options *o);
+void pa_native_options_unref(pa_native_options *o);
+int pa_native_options_parse(pa_native_options *o, pa_core *c, pa_modargs *ma);
+
+#endif
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
new file mode 100644 (file)
index 0000000..923ec87
--- /dev/null
@@ -0,0 +1,771 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/client.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/shared.h>
+
+#include "protocol-simple.h"
+
+/* Don't allow more than this many concurrent connections */
+#define MAX_CONNECTIONS 10
+
+typedef struct connection {
+    pa_msgobject parent;
+    pa_simple_protocol *protocol;
+    pa_simple_options *options;
+    pa_iochannel *io;
+    pa_sink_input *sink_input;
+    pa_source_output *source_output;
+    pa_client *client;
+    pa_memblockq *input_memblockq, *output_memblockq;
+
+    bool dead;
+
+    struct {
+        pa_memblock *current_memblock;
+        size_t memblock_index;
+        pa_atomic_t missing;
+        bool underrun;
+    } playback;
+} connection;
+
+PA_DEFINE_PRIVATE_CLASS(connection, pa_msgobject);
+#define CONNECTION(o) (connection_cast(o))
+
+struct pa_simple_protocol {
+    PA_REFCNT_DECLARE;
+
+    pa_core *core;
+    pa_idxset *connections;
+};
+
+enum {
+    SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+    SINK_INPUT_MESSAGE_DISABLE_PREBUF /* disabled prebuf, get playback started. */
+};
+
+enum {
+    CONNECTION_MESSAGE_REQUEST_DATA,      /* data requested from sink input from the main loop */
+    CONNECTION_MESSAGE_POST_DATA,         /* data from source output to main loop */
+    CONNECTION_MESSAGE_UNLINK_CONNECTION  /* Please drop the connection now */
+};
+
+#define PLAYBACK_BUFFER_SECONDS (.5)
+#define PLAYBACK_BUFFER_FRAGMENTS (10)
+#define RECORD_BUFFER_SECONDS (5)
+#define DEFAULT_SINK_LATENCY (300*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
+
+static void connection_unlink(connection *c) {
+    pa_assert(c);
+
+    if (!c->protocol)
+        return;
+
+    if (c->options) {
+        pa_simple_options_unref(c->options);
+        c->options = NULL;
+    }
+
+    if (c->sink_input) {
+        pa_sink_input_unlink(c->sink_input);
+        pa_sink_input_unref(c->sink_input);
+        c->sink_input = NULL;
+    }
+
+    if (c->source_output) {
+        pa_source_output_unlink(c->source_output);
+        pa_source_output_unref(c->source_output);
+        c->source_output = NULL;
+    }
+
+    if (c->client) {
+        pa_client_free(c->client);
+        c->client = NULL;
+    }
+
+    if (c->io) {
+        pa_iochannel_free(c->io);
+        c->io = NULL;
+    }
+
+    pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+    c->protocol = NULL;
+    connection_unref(c);
+}
+
+static void connection_free(pa_object *o) {
+    connection *c = CONNECTION(o);
+    pa_assert(c);
+
+    if (c->playback.current_memblock)
+        pa_memblock_unref(c->playback.current_memblock);
+
+    if (c->input_memblockq)
+        pa_memblockq_free(c->input_memblockq);
+    if (c->output_memblockq)
+        pa_memblockq_free(c->output_memblockq);
+
+    pa_xfree(c);
+}
+
+static int do_read(connection *c) {
+    pa_memchunk chunk;
+    ssize_t r;
+    size_t l;
+    void *p;
+    size_t space = 0;
+
+    connection_assert_ref(c);
+
+    if (!c->sink_input || (l = (size_t) pa_atomic_load(&c->playback.missing)) <= 0)
+        return 0;
+
+    if (c->playback.current_memblock) {
+
+        space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
+
+        if (space <= 0) {
+            pa_memblock_unref(c->playback.current_memblock);
+            c->playback.current_memblock = NULL;
+        }
+    }
+
+    if (!c->playback.current_memblock) {
+        pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1));
+        c->playback.memblock_index = 0;
+
+        space = pa_memblock_get_length(c->playback.current_memblock);
+    }
+
+    if (l > space)
+        l = space;
+
+    p = pa_memblock_acquire(c->playback.current_memblock);
+    r = pa_iochannel_read(c->io, (uint8_t*) p + c->playback.memblock_index, l);
+    pa_memblock_release(c->playback.current_memblock);
+
+    if (r <= 0) {
+
+        if (r < 0 && (errno == EINTR || errno == EAGAIN))
+            return 0;
+
+        pa_log_debug("read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno));
+        return -1;
+    }
+
+    chunk.memblock = c->playback.current_memblock;
+    chunk.index = c->playback.memblock_index;
+    chunk.length = (size_t) r;
+
+    c->playback.memblock_index += (size_t) r;
+
+    pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
+    pa_atomic_sub(&c->playback.missing, (int) r);
+
+    return 0;
+}
+
+static int do_write(connection *c) {
+    pa_memchunk chunk;
+    ssize_t r;
+    void *p;
+
+    connection_assert_ref(c);
+
+    if (!c->source_output)
+        return 0;
+
+    if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) {
+/*         pa_log("peek failed"); */
+        return 0;
+    }
+
+    pa_assert(chunk.memblock);
+    pa_assert(chunk.length);
+
+    p = pa_memblock_acquire(chunk.memblock);
+    r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
+    pa_memblock_release(chunk.memblock);
+
+    pa_memblock_unref(chunk.memblock);
+
+    if (r < 0) {
+        pa_log("write(): %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_memblockq_drop(c->output_memblockq, (size_t) r);
+
+    return 1;
+}
+
+static void do_work(connection *c) {
+    connection_assert_ref(c);
+
+    if (c->dead)
+        return;
+
+    if (pa_iochannel_is_readable(c->io))
+        if (do_read(c) < 0)
+            goto fail;
+
+    if (!c->sink_input && pa_iochannel_is_hungup(c->io))
+        goto fail;
+
+    while (pa_iochannel_is_writable(c->io)) {
+        int r = do_write(c);
+        if (r < 0)
+            goto fail;
+        if (r == 0)
+            break;
+    }
+
+    return;
+
+fail:
+
+    if (c->sink_input) {
+
+        /* If there is a sink input, we first drain what we already have read before shutting down the connection */
+        c->dead = true;
+
+        pa_iochannel_free(c->io);
+        c->io = NULL;
+
+        pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL);
+    } else
+        connection_unlink(c);
+}
+
+static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    connection *c = CONNECTION(o);
+    connection_assert_ref(c);
+
+    if (!c->protocol)
+        return -1;
+
+    switch (code) {
+        case CONNECTION_MESSAGE_REQUEST_DATA:
+            do_work(c);
+            break;
+
+        case CONNECTION_MESSAGE_POST_DATA:
+/*             pa_log("got data %u", chunk->length); */
+            pa_memblockq_push_align(c->output_memblockq, chunk);
+            do_work(c);
+            break;
+
+        case CONNECTION_MESSAGE_UNLINK_CONNECTION:
+            connection_unlink(c);
+            break;
+    }
+
+    return 0;
+}
+
+/*** sink_input callbacks ***/
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_sink_input *i = PA_SINK_INPUT(o);
+    connection*c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    switch (code) {
+
+        case SINK_INPUT_MESSAGE_POST_DATA: {
+            pa_assert(chunk);
+
+            /* New data from the main loop */
+            pa_memblockq_push_align(c->input_memblockq, chunk);
+
+            if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
+                pa_log_debug("Requesting rewind due to end of underrun.");
+                pa_sink_input_request_rewind(c->sink_input, 0, false, true, false);
+            }
+
+/*             pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
+
+            return 0;
+        }
+
+        case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
+            pa_memblockq_prebuf_disable(c->input_memblockq);
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+        }
+
+        default:
+            return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
+    }
+}
+
+/* Called from thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+    pa_assert(chunk);
+
+    if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
+
+        c->playback.underrun = true;
+
+        if (c->dead && pa_sink_input_safe_to_remove(i))
+            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+
+        return -1;
+    } else {
+        size_t m;
+
+        chunk->length = PA_MIN(length, chunk->length);
+
+        c->playback.underrun = false;
+
+        pa_memblockq_drop(c->input_memblockq, chunk->length);
+        m = pa_memblockq_pop_missing(c->input_memblockq);
+
+        if (m > 0)
+            if (pa_atomic_add(&c->playback.missing, (int) m) <= 0)
+                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
+
+        return 0;
+    }
+}
+
+/* Called from thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    /* If we are in an underrun, then we don't rewind */
+    if (i->thread_info.underrun_for > 0)
+        return;
+
+    pa_memblockq_rewind(c->input_memblockq, nbytes);
+}
+
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+
+    connection_unlink(CONNECTION(i->userdata));
+}
+
+/*** source_output callbacks ***/
+
+/* Called from thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    connection *c;
+
+    pa_source_output_assert_ref(o);
+    c = CONNECTION(o->userdata);
+    pa_assert(c);
+    pa_assert(chunk);
+
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+}
+
+/* Called from main context */
+static void source_output_kill_cb(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+
+    connection_unlink(CONNECTION(o->userdata));
+}
+
+/* Called from main context */
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+    connection*c;
+
+    pa_source_output_assert_ref(o);
+    c = CONNECTION(o->userdata);
+    pa_assert(c);
+
+    return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(pa_client *client) {
+    connection*c;
+
+    pa_assert(client);
+    c = CONNECTION(client->userdata);
+    pa_assert(c);
+
+    connection_unlink(c);
+}
+
+/*** pa_iochannel callbacks ***/
+
+static void io_callback(pa_iochannel*io, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    connection_assert_ref(c);
+    pa_assert(io);
+
+    do_work(c);
+}
+
+/*** socket_server callbacks ***/
+
+void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simple_options *o) {
+    connection *c = NULL;
+    char pname[128];
+    pa_client_new_data client_data;
+
+    pa_assert(p);
+    pa_assert(io);
+    pa_assert(o);
+
+    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
+        pa_iochannel_free(io);
+        return;
+    }
+
+    c = pa_msgobject_new(connection);
+    c->parent.parent.free = connection_free;
+    c->parent.process_msg = connection_process_msg;
+    c->io = io;
+    pa_iochannel_set_callback(c->io, io_callback, c);
+
+    c->sink_input = NULL;
+    c->source_output = NULL;
+    c->input_memblockq = c->output_memblockq = NULL;
+    c->protocol = p;
+    c->options = pa_simple_options_ref(o);
+    c->playback.current_memblock = NULL;
+    c->playback.memblock_index = 0;
+    c->dead = false;
+    c->playback.underrun = true;
+    pa_atomic_store(&c->playback.missing, 0);
+
+    pa_client_new_data_init(&client_data);
+    client_data.module = o->module;
+    client_data.driver = __FILE__;
+    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+    pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "Simple client (%s)", pname);
+    pa_proplist_sets(client_data.proplist, "simple-protocol.peer", pname);
+    c->client = pa_client_new(p->core, &client_data);
+    pa_client_new_data_done(&client_data);
+
+    if (!c->client)
+        goto fail;
+
+    c->client->kill = client_kill_cb;
+    c->client->userdata = c;
+
+    if (o->playback) {
+        pa_sink_input_new_data data;
+        pa_memchunk silence;
+        size_t l;
+        pa_sink *sink;
+
+        if (!(sink = pa_namereg_get(c->protocol->core, o->default_sink, PA_NAMEREG_SINK))) {
+            pa_log("Failed to get sink.");
+            goto fail;
+        }
+
+        pa_sink_input_new_data_init(&data);
+        data.driver = __FILE__;
+        data.module = o->module;
+        data.client = c->client;
+        pa_sink_input_new_data_set_sink(&data, sink, false);
+        pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+        pa_sink_input_new_data_set_sample_spec(&data, &o->sample_spec);
+
+        pa_sink_input_new(&c->sink_input, p->core, &data);
+        pa_sink_input_new_data_done(&data);
+
+        if (!c->sink_input) {
+            pa_log("Failed to create sink input.");
+            goto fail;
+        }
+
+        c->sink_input->parent.process_msg = sink_input_process_msg;
+        c->sink_input->pop = sink_input_pop_cb;
+        c->sink_input->process_rewind = sink_input_process_rewind_cb;
+        c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+        c->sink_input->kill = sink_input_kill_cb;
+        c->sink_input->userdata = c;
+
+        pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
+        l = (size_t) ((double) pa_bytes_per_second(&o->sample_spec)*PLAYBACK_BUFFER_SECONDS);
+        pa_sink_input_get_silence(c->sink_input, &silence);
+        c->input_memblockq = pa_memblockq_new(
+                "simple protocol connection input_memblockq",
+                0,
+                l,
+                l,
+                &o->sample_spec,
+                (size_t) -1,
+                l/PLAYBACK_BUFFER_FRAGMENTS,
+                0,
+                &silence);
+        pa_memblock_unref(silence.memblock);
+
+        pa_iochannel_socket_set_rcvbuf(io, l);
+
+        pa_atomic_store(&c->playback.missing, (int) pa_memblockq_pop_missing(c->input_memblockq));
+
+        pa_sink_input_put(c->sink_input);
+    }
+
+    if (o->record) {
+        pa_source_output_new_data data;
+        size_t l;
+        pa_source *source;
+
+        if (!(source = pa_namereg_get(c->protocol->core, o->default_source, PA_NAMEREG_SOURCE))) {
+            pa_log("Failed to get source.");
+            goto fail;
+        }
+
+        pa_source_output_new_data_init(&data);
+        data.driver = __FILE__;
+        data.module = o->module;
+        data.client = c->client;
+        pa_source_output_new_data_set_source(&data, source, false);
+        pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+        pa_source_output_new_data_set_sample_spec(&data, &o->sample_spec);
+
+        pa_source_output_new(&c->source_output, p->core, &data);
+        pa_source_output_new_data_done(&data);
+
+        if (!c->source_output) {
+            pa_log("Failed to create source output.");
+            goto fail;
+        }
+        c->source_output->push = source_output_push_cb;
+        c->source_output->kill = source_output_kill_cb;
+        c->source_output->get_latency = source_output_get_latency_cb;
+        c->source_output->userdata = c;
+
+        pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
+        l = (size_t) (pa_bytes_per_second(&o->sample_spec)*RECORD_BUFFER_SECONDS);
+        c->output_memblockq = pa_memblockq_new(
+                "simple protocol connection output_memblockq",
+                0,
+                l,
+                0,
+                &o->sample_spec,
+                1,
+                0,
+                0,
+                NULL);
+        pa_iochannel_socket_set_sndbuf(io, l);
+
+        pa_source_output_put(c->source_output);
+    }
+
+    pa_idxset_put(p->connections, c, NULL);
+
+    return;
+
+fail:
+    connection_unlink(c);
+}
+
+void pa_simple_protocol_disconnect(pa_simple_protocol *p, pa_module *m) {
+    connection *c;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(m);
+
+    while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+        if (c->options->module == m)
+            connection_unlink(c);
+}
+
+static pa_simple_protocol* simple_protocol_new(pa_core *c) {
+    pa_simple_protocol *p;
+
+    pa_assert(c);
+
+    p = pa_xnew(pa_simple_protocol, 1);
+    PA_REFCNT_INIT(p);
+    p->core = c;
+    p->connections = pa_idxset_new(NULL, NULL);
+
+    pa_assert_se(pa_shared_set(c, "simple-protocol", p) >= 0);
+
+    return p;
+}
+
+pa_simple_protocol* pa_simple_protocol_get(pa_core *c) {
+    pa_simple_protocol *p;
+
+    if ((p = pa_shared_get(c, "simple-protocol")))
+        return pa_simple_protocol_ref(p);
+
+    return simple_protocol_new(c);
+}
+
+pa_simple_protocol* pa_simple_protocol_ref(pa_simple_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    PA_REFCNT_INC(p);
+
+    return p;
+}
+
+void pa_simple_protocol_unref(pa_simple_protocol *p) {
+    connection *c;
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    if (PA_REFCNT_DEC(p) > 0)
+        return;
+
+    while ((c = pa_idxset_first(p->connections, NULL)))
+        connection_unlink(c);
+
+    pa_idxset_free(p->connections, NULL);
+
+    pa_assert_se(pa_shared_remove(p->core, "simple-protocol") >= 0);
+
+    pa_xfree(p);
+}
+
+pa_simple_options* pa_simple_options_new(void) {
+    pa_simple_options *o;
+
+    o = pa_xnew0(pa_simple_options, 1);
+    PA_REFCNT_INIT(o);
+
+    o->record = false;
+    o->playback = true;
+
+    return o;
+}
+
+pa_simple_options* pa_simple_options_ref(pa_simple_options *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    PA_REFCNT_INC(o);
+
+    return o;
+}
+
+void pa_simple_options_unref(pa_simple_options *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (PA_REFCNT_DEC(o) > 0)
+        return;
+
+    pa_xfree(o->default_sink);
+    pa_xfree(o->default_source);
+
+    pa_xfree(o);
+}
+
+int pa_simple_options_parse(pa_simple_options *o, pa_core *c, pa_modargs *ma) {
+    bool enabled;
+
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+    pa_assert(ma);
+
+    o->sample_spec = c->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &o->sample_spec, &o->channel_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Failed to parse sample type specification.");
+        return -1;
+    }
+
+    pa_xfree(o->default_source);
+    o->default_source = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
+
+    pa_xfree(o->default_sink);
+    o->default_sink = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+
+    enabled = o->record;
+    if (pa_modargs_get_value_boolean(ma, "record", &enabled) < 0) {
+        pa_log("record= expects a boolean argument.");
+        return -1;
+    }
+    o->record = enabled;
+
+    enabled = o->playback;
+    if (pa_modargs_get_value_boolean(ma, "playback", &enabled) < 0) {
+        pa_log("playback= expects a boolean argument.");
+        return -1;
+    }
+    o->playback = enabled;
+
+    if (!o->playback && !o->record) {
+        pa_log("neither playback nor recording enabled for protocol.");
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/src/pulsecore/protocol-simple.h b/src/pulsecore/protocol-simple.h
new file mode 100644 (file)
index 0000000..0a72cd7
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef fooprotocolsimplehfoo
+#define fooprotocolsimplehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/socket-server.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core.h>
+#include <pulsecore/modargs.h>
+
+typedef struct pa_simple_protocol pa_simple_protocol;
+
+typedef struct pa_simple_options {
+    PA_REFCNT_DECLARE;
+
+    pa_module *module;
+
+    char *default_sink, *default_source;
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+
+    bool record:1;
+    bool playback:1;
+} pa_simple_options;
+
+pa_simple_protocol* pa_simple_protocol_get(pa_core*core);
+pa_simple_protocol* pa_simple_protocol_ref(pa_simple_protocol *p);
+void pa_simple_protocol_unref(pa_simple_protocol *p);
+void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simple_options *o);
+void pa_simple_protocol_disconnect(pa_simple_protocol *p, pa_module *m);
+
+pa_simple_options* pa_simple_options_new(void);
+pa_simple_options* pa_simple_options_ref(pa_simple_options *o);
+void pa_simple_options_unref(pa_simple_options *o);
+int pa_simple_options_parse(pa_simple_options *o, pa_core *c, pa_modargs *ma);
+
+#endif
diff --git a/src/pulsecore/pstream-util.c b/src/pulsecore/pstream-util.c
new file mode 100644 (file)
index 0000000..d0d6c66
--- /dev/null
@@ -0,0 +1,198 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/refcnt.h>
+#include <pulse/xmalloc.h>
+
+#include "pstream-util.h"
+
+static void pa_pstream_send_tagstruct_with_ancil_data(pa_pstream *p, pa_tagstruct *t, pa_cmsg_ancil_data *ancil_data) {
+    size_t length;
+    const uint8_t *data;
+    pa_packet *packet;
+
+    pa_assert(p);
+    pa_assert(t);
+
+    pa_assert_se(data = pa_tagstruct_data(t, &length));
+    pa_assert_se(packet = pa_packet_new_data(data, length));
+    pa_tagstruct_free(t);
+
+    pa_pstream_send_packet(p, packet, ancil_data);
+    pa_packet_unref(packet);
+}
+
+#ifdef HAVE_CREDS
+
+void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const pa_creds *creds) {
+    if (creds) {
+        pa_cmsg_ancil_data a;
+
+        a.nfd = 0;
+        a.creds_valid = true;
+        a.creds = *creds;
+        pa_pstream_send_tagstruct_with_ancil_data(p, t, &a);
+    }
+    else
+        pa_pstream_send_tagstruct_with_ancil_data(p, t, NULL);
+}
+
+/* @close_fds: If set then the pstreams code, after invoking a sendmsg(),
+ * will close all passed fds.
+ *
+ * Such fds cannot be closed here as this might lead to freeing them
+ * before they're actually passed to the other end. The internally-used
+ * pa_pstream_send_packet() does not do any actual writes and just
+ * defers write events over the pstream. */
+void pa_pstream_send_tagstruct_with_fds(pa_pstream *p, pa_tagstruct *t, int nfd, const int *fds,
+                                        bool close_fds) {
+    if (nfd > 0) {
+        pa_cmsg_ancil_data a;
+
+        a.nfd = nfd;
+        a.creds_valid = false;
+        a.close_fds_on_cleanup = close_fds;
+        pa_assert(nfd <= MAX_ANCIL_DATA_FDS);
+        memcpy(a.fds, fds, sizeof(int) * nfd);
+        pa_pstream_send_tagstruct_with_ancil_data(p, t, &a);
+    }
+    else
+        pa_pstream_send_tagstruct_with_ancil_data(p, t, NULL);
+}
+
+#else
+
+void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const pa_creds *creds) {
+    pa_pstream_send_tagstruct_with_ancil_data(p, t, NULL);
+}
+
+void PA_GCC_NORETURN pa_pstream_send_tagstruct_with_fds(pa_pstream *p, pa_tagstruct *t, int nfd, const int *fds,
+                                                        bool close_fds) {
+    pa_assert_not_reached();
+}
+
+#endif
+
+void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {
+    pa_tagstruct *t;
+
+    pa_assert_se(t = pa_tagstruct_new());
+    pa_tagstruct_putu32(t, PA_COMMAND_ERROR);
+    pa_tagstruct_putu32(t, tag);
+    pa_tagstruct_putu32(t, error);
+    pa_pstream_send_tagstruct(p, t);
+}
+
+void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) {
+    pa_tagstruct *t;
+
+    pa_assert_se(t = pa_tagstruct_new());
+    pa_tagstruct_putu32(t, PA_COMMAND_REPLY);
+    pa_tagstruct_putu32(t, tag);
+    pa_pstream_send_tagstruct(p, t);
+}
+
+/* Before sending blocks from a memfd-backed pool over the pipe, we
+ * must call this method first.
+ *
+ * This is needed to transfer memfd blocks without passing their fd
+ * every time, thus minimizing overhead and avoiding fd leaks.
+ *
+ * On registration a packet is sent with the memfd fd as ancil data;
+ * such packet has an ID that uniquely identifies the pool's memfd
+ * region. Upon arrival the other end creates a permanent mapping
+ * between that ID and the passed memfd memory area.
+ *
+ * By doing so, we won't need to reference the pool's memfd fd any
+ * further - just its ID. Both endpoints can then close their fds. */
+int pa_pstream_register_memfd_mempool(pa_pstream *p, pa_mempool *pool, const char **fail_reason) {
+#if defined(HAVE_CREDS) && defined(HAVE_MEMFD)
+    unsigned shm_id;
+    int memfd_fd, ret = -1;
+    pa_tagstruct *t;
+    bool per_client_mempool;
+
+    pa_assert(p);
+    pa_assert(fail_reason);
+
+    *fail_reason = NULL;
+    per_client_mempool = pa_mempool_is_per_client(pool);
+
+    pa_pstream_ref(p);
+
+    if (!pa_mempool_is_shared(pool)) {
+        *fail_reason = "mempool is not shared";
+        goto finish;
+    }
+
+    if (!pa_mempool_is_memfd_backed(pool)) {
+        *fail_reason = "mempool is not memfd-backed";
+        goto finish;
+    }
+
+    if (pa_mempool_get_shm_id(pool, &shm_id)) {
+        *fail_reason = "could not extract pool SHM ID";
+        goto finish;
+    }
+
+    if (!pa_pstream_get_memfd(p)) {
+        *fail_reason = "pipe does not support memfd transport";
+        goto finish;
+    }
+
+    memfd_fd = (per_client_mempool) ? pa_mempool_take_memfd_fd(pool) :
+                                      pa_mempool_get_memfd_fd(pool);
+
+    /* Note! For per-client mempools we've taken ownership of the memfd
+     * fd, and we're thus the sole code path responsible for closing it.
+     * In case of any failure, it MUST be closed. */
+
+    if (pa_pstream_attach_memfd_shmid(p, shm_id, memfd_fd)) {
+        *fail_reason = "could not attach memfd SHM ID to pipe";
+
+        if (per_client_mempool)
+            pa_assert_se(pa_close(memfd_fd) == 0);
+        goto finish;
+    }
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, PA_COMMAND_REGISTER_MEMFD_SHMID);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, shm_id);
+    pa_pstream_send_tagstruct_with_fds(p, t, 1, &memfd_fd, per_client_mempool);
+
+    ret = 0;
+finish:
+    pa_pstream_unref(p);
+    return ret;
+
+#else
+    pa_assert(fail_reason);
+    *fail_reason = "memfd support not compiled in";
+    return -1;
+#endif
+}
diff --git a/src/pulsecore/pstream-util.h b/src/pulsecore/pstream-util.h
new file mode 100644 (file)
index 0000000..1191d48
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef foopstreamutilhfoo
+#define foopstreamutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/tagstruct.h>
+#include <pulsecore/creds.h>
+
+/* The tagstruct is freed!*/
+void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const pa_creds *creds);
+void pa_pstream_send_tagstruct_with_fds(pa_pstream *p, pa_tagstruct *t, int nfd, const int *fds, bool close_fds);
+
+#define pa_pstream_send_tagstruct(p, t) pa_pstream_send_tagstruct_with_creds((p), (t), NULL)
+
+void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error);
+void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag);
+
+int pa_pstream_register_memfd_mempool(pa_pstream *p, pa_mempool *pool, const char **fail_reason);
+
+#endif
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
new file mode 100644 (file)
index 0000000..3b94a3a
--- /dev/null
@@ -0,0 +1,1282 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/idxset.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/queue.h>
+#include <pulsecore/log.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/macro.h>
+
+#include "pstream.h"
+
+/* We piggyback information if audio data blocks are stored in SHM on the seek mode */
+#define PA_FLAG_SHMDATA     0x80000000LU
+#define PA_FLAG_SHMDATA_MEMFD_BLOCK         0x20000000LU
+#define PA_FLAG_SHMRELEASE  0x40000000LU
+#define PA_FLAG_SHMREVOKE   0xC0000000LU
+#define PA_FLAG_SHMMASK     0xFF000000LU
+#define PA_FLAG_SEEKMASK    0x000000FFLU
+#define PA_FLAG_SHMWRITABLE 0x00800000LU
+
+/* The sequence descriptor header consists of 5 32bit integers: */
+enum {
+    PA_PSTREAM_DESCRIPTOR_LENGTH,
+    PA_PSTREAM_DESCRIPTOR_CHANNEL,
+    PA_PSTREAM_DESCRIPTOR_OFFSET_HI,
+    PA_PSTREAM_DESCRIPTOR_OFFSET_LO,
+    PA_PSTREAM_DESCRIPTOR_FLAGS,
+    PA_PSTREAM_DESCRIPTOR_MAX
+};
+
+/* If we have an SHM block, this info follows the descriptor */
+enum {
+    PA_PSTREAM_SHM_BLOCKID,
+    PA_PSTREAM_SHM_SHMID,
+    PA_PSTREAM_SHM_INDEX,
+    PA_PSTREAM_SHM_LENGTH,
+    PA_PSTREAM_SHM_MAX
+};
+
+typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX];
+
+#define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t))
+
+#define MINIBUF_SIZE (256)
+
+/* To allow uploading a single sample in one frame, this value should be the
+ * same size (16 MB) as PA_SCACHE_ENTRY_SIZE_MAX from pulsecore/core-scache.h.
+ */
+#define FRAME_SIZE_MAX_ALLOW (1024*1024*16)
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
+
+struct item_info {
+    enum {
+        PA_PSTREAM_ITEM_PACKET,
+        PA_PSTREAM_ITEM_MEMBLOCK,
+        PA_PSTREAM_ITEM_SHMRELEASE,
+        PA_PSTREAM_ITEM_SHMREVOKE
+    } type;
+
+    /* packet info */
+    pa_packet *packet;
+#ifdef HAVE_CREDS
+    bool with_ancil_data;
+    pa_cmsg_ancil_data ancil_data;
+#endif
+
+    /* memblock info */
+    pa_memchunk chunk;
+    uint32_t channel;
+    int64_t offset;
+    pa_seek_mode_t seek_mode;
+
+    /* release/revoke info */
+    uint32_t block_id;
+};
+
+struct pstream_read {
+    pa_pstream_descriptor descriptor;
+    pa_memblock *memblock;
+    pa_packet *packet;
+    uint32_t shm_info[PA_PSTREAM_SHM_MAX];
+    void *data;
+    size_t index;
+};
+
+struct pa_pstream {
+    PA_REFCNT_DECLARE;
+
+    pa_mainloop_api *mainloop;
+    pa_defer_event *defer_event;
+    pa_iochannel *io;
+    pa_srbchannel *srb, *srbpending;
+    bool is_srbpending;
+
+    pa_queue *send_queue;
+
+    bool dead;
+
+    struct {
+        union {
+            uint8_t minibuf[MINIBUF_SIZE];
+            pa_pstream_descriptor descriptor;
+        };
+        struct item_info* current;
+        void *data;
+        size_t index;
+        int minibuf_validsize;
+        pa_memchunk memchunk;
+    } write;
+
+    struct pstream_read readio, readsrb;
+
+    /* @use_shm: beside copying the full audio data to the other
+     * PA end, this pipe supports just sending references of the
+     * same audio data blocks if they reside in a SHM pool.
+     *
+     * @use_memfd: pipe supports sending SHM memfd block references
+     *
+     * @registered_memfd_ids: registered memfd pools SHM IDs. Check
+     * pa_pstream_register_memfd_mempool() for more information. */
+    bool use_shm, use_memfd;
+    pa_idxset *registered_memfd_ids;
+
+    pa_memimport *import;
+    pa_memexport *export;
+
+    pa_pstream_packet_cb_t receive_packet_callback;
+    void *receive_packet_callback_userdata;
+
+    pa_pstream_memblock_cb_t receive_memblock_callback;
+    void *receive_memblock_callback_userdata;
+
+    pa_pstream_notify_cb_t drain_callback;
+    void *drain_callback_userdata;
+
+    pa_pstream_notify_cb_t die_callback;
+    void *die_callback_userdata;
+
+    pa_pstream_block_id_cb_t revoke_callback;
+    void *revoke_callback_userdata;
+
+    pa_pstream_block_id_cb_t release_callback;
+    void *release_callback_userdata;
+
+    pa_mempool *mempool;
+
+#ifdef HAVE_CREDS
+    pa_cmsg_ancil_data read_ancil_data, *write_ancil_data;
+    bool send_ancil_data_now;
+#endif
+};
+
+#ifdef HAVE_CREDS
+/*
+ * memfd-backed SHM pools blocks transfer occur without passing the pool's
+ * fd every time, thus minimizing overhead and avoiding fd leaks. A
+ * REGISTER_MEMFD_SHMID command is sent, with the pool's memfd fd, very early
+ * on. This command has an ID that uniquely identifies the pool in question.
+ * Further pool's block references can then be exclusively done using such ID;
+ * the fd can be safely closed – on both ends – afterwards.
+ *
+ * On the sending side of this command, we want to close the passed fds
+ * directly after being sent. Meanwhile we're only allowed to asynchronously
+ * schedule packet writes to the pstream, so the job of closing passed fds is
+ * left to the pstream's actual writing function do_write(): it knows the
+ * exact point in time where the fds are passed to the other end through
+ * iochannels and the sendmsg() system call.
+ *
+ * Nonetheless not all code paths in the system desire their socket-passed
+ * fds to be closed after the send. srbchannel needs the passed fds to still
+ * be open for further communication. System-wide global memfd-backed pools
+ * also require the passed fd to be open: they pass the same fd, with the same
+ * ID registration mechanism, for each newly connected client to the system.
+ *
+ * So from all of the above, never close the ancillary fds by your own and
+ * always call below method instead. It takes care of closing the passed fds
+ * _only if allowed_ by the code paths that originally created them to do so.
+ * Moreover, it is multiple-invocations safe: failure handlers can, and
+ * should, call it for passed fds cleanup without worrying too much about
+ * the system state.
+ */
+void pa_cmsg_ancil_data_close_fds(struct pa_cmsg_ancil_data *ancil) {
+    if (ancil && ancil->nfd > 0 && ancil->close_fds_on_cleanup) {
+        int i;
+
+        pa_assert(ancil->nfd <= MAX_ANCIL_DATA_FDS);
+
+        for (i = 0; i < ancil->nfd; i++)
+            if (ancil->fds[i] != -1) {
+                pa_assert_se(pa_close(ancil->fds[i]) == 0);
+                ancil->fds[i] = -1;
+            }
+
+        ancil->nfd = 0;
+        ancil->close_fds_on_cleanup = false;
+    }
+}
+#endif
+
+static int do_write(pa_pstream *p);
+static int do_read(pa_pstream *p, struct pstream_read *re);
+
+static void do_pstream_read_write(pa_pstream *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    pa_pstream_ref(p);
+
+    p->mainloop->defer_enable(p->defer_event, 0);
+
+    if (!p->dead && p->srb) {
+         do_write(p);
+         while (!p->dead && do_read(p, &p->readsrb) == 0);
+    }
+
+    if (!p->dead && pa_iochannel_is_readable(p->io)) {
+        if (do_read(p, &p->readio) < 0)
+            goto fail;
+    } else if (!p->dead && pa_iochannel_is_hungup(p->io))
+        goto fail;
+
+    while (!p->dead && pa_iochannel_is_writable(p->io)) {
+        int r = do_write(p);
+        if (r < 0)
+            goto fail;
+        if (r == 0)
+            break;
+    }
+
+    pa_pstream_unref(p);
+    return;
+
+fail:
+
+    if (p->die_callback)
+        p->die_callback(p, p->die_callback_userdata);
+
+    pa_pstream_unlink(p);
+    pa_pstream_unref(p);
+}
+
+static bool srb_callback(pa_srbchannel *srb, void *userdata) {
+    bool b;
+    pa_pstream *p = userdata;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+    pa_assert(p->srb == srb);
+
+    pa_pstream_ref(p);
+
+    do_pstream_read_write(p);
+
+    /* If either pstream or the srb is going away, return false.
+       We need to check this before p is destroyed. */
+    b = (PA_REFCNT_VALUE(p) > 1) && (p->srb == srb);
+    pa_pstream_unref(p);
+
+    return b;
+}
+
+static void io_callback(pa_iochannel*io, void *userdata) {
+    pa_pstream *p = userdata;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+    pa_assert(p->io == io);
+
+    do_pstream_read_write(p);
+}
+
+static void defer_callback(pa_mainloop_api *m, pa_defer_event *e, void*userdata) {
+    pa_pstream *p = userdata;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+    pa_assert(p->defer_event == e);
+    pa_assert(p->mainloop == m);
+
+    do_pstream_read_write(p);
+}
+
+static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata);
+
+pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *pool) {
+    pa_pstream *p;
+
+    pa_assert(m);
+    pa_assert(io);
+    pa_assert(pool);
+
+    p = pa_xnew0(pa_pstream, 1);
+    PA_REFCNT_INIT(p);
+    p->io = io;
+    pa_iochannel_set_callback(io, io_callback, p);
+
+    p->mainloop = m;
+    p->defer_event = m->defer_new(m, defer_callback, p);
+    m->defer_enable(p->defer_event, 0);
+
+    p->send_queue = pa_queue_new();
+
+    p->mempool = pool;
+
+    /* We do importing unconditionally */
+    p->import = pa_memimport_new(p->mempool, memimport_release_cb, p);
+
+    pa_iochannel_socket_set_rcvbuf(io, pa_mempool_block_size_max(p->mempool));
+    pa_iochannel_socket_set_sndbuf(io, pa_mempool_block_size_max(p->mempool));
+
+    return p;
+}
+
+/* Attach memfd<->SHM_ID mapping to given pstream and its memimport.
+ * Check pa_pstream_register_memfd_mempool() for further info.
+ *
+ * Caller owns the passed @memfd_fd and must close it down when appropriate. */
+int pa_pstream_attach_memfd_shmid(pa_pstream *p, unsigned shm_id, int memfd_fd) {
+    int err = -1;
+
+    pa_assert(memfd_fd != -1);
+
+    if (!p->use_memfd) {
+        pa_log_warn("Received memfd ID registration request over a pipe "
+                    "that does not support memfds");
+        return err;
+    }
+
+    if (pa_idxset_get_by_data(p->registered_memfd_ids, PA_UINT32_TO_PTR(shm_id), NULL)) {
+        pa_log_warn("previously registered memfd SHM ID = %u", shm_id);
+        return err;
+    }
+
+    if (pa_memimport_attach_memfd(p->import, shm_id, memfd_fd, true)) {
+        pa_log("Failed to create permanent mapping for memfd region with ID = %u", shm_id);
+        return err;
+    }
+
+    pa_assert_se(pa_idxset_put(p->registered_memfd_ids, PA_UINT32_TO_PTR(shm_id), NULL) == 0);
+    return 0;
+}
+
+static void item_free(void *item) {
+    struct item_info *i = item;
+    pa_assert(i);
+
+    if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) {
+        pa_assert(i->chunk.memblock);
+        pa_memblock_unref(i->chunk.memblock);
+    } else if (i->type == PA_PSTREAM_ITEM_PACKET) {
+        pa_assert(i->packet);
+        pa_packet_unref(i->packet);
+    }
+
+#ifdef HAVE_CREDS
+    /* On error recovery paths, there might be lingering items
+     * on the pstream send queue and they are usually freed with
+     * a call to 'pa_queue_free(p->send_queue, item_free)'. Make
+     * sure we do not leak any fds in that case! */
+    if (i->with_ancil_data)
+        pa_cmsg_ancil_data_close_fds(&i->ancil_data);
+#endif
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+        pa_xfree(i);
+}
+
+static void pstream_free(pa_pstream *p) {
+    pa_assert(p);
+
+    pa_pstream_unlink(p);
+
+    pa_queue_free(p->send_queue, item_free);
+
+    if (p->write.current)
+        item_free(p->write.current);
+
+    if (p->write.memchunk.memblock)
+        pa_memblock_unref(p->write.memchunk.memblock);
+
+    if (p->readsrb.memblock)
+        pa_memblock_unref(p->readsrb.memblock);
+
+    if (p->readsrb.packet)
+        pa_packet_unref(p->readsrb.packet);
+
+    if (p->readio.memblock)
+        pa_memblock_unref(p->readio.memblock);
+
+    if (p->readio.packet)
+        pa_packet_unref(p->readio.packet);
+
+    if (p->registered_memfd_ids)
+        pa_idxset_free(p->registered_memfd_ids, NULL);
+
+    pa_xfree(p);
+}
+
+void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data) {
+    struct item_info *i;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+    pa_assert(packet);
+
+    if (p->dead) {
+#ifdef HAVE_CREDS
+        pa_cmsg_ancil_data_close_fds(ancil_data);
+#endif
+        return;
+    }
+
+    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+        i = pa_xnew(struct item_info, 1);
+
+    i->type = PA_PSTREAM_ITEM_PACKET;
+    i->packet = pa_packet_ref(packet);
+
+#ifdef HAVE_CREDS
+    if ((i->with_ancil_data = !!ancil_data)) {
+        i->ancil_data = *ancil_data;
+        if (ancil_data->creds_valid)
+            pa_assert(ancil_data->nfd == 0);
+        else
+            pa_assert(ancil_data->nfd > 0);
+    }
+#endif
+
+    pa_queue_push(p->send_queue, i);
+
+    p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek_mode, const pa_memchunk *chunk) {
+    size_t length, idx;
+    size_t bsm;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+    pa_assert(channel != (uint32_t) -1);
+    pa_assert(chunk);
+
+    if (p->dead)
+        return;
+
+    idx = 0;
+    length = chunk->length;
+
+    bsm = pa_mempool_block_size_max(p->mempool);
+
+    while (length > 0) {
+        struct item_info *i;
+        size_t n;
+
+        if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+            i = pa_xnew(struct item_info, 1);
+        i->type = PA_PSTREAM_ITEM_MEMBLOCK;
+
+        n = PA_MIN(length, bsm);
+        i->chunk.index = chunk->index + idx;
+        i->chunk.length = n;
+        i->chunk.memblock = pa_memblock_ref(chunk->memblock);
+
+        i->channel = channel;
+        i->offset = offset;
+        i->seek_mode = seek_mode;
+#ifdef HAVE_CREDS
+        i->with_ancil_data = false;
+#endif
+
+        pa_queue_push(p->send_queue, i);
+
+        idx += n;
+        length -= n;
+    }
+
+    p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+void pa_pstream_send_release(pa_pstream *p, uint32_t block_id) {
+    struct item_info *item;
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (p->dead)
+        return;
+
+/*     pa_log("Releasing block %u", block_id); */
+
+    if (!(item = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+        item = pa_xnew(struct item_info, 1);
+    item->type = PA_PSTREAM_ITEM_SHMRELEASE;
+    item->block_id = block_id;
+#ifdef HAVE_CREDS
+    item->with_ancil_data = false;
+#endif
+
+    pa_queue_push(p->send_queue, item);
+    p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+/* might be called from thread context */
+static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata) {
+    pa_pstream *p = userdata;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (p->dead)
+        return;
+
+    if (p->release_callback)
+        p->release_callback(p, block_id, p->release_callback_userdata);
+    else
+        pa_pstream_send_release(p, block_id);
+}
+
+void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id) {
+    struct item_info *item;
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (p->dead)
+        return;
+/*     pa_log("Revoking block %u", block_id); */
+
+    if (!(item = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+        item = pa_xnew(struct item_info, 1);
+    item->type = PA_PSTREAM_ITEM_SHMREVOKE;
+    item->block_id = block_id;
+#ifdef HAVE_CREDS
+    item->with_ancil_data = false;
+#endif
+
+    pa_queue_push(p->send_queue, item);
+    p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+/* might be called from thread context */
+static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userdata) {
+    pa_pstream *p = userdata;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (p->revoke_callback)
+        p->revoke_callback(p, block_id, p->revoke_callback_userdata);
+    else
+        pa_pstream_send_revoke(p, block_id);
+}
+
+static void prepare_next_write_item(pa_pstream *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->write.current = pa_queue_pop(p->send_queue);
+
+    if (!p->write.current)
+        return;
+    p->write.index = 0;
+    p->write.data = NULL;
+    p->write.minibuf_validsize = 0;
+    pa_memchunk_reset(&p->write.memchunk);
+
+    p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = 0;
+    p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1);
+    p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = 0;
+    p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = 0;
+    p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = 0;
+
+    if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) {
+        size_t plen;
+
+        pa_assert(p->write.current->packet);
+
+        p->write.data = (void *) pa_packet_data(p->write.current->packet, &plen);
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl((uint32_t) plen);
+
+        if (plen <= MINIBUF_SIZE - PA_PSTREAM_DESCRIPTOR_SIZE) {
+            memcpy(&p->write.minibuf[PA_PSTREAM_DESCRIPTOR_SIZE], p->write.data, plen);
+            p->write.minibuf_validsize = PA_PSTREAM_DESCRIPTOR_SIZE + plen;
+        }
+
+    } else if (p->write.current->type == PA_PSTREAM_ITEM_SHMRELEASE) {
+
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = htonl(PA_FLAG_SHMRELEASE);
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl(p->write.current->block_id);
+
+    } else if (p->write.current->type == PA_PSTREAM_ITEM_SHMREVOKE) {
+
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = htonl(PA_FLAG_SHMREVOKE);
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl(p->write.current->block_id);
+
+    } else {
+        uint32_t flags;
+        bool send_payload = true;
+
+        pa_assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK);
+        pa_assert(p->write.current->chunk.memblock);
+
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel);
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl((uint32_t) (((uint64_t) p->write.current->offset) >> 32));
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = htonl((uint32_t) ((uint64_t) p->write.current->offset));
+
+        flags = (uint32_t) (p->write.current->seek_mode & PA_FLAG_SEEKMASK);
+
+        if (p->use_shm) {
+            pa_mem_type_t type;
+            uint32_t block_id, shm_id;
+            size_t offset, length;
+            uint32_t *shm_info = (uint32_t *) &p->write.minibuf[PA_PSTREAM_DESCRIPTOR_SIZE];
+            size_t shm_size = sizeof(uint32_t) * PA_PSTREAM_SHM_MAX;
+            pa_mempool *current_pool = pa_memblock_get_pool(p->write.current->chunk.memblock);
+            pa_memexport *current_export;
+
+            if (p->mempool == current_pool)
+                pa_assert_se(current_export = p->export);
+            else
+                pa_assert_se(current_export = pa_memexport_new(current_pool, memexport_revoke_cb, p));
+
+            if (pa_memexport_put(current_export,
+                                 p->write.current->chunk.memblock,
+                                 &type,
+                                 &block_id,
+                                 &shm_id,
+                                 &offset,
+                                 &length) >= 0) {
+
+                if (type == PA_MEM_TYPE_SHARED_POSIX)
+                    send_payload = false;
+
+                if (type == PA_MEM_TYPE_SHARED_MEMFD && p->use_memfd) {
+                    if (pa_idxset_get_by_data(p->registered_memfd_ids, PA_UINT32_TO_PTR(shm_id), NULL)) {
+                        flags |= PA_FLAG_SHMDATA_MEMFD_BLOCK;
+                        send_payload = false;
+                    } else {
+                        if (pa_log_ratelimit(PA_LOG_ERROR)) {
+                            pa_log("Cannot send block reference with non-registered memfd ID = %u", shm_id);
+                            pa_log("Fallig back to copying full block data over socket");
+                        }
+                    }
+                }
+
+                if (send_payload) {
+                    pa_assert_se(pa_memexport_process_release(current_export, block_id) == 0);
+                } else {
+                    flags |= PA_FLAG_SHMDATA;
+                    if (pa_mempool_is_remote_writable(current_pool))
+                        flags |= PA_FLAG_SHMWRITABLE;
+
+                    shm_info[PA_PSTREAM_SHM_BLOCKID] = htonl(block_id);
+                    shm_info[PA_PSTREAM_SHM_SHMID] = htonl(shm_id);
+                    shm_info[PA_PSTREAM_SHM_INDEX] = htonl((uint32_t) (offset + p->write.current->chunk.index));
+                    shm_info[PA_PSTREAM_SHM_LENGTH] = htonl((uint32_t) p->write.current->chunk.length);
+
+                    p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(shm_size);
+                    p->write.minibuf_validsize = PA_PSTREAM_DESCRIPTOR_SIZE + shm_size;
+                }
+            }
+/*             else */
+/*                 FIXME: Avoid memexport slot leaks. Call pa_memexport_process_release() */
+/*                 pa_log_warn("Failed to export memory block."); */
+
+            if (current_export != p->export)
+                pa_memexport_free(current_export);
+            pa_mempool_unref(current_pool);
+        }
+
+        if (send_payload) {
+            p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl((uint32_t) p->write.current->chunk.length);
+            p->write.memchunk = p->write.current->chunk;
+            pa_memblock_ref(p->write.memchunk.memblock);
+        }
+
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = htonl(flags);
+    }
+
+#ifdef HAVE_CREDS
+    if ((p->send_ancil_data_now = p->write.current->with_ancil_data))
+        p->write_ancil_data = &p->write.current->ancil_data;
+#endif
+}
+
+static void check_srbpending(pa_pstream *p) {
+    if (!p->is_srbpending)
+        return;
+
+    if (p->srb)
+        pa_srbchannel_free(p->srb);
+
+    p->srb = p->srbpending;
+    p->is_srbpending = false;
+
+    if (p->srb)
+        pa_srbchannel_set_callback(p->srb, srb_callback, p);
+}
+
+static int do_write(pa_pstream *p) {
+    void *d;
+    size_t l;
+    ssize_t r;
+    pa_memblock *release_memblock = NULL;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (!p->write.current)
+        prepare_next_write_item(p);
+
+    if (!p->write.current) {
+        /* The out queue is empty, so switching channels is safe */
+        check_srbpending(p);
+        return 0;
+    }
+
+    if (p->write.minibuf_validsize > 0) {
+        d = p->write.minibuf + p->write.index;
+        l = p->write.minibuf_validsize - p->write.index;
+    } else if (p->write.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
+        d = (uint8_t*) p->write.descriptor + p->write.index;
+        l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index;
+    } else {
+        pa_assert(p->write.data || p->write.memchunk.memblock);
+
+        if (p->write.data)
+            d = p->write.data;
+        else {
+            d = pa_memblock_acquire_chunk(&p->write.memchunk);
+            release_memblock = p->write.memchunk.memblock;
+        }
+
+        d = (uint8_t*) d + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
+        l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);
+    }
+
+    pa_assert(l > 0);
+
+#ifdef HAVE_CREDS
+    if (p->send_ancil_data_now) {
+        if (p->write_ancil_data->creds_valid) {
+            pa_assert(p->write_ancil_data->nfd == 0);
+            if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_ancil_data->creds)) < 0)
+                goto fail;
+        }
+        else
+            if ((r = pa_iochannel_write_with_fds(p->io, d, l, p->write_ancil_data->nfd, p->write_ancil_data->fds)) < 0)
+                goto fail;
+
+        pa_cmsg_ancil_data_close_fds(p->write_ancil_data);
+        p->send_ancil_data_now = false;
+    } else
+#endif
+    if (p->srb)
+        r = pa_srbchannel_write(p->srb, d, l);
+    else if ((r = pa_iochannel_write(p->io, d, l)) < 0)
+        goto fail;
+
+    if (release_memblock)
+        pa_memblock_release(release_memblock);
+
+    p->write.index += (size_t) r;
+
+    if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE + ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) {
+        pa_assert(p->write.current);
+        item_free(p->write.current);
+        p->write.current = NULL;
+
+        if (p->write.memchunk.memblock)
+            pa_memblock_unref(p->write.memchunk.memblock);
+
+        pa_memchunk_reset(&p->write.memchunk);
+
+        if (p->drain_callback && !pa_pstream_is_pending(p))
+            p->drain_callback(p, p->drain_callback_userdata);
+    }
+
+    return (size_t) r == l ? 1 : 0;
+
+fail:
+#ifdef HAVE_CREDS
+    if (p->send_ancil_data_now)
+        pa_cmsg_ancil_data_close_fds(p->write_ancil_data);
+#endif
+
+    if (release_memblock)
+        pa_memblock_release(release_memblock);
+
+    return -1;
+}
+
+static void memblock_complete(pa_pstream *p, struct pstream_read *re) {
+    pa_memchunk chunk;
+    int64_t offset;
+
+    if (!p->receive_memblock_callback)
+        return;
+
+    chunk.memblock = re->memblock;
+    chunk.index = 0;
+    chunk.length = re->index - PA_PSTREAM_DESCRIPTOR_SIZE;
+
+    offset = (int64_t) (
+             (((uint64_t) ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])) << 32) |
+             (((uint64_t) ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO]))));
+
+    p->receive_memblock_callback(
+        p,
+        ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]),
+        offset,
+        ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]) & PA_FLAG_SEEKMASK,
+        &chunk,
+        p->receive_memblock_callback_userdata);
+}
+
+static int do_read(pa_pstream *p, struct pstream_read *re) {
+    void *d;
+    size_t l;
+    ssize_t r;
+    pa_memblock *release_memblock = NULL;
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (re->index < PA_PSTREAM_DESCRIPTOR_SIZE) {
+        d = (uint8_t*) re->descriptor + re->index;
+        l = PA_PSTREAM_DESCRIPTOR_SIZE - re->index;
+    } else {
+        pa_assert(re->data || re->memblock);
+
+        if (re->data)
+            d = re->data;
+        else {
+            d = pa_memblock_acquire(re->memblock);
+            release_memblock = re->memblock;
+        }
+
+        d = (uint8_t*) d + re->index - PA_PSTREAM_DESCRIPTOR_SIZE;
+        l = ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (re->index - PA_PSTREAM_DESCRIPTOR_SIZE);
+    }
+
+    if (re == &p->readsrb) {
+        r = pa_srbchannel_read(p->srb, d, l);
+        if (r == 0) {
+            if (release_memblock)
+                pa_memblock_release(release_memblock);
+            return 1;
+        }
+    }
+    else
+#ifdef HAVE_CREDS
+    {
+        pa_cmsg_ancil_data b;
+
+        if ((r = pa_iochannel_read_with_ancil_data(p->io, d, l, &b)) <= 0)
+            goto fail;
+
+        if (b.creds_valid) {
+            p->read_ancil_data.creds_valid = true;
+            p->read_ancil_data.creds = b.creds;
+        }
+        if (b.nfd > 0) {
+            pa_assert(b.nfd <= MAX_ANCIL_DATA_FDS);
+            p->read_ancil_data.nfd = b.nfd;
+            memcpy(p->read_ancil_data.fds, b.fds, sizeof(int) * b.nfd);
+            p->read_ancil_data.close_fds_on_cleanup = b.close_fds_on_cleanup;
+        }
+    }
+#else
+    if ((r = pa_iochannel_read(p->io, d, l)) <= 0)
+        goto fail;
+#endif
+
+    if (release_memblock)
+        pa_memblock_release(release_memblock);
+
+    re->index += (size_t) r;
+
+    if (re->index == PA_PSTREAM_DESCRIPTOR_SIZE) {
+        uint32_t flags, length, channel;
+        /* Reading of frame descriptor complete */
+
+        flags = ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]);
+
+        if (!p->use_shm && (flags & PA_FLAG_SHMMASK) != 0) {
+            pa_log_warn("Received SHM frame on a socket where SHM is disabled.");
+            return -1;
+        }
+
+        if (flags == PA_FLAG_SHMRELEASE) {
+
+            /* This is a SHM memblock release frame with no payload */
+
+/*             pa_log("Got release frame for %u", ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])); */
+
+            pa_assert(p->export);
+            pa_memexport_process_release(p->export, ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI]));
+
+            goto frame_done;
+
+        } else if (flags == PA_FLAG_SHMREVOKE) {
+
+            /* This is a SHM memblock revoke frame with no payload */
+
+/*             pa_log("Got revoke frame for %u", ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])); */
+
+            pa_assert(p->import);
+            pa_memimport_process_revoke(p->import, ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI]));
+
+            goto frame_done;
+        }
+
+        length = ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]);
+
+        if (length > FRAME_SIZE_MAX_ALLOW || length <= 0) {
+            pa_log_warn("Received invalid frame size: %lu", (unsigned long) length);
+            return -1;
+        }
+
+        pa_assert(!re->packet && !re->memblock);
+
+        channel = ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]);
+
+        if (channel == (uint32_t) -1) {
+            size_t plen;
+
+            if (flags != 0) {
+                pa_log_warn("Received packet frame with invalid flags value.");
+                return -1;
+            }
+
+            /* Frame is a packet frame */
+            re->packet = pa_packet_new(length);
+            re->data = (void *) pa_packet_data(re->packet, &plen);
+
+        } else {
+
+            if ((flags & PA_FLAG_SEEKMASK) > PA_SEEK_RELATIVE_END) {
+                pa_log_warn("Received memblock frame with invalid seek mode.");
+                return -1;
+            }
+
+            if (((flags & PA_FLAG_SHMMASK) & PA_FLAG_SHMDATA) != 0) {
+
+                if (length != sizeof(re->shm_info)) {
+                    pa_log_warn("Received SHM memblock frame with invalid frame length.");
+                    return -1;
+                }
+
+                /* Frame is a memblock frame referencing an SHM memblock */
+                re->data = re->shm_info;
+
+            } else if ((flags & PA_FLAG_SHMMASK) == 0) {
+
+                /* Frame is a memblock frame */
+
+                re->memblock = pa_memblock_new(p->mempool, length);
+                re->data = NULL;
+            } else {
+
+                pa_log_warn("Received memblock frame with invalid flags value.");
+                return -1;
+            }
+        }
+
+    } else if (re->index >= ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) + PA_PSTREAM_DESCRIPTOR_SIZE) {
+        /* Frame complete */
+
+        if (re->memblock) {
+            memblock_complete(p, re);
+
+            /* This was a memblock frame. We can unref the memblock now */
+            pa_memblock_unref(re->memblock);
+
+        } else if (re->packet) {
+
+            if (p->receive_packet_callback)
+#ifdef HAVE_CREDS
+                p->receive_packet_callback(p, re->packet, &p->read_ancil_data, p->receive_packet_callback_userdata);
+#else
+                p->receive_packet_callback(p, re->packet, NULL, p->receive_packet_callback_userdata);
+#endif
+
+            pa_packet_unref(re->packet);
+        } else {
+            pa_memblock *b = NULL;
+            uint32_t flags = ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]);
+            uint32_t shm_id = ntohl(re->shm_info[PA_PSTREAM_SHM_SHMID]);
+            pa_mem_type_t type = (flags & PA_FLAG_SHMDATA_MEMFD_BLOCK) ?
+                                 PA_MEM_TYPE_SHARED_MEMFD : PA_MEM_TYPE_SHARED_POSIX;
+
+            pa_assert(((flags & PA_FLAG_SHMMASK) & PA_FLAG_SHMDATA) != 0);
+            pa_assert(p->import);
+
+            if (type == PA_MEM_TYPE_SHARED_MEMFD && p->use_memfd &&
+                !pa_idxset_get_by_data(p->registered_memfd_ids, PA_UINT32_TO_PTR(shm_id), NULL)) {
+
+                if (pa_log_ratelimit(PA_LOG_ERROR))
+                    pa_log("Ignoring received block reference with non-registered memfd ID = %u", shm_id);
+
+            } else if (!(b = pa_memimport_get(p->import,
+                                              type,
+                                              ntohl(re->shm_info[PA_PSTREAM_SHM_BLOCKID]),
+                                              shm_id,
+                                              ntohl(re->shm_info[PA_PSTREAM_SHM_INDEX]),
+                                              ntohl(re->shm_info[PA_PSTREAM_SHM_LENGTH]),
+                                              !!(flags & PA_FLAG_SHMWRITABLE)))) {
+
+                if (pa_log_ratelimit(PA_LOG_DEBUG))
+                    pa_log_debug("Failed to import memory block.");
+            }
+
+            if (p->receive_memblock_callback) {
+                int64_t offset;
+                pa_memchunk chunk;
+
+                chunk.memblock = b;
+                chunk.index = 0;
+                chunk.length = b ? pa_memblock_get_length(b) : ntohl(re->shm_info[PA_PSTREAM_SHM_LENGTH]);
+
+                offset = (int64_t) (
+                        (((uint64_t) ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])) << 32) |
+                        (((uint64_t) ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO]))));
+
+                p->receive_memblock_callback(
+                        p,
+                        ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]),
+                        offset,
+                        ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]) & PA_FLAG_SEEKMASK,
+                        &chunk,
+                        p->receive_memblock_callback_userdata);
+            }
+
+            if (b)
+                pa_memblock_unref(b);
+        }
+
+        goto frame_done;
+    }
+
+    return 0;
+
+frame_done:
+    re->memblock = NULL;
+    re->packet = NULL;
+    re->index = 0;
+    re->data = NULL;
+
+#ifdef HAVE_CREDS
+    /* FIXME: Close received ancillary data fds if the pstream's
+     * receive_packet_callback did not do so.
+     *
+     * Malicious clients can attach fds to unknown commands, or attach them
+     * to commands that does not expect fds. By doing so, server will reach
+     * its open fd limit and future clients' SHM transfers will always fail.
+     */
+    p->read_ancil_data.creds_valid = false;
+    p->read_ancil_data.nfd = 0;
+#endif
+
+    return 0;
+
+fail:
+    if (release_memblock)
+        pa_memblock_release(release_memblock);
+
+    return -1;
+}
+
+void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->die_callback = cb;
+    p->die_callback_userdata = userdata;
+}
+
+void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->drain_callback = cb;
+    p->drain_callback_userdata = userdata;
+}
+
+void pa_pstream_set_receive_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->receive_packet_callback = cb;
+    p->receive_packet_callback_userdata = userdata;
+}
+
+void pa_pstream_set_receive_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->receive_memblock_callback = cb;
+    p->receive_memblock_callback_userdata = userdata;
+}
+
+void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->release_callback = cb;
+    p->release_callback_userdata = userdata;
+}
+
+void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->revoke_callback = cb;
+    p->revoke_callback_userdata = userdata;
+}
+
+bool pa_pstream_is_pending(pa_pstream *p) {
+    bool b;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (p->dead)
+        b = false;
+    else
+        b = p->write.current || !pa_queue_isempty(p->send_queue);
+
+    return b;
+}
+
+void pa_pstream_unref(pa_pstream*p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (PA_REFCNT_DEC(p) <= 0)
+        pstream_free(p);
+}
+
+pa_pstream* pa_pstream_ref(pa_pstream*p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    PA_REFCNT_INC(p);
+    return p;
+}
+
+void pa_pstream_unlink(pa_pstream *p) {
+    pa_assert(p);
+
+    if (p->dead)
+        return;
+
+    p->dead = true;
+
+    while (p->srb || p->is_srbpending) /* In theory there could be one active and one pending */
+        pa_pstream_set_srbchannel(p, NULL);
+
+    if (p->import) {
+        pa_memimport_free(p->import);
+        p->import = NULL;
+    }
+
+    if (p->export) {
+        pa_memexport_free(p->export);
+        p->export = NULL;
+    }
+
+    if (p->io) {
+        pa_iochannel_free(p->io);
+        p->io = NULL;
+    }
+
+    if (p->defer_event) {
+        p->mainloop->defer_free(p->defer_event);
+        p->defer_event = NULL;
+    }
+
+    p->die_callback = NULL;
+    p->drain_callback = NULL;
+    p->receive_packet_callback = NULL;
+    p->receive_memblock_callback = NULL;
+}
+
+void pa_pstream_enable_shm(pa_pstream *p, bool enable) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->use_shm = enable;
+
+    if (enable) {
+
+        if (!p->export)
+            p->export = pa_memexport_new(p->mempool, memexport_revoke_cb, p);
+
+    } else {
+
+        if (p->export) {
+            pa_memexport_free(p->export);
+            p->export = NULL;
+        }
+    }
+}
+
+void pa_pstream_enable_memfd(pa_pstream *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+    pa_assert(p->use_shm);
+
+    p->use_memfd = true;
+
+    if (!p->registered_memfd_ids) {
+        p->registered_memfd_ids = pa_idxset_new(NULL, NULL);
+    }
+}
+
+bool pa_pstream_get_shm(pa_pstream *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    return p->use_shm;
+}
+
+bool pa_pstream_get_memfd(pa_pstream *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    return p->use_memfd;
+}
+
+void pa_pstream_set_srbchannel(pa_pstream *p, pa_srbchannel *srb) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0 || srb == NULL);
+
+    if (srb == p->srb)
+        return;
+
+    /* We can't handle quick switches between srbchannels. */
+    pa_assert(!p->is_srbpending);
+
+    p->srbpending = srb;
+    p->is_srbpending = true;
+
+    /* Switch immediately, if possible. */
+    if (p->dead)
+        check_srbpending(p);
+    else
+        do_write(p);
+}
diff --git a/src/pulsecore/pstream.h b/src/pulsecore/pstream.h
new file mode 100644 (file)
index 0000000..2bff270
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef foopstreamhfoo
+#define foopstreamhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulse/def.h>
+
+#include <pulsecore/packet.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/srbchannel.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
+
+typedef struct pa_pstream pa_pstream;
+
+typedef void (*pa_pstream_packet_cb_t)(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata);
+typedef void (*pa_pstream_memblock_cb_t)(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata);
+typedef void (*pa_pstream_notify_cb_t)(pa_pstream *p, void *userdata);
+typedef void (*pa_pstream_block_id_cb_t)(pa_pstream *p, uint32_t block_id, void *userdata);
+
+pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *p);
+
+pa_pstream* pa_pstream_ref(pa_pstream*p);
+void pa_pstream_unref(pa_pstream*p);
+
+void pa_pstream_unlink(pa_pstream *p);
+
+int pa_pstream_attach_memfd_shmid(pa_pstream *p, unsigned shm_id, int memfd_fd);
+
+void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data);
+void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk);
+void pa_pstream_send_release(pa_pstream *p, uint32_t block_id);
+void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id);
+
+void pa_pstream_set_receive_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata);
+void pa_pstream_set_receive_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata);
+void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
+void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
+void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata);
+void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata);
+
+bool pa_pstream_is_pending(pa_pstream *p);
+
+void pa_pstream_enable_shm(pa_pstream *p, bool enable);
+void pa_pstream_enable_memfd(pa_pstream *p);
+bool pa_pstream_get_shm(pa_pstream *p);
+bool pa_pstream_get_memfd(pa_pstream *p);
+
+/* Enables shared ringbuffer channel. Note that the srbchannel is now owned by the pstream.
+   Setting srb to NULL will free any existing srbchannel. */
+void pa_pstream_set_srbchannel(pa_pstream *p, pa_srbchannel *srb);
+
+#endif
diff --git a/src/pulsecore/queue.c b/src/pulsecore/queue.c
new file mode 100644 (file)
index 0000000..2b952b6
--- /dev/null
@@ -0,0 +1,122 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+
+#include "queue.h"
+
+PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
+
+struct queue_entry {
+    struct queue_entry *next;
+    void *data;
+};
+
+struct pa_queue {
+    struct queue_entry *front, *back;
+    unsigned length;
+};
+
+pa_queue* pa_queue_new(void) {
+    pa_queue *q = pa_xnew(pa_queue, 1);
+
+    q->front = q->back = NULL;
+    q->length = 0;
+
+    return q;
+}
+
+void pa_queue_free(pa_queue *q, pa_free_cb_t free_func) {
+    void *data;
+    pa_assert(q);
+
+    while ((data = pa_queue_pop(q)))
+        if (free_func)
+            free_func(data);
+
+    pa_assert(!q->front);
+    pa_assert(!q->back);
+    pa_assert(q->length == 0);
+
+    pa_xfree(q);
+}
+
+void pa_queue_push(pa_queue *q, void *p) {
+    struct queue_entry *e;
+
+    pa_assert(q);
+    pa_assert(p);
+
+    if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
+        e = pa_xnew(struct queue_entry, 1);
+
+    e->data = p;
+    e->next = NULL;
+
+    if (q->back) {
+        pa_assert(q->front);
+        q->back->next = e;
+    } else {
+        pa_assert(!q->front);
+        q->front = e;
+    }
+
+    q->back = e;
+    q->length++;
+}
+
+void* pa_queue_pop(pa_queue *q) {
+    void *p;
+    struct queue_entry *e;
+
+    pa_assert(q);
+
+    if (!(e = q->front))
+        return NULL;
+
+    q->front = e->next;
+
+    if (q->back == e) {
+        pa_assert(!e->next);
+        q->back = NULL;
+    }
+
+    p = e->data;
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+        pa_xfree(e);
+
+    q->length--;
+
+    return p;
+}
+
+int pa_queue_isempty(pa_queue *q) {
+    pa_assert(q);
+
+    return q->length == 0;
+}
diff --git a/src/pulsecore/queue.h b/src/pulsecore/queue.h
new file mode 100644 (file)
index 0000000..14cc555
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef foopulsecorequeuehfoo
+#define foopulsecorequeuehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/def.h>
+
+typedef struct pa_queue pa_queue;
+
+/* A simple implementation of the abstract data type queue. Stores
+ * pointers as members. The memory has to be managed by the caller. */
+
+pa_queue* pa_queue_new(void);
+
+/* Free the queue and run the specified callback function for every
+ * remaining entry. The callback function may be NULL. */
+void pa_queue_free(pa_queue *q, pa_free_cb_t free_func);
+
+void pa_queue_push(pa_queue *q, void *p);
+void* pa_queue_pop(pa_queue *q);
+
+int pa_queue_isempty(pa_queue *q);
+
+#endif
diff --git a/src/pulsecore/random.c b/src/pulsecore/random.c
new file mode 100644 (file)
index 0000000..508a6f8
--- /dev/null
@@ -0,0 +1,129 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#include <wincrypt.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "random.h"
+
+static bool has_whined = false;
+
+static const char * const devices[] = { "/dev/urandom", "/dev/random", NULL };
+
+static int random_proper(void *ret_data, size_t length) {
+#ifdef OS_IS_WIN32
+    int ret = -1;
+
+    HCRYPTPROV hCryptProv = 0;
+
+    pa_assert(ret_data);
+    pa_assert(length > 0);
+
+    if (CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+        if (CryptGenRandom(hCryptProv, length, ret_data))
+            ret = 0;
+        CryptReleaseContext(hCryptProv, 0);
+    }
+
+    return ret;
+
+#else /* OS_IS_WIN32 */
+
+    int fd, ret = -1;
+    ssize_t r = 0;
+    const char *const * device;
+
+    pa_assert(ret_data);
+    pa_assert(length > 0);
+
+    device = devices;
+
+    while (*device) {
+        ret = 0;
+
+        if ((fd = pa_open_cloexec(*device, O_RDONLY, 0)) >= 0) {
+
+            if ((r = pa_loop_read(fd, ret_data, length, NULL)) < 0 || (size_t) r != length)
+                ret = -1;
+
+            pa_close(fd);
+        } else
+            ret = -1;
+
+        if (ret == 0)
+            break;
+
+        device++;
+    }
+
+    return ret;
+#endif /* OS_IS_WIN32 */
+}
+
+void pa_random_seed(void) {
+    unsigned int seed;
+
+    if (random_proper(&seed, sizeof(unsigned int)) < 0) {
+
+        if (!has_whined) {
+            pa_log_warn("Failed to get proper entropy. Falling back to seeding with current time.");
+            has_whined = true;
+        }
+
+        seed = (unsigned int) time(NULL);
+    }
+
+    srand(seed);
+}
+
+void pa_random(void *ret_data, size_t length) {
+    uint8_t *p;
+    size_t l;
+
+    pa_assert(ret_data);
+    pa_assert(length > 0);
+
+    if (random_proper(ret_data, length) >= 0)
+        return;
+
+    if (!has_whined) {
+        pa_log_warn("Failed to get proper entropy. Falling back to unsecure pseudo RNG.");
+        has_whined = true;
+    }
+
+    for (p = ret_data, l = length; l > 0; p++, l--)
+        *p = (uint8_t) rand();
+}
diff --git a/src/pulsecore/random.h b/src/pulsecore/random.h
new file mode 100644 (file)
index 0000000..7c7755d
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef foorandomhfoo
+#define foorandomhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+void pa_random_seed(void);
+void pa_random(void *ret_data, size_t length);
+
+#endif
diff --git a/src/pulsecore/ratelimit.c b/src/pulsecore/ratelimit.c
new file mode 100644 (file)
index 0000000..0bfc956
--- /dev/null
@@ -0,0 +1,74 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/rtclock.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/mutex.h>
+
+#include "ratelimit.h"
+
+static pa_static_mutex mutex = PA_STATIC_MUTEX_INIT;
+
+/* Modelled after Linux' lib/ratelimit.c by Dave Young
+ * <hidave.darkstar@gmail.com>, which is licensed GPLv2. */
+
+bool pa_ratelimit_test(pa_ratelimit *r, pa_log_level_t t) {
+    pa_usec_t now;
+    pa_mutex *m;
+
+    now = pa_rtclock_now();
+
+    m = pa_static_mutex_get(&mutex, false, false);
+    pa_mutex_lock(m);
+
+    pa_assert(r);
+    pa_assert(r->interval > 0);
+    pa_assert(r->burst > 0);
+
+    if (r->begin <= 0 ||
+        r->begin + r->interval < now) {
+
+        if (r->n_missed > 0)
+            pa_logl(t, "%u events suppressed", r->n_missed);
+
+        r->begin = now;
+
+        /* Reset counters */
+        r->n_printed = 0;
+        r->n_missed = 0;
+        goto good;
+    }
+
+    if (r->n_printed <= r->burst)
+        goto good;
+
+    r->n_missed++;
+    pa_mutex_unlock(m);
+    return false;
+
+good:
+    r->n_printed++;
+    pa_mutex_unlock(m);
+    return true;
+}
diff --git a/src/pulsecore/ratelimit.h b/src/pulsecore/ratelimit.h
new file mode 100644 (file)
index 0000000..d1e2f22
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef foopulsecoreratelimithfoo
+#define foopulsecoreratelimithfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/sample.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+typedef struct pa_ratelimit {
+    pa_usec_t interval;
+    unsigned burst;
+    unsigned n_printed, n_missed;
+    pa_usec_t begin;
+} pa_ratelimit;
+
+#define PA_DEFINE_RATELIMIT(_name, _interval, _burst)   \
+    pa_ratelimit _name = {                              \
+        .interval = (_interval),                        \
+        .burst = (_burst),                              \
+        .n_printed = 0,                                 \
+        .n_missed = 0,                                  \
+        .begin = 0                                      \
+    }
+
+#define PA_INIT_RATELIMIT(v, _interval, _burst)         \
+    do {                                                \
+        pa_ratelimit *r = &(v);                         \
+        r->interval = (_interval);                      \
+        r->burst = (_burst);                            \
+        r->n_printed = 0;                               \
+        r->n_missed = 0;                                \
+        r->begin = 0;                                   \
+    } while (false);
+
+bool pa_ratelimit_test(pa_ratelimit *r, pa_log_level_t t);
+
+#endif
diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h
new file mode 100644 (file)
index 0000000..2e818ab
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef foopulserefcnthfoo
+#define foopulserefcnthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+
+/* #define DEBUG_REF */
+
+#define PA_REFCNT_DECLARE \
+    pa_atomic_t _ref
+
+#define PA_REFCNT_VALUE(p) \
+    pa_atomic_load(&(p)->_ref)
+
+#define PA_REFCNT_INIT_ZERO(p) \
+    pa_atomic_store(&(p)->_ref, 0)
+
+#ifndef DEBUG_REF
+
+#define PA_REFCNT_INIT(p) \
+    pa_atomic_store(&(p)->_ref, 1)
+
+#define PA_REFCNT_INC(p) \
+    pa_atomic_inc(&(p)->_ref)
+
+#define PA_REFCNT_DEC(p) \
+    (pa_atomic_dec(&(p)->_ref)-1)
+
+#else
+
+/* If you need to debug ref counting problems define DEBUG_REF and
+ * set $PULSE_LOG_BACKTRACE=5 or suchlike in the shell when running
+ * PA */
+
+#define PA_REFCNT_INIT(p)                       \
+    do {                                        \
+        pa_atomic_store(&(p)->_ref, 1);         \
+        pa_log("REF: Init %p", p);              \
+    } while (false)
+
+#define PA_REFCNT_INC(p)                        \
+    do {                                        \
+        pa_atomic_inc(&(p)->_ref);              \
+        pa_log("REF: Inc %p", p);               \
+    } while (false)                             \
+
+#define PA_REFCNT_DEC(p)                        \
+    ({                                          \
+        int _j = (pa_atomic_dec(&(p)->_ref)-1); \
+        if (_j <= 0)                            \
+            pa_log("REF: Done %p", p);          \
+        else                                    \
+            pa_log("REF: Dec %p", p);           \
+        _j;                                     \
+     })
+
+#endif
+
+#endif
diff --git a/src/pulsecore/remap.c b/src/pulsecore/remap.c
new file mode 100644 (file)
index 0000000..09e2c8f
--- /dev/null
@@ -0,0 +1,466 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "cpu.h"
+#include "remap.h"
+
+static void remap_mono_to_stereo_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    unsigned i;
+
+    for (i = n >> 2; i; i--) {
+        dst[0] = dst[1] = src[0];
+        dst[2] = dst[3] = src[1];
+        dst[4] = dst[5] = src[2];
+        dst[6] = dst[7] = src[3];
+        src += 4;
+        dst += 8;
+    }
+    for (i = n & 3; i; i--) {
+        dst[0] = dst[1] = src[0];
+        src++;
+        dst += 2;
+    }
+}
+
+static void remap_mono_to_stereo_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    unsigned i;
+
+    for (i = n >> 2; i; i--) {
+        dst[0] = dst[1] = src[0];
+        dst[2] = dst[3] = src[1];
+        dst[4] = dst[5] = src[2];
+        dst[6] = dst[7] = src[3];
+        src += 4;
+        dst += 8;
+    }
+    for (i = n & 3; i; i--) {
+        dst[0] = dst[1] = src[0];
+        src++;
+        dst += 2;
+    }
+}
+
+static void remap_stereo_to_mono_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    unsigned i;
+
+    for (i = n >> 2; i > 0; i--) {
+        dst[0] = (src[0] + src[1])/2;
+        dst[1] = (src[2] + src[3])/2;
+        dst[2] = (src[4] + src[5])/2;
+        dst[3] = (src[6] + src[7])/2;
+        src += 8;
+        dst += 4;
+    }
+    for (i = n & 3; i; i--) {
+        dst[0] = (src[0] + src[1])/2;
+        src += 2;
+        dst += 1;
+    }
+}
+
+static void remap_stereo_to_mono_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    unsigned i;
+
+    for (i = n >> 2; i > 0; i--) {
+        dst[0] = (src[0] + src[1])*0.5f;
+        dst[1] = (src[2] + src[3])*0.5f;
+        dst[2] = (src[4] + src[5])*0.5f;
+        dst[3] = (src[6] + src[7])*0.5f;
+        src += 8;
+        dst += 4;
+    }
+    for (i = n & 3; i; i--) {
+        dst[0] = (src[0] + src[1])*0.5f;
+        src += 2;
+        dst += 1;
+    }
+}
+
+static void remap_mono_to_ch4_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    unsigned i;
+
+    for (i = n >> 2; i; i--) {
+        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
+        dst[4] = dst[5] = dst[6] = dst[7] = src[1];
+        dst[8] = dst[9] = dst[10] = dst[11] = src[2];
+        dst[12] = dst[13] = dst[14] = dst[15] = src[3];
+        src += 4;
+        dst += 16;
+    }
+    for (i = n & 3; i; i--) {
+        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
+        src++;
+        dst += 4;
+    }
+}
+
+static void remap_mono_to_ch4_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    unsigned i;
+
+    for (i = n >> 2; i; i--) {
+        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
+        dst[4] = dst[5] = dst[6] = dst[7] = src[1];
+        dst[8] = dst[9] = dst[10] = dst[11] = src[2];
+        dst[12] = dst[13] = dst[14] = dst[15] = src[3];
+        src += 4;
+        dst += 16;
+    }
+    for (i = n & 3; i; i--) {
+        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
+        src++;
+        dst += 4;
+    }
+}
+
+static void remap_ch4_to_mono_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    unsigned i;
+
+    for (i = n >> 2; i > 0; i--) {
+        dst[0] = (src[0] + src[1] + src[2] + src[3])/4;
+        dst[1] = (src[4] + src[5] + src[6] + src[7])/4;
+        dst[2] = (src[8] + src[9] + src[10] + src[11])/4;
+        dst[3] = (src[12] + src[13] + src[14] + src[15])/4;
+        src += 16;
+        dst += 4;
+    }
+    for (i = n & 3; i; i--) {
+        dst[0] = (src[0] + src[1] + src[2] + src[3])/4;
+        src += 4;
+        dst += 1;
+    }
+}
+
+static void remap_ch4_to_mono_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    unsigned i;
+
+    for (i = n >> 2; i > 0; i--) {
+        dst[0] = (src[0] + src[1] + src[2] + src[3])*0.25f;
+        dst[1] = (src[4] + src[5] + src[6] + src[7])*0.25f;
+        dst[2] = (src[8] + src[9] + src[10] + src[11])*0.25f;
+        dst[3] = (src[12] + src[13] + src[14] + src[15])*0.25f;
+        src += 16;
+        dst += 4;
+    }
+    for (i = n & 3; i; i--) {
+        dst[0] = (src[0] + src[1] + src[2] + src[3])*0.25f;
+        src += 4;
+        dst += 1;
+    }
+}
+
+static void remap_channels_matrix_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+
+    unsigned oc, ic, i;
+    unsigned n_ic, n_oc;
+
+    n_ic = m->i_ss.channels;
+    n_oc = m->o_ss.channels;
+
+    memset(dst, 0, n * sizeof(int16_t) * n_oc);
+
+    for (oc = 0; oc < n_oc; oc++) {
+
+        for (ic = 0; ic < n_ic; ic++) {
+            int16_t *d = dst + oc;
+            const int16_t *s = src + ic;
+            int32_t vol = m->map_table_i[oc][ic];
+
+            if (vol <= 0)
+                continue;
+
+            if (vol >= 0x10000) {
+                for (i = n; i > 0; i--, s += n_ic, d += n_oc)
+                    *d += *s;
+            } else {
+                for (i = n; i > 0; i--, s += n_ic, d += n_oc)
+                    *d += (int16_t) (((int32_t)*s * vol) >> 16);
+            }
+        }
+    }
+}
+
+static void remap_channels_matrix_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    unsigned oc, ic, i;
+    unsigned n_ic, n_oc;
+
+    n_ic = m->i_ss.channels;
+    n_oc = m->o_ss.channels;
+
+    memset(dst, 0, n * sizeof(float) * n_oc);
+
+    for (oc = 0; oc < n_oc; oc++) {
+
+        for (ic = 0; ic < n_ic; ic++) {
+            float *d = dst + oc;
+            const float *s = src + ic;
+            float vol = m->map_table_f[oc][ic];
+
+            if (vol <= 0.0f)
+                continue;
+
+            if (vol >= 1.0f) {
+                for (i = n; i > 0; i--, s += n_ic, d += n_oc)
+                    *d += *s;
+            } else {
+                for (i = n; i > 0; i--, s += n_ic, d += n_oc)
+                    *d += *s * vol;
+            }
+        }
+    }
+}
+
+/* Produce an array containing input channel indices to map to output channels.
+ * If the output channel is empty, the array element is -1. */
+bool pa_setup_remap_arrange(const pa_remap_t *m, int8_t arrange[PA_CHANNELS_MAX]) {
+    unsigned ic, oc;
+    unsigned n_ic, n_oc;
+    unsigned count_output = 0;
+
+    pa_assert(m);
+
+    n_ic = m->i_ss.channels;
+    n_oc = m->o_ss.channels;
+
+    for (oc = 0; oc < n_oc; oc++) {
+        arrange[oc] = -1;
+        for (ic = 0; ic < n_ic; ic++) {
+            int32_t vol = m->map_table_i[oc][ic];
+
+            /* input channel is not used */
+            if (vol == 0)
+                continue;
+
+            /* if mixing this channel, we cannot just rearrange */
+            if (vol != 0x10000 || arrange[oc] >= 0)
+                return false;
+
+            arrange[oc] = ic;
+            count_output++;
+        }
+    }
+
+    return count_output > 0;
+}
+
+static void remap_arrange_mono_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    const unsigned n_ic = m->i_ss.channels;
+    const int8_t *arrange = m->state;
+
+    src += arrange[0];
+    for (; n > 0; n--) {
+        *dst++ = *src;
+        src += n_ic;
+    }
+}
+
+static void remap_arrange_stereo_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    const unsigned n_ic = m->i_ss.channels;
+    const int8_t *arrange = m->state;
+    const int8_t ic0 = arrange[0], ic1 = arrange[1];
+
+    for (; n > 0; n--) {
+        *dst++ = (ic0 >= 0) ? *(src + ic0) : 0;
+        *dst++ = (ic1 >= 0) ? *(src + ic1) : 0;
+        src += n_ic;
+    }
+}
+
+static void remap_arrange_ch4_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    const unsigned n_ic = m->i_ss.channels;
+    const int8_t *arrange = m->state;
+    const int8_t ic0 = arrange[0], ic1 = arrange[1],
+        ic2 = arrange[2], ic3 = arrange[3];
+
+    for (; n > 0; n--) {
+        *dst++ = (ic0 >= 0) ? *(src + ic0) : 0;
+        *dst++ = (ic1 >= 0) ? *(src + ic1) : 0;
+        *dst++ = (ic2 >= 0) ? *(src + ic2) : 0;
+        *dst++ = (ic3 >= 0) ? *(src + ic3) : 0;
+        src += n_ic;
+    }
+}
+
+static void remap_arrange_mono_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    const unsigned n_ic = m->i_ss.channels;
+    const int8_t *arrange = m->state;
+
+    src += arrange[0];
+    for (; n > 0; n--) {
+        *dst++ = *src;
+        src += n_ic;
+    }
+}
+
+static void remap_arrange_stereo_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    const unsigned n_ic = m->i_ss.channels;
+    const int8_t *arrange = m->state;
+    const int ic0 = arrange[0], ic1 = arrange[1];
+
+    for (; n > 0; n--) {
+        *dst++ = (ic0 >= 0) ? *(src + ic0) : 0.0f;
+        *dst++ = (ic1 >= 0) ? *(src + ic1) : 0.0f;
+        src += n_ic;
+    }
+}
+
+static void remap_arrange_ch4_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    const unsigned n_ic = m->i_ss.channels;
+    const int8_t *arrange = m->state;
+    const int ic0 = arrange[0], ic1 = arrange[1],
+        ic2 = arrange[2], ic3 = arrange[3];
+
+    for (; n > 0; n--) {
+        *dst++ = (ic0 >= 0) ? *(src + ic0) : 0.0f;
+        *dst++ = (ic1 >= 0) ? *(src + ic1) : 0.0f;
+        *dst++ = (ic2 >= 0) ? *(src + ic2) : 0.0f;
+        *dst++ = (ic3 >= 0) ? *(src + ic3) : 0.0f;
+        src += n_ic;
+    }
+}
+
+void pa_set_remap_func(pa_remap_t *m, pa_do_remap_func_t func_s16,
+    pa_do_remap_func_t func_float) {
+
+    pa_assert(m);
+
+    if (m->format == PA_SAMPLE_S16NE)
+        m->do_remap = func_s16;
+    else if (m->format == PA_SAMPLE_FLOAT32NE)
+        m->do_remap = func_float;
+    else
+        pa_assert_not_reached();
+}
+
+static bool force_generic_code = false;
+
+/* set the function that will execute the remapping based on the matrices */
+static void init_remap_c(pa_remap_t *m) {
+    unsigned n_oc, n_ic;
+    int8_t arrange[PA_CHANNELS_MAX];
+
+    n_oc = m->o_ss.channels;
+    n_ic = m->i_ss.channels;
+
+    /* find some common channel remappings, fall back to full matrix operation. */
+    if (force_generic_code) {
+        pa_log_info("Forced to use generic matrix remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_channels_matrix_s16ne_c,
+            (pa_do_remap_func_t) remap_channels_matrix_float32ne_c);
+        return;
+    }
+
+    if (n_ic == 1 && n_oc == 2 &&
+            m->map_table_i[0][0] == 0x10000 && m->map_table_i[1][0] == 0x10000) {
+
+        pa_log_info("Using mono to stereo remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_mono_to_stereo_s16ne_c,
+            (pa_do_remap_func_t) remap_mono_to_stereo_float32ne_c);
+    } else if (n_ic == 2 && n_oc == 1 &&
+            m->map_table_i[0][0] == 0x8000 && m->map_table_i[0][1] == 0x8000) {
+
+        pa_log_info("Using stereo to mono remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_stereo_to_mono_s16ne_c,
+            (pa_do_remap_func_t) remap_stereo_to_mono_float32ne_c);
+    } else if (n_ic == 1 && n_oc == 4 &&
+            m->map_table_i[0][0] == 0x10000 && m->map_table_i[1][0] == 0x10000 &&
+            m->map_table_i[2][0] == 0x10000 && m->map_table_i[3][0] == 0x10000) {
+
+        pa_log_info("Using mono to 4-channel remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t)remap_mono_to_ch4_s16ne_c,
+            (pa_do_remap_func_t) remap_mono_to_ch4_float32ne_c);
+    } else if (n_ic == 4 && n_oc == 1 &&
+            m->map_table_i[0][0] == 0x4000 && m->map_table_i[0][1] == 0x4000 &&
+            m->map_table_i[0][2] == 0x4000 && m->map_table_i[0][3] == 0x4000) {
+
+        pa_log_info("Using 4-channel to mono remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_ch4_to_mono_s16ne_c,
+            (pa_do_remap_func_t) remap_ch4_to_mono_float32ne_c);
+    } else if (pa_setup_remap_arrange(m, arrange) && n_oc == 1) {
+
+        pa_log_info("Using mono arrange remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_arrange_mono_s16ne_c,
+            (pa_do_remap_func_t) remap_arrange_mono_float32ne_c);
+
+        /* setup state */
+        m->state = pa_xnewdup(int8_t, arrange, PA_CHANNELS_MAX);
+    } else if (pa_setup_remap_arrange(m, arrange) && n_oc == 2) {
+
+        pa_log_info("Using stereo arrange remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_arrange_stereo_s16ne_c,
+            (pa_do_remap_func_t) remap_arrange_stereo_float32ne_c);
+
+        /* setup state */
+        m->state = pa_xnewdup(int8_t, arrange, PA_CHANNELS_MAX);
+    } else if (pa_setup_remap_arrange(m, arrange) && n_oc == 4) {
+
+        pa_log_info("Using 4-channel arrange remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_arrange_ch4_s16ne_c,
+            (pa_do_remap_func_t) remap_arrange_ch4_float32ne_c);
+
+        /* setup state */
+        m->state = pa_xnewdup(int8_t, arrange, PA_CHANNELS_MAX);
+    } else {
+
+        pa_log_info("Using generic matrix remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_channels_matrix_s16ne_c,
+            (pa_do_remap_func_t) remap_channels_matrix_float32ne_c);
+    }
+}
+
+/* default C implementation */
+static pa_init_remap_func_t init_remap_func = init_remap_c;
+
+void pa_init_remap_func(pa_remap_t *m) {
+    pa_assert(init_remap_func);
+
+    m->do_remap = NULL;
+
+    /* call the installed remap init function */
+    init_remap_func(m);
+
+    if (m->do_remap == NULL) {
+        /* nothing was installed, fallback to C version */
+        init_remap_c(m);
+    }
+}
+
+pa_init_remap_func_t pa_get_init_remap_func(void) {
+    return init_remap_func;
+}
+
+void pa_set_init_remap_func(pa_init_remap_func_t func) {
+    init_remap_func = func;
+}
+
+void pa_remap_func_init(const pa_cpu_info *cpu_info) {
+    force_generic_code = cpu_info->force_generic_code;
+}
diff --git a/src/pulsecore/remap.h b/src/pulsecore/remap.h
new file mode 100644 (file)
index 0000000..4bad3ea
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef fooremapfoo
+#define fooremapfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/sample.h>
+
+typedef struct pa_remap pa_remap_t;
+
+typedef void (*pa_do_remap_func_t) (pa_remap_t *m, void *d, const void *s, unsigned n);
+
+struct pa_remap {
+    pa_sample_format_t format;
+    pa_sample_spec i_ss, o_ss;
+    float map_table_f[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
+    int32_t map_table_i[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
+    pa_do_remap_func_t do_remap;
+    void *state; /* optional state information for the remap function */
+};
+
+void pa_init_remap_func(pa_remap_t *m);
+
+/* custom installation of init functions */
+typedef void (*pa_init_remap_func_t) (pa_remap_t *m);
+
+pa_init_remap_func_t pa_get_init_remap_func(void);
+void pa_set_init_remap_func(pa_init_remap_func_t func);
+
+/* Check if remapping can be performed by just copying some or all input
+ * channels' data to output channels. Returns true and a table of input
+ * channel indices, or false otherwise.
+ *
+ * The table contains an entry for each output channels. Each table entry given
+ * either the input channel index to be copied, or -1 indicating that the
+ * output channel is not used and hence zero.
+ */
+bool pa_setup_remap_arrange(const pa_remap_t *m, int8_t arrange[PA_CHANNELS_MAX]);
+
+void pa_set_remap_func(pa_remap_t *m, pa_do_remap_func_t func_s16,
+    pa_do_remap_func_t func_float);
+
+#endif /* fooremapfoo */
diff --git a/src/pulsecore/remap_mmx.c b/src/pulsecore/remap_mmx.c
new file mode 100644 (file)
index 0000000..688da6c
--- /dev/null
@@ -0,0 +1,153 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "cpu-x86.h"
+#include "remap.h"
+
+#define LOAD_SAMPLES                                   \
+                " movq (%1), %%mm0              \n\t"  \
+                " movq 8(%1), %%mm2             \n\t"  \
+                " movq 16(%1), %%mm4            \n\t"  \
+                " movq 24(%1), %%mm6            \n\t"  \
+                " movq %%mm0, %%mm1             \n\t"  \
+                " movq %%mm2, %%mm3             \n\t"  \
+                " movq %%mm4, %%mm5             \n\t"  \
+                " movq %%mm6, %%mm7             \n\t"
+
+#define UNPACK_SAMPLES(s)                              \
+                " punpckl"#s" %%mm0, %%mm0      \n\t"  \
+                " punpckh"#s" %%mm1, %%mm1      \n\t"  \
+                " punpckl"#s" %%mm2, %%mm2      \n\t"  \
+                " punpckh"#s" %%mm3, %%mm3      \n\t"  \
+                " punpckl"#s" %%mm4, %%mm4      \n\t"  \
+                " punpckh"#s" %%mm5, %%mm5      \n\t"  \
+                " punpckl"#s" %%mm6, %%mm6      \n\t"  \
+                " punpckh"#s" %%mm7, %%mm7      \n\t"
+
+#define STORE_SAMPLES                                  \
+                " movq %%mm0, (%0)              \n\t"  \
+                " movq %%mm1, 8(%0)             \n\t"  \
+                " movq %%mm2, 16(%0)            \n\t"  \
+                " movq %%mm3, 24(%0)            \n\t"  \
+                " movq %%mm4, 32(%0)            \n\t"  \
+                " movq %%mm5, 40(%0)            \n\t"  \
+                " movq %%mm6, 48(%0)            \n\t"  \
+                " movq %%mm7, 56(%0)            \n\t"  \
+                " add $32, %1                   \n\t"  \
+                " add $64, %0                   \n\t"
+
+#define HANDLE_SINGLE_dq()                            \
+                " movd (%1), %%mm0              \n\t"  \
+                " punpckldq %%mm0, %%mm0        \n\t"  \
+                " movq %%mm0, (%0)              \n\t"  \
+                " add $4, %1                    \n\t"  \
+                " add $8, %0                    \n\t"
+
+#define HANDLE_SINGLE_wd()                             \
+                " movw (%1), %w3                \n\t"  \
+                " movd %3,  %%mm0               \n\t"  \
+                " punpcklwd %%mm0, %%mm0        \n\t"  \
+                " movd %%mm0, (%0)              \n\t"  \
+                " add $2, %1                    \n\t"  \
+                " add $4, %0                    \n\t"
+
+#define MONO_TO_STEREO(s,shift,mask)                   \
+                " mov %4, %2                    \n\t"  \
+                " sar $"#shift", %2             \n\t"  \
+                " cmp $0, %2                    \n\t"  \
+                " je 2f                         \n\t"  \
+                "1:                             \n\t"  \
+                LOAD_SAMPLES                           \
+                UNPACK_SAMPLES(s)                      \
+                STORE_SAMPLES                          \
+                " dec %2                        \n\t"  \
+                " jne 1b                        \n\t"  \
+                "2:                             \n\t"  \
+                " mov %4, %2                    \n\t"  \
+                " and $"#mask", %2              \n\t"  \
+                " je 4f                         \n\t"  \
+                "3:                             \n\t"  \
+                HANDLE_SINGLE_##s()                    \
+                " dec %2                        \n\t"  \
+                " jne 3b                        \n\t"  \
+                "4:                             \n\t"  \
+                " emms                          \n\t"
+
+#if defined (__i386__) || defined (__amd64__)
+static void remap_mono_to_stereo_s16ne_mmx(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    pa_reg_x86 temp, temp2;
+
+    __asm__ __volatile__ (
+        MONO_TO_STEREO(wd,4,15) /* do words to doubles */
+        : "+r" (dst), "+r" (src), "=&r" (temp), "=&r" (temp2)
+        : "r" ((pa_reg_x86)n)
+        : "cc"
+    );
+}
+
+static void remap_mono_to_stereo_float32ne_mmx(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    pa_reg_x86 temp, temp2;
+
+    __asm__ __volatile__ (
+        MONO_TO_STEREO(dq,3,7) /* do doubles to quads */
+        : "+r" (dst), "+r" (src), "=&r" (temp), "=&r" (temp2)
+        : "r" ((pa_reg_x86)n)
+        : "cc"
+    );
+}
+
+/* set the function that will execute the remapping based on the matrices */
+static void init_remap_mmx(pa_remap_t *m) {
+    unsigned n_oc, n_ic;
+
+    n_oc = m->o_ss.channels;
+    n_ic = m->i_ss.channels;
+
+    /* find some common channel remappings, fall back to full matrix operation. */
+    if (n_ic == 1 && n_oc == 2 &&
+            m->map_table_i[0][0] == 0x10000 && m->map_table_i[1][0] == 0x10000) {
+
+        pa_log_info("Using MMX mono to stereo remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_mono_to_stereo_s16ne_mmx,
+            (pa_do_remap_func_t) remap_mono_to_stereo_float32ne_mmx);
+    }
+}
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+void pa_remap_func_init_mmx(pa_cpu_x86_flag_t flags) {
+#if defined (__i386__) || defined (__amd64__)
+
+    if (flags & PA_CPU_X86_MMX) {
+        pa_log_info("Initialising MMX optimized remappers.");
+
+        pa_set_init_remap_func((pa_init_remap_func_t) init_remap_mmx);
+    }
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/remap_neon.c b/src/pulsecore/remap_neon.c
new file mode 100644 (file)
index 0000000..ebacf92
--- /dev/null
@@ -0,0 +1,498 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Peter Meerwald <p.meerwald@bct-electronic.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/sample.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "cpu-arm.h"
+#include "remap.h"
+
+#include <arm_neon.h>
+
+static void remap_mono_to_stereo_float32ne_neon_a8(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    for (; n >= 4; n -= 4) {
+        __asm__ __volatile__ (
+            "vld1.32    {q0}, [%[src]]!         \n\t"
+            "vmov       q1, q0                  \n\t"
+            "vst2.32    {q0,q1}, [%[dst]]!      \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : /* input operands */
+            : "memory", "q0", "q1" /* clobber list */
+        );
+    }
+
+    for (; n > 0; n--) {
+        dst[0] = dst[1] = src[0];
+        src++;
+        dst += 2;
+    }
+}
+
+static void remap_mono_to_stereo_float32ne_generic_arm(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    for (; n >= 2; n -= 2) {
+        __asm__ __volatile__ (
+            "ldm        %[src]!, {r4,r6}        \n\t"
+            "mov        r5, r4                  \n\t"
+            "mov        r7, r6                  \n\t"
+            "stm        %[dst]!, {r4-r7}        \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : /* input operands */
+            : "memory", "r4", "r5", "r6", "r7" /* clobber list */
+        );
+    }
+
+    if (n > 0)
+        dst[0] = dst[1] = src[0];
+}
+
+static void remap_mono_to_stereo_s16ne_neon(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    for (; n >= 8; n -= 8) {
+        __asm__ __volatile__ (
+            "vld1.16    {q0}, [%[src]]!         \n\t"
+            "vmov       q1, q0                  \n\t"
+            "vst2.16    {q0,q1}, [%[dst]]!      \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : /* input operands */
+            : "memory", "q0", "q1" /* clobber list */
+        );
+    }
+
+    for (; n > 0; n--) {
+        dst[0] = dst[1] = src[0];
+        src++;
+        dst += 2;
+    }
+}
+
+static void remap_mono_to_ch4_float32ne_neon(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    for (; n >= 2; n -= 2) {
+        __asm__ __volatile__ (
+            "vld1.32    {d0}, [%[src]]!         \n\t"
+            "vdup.f32   q1, d0[0]               \n\t"
+            "vdup.f32   q2, d0[1]               \n\t"
+            "vst1.32    {q1,q2}, [%[dst]]!      \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : /* input operands */
+            : "memory", "q0", "q1", "q2" /* clobber list */
+        );
+    }
+
+    if (n--)
+        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
+}
+
+static void remap_mono_to_ch4_s16ne_neon(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    for (; n >= 4; n -= 4) {
+        __asm__ __volatile__ (
+            "vld1.16    {d0}, [%[src]]!         \n\t"
+            "vdup.s16   d1, d0[1]               \n\t"
+            "vdup.s16   d2, d0[2]               \n\t"
+            "vdup.s16   d3, d0[3]               \n\t"
+            "vdup.s16   d0, d0[0]               \n\t"
+            "vst1.16    {d0,d1,d2,d3}, [%[dst]]!\n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : /* input operands */
+            : "memory", "d0", "d1", "d2", "d3" /* clobber list */
+        );
+    }
+
+    for (; n > 0; n--) {
+        dst[0] = dst[1] = dst[2] = dst[3] = src[0];
+        src++;
+        dst += 4;
+    }
+}
+
+static void remap_stereo_to_mono_float32ne_neon(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    const float32x4_t halve = vdupq_n_f32(0.5f);
+    for (; n >= 4; n -= 4) {
+        __asm__ __volatile__ (
+            "vld2.32    {q0,q1}, [%[src]]!      \n\t"
+            "vadd.f32   q0, q0, q1              \n\t"
+            "vmul.f32   q0, q0, %q[halve]       \n\t"
+            "vst1.32    {q0}, [%[dst]]!         \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : [halve] "w" (halve) /* input operands */
+            : "memory", "q0", "q1" /* clobber list */
+        );
+    }
+
+    for (; n > 0; n--) {
+        dst[0] = (src[0] + src[1])*0.5f;
+        src += 2;
+        dst++;
+    }
+}
+
+static void remap_stereo_to_mono_s16ne_neon(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    for (; n >= 8; n -= 8) {
+        __asm__ __volatile__ (
+            "vld2.16    {q0,q1}, [%[src]]!      \n\t"
+            "vrhadd.s16 q0, q0, q1              \n\t"
+            "vst1.16    {q0}, [%[dst]]!         \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : /* input operands */
+            : "memory", "q0", "q1" /* clobber list */
+        );
+    }
+
+    for (; n > 0; n--) {
+        dst[0] = (src[0] + src[1])/2;
+        src += 2;
+        dst++;
+    }
+}
+
+static void remap_ch4_to_mono_float32ne_neon(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    const float32x2_t quart = vdup_n_f32(0.25f);
+    for (; n >= 2; n -= 2) {
+        __asm__ __volatile__ (
+            "vld4.32    {d0,d1,d2,d3}, [%[src]]!\n\t"
+            "vadd.f32   d0, d0, d1              \n\t"
+            "vadd.f32   d2, d2, d3              \n\t"
+            "vadd.f32   d0, d0, d2              \n\t"
+            "vmul.f32   d0, d0, %[quart]        \n\t"
+            "vst1.32    {d0}, [%[dst]]!         \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : [quart] "w" (quart) /* input operands */
+            : "memory", "d0", "d1", "d2", "d3" /* clobber list */
+        );
+    }
+
+    if (n > 0)
+        dst[0] = (src[0] + src[1] + src[2] + src[3])*0.25f;
+}
+
+static void remap_ch4_to_mono_s16ne_neon(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    for (; n >= 4; n -= 4) {
+        __asm__ __volatile__ (
+            "vld4.16    {d0,d1,d2,d3}, [%[src]]!\n\t"
+            "vrhadd.s16 d0, d0, d1              \n\t"
+            "vrhadd.s16 d2, d2, d3              \n\t"
+            "vrhadd.s16 d0, d0, d2              \n\t"
+            "vst1.16    {d0}, [%[dst]]!         \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : /* input operands */
+            : "memory", "d0", "d1", "d2", "d3" /* clobber list */
+        );
+    }
+
+    for (; n > 0; n--) {
+        dst[0] = (src[0] + src[1] + src[2] + src[3])/4;
+        src += 4;
+        dst++;
+    }
+}
+
+static void remap_ch4_s16ne_neon(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    int32x4_t *f = m->state;
+    const int32x4_t f0 = f[0], f1 = f[1], f2 = f[2], f3 = f[3];
+
+    for (; n > 0; n--) {
+        __asm__ __volatile__ (
+            "vld1.16    {d0}, [%[src]]!         \n\t"
+            "vmovl.s16  q0, d0                  \n\t"
+            "vdup.s32   q1, d0[0]               \n\t"
+            "vmul.s32   q1, q1, %q[f0]          \n\t"
+            "vdup.s32   q2, d0[1]               \n\t"
+            "vmla.s32   q1, q2, %q[f1]          \n\t"
+            "vdup.s32   q2, d1[0]               \n\t"
+            "vmla.s32   q1, q2, %q[f2]          \n\t"
+            "vdup.s32   q2, d1[1]               \n\t"
+            "vmla.s32   q1, q2, %q[f3]          \n\t"
+            "vqshrn.s32  d2, q1, #16            \n\t"
+            "vst1.32    {d2}, [%[dst]]!         \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src)
+            : [f0] "w" (f0), [f1] "w" (f1), [f2] "w" (f2), [f3] "w" (f3)
+            : "memory", "q0", "q1", "q2"
+        );
+    }
+}
+
+static void remap_ch4_float32ne_neon(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    float32x4_t *f = m->state;
+    const float32x4_t f0 = f[0], f1 = f[1], f2 = f[2], f3 = f[3];
+
+    for (; n > 0; n--) {
+        __asm__ __volatile__ (
+            "vld1.32    {d0,d1}, [%[src]]!      \n\t"
+            "vdup.f32   q1, d0[0]               \n\t"
+            "vmul.f32   q1, q1, %q[f0]          \n\t"
+            "vdup.f32   q2, d0[1]               \n\t"
+            "vmla.f32   q1, q2, %q[f1]          \n\t"
+            "vdup.f32   q2, d1[0]               \n\t"
+            "vmla.f32   q1, q2, %q[f2]          \n\t"
+            "vdup.f32   q2, d1[1]               \n\t"
+            "vmla.f32   q1, q2, %q[f3]          \n\t"
+            "vst1.32    {d2,d3}, [%[dst]]!      \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src)
+            : [f0] "w" (f0), [f1] "w" (f1), [f2] "w" (f2), [f3] "w" (f3)
+            : "memory", "q0", "q1", "q2"
+        );
+    }
+}
+
+static void remap_arrange_stereo_s16ne_neon(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    const uint8x8_t t = ((uint8x8_t *) m->state)[0];
+
+    for (; n >= 2; n -= 2) {
+        __asm__ __volatile__ (
+            "vld1.s16   d0, [%[src]]!           \n\t"
+            "vtbl.8     d0, {d0}, %[t]          \n\t"
+            "vst1.s16   d0, [%[dst]]!           \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : [t] "w" (t) /* input operands */
+            : "memory", "d0" /* clobber list */
+        );
+    }
+
+    if (n > 0) {
+        __asm__ __volatile__ (
+            "vld1.32   d0[0], [%[src]]!         \n\t"
+            "vtbl.8    d0, {d0}, %[t]           \n\t"
+            "vst1.32   d0[0], [%[dst]]!         \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : [t] "w" (t) /* input operands */
+            : "memory", "d0" /* clobber list */
+        );
+    }
+}
+
+static void remap_arrange_ch2_ch4_s16ne_neon(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    const uint8x8_t t = ((uint8x8_t *) m->state)[0];
+
+    for (; n > 0; n--) {
+        __asm__ __volatile__ (
+            "vld1.32    d0[0], [%[src]]!           \n\t"
+            "vtbl.8     d0, {d0}, %[t]          \n\t"
+            "vst1.s16   d0, [%[dst]]!           \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : [t] "w" (t) /* input operands */
+            : "memory", "d0" /* clobber list */
+        );
+    }
+}
+
+static void remap_arrange_ch4_s16ne_neon(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    const uint8x8_t t = ((uint8x8_t *) m->state)[0];
+
+    for (; n > 0; n--) {
+        __asm__ __volatile__ (
+            "vld1.s16   d0, [%[src]]!           \n\t"
+            "vtbl.8     d0, {d0}, %[t]          \n\t"
+            "vst1.s16   d0, [%[dst]]!           \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : [t] "w" (t) /* input operands */
+            : "memory", "d0" /* clobber list */
+        );
+    }
+}
+
+static void remap_arrange_stereo_float32ne_neon(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    const uint8x8_t t = ((uint8x8_t *)m->state)[0];
+
+    for (; n > 0; n--) {
+        __asm__ __volatile__ (
+            "vld1.f32   d0, [%[src]]!           \n\t"
+            "vtbl.8     d0, {d0}, %[t]          \n\t"
+            "vst1.s16   {d0}, [%[dst]]!         \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : [t] "w" (t) /* input operands */
+            : "memory", "d0" /* clobber list */
+        );
+    }
+}
+
+static void remap_arrange_ch2_ch4_float32ne_neon(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    const uint8x8_t t0 = ((uint8x8_t *)m->state)[0];
+    const uint8x8_t t1 = ((uint8x8_t *)m->state)[1];
+
+    for (; n > 0; n--) {
+        __asm__ __volatile__ (
+            "vld1.f32   d0, [%[src]]!           \n\t"
+            "vtbl.8     d1, {d0}, %[t0]         \n\t"
+            "vtbl.8     d2, {d0}, %[t1]         \n\t"
+            "vst1.s16   {d1,d2}, [%[dst]]!      \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : [t0] "w" (t0), [t1] "w" (t1) /* input operands */
+            : "memory", "d0", "d1", "d2" /* clobber list */
+        );
+    }
+}
+
+static void remap_arrange_ch4_float32ne_neon(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    const uint8x8_t t0 = ((uint8x8_t *)m->state)[0];
+    const uint8x8_t t1 = ((uint8x8_t *)m->state)[1];
+
+    for (; n > 0; n--) {
+        __asm__ __volatile__ (
+            "vld1.f32   {d0,d1}, [%[src]]!      \n\t"
+            "vtbl.8     d2, {d0,d1}, %[t0]      \n\t"
+            "vtbl.8     d3, {d0,d1}, %[t1]      \n\t"
+            "vst1.s16   {d2,d3}, [%[dst]]!      \n\t"
+            : [dst] "+r" (dst), [src] "+r" (src) /* output operands */
+            : [t0] "w" (t0), [t1] "w" (t1) /* input operands */
+            : "memory", "d0", "d1", "d2", "d3" /* clobber list */
+        );
+    }
+}
+
+static pa_cpu_arm_flag_t arm_flags;
+
+static void init_remap_neon(pa_remap_t *m) {
+    unsigned n_oc, n_ic;
+    int8_t arrange[PA_CHANNELS_MAX];
+
+    n_oc = m->o_ss.channels;
+    n_ic = m->i_ss.channels;
+
+    if (n_ic == 1 && n_oc == 2 &&
+            m->map_table_i[0][0] == 0x10000 && m->map_table_i[1][0] == 0x10000) {
+        if (arm_flags & PA_CPU_ARM_CORTEX_A8) {
+
+            pa_log_info("Using ARM NEON/A8 mono to stereo remapping");
+            pa_set_remap_func(m, (pa_do_remap_func_t) remap_mono_to_stereo_s16ne_neon,
+                (pa_do_remap_func_t) remap_mono_to_stereo_float32ne_neon_a8);
+        }
+        else {
+            pa_log_info("Using ARM NEON mono to stereo remapping");
+            pa_set_remap_func(m, (pa_do_remap_func_t) remap_mono_to_stereo_s16ne_neon,
+                (pa_do_remap_func_t) remap_mono_to_stereo_float32ne_generic_arm);
+        }
+    } else if (n_ic == 1 && n_oc == 4 &&
+            m->map_table_i[0][0] == 0x10000 && m->map_table_i[1][0] == 0x10000 &&
+            m->map_table_i[2][0] == 0x10000 && m->map_table_i[3][0] == 0x10000) {
+
+        pa_log_info("Using ARM NEON mono to 4-channel remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_mono_to_ch4_s16ne_neon,
+            (pa_do_remap_func_t) remap_mono_to_ch4_float32ne_neon);
+    } else if (n_ic == 2 && n_oc == 1 &&
+            m->map_table_i[0][0] == 0x8000 && m->map_table_i[0][1] == 0x8000) {
+
+        pa_log_info("Using ARM NEON stereo to mono remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_stereo_to_mono_s16ne_neon,
+            (pa_do_remap_func_t) remap_stereo_to_mono_float32ne_neon);
+    } else if (n_ic == 4 && n_oc == 1 &&
+            m->map_table_i[0][0] == 0x4000 && m->map_table_i[0][1] == 0x4000 &&
+            m->map_table_i[0][2] == 0x4000 && m->map_table_i[0][3] == 0x4000) {
+
+        pa_log_info("Using ARM NEON 4-channel to mono remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_ch4_to_mono_s16ne_neon,
+            (pa_do_remap_func_t) remap_ch4_to_mono_float32ne_neon);
+    } else if (pa_setup_remap_arrange(m, arrange) &&
+        ((n_ic == 2 && n_oc == 2) ||
+         (n_ic == 2 && n_oc == 4) ||
+         (n_ic == 4 && n_oc == 4))) {
+        unsigned o;
+
+        if (n_ic == 2 && n_oc == 2) {
+            pa_log_info("Using NEON stereo arrange remapping");
+            pa_set_remap_func(m, (pa_do_remap_func_t) remap_arrange_stereo_s16ne_neon,
+                (pa_do_remap_func_t) remap_arrange_stereo_float32ne_neon);
+        } else if (n_ic == 2 && n_oc == 4) {
+            pa_log_info("Using NEON 2-channel to 4-channel arrange remapping");
+            pa_set_remap_func(m, (pa_do_remap_func_t) remap_arrange_ch2_ch4_s16ne_neon,
+                (pa_do_remap_func_t) remap_arrange_ch2_ch4_float32ne_neon);
+        } else if (n_ic == 4 && n_oc == 4) {
+            pa_log_info("Using NEON 4-channel arrange remapping");
+            pa_set_remap_func(m, (pa_do_remap_func_t) remap_arrange_ch4_s16ne_neon,
+                (pa_do_remap_func_t) remap_arrange_ch4_float32ne_neon);
+        }
+
+        /* setup state */
+        switch (m->format) {
+        case PA_SAMPLE_S16NE: {
+            uint8x8_t *t = m->state = pa_xnew0(uint8x8_t, 1);
+            for (o = 0; o < 4; o++) {
+                if (arrange[o % n_oc] >= 0) {
+                    /* convert channel index to vtbl indices */
+                    unsigned frame = o / n_oc;
+                    ((uint8_t *) t)[o * 2 + 0] = (frame * n_oc + arrange[o % n_oc]) * 2 + 0;
+                    ((uint8_t *) t)[o * 2 + 1] = (frame * n_oc + arrange[o % n_oc]) * 2 + 1;
+                } else {
+                    /* use invalid table indices to map to 0 */
+                    ((uint8_t *) t)[o * 2 + 0] = 0xff;
+                    ((uint8_t *) t)[o * 2 + 1] = 0xff;
+                }
+            }
+            break;
+        }
+        case PA_SAMPLE_FLOAT32NE: {
+            uint8x8_t *t = m->state = pa_xnew0(uint8x8_t, 2);
+            for (o = 0; o < n_oc; o++) {
+                if (arrange[o] >= 0) {
+                    /* convert channel index to vtbl indices */
+                    ((uint8_t *) t)[o * 4 + 0] = arrange[o] * 4 + 0;
+                    ((uint8_t *) t)[o * 4 + 1] = arrange[o] * 4 + 1;
+                    ((uint8_t *) t)[o * 4 + 2] = arrange[o] * 4 + 2;
+                    ((uint8_t *) t)[o * 4 + 3] = arrange[o] * 4 + 3;
+                } else {
+                    /* use invalid table indices to map to 0 */
+                    ((uint8_t *) t)[o * 4 + 0] = 0xff;
+                    ((uint8_t *) t)[o * 4 + 1] = 0xff;
+                    ((uint8_t *) t)[o * 4 + 2] = 0xff;
+                    ((uint8_t *) t)[o * 4 + 3] = 0xff;
+                }
+            }
+            break;
+        }
+        default:
+            pa_assert_not_reached();
+        }
+    } else if (n_ic == 4 && n_oc == 4) {
+        unsigned i, o;
+
+        pa_log_info("Using ARM NEON 4-channel remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_ch4_s16ne_neon,
+            (pa_do_remap_func_t) remap_ch4_float32ne_neon);
+
+        /* setup state */
+        switch (m->format) {
+        case PA_SAMPLE_S16NE: {
+            int32x4_t *f = m->state = pa_xnew0(int32x4_t, 4);
+            for (o = 0; o < 4; o++) {
+                for (i = 0; i < 4; i++) {
+                    ((int *) &f[i])[o] = PA_CLAMP_UNLIKELY(m->map_table_i[o][i], 0, 0x10000);
+                }
+            }
+            break;
+        }
+        case PA_SAMPLE_FLOAT32NE: {
+            float32x4_t *f = m->state = pa_xnew0(float32x4_t, 4);
+            for (o = 0; o < 4; o++) {
+                for (i = 0; i < 4; i++) {
+                    ((float *) &f[i])[o] = PA_CLAMP_UNLIKELY(m->map_table_f[o][i], 0.0f, 1.0f);
+                }
+            }
+            break;
+        }
+        default:
+            pa_assert_not_reached();
+        }
+    }
+}
+
+void pa_remap_func_init_neon(pa_cpu_arm_flag_t flags) {
+    pa_log_info("Initialising ARM NEON optimized remappers.");
+    arm_flags = flags;
+    pa_set_init_remap_func((pa_init_remap_func_t) init_remap_neon);
+}
diff --git a/src/pulsecore/remap_sse.c b/src/pulsecore/remap_sse.c
new file mode 100644 (file)
index 0000000..73e1cc8
--- /dev/null
@@ -0,0 +1,151 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "cpu-x86.h"
+#include "remap.h"
+
+#define LOAD_SAMPLES                                   \
+                " movdqu (%1), %%xmm0           \n\t"  \
+                " movdqu 16(%1), %%xmm2         \n\t"  \
+                " movdqu 32(%1), %%xmm4         \n\t"  \
+                " movdqu 48(%1), %%xmm6         \n\t"  \
+                " movdqa %%xmm0, %%xmm1         \n\t"  \
+                " movdqa %%xmm2, %%xmm3         \n\t"  \
+                " movdqa %%xmm4, %%xmm5         \n\t"  \
+                " movdqa %%xmm6, %%xmm7         \n\t"
+
+#define UNPACK_SAMPLES(s)                              \
+                " punpckl"#s" %%xmm0, %%xmm0    \n\t"  \
+                " punpckh"#s" %%xmm1, %%xmm1    \n\t"  \
+                " punpckl"#s" %%xmm2, %%xmm2    \n\t"  \
+                " punpckh"#s" %%xmm3, %%xmm3    \n\t"  \
+                " punpckl"#s" %%xmm4, %%xmm4    \n\t"  \
+                " punpckh"#s" %%xmm5, %%xmm5    \n\t"  \
+                " punpckl"#s" %%xmm6, %%xmm6    \n\t"  \
+                " punpckh"#s" %%xmm7, %%xmm7    \n\t"
+
+#define STORE_SAMPLES                                  \
+                " movdqu %%xmm0, (%0)           \n\t"  \
+                " movdqu %%xmm1, 16(%0)         \n\t"  \
+                " movdqu %%xmm2, 32(%0)         \n\t"  \
+                " movdqu %%xmm3, 48(%0)         \n\t"  \
+                " movdqu %%xmm4, 64(%0)         \n\t"  \
+                " movdqu %%xmm5, 80(%0)         \n\t"  \
+                " movdqu %%xmm6, 96(%0)         \n\t"  \
+                " movdqu %%xmm7, 112(%0)        \n\t"  \
+                " add $64, %1                   \n\t"  \
+                " add $128, %0                  \n\t"
+
+#define HANDLE_SINGLE_dq()                             \
+                " movd (%1), %%xmm0             \n\t"  \
+                " punpckldq %%xmm0, %%xmm0      \n\t"  \
+                " movq %%xmm0, (%0)             \n\t"  \
+                " add $4, %1                    \n\t"  \
+                " add $8, %0                    \n\t"
+
+#define HANDLE_SINGLE_wd()                             \
+                " movw (%1), %w3                \n\t"  \
+                " movd %3, %%xmm0               \n\t"  \
+                " punpcklwd %%xmm0, %%xmm0      \n\t"  \
+                " movd %%xmm0, (%0)             \n\t"  \
+                " add $2, %1                    \n\t"  \
+                " add $4, %0                    \n\t"
+
+#define MONO_TO_STEREO(s,shift,mask)                   \
+                " mov %4, %2                    \n\t"  \
+                " sar $"#shift", %2             \n\t"  \
+                " cmp $0, %2                    \n\t"  \
+                " je 2f                         \n\t"  \
+                "1:                             \n\t"  \
+                LOAD_SAMPLES                           \
+                UNPACK_SAMPLES(s)                      \
+                STORE_SAMPLES                          \
+                " dec %2                        \n\t"  \
+                " jne 1b                        \n\t"  \
+                "2:                             \n\t"  \
+                " mov %4, %2                    \n\t"  \
+                " and $"#mask", %2              \n\t"  \
+                " je 4f                         \n\t"  \
+                "3:                             \n\t"  \
+                HANDLE_SINGLE_##s()                    \
+                " dec %2                        \n\t"  \
+                " jne 3b                        \n\t"  \
+                "4:                             \n\t"
+
+#if defined (__i386__) || defined (__amd64__)
+static void remap_mono_to_stereo_s16ne_sse2(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
+    pa_reg_x86 temp, temp2;
+
+    __asm__ __volatile__ (
+        MONO_TO_STEREO(wd, 5, 31) /* do words to doubles */
+        : "+r" (dst), "+r" (src), "=&r" (temp), "=&r" (temp2)
+        : "r" ((pa_reg_x86)n)
+        : "cc"
+    );
+}
+
+static void remap_mono_to_stereo_float32ne_sse2(pa_remap_t *m, float *dst, const float *src, unsigned n) {
+    pa_reg_x86 temp, temp2;
+
+    __asm__ __volatile__ (
+        MONO_TO_STEREO(dq, 4, 15) /* do doubles to quads */
+        : "+r" (dst), "+r" (src), "=&r" (temp), "=&r" (temp2)
+        : "r" ((pa_reg_x86)n)
+        : "cc"
+    );
+}
+
+/* set the function that will execute the remapping based on the matrices */
+static void init_remap_sse2(pa_remap_t *m) {
+    unsigned n_oc, n_ic;
+
+    n_oc = m->o_ss.channels;
+    n_ic = m->i_ss.channels;
+
+    /* find some common channel remappings, fall back to full matrix operation. */
+    if (n_ic == 1 && n_oc == 2 &&
+            m->map_table_i[0][0] == 0x10000 && m->map_table_i[1][0] == 0x10000) {
+
+        pa_log_info("Using SSE2 mono to stereo remapping");
+        pa_set_remap_func(m, (pa_do_remap_func_t) remap_mono_to_stereo_s16ne_sse2,
+            (pa_do_remap_func_t) remap_mono_to_stereo_float32ne_sse2);
+    }
+}
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+void pa_remap_func_init_sse(pa_cpu_x86_flag_t flags) {
+#if defined (__i386__) || defined (__amd64__)
+
+    if (flags & PA_CPU_X86_SSE2) {
+        pa_log_info("Initialising SSE2 optimized remappers.");
+        pa_set_init_remap_func ((pa_init_remap_func_t) init_remap_sse2);
+    }
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
new file mode 100644 (file)
index 0000000..063c4c5
--- /dev/null
@@ -0,0 +1,1477 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core-util.h>
+
+#include "resampler.h"
+
+/* Number of samples of extra space we allow the resamplers to return */
+#define EXTRA_FRAMES 128
+
+struct ffmpeg_data { /* data specific to ffmpeg */
+    struct AVResampleContext *state;
+};
+
+static int copy_init(pa_resampler *r);
+
+static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed);
+static void free_remap(pa_remap_t *m);
+
+static int (* const init_table[])(pa_resampler *r) = {
+#ifdef HAVE_LIBSAMPLERATE
+    [PA_RESAMPLER_SRC_SINC_BEST_QUALITY]   = pa_resampler_libsamplerate_init,
+    [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = pa_resampler_libsamplerate_init,
+    [PA_RESAMPLER_SRC_SINC_FASTEST]        = pa_resampler_libsamplerate_init,
+    [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD]     = pa_resampler_libsamplerate_init,
+    [PA_RESAMPLER_SRC_LINEAR]              = pa_resampler_libsamplerate_init,
+#else
+    [PA_RESAMPLER_SRC_SINC_BEST_QUALITY]   = NULL,
+    [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = NULL,
+    [PA_RESAMPLER_SRC_SINC_FASTEST]        = NULL,
+    [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD]     = NULL,
+    [PA_RESAMPLER_SRC_LINEAR]              = NULL,
+#endif
+    [PA_RESAMPLER_TRIVIAL]                 = pa_resampler_trivial_init,
+#ifdef HAVE_SPEEX
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+0]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+1]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+2]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+3]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+4]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+5]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+6]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+7]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+8]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+9]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+10]     = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+0]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+1]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+2]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+3]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+4]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+5]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+6]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+7]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+8]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+9]      = pa_resampler_speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+10]     = pa_resampler_speex_init,
+#else
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+0]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+1]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+2]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+3]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+4]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+5]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+6]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+7]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+8]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+9]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+10]     = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+0]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+1]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+2]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+3]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+4]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+5]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+6]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+7]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+8]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+9]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+10]     = NULL,
+#endif
+    [PA_RESAMPLER_FFMPEG]                  = pa_resampler_ffmpeg_init,
+    [PA_RESAMPLER_AUTO]                    = NULL,
+    [PA_RESAMPLER_COPY]                    = copy_init,
+    [PA_RESAMPLER_PEAKS]                   = pa_resampler_peaks_init,
+#ifdef HAVE_SOXR
+    [PA_RESAMPLER_SOXR_MQ]                 = pa_resampler_soxr_init,
+    [PA_RESAMPLER_SOXR_HQ]                 = pa_resampler_soxr_init,
+    [PA_RESAMPLER_SOXR_VHQ]                = pa_resampler_soxr_init,
+#else
+    [PA_RESAMPLER_SOXR_MQ]                 = NULL,
+    [PA_RESAMPLER_SOXR_HQ]                 = NULL,
+    [PA_RESAMPLER_SOXR_VHQ]                = NULL,
+#endif
+};
+
+static pa_resample_method_t choose_auto_resampler(pa_resample_flags_t flags) {
+    pa_resample_method_t method;
+
+    if (pa_resample_method_supported(PA_RESAMPLER_SPEEX_FLOAT_BASE + 1))
+        method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1;
+    else if (flags & PA_RESAMPLER_VARIABLE_RATE)
+        method = PA_RESAMPLER_TRIVIAL;
+    else
+        method = PA_RESAMPLER_FFMPEG;
+
+    return method;
+}
+
+static pa_resample_method_t fix_method(
+                pa_resample_flags_t flags,
+                pa_resample_method_t method,
+                const uint32_t rate_a,
+                const uint32_t rate_b) {
+
+    pa_assert(pa_sample_rate_valid(rate_a));
+    pa_assert(pa_sample_rate_valid(rate_b));
+    pa_assert(method >= 0);
+    pa_assert(method < PA_RESAMPLER_MAX);
+
+    if (!(flags & PA_RESAMPLER_VARIABLE_RATE) && rate_a == rate_b) {
+        pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates.");
+        method = PA_RESAMPLER_COPY;
+    }
+
+    if (!pa_resample_method_supported(method)) {
+        pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(method));
+        method = PA_RESAMPLER_AUTO;
+    }
+
+    switch (method) {
+        case PA_RESAMPLER_COPY:
+            if (rate_a != rate_b) {
+                pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'.");
+                method = PA_RESAMPLER_AUTO;
+                break;
+            }
+                                     /* Else fall through */
+        case PA_RESAMPLER_FFMPEG:
+        case PA_RESAMPLER_SOXR_MQ:
+        case PA_RESAMPLER_SOXR_HQ:
+        case PA_RESAMPLER_SOXR_VHQ:
+            if (flags & PA_RESAMPLER_VARIABLE_RATE) {
+                pa_log_info("Resampler '%s' cannot do variable rate, reverting to resampler 'auto'.", pa_resample_method_to_string(method));
+                method = PA_RESAMPLER_AUTO;
+            }
+            break;
+
+        /* The Peaks resampler only supports downsampling.
+         * Revert to auto if we are upsampling */
+        case PA_RESAMPLER_PEAKS:
+            if (rate_a < rate_b) {
+                pa_log_warn("The 'peaks' resampler only supports downsampling, reverting to resampler 'auto'.");
+                method = PA_RESAMPLER_AUTO;
+            }
+            break;
+
+        default:
+            break;
+    }
+
+    if (method == PA_RESAMPLER_AUTO)
+        method = choose_auto_resampler(flags);
+
+#ifdef HAVE_SPEEX
+    /* At this point, method is supported in the sense that it
+     * has an init function and supports the required flags. However,
+     * speex-float implementation in PulseAudio relies on the
+     * assumption that is invalid if speex has been compiled with
+     * --enable-fixed-point. Besides, speex-fixed is more efficient
+     * in this configuration. So use it instead.
+     */
+    if (method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && method <= PA_RESAMPLER_SPEEX_FLOAT_MAX) {
+        if (pa_speex_is_fixed_point()) {
+            pa_log_info("Speex appears to be compiled with --enable-fixed-point. "
+                        "Switching to a fixed-point resampler because it should be faster.");
+            method = method - PA_RESAMPLER_SPEEX_FLOAT_BASE + PA_RESAMPLER_SPEEX_FIXED_BASE;
+        }
+    }
+#endif
+
+    return method;
+}
+
+/* Return true if a is a more precise sample format than b, else return false */
+static bool sample_format_more_precise(pa_sample_format_t a, pa_sample_format_t b) {
+    pa_assert(pa_sample_format_valid(a));
+    pa_assert(pa_sample_format_valid(b));
+
+    switch (a) {
+        case PA_SAMPLE_U8:
+        case PA_SAMPLE_ALAW:
+        case PA_SAMPLE_ULAW:
+            return false;
+            break;
+
+        case PA_SAMPLE_S16LE:
+        case PA_SAMPLE_S16BE:
+            if (b == PA_SAMPLE_ULAW || b == PA_SAMPLE_ALAW || b == PA_SAMPLE_U8)
+                return true;
+            else
+                return false;
+            break;
+
+        case PA_SAMPLE_S24LE:
+        case PA_SAMPLE_S24BE:
+        case PA_SAMPLE_S24_32LE:
+        case PA_SAMPLE_S24_32BE:
+            if (b == PA_SAMPLE_ULAW || b == PA_SAMPLE_ALAW || b == PA_SAMPLE_U8 ||
+                b == PA_SAMPLE_S16LE || b == PA_SAMPLE_S16BE)
+                return true;
+            else
+                return false;
+            break;
+
+        case PA_SAMPLE_FLOAT32LE:
+        case PA_SAMPLE_FLOAT32BE:
+        case PA_SAMPLE_S32LE:
+        case PA_SAMPLE_S32BE:
+            if (b == PA_SAMPLE_FLOAT32LE || b == PA_SAMPLE_FLOAT32BE ||
+                b == PA_SAMPLE_S32LE || b == PA_SAMPLE_S32BE)
+                return false;
+            else
+                return true;
+            break;
+
+        default:
+            return false;
+    }
+}
+
+static pa_sample_format_t choose_work_format(
+                    pa_resample_method_t method,
+                    pa_sample_format_t a,
+                    pa_sample_format_t b,
+                    bool map_required) {
+    pa_sample_format_t work_format;
+
+    pa_assert(pa_sample_format_valid(a));
+    pa_assert(pa_sample_format_valid(b));
+    pa_assert(method >= 0);
+    pa_assert(method < PA_RESAMPLER_MAX);
+
+    if (method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+        method = PA_RESAMPLER_SPEEX_FIXED_BASE;
+
+    switch (method) {
+        /* This block is for resampling functions that only
+         * support the S16 sample format. */
+        case PA_RESAMPLER_SPEEX_FIXED_BASE:     /* fall through */
+        case PA_RESAMPLER_FFMPEG:
+            work_format = PA_SAMPLE_S16NE;
+            break;
+
+        /* This block is for resampling functions that support
+         * any sample format. */
+        case PA_RESAMPLER_COPY:                 /* fall through */
+        case PA_RESAMPLER_TRIVIAL:
+            if (!map_required && a == b) {
+                work_format = a;
+                break;
+            }
+                                                /* Else fall trough */
+        case PA_RESAMPLER_PEAKS:
+            /* PEAKS, COPY and TRIVIAL do not benefit from increased
+             * working precision, so for better performance use s16ne
+             * if either input or output fits in it. */
+            if (a == PA_SAMPLE_S16NE || b == PA_SAMPLE_S16NE) {
+                work_format = PA_SAMPLE_S16NE;
+                break;
+            }
+                                                /* Else fall trough */
+        case PA_RESAMPLER_SOXR_MQ:
+        case PA_RESAMPLER_SOXR_HQ:
+        case PA_RESAMPLER_SOXR_VHQ:
+            /* Do processing with max precision of input and output. */
+            if (sample_format_more_precise(a, PA_SAMPLE_S16NE) ||
+                sample_format_more_precise(b, PA_SAMPLE_S16NE))
+                work_format = PA_SAMPLE_FLOAT32NE;
+            else
+                work_format = PA_SAMPLE_S16NE;
+            break;
+
+        default:
+            work_format = PA_SAMPLE_FLOAT32NE;
+    }
+
+    return work_format;
+}
+
+pa_resampler* pa_resampler_new(
+        pa_mempool *pool,
+        const pa_sample_spec *a,
+        const pa_channel_map *am,
+        const pa_sample_spec *b,
+        const pa_channel_map *bm,
+       unsigned crossover_freq,
+        pa_resample_method_t method,
+        pa_resample_flags_t flags) {
+
+    pa_resampler *r = NULL;
+    bool lfe_remixed = false;
+
+    pa_assert(pool);
+    pa_assert(a);
+    pa_assert(b);
+    pa_assert(pa_sample_spec_valid(a));
+    pa_assert(pa_sample_spec_valid(b));
+    pa_assert(method >= 0);
+    pa_assert(method < PA_RESAMPLER_MAX);
+
+    method = fix_method(flags, method, a->rate, b->rate);
+
+    r = pa_xnew0(pa_resampler, 1);
+    r->mempool = pool;
+    r->method = method;
+    r->flags = flags;
+
+    /* Fill sample specs */
+    r->i_ss = *a;
+    r->o_ss = *b;
+
+    if (am)
+        r->i_cm = *am;
+    else if (!pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT))
+        goto fail;
+
+    if (bm)
+        r->o_cm = *bm;
+    else if (!pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels, PA_CHANNEL_MAP_DEFAULT))
+        goto fail;
+
+    r->i_fz = pa_frame_size(a);
+    r->o_fz = pa_frame_size(b);
+
+    r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) &&
+        !pa_channel_map_equal(&r->i_cm, &r->o_cm)));
+
+    r->work_format = choose_work_format(method, a->format, b->format, r->map_required);
+    r->w_sz = pa_sample_size_of_format(r->work_format);
+
+    if (r->i_ss.format != r->work_format) {
+        if (r->work_format == PA_SAMPLE_FLOAT32NE) {
+            if (!(r->to_work_format_func = pa_get_convert_to_float32ne_function(r->i_ss.format)))
+                goto fail;
+        } else {
+            pa_assert(r->work_format == PA_SAMPLE_S16NE);
+            if (!(r->to_work_format_func = pa_get_convert_to_s16ne_function(r->i_ss.format)))
+                goto fail;
+        }
+    }
+
+    if (r->o_ss.format != r->work_format) {
+        if (r->work_format == PA_SAMPLE_FLOAT32NE) {
+            if (!(r->from_work_format_func = pa_get_convert_from_float32ne_function(r->o_ss.format)))
+                goto fail;
+        } else {
+            pa_assert(r->work_format == PA_SAMPLE_S16NE);
+            if (!(r->from_work_format_func = pa_get_convert_from_s16ne_function(r->o_ss.format)))
+                goto fail;
+        }
+    }
+
+    if (r->o_ss.channels <= r->i_ss.channels) {
+        /* pipeline is: format conv. -> remap -> resample -> format conv. */
+        r->work_channels = r->o_ss.channels;
+
+        /* leftover buffer is remap output buffer (before resampling) */
+        r->leftover_buf = &r->remap_buf;
+        r->leftover_buf_size = &r->remap_buf_size;
+        r->have_leftover = &r->leftover_in_remap;
+    } else {
+        /* pipeline is: format conv. -> resample -> remap -> format conv. */
+        r->work_channels = r->i_ss.channels;
+
+        /* leftover buffer is to_work output buffer (before resampling) */
+        r->leftover_buf = &r->to_work_format_buf;
+        r->leftover_buf_size = &r->to_work_format_buf_size;
+        r->have_leftover = &r->leftover_in_to_work;
+    }
+    r->w_fz = pa_sample_size_of_format(r->work_format) * r->work_channels;
+
+    pa_log_debug("Resampler:");
+    pa_log_debug("  rate %d -> %d (method %s)", a->rate, b->rate, pa_resample_method_to_string(r->method));
+    pa_log_debug("  format %s -> %s (intermediate %s)", pa_sample_format_to_string(a->format),
+                 pa_sample_format_to_string(b->format), pa_sample_format_to_string(r->work_format));
+    pa_log_debug("  channels %d -> %d (resampling %d)", a->channels, b->channels, r->work_channels);
+
+    /* set up the remap structure */
+    if (r->map_required)
+        setup_remap(r, &r->remap, &lfe_remixed);
+
+    if (lfe_remixed && crossover_freq > 0) {
+        pa_sample_spec wss = r->o_ss;
+        wss.format = r->work_format;
+        /* FIXME: For now just hardcode maxrewind to 3 seconds */
+        r->lfe_filter = pa_lfe_filter_new(&wss, &r->o_cm, (float)crossover_freq, b->rate * 3);
+        pa_log_debug("  lfe filter activated (LR4 type), the crossover_freq = %uHz", crossover_freq);
+    }
+
+    /* initialize implementation */
+    if (init_table[method](r) < 0)
+        goto fail;
+
+    return r;
+
+fail:
+    if (r->lfe_filter)
+      pa_lfe_filter_free(r->lfe_filter);
+    pa_xfree(r);
+
+    return NULL;
+}
+
+void pa_resampler_free(pa_resampler *r) {
+    pa_assert(r);
+
+    if (r->impl.free)
+        r->impl.free(r);
+    else
+        pa_xfree(r->impl.data);
+
+    if (r->lfe_filter)
+        pa_lfe_filter_free(r->lfe_filter);
+
+    if (r->to_work_format_buf.memblock)
+        pa_memblock_unref(r->to_work_format_buf.memblock);
+    if (r->remap_buf.memblock)
+        pa_memblock_unref(r->remap_buf.memblock);
+    if (r->resample_buf.memblock)
+        pa_memblock_unref(r->resample_buf.memblock);
+    if (r->from_work_format_buf.memblock)
+        pa_memblock_unref(r->from_work_format_buf.memblock);
+
+    free_remap(&r->remap);
+
+    pa_xfree(r);
+}
+
+void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) {
+    pa_assert(r);
+    pa_assert(rate > 0);
+    pa_assert(r->impl.update_rates);
+
+    if (r->i_ss.rate == rate)
+        return;
+
+    r->i_ss.rate = rate;
+
+    r->impl.update_rates(r);
+}
+
+void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) {
+    pa_assert(r);
+    pa_assert(rate > 0);
+    pa_assert(r->impl.update_rates);
+
+    if (r->o_ss.rate == rate)
+        return;
+
+    r->o_ss.rate = rate;
+
+    r->impl.update_rates(r);
+
+    if (r->lfe_filter)
+        pa_lfe_filter_update_rate(r->lfe_filter, rate);
+}
+
+size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
+    pa_assert(r);
+
+    /* Let's round up here to make it more likely that the caller will get at
+     * least out_length amount of data from pa_resampler_run().
+     *
+     * We don't take the leftover into account here. If we did, then it might
+     * be in theory possible that this function would return 0 and
+     * pa_resampler_run() would also return 0. That could lead to infinite
+     * loops. When the leftover is ignored here, such loops would eventually
+     * terminate, because the leftover would grow each round, finally
+     * surpassing the minimum input threshold of the resampler. */
+    return ((((uint64_t) ((out_length + r->o_fz-1) / r->o_fz) * r->i_ss.rate) + r->o_ss.rate-1) / r->o_ss.rate) * r->i_fz;
+}
+
+size_t pa_resampler_result(pa_resampler *r, size_t in_length) {
+    size_t frames;
+
+    pa_assert(r);
+
+    /* Let's round up here to ensure that the caller will always allocate big
+     * enough output buffer. */
+
+    frames = (in_length + r->i_fz - 1) / r->i_fz;
+    if (*r->have_leftover)
+        frames += r->leftover_buf->length / r->w_fz;
+
+    return (((uint64_t) frames * r->o_ss.rate + r->i_ss.rate - 1) / r->i_ss.rate) * r->o_fz;
+}
+
+size_t pa_resampler_max_block_size(pa_resampler *r) {
+    size_t block_size_max;
+    pa_sample_spec max_ss;
+    size_t max_fs;
+    size_t frames;
+
+    pa_assert(r);
+
+    block_size_max = pa_mempool_block_size_max(r->mempool);
+
+    /* We deduce the "largest" sample spec we're using during the
+     * conversion */
+    max_ss.channels = (uint8_t) (PA_MAX(r->i_ss.channels, r->o_ss.channels));
+
+    /* We silently assume that the format enum is ordered by size */
+    max_ss.format = PA_MAX(r->i_ss.format, r->o_ss.format);
+    max_ss.format = PA_MAX(max_ss.format, r->work_format);
+
+    max_ss.rate = PA_MAX(r->i_ss.rate, r->o_ss.rate);
+
+    max_fs = pa_frame_size(&max_ss);
+    frames = block_size_max / max_fs - EXTRA_FRAMES;
+
+    pa_assert(frames >= (r->leftover_buf->length / r->w_fz));
+    if (*r->have_leftover)
+        frames -= r->leftover_buf->length / r->w_fz;
+
+    block_size_max = ((uint64_t) frames * r->i_ss.rate / max_ss.rate) * r->i_fz;
+
+    if (block_size_max > 0)
+        return block_size_max;
+    else
+        /* A single input frame may result in so much output that it doesn't
+         * fit in one standard memblock (e.g. converting 1 Hz to 44100 Hz). In
+         * this case the max block size will be set to one frame, and some
+         * memory will be probably be allocated with malloc() instead of using
+         * the memory pool.
+         *
+         * XXX: Should we support this case at all? We could also refuse to
+         * create resamplers whose max block size would exceed the memory pool
+         * block size. In this case also updating the resampler rate should
+         * fail if the new rate would cause an excessive max block size (in
+         * which case the stream would probably have to be killed). */
+        return r->i_fz;
+}
+
+void pa_resampler_reset(pa_resampler *r) {
+    pa_assert(r);
+
+    if (r->impl.reset)
+        r->impl.reset(r);
+
+    if (r->lfe_filter)
+        pa_lfe_filter_reset(r->lfe_filter);
+
+    *r->have_leftover = false;
+}
+
+void pa_resampler_rewind(pa_resampler *r, size_t out_frames) {
+    pa_assert(r);
+
+    /* For now, we don't have any rewindable resamplers, so we just
+       reset the resampler instead (and hope that nobody hears the difference). */
+    if (r->impl.reset)
+        r->impl.reset(r);
+
+    if (r->lfe_filter)
+        pa_lfe_filter_rewind(r->lfe_filter, out_frames);
+
+    *r->have_leftover = false;
+}
+
+pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
+    pa_assert(r);
+
+    return r->method;
+}
+
+const pa_channel_map* pa_resampler_input_channel_map(pa_resampler *r) {
+    pa_assert(r);
+
+    return &r->i_cm;
+}
+
+const pa_sample_spec* pa_resampler_input_sample_spec(pa_resampler *r) {
+    pa_assert(r);
+
+    return &r->i_ss;
+}
+
+const pa_channel_map* pa_resampler_output_channel_map(pa_resampler *r) {
+    pa_assert(r);
+
+    return &r->o_cm;
+}
+
+const pa_sample_spec* pa_resampler_output_sample_spec(pa_resampler *r) {
+    pa_assert(r);
+
+    return &r->o_ss;
+}
+
+static const char * const resample_methods[] = {
+    "src-sinc-best-quality",
+    "src-sinc-medium-quality",
+    "src-sinc-fastest",
+    "src-zero-order-hold",
+    "src-linear",
+    "trivial",
+    "speex-float-0",
+    "speex-float-1",
+    "speex-float-2",
+    "speex-float-3",
+    "speex-float-4",
+    "speex-float-5",
+    "speex-float-6",
+    "speex-float-7",
+    "speex-float-8",
+    "speex-float-9",
+    "speex-float-10",
+    "speex-fixed-0",
+    "speex-fixed-1",
+    "speex-fixed-2",
+    "speex-fixed-3",
+    "speex-fixed-4",
+    "speex-fixed-5",
+    "speex-fixed-6",
+    "speex-fixed-7",
+    "speex-fixed-8",
+    "speex-fixed-9",
+    "speex-fixed-10",
+    "ffmpeg",
+    "auto",
+    "copy",
+    "peaks",
+    "soxr-mq",
+    "soxr-hq",
+    "soxr-vhq"
+};
+
+const char *pa_resample_method_to_string(pa_resample_method_t m) {
+
+    if (m < 0 || m >= PA_RESAMPLER_MAX)
+        return NULL;
+
+    return resample_methods[m];
+}
+
+int pa_resample_method_supported(pa_resample_method_t m) {
+
+    if (m < 0 || m >= PA_RESAMPLER_MAX)
+        return 0;
+
+#ifndef HAVE_LIBSAMPLERATE
+    if (m <= PA_RESAMPLER_SRC_LINEAR)
+        return 0;
+#endif
+
+#ifndef HAVE_SPEEX
+    if (m >= PA_RESAMPLER_SPEEX_FLOAT_BASE && m <= PA_RESAMPLER_SPEEX_FLOAT_MAX)
+        return 0;
+    if (m >= PA_RESAMPLER_SPEEX_FIXED_BASE && m <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+        return 0;
+#endif
+
+#ifndef HAVE_SOXR
+    if (m >= PA_RESAMPLER_SOXR_MQ && m <= PA_RESAMPLER_SOXR_VHQ)
+        return 0;
+#endif
+
+    return 1;
+}
+
+pa_resample_method_t pa_parse_resample_method(const char *string) {
+    pa_resample_method_t m;
+
+    pa_assert(string);
+
+    for (m = 0; m < PA_RESAMPLER_MAX; m++)
+        if (pa_streq(string, resample_methods[m]))
+            return m;
+
+    if (pa_streq(string, "speex-fixed"))
+        return PA_RESAMPLER_SPEEX_FIXED_BASE + 1;
+
+    if (pa_streq(string, "speex-float"))
+        return PA_RESAMPLER_SPEEX_FLOAT_BASE + 1;
+
+    return PA_RESAMPLER_INVALID;
+}
+
+static bool on_left(pa_channel_position_t p) {
+
+    return
+        p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_REAR_LEFT ||
+        p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+        p == PA_CHANNEL_POSITION_SIDE_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+}
+
+static bool on_right(pa_channel_position_t p) {
+
+    return
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_REAR_RIGHT ||
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
+        p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+}
+
+static bool on_center(pa_channel_position_t p) {
+
+    return
+        p == PA_CHANNEL_POSITION_FRONT_CENTER ||
+        p == PA_CHANNEL_POSITION_REAR_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+}
+
+static bool on_lfe(pa_channel_position_t p) {
+    return
+        p == PA_CHANNEL_POSITION_LFE;
+}
+
+static bool on_front(pa_channel_position_t p) {
+    return
+        p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_FRONT_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER ||
+        p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+}
+
+static bool on_rear(pa_channel_position_t p) {
+    return
+        p == PA_CHANNEL_POSITION_REAR_LEFT ||
+        p == PA_CHANNEL_POSITION_REAR_RIGHT ||
+        p == PA_CHANNEL_POSITION_REAR_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+}
+
+static bool on_side(pa_channel_position_t p) {
+    return
+        p == PA_CHANNEL_POSITION_SIDE_LEFT ||
+        p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_CENTER;
+}
+
+enum {
+    ON_FRONT,
+    ON_REAR,
+    ON_SIDE,
+    ON_OTHER
+};
+
+static int front_rear_side(pa_channel_position_t p) {
+    if (on_front(p))
+        return ON_FRONT;
+    if (on_rear(p))
+        return ON_REAR;
+    if (on_side(p))
+        return ON_SIDE;
+    return ON_OTHER;
+}
+
+/* Fill a map of which output channels should get mono from input, not including
+ * LFE output channels. (The LFE output channels are mapped separately.)
+ */
+static void setup_oc_mono_map(const pa_resampler *r, float *oc_mono_map) {
+    unsigned oc;
+    unsigned n_oc;
+    bool found_oc_for_mono = false;
+
+    pa_assert(r);
+    pa_assert(oc_mono_map);
+
+    n_oc = r->o_ss.channels;
+
+    if (!(r->flags & PA_RESAMPLER_NO_FILL_SINK)) {
+        /* Mono goes to all non-LFE output channels and we're done. */
+        for (oc = 0; oc < n_oc; oc++)
+            oc_mono_map[oc] = on_lfe(r->o_cm.map[oc]) ? 0.0f : 1.0f;
+        return;
+    } else {
+        /* Initialize to all zero so we can select individual channels below. */
+        for (oc = 0; oc < n_oc; oc++)
+            oc_mono_map[oc] = 0.0f;
+    }
+
+    for (oc = 0; oc < n_oc; oc++) {
+        if (r->o_cm.map[oc] == PA_CHANNEL_POSITION_MONO) {
+            oc_mono_map[oc] = 1.0f;
+            found_oc_for_mono = true;
+        }
+    }
+    if (found_oc_for_mono)
+        return;
+
+    for (oc = 0; oc < n_oc; oc++) {
+        if (r->o_cm.map[oc] == PA_CHANNEL_POSITION_FRONT_CENTER) {
+            oc_mono_map[oc] = 1.0f;
+            found_oc_for_mono = true;
+        }
+    }
+    if (found_oc_for_mono)
+        return;
+
+    for (oc = 0; oc < n_oc; oc++) {
+        if (r->o_cm.map[oc] == PA_CHANNEL_POSITION_FRONT_LEFT || r->o_cm.map[oc] == PA_CHANNEL_POSITION_FRONT_RIGHT) {
+            oc_mono_map[oc] = 1.0f;
+            found_oc_for_mono = true;
+        }
+    }
+    if (found_oc_for_mono)
+        return;
+
+    /* Give up on finding a suitable map for mono, and just send it to all
+     * non-LFE output channels.
+     */
+    for (oc = 0; oc < n_oc; oc++)
+        oc_mono_map[oc] = on_lfe(r->o_cm.map[oc]) ? 0.0f : 1.0f;
+}
+
+static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) {
+    unsigned oc, ic;
+    unsigned n_oc, n_ic;
+    bool ic_connected[PA_CHANNELS_MAX];
+    pa_strbuf *s;
+    char *t;
+
+    pa_assert(r);
+    pa_assert(m);
+    pa_assert(lfe_remixed);
+
+    n_oc = r->o_ss.channels;
+    n_ic = r->i_ss.channels;
+
+    m->format = r->work_format;
+    m->i_ss = r->i_ss;
+    m->o_ss = r->o_ss;
+
+    memset(m->map_table_f, 0, sizeof(m->map_table_f));
+    memset(m->map_table_i, 0, sizeof(m->map_table_i));
+
+    memset(ic_connected, 0, sizeof(ic_connected));
+    *lfe_remixed = false;
+
+    if (r->flags & PA_RESAMPLER_NO_REMAP) {
+        for (oc = 0; oc < PA_MIN(n_ic, n_oc); oc++)
+            m->map_table_f[oc][oc] = 1.0f;
+
+    } else if (r->flags & PA_RESAMPLER_NO_REMIX) {
+        for (oc = 0; oc < n_oc; oc++) {
+            pa_channel_position_t b = r->o_cm.map[oc];
+
+            for (ic = 0; ic < n_ic; ic++) {
+                pa_channel_position_t a = r->i_cm.map[ic];
+
+                /* We shall not do any remixing. Hence, just check by name */
+                if (a == b)
+                    m->map_table_f[oc][ic] = 1.0f;
+            }
+        }
+    } else {
+
+        /* OK, we shall do the full monty: upmixing and downmixing. Our
+         * algorithm is relatively simple, does not do spacialization, or delay
+         * elements. LFE filters are done after the remap step. Patches are always
+         * welcome, though. Oh, and it doesn't do any matrix decoding. (Which
+         * probably wouldn't make any sense anyway.)
+         *
+         * This code is not idempotent: downmixing an upmixed stereo stream is
+         * not identical to the original. The volume will not match, and the
+         * two channels will be a linear combination of both.
+         *
+         * This is loosely based on random suggestions found on the Internet,
+         * such as this:
+         * http://www.halfgaar.net/surround-sound-in-linux and the alsa upmix
+         * plugin.
+         *
+         * The algorithm works basically like this:
+         *
+         * 1) Connect all channels with matching names.
+         *
+         * 2) Mono Handling:
+         *    S:Mono: See setup_oc_mono_map().
+         *    D:Mono: Avg all S:channels
+         *
+         * 3) Mix D:Left, D:Right (if PA_RESAMPLER_NO_FILL_SINK is clear):
+         *    D:Left: If not connected, avg all S:Left
+         *    D:Right: If not connected, avg all S:Right
+         *
+         * 4) Mix D:Center (if PA_RESAMPLER_NO_FILL_SINK is clear):
+         *    If not connected, avg all S:Center
+         *    If still not connected, avg all S:Left, S:Right
+         *
+         * 5) Mix D:LFE
+         *    If not connected, avg all S:*
+         *
+         * 6) Make sure S:Left/S:Right is used: S:Left/S:Right: If not
+         *    connected, mix into all D:left and all D:right channels. Gain is
+         *    1/9.
+         *
+         * 7) Make sure S:Center, S:LFE is used:
+         *
+         *    S:Center, S:LFE: If not connected, mix into all D:left, all
+         *    D:right, all D:center channels. Gain is 0.5 for center and 0.375
+         *    for LFE. C-front is only mixed into L-front/R-front if available,
+         *    otherwise into all L/R channels. Similarly for C-rear.
+         *
+         * 8) Normalize each row in the matrix such that the sum for each row is
+         *    not larger than 1.0 in order to avoid clipping.
+         *
+         * S: and D: shall relate to the source resp. destination channels.
+         *
+         * Rationale: 1, 2 are probably obvious. For 3: this copies front to
+         * rear if needed. For 4: we try to find some suitable C source for C,
+         * if we don't find any, we avg L and R. For 5: LFE is mixed from all
+         * channels. For 6: the rear channels should not be dropped entirely,
+         * however have only minimal impact. For 7: movies usually encode
+         * speech on the center channel. Thus we have to make sure this channel
+         * is distributed to L and R if not available in the output. Also, LFE
+         * is used to achieve a greater dynamic range, and thus we should try
+         * to do our best to pass it to L+R.
+         */
+
+        unsigned
+            ic_left = 0,
+            ic_right = 0,
+            ic_center = 0,
+            ic_unconnected_left = 0,
+            ic_unconnected_right = 0,
+            ic_unconnected_center = 0,
+            ic_unconnected_lfe = 0;
+        bool ic_unconnected_center_mixed_in = 0;
+        float oc_mono_map[PA_CHANNELS_MAX];
+
+        for (ic = 0; ic < n_ic; ic++) {
+            if (on_left(r->i_cm.map[ic]))
+                ic_left++;
+            if (on_right(r->i_cm.map[ic]))
+                ic_right++;
+            if (on_center(r->i_cm.map[ic]))
+                ic_center++;
+        }
+
+        setup_oc_mono_map(r, oc_mono_map);
+
+        for (oc = 0; oc < n_oc; oc++) {
+            bool oc_connected = false;
+            pa_channel_position_t b = r->o_cm.map[oc];
+
+            for (ic = 0; ic < n_ic; ic++) {
+                pa_channel_position_t a = r->i_cm.map[ic];
+
+                if (a == b) {
+                    m->map_table_f[oc][ic] = 1.0f;
+
+                    oc_connected = true;
+                    ic_connected[ic] = true;
+                }
+                else if (a == PA_CHANNEL_POSITION_MONO && oc_mono_map[oc] > 0.0f) {
+                    m->map_table_f[oc][ic] = oc_mono_map[oc];
+
+                    oc_connected = true;
+                    ic_connected[ic] = true;
+                }
+                else if (b == PA_CHANNEL_POSITION_MONO) {
+                    m->map_table_f[oc][ic] = 1.0f / (float) n_ic;
+
+                    oc_connected = true;
+                    ic_connected[ic] = true;
+                }
+            }
+
+            if (!oc_connected) {
+                /* Try to find matching input ports for this output port */
+
+                if (on_left(b) && !(r->flags & PA_RESAMPLER_NO_FILL_SINK)) {
+
+                    /* We are not connected and on the left side, let's
+                     * average all left side input channels. */
+
+                    if (ic_left > 0)
+                        for (ic = 0; ic < n_ic; ic++)
+                            if (on_left(r->i_cm.map[ic])) {
+                                m->map_table_f[oc][ic] = 1.0f / (float) ic_left;
+                                ic_connected[ic] = true;
+                            }
+
+                    /* We ignore the case where there is no left input channel.
+                     * Something is really wrong in this case anyway. */
+
+                } else if (on_right(b) && !(r->flags & PA_RESAMPLER_NO_FILL_SINK)) {
+
+                    /* We are not connected and on the right side, let's
+                     * average all right side input channels. */
+
+                    if (ic_right > 0)
+                        for (ic = 0; ic < n_ic; ic++)
+                            if (on_right(r->i_cm.map[ic])) {
+                                m->map_table_f[oc][ic] = 1.0f / (float) ic_right;
+                                ic_connected[ic] = true;
+                            }
+
+                    /* We ignore the case where there is no right input
+                     * channel. Something is really wrong in this case anyway.
+                     * */
+
+                } else if (on_center(b) && !(r->flags & PA_RESAMPLER_NO_FILL_SINK)) {
+
+                    if (ic_center > 0) {
+
+                        /* We are not connected and at the center. Let's average
+                         * all center input channels. */
+
+                        for (ic = 0; ic < n_ic; ic++)
+                            if (on_center(r->i_cm.map[ic])) {
+                                m->map_table_f[oc][ic] = 1.0f / (float) ic_center;
+                                ic_connected[ic] = true;
+                            }
+
+                    } else if (ic_left + ic_right > 0) {
+
+                        /* Hmm, no center channel around, let's synthesize it
+                         * by mixing L and R.*/
+
+                        for (ic = 0; ic < n_ic; ic++)
+                            if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) {
+                                m->map_table_f[oc][ic] = 1.0f / (float) (ic_left + ic_right);
+                                ic_connected[ic] = true;
+                            }
+                    }
+
+                    /* We ignore the case where there is not even a left or
+                     * right input channel. Something is really wrong in this
+                     * case anyway. */
+
+                } else if (on_lfe(b) && !(r->flags & PA_RESAMPLER_NO_LFE)) {
+
+                    /* We are not connected and an LFE. Let's average all
+                     * channels for LFE. */
+
+                    for (ic = 0; ic < n_ic; ic++)
+                        m->map_table_f[oc][ic] = 1.0f / (float) n_ic;
+
+                    /* Please note that a channel connected to LFE doesn't
+                     * really count as connected. */
+
+                    *lfe_remixed = true;
+                }
+            }
+        }
+
+        for (ic = 0; ic < n_ic; ic++) {
+            pa_channel_position_t a = r->i_cm.map[ic];
+
+            if (ic_connected[ic])
+                continue;
+
+            if (on_left(a))
+                ic_unconnected_left++;
+            else if (on_right(a))
+                ic_unconnected_right++;
+            else if (on_center(a))
+                ic_unconnected_center++;
+            else if (on_lfe(a))
+                ic_unconnected_lfe++;
+        }
+
+        for (ic = 0; ic < n_ic; ic++) {
+            pa_channel_position_t a = r->i_cm.map[ic];
+
+            if (ic_connected[ic])
+                continue;
+
+            for (oc = 0; oc < n_oc; oc++) {
+                pa_channel_position_t b = r->o_cm.map[oc];
+
+                if (on_left(a) && on_left(b))
+                    m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_left;
+
+                else if (on_right(a) && on_right(b))
+                    m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_right;
+
+                else if (on_center(a) && on_center(b)) {
+                    m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_center;
+                    ic_unconnected_center_mixed_in = true;
+
+                } else if (on_lfe(a) && !(r->flags & PA_RESAMPLER_NO_LFE))
+                    m->map_table_f[oc][ic] = .375f / (float) ic_unconnected_lfe;
+            }
+        }
+
+        if (ic_unconnected_center > 0 && !ic_unconnected_center_mixed_in) {
+            unsigned ncenter[PA_CHANNELS_MAX];
+            bool found_frs[PA_CHANNELS_MAX];
+
+            memset(ncenter, 0, sizeof(ncenter));
+            memset(found_frs, 0, sizeof(found_frs));
+
+            /* Hmm, as it appears there was no center channel we
+               could mix our center channel in. In this case, mix it into
+               left and right. Using .5 as the factor. */
+
+            for (ic = 0; ic < n_ic; ic++) {
+
+                if (ic_connected[ic])
+                    continue;
+
+                if (!on_center(r->i_cm.map[ic]))
+                    continue;
+
+                for (oc = 0; oc < n_oc; oc++) {
+
+                    if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
+                        continue;
+
+                    if (front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) {
+                        found_frs[ic] = true;
+                        break;
+                    }
+                }
+
+                for (oc = 0; oc < n_oc; oc++) {
+
+                    if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
+                        continue;
+
+                    if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc]))
+                        ncenter[oc]++;
+                }
+            }
+
+            for (oc = 0; oc < n_oc; oc++) {
+
+                if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
+                    continue;
+
+                if (ncenter[oc] <= 0)
+                    continue;
+
+                for (ic = 0; ic < n_ic; ic++) {
+
+                    if (!on_center(r->i_cm.map[ic]))
+                        continue;
+
+                    if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc]))
+                        m->map_table_f[oc][ic] = .5f / (float) ncenter[oc];
+                }
+            }
+        }
+    }
+
+    for (oc = 0; oc < n_oc; oc++) {
+        float sum = 0.0f;
+        for (ic = 0; ic < n_ic; ic++)
+            sum += m->map_table_f[oc][ic];
+
+        if (sum > 1.0f)
+            for (ic = 0; ic < n_ic; ic++)
+                m->map_table_f[oc][ic] /= sum;
+    }
+
+    /* make an 16:16 int version of the matrix */
+    for (oc = 0; oc < n_oc; oc++)
+        for (ic = 0; ic < n_ic; ic++)
+            m->map_table_i[oc][ic] = (int32_t) (m->map_table_f[oc][ic] * 0x10000);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "     ");
+    for (ic = 0; ic < n_ic; ic++)
+        pa_strbuf_printf(s, "  I%02u ", ic);
+    pa_strbuf_puts(s, "\n    +");
+
+    for (ic = 0; ic < n_ic; ic++)
+        pa_strbuf_printf(s, "------");
+    pa_strbuf_puts(s, "\n");
+
+    for (oc = 0; oc < n_oc; oc++) {
+        pa_strbuf_printf(s, "O%02u |", oc);
+
+        for (ic = 0; ic < n_ic; ic++)
+            pa_strbuf_printf(s, " %1.3f", m->map_table_f[oc][ic]);
+
+        pa_strbuf_puts(s, "\n");
+    }
+
+    pa_log_debug("Channel matrix:\n%s", t = pa_strbuf_to_string_free(s));
+    pa_xfree(t);
+
+    /* initialize the remapping function */
+    pa_init_remap_func(m);
+}
+
+static void free_remap(pa_remap_t *m) {
+    pa_assert(m);
+
+    pa_xfree(m->state);
+}
+
+/* check if buf's memblock is large enough to hold 'len' bytes; create a
+ * new memblock if necessary and optionally preserve 'copy' data bytes */
+static void fit_buf(pa_resampler *r, pa_memchunk *buf, size_t len, size_t *size, size_t copy) {
+    pa_assert(size);
+
+    if (!buf->memblock || len > *size) {
+        pa_memblock *new_block = pa_memblock_new(r->mempool, len);
+
+        if (buf->memblock) {
+            if (copy > 0) {
+                void *src = pa_memblock_acquire(buf->memblock);
+                void *dst = pa_memblock_acquire(new_block);
+                pa_assert(copy <= len);
+                memcpy(dst, src, copy);
+                pa_memblock_release(new_block);
+                pa_memblock_release(buf->memblock);
+            }
+
+            pa_memblock_unref(buf->memblock);
+        }
+
+        buf->memblock = new_block;
+        *size = len;
+    }
+
+    buf->length = len;
+}
+
+static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
+    unsigned in_n_samples, out_n_samples;
+    void *src, *dst;
+    bool have_leftover;
+    size_t leftover_length = 0;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(input->memblock);
+
+    /* Convert the incoming sample into the work sample format and place them
+     * in to_work_format_buf. The leftover data is already converted, so it's
+     * part of the output buffer. */
+
+    have_leftover = r->leftover_in_to_work;
+    r->leftover_in_to_work = false;
+
+    if (!have_leftover && (!r->to_work_format_func || !input->length))
+        return input;
+    else if (input->length <= 0)
+        return &r->to_work_format_buf;
+
+    in_n_samples = out_n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);
+
+    if (have_leftover) {
+        leftover_length = r->to_work_format_buf.length;
+        out_n_samples += (unsigned) (leftover_length / r->w_sz);
+    }
+
+    fit_buf(r, &r->to_work_format_buf, r->w_sz * out_n_samples, &r->to_work_format_buf_size, leftover_length);
+
+    src = pa_memblock_acquire_chunk(input);
+    dst = (uint8_t *) pa_memblock_acquire(r->to_work_format_buf.memblock) + leftover_length;
+
+    if (r->to_work_format_func)
+        r->to_work_format_func(in_n_samples, src, dst);
+    else
+        memcpy(dst, src, input->length);
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(r->to_work_format_buf.memblock);
+
+    return &r->to_work_format_buf;
+}
+
+static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
+    unsigned in_n_samples, out_n_samples, in_n_frames, out_n_frames;
+    void *src, *dst;
+    size_t leftover_length = 0;
+    bool have_leftover;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(input->memblock);
+
+    /* Remap channels and place the result in remap_buf. There may be leftover
+     * data in the beginning of remap_buf. The leftover data is already
+     * remapped, so it's not part of the input, it's part of the output. */
+
+    have_leftover = r->leftover_in_remap;
+    r->leftover_in_remap = false;
+
+    if (!have_leftover && (!r->map_required || input->length <= 0))
+        return input;
+    else if (input->length <= 0)
+        return &r->remap_buf;
+
+    in_n_samples = (unsigned) (input->length / r->w_sz);
+    in_n_frames = out_n_frames = in_n_samples / r->i_ss.channels;
+
+    if (have_leftover) {
+        leftover_length = r->remap_buf.length;
+        out_n_frames += leftover_length / r->w_fz;
+    }
+
+    out_n_samples = out_n_frames * r->o_ss.channels;
+    fit_buf(r, &r->remap_buf, out_n_samples * r->w_sz, &r->remap_buf_size, leftover_length);
+
+    src = pa_memblock_acquire_chunk(input);
+    dst = (uint8_t *) pa_memblock_acquire(r->remap_buf.memblock) + leftover_length;
+
+    if (r->map_required) {
+        pa_remap_t *remap = &r->remap;
+
+        pa_assert(remap->do_remap);
+        remap->do_remap(remap, dst, src, in_n_frames);
+
+    } else
+        memcpy(dst, src, input->length);
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(r->remap_buf.memblock);
+
+    return &r->remap_buf;
+}
+
+static void save_leftover(pa_resampler *r, void *buf, size_t len) {
+    void *dst;
+
+    pa_assert(r);
+    pa_assert(buf);
+    pa_assert(len > 0);
+
+    /* Store the leftover data. */
+    fit_buf(r, r->leftover_buf, len, r->leftover_buf_size, 0);
+    *r->have_leftover = true;
+
+    dst = pa_memblock_acquire(r->leftover_buf->memblock);
+    memmove(dst, buf, len);
+    pa_memblock_release(r->leftover_buf->memblock);
+}
+
+static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
+    unsigned in_n_frames, out_n_frames, leftover_n_frames;
+
+    pa_assert(r);
+    pa_assert(input);
+
+    /* Resample the data and place the result in resample_buf. */
+
+    if (!r->impl.resample || !input->length)
+        return input;
+
+    in_n_frames = (unsigned) (input->length / r->w_fz);
+
+    out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES;
+    fit_buf(r, &r->resample_buf, r->w_fz * out_n_frames, &r->resample_buf_size, 0);
+
+    leftover_n_frames = r->impl.resample(r, input, in_n_frames, &r->resample_buf, &out_n_frames);
+
+    if (leftover_n_frames > 0) {
+        void *leftover_data = (uint8_t *) pa_memblock_acquire_chunk(input) + (in_n_frames - leftover_n_frames) * r->w_fz;
+        save_leftover(r, leftover_data, leftover_n_frames * r->w_fz);
+        pa_memblock_release(input->memblock);
+    }
+
+    r->resample_buf.length = out_n_frames * r->w_fz;
+
+    return &r->resample_buf;
+}
+
+static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input) {
+    unsigned n_samples, n_frames;
+    void *src, *dst;
+
+    pa_assert(r);
+    pa_assert(input);
+
+    /* Convert the data into the correct sample type and place the result in
+     * from_work_format_buf. */
+
+    if (!r->from_work_format_func || !input->length)
+        return input;
+
+    n_samples = (unsigned) (input->length / r->w_sz);
+    n_frames = n_samples / r->o_ss.channels;
+    fit_buf(r, &r->from_work_format_buf, r->o_fz * n_frames, &r->from_work_format_buf_size, 0);
+
+    src = pa_memblock_acquire_chunk(input);
+    dst = pa_memblock_acquire(r->from_work_format_buf.memblock);
+    r->from_work_format_func(n_samples, src, dst);
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(r->from_work_format_buf.memblock);
+
+    return &r->from_work_format_buf;
+}
+
+void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
+    pa_memchunk *buf;
+
+    pa_assert(r);
+    pa_assert(in);
+    pa_assert(out);
+    pa_assert(in->length);
+    pa_assert(in->memblock);
+    pa_assert(in->length % r->i_fz == 0);
+
+    buf = (pa_memchunk*) in;
+    buf = convert_to_work_format(r, buf);
+
+    /* Try to save resampling effort: if we have more output channels than
+     * input channels, do resampling first, then remapping. */
+    if (r->o_ss.channels <= r->i_ss.channels) {
+        buf = remap_channels(r, buf);
+        buf = resample(r, buf);
+    } else {
+        buf = resample(r, buf);
+        buf = remap_channels(r, buf);
+    }
+
+    if (r->lfe_filter)
+        buf = pa_lfe_filter_process(r->lfe_filter, buf);
+
+    if (buf->length) {
+        buf = convert_from_work_format(r, buf);
+        *out = *buf;
+
+        if (buf == in)
+            pa_memblock_ref(buf->memblock);
+        else
+            pa_memchunk_reset(buf);
+    } else
+        pa_memchunk_reset(out);
+}
+
+/*** copy (noop) implementation ***/
+
+static int copy_init(pa_resampler *r) {
+    pa_assert(r);
+
+    pa_assert(r->o_ss.rate == r->i_ss.rate);
+
+    return 0;
+}
diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h
new file mode 100644 (file)
index 0000000..5d3171f
--- /dev/null
@@ -0,0 +1,180 @@
+#ifndef fooresamplerhfoo
+#define fooresamplerhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sconv.h>
+#include <pulsecore/remap.h>
+#include <pulsecore/filter/lfe-filter.h>
+
+typedef struct pa_resampler pa_resampler;
+typedef struct pa_resampler_impl pa_resampler_impl;
+
+struct pa_resampler_impl {
+    void (*free)(pa_resampler *r);
+    void (*update_rates)(pa_resampler *r);
+
+    /* Returns the number of leftover frames in the input buffer. */
+    unsigned (*resample)(pa_resampler *r, const pa_memchunk *in, unsigned in_n_frames, pa_memchunk *out, unsigned *out_n_frames);
+
+    void (*reset)(pa_resampler *r);
+    void *data;
+};
+
+typedef enum pa_resample_method {
+    PA_RESAMPLER_INVALID                 = -1,
+    PA_RESAMPLER_SRC_SINC_BEST_QUALITY   = 0, /* = SRC_SINC_BEST_QUALITY */
+    PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = 1, /* = SRC_SINC_MEDIUM_QUALITY */
+    PA_RESAMPLER_SRC_SINC_FASTEST        = 2, /* = SRC_SINC_FASTEST */
+    PA_RESAMPLER_SRC_ZERO_ORDER_HOLD     = 3, /* = SRC_ZERO_ORDER_HOLD */
+    PA_RESAMPLER_SRC_LINEAR              = 4, /* = SRC_LINEAR */
+    PA_RESAMPLER_TRIVIAL,
+    PA_RESAMPLER_SPEEX_FLOAT_BASE,
+    PA_RESAMPLER_SPEEX_FLOAT_MAX = PA_RESAMPLER_SPEEX_FLOAT_BASE + 10,
+    PA_RESAMPLER_SPEEX_FIXED_BASE,
+    PA_RESAMPLER_SPEEX_FIXED_MAX = PA_RESAMPLER_SPEEX_FIXED_BASE + 10,
+    PA_RESAMPLER_FFMPEG,
+    PA_RESAMPLER_AUTO, /* automatic select based on sample format */
+    PA_RESAMPLER_COPY,
+    PA_RESAMPLER_PEAKS,
+    PA_RESAMPLER_SOXR_MQ,
+    PA_RESAMPLER_SOXR_HQ,
+    PA_RESAMPLER_SOXR_VHQ,
+    PA_RESAMPLER_MAX
+} pa_resample_method_t;
+
+typedef enum pa_resample_flags {
+    PA_RESAMPLER_VARIABLE_RATE = 0x0001U,
+    PA_RESAMPLER_NO_REMAP      = 0x0002U,  /* implies NO_REMIX */
+    PA_RESAMPLER_NO_REMIX      = 0x0004U,
+    PA_RESAMPLER_NO_LFE        = 0x0008U,
+    PA_RESAMPLER_NO_FILL_SINK  = 0x0010U,
+} pa_resample_flags_t;
+
+struct pa_resampler {
+    pa_resample_method_t method;
+    pa_resample_flags_t flags;
+
+    pa_sample_spec i_ss, o_ss;
+    pa_channel_map i_cm, o_cm;
+    size_t i_fz, o_fz, w_fz, w_sz;
+    pa_mempool *mempool;
+
+    pa_memchunk to_work_format_buf;
+    pa_memchunk remap_buf;
+    pa_memchunk resample_buf;
+    pa_memchunk from_work_format_buf;
+    size_t to_work_format_buf_size;
+    size_t remap_buf_size;
+    size_t resample_buf_size;
+    size_t from_work_format_buf_size;
+
+    /* points to buffer before resampling stage, remap or to_work */
+    pa_memchunk *leftover_buf;
+    size_t *leftover_buf_size;
+
+    /* have_leftover points to leftover_in_remap or leftover_in_to_work */
+    bool *have_leftover;
+    bool leftover_in_remap;
+    bool leftover_in_to_work;
+
+    pa_sample_format_t work_format;
+    uint8_t work_channels;
+
+    pa_convert_func_t to_work_format_func;
+    pa_convert_func_t from_work_format_func;
+
+    pa_remap_t remap;
+    bool map_required;
+
+    pa_lfe_filter_t *lfe_filter;
+
+    pa_resampler_impl impl;
+};
+
+pa_resampler* pa_resampler_new(
+        pa_mempool *pool,
+        const pa_sample_spec *a,
+        const pa_channel_map *am,
+        const pa_sample_spec *b,
+        const pa_channel_map *bm,
+       unsigned crossover_freq,
+        pa_resample_method_t resample_method,
+        pa_resample_flags_t flags);
+
+void pa_resampler_free(pa_resampler *r);
+
+/* Returns the size of an input memory block which is required to return the specified amount of output data */
+size_t pa_resampler_request(pa_resampler *r, size_t out_length);
+
+/* Inverse of pa_resampler_request() */
+size_t pa_resampler_result(pa_resampler *r, size_t in_length);
+
+/* Returns the maximum size of input blocks we can process without needing bounce buffers larger than the mempool tile size. */
+size_t pa_resampler_max_block_size(pa_resampler *r);
+
+/* Pass the specified memory chunk to the resampler and return the newly resampled data */
+void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out);
+
+/* Change the input rate of the resampler object */
+void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate);
+
+/* Change the output rate of the resampler object */
+void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate);
+
+/* Reinitialize state of the resampler, possibly due to seeking or other discontinuities */
+void pa_resampler_reset(pa_resampler *r);
+
+/* Rewind resampler */
+void pa_resampler_rewind(pa_resampler *r, size_t out_frames);
+
+/* Return the resampling method of the resampler object */
+pa_resample_method_t pa_resampler_get_method(pa_resampler *r);
+
+/* Try to parse the resampler method */
+pa_resample_method_t pa_parse_resample_method(const char *string);
+
+/* return a human readable string for the specified resampling method. Inverse of pa_parse_resample_method() */
+const char *pa_resample_method_to_string(pa_resample_method_t m);
+
+/* Return 1 when the specified resampling method is supported */
+int pa_resample_method_supported(pa_resample_method_t m);
+
+const pa_channel_map* pa_resampler_input_channel_map(pa_resampler *r);
+const pa_sample_spec* pa_resampler_input_sample_spec(pa_resampler *r);
+const pa_channel_map* pa_resampler_output_channel_map(pa_resampler *r);
+const pa_sample_spec* pa_resampler_output_sample_spec(pa_resampler *r);
+
+/* Implementation specific init functions */
+int pa_resampler_ffmpeg_init(pa_resampler *r);
+int pa_resampler_libsamplerate_init(pa_resampler *r);
+int pa_resampler_peaks_init(pa_resampler *r);
+int pa_resampler_speex_init(pa_resampler *r);
+int pa_resampler_trivial_init(pa_resampler*r);
+int pa_resampler_soxr_init(pa_resampler *r);
+
+/* Resampler-specific quirks */
+bool pa_speex_is_fixed_point(void);
+
+#endif
diff --git a/src/pulsecore/resampler/ffmpeg.c b/src/pulsecore/resampler/ffmpeg.c
new file mode 100644 (file)
index 0000000..388b555
--- /dev/null
@@ -0,0 +1,132 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include "pulsecore/ffmpeg/avcodec.h"
+
+#include <pulsecore/resampler.h>
+
+struct ffmpeg_data { /* data specific to ffmpeg */
+    struct AVResampleContext *state;
+};
+
+static unsigned ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    unsigned used_frames = 0, c;
+    int previous_consumed_frames = -1;
+    struct ffmpeg_data *ffmpeg_data;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    ffmpeg_data = r->impl.data;
+
+    for (c = 0; c < r->work_channels; c++) {
+        unsigned u;
+        pa_memblock *b, *w;
+        int16_t *p, *t, *k, *q, *s;
+        int consumed_frames;
+
+        /* Allocate a new block */
+        b = pa_memblock_new(r->mempool, in_n_frames * sizeof(int16_t));
+        p = pa_memblock_acquire(b);
+
+        /* Now copy the input data, splitting up channels */
+        t = (int16_t*) pa_memblock_acquire_chunk(input) + c;
+        k = p;
+        for (u = 0; u < in_n_frames; u++) {
+            *k = *t;
+            t += r->work_channels;
+            k ++;
+        }
+        pa_memblock_release(input->memblock);
+
+        /* Allocate buffer for the result */
+        w = pa_memblock_new(r->mempool, *out_n_frames * sizeof(int16_t));
+        q = pa_memblock_acquire(w);
+
+        /* Now, resample */
+        used_frames = (unsigned) av_resample(ffmpeg_data->state,
+                                             q, p,
+                                             &consumed_frames,
+                                             (int) in_n_frames, (int) *out_n_frames,
+                                             c >= (unsigned) (r->work_channels-1));
+
+        pa_memblock_release(b);
+        pa_memblock_unref(b);
+
+        pa_assert(consumed_frames <= (int) in_n_frames);
+        pa_assert(previous_consumed_frames == -1 || consumed_frames == previous_consumed_frames);
+        previous_consumed_frames = consumed_frames;
+
+        /* And place the results in the output buffer */
+        s = (int16_t *) pa_memblock_acquire_chunk(output) + c;
+        for (u = 0; u < used_frames; u++) {
+            *s = *q;
+            q++;
+            s += r->work_channels;
+        }
+        pa_memblock_release(output->memblock);
+        pa_memblock_release(w);
+        pa_memblock_unref(w);
+    }
+
+    *out_n_frames = used_frames;
+
+    return in_n_frames - previous_consumed_frames;
+}
+
+static void ffmpeg_free(pa_resampler *r) {
+    struct ffmpeg_data *ffmpeg_data;
+
+    pa_assert(r);
+
+    ffmpeg_data = r->impl.data;
+    if (ffmpeg_data->state)
+        av_resample_close(ffmpeg_data->state);
+}
+
+int pa_resampler_ffmpeg_init(pa_resampler *r) {
+    struct ffmpeg_data *ffmpeg_data;
+
+    pa_assert(r);
+
+    ffmpeg_data = pa_xnew(struct ffmpeg_data, 1);
+
+    /* We could probably implement different quality levels by
+     * adjusting the filter parameters here. However, ffmpeg
+     * internally only uses these hardcoded values, so let's use them
+     * here for now as well until ffmpeg makes this configurable. */
+
+    if (!(ffmpeg_data->state = av_resample_init((int) r->o_ss.rate, (int) r->i_ss.rate, 16, 10, 0, 0.8))) {
+        pa_xfree(ffmpeg_data);
+        return -1;
+    }
+
+    r->impl.free = ffmpeg_free;
+    r->impl.resample = ffmpeg_resample;
+    r->impl.data = (void *) ffmpeg_data;
+
+    return 0;
+}
diff --git a/src/pulsecore/resampler/libsamplerate.c b/src/pulsecore/resampler/libsamplerate.c
new file mode 100644 (file)
index 0000000..06704fe
--- /dev/null
@@ -0,0 +1,100 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <samplerate.h>
+
+#include <pulsecore/resampler.h>
+
+static unsigned libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    SRC_DATA data;
+    SRC_STATE *state;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    state = r->impl.data;
+    memset(&data, 0, sizeof(data));
+
+    data.data_in = pa_memblock_acquire_chunk(input);
+    data.input_frames = (long int) in_n_frames;
+
+    data.data_out = pa_memblock_acquire_chunk(output);
+    data.output_frames = (long int) *out_n_frames;
+
+    data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
+    data.end_of_input = 0;
+
+    pa_assert_se(src_process(state, &data) == 0);
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    *out_n_frames = (unsigned) data.output_frames_gen;
+
+    return in_n_frames - data.input_frames_used;
+}
+
+static void libsamplerate_update_rates(pa_resampler *r) {
+    SRC_STATE *state;
+    pa_assert(r);
+
+    state = r->impl.data;
+    pa_assert_se(src_set_ratio(state, (double) r->o_ss.rate / r->i_ss.rate) == 0);
+}
+
+static void libsamplerate_reset(pa_resampler *r) {
+    SRC_STATE *state;
+    pa_assert(r);
+
+    state = r->impl.data;
+    pa_assert_se(src_reset(state) == 0);
+}
+
+static void libsamplerate_free(pa_resampler *r) {
+    SRC_STATE *state;
+    pa_assert(r);
+
+    state = r->impl.data;
+    if (state)
+        src_delete(state);
+}
+
+int pa_resampler_libsamplerate_init(pa_resampler *r) {
+    int err;
+    SRC_STATE *state;
+
+    pa_assert(r);
+
+    if (!(state = src_new(r->method, r->work_channels, &err)))
+        return -1;
+
+    r->impl.free = libsamplerate_free;
+    r->impl.update_rates = libsamplerate_update_rates;
+    r->impl.resample = libsamplerate_resample;
+    r->impl.reset = libsamplerate_reset;
+    r->impl.data = state;
+
+    return 0;
+}
diff --git a/src/pulsecore/resampler/peaks.c b/src/pulsecore/resampler/peaks.c
new file mode 100644 (file)
index 0000000..c9b808e
--- /dev/null
@@ -0,0 +1,161 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <math.h>
+
+#include <pulsecore/resampler.h>
+
+struct peaks_data { /* data specific to the peak finder pseudo resampler */
+    unsigned o_counter;
+    unsigned i_counter;
+
+    float max_f[PA_CHANNELS_MAX];
+    int16_t max_i[PA_CHANNELS_MAX];
+};
+
+static unsigned peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    unsigned c, o_index = 0;
+    unsigned i, i_end = 0;
+    void *src, *dst;
+    struct peaks_data *peaks_data;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    peaks_data = r->impl.data;
+    src = pa_memblock_acquire_chunk(input);
+    dst = pa_memblock_acquire_chunk(output);
+
+    i = ((uint64_t) peaks_data->o_counter * r->i_ss.rate) / r->o_ss.rate;
+    i = i > peaks_data->i_counter ? i - peaks_data->i_counter : 0;
+
+    while (i_end < in_n_frames) {
+        i_end = ((uint64_t) (peaks_data->o_counter + 1) * r->i_ss.rate) / r->o_ss.rate;
+        i_end = i_end > peaks_data->i_counter ? i_end - peaks_data->i_counter : 0;
+
+        pa_assert_fp(o_index * r->w_fz < pa_memblock_get_length(output->memblock));
+
+        /* 1ch float is treated separately, because that is the common case */
+        if (r->work_channels == 1 && r->work_format == PA_SAMPLE_FLOAT32NE) {
+            float *s = (float*) src + i;
+            float *d = (float*) dst + o_index;
+
+            for (; i < i_end && i < in_n_frames; i++) {
+                float n = fabsf(*s++);
+
+                if (n > peaks_data->max_f[0])
+                    peaks_data->max_f[0] = n;
+            }
+
+            if (i == i_end) {
+                *d = peaks_data->max_f[0];
+                peaks_data->max_f[0] = 0;
+                o_index++, peaks_data->o_counter++;
+            }
+        } else if (r->work_format == PA_SAMPLE_S16NE) {
+            int16_t *s = (int16_t*) src + r->work_channels * i;
+            int16_t *d = (int16_t*) dst + r->work_channels * o_index;
+
+            for (; i < i_end && i < in_n_frames; i++)
+                for (c = 0; c < r->work_channels; c++) {
+                    int16_t n = abs(*s++);
+
+                    if (n > peaks_data->max_i[c])
+                        peaks_data->max_i[c] = n;
+                }
+
+            if (i == i_end) {
+                for (c = 0; c < r->work_channels; c++, d++) {
+                    *d = peaks_data->max_i[c];
+                    peaks_data->max_i[c] = 0;
+                }
+                o_index++, peaks_data->o_counter++;
+            }
+        } else {
+            float *s = (float*) src + r->work_channels * i;
+            float *d = (float*) dst + r->work_channels * o_index;
+
+            for (; i < i_end && i < in_n_frames; i++)
+                for (c = 0; c < r->work_channels; c++) {
+                    float n = fabsf(*s++);
+
+                    if (n > peaks_data->max_f[c])
+                        peaks_data->max_f[c] = n;
+                }
+
+            if (i == i_end) {
+                for (c = 0; c < r->work_channels; c++, d++) {
+                    *d = peaks_data->max_f[c];
+                    peaks_data->max_f[c] = 0;
+                }
+                o_index++, peaks_data->o_counter++;
+            }
+        }
+    }
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    *out_n_frames = o_index;
+
+    peaks_data->i_counter += in_n_frames;
+
+    /* Normalize counters */
+    while (peaks_data->i_counter >= r->i_ss.rate) {
+        pa_assert(peaks_data->o_counter >= r->o_ss.rate);
+
+        peaks_data->i_counter -= r->i_ss.rate;
+        peaks_data->o_counter -= r->o_ss.rate;
+    }
+
+    return 0;
+}
+
+static void peaks_update_rates_or_reset(pa_resampler *r) {
+    struct peaks_data *peaks_data;
+    pa_assert(r);
+
+    peaks_data = r->impl.data;
+
+    peaks_data->i_counter = 0;
+    peaks_data->o_counter = 0;
+}
+
+int pa_resampler_peaks_init(pa_resampler*r) {
+    struct peaks_data *peaks_data;
+    pa_assert(r);
+    pa_assert(r->i_ss.rate >= r->o_ss.rate);
+    pa_assert(r->work_format == PA_SAMPLE_S16NE || r->work_format == PA_SAMPLE_FLOAT32NE);
+
+    peaks_data = pa_xnew0(struct peaks_data, 1);
+
+    r->impl.resample = peaks_resample;
+    r->impl.update_rates = peaks_update_rates_or_reset;
+    r->impl.reset = peaks_update_rates_or_reset;
+    r->impl.data = peaks_data;
+
+    return 0;
+}
diff --git a/src/pulsecore/resampler/soxr.c b/src/pulsecore/resampler/soxr.c
new file mode 100644 (file)
index 0000000..b1b2e19
--- /dev/null
@@ -0,0 +1,168 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014, 2015 Andrey Semashev
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#include <soxr.h>
+
+#include <pulsecore/resampler.h>
+
+static unsigned resampler_soxr_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames,
+                                        pa_memchunk *output, unsigned *out_n_frames) {
+    soxr_t state;
+    void *in, *out;
+    size_t consumed = 0, produced = 0;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    state = r->impl.data;
+    pa_assert(state);
+
+    in = pa_memblock_acquire_chunk(input);
+    out = pa_memblock_acquire_chunk(output);
+
+    pa_assert_se(soxr_process(state, in, in_n_frames, &consumed, out, *out_n_frames, &produced) == 0);
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    *out_n_frames = produced;
+
+    return in_n_frames - consumed;
+}
+
+static void resampler_soxr_free(pa_resampler *r) {
+    pa_assert(r);
+
+    if (!r->impl.data)
+        return;
+
+    soxr_delete(r->impl.data);
+    r->impl.data = NULL;
+}
+
+static void resampler_soxr_reset(pa_resampler *r) {
+#if SOXR_THIS_VERSION >= SOXR_VERSION(0, 1, 2)
+    pa_assert(r);
+
+    soxr_clear(r->impl.data);
+#else
+    /* With libsoxr prior to 0.1.2 soxr_clear() makes soxr_process() crash afterwards,
+     * so don't use this function and re-create the context instead. */
+    soxr_t old_state;
+
+    pa_assert(r);
+
+    old_state = r->impl.data;
+    r->impl.data = NULL;
+
+    if (pa_resampler_soxr_init(r) == 0) {
+        if (old_state)
+            soxr_delete(old_state);
+    } else {
+        r->impl.data = old_state;
+        pa_log_error("Failed to reset libsoxr context");
+    }
+#endif
+}
+
+static void resampler_soxr_update_rates(pa_resampler *r) {
+    soxr_t old_state;
+
+    pa_assert(r);
+
+    /* There is no update method in libsoxr,
+     * so just re-create the resampler context */
+
+    old_state = r->impl.data;
+    r->impl.data = NULL;
+
+    if (pa_resampler_soxr_init(r) == 0) {
+        if (old_state)
+            soxr_delete(old_state);
+    } else {
+        r->impl.data = old_state;
+        pa_log_error("Failed to update libsoxr sample rates");
+    }
+}
+
+int pa_resampler_soxr_init(pa_resampler *r) {
+    soxr_t state;
+    soxr_datatype_t io_format;
+    soxr_io_spec_t io_spec;
+    soxr_runtime_spec_t runtime_spec;
+    unsigned long quality_recipe;
+    soxr_quality_spec_t quality;
+    soxr_error_t err = NULL;
+
+    pa_assert(r);
+
+    switch (r->work_format) {
+        case PA_SAMPLE_S16NE:
+            io_format = SOXR_INT16_I;
+            break;
+        case PA_SAMPLE_FLOAT32NE:
+            io_format = SOXR_FLOAT32_I;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    io_spec = soxr_io_spec(io_format, io_format);
+
+    /* Resample in one thread. Multithreading makes
+     * performance worse with small chunks of audio. */
+    runtime_spec = soxr_runtime_spec(1);
+
+    switch (r->method) {
+        case PA_RESAMPLER_SOXR_MQ:
+            quality_recipe = SOXR_MQ | SOXR_LINEAR_PHASE;
+            break;
+        case PA_RESAMPLER_SOXR_HQ:
+            quality_recipe = SOXR_HQ | SOXR_LINEAR_PHASE;
+            break;
+        case PA_RESAMPLER_SOXR_VHQ:
+            quality_recipe = SOXR_VHQ | SOXR_LINEAR_PHASE;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    quality = soxr_quality_spec(quality_recipe, 0);
+
+    state = soxr_create(r->i_ss.rate, r->o_ss.rate, r->work_channels, &err, &io_spec, &quality, &runtime_spec);
+    if (!state) {
+        pa_log_error("Failed to create libsoxr resampler context: %s.", (err ? err : "[unknown error]"));
+        return -1;
+    }
+
+    r->impl.free = resampler_soxr_free;
+    r->impl.reset = resampler_soxr_reset;
+    r->impl.update_rates = resampler_soxr_update_rates;
+    r->impl.resample = resampler_soxr_resample;
+    r->impl.data = state;
+
+    return 0;
+}
diff --git a/src/pulsecore/resampler/speex.c b/src/pulsecore/resampler/speex.c
new file mode 100644 (file)
index 0000000..66387e5
--- /dev/null
@@ -0,0 +1,178 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <speex/speex_resampler.h>
+#include <math.h>
+
+#include <pulsecore/once.h>
+#include <pulsecore/resampler.h>
+
+bool pa_speex_is_fixed_point(void) {
+    static bool result = false;
+    PA_ONCE_BEGIN {
+        float f_out = -1.0f, f_in = 1.0f;
+        spx_uint32_t in_len = 1, out_len = 1;
+        SpeexResamplerState *s;
+
+        pa_assert_se(s = speex_resampler_init(1, 1, 1,
+            SPEEX_RESAMPLER_QUALITY_MIN, NULL));
+
+        /* feed one sample that is too soft for fixed-point speex */
+        pa_assert_se(speex_resampler_process_float(s, 0, &f_in, &in_len,
+            &f_out, &out_len) == RESAMPLER_ERR_SUCCESS);
+
+        /* expecting sample has been processed, one sample output */
+        pa_assert_se(in_len == 1 && out_len == 1);
+
+        /* speex compiled with --enable-fixed-point will output 0.0 due to insufficient precision */
+        if (fabsf(f_out) < 0.00001f)
+            result = true;
+
+        speex_resampler_destroy(s);
+    } PA_ONCE_END;
+    return result;
+}
+
+
+static unsigned speex_resample_float(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    float *in, *out;
+    uint32_t inf = in_n_frames, outf = *out_n_frames;
+    SpeexResamplerState *state;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    state = r->impl.data;
+
+    in = pa_memblock_acquire_chunk(input);
+    out = pa_memblock_acquire_chunk(output);
+
+    /* Strictly speaking, speex resampler expects its input
+     * to be normalized to the [-32768.0 .. 32767.0] range.
+     * This matters if speex has been compiled with --enable-fixed-point,
+     * because such speex will round the samples to the nearest
+     * integer. speex with --enable-fixed-point is therefore incompatible
+     * with PulseAudio's floating-point sample range [-1 .. 1]. speex
+     * without --enable-fixed-point works fine with this range.
+     * Care has been taken to call speex_resample_float() only
+     * for speex compiled without --enable-fixed-point.
+     */
+    pa_assert_se(speex_resampler_process_interleaved_float(state, in, &inf, out, &outf) == 0);
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    pa_assert(inf == in_n_frames);
+    *out_n_frames = outf;
+
+    return 0;
+}
+
+static unsigned speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    int16_t *in, *out;
+    uint32_t inf = in_n_frames, outf = *out_n_frames;
+    SpeexResamplerState *state;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    state = r->impl.data;
+
+    in = pa_memblock_acquire_chunk(input);
+    out = pa_memblock_acquire_chunk(output);
+
+    pa_assert_se(speex_resampler_process_interleaved_int(state, in, &inf, out, &outf) == 0);
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    pa_assert(inf == in_n_frames);
+    *out_n_frames = outf;
+
+    return 0;
+}
+
+static void speex_update_rates(pa_resampler *r) {
+    SpeexResamplerState *state;
+    pa_assert(r);
+
+    state = r->impl.data;
+
+    pa_assert_se(speex_resampler_set_rate(state, r->i_ss.rate, r->o_ss.rate) == 0);
+}
+
+static void speex_reset(pa_resampler *r) {
+    SpeexResamplerState *state;
+    pa_assert(r);
+
+    state = r->impl.data;
+
+    pa_assert_se(speex_resampler_reset_mem(state) == 0);
+}
+
+static void speex_free(pa_resampler *r) {
+    SpeexResamplerState *state;
+    pa_assert(r);
+
+    state = r->impl.data;
+    if (!state)
+        return;
+
+    speex_resampler_destroy(state);
+}
+
+int pa_resampler_speex_init(pa_resampler *r) {
+    int q, err;
+    SpeexResamplerState *state;
+
+    pa_assert(r);
+
+    r->impl.free = speex_free;
+    r->impl.update_rates = speex_update_rates;
+    r->impl.reset = speex_reset;
+
+    if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX) {
+
+        q = r->method - PA_RESAMPLER_SPEEX_FIXED_BASE;
+        r->impl.resample = speex_resample_int;
+
+    } else {
+        pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+
+        q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
+        r->impl.resample = speex_resample_float;
+    }
+
+    pa_log_info("Choosing speex quality setting %i.", q);
+
+    if (!(state = speex_resampler_init(r->work_channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
+        return -1;
+
+    r->impl.data = state;
+
+    return 0;
+}
diff --git a/src/pulsecore/resampler/trivial.c b/src/pulsecore/resampler/trivial.c
new file mode 100644 (file)
index 0000000..14e7ef3
--- /dev/null
@@ -0,0 +1,100 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/resampler.h>
+
+struct trivial_data { /* data specific to the trivial resampler */
+    unsigned o_counter;
+    unsigned i_counter;
+};
+
+static unsigned trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    unsigned i_index, o_index;
+    void *src, *dst;
+    struct trivial_data *trivial_data;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    trivial_data = r->impl.data;
+
+    src = pa_memblock_acquire_chunk(input);
+    dst = pa_memblock_acquire_chunk(output);
+
+    for (o_index = 0;; o_index++, trivial_data->o_counter++) {
+        i_index = ((uint64_t) trivial_data->o_counter * r->i_ss.rate) / r->o_ss.rate;
+        i_index = i_index > trivial_data->i_counter ? i_index - trivial_data->i_counter : 0;
+
+        if (i_index >= in_n_frames)
+            break;
+
+        pa_assert_fp(o_index * r->w_fz < pa_memblock_get_length(output->memblock));
+
+        memcpy((uint8_t*) dst + r->w_fz * o_index, (uint8_t*) src + r->w_fz * i_index, (int) r->w_fz);
+    }
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    *out_n_frames = o_index;
+
+    trivial_data->i_counter += in_n_frames;
+
+    /* Normalize counters */
+    while (trivial_data->i_counter >= r->i_ss.rate) {
+        pa_assert(trivial_data->o_counter >= r->o_ss.rate);
+
+        trivial_data->i_counter -= r->i_ss.rate;
+        trivial_data->o_counter -= r->o_ss.rate;
+    }
+
+    return 0;
+}
+
+static void trivial_update_rates_or_reset(pa_resampler *r) {
+    struct trivial_data *trivial_data;
+    pa_assert(r);
+
+    trivial_data = r->impl.data;
+
+    trivial_data->i_counter = 0;
+    trivial_data->o_counter = 0;
+}
+
+int pa_resampler_trivial_init(pa_resampler *r) {
+    struct trivial_data *trivial_data;
+    pa_assert(r);
+
+    trivial_data = pa_xnew0(struct trivial_data, 1);
+
+    r->impl.resample = trivial_resample;
+    r->impl.update_rates = trivial_update_rates_or_reset;
+    r->impl.reset = trivial_update_rates_or_reset;
+    r->impl.data = trivial_data;
+
+    return 0;
+}
diff --git a/src/pulsecore/rtkit.c b/src/pulsecore/rtkit.c
new file mode 100644 (file)
index 0000000..2b7eb6a
--- /dev/null
@@ -0,0 +1,313 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+  Copyright 2009 Lennart Poettering
+  Copyright 2010 David Henningsson <diwic@ubuntu.com>
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#include <errno.h>
+
+#include "rtkit.h"
+
+#if defined(__linux__) && !defined(__ANDROID__)
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <pulsecore/core-util.h>
+
+static pid_t _gettid(void) {
+        return (pid_t) syscall(SYS_gettid);
+}
+
+static int translate_error(const char *name) {
+        if (pa_streq(name, DBUS_ERROR_NO_MEMORY))
+                return -ENOMEM;
+        if (pa_streq(name, DBUS_ERROR_SERVICE_UNKNOWN) ||
+            pa_streq(name, DBUS_ERROR_NAME_HAS_NO_OWNER))
+                return -ENOENT;
+        if (pa_streq(name, DBUS_ERROR_ACCESS_DENIED) ||
+            pa_streq(name, DBUS_ERROR_AUTH_FAILED))
+                return -EACCES;
+
+        return -EIO;
+}
+
+static long long rtkit_get_int_property(DBusConnection *connection, const char* propname, long long* propval) {
+        DBusMessage *m = NULL, *r = NULL;
+        DBusMessageIter iter, subiter;
+        dbus_int64_t i64;
+        dbus_int32_t i32;
+        DBusError error;
+        int current_type;
+        long long ret;
+        const char * interfacestr = "org.freedesktop.RealtimeKit1";
+
+        dbus_error_init(&error);
+
+        if (!(m = dbus_message_new_method_call(
+                              RTKIT_SERVICE_NAME,
+                              RTKIT_OBJECT_PATH,
+                              "org.freedesktop.DBus.Properties",
+                              "Get"))) {
+                ret = -ENOMEM;
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(
+                            m,
+                            DBUS_TYPE_STRING, &interfacestr,
+                            DBUS_TYPE_STRING, &propname,
+                            DBUS_TYPE_INVALID)) {
+                ret = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
+                ret = translate_error(error.name);
+                goto finish;
+        }
+
+        if (dbus_set_error_from_message(&error, r)) {
+                ret = translate_error(error.name);
+                goto finish;
+        }
+
+        ret = -EBADMSG;
+        dbus_message_iter_init(r, &iter);
+        while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) {
+
+                if (current_type == DBUS_TYPE_VARIANT) {
+                        dbus_message_iter_recurse(&iter, &subiter);
+
+                        while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) {
+
+                                if (current_type == DBUS_TYPE_INT32) {
+                                        dbus_message_iter_get_basic(&subiter, &i32);
+                                        *propval = i32;
+                                        ret = 0;
+                                }
+
+                                if (current_type == DBUS_TYPE_INT64) {
+                                        dbus_message_iter_get_basic(&subiter, &i64);
+                                        *propval = i64;
+                                        ret = 0;
+                                }
+
+                                dbus_message_iter_next (&subiter);
+                         }
+                }
+                dbus_message_iter_next (&iter);
+        }
+
+finish:
+
+        if (m)
+                dbus_message_unref(m);
+
+        if (r)
+                dbus_message_unref(r);
+
+        dbus_error_free(&error);
+
+        return ret;
+}
+
+int rtkit_get_max_realtime_priority(DBusConnection *connection) {
+        long long retval;
+        int err;
+
+        err = rtkit_get_int_property(connection, "MaxRealtimePriority", &retval);
+        return err < 0 ? err : retval;
+}
+
+int rtkit_get_min_nice_level(DBusConnection *connection, int* min_nice_level) {
+        long long retval;
+        int err;
+
+        err = rtkit_get_int_property(connection, "MinNiceLevel", &retval);
+        if (err >= 0)
+                *min_nice_level = retval;
+        return err;
+}
+
+long long rtkit_get_rttime_usec_max(DBusConnection *connection) {
+        long long retval;
+        int err;
+
+        err = rtkit_get_int_property(connection, "RTTimeUSecMax", &retval);
+        return err < 0 ? err : retval;
+}
+
+int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) {
+        DBusMessage *m = NULL, *r = NULL;
+        dbus_uint64_t u64;
+        dbus_uint32_t u32;
+        DBusError error;
+        int ret;
+
+        dbus_error_init(&error);
+
+        if (thread == 0)
+                thread = _gettid();
+
+        if (!(m = dbus_message_new_method_call(
+                              RTKIT_SERVICE_NAME,
+                              RTKIT_OBJECT_PATH,
+                              "org.freedesktop.RealtimeKit1",
+                              "MakeThreadRealtime"))) {
+                ret = -ENOMEM;
+                goto finish;
+        }
+
+        u64 = (dbus_uint64_t) thread;
+        u32 = (dbus_uint32_t) priority;
+
+        if (!dbus_message_append_args(
+                            m,
+                            DBUS_TYPE_UINT64, &u64,
+                            DBUS_TYPE_UINT32, &u32,
+                            DBUS_TYPE_INVALID)) {
+                ret = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
+                ret = translate_error(error.name);
+                goto finish;
+        }
+
+
+        if (dbus_set_error_from_message(&error, r)) {
+                ret = translate_error(error.name);
+                goto finish;
+        }
+
+        ret = 0;
+
+finish:
+
+        if (m)
+                dbus_message_unref(m);
+
+        if (r)
+                dbus_message_unref(r);
+
+        dbus_error_free(&error);
+
+        return ret;
+}
+
+int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) {
+        DBusMessage *m = NULL, *r = NULL;
+        dbus_uint64_t u64;
+        dbus_int32_t s32;
+        DBusError error;
+        int ret;
+
+        dbus_error_init(&error);
+
+        if (thread == 0)
+                thread = _gettid();
+
+        if (!(m = dbus_message_new_method_call(
+                              RTKIT_SERVICE_NAME,
+                              RTKIT_OBJECT_PATH,
+                              "org.freedesktop.RealtimeKit1",
+                              "MakeThreadHighPriority"))) {
+                ret = -ENOMEM;
+                goto finish;
+        }
+
+        u64 = (dbus_uint64_t) thread;
+        s32 = (dbus_int32_t) nice_level;
+
+        if (!dbus_message_append_args(
+                            m,
+                            DBUS_TYPE_UINT64, &u64,
+                            DBUS_TYPE_INT32, &s32,
+                            DBUS_TYPE_INVALID)) {
+                ret = -ENOMEM;
+                goto finish;
+        }
+
+
+
+        if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
+                ret = translate_error(error.name);
+                goto finish;
+        }
+
+
+        if (dbus_set_error_from_message(&error, r)) {
+                ret = translate_error(error.name);
+                goto finish;
+        }
+
+        ret = 0;
+
+finish:
+
+        if (m)
+                dbus_message_unref(m);
+
+        if (r)
+                dbus_message_unref(r);
+
+        dbus_error_free(&error);
+
+        return ret;
+}
+
+#else
+
+int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) {
+        return -ENOTSUP;
+}
+
+int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) {
+        return -ENOTSUP;
+}
+
+int rtkit_get_max_realtime_priority(DBusConnection *connection) {
+        return -ENOTSUP;
+}
+
+int rtkit_get_min_nice_level(DBusConnection *connection, int* min_nice_level) {
+        return -ENOTSUP;
+}
+
+long long rtkit_get_rttime_usec_max(DBusConnection *connection) {
+        return -ENOTSUP;
+}
+
+#endif
diff --git a/src/pulsecore/rtkit.h b/src/pulsecore/rtkit.h
new file mode 100644 (file)
index 0000000..ac4d599
--- /dev/null
@@ -0,0 +1,79 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foortkithfoo
+#define foortkithfoo
+
+/***
+  Copyright 2009 Lennart Poettering
+  Copyright 2010 David Henningsson <diwic@ubuntu.com>
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#include <sys/types.h>
+#include <dbus/dbus.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is the reference implementation for a client for
+ * RealtimeKit. You don't have to use this, but if do, just copy these
+ * sources into your repository */
+
+#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1"
+#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1"
+
+/* This is mostly equivalent to sched_setparam(thread, SCHED_RR, {
+ * .sched_priority = priority }). 'thread' needs to be a kernel thread
+ * id as returned by gettid(), not a pthread_t! If 'thread' is 0 the
+ * current thread is used. The returned value is a negative errno
+ * style error code, or 0 on success. */
+int rtkit_make_realtime(DBusConnection *system_bus, pid_t thread, int priority);
+
+/* This is mostly equivalent to setpriority(PRIO_PROCESS, thread,
+ * nice_level). 'thread' needs to be a kernel thread id as returned by
+ * gettid(), not a pthread_t! If 'thread' is 0 the current thread is
+ * used. The returned value is a negative errno style error code, or 0
+ * on success.*/
+int rtkit_make_high_priority(DBusConnection *system_bus, pid_t thread, int nice_level);
+
+/* Return the maximum value of realtime priority available. Realtime requests
+ * above this value will fail. A negative value is an errno style error code.
+ */
+int rtkit_get_max_realtime_priority(DBusConnection *system_bus);
+
+/* Retreive the minimum value of nice level available. High prio requests
+ * below this value will fail. The returned value is a negative errno
+ * style error code, or 0 on success.*/
+int rtkit_get_min_nice_level(DBusConnection *system_bus, int* min_nice_level);
+
+/* Return the maximum value of RLIMIT_RTTIME to set before attempting a
+ * realtime request. A negative value is an errno style error code.
+ */
+long long rtkit_get_rttime_usec_max(DBusConnection *system_bus);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
new file mode 100644 (file)
index 0000000..98cf88f
--- /dev/null
@@ -0,0 +1,634 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/poll.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/ratelimit.h>
+#include <pulse/rtclock.h>
+
+#include "rtpoll.h"
+
+/* #define DEBUG_TIMING */
+
+struct pa_rtpoll {
+    struct pollfd *pollfd, *pollfd2;
+    unsigned n_pollfd_alloc, n_pollfd_used;
+
+    struct timeval next_elapse;
+    bool timer_enabled:1;
+
+    bool scan_for_dead:1;
+    bool running:1;
+    bool rebuild_needed:1;
+    bool quit:1;
+    bool timer_elapsed:1;
+
+#ifdef DEBUG_TIMING
+    pa_usec_t timestamp;
+    pa_usec_t slept, awake;
+#endif
+
+    PA_LLIST_HEAD(pa_rtpoll_item, items);
+};
+
+struct pa_rtpoll_item {
+    pa_rtpoll *rtpoll;
+    bool dead;
+
+    pa_rtpoll_priority_t priority;
+
+    struct pollfd *pollfd;
+    unsigned n_pollfd;
+
+    int (*work_cb)(pa_rtpoll_item *i);
+    int (*before_cb)(pa_rtpoll_item *i);
+    void (*after_cb)(pa_rtpoll_item *i);
+    void *userdata;
+
+    PA_LLIST_FIELDS(pa_rtpoll_item);
+};
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
+
+pa_rtpoll *pa_rtpoll_new(void) {
+    pa_rtpoll *p;
+
+    p = pa_xnew0(pa_rtpoll, 1);
+
+    p->n_pollfd_alloc = 32;
+    p->pollfd = pa_xnew(struct pollfd, p->n_pollfd_alloc);
+    p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc);
+
+#ifdef DEBUG_TIMING
+    p->timestamp = pa_rtclock_now();
+#endif
+
+    return p;
+}
+
+static void rtpoll_rebuild(pa_rtpoll *p) {
+
+    struct pollfd *e, *t;
+    pa_rtpoll_item *i;
+    int ra = 0;
+
+    pa_assert(p);
+
+    p->rebuild_needed = false;
+
+    if (p->n_pollfd_used > p->n_pollfd_alloc) {
+        /* Hmm, we have to allocate some more space */
+        p->n_pollfd_alloc = p->n_pollfd_used * 2;
+        p->pollfd2 = pa_xrealloc(p->pollfd2, p->n_pollfd_alloc * sizeof(struct pollfd));
+        ra = 1;
+    }
+
+    e = p->pollfd2;
+
+    for (i = p->items; i; i = i->next) {
+
+        if (i->n_pollfd > 0) {
+            size_t l = i->n_pollfd * sizeof(struct pollfd);
+
+            if (i->pollfd)
+                memcpy(e, i->pollfd, l);
+            else
+                memset(e, 0, l);
+
+            i->pollfd = e;
+        } else
+            i->pollfd = NULL;
+
+        e += i->n_pollfd;
+    }
+
+    pa_assert((unsigned) (e - p->pollfd2) == p->n_pollfd_used);
+    t = p->pollfd;
+    p->pollfd = p->pollfd2;
+    p->pollfd2 = t;
+
+    if (ra)
+        p->pollfd2 = pa_xrealloc(p->pollfd2, p->n_pollfd_alloc * sizeof(struct pollfd));
+}
+
+static void rtpoll_item_destroy(pa_rtpoll_item *i) {
+    pa_rtpoll *p;
+
+    pa_assert(i);
+
+    p = i->rtpoll;
+
+    PA_LLIST_REMOVE(pa_rtpoll_item, p->items, i);
+
+    p->n_pollfd_used -= i->n_pollfd;
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+        pa_xfree(i);
+
+    p->rebuild_needed = true;
+}
+
+void pa_rtpoll_free(pa_rtpoll *p) {
+    pa_assert(p);
+
+    while (p->items)
+        rtpoll_item_destroy(p->items);
+
+    pa_xfree(p->pollfd);
+    pa_xfree(p->pollfd2);
+
+    pa_xfree(p);
+}
+
+static void reset_revents(pa_rtpoll_item *i) {
+    struct pollfd *f;
+    unsigned n;
+
+    pa_assert(i);
+
+    if (!(f = pa_rtpoll_item_get_pollfd(i, &n)))
+        return;
+
+    for (; n > 0; n--)
+        f[n-1].revents = 0;
+}
+
+static void reset_all_revents(pa_rtpoll *p) {
+    pa_rtpoll_item *i;
+
+    pa_assert(p);
+
+    for (i = p->items; i; i = i->next) {
+
+        if (i->dead)
+            continue;
+
+        reset_revents(i);
+    }
+}
+
+int pa_rtpoll_run(pa_rtpoll *p) {
+    pa_rtpoll_item *i;
+    int r = 0;
+    struct timeval timeout;
+
+    pa_assert(p);
+    pa_assert(!p->running);
+
+#ifdef DEBUG_TIMING
+    pa_log("rtpoll_run");
+#endif
+
+    p->running = true;
+    p->timer_elapsed = false;
+
+    /* First, let's do some work */
+    for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+        int k;
+
+        if (i->dead)
+            continue;
+
+        if (!i->work_cb)
+            continue;
+
+        if (p->quit) {
+#ifdef DEBUG_TIMING
+            pa_log("rtpoll finish");
+#endif
+            goto finish;
+        }
+
+        if ((k = i->work_cb(i)) != 0) {
+            if (k < 0)
+                r = k;
+#ifdef DEBUG_TIMING
+            pa_log("rtpoll finish");
+#endif
+            goto finish;
+        }
+    }
+
+    /* Now let's prepare for entering the sleep */
+    for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+        int k = 0;
+
+        if (i->dead)
+            continue;
+
+        if (!i->before_cb)
+            continue;
+
+        if (p->quit || (k = i->before_cb(i)) != 0) {
+
+            /* Hmm, this one doesn't let us enter the poll, so rewind everything */
+
+            for (i = i->prev; i; i = i->prev) {
+
+                if (i->dead)
+                    continue;
+
+                if (!i->after_cb)
+                    continue;
+
+                i->after_cb(i);
+            }
+
+            if (k < 0)
+                r = k;
+#ifdef DEBUG_TIMING
+            pa_log("rtpoll finish");
+#endif
+            goto finish;
+        }
+    }
+
+    if (p->rebuild_needed)
+        rtpoll_rebuild(p);
+
+    pa_zero(timeout);
+
+    /* Calculate timeout */
+    if (!p->quit && p->timer_enabled) {
+        struct timeval now;
+        pa_rtclock_get(&now);
+
+        if (pa_timeval_cmp(&p->next_elapse, &now) > 0)
+            pa_timeval_add(&timeout, pa_timeval_diff(&p->next_elapse, &now));
+    }
+
+#ifdef DEBUG_TIMING
+    {
+        pa_usec_t now = pa_rtclock_now();
+        p->awake = now - p->timestamp;
+        p->timestamp = now;
+        if (!p->quit && p->timer_enabled)
+            pa_log("poll timeout: %d ms ",(int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)));
+        else if (p->quit)
+            pa_log("poll timeout is ZERO");
+        else
+            pa_log("poll timeout is FOREVER");
+    }
+#endif
+
+    /* OK, now let's sleep */
+#ifdef HAVE_PPOLL
+    {
+        struct timespec ts;
+        ts.tv_sec = timeout.tv_sec;
+        ts.tv_nsec = timeout.tv_usec * 1000;
+        r = ppoll(p->pollfd, p->n_pollfd_used, (p->quit || p->timer_enabled) ? &ts : NULL, NULL);
+    }
+#else
+    r = pa_poll(p->pollfd, p->n_pollfd_used, (p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);
+#endif
+
+    p->timer_elapsed = r == 0;
+
+#ifdef DEBUG_TIMING
+    {
+        pa_usec_t now = pa_rtclock_now();
+        p->slept = now - p->timestamp;
+        p->timestamp = now;
+
+        pa_log("Process time %llu ms; sleep time %llu ms",
+               (unsigned long long) (p->awake / PA_USEC_PER_MSEC),
+               (unsigned long long) (p->slept / PA_USEC_PER_MSEC));
+    }
+#endif
+
+    if (r < 0) {
+        if (errno == EAGAIN || errno == EINTR)
+            r = 0;
+        else
+            pa_log_error("poll(): %s", pa_cstrerror(errno));
+
+        reset_all_revents(p);
+    }
+
+    /* Let's tell everyone that we left the sleep */
+    for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+
+        if (i->dead)
+            continue;
+
+        if (!i->after_cb)
+            continue;
+
+        i->after_cb(i);
+    }
+
+finish:
+
+    p->running = false;
+
+    if (p->scan_for_dead) {
+        pa_rtpoll_item *n;
+
+        p->scan_for_dead = false;
+
+        for (i = p->items; i; i = n) {
+            n = i->next;
+
+            if (i->dead)
+                rtpoll_item_destroy(i);
+        }
+    }
+
+    return r < 0 ? r : !p->quit;
+}
+
+void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec) {
+    pa_assert(p);
+
+    pa_timeval_store(&p->next_elapse, usec);
+    p->timer_enabled = true;
+}
+
+void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) {
+    pa_assert(p);
+
+    /* Scheduling a timeout for more than an hour is very very suspicious */
+    pa_assert(usec <= PA_USEC_PER_SEC*60ULL*60ULL);
+
+    pa_rtclock_get(&p->next_elapse);
+    pa_timeval_add(&p->next_elapse, usec);
+    p->timer_enabled = true;
+}
+
+void pa_rtpoll_set_timer_disabled(pa_rtpoll *p) {
+    pa_assert(p);
+
+    memset(&p->next_elapse, 0, sizeof(p->next_elapse));
+    p->timer_enabled = false;
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds) {
+    pa_rtpoll_item *i, *j, *l = NULL;
+
+    pa_assert(p);
+
+    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+        i = pa_xnew(pa_rtpoll_item, 1);
+
+    i->rtpoll = p;
+    i->dead = false;
+    i->n_pollfd = n_fds;
+    i->pollfd = NULL;
+    i->priority = prio;
+
+    i->userdata = NULL;
+    i->before_cb = NULL;
+    i->after_cb = NULL;
+    i->work_cb = NULL;
+
+    for (j = p->items; j; j = j->next) {
+        if (prio <= j->priority)
+            break;
+
+        l = j;
+    }
+
+    PA_LLIST_INSERT_AFTER(pa_rtpoll_item, p->items, j ? j->prev : l, i);
+
+    if (n_fds > 0) {
+        p->rebuild_needed = 1;
+        p->n_pollfd_used += n_fds;
+    }
+
+    return i;
+}
+
+void pa_rtpoll_item_free(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    if (i->rtpoll->running) {
+        i->dead = true;
+        i->rtpoll->scan_for_dead = true;
+        return;
+    }
+
+    rtpoll_item_destroy(i);
+}
+
+struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds) {
+    pa_assert(i);
+
+    if (i->n_pollfd > 0)
+        if (i->rtpoll->rebuild_needed)
+            rtpoll_rebuild(i->rtpoll);
+
+    if (n_fds)
+        *n_fds = i->n_pollfd;
+
+    return i->pollfd;
+}
+
+void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i)) {
+    pa_assert(i);
+    pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+    i->before_cb = before_cb;
+}
+
+void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i)) {
+    pa_assert(i);
+    pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+    i->after_cb = after_cb;
+}
+
+void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i)) {
+    pa_assert(i);
+    pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+    i->work_cb = work_cb;
+}
+
+void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata) {
+    pa_assert(i);
+
+    i->userdata = userdata;
+}
+
+void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    return i->userdata;
+}
+
+static int fdsem_before(pa_rtpoll_item *i) {
+
+    if (pa_fdsem_before_poll(i->userdata) < 0)
+        return 1; /* 1 means immediate restart of the loop */
+
+    return 0;
+}
+
+static void fdsem_after(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+    pa_fdsem_after_poll(i->userdata);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *f) {
+    pa_rtpoll_item *i;
+    struct pollfd *pollfd;
+
+    pa_assert(p);
+    pa_assert(f);
+
+    i = pa_rtpoll_item_new(p, prio, 1);
+
+    pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+
+    pollfd->fd = pa_fdsem_get(f);
+    pollfd->events = POLLIN;
+
+    i->before_cb = fdsem_before;
+    i->after_cb = fdsem_after;
+    i->userdata = f;
+
+    return i;
+}
+
+static int asyncmsgq_read_before(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    if (pa_asyncmsgq_read_before_poll(i->userdata) < 0)
+        return 1; /* 1 means immediate restart of the loop */
+
+    return 0;
+}
+
+static void asyncmsgq_read_after(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+    pa_asyncmsgq_read_after_poll(i->userdata);
+}
+
+static int asyncmsgq_read_work(pa_rtpoll_item *i) {
+    pa_msgobject *object;
+    int code;
+    void *data;
+    pa_memchunk chunk;
+    int64_t offset;
+
+    pa_assert(i);
+
+    if (pa_asyncmsgq_get(i->userdata, &object, &code, &data, &offset, &chunk, 0) == 0) {
+        int ret;
+
+        if (!object && code == PA_MESSAGE_SHUTDOWN) {
+            pa_asyncmsgq_done(i->userdata, 0);
+            /* Requests the loop to exit. Will cause the next iteration of
+             * pa_rtpoll_run() to return 0 */
+            i->rtpoll->quit = true;
+            return 1;
+        }
+
+        ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+        pa_asyncmsgq_done(i->userdata, ret);
+        return 1;
+    }
+
+    return 0;
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
+    pa_rtpoll_item *i;
+    struct pollfd *pollfd;
+
+    pa_assert(p);
+    pa_assert(q);
+
+    i = pa_rtpoll_item_new(p, prio, 1);
+
+    pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+    pollfd->fd = pa_asyncmsgq_read_fd(q);
+    pollfd->events = POLLIN;
+
+    i->before_cb = asyncmsgq_read_before;
+    i->after_cb = asyncmsgq_read_after;
+    i->work_cb = asyncmsgq_read_work;
+    i->userdata = q;
+
+    return i;
+}
+
+static int asyncmsgq_write_before(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    pa_asyncmsgq_write_before_poll(i->userdata);
+    return 0;
+}
+
+static void asyncmsgq_write_after(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+    pa_asyncmsgq_write_after_poll(i->userdata);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
+    pa_rtpoll_item *i;
+    struct pollfd *pollfd;
+
+    pa_assert(p);
+    pa_assert(q);
+
+    i = pa_rtpoll_item_new(p, prio, 1);
+
+    pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+    pollfd->fd = pa_asyncmsgq_write_fd(q);
+    pollfd->events = POLLIN;
+
+    i->before_cb = asyncmsgq_write_before;
+    i->after_cb = asyncmsgq_write_after;
+    i->work_cb = NULL;
+    i->userdata = q;
+
+    return i;
+}
+
+bool pa_rtpoll_timer_elapsed(pa_rtpoll *p) {
+    pa_assert(p);
+
+    return p->timer_elapsed;
+}
diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h
new file mode 100644 (file)
index 0000000..8f0715a
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef foopulsertpollhfoo
+#define foopulsertpollhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <limits.h>
+
+#include <pulse/sample.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/fdsem.h>
+#include <pulsecore/macro.h>
+
+/* An implementation of a "real-time" poll loop. Basically, this is
+ * yet another wrapper around poll(). However it has certain
+ * advantages over pa_mainloop and suchlike:
+ *
+ * 1) High resolution timers are used
+ *
+ * 2) It allows raw access to the pollfd data to users
+ *
+ * 3) It allows arbitrary functions to be run before entering the
+ * actual poll() and after it.
+ *
+ * Only a single interval timer is supported. */
+
+typedef struct pa_rtpoll pa_rtpoll;
+typedef struct pa_rtpoll_item pa_rtpoll_item;
+
+typedef enum pa_rtpoll_priority {
+    PA_RTPOLL_EARLY  = -100,          /* For very important stuff, like handling control messages */
+    PA_RTPOLL_NORMAL = 0,             /* For normal stuff */
+    PA_RTPOLL_LATE   = +100,          /* For housekeeping */
+    PA_RTPOLL_NEVER  = INT_MAX,       /* For stuff that doesn't register any callbacks, but only fds to listen on */
+} pa_rtpoll_priority_t;
+
+pa_rtpoll *pa_rtpoll_new(void);
+void pa_rtpoll_free(pa_rtpoll *p);
+
+/* Sleep on the rtpoll until the time event, or any of the fd events
+ * is triggered. Returns negative on error, positive if the loop
+ * should continue to run, 0 when the loop should be terminated
+ * cleanly. */
+int pa_rtpoll_run(pa_rtpoll *f);
+
+void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec);
+void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec);
+void pa_rtpoll_set_timer_disabled(pa_rtpoll *p);
+
+/* Return true when the elapsed timer was the reason for
+ * the last pa_rtpoll_run() invocation to finish */
+bool pa_rtpoll_timer_elapsed(pa_rtpoll *p);
+
+/* A new fd wakeup item for pa_rtpoll */
+pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds);
+void pa_rtpoll_item_free(pa_rtpoll_item *i);
+
+/* Please note that this pointer might change on every call and when
+ * pa_rtpoll_run() is called. Hence: call this immediately before
+ * using the pointer and don't save the result anywhere */
+struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds);
+
+/* Set the callback that shall be called when there's time to do some work: If the
+ * callback returns a value > 0, the poll is skipped and the next
+ * iteration of the loop will start immediately. */
+void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i));
+
+/* Set the callback that shall be called immediately before entering
+ * the sleeping poll: If the callback returns a value > 0, the poll is
+ * skipped and the next iteration of the loop will start immediately. */
+void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i));
+
+/* Set the callback that shall be called immediately after having
+ * entered the sleeping poll */
+void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i));
+
+void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata);
+void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i);
+
+pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *s);
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
+
+#endif
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
new file mode 100644 (file)
index 0000000..b2a28eb
--- /dev/null
@@ -0,0 +1,405 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <math.h>
+
+#include <pulse/timeval.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/endianmacros.h>
+
+#include "sample-util.h"
+
+#define PA_SILENCE_MAX (pa_page_size()*16)
+
+pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) {
+    void *data;
+
+    pa_assert(b);
+    pa_assert(spec);
+
+    data = pa_memblock_acquire(b);
+    pa_silence_memory(data, pa_memblock_get_length(b), spec);
+    pa_memblock_release(b);
+
+    return b;
+}
+
+pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
+    void *data;
+
+    pa_assert(c);
+    pa_assert(c->memblock);
+    pa_assert(spec);
+
+    data = pa_memblock_acquire(c->memblock);
+    pa_silence_memory((uint8_t*) data+c->index, c->length, spec);
+    pa_memblock_release(c->memblock);
+
+    return c;
+}
+
+static uint8_t silence_byte(pa_sample_format_t format) {
+    switch (format) {
+        case PA_SAMPLE_U8:
+            return 0x80;
+        case PA_SAMPLE_S16LE:
+        case PA_SAMPLE_S16BE:
+        case PA_SAMPLE_S32LE:
+        case PA_SAMPLE_S32BE:
+        case PA_SAMPLE_FLOAT32LE:
+        case PA_SAMPLE_FLOAT32BE:
+        case PA_SAMPLE_S24LE:
+        case PA_SAMPLE_S24BE:
+        case PA_SAMPLE_S24_32LE:
+        case PA_SAMPLE_S24_32BE:
+            return 0;
+        case PA_SAMPLE_ALAW:
+            return 0xd5;
+        case PA_SAMPLE_ULAW:
+            return 0xff;
+        default:
+            pa_assert_not_reached();
+    }
+}
+
+void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
+    pa_assert(p);
+    pa_assert(length > 0);
+    pa_assert(spec);
+
+    memset(p, silence_byte(spec->format), length);
+    return p;
+}
+
+size_t pa_frame_align(size_t l, const pa_sample_spec *ss) {
+    size_t fs;
+
+    pa_assert(ss);
+
+    fs = pa_frame_size(ss);
+
+    return (l/fs) * fs;
+}
+
+bool pa_frame_aligned(size_t l, const pa_sample_spec *ss) {
+    size_t fs;
+
+    pa_assert(ss);
+
+    fs = pa_frame_size(ss);
+
+    return l % fs == 0;
+}
+
+void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n) {
+    unsigned c;
+    size_t fs;
+
+    pa_assert(src);
+    pa_assert(channels > 0);
+    pa_assert(dst);
+    pa_assert(ss > 0);
+    pa_assert(n > 0);
+
+    fs = ss * channels;
+
+    for (c = 0; c < channels; c++) {
+        unsigned j;
+        void *d;
+        const void *s;
+
+        s = src[c];
+        d = (uint8_t*) dst + c * ss;
+
+        for (j = 0; j < n; j ++) {
+            memcpy(d, s, (int) ss);
+            s = (uint8_t*) s + ss;
+            d = (uint8_t*) d + fs;
+        }
+    }
+}
+
+void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n) {
+    size_t fs;
+    unsigned c;
+
+    pa_assert(src);
+    pa_assert(dst);
+    pa_assert(channels > 0);
+    pa_assert(ss > 0);
+    pa_assert(n > 0);
+
+    fs = ss * channels;
+
+    for (c = 0; c < channels; c++) {
+        unsigned j;
+        const void *s;
+        void *d;
+
+        s = (uint8_t*) src + c * ss;
+        d = dst[c];
+
+        for (j = 0; j < n; j ++) {
+            memcpy(d, s, (int) ss);
+            s = (uint8_t*) s + fs;
+            d = (uint8_t*) d + ss;
+        }
+    }
+}
+
+static pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) {
+    pa_memblock *b;
+    size_t length;
+    void *data;
+
+    pa_assert(pool);
+
+    length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX);
+
+    b = pa_memblock_new(pool, length);
+
+    data = pa_memblock_acquire(b);
+    memset(data, c, length);
+    pa_memblock_release(b);
+
+    pa_memblock_set_is_silence(b, true);
+
+    return b;
+}
+
+void pa_silence_cache_init(pa_silence_cache *cache) {
+    pa_assert(cache);
+
+    memset(cache, 0, sizeof(pa_silence_cache));
+}
+
+void pa_silence_cache_done(pa_silence_cache *cache) {
+    pa_sample_format_t f;
+    pa_assert(cache);
+
+    for (f = 0; f < PA_SAMPLE_MAX; f++)
+        if (cache->blocks[f])
+            pa_memblock_unref(cache->blocks[f]);
+
+    memset(cache, 0, sizeof(pa_silence_cache));
+}
+
+pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length) {
+    pa_memblock *b;
+    size_t l;
+
+    pa_assert(cache);
+    pa_assert(pa_sample_spec_valid(spec));
+
+    if (!(b = cache->blocks[spec->format]))
+
+        switch (spec->format) {
+            case PA_SAMPLE_U8:
+                cache->blocks[PA_SAMPLE_U8] = b = silence_memblock_new(pool, 0x80);
+                break;
+            case PA_SAMPLE_S16LE:
+            case PA_SAMPLE_S16BE:
+            case PA_SAMPLE_S32LE:
+            case PA_SAMPLE_S32BE:
+            case PA_SAMPLE_S24LE:
+            case PA_SAMPLE_S24BE:
+            case PA_SAMPLE_S24_32LE:
+            case PA_SAMPLE_S24_32BE:
+            case PA_SAMPLE_FLOAT32LE:
+            case PA_SAMPLE_FLOAT32BE:
+                cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
+                cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_S24LE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_S24BE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_S24_32LE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_S24_32BE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
+                break;
+            case PA_SAMPLE_ALAW:
+                cache->blocks[PA_SAMPLE_ALAW] = b = silence_memblock_new(pool, 0xd5);
+                break;
+            case PA_SAMPLE_ULAW:
+                cache->blocks[PA_SAMPLE_ULAW] = b = silence_memblock_new(pool, 0xff);
+                break;
+            default:
+                pa_assert_not_reached();
+    }
+
+    pa_assert(b);
+
+    ret->memblock = pa_memblock_ref(b);
+
+    l = pa_memblock_get_length(b);
+    if (length > l || length == 0)
+        length = l;
+
+    ret->length = pa_frame_align(length, spec);
+    ret->index = 0;
+
+    return ret;
+}
+
+void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n) {
+    const float *s;
+    float *d;
+
+    s = src; d = dst;
+
+    if (format == PA_SAMPLE_FLOAT32NE) {
+        for (; n > 0; n--) {
+            float f;
+
+            f = *s;
+            *d = PA_CLAMP_UNLIKELY(f, -1.0f, 1.0f);
+
+            s = (const float*) ((const uint8_t*) s + sstr);
+            d = (float*) ((uint8_t*) d + dstr);
+        }
+    } else {
+        pa_assert(format == PA_SAMPLE_FLOAT32RE);
+
+        for (; n > 0; n--) {
+            float f;
+
+            f = PA_READ_FLOAT32RE(s);
+            f = PA_CLAMP_UNLIKELY(f, -1.0f, 1.0f);
+            PA_WRITE_FLOAT32RE(d, f);
+
+            s = (const float*) ((const uint8_t*) s + sstr);
+            d = (float*) ((uint8_t*) d + dstr);
+        }
+    }
+}
+
+/* Similar to pa_bytes_to_usec() but rounds up, not down */
+
+pa_usec_t pa_bytes_to_usec_round_up(uint64_t length, const pa_sample_spec *spec) {
+    size_t fs;
+    pa_usec_t usec;
+
+    pa_assert(spec);
+
+    fs = pa_frame_size(spec);
+    length = (length + fs - 1) / fs;
+
+    usec = (pa_usec_t) length * PA_USEC_PER_SEC;
+
+    return (usec + spec->rate - 1) / spec->rate;
+}
+
+/* Similar to pa_usec_to_bytes() but rounds up, not down */
+
+size_t pa_usec_to_bytes_round_up(pa_usec_t t, const pa_sample_spec *spec) {
+    uint64_t u;
+    pa_assert(spec);
+
+    u = (uint64_t) t * (uint64_t) spec->rate;
+
+    u = (u + PA_USEC_PER_SEC - 1) / PA_USEC_PER_SEC;
+
+    u *= pa_frame_size(spec);
+
+    return (size_t) u;
+}
+
+void pa_memchunk_dump_to_file(pa_memchunk *c, const char *fn) {
+    FILE *f;
+    void *p;
+
+    pa_assert(c);
+    pa_assert(fn);
+
+    /* Only for debugging purposes */
+
+    f = pa_fopen_cloexec(fn, "a");
+
+    if (!f) {
+        pa_log_warn("Failed to open '%s': %s", fn, pa_cstrerror(errno));
+        return;
+    }
+
+    p = pa_memblock_acquire(c->memblock);
+
+    if (fwrite((uint8_t*) p + c->index, 1, c->length, f) != c->length)
+        pa_log_warn("Failed to write to '%s': %s", fn, pa_cstrerror(errno));
+
+    pa_memblock_release(c->memblock);
+
+    fclose(f);
+}
+
+static void calc_sine(float *f, size_t l, double freq) {
+    size_t i;
+
+    l /= sizeof(float);
+
+    for (i = 0; i < l; i++)
+        *(f++) = (float) 0.5f * sin((double) i*M_PI*2*freq / (double) l);
+}
+
+void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned freq) {
+    size_t l;
+    unsigned gcd, n;
+    void *p;
+
+    pa_memchunk_reset(c);
+
+    gcd = pa_gcd(rate, freq);
+    n = rate / gcd;
+
+    l = pa_mempool_block_size_max(pool) / sizeof(float);
+
+    l /= n;
+    if (l <= 0) l = 1;
+    l *= n;
+
+    c->length = l * sizeof(float);
+    c->memblock = pa_memblock_new(pool, c->length);
+
+    p = pa_memblock_acquire(c->memblock);
+    calc_sine(p, c->length, freq * l / rate);
+    pa_memblock_release(c->memblock);
+}
+
+size_t pa_convert_size(size_t size, const pa_sample_spec *from, const pa_sample_spec *to) {
+    pa_usec_t usec;
+
+    pa_assert(from);
+    pa_assert(to);
+
+    usec = pa_bytes_to_usec_round_up(size, from);
+    return pa_usec_to_bytes_round_up(usec, to);
+}
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
new file mode 100644 (file)
index 0000000..0732e8d
--- /dev/null
@@ -0,0 +1,154 @@
+#ifndef foosampleutilhfoo
+#define foosampleutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <limits.h>
+
+#include <pulse/gccmacro.h>
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+#include <pulse/channelmap.h>
+
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_silence_cache {
+    pa_memblock* blocks[PA_SAMPLE_MAX];
+} pa_silence_cache;
+
+void pa_silence_cache_init(pa_silence_cache *cache);
+void pa_silence_cache_done(pa_silence_cache *cache);
+
+void *pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
+pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
+pa_memblock* pa_silence_memblock(pa_memblock *b, const pa_sample_spec *spec);
+
+pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length);
+
+size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
+
+bool pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
+
+void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n);
+void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n);
+
+void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n);
+
+static inline int32_t pa_mult_s16_volume(int16_t v, int32_t cv) {
+#ifdef HAVE_FAST_64BIT_OPERATIONS
+    /* Multiply with 64 bit integers on 64 bit platforms */
+    return (v * (int64_t) cv) >> 16;
+#else
+    /* Multiplying the 32 bit volume factor with the
+     * 16 bit sample might result in an 48 bit value. We
+     * want to do without 64 bit integers and hence do
+     * the multiplication independently for the HI and
+     * LO part of the volume. */
+
+    int32_t hi = cv >> 16;
+    int32_t lo = cv & 0xFFFF;
+    return ((v * lo) >> 16) + (v * hi);
+#endif
+}
+
+pa_usec_t pa_bytes_to_usec_round_up(uint64_t length, const pa_sample_spec *spec);
+size_t pa_usec_to_bytes_round_up(pa_usec_t t, const pa_sample_spec *spec);
+
+void pa_memchunk_dump_to_file(pa_memchunk *c, const char *fn);
+
+void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned freq);
+
+typedef void (*pa_do_volume_func_t) (void *samples, const void *volumes, unsigned channels, unsigned length);
+
+pa_do_volume_func_t pa_get_volume_func(pa_sample_format_t f);
+void pa_set_volume_func(pa_sample_format_t f, pa_do_volume_func_t func);
+
+size_t pa_convert_size(size_t size, const pa_sample_spec *from, const pa_sample_spec *to);
+
+#define PA_CHANNEL_POSITION_MASK_LEFT                                   \
+    (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT)           \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT)          \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT)          \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT)     \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT))     \
+
+#define PA_CHANNEL_POSITION_MASK_RIGHT                                  \
+    (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT)          \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT)         \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT)         \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT)    \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT))
+
+#define PA_CHANNEL_POSITION_MASK_CENTER                                 \
+    (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER)         \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER)        \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER)         \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER)   \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_FRONT                                  \
+    (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT)           \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT)        \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER)       \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT)     \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT)    \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_REAR                                   \
+    (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT)            \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT)         \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER)        \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)      \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT)     \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_LFE                                    \
+    PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE)
+
+#define PA_CHANNEL_POSITION_MASK_HFE                                    \
+    (PA_CHANNEL_POSITION_MASK_REAR | PA_CHANNEL_POSITION_MASK_FRONT     \
+     | PA_CHANNEL_POSITION_MASK_LEFT | PA_CHANNEL_POSITION_MASK_RIGHT   \
+     | PA_CHANNEL_POSITION_MASK_CENTER)
+
+#define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER                     \
+    (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT)            \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT)         \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_TOP                                    \
+    (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER)           \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT)     \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT)    \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER)   \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)      \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT)     \
+     | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_ALL            \
+    ((pa_channel_position_mask_t) (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_MAX)-1))
+
+#endif
diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c
new file mode 100644 (file)
index 0000000..6228c74
--- /dev/null
@@ -0,0 +1,81 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "endianmacros.h"
+
+#define INT16_FROM PA_INT16_FROM_BE
+#define INT16_TO PA_INT16_TO_BE
+#define UINT16_FROM PA_UINT16_FROM_BE
+#define UINT16_TO PA_UINT16_TO_BE
+
+#define INT32_FROM PA_INT32_FROM_BE
+#define INT32_TO PA_INT32_TO_BE
+#define UINT32_FROM PA_UINT32_FROM_BE
+#define UINT32_TO PA_UINT32_TO_BE
+
+#define READ24 PA_READ24BE
+#define WRITE24 PA_WRITE24BE
+
+#define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne
+#define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne
+#define pa_sconv_s16le_to_float32re pa_sconv_s16be_to_float32re
+#define pa_sconv_s16le_from_float32re pa_sconv_s16be_from_float32re
+
+#define pa_sconv_s32le_to_float32ne pa_sconv_s32be_to_float32ne
+#define pa_sconv_s32le_from_float32ne pa_sconv_s32be_from_float32ne
+#define pa_sconv_s32le_to_float32re pa_sconv_s32be_to_float32re
+#define pa_sconv_s32le_from_float32re pa_sconv_s32be_from_float32re
+
+#define pa_sconv_s24le_to_float32ne pa_sconv_s24be_to_float32ne
+#define pa_sconv_s24le_from_float32ne pa_sconv_s24be_from_float32ne
+#define pa_sconv_s24le_to_float32re pa_sconv_s24be_to_float32re
+#define pa_sconv_s24le_from_float32re pa_sconv_s24be_from_float32re
+
+#define pa_sconv_s24_32le_to_float32ne pa_sconv_s24_32be_to_float32ne
+#define pa_sconv_s24_32le_from_float32ne pa_sconv_s24_32be_from_float32ne
+#define pa_sconv_s24_32le_to_float32re pa_sconv_s24_32be_to_float32re
+#define pa_sconv_s24_32le_from_float32re pa_sconv_s24_32be_from_float32re
+
+#define pa_sconv_s32le_to_s16ne pa_sconv_s32be_to_s16ne
+#define pa_sconv_s32le_from_s16ne pa_sconv_s32be_from_s16ne
+#define pa_sconv_s32le_to_s16re pa_sconv_s32be_to_s16re
+#define pa_sconv_s32le_from_s16re pa_sconv_s32be_from_s16re
+
+#define pa_sconv_s24le_to_s16ne pa_sconv_s24be_to_s16ne
+#define pa_sconv_s24le_from_s16ne pa_sconv_s24be_from_s16ne
+#define pa_sconv_s24le_to_s16re pa_sconv_s24be_to_s16re
+#define pa_sconv_s24le_from_s16re pa_sconv_s24be_from_s16re
+
+#define pa_sconv_s24_32le_to_s16ne pa_sconv_s24_32be_to_s16ne
+#define pa_sconv_s24_32le_from_s16ne pa_sconv_s24_32be_from_s16ne
+#define pa_sconv_s24_32le_to_s16re pa_sconv_s24_32be_to_s16re
+#define pa_sconv_s24_32le_from_s16re pa_sconv_s24_32be_from_s16re
+
+#ifdef WORDS_BIGENDIAN
+#define SWAP_WORDS 0
+#else
+#define SWAP_WORDS 1
+#endif
+
+#include "sconv-s16le.h"
+#include "sconv-s16le.c"
diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h
new file mode 100644 (file)
index 0000000..4e0e03d
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef foosconv_s16befoo
+#define foosconv_s16befoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+void pa_sconv_s16be_to_float32ne(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, int16_t *b);
+void pa_sconv_s16be_to_float32re(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16be_from_float32re(unsigned n, const float *a, int16_t *b);
+
+void pa_sconv_s32be_to_float32ne(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32be_from_float32ne(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s32be_to_float32re(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32be_from_float32re(unsigned n, const float *a, int32_t *b);
+
+void pa_sconv_s24be_to_float32ne(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24be_from_float32ne(unsigned n, const float *a, uint8_t *b);
+void pa_sconv_s24be_to_float32re(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24be_from_float32re(unsigned n, const float *a, uint8_t *b);
+
+void pa_sconv_s24_32be_to_float32ne(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24_32be_from_float32ne(unsigned n, const float *a, uint8_t *b);
+void pa_sconv_s24_32be_to_float32re(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24_32be_from_float32re(unsigned n, const float *a, uint8_t *b);
+
+void pa_sconv_s32be_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32be_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s32be_to_s16re(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32be_from_s16re(unsigned n, const int16_t *a, int32_t *b);
+
+void pa_sconv_s24be_to_s16ne(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24be_from_s16ne(unsigned n, const int16_t *a, uint8_t *b);
+void pa_sconv_s24be_to_s16re(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24be_from_s16re(unsigned n, const int16_t *a, uint8_t *b);
+
+void pa_sconv_s24_32be_to_s16ne(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24_32be_from_s16ne(unsigned n, const int16_t *a, uint8_t *b);
+void pa_sconv_s24_32be_to_s16re(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24_32be_from_s16re(unsigned n, const int16_t *a, uint8_t *b);
+
+#ifdef WORDS_BIGENDIAN
+#define pa_sconv_float32be_to_s16ne pa_sconv_s16be_from_float32ne
+#define pa_sconv_float32be_from_s16ne pa_sconv_s16be_to_float32ne
+#define pa_sconv_float32le_to_s16ne pa_sconv_s16be_from_float32re
+#define pa_sconv_float32le_from_s16ne pa_sconv_s16be_to_float32re
+#endif
+
+#endif
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
new file mode 100644 (file)
index 0000000..c503e0e
--- /dev/null
@@ -0,0 +1,439 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* Despite the name of this file we implement S32 and S24 handling here, too. */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <math.h>
+
+#include <pulsecore/sconv.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+
+#include "sconv-s16le.h"
+
+#ifndef INT16_FROM
+#define INT16_FROM PA_INT16_FROM_LE
+#endif
+#ifndef UINT16_FROM
+#define UINT16_FROM PA_UINT16_FROM_LE
+#endif
+
+#ifndef INT16_TO
+#define INT16_TO PA_INT16_TO_LE
+#endif
+#ifndef UINT16_TO
+#define UINT16_TO PA_UINT16_TO_LE
+#endif
+
+#ifndef INT32_FROM
+#define INT32_FROM PA_INT32_FROM_LE
+#endif
+#ifndef UINT32_FROM
+#define UINT32_FROM PA_UINT32_FROM_LE
+#endif
+
+#ifndef INT32_TO
+#define INT32_TO PA_INT32_TO_LE
+#endif
+#ifndef UINT32_TO
+#define UINT32_TO PA_UINT32_TO_LE
+#endif
+
+#ifndef READ24
+#define READ24 PA_READ24LE
+#endif
+#ifndef WRITE24
+#define WRITE24 PA_WRITE24LE
+#endif
+
+#ifndef SWAP_WORDS
+#ifdef WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#else
+#define SWAP_WORDS 0
+#endif
+#endif
+
+void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+#if SWAP_WORDS == 1
+    for (; n > 0; n--) {
+        int16_t s = *(a++);
+        *(b++) = INT16_FROM(s) * (1.0f / (1 << 15));
+    }
+#else
+    for (; n > 0; n--)
+        *(b++) = *(a++) * (1.0f / (1 << 15));
+#endif
+}
+
+void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+#if SWAP_WORDS == 1
+    for (; n > 0; n--) {
+        int32_t s = *(a++);
+        *(b++) = INT32_FROM(s) * (1.0f / (1U << 31));
+    }
+#else
+    for (; n > 0; n--)
+        *(b++) = *(a++) * (1.0f / (1U << 31));
+#endif
+}
+
+void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+#if SWAP_WORDS == 1
+    for (; n > 0; n--) {
+        int16_t s;
+        float v = *(a++) * (1 << 15);
+
+        s = (int16_t) PA_CLAMP_UNLIKELY(lrintf(v), -0x8000, 0x7FFF);
+        *(b++) = INT16_TO(s);
+    }
+#else
+    for (; n > 0; n--) {
+        float v = *(a++) * (1 << 15);
+
+        *(b++) = (int16_t) PA_CLAMP_UNLIKELY(lrintf(v), -0x8000, 0x7FFF);
+    }
+#endif
+}
+
+void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+#if SWAP_WORDS == 1
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = *(a++) * (1U << 31);
+
+        s = (int32_t) PA_CLAMP_UNLIKELY(llrintf(v), -0x80000000LL, 0x7FFFFFFFLL);
+        *(b++) = INT32_TO(s);
+    }
+#else
+    for (; n > 0; n--) {
+        float v = *(a++) * (1U << 31);
+
+        *(b++) = (int32_t) PA_CLAMP_UNLIKELY(llrintf(v), -0x80000000LL, 0x7FFFFFFFLL);
+    }
+#endif
+}
+
+void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int16_t s = *(a++);
+        float k = INT16_FROM(s) * (1.0f / (1 << 15));
+        PA_WRITE_FLOAT32RE(b++, k);
+    }
+}
+
+void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = *(a++);
+        float k = INT32_FROM(s) * (1.0f / (1U << 31));
+        PA_WRITE_FLOAT32RE(b++, k);
+    }
+}
+
+void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int16_t s;
+        float v = PA_READ_FLOAT32RE(a++) * (1 << 15);
+        s = (int16_t) PA_CLAMP_UNLIKELY(lrintf(v), -0x8000, 0x7FFF);
+        *(b++) = INT16_TO(s);
+    }
+}
+
+void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = PA_READ_FLOAT32RE(a++) * (1U << 31);
+        s = (int32_t) PA_CLAMP_UNLIKELY(llrintf(v), -0x80000000LL, 0x7FFFFFFFLL);
+        *(b++) = INT32_TO(s);
+    }
+}
+
+void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t*a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        *b = (int16_t) (INT32_FROM(*a) >> 16);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s32le_to_s16re(unsigned n, const int32_t*a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int16_t s = (int16_t) (INT32_FROM(*a) >> 16);
+        *b = PA_INT16_SWAP(s);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        *b = INT32_TO(((int32_t) *a) << 16);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = ((int32_t) PA_INT16_SWAP(*a)) << 16;
+        *b = INT32_TO(s);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24le_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        *b = (int16_t) (READ24(a) >> 8);
+        a += 3;
+        b++;
+    }
+}
+
+void pa_sconv_s24le_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        WRITE24(b, ((uint32_t) *a) << 8);
+        a++;
+        b += 3;
+    }
+}
+
+void pa_sconv_s24le_to_s16re(unsigned n, const uint8_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int16_t s = (int16_t) (READ24(a) >> 8);
+        *b = PA_INT16_SWAP(s);
+        a += 3;
+        b++;
+    }
+}
+
+void pa_sconv_s24le_from_s16re(unsigned n, const int16_t *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        uint32_t s = ((uint32_t) PA_INT16_SWAP(*a)) << 8;
+        WRITE24(b, s);
+        a++;
+        b += 3;
+    }
+}
+
+void pa_sconv_s24le_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = READ24(a) << 8;
+        *b = s * (1.0f / (1U << 31));
+        a += 3;
+        b++;
+    }
+}
+
+void pa_sconv_s24le_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = *a * (1U << 31);
+        s = (int32_t) PA_CLAMP_UNLIKELY(llrint(v), -0x80000000LL, 0x7FFFFFFFLL);
+        WRITE24(b, ((uint32_t) s) >> 8);
+        a++;
+        b += 3;
+    }
+}
+
+void pa_sconv_s24le_to_float32re(unsigned n, const uint8_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = READ24(a) << 8;
+        float k = s * (1.0f / (1U << 31));
+        PA_WRITE_FLOAT32RE(b, k);
+        a += 3;
+        b++;
+    }
+}
+
+void pa_sconv_s24le_from_float32re(unsigned n, const float *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = PA_READ_FLOAT32RE(a) * (1U << 31);
+        s = (int32_t) PA_CLAMP_UNLIKELY(llrint(v), -0x80000000LL, 0x7FFFFFFFLL);
+        WRITE24(b, ((uint32_t) s) >> 8);
+        a++;
+        b+=3;
+    }
+}
+
+void pa_sconv_s24_32le_to_s16ne(unsigned n, const uint32_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        *b = (int16_t) (((int32_t) (UINT32_FROM(*a) << 8)) >> 16);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_to_s16re(unsigned n, const uint32_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int16_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8) >> 16);
+        *b = PA_INT16_SWAP(s);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_from_s16ne(unsigned n, const int16_t *a, uint32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        *b = UINT32_TO(((uint32_t) ((int32_t) *a << 16)) >> 8);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_from_s16re(unsigned n, const int16_t *a, uint32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        uint32_t s = ((uint32_t) ((int32_t) PA_INT16_SWAP(*a) << 16)) >> 8;
+        *b = UINT32_TO(s);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_to_float32ne(unsigned n, const uint32_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = (int32_t) (UINT32_FROM(*a) << 8);
+        *b = s * (1.0f / (1U << 31));
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_to_float32re(unsigned n, const uint32_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = (int32_t) (UINT32_FROM(*a) << 8);
+        float k = s * (1.0f / (1U << 31));
+        PA_WRITE_FLOAT32RE(b, k);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_from_float32ne(unsigned n, const float *a, uint32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = *a * (1U << 31);
+        s = (int32_t) PA_CLAMP_UNLIKELY(llrint(v), -0x80000000LL, 0x7FFFFFFFLL);
+        *b = UINT32_TO(((uint32_t) s) >> 8);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_from_float32re(unsigned n, const float *a, uint32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = PA_READ_FLOAT32RE(a) * (1U << 31);
+        s = (int32_t) PA_CLAMP_UNLIKELY(llrint(v), -0x80000000LL, 0x7FFFFFFFLL);
+        *b = UINT32_TO(((uint32_t) s) >> 8);
+        a++;
+        b++;
+    }
+}
diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h
new file mode 100644 (file)
index 0000000..5301373
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef foosconv_s16lefoo
+#define foosconv_s16lefoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b);
+void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b);
+
+void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b);
+
+void pa_sconv_s24le_to_float32ne(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24le_from_float32ne(unsigned n, const float *a, uint8_t *b);
+void pa_sconv_s24le_to_float32re(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24le_from_float32re(unsigned n, const float *a, uint8_t *b);
+
+void pa_sconv_s24_32le_to_float32ne(unsigned n, const uint32_t *a, float *b);
+void pa_sconv_s24_32le_from_float32ne(unsigned n, const float *a, uint32_t *b);
+void pa_sconv_s24_32le_to_float32re(unsigned n, const uint32_t *a, float *b);
+void pa_sconv_s24_32le_from_float32re(unsigned n, const float *a, uint32_t *b);
+
+void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s32le_to_s16re(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b);
+
+void pa_sconv_s24le_to_s16ne(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24le_from_s16ne(unsigned n, const int16_t *a, uint8_t *b);
+void pa_sconv_s24le_to_s16re(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24le_from_s16re(unsigned n, const int16_t *a, uint8_t *b);
+
+void pa_sconv_s24_32le_to_s16ne(unsigned n, const uint32_t *a, int16_t *b);
+void pa_sconv_s24_32le_from_s16ne(unsigned n, const int16_t *a, uint32_t *b);
+void pa_sconv_s24_32le_to_s16re(unsigned n, const uint32_t *a, int16_t *b);
+void pa_sconv_s24_32le_from_s16re(unsigned n, const int16_t *a, uint32_t *b);
+
+#ifndef WORDS_BIGENDIAN
+#define pa_sconv_float32be_to_s16ne pa_sconv_s16le_from_float32re
+#define pa_sconv_float32be_from_s16ne pa_sconv_s16le_to_float32re
+#define pa_sconv_float32le_to_s16ne pa_sconv_s16le_from_float32ne
+#define pa_sconv_float32le_from_s16ne pa_sconv_s16le_to_float32ne
+#endif
+
+#endif
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
new file mode 100644 (file)
index 0000000..0781b6e
--- /dev/null
@@ -0,0 +1,296 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <pulsecore/g711.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+
+#include <pulsecore/sconv-s16le.h>
+#include <pulsecore/sconv-s16be.h>
+
+#include "sconv.h"
+
+/* u8 */
+static void u8_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = (*a * 1.0/128.0) - 1.0;
+}
+
+static void u8_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++) {
+        float v;
+        v = (*a * 127.0) + 128.0;
+        v = PA_CLAMP_UNLIKELY (v, 0.0, 255.0);
+        *b = rint (v);
+    }
+}
+
+static void u8_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = (((int16_t)*a) - 128) << 8;
+}
+
+static void u8_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = (uint8_t) ((uint16_t) *a >> 8) + (uint8_t) 0x80U;
+}
+
+/* float32 */
+
+static void float32ne_to_float32ne(unsigned n, const float *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    memcpy(b, a, (int) (sizeof(float) * n));
+}
+
+static void float32re_to_float32ne(unsigned n, const float *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *((uint32_t *) b) = PA_UINT32_SWAP(*((uint32_t *) a));
+}
+
+/* s16 */
+
+static void s16ne_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    memcpy(b, a, (int) (sizeof(int16_t) * n));
+}
+
+static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = PA_INT16_SWAP(*a);
+}
+
+/* ulaw */
+
+static void ulaw_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--)
+        *(b++) = (float) st_ulaw2linear16(*(a++)) / 0x8000;
+}
+
+static void ulaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        float v = *(a++);
+        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+        v *= 0x1FFF;
+        *(b++) = st_14linear2ulaw((int16_t) lrintf(v));
+    }
+}
+
+static void ulaw_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = st_ulaw2linear16(*a);
+}
+
+static void ulaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = st_14linear2ulaw(*a >> 2);
+}
+
+/* alaw */
+
+static void alaw_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = (float) st_alaw2linear16(*a) / 0x8000;
+}
+
+static void alaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++) {
+        float v = *a;
+        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+        v *= 0xFFF;
+        *b = st_13linear2alaw((int16_t) lrintf(v));
+    }
+}
+
+static void alaw_to_s16ne(unsigned n, const int8_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = st_alaw2linear16((uint8_t) *a);
+}
+
+static void alaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = st_13linear2alaw(*a >> 3);
+}
+
+static pa_convert_func_t to_float32ne_table[] = {
+    [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_to_float32ne,
+    [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_to_float32ne,
+    [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_to_float32ne,
+    [PA_SAMPLE_S16LE]     = (pa_convert_func_t) pa_sconv_s16le_to_float32ne,
+    [PA_SAMPLE_S16BE]     = (pa_convert_func_t) pa_sconv_s16be_to_float32ne,
+    [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_to_float32ne,
+    [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
+    [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_to_float32ne,
+    [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_to_float32ne,
+    [PA_SAMPLE_S24_32LE]  = (pa_convert_func_t) pa_sconv_s24_32le_to_float32ne,
+    [PA_SAMPLE_S24_32BE]  = (pa_convert_func_t) pa_sconv_s24_32be_to_float32ne,
+    [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
+    [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
+};
+
+pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
+    pa_assert(pa_sample_format_valid(f));
+
+    return to_float32ne_table[f];
+}
+
+void pa_set_convert_to_float32ne_function(pa_sample_format_t f, pa_convert_func_t func) {
+    pa_assert(pa_sample_format_valid(f));
+
+    to_float32ne_table[f] = func;
+}
+
+static pa_convert_func_t from_float32ne_table[] = {
+    [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_from_float32ne,
+    [PA_SAMPLE_S16LE]     = (pa_convert_func_t) pa_sconv_s16le_from_float32ne,
+    [PA_SAMPLE_S16BE]     = (pa_convert_func_t) pa_sconv_s16be_from_float32ne,
+    [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_from_float32ne,
+    [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_from_float32ne,
+    [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_from_float32ne,
+    [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_from_float32ne,
+    [PA_SAMPLE_S24_32LE]  = (pa_convert_func_t) pa_sconv_s24_32le_from_float32ne,
+    [PA_SAMPLE_S24_32BE]  = (pa_convert_func_t) pa_sconv_s24_32be_from_float32ne,
+    [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
+    [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
+    [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_from_float32ne,
+    [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_from_float32ne
+};
+
+pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
+    pa_assert(pa_sample_format_valid(f));
+
+    return from_float32ne_table[f];
+}
+
+void pa_set_convert_from_float32ne_function(pa_sample_format_t f, pa_convert_func_t func) {
+    pa_assert(pa_sample_format_valid(f));
+
+    from_float32ne_table[f] = func;
+}
+
+static pa_convert_func_t to_s16ne_table[] = {
+    [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_to_s16ne,
+    [PA_SAMPLE_S16NE]     = (pa_convert_func_t) s16ne_to_s16ne,
+    [PA_SAMPLE_S16RE]     = (pa_convert_func_t) s16re_to_s16ne,
+    [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_to_s16ne,
+    [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne,
+    [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_to_s16ne,
+    [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
+    [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_to_s16ne,
+    [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_to_s16ne,
+    [PA_SAMPLE_S24_32BE]  = (pa_convert_func_t) pa_sconv_s24_32be_to_s16ne,
+    [PA_SAMPLE_S24_32LE]  = (pa_convert_func_t) pa_sconv_s24_32le_to_s16ne,
+    [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_to_s16ne,
+    [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_to_s16ne
+};
+
+pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) {
+    pa_assert(pa_sample_format_valid(f));
+
+    return to_s16ne_table[f];
+}
+
+void pa_set_convert_to_s16ne_function(pa_sample_format_t f, pa_convert_func_t func) {
+    pa_assert(pa_sample_format_valid(f));
+
+    to_s16ne_table[f] = func;
+}
+
+static pa_convert_func_t from_s16ne_table[] = {
+    [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_from_s16ne,
+    [PA_SAMPLE_S16NE]     = (pa_convert_func_t) s16ne_to_s16ne,
+    [PA_SAMPLE_S16RE]     = (pa_convert_func_t) s16re_to_s16ne,
+    [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_from_s16ne,
+    [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne,
+    [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_from_s16ne,
+    [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
+    [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_from_s16ne,
+    [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_from_s16ne,
+    [PA_SAMPLE_S24_32BE]  = (pa_convert_func_t) pa_sconv_s24_32be_from_s16ne,
+    [PA_SAMPLE_S24_32LE]  = (pa_convert_func_t) pa_sconv_s24_32le_from_s16ne,
+    [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_from_s16ne,
+    [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_from_s16ne,
+};
+
+pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) {
+    pa_assert(pa_sample_format_valid(f));
+
+    return from_s16ne_table[f];
+}
+
+void pa_set_convert_from_s16ne_function(pa_sample_format_t f, pa_convert_func_t func) {
+    pa_assert(pa_sample_format_valid(f));
+
+    from_s16ne_table[f] = func;
+}
diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h
new file mode 100644 (file)
index 0000000..c457429
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef foosconvhfoo
+#define foosconvhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/gccmacro.h>
+#include <pulse/sample.h>
+
+typedef void (*pa_convert_func_t)(unsigned n, const void *a, void *b);
+
+pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) PA_GCC_PURE;
+pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) PA_GCC_PURE;
+
+pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
+pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
+
+void pa_set_convert_to_float32ne_function(pa_sample_format_t f, pa_convert_func_t func);
+void pa_set_convert_from_float32ne_function(pa_sample_format_t f, pa_convert_func_t func);
+
+void pa_set_convert_to_s16ne_function(pa_sample_format_t f, pa_convert_func_t func);
+void pa_set_convert_from_s16ne_function(pa_sample_format_t f, pa_convert_func_t func);
+
+#endif
diff --git a/src/pulsecore/sconv_neon.c b/src/pulsecore/sconv_neon.c
new file mode 100644 (file)
index 0000000..11d94d2
--- /dev/null
@@ -0,0 +1,96 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2012 Peter Meerwald <p.meerwald@bct-electronic.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+
+#include "cpu-arm.h"
+#include "sconv.h"
+
+#include <math.h>
+#include <arm_neon.h>
+
+static void pa_sconv_s16le_from_f32ne_neon(unsigned n, const float *src, int16_t *dst) {
+    unsigned i = n & 3;
+
+    __asm__ __volatile__ (
+        "movs       %[n], %[n], lsr #2      \n\t"
+        "beq        2f                      \n\t"
+
+        "1:                                 \n\t"
+        "vld1.32    {q0}, [%[src]]!         \n\t"
+        "vcvt.s32.f32 q0, q0, #31           \n\t" /* s32<-f32 as 16:16 fixed-point, with implicit multiplication by 32768 */
+        "vqrshrn.s32 d0, q0, #16            \n\t" /* shift, round, narrow */
+        "subs       %[n], %[n], #1          \n\t"
+        "vst1.16    {d0}, [%[dst]]!         \n\t"
+        "bgt        1b                      \n\t"
+
+        "2:                                 \n\t"
+
+        : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n) /* output operands (or input operands that get modified) */
+        : /* input operands */
+        : "memory", "cc", "q0" /* clobber list */
+    );
+
+    /* leftovers */
+    while (i--) {
+        *dst++ = (int16_t) PA_CLAMP_UNLIKELY(lrintf(*src * (1 << 15)), -0x8000, 0x7FFF);
+        src++;
+    }
+}
+
+static void pa_sconv_s16le_to_f32ne_neon(unsigned n, const int16_t *src, float *dst) {
+    unsigned i = n & 3;
+    const float invscale = 1.0f / (1 << 15);
+
+    __asm__ __volatile__ (
+        "movs       %[n], %[n], lsr #2      \n\t"
+        "beq        2f                      \n\t"
+
+        "1:                                 \n\t"
+        "vld1.16    {d0}, [%[src]]!         \n\t"
+        "vmovl.s16  q0, d0                  \n\t" /* widen */
+        "vcvt.f32.s32 q0, q0, #15           \n\t" /* f32<-s32 and divide by (1<<15) */
+        "subs       %[n], %[n], #1          \n\t"
+        "vst1.32    {q0}, [%[dst]]!         \n\t"
+        "bgt        1b                      \n\t"
+
+        "2:                                 \n\t"
+
+        : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n) /* output operands (or input operands that get modified) */
+        : /* input operands */
+        : "memory", "cc", "q0" /* clobber list */
+    );
+
+    /* leftovers */
+    while (i--) {
+        *dst++ = *src++ * invscale;
+    }
+}
+
+void pa_convert_func_init_neon(pa_cpu_arm_flag_t flags) {
+    pa_log_info("Initialising ARM NEON optimized conversions.");
+    pa_set_convert_from_float32ne_function(PA_SAMPLE_S16LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_neon);
+    pa_set_convert_to_float32ne_function(PA_SAMPLE_S16LE, (pa_convert_func_t) pa_sconv_s16le_to_f32ne_neon);
+#ifndef WORDS_BIGENDIAN
+    pa_set_convert_from_s16ne_function(PA_SAMPLE_FLOAT32LE, (pa_convert_func_t) pa_sconv_s16le_to_f32ne_neon);
+    pa_set_convert_to_s16ne_function(PA_SAMPLE_FLOAT32LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_neon);
+#endif
+}
diff --git a/src/pulsecore/sconv_sse.c b/src/pulsecore/sconv_sse.c
new file mode 100644 (file)
index 0000000..1b097b8
--- /dev/null
@@ -0,0 +1,177 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+
+#include "cpu-x86.h"
+#include "sconv.h"
+
+#if (!defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && defined (__i386__)) || defined (__amd64__)
+
+static const PA_DECLARE_ALIGNED (16, float, scale[4]) = { 0x8000, 0x8000, 0x8000, 0x8000 };
+
+static void pa_sconv_s16le_from_f32ne_sse(unsigned n, const float *a, int16_t *b) {
+    pa_reg_x86 temp, i;
+
+    __asm__ __volatile__ (
+        " movaps %5, %%xmm5             \n\t"
+        " xor %0, %0                    \n\t"
+
+        " mov %4, %1                    \n\t"
+        " sar $3, %1                    \n\t" /* 8 floats at a time */
+        " cmp $0, %1                    \n\t"
+        " je 2f                         \n\t"
+
+        "1:                             \n\t"
+        " movups (%q2, %0, 2), %%xmm0   \n\t" /* read 8 floats */
+        " movups 16(%q2, %0, 2), %%xmm2 \n\t"
+        " mulps  %%xmm5, %%xmm0         \n\t" /* *= 0x8000 */
+        " mulps  %%xmm5, %%xmm2         \n\t"
+
+        " cvtps2pi %%xmm0, %%mm0        \n\t" /* low part to int */
+        " cvtps2pi %%xmm2, %%mm2        \n\t"
+        " movhlps  %%xmm0, %%xmm0       \n\t" /* bring high part in position */
+        " movhlps  %%xmm2, %%xmm2       \n\t"
+        " cvtps2pi %%xmm0, %%mm1        \n\t" /* high part to int */
+        " cvtps2pi %%xmm2, %%mm3        \n\t"
+
+        " packssdw %%mm1, %%mm0         \n\t" /* pack parts */
+        " packssdw %%mm3, %%mm2         \n\t"
+        " movq     %%mm0, (%q3, %0)     \n\t"
+        " movq     %%mm2, 8(%q3, %0)    \n\t"
+
+        " add $16, %0                   \n\t"
+        " dec %1                        \n\t"
+        " jne 1b                        \n\t"
+
+        "2:                             \n\t"
+        " mov %4, %1                    \n\t" /* prepare for leftovers */
+        " and $7, %1                    \n\t"
+        " je 5f                         \n\t"
+
+        "3:                             \n\t"
+        " movss (%q2, %0, 2), %%xmm0    \n\t"
+        " mulss  %%xmm5, %%xmm0         \n\t"
+        " cvtss2si %%xmm0, %4           \n\t"
+        " add $0x8000, %4               \n\t" /* check for saturation */
+        " and $~0xffff, %4              \n\t"
+        " cvtss2si %%xmm0, %4           \n\t"
+        " je 4f                         \n\t"
+        " sar $31, %4                   \n\t"
+        " xor $0x7fff, %4               \n\t"
+
+        "4:                             \n\t"
+        " movw  %w4, (%q3, %0)          \n\t" /* store leftover */
+        " add $2, %0                    \n\t"
+        " dec %1                        \n\t"
+        " jne 3b                        \n\t"
+
+        "5:                             \n\t"
+        " emms                          \n\t"
+
+        : "=&r" (i), "=&r" (temp)
+        : "r" (a), "r" (b), "r" ((pa_reg_x86)n), "m" (*scale)
+        : "cc", "memory"
+    );
+}
+
+static void pa_sconv_s16le_from_f32ne_sse2(unsigned n, const float *a, int16_t *b) {
+    pa_reg_x86 temp, i;
+
+    __asm__ __volatile__ (
+        " movaps %5, %%xmm5             \n\t"
+        " xor %0, %0                    \n\t"
+
+        " mov %4, %1                    \n\t"
+        " sar $3, %1                    \n\t" /* 8 floats at a time */
+        " cmp $0, %1                    \n\t"
+        " je 2f                         \n\t"
+
+        "1:                             \n\t"
+        " movups (%q2, %0, 2), %%xmm0   \n\t" /* read 8 floats */
+        " movups 16(%q2, %0, 2), %%xmm2 \n\t"
+        " mulps  %%xmm5, %%xmm0         \n\t" /* *= 0x8000 */
+        " mulps  %%xmm5, %%xmm2         \n\t"
+
+        " cvtps2dq %%xmm0, %%xmm0       \n\t"
+        " cvtps2dq %%xmm2, %%xmm2       \n\t"
+
+        " packssdw %%xmm2, %%xmm0       \n\t"
+        " movdqu   %%xmm0, (%q3, %0)    \n\t"
+
+        " add $16, %0                   \n\t"
+        " dec %1                        \n\t"
+        " jne 1b                        \n\t"
+
+        "2:                             \n\t"
+        " mov %4, %1                    \n\t" /* prepare for leftovers */
+        " and $7, %1                    \n\t"
+        " je 5f                         \n\t"
+
+        "3:                             \n\t"
+        " movss (%q2, %0, 2), %%xmm0    \n\t"
+        " mulss  %%xmm5, %%xmm0         \n\t"
+        " cvtss2si %%xmm0, %4           \n\t"
+        " add $0x8000, %4               \n\t"
+        " and $~0xffff, %4              \n\t" /* check for saturation */
+        " cvtss2si %%xmm0, %4           \n\t"
+        " je 4f                         \n\t"
+        " sar $31, %4                   \n\t"
+        " xor $0x7fff, %4               \n\t"
+
+        "4:                             \n\t"
+        " movw  %w4, (%q3, %0)          \n\t" /* store leftover */
+        " add $2, %0                    \n\t"
+        " dec %1                        \n\t"
+        " jne 3b                        \n\t"
+
+        "5:                             \n\t"
+
+        : "=&r" (i), "=&r" (temp)
+        : "r" (a), "r" (b), "r" ((pa_reg_x86)n), "m" (*scale)
+        : "cc", "memory"
+    );
+}
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+void pa_convert_func_init_sse(pa_cpu_x86_flag_t flags) {
+#if (!defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && defined (__i386__)) || defined (__amd64__)
+
+    if (flags & PA_CPU_X86_SSE2) {
+        pa_log_info("Initialising SSE2 optimized conversions.");
+        pa_set_convert_from_float32ne_function(PA_SAMPLE_S16LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_sse2);
+        pa_set_convert_to_s16ne_function(PA_SAMPLE_FLOAT32LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_sse2);
+    } else if (flags & PA_CPU_X86_SSE) {
+        pa_log_info("Initialising SSE optimized conversions.");
+        pa_set_convert_from_float32ne_function(PA_SAMPLE_S16LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_sse);
+        pa_set_convert_to_s16ne_function(PA_SAMPLE_FLOAT32LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_sse);
+    }
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/semaphore-osx.c b/src/pulsecore/semaphore-osx.c
new file mode 100644 (file)
index 0000000..c957f26
--- /dev/null
@@ -0,0 +1,92 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2013 Albert Zeyer
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/core-util.h>
+
+#include "semaphore.h"
+
+/* OSX doesn't support unnamed semaphores (via sem_init).
+ * Thus, we use a counter to give them enumerated names. */
+static pa_atomic_t id_counter = PA_ATOMIC_INIT(0);
+
+struct pa_semaphore {
+    sem_t *sem;
+    int id;
+};
+
+static char *sem_name(char *fn, size_t l, int id) {
+    pa_snprintf(fn, l, "/pulse-sem-%u-%u", getpid(), id);
+    return fn;
+}
+
+pa_semaphore *pa_semaphore_new(unsigned value) {
+    pa_semaphore *s;
+    char fn[32];
+
+    s = pa_xnew(pa_semaphore, 1);
+    s->id = pa_atomic_inc(&id_counter);
+    sem_name(fn, sizeof(fn), s->id);
+    sem_unlink(fn); /* in case an old stale semaphore is left around */
+    pa_assert_se(s->sem = sem_open(fn, O_CREAT|O_EXCL, 0700, value));
+    pa_assert(s->sem != SEM_FAILED);
+    return s;
+}
+
+void pa_semaphore_free(pa_semaphore *s) {
+    char fn[32];
+
+    pa_assert(s);
+
+    pa_assert_se(sem_close(s->sem) == 0);
+    sem_name(fn, sizeof(fn), s->id);
+    pa_assert_se(sem_unlink(fn) == 0);
+    pa_xfree(s);
+}
+
+void pa_semaphore_post(pa_semaphore *s) {
+    pa_assert(s);
+    pa_assert_se(sem_post(s->sem) == 0);
+}
+
+void pa_semaphore_wait(pa_semaphore *s) {
+    int ret;
+
+    pa_assert(s);
+
+    do {
+        ret = sem_wait(s->sem);
+    } while (ret < 0 && errno == EINTR);
+
+    pa_assert(ret == 0);
+}
diff --git a/src/pulsecore/semaphore-posix.c b/src/pulsecore/semaphore-posix.c
new file mode 100644 (file)
index 0000000..7907c19
--- /dev/null
@@ -0,0 +1,87 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "semaphore.h"
+
+struct pa_semaphore {
+    sem_t sem;
+};
+
+pa_semaphore* pa_semaphore_new(unsigned value) {
+    pa_semaphore *s;
+
+    s = pa_xnew(pa_semaphore, 1);
+    pa_assert_se(sem_init(&s->sem, 0, value) == 0);
+    return s;
+}
+
+void pa_semaphore_free(pa_semaphore *s) {
+    pa_assert(s);
+    pa_assert_se(sem_destroy(&s->sem) == 0);
+    pa_xfree(s);
+}
+
+void pa_semaphore_post(pa_semaphore *s) {
+    pa_assert(s);
+    pa_assert_se(sem_post(&s->sem) == 0);
+}
+
+void pa_semaphore_wait(pa_semaphore *s) {
+    int ret;
+    pa_assert(s);
+
+    do {
+        ret = sem_wait(&s->sem);
+    } while (ret < 0 && errno == EINTR);
+
+    pa_assert(ret == 0);
+}
+
+pa_semaphore* pa_static_semaphore_get(pa_static_semaphore *s, unsigned value) {
+    pa_semaphore *m;
+
+    pa_assert(s);
+
+    /* First, check if already initialized and short cut */
+    if ((m = pa_atomic_ptr_load(&s->ptr)))
+        return m;
+
+    /* OK, not initialized, so let's allocate, and fill in */
+    m = pa_semaphore_new(value);
+    if ((pa_atomic_ptr_cmpxchg(&s->ptr, NULL, m)))
+        return m;
+
+    pa_semaphore_free(m);
+
+    /* Him, filling in failed, so someone else must have filled in
+     * already */
+    pa_assert_se(m = pa_atomic_ptr_load(&s->ptr));
+    return m;
+}
diff --git a/src/pulsecore/semaphore-win32.c b/src/pulsecore/semaphore-win32.c
new file mode 100644 (file)
index 0000000..660d4bd
--- /dev/null
@@ -0,0 +1,60 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <windows.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "semaphore.h"
+
+struct pa_semaphore {
+    HANDLE sema;
+};
+
+pa_semaphore* pa_semaphore_new(unsigned value) {
+    pa_semaphore *s;
+
+    s = pa_xnew(pa_semaphore, 1);
+
+    s->sema = CreateSemaphore(NULL, value, 32767, NULL);
+    pa_assert(s->sema != NULL);
+
+    return s;
+}
+
+void pa_semaphore_free(pa_semaphore *s) {
+    pa_assert(s);
+    CloseHandle(s->sema);
+    pa_xfree(s);
+}
+
+void pa_semaphore_post(pa_semaphore *s) {
+    pa_assert(s);
+    ReleaseSemaphore(s->sema, 1, NULL);
+}
+
+void pa_semaphore_wait(pa_semaphore *s) {
+    pa_assert(s);
+    WaitForSingleObject(s->sema, INFINITE);
+}
diff --git a/src/pulsecore/semaphore.h b/src/pulsecore/semaphore.h
new file mode 100644 (file)
index 0000000..4c746f1
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef foopulsesemaphorehfoo
+#define foopulsesemaphorehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
+
+typedef struct pa_semaphore pa_semaphore;
+
+pa_semaphore* pa_semaphore_new(unsigned value);
+void pa_semaphore_free(pa_semaphore *m);
+
+void pa_semaphore_post(pa_semaphore *m);
+void pa_semaphore_wait(pa_semaphore *m);
+
+/* Static semaphores are basically just atomically updated pointers to
+ * pa_semaphore objects */
+
+typedef struct pa_static_semaphore {
+    pa_atomic_ptr_t ptr;
+} pa_static_semaphore;
+
+#define PA_STATIC_SEMAPHORE_INIT { PA_ATOMIC_PTR_INIT(NULL) }
+
+/* When you call this make sure to pass always the same value parameter! */
+pa_semaphore* pa_static_semaphore_get(pa_static_semaphore *m, unsigned value);
+
+#endif
diff --git a/src/pulsecore/shared.c b/src/pulsecore/shared.c
new file mode 100644 (file)
index 0000000..9bc7eb5
--- /dev/null
@@ -0,0 +1,116 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "shared.h"
+
+typedef struct pa_shared {
+    char *name;  /* Points to memory allocated by the shared property system */
+    void *data;  /* Points to memory maintained by the caller */
+} pa_shared;
+
+/* Allocate a new shared property object */
+static pa_shared* shared_new(const char *name, void *data) {
+    pa_shared* p;
+
+    pa_assert(name);
+    pa_assert(data);
+
+    p = pa_xnew(pa_shared, 1);
+    p->name = pa_xstrdup(name);
+    p->data = data;
+
+    return p;
+}
+
+/* Free a shared property object */
+static void shared_free(pa_shared *p) {
+    pa_assert(p);
+
+    pa_xfree(p->name);
+    pa_xfree(p);
+}
+
+void* pa_shared_get(pa_core *c, const char *name) {
+    pa_shared *p;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(c->shared);
+
+    if (!(p = pa_hashmap_get(c->shared, name)))
+        return NULL;
+
+    return p->data;
+}
+
+int pa_shared_set(pa_core *c, const char *name, void *data) {
+    pa_shared *p;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(data);
+    pa_assert(c->shared);
+
+    if (pa_hashmap_get(c->shared, name))
+        return -1;
+
+    p = shared_new(name, data);
+    pa_hashmap_put(c->shared, p->name, p);
+    return 0;
+}
+
+int pa_shared_remove(pa_core *c, const char *name) {
+    pa_shared *p;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(c->shared);
+
+    if (!(p = pa_hashmap_remove(c->shared, name)))
+        return -1;
+
+    shared_free(p);
+    return 0;
+}
+
+void pa_shared_dump(pa_core *c, pa_strbuf *s) {
+    void *state = NULL;
+    pa_shared *p;
+
+    pa_assert(c);
+    pa_assert(s);
+
+    while ((p = pa_hashmap_iterate(c->shared, &state, NULL)))
+        pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data);
+}
+
+int pa_shared_replace(pa_core *c, const char *name, void *data) {
+    pa_assert(c);
+    pa_assert(name);
+
+    (void) pa_shared_remove(c, name);
+    return pa_shared_set(c, name, data);
+}
diff --git a/src/pulsecore/shared.h b/src/pulsecore/shared.h
new file mode 100644 (file)
index 0000000..19ac462
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef foosharedshfoo
+#define foosharedshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/strbuf.h>
+
+/* The shared property subsystem is to be used to share data between
+ * modules. Consider them to be kind of "global" variables for a
+ * core. Why not use the hashmap functions directly? The hashmap
+ * functions copy neither the key nor value, while this property
+ * system copies the key. Users of this system have to think about
+ * reference counting themselves. */
+
+/* Note: please don't confuse this with the proplist framework in
+ * pulse/proplist.[ch]! */
+
+/* Return a pointer to the value of the specified shared property. */
+void* pa_shared_get(pa_core *c, const char *name);
+
+/* Set the shared property 'name' to 'data'. This function fails in
+ * case a property by this name already exists. The property data is
+ * not copied or reference counted. This is the caller's job. */
+int pa_shared_set(pa_core *c, const char *name, void *data);
+
+/* Remove the specified shared property. Return non-zero on failure */
+int pa_shared_remove(pa_core *c, const char *name);
+
+/* A combination of pa_shared_remove() and pa_shared_set(); this function
+ * first tries to remove the property by this name and then sets the
+ * property. Return non-zero on failure. */
+int pa_shared_replace(pa_core *c, const char *name, void *data);
+
+/* Dump the current set of shared properties */
+void pa_shared_dump(pa_core *c, pa_strbuf *s);
+
+#endif
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
new file mode 100644 (file)
index 0000000..0742cb8
--- /dev/null
@@ -0,0 +1,495 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2006 Lennart Poettering
+    Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as
+    published by the Free Software Foundation; either version 2.1 of the
+    License, or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+/* This is deprecated on glibc but is still used by FreeBSD */
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/memfd-wrappers.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/random.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/mem.h>
+
+#include "shm.h"
+
+#if defined(__linux__) && !defined(MADV_REMOVE)
+#define MADV_REMOVE 9
+#endif
+
+/* 1 GiB at max */
+#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*1024))
+
+#ifdef __linux__
+/* On Linux we know that the shared memory blocks are files in
+ * /dev/shm. We can use that information to list all blocks and
+ * cleanup unused ones */
+#define SHM_PATH "/dev/shm/"
+#define SHM_ID_LEN 10
+#elif defined(__sun)
+#define SHM_PATH "/tmp"
+#define SHM_ID_LEN 15
+#else
+#undef SHM_PATH
+#undef SHM_ID_LEN
+#endif
+
+#define SHM_MARKER ((int) 0xbeefcafe)
+
+/* We now put this SHM marker at the end of each segment. It's
+ * optional, to not require a reboot when upgrading, though. Note that
+ * on multiarch systems 32bit and 64bit processes might access this
+ * region simultaneously. The header fields need to be independent
+ * from the process' word with */
+struct shm_marker {
+    pa_atomic_t marker; /* 0xbeefcafe */
+    pa_atomic_t pid;
+    uint64_t _reserved1;
+    uint64_t _reserved2;
+    uint64_t _reserved3;
+    uint64_t _reserved4;
+} PA_GCC_PACKED;
+
+static inline size_t shm_marker_size(pa_mem_type_t type) {
+    if (type == PA_MEM_TYPE_SHARED_POSIX)
+        return PA_ALIGN(sizeof(struct shm_marker));
+
+    return 0;
+}
+
+#ifdef HAVE_SHM_OPEN
+static char *segment_name(char *fn, size_t l, unsigned id) {
+    pa_snprintf(fn, l, "/pulse-shm-%u", id);
+    return fn;
+}
+#endif
+
+static int privatemem_create(pa_shm *m, size_t size) {
+    pa_assert(m);
+    pa_assert(size > 0);
+
+    m->type = PA_MEM_TYPE_PRIVATE;
+    m->id = 0;
+    m->size = size;
+    m->do_unlink = false;
+    m->fd = -1;
+
+#ifdef MAP_ANONYMOUS
+    if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) {
+        pa_log("mmap() failed: %s", pa_cstrerror(errno));
+        return -1;
+    }
+#elif defined(HAVE_POSIX_MEMALIGN)
+    {
+        int r;
+
+        if ((r = posix_memalign(&m->ptr, pa_page_size(), size)) < 0) {
+            pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
+            return r;
+        }
+    }
+#else
+    m->ptr = pa_xmalloc(m->size);
+#endif
+
+    return 0;
+}
+
+static int sharedmem_create(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode) {
+#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
+    char fn[32];
+    int fd = -1;
+    struct shm_marker *marker;
+    bool do_unlink = false;
+
+    /* Each time we create a new SHM area, let's first drop all stale
+     * ones */
+    pa_shm_cleanup();
+
+    pa_random(&m->id, sizeof(m->id));
+
+    switch (type) {
+#ifdef HAVE_SHM_OPEN
+    case PA_MEM_TYPE_SHARED_POSIX:
+        segment_name(fn, sizeof(fn), m->id);
+        fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode);
+        do_unlink = true;
+        break;
+#endif
+#ifdef HAVE_MEMFD
+    case PA_MEM_TYPE_SHARED_MEMFD:
+        fd = memfd_create("pulseaudio", MFD_ALLOW_SEALING);
+        break;
+#endif
+    default:
+        goto fail;
+    }
+
+    if (fd < 0) {
+        pa_log("%s open() failed: %s", pa_mem_type_to_string(type), pa_cstrerror(errno));
+        goto fail;
+    }
+
+    m->type = type;
+    m->size = size + shm_marker_size(type);
+    m->do_unlink = do_unlink;
+
+    if (ftruncate(fd, (off_t) m->size) < 0) {
+        pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+#ifndef MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+
+    if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (off_t) 0)) == MAP_FAILED) {
+        pa_log("mmap() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (type == PA_MEM_TYPE_SHARED_POSIX) {
+        /* We store our PID at the end of the shm block, so that we
+         * can check for dead shm segments later */
+        marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - shm_marker_size(type));
+        pa_atomic_store(&marker->pid, (int) getpid());
+        pa_atomic_store(&marker->marker, SHM_MARKER);
+    }
+
+    /* For memfds, we keep the fd open until we pass it
+     * to the other PA endpoint over unix domain socket. */
+    if (type != PA_MEM_TYPE_SHARED_MEMFD) {
+        pa_assert_se(pa_close(fd) == 0);
+        m->fd = -1;
+    }
+#ifdef HAVE_MEMFD
+    else
+        m->fd = fd;
+#endif
+
+    return 0;
+
+fail:
+    if (fd >= 0) {
+#ifdef HAVE_SHM_OPEN
+        if (type == PA_MEM_TYPE_SHARED_POSIX)
+            shm_unlink(fn);
+#endif
+        pa_close(fd);
+    }
+#endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */
+
+    return -1;
+}
+
+int pa_shm_create_rw(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode) {
+    pa_assert(m);
+    pa_assert(size > 0);
+    pa_assert(size <= MAX_SHM_SIZE);
+    pa_assert(!(mode & ~0777));
+    pa_assert(mode >= 0600);
+
+    /* Round up to make it page aligned */
+    size = PA_PAGE_ALIGN(size);
+
+    if (type == PA_MEM_TYPE_PRIVATE)
+        return privatemem_create(m, size);
+
+    return sharedmem_create(m, type, size, mode);
+}
+
+static void privatemem_free(pa_shm *m) {
+    pa_assert(m);
+    pa_assert(m->ptr);
+    pa_assert(m->size > 0);
+
+#ifdef MAP_ANONYMOUS
+    if (munmap(m->ptr, m->size) < 0)
+        pa_log("munmap() failed: %s", pa_cstrerror(errno));
+#elif defined(HAVE_POSIX_MEMALIGN)
+    free(m->ptr);
+#else
+    pa_xfree(m->ptr);
+#endif
+}
+
+void pa_shm_free(pa_shm *m) {
+    pa_assert(m);
+    pa_assert(m->ptr);
+    pa_assert(m->size > 0);
+
+#ifdef MAP_FAILED
+    pa_assert(m->ptr != MAP_FAILED);
+#endif
+
+    if (m->type == PA_MEM_TYPE_PRIVATE) {
+        privatemem_free(m);
+        goto finish;
+    }
+
+#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
+    if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0)
+        pa_log("munmap() failed: %s", pa_cstrerror(errno));
+
+#ifdef HAVE_SHM_OPEN
+    if (m->type == PA_MEM_TYPE_SHARED_POSIX && m->do_unlink) {
+        char fn[32];
+
+        segment_name(fn, sizeof(fn), m->id);
+        if (shm_unlink(fn) < 0)
+            pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
+    }
+#endif
+#ifdef HAVE_MEMFD
+    if (m->type == PA_MEM_TYPE_SHARED_MEMFD && m->fd != -1)
+        pa_assert_se(pa_close(m->fd) == 0);
+#endif
+
+#else
+    /* We shouldn't be here without shm or memfd support */
+    pa_assert_not_reached();
+#endif
+
+finish:
+    pa_zero(*m);
+}
+
+void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
+    void *ptr;
+    size_t o;
+    const size_t page_size = pa_page_size();
+
+    pa_assert(m);
+    pa_assert(m->ptr);
+    pa_assert(m->size > 0);
+    pa_assert(offset+size <= m->size);
+
+#ifdef MAP_FAILED
+    pa_assert(m->ptr != MAP_FAILED);
+#endif
+
+    /* You're welcome to implement this as NOOP on systems that don't
+     * support it */
+
+    /* Align the pointer up to multiples of the page size */
+    ptr = (uint8_t*) m->ptr + offset;
+    o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr));
+
+    if (o > 0) {
+        size_t delta = page_size - o;
+        ptr = (uint8_t*) ptr + delta;
+        size -= delta;
+    }
+
+    /* Align the size down to multiples of page size */
+    size = (size / page_size) * page_size;
+
+#ifdef MADV_REMOVE
+    if (madvise(ptr, size, MADV_REMOVE) >= 0)
+        return;
+#endif
+
+#ifdef MADV_FREE
+    if (madvise(ptr, size, MADV_FREE) >= 0)
+        return;
+#endif
+
+#ifdef MADV_DONTNEED
+    madvise(ptr, size, MADV_DONTNEED);
+#elif defined(POSIX_MADV_DONTNEED)
+    posix_madvise(ptr, size, POSIX_MADV_DONTNEED);
+#endif
+}
+
+static int shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable, bool for_cleanup) {
+#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
+    char fn[32];
+    int fd = -1;
+    int prot;
+    struct stat st;
+
+    pa_assert(m);
+
+    switch (type) {
+#ifdef HAVE_SHM_OPEN
+    case PA_MEM_TYPE_SHARED_POSIX:
+        pa_assert(memfd_fd == -1);
+        segment_name(fn, sizeof(fn), id);
+        if ((fd = shm_open(fn, writable ? O_RDWR : O_RDONLY, 0)) < 0) {
+            if ((errno != EACCES && errno != ENOENT) || !for_cleanup)
+                pa_log("shm_open() failed: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+        break;
+#endif
+#ifdef HAVE_MEMFD
+    case PA_MEM_TYPE_SHARED_MEMFD:
+        pa_assert(memfd_fd != -1);
+        fd = memfd_fd;
+        break;
+#endif
+    default:
+        goto fail;
+    }
+
+    if (fstat(fd, &st) < 0) {
+        pa_log("fstat() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (st.st_size <= 0 ||
+        st.st_size > (off_t) MAX_SHM_SIZE + (off_t) shm_marker_size(type) ||
+        PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
+        pa_log("Invalid shared memory segment size");
+        goto fail;
+    }
+
+    prot = writable ? PROT_READ | PROT_WRITE : PROT_READ;
+    if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(st.st_size), prot, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
+        pa_log("mmap() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    /* In case of attaching to memfd areas, _the caller_ maintains
+     * ownership of the passed fd and has the sole responsibility
+     * of closing it down.. For other types, we're the code path
+     * which created the fd in the first place and we're thus the
+     * ones responsible for closing it down */
+    if (type != PA_MEM_TYPE_SHARED_MEMFD)
+        pa_assert_se(pa_close(fd) == 0);
+
+    m->type = type;
+    m->id = id;
+    m->size = (size_t) st.st_size;
+    m->do_unlink = false;
+    m->fd = -1;
+
+    return 0;
+
+fail:
+    /* In case of memfds, caller maintains fd ownership */
+    if (fd >= 0 && type != PA_MEM_TYPE_SHARED_MEMFD)
+        pa_close(fd);
+
+#endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */
+
+    return -1;
+}
+
+/* Caller owns passed @memfd_fd and must close it down when appropriate. */
+int pa_shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable) {
+    return shm_attach(m, type, id, memfd_fd, writable, false);
+}
+
+int pa_shm_cleanup(void) {
+
+#ifdef HAVE_SHM_OPEN
+#ifdef SHM_PATH
+    DIR *d;
+    struct dirent *de;
+
+    if (!(d = opendir(SHM_PATH))) {
+        pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    while ((de = readdir(d))) {
+        pa_shm seg;
+        unsigned id;
+        pid_t pid;
+        char fn[128];
+        struct shm_marker *m;
+
+#if defined(__sun)
+        if (strncmp(de->d_name, ".SHMDpulse-shm-", SHM_ID_LEN))
+#else
+        if (strncmp(de->d_name, "pulse-shm-", SHM_ID_LEN))
+#endif
+            continue;
+
+        if (pa_atou(de->d_name + SHM_ID_LEN, &id) < 0)
+            continue;
+
+        if (shm_attach(&seg, PA_MEM_TYPE_SHARED_POSIX, id, -1, false, true) < 0)
+            continue;
+
+        if (seg.size < shm_marker_size(seg.type)) {
+            pa_shm_free(&seg);
+            continue;
+        }
+
+        m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - shm_marker_size(seg.type));
+
+        if (pa_atomic_load(&m->marker) != SHM_MARKER) {
+            pa_shm_free(&seg);
+            continue;
+        }
+
+        if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
+            pa_shm_free(&seg);
+            continue;
+        }
+
+        if (kill(pid, 0) == 0 || errno != ESRCH) {
+            pa_shm_free(&seg);
+            continue;
+        }
+
+        pa_shm_free(&seg);
+
+        /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
+        segment_name(fn, sizeof(fn), id);
+
+        if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
+            pa_log_warn("Failed to remove SHM segment %s: %s", fn, pa_cstrerror(errno));
+    }
+
+    closedir(d);
+#endif /* SHM_PATH */
+#endif /* HAVE_SHM_OPEN */
+
+    return 0;
+}
diff --git a/src/pulsecore/shm.h b/src/pulsecore/shm.h
new file mode 100644 (file)
index 0000000..67a2114
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef foopulseshmhfoo
+#define foopulseshmhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/mem.h>
+
+typedef struct pa_shm {
+    pa_mem_type_t type;
+    unsigned id;
+    void *ptr;
+    size_t size;
+
+    /* Only for type = PA_MEM_TYPE_SHARED_POSIX */
+    bool do_unlink:1;
+
+    /* Only for type = PA_MEM_TYPE_SHARED_MEMFD
+     *
+     * To avoid fd leaks, we keep this fd open only until we pass it
+     * to the other PA endpoint over unix domain socket.
+     *
+     * When we don't have ownership for the memfd fd in question (e.g.
+     * pa_shm_attach()), or the file descriptor has now been closed,
+     * this is set to -1.
+     *
+     * For the special case of a global mempool, we keep this fd
+     * always open. Check comments on top of pa_mempool_new() for
+     * rationale. */
+    int fd;
+} pa_shm;
+
+int pa_shm_create_rw(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode);
+int pa_shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable);
+
+void pa_shm_punch(pa_shm *m, size_t offset, size_t size);
+
+void pa_shm_free(pa_shm *m);
+
+int pa_shm_cleanup(void);
+
+#endif
diff --git a/src/pulsecore/shmasyncq.c b/src/pulsecore/shmasyncq.c
new file mode 100644 (file)
index 0000000..727cca7
--- /dev/null
@@ -0,0 +1,218 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulse/xmalloc.h>
+
+#include "fdsem.h"
+
+/* For debugging purposes we can define _Y to put and extra thread
+ * yield between each operation. */
+
+/* #define PROFILE */
+
+#ifdef PROFILE
+#define _Y pa_thread_yield()
+#else
+#define _Y do { } while(0)
+#endif
+
+struct pa_shmasyncq {
+    pa_fdsem *read_fdsem, *write_fdsem;
+    pa_shmasyncq_data *data;
+};
+
+static int is_power_of_two(unsigned size) {
+    return !(size & (size - 1));
+}
+
+static int reduce(pa_shmasyncq *l, int value) {
+    return value & (unsigned) (l->n_elements - 1);
+}
+
+static pa_atomic_t* get_cell(pa_shmasyncq *l, unsigned i) {
+    pa_assert(i < l->data->n_elements);
+
+    return (pa_atomic_t*) ((uint8*t) l->data + PA_ALIGN(sizeof(pa_shmasyncq_data)) + i * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))
+}
+
+static void *get_cell_data(pa_atomic_t *a) {
+    return (uint8_t*) a + PA_ALIGN(sizeof(atomic_t));
+}
+
+pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]) {
+    pa_shmasyncq *l;
+
+    pa_assert(n_elements > 0);
+    pa_assert(is_power_of_two(n_elements));
+    pa_assert(element_size > 0);
+    pa_assert(data);
+    pa_assert(fd);
+
+    l = pa_xnew(pa_shmasyncq, 1);
+
+    l->data = data;
+    memset(data, 0, PA_SHMASYNCQ_SIZE(n_elements, element_size));
+
+    l->data->n_elements = n_elements;
+    l->data->element_size = element_size;
+
+    if (!(l->read_fdsem = pa_fdsem_new_shm(&d->read_fdsem_data))) {
+        pa_xfree(l);
+        return NULL;
+    }
+    fd[0] = pa_fdsem_get(l->read_fdsem);
+
+    if (!(l->write_fdsem = pa_fdsem_new(&d->write_fdsem_data, &fd[1]))) {
+        pa_fdsem_free(l->read_fdsem);
+        pa_xfree(l);
+        return NULL;
+    }
+
+    return l;
+}
+
+void pa_shmasyncq_free(pa_shmasyncq *l, pa_free_cb_t free_cb) {
+    pa_assert(l);
+
+    if (free_cb) {
+        void *p;
+
+        while ((p = pa_shmasyncq_pop(l, 0)))
+            free_cb(p);
+    }
+
+    pa_fdsem_free(l->read_fdsem);
+    pa_fdsem_free(l->write_fdsem);
+    pa_xfree(l);
+}
+
+int pa_shmasyncq_push(pa_shmasyncq*l, void *p, int wait) {
+    int idx;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+    pa_assert(p);
+
+    cells = PA_SHMASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->write_idx);
+
+    if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) {
+
+        if (!wait)
+            return -1;
+
+/*         pa_log("sleeping on push"); */
+
+        do {
+            pa_fdsem_wait(l->read_fdsem);
+        } while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p));
+    }
+
+    _Y;
+    l->write_idx++;
+
+    pa_fdsem_post(l->write_fdsem);
+
+    return 0;
+}
+
+void* pa_shmasyncq_pop(pa_shmasyncq*l, int wait) {
+    int idx;
+    void *ret;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+
+    cells = PA_SHMASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->read_idx);
+
+    if (!(ret = pa_atomic_ptr_load(&cells[idx]))) {
+
+        if (!wait)
+            return NULL;
+
+/*         pa_log("sleeping on pop"); */
+
+        do {
+            pa_fdsem_wait(l->write_fdsem);
+        } while (!(ret = pa_atomic_ptr_load(&cells[idx])));
+    }
+
+    pa_assert(ret);
+
+    /* Guaranteed to succeed if we only have a single reader */
+    pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL));
+
+    _Y;
+    l->read_idx++;
+
+    pa_fdsem_post(l->read_fdsem);
+
+    return ret;
+}
+
+int pa_shmasyncq_get_fd(pa_shmasyncq *q) {
+    pa_assert(q);
+
+    return pa_fdsem_get(q->write_fdsem);
+}
+
+int pa_shmasyncq_before_poll(pa_shmasyncq *l) {
+    int idx;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+
+    cells = PA_SHMASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->read_idx);
+
+    for (;;) {
+        if (pa_atomic_ptr_load(&cells[idx]))
+            return -1;
+
+        if (pa_fdsem_before_poll(l->write_fdsem) >= 0)
+            return 0;
+    }
+
+    return 0;
+}
+
+void pa_shmasyncq_after_poll(pa_shmasyncq *l) {
+    pa_assert(l);
+
+    pa_fdsem_after_poll(l->write_fdsem);
+}
diff --git a/src/pulsecore/shmasyncq.h b/src/pulsecore/shmasyncq.h
new file mode 100644 (file)
index 0000000..676fe2a
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef foopulseshmasyncqhfoo
+#define foopulseshmasyncqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/macro.h>
+
+/* Similar to pa_asyncq, but stores data in a shared memory segment */
+
+struct pa_shmasyncq_data {
+    unsigned n_elements;
+    size_t element_size;
+    unsigned read_idx;
+    unsigned write_idx;
+    pa_fdsem_data read_fdsem_data, write_fdsem_data;
+};
+
+#define PA_SHMASYNCQ_DEFAULT_N_ELEMENTS 128
+#define PA_SHMASYNCQ_SIZE(n_elements, element_size) (PA_ALIGN(sizeof(pa_shmasyncq_data)) + (((n_elements) * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))))
+#define PA_SHMASYNCQ_DEFAULT_SIZE(element_size) PA_SHMASYNCQ_SIZE(PA_SHMASYNCQ_DEFAULT_N_ELEMENTS, element_size)
+
+typedef struct pa_shmasyncq pa_shmasyncq;
+
+pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]);
+void pa_shmasyncq_free(pa_shmasyncq* q, pa_free_cb_t free_cb);
+
+void* pa_shmasyncq_pop_begin(pa_shmasyncq *q, bool wait);
+void pa_shmasyncq_pop_commit(pa_shmasyncq *q);
+
+int* pa_shmasyncq_push_begin(pa_shmasyncq *q, bool wait);
+void pa_shmasyncq_push_commit(pa_shmasyncq *q);
+
+int pa_shmasyncq_get_fd(pa_shmasyncq *q);
+int pa_shmasyncq_before_poll(pa_shmasyncq *a);
+void pa_shmasyncq_after_poll(pa_shmasyncq *a);
+
+#endif
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
new file mode 100644 (file)
index 0000000..4155b69
--- /dev/null
@@ -0,0 +1,2420 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulse/internal.h>
+
+#include <pulsecore/core-format.h>
+#include <pulsecore/mix.h>
+#include <pulsecore/stream-util.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/play-memblockq.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+
+#include "sink-input.h"
+
+/* #define SINK_INPUT_DEBUG */
+
+#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
+#define CONVERT_BUFFER_LENGTH (pa_page_size())
+
+PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject);
+
+struct volume_factor_entry {
+    char *key;
+    pa_cvolume volume;
+};
+
+static struct volume_factor_entry *volume_factor_entry_new(const char *key, const pa_cvolume *volume) {
+    struct volume_factor_entry *entry;
+
+    pa_assert(key);
+    pa_assert(volume);
+
+    entry = pa_xnew(struct volume_factor_entry, 1);
+    entry->key = pa_xstrdup(key);
+
+    entry->volume = *volume;
+
+    return entry;
+}
+
+static void volume_factor_entry_free(struct volume_factor_entry *volume_entry) {
+    pa_assert(volume_entry);
+
+    pa_xfree(volume_entry->key);
+    pa_xfree(volume_entry);
+}
+
+static void volume_factor_from_hashmap(pa_cvolume *v, pa_hashmap *items, uint8_t channels) {
+    struct volume_factor_entry *entry;
+    void *state = NULL;
+
+    pa_cvolume_reset(v, channels);
+    PA_HASHMAP_FOREACH(entry, items, state)
+        pa_sw_cvolume_multiply(v, v, &entry->volume);
+}
+
+static void sink_input_free(pa_object *o);
+static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
+
+static int check_passthrough_connection(bool passthrough, pa_sink *dest) {
+    if (pa_sink_is_passthrough(dest)) {
+        pa_log_warn("Sink is already connected to PASSTHROUGH input");
+        return -PA_ERR_BUSY;
+    }
+
+    /* If current input(s) exist, check new input is not PASSTHROUGH */
+    if (pa_idxset_size(dest->inputs) > 0 && passthrough) {
+        pa_log_warn("Sink is already connected, cannot accept new PASSTHROUGH INPUT");
+        return -PA_ERR_BUSY;
+    }
+
+    return PA_OK;
+}
+
+pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) {
+    pa_assert(data);
+
+    pa_zero(*data);
+    data->resample_method = PA_RESAMPLER_INVALID;
+    data->proplist = pa_proplist_new();
+    data->volume_writable = true;
+
+    data->volume_factor_items = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
+                                                    (pa_free_cb_t) volume_factor_entry_free);
+    data->volume_factor_sink_items = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
+                                                         (pa_free_cb_t) volume_factor_entry_free);
+
+    return data;
+}
+
+void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) {
+    pa_assert(data);
+
+    if ((data->sample_spec_is_set = !!spec))
+        data->sample_spec = *spec;
+}
+
+void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map) {
+    pa_assert(data);
+
+    if ((data->channel_map_is_set = !!map))
+        data->channel_map = *map;
+}
+
+bool pa_sink_input_new_data_is_passthrough(pa_sink_input_new_data *data) {
+    pa_assert(data);
+
+    if (PA_LIKELY(data->format) && PA_UNLIKELY(!pa_format_info_is_pcm(data->format)))
+        return true;
+
+    if (PA_UNLIKELY(data->flags & PA_SINK_INPUT_PASSTHROUGH))
+        return true;
+
+    return false;
+}
+
+void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
+    pa_assert(data);
+    pa_assert(data->volume_writable);
+
+    if ((data->volume_is_set = !!volume))
+        data->volume = *volume;
+}
+
+void pa_sink_input_new_data_add_volume_factor(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor) {
+    struct volume_factor_entry *v;
+
+    pa_assert(data);
+    pa_assert(key);
+    pa_assert(volume_factor);
+
+    v = volume_factor_entry_new(key, volume_factor);
+    pa_assert_se(pa_hashmap_put(data->volume_factor_items, v->key, v) >= 0);
+}
+
+void pa_sink_input_new_data_add_volume_factor_sink(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor) {
+    struct volume_factor_entry *v;
+
+    pa_assert(data);
+    pa_assert(key);
+    pa_assert(volume_factor);
+
+    v = volume_factor_entry_new(key, volume_factor);
+    pa_assert_se(pa_hashmap_put(data->volume_factor_sink_items, v->key, v) >= 0);
+}
+
+void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, bool mute) {
+    pa_assert(data);
+
+    data->muted_is_set = true;
+    data->muted = mute;
+}
+
+bool pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, bool save) {
+    bool ret = true;
+    pa_idxset *formats = NULL;
+
+    pa_assert(data);
+    pa_assert(s);
+
+    if (!data->req_formats) {
+        /* We're not working with the extended API */
+        data->sink = s;
+        data->save_sink = save;
+    } else {
+        /* Extended API: let's see if this sink supports the formats the client can provide */
+        formats = pa_sink_check_formats(s, data->req_formats);
+
+        if (formats && !pa_idxset_isempty(formats)) {
+            /* Sink supports at least one of the requested formats */
+            data->sink = s;
+            data->save_sink = save;
+            if (data->nego_formats)
+                pa_idxset_free(data->nego_formats, (pa_free_cb_t) pa_format_info_free);
+            data->nego_formats = formats;
+        } else {
+            /* Sink doesn't support any of the formats requested by the client */
+            if (formats)
+                pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+            ret = false;
+        }
+    }
+
+    return ret;
+}
+
+bool pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset *formats) {
+    pa_assert(data);
+    pa_assert(formats);
+
+    if (data->req_formats)
+        pa_idxset_free(data->req_formats, (pa_free_cb_t) pa_format_info_free);
+
+    data->req_formats = formats;
+
+    if (data->sink) {
+        /* Trigger format negotiation */
+        return pa_sink_input_new_data_set_sink(data, data->sink, data->save_sink);
+    }
+
+    return true;
+}
+
+void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
+    pa_assert(data);
+
+    if (data->req_formats)
+        pa_idxset_free(data->req_formats, (pa_free_cb_t) pa_format_info_free);
+
+    if (data->nego_formats)
+        pa_idxset_free(data->nego_formats, (pa_free_cb_t) pa_format_info_free);
+
+    if (data->format)
+        pa_format_info_free(data->format);
+
+    if (data->volume_factor_items)
+        pa_hashmap_free(data->volume_factor_items);
+
+    if (data->volume_factor_sink_items)
+        pa_hashmap_free(data->volume_factor_sink_items);
+
+    pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_sink_input *i) {
+    pa_assert(i);
+
+    i->pop = NULL;
+    i->process_underrun = NULL;
+    i->process_rewind = NULL;
+    i->update_max_rewind = NULL;
+    i->update_max_request = NULL;
+    i->update_sink_requested_latency = NULL;
+    i->update_sink_latency_range = NULL;
+    i->update_sink_fixed_latency = NULL;
+    i->attach = NULL;
+    i->detach = NULL;
+    i->suspend = NULL;
+    i->suspend_within_thread = NULL;
+    i->moving = NULL;
+    i->kill = NULL;
+    i->get_latency = NULL;
+    i->state_change = NULL;
+    i->may_move_to = NULL;
+    i->send_event = NULL;
+    i->volume_changed = NULL;
+    i->mute_changed = NULL;
+}
+
+/* Called from main context */
+int pa_sink_input_new(
+        pa_sink_input **_i,
+        pa_core *core,
+        pa_sink_input_new_data *data) {
+
+    pa_sink_input *i;
+    pa_resampler *resampler = NULL;
+    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], fmt[PA_FORMAT_INFO_SNPRINT_MAX];
+    pa_channel_map volume_map;
+    int r;
+    char *pt;
+    char *memblockq_name;
+
+    pa_assert(_i);
+    pa_assert(core);
+    pa_assert(data);
+    pa_assert_ctl_context();
+
+    if (data->client)
+        pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
+
+    if (data->origin_sink && (data->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
+        data->volume_writable = false;
+
+    if (!data->req_formats) {
+        /* From this point on, we want to work only with formats, and get back
+         * to using the sample spec and channel map after all decisions w.r.t.
+         * routing are complete. */
+        pa_format_info *f;
+        pa_idxset *formats;
+
+        f = pa_format_info_from_sample_spec2(&data->sample_spec, data->channel_map_is_set ? &data->channel_map : NULL,
+                                             !(data->flags & PA_SINK_INPUT_FIX_FORMAT),
+                                             !(data->flags & PA_SINK_INPUT_FIX_RATE),
+                                             !(data->flags & PA_SINK_INPUT_FIX_CHANNELS));
+        if (!f)
+            return -PA_ERR_INVALID;
+
+        formats = pa_idxset_new(NULL, NULL);
+        pa_idxset_put(formats, f, NULL);
+        pa_sink_input_new_data_set_formats(data, formats);
+    }
+
+    if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0)
+        return r;
+
+    pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
+
+    if (!data->sink) {
+        pa_sink *sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK);
+        pa_return_val_if_fail(sink, -PA_ERR_NOENTITY);
+        pa_sink_input_new_data_set_sink(data, sink, false);
+    }
+
+    /* If something didn't pick a format for us, pick the top-most format since
+     * we assume this is sorted in priority order */
+    if (!data->format && data->nego_formats && !pa_idxset_isempty(data->nego_formats))
+        data->format = pa_format_info_copy(pa_idxset_first(data->nego_formats, NULL));
+
+    if (PA_LIKELY(data->format)) {
+        pa_log_debug("Negotiated format: %s", pa_format_info_snprint(fmt, sizeof(fmt), data->format));
+    } else {
+        pa_format_info *format;
+        uint32_t idx;
+
+        pa_log_info("Sink does not support any requested format:");
+        PA_IDXSET_FOREACH(format, data->req_formats, idx)
+            pa_log_info(" -- %s", pa_format_info_snprint(fmt, sizeof(fmt), format));
+
+        return -PA_ERR_NOTSUPPORTED;
+    }
+
+    pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE);
+    pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink
+                                               && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED),
+                          -PA_ERR_INVALID);
+
+    /* Routing is done. We have a sink and a format. */
+
+    if (data->volume_is_set && pa_format_info_is_pcm(data->format)) {
+        /* If volume is set, we need to save the original data->channel_map,
+         * so that we can remap the volume from the original channel map to the
+         * final channel map of the stream in case data->channel_map gets
+         * modified in pa_format_info_to_sample_spec2(). */
+        r = pa_stream_get_volume_channel_map(&data->volume, data->channel_map_is_set ? &data->channel_map : NULL, data->format, &volume_map);
+        if (r < 0)
+            return r;
+    }
+
+    /* Now populate the sample spec and channel map according to the final
+     * format that we've negotiated */
+    r = pa_format_info_to_sample_spec2(data->format, &data->sample_spec, &data->channel_map, &data->sink->sample_spec,
+                                       &data->sink->channel_map);
+    if (r < 0)
+        return r;
+
+    r = check_passthrough_connection(pa_sink_input_new_data_is_passthrough(data), data->sink);
+    if (r != PA_OK)
+        return r;
+
+    /* Don't restore (or save) stream volume for passthrough streams and
+     * prevent attenuation/gain */
+    if (pa_sink_input_new_data_is_passthrough(data)) {
+        data->volume_is_set = true;
+        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+        data->volume_is_absolute = true;
+        data->save_volume = false;
+    }
+
+    if (!data->volume_is_set) {
+        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+        data->volume_is_absolute = false;
+        data->save_volume = false;
+    }
+
+    if (!data->volume_writable)
+        data->save_volume = false;
+
+    if (data->volume_is_set)
+        /* The original volume channel map may be different than the final
+         * stream channel map, so remapping may be needed. */
+        pa_cvolume_remap(&data->volume, &volume_map, &data->channel_map);
+
+    if (!data->muted_is_set)
+        data->muted = false;
+
+    if (!(data->flags & PA_SINK_INPUT_VARIABLE_RATE) &&
+        !pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec)) {
+        /* try to change sink rate. This is done before the FIXATE hook since
+           module-suspend-on-idle can resume a sink */
+
+        pa_log_info("Trying to change sample rate");
+        if (pa_sink_update_rate(data->sink, data->sample_spec.rate, pa_sink_input_new_data_is_passthrough(data)) >= 0)
+            pa_log_info("Rate changed to %u Hz", data->sink->sample_spec.rate);
+    }
+
+    if (pa_sink_input_new_data_is_passthrough(data) &&
+        !pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec)) {
+        /* rate update failed, or other parts of sample spec didn't match */
+
+        pa_log_debug("Could not update sink sample spec to match passthrough stream");
+        return -PA_ERR_NOTSUPPORTED;
+    }
+
+    if (data->resample_method == PA_RESAMPLER_INVALID)
+        data->resample_method = core->resample_method;
+
+    pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID);
+
+    if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0)
+        return r;
+
+    if ((data->flags & PA_SINK_INPUT_NO_CREATE_ON_SUSPEND) &&
+        pa_sink_get_state(data->sink) == PA_SINK_SUSPENDED) {
+        pa_log_warn("Failed to create sink input: sink is suspended.");
+        return -PA_ERR_BADSTATE;
+    }
+
+    if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
+        pa_log_warn("Failed to create sink input: too many inputs per sink.");
+        return -PA_ERR_TOOLARGE;
+    }
+
+    if ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ||
+        !pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec) ||
+        !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) {
+
+        /* Note: for passthrough content we need to adjust the output rate to that of the current sink-input */
+        if (!pa_sink_input_new_data_is_passthrough(data)) /* no resampler for passthrough content */
+            if (!(resampler = pa_resampler_new(
+                          core->mempool,
+                          &data->sample_spec, &data->channel_map,
+                          &data->sink->sample_spec, &data->sink->channel_map,
+                          core->lfe_crossover_freq,
+                          data->resample_method,
+                          ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+                          ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+                          (core->disable_remixing || (data->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+                          (core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) |
+                          (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
+                pa_log_warn("Unsupported resampling operation.");
+                return -PA_ERR_NOTSUPPORTED;
+            }
+    }
+
+    i = pa_msgobject_new(pa_sink_input);
+    i->parent.parent.free = sink_input_free;
+    i->parent.process_msg = pa_sink_input_process_msg;
+
+    i->core = core;
+    i->state = PA_SINK_INPUT_INIT;
+    i->flags = data->flags;
+    i->proplist = pa_proplist_copy(data->proplist);
+    i->driver = pa_xstrdup(pa_path_get_filename(data->driver));
+    i->module = data->module;
+    i->sink = data->sink;
+    i->origin_sink = data->origin_sink;
+    i->client = data->client;
+
+    i->requested_resample_method = data->resample_method;
+    i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
+    i->sample_spec = data->sample_spec;
+    i->channel_map = data->channel_map;
+    i->format = pa_format_info_copy(data->format);
+
+    if (!data->volume_is_absolute && pa_sink_flat_volume_enabled(i->sink)) {
+        pa_cvolume remapped;
+
+        /* When the 'absolute' bool is not set then we'll treat the volume
+         * as relative to the sink volume even in flat volume mode */
+        remapped = data->sink->reference_volume;
+        pa_cvolume_remap(&remapped, &data->sink->channel_map, &data->channel_map);
+        pa_sw_cvolume_multiply(&i->volume, &data->volume, &remapped);
+    } else
+        i->volume = data->volume;
+
+    i->volume_factor_items = data->volume_factor_items;
+    data->volume_factor_items = NULL;
+    volume_factor_from_hashmap(&i->volume_factor, i->volume_factor_items, i->sample_spec.channels);
+
+    i->volume_factor_sink_items = data->volume_factor_sink_items;
+    data->volume_factor_sink_items = NULL;
+    volume_factor_from_hashmap(&i->volume_factor_sink, i->volume_factor_sink_items, i->sink->sample_spec.channels);
+
+    i->real_ratio = i->reference_ratio = data->volume;
+    pa_cvolume_reset(&i->soft_volume, i->sample_spec.channels);
+    pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels);
+    i->volume_writable = data->volume_writable;
+    i->save_volume = data->save_volume;
+    i->save_sink = data->save_sink;
+    i->save_muted = data->save_muted;
+
+    i->muted = data->muted;
+
+    if (data->sync_base) {
+        i->sync_next = data->sync_base->sync_next;
+        i->sync_prev = data->sync_base;
+
+        if (data->sync_base->sync_next)
+            data->sync_base->sync_next->sync_prev = i;
+        data->sync_base->sync_next = i;
+    } else
+        i->sync_next = i->sync_prev = NULL;
+
+    i->direct_outputs = pa_idxset_new(NULL, NULL);
+
+    reset_callbacks(i);
+    i->userdata = NULL;
+
+    i->thread_info.state = i->state;
+    i->thread_info.attached = false;
+    pa_atomic_store(&i->thread_info.drained, 1);
+    i->thread_info.sample_spec = i->sample_spec;
+    i->thread_info.resampler = resampler;
+    i->thread_info.soft_volume = i->soft_volume;
+    i->thread_info.muted = i->muted;
+    i->thread_info.requested_sink_latency = (pa_usec_t) -1;
+    i->thread_info.rewrite_nbytes = 0;
+    i->thread_info.rewrite_flush = false;
+    i->thread_info.dont_rewind_render = false;
+    i->thread_info.underrun_for = (uint64_t) -1;
+    i->thread_info.underrun_for_sink = 0;
+    i->thread_info.playing_for = 0;
+    i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0);
+    pa_assert_se(pa_idxset_put(i->sink->inputs, pa_sink_input_ref(i), NULL) == 0);
+
+    if (i->client)
+        pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0);
+
+    memblockq_name = pa_sprintf_malloc("sink input render_memblockq [%u]", i->index);
+    i->thread_info.render_memblockq = pa_memblockq_new(
+            memblockq_name,
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            0,
+            &i->sink->sample_spec,
+            0,
+            1,
+            0,
+            &i->sink->silence);
+    pa_xfree(memblockq_name);
+
+    pt = pa_proplist_to_string_sep(i->proplist, "\n    ");
+    pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s\n    %s",
+                i->index,
+                pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)),
+                i->sink->name,
+                pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec),
+                pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+                pt);
+    pa_xfree(pt);
+
+    /* Don't forget to call pa_sink_input_put! */
+
+    *_i = i;
+    return 0;
+}
+
+/* Called from main context */
+static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
+    pa_assert(i);
+    pa_assert_ctl_context();
+
+    if (!i->sink)
+        return;
+
+    if (i->state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED)
+        pa_assert_se(i->sink->n_corked -- >= 1);
+    else if (i->state != PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_CORKED)
+        i->sink->n_corked++;
+}
+
+/* Called from main context */
+static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
+    pa_sink_input *ssync;
+    pa_assert(i);
+    pa_assert_ctl_context();
+
+    if (state == PA_SINK_INPUT_DRAINED)
+        state = PA_SINK_INPUT_RUNNING;
+
+    if (i->state == state)
+        return;
+
+    if (i->sink) {
+        if (i->state == PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_RUNNING && pa_sink_used_by(i->sink) == 0 &&
+            !pa_sample_spec_equal(&i->sample_spec, &i->sink->sample_spec)) {
+            /* We were uncorked and the sink was not playing anything -- let's try
+             * to update the sample rate to avoid resampling */
+            pa_sink_update_rate(i->sink, i->sample_spec.rate, pa_sink_input_is_passthrough(i));
+        }
+
+        pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+    } else {
+        /* If the sink is not valid, pa_sink_input_set_state_within_thread() must be called directly */
+
+        pa_sink_input_set_state_within_thread(i, state);
+
+        for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev)
+            pa_sink_input_set_state_within_thread(ssync, state);
+
+        for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next)
+            pa_sink_input_set_state_within_thread(ssync, state);
+    }
+
+    update_n_corked(i, state);
+    i->state = state;
+
+    for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev) {
+        update_n_corked(ssync, state);
+        ssync->state = state;
+    }
+    for (ssync = i->sync_next; ssync; ssync = ssync->sync_next) {
+        update_n_corked(ssync, state);
+        ssync->state = state;
+    }
+
+    if (state != PA_SINK_INPUT_UNLINKED) {
+        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i);
+
+        for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev)
+            pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+
+        for (ssync = i->sync_next; ssync; ssync = ssync->sync_next)
+            pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+
+        if (PA_SINK_INPUT_IS_LINKED(state))
+            pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    }
+
+    if (i->sink)
+        pa_sink_update_status(i->sink);
+}
+
+/* Called from main context */
+void pa_sink_input_unlink(pa_sink_input *i) {
+    bool linked;
+    pa_source_output *o, PA_UNUSED *p = NULL;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+
+    /* See pa_sink_unlink() for a couple of comments how this function
+     * works */
+
+    pa_sink_input_ref(i);
+
+    linked = PA_SINK_INPUT_IS_LINKED(i->state);
+
+    if (linked)
+        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
+
+    if (i->sync_prev)
+        i->sync_prev->sync_next = i->sync_next;
+    if (i->sync_next)
+        i->sync_next->sync_prev = i->sync_prev;
+
+    i->sync_prev = i->sync_next = NULL;
+
+    pa_idxset_remove_by_data(i->core->sink_inputs, i, NULL);
+
+    if (i->sink)
+        if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
+            pa_sink_input_unref(i);
+
+    if (i->client)
+        pa_idxset_remove_by_data(i->client->sink_inputs, i, NULL);
+
+    while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
+        pa_assert(o != p);
+        pa_source_output_kill(o);
+        p = o;
+    }
+
+    update_n_corked(i, PA_SINK_INPUT_UNLINKED);
+    i->state = PA_SINK_INPUT_UNLINKED;
+
+    if (linked && i->sink) {
+        if (pa_sink_input_is_passthrough(i))
+            pa_sink_leave_passthrough(i->sink);
+
+        /* We might need to update the sink's volume if we are in flat volume mode. */
+        if (pa_sink_flat_volume_enabled(i->sink))
+            pa_sink_set_volume(i->sink, NULL, false, false);
+
+        if (i->sink->asyncmsgq)
+            pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
+    }
+
+    reset_callbacks(i);
+
+    if (i->sink) {
+        if (PA_SINK_IS_LINKED(pa_sink_get_state(i->sink)))
+            pa_sink_update_status(i->sink);
+
+        i->sink = NULL;
+    }
+
+    if (linked) {
+        pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
+        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i);
+    }
+
+    pa_core_maybe_vacuum(i->core);
+
+    pa_sink_input_unref(i);
+}
+
+/* Called from main context */
+static void sink_input_free(pa_object *o) {
+    pa_sink_input* i = PA_SINK_INPUT(o);
+
+    pa_assert(i);
+    pa_assert_ctl_context();
+    pa_assert(pa_sink_input_refcnt(i) == 0);
+    pa_assert(!PA_SINK_INPUT_IS_LINKED(i->state));
+
+    pa_log_info("Freeing input %u \"%s\"", i->index,
+                i->proplist ? pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)) : "");
+
+    /* Side note: this function must be able to destruct properly any
+     * kind of sink input in any state, even those which are
+     * "half-moved" or are connected to sinks that have no asyncmsgq
+     * and are hence half-destructed themselves! */
+
+    if (i->thread_info.render_memblockq)
+        pa_memblockq_free(i->thread_info.render_memblockq);
+
+    if (i->thread_info.resampler)
+        pa_resampler_free(i->thread_info.resampler);
+
+    if (i->format)
+        pa_format_info_free(i->format);
+
+    if (i->proplist)
+        pa_proplist_free(i->proplist);
+
+    if (i->direct_outputs)
+        pa_idxset_free(i->direct_outputs, NULL);
+
+    if (i->thread_info.direct_outputs)
+        pa_hashmap_free(i->thread_info.direct_outputs);
+
+    if (i->volume_factor_items)
+        pa_hashmap_free(i->volume_factor_items);
+
+    if (i->volume_factor_sink_items)
+        pa_hashmap_free(i->volume_factor_sink_items);
+
+    pa_xfree(i->driver);
+    pa_xfree(i);
+}
+
+/* Called from main context */
+void pa_sink_input_put(pa_sink_input *i) {
+    pa_sink_input_state_t state;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+
+    pa_assert(i->state == PA_SINK_INPUT_INIT);
+
+    /* The following fields must be initialized properly */
+    pa_assert(i->pop);
+    pa_assert(i->process_rewind);
+    pa_assert(i->kill);
+
+    state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
+
+    update_n_corked(i, state);
+    i->state = state;
+
+    /* We might need to update the sink's volume if we are in flat volume mode. */
+    if (pa_sink_flat_volume_enabled(i->sink))
+        pa_sink_set_volume(i->sink, NULL, false, i->save_volume);
+    else {
+        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
+            pa_assert(pa_cvolume_is_norm(&i->volume));
+            pa_assert(pa_cvolume_is_norm(&i->reference_ratio));
+        }
+
+        set_real_ratio(i, &i->volume);
+    }
+
+    if (pa_sink_input_is_passthrough(i))
+        pa_sink_enter_passthrough(i->sink);
+
+    i->thread_info.soft_volume = i->soft_volume;
+    i->thread_info.muted = i->muted;
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0);
+
+    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
+    pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i);
+
+    pa_sink_update_status(i->sink);
+}
+
+/* Called from main context */
+void pa_sink_input_kill(pa_sink_input*i) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    i->kill(i);
+}
+
+/* Called from main context */
+pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) {
+    pa_usec_t r[2] = { 0, 0 };
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
+
+    if (i->get_latency)
+        r[0] += i->get_latency(i);
+
+    if (sink_latency)
+        *sink_latency = r[1];
+
+    return r[0];
+}
+
+/* Called from thread context */
+void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa_memchunk *chunk, pa_cvolume *volume) {
+    bool do_volume_adj_here, need_volume_factor_sink;
+    bool volume_is_norm;
+    size_t block_size_max_sink, block_size_max_sink_input;
+    size_t ilength;
+    size_t ilength_full;
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+    pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec));
+    pa_assert(chunk);
+    pa_assert(volume);
+
+#ifdef SINK_INPUT_DEBUG
+    pa_log_debug("peek");
+#endif
+
+    block_size_max_sink_input = i->thread_info.resampler ?
+        pa_resampler_max_block_size(i->thread_info.resampler) :
+        pa_frame_align(pa_mempool_block_size_max(i->core->mempool), &i->sample_spec);
+
+    block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->core->mempool), &i->sink->sample_spec);
+
+    /* Default buffer size */
+    if (slength <= 0)
+        slength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sink->sample_spec);
+
+    if (slength > block_size_max_sink)
+        slength = block_size_max_sink;
+
+    if (i->thread_info.resampler) {
+        ilength = pa_resampler_request(i->thread_info.resampler, slength);
+
+        if (ilength <= 0)
+            ilength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sample_spec);
+    } else
+        ilength = slength;
+
+    /* Length corresponding to slength (without limiting to
+     * block_size_max_sink_input). */
+    ilength_full = ilength;
+
+    if (ilength > block_size_max_sink_input)
+        ilength = block_size_max_sink_input;
+
+    /* If the channel maps of the sink and this stream differ, we need
+     * to adjust the volume *before* we resample. Otherwise we can do
+     * it after and leave it for the sink code */
+
+    do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map);
+    volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.muted;
+    need_volume_factor_sink = !pa_cvolume_is_norm(&i->volume_factor_sink);
+
+    while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) {
+        pa_memchunk tchunk;
+
+        /* There's nothing in our render queue. We need to fill it up
+         * with data from the implementor. */
+
+        if (i->thread_info.state == PA_SINK_INPUT_CORKED ||
+            i->pop(i, ilength, &tchunk) < 0) {
+
+            /* OK, we're corked or the implementor didn't give us any
+             * data, so let's just hand out silence */
+            pa_atomic_store(&i->thread_info.drained, 1);
+
+            pa_memblockq_seek(i->thread_info.render_memblockq, (int64_t) slength, PA_SEEK_RELATIVE, true);
+            i->thread_info.playing_for = 0;
+            if (i->thread_info.underrun_for != (uint64_t) -1) {
+                i->thread_info.underrun_for += ilength_full;
+                i->thread_info.underrun_for_sink += slength;
+            }
+            break;
+        }
+
+        pa_atomic_store(&i->thread_info.drained, 0);
+
+        pa_assert(tchunk.length > 0);
+        pa_assert(tchunk.memblock);
+
+        i->thread_info.underrun_for = 0;
+        i->thread_info.underrun_for_sink = 0;
+        i->thread_info.playing_for += tchunk.length;
+
+        while (tchunk.length > 0) {
+            pa_memchunk wchunk;
+            bool nvfs = need_volume_factor_sink;
+
+            wchunk = tchunk;
+            pa_memblock_ref(wchunk.memblock);
+
+            if (wchunk.length > block_size_max_sink_input)
+                wchunk.length = block_size_max_sink_input;
+
+            /* It might be necessary to adjust the volume here */
+            if (do_volume_adj_here && !volume_is_norm) {
+                pa_memchunk_make_writable(&wchunk, 0);
+
+                if (i->thread_info.muted) {
+                    pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec);
+                    nvfs = false;
+
+                } else if (!i->thread_info.resampler && nvfs) {
+                    pa_cvolume v;
+
+                    /* If we don't need a resampler we can merge the
+                     * post and the pre volume adjustment into one */
+
+                    pa_sw_cvolume_multiply(&v, &i->thread_info.soft_volume, &i->volume_factor_sink);
+                    pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &v);
+                    nvfs = false;
+
+                } else
+                    pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.soft_volume);
+            }
+
+            if (!i->thread_info.resampler) {
+
+                if (nvfs) {
+                    pa_memchunk_make_writable(&wchunk, 0);
+                    pa_volume_memchunk(&wchunk, &i->sink->sample_spec, &i->volume_factor_sink);
+                }
+
+                pa_memblockq_push_align(i->thread_info.render_memblockq, &wchunk);
+            } else {
+                pa_memchunk rchunk;
+                pa_resampler_run(i->thread_info.resampler, &wchunk, &rchunk);
+
+#ifdef SINK_INPUT_DEBUG
+                pa_log_debug("pushing %lu", (unsigned long) rchunk.length);
+#endif
+
+                if (rchunk.memblock) {
+
+                    if (nvfs) {
+                        pa_memchunk_make_writable(&rchunk, 0);
+                        pa_volume_memchunk(&rchunk, &i->sink->sample_spec, &i->volume_factor_sink);
+                    }
+
+                    pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk);
+                    pa_memblock_unref(rchunk.memblock);
+                }
+            }
+
+            pa_memblock_unref(wchunk.memblock);
+
+            tchunk.index += wchunk.length;
+            tchunk.length -= wchunk.length;
+        }
+
+        pa_memblock_unref(tchunk.memblock);
+    }
+
+    pa_assert_se(pa_memblockq_peek(i->thread_info.render_memblockq, chunk) >= 0);
+
+    pa_assert(chunk->length > 0);
+    pa_assert(chunk->memblock);
+
+#ifdef SINK_INPUT_DEBUG
+    pa_log_debug("peeking %lu", (unsigned long) chunk->length);
+#endif
+
+    if (chunk->length > block_size_max_sink)
+        chunk->length = block_size_max_sink;
+
+    /* Let's see if we had to apply the volume adjustment ourselves,
+     * or if this can be done by the sink for us */
+
+    if (do_volume_adj_here)
+        /* We had different channel maps, so we already did the adjustment */
+        pa_cvolume_reset(volume, i->sink->sample_spec.channels);
+    else if (i->thread_info.muted)
+        /* We've both the same channel map, so let's have the sink do the adjustment for us*/
+        pa_cvolume_mute(volume, i->sink->sample_spec.channels);
+    else
+        *volume = i->thread_info.soft_volume;
+}
+
+/* Called from thread context */
+void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+    pa_assert(nbytes > 0);
+
+#ifdef SINK_INPUT_DEBUG
+    pa_log_debug("dropping %lu", (unsigned long) nbytes);
+#endif
+
+    pa_memblockq_drop(i->thread_info.render_memblockq, nbytes);
+}
+
+/* Called from thread context */
+bool pa_sink_input_process_underrun(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+
+    if (pa_memblockq_is_readable(i->thread_info.render_memblockq))
+        return false;
+
+    if (i->process_underrun && i->process_underrun(i)) {
+        /* All valid data has been played back, so we can empty this queue. */
+        pa_memblockq_silence(i->thread_info.render_memblockq);
+        return true;
+    }
+    return false;
+}
+
+/* Called from thread context */
+void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
+    size_t lbq;
+    bool called = false;
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+
+#ifdef SINK_INPUT_DEBUG
+    pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes);
+#endif
+
+    lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
+
+    if (nbytes > 0 && !i->thread_info.dont_rewind_render) {
+        pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes);
+        pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes);
+    }
+
+    if (i->thread_info.rewrite_nbytes == (size_t) -1) {
+
+        /* We were asked to drop all buffered data, and rerequest new
+         * data from implementor the next time peek() is called */
+
+        pa_memblockq_flush_write(i->thread_info.render_memblockq, true);
+
+    } else if (i->thread_info.rewrite_nbytes > 0) {
+        size_t max_rewrite, amount;
+
+        /* Calculate how much make sense to rewrite at most */
+        max_rewrite = nbytes + lbq;
+
+        /* Transform into local domain */
+        if (i->thread_info.resampler)
+            max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite);
+
+        /* Calculate how much of the rewinded data should actually be rewritten */
+        amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite);
+
+        if (amount > 0) {
+            pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount);
+
+            /* Tell the implementor */
+            if (i->process_rewind)
+                i->process_rewind(i, amount);
+            called = true;
+
+            /* Convert back to sink domain */
+            if (i->thread_info.resampler)
+                amount = pa_resampler_result(i->thread_info.resampler, amount);
+
+            if (amount > 0)
+                /* Ok, now update the write pointer */
+                pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE, true);
+
+            if (i->thread_info.rewrite_flush)
+                pa_memblockq_silence(i->thread_info.render_memblockq);
+
+            /* And rewind the resampler */
+            if (i->thread_info.resampler)
+                pa_resampler_rewind(i->thread_info.resampler, amount);
+        }
+    }
+
+    if (!called)
+        if (i->process_rewind)
+            i->process_rewind(i, 0);
+
+    i->thread_info.rewrite_nbytes = 0;
+    i->thread_info.rewrite_flush = false;
+    i->thread_info.dont_rewind_render = false;
+}
+
+/* Called from thread context */
+size_t pa_sink_input_get_max_rewind(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+
+    return i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, i->sink->thread_info.max_rewind) : i->sink->thread_info.max_rewind;
+}
+
+/* Called from thread context */
+size_t pa_sink_input_get_max_request(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+
+    /* We're not verifying the status here, to allow this to be called
+     * in the state change handler between _INIT and _RUNNING */
+
+    return i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, i->sink->thread_info.max_request) : i->sink->thread_info.max_request;
+}
+
+/* Called from thread context */
+void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes  /* in the sink's sample spec */) {
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+
+    pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes);
+
+    if (i->update_max_rewind)
+        i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
+
+/* Called from thread context */
+void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes  /* in the sink's sample spec */) {
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+
+    if (i->update_max_request)
+        i->update_max_request(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
+
+/* Called from thread context */
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+
+    if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
+        usec = i->sink->thread_info.fixed_latency;
+
+    if (usec != (pa_usec_t) -1)
+        usec = PA_CLAMP(usec, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+
+    i->thread_info.requested_sink_latency = usec;
+    pa_sink_invalidate_requested_latency(i->sink, true);
+
+    return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+
+    if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
+        pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+        return usec;
+    }
+
+    /* If this sink input is not realized yet or we are being moved,
+     * we have to touch the thread info data directly */
+
+    if (i->sink) {
+        if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
+            usec = pa_sink_get_fixed_latency(i->sink);
+
+        if (usec != (pa_usec_t) -1) {
+            pa_usec_t min_latency, max_latency;
+            pa_sink_get_latency_range(i->sink, &min_latency, &max_latency);
+            usec = PA_CLAMP(usec, min_latency, max_latency);
+        }
+    }
+
+    i->thread_info.requested_sink_latency = usec;
+
+    return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+
+    if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
+        pa_usec_t usec = 0;
+        pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+        return usec;
+    }
+
+    /* If this sink input is not realized yet or we are being moved,
+     * we have to touch the thread info data directly */
+
+    return i->thread_info.requested_sink_latency;
+}
+
+/* Called from main context */
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool save, bool absolute) {
+    pa_cvolume v;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(volume);
+    pa_assert(pa_cvolume_valid(volume));
+    pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec));
+    pa_assert(i->volume_writable);
+
+    if (!absolute && pa_sink_flat_volume_enabled(i->sink)) {
+        v = i->sink->reference_volume;
+        pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
+
+        if (pa_cvolume_compatible(volume, &i->sample_spec))
+            volume = pa_sw_cvolume_multiply(&v, &v, volume);
+        else
+            volume = pa_sw_cvolume_multiply_scalar(&v, &v, pa_cvolume_max(volume));
+    } else {
+        if (!pa_cvolume_compatible(volume, &i->sample_spec)) {
+            v = i->volume;
+            volume = pa_cvolume_scale(&v, pa_cvolume_max(volume));
+        }
+    }
+
+    if (pa_cvolume_equal(volume, &i->volume)) {
+        i->save_volume = i->save_volume || save;
+        return;
+    }
+
+    pa_sink_input_set_volume_direct(i, volume);
+    i->save_volume = save;
+
+    if (pa_sink_flat_volume_enabled(i->sink)) {
+        /* We are in flat volume mode, so let's update all sink input
+         * volumes and update the flat volume of the sink */
+
+        pa_sink_set_volume(i->sink, NULL, true, save);
+
+    } else {
+        /* OK, we are in normal volume mode. The volume only affects
+         * ourselves */
+        set_real_ratio(i, volume);
+        pa_sink_input_set_reference_ratio(i, &i->volume);
+
+        /* Copy the new soft_volume to the thread_info struct */
+        pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
+    }
+}
+
+void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa_cvolume *volume_factor) {
+    struct volume_factor_entry *v;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(volume_factor);
+    pa_assert(key);
+    pa_assert(pa_cvolume_valid(volume_factor));
+    pa_assert(volume_factor->channels == 1 || pa_cvolume_compatible(volume_factor, &i->sample_spec));
+
+    v = volume_factor_entry_new(key, volume_factor);
+    if (!pa_cvolume_compatible(volume_factor, &i->sample_spec))
+        pa_cvolume_set(&v->volume, i->sample_spec.channels, volume_factor->values[0]);
+
+    pa_assert_se(pa_hashmap_put(i->volume_factor_items, v->key, v) >= 0);
+    if (pa_hashmap_size(i->volume_factor_items) == 1)
+        i->volume_factor = v->volume;
+    else
+        pa_sw_cvolume_multiply(&i->volume_factor, &i->volume_factor, &v->volume);
+
+    pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
+
+    /* Copy the new soft_volume to the thread_info struct */
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
+}
+
+/* Returns 0 if an entry was removed and -1 if no entry for the given key was
+ * found. */
+int pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key) {
+    struct volume_factor_entry *v;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(key);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    if (pa_hashmap_remove_and_free(i->volume_factor_items, key) < 0)
+        return -1;
+
+    switch (pa_hashmap_size(i->volume_factor_items)) {
+        case 0:
+            pa_cvolume_reset(&i->volume_factor, i->sample_spec.channels);
+            break;
+        case 1:
+            v = pa_hashmap_first(i->volume_factor_items);
+            i->volume_factor = v->volume;
+            break;
+        default:
+            volume_factor_from_hashmap(&i->volume_factor, i->volume_factor_items, i->volume_factor.channels);
+    }
+
+    pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
+
+    /* Copy the new soft_volume to the thread_info struct */
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
+
+    return 0;
+}
+
+/* Called from main context */
+static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec));
+
+    /* This basically calculates:
+     *
+     * i->real_ratio := v
+     * i->soft_volume := i->real_ratio * i->volume_factor */
+
+    if (v)
+        i->real_ratio = *v;
+    else
+        pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels);
+
+    pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
+    /* We don't copy the data to the thread_info data. That's left for someone else to do */
+}
+
+/* Called from main or I/O context */
+bool pa_sink_input_is_passthrough(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+
+    if (PA_UNLIKELY(!pa_format_info_is_pcm(i->format)))
+        return true;
+
+    if (PA_UNLIKELY(i->flags & PA_SINK_INPUT_PASSTHROUGH))
+        return true;
+
+    return false;
+}
+
+/* Called from main context */
+bool pa_sink_input_is_volume_readable(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+
+    return !pa_sink_input_is_passthrough(i);
+}
+
+/* Called from main context */
+pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool absolute) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(pa_sink_input_is_volume_readable(i));
+
+    if (absolute || !pa_sink_flat_volume_enabled(i->sink))
+        *volume = i->volume;
+    else
+        *volume = i->reference_ratio;
+
+    return volume;
+}
+
+/* Called from main context */
+void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) {
+    bool old_mute;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    old_mute = i->muted;
+
+    if (mute == old_mute) {
+        i->save_muted |= save;
+        return;
+    }
+
+    i->muted = mute;
+    pa_log_debug("The mute of sink input %u changed from %s to %s.", i->index, pa_yes_no(old_mute), pa_yes_no(mute));
+
+    i->save_muted = save;
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
+
+    /* The mute status changed, let's tell people so */
+    if (i->mute_changed)
+        i->mute_changed(i);
+
+    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], i);
+}
+
+void pa_sink_input_set_property(pa_sink_input *i, const char *key, const char *value) {
+    char *old_value = NULL;
+    const char *new_value;
+
+    pa_assert(i);
+    pa_assert(key);
+
+    if (pa_proplist_contains(i->proplist, key)) {
+        old_value = pa_xstrdup(pa_proplist_gets(i->proplist, key));
+        if (value && old_value && pa_streq(value, old_value))
+            goto finish;
+
+        if (!old_value)
+            old_value = pa_xstrdup("(data)");
+    } else {
+        if (!value)
+            goto finish;
+
+        old_value = pa_xstrdup("(unset)");
+    }
+
+    if (value) {
+        pa_proplist_sets(i->proplist, key, value);
+        new_value = value;
+    } else {
+        pa_proplist_unset(i->proplist, key);
+        new_value = "(unset)";
+    }
+
+    if (PA_SINK_INPUT_IS_LINKED(i->state)) {
+        pa_log_debug("Sink input %u: proplist[%s]: %s -> %s", i->index, key, old_value, new_value);
+        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+        pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    }
+
+finish:
+    pa_xfree(old_value);
+}
+
+void pa_sink_input_set_property_arbitrary(pa_sink_input *i, const char *key, const uint8_t *value, size_t nbytes) {
+    const uint8_t *old_value;
+    size_t old_nbytes;
+    const char *old_value_str;
+    const char *new_value_str;
+
+    pa_assert(i);
+    pa_assert(key);
+
+    if (pa_proplist_get(i->proplist, key, (const void **) &old_value, &old_nbytes) >= 0) {
+        if (value && nbytes == old_nbytes && !memcmp(value, old_value, nbytes))
+            return;
+
+        old_value_str = "(data)";
+
+    } else {
+        if (!value)
+            return;
+
+        old_value_str = "(unset)";
+    }
+
+    if (value) {
+        pa_proplist_set(i->proplist, key, value, nbytes);
+        new_value_str = "(data)";
+    } else {
+        pa_proplist_unset(i->proplist, key);
+        new_value_str = "(unset)";
+    }
+
+    if (PA_SINK_INPUT_IS_LINKED(i->state)) {
+        pa_log_debug("Sink input %u: proplist[%s]: %s -> %s", i->index, key, old_value_str, new_value_str);
+        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+        pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    }
+}
+
+/* Called from main thread */
+void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) {
+    void *state;
+    const char *key;
+    const uint8_t *value;
+    size_t nbytes;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(p);
+    pa_assert_ctl_context();
+
+    switch (mode) {
+        case PA_UPDATE_SET: {
+            /* Delete everything that is not in p. */
+            for (state = NULL; (key = pa_proplist_iterate(i->proplist, &state));) {
+                if (!pa_proplist_contains(p, key))
+                    pa_sink_input_set_property(i, key, NULL);
+            }
+
+            /* Fall through. */
+        }
+
+        case PA_UPDATE_REPLACE: {
+            for (state = NULL; (key = pa_proplist_iterate(p, &state));) {
+                pa_proplist_get(p, key, (const void **) &value, &nbytes);
+                pa_sink_input_set_property_arbitrary(i, key, value, nbytes);
+            }
+
+            break;
+        }
+
+        case PA_UPDATE_MERGE: {
+            for (state = NULL; (key = pa_proplist_iterate(p, &state));) {
+                if (pa_proplist_contains(i->proplist, key))
+                    continue;
+
+                pa_proplist_get(p, key, (const void **) &value, &nbytes);
+                pa_sink_input_set_property_arbitrary(i, key, value, nbytes);
+            }
+
+            break;
+        }
+    }
+}
+
+/* Called from main context */
+void pa_sink_input_cork(pa_sink_input *i, bool b) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
+}
+
+/* Called from main context */
+int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_return_val_if_fail(i->thread_info.resampler, -PA_ERR_BADSTATE);
+
+    if (i->sample_spec.rate == rate)
+        return 0;
+
+    i->sample_spec.rate = rate;
+
+    if (i->sink)
+        pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
+    else {
+        i->thread_info.sample_spec.rate = rate;
+        pa_resampler_set_input_rate(i->thread_info.resampler, rate);
+    }
+
+    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    return 0;
+}
+
+/* Called from main context */
+pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+
+    return i->actual_resample_method;
+}
+
+/* Called from main context */
+bool pa_sink_input_may_move(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    if (i->flags & PA_SINK_INPUT_DONT_MOVE)
+        return false;
+
+    if (i->sync_next || i->sync_prev) {
+        pa_log_warn("Moving synchronized streams not supported.");
+        return false;
+    }
+
+    return true;
+}
+
+static bool find_filter_sink_input(pa_sink_input *target, pa_sink *s) {
+    unsigned PA_UNUSED i = 0;
+    while (s && s->input_to_master) {
+        if (s->input_to_master == target)
+            return true;
+        s = s->input_to_master->sink;
+        pa_assert(i++ < 100);
+    }
+    return false;
+}
+
+static bool is_filter_sink_moving(pa_sink_input *i) {
+    pa_sink *sink = i->sink;
+
+    if (!sink)
+        return false;
+
+    while (sink->input_to_master) {
+        sink = sink->input_to_master->sink;
+
+        if (!sink)
+            return true;
+    }
+
+    return false;
+}
+
+/* Called from main context */
+bool pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_sink_assert_ref(dest);
+
+    if (dest == i->sink)
+        return true;
+
+    if (dest->unlink_requested)
+        return false;
+
+    if (!pa_sink_input_may_move(i))
+        return false;
+
+    /* Make sure we're not creating a filter sink cycle */
+    if (find_filter_sink_input(i, dest)) {
+        pa_log_debug("Can't connect input to %s, as that would create a cycle.", dest->name);
+        return false;
+    }
+
+    /* If this sink input is connected to a filter sink that itself is moving,
+     * then don't allow the move. Moving requires sending a message to the IO
+     * thread of the old sink, and if the old sink is a filter sink that is
+     * moving, there's no IO thread associated to the old sink. */
+    if (is_filter_sink_moving(i)) {
+        pa_log_debug("Can't move input from filter sink %s, because the filter sink itself is currently moving.",
+                     i->sink->name);
+        return false;
+    }
+
+    if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) {
+        pa_log_warn("Failed to move sink input: too many inputs per sink.");
+        return false;
+    }
+
+    if (check_passthrough_connection(pa_sink_input_is_passthrough(i), dest) < 0)
+        return false;
+
+    if (i->may_move_to)
+        if (!i->may_move_to(i, dest))
+            return false;
+
+    return true;
+}
+
+/* Called from main context */
+int pa_sink_input_start_move(pa_sink_input *i) {
+    pa_source_output *o, PA_UNUSED *p = NULL;
+    struct volume_factor_entry *v;
+    void *state = NULL;
+    int r;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(i->sink);
+
+    if (!pa_sink_input_may_move(i))
+        return -PA_ERR_NOTSUPPORTED;
+
+    if ((r = pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], i)) < 0)
+        return r;
+
+    pa_log_debug("Starting to move sink input %u from '%s'", (unsigned) i->index, i->sink->name);
+
+    /* Kill directly connected outputs */
+    while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
+        pa_assert(o != p);
+        pa_source_output_kill(o);
+        p = o;
+    }
+    pa_assert(pa_idxset_isempty(i->direct_outputs));
+
+    pa_idxset_remove_by_data(i->sink->inputs, i, NULL);
+
+    if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
+        pa_assert_se(i->sink->n_corked-- >= 1);
+
+    if (pa_sink_input_is_passthrough(i))
+        pa_sink_leave_passthrough(i->sink);
+
+    if (pa_sink_flat_volume_enabled(i->sink))
+        /* We might need to update the sink's volume if we are in flat
+         * volume mode. */
+        pa_sink_set_volume(i->sink, NULL, false, false);
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
+
+    pa_sink_update_status(i->sink);
+
+    PA_HASHMAP_FOREACH(v, i->volume_factor_sink_items, state)
+        pa_cvolume_remap(&v->volume, &i->sink->channel_map, &i->channel_map);
+
+    pa_cvolume_remap(&i->volume_factor_sink, &i->sink->channel_map, &i->channel_map);
+
+    i->sink = NULL;
+
+    pa_sink_input_unref(i);
+
+    return 0;
+}
+
+/* Called from main context. If i has an origin sink that uses volume sharing,
+ * then also the origin sink and all streams connected to it need to update
+ * their volume - this function does all that by using recursion. */
+static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
+    pa_cvolume new_volume;
+
+    pa_assert(i);
+    pa_assert(dest);
+    pa_assert(i->sink); /* The destination sink should already be set. */
+
+    if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
+        pa_sink *root_sink = pa_sink_get_master(i->sink);
+        pa_sink_input *origin_sink_input;
+        uint32_t idx;
+
+        if (PA_UNLIKELY(!root_sink))
+            return;
+
+        if (pa_sink_flat_volume_enabled(i->sink)) {
+            /* Ok, so the origin sink uses volume sharing, and flat volume is
+             * enabled. The volume will have to be updated as follows:
+             *
+             *     i->volume := i->sink->real_volume
+             *         (handled later by pa_sink_set_volume)
+             *     i->reference_ratio := i->volume / i->sink->reference_volume
+             *         (handled later by pa_sink_set_volume)
+             *     i->real_ratio stays unchanged
+             *         (streams whose origin sink uses volume sharing should
+             *          always have real_ratio of 0 dB)
+             *     i->soft_volume stays unchanged
+             *         (streams whose origin sink uses volume sharing should
+             *          always have volume_factor as soft_volume, so no change
+             *          should be needed) */
+
+            pa_assert(pa_cvolume_is_norm(&i->real_ratio));
+            pa_assert(pa_cvolume_equal(&i->soft_volume, &i->volume_factor));
+
+            /* Notifications will be sent by pa_sink_set_volume(). */
+
+        } else {
+            /* Ok, so the origin sink uses volume sharing, and flat volume is
+             * disabled. The volume will have to be updated as follows:
+             *
+             *     i->volume := 0 dB
+             *     i->reference_ratio := 0 dB
+             *     i->real_ratio stays unchanged
+             *         (streams whose origin sink uses volume sharing should
+             *          always have real_ratio of 0 dB)
+             *     i->soft_volume stays unchanged
+             *         (streams whose origin sink uses volume sharing should
+             *          always have volume_factor as soft_volume, so no change
+             *          should be needed) */
+
+            pa_cvolume_reset(&new_volume, i->volume.channels);
+            pa_sink_input_set_volume_direct(i, &new_volume);
+            pa_sink_input_set_reference_ratio(i, &new_volume);
+            pa_assert(pa_cvolume_is_norm(&i->real_ratio));
+            pa_assert(pa_cvolume_equal(&i->soft_volume, &i->volume_factor));
+        }
+
+        /* Additionally, the origin sink volume needs updating:
+         *
+         *     i->origin_sink->reference_volume := root_sink->reference_volume
+         *     i->origin_sink->real_volume := root_sink->real_volume
+         *     i->origin_sink->soft_volume stays unchanged
+         *         (sinks that use volume sharing should always have
+         *          soft_volume of 0 dB) */
+
+        new_volume = root_sink->reference_volume;
+        pa_cvolume_remap(&new_volume, &root_sink->channel_map, &i->origin_sink->channel_map);
+        pa_sink_set_reference_volume_direct(i->origin_sink, &new_volume);
+
+        i->origin_sink->real_volume = root_sink->real_volume;
+        pa_cvolume_remap(&i->origin_sink->real_volume, &root_sink->channel_map, &i->origin_sink->channel_map);
+
+        pa_assert(pa_cvolume_is_norm(&i->origin_sink->soft_volume));
+
+        /* If you wonder whether i->origin_sink->set_volume() should be called
+         * somewhere, that's not the case, because sinks that use volume
+         * sharing shouldn't have any internal volume that set_volume() would
+         * update. If you wonder whether the thread_info variables should be
+         * synced, yes, they should, and it's done by the
+         * PA_SINK_MESSAGE_FINISH_MOVE message handler. */
+
+        /* Recursively update origin sink inputs. */
+        PA_IDXSET_FOREACH(origin_sink_input, i->origin_sink->inputs, idx)
+            update_volume_due_to_moving(origin_sink_input, dest);
+
+    } else {
+        if (pa_sink_flat_volume_enabled(i->sink)) {
+            /* Ok, so this is a regular stream, and flat volume is enabled. The
+             * volume will have to be updated as follows:
+             *
+             *     i->volume := i->reference_ratio * i->sink->reference_volume
+             *     i->reference_ratio stays unchanged
+             *     i->real_ratio := i->volume / i->sink->real_volume
+             *         (handled later by pa_sink_set_volume)
+             *     i->soft_volume := i->real_ratio * i->volume_factor
+             *         (handled later by pa_sink_set_volume) */
+
+            new_volume = i->sink->reference_volume;
+            pa_cvolume_remap(&new_volume, &i->sink->channel_map, &i->channel_map);
+            pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
+            pa_sink_input_set_volume_direct(i, &new_volume);
+
+        } else {
+            /* Ok, so this is a regular stream, and flat volume is disabled.
+             * The volume will have to be updated as follows:
+             *
+             *     i->volume := i->reference_ratio
+             *     i->reference_ratio stays unchanged
+             *     i->real_ratio := i->reference_ratio
+             *     i->soft_volume := i->real_ratio * i->volume_factor */
+
+            pa_sink_input_set_volume_direct(i, &i->reference_ratio);
+            i->real_ratio = i->reference_ratio;
+            pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
+        }
+    }
+
+    /* If i->sink == dest, then recursion has finished, and we can finally call
+     * pa_sink_set_volume(), which will do the rest of the updates. */
+    if ((i->sink == dest) && pa_sink_flat_volume_enabled(i->sink))
+        pa_sink_set_volume(i->sink, NULL, false, i->save_volume);
+}
+
+/* Called from main context */
+int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, bool save) {
+    struct volume_factor_entry *v;
+    void *state = NULL;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(!i->sink);
+    pa_sink_assert_ref(dest);
+
+    if (!pa_sink_input_may_move_to(i, dest))
+        return -PA_ERR_NOTSUPPORTED;
+
+    if (pa_sink_input_is_passthrough(i) && !pa_sink_check_format(dest, i->format)) {
+        pa_proplist *p = pa_proplist_new();
+        pa_log_debug("New sink doesn't support stream format, sending format-changed and killing");
+        /* Tell the client what device we want to be on if it is going to
+         * reconnect */
+        pa_proplist_sets(p, "device", dest->name);
+        pa_sink_input_send_event(i, PA_STREAM_EVENT_FORMAT_LOST, p);
+        pa_proplist_free(p);
+        return -PA_ERR_NOTSUPPORTED;
+    }
+
+    if (!(i->flags & PA_SINK_INPUT_VARIABLE_RATE) &&
+        !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec)) {
+        /* try to change dest sink rate if possible without glitches.
+           module-suspend-on-idle resumes destination sink with
+           SINK_INPUT_MOVE_FINISH hook */
+
+        pa_log_info("Trying to change sample rate");
+        if (pa_sink_update_rate(dest, i->sample_spec.rate, pa_sink_input_is_passthrough(i)) >= 0)
+            pa_log_info("Rate changed to %u Hz", dest->sample_spec.rate);
+    }
+
+    if (i->moving)
+        i->moving(i, dest);
+
+    i->sink = dest;
+    i->save_sink = save;
+    pa_idxset_put(dest->inputs, pa_sink_input_ref(i), NULL);
+
+    PA_HASHMAP_FOREACH(v, i->volume_factor_sink_items, state)
+        pa_cvolume_remap(&v->volume, &i->channel_map, &i->sink->channel_map);
+
+    pa_cvolume_remap(&i->volume_factor_sink, &i->channel_map, &i->sink->channel_map);
+
+    if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
+        i->sink->n_corked++;
+
+    pa_sink_input_update_rate(i);
+
+    pa_sink_update_status(dest);
+
+    update_volume_due_to_moving(i, dest);
+
+    if (pa_sink_input_is_passthrough(i))
+        pa_sink_enter_passthrough(i->sink);
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
+
+    pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
+
+    /* Notify everyone */
+    pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
+    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+
+    return 0;
+}
+
+/* Called from main context */
+void pa_sink_input_fail_move(pa_sink_input *i) {
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(!i->sink);
+
+    /* Check if someone wants this sink input? */
+    if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], i) == PA_HOOK_STOP)
+        return;
+
+    if (i->moving)
+        i->moving(i, NULL);
+
+    pa_sink_input_kill(i);
+}
+
+/* Called from main context */
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, bool save) {
+    int r;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(i->sink);
+    pa_sink_assert_ref(dest);
+
+    if (dest == i->sink)
+        return 0;
+
+    if (!pa_sink_input_may_move_to(i, dest))
+        return -PA_ERR_NOTSUPPORTED;
+
+    pa_sink_input_ref(i);
+
+    if ((r = pa_sink_input_start_move(i)) < 0) {
+        pa_sink_input_unref(i);
+        return r;
+    }
+
+    if ((r = pa_sink_input_finish_move(i, dest, save)) < 0) {
+        pa_sink_input_fail_move(i);
+        pa_sink_input_unref(i);
+        return r;
+    }
+
+    pa_sink_input_unref(i);
+
+    return 0;
+}
+
+/* Called from IO thread context except when cork() is called without a valid sink. */
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) {
+    bool corking, uncorking;
+
+    pa_sink_input_assert_ref(i);
+
+    if (state == i->thread_info.state)
+        return;
+
+    if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) &&
+        !(i->thread_info.state == PA_SINK_INPUT_DRAINED || i->thread_info.state != PA_SINK_INPUT_RUNNING))
+        pa_atomic_store(&i->thread_info.drained, 1);
+
+    corking = state == PA_SINK_INPUT_CORKED && i->thread_info.state == PA_SINK_INPUT_RUNNING;
+    uncorking = i->thread_info.state == PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_RUNNING;
+
+    if (i->state_change)
+        i->state_change(i, state);
+
+    if (corking) {
+
+        pa_log_debug("Requesting rewind due to corking");
+
+        /* This will tell the implementing sink input driver to rewind
+         * so that the unplayed already mixed data is not lost */
+        if (i->sink)
+            pa_sink_input_request_rewind(i, 0, true, true, false);
+
+        /* Set the corked state *after* requesting rewind */
+        i->thread_info.state = state;
+
+    } else if (uncorking) {
+
+        pa_log_debug("Requesting rewind due to uncorking");
+
+        i->thread_info.underrun_for = (uint64_t) -1;
+        i->thread_info.underrun_for_sink = 0;
+        i->thread_info.playing_for = 0;
+
+        /* Set the uncorked state *before* requesting rewind */
+        i->thread_info.state = state;
+
+        /* OK, we're being uncorked. Make sure we're not rewound when
+         * the hw buffer is remixed and request a remix. */
+        if (i->sink)
+            pa_sink_input_request_rewind(i, 0, false, true, true);
+    } else
+        /* We may not be corking or uncorking, but we still need to set the state. */
+        i->thread_info.state = state;
+}
+
+/* Called from thread context, except when it is not. */
+int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_sink_input *i = PA_SINK_INPUT(o);
+    pa_sink_input_assert_ref(i);
+
+    switch (code) {
+
+        case PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME:
+            if (!pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume)) {
+                i->thread_info.soft_volume = i->soft_volume;
+                pa_sink_input_request_rewind(i, 0, true, false, false);
+            }
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE:
+            if (i->thread_info.muted != i->muted) {
+                i->thread_info.muted = i->muted;
+                pa_sink_input_request_rewind(i, 0, true, false, false);
+            }
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            r[0] += pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec);
+            r[1] += pa_sink_get_latency_within_thread(i->sink, false);
+
+            return 0;
+        }
+
+        case PA_SINK_INPUT_MESSAGE_SET_RATE:
+
+            i->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
+            pa_resampler_set_input_rate(i->thread_info.resampler, PA_PTR_TO_UINT(userdata));
+
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_SET_STATE: {
+            pa_sink_input *ssync;
+
+            pa_sink_input_set_state_within_thread(i, PA_PTR_TO_UINT(userdata));
+
+            for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev)
+                pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
+
+            for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next)
+                pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
+
+            return 0;
+        }
+
+        case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: {
+            pa_usec_t *usec = userdata;
+
+            *usec = pa_sink_input_set_requested_latency_within_thread(i, *usec);
+            return 0;
+        }
+
+        case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            *r = i->thread_info.requested_sink_latency;
+            return 0;
+        }
+    }
+
+    return -PA_ERR_NOTIMPLEMENTED;
+}
+
+/* Called from main thread */
+pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+
+    if (i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED)
+        return pa_atomic_load(&i->thread_info.drained) ? PA_SINK_INPUT_DRAINED : PA_SINK_INPUT_RUNNING;
+
+    return i->state;
+}
+
+/* Called from IO context */
+bool pa_sink_input_safe_to_remove(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+
+    if (PA_SINK_INPUT_IS_LINKED(i->thread_info.state))
+        return pa_memblockq_is_empty(i->thread_info.render_memblockq);
+
+    return true;
+}
+
+/* Called from IO context */
+void pa_sink_input_request_rewind(
+        pa_sink_input *i,
+        size_t nbytes  /* in our sample spec */,
+        bool rewrite,  /* rewrite what we have, or get fresh data? */
+        bool flush,    /* flush render memblockq? */
+        bool dont_rewind_render) {
+
+    size_t lbq;
+
+    /* If 'rewrite' is true the sink is rewound as far as requested
+     * and possible and the exact value of this is passed back the
+     * implementor via process_rewind(). If 'flush' is also true all
+     * already rendered data is also dropped.
+     *
+     * If 'rewrite' is false the sink is rewound as far as requested
+     * and possible and the already rendered data is dropped so that
+     * in the next iteration we read new data from the
+     * implementor. This implies 'flush' is true.  If
+     * dont_rewind_render is true then the render memblockq is not
+     * rewound. */
+
+    /* nbytes = 0 means maximum rewind request */
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert(rewrite || flush);
+    pa_assert(!dont_rewind_render || !rewrite);
+
+    /* We don't take rewind requests while we are corked */
+    if (i->thread_info.state == PA_SINK_INPUT_CORKED)
+        return;
+
+    nbytes = PA_MAX(i->thread_info.rewrite_nbytes, nbytes);
+
+#ifdef SINK_INPUT_DEBUG
+    pa_log_debug("request rewrite %zu", nbytes);
+#endif
+
+    /* Calculate how much we can rewind locally without having to
+     * touch the sink */
+    if (rewrite)
+        lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
+    else
+        lbq = 0;
+
+    /* Check if rewinding for the maximum is requested, and if so, fix up */
+    if (nbytes <= 0) {
+
+        /* Calculate maximum number of bytes that could be rewound in theory */
+        nbytes = i->sink->thread_info.max_rewind + lbq;
+
+        /* Transform from sink domain */
+        if (i->thread_info.resampler)
+            nbytes = pa_resampler_request(i->thread_info.resampler, nbytes);
+    }
+
+    /* Remember how much we actually want to rewrite */
+    if (i->thread_info.rewrite_nbytes != (size_t) -1) {
+        if (rewrite) {
+            /* Make sure to not overwrite over underruns */
+            if (nbytes > i->thread_info.playing_for)
+                nbytes = (size_t) i->thread_info.playing_for;
+
+            i->thread_info.rewrite_nbytes = nbytes;
+        } else
+            i->thread_info.rewrite_nbytes = (size_t) -1;
+    }
+
+    i->thread_info.rewrite_flush =
+        i->thread_info.rewrite_flush || flush;
+
+    i->thread_info.dont_rewind_render =
+        i->thread_info.dont_rewind_render ||
+        dont_rewind_render;
+
+    /* nbytes is -1 if some earlier rewind request had rewrite == false. */
+    if (nbytes != (size_t) -1) {
+
+        /* Transform to sink domain */
+        if (i->thread_info.resampler)
+            nbytes = pa_resampler_result(i->thread_info.resampler, nbytes);
+
+        if (nbytes > lbq)
+            pa_sink_request_rewind(i->sink, nbytes - lbq);
+        else
+            /* This call will make sure process_rewind() is called later */
+            pa_sink_request_rewind(i->sink, 0);
+    }
+}
+
+/* Called from main context */
+pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(ret);
+
+    /* FIXME: Shouldn't access resampler object from main context! */
+
+    pa_silence_memchunk_get(
+                &i->core->silence_cache,
+                i->core->mempool,
+                ret,
+                &i->sample_spec,
+                i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0);
+
+    return ret;
+}
+
+/* Called from main context */
+void pa_sink_input_send_event(pa_sink_input *i, const char *event, pa_proplist *data) {
+    pa_proplist *pl = NULL;
+    pa_sink_input_send_event_hook_data hook_data;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(event);
+
+    if (!i->send_event)
+        return;
+
+    if (!data)
+        data = pl = pa_proplist_new();
+
+    hook_data.sink_input = i;
+    hook_data.data = data;
+    hook_data.event = event;
+
+    if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], &hook_data) < 0)
+        goto finish;
+
+    i->send_event(i, event, data);
+
+finish:
+    if (pl)
+        pa_proplist_free(pl);
+}
+
+/* Called from main context */
+/* Updates the sink input's resampler with whatever the current sink requires
+ * -- useful when the underlying sink's rate might have changed */
+int pa_sink_input_update_rate(pa_sink_input *i) {
+    pa_resampler *new_resampler;
+    char *memblockq_name;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+
+    if (i->thread_info.resampler &&
+        pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &i->sink->sample_spec) &&
+        pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &i->sink->channel_map))
+
+        new_resampler = i->thread_info.resampler;
+
+    else if (!pa_sink_input_is_passthrough(i) &&
+        ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ||
+         !pa_sample_spec_equal(&i->sample_spec, &i->sink->sample_spec) ||
+         !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map))) {
+
+        new_resampler = pa_resampler_new(i->core->mempool,
+                                     &i->sample_spec, &i->channel_map,
+                                     &i->sink->sample_spec, &i->sink->channel_map,
+                                     i->core->lfe_crossover_freq,
+                                     i->requested_resample_method,
+                                     ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+                                     ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+                                     (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+                                     (i->core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) |
+                                     (i->core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0));
+
+        if (!new_resampler) {
+            pa_log_warn("Unsupported resampling operation.");
+            return -PA_ERR_NOTSUPPORTED;
+        }
+    } else
+        new_resampler = NULL;
+
+    if (new_resampler == i->thread_info.resampler)
+        return 0;
+
+    if (i->thread_info.resampler)
+        pa_resampler_free(i->thread_info.resampler);
+
+    i->thread_info.resampler = new_resampler;
+
+    pa_memblockq_free(i->thread_info.render_memblockq);
+
+    memblockq_name = pa_sprintf_malloc("sink input render_memblockq [%u]", i->index);
+    i->thread_info.render_memblockq = pa_memblockq_new(
+            memblockq_name,
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            0,
+            &i->sink->sample_spec,
+            0,
+            1,
+            0,
+            &i->sink->silence);
+    pa_xfree(memblockq_name);
+
+    i->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID;
+
+    pa_log_debug("Updated resampler for sink input %d", i->index);
+
+    return 0;
+}
+
+/* Called from the IO thread. */
+void pa_sink_input_attach(pa_sink_input *i) {
+    pa_assert(i);
+    pa_assert(!i->thread_info.attached);
+
+    i->thread_info.attached = true;
+
+    if (i->attach)
+        i->attach(i);
+}
+
+/* Called from the IO thread. */
+void pa_sink_input_detach(pa_sink_input *i) {
+    pa_assert(i);
+
+    if (!i->thread_info.attached)
+        return;
+
+    i->thread_info.attached = false;
+
+    if (i->detach)
+        i->detach(i);
+}
+
+/* Called from the main thread. */
+void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume) {
+    pa_cvolume old_volume;
+    char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(i);
+    pa_assert(volume);
+
+    old_volume = i->volume;
+
+    if (pa_cvolume_equal(volume, &old_volume))
+        return;
+
+    i->volume = *volume;
+    pa_log_debug("The volume of sink input %u changed from %s to %s.", i->index,
+                 pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &i->channel_map, true),
+                 pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &i->channel_map, true));
+
+    if (i->volume_changed)
+        i->volume_changed(i);
+
+    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], i);
+}
+
+/* Called from the main thread. */
+void pa_sink_input_set_reference_ratio(pa_sink_input *i, const pa_cvolume *ratio) {
+    pa_cvolume old_ratio;
+    char old_ratio_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    char new_ratio_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(i);
+    pa_assert(ratio);
+
+    old_ratio = i->reference_ratio;
+
+    if (pa_cvolume_equal(ratio, &old_ratio))
+        return;
+
+    i->reference_ratio = *ratio;
+
+    if (!PA_SINK_INPUT_IS_LINKED(i->state))
+        return;
+
+    pa_log_debug("Sink input %u reference ratio changed from %s to %s.", i->index,
+                 pa_cvolume_snprint_verbose(old_ratio_str, sizeof(old_ratio_str), &old_ratio, &i->channel_map, true),
+                 pa_cvolume_snprint_verbose(new_ratio_str, sizeof(new_ratio_str), ratio, &i->channel_map, true));
+}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
new file mode 100644 (file)
index 0000000..f957a0e
--- /dev/null
@@ -0,0 +1,457 @@
+#ifndef foopulsesinkinputhfoo
+#define foopulsesinkinputhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include <pulsecore/typedefs.h>
+#include <pulse/sample.h>
+#include <pulse/format.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/module.h>
+#include <pulsecore/client.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/core.h>
+
+typedef enum pa_sink_input_state {
+    PA_SINK_INPUT_INIT,         /*< The stream is not active yet, because pa_sink_input_put() has not been called yet */
+    PA_SINK_INPUT_DRAINED,      /*< The stream stopped playing because there was no data to play */
+    PA_SINK_INPUT_RUNNING,      /*< The stream is alive and kicking */
+    PA_SINK_INPUT_CORKED,       /*< The stream was corked on user request */
+    PA_SINK_INPUT_UNLINKED      /*< The stream is dead */
+    /* FIXME: we need a state for MOVING here */
+} pa_sink_input_state_t;
+
+static inline bool PA_SINK_INPUT_IS_LINKED(pa_sink_input_state_t x) {
+    return x == PA_SINK_INPUT_DRAINED || x == PA_SINK_INPUT_RUNNING || x == PA_SINK_INPUT_CORKED;
+}
+
+typedef enum pa_sink_input_flags {
+    PA_SINK_INPUT_VARIABLE_RATE = 1,
+    PA_SINK_INPUT_DONT_MOVE = 2,
+    PA_SINK_INPUT_START_CORKED = 4,
+    PA_SINK_INPUT_NO_REMAP = 8,
+    PA_SINK_INPUT_NO_REMIX = 16,
+    PA_SINK_INPUT_FIX_FORMAT = 32,
+    PA_SINK_INPUT_FIX_RATE = 64,
+    PA_SINK_INPUT_FIX_CHANNELS = 128,
+    PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
+    PA_SINK_INPUT_NO_CREATE_ON_SUSPEND = 512,
+    PA_SINK_INPUT_KILL_ON_SUSPEND = 1024,
+    PA_SINK_INPUT_PASSTHROUGH = 2048
+} pa_sink_input_flags_t;
+
+struct pa_sink_input {
+    pa_msgobject parent;
+
+    uint32_t index;
+    pa_core *core;
+
+    /* Please note that this state should only be read with
+     * pa_sink_input_get_state(). That function will transparently
+     * merge the thread_info.drained value in. */
+    pa_sink_input_state_t state;
+    pa_sink_input_flags_t flags;
+
+    char *driver;                       /* may be NULL */
+    pa_proplist *proplist;
+
+    pa_module *module;                  /* may be NULL */
+    pa_client *client;                  /* may be NULL */
+
+    pa_sink *sink;                      /* NULL while we are being moved */
+    pa_sink *origin_sink;               /* only set by filter sinks */
+
+    /* A sink input may be connected to multiple source outputs
+     * directly, so that they don't get mixed data of the entire
+     * source. */
+    pa_idxset *direct_outputs;
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    pa_format_info *format;
+
+    pa_sink_input *sync_prev, *sync_next;
+
+    /* Also see http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Volumes/ */
+    pa_cvolume volume;             /* The volume clients are informed about */
+    pa_cvolume reference_ratio;    /* The ratio of the stream's volume to the sink's reference volume */
+    pa_cvolume real_ratio;         /* The ratio of the stream's volume to the sink's real volume */
+    /* volume_factor is an internally used "additional volume" that can be used
+     * by modules without having the volume visible to clients. volume_factor
+     * calculated by merging all the individual items in volume_factor_items.
+     * Modules must not modify these variables directly, instead
+     * pa_sink_input_add/remove_volume_factor() have to be used to add and
+     * remove items, or pa_sink_input_new_data_add_volume_factor() during input
+     * creation time. */
+    pa_cvolume volume_factor;
+    pa_hashmap *volume_factor_items;
+    pa_cvolume soft_volume;          /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as real_ratio * volume_factor */
+
+    pa_cvolume volume_factor_sink; /* A second volume factor in format of the sink this stream is connected to. */
+    pa_hashmap *volume_factor_sink_items;
+
+    bool volume_writable:1;
+
+    bool muted:1;
+
+    /* if true then the sink we are connected to and/or the volume
+     * set is worth remembering, i.e. was explicitly chosen by the
+     * user and not automatically. module-stream-restore looks for
+     * this.*/
+    bool save_sink:1, save_volume:1, save_muted:1;
+
+    pa_resample_method_t requested_resample_method, actual_resample_method;
+
+    /* Returns the chunk of audio data and drops it from the
+     * queue. Returns -1 on failure. Called from IO thread context. If
+     * data needs to be generated from scratch then please in the
+     * specified length request_nbytes. This is an optimization
+     * only. If less data is available, it's fine to return a smaller
+     * block. If more data is already ready, it is better to return
+     * the full block. */
+    int (*pop) (pa_sink_input *i, size_t request_nbytes, pa_memchunk *chunk); /* may NOT be NULL */
+
+    /* This is called when the playback buffer has actually played back
+       all available data. Return true unless there is more data to play back.
+       Called from IO context. */
+    bool (*process_underrun) (pa_sink_input *i);
+
+    /* Rewind the queue by the specified number of bytes. Called just
+     * before peek() if it is called at all. Only called if the sink
+     * input driver ever plans to call
+     * pa_sink_input_request_rewind(). Called from IO context. */
+    void (*process_rewind) (pa_sink_input *i, size_t nbytes);     /* may NOT be NULL */
+
+    /* Called whenever the maximum rewindable size of the sink
+     * changes. Called from IO context. */
+    void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */
+
+    /* Called whenever the maximum request size of the sink
+     * changes. Called from IO context. */
+    void (*update_max_request) (pa_sink_input *i, size_t nbytes); /* may be NULL */
+
+    /* Called whenever the configured latency of the sink
+     * changes. Called from IO context. */
+    void (*update_sink_requested_latency) (pa_sink_input *i); /* may be NULL */
+
+    /* Called whenever the latency range of the sink changes. Called
+     * from IO context. */
+    void (*update_sink_latency_range) (pa_sink_input *i); /* may be NULL */
+
+    /* Called whenever the fixed latency of the sink changes, if there
+     * is one. Called from IO context. */
+    void (*update_sink_fixed_latency) (pa_sink_input *i); /* may be NULL */
+
+    /* If non-NULL this function is called when the input is first
+     * connected to a sink or when the rtpoll/asyncmsgq fields
+     * change. You usually don't need to implement this function
+     * unless you rewrite a sink that is piggy-backed onto
+     * another. Called from IO thread context */
+    void (*attach) (pa_sink_input *i);           /* may be NULL */
+
+    /* If non-NULL this function is called when the output is
+     * disconnected from its sink. Called from IO thread context */
+    void (*detach) (pa_sink_input *i);           /* may be NULL */
+
+    /* If non-NULL called whenever the sink this input is attached
+     * to suspends or resumes. Called from main context */
+    void (*suspend) (pa_sink_input *i, bool b);   /* may be NULL */
+
+    /* If non-NULL called whenever the sink this input is attached
+     * to suspends or resumes. Called from IO context */
+    void (*suspend_within_thread) (pa_sink_input *i, bool b);   /* may be NULL */
+
+    /* If non-NULL called whenever the sink input is moved to a new
+     * sink. Called from main context after the sink input has been
+     * detached from the old sink and before it has been attached to
+     * the new sink. If dest is NULL the move was executed in two
+     * phases and the second one failed; the stream will be destroyed
+     * after this call. */
+    void (*moving) (pa_sink_input *i, pa_sink *dest);   /* may be NULL */
+
+    /* Supposed to unlink and destroy this stream. Called from main
+     * context. */
+    void (*kill) (pa_sink_input *i);             /* may NOT be NULL */
+
+    /* Return the current latency (i.e. length of buffered audio) of
+    this stream. Called from main context. This is added to what the
+    PA_SINK_INPUT_MESSAGE_GET_LATENCY message sent to the IO thread
+    returns */
+    pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */
+
+    /* If non-NULL this function is called from thread context if the
+     * state changes. The old state is found in thread_info.state.  */
+    void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */
+
+    /* If non-NULL this function is called before this sink input is
+     * move to a sink and if it returns false the move will not
+     * be allowed */
+    bool (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */
+
+    /* If non-NULL this function is used to dispatch asynchronous
+     * control events. Called from main context. */
+    void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data); /* may be NULL */
+
+    /* If non-NULL this function is called whenever the sink input
+     * volume changes. Called from main context */
+    void (*volume_changed)(pa_sink_input *i); /* may be NULL */
+
+    /* If non-NULL this function is called whenever the sink input
+     * mute status changes. Called from main context */
+    void (*mute_changed)(pa_sink_input *i); /* may be NULL */
+
+    struct {
+        pa_sink_input_state_t state;
+        pa_atomic_t drained;
+
+        pa_cvolume soft_volume;
+        bool muted:1;
+
+        bool attached:1; /* True only between ->attach() and ->detach() calls */
+
+        /* rewrite_nbytes: 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */
+        bool rewrite_flush:1, dont_rewind_render:1;
+        size_t rewrite_nbytes;
+        uint64_t underrun_for, playing_for;
+        uint64_t underrun_for_sink; /* Like underrun_for, but in sink sample spec */
+
+        pa_sample_spec sample_spec;
+
+        pa_resampler *resampler;                     /* may be NULL */
+
+        /* We maintain a history of resampled audio data here. */
+        pa_memblockq *render_memblockq;
+
+        pa_sink_input *sync_prev, *sync_next;
+
+        /* The requested latency for the sink */
+        pa_usec_t requested_sink_latency;
+
+        pa_hashmap *direct_outputs;
+    } thread_info;
+
+    void *userdata;
+};
+
+PA_DECLARE_PUBLIC_CLASS(pa_sink_input);
+#define PA_SINK_INPUT(o) pa_sink_input_cast(o)
+
+enum {
+    PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME,
+    PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE,
+    PA_SINK_INPUT_MESSAGE_GET_LATENCY,
+    PA_SINK_INPUT_MESSAGE_SET_RATE,
+    PA_SINK_INPUT_MESSAGE_SET_STATE,
+    PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY,
+    PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY,
+    PA_SINK_INPUT_MESSAGE_MAX
+};
+
+typedef struct pa_sink_input_send_event_hook_data {
+    pa_sink_input *sink_input;
+    const char *event;
+    pa_proplist *data;
+} pa_sink_input_send_event_hook_data;
+
+typedef struct pa_sink_input_new_data {
+    pa_sink_input_flags_t flags;
+
+    pa_proplist *proplist;
+
+    const char *driver;
+    pa_module *module;
+    pa_client *client;
+
+    pa_sink *sink;
+    pa_sink *origin_sink;
+
+    pa_resample_method_t resample_method;
+
+    pa_sink_input *sync_base;
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    pa_format_info *format;
+    pa_idxset *req_formats;
+    pa_idxset *nego_formats;
+
+    pa_cvolume volume;
+    bool muted:1;
+    pa_hashmap *volume_factor_items, *volume_factor_sink_items;
+
+    bool sample_spec_is_set:1;
+    bool channel_map_is_set:1;
+
+    bool volume_is_set:1;
+    bool muted_is_set:1;
+
+    bool volume_is_absolute:1;
+
+    bool volume_writable:1;
+
+    bool save_sink:1, save_volume:1, save_muted:1;
+} pa_sink_input_new_data;
+
+pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
+void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec);
+void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
+bool pa_sink_input_new_data_is_passthrough(pa_sink_input_new_data *data);
+void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
+void pa_sink_input_new_data_add_volume_factor(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor);
+void pa_sink_input_new_data_add_volume_factor_sink(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor);
+void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, bool mute);
+bool pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, bool save);
+bool pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset *formats);
+void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
+
+/* To be called by the implementing module only */
+
+int pa_sink_input_new(
+        pa_sink_input **i,
+        pa_core *core,
+        pa_sink_input_new_data *data);
+
+void pa_sink_input_put(pa_sink_input *i);
+void pa_sink_input_unlink(pa_sink_input* i);
+
+pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec);
+
+/* Request that the specified number of bytes already written out to
+the hw device is rewritten, if possible.  Please note that this is
+only a kind request. The sink driver may not be able to fulfill it
+fully -- or at all. If the request for a rewrite was successful, the
+sink driver will call ->rewind() and pass the number of bytes that
+could be rewound in the HW device. This functionality is required for
+implementing the "zero latency" write-through functionality. */
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, bool rewrite, bool flush, bool dont_rewind_render);
+
+void pa_sink_input_cork(pa_sink_input *i, bool b);
+
+int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
+int pa_sink_input_update_rate(pa_sink_input *i);
+
+/* This returns the sink's fields converted into out sample type */
+size_t pa_sink_input_get_max_rewind(pa_sink_input *i);
+size_t pa_sink_input_get_max_request(pa_sink_input *i);
+
+/* Callable by everyone from main thread*/
+
+/* External code may request disconnection with this function */
+void pa_sink_input_kill(pa_sink_input*i);
+
+pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
+
+bool pa_sink_input_is_passthrough(pa_sink_input *i);
+bool pa_sink_input_is_volume_readable(pa_sink_input *i);
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool save, bool absolute);
+void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa_cvolume *volume_factor);
+int pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key);
+pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool absolute);
+
+void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save);
+
+void pa_sink_input_set_property(pa_sink_input *i, const char *key, const char *value);
+void pa_sink_input_set_property_arbitrary(pa_sink_input *i, const char *key, const uint8_t *value, size_t nbytes);
+void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p);
+
+pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
+
+void pa_sink_input_send_event(pa_sink_input *i, const char *name, pa_proplist *data);
+
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, bool save);
+bool pa_sink_input_may_move(pa_sink_input *i); /* may this sink input move at all? */
+bool pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may this sink input move to this sink? */
+
+/* The same as pa_sink_input_move_to() but in two separate steps,
+ * first the detaching from the old sink, then the attaching to the
+ * new sink */
+int pa_sink_input_start_move(pa_sink_input *i);
+int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, bool save);
+void pa_sink_input_fail_move(pa_sink_input *i);
+
+pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
+
+pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i);
+
+/* To be used exclusively by the sink driver IO thread */
+
+void pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume);
+void pa_sink_input_drop(pa_sink_input *i, size_t length);
+void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
+void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes  /* in the sink's sample spec */);
+void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes  /* in the sink's sample spec */);
+
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state);
+
+int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec);
+
+bool pa_sink_input_safe_to_remove(pa_sink_input *i);
+bool pa_sink_input_process_underrun(pa_sink_input *i);
+
+pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
+
+/* Calls the attach() callback if it's set. The input must be in detached
+ * state. */
+void pa_sink_input_attach(pa_sink_input *i);
+
+/* Calls the detach() callback if it's set and the input is attached. The input
+ * is allowed to be already detached, in which case this does nothing.
+ *
+ * The reason why this can be called for already-detached inputs is that when
+ * a filter sink's input is detached, it has to detach also all inputs
+ * connected to the filter sink. In case the filter sink's input was detached
+ * because the filter sink is being removed, those other inputs will be moved
+ * to another sink or removed, and moving and removing involve detaching the
+ * inputs, but the inputs at that point are already detached.
+ *
+ * XXX: Moving or removing an input also involves sending messages to the
+ * input's sink. If the input's sink is a detached filter sink, shouldn't
+ * sending messages to it be prohibited? The messages are processed in the
+ * root sink's IO thread, and when the filter sink is detached, it would seem
+ * logical to prohibit any interaction with the IO thread that isn't any more
+ * associated with the filter sink. Currently sending messages to detached
+ * filter sinks mostly works, because the filter sinks don't update their
+ * asyncmsgq pointer when detaching, so messages still find their way to the
+ * old IO thread. */
+void pa_sink_input_detach(pa_sink_input *i);
+
+/* Called from the main thread, from sink.c only. The normal way to set the
+ * sink input volume is to call pa_sink_input_set_volume(), but the flat volume
+ * logic in sink.c needs also a function that doesn't do all the extra stuff
+ * that pa_sink_input_set_volume() does. This function simply sets i->volume
+ * and fires change notifications. */
+void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume);
+
+/* Called from the main thread, from sink.c only. This shouldn't be a public
+ * function, but the flat volume logic in sink.c currently needs a way to
+ * directly set the sink input reference ratio. This function simply sets
+ * i->reference_ratio and logs a message if the value changes. */
+void pa_sink_input_set_reference_ratio(pa_sink_input *i, const pa_cvolume *ratio);
+
+#define pa_sink_input_assert_io_context(s) \
+    pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state))
+
+#endif
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
new file mode 100644 (file)
index 0000000..cbca889
--- /dev/null
@@ -0,0 +1,3853 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/introspect.h>
+#include <pulse/format.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/rtclock.h>
+#include <pulse/internal.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/mix.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/play-memblockq.h>
+#include <pulsecore/flist.h>
+
+#include "sink.h"
+
+#define MAX_MIX_CHANNELS 32
+#define MIX_BUFFER_LENGTH (pa_page_size())
+#define ABSOLUTE_MIN_LATENCY (500)
+#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
+#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
+
+PA_DEFINE_PUBLIC_CLASS(pa_sink, pa_msgobject);
+
+struct pa_sink_volume_change {
+    pa_usec_t at;
+    pa_cvolume hw_volume;
+
+    PA_LLIST_FIELDS(pa_sink_volume_change);
+};
+
+struct sink_message_set_port {
+    pa_device_port *port;
+    int ret;
+};
+
+static void sink_free(pa_object *s);
+
+static void pa_sink_volume_change_push(pa_sink *s);
+static void pa_sink_volume_change_flush(pa_sink *s);
+static void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes);
+
+pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
+    pa_assert(data);
+
+    pa_zero(*data);
+    data->proplist = pa_proplist_new();
+    data->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_device_port_unref);
+
+    return data;
+}
+
+void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    data->name = pa_xstrdup(name);
+}
+
+void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec) {
+    pa_assert(data);
+
+    if ((data->sample_spec_is_set = !!spec))
+        data->sample_spec = *spec;
+}
+
+void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map) {
+    pa_assert(data);
+
+    if ((data->channel_map_is_set = !!map))
+        data->channel_map = *map;
+}
+
+void pa_sink_new_data_set_alternate_sample_rate(pa_sink_new_data *data, const uint32_t alternate_sample_rate) {
+    pa_assert(data);
+
+    data->alternate_sample_rate_is_set = true;
+    data->alternate_sample_rate = alternate_sample_rate;
+}
+
+void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) {
+    pa_assert(data);
+
+    if ((data->volume_is_set = !!volume))
+        data->volume = *volume;
+}
+
+void pa_sink_new_data_set_muted(pa_sink_new_data *data, bool mute) {
+    pa_assert(data);
+
+    data->muted_is_set = true;
+    data->muted = mute;
+}
+
+void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port) {
+    pa_assert(data);
+
+    pa_xfree(data->active_port);
+    data->active_port = pa_xstrdup(port);
+}
+
+void pa_sink_new_data_done(pa_sink_new_data *data) {
+    pa_assert(data);
+
+    pa_proplist_free(data->proplist);
+
+    if (data->ports)
+        pa_hashmap_free(data->ports);
+
+    pa_xfree(data->name);
+    pa_xfree(data->active_port);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_sink *s) {
+    pa_assert(s);
+
+    s->set_state = NULL;
+    s->get_volume = NULL;
+    s->set_volume = NULL;
+    s->write_volume = NULL;
+    s->get_mute = NULL;
+    s->set_mute = NULL;
+    s->request_rewind = NULL;
+    s->update_requested_latency = NULL;
+    s->set_port = NULL;
+    s->get_formats = NULL;
+    s->set_formats = NULL;
+    s->update_rate = NULL;
+}
+
+/* Called from main context */
+pa_sink* pa_sink_new(
+        pa_core *core,
+        pa_sink_new_data *data,
+        pa_sink_flags_t flags) {
+
+    pa_sink *s;
+    const char *name;
+    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    pa_source_new_data source_data;
+    const char *dn;
+    char *pt;
+
+    pa_assert(core);
+    pa_assert(data);
+    pa_assert(data->name);
+    pa_assert_ctl_context();
+
+    s = pa_msgobject_new(pa_sink);
+
+    if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) {
+        pa_log_debug("Failed to register name %s.", data->name);
+        pa_xfree(s);
+        return NULL;
+    }
+
+    pa_sink_new_data_set_name(data, name);
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) {
+        pa_xfree(s);
+        pa_namereg_unregister(core, name);
+        return NULL;
+    }
+
+    /* FIXME, need to free s here on failure */
+
+    pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+    pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
+
+    pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
+
+    if (!data->channel_map_is_set)
+        pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+
+    pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+    pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
+    /* FIXME: There should probably be a general function for checking whether
+     * the sink volume is allowed to be set, like there is for sink inputs. */
+    pa_assert(!data->volume_is_set || !(flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
+
+    if (!data->volume_is_set) {
+        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+        data->save_volume = false;
+    }
+
+    pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
+    pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
+
+    if (!data->muted_is_set)
+        data->muted = false;
+
+    if (data->card)
+        pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
+
+    pa_device_init_description(data->proplist, data->card);
+    pa_device_init_icon(data->proplist, true);
+    pa_device_init_intended_roles(data->proplist);
+
+    if (!data->active_port) {
+        pa_device_port *p = pa_device_port_find_best(data->ports);
+        if (p)
+            pa_sink_new_data_set_port(data, p->name);
+    }
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
+        pa_xfree(s);
+        pa_namereg_unregister(core, name);
+        return NULL;
+    }
+
+    s->parent.parent.free = sink_free;
+    s->parent.process_msg = pa_sink_process_msg;
+
+    s->core = core;
+    s->state = PA_SINK_INIT;
+    s->flags = flags;
+    s->priority = 0;
+    s->suspend_cause = data->suspend_cause;
+    pa_sink_set_mixer_dirty(s, false);
+    s->name = pa_xstrdup(name);
+    s->proplist = pa_proplist_copy(data->proplist);
+    s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
+    s->module = data->module;
+    s->card = data->card;
+
+    s->priority = pa_device_init_priority(s->proplist);
+
+    s->sample_spec = data->sample_spec;
+    s->channel_map = data->channel_map;
+    s->default_sample_rate = s->sample_spec.rate;
+
+    if (data->alternate_sample_rate_is_set)
+        s->alternate_sample_rate = data->alternate_sample_rate;
+    else
+        s->alternate_sample_rate = s->core->alternate_sample_rate;
+
+    if (s->sample_spec.rate == s->alternate_sample_rate) {
+        pa_log_warn("Default and alternate sample rates are the same.");
+        s->alternate_sample_rate = 0;
+    }
+
+    s->inputs = pa_idxset_new(NULL, NULL);
+    s->n_corked = 0;
+    s->input_to_master = NULL;
+
+    s->reference_volume = s->real_volume = data->volume;
+    pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+    s->base_volume = PA_VOLUME_NORM;
+    s->n_volume_steps = PA_VOLUME_NORM+1;
+    s->muted = data->muted;
+    s->refresh_volume = s->refresh_muted = false;
+
+    reset_callbacks(s);
+    s->userdata = NULL;
+
+    s->asyncmsgq = NULL;
+
+    /* As a minor optimization we just steal the list instead of
+     * copying it here */
+    s->ports = data->ports;
+    data->ports = NULL;
+
+    s->active_port = NULL;
+    s->save_port = false;
+
+    if (data->active_port)
+        if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
+            s->save_port = data->save_port;
+
+    /* Hopefully the active port has already been assigned in the previous call
+       to pa_device_port_find_best, but better safe than sorry */
+    if (!s->active_port)
+        s->active_port = pa_device_port_find_best(s->ports);
+
+    if (s->active_port)
+        s->port_latency_offset = s->active_port->latency_offset;
+    else
+        s->port_latency_offset = 0;
+
+    s->save_volume = data->save_volume;
+    s->save_muted = data->save_muted;
+
+    pa_silence_memchunk_get(
+            &core->silence_cache,
+            core->mempool,
+            &s->silence,
+            &s->sample_spec,
+            0);
+
+    s->thread_info.rtpoll = NULL;
+    s->thread_info.inputs = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+                                                (pa_free_cb_t) pa_sink_input_unref);
+    s->thread_info.soft_volume =  s->soft_volume;
+    s->thread_info.soft_muted = s->muted;
+    s->thread_info.state = s->state;
+    s->thread_info.rewind_nbytes = 0;
+    s->thread_info.rewind_requested = false;
+    s->thread_info.max_rewind = 0;
+    s->thread_info.max_request = 0;
+    s->thread_info.requested_latency_valid = false;
+    s->thread_info.requested_latency = 0;
+    s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
+    s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
+    s->thread_info.fixed_latency = flags & PA_SINK_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
+
+    PA_LLIST_HEAD_INIT(pa_sink_volume_change, s->thread_info.volume_changes);
+    s->thread_info.volume_changes_tail = NULL;
+    pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
+    s->thread_info.volume_change_safety_margin = core->deferred_volume_safety_margin_usec;
+    s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec;
+    s->thread_info.port_latency_offset = s->port_latency_offset;
+
+    /* FIXME: This should probably be moved to pa_sink_put() */
+    pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
+
+    if (s->card)
+        pa_assert_se(pa_idxset_put(s->card->sinks, s, NULL) >= 0);
+
+    pt = pa_proplist_to_string_sep(s->proplist, "\n    ");
+    pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n    %s",
+                s->index,
+                s->name,
+                pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
+                pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map),
+                pt);
+    pa_xfree(pt);
+
+    pa_source_new_data_init(&source_data);
+    pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
+    pa_source_new_data_set_channel_map(&source_data, &s->channel_map);
+    pa_source_new_data_set_alternate_sample_rate(&source_data, s->alternate_sample_rate);
+    source_data.name = pa_sprintf_malloc("%s.monitor", name);
+    source_data.driver = data->driver;
+    source_data.module = data->module;
+    source_data.card = data->card;
+
+    dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+    pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
+    pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
+
+    s->monitor_source = pa_source_new(core, &source_data,
+                                      ((flags & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
+                                      ((flags & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0));
+
+    pa_source_new_data_done(&source_data);
+
+    if (!s->monitor_source) {
+        pa_sink_unlink(s);
+        pa_sink_unref(s);
+        return NULL;
+    }
+
+    s->monitor_source->monitor_of = s;
+
+    pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency);
+    pa_source_set_fixed_latency(s->monitor_source, s->thread_info.fixed_latency);
+    pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
+
+    return s;
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+    int ret;
+    bool suspend_change;
+    pa_sink_state_t original_state;
+
+    pa_assert(s);
+    pa_assert_ctl_context();
+
+    if (s->state == state)
+        return 0;
+
+    original_state = s->state;
+
+    suspend_change =
+        (original_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
+        (PA_SINK_IS_OPENED(original_state) && state == PA_SINK_SUSPENDED);
+
+    if (s->set_state)
+        if ((ret = s->set_state(s, state)) < 0)
+            return ret;
+
+    if (s->asyncmsgq)
+        if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
+
+            if (s->set_state)
+                s->set_state(s, original_state);
+
+            return ret;
+        }
+
+    s->state = state;
+
+    if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the appropriate events */
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
+    if (suspend_change) {
+        pa_sink_input *i;
+        uint32_t idx;
+
+        /* We're suspending or resuming, tell everyone about it */
+
+        PA_IDXSET_FOREACH(i, s->inputs, idx)
+            if (s->state == PA_SINK_SUSPENDED &&
+                (i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND))
+                pa_sink_input_kill(i);
+            else if (i->suspend)
+                i->suspend(i, state == PA_SINK_SUSPENDED);
+
+        if (s->monitor_source)
+            pa_source_sync_suspend(s->monitor_source);
+    }
+
+    return 0;
+}
+
+void pa_sink_set_get_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
+    pa_assert(s);
+
+    s->get_volume = cb;
+}
+
+void pa_sink_set_set_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
+    pa_sink_flags_t flags;
+
+    pa_assert(s);
+    pa_assert(!s->write_volume || cb);
+
+    s->set_volume = cb;
+
+    /* Save the current flags so we can tell if they've changed */
+    flags = s->flags;
+
+    if (cb) {
+        /* The sink implementor is responsible for setting decibel volume support */
+        s->flags |= PA_SINK_HW_VOLUME_CTRL;
+    } else {
+        s->flags &= ~PA_SINK_HW_VOLUME_CTRL;
+        /* See note below in pa_sink_put() about volume sharing and decibel volumes */
+        pa_sink_enable_decibel_volume(s, !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
+    }
+
+    /* If the flags have changed after init, let any clients know via a change event */
+    if (s->state != PA_SINK_INIT && flags != s->flags)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
+    pa_sink_flags_t flags;
+
+    pa_assert(s);
+    pa_assert(!cb || s->set_volume);
+
+    s->write_volume = cb;
+
+    /* Save the current flags so we can tell if they've changed */
+    flags = s->flags;
+
+    if (cb)
+        s->flags |= PA_SINK_DEFERRED_VOLUME;
+    else
+        s->flags &= ~PA_SINK_DEFERRED_VOLUME;
+
+    /* If the flags have changed after init, let any clients know via a change event */
+    if (s->state != PA_SINK_INIT && flags != s->flags)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_get_mute_cb_t cb) {
+    pa_assert(s);
+
+    s->get_mute = cb;
+}
+
+void pa_sink_set_set_mute_callback(pa_sink *s, pa_sink_cb_t cb) {
+    pa_sink_flags_t flags;
+
+    pa_assert(s);
+
+    s->set_mute = cb;
+
+    /* Save the current flags so we can tell if they've changed */
+    flags = s->flags;
+
+    if (cb)
+        s->flags |= PA_SINK_HW_MUTE_CTRL;
+    else
+        s->flags &= ~PA_SINK_HW_MUTE_CTRL;
+
+    /* If the flags have changed after init, let any clients know via a change event */
+    if (s->state != PA_SINK_INIT && flags != s->flags)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+static void enable_flat_volume(pa_sink *s, bool enable) {
+    pa_sink_flags_t flags;
+
+    pa_assert(s);
+
+    /* Always follow the overall user preference here */
+    enable = enable && s->core->flat_volumes;
+
+    /* Save the current flags so we can tell if they've changed */
+    flags = s->flags;
+
+    if (enable)
+        s->flags |= PA_SINK_FLAT_VOLUME;
+    else
+        s->flags &= ~PA_SINK_FLAT_VOLUME;
+
+    /* If the flags have changed after init, let any clients know via a change event */
+    if (s->state != PA_SINK_INIT && flags != s->flags)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+void pa_sink_enable_decibel_volume(pa_sink *s, bool enable) {
+    pa_sink_flags_t flags;
+
+    pa_assert(s);
+
+    /* Save the current flags so we can tell if they've changed */
+    flags = s->flags;
+
+    if (enable) {
+        s->flags |= PA_SINK_DECIBEL_VOLUME;
+        enable_flat_volume(s, true);
+    } else {
+        s->flags &= ~PA_SINK_DECIBEL_VOLUME;
+        enable_flat_volume(s, false);
+    }
+
+    /* If the flags have changed after init, let any clients know via a change event */
+    if (s->state != PA_SINK_INIT && flags != s->flags)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main context */
+void pa_sink_put(pa_sink* s) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    pa_assert(s->state == PA_SINK_INIT);
+    pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || pa_sink_is_filter(s));
+
+    /* The following fields must be initialized properly when calling _put() */
+    pa_assert(s->asyncmsgq);
+    pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
+
+    /* Generally, flags should be initialized via pa_sink_new(). As a
+     * special exception we allow some volume related flags to be set
+     * between _new() and _put() by the callback setter functions above.
+     *
+     * Thus we implement a couple safeguards here which ensure the above
+     * setters were used (or at least the implementor made manual changes
+     * in a compatible way).
+     *
+     * Note: All of these flags set here can change over the life time
+     * of the sink. */
+    pa_assert(!(s->flags & PA_SINK_HW_VOLUME_CTRL) || s->set_volume);
+    pa_assert(!(s->flags & PA_SINK_DEFERRED_VOLUME) || s->write_volume);
+    pa_assert(!(s->flags & PA_SINK_HW_MUTE_CTRL) || s->set_mute);
+
+    /* XXX: Currently decibel volume is disabled for all sinks that use volume
+     * sharing. When the master sink supports decibel volume, it would be good
+     * to have the flag also in the filter sink, but currently we don't do that
+     * so that the flags of the filter sink never change when it's moved from
+     * a master sink to another. One solution for this problem would be to
+     * remove user-visible volume altogether from filter sinks when volume
+     * sharing is used, but the current approach was easier to implement... */
+    /* We always support decibel volumes in software, otherwise we leave it to
+     * the sink implementor to set this flag as needed.
+     *
+     * Note: This flag can also change over the life time of the sink. */
+    if (!(s->flags & PA_SINK_HW_VOLUME_CTRL) && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
+        pa_sink_enable_decibel_volume(s, true);
+        s->soft_volume = s->reference_volume;
+    }
+
+    /* If the sink implementor support DB volumes by itself, we should always
+     * try and enable flat volumes too */
+    if ((s->flags & PA_SINK_DECIBEL_VOLUME))
+        enable_flat_volume(s, true);
+
+    if (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) {
+        pa_sink *root_sink = pa_sink_get_master(s);
+
+        pa_assert(root_sink);
+
+        s->reference_volume = root_sink->reference_volume;
+        pa_cvolume_remap(&s->reference_volume, &root_sink->channel_map, &s->channel_map);
+
+        s->real_volume = root_sink->real_volume;
+        pa_cvolume_remap(&s->real_volume, &root_sink->channel_map, &s->channel_map);
+    } else
+        /* We assume that if the sink implementor changed the default
+         * volume he did so in real_volume, because that is the usual
+         * place where he is supposed to place his changes.  */
+        s->reference_volume = s->real_volume;
+
+    s->thread_info.soft_volume = s->soft_volume;
+    s->thread_info.soft_muted = s->muted;
+    pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
+
+    pa_assert((s->flags & PA_SINK_HW_VOLUME_CTRL)
+              || (s->base_volume == PA_VOLUME_NORM
+                  && ((s->flags & PA_SINK_DECIBEL_VOLUME || (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)))));
+    pa_assert(!(s->flags & PA_SINK_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
+    pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == !(s->thread_info.fixed_latency == 0));
+    pa_assert(!(s->flags & PA_SINK_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_LATENCY));
+    pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_DYNAMIC_LATENCY));
+
+    pa_assert(s->monitor_source->thread_info.fixed_latency == s->thread_info.fixed_latency);
+    pa_assert(s->monitor_source->thread_info.min_latency == s->thread_info.min_latency);
+    pa_assert(s->monitor_source->thread_info.max_latency == s->thread_info.max_latency);
+
+    if (s->suspend_cause)
+        pa_assert_se(sink_set_state(s, PA_SINK_SUSPENDED) == 0);
+    else
+        pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
+
+    pa_source_put(s->monitor_source);
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s);
+
+    /* This function must be called after the PA_CORE_HOOK_SINK_PUT hook,
+     * because module-switch-on-connect needs to know the old default sink */
+    pa_core_update_default_sink(s->core);
+}
+
+/* Called from main context */
+void pa_sink_unlink(pa_sink* s) {
+    bool linked;
+    pa_sink_input *i, PA_UNUSED *j = NULL;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    /* Please note that pa_sink_unlink() does more than simply
+     * reversing pa_sink_put(). It also undoes the registrations
+     * already done in pa_sink_new()! */
+
+    if (s->unlink_requested)
+        return;
+
+    s->unlink_requested = true;
+
+    linked = PA_SINK_IS_LINKED(s->state);
+
+    if (linked)
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
+
+    if (s->state != PA_SINK_UNLINKED)
+        pa_namereg_unregister(s->core, s->name);
+    pa_idxset_remove_by_data(s->core->sinks, s, NULL);
+
+    pa_core_update_default_sink(s->core);
+
+    if (s->card)
+        pa_idxset_remove_by_data(s->card->sinks, s, NULL);
+
+    while ((i = pa_idxset_first(s->inputs, NULL))) {
+        pa_assert(i != j);
+        pa_sink_input_kill(i);
+        j = i;
+    }
+
+    if (linked)
+        sink_set_state(s, PA_SINK_UNLINKED);
+    else
+        s->state = PA_SINK_UNLINKED;
+
+    reset_callbacks(s);
+
+    if (s->monitor_source)
+        pa_source_unlink(s->monitor_source);
+
+    if (linked) {
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s);
+    }
+}
+
+/* Called from main context */
+static void sink_free(pa_object *o) {
+    pa_sink *s = PA_SINK(o);
+
+    pa_assert(s);
+    pa_assert_ctl_context();
+    pa_assert(pa_sink_refcnt(s) == 0);
+    pa_assert(!PA_SINK_IS_LINKED(s->state));
+
+    pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
+
+    pa_sink_volume_change_flush(s);
+
+    if (s->monitor_source) {
+        pa_source_unref(s->monitor_source);
+        s->monitor_source = NULL;
+    }
+
+    pa_idxset_free(s->inputs, NULL);
+    pa_hashmap_free(s->thread_info.inputs);
+
+    if (s->silence.memblock)
+        pa_memblock_unref(s->silence.memblock);
+
+    pa_xfree(s->name);
+    pa_xfree(s->driver);
+
+    if (s->proplist)
+        pa_proplist_free(s->proplist);
+
+    if (s->ports)
+        pa_hashmap_free(s->ports);
+
+    pa_xfree(s);
+}
+
+/* Called from main context, and not while the IO thread is active, please */
+void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    s->asyncmsgq = q;
+
+    if (s->monitor_source)
+        pa_source_set_asyncmsgq(s->monitor_source, q);
+}
+
+/* Called from main context, and not while the IO thread is active, please */
+void pa_sink_update_flags(pa_sink *s, pa_sink_flags_t mask, pa_sink_flags_t value) {
+    pa_sink_flags_t old_flags;
+    pa_sink_input *input;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    /* For now, allow only a minimal set of flags to be changed. */
+    pa_assert((mask & ~(PA_SINK_DYNAMIC_LATENCY|PA_SINK_LATENCY)) == 0);
+
+    old_flags = s->flags;
+    s->flags = (s->flags & ~mask) | (value & mask);
+
+    if (s->flags == old_flags)
+        return;
+
+    if ((s->flags & PA_SINK_LATENCY) != (old_flags & PA_SINK_LATENCY))
+        pa_log_debug("Sink %s: LATENCY flag %s.", s->name, (s->flags & PA_SINK_LATENCY) ? "enabled" : "disabled");
+
+    if ((s->flags & PA_SINK_DYNAMIC_LATENCY) != (old_flags & PA_SINK_DYNAMIC_LATENCY))
+        pa_log_debug("Sink %s: DYNAMIC_LATENCY flag %s.",
+                     s->name, (s->flags & PA_SINK_DYNAMIC_LATENCY) ? "enabled" : "disabled");
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_FLAGS_CHANGED], s);
+
+    if (s->monitor_source)
+        pa_source_update_flags(s->monitor_source,
+                               ((mask & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
+                               ((mask & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0),
+                               ((value & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
+                               ((value & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0));
+
+    PA_IDXSET_FOREACH(input, s->inputs, idx) {
+        if (input->origin_sink)
+            pa_sink_update_flags(input->origin_sink, mask, value);
+    }
+}
+
+/* Called from IO context, or before _put() from main context */
+void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+
+    s->thread_info.rtpoll = p;
+
+    if (s->monitor_source)
+        pa_source_set_rtpoll(s->monitor_source, p);
+}
+
+/* Called from main context */
+int pa_sink_update_status(pa_sink*s) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if (s->state == PA_SINK_SUSPENDED)
+        return 0;
+
+    return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
+}
+
+/* Called from any context - must be threadsafe */
+void pa_sink_set_mixer_dirty(pa_sink *s, bool is_dirty) {
+    pa_atomic_store(&s->mixer_dirty, is_dirty ? 1 : 0);
+}
+
+/* Called from main context */
+int pa_sink_suspend(pa_sink *s, bool suspend, pa_suspend_cause_t cause) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(cause != 0);
+
+    if (suspend) {
+        s->suspend_cause |= cause;
+        s->monitor_source->suspend_cause |= cause;
+    } else {
+        s->suspend_cause &= ~cause;
+        s->monitor_source->suspend_cause &= ~cause;
+    }
+
+    if (!(s->suspend_cause & PA_SUSPEND_SESSION) && (pa_atomic_load(&s->mixer_dirty) != 0)) {
+        /* This might look racy but isn't: If somebody sets mixer_dirty exactly here,
+           it'll be handled just fine. */
+        pa_sink_set_mixer_dirty(s, false);
+        pa_log_debug("Mixer is now accessible. Updating alsa mixer settings.");
+        if (s->active_port && s->set_port) {
+            if (s->flags & PA_SINK_DEFERRED_VOLUME) {
+                struct sink_message_set_port msg = { .port = s->active_port, .ret = 0 };
+                pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
+            }
+            else
+                s->set_port(s, s->active_port);
+        }
+        else {
+            if (s->set_mute)
+                s->set_mute(s);
+            if (s->set_volume)
+                s->set_volume(s);
+        }
+    }
+
+    if ((pa_sink_get_state(s) == PA_SINK_SUSPENDED) == !!s->suspend_cause)
+        return 0;
+
+    pa_log_debug("Suspend cause of sink %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming");
+
+    if (s->suspend_cause)
+        return sink_set_state(s, PA_SINK_SUSPENDED);
+    else
+        return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
+}
+
+/* Called from main context */
+pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q) {
+    pa_sink_input *i, *n;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if (!q)
+        q = pa_queue_new();
+
+    for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
+        n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
+
+        pa_sink_input_ref(i);
+
+        if (pa_sink_input_start_move(i) >= 0)
+            pa_queue_push(q, i);
+        else
+            pa_sink_input_unref(i);
+    }
+
+    return q;
+}
+
+/* Called from main context */
+void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, bool save) {
+    pa_sink_input *i;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(q);
+
+    while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
+        if (pa_sink_input_finish_move(i, s, save) < 0)
+            pa_sink_input_fail_move(i);
+
+        pa_sink_input_unref(i);
+    }
+
+    pa_queue_free(q, NULL);
+}
+
+/* Called from main context */
+void pa_sink_move_all_fail(pa_queue *q) {
+    pa_sink_input *i;
+
+    pa_assert_ctl_context();
+    pa_assert(q);
+
+    while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
+        pa_sink_input_fail_move(i);
+        pa_sink_input_unref(i);
+    }
+
+    pa_queue_free(q, NULL);
+}
+
+ /* Called from IO thread context */
+size_t pa_sink_process_input_underruns(pa_sink *s, size_t left_to_play) {
+    pa_sink_input *i;
+    void *state = NULL;
+    size_t result = 0;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+
+    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
+        size_t uf = i->thread_info.underrun_for_sink;
+
+        /* Propagate down the filter tree */
+        if (i->origin_sink) {
+            size_t filter_result, left_to_play_origin;
+
+            /* The recursive call works in the origin sink domain ... */
+            left_to_play_origin = pa_convert_size(left_to_play, &i->sink->sample_spec, &i->origin_sink->sample_spec);
+
+            /* .. and returns the time to sleep before waking up. We need the
+             * underrun duration for comparisons, so we undo the subtraction on
+             * the return value... */
+            filter_result = left_to_play_origin - pa_sink_process_input_underruns(i->origin_sink, left_to_play_origin);
+
+            /* ... and convert it back to the master sink domain */
+            filter_result = pa_convert_size(filter_result, &i->origin_sink->sample_spec, &i->sink->sample_spec);
+
+            /* Remember the longest underrun so far */
+            if (filter_result > result)
+                result = filter_result;
+        }
+
+        if (uf == 0) {
+            /* No underrun here, move on */
+            continue;
+        } else if (uf >= left_to_play) {
+            /* The sink has possibly consumed all the data the sink input provided */
+            pa_sink_input_process_underrun(i);
+        } else if (uf > result) {
+            /* Remember the longest underrun so far */
+            result = uf;
+        }
+    }
+
+    if (result > 0)
+        pa_log_debug("%s: Found underrun %ld bytes ago (%ld bytes ahead in playback buffer)", s->name,
+                (long) result, (long) left_to_play - result);
+    return left_to_play - result;
+}
+
+/* Called from IO thread context */
+void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+    /* If nobody requested this and this is actually no real rewind
+     * then we can short cut this. Please note that this means that
+     * not all rewind requests triggered upstream will always be
+     * translated in actual requests! */
+    if (!s->thread_info.rewind_requested && nbytes <= 0)
+        return;
+
+    s->thread_info.rewind_nbytes = 0;
+    s->thread_info.rewind_requested = false;
+
+    if (nbytes > 0) {
+        pa_log_debug("Processing rewind...");
+        if (s->flags & PA_SINK_DEFERRED_VOLUME)
+            pa_sink_volume_change_rewind(s, nbytes);
+    }
+
+    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
+        pa_sink_input_assert_ref(i);
+        pa_sink_input_process_rewind(i, nbytes);
+    }
+
+    if (nbytes > 0) {
+        if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
+            pa_source_process_rewind(s->monitor_source, nbytes);
+    }
+}
+
+/* Called from IO thread context */
+static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) {
+    pa_sink_input *i;
+    unsigned n = 0;
+    void *state = NULL;
+    size_t mixlength = *length;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+    pa_assert(info);
+
+    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
+        pa_sink_input_assert_ref(i);
+
+        pa_sink_input_peek(i, *length, &info->chunk, &info->volume);
+
+        if (mixlength == 0 || info->chunk.length < mixlength)
+            mixlength = info->chunk.length;
+
+        if (pa_memblock_is_silence(info->chunk.memblock)) {
+            pa_memblock_unref(info->chunk.memblock);
+            continue;
+        }
+
+        info->userdata = pa_sink_input_ref(i);
+
+        pa_assert(info->chunk.memblock);
+        pa_assert(info->chunk.length > 0);
+
+        info++;
+        n++;
+        maxinfo--;
+    }
+
+    if (mixlength > 0)
+        *length = mixlength;
+
+    return n;
+}
+
+/* Called from IO thread context */
+static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
+    pa_sink_input *i;
+    void *state;
+    unsigned p = 0;
+    unsigned n_unreffed = 0;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+    pa_assert(result);
+    pa_assert(result->memblock);
+    pa_assert(result->length > 0);
+
+    /* We optimize for the case where the order of the inputs has not changed */
+
+    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
+        unsigned j;
+        pa_mix_info* m = NULL;
+
+        pa_sink_input_assert_ref(i);
+
+        /* Let's try to find the matching entry info the pa_mix_info array */
+        for (j = 0; j < n; j ++) {
+
+            if (info[p].userdata == i) {
+                m = info + p;
+                break;
+            }
+
+            p++;
+            if (p >= n)
+                p = 0;
+        }
+
+        /* Drop read data */
+        pa_sink_input_drop(i, result->length);
+
+        if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state)) {
+
+            if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) {
+                void *ostate = NULL;
+                pa_source_output *o;
+                pa_memchunk c;
+
+                if (m && m->chunk.memblock) {
+                    c = m->chunk;
+                    pa_memblock_ref(c.memblock);
+                    pa_assert(result->length <= c.length);
+                    c.length = result->length;
+
+                    pa_memchunk_make_writable(&c, 0);
+                    pa_volume_memchunk(&c, &s->sample_spec, &m->volume);
+                } else {
+                    c = s->silence;
+                    pa_memblock_ref(c.memblock);
+                    pa_assert(result->length <= c.length);
+                    c.length = result->length;
+                }
+
+                while ((o = pa_hashmap_iterate(i->thread_info.direct_outputs, &ostate, NULL))) {
+                    pa_source_output_assert_ref(o);
+                    pa_assert(o->direct_on_input == i);
+                    pa_source_post_direct(s->monitor_source, o, &c);
+                }
+
+                pa_memblock_unref(c.memblock);
+            }
+        }
+
+        if (m) {
+            if (m->chunk.memblock) {
+                pa_memblock_unref(m->chunk.memblock);
+                pa_memchunk_reset(&m->chunk);
+            }
+
+            pa_sink_input_unref(m->userdata);
+            m->userdata = NULL;
+
+            n_unreffed += 1;
+        }
+    }
+
+    /* Now drop references to entries that are included in the
+     * pa_mix_info array but don't exist anymore */
+
+    if (n_unreffed < n) {
+        for (; n > 0; info++, n--) {
+            if (info->userdata)
+                pa_sink_input_unref(info->userdata);
+            if (info->chunk.memblock)
+                pa_memblock_unref(info->chunk.memblock);
+        }
+    }
+
+    if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
+        pa_source_post(s->monitor_source, result);
+}
+
+/* Called from IO thread context */
+void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
+    pa_mix_info info[MAX_MIX_CHANNELS];
+    unsigned n;
+    size_t block_size_max;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+    pa_assert(pa_frame_aligned(length, &s->sample_spec));
+    pa_assert(result);
+
+    pa_assert(!s->thread_info.rewind_requested);
+    pa_assert(s->thread_info.rewind_nbytes == 0);
+
+    if (s->thread_info.state == PA_SINK_SUSPENDED) {
+        result->memblock = pa_memblock_ref(s->silence.memblock);
+        result->index = s->silence.index;
+        result->length = PA_MIN(s->silence.length, length);
+        return;
+    }
+
+    pa_sink_ref(s);
+
+    if (length <= 0)
+        length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
+
+    block_size_max = pa_mempool_block_size_max(s->core->mempool);
+    if (length > block_size_max)
+        length = pa_frame_align(block_size_max, &s->sample_spec);
+
+    pa_assert(length > 0);
+
+    n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
+
+    if (n == 0) {
+
+        *result = s->silence;
+        pa_memblock_ref(result->memblock);
+
+        if (result->length > length)
+            result->length = length;
+
+    } else if (n == 1) {
+        pa_cvolume volume;
+
+        *result = info[0].chunk;
+        pa_memblock_ref(result->memblock);
+
+        if (result->length > length)
+            result->length = length;
+
+        pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
+
+        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
+            pa_memblock_unref(result->memblock);
+            pa_silence_memchunk_get(&s->core->silence_cache,
+                                    s->core->mempool,
+                                    result,
+                                    &s->sample_spec,
+                                    result->length);
+        } else if (!pa_cvolume_is_norm(&volume)) {
+            pa_memchunk_make_writable(result, 0);
+            pa_volume_memchunk(result, &s->sample_spec, &volume);
+        }
+    } else {
+        void *ptr;
+        result->memblock = pa_memblock_new(s->core->mempool, length);
+
+        ptr = pa_memblock_acquire(result->memblock);
+        result->length = pa_mix(info, n,
+                                ptr, length,
+                                &s->sample_spec,
+                                &s->thread_info.soft_volume,
+                                s->thread_info.soft_muted);
+        pa_memblock_release(result->memblock);
+
+        result->index = 0;
+    }
+
+    inputs_drop(s, info, n, result);
+
+    pa_sink_unref(s);
+}
+
+/* Called from IO thread context */
+void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
+    pa_mix_info info[MAX_MIX_CHANNELS];
+    unsigned n;
+    size_t length, block_size_max;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+    pa_assert(target);
+    pa_assert(target->memblock);
+    pa_assert(target->length > 0);
+    pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
+
+    pa_assert(!s->thread_info.rewind_requested);
+    pa_assert(s->thread_info.rewind_nbytes == 0);
+
+    if (s->thread_info.state == PA_SINK_SUSPENDED) {
+        pa_silence_memchunk(target, &s->sample_spec);
+        return;
+    }
+
+    pa_sink_ref(s);
+
+    length = target->length;
+    block_size_max = pa_mempool_block_size_max(s->core->mempool);
+    if (length > block_size_max)
+        length = pa_frame_align(block_size_max, &s->sample_spec);
+
+    pa_assert(length > 0);
+
+    n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
+
+    if (n == 0) {
+        if (target->length > length)
+            target->length = length;
+
+        pa_silence_memchunk(target, &s->sample_spec);
+    } else if (n == 1) {
+        pa_cvolume volume;
+
+        if (target->length > length)
+            target->length = length;
+
+        pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
+
+        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
+            pa_silence_memchunk(target, &s->sample_spec);
+        else {
+            pa_memchunk vchunk;
+
+            vchunk = info[0].chunk;
+            pa_memblock_ref(vchunk.memblock);
+
+            if (vchunk.length > length)
+                vchunk.length = length;
+
+            if (!pa_cvolume_is_norm(&volume)) {
+                pa_memchunk_make_writable(&vchunk, 0);
+                pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+            }
+
+            pa_memchunk_memcpy(target, &vchunk);
+            pa_memblock_unref(vchunk.memblock);
+        }
+
+    } else {
+        void *ptr;
+
+        ptr = pa_memblock_acquire(target->memblock);
+
+        target->length = pa_mix(info, n,
+                                (uint8_t*) ptr + target->index, length,
+                                &s->sample_spec,
+                                &s->thread_info.soft_volume,
+                                s->thread_info.soft_muted);
+
+        pa_memblock_release(target->memblock);
+    }
+
+    inputs_drop(s, info, n, target);
+
+    pa_sink_unref(s);
+}
+
+/* Called from IO thread context */
+void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
+    pa_memchunk chunk;
+    size_t l, d;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+    pa_assert(target);
+    pa_assert(target->memblock);
+    pa_assert(target->length > 0);
+    pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
+
+    pa_assert(!s->thread_info.rewind_requested);
+    pa_assert(s->thread_info.rewind_nbytes == 0);
+
+    if (s->thread_info.state == PA_SINK_SUSPENDED) {
+        pa_silence_memchunk(target, &s->sample_spec);
+        return;
+    }
+
+    pa_sink_ref(s);
+
+    l = target->length;
+    d = 0;
+    while (l > 0) {
+        chunk = *target;
+        chunk.index += d;
+        chunk.length -= d;
+
+        pa_sink_render_into(s, &chunk);
+
+        d += chunk.length;
+        l -= chunk.length;
+    }
+
+    pa_sink_unref(s);
+}
+
+/* Called from IO thread context */
+void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+    pa_assert(length > 0);
+    pa_assert(pa_frame_aligned(length, &s->sample_spec));
+    pa_assert(result);
+
+    pa_assert(!s->thread_info.rewind_requested);
+    pa_assert(s->thread_info.rewind_nbytes == 0);
+
+    pa_sink_ref(s);
+
+    pa_sink_render(s, length, result);
+
+    if (result->length < length) {
+        pa_memchunk chunk;
+
+        pa_memchunk_make_writable(result, length);
+
+        chunk.memblock = result->memblock;
+        chunk.index = result->index + result->length;
+        chunk.length = length - result->length;
+
+        pa_sink_render_into_full(s, &chunk);
+
+        result->length = length;
+    }
+
+    pa_sink_unref(s);
+}
+
+/* Called from main thread */
+int pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough) {
+    int ret = -1;
+    uint32_t desired_rate;
+    uint32_t default_rate = s->default_sample_rate;
+    uint32_t alternate_rate = s->alternate_sample_rate;
+    uint32_t idx;
+    pa_sink_input *i;
+    bool default_rate_is_usable = false;
+    bool alternate_rate_is_usable = false;
+    bool avoid_resampling = s->core->avoid_resampling;
+
+    if (rate == s->sample_spec.rate)
+        return 0;
+
+    if (!s->update_rate)
+        return -1;
+
+    if (PA_UNLIKELY(default_rate == alternate_rate && !passthrough && !avoid_resampling)) {
+        pa_log_debug("Default and alternate sample rates are the same, so there is no point in switching.");
+        return -1;
+    }
+
+    if (PA_SINK_IS_RUNNING(s->state)) {
+        pa_log_info("Cannot update rate, SINK_IS_RUNNING, will keep using %u Hz",
+                    s->sample_spec.rate);
+        return -1;
+    }
+
+    if (s->monitor_source) {
+        if (PA_SOURCE_IS_RUNNING(s->monitor_source->state) == true) {
+            pa_log_info("Cannot update rate, monitor source is RUNNING");
+            return -1;
+        }
+    }
+
+    if (PA_UNLIKELY(!pa_sample_rate_valid(rate)))
+        return -1;
+
+    if (passthrough) {
+        /* We have to try to use the sink input rate */
+        desired_rate = rate;
+
+    } else if (avoid_resampling && (rate >= default_rate || rate >= alternate_rate)) {
+        /* We just try to set the sink input's sample rate if it's not too low */
+        desired_rate = rate;
+
+    } else if (default_rate == rate || alternate_rate == rate) {
+        /* We can directly try to use this rate */
+        desired_rate = rate;
+
+    } else {
+        /* See if we can pick a rate that results in less resampling effort */
+        if (default_rate % 11025 == 0 && rate % 11025 == 0)
+            default_rate_is_usable = true;
+        if (default_rate % 4000 == 0 && rate % 4000 == 0)
+            default_rate_is_usable = true;
+        if (alternate_rate && alternate_rate % 11025 == 0 && rate % 11025 == 0)
+            alternate_rate_is_usable = true;
+        if (alternate_rate && alternate_rate % 4000 == 0 && rate % 4000 == 0)
+            alternate_rate_is_usable = true;
+
+        if (alternate_rate_is_usable && !default_rate_is_usable)
+            desired_rate = alternate_rate;
+        else
+            desired_rate = default_rate;
+    }
+
+    if (desired_rate == s->sample_spec.rate)
+        return -1;
+
+    if (!passthrough && pa_sink_used_by(s) > 0)
+        return -1;
+
+    pa_log_debug("Suspending sink %s due to changing the sample rate.", s->name);
+    pa_sink_suspend(s, true, PA_SUSPEND_INTERNAL);
+
+    if (s->update_rate(s, desired_rate) >= 0) {
+        /* update monitor source as well */
+        if (s->monitor_source && !passthrough)
+            pa_source_update_rate(s->monitor_source, desired_rate, false);
+        pa_log_info("Changed sampling rate successfully");
+
+        PA_IDXSET_FOREACH(i, s->inputs, idx) {
+            if (i->state == PA_SINK_INPUT_CORKED)
+                pa_sink_input_update_rate(i);
+        }
+
+        ret = 0;
+    }
+
+    pa_sink_suspend(s, false, PA_SUSPEND_INTERNAL);
+
+    return ret;
+}
+
+/* Called from main thread */
+pa_usec_t pa_sink_get_latency(pa_sink *s) {
+    int64_t usec = 0;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    /* The returned value is supposed to be in the time domain of the sound card! */
+
+    if (s->state == PA_SINK_SUSPENDED)
+        return 0;
+
+    if (!(s->flags & PA_SINK_LATENCY))
+        return 0;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
+
+    /* the return value is unsigned, so check that the offset can be added to usec without
+     * underflowing. */
+    if (-s->port_latency_offset <= usec)
+        usec += s->port_latency_offset;
+    else
+        usec = 0;
+
+    return (pa_usec_t)usec;
+}
+
+/* Called from IO thread */
+int64_t pa_sink_get_latency_within_thread(pa_sink *s, bool allow_negative) {
+    int64_t usec = 0;
+    pa_msgobject *o;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+    /* The returned value is supposed to be in the time domain of the sound card! */
+
+    if (s->thread_info.state == PA_SINK_SUSPENDED)
+        return 0;
+
+    if (!(s->flags & PA_SINK_LATENCY))
+        return 0;
+
+    o = PA_MSGOBJECT(s);
+
+    /* FIXME: We probably should make this a proper vtable callback instead of going through process_msg() */
+
+    o->process_msg(o, PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL);
+
+    /* If allow_negative is false, the call should only return positive values, */
+    usec += s->thread_info.port_latency_offset;
+    if (!allow_negative && usec < 0)
+        usec = 0;
+
+    return usec;
+}
+
+/* Called from the main thread (and also from the IO thread while the main
+ * thread is waiting).
+ *
+ * When a sink uses volume sharing, it never has the PA_SINK_FLAT_VOLUME flag
+ * set. Instead, flat volume mode is detected by checking whether the root sink
+ * has the flag set. */
+bool pa_sink_flat_volume_enabled(pa_sink *s) {
+    pa_sink_assert_ref(s);
+
+    s = pa_sink_get_master(s);
+
+    if (PA_LIKELY(s))
+        return (s->flags & PA_SINK_FLAT_VOLUME);
+    else
+        return false;
+}
+
+/* Called from the main thread (and also from the IO thread while the main
+ * thread is waiting). */
+pa_sink *pa_sink_get_master(pa_sink *s) {
+    pa_sink_assert_ref(s);
+
+    while (s && (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
+        if (PA_UNLIKELY(!s->input_to_master))
+            return NULL;
+
+        s = s->input_to_master->sink;
+    }
+
+    return s;
+}
+
+/* Called from main context */
+bool pa_sink_is_filter(pa_sink *s) {
+    pa_sink_assert_ref(s);
+
+    return (s->input_to_master != NULL);
+}
+
+/* Called from main context */
+bool pa_sink_is_passthrough(pa_sink *s) {
+    pa_sink_input *alt_i;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+
+    /* one and only one PASSTHROUGH input can possibly be connected */
+    if (pa_idxset_size(s->inputs) == 1) {
+        alt_i = pa_idxset_first(s->inputs, &idx);
+
+        if (pa_sink_input_is_passthrough(alt_i))
+            return true;
+    }
+
+    return false;
+}
+
+/* Called from main context */
+void pa_sink_enter_passthrough(pa_sink *s) {
+    pa_cvolume volume;
+
+    /* disable the monitor in passthrough mode */
+    if (s->monitor_source) {
+        pa_log_debug("Suspending monitor source %s, because the sink is entering the passthrough mode.", s->monitor_source->name);
+        pa_source_suspend(s->monitor_source, true, PA_SUSPEND_PASSTHROUGH);
+    }
+
+    /* set the volume to NORM */
+    s->saved_volume = *pa_sink_get_volume(s, true);
+    s->saved_save_volume = s->save_volume;
+
+    pa_cvolume_set(&volume, s->sample_spec.channels, PA_MIN(s->base_volume, PA_VOLUME_NORM));
+    pa_sink_set_volume(s, &volume, true, false);
+}
+
+/* Called from main context */
+void pa_sink_leave_passthrough(pa_sink *s) {
+    /* Unsuspend monitor */
+    if (s->monitor_source) {
+        pa_log_debug("Resuming monitor source %s, because the sink is leaving the passthrough mode.", s->monitor_source->name);
+        pa_source_suspend(s->monitor_source, false, PA_SUSPEND_PASSTHROUGH);
+    }
+
+    /* Restore sink volume to what it was before we entered passthrough mode */
+    pa_sink_set_volume(s, &s->saved_volume, true, s->saved_save_volume);
+
+    pa_cvolume_init(&s->saved_volume);
+    s->saved_save_volume = false;
+}
+
+/* Called from main context. */
+static void compute_reference_ratio(pa_sink_input *i) {
+    unsigned c = 0;
+    pa_cvolume remapped;
+    pa_cvolume ratio;
+
+    pa_assert(i);
+    pa_assert(pa_sink_flat_volume_enabled(i->sink));
+
+    /*
+     * Calculates the reference ratio from the sink's reference
+     * volume. This basically calculates:
+     *
+     * i->reference_ratio = i->volume / i->sink->reference_volume
+     */
+
+    remapped = i->sink->reference_volume;
+    pa_cvolume_remap(&remapped, &i->sink->channel_map, &i->channel_map);
+
+    ratio = i->reference_ratio;
+
+    for (c = 0; c < i->sample_spec.channels; c++) {
+
+        /* We don't update when the sink volume is 0 anyway */
+        if (remapped.values[c] <= PA_VOLUME_MUTED)
+            continue;
+
+        /* Don't update the reference ratio unless necessary */
+        if (pa_sw_volume_multiply(
+                    ratio.values[c],
+                    remapped.values[c]) == i->volume.values[c])
+            continue;
+
+        ratio.values[c] = pa_sw_volume_divide(
+                i->volume.values[c],
+                remapped.values[c]);
+    }
+
+    pa_sink_input_set_reference_ratio(i, &ratio);
+}
+
+/* Called from main context. Only called for the root sink in volume sharing
+ * cases, except for internal recursive calls. */
+static void compute_reference_ratios(pa_sink *s) {
+    uint32_t idx;
+    pa_sink_input *i;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(pa_sink_flat_volume_enabled(s));
+
+    PA_IDXSET_FOREACH(i, s->inputs, idx) {
+        compute_reference_ratio(i);
+
+        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)
+                && PA_SINK_IS_LINKED(i->origin_sink->state))
+            compute_reference_ratios(i->origin_sink);
+    }
+}
+
+/* Called from main context. Only called for the root sink in volume sharing
+ * cases, except for internal recursive calls. */
+static void compute_real_ratios(pa_sink *s) {
+    pa_sink_input *i;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(pa_sink_flat_volume_enabled(s));
+
+    PA_IDXSET_FOREACH(i, s->inputs, idx) {
+        unsigned c;
+        pa_cvolume remapped;
+
+        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
+            /* The origin sink uses volume sharing, so this input's real ratio
+             * is handled as a special case - the real ratio must be 0 dB, and
+             * as a result i->soft_volume must equal i->volume_factor. */
+            pa_cvolume_reset(&i->real_ratio, i->real_ratio.channels);
+            i->soft_volume = i->volume_factor;
+
+            if (PA_SINK_IS_LINKED(i->origin_sink->state))
+                compute_real_ratios(i->origin_sink);
+
+            continue;
+        }
+
+        /*
+         * This basically calculates:
+         *
+         * i->real_ratio := i->volume / s->real_volume
+         * i->soft_volume := i->real_ratio * i->volume_factor
+         */
+
+        remapped = s->real_volume;
+        pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
+
+        i->real_ratio.channels = i->sample_spec.channels;
+        i->soft_volume.channels = i->sample_spec.channels;
+
+        for (c = 0; c < i->sample_spec.channels; c++) {
+
+            if (remapped.values[c] <= PA_VOLUME_MUTED) {
+                /* We leave i->real_ratio untouched */
+                i->soft_volume.values[c] = PA_VOLUME_MUTED;
+                continue;
+            }
+
+            /* Don't lose accuracy unless necessary */
+            if (pa_sw_volume_multiply(
+                        i->real_ratio.values[c],
+                        remapped.values[c]) != i->volume.values[c])
+
+                i->real_ratio.values[c] = pa_sw_volume_divide(
+                        i->volume.values[c],
+                        remapped.values[c]);
+
+            i->soft_volume.values[c] = pa_sw_volume_multiply(
+                    i->real_ratio.values[c],
+                    i->volume_factor.values[c]);
+        }
+
+        /* We don't copy the soft_volume to the thread_info data
+         * here. That must be done by the caller */
+    }
+}
+
+static pa_cvolume *cvolume_remap_minimal_impact(
+        pa_cvolume *v,
+        const pa_cvolume *template,
+        const pa_channel_map *from,
+        const pa_channel_map *to) {
+
+    pa_cvolume t;
+
+    pa_assert(v);
+    pa_assert(template);
+    pa_assert(from);
+    pa_assert(to);
+    pa_assert(pa_cvolume_compatible_with_channel_map(v, from));
+    pa_assert(pa_cvolume_compatible_with_channel_map(template, to));
+
+    /* Much like pa_cvolume_remap(), but tries to minimize impact when
+     * mapping from sink input to sink volumes:
+     *
+     * If template is a possible remapping from v it is used instead
+     * of remapping anew.
+     *
+     * If the channel maps don't match we set an all-channel volume on
+     * the sink to ensure that changing a volume on one stream has no
+     * effect that cannot be compensated for in another stream that
+     * does not have the same channel map as the sink. */
+
+    if (pa_channel_map_equal(from, to))
+        return v;
+
+    t = *template;
+    if (pa_cvolume_equal(pa_cvolume_remap(&t, to, from), v)) {
+        *v = *template;
+        return v;
+    }
+
+    pa_cvolume_set(v, to->channels, pa_cvolume_max(v));
+    return v;
+}
+
+/* Called from main thread. Only called for the root sink in volume sharing
+ * cases, except for internal recursive calls. */
+static void get_maximum_input_volume(pa_sink *s, pa_cvolume *max_volume, const pa_channel_map *channel_map) {
+    pa_sink_input *i;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+    pa_assert(max_volume);
+    pa_assert(channel_map);
+    pa_assert(pa_sink_flat_volume_enabled(s));
+
+    PA_IDXSET_FOREACH(i, s->inputs, idx) {
+        pa_cvolume remapped;
+
+        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
+            if (PA_SINK_IS_LINKED(i->origin_sink->state))
+                get_maximum_input_volume(i->origin_sink, max_volume, channel_map);
+
+            /* Ignore this input. The origin sink uses volume sharing, so this
+             * input's volume will be set to be equal to the root sink's real
+             * volume. Obviously this input's current volume must not then
+             * affect what the root sink's real volume will be. */
+            continue;
+        }
+
+        remapped = i->volume;
+        cvolume_remap_minimal_impact(&remapped, max_volume, &i->channel_map, channel_map);
+        pa_cvolume_merge(max_volume, max_volume, &remapped);
+    }
+}
+
+/* Called from main thread. Only called for the root sink in volume sharing
+ * cases, except for internal recursive calls. */
+static bool has_inputs(pa_sink *s) {
+    pa_sink_input *i;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+
+    PA_IDXSET_FOREACH(i, s->inputs, idx) {
+        if (!i->origin_sink || !(i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || has_inputs(i->origin_sink))
+            return true;
+    }
+
+    return false;
+}
+
+/* Called from main thread. Only called for the root sink in volume sharing
+ * cases, except for internal recursive calls. */
+static void update_real_volume(pa_sink *s, const pa_cvolume *new_volume, pa_channel_map *channel_map) {
+    pa_sink_input *i;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+    pa_assert(new_volume);
+    pa_assert(channel_map);
+
+    s->real_volume = *new_volume;
+    pa_cvolume_remap(&s->real_volume, channel_map, &s->channel_map);
+
+    PA_IDXSET_FOREACH(i, s->inputs, idx) {
+        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
+            if (pa_sink_flat_volume_enabled(s)) {
+                pa_cvolume new_input_volume;
+
+                /* Follow the root sink's real volume. */
+                new_input_volume = *new_volume;
+                pa_cvolume_remap(&new_input_volume, channel_map, &i->channel_map);
+                pa_sink_input_set_volume_direct(i, &new_input_volume);
+                compute_reference_ratio(i);
+            }
+
+            if (PA_SINK_IS_LINKED(i->origin_sink->state))
+                update_real_volume(i->origin_sink, new_volume, channel_map);
+        }
+    }
+}
+
+/* Called from main thread. Only called for the root sink in shared volume
+ * cases. */
+static void compute_real_volume(pa_sink *s) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(pa_sink_flat_volume_enabled(s));
+    pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
+
+    /* This determines the maximum volume of all streams and sets
+     * s->real_volume accordingly. */
+
+    if (!has_inputs(s)) {
+        /* In the special case that we have no sink inputs we leave the
+         * volume unmodified. */
+        update_real_volume(s, &s->reference_volume, &s->channel_map);
+        return;
+    }
+
+    pa_cvolume_mute(&s->real_volume, s->channel_map.channels);
+
+    /* First let's determine the new maximum volume of all inputs
+     * connected to this sink */
+    get_maximum_input_volume(s, &s->real_volume, &s->channel_map);
+    update_real_volume(s, &s->real_volume, &s->channel_map);
+
+    /* Then, let's update the real ratios/soft volumes of all inputs
+     * connected to this sink */
+    compute_real_ratios(s);
+}
+
+/* Called from main thread. Only called for the root sink in shared volume
+ * cases, except for internal recursive calls. */
+static void propagate_reference_volume(pa_sink *s) {
+    pa_sink_input *i;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(pa_sink_flat_volume_enabled(s));
+
+    /* This is called whenever the sink volume changes that is not
+     * caused by a sink input volume change. We need to fix up the
+     * sink input volumes accordingly */
+
+    PA_IDXSET_FOREACH(i, s->inputs, idx) {
+        pa_cvolume new_volume;
+
+        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
+            if (PA_SINK_IS_LINKED(i->origin_sink->state))
+                propagate_reference_volume(i->origin_sink);
+
+            /* Since the origin sink uses volume sharing, this input's volume
+             * needs to be updated to match the root sink's real volume, but
+             * that will be done later in update_real_volume(). */
+            continue;
+        }
+
+        /* This basically calculates:
+         *
+         * i->volume := s->reference_volume * i->reference_ratio  */
+
+        new_volume = s->reference_volume;
+        pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map);
+        pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
+        pa_sink_input_set_volume_direct(i, &new_volume);
+    }
+}
+
+/* Called from main thread. Only called for the root sink in volume sharing
+ * cases, except for internal recursive calls. The return value indicates
+ * whether any reference volume actually changed. */
+static bool update_reference_volume(pa_sink *s, const pa_cvolume *v, const pa_channel_map *channel_map, bool save) {
+    pa_cvolume volume;
+    bool reference_volume_changed;
+    pa_sink_input *i;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(v);
+    pa_assert(channel_map);
+    pa_assert(pa_cvolume_valid(v));
+
+    volume = *v;
+    pa_cvolume_remap(&volume, channel_map, &s->channel_map);
+
+    reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume);
+    pa_sink_set_reference_volume_direct(s, &volume);
+
+    s->save_volume = (!reference_volume_changed && s->save_volume) || save;
+
+    if (!reference_volume_changed && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
+        /* If the root sink's volume doesn't change, then there can't be any
+         * changes in the other sinks in the sink tree either.
+         *
+         * It's probably theoretically possible that even if the root sink's
+         * volume changes slightly, some filter sink doesn't change its volume
+         * due to rounding errors. If that happens, we still want to propagate
+         * the changed root sink volume to the sinks connected to the
+         * intermediate sink that didn't change its volume. This theoretical
+         * possibility is the reason why we have that !(s->flags &
+         * PA_SINK_SHARE_VOLUME_WITH_MASTER) condition. Probably nobody would
+         * notice even if we returned here false always if
+         * reference_volume_changed is false. */
+        return false;
+
+    PA_IDXSET_FOREACH(i, s->inputs, idx) {
+        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)
+                && PA_SINK_IS_LINKED(i->origin_sink->state))
+            update_reference_volume(i->origin_sink, v, channel_map, false);
+    }
+
+    return true;
+}
+
+/* Called from main thread */
+void pa_sink_set_volume(
+        pa_sink *s,
+        const pa_cvolume *volume,
+        bool send_msg,
+        bool save) {
+
+    pa_cvolume new_reference_volume;
+    pa_sink *root_sink;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(!volume || pa_cvolume_valid(volume));
+    pa_assert(volume || pa_sink_flat_volume_enabled(s));
+    pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
+
+    /* make sure we don't change the volume when a PASSTHROUGH input is connected ...
+     * ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */
+    if (pa_sink_is_passthrough(s) && (!volume || !pa_cvolume_is_norm(volume))) {
+        pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input");
+        return;
+    }
+
+    /* In case of volume sharing, the volume is set for the root sink first,
+     * from which it's then propagated to the sharing sinks. */
+    root_sink = pa_sink_get_master(s);
+
+    if (PA_UNLIKELY(!root_sink))
+        return;
+
+    /* As a special exception we accept mono volumes on all sinks --
+     * even on those with more complex channel maps */
+
+    if (volume) {
+        if (pa_cvolume_compatible(volume, &s->sample_spec))
+            new_reference_volume = *volume;
+        else {
+            new_reference_volume = s->reference_volume;
+            pa_cvolume_scale(&new_reference_volume, pa_cvolume_max(volume));
+        }
+
+        pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_sink->channel_map);
+
+        if (update_reference_volume(root_sink, &new_reference_volume, &root_sink->channel_map, save)) {
+            if (pa_sink_flat_volume_enabled(root_sink)) {
+                /* OK, propagate this volume change back to the inputs */
+                propagate_reference_volume(root_sink);
+
+                /* And now recalculate the real volume */
+                compute_real_volume(root_sink);
+            } else
+                update_real_volume(root_sink, &root_sink->reference_volume, &root_sink->channel_map);
+        }
+
+    } else {
+        /* If volume is NULL we synchronize the sink's real and
+         * reference volumes with the stream volumes. */
+
+        pa_assert(pa_sink_flat_volume_enabled(root_sink));
+
+        /* Ok, let's determine the new real volume */
+        compute_real_volume(root_sink);
+
+        /* Let's 'push' the reference volume if necessary */
+        pa_cvolume_merge(&new_reference_volume, &s->reference_volume, &root_sink->real_volume);
+        /* If the sink and its root don't have the same number of channels, we need to remap */
+        if (s != root_sink && !pa_channel_map_equal(&s->channel_map, &root_sink->channel_map))
+            pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_sink->channel_map);
+        update_reference_volume(root_sink, &new_reference_volume, &root_sink->channel_map, save);
+
+        /* Now that the reference volume is updated, we can update the streams'
+         * reference ratios. */
+        compute_reference_ratios(root_sink);
+    }
+
+    if (root_sink->set_volume) {
+        /* If we have a function set_volume(), then we do not apply a
+         * soft volume by default. However, set_volume() is free to
+         * apply one to root_sink->soft_volume */
+
+        pa_cvolume_reset(&root_sink->soft_volume, root_sink->sample_spec.channels);
+        if (!(root_sink->flags & PA_SINK_DEFERRED_VOLUME))
+            root_sink->set_volume(root_sink);
+
+    } else
+        /* If we have no function set_volume(), then the soft volume
+         * becomes the real volume */
+        root_sink->soft_volume = root_sink->real_volume;
+
+    /* This tells the sink that soft volume and/or real volume changed */
+    if (send_msg)
+        pa_assert_se(pa_asyncmsgq_send(root_sink->asyncmsgq, PA_MSGOBJECT(root_sink), PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0);
+}
+
+/* Called from the io thread if sync volume is used, otherwise from the main thread.
+ * Only to be called by sink implementor */
+void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
+
+    pa_sink_assert_ref(s);
+    pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
+
+    if (s->flags & PA_SINK_DEFERRED_VOLUME)
+        pa_sink_assert_io_context(s);
+    else
+        pa_assert_ctl_context();
+
+    if (!volume)
+        pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+    else
+        s->soft_volume = *volume;
+
+    if (PA_SINK_IS_LINKED(s->state) && !(s->flags & PA_SINK_DEFERRED_VOLUME))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
+    else
+        s->thread_info.soft_volume = s->soft_volume;
+}
+
+/* Called from the main thread. Only called for the root sink in volume sharing
+ * cases, except for internal recursive calls. */
+static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) {
+    pa_sink_input *i;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+    pa_assert(old_real_volume);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    /* This is called when the hardware's real volume changes due to
+     * some external event. We copy the real volume into our
+     * reference volume and then rebuild the stream volumes based on
+     * i->real_ratio which should stay fixed. */
+
+    if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
+        if (pa_cvolume_equal(old_real_volume, &s->real_volume))
+            return;
+
+        /* 1. Make the real volume the reference volume */
+        update_reference_volume(s, &s->real_volume, &s->channel_map, true);
+    }
+
+    if (pa_sink_flat_volume_enabled(s)) {
+
+        PA_IDXSET_FOREACH(i, s->inputs, idx) {
+            pa_cvolume new_volume;
+
+            /* 2. Since the sink's reference and real volumes are equal
+             * now our ratios should be too. */
+            pa_sink_input_set_reference_ratio(i, &i->real_ratio);
+
+            /* 3. Recalculate the new stream reference volume based on the
+             * reference ratio and the sink's reference volume.
+             *
+             * This basically calculates:
+             *
+             * i->volume = s->reference_volume * i->reference_ratio
+             *
+             * This is identical to propagate_reference_volume() */
+            new_volume = s->reference_volume;
+            pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map);
+            pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
+            pa_sink_input_set_volume_direct(i, &new_volume);
+
+            if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)
+                    && PA_SINK_IS_LINKED(i->origin_sink->state))
+                propagate_real_volume(i->origin_sink, old_real_volume);
+        }
+    }
+
+    /* Something got changed in the hardware. It probably makes sense
+     * to save changed hw settings given that hw volume changes not
+     * triggered by PA are almost certainly done by the user. */
+    if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
+        s->save_volume = true;
+}
+
+/* Called from io thread */
+void pa_sink_update_volume_and_mute(pa_sink *s) {
+    pa_assert(s);
+    pa_sink_assert_io_context(s);
+
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE, NULL, 0, NULL, NULL);
+}
+
+/* Called from main thread */
+const pa_cvolume *pa_sink_get_volume(pa_sink *s, bool force_refresh) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if (s->refresh_volume || force_refresh) {
+        struct pa_cvolume old_real_volume;
+
+        pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
+
+        old_real_volume = s->real_volume;
+
+        if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume)
+            s->get_volume(s);
+
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
+
+        update_real_volume(s, &s->real_volume, &s->channel_map);
+        propagate_real_volume(s, &old_real_volume);
+    }
+
+    return &s->reference_volume;
+}
+
+/* Called from main thread. In volume sharing cases, only the root sink may
+ * call this. */
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_real_volume) {
+    pa_cvolume old_real_volume;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
+
+    /* The sink implementor may call this if the volume changed to make sure everyone is notified */
+
+    old_real_volume = s->real_volume;
+    update_real_volume(s, new_real_volume, &s->channel_map);
+    propagate_real_volume(s, &old_real_volume);
+}
+
+/* Called from main thread */
+void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
+    bool old_muted;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    old_muted = s->muted;
+
+    if (mute == old_muted) {
+        s->save_muted |= save;
+        return;
+    }
+
+    s->muted = mute;
+    s->save_muted = save;
+
+    if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute) {
+        s->set_mute_in_progress = true;
+        s->set_mute(s);
+        s->set_mute_in_progress = false;
+    }
+
+    if (!PA_SINK_IS_LINKED(s->state))
+        return;
+
+    pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED], s);
+}
+
+/* Called from main thread */
+bool pa_sink_get_mute(pa_sink *s, bool force_refresh) {
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if ((s->refresh_muted || force_refresh) && s->get_mute) {
+        bool mute;
+
+        if (s->flags & PA_SINK_DEFERRED_VOLUME) {
+            if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &mute, 0, NULL) >= 0)
+                pa_sink_mute_changed(s, mute);
+        } else {
+            if (s->get_mute(s, &mute) >= 0)
+                pa_sink_mute_changed(s, mute);
+        }
+    }
+
+    return s->muted;
+}
+
+/* Called from main thread */
+void pa_sink_mute_changed(pa_sink *s, bool new_muted) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if (s->set_mute_in_progress)
+        return;
+
+    /* pa_sink_set_mute() does this same check, so this may appear redundant,
+     * but we must have this here also, because the save parameter of
+     * pa_sink_set_mute() would otherwise have unintended side effects (saving
+     * the mute state when it shouldn't be saved). */
+    if (new_muted == s->muted)
+        return;
+
+    pa_sink_set_mute(s, new_muted, true);
+}
+
+/* Called from main thread */
+bool pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (p)
+        pa_proplist_update(s->proplist, mode, p);
+
+    if (PA_SINK_IS_LINKED(s->state)) {
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
+    return true;
+}
+
+/* Called from main thread */
+/* FIXME -- this should be dropped and be merged into pa_sink_update_proplist() */
+void pa_sink_set_description(pa_sink *s, const char *description) {
+    const char *old;
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
+        return;
+
+    old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+    if (old && description && pa_streq(old, description))
+        return;
+
+    if (description)
+        pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
+    else
+        pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+    if (s->monitor_source) {
+        char *n;
+
+        n = pa_sprintf_malloc("Monitor Source of %s", description ? description : s->name);
+        pa_source_set_description(s->monitor_source, n);
+        pa_xfree(n);
+    }
+
+    if (PA_SINK_IS_LINKED(s->state)) {
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
+    }
+}
+
+/* Called from main thread */
+unsigned pa_sink_linked_by(pa_sink *s) {
+    unsigned ret;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    ret = pa_idxset_size(s->inputs);
+
+    /* We add in the number of streams connected to us here. Please
+     * note the asymmetry to pa_sink_used_by()! */
+
+    if (s->monitor_source)
+        ret += pa_source_linked_by(s->monitor_source);
+
+    return ret;
+}
+
+/* Called from main thread */
+unsigned pa_sink_used_by(pa_sink *s) {
+    unsigned ret;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    ret = pa_idxset_size(s->inputs);
+    pa_assert(ret >= s->n_corked);
+
+    /* Streams connected to our monitor source do not matter for
+     * pa_sink_used_by()!.*/
+
+    return ret - s->n_corked;
+}
+
+/* Called from main thread */
+unsigned pa_sink_check_suspend(pa_sink *s, pa_sink_input *ignore_input, pa_source_output *ignore_output) {
+    unsigned ret;
+    pa_sink_input *i;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (!PA_SINK_IS_LINKED(s->state))
+        return 0;
+
+    ret = 0;
+
+    PA_IDXSET_FOREACH(i, s->inputs, idx) {
+        pa_sink_input_state_t st;
+
+        if (i == ignore_input)
+            continue;
+
+        st = pa_sink_input_get_state(i);
+
+        /* We do not assert here. It is perfectly valid for a sink input to
+         * be in the INIT state (i.e. created, marked done but not yet put)
+         * and we should not care if it's unlinked as it won't contribute
+         * towards our busy status.
+         */
+        if (!PA_SINK_INPUT_IS_LINKED(st))
+            continue;
+
+        if (st == PA_SINK_INPUT_CORKED)
+            continue;
+
+        if (i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND)
+            continue;
+
+        ret ++;
+    }
+
+    if (s->monitor_source)
+        ret += pa_source_check_suspend(s->monitor_source, ignore_output);
+
+    return ret;
+}
+
+/* Called from the IO thread */
+static void sync_input_volumes_within_thread(pa_sink *s) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+
+    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
+        if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
+            continue;
+
+        i->thread_info.soft_volume = i->soft_volume;
+        pa_sink_input_request_rewind(i, 0, true, false, false);
+    }
+}
+
+/* Called from the IO thread. Only called for the root sink in volume sharing
+ * cases, except for internal recursive calls. */
+static void set_shared_volume_within_thread(pa_sink *s) {
+    pa_sink_input *i = NULL;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+
+    PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL);
+
+    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
+        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
+            set_shared_volume_within_thread(i->origin_sink);
+    }
+}
+
+/* Called from IO thread, except when it is not */
+int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_sink *s = PA_SINK(o);
+    pa_sink_assert_ref(s);
+
+    switch ((pa_sink_message_t) code) {
+
+        case PA_SINK_MESSAGE_ADD_INPUT: {
+            pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+            /* If you change anything here, make sure to change the
+             * sink input handling a few lines down at
+             * PA_SINK_MESSAGE_FINISH_MOVE, too. */
+
+            pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
+
+            /* Since the caller sleeps in pa_sink_input_put(), we can
+             * safely access data outside of thread_info even though
+             * it is mutable */
+
+            if ((i->thread_info.sync_prev = i->sync_prev)) {
+                pa_assert(i->sink == i->thread_info.sync_prev->sink);
+                pa_assert(i->sync_prev->sync_next == i);
+                i->thread_info.sync_prev->thread_info.sync_next = i;
+            }
+
+            if ((i->thread_info.sync_next = i->sync_next)) {
+                pa_assert(i->sink == i->thread_info.sync_next->sink);
+                pa_assert(i->sync_next->sync_prev == i);
+                i->thread_info.sync_next->thread_info.sync_prev = i;
+            }
+
+            pa_sink_input_attach(i);
+
+            pa_sink_input_set_state_within_thread(i, i->state);
+
+            /* The requested latency of the sink input needs to be fixed up and
+             * then configured on the sink. If this causes the sink latency to
+             * go down, the sink implementor is responsible for doing a rewind
+             * in the update_requested_latency() callback to ensure that the
+             * sink buffer doesn't contain more data than what the new latency
+             * allows.
+             *
+             * XXX: Does it really make sense to push this responsibility to
+             * the sink implementors? Wouldn't it be better to do it once in
+             * the core than many times in the modules? */
+
+            if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
+                pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
+
+            pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+            pa_sink_input_update_max_request(i, s->thread_info.max_request);
+
+            /* We don't rewind here automatically. This is left to the
+             * sink input implementor because some sink inputs need a
+             * slow start, i.e. need some time to buffer client
+             * samples before beginning streaming.
+             *
+             * XXX: Does it really make sense to push this functionality to
+             * the sink implementors? Wouldn't it be better to do it once in
+             * the core than many times in the modules? */
+
+            /* In flat volume mode we need to update the volume as
+             * well */
+            return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
+        }
+
+        case PA_SINK_MESSAGE_REMOVE_INPUT: {
+            pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+            /* If you change anything here, make sure to change the
+             * sink input handling a few lines down at
+             * PA_SINK_MESSAGE_START_MOVE, too. */
+
+            pa_sink_input_detach(i);
+
+            pa_sink_input_set_state_within_thread(i, i->state);
+
+            /* Since the caller sleeps in pa_sink_input_unlink(),
+             * we can safely access data outside of thread_info even
+             * though it is mutable */
+
+            pa_assert(!i->sync_prev);
+            pa_assert(!i->sync_next);
+
+            if (i->thread_info.sync_prev) {
+                i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next;
+                i->thread_info.sync_prev = NULL;
+            }
+
+            if (i->thread_info.sync_next) {
+                i->thread_info.sync_next->thread_info.sync_prev = i->thread_info.sync_next->sync_prev;
+                i->thread_info.sync_next = NULL;
+            }
+
+            pa_hashmap_remove_and_free(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index));
+            pa_sink_invalidate_requested_latency(s, true);
+            pa_sink_request_rewind(s, (size_t) -1);
+
+            /* In flat volume mode we need to update the volume as
+             * well */
+            return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
+        }
+
+        case PA_SINK_MESSAGE_START_MOVE: {
+            pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+            /* We don't support moving synchronized streams. */
+            pa_assert(!i->sync_prev);
+            pa_assert(!i->sync_next);
+            pa_assert(!i->thread_info.sync_next);
+            pa_assert(!i->thread_info.sync_prev);
+
+            if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+                pa_usec_t usec = 0;
+                size_t sink_nbytes, total_nbytes;
+
+                /* The old sink probably has some audio from this
+                 * stream in its buffer. We want to "take it back" as
+                 * much as possible and play it to the new sink. We
+                 * don't know at this point how much the old sink can
+                 * rewind. We have to pick something, and that
+                 * something is the full latency of the old sink here.
+                 * So we rewind the stream buffer by the sink latency
+                 * amount, which may be more than what we should
+                 * rewind. This can result in a chunk of audio being
+                 * played both to the old sink and the new sink.
+                 *
+                 * FIXME: Fix this code so that we don't have to make
+                 * guesses about how much the sink will actually be
+                 * able to rewind. If someone comes up with a solution
+                 * for this, something to note is that the part of the
+                 * latency that the old sink couldn't rewind should
+                 * ideally be compensated after the stream has moved
+                 * to the new sink by adding silence. The new sink
+                 * most likely can't start playing the moved stream
+                 * immediately, and that gap should be removed from
+                 * the "compensation silence" (at least at the time of
+                 * writing this, the move finish code will actually
+                 * already take care of dropping the new sink's
+                 * unrewindable latency, so taking into account the
+                 * unrewindable latency of the old sink is the only
+                 * problem).
+                 *
+                 * The render_memblockq contents are discarded,
+                 * because when the sink changes, the format of the
+                 * audio stored in the render_memblockq may change
+                 * too, making the stored audio invalid. FIXME:
+                 * However, the read and write indices are moved back
+                 * the same amount, so if they are not the same now,
+                 * they won't be the same after the rewind either. If
+                 * the write index of the render_memblockq is ahead of
+                 * the read index, then the render_memblockq will feed
+                 * the new sink some silence first, which it shouldn't
+                 * do. The write index should be flushed to be the
+                 * same as the read index. */
+
+                /* Get the latency of the sink */
+                usec = pa_sink_get_latency_within_thread(s, false);
+                sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
+                total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
+
+                if (total_nbytes > 0) {
+                    i->thread_info.rewrite_nbytes = i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, total_nbytes) : total_nbytes;
+                    i->thread_info.rewrite_flush = true;
+                    pa_sink_input_process_rewind(i, sink_nbytes);
+                }
+            }
+
+            pa_sink_input_detach(i);
+
+            /* Let's remove the sink input ...*/
+            pa_hashmap_remove_and_free(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index));
+
+            pa_sink_invalidate_requested_latency(s, true);
+
+            pa_log_debug("Requesting rewind due to started move");
+            pa_sink_request_rewind(s, (size_t) -1);
+
+            /* In flat volume mode we need to update the volume as
+             * well */
+            return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
+        }
+
+        case PA_SINK_MESSAGE_FINISH_MOVE: {
+            pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+            /* We don't support moving synchronized streams. */
+            pa_assert(!i->sync_prev);
+            pa_assert(!i->sync_next);
+            pa_assert(!i->thread_info.sync_next);
+            pa_assert(!i->thread_info.sync_prev);
+
+            pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
+
+            pa_sink_input_attach(i);
+
+            if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+                pa_usec_t usec = 0;
+                size_t nbytes;
+
+                /* In the ideal case the new sink would start playing
+                 * the stream immediately. That requires the sink to
+                 * be able to rewind all of its latency, which usually
+                 * isn't possible, so there will probably be some gap
+                 * before the moved stream becomes audible. We then
+                 * have two possibilities: 1) start playing the stream
+                 * from where it is now, or 2) drop the unrewindable
+                 * latency of the sink from the stream. With option 1
+                 * we won't lose any audio but the stream will have a
+                 * pause. With option 2 we may lose some audio but the
+                 * stream time will be somewhat in sync with the wall
+                 * clock. Lennart seems to have chosen option 2 (one
+                 * of the reasons might have been that option 1 is
+                 * actually much harder to implement), so we drop the
+                 * latency of the new sink from the moved stream and
+                 * hope that the sink will undo most of that in the
+                 * rewind. */
+
+                /* Get the latency of the sink */
+                usec = pa_sink_get_latency_within_thread(s, false);
+                nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
+
+                if (nbytes > 0)
+                    pa_sink_input_drop(i, nbytes);
+
+                pa_log_debug("Requesting rewind due to finished move");
+                pa_sink_request_rewind(s, nbytes);
+            }
+
+            /* Updating the requested sink latency has to be done
+             * after the sink rewind request, not before, because
+             * otherwise the sink may limit the rewind amount
+             * needlessly. */
+
+            if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
+                pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
+
+            pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+            pa_sink_input_update_max_request(i, s->thread_info.max_request);
+
+            return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
+        }
+
+        case PA_SINK_MESSAGE_SET_SHARED_VOLUME: {
+            pa_sink *root_sink = pa_sink_get_master(s);
+
+            if (PA_LIKELY(root_sink))
+                set_shared_volume_within_thread(root_sink);
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_VOLUME_SYNCED:
+
+            if (s->flags & PA_SINK_DEFERRED_VOLUME) {
+                s->set_volume(s);
+                pa_sink_volume_change_push(s);
+            }
+            /* Fall through ... */
+
+        case PA_SINK_MESSAGE_SET_VOLUME:
+
+            if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
+                s->thread_info.soft_volume = s->soft_volume;
+                pa_sink_request_rewind(s, (size_t) -1);
+            }
+
+            /* Fall through ... */
+
+        case PA_SINK_MESSAGE_SYNC_VOLUMES:
+            sync_input_volumes_within_thread(s);
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_VOLUME:
+
+            if ((s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume) {
+                s->get_volume(s);
+                pa_sink_volume_change_flush(s);
+                pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
+            }
+
+            /* In case sink implementor reset SW volume. */
+            if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
+                s->thread_info.soft_volume = s->soft_volume;
+                pa_sink_request_rewind(s, (size_t) -1);
+            }
+
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_MUTE:
+
+            if (s->thread_info.soft_muted != s->muted) {
+                s->thread_info.soft_muted = s->muted;
+                pa_sink_request_rewind(s, (size_t) -1);
+            }
+
+            if (s->flags & PA_SINK_DEFERRED_VOLUME && s->set_mute)
+                s->set_mute(s);
+
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_MUTE:
+
+            if (s->flags & PA_SINK_DEFERRED_VOLUME && s->get_mute)
+                return s->get_mute(s, userdata);
+
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+
+            bool suspend_change =
+                (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
+                (PA_SINK_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SINK_SUSPENDED);
+
+            s->thread_info.state = PA_PTR_TO_UINT(userdata);
+
+            if (s->thread_info.state == PA_SINK_SUSPENDED) {
+                s->thread_info.rewind_nbytes = 0;
+                s->thread_info.rewind_requested = false;
+            }
+
+            if (suspend_change) {
+                pa_sink_input *i;
+                void *state = NULL;
+
+                while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+                    if (i->suspend_within_thread)
+                        i->suspend_within_thread(i, s->thread_info.state == PA_SINK_SUSPENDED);
+            }
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
+
+            pa_usec_t *usec = userdata;
+            *usec = pa_sink_get_requested_latency_within_thread(s);
+
+            /* Yes, that's right, the IO thread will see -1 when no
+             * explicit requested latency is configured, the main
+             * thread will see max_latency */
+            if (*usec == (pa_usec_t) -1)
+                *usec = s->thread_info.max_latency;
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
+            pa_usec_t *r = userdata;
+
+            pa_sink_set_latency_range_within_thread(s, r[0], r[1]);
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_GET_LATENCY_RANGE: {
+            pa_usec_t *r = userdata;
+
+            r[0] = s->thread_info.min_latency;
+            r[1] = s->thread_info.max_latency;
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_GET_FIXED_LATENCY:
+
+            *((pa_usec_t*) userdata) = s->thread_info.fixed_latency;
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_FIXED_LATENCY:
+
+            pa_sink_set_fixed_latency_within_thread(s, (pa_usec_t) offset);
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_MAX_REWIND:
+
+            *((size_t*) userdata) = s->thread_info.max_rewind;
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_MAX_REQUEST:
+
+            *((size_t*) userdata) = s->thread_info.max_request;
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_MAX_REWIND:
+
+            pa_sink_set_max_rewind_within_thread(s, (size_t) offset);
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_MAX_REQUEST:
+
+            pa_sink_set_max_request_within_thread(s, (size_t) offset);
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_PORT:
+
+            pa_assert(userdata);
+            if (s->set_port) {
+                struct sink_message_set_port *msg_data = userdata;
+                msg_data->ret = s->set_port(s, msg_data->port);
+            }
+            return 0;
+
+        case PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE:
+            /* This message is sent from IO-thread and handled in main thread. */
+            pa_assert_ctl_context();
+
+            /* Make sure we're not messing with main thread when no longer linked */
+            if (!PA_SINK_IS_LINKED(s->state))
+                return 0;
+
+            pa_sink_get_volume(s, true);
+            pa_sink_get_mute(s, true);
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_PORT_LATENCY_OFFSET:
+            s->thread_info.port_latency_offset = offset;
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_LATENCY:
+        case PA_SINK_MESSAGE_MAX:
+            ;
+    }
+
+    return -1;
+}
+
+/* Called from main thread */
+int pa_sink_suspend_all(pa_core *c, bool suspend, pa_suspend_cause_t cause) {
+    pa_sink *sink;
+    uint32_t idx;
+    int ret = 0;
+
+    pa_core_assert_ref(c);
+    pa_assert_ctl_context();
+    pa_assert(cause != 0);
+
+    PA_IDXSET_FOREACH(sink, c->sinks, idx) {
+        int r;
+
+        if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
+            ret = r;
+    }
+
+    return ret;
+}
+
+/* Called from IO thread */
+void pa_sink_detach_within_thread(pa_sink *s) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
+        pa_sink_input_detach(i);
+
+    if (s->monitor_source)
+        pa_source_detach_within_thread(s->monitor_source);
+}
+
+/* Called from IO thread */
+void pa_sink_attach_within_thread(pa_sink *s) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
+        pa_sink_input_attach(i);
+
+    if (s->monitor_source)
+        pa_source_attach_within_thread(s->monitor_source);
+}
+
+/* Called from IO thread */
+void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+    if (nbytes == (size_t) -1)
+        nbytes = s->thread_info.max_rewind;
+
+    nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
+
+    if (s->thread_info.rewind_requested &&
+        nbytes <= s->thread_info.rewind_nbytes)
+        return;
+
+    s->thread_info.rewind_nbytes = nbytes;
+    s->thread_info.rewind_requested = true;
+
+    if (s->request_rewind)
+        s->request_rewind(s);
+}
+
+/* Called from IO thread */
+pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
+    pa_usec_t result = (pa_usec_t) -1;
+    pa_sink_input *i;
+    void *state = NULL;
+    pa_usec_t monitor_latency;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+
+    if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+        return PA_CLAMP(s->thread_info.fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+
+    if (s->thread_info.requested_latency_valid)
+        return s->thread_info.requested_latency;
+
+    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
+        if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
+            (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
+            result = i->thread_info.requested_sink_latency;
+
+    monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source);
+
+    if (monitor_latency != (pa_usec_t) -1 &&
+        (result == (pa_usec_t) -1 || result > monitor_latency))
+        result = monitor_latency;
+
+    if (result != (pa_usec_t) -1)
+        result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
+
+    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+        /* Only cache if properly initialized */
+        s->thread_info.requested_latency = result;
+        s->thread_info.requested_latency_valid = true;
+    }
+
+    return result;
+}
+
+/* Called from main thread */
+pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
+    pa_usec_t usec = 0;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if (s->state == PA_SINK_SUSPENDED)
+        return 0;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+
+    return usec;
+}
+
+/* Called from IO as well as the main thread -- the latter only before the IO thread started up */
+void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+
+    if (max_rewind == s->thread_info.max_rewind)
+        return;
+
+    s->thread_info.max_rewind = max_rewind;
+
+    if (PA_SINK_IS_LINKED(s->thread_info.state))
+        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
+            pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+
+    if (s->monitor_source)
+        pa_source_set_max_rewind_within_thread(s->monitor_source, s->thread_info.max_rewind);
+}
+
+/* Called from main thread */
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (PA_SINK_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
+    else
+        pa_sink_set_max_rewind_within_thread(s, max_rewind);
+}
+
+/* Called from IO as well as the main thread -- the latter only before the IO thread started up */
+void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+
+    if (max_request == s->thread_info.max_request)
+        return;
+
+    s->thread_info.max_request = max_request;
+
+    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+        pa_sink_input *i;
+
+        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
+            pa_sink_input_update_max_request(i, s->thread_info.max_request);
+    }
+}
+
+/* Called from main thread */
+void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (PA_SINK_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REQUEST, NULL, max_request, NULL) == 0);
+    else
+        pa_sink_set_max_request_within_thread(s, max_request);
+}
+
+/* Called from IO thread */
+void pa_sink_invalidate_requested_latency(pa_sink *s, bool dynamic) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+
+    if ((s->flags & PA_SINK_DYNAMIC_LATENCY))
+        s->thread_info.requested_latency_valid = false;
+    else if (dynamic)
+        return;
+
+    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+
+        if (s->update_requested_latency)
+            s->update_requested_latency(s);
+
+        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
+            if (i->update_sink_requested_latency)
+                i->update_sink_requested_latency(i);
+    }
+}
+
+/* Called from main thread */
+void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    /* min_latency == 0:           no limit
+     * min_latency anything else:  specified limit
+     *
+     * Similar for max_latency */
+
+    if (min_latency < ABSOLUTE_MIN_LATENCY)
+        min_latency = ABSOLUTE_MIN_LATENCY;
+
+    if (max_latency <= 0 ||
+        max_latency > ABSOLUTE_MAX_LATENCY)
+        max_latency = ABSOLUTE_MAX_LATENCY;
+
+    pa_assert(min_latency <= max_latency);
+
+    /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
+    pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+               max_latency == ABSOLUTE_MAX_LATENCY) ||
+              (s->flags & PA_SINK_DYNAMIC_LATENCY));
+
+    if (PA_SINK_IS_LINKED(s->state)) {
+        pa_usec_t r[2];
+
+        r[0] = min_latency;
+        r[1] = max_latency;
+
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
+    } else
+        pa_sink_set_latency_range_within_thread(s, min_latency, max_latency);
+}
+
+/* Called from main thread */
+void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(min_latency);
+    pa_assert(max_latency);
+
+    if (PA_SINK_IS_LINKED(s->state)) {
+        pa_usec_t r[2] = { 0, 0 };
+
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
+
+        *min_latency = r[0];
+        *max_latency = r[1];
+    } else {
+        *min_latency = s->thread_info.min_latency;
+        *max_latency = s->thread_info.max_latency;
+    }
+}
+
+/* Called from IO thread */
+void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+
+    pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
+    pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
+    pa_assert(min_latency <= max_latency);
+
+    /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
+    pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+               max_latency == ABSOLUTE_MAX_LATENCY) ||
+              (s->flags & PA_SINK_DYNAMIC_LATENCY));
+
+    if (s->thread_info.min_latency == min_latency &&
+        s->thread_info.max_latency == max_latency)
+        return;
+
+    s->thread_info.min_latency = min_latency;
+    s->thread_info.max_latency = max_latency;
+
+    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+        pa_sink_input *i;
+        void *state = NULL;
+
+        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
+            if (i->update_sink_latency_range)
+                i->update_sink_latency_range(i);
+    }
+
+    pa_sink_invalidate_requested_latency(s, false);
+
+    pa_source_set_latency_range_within_thread(s->monitor_source, min_latency, max_latency);
+}
+
+/* Called from main thread */
+void pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency) {
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
+        pa_assert(latency == 0);
+        return;
+    }
+
+    if (latency < ABSOLUTE_MIN_LATENCY)
+        latency = ABSOLUTE_MIN_LATENCY;
+
+    if (latency > ABSOLUTE_MAX_LATENCY)
+        latency = ABSOLUTE_MAX_LATENCY;
+
+    if (PA_SINK_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_FIXED_LATENCY, NULL, (int64_t) latency, NULL) == 0);
+    else
+        s->thread_info.fixed_latency = latency;
+
+    pa_source_set_fixed_latency(s->monitor_source, latency);
+}
+
+/* Called from main thread */
+pa_usec_t pa_sink_get_fixed_latency(pa_sink *s) {
+    pa_usec_t latency;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (s->flags & PA_SINK_DYNAMIC_LATENCY)
+        return 0;
+
+    if (PA_SINK_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_FIXED_LATENCY, &latency, 0, NULL) == 0);
+    else
+        latency = s->thread_info.fixed_latency;
+
+    return latency;
+}
+
+/* Called from IO thread */
+void pa_sink_set_fixed_latency_within_thread(pa_sink *s, pa_usec_t latency) {
+    pa_sink_assert_ref(s);
+    pa_sink_assert_io_context(s);
+
+    if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
+        pa_assert(latency == 0);
+        s->thread_info.fixed_latency = 0;
+
+        if (s->monitor_source)
+            pa_source_set_fixed_latency_within_thread(s->monitor_source, 0);
+
+        return;
+    }
+
+    pa_assert(latency >= ABSOLUTE_MIN_LATENCY);
+    pa_assert(latency <= ABSOLUTE_MAX_LATENCY);
+
+    if (s->thread_info.fixed_latency == latency)
+        return;
+
+    s->thread_info.fixed_latency = latency;
+
+    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+        pa_sink_input *i;
+        void *state = NULL;
+
+        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
+            if (i->update_sink_fixed_latency)
+                i->update_sink_fixed_latency(i);
+    }
+
+    pa_sink_invalidate_requested_latency(s, false);
+
+    pa_source_set_fixed_latency_within_thread(s->monitor_source, latency);
+}
+
+/* Called from main context */
+void pa_sink_set_port_latency_offset(pa_sink *s, int64_t offset) {
+    pa_sink_assert_ref(s);
+
+    s->port_latency_offset = offset;
+
+    if (PA_SINK_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT_LATENCY_OFFSET, NULL, offset, NULL) == 0);
+    else
+        s->thread_info.port_latency_offset = offset;
+
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_LATENCY_OFFSET_CHANGED], s);
+}
+
+/* Called from main context */
+size_t pa_sink_get_max_rewind(pa_sink *s) {
+    size_t r;
+    pa_assert_ctl_context();
+    pa_sink_assert_ref(s);
+
+    if (!PA_SINK_IS_LINKED(s->state))
+        return s->thread_info.max_rewind;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
+
+    return r;
+}
+
+/* Called from main context */
+size_t pa_sink_get_max_request(pa_sink *s) {
+    size_t r;
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (!PA_SINK_IS_LINKED(s->state))
+        return s->thread_info.max_request;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0);
+
+    return r;
+}
+
+/* Called from main context */
+int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
+    pa_device_port *port;
+    int ret;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (!s->set_port) {
+        pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name);
+        return -PA_ERR_NOTIMPLEMENTED;
+    }
+
+    if (!name)
+        return -PA_ERR_NOENTITY;
+
+    if (!(port = pa_hashmap_get(s->ports, name)))
+        return -PA_ERR_NOENTITY;
+
+    if (s->active_port == port) {
+        s->save_port = s->save_port || save;
+        return 0;
+    }
+
+    if (s->flags & PA_SINK_DEFERRED_VOLUME) {
+        struct sink_message_set_port msg = { .port = port, .ret = 0 };
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
+        ret = msg.ret;
+    }
+    else
+        ret = s->set_port(s, port);
+
+    if (ret < 0)
+        return -PA_ERR_NOENTITY;
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+    pa_log_info("Changed port of sink %u \"%s\" to %s", s->index, s->name, port->name);
+
+    s->active_port = port;
+    s->save_port = save;
+
+    pa_sink_set_port_latency_offset(s, s->active_port->latency_offset);
+
+    /* The active port affects the default sink selection. */
+    pa_core_update_default_sink(s->core);
+
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], s);
+
+    return 0;
+}
+
+bool pa_device_init_icon(pa_proplist *p, bool is_sink) {
+    const char *ff, *c, *t = NULL, *s = "", *profile, *bus;
+
+    pa_assert(p);
+
+    if (pa_proplist_contains(p, PA_PROP_DEVICE_ICON_NAME))
+        return true;
+
+    if ((ff = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
+
+        if (pa_streq(ff, "microphone"))
+            t = "audio-input-microphone";
+        else if (pa_streq(ff, "webcam"))
+            t = "camera-web";
+        else if (pa_streq(ff, "computer"))
+            t = "computer";
+        else if (pa_streq(ff, "handset"))
+            t = "phone";
+        else if (pa_streq(ff, "portable"))
+            t = "multimedia-player";
+        else if (pa_streq(ff, "tv"))
+            t = "video-display";
+
+        /*
+         * The following icons are not part of the icon naming spec,
+         * because Rodney Dawes sucks as the maintainer of that spec.
+         *
+         * http://lists.freedesktop.org/archives/xdg/2009-May/010397.html
+         */
+        else if (pa_streq(ff, "headset"))
+            t = "audio-headset";
+        else if (pa_streq(ff, "headphone"))
+            t = "audio-headphones";
+        else if (pa_streq(ff, "speaker"))
+            t = "audio-speakers";
+        else if (pa_streq(ff, "hands-free"))
+            t = "audio-handsfree";
+    }
+
+    if (!t)
+        if ((c = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
+            if (pa_streq(c, "modem"))
+                t = "modem";
+
+    if (!t) {
+        if (is_sink)
+            t = "audio-card";
+        else
+            t = "audio-input-microphone";
+    }
+
+    if ((profile = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
+        if (strstr(profile, "analog"))
+            s = "-analog";
+        else if (strstr(profile, "iec958"))
+            s = "-iec958";
+        else if (strstr(profile, "hdmi"))
+            s = "-hdmi";
+    }
+
+    bus = pa_proplist_gets(p, PA_PROP_DEVICE_BUS);
+
+    pa_proplist_setf(p, PA_PROP_DEVICE_ICON_NAME, "%s%s%s%s", t, pa_strempty(s), bus ? "-" : "", pa_strempty(bus));
+
+    return true;
+}
+
+bool pa_device_init_description(pa_proplist *p, pa_card *card) {
+    const char *s, *d = NULL, *k;
+    pa_assert(p);
+
+    if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
+        return true;
+
+    if (card)
+        if ((s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+            d = s;
+
+    if (!d)
+        if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
+            if (pa_streq(s, "internal"))
+                d = _("Built-in Audio");
+
+    if (!d)
+        if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
+            if (pa_streq(s, "modem"))
+                d = _("Modem");
+
+    if (!d)
+        d = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME);
+
+    if (!d)
+        return false;
+
+    k = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_DESCRIPTION);
+
+    if (d && k)
+        pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s %s", d, k);
+    else if (d)
+        pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, d);
+
+    return true;
+}
+
+bool pa_device_init_intended_roles(pa_proplist *p) {
+    const char *s;
+    pa_assert(p);
+
+    if (pa_proplist_contains(p, PA_PROP_DEVICE_INTENDED_ROLES))
+        return true;
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
+        if (pa_streq(s, "handset") || pa_streq(s, "hands-free")
+            || pa_streq(s, "headset")) {
+            pa_proplist_sets(p, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
+            return true;
+        }
+
+    return false;
+}
+
+unsigned pa_device_init_priority(pa_proplist *p) {
+    const char *s;
+    unsigned priority = 0;
+
+    pa_assert(p);
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS))) {
+
+        if (pa_streq(s, "sound"))
+            priority += 9000;
+        else if (!pa_streq(s, "modem"))
+            priority += 1000;
+    }
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
+
+        if (pa_streq(s, "headphone"))
+            priority += 900;
+        else if (pa_streq(s, "hifi"))
+            priority += 600;
+        else if (pa_streq(s, "speaker"))
+            priority += 500;
+        else if (pa_streq(s, "portable"))
+            priority += 450;
+    }
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_BUS))) {
+
+        if (pa_streq(s, "bluetooth"))
+            priority += 50;
+        else if (pa_streq(s, "usb"))
+            priority += 40;
+        else if (pa_streq(s, "pci"))
+            priority += 30;
+    }
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
+
+        if (pa_startswith(s, "analog-"))
+            priority += 9;
+        else if (pa_startswith(s, "iec958-"))
+            priority += 8;
+    }
+
+    return priority;
+}
+
+PA_STATIC_FLIST_DECLARE(pa_sink_volume_change, 0, pa_xfree);
+
+/* Called from the IO thread. */
+static pa_sink_volume_change *pa_sink_volume_change_new(pa_sink *s) {
+    pa_sink_volume_change *c;
+    if (!(c = pa_flist_pop(PA_STATIC_FLIST_GET(pa_sink_volume_change))))
+        c = pa_xnew(pa_sink_volume_change, 1);
+
+    PA_LLIST_INIT(pa_sink_volume_change, c);
+    c->at = 0;
+    pa_cvolume_reset(&c->hw_volume, s->sample_spec.channels);
+    return c;
+}
+
+/* Called from the IO thread. */
+static void pa_sink_volume_change_free(pa_sink_volume_change *c) {
+    pa_assert(c);
+    if (pa_flist_push(PA_STATIC_FLIST_GET(pa_sink_volume_change), c) < 0)
+        pa_xfree(c);
+}
+
+/* Called from the IO thread. */
+void pa_sink_volume_change_push(pa_sink *s) {
+    pa_sink_volume_change *c = NULL;
+    pa_sink_volume_change *nc = NULL;
+    pa_sink_volume_change *pc = NULL;
+    uint32_t safety_margin = s->thread_info.volume_change_safety_margin;
+
+    const char *direction = NULL;
+
+    pa_assert(s);
+    nc = pa_sink_volume_change_new(s);
+
+    /* NOTE: There is already more different volumes in pa_sink that I can remember.
+     *       Adding one more volume for HW would get us rid of this, but I am trying
+     *       to survive with the ones we already have. */
+    pa_sw_cvolume_divide(&nc->hw_volume, &s->real_volume, &s->soft_volume);
+
+    if (!s->thread_info.volume_changes && pa_cvolume_equal(&nc->hw_volume, &s->thread_info.current_hw_volume)) {
+        pa_log_debug("Volume not changing");
+        pa_sink_volume_change_free(nc);
+        return;
+    }
+
+    nc->at = pa_sink_get_latency_within_thread(s, false);
+    nc->at += pa_rtclock_now() + s->thread_info.volume_change_extra_delay;
+
+    if (s->thread_info.volume_changes_tail) {
+        for (c = s->thread_info.volume_changes_tail; c; c = c->prev) {
+            /* If volume is going up let's do it a bit late. If it is going
+             * down let's do it a bit early. */
+            if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&c->hw_volume)) {
+                if (nc->at + safety_margin > c->at) {
+                    nc->at += safety_margin;
+                    direction = "up";
+                    break;
+                }
+            }
+            else if (nc->at - safety_margin > c->at) {
+                    nc->at -= safety_margin;
+                    direction = "down";
+                    break;
+            }
+        }
+    }
+
+    if (c == NULL) {
+        if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&s->thread_info.current_hw_volume)) {
+            nc->at += safety_margin;
+            direction = "up";
+        } else {
+            nc->at -= safety_margin;
+            direction = "down";
+        }
+        PA_LLIST_PREPEND(pa_sink_volume_change, s->thread_info.volume_changes, nc);
+    }
+    else {
+        PA_LLIST_INSERT_AFTER(pa_sink_volume_change, s->thread_info.volume_changes, c, nc);
+    }
+
+    pa_log_debug("Volume going %s to %d at %llu", direction, pa_cvolume_avg(&nc->hw_volume), (long long unsigned) nc->at);
+
+    /* We can ignore volume events that came earlier but should happen later than this. */
+    PA_LLIST_FOREACH_SAFE(c, pc, nc->next) {
+        pa_log_debug("Volume change to %d at %llu was dropped", pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at);
+        pa_sink_volume_change_free(c);
+    }
+    nc->next = NULL;
+    s->thread_info.volume_changes_tail = nc;
+}
+
+/* Called from the IO thread. */
+static void pa_sink_volume_change_flush(pa_sink *s) {
+    pa_sink_volume_change *c = s->thread_info.volume_changes;
+    pa_assert(s);
+    s->thread_info.volume_changes = NULL;
+    s->thread_info.volume_changes_tail = NULL;
+    while (c) {
+        pa_sink_volume_change *next = c->next;
+        pa_sink_volume_change_free(c);
+        c = next;
+    }
+}
+
+/* Called from the IO thread. */
+bool pa_sink_volume_change_apply(pa_sink *s, pa_usec_t *usec_to_next) {
+    pa_usec_t now;
+    bool ret = false;
+
+    pa_assert(s);
+
+    if (!s->thread_info.volume_changes || !PA_SINK_IS_LINKED(s->state)) {
+        if (usec_to_next)
+            *usec_to_next = 0;
+        return ret;
+    }
+
+    pa_assert(s->write_volume);
+
+    now = pa_rtclock_now();
+
+    while (s->thread_info.volume_changes && now >= s->thread_info.volume_changes->at) {
+        pa_sink_volume_change *c = s->thread_info.volume_changes;
+        PA_LLIST_REMOVE(pa_sink_volume_change, s->thread_info.volume_changes, c);
+        pa_log_debug("Volume change to %d at %llu was written %llu usec late",
+                     pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at, (long long unsigned) (now - c->at));
+        ret = true;
+        s->thread_info.current_hw_volume = c->hw_volume;
+        pa_sink_volume_change_free(c);
+    }
+
+    if (ret)
+        s->write_volume(s);
+
+    if (s->thread_info.volume_changes) {
+        if (usec_to_next)
+            *usec_to_next = s->thread_info.volume_changes->at - now;
+        if (pa_log_ratelimit(PA_LOG_DEBUG))
+            pa_log_debug("Next volume change in %lld usec", (long long) (s->thread_info.volume_changes->at - now));
+    }
+    else {
+        if (usec_to_next)
+            *usec_to_next = 0;
+        s->thread_info.volume_changes_tail = NULL;
+    }
+    return ret;
+}
+
+/* Called from the IO thread. */
+static void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes) {
+    /* All the queued volume events later than current latency are shifted to happen earlier. */
+    pa_sink_volume_change *c;
+    pa_volume_t prev_vol = pa_cvolume_avg(&s->thread_info.current_hw_volume);
+    pa_usec_t rewound = pa_bytes_to_usec(nbytes, &s->sample_spec);
+    pa_usec_t limit = pa_sink_get_latency_within_thread(s, false);
+
+    pa_log_debug("latency = %lld", (long long) limit);
+    limit += pa_rtclock_now() + s->thread_info.volume_change_extra_delay;
+
+    PA_LLIST_FOREACH(c, s->thread_info.volume_changes) {
+        pa_usec_t modified_limit = limit;
+        if (prev_vol > pa_cvolume_avg(&c->hw_volume))
+            modified_limit -= s->thread_info.volume_change_safety_margin;
+        else
+            modified_limit += s->thread_info.volume_change_safety_margin;
+        if (c->at > modified_limit) {
+            c->at -= rewound;
+            if (c->at < modified_limit)
+                c->at = modified_limit;
+        }
+        prev_vol = pa_cvolume_avg(&c->hw_volume);
+    }
+    pa_sink_volume_change_apply(s, NULL);
+}
+
+/* Called from the main thread */
+/* Gets the list of formats supported by the sink. The members and idxset must
+ * be freed by the caller. */
+pa_idxset* pa_sink_get_formats(pa_sink *s) {
+    pa_idxset *ret;
+
+    pa_assert(s);
+
+    if (s->get_formats) {
+        /* Sink supports format query, all is good */
+        ret = s->get_formats(s);
+    } else {
+        /* Sink doesn't support format query, so assume it does PCM */
+        pa_format_info *f = pa_format_info_new();
+        f->encoding = PA_ENCODING_PCM;
+
+        ret = pa_idxset_new(NULL, NULL);
+        pa_idxset_put(ret, f, NULL);
+    }
+
+    return ret;
+}
+
+/* Called from the main thread */
+/* Allows an external source to set what formats a sink supports if the sink
+ * permits this. The function makes a copy of the formats on success. */
+bool pa_sink_set_formats(pa_sink *s, pa_idxset *formats) {
+    pa_assert(s);
+    pa_assert(formats);
+
+    if (s->set_formats)
+        /* Sink supports setting formats -- let's give it a shot */
+        return s->set_formats(s, formats);
+    else
+        /* Sink doesn't support setting this -- bail out */
+        return false;
+}
+
+/* Called from the main thread */
+/* Checks if the sink can accept this format */
+bool pa_sink_check_format(pa_sink *s, pa_format_info *f) {
+    pa_idxset *formats = NULL;
+    bool ret = false;
+
+    pa_assert(s);
+    pa_assert(f);
+
+    formats = pa_sink_get_formats(s);
+
+    if (formats) {
+        pa_format_info *finfo_device;
+        uint32_t i;
+
+        PA_IDXSET_FOREACH(finfo_device, formats, i) {
+            if (pa_format_info_is_compatible(finfo_device, f)) {
+                ret = true;
+                break;
+            }
+        }
+
+        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+    }
+
+    return ret;
+}
+
+/* Called from the main thread */
+/* Calculates the intersection between formats supported by the sink and
+ * in_formats, and returns these, in the order of the sink's formats. */
+pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) {
+    pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *sink_formats = NULL;
+    pa_format_info *f_sink, *f_in;
+    uint32_t i, j;
+
+    pa_assert(s);
+
+    if (!in_formats || pa_idxset_isempty(in_formats))
+        goto done;
+
+    sink_formats = pa_sink_get_formats(s);
+
+    PA_IDXSET_FOREACH(f_sink, sink_formats, i) {
+        PA_IDXSET_FOREACH(f_in, in_formats, j) {
+            if (pa_format_info_is_compatible(f_sink, f_in))
+                pa_idxset_put(out_formats, pa_format_info_copy(f_in), NULL);
+        }
+    }
+
+done:
+    if (sink_formats)
+        pa_idxset_free(sink_formats, (pa_free_cb_t) pa_format_info_free);
+
+    return out_formats;
+}
+
+/* Called from the main thread. */
+void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
+    pa_cvolume old_volume;
+    char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(s);
+    pa_assert(volume);
+
+    old_volume = s->reference_volume;
+
+    if (pa_cvolume_equal(volume, &old_volume))
+        return;
+
+    s->reference_volume = *volume;
+    pa_log_debug("The reference volume of sink %s changed from %s to %s.", s->name,
+                 pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &s->channel_map,
+                                            s->flags & PA_SINK_DECIBEL_VOLUME),
+                 pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &s->channel_map,
+                                            s->flags & PA_SINK_DECIBEL_VOLUME));
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED], s);
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
new file mode 100644 (file)
index 0000000..0e79cf3
--- /dev/null
@@ -0,0 +1,544 @@
+#ifndef foopulsesinkhfoo
+#define foopulsesinkhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include <pulsecore/typedefs.h>
+#include <pulse/def.h>
+#include <pulse/format.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/msgobject.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/device-port.h>
+#include <pulsecore/card.h>
+#include <pulsecore/queue.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/sink-input.h>
+
+#define PA_MAX_INPUTS_PER_SINK 256
+
+/* Returns true if sink is linked: registered and accessible from client side. */
+static inline bool PA_SINK_IS_LINKED(pa_sink_state_t x) {
+    return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
+}
+
+/* A generic definition for void callback functions */
+typedef void(*pa_sink_cb_t)(pa_sink *s);
+
+typedef int (*pa_sink_get_mute_cb_t)(pa_sink *s, bool *mute);
+
+struct pa_sink {
+    pa_msgobject parent;
+
+    uint32_t index;
+    pa_core *core;
+
+    pa_sink_state_t state;
+
+    /* Set in the beginning of pa_sink_unlink() before setting the sink state
+     * to UNLINKED. The purpose is to prevent moving streams to a sink that is
+     * about to be removed. */
+    bool unlink_requested;
+
+    pa_sink_flags_t flags;
+    pa_suspend_cause_t suspend_cause;
+
+    char *name;
+    char *driver;                           /* may be NULL */
+    pa_proplist *proplist;
+
+    pa_module *module;                      /* may be NULL */
+    pa_card *card;                          /* may be NULL */
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    uint32_t default_sample_rate;
+    uint32_t alternate_sample_rate;
+
+    pa_idxset *inputs;
+    unsigned n_corked;
+    pa_source *monitor_source;
+    pa_sink_input *input_to_master;         /* non-NULL only for filter sinks */
+
+    pa_volume_t base_volume; /* shall be constant */
+    unsigned n_volume_steps; /* shall be constant */
+
+    /* Also see http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Volumes/ */
+    pa_cvolume reference_volume; /* The volume exported and taken as reference base for relative sink input volumes */
+    pa_cvolume real_volume;      /* The volume that the hardware is configured to  */
+    pa_cvolume soft_volume;      /* The internal software volume we apply to all PCM data while it passes through */
+
+    bool muted:1;
+
+    bool refresh_volume:1;
+    bool refresh_muted:1;
+    bool save_port:1;
+    bool save_volume:1;
+    bool save_muted:1;
+
+    /* Saved volume state while we're in passthrough mode */
+    pa_cvolume saved_volume;
+    bool saved_save_volume:1;
+
+    pa_asyncmsgq *asyncmsgq;
+
+    pa_memchunk silence;
+
+    pa_hashmap *ports;
+    pa_device_port *active_port;
+    pa_atomic_t mixer_dirty;
+
+    /* The latency offset is inherited from the currently active port */
+    int64_t port_latency_offset;
+
+    unsigned priority;
+
+    bool set_mute_in_progress;
+
+    /* Called when the main loop requests a state change. Called from
+     * main loop context. If returns -1 the state change will be
+     * inhibited */
+    int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
+
+    /* Sink drivers that support hardware volume may set this
+     * callback. This is called when the current volume needs to be
+     * re-read from the hardware.
+     *
+     * There are two ways for drivers to implement hardware volume
+     * query: either set this callback or handle
+     * PA_SINK_MESSAGE_GET_VOLUME. The callback implementation or the
+     * message handler must update s->real_volume and s->soft_volume
+     * (using pa_sink_set_soft_volume()) to match the current hardware
+     * volume.
+     *
+     * If PA_SINK_DEFERRED_VOLUME is not set, then this is called from the
+     * main thread before sending PA_SINK_MESSAGE_GET_VOLUME, so in
+     * this case the driver can choose whether to read the volume from
+     * the hardware in the main thread or in the IO thread.
+     *
+     * If PA_SINK_DEFERRED_VOLUME is set, then this is called from the IO
+     * thread within the default handler for
+     * PA_SINK_MESSAGE_GET_VOLUME (the main thread is waiting while
+     * the message is being processed), so there's no choice of where
+     * to do the volume reading - it has to be done in the IO thread
+     * always.
+     *
+     * You must use the function pa_sink_set_get_volume_callback() to
+     * set this callback. */
+    pa_sink_cb_t get_volume; /* may be NULL */
+
+    /* Sink drivers that support hardware volume must set this
+     * callback. This is called when the hardware volume needs to be
+     * updated.
+     *
+     * If PA_SINK_DEFERRED_VOLUME is not set, then this is called from the
+     * main thread. The callback implementation must set the hardware
+     * volume according to s->real_volume. If the driver can't set the
+     * hardware volume to the exact requested value, it has to update
+     * s->real_volume and/or s->soft_volume so that they together
+     * match the actual hardware volume that was set.
+     *
+     * If PA_SINK_DEFERRED_VOLUME is set, then this is called from the IO
+     * thread. The callback implementation must not actually set the
+     * hardware volume yet, but it must check how close to the
+     * requested volume the hardware volume can be set, and update
+     * s->real_volume and/or s->soft_volume so that they together
+     * match the actual hardware volume that will be set later in the
+     * write_volume callback.
+     *
+     * You must use the function pa_sink_set_set_volume_callback() to
+     * set this callback. */
+    pa_sink_cb_t set_volume; /* may be NULL */
+
+    /* Sink drivers that set PA_SINK_DEFERRED_VOLUME must provide this
+     * callback. This callback is not used with sinks that do not set
+     * PA_SINK_DEFERRED_VOLUME. This is called from the IO thread when a
+     * pending hardware volume change has to be written to the
+     * hardware. The requested volume is passed to the callback
+     * implementation in s->thread_info.current_hw_volume.
+     *
+     * The call is done inside pa_sink_volume_change_apply(), which is
+     * not called automatically - it is the driver's responsibility to
+     * schedule that function to be called at the right times in the
+     * IO thread.
+     *
+     * You must use the function pa_sink_set_write_volume_callback() to
+     * set this callback. */
+    pa_sink_cb_t write_volume; /* may be NULL */
+
+    /* If the sink mute can change "spontaneously" (i.e. initiated by the sink
+     * implementation, not by someone else calling pa_sink_set_mute()), then
+     * the sink implementation can notify about changed mute either by calling
+     * pa_sink_mute_changed() or by calling pa_sink_get_mute() with
+     * force_refresh=true. If the implementation chooses the latter approach,
+     * it should implement the get_mute callback. Otherwise get_mute can be
+     * NULL.
+     *
+     * This is called when pa_sink_get_mute() is called with
+     * force_refresh=true. This is called from the IO thread if the
+     * PA_SINK_DEFERRED_VOLUME flag is set, otherwise this is called from the
+     * main thread. On success, the implementation is expected to return 0 and
+     * set the mute parameter that is passed as a reference. On failure, the
+     * implementation is expected to return -1.
+     *
+     * You must use the function pa_sink_set_get_mute_callback() to
+     * set this callback. */
+    pa_sink_get_mute_cb_t get_mute;
+
+    /* Called when the mute setting shall be changed. A PA_SINK_MESSAGE_SET_MUTE
+     * message will also be sent. Called from IO thread if PA_SINK_DEFERRED_VOLUME
+     * flag is set otherwise from main loop context.
+     *
+     * You must use the function pa_sink_set_set_mute_callback() to
+     * set this callback. */
+    pa_sink_cb_t set_mute; /* may be NULL */
+
+    /* Called when a rewind request is issued. Called from IO thread
+     * context. */
+    pa_sink_cb_t request_rewind; /* may be NULL */
+
+    /* Called when a the requested latency is changed. Called from IO
+     * thread context. */
+    pa_sink_cb_t update_requested_latency; /* may be NULL */
+
+    /* Called whenever the port shall be changed. Called from IO
+     * thread if deferred volumes are enabled, and main thread otherwise. */
+    int (*set_port)(pa_sink *s, pa_device_port *port); /* may be NULL */
+
+    /* Called to get the list of formats supported by the sink, sorted
+     * in descending order of preference. */
+    pa_idxset* (*get_formats)(pa_sink *s); /* may be NULL */
+
+    /* Called to set the list of formats supported by the sink. Can be
+     * NULL if the sink does not support this. Returns true on success,
+     * false otherwise (for example when an unsupportable format is
+     * set). Makes a copy of the formats passed in. */
+    bool (*set_formats)(pa_sink *s, pa_idxset *formats); /* may be NULL */
+
+    /* Called whenever the sampling frequency shall be changed. Called from
+     * main thread. */
+    int (*update_rate)(pa_sink *s, uint32_t rate);
+
+    /* Contains copies of the above data so that the real-time worker
+     * thread can work without access locking */
+    struct {
+        pa_sink_state_t state;
+        pa_hashmap *inputs;
+
+        pa_rtpoll *rtpoll;
+
+        pa_cvolume soft_volume;
+        bool soft_muted:1;
+
+        /* The requested latency is used for dynamic latency
+         * sinks. For fixed latency sinks it is always identical to
+         * the fixed_latency. See below. */
+        bool requested_latency_valid:1;
+        pa_usec_t requested_latency;
+
+        /* The number of bytes streams need to keep around as history to
+         * be able to satisfy every DMA buffer rewrite */
+        size_t max_rewind;
+
+        /* The number of bytes streams need to keep around to satisfy
+         * every DMA write request */
+        size_t max_request;
+
+        /* Maximum of what clients requested to rewind in this cycle */
+        size_t rewind_nbytes;
+        bool rewind_requested;
+
+        /* Both dynamic and fixed latencies will be clamped to this
+         * range. */
+        pa_usec_t min_latency; /* we won't go below this latency */
+        pa_usec_t max_latency; /* An upper limit for the latencies */
+
+        /* 'Fixed' simply means that the latency is exclusively
+         * decided on by the sink, and the clients have no influence
+         * in changing it */
+        pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */
+
+        /* This latency offset is a direct copy from s->port_latency_offset */
+        int64_t port_latency_offset;
+
+        /* Delayed volume change events are queued here. The events
+         * are stored in expiration order. The one expiring next is in
+         * the head of the list. */
+        PA_LLIST_HEAD(pa_sink_volume_change, volume_changes);
+        pa_sink_volume_change *volume_changes_tail;
+        /* This value is updated in pa_sink_volume_change_apply() and
+         * used only by sinks with PA_SINK_DEFERRED_VOLUME. */
+        pa_cvolume current_hw_volume;
+
+        /* The amount of usec volume up events are delayed and volume
+         * down events are made earlier. */
+        uint32_t volume_change_safety_margin;
+        /* Usec delay added to all volume change events, may be negative. */
+        int32_t volume_change_extra_delay;
+    } thread_info;
+
+    void *userdata;
+};
+
+PA_DECLARE_PUBLIC_CLASS(pa_sink);
+#define PA_SINK(s) (pa_sink_cast(s))
+
+typedef enum pa_sink_message {
+    PA_SINK_MESSAGE_ADD_INPUT,
+    PA_SINK_MESSAGE_REMOVE_INPUT,
+    PA_SINK_MESSAGE_GET_VOLUME,
+    PA_SINK_MESSAGE_SET_SHARED_VOLUME,
+    PA_SINK_MESSAGE_SET_VOLUME_SYNCED,
+    PA_SINK_MESSAGE_SET_VOLUME,
+    PA_SINK_MESSAGE_SYNC_VOLUMES,
+    PA_SINK_MESSAGE_GET_MUTE,
+    PA_SINK_MESSAGE_SET_MUTE,
+    PA_SINK_MESSAGE_GET_LATENCY,
+    PA_SINK_MESSAGE_GET_REQUESTED_LATENCY,
+    PA_SINK_MESSAGE_SET_STATE,
+    PA_SINK_MESSAGE_START_MOVE,
+    PA_SINK_MESSAGE_FINISH_MOVE,
+    PA_SINK_MESSAGE_SET_LATENCY_RANGE,
+    PA_SINK_MESSAGE_GET_LATENCY_RANGE,
+    PA_SINK_MESSAGE_SET_FIXED_LATENCY,
+    PA_SINK_MESSAGE_GET_FIXED_LATENCY,
+    PA_SINK_MESSAGE_GET_MAX_REWIND,
+    PA_SINK_MESSAGE_GET_MAX_REQUEST,
+    PA_SINK_MESSAGE_SET_MAX_REWIND,
+    PA_SINK_MESSAGE_SET_MAX_REQUEST,
+    PA_SINK_MESSAGE_SET_PORT,
+    PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE,
+    PA_SINK_MESSAGE_SET_PORT_LATENCY_OFFSET,
+    PA_SINK_MESSAGE_MAX
+} pa_sink_message_t;
+
+typedef struct pa_sink_new_data {
+    pa_suspend_cause_t suspend_cause;
+
+    char *name;
+    pa_proplist *proplist;
+
+    const char *driver;
+    pa_module *module;
+    pa_card *card;
+
+    pa_hashmap *ports;
+    char *active_port;
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    uint32_t alternate_sample_rate;
+    pa_cvolume volume;
+    bool muted:1;
+
+    bool sample_spec_is_set:1;
+    bool channel_map_is_set:1;
+    bool alternate_sample_rate_is_set:1;
+    bool volume_is_set:1;
+    bool muted_is_set:1;
+
+    bool namereg_fail:1;
+
+    bool save_port:1;
+    bool save_volume:1;
+    bool save_muted:1;
+} pa_sink_new_data;
+
+pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data);
+void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name);
+void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec);
+void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map);
+void pa_sink_new_data_set_alternate_sample_rate(pa_sink_new_data *data, const uint32_t alternate_sample_rate);
+void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume);
+void pa_sink_new_data_set_muted(pa_sink_new_data *data, bool mute);
+void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port);
+void pa_sink_new_data_done(pa_sink_new_data *data);
+
+/*** To be called exclusively by the sink driver, from main context */
+
+pa_sink* pa_sink_new(
+        pa_core *core,
+        pa_sink_new_data *data,
+        pa_sink_flags_t flags);
+
+void pa_sink_set_get_volume_callback(pa_sink *s, pa_sink_cb_t cb);
+void pa_sink_set_set_volume_callback(pa_sink *s, pa_sink_cb_t cb);
+void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb);
+void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_get_mute_cb_t cb);
+void pa_sink_set_set_mute_callback(pa_sink *s, pa_sink_cb_t cb);
+void pa_sink_enable_decibel_volume(pa_sink *s, bool enable);
+
+void pa_sink_put(pa_sink *s);
+void pa_sink_unlink(pa_sink* s);
+
+void pa_sink_set_description(pa_sink *s, const char *description);
+void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q);
+void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p);
+
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind);
+void pa_sink_set_max_request(pa_sink *s, size_t max_request);
+void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency);
+
+void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume);
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume);
+void pa_sink_mute_changed(pa_sink *s, bool new_muted);
+
+void pa_sink_update_flags(pa_sink *s, pa_sink_flags_t mask, pa_sink_flags_t value);
+
+bool pa_device_init_description(pa_proplist *p, pa_card *card);
+bool pa_device_init_icon(pa_proplist *p, bool is_sink);
+bool pa_device_init_intended_roles(pa_proplist *p);
+unsigned pa_device_init_priority(pa_proplist *p);
+
+/**** May be called by everyone, from main context */
+
+int pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough);
+void pa_sink_set_port_latency_offset(pa_sink *s, int64_t offset);
+
+/* The returned value is supposed to be in the time domain of the sound card! */
+pa_usec_t pa_sink_get_latency(pa_sink *s);
+pa_usec_t pa_sink_get_requested_latency(pa_sink *s);
+void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
+pa_usec_t pa_sink_get_fixed_latency(pa_sink *s);
+
+size_t pa_sink_get_max_rewind(pa_sink *s);
+size_t pa_sink_get_max_request(pa_sink *s);
+
+int pa_sink_update_status(pa_sink*s);
+int pa_sink_suspend(pa_sink *s, bool suspend, pa_suspend_cause_t cause);
+int pa_sink_suspend_all(pa_core *c, bool suspend, pa_suspend_cause_t cause);
+
+/* Use this instead of checking s->flags & PA_SINK_FLAT_VOLUME directly. */
+bool pa_sink_flat_volume_enabled(pa_sink *s);
+
+/* Get the master sink when sharing volumes */
+pa_sink *pa_sink_get_master(pa_sink *s);
+
+bool pa_sink_is_filter(pa_sink *s);
+
+/* Is the sink in passthrough mode? (that is, is there a passthrough sink input
+ * connected to this sink? */
+bool pa_sink_is_passthrough(pa_sink *s);
+/* These should be called when a sink enters/leaves passthrough mode */
+void pa_sink_enter_passthrough(pa_sink *s);
+void pa_sink_leave_passthrough(pa_sink *s);
+
+void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, bool sendmsg, bool save);
+const pa_cvolume *pa_sink_get_volume(pa_sink *sink, bool force_refresh);
+
+void pa_sink_set_mute(pa_sink *sink, bool mute, bool save);
+bool pa_sink_get_mute(pa_sink *sink, bool force_refresh);
+
+bool pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
+
+int pa_sink_set_port(pa_sink *s, const char *name, bool save);
+void pa_sink_set_mixer_dirty(pa_sink *s, bool is_dirty);
+
+unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
+unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
+
+/* Returns how many streams are active that don't allow suspensions. If
+ * "ignore_input" or "ignore_output" is non-NULL, that stream is not included
+ * in the count (the returned count includes the value from
+ * pa_source_check_suspend(), which is called for the monitor source, so that's
+ * why "ignore_output" may be relevant). */
+unsigned pa_sink_check_suspend(pa_sink *s, pa_sink_input *ignore_input, pa_source_output *ignore_output);
+
+#define pa_sink_get_state(s) ((s)->state)
+
+/* Moves all inputs away, and stores them in pa_queue */
+pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q);
+void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, bool save);
+void pa_sink_move_all_fail(pa_queue *q);
+
+/* Returns a copy of the sink formats. TODO: Get rid of this function (or at
+ * least get rid of the copying). There's no good reason to copy the formats
+ * every time someone wants to know what formats the sink supports. The formats
+ * idxset could be stored directly in the pa_sink struct.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=71924 */
+pa_idxset* pa_sink_get_formats(pa_sink *s);
+
+bool pa_sink_set_formats(pa_sink *s, pa_idxset *formats);
+bool pa_sink_check_format(pa_sink *s, pa_format_info *f);
+pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats);
+
+/*** To be called exclusively by the sink driver, from IO context */
+
+void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
+void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
+void pa_sink_render_into(pa_sink*s, pa_memchunk *target);
+void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target);
+
+void pa_sink_process_rewind(pa_sink *s, size_t nbytes);
+
+int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
+void pa_sink_attach_within_thread(pa_sink *s);
+void pa_sink_detach_within_thread(pa_sink *s);
+
+pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s);
+
+void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind);
+void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request);
+
+void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_sink_set_fixed_latency_within_thread(pa_sink *s, pa_usec_t latency);
+
+void pa_sink_update_volume_and_mute(pa_sink *s);
+
+bool pa_sink_volume_change_apply(pa_sink *s, pa_usec_t *usec_to_next);
+
+size_t pa_sink_process_input_underruns(pa_sink *s, size_t left_to_play);
+
+/*** To be called exclusively by sink input drivers, from IO context */
+
+void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
+
+void pa_sink_invalidate_requested_latency(pa_sink *s, bool dynamic);
+
+int64_t pa_sink_get_latency_within_thread(pa_sink *s, bool allow_negative);
+
+/* Called from the main thread, from sink-input.c only. The normal way to set
+ * the sink reference volume is to call pa_sink_set_volume(), but the flat
+ * volume logic in sink-input.c needs also a function that doesn't do all the
+ * extra stuff that pa_sink_set_volume() does. This function simply sets
+ * s->reference_volume and fires change notifications. */
+void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume);
+
+/* Verify that we called in IO context (aka 'thread context), or that
+ * the sink is not yet set up, i.e. the thread not set up yet. See
+ * pa_assert_io_context() in thread-mq.h for more information. */
+#define pa_sink_assert_io_context(s) \
+    pa_assert(pa_thread_mq_get() || !PA_SINK_IS_LINKED((s)->state))
+
+#endif
diff --git a/src/pulsecore/sioman.c b/src/pulsecore/sioman.c
new file mode 100644 (file)
index 0000000..315f10a
--- /dev/null
@@ -0,0 +1,37 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
+
+#include "sioman.h"
+
+static pa_atomic_t stdio_inuse = PA_ATOMIC_INIT(0);
+
+int pa_stdio_acquire(void) {
+    return pa_atomic_cmpxchg(&stdio_inuse, 0, 1) ? 0 : -1;
+}
+
+void pa_stdio_release(void) {
+    pa_assert_se(pa_atomic_cmpxchg(&stdio_inuse, 1, 0));
+}
diff --git a/src/pulsecore/sioman.h b/src/pulsecore/sioman.h
new file mode 100644 (file)
index 0000000..10ad382
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef foosiomanhfoo
+#define foosiomanhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+int pa_stdio_acquire(void);
+void pa_stdio_release(void);
+
+#endif
diff --git a/src/pulsecore/sndfile-util.c b/src/pulsecore/sndfile-util.c
new file mode 100644 (file)
index 0000000..b6cc65e
--- /dev/null
@@ -0,0 +1,461 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* Shared between pacat/parec/paplay and the server */
+
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/macro.h>
+
+#include "sndfile-util.h"
+
+int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss) {
+    SF_INFO sfi;
+    int sf_errno;
+
+    pa_assert(sf);
+    pa_assert(ss);
+
+    pa_zero(sfi);
+    if ((sf_errno = sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)))) {
+        pa_log_error("sndfile: %s", sf_error_number(sf_errno));
+        return -1;
+    }
+
+    switch (sfi.format & SF_FORMAT_SUBMASK) {
+
+        case SF_FORMAT_PCM_16:
+        case SF_FORMAT_PCM_U8:
+        case SF_FORMAT_PCM_S8:
+            ss->format = PA_SAMPLE_S16NE;
+            break;
+
+        case SF_FORMAT_PCM_24:
+            ss->format = PA_SAMPLE_S24NE;
+            break;
+
+        case SF_FORMAT_PCM_32:
+            ss->format = PA_SAMPLE_S32NE;
+            break;
+
+        case SF_FORMAT_ULAW:
+            ss->format = PA_SAMPLE_ULAW;
+            break;
+
+        case SF_FORMAT_ALAW:
+            ss->format = PA_SAMPLE_ALAW;
+            break;
+
+        case SF_FORMAT_FLOAT:
+        case SF_FORMAT_DOUBLE:
+        default:
+            ss->format = PA_SAMPLE_FLOAT32NE;
+            break;
+    }
+
+    ss->rate = (uint32_t) sfi.samplerate;
+    ss->channels = (uint8_t) sfi.channels;
+
+    if (!pa_sample_spec_valid(ss))
+        return -1;
+
+    return 0;
+}
+
+int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss) {
+    pa_assert(sfi);
+    pa_assert(ss);
+
+    sfi->samplerate = (int) ss->rate;
+    sfi->channels = (int) ss->channels;
+
+    if (pa_sample_format_is_le(ss->format) > 0)
+        sfi->format = SF_ENDIAN_LITTLE;
+    else if (pa_sample_format_is_be(ss->format) > 0)
+        sfi->format = SF_ENDIAN_BIG;
+
+    switch (ss->format) {
+
+        case PA_SAMPLE_U8:
+            ss->format = PA_SAMPLE_S16NE;
+            sfi->format = SF_FORMAT_PCM_U8;
+            break;
+
+        case PA_SAMPLE_S16LE:
+        case PA_SAMPLE_S16BE:
+            ss->format = PA_SAMPLE_S16NE;
+            sfi->format |= SF_FORMAT_PCM_16;
+            break;
+
+        case PA_SAMPLE_S24LE:
+        case PA_SAMPLE_S24BE:
+            ss->format = PA_SAMPLE_S24NE;
+            sfi->format |= SF_FORMAT_PCM_24;
+            break;
+
+        case PA_SAMPLE_S24_32LE:
+        case PA_SAMPLE_S24_32BE:
+            ss->format = PA_SAMPLE_S24_32NE;
+            sfi->format |= SF_FORMAT_PCM_32;
+            break;
+
+        case PA_SAMPLE_S32LE:
+        case PA_SAMPLE_S32BE:
+            ss->format = PA_SAMPLE_S32NE;
+            sfi->format |= SF_FORMAT_PCM_32;
+            break;
+
+        case PA_SAMPLE_ULAW:
+            sfi->format = SF_FORMAT_ULAW;
+            break;
+
+        case PA_SAMPLE_ALAW:
+            sfi->format = SF_FORMAT_ALAW;
+            break;
+
+        case PA_SAMPLE_FLOAT32LE:
+        case PA_SAMPLE_FLOAT32BE:
+        default:
+            ss->format = PA_SAMPLE_FLOAT32NE;
+            sfi->format |= SF_FORMAT_FLOAT;
+            break;
+    }
+
+    if (!pa_sample_spec_valid(ss))
+        return -1;
+
+    return 0;
+}
+
+int pa_sndfile_read_channel_map(SNDFILE *sf, pa_channel_map *cm) {
+
+    static const pa_channel_position_t table[] = {
+        [SF_CHANNEL_MAP_MONO] =                  PA_CHANNEL_POSITION_MONO,
+        [SF_CHANNEL_MAP_LEFT] =                  PA_CHANNEL_POSITION_FRONT_LEFT, /* libsndfile distinguishes left and front-left, which we don't */
+        [SF_CHANNEL_MAP_RIGHT] =                 PA_CHANNEL_POSITION_FRONT_RIGHT,
+        [SF_CHANNEL_MAP_CENTER] =                PA_CHANNEL_POSITION_FRONT_CENTER,
+        [SF_CHANNEL_MAP_FRONT_LEFT] =            PA_CHANNEL_POSITION_FRONT_LEFT,
+        [SF_CHANNEL_MAP_FRONT_RIGHT] =           PA_CHANNEL_POSITION_FRONT_RIGHT,
+        [SF_CHANNEL_MAP_FRONT_CENTER] =          PA_CHANNEL_POSITION_FRONT_CENTER,
+        [SF_CHANNEL_MAP_REAR_CENTER] =           PA_CHANNEL_POSITION_REAR_CENTER,
+        [SF_CHANNEL_MAP_REAR_LEFT] =             PA_CHANNEL_POSITION_REAR_LEFT,
+        [SF_CHANNEL_MAP_REAR_RIGHT] =            PA_CHANNEL_POSITION_REAR_RIGHT,
+        [SF_CHANNEL_MAP_LFE] =                   PA_CHANNEL_POSITION_LFE,
+        [SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER] =  PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
+        [SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
+        [SF_CHANNEL_MAP_SIDE_LEFT] =             PA_CHANNEL_POSITION_SIDE_LEFT,
+        [SF_CHANNEL_MAP_SIDE_RIGHT] =            PA_CHANNEL_POSITION_SIDE_RIGHT,
+        [SF_CHANNEL_MAP_TOP_CENTER] =            PA_CHANNEL_POSITION_TOP_CENTER,
+        [SF_CHANNEL_MAP_TOP_FRONT_LEFT] =        PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
+        [SF_CHANNEL_MAP_TOP_FRONT_RIGHT] =       PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
+        [SF_CHANNEL_MAP_TOP_FRONT_CENTER] =      PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
+        [SF_CHANNEL_MAP_TOP_REAR_LEFT] =         PA_CHANNEL_POSITION_TOP_REAR_LEFT,
+        [SF_CHANNEL_MAP_TOP_REAR_RIGHT] =        PA_CHANNEL_POSITION_TOP_REAR_RIGHT,
+        [SF_CHANNEL_MAP_TOP_REAR_CENTER] =       PA_CHANNEL_POSITION_TOP_REAR_CENTER
+    };
+
+    SF_INFO sfi;
+    int sf_errno;
+    int *channels;
+    unsigned c;
+
+    pa_assert(sf);
+    pa_assert(cm);
+
+    pa_zero(sfi);
+    if ((sf_errno = sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)))) {
+        pa_log_error("sndfile: %s", sf_error_number(sf_errno));
+        return -1;
+    }
+
+    channels = pa_xnew(int, sfi.channels);
+    if (!sf_command(sf, SFC_GET_CHANNEL_MAP_INFO, channels, sizeof(channels[0]) * sfi.channels)) {
+        pa_xfree(channels);
+        return -1;
+    }
+
+    cm->channels = (uint8_t) sfi.channels;
+    for (c = 0; c < cm->channels; c++) {
+        if (channels[c] <= SF_CHANNEL_MAP_INVALID ||
+            (unsigned) channels[c] >= PA_ELEMENTSOF(table)) {
+            pa_xfree(channels);
+            return -1;
+        }
+
+        cm->map[c] = table[channels[c]];
+    }
+
+    pa_xfree(channels);
+
+    if (!pa_channel_map_valid(cm))
+        return -1;
+
+    return 0;
+}
+
+int pa_sndfile_write_channel_map(SNDFILE *sf, pa_channel_map *cm) {
+    static const int table[PA_CHANNEL_POSITION_MAX] = {
+        [PA_CHANNEL_POSITION_MONO] = SF_CHANNEL_MAP_MONO,
+
+        [PA_CHANNEL_POSITION_FRONT_LEFT] = SF_CHANNEL_MAP_FRONT_LEFT,
+        [PA_CHANNEL_POSITION_FRONT_RIGHT] = SF_CHANNEL_MAP_FRONT_RIGHT,
+        [PA_CHANNEL_POSITION_FRONT_CENTER] = SF_CHANNEL_MAP_FRONT_CENTER,
+
+        [PA_CHANNEL_POSITION_REAR_CENTER] = SF_CHANNEL_MAP_REAR_CENTER,
+        [PA_CHANNEL_POSITION_REAR_LEFT] = SF_CHANNEL_MAP_REAR_LEFT,
+        [PA_CHANNEL_POSITION_REAR_RIGHT] = SF_CHANNEL_MAP_REAR_RIGHT,
+
+        [PA_CHANNEL_POSITION_LFE] = SF_CHANNEL_MAP_LFE,
+
+        [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER,
+        [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER,
+
+        [PA_CHANNEL_POSITION_SIDE_LEFT] = SF_CHANNEL_MAP_SIDE_LEFT,
+        [PA_CHANNEL_POSITION_SIDE_RIGHT] = SF_CHANNEL_MAP_SIDE_RIGHT,
+
+        [PA_CHANNEL_POSITION_AUX0] = -1,
+        [PA_CHANNEL_POSITION_AUX1] = -1,
+        [PA_CHANNEL_POSITION_AUX2] = -1,
+        [PA_CHANNEL_POSITION_AUX3] = -1,
+        [PA_CHANNEL_POSITION_AUX4] = -1,
+        [PA_CHANNEL_POSITION_AUX5] = -1,
+        [PA_CHANNEL_POSITION_AUX6] = -1,
+        [PA_CHANNEL_POSITION_AUX7] = -1,
+        [PA_CHANNEL_POSITION_AUX8] = -1,
+        [PA_CHANNEL_POSITION_AUX9] = -1,
+        [PA_CHANNEL_POSITION_AUX10] = -1,
+        [PA_CHANNEL_POSITION_AUX11] = -1,
+        [PA_CHANNEL_POSITION_AUX12] = -1,
+        [PA_CHANNEL_POSITION_AUX13] = -1,
+        [PA_CHANNEL_POSITION_AUX14] = -1,
+        [PA_CHANNEL_POSITION_AUX15] = -1,
+        [PA_CHANNEL_POSITION_AUX16] = -1,
+        [PA_CHANNEL_POSITION_AUX17] = -1,
+        [PA_CHANNEL_POSITION_AUX18] = -1,
+        [PA_CHANNEL_POSITION_AUX19] = -1,
+        [PA_CHANNEL_POSITION_AUX20] = -1,
+        [PA_CHANNEL_POSITION_AUX21] = -1,
+        [PA_CHANNEL_POSITION_AUX22] = -1,
+        [PA_CHANNEL_POSITION_AUX23] = -1,
+        [PA_CHANNEL_POSITION_AUX24] = -1,
+        [PA_CHANNEL_POSITION_AUX25] = -1,
+        [PA_CHANNEL_POSITION_AUX26] = -1,
+        [PA_CHANNEL_POSITION_AUX27] = -1,
+        [PA_CHANNEL_POSITION_AUX28] = -1,
+        [PA_CHANNEL_POSITION_AUX29] = -1,
+        [PA_CHANNEL_POSITION_AUX30] = -1,
+        [PA_CHANNEL_POSITION_AUX31] = -1,
+
+        [PA_CHANNEL_POSITION_TOP_CENTER] = SF_CHANNEL_MAP_TOP_CENTER,
+
+        [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SF_CHANNEL_MAP_TOP_FRONT_LEFT,
+        [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SF_CHANNEL_MAP_TOP_FRONT_RIGHT,
+        [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SF_CHANNEL_MAP_TOP_FRONT_CENTER ,
+
+        [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SF_CHANNEL_MAP_TOP_REAR_LEFT,
+        [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SF_CHANNEL_MAP_TOP_REAR_RIGHT,
+        [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SF_CHANNEL_MAP_TOP_REAR_CENTER,
+    };
+
+    int *channels;
+    unsigned c;
+
+    pa_assert(sf);
+    pa_assert(cm);
+
+    /* Suppress channel mapping for the obvious cases */
+    if (cm->channels == 1 && cm->map[0] == PA_CHANNEL_POSITION_MONO)
+        return 0;
+
+    if (cm->channels == 2 &&
+        cm->map[0] == PA_CHANNEL_POSITION_FRONT_LEFT &&
+        cm->map[1] == PA_CHANNEL_POSITION_FRONT_RIGHT)
+        return 0;
+
+    channels = pa_xnew(int, cm->channels);
+    for (c = 0; c < cm->channels; c++) {
+
+        if (cm->map[c] < 0 ||
+            cm->map[c] >= PA_CHANNEL_POSITION_MAX ||
+            table[cm->map[c]] < 0) {
+            pa_xfree(channels);
+            return -1;
+        }
+
+        channels[c] = table[cm->map[c]];
+    }
+
+    if (!sf_command(sf, SFC_SET_CHANNEL_MAP_INFO, channels, sizeof(channels[0]) * cm->channels)) {
+        pa_xfree(channels);
+        return -1;
+    }
+
+    pa_xfree(channels);
+    return 0;
+}
+
+void pa_sndfile_init_proplist(SNDFILE *sf, pa_proplist *p) {
+
+    static const char* table[] = {
+        [SF_STR_TITLE] = PA_PROP_MEDIA_TITLE,
+        [SF_STR_COPYRIGHT] = PA_PROP_MEDIA_COPYRIGHT,
+        [SF_STR_SOFTWARE] = PA_PROP_MEDIA_SOFTWARE,
+        [SF_STR_ARTIST] = PA_PROP_MEDIA_ARTIST,
+        [SF_STR_COMMENT] = "media.comment",
+        [SF_STR_DATE] = "media.date"
+    };
+
+    SF_INFO sfi;
+    SF_FORMAT_INFO fi;
+    int sf_errno;
+    unsigned c;
+
+    pa_assert(sf);
+    pa_assert(p);
+
+    for (c = 0; c < PA_ELEMENTSOF(table); c++) {
+        const char *s;
+        char *t;
+
+        if (!table[c])
+            continue;
+
+        if (!(s = sf_get_string(sf, c)))
+            continue;
+
+        t = pa_utf8_filter(s);
+        pa_proplist_sets(p, table[c], t);
+        pa_xfree(t);
+    }
+
+    pa_zero(sfi);
+    if ((sf_errno = sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)))) {
+        pa_log_error("sndfile: %s", sf_error_number(sf_errno));
+        return;
+    }
+
+    pa_zero(fi);
+    fi.format = sfi.format;
+    if (sf_command(sf, SFC_GET_FORMAT_INFO, &fi, sizeof(fi)) == 0 && fi.name) {
+        char *t;
+
+        t = pa_utf8_filter(fi.name);
+        pa_proplist_sets(p, "media.format", t);
+        pa_xfree(t);
+    }
+}
+
+pa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss) {
+    pa_assert(ss);
+
+    switch (ss->format) {
+        case PA_SAMPLE_S16NE:
+            return (pa_sndfile_readf_t) sf_readf_short;
+
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S24_32NE:
+            return (pa_sndfile_readf_t) sf_readf_int;
+
+        case PA_SAMPLE_FLOAT32NE:
+            return (pa_sndfile_readf_t) sf_readf_float;
+
+        case PA_SAMPLE_ULAW:
+        case PA_SAMPLE_ALAW:
+        case PA_SAMPLE_S24NE:
+            return NULL;
+
+        default:
+            pa_assert_not_reached();
+    }
+}
+
+pa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss) {
+    pa_assert(ss);
+
+    switch (ss->format) {
+        case PA_SAMPLE_S16NE:
+            return (pa_sndfile_writef_t) sf_writef_short;
+
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S24_32NE:
+            return (pa_sndfile_writef_t) sf_writef_int;
+
+        case PA_SAMPLE_FLOAT32NE:
+            return (pa_sndfile_writef_t) sf_writef_float;
+
+        case PA_SAMPLE_ULAW:
+        case PA_SAMPLE_ALAW:
+        case PA_SAMPLE_S24NE:
+            return NULL;
+
+        default:
+            pa_assert_not_reached();
+    }
+}
+
+int pa_sndfile_format_from_string(const char *name) {
+    int i, count = 0;
+
+    if (!name[0])
+        return -1;
+
+    pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0);
+
+    for (i = 0; i < count; i++) {
+        SF_FORMAT_INFO fi;
+        pa_zero(fi);
+        fi.format = i;
+
+        pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
+
+        /* First try to match via full type string */
+        if (strcasecmp(name, fi.name) == 0)
+            return fi.format;
+
+        /* Then, try to match via the full extension */
+        if (strcasecmp(name, fi.extension) == 0)
+            return fi.format;
+
+        /* Then, try to match via the start of the type string */
+        if (strncasecmp(name, fi.name, strlen(name)) == 0)
+            return fi.format;
+    }
+
+    return -1;
+}
+
+void pa_sndfile_dump_formats(void) {
+    int i, count = 0;
+
+    pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0);
+
+    for (i = 0; i < count; i++) {
+        SF_FORMAT_INFO fi;
+        pa_zero(fi);
+        fi.format = i;
+
+        pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
+        printf("%s\t%s\n", fi.extension, fi.name);
+    }
+}
diff --git a/src/pulsecore/sndfile-util.h b/src/pulsecore/sndfile-util.h
new file mode 100644 (file)
index 0000000..1f67ac3
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef foopulsecoresndfileutilhfoo
+#define foopulsecoresndfileutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sndfile.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/proplist.h>
+
+int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss);
+int pa_sndfile_read_channel_map(SNDFILE *sf, pa_channel_map *cm);
+
+int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss);
+int pa_sndfile_write_channel_map(SNDFILE *sf, pa_channel_map *cm);
+
+void pa_sndfile_init_proplist(SNDFILE *sf, pa_proplist *p);
+
+typedef sf_count_t (*pa_sndfile_readf_t)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
+typedef sf_count_t (*pa_sndfile_writef_t)(SNDFILE *sndfile, const void *ptr, sf_count_t frames);
+
+/* Returns NULL if sf_read_raw() shall be used */
+pa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss);
+
+/* Returns NULL if sf_write_raw() shall be used */
+pa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss);
+
+int pa_sndfile_format_from_string(const char *extension);
+
+void pa_sndfile_dump_formats(void);
+
+#endif
diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c
new file mode 100644 (file)
index 0000000..c87406d
--- /dev/null
@@ -0,0 +1,563 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2004-2006 Lennart Poettering
+    Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as
+    published by the Free Software Foundation; either version 2.1 of the
+    License, or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* #undef HAVE_LIBASYNCNS */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_LIBASYNCNS
+#include <asyncns.h>
+#endif
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/socket.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/parseaddr.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/arpa-inet.h>
+
+#include "socket-client.h"
+
+#define CONNECT_TIMEOUT 5
+
+struct pa_socket_client {
+    PA_REFCNT_DECLARE;
+    int fd;
+
+    pa_mainloop_api *mainloop;
+    pa_io_event *io_event;
+    pa_time_event *timeout_event;
+    pa_defer_event *defer_event;
+
+    pa_socket_client_cb_t callback;
+    void *userdata;
+
+    bool local;
+
+#ifdef HAVE_LIBASYNCNS
+    asyncns_t *asyncns;
+    asyncns_query_t * asyncns_query;
+    pa_io_event *asyncns_io_event;
+#endif
+};
+
+static pa_socket_client* socket_client_new(pa_mainloop_api *m) {
+    pa_socket_client *c;
+    pa_assert(m);
+
+    c = pa_xnew0(pa_socket_client, 1);
+    PA_REFCNT_INIT(c);
+    c->mainloop = m;
+    c->fd = -1;
+
+    return c;
+}
+
+static void free_events(pa_socket_client *c) {
+    pa_assert(c);
+
+    if (c->io_event) {
+        c->mainloop->io_free(c->io_event);
+        c->io_event = NULL;
+    }
+
+    if (c->timeout_event) {
+        c->mainloop->time_free(c->timeout_event);
+        c->timeout_event = NULL;
+    }
+
+    if (c->defer_event) {
+        c->mainloop->defer_free(c->defer_event);
+        c->defer_event = NULL;
+    }
+}
+
+static void do_call(pa_socket_client *c) {
+    pa_iochannel *io = NULL;
+    int error;
+    socklen_t lerror;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(c->callback);
+
+    pa_socket_client_ref(c);
+
+    if (c->fd < 0)
+        goto finish;
+
+    lerror = sizeof(error);
+    if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &lerror) < 0) {
+        pa_log("getsockopt(): %s", pa_cstrerror(errno));
+        goto finish;
+    }
+
+    if (lerror != sizeof(error)) {
+        pa_log("getsockopt() returned invalid size.");
+        goto finish;
+    }
+
+    if (error != 0) {
+        pa_log_debug("connect(): %s", pa_cstrerror(error));
+        errno = error;
+        goto finish;
+    }
+
+    io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
+
+finish:
+    if (!io && c->fd >= 0)
+        pa_close(c->fd);
+    c->fd = -1;
+
+    free_events(c);
+
+    c->callback(c, io, c->userdata);
+
+    pa_socket_client_unref(c);
+}
+
+static void connect_defer_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+    pa_socket_client *c = userdata;
+
+    pa_assert(m);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(c->defer_event == e);
+
+    do_call(c);
+}
+
+static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    pa_socket_client *c = userdata;
+
+    pa_assert(m);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(c->io_event == e);
+    pa_assert(fd >= 0);
+
+    do_call(c);
+}
+
+static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(sa);
+    pa_assert(len > 0);
+
+    pa_make_fd_nonblock(c->fd);
+
+    if (connect(c->fd, sa, len) < 0) {
+#ifdef OS_IS_WIN32
+        if (WSAGetLastError() != EWOULDBLOCK) {
+            pa_log_debug("connect(): %d", WSAGetLastError());
+#else
+        if (errno != EINPROGRESS) {
+            pa_log_debug("connect(): %s (%d)", pa_cstrerror(errno), errno);
+#endif
+            return -1;
+        }
+
+        c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c);
+    } else
+        c->defer_event = c->mainloop->defer_new(c->mainloop, connect_defer_cb, c);
+
+    return 0;
+}
+
+pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port) {
+    struct sockaddr_in sa;
+
+    pa_assert(m);
+    pa_assert(port > 0);
+
+    pa_zero(sa);
+    sa.sin_family = AF_INET;
+    sa.sin_port = htons(port);
+    sa.sin_addr.s_addr = htonl(address);
+
+    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
+}
+
+pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {
+#ifdef HAVE_SYS_UN_H
+    struct sockaddr_un sa;
+
+    pa_assert(m);
+    pa_assert(filename);
+
+    pa_zero(sa);
+    sa.sun_family = AF_UNIX;
+    pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
+
+    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
+#else /* HAVE_SYS_UN_H */
+
+    return NULL;
+#endif /* HAVE_SYS_UN_H */
+}
+
+static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size_t salen) {
+    pa_assert(c);
+    pa_assert(sa);
+    pa_assert(salen);
+
+    c->local = pa_socket_address_is_local(sa);
+
+    if ((c->fd = pa_socket_cloexec(sa->sa_family, SOCK_STREAM, 0)) < 0) {
+        pa_log("socket(): %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+#ifdef HAVE_IPV6
+    if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
+#else
+    if (sa->sa_family == AF_INET)
+#endif
+        pa_make_tcp_socket_low_delay(c->fd);
+    else
+        pa_make_socket_low_delay(c->fd);
+
+    if (do_connect(c, sa, (socklen_t) salen) < 0)
+        return -1;
+
+    return 0;
+}
+
+pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) {
+    pa_socket_client *c;
+
+    pa_assert(m);
+    pa_assert(sa);
+    pa_assert(salen > 0);
+
+    c = socket_client_new(m);
+
+    if (sockaddr_prepare(c, sa, salen) < 0)
+        goto fail;
+
+    return c;
+
+fail:
+    pa_socket_client_unref(c);
+    return NULL;
+}
+
+static void socket_client_free(pa_socket_client *c) {
+    pa_assert(c);
+    pa_assert(c->mainloop);
+
+    free_events(c);
+
+    if (c->fd >= 0)
+        pa_close(c->fd);
+
+#ifdef HAVE_LIBASYNCNS
+    if (c->asyncns_query)
+        asyncns_cancel(c->asyncns, c->asyncns_query);
+    if (c->asyncns)
+        asyncns_free(c->asyncns);
+    if (c->asyncns_io_event)
+        c->mainloop->io_free(c->asyncns_io_event);
+#endif
+
+    pa_xfree(c);
+}
+
+void pa_socket_client_unref(pa_socket_client *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (PA_REFCNT_DEC(c) <= 0)
+        socket_client_free(c);
+}
+
+pa_socket_client* pa_socket_client_ref(pa_socket_client *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_REFCNT_INC(c);
+    return c;
+}
+
+void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    c->callback = on_connection;
+    c->userdata = userdata;
+}
+
+pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 sa;
+
+    pa_assert(m);
+    pa_assert(address);
+    pa_assert(port > 0);
+
+    pa_zero(sa);
+    sa.sin6_family = AF_INET6;
+    sa.sin6_port = htons(port);
+    memcpy(&sa.sin6_addr, address, sizeof(sa.sin6_addr));
+
+    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
+
+#else
+    return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBASYNCNS
+
+static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    pa_socket_client *c = userdata;
+    struct addrinfo *res = NULL;
+    int ret;
+
+    pa_assert(m);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(c->asyncns_io_event == e);
+    pa_assert(fd >= 0);
+
+    if (asyncns_wait(c->asyncns, 0) < 0)
+        goto fail;
+
+    if (!asyncns_isdone(c->asyncns, c->asyncns_query))
+        return;
+
+    ret = asyncns_getaddrinfo_done(c->asyncns, c->asyncns_query, &res);
+    c->asyncns_query = NULL;
+
+    if (ret != 0 || !res)
+        goto fail;
+
+    if (res->ai_addr)
+        if (sockaddr_prepare(c, res->ai_addr, res->ai_addrlen) < 0)
+            goto fail;
+
+    asyncns_freeaddrinfo(res);
+
+    m->io_free(c->asyncns_io_event);
+    c->asyncns_io_event = NULL;
+    return;
+
+fail:
+    m->io_free(c->asyncns_io_event);
+    c->asyncns_io_event = NULL;
+
+    errno = EHOSTUNREACH;
+    do_call(c);
+    return;
+
+}
+
+#endif
+
+static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
+    pa_socket_client *c = userdata;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(c);
+
+    if (c->fd >= 0) {
+        pa_close(c->fd);
+        c->fd = -1;
+    }
+
+    errno = ETIMEDOUT;
+    do_call(c);
+}
+
+static void start_timeout(pa_socket_client *c, bool use_rtclock) {
+    struct timeval tv;
+
+    pa_assert(c);
+    pa_assert(!c->timeout_event);
+
+    c->timeout_event = c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, pa_rtclock_now() + CONNECT_TIMEOUT * PA_USEC_PER_SEC, use_rtclock), timeout_cb, c);
+}
+
+pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, bool use_rtclock, const char*name, uint16_t default_port) {
+    pa_socket_client *c = NULL;
+    pa_parsed_address a;
+    char *name_buf;
+
+    pa_assert(m);
+    pa_assert(name);
+
+    a.path_or_host = NULL;
+
+    if (pa_is_ip6_address(name)) {
+        size_t len = strlen(name);
+        name_buf = pa_xmalloc(len + 3);
+        memcpy(name_buf + 1, name, len);
+        name_buf[0] = '[';
+        name_buf[len + 1] = ']';
+        name_buf[len + 2] = '\0';
+    } else {
+        name_buf = pa_xstrdup(name);
+    }
+
+    if (pa_parse_address(name_buf, &a) < 0) {
+        pa_log_warn("parsing address failed: %s", name_buf);
+        goto finish;
+    }
+
+    if (!a.port)
+        a.port = default_port;
+
+    switch (a.type) {
+        case PA_PARSED_ADDRESS_UNIX:
+            if ((c = pa_socket_client_new_unix(m, a.path_or_host)))
+                start_timeout(c, use_rtclock);
+            break;
+
+        case PA_PARSED_ADDRESS_TCP4:  /* Fallthrough */
+        case PA_PARSED_ADDRESS_TCP6:  /* Fallthrough */
+        case PA_PARSED_ADDRESS_TCP_AUTO: {
+            struct addrinfo hints;
+            char port[12];
+
+            pa_snprintf(port, sizeof(port), "%u", (unsigned) a.port);
+
+            pa_zero(hints);
+            if (a.type == PA_PARSED_ADDRESS_TCP4)
+                hints.ai_family = PF_INET;
+#ifdef HAVE_IPV6
+            else if (a.type == PA_PARSED_ADDRESS_TCP6)
+                hints.ai_family = PF_INET6;
+#endif
+            else
+                hints.ai_family = PF_UNSPEC;
+
+            hints.ai_socktype = SOCK_STREAM;
+
+#if defined(HAVE_LIBASYNCNS)
+            {
+                asyncns_t *asyncns;
+
+                if (!(asyncns = asyncns_new(1)))
+                    goto finish;
+
+                c = socket_client_new(m);
+                c->asyncns = asyncns;
+                c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c);
+                pa_assert_se(c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints));
+                start_timeout(c, use_rtclock);
+            }
+#elif defined(HAVE_GETADDRINFO)
+            {
+                int ret;
+                struct addrinfo *res = NULL;
+
+                ret = getaddrinfo(a.path_or_host, port, &hints, &res);
+
+                if (ret < 0 || !res)
+                    goto finish;
+
+                if (res->ai_addr) {
+                    if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen)))
+                        start_timeout(c, use_rtclock);
+                }
+
+                freeaddrinfo(res);
+            }
+#else
+            {
+                struct hostent *host = NULL;
+                struct sockaddr_in s;
+
+#ifdef HAVE_IPV6
+                /* FIXME: PF_INET6 support */
+                if (hints.ai_family == PF_INET6) {
+                    pa_log_error("IPv6 is not supported on Windows");
+                    goto finish;
+                }
+#endif
+
+                host = gethostbyname(a.path_or_host);
+                if (!host) {
+                    unsigned int addr = inet_addr(a.path_or_host);
+                    if (addr != INADDR_NONE)
+                        host = gethostbyaddr((char*)&addr, 4, AF_INET);
+                }
+
+                if (!host)
+                    goto finish;
+
+                pa_zero(s);
+                s.sin_family = AF_INET;
+                memcpy(&s.sin_addr, host->h_addr, sizeof(struct in_addr));
+                s.sin_port = htons(a.port);
+
+                if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s))))
+                    start_timeout(c, use_rtclock);
+            }
+#endif /* HAVE_LIBASYNCNS */
+        }
+    }
+
+finish:
+    pa_xfree(name_buf);
+    pa_xfree(a.path_or_host);
+    return c;
+
+}
+
+/* Return non-zero when the target sockaddr is considered
+   local. "local" means UNIX socket or TCP socket on localhost. Other
+   local IP addresses are not considered local. */
+bool pa_socket_client_is_local(pa_socket_client *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    return c->local;
+}
diff --git a/src/pulsecore/socket-client.h b/src/pulsecore/socket-client.h
new file mode 100644 (file)
index 0000000..9d22072
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef foosocketclienthfoo
+#define foosocketclienthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulsecore/iochannel.h>
+
+struct sockaddr;
+
+typedef struct pa_socket_client pa_socket_client;
+
+typedef void (*pa_socket_client_cb_t)(pa_socket_client *c, pa_iochannel*io, void *userdata);
+
+pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port);
+pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port);
+pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename);
+pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen);
+pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, bool use_rtclock, const char *a, uint16_t default_port);
+
+pa_socket_client* pa_socket_client_ref(pa_socket_client *c);
+void pa_socket_client_unref(pa_socket_client *c);
+
+void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata);
+
+bool pa_socket_client_is_local(pa_socket_client *c);
+
+#endif
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
new file mode 100644 (file)
index 0000000..bc5116a
--- /dev/null
@@ -0,0 +1,613 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#ifndef SUN_LEN
+#define SUN_LEN(ptr) \
+    ((size_t)(((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path))
+#endif
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+
+/* Solaris requires that the allow_severity and deny_severity variables be
+ * defined in the client program. */
+#ifdef __sun
+#include <syslog.h>
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif
+
+#endif /* HAVE_LIBWRAP */
+
+#ifdef HAVE_SYSTEMD_DAEMON
+#include <systemd/sd-daemon.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/socket.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/arpa-inet.h>
+
+#include "socket-server.h"
+
+struct pa_socket_server {
+    PA_REFCNT_DECLARE;
+    int fd;
+    char *filename;
+    bool activated;
+    char *tcpwrap_service;
+
+    pa_socket_server_on_connection_cb_t on_connection;
+    void *userdata;
+
+    pa_io_event *io_event;
+    pa_mainloop_api *mainloop;
+    enum {
+        SOCKET_SERVER_IPV4,
+        SOCKET_SERVER_UNIX,
+        SOCKET_SERVER_IPV6
+    } type;
+};
+
+static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    pa_socket_server *s = userdata;
+    pa_iochannel *io;
+    int nfd;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(s->mainloop == mainloop);
+    pa_assert(s->io_event == e);
+    pa_assert(e);
+    pa_assert(fd >= 0);
+    pa_assert(fd == s->fd);
+
+    pa_socket_server_ref(s);
+
+    if ((nfd = pa_accept_cloexec(fd, NULL, NULL)) < 0) {
+        pa_log("accept(): %s", pa_cstrerror(errno));
+        goto finish;
+    }
+
+    if (!s->on_connection) {
+        pa_close(nfd);
+        goto finish;
+    }
+
+#ifdef HAVE_LIBWRAP
+
+    if (s->tcpwrap_service) {
+        struct request_info req;
+
+        request_init(&req, RQ_DAEMON, s->tcpwrap_service, RQ_FILE, nfd, NULL);
+        fromhost(&req);
+        if (!hosts_access(&req)) {
+            pa_log_warn("TCP connection refused by tcpwrap.");
+            pa_close(nfd);
+            goto finish;
+        }
+
+        pa_log_info("TCP connection accepted by tcpwrap.");
+    }
+#endif
+
+    /* There should be a check for socket type here */
+    if (s->type == SOCKET_SERVER_IPV4)
+        pa_make_tcp_socket_low_delay(nfd);
+    else
+        pa_make_socket_low_delay(nfd);
+
+    pa_assert_se(io = pa_iochannel_new(s->mainloop, nfd, nfd));
+    s->on_connection(s, io, s->userdata);
+
+finish:
+    pa_socket_server_unref(s);
+}
+
+static pa_socket_server* socket_server_new(pa_mainloop_api *m, int fd) {
+    pa_socket_server *s;
+
+    pa_assert(m);
+    pa_assert(fd >= 0);
+
+    s = pa_xnew0(pa_socket_server, 1);
+    PA_REFCNT_INIT(s);
+    s->fd = fd;
+    s->mainloop = m;
+
+    pa_assert_se(s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s));
+
+    return s;
+}
+
+pa_socket_server* pa_socket_server_ref(pa_socket_server *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_REFCNT_INC(s);
+    return s;
+}
+
+#ifdef HAVE_SYS_UN_H
+
+pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
+    int fd = -1;
+    bool activated = false;
+    struct sockaddr_un sa;
+    pa_socket_server *s;
+
+    pa_assert(m);
+    pa_assert(filename);
+
+#ifdef HAVE_SYSTEMD_DAEMON
+    {
+        int n = sd_listen_fds(0);
+        if (n > 0) {
+            for (int i = 0; i < n; ++i) {
+                if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM, 1, filename, 0) > 0) {
+                    fd = SD_LISTEN_FDS_START + i;
+                    activated = true;
+                    pa_log_info("Found socket activation socket for '%s' \\o/", filename);
+                    break;
+                }
+            }
+        }
+    }
+#endif
+
+    if (fd < 0) {
+        if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+            pa_log("socket(PF_UNIX): %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+        memset(&sa, 0, sizeof(sa));
+        sa.sun_family = AF_UNIX;
+        pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
+
+        pa_make_socket_low_delay(fd);
+
+        if (bind(fd, (struct sockaddr*) &sa, (socklen_t) SUN_LEN(&sa)) < 0) {
+            pa_log("bind(): %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+        /* Allow access from all clients. Sockets like this one should
+        * always be put inside a directory with proper access rights,
+        * because not all OS check the access rights on the socket
+        * inodes. */
+        chmod(filename, 0777);
+
+        if (listen(fd, 5) < 0) {
+            pa_log("listen(): %s", pa_cstrerror(errno));
+            goto fail;
+        }
+    }
+
+    pa_assert_se(s = socket_server_new(m, fd));
+
+    s->filename = pa_xstrdup(filename);
+    s->type = SOCKET_SERVER_UNIX;
+    s->activated = activated;
+
+    return s;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return NULL;
+}
+
+#else /* HAVE_SYS_UN_H */
+
+pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
+    return NULL;
+}
+
+#endif /* HAVE_SYS_UN_H */
+
+pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, bool fallback, const char *tcpwrap_service) {
+    pa_socket_server *ss;
+    int fd = -1;
+    bool activated = false;
+    struct sockaddr_in sa;
+    int on = 1;
+
+    pa_assert(m);
+    pa_assert(port);
+
+#ifdef HAVE_SYSTEMD_DAEMON
+    {
+        int n = sd_listen_fds(0);
+        if (n > 0) {
+            for (int i = 0; i < n; ++i) {
+                if (sd_is_socket_inet(SD_LISTEN_FDS_START + i, AF_INET, SOCK_STREAM, 1, port) > 0) {
+                    fd = SD_LISTEN_FDS_START + i;
+                    activated = true;
+                    pa_log_info("Found socket activation socket for ipv4 in port '%d' \\o/", port);
+                    break;
+                }
+            }
+        }
+    }
+#endif
+
+    if (fd < 0) {
+        if ((fd = pa_socket_cloexec(PF_INET, SOCK_STREAM, 0)) < 0) {
+            pa_log("socket(PF_INET): %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+#ifdef SO_REUSEADDR
+        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof(on)) < 0)
+            pa_log("setsockopt(): %s", pa_cstrerror(errno));
+#endif
+
+        pa_make_tcp_socket_low_delay(fd);
+
+        memset(&sa, 0, sizeof(sa));
+        sa.sin_family = AF_INET;
+        sa.sin_port = htons(port);
+        sa.sin_addr.s_addr = htonl(address);
+
+        if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+
+            if (errno == EADDRINUSE && fallback) {
+                sa.sin_port = 0;
+
+                if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+                    pa_log("bind(): %s", pa_cstrerror(errno));
+                    goto fail;
+                }
+            } else {
+                pa_log("bind(): %s", pa_cstrerror(errno));
+                goto fail;
+            }
+        }
+
+        if (listen(fd, 5) < 0) {
+            pa_log("listen(): %s", pa_cstrerror(errno));
+            goto fail;
+        }
+    }
+
+    pa_assert_se(ss = socket_server_new(m, fd));
+
+    ss->type = SOCKET_SERVER_IPV4;
+    ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
+    ss->activated = activated;
+
+    return ss;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return NULL;
+}
+
+#ifdef HAVE_IPV6
+pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port, bool fallback, const char *tcpwrap_service) {
+    pa_socket_server *ss;
+    int fd = -1;
+    bool activated = false;
+    struct sockaddr_in6 sa;
+    int on;
+
+    pa_assert(m);
+    pa_assert(port > 0);
+
+#ifdef HAVE_SYSTEMD_DAEMON
+    {
+        int n = sd_listen_fds(0);
+        if (n > 0) {
+            for (int i = 0; i < n; ++i) {
+                if (sd_is_socket_inet(SD_LISTEN_FDS_START + i, AF_INET6, SOCK_STREAM, 1, port) > 0) {
+                    fd = SD_LISTEN_FDS_START + i;
+                    activated = true;
+                    pa_log_info("Found socket activation socket for ipv6 in port '%d' \\o/", port);
+                    break;
+                }
+            }
+        }
+    }
+#endif
+
+    if (fd < 0) {
+        if ((fd = pa_socket_cloexec(PF_INET6, SOCK_STREAM, 0)) < 0) {
+            pa_log("socket(PF_INET6): %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+#ifdef IPV6_V6ONLY
+        on = 1;
+        if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const void *) &on, sizeof(on)) < 0)
+            pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno));
+#endif
+
+#ifdef SO_REUSEADDR
+        on = 1;
+        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof(on)) < 0)
+            pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno));
+#endif
+
+        pa_make_tcp_socket_low_delay(fd);
+
+        memset(&sa, 0, sizeof(sa));
+        sa.sin6_family = AF_INET6;
+        sa.sin6_port = htons(port);
+        memcpy(sa.sin6_addr.s6_addr, address, 16);
+
+        if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+
+            if (errno == EADDRINUSE && fallback) {
+                sa.sin6_port = 0;
+
+                if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+                    pa_log("bind(): %s", pa_cstrerror(errno));
+                    goto fail;
+                }
+            } else {
+                pa_log("bind(): %s", pa_cstrerror(errno));
+                goto fail;
+            }
+        }
+
+        if (listen(fd, 5) < 0) {
+            pa_log("listen(): %s", pa_cstrerror(errno));
+            goto fail;
+        }
+    }
+
+    pa_assert_se(ss = socket_server_new(m, fd));
+
+    ss->type = SOCKET_SERVER_IPV6;
+    ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
+    ss->activated = activated;
+
+    return ss;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return NULL;
+}
+#endif
+
+pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) {
+    pa_assert(m);
+    pa_assert(port > 0);
+
+    return pa_socket_server_new_ipv4(m, INADDR_LOOPBACK, port, fallback, tcpwrap_service);
+}
+
+#ifdef HAVE_IPV6
+pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) {
+    pa_assert(m);
+    pa_assert(port > 0);
+
+    return pa_socket_server_new_ipv6(m, in6addr_loopback.s6_addr, port, fallback, tcpwrap_service);
+}
+#endif
+
+pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) {
+    pa_assert(m);
+    pa_assert(port > 0);
+
+    return pa_socket_server_new_ipv4(m, INADDR_ANY, port, fallback, tcpwrap_service);
+}
+
+#ifdef HAVE_IPV6
+pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) {
+    pa_assert(m);
+    pa_assert(port > 0);
+
+    return pa_socket_server_new_ipv6(m, in6addr_any.s6_addr, port, fallback, tcpwrap_service);
+}
+#endif
+
+pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, bool fallback, const char *tcpwrap_service) {
+    struct in_addr ipv4;
+
+    pa_assert(m);
+    pa_assert(name);
+    pa_assert(port > 0);
+
+    if (inet_pton(AF_INET, name, &ipv4) > 0)
+        return pa_socket_server_new_ipv4(m, ntohl(ipv4.s_addr), port, fallback, tcpwrap_service);
+
+    return NULL;
+}
+
+#ifdef HAVE_IPV6
+pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, bool fallback, const char *tcpwrap_service) {
+    struct in6_addr ipv6;
+
+    pa_assert(m);
+    pa_assert(name);
+    pa_assert(port > 0);
+
+    if (inet_pton(AF_INET6, name, &ipv6) > 0)
+        return pa_socket_server_new_ipv6(m, ipv6.s6_addr, port, fallback, tcpwrap_service);
+
+    return NULL;
+}
+#endif
+
+static void socket_server_free(pa_socket_server*s) {
+    pa_assert(s);
+
+    if (!s->activated && s->filename)
+        unlink(s->filename);
+    pa_xfree(s->filename);
+
+    pa_close(s->fd);
+
+    pa_xfree(s->tcpwrap_service);
+
+    s->mainloop->io_free(s->io_event);
+    pa_xfree(s);
+}
+
+void pa_socket_server_unref(pa_socket_server *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (PA_REFCNT_DEC(s) <= 0)
+        socket_server_free(s);
+}
+
+void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t on_connection, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    s->on_connection = on_connection;
+    s->userdata = userdata;
+}
+
+char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(c);
+    pa_assert(l > 0);
+
+    switch (s->type) {
+#ifdef HAVE_IPV6
+        case SOCKET_SERVER_IPV6: {
+            struct sockaddr_in6 sa;
+            socklen_t sa_len = sizeof(sa);
+
+            if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
+                pa_log("getsockname(): %s", pa_cstrerror(errno));
+                return NULL;
+            }
+
+            if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) {
+                char fqdn[256];
+                if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
+                    return NULL;
+
+                pa_snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
+
+            } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
+                char *id;
+
+                if (!(id = pa_machine_id()))
+                    return NULL;
+
+                pa_snprintf(c, l, "{%s}tcp6:localhost:%u", id, (unsigned) ntohs(sa.sin6_port));
+                pa_xfree(id);
+            } else {
+                char ip[INET6_ADDRSTRLEN];
+
+                if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) {
+                    pa_log("inet_ntop(): %s", pa_cstrerror(errno));
+                    return NULL;
+                }
+
+                pa_snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
+            }
+
+            return c;
+        }
+#endif
+
+        case SOCKET_SERVER_IPV4: {
+            struct sockaddr_in sa;
+            socklen_t sa_len = sizeof(sa);
+
+            if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
+                pa_log("getsockname(): %s", pa_cstrerror(errno));
+                return NULL;
+            }
+
+            if (sa.sin_addr.s_addr == INADDR_ANY) {
+                char fqdn[256];
+                if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
+                    return NULL;
+
+                pa_snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
+            } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
+                char *id;
+
+                if (!(id = pa_machine_id()))
+                    return NULL;
+
+                pa_snprintf(c, l, "{%s}tcp:localhost:%u", id, (unsigned) ntohs(sa.sin_port));
+                pa_xfree(id);
+            } else {
+                char ip[INET_ADDRSTRLEN];
+
+                if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) {
+                    pa_log("inet_ntop(): %s", pa_cstrerror(errno));
+                    return NULL;
+                }
+
+                pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
+            }
+
+            return c;
+        }
+
+        case SOCKET_SERVER_UNIX: {
+            char *id;
+
+            if (!s->filename)
+                return NULL;
+
+            if (!(id = pa_machine_id()))
+                return NULL;
+
+            pa_snprintf(c, l, "{%s}unix:%s", id, s->filename);
+            pa_xfree(id);
+            return c;
+        }
+
+        default:
+            return NULL;
+    }
+}
diff --git a/src/pulsecore/socket-server.h b/src/pulsecore/socket-server.h
new file mode 100644 (file)
index 0000000..0793baf
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef foosocketserverhfoo
+#define foosocketserverhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <pulse/mainloop-api.h>
+#include <pulsecore/iochannel.h>
+
+/* It is safe to destroy the calling socket_server object from the callback */
+
+typedef struct pa_socket_server pa_socket_server;
+
+pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename);
+pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, bool fallback, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, bool fallback, const char *tcpwrap_service);
+#ifdef HAVE_IPV6
+pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port, bool fallback, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, bool fallback, const char *tcpwrap_service);
+#endif
+
+void pa_socket_server_unref(pa_socket_server*s);
+pa_socket_server* pa_socket_server_ref(pa_socket_server *s);
+
+typedef void (*pa_socket_server_on_connection_cb_t)(pa_socket_server*s, pa_iochannel *io, void *userdata);
+
+void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t connection_cb, void *userdata);
+
+char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l);
+
+#endif
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
new file mode 100644 (file)
index 0000000..e389ef2
--- /dev/null
@@ -0,0 +1,338 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2004 Joe Marcus Clarke
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_SYSTEMD_DAEMON
+#include <systemd/sd-daemon.h>
+#endif
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/arpa-inet.h>
+
+#include "socket-util.h"
+
+void pa_socket_peer_to_string(int fd, char *c, size_t l) {
+#ifndef OS_IS_WIN32
+    struct stat st;
+#endif
+
+    pa_assert(fd >= 0);
+    pa_assert(c);
+    pa_assert(l > 0);
+
+#ifndef OS_IS_WIN32
+    pa_assert_se(fstat(fd, &st) == 0);
+
+    if (S_ISSOCK(st.st_mode))
+#endif
+    {
+        union {
+            struct sockaddr_storage storage;
+            struct sockaddr sa;
+            struct sockaddr_in in;
+#ifdef HAVE_IPV6
+            struct sockaddr_in6 in6;
+#endif
+#ifdef HAVE_SYS_UN_H
+            struct sockaddr_un un;
+#endif
+        } sa;
+        socklen_t sa_len = sizeof(sa);
+
+        if (getpeername(fd, &sa.sa, &sa_len) >= 0) {
+
+            if (sa.sa.sa_family == AF_INET) {
+                uint32_t ip = ntohl(sa.in.sin_addr.s_addr);
+
+                pa_snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u",
+                            ip >> 24,
+                            (ip >> 16) & 0xFF,
+                            (ip >> 8) & 0xFF,
+                            ip & 0xFF,
+                            ntohs(sa.in.sin_port));
+                return;
+#ifdef HAVE_IPV6
+            } else if (sa.sa.sa_family == AF_INET6) {
+                char buf[INET6_ADDRSTRLEN];
+                const char *res;
+
+                res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf));
+                if (res) {
+                    pa_snprintf(c, l, "TCP/IP client from [%s]:%u", buf, ntohs(sa.in6.sin6_port));
+                    return;
+                }
+#endif
+#ifdef HAVE_SYS_UN_H
+            } else if (sa.sa.sa_family == AF_UNIX) {
+                pa_snprintf(c, l, "UNIX socket client");
+                return;
+#endif
+            }
+        }
+
+        pa_snprintf(c, l, "Unknown network client");
+        return;
+    }
+#ifndef OS_IS_WIN32
+    else if (S_ISCHR(st.st_mode) && (fd == 0 || fd == 1)) {
+        pa_snprintf(c, l, "STDIN/STDOUT client");
+        return;
+    }
+#endif /* OS_IS_WIN32 */
+
+    pa_snprintf(c, l, "Unknown client");
+}
+
+void pa_make_socket_low_delay(int fd) {
+
+#ifdef SO_PRIORITY
+    int priority;
+    pa_assert(fd >= 0);
+
+    priority = 6;
+    if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (const void *) &priority, sizeof(priority)) < 0)
+        pa_log_warn("SO_PRIORITY failed: %s", pa_cstrerror(errno));
+#endif
+}
+
+void pa_make_tcp_socket_low_delay(int fd) {
+    pa_assert(fd >= 0);
+
+    pa_make_socket_low_delay(fd);
+
+#if defined(SOL_TCP) || defined(IPPROTO_TCP)
+    {
+        int on = 1;
+#if defined(SOL_TCP)
+        if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *) &on, sizeof(on)) < 0)
+#else
+        if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &on, sizeof(on)) < 0)
+#endif
+            pa_log_warn("TCP_NODELAY failed: %s", pa_cstrerror(errno));
+    }
+#endif
+
+#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
+    {
+        int tos = IPTOS_LOWDELAY;
+#ifdef SOL_IP
+        if (setsockopt(fd, SOL_IP, IP_TOS, (const void *) &tos, sizeof(tos)) < 0)
+#else
+        if (setsockopt(fd, IPPROTO_IP, IP_TOS, (const void *) &tos, sizeof(tos)) < 0)
+#endif
+            pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
+    }
+#endif
+}
+
+void pa_make_udp_socket_low_delay(int fd) {
+    pa_assert(fd >= 0);
+
+    pa_make_socket_low_delay(fd);
+
+#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
+    {
+        int tos = IPTOS_LOWDELAY;
+#ifdef SOL_IP
+        if (setsockopt(fd, SOL_IP, IP_TOS, (const void *) &tos, sizeof(tos)) < 0)
+#else
+        if (setsockopt(fd, IPPROTO_IP, IP_TOS, (const void *) &tos, sizeof(tos)) < 0)
+#endif
+            pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
+    }
+#endif
+}
+
+int pa_socket_set_rcvbuf(int fd, size_t l) {
+    int bufsz = (int) l;
+
+    pa_assert(fd >= 0);
+
+    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const void *) &bufsz, sizeof(bufsz)) < 0) {
+        pa_log_warn("SO_RCVBUF: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+int pa_socket_set_sndbuf(int fd, size_t l) {
+    int bufsz = (int) l;
+
+    pa_assert(fd >= 0);
+
+    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &bufsz, sizeof(bufsz)) < 0) {
+        pa_log_warn("SO_SNDBUF: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+#ifdef HAVE_SYS_UN_H
+
+int pa_unix_socket_is_stale(const char *fn) {
+    struct sockaddr_un sa;
+    int fd = -1, ret = -1;
+
+    pa_assert(fn);
+
+    if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+        pa_log("socket(): %s", pa_cstrerror(errno));
+        goto finish;
+    }
+
+    sa.sun_family = AF_UNIX;
+    strncpy(sa.sun_path, fn, sizeof(sa.sun_path)-1);
+    sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
+
+    if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
+        if (errno == ECONNREFUSED)
+            ret = 1;
+    } else
+        ret = 0;
+
+finish:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return ret;
+}
+
+int pa_unix_socket_remove_stale(const char *fn) {
+    int r;
+
+    pa_assert(fn);
+
+#ifdef HAVE_SYSTEMD_DAEMON
+    {
+        int n = sd_listen_fds(0);
+        if (n > 0) {
+            for (int i = 0; i < n; ++i) {
+                if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM, 1, fn, 0) > 0) {
+                    /* This is a socket activated socket, therefore do not consider
+                    * it stale. */
+                    return 0;
+                }
+            }
+        }
+    }
+#endif
+
+    if ((r = pa_unix_socket_is_stale(fn)) < 0)
+        return errno != ENOENT ? -1 : 0;
+
+    if (!r)
+        return 0;
+
+    /* Yes, here is a race condition. But who cares? */
+    if (unlink(fn) < 0)
+        return -1;
+
+    return 0;
+}
+
+#else /* HAVE_SYS_UN_H */
+
+int pa_unix_socket_is_stale(const char *fn) {
+    return -1;
+}
+
+int pa_unix_socket_remove_stale(const char *fn) {
+    return -1;
+}
+
+#endif /* HAVE_SYS_UN_H */
+
+bool pa_socket_address_is_local(const struct sockaddr *sa) {
+    pa_assert(sa);
+
+    switch (sa->sa_family) {
+        case AF_UNIX:
+            return true;
+
+        case AF_INET:
+            return ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
+
+#ifdef HAVE_IPV6
+        case AF_INET6:
+            return memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
+#endif
+
+        default:
+            return false;
+    }
+}
+
+bool pa_socket_is_local(int fd) {
+
+    union {
+        struct sockaddr_storage storage;
+        struct sockaddr sa;
+        struct sockaddr_in in;
+#ifdef HAVE_IPV6
+        struct sockaddr_in6 in6;
+#endif
+#ifdef HAVE_SYS_UN_H
+        struct sockaddr_un un;
+#endif
+    } sa;
+    socklen_t sa_len = sizeof(sa);
+
+    if (getpeername(fd, &sa.sa, &sa_len) < 0)
+        return false;
+
+    return pa_socket_address_is_local(&sa.sa);
+}
diff --git a/src/pulsecore/socket-util.h b/src/pulsecore/socket-util.h
new file mode 100644 (file)
index 0000000..f120769
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef foosocketutilhfoo
+#define foosocketutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/socket.h>
+#include <pulsecore/macro.h>
+
+void pa_socket_peer_to_string(int fd, char *c, size_t l);
+
+void pa_make_socket_low_delay(int fd);
+void pa_make_tcp_socket_low_delay(int fd);
+void pa_make_udp_socket_low_delay(int fd);
+
+int pa_socket_set_sndbuf(int fd, size_t l);
+int pa_socket_set_rcvbuf(int fd, size_t l);
+
+int pa_unix_socket_is_stale(const char *fn);
+int pa_unix_socket_remove_stale(const char *fn);
+
+bool pa_socket_address_is_local(const struct sockaddr *sa);
+bool pa_socket_is_local(int fd);
+
+#endif
diff --git a/src/pulsecore/socket.h b/src/pulsecore/socket.h
new file mode 100644 (file)
index 0000000..72f2228
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef foopulsecoresockethfoo
+#define foopulsecoresockethfoo
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#include "winerrno.h"
+
+typedef long suseconds_t;
+
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+#endif
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
new file mode 100644 (file)
index 0000000..aeaa608
--- /dev/null
@@ -0,0 +1,339 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sndfile.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/mix.h>
+#include <pulsecore/sndfile-util.h>
+
+#include "sound-file-stream.h"
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+
+typedef struct file_stream {
+    pa_msgobject parent;
+    pa_core *core;
+    pa_sink_input *sink_input;
+
+    SNDFILE *sndfile;
+    sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
+
+    /* We need this memblockq here to easily fulfill rewind requests
+     * (even beyond the file start!) */
+    pa_memblockq *memblockq;
+} file_stream;
+
+enum {
+    FILE_STREAM_MESSAGE_UNLINK
+};
+
+PA_DEFINE_PRIVATE_CLASS(file_stream, pa_msgobject);
+#define FILE_STREAM(o) (file_stream_cast(o))
+
+/* Called from main context */
+static void file_stream_unlink(file_stream *u) {
+    pa_assert(u);
+
+    if (!u->sink_input)
+        return;
+
+    pa_sink_input_unlink(u->sink_input);
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    /* Make sure we don't decrease the ref count twice. */
+    file_stream_unref(u);
+}
+
+/* Called from main context */
+static void file_stream_free(pa_object *o) {
+    file_stream *u = FILE_STREAM(o);
+    pa_assert(u);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    if (u->sndfile)
+        sf_close(u->sndfile);
+
+    pa_xfree(u);
+}
+
+/* Called from main context */
+static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    file_stream *u = FILE_STREAM(o);
+    file_stream_assert_ref(u);
+
+    switch (code) {
+        case FILE_STREAM_MESSAGE_UNLINK:
+            file_stream_unlink(u);
+            break;
+    }
+
+    return 0;
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    file_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = FILE_STREAM(i->userdata);
+    file_stream_assert_ref(u);
+
+    file_stream_unlink(u);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    file_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = FILE_STREAM(i->userdata);
+    file_stream_assert_ref(u);
+
+    /* If we are added for the first time, ask for a rewinding so that
+     * we are heard right-away. */
+    if (PA_SINK_INPUT_IS_LINKED(state) &&
+        i->thread_info.state == PA_SINK_INPUT_INIT && i->sink)
+        pa_sink_input_request_rewind(i, 0, false, true, true);
+}
+
+/* Called from IO thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+    file_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    u = FILE_STREAM(i->userdata);
+    file_stream_assert_ref(u);
+
+    if (!u->memblockq)
+        return -1;
+
+    for (;;) {
+        pa_memchunk tchunk;
+        size_t fs;
+        void *p;
+        sf_count_t n;
+
+        if (pa_memblockq_peek(u->memblockq, chunk) >= 0) {
+            chunk->length = PA_MIN(chunk->length, length);
+            pa_memblockq_drop(u->memblockq, chunk->length);
+            return 0;
+        }
+
+        if (!u->sndfile)
+            break;
+
+        tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
+        tchunk.index = 0;
+
+        p = pa_memblock_acquire(tchunk.memblock);
+
+        if (u->readf_function) {
+            fs = pa_frame_size(&i->sample_spec);
+            n = u->readf_function(u->sndfile, p, (sf_count_t) (length/fs));
+        } else {
+            fs = 1;
+            n = sf_read_raw(u->sndfile, p, (sf_count_t) length);
+        }
+
+        pa_memblock_release(tchunk.memblock);
+
+        if (n <= 0) {
+            pa_memblock_unref(tchunk.memblock);
+
+            sf_close(u->sndfile);
+            u->sndfile = NULL;
+            break;
+        }
+
+        tchunk.length = (size_t) n * fs;
+
+        pa_memblockq_push(u->memblockq, &tchunk);
+        pa_memblock_unref(tchunk.memblock);
+    }
+
+    if (pa_sink_input_safe_to_remove(i)) {
+        pa_memblockq_free(u->memblockq);
+        u->memblockq = NULL;
+
+        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+    }
+
+    return -1;
+}
+
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    file_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = FILE_STREAM(i->userdata);
+    file_stream_assert_ref(u);
+
+    if (!u->memblockq)
+        return;
+
+    pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    file_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = FILE_STREAM(i->userdata);
+    file_stream_assert_ref(u);
+
+    if (!u->memblockq)
+        return;
+
+    pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+}
+
+int pa_play_file(
+        pa_sink *sink,
+        const char *fname,
+        const pa_cvolume *volume) {
+
+    file_stream *u = NULL;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    pa_sink_input_new_data data;
+    int fd;
+    SF_INFO sfi;
+    pa_memchunk silence;
+
+    pa_assert(sink);
+    pa_assert(fname);
+
+    u = pa_msgobject_new(file_stream);
+    u->parent.parent.free = file_stream_free;
+    u->parent.process_msg = file_stream_process_msg;
+    u->core = sink->core;
+    u->sink_input = NULL;
+    u->sndfile = NULL;
+    u->readf_function = NULL;
+    u->memblockq = NULL;
+
+    if ((fd = pa_open_cloexec(fname, O_RDONLY, 0)) < 0) {
+        pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    /* FIXME: For now we just use posix_fadvise to avoid page faults
+     * when accessing the file data. Eventually we should move the
+     * file reader into the main event loop and pass the data over the
+     * asyncmsgq. */
+
+#ifdef HAVE_POSIX_FADVISE
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
+        pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
+        goto fail;
+    } else
+        pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
+
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED) < 0) {
+        pa_log_warn("POSIX_FADV_WILLNEED failed: %s", pa_cstrerror(errno));
+        goto fail;
+    } else
+        pa_log_debug("POSIX_FADV_WILLNEED succeeded.");
+#endif
+
+    pa_zero(sfi);
+    if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfi, 1))) {
+        pa_log("Failed to open file %s", fname);
+        goto fail;
+    }
+
+    fd = -1;
+
+    if (pa_sndfile_read_sample_spec(u->sndfile, &ss) < 0) {
+        pa_log("Failed to determine file sample format.");
+        goto fail;
+    }
+
+    if (pa_sndfile_read_channel_map(u->sndfile, &cm) < 0) {
+        if (ss.channels > 2)
+            pa_log_info("Failed to determine file channel map, synthesizing one.");
+        pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);
+    }
+
+    u->readf_function = pa_sndfile_readf_function(&ss);
+
+    pa_sink_input_new_data_init(&data);
+    pa_sink_input_new_data_set_sink(&data, sink, false);
+    data.driver = __FILE__;
+    pa_sink_input_new_data_set_sample_spec(&data, &ss);
+    pa_sink_input_new_data_set_channel_map(&data, &cm);
+    pa_sink_input_new_data_set_volume(&data, volume);
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
+    pa_sndfile_init_proplist(u->sndfile, data.proplist);
+
+    pa_sink_input_new(&u->sink_input, sink->core, &data);
+    pa_sink_input_new_data_done(&data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
+    u->sink_input->userdata = u;
+
+    pa_sink_input_get_silence(u->sink_input, &silence);
+    u->memblockq = pa_memblockq_new("sound-file-stream memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, &silence);
+    pa_memblock_unref(silence.memblock);
+
+    pa_sink_input_put(u->sink_input);
+
+    /* The reference to u is dangling here, because we want to keep
+     * this stream around until it is fully played. */
+
+    return 0;
+
+fail:
+    file_stream_unref(u);
+
+    if (fd >= 0)
+        pa_close(fd);
+
+    return -1;
+}
diff --git a/src/pulsecore/sound-file-stream.h b/src/pulsecore/sound-file-stream.h
new file mode 100644 (file)
index 0000000..de55c6d
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef foosoundfilestreamhfoo
+#define foosoundfilestreamhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/sink.h>
+
+int pa_play_file(pa_sink *sink, const char *fname, const pa_cvolume *volume);
+
+#endif
diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c
new file mode 100644 (file)
index 0000000..f678430
--- /dev/null
@@ -0,0 +1,163 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sndfile.h>
+
+#include <pulse/sample.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/sndfile-util.h>
+
+#include "sound-file.h"
+
+int pa_sound_file_load(
+        pa_mempool *pool,
+        const char *fname,
+        pa_sample_spec *ss,
+        pa_channel_map *map,
+        pa_memchunk *chunk,
+        pa_proplist *p) {
+
+    SNDFILE *sf = NULL;
+    SF_INFO sfi;
+    int ret = -1;
+    size_t l;
+    sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL;
+    void *ptr = NULL;
+    int fd;
+
+    pa_assert(fname);
+    pa_assert(ss);
+    pa_assert(chunk);
+
+    pa_memchunk_reset(chunk);
+
+    if ((fd = pa_open_cloexec(fname, O_RDONLY, 0)) < 0) {
+        pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
+        goto finish;
+    }
+
+#ifdef HAVE_POSIX_FADVISE
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
+        pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
+        goto finish;
+    } else
+        pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
+#endif
+
+    pa_zero(sfi);
+    if (!(sf = sf_open_fd(fd, SFM_READ, &sfi, 1))) {
+        pa_log("Failed to open file %s", fname);
+        goto finish;
+    }
+
+    fd = -1;
+
+    if (pa_sndfile_read_sample_spec(sf, ss) < 0) {
+        pa_log("Failed to determine file sample format.");
+        goto finish;
+    }
+
+    if ((map && pa_sndfile_read_channel_map(sf, map) < 0)) {
+        if (ss->channels > 2)
+            pa_log("Failed to determine file channel map, synthesizing one.");
+        pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+    }
+
+    if (p)
+        pa_sndfile_init_proplist(sf, p);
+
+    if ((l = pa_frame_size(ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
+        pa_log("File too large");
+        goto finish;
+    }
+
+    chunk->memblock = pa_memblock_new(pool, l);
+    chunk->index = 0;
+    chunk->length = l;
+
+    readf_function = pa_sndfile_readf_function(ss);
+
+    ptr = pa_memblock_acquire(chunk->memblock);
+
+    if ((readf_function && readf_function(sf, ptr, sfi.frames) != sfi.frames) ||
+        (!readf_function && sf_read_raw(sf, ptr, (sf_count_t) l) != (sf_count_t) l)) {
+        pa_log("Premature file end");
+        goto finish;
+    }
+
+    ret = 0;
+
+finish:
+
+    if (sf)
+        sf_close(sf);
+
+    if (ptr)
+        pa_memblock_release(chunk->memblock);
+
+    if (ret != 0 && chunk->memblock)
+        pa_memblock_unref(chunk->memblock);
+
+    if (fd >= 0)
+        pa_close(fd);
+
+    return ret;
+}
+
+int pa_sound_file_too_big_to_cache(const char *fname) {
+
+    SNDFILE*sf = NULL;
+    SF_INFO sfi;
+    pa_sample_spec ss;
+
+    pa_assert(fname);
+
+    pa_zero(sfi);
+    if (!(sf = sf_open(fname, SFM_READ, &sfi))) {
+        pa_log("Failed to open file %s", fname);
+        return -1;
+    }
+
+    if (pa_sndfile_read_sample_spec(sf, &ss) < 0) {
+        pa_log("Failed to determine file sample format.");
+        sf_close(sf);
+        return -1;
+    }
+
+    sf_close(sf);
+
+    if ((pa_frame_size(&ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
+        pa_log("File too large: %s", fname);
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/src/pulsecore/sound-file.h b/src/pulsecore/sound-file.h
new file mode 100644 (file)
index 0000000..01afa4e
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef soundfilehfoo
+#define soundfilehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulsecore/memchunk.h>
+
+int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk, pa_proplist *p);
+
+int pa_sound_file_too_big_to_cache(const char *fname);
+
+#endif
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
new file mode 100644 (file)
index 0000000..a4c99af
--- /dev/null
@@ -0,0 +1,1861 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulse/internal.h>
+
+#include <pulsecore/core-format.h>
+#include <pulsecore/mix.h>
+#include <pulsecore/stream-util.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+
+#include "source-output.h"
+
+#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
+
+PA_DEFINE_PUBLIC_CLASS(pa_source_output, pa_msgobject);
+
+static void source_output_free(pa_object* mo);
+static void set_real_ratio(pa_source_output *o, const pa_cvolume *v);
+
+pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) {
+    pa_assert(data);
+
+    pa_zero(*data);
+    data->resample_method = PA_RESAMPLER_INVALID;
+    data->proplist = pa_proplist_new();
+    data->volume_writable = true;
+
+    return data;
+}
+
+void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
+    pa_assert(data);
+
+    if ((data->sample_spec_is_set = !!spec))
+        data->sample_spec = *spec;
+}
+
+void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map) {
+    pa_assert(data);
+
+    if ((data->channel_map_is_set = !!map))
+        data->channel_map = *map;
+}
+
+bool pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data) {
+    pa_assert(data);
+
+    if (PA_LIKELY(data->format) && PA_UNLIKELY(!pa_format_info_is_pcm(data->format)))
+        return true;
+
+    if (PA_UNLIKELY(data->flags & PA_SOURCE_OUTPUT_PASSTHROUGH))
+        return true;
+
+    return false;
+}
+
+void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume) {
+    pa_assert(data);
+    pa_assert(data->volume_writable);
+
+    if ((data->volume_is_set = !!volume))
+        data->volume = *volume;
+}
+
+void pa_source_output_new_data_apply_volume_factor(pa_source_output_new_data *data, const pa_cvolume *volume_factor) {
+    pa_assert(data);
+    pa_assert(volume_factor);
+
+    if (data->volume_factor_is_set)
+        pa_sw_cvolume_multiply(&data->volume_factor, &data->volume_factor, volume_factor);
+    else {
+        data->volume_factor_is_set = true;
+        data->volume_factor = *volume_factor;
+    }
+}
+
+void pa_source_output_new_data_apply_volume_factor_source(pa_source_output_new_data *data, const pa_cvolume *volume_factor) {
+    pa_assert(data);
+    pa_assert(volume_factor);
+
+    if (data->volume_factor_source_is_set)
+        pa_sw_cvolume_multiply(&data->volume_factor_source, &data->volume_factor_source, volume_factor);
+    else {
+        data->volume_factor_source_is_set = true;
+        data->volume_factor_source = *volume_factor;
+    }
+}
+
+void pa_source_output_new_data_set_muted(pa_source_output_new_data *data, bool mute) {
+    pa_assert(data);
+
+    data->muted_is_set = true;
+    data->muted = mute;
+}
+
+bool pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_source *s, bool save) {
+    bool ret = true;
+    pa_idxset *formats = NULL;
+
+    pa_assert(data);
+    pa_assert(s);
+
+    if (!data->req_formats) {
+        /* We're not working with the extended API */
+        data->source = s;
+        data->save_source = save;
+    } else {
+        /* Extended API: let's see if this source supports the formats the client would like */
+        formats = pa_source_check_formats(s, data->req_formats);
+
+        if (formats && !pa_idxset_isempty(formats)) {
+            /* Source supports at least one of the requested formats */
+            data->source = s;
+            data->save_source = save;
+            if (data->nego_formats)
+                pa_idxset_free(data->nego_formats, (pa_free_cb_t) pa_format_info_free);
+            data->nego_formats = formats;
+        } else {
+            /* Source doesn't support any of the formats requested by the client */
+            if (formats)
+                pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+            ret = false;
+        }
+    }
+
+    return ret;
+}
+
+bool pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats) {
+    pa_assert(data);
+    pa_assert(formats);
+
+    if (data->req_formats)
+        pa_idxset_free(data->req_formats, (pa_free_cb_t) pa_format_info_free);
+
+    data->req_formats = formats;
+
+    if (data->source) {
+        /* Trigger format negotiation */
+        return pa_source_output_new_data_set_source(data, data->source, data->save_source);
+    }
+
+    return true;
+}
+
+void pa_source_output_new_data_done(pa_source_output_new_data *data) {
+    pa_assert(data);
+
+    if (data->req_formats)
+        pa_idxset_free(data->req_formats, (pa_free_cb_t) pa_format_info_free);
+
+    if (data->nego_formats)
+        pa_idxset_free(data->nego_formats, (pa_free_cb_t) pa_format_info_free);
+
+    if (data->format)
+        pa_format_info_free(data->format);
+
+    pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_source_output *o) {
+    pa_assert(o);
+
+    o->push = NULL;
+    o->process_rewind = NULL;
+    o->update_max_rewind = NULL;
+    o->update_source_requested_latency = NULL;
+    o->update_source_latency_range = NULL;
+    o->update_source_fixed_latency = NULL;
+    o->attach = NULL;
+    o->detach = NULL;
+    o->suspend = NULL;
+    o->suspend_within_thread = NULL;
+    o->moving = NULL;
+    o->kill = NULL;
+    o->get_latency = NULL;
+    o->state_change = NULL;
+    o->may_move_to = NULL;
+    o->send_event = NULL;
+    o->volume_changed = NULL;
+    o->mute_changed = NULL;
+}
+
+/* Called from main context */
+int pa_source_output_new(
+        pa_source_output**_o,
+        pa_core *core,
+        pa_source_output_new_data *data) {
+
+    pa_source_output *o;
+    pa_resampler *resampler = NULL;
+    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], fmt[PA_FORMAT_INFO_SNPRINT_MAX];
+    pa_channel_map volume_map;
+    int r;
+    char *pt;
+
+    pa_assert(_o);
+    pa_assert(core);
+    pa_assert(data);
+    pa_assert_ctl_context();
+
+    if (data->client)
+        pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
+
+    if (data->destination_source && (data->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+        data->volume_writable = false;
+
+    if (!data->req_formats) {
+        /* From this point on, we want to work only with formats, and get back
+         * to using the sample spec and channel map after all decisions w.r.t.
+         * routing are complete. */
+        pa_format_info *f;
+        pa_idxset *formats;
+
+        f = pa_format_info_from_sample_spec2(&data->sample_spec, data->channel_map_is_set ? &data->channel_map : NULL,
+                                             !(data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT),
+                                             !(data->flags & PA_SOURCE_OUTPUT_FIX_RATE),
+                                             !(data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS));
+        if (!f)
+            return -PA_ERR_INVALID;
+
+        formats = pa_idxset_new(NULL, NULL);
+        pa_idxset_put(formats, f, NULL);
+        pa_source_output_new_data_set_formats(data, formats);
+    }
+
+    if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0)
+        return r;
+
+    pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
+
+    if (!data->source) {
+        pa_source *source;
+
+        if (data->direct_on_input) {
+            source = data->direct_on_input->sink->monitor_source;
+            pa_return_val_if_fail(source, -PA_ERR_INVALID);
+        } else {
+            source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
+            pa_return_val_if_fail(source, -PA_ERR_NOENTITY);
+        }
+
+        pa_source_output_new_data_set_source(data, source, false);
+    }
+
+    /* If something didn't pick a format for us, pick the top-most format since
+     * we assume this is sorted in priority order */
+    if (!data->format && data->nego_formats && !pa_idxset_isempty(data->nego_formats))
+        data->format = pa_format_info_copy(pa_idxset_first(data->nego_formats, NULL));
+
+    if (PA_LIKELY(data->format)) {
+        pa_log_debug("Negotiated format: %s", pa_format_info_snprint(fmt, sizeof(fmt), data->format));
+    } else {
+        pa_format_info *format;
+        uint32_t idx;
+
+        pa_log_info("Source does not support any requested format:");
+        PA_IDXSET_FOREACH(format, data->req_formats, idx)
+            pa_log_info(" -- %s", pa_format_info_snprint(fmt, sizeof(fmt), format));
+
+        return -PA_ERR_NOTSUPPORTED;
+    }
+
+    pa_return_val_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)), -PA_ERR_BADSTATE);
+    pa_return_val_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of, -PA_ERR_INVALID);
+
+    /* Routing is done. We have a source and a format. */
+
+    if (data->volume_is_set && pa_format_info_is_pcm(data->format)) {
+        /* If volume is set, we need to save the original data->channel_map,
+         * so that we can remap the volume from the original channel map to the
+         * final channel map of the stream in case data->channel_map gets
+         * modified in pa_format_info_to_sample_spec2(). */
+        r = pa_stream_get_volume_channel_map(&data->volume, data->channel_map_is_set ? &data->channel_map : NULL, data->format, &volume_map);
+        if (r < 0)
+            return r;
+    }
+
+    /* Now populate the sample spec and channel map according to the final
+     * format that we've negotiated */
+    r = pa_format_info_to_sample_spec2(data->format, &data->sample_spec, &data->channel_map, &data->source->sample_spec,
+                                       &data->source->channel_map);
+    if (r < 0)
+        return r;
+
+    /* Don't restore (or save) stream volume for passthrough streams and
+     * prevent attenuation/gain */
+    if (pa_source_output_new_data_is_passthrough(data)) {
+        data->volume_is_set = true;
+        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+        data->volume_is_absolute = true;
+        data->save_volume = false;
+    }
+
+    if (!data->volume_is_set) {
+        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+        data->volume_is_absolute = false;
+        data->save_volume = false;
+    }
+
+    if (!data->volume_writable)
+        data->save_volume = false;
+
+    if (data->volume_is_set)
+        /* The original volume channel map may be different than the final
+         * stream channel map, so remapping may be needed. */
+        pa_cvolume_remap(&data->volume, &volume_map, &data->channel_map);
+
+    if (!data->volume_factor_is_set)
+        pa_cvolume_reset(&data->volume_factor, data->sample_spec.channels);
+
+    pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor, &data->sample_spec), -PA_ERR_INVALID);
+
+    if (!data->volume_factor_source_is_set)
+        pa_cvolume_reset(&data->volume_factor_source, data->source->sample_spec.channels);
+
+    pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor_source, &data->source->sample_spec), -PA_ERR_INVALID);
+
+    if (!data->muted_is_set)
+        data->muted = false;
+
+    if (!(data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) &&
+        !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec)) {
+        /* try to change source rate. This is done before the FIXATE hook since
+           module-suspend-on-idle can resume a source */
+
+        pa_log_info("Trying to change sample rate");
+        if (pa_source_update_rate(data->source, data->sample_spec.rate, pa_source_output_new_data_is_passthrough(data)) >= 0)
+            pa_log_info("Rate changed to %u Hz", data->source->sample_spec.rate);
+    }
+
+    if (pa_source_output_new_data_is_passthrough(data) &&
+        !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec)) {
+        /* rate update failed, or other parts of sample spec didn't match */
+
+        pa_log_debug("Could not update source sample spec to match passthrough stream");
+        return -PA_ERR_NOTSUPPORTED;
+    }
+
+    if (data->resample_method == PA_RESAMPLER_INVALID)
+        data->resample_method = core->resample_method;
+
+    pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID);
+
+    if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0)
+        return r;
+
+    if ((data->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND) &&
+        pa_source_get_state(data->source) == PA_SOURCE_SUSPENDED) {
+        pa_log("Failed to create source output: source is suspended.");
+        return -PA_ERR_BADSTATE;
+    }
+
+    if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
+        pa_log("Failed to create source output: too many outputs per source.");
+        return -PA_ERR_TOOLARGE;
+    }
+
+    if ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
+        !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) ||
+        !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) {
+
+        if (!pa_source_output_new_data_is_passthrough(data)) /* no resampler for passthrough content */
+            if (!(resampler = pa_resampler_new(
+                        core->mempool,
+                        &data->source->sample_spec, &data->source->channel_map,
+                        &data->sample_spec, &data->channel_map,
+                        core->lfe_crossover_freq,
+                        data->resample_method,
+                        ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+                        ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+                        (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+                        (core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) |
+                        (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
+                pa_log_warn("Unsupported resampling operation.");
+                return -PA_ERR_NOTSUPPORTED;
+            }
+    }
+
+    o = pa_msgobject_new(pa_source_output);
+    o->parent.parent.free = source_output_free;
+    o->parent.process_msg = pa_source_output_process_msg;
+
+    o->core = core;
+    o->state = PA_SOURCE_OUTPUT_INIT;
+    o->flags = data->flags;
+    o->proplist = pa_proplist_copy(data->proplist);
+    o->driver = pa_xstrdup(pa_path_get_filename(data->driver));
+    o->module = data->module;
+    o->source = data->source;
+    o->destination_source = data->destination_source;
+    o->client = data->client;
+
+    o->requested_resample_method = data->resample_method;
+    o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
+    o->sample_spec = data->sample_spec;
+    o->channel_map = data->channel_map;
+    o->format = pa_format_info_copy(data->format);
+
+    if (!data->volume_is_absolute && pa_source_flat_volume_enabled(o->source)) {
+        pa_cvolume remapped;
+
+        /* When the 'absolute' bool is not set then we'll treat the volume
+         * as relative to the source volume even in flat volume mode */
+        remapped = data->source->reference_volume;
+        pa_cvolume_remap(&remapped, &data->source->channel_map, &data->channel_map);
+        pa_sw_cvolume_multiply(&o->volume, &data->volume, &remapped);
+    } else
+        o->volume = data->volume;
+
+    o->volume_factor = data->volume_factor;
+    o->volume_factor_source = data->volume_factor_source;
+    o->real_ratio = o->reference_ratio = data->volume;
+    pa_cvolume_reset(&o->soft_volume, o->sample_spec.channels);
+    pa_cvolume_reset(&o->real_ratio, o->sample_spec.channels);
+    o->volume_writable = data->volume_writable;
+    o->save_volume = data->save_volume;
+    o->save_source = data->save_source;
+    o->save_muted = data->save_muted;
+
+    o->muted = data->muted;
+
+    o->direct_on_input = data->direct_on_input;
+
+    reset_callbacks(o);
+    o->userdata = NULL;
+
+    o->thread_info.state = o->state;
+    o->thread_info.attached = false;
+    o->thread_info.sample_spec = o->sample_spec;
+    o->thread_info.resampler = resampler;
+    o->thread_info.soft_volume = o->soft_volume;
+    o->thread_info.muted = o->muted;
+    o->thread_info.requested_source_latency = (pa_usec_t) -1;
+    o->thread_info.direct_on_input = o->direct_on_input;
+
+    o->thread_info.delay_memblockq = pa_memblockq_new(
+            "source output delay_memblockq",
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            0,
+            &o->source->sample_spec,
+            0,
+            1,
+            0,
+            &o->source->silence);
+
+    pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
+    pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
+
+    if (o->client)
+        pa_assert_se(pa_idxset_put(o->client->source_outputs, o, NULL) >= 0);
+
+    if (o->direct_on_input)
+        pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0);
+
+    pt = pa_proplist_to_string_sep(o->proplist, "\n    ");
+    pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s\n    %s",
+                o->index,
+                pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)),
+                o->source->name,
+                pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec),
+                pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
+                pt);
+    pa_xfree(pt);
+
+    /* Don't forget to call pa_source_output_put! */
+
+    *_o = o;
+    return 0;
+}
+
+/* Called from main context */
+static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) {
+    pa_assert(o);
+    pa_assert_ctl_context();
+
+    if (!o->source)
+        return;
+
+    if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED)
+        pa_assert_se(o->source->n_corked -- >= 1);
+    else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED)
+        o->source->n_corked++;
+}
+
+/* Called from main context */
+static void source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
+
+    pa_assert(o);
+    pa_assert_ctl_context();
+
+    if (o->state == state)
+        return;
+
+    if (o->source) {
+        if (o->state == PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_RUNNING && pa_source_used_by(o->source) == 0 &&
+            !pa_sample_spec_equal(&o->sample_spec, &o->source->sample_spec)) {
+            /* We were uncorked and the source was not playing anything -- let's try
+             * to update the sample rate to avoid resampling */
+            pa_source_update_rate(o->source, o->sample_spec.rate, pa_source_output_is_passthrough(o));
+        }
+
+        pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+    } else
+        /* If the source is not valid, pa_source_output_set_state_within_thread() must be called directly */
+        pa_source_output_set_state_within_thread(o, state);
+
+    update_n_corked(o, state);
+    o->state = state;
+
+    if (state != PA_SOURCE_OUTPUT_UNLINKED) {
+        pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
+
+        if (PA_SOURCE_OUTPUT_IS_LINKED(state))
+            pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    }
+
+    if (o->source)
+        pa_source_update_status(o->source);
+}
+
+/* Called from main context */
+void pa_source_output_unlink(pa_source_output*o) {
+    bool linked;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+
+    /* See pa_sink_unlink() for a couple of comments how this function
+     * works */
+
+    pa_source_output_ref(o);
+
+    linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
+
+    if (linked)
+        pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
+
+    if (o->direct_on_input)
+        pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL);
+
+    pa_idxset_remove_by_data(o->core->source_outputs, o, NULL);
+
+    if (o->source)
+        if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
+            pa_source_output_unref(o);
+
+    if (o->client)
+        pa_idxset_remove_by_data(o->client->source_outputs, o, NULL);
+
+    update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED);
+    o->state = PA_SOURCE_OUTPUT_UNLINKED;
+
+    if (linked && o->source) {
+        if (pa_source_output_is_passthrough(o))
+            pa_source_leave_passthrough(o->source);
+
+        /* We might need to update the source's volume if we are in flat volume mode. */
+        if (pa_source_flat_volume_enabled(o->source))
+            pa_source_set_volume(o->source, NULL, false, false);
+
+        if (o->source->asyncmsgq)
+            pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
+    }
+
+    reset_callbacks(o);
+
+    if (o->source) {
+        if (PA_SOURCE_IS_LINKED(pa_source_get_state(o->source)))
+            pa_source_update_status(o->source);
+
+        o->source = NULL;
+    }
+
+    if (linked) {
+        pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
+        pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
+    }
+
+    pa_core_maybe_vacuum(o->core);
+
+    pa_source_output_unref(o);
+}
+
+/* Called from main context */
+static void source_output_free(pa_object* mo) {
+    pa_source_output *o = PA_SOURCE_OUTPUT(mo);
+
+    pa_assert(o);
+    pa_assert_ctl_context();
+    pa_assert(pa_source_output_refcnt(o) == 0);
+    pa_assert(!PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    pa_log_info("Freeing output %u \"%s\"", o->index,
+                o->proplist ? pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)) : "");
+
+    if (o->thread_info.delay_memblockq)
+        pa_memblockq_free(o->thread_info.delay_memblockq);
+
+    if (o->thread_info.resampler)
+        pa_resampler_free(o->thread_info.resampler);
+
+    if (o->format)
+        pa_format_info_free(o->format);
+
+    if (o->proplist)
+        pa_proplist_free(o->proplist);
+
+    pa_xfree(o->driver);
+    pa_xfree(o);
+}
+
+/* Called from main context */
+void pa_source_output_put(pa_source_output *o) {
+    pa_source_output_state_t state;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+
+    pa_assert(o->state == PA_SOURCE_OUTPUT_INIT);
+
+    /* The following fields must be initialized properly */
+    pa_assert(o->push);
+    pa_assert(o->kill);
+
+    state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
+
+    update_n_corked(o, state);
+    o->state = state;
+
+    /* We might need to update the source's volume if we are in flat volume mode. */
+    if (pa_source_flat_volume_enabled(o->source))
+        pa_source_set_volume(o->source, NULL, false, o->save_volume);
+    else {
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+            pa_assert(pa_cvolume_is_norm(&o->volume));
+            pa_assert(pa_cvolume_is_norm(&o->reference_ratio));
+        }
+
+        set_real_ratio(o, &o->volume);
+    }
+
+    if (pa_source_output_is_passthrough(o))
+        pa_source_enter_passthrough(o->source);
+
+    o->thread_info.soft_volume = o->soft_volume;
+    o->thread_info.muted = o->muted;
+
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
+
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
+    pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);
+
+    pa_source_update_status(o->source);
+}
+
+/* Called from main context */
+void pa_source_output_kill(pa_source_output*o) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    o->kill(o);
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency) {
+    pa_usec_t r[2] = { 0, 0 };
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
+
+    if (o->get_latency)
+        r[0] += o->get_latency(o);
+
+    if (source_latency)
+        *source_latency = r[1];
+
+    return r[0];
+}
+
+/* Called from thread context */
+void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
+    bool need_volume_factor_source;
+    bool volume_is_norm;
+    size_t length;
+    size_t limit, mbs = 0;
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+    pa_assert(chunk);
+    pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec));
+
+    if (!o->push || o->thread_info.state == PA_SOURCE_OUTPUT_CORKED)
+        return;
+
+    pa_assert(o->thread_info.state == PA_SOURCE_OUTPUT_RUNNING);
+
+    if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) {
+        pa_log_debug("Delay queue overflow!");
+        pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, true);
+    }
+
+    limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
+
+    volume_is_norm = pa_cvolume_is_norm(&o->thread_info.soft_volume) && !o->thread_info.muted;
+    need_volume_factor_source = !pa_cvolume_is_norm(&o->volume_factor_source);
+
+    if (limit > 0 && o->source->monitor_of) {
+        pa_usec_t latency;
+        size_t n;
+
+        /* Hmm, check the latency for knowing how much of the buffered
+         * data is actually still unplayed and might hence still
+         * change. This is suboptimal. Ideally we'd have a call like
+         * pa_sink_get_changeable_size() or so that tells us how much
+         * of the queued data is actually still changeable. Hence
+         * FIXME! */
+
+        latency = pa_sink_get_latency_within_thread(o->source->monitor_of, false);
+
+        n = pa_usec_to_bytes(latency, &o->source->sample_spec);
+
+        if (n < limit)
+            limit = n;
+    }
+
+    /* Implement the delay queue */
+    while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) {
+        pa_memchunk qchunk;
+        bool nvfs = need_volume_factor_source;
+
+        length -= limit;
+
+        pa_assert_se(pa_memblockq_peek(o->thread_info.delay_memblockq, &qchunk) >= 0);
+
+        if (qchunk.length > length)
+            qchunk.length = length;
+
+        pa_assert(qchunk.length > 0);
+
+        /* It might be necessary to adjust the volume here */
+        if (!volume_is_norm) {
+            pa_memchunk_make_writable(&qchunk, 0);
+
+            if (o->thread_info.muted) {
+                pa_silence_memchunk(&qchunk, &o->source->sample_spec);
+                nvfs = false;
+
+            } else if (!o->thread_info.resampler && nvfs) {
+                pa_cvolume v;
+
+                /* If we don't need a resampler we can merge the
+                 * post and the pre volume adjustment into one */
+
+                pa_sw_cvolume_multiply(&v, &o->thread_info.soft_volume, &o->volume_factor_source);
+                pa_volume_memchunk(&qchunk, &o->source->sample_spec, &v);
+                nvfs = false;
+
+            } else
+                pa_volume_memchunk(&qchunk, &o->source->sample_spec, &o->thread_info.soft_volume);
+        }
+
+        if (nvfs) {
+            pa_memchunk_make_writable(&qchunk, 0);
+            pa_volume_memchunk(&qchunk, &o->source->sample_spec, &o->volume_factor_source);
+        }
+
+        if (!o->thread_info.resampler)
+            o->push(o, &qchunk);
+        else {
+            pa_memchunk rchunk;
+
+            if (mbs == 0)
+                mbs = pa_resampler_max_block_size(o->thread_info.resampler);
+
+            if (qchunk.length > mbs)
+                qchunk.length = mbs;
+
+            pa_resampler_run(o->thread_info.resampler, &qchunk, &rchunk);
+
+            if (rchunk.length > 0)
+                o->push(o, &rchunk);
+
+            if (rchunk.memblock)
+                pa_memblock_unref(rchunk.memblock);
+        }
+
+        pa_memblock_unref(qchunk.memblock);
+        pa_memblockq_drop(o->thread_info.delay_memblockq, qchunk.length);
+    }
+}
+
+/* Called from thread context */
+void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in source sample spec */) {
+
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
+
+    if (nbytes <= 0)
+        return;
+
+    if (o->process_rewind) {
+        pa_assert(pa_memblockq_get_length(o->thread_info.delay_memblockq) == 0);
+
+        if (o->thread_info.resampler)
+            nbytes = pa_resampler_result(o->thread_info.resampler, nbytes);
+
+        pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes);
+
+        if (nbytes > 0)
+            o->process_rewind(o, nbytes);
+
+        if (o->thread_info.resampler)
+            pa_resampler_rewind(o->thread_info.resampler, nbytes);
+
+    } else
+        pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes);
+}
+
+/* Called from thread context */
+size_t pa_source_output_get_max_rewind(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+
+    return o->thread_info.resampler ? pa_resampler_request(o->thread_info.resampler, o->source->thread_info.max_rewind) : o->source->thread_info.max_rewind;
+}
+
+/* Called from thread context */
+void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes  /* in the source's sample spec */) {
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
+
+    if (o->update_max_rewind)
+        o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
+}
+
+/* Called from thread context */
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
+    pa_source_output_assert_ref(o);
+    pa_source_output_assert_io_context(o);
+
+    if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
+        usec = o->source->thread_info.fixed_latency;
+
+    if (usec != (pa_usec_t) -1)
+        usec = PA_CLAMP(usec, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
+
+    o->thread_info.requested_source_latency = usec;
+    pa_source_invalidate_requested_latency(o->source, true);
+
+    return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
+        pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+        return usec;
+    }
+
+    /* If this source output is not realized yet or is being moved, we
+     * have to touch the thread info data directly */
+
+    if (o->source) {
+        if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
+            usec = pa_source_get_fixed_latency(o->source);
+
+        if (usec != (pa_usec_t) -1) {
+            pa_usec_t min_latency, max_latency;
+            pa_source_get_latency_range(o->source, &min_latency, &max_latency);
+            usec = PA_CLAMP(usec, min_latency, max_latency);
+        }
+    }
+
+    o->thread_info.requested_source_latency = usec;
+
+    return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
+        pa_usec_t usec = 0;
+        pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+        return usec;
+    }
+
+    /* If this source output is not realized yet or is being moved, we
+     * have to touch the thread info data directly */
+
+    return o->thread_info.requested_source_latency;
+}
+
+/* Called from main context */
+void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume, bool save, bool absolute) {
+    pa_cvolume v;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(volume);
+    pa_assert(pa_cvolume_valid(volume));
+    pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &o->sample_spec));
+    pa_assert(o->volume_writable);
+
+    if (!absolute && pa_source_flat_volume_enabled(o->source)) {
+        v = o->source->reference_volume;
+        pa_cvolume_remap(&v, &o->source->channel_map, &o->channel_map);
+
+        if (pa_cvolume_compatible(volume, &o->sample_spec))
+            volume = pa_sw_cvolume_multiply(&v, &v, volume);
+        else
+            volume = pa_sw_cvolume_multiply_scalar(&v, &v, pa_cvolume_max(volume));
+    } else {
+        if (!pa_cvolume_compatible(volume, &o->sample_spec)) {
+            v = o->volume;
+            volume = pa_cvolume_scale(&v, pa_cvolume_max(volume));
+        }
+    }
+
+    if (pa_cvolume_equal(volume, &o->volume)) {
+        o->save_volume = o->save_volume || save;
+        return;
+    }
+
+    pa_source_output_set_volume_direct(o, volume);
+    o->save_volume = save;
+
+    if (pa_source_flat_volume_enabled(o->source)) {
+        /* We are in flat volume mode, so let's update all source input
+         * volumes and update the flat volume of the source */
+
+        pa_source_set_volume(o->source, NULL, true, save);
+
+    } else {
+        /* OK, we are in normal volume mode. The volume only affects
+         * ourselves */
+        set_real_ratio(o, volume);
+
+        /* Copy the new soft_volume to the thread_info struct */
+        pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
+    }
+
+    /* The volume changed, let's tell people so */
+    if (o->volume_changed)
+        o->volume_changed(o);
+
+    /* The virtual volume changed, let's tell people so */
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+}
+
+/* Called from main context */
+static void set_real_ratio(pa_source_output *o, const pa_cvolume *v) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(!v || pa_cvolume_compatible(v, &o->sample_spec));
+
+    /* This basically calculates:
+     *
+     * o->real_ratio := v
+     * o->soft_volume := o->real_ratio * o->volume_factor */
+
+    if (v)
+        o->real_ratio = *v;
+    else
+        pa_cvolume_reset(&o->real_ratio, o->sample_spec.channels);
+
+    pa_sw_cvolume_multiply(&o->soft_volume, &o->real_ratio, &o->volume_factor);
+    /* We don't copy the data to the thread_info data. That's left for someone else to do */
+}
+
+/* Called from main or I/O context */
+bool pa_source_output_is_passthrough(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+
+    if (PA_UNLIKELY(!pa_format_info_is_pcm(o->format)))
+        return true;
+
+    if (PA_UNLIKELY(o->flags & PA_SOURCE_OUTPUT_PASSTHROUGH))
+        return true;
+
+    return false;
+}
+
+/* Called from main context */
+bool pa_source_output_is_volume_readable(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+
+    return !pa_source_output_is_passthrough(o);
+}
+
+/* Called from main context */
+pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, bool absolute) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(pa_source_output_is_volume_readable(o));
+
+    if (absolute || !pa_source_flat_volume_enabled(o->source))
+        *volume = o->volume;
+    else
+        *volume = o->reference_ratio;
+
+    return volume;
+}
+
+/* Called from main context */
+void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) {
+    bool old_mute;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    old_mute = o->muted;
+
+    if (mute == old_mute) {
+        o->save_muted |= save;
+        return;
+    }
+
+    o->muted = mute;
+    pa_log_debug("The mute of source output %u changed from %s to %s.", o->index, pa_yes_no(old_mute), pa_yes_no(mute));
+
+    o->save_muted = save;
+
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
+
+    /* The mute status changed, let's tell people so */
+    if (o->mute_changed)
+        o->mute_changed(o);
+
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED], o);
+}
+
+void pa_source_output_set_property(pa_source_output *o, const char *key, const char *value) {
+    char *old_value = NULL;
+    const char *new_value;
+
+    pa_assert(o);
+    pa_assert(key);
+
+    if (pa_proplist_contains(o->proplist, key)) {
+        old_value = pa_xstrdup(pa_proplist_gets(o->proplist, key));
+        if (value && old_value && pa_streq(value, old_value))
+            goto finish;
+
+        if (!old_value)
+            old_value = pa_xstrdup("(data)");
+    } else {
+        if (!value)
+            goto finish;
+
+        old_value = pa_xstrdup("(unset)");
+    }
+
+    if (value) {
+        pa_proplist_sets(o->proplist, key, value);
+        new_value = value;
+    } else {
+        pa_proplist_unset(o->proplist, key);
+        new_value = "(unset)";
+    }
+
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
+        pa_log_debug("Source output %u: proplist[%s]: %s -> %s", o->index, key, old_value, new_value);
+        pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
+        pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT | PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    }
+
+finish:
+    pa_xfree(old_value);
+}
+
+void pa_source_output_set_property_arbitrary(pa_source_output *o, const char *key, const uint8_t *value, size_t nbytes) {
+    const uint8_t *old_value;
+    size_t old_nbytes;
+    const char *old_value_str;
+    const char *new_value_str;
+
+    pa_assert(o);
+    pa_assert(key);
+
+    if (pa_proplist_get(o->proplist, key, (const void **) &old_value, &old_nbytes) >= 0) {
+        if (value && nbytes == old_nbytes && !memcmp(value, old_value, nbytes))
+            return;
+
+        old_value_str = "(data)";
+
+    } else {
+        if (!value)
+            return;
+
+        old_value_str = "(unset)";
+    }
+
+    if (value) {
+        pa_proplist_set(o->proplist, key, value, nbytes);
+        new_value_str = "(data)";
+    } else {
+        pa_proplist_unset(o->proplist, key);
+        new_value_str = "(unset)";
+    }
+
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
+        pa_log_debug("Source output %u: proplist[%s]: %s -> %s", o->index, key, old_value_str, new_value_str);
+        pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
+        pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT | PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    }
+}
+
+/* Called from main thread */
+void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
+    void *state;
+    const char *key;
+    const uint8_t *value;
+    size_t nbytes;
+
+    pa_source_output_assert_ref(o);
+    pa_assert(p);
+    pa_assert_ctl_context();
+
+    switch (mode) {
+        case PA_UPDATE_SET: {
+            /* Delete everything that is not in p. */
+            for (state = NULL; (key = pa_proplist_iterate(o->proplist, &state));) {
+                if (!pa_proplist_contains(p, key))
+                    pa_source_output_set_property(o, key, NULL);
+            }
+
+            /* Fall through. */
+        }
+
+        case PA_UPDATE_REPLACE: {
+            for (state = NULL; (key = pa_proplist_iterate(p, &state));) {
+                pa_proplist_get(p, key, (const void **) &value, &nbytes);
+                pa_source_output_set_property_arbitrary(o, key, value, nbytes);
+            }
+
+            break;
+        }
+
+        case PA_UPDATE_MERGE: {
+            for (state = NULL; (key = pa_proplist_iterate(p, &state));) {
+                if (pa_proplist_contains(o->proplist, key))
+                    continue;
+
+                pa_proplist_get(p, key, (const void **) &value, &nbytes);
+                pa_source_output_set_property_arbitrary(o, key, value, nbytes);
+            }
+
+            break;
+        }
+    }
+}
+
+/* Called from main context */
+void pa_source_output_cork(pa_source_output *o, bool b) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
+}
+
+/* Called from main context */
+int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_return_val_if_fail(o->thread_info.resampler, -PA_ERR_BADSTATE);
+
+    if (o->sample_spec.rate == rate)
+        return 0;
+
+    o->sample_spec.rate = rate;
+
+    pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
+
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    return 0;
+}
+
+/* Called from main context */
+pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+
+    return o->actual_resample_method;
+}
+
+/* Called from main context */
+bool pa_source_output_may_move(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
+        return false;
+
+    if (o->direct_on_input)
+        return false;
+
+    return true;
+}
+
+static bool find_filter_source_output(pa_source_output *target, pa_source *s) {
+    unsigned PA_UNUSED i = 0;
+    while (s && s->output_from_master) {
+        if (s->output_from_master == target)
+            return true;
+        s = s->output_from_master->source;
+        pa_assert(i++ < 100);
+    }
+    return false;
+}
+
+static bool is_filter_source_moving(pa_source_output *o) {
+    pa_source *source = o->source;
+
+    if (!source)
+        return false;
+
+    while (source->output_from_master) {
+        source = source->output_from_master->source;
+
+        if (!source)
+            return true;
+    }
+
+    return false;
+}
+
+/* Called from main context */
+bool pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
+    pa_source_output_assert_ref(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_source_assert_ref(dest);
+
+    if (dest == o->source)
+        return true;
+
+    if (dest->unlink_requested)
+        return false;
+
+    if (!pa_source_output_may_move(o))
+        return false;
+
+    /* Make sure we're not creating a filter source cycle */
+    if (find_filter_source_output(o, dest)) {
+        pa_log_debug("Can't connect output to %s, as that would create a cycle.", dest->name);
+        return false;
+    }
+
+    /* If this source output is connected to a filter source that itself is
+     * moving, then don't allow the move. Moving requires sending a message to
+     * the IO thread of the old source, and if the old source is a filter
+     * source that is moving, there's no IO thread associated to the old
+     * source. */
+    if (is_filter_source_moving(o)) {
+        pa_log_debug("Can't move output from filter source %s, because the filter source itself is currently moving.",
+                     o->source->name);
+        return false;
+    }
+
+    if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
+        pa_log_warn("Failed to move source output: too many outputs per source.");
+        return false;
+    }
+
+    if (o->may_move_to)
+        if (!o->may_move_to(o, dest))
+            return false;
+
+    return true;
+}
+
+/* Called from main context */
+int pa_source_output_start_move(pa_source_output *o) {
+    pa_source *origin;
+    int r;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(o->source);
+
+    if (!pa_source_output_may_move(o))
+        return -PA_ERR_NOTSUPPORTED;
+
+    if ((r = pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], o)) < 0)
+        return r;
+
+    pa_log_debug("Starting to move source output %u from '%s'", (unsigned) o->index, o->source->name);
+
+    origin = o->source;
+
+    pa_idxset_remove_by_data(o->source->outputs, o, NULL);
+
+    if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
+        pa_assert_se(origin->n_corked-- >= 1);
+
+    if (pa_source_output_is_passthrough(o))
+        pa_source_leave_passthrough(o->source);
+
+    if (pa_source_flat_volume_enabled(o->source))
+        /* We might need to update the source's volume if we are in flat
+         * volume mode. */
+        pa_source_set_volume(o->source, NULL, false, false);
+
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
+
+    pa_source_update_status(o->source);
+
+    pa_cvolume_remap(&o->volume_factor_source, &o->source->channel_map, &o->channel_map);
+
+    o->source = NULL;
+
+    pa_source_output_unref(o);
+
+    return 0;
+}
+
+/* Called from main context. If it has an origin source that uses volume sharing,
+ * then also the origin source and all streams connected to it need to update
+ * their volume - this function does all that by using recursion. */
+static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
+    pa_cvolume new_volume;
+
+    pa_assert(o);
+    pa_assert(dest);
+    pa_assert(o->source); /* The destination source should already be set. */
+
+    if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+        pa_source *root_source;
+        pa_source_output *destination_source_output;
+        uint32_t idx;
+
+        root_source = pa_source_get_master(o->source);
+
+        if (PA_UNLIKELY(!root_source))
+            return;
+
+        if (pa_source_flat_volume_enabled(o->source)) {
+            /* Ok, so the origin source uses volume sharing, and flat volume is
+             * enabled. The volume will have to be updated as follows:
+             *
+             *     o->volume := o->source->real_volume
+             *         (handled later by pa_source_set_volume)
+             *     o->reference_ratio := o->volume / o->source->reference_volume
+             *         (handled later by pa_source_set_volume)
+             *     o->real_ratio stays unchanged
+             *         (streams whose origin source uses volume sharing should
+             *          always have real_ratio of 0 dB)
+             *     o->soft_volume stays unchanged
+             *         (streams whose origin source uses volume sharing should
+             *          always have volume_factor as soft_volume, so no change
+             *          should be needed) */
+
+            pa_assert(pa_cvolume_is_norm(&o->real_ratio));
+            pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor));
+
+            /* Notifications will be sent by pa_source_set_volume(). */
+
+        } else {
+            /* Ok, so the origin source uses volume sharing, and flat volume is
+             * disabled. The volume will have to be updated as follows:
+             *
+             *     o->volume := 0 dB
+             *     o->reference_ratio := 0 dB
+             *     o->real_ratio stays unchanged
+             *         (streams whose origin source uses volume sharing should
+             *          always have real_ratio of 0 dB)
+             *     o->soft_volume stays unchanged
+             *         (streams whose origin source uses volume sharing should
+             *          always have volume_factor as soft_volume, so no change
+             *          should be needed) */
+
+            pa_cvolume_reset(&new_volume, o->volume.channels);
+            pa_source_output_set_volume_direct(o, &new_volume);
+            pa_source_output_set_reference_ratio(o, &new_volume);
+            pa_assert(pa_cvolume_is_norm(&o->real_ratio));
+            pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor));
+        }
+
+        /* Additionally, the origin source volume needs updating:
+         *
+         *     o->destination_source->reference_volume := root_source->reference_volume
+         *     o->destination_source->real_volume := root_source->real_volume
+         *     o->destination_source->soft_volume stays unchanged
+         *         (sources that use volume sharing should always have
+         *          soft_volume of 0 dB) */
+
+        new_volume = root_source->reference_volume;
+        pa_cvolume_remap(&new_volume, &root_source->channel_map, &o->destination_source->channel_map);
+        pa_source_set_reference_volume_direct(o->destination_source, &new_volume);
+
+        o->destination_source->real_volume = root_source->real_volume;
+        pa_cvolume_remap(&o->destination_source->real_volume, &root_source->channel_map, &o->destination_source->channel_map);
+
+        pa_assert(pa_cvolume_is_norm(&o->destination_source->soft_volume));
+
+        /* If you wonder whether o->destination_source->set_volume() should be
+         * called somewhere, that's not the case, because sources that use
+         * volume sharing shouldn't have any internal volume that set_volume()
+         * would update. If you wonder whether the thread_info variables should
+         * be synced, yes, they should, and it's done by the
+         * PA_SOURCE_MESSAGE_FINISH_MOVE message handler. */
+
+        /* Recursively update origin source outputs. */
+        PA_IDXSET_FOREACH(destination_source_output, o->destination_source->outputs, idx)
+            update_volume_due_to_moving(destination_source_output, dest);
+
+    } else {
+        if (pa_source_flat_volume_enabled(o->source)) {
+            /* Ok, so this is a regular stream, and flat volume is enabled. The
+             * volume will have to be updated as follows:
+             *
+             *     o->volume := o->reference_ratio * o->source->reference_volume
+             *     o->reference_ratio stays unchanged
+             *     o->real_ratio := o->volume / o->source->real_volume
+             *         (handled later by pa_source_set_volume)
+             *     o->soft_volume := o->real_ratio * o->volume_factor
+             *         (handled later by pa_source_set_volume) */
+
+            new_volume = o->source->reference_volume;
+            pa_cvolume_remap(&new_volume, &o->source->channel_map, &o->channel_map);
+            pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio);
+            pa_source_output_set_volume_direct(o, &new_volume);
+
+        } else {
+            /* Ok, so this is a regular stream, and flat volume is disabled.
+             * The volume will have to be updated as follows:
+             *
+             *     o->volume := o->reference_ratio
+             *     o->reference_ratio stays unchanged
+             *     o->real_ratio := o->reference_ratio
+             *     o->soft_volume := o->real_ratio * o->volume_factor */
+
+            pa_source_output_set_volume_direct(o, &o->reference_ratio);
+            o->real_ratio = o->reference_ratio;
+            pa_sw_cvolume_multiply(&o->soft_volume, &o->real_ratio, &o->volume_factor);
+        }
+    }
+
+    /* If o->source == dest, then recursion has finished, and we can finally call
+     * pa_source_set_volume(), which will do the rest of the updates. */
+    if ((o->source == dest) && pa_source_flat_volume_enabled(o->source))
+        pa_source_set_volume(o->source, NULL, false, o->save_volume);
+}
+
+/* Called from main context */
+int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, bool save) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(!o->source);
+    pa_source_assert_ref(dest);
+
+    if (!pa_source_output_may_move_to(o, dest))
+        return -PA_ERR_NOTSUPPORTED;
+
+    if (pa_source_output_is_passthrough(o) && !pa_source_check_format(dest, o->format)) {
+        pa_proplist *p = pa_proplist_new();
+        pa_log_debug("New source doesn't support stream format, sending format-changed and killing");
+        /* Tell the client what device we want to be on if it is going to
+         * reconnect */
+        pa_proplist_sets(p, "device", dest->name);
+        pa_source_output_send_event(o, PA_STREAM_EVENT_FORMAT_LOST, p);
+        pa_proplist_free(p);
+        return -PA_ERR_NOTSUPPORTED;
+    }
+
+    if (!(o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) &&
+        !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec)) {
+        /* try to change dest sink rate if possible without glitches.
+           module-suspend-on-idle resumes destination source with
+           SOURCE_OUTPUT_MOVE_FINISH hook */
+
+        pa_log_info("Trying to change sample rate");
+        if (pa_source_update_rate(dest, o->sample_spec.rate, pa_source_output_is_passthrough(o)) >= 0)
+            pa_log_info("Rate changed to %u Hz", dest->sample_spec.rate);
+    }
+
+    if (o->moving)
+        o->moving(o, dest);
+
+    o->source = dest;
+    o->save_source = save;
+    pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL);
+
+    pa_cvolume_remap(&o->volume_factor_source, &o->channel_map, &o->source->channel_map);
+
+    if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
+        o->source->n_corked++;
+
+    pa_source_output_update_rate(o);
+
+    pa_source_update_status(dest);
+
+    update_volume_due_to_moving(o, dest);
+
+    if (pa_source_output_is_passthrough(o))
+        pa_source_enter_passthrough(o->source);
+
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
+
+    pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name);
+
+    /* Notify everyone */
+    pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o);
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+
+    return 0;
+}
+
+/* Called from main context */
+void pa_source_output_fail_move(pa_source_output *o) {
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(!o->source);
+
+    /* Check if someone wants this source output? */
+    if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_STOP)
+        return;
+
+    if (o->moving)
+        o->moving(o, NULL);
+
+    pa_source_output_kill(o);
+}
+
+/* Called from main context */
+int pa_source_output_move_to(pa_source_output *o, pa_source *dest, bool save) {
+    int r;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(o->source);
+    pa_source_assert_ref(dest);
+
+    if (dest == o->source)
+        return 0;
+
+    if (!pa_source_output_may_move_to(o, dest))
+        return -PA_ERR_NOTSUPPORTED;
+
+    pa_source_output_ref(o);
+
+    if ((r = pa_source_output_start_move(o)) < 0) {
+        pa_source_output_unref(o);
+        return r;
+    }
+
+    if ((r = pa_source_output_finish_move(o, dest, save)) < 0) {
+        pa_source_output_fail_move(o);
+        pa_source_output_unref(o);
+        return r;
+    }
+
+    pa_source_output_unref(o);
+
+    return 0;
+}
+
+/* Called from IO thread context except when cork() is called without a valid source. */
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
+    pa_source_output_assert_ref(o);
+
+    if (state == o->thread_info.state)
+        return;
+
+    if (o->state_change)
+        o->state_change(o, state);
+
+    o->thread_info.state = state;
+}
+
+/* Called from IO thread context, except when it is not */
+int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) {
+    pa_source_output *o = PA_SOURCE_OUTPUT(mo);
+    pa_source_output_assert_ref(o);
+
+    switch (code) {
+
+        case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            r[0] += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec);
+            r[1] += pa_source_get_latency_within_thread(o->source, false);
+
+            return 0;
+        }
+
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE:
+
+            o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
+            pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
+            return 0;
+
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
+
+            pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
+
+            return 0;
+
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: {
+            pa_usec_t *usec = userdata;
+
+            *usec = pa_source_output_set_requested_latency_within_thread(o, *usec);
+
+            return 0;
+        }
+
+        case PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            *r = o->thread_info.requested_source_latency;
+            return 0;
+        }
+
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_VOLUME:
+            if (!pa_cvolume_equal(&o->thread_info.soft_volume, &o->soft_volume)) {
+                o->thread_info.soft_volume = o->soft_volume;
+            }
+            return 0;
+
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE:
+            if (o->thread_info.muted != o->muted) {
+                o->thread_info.muted = o->muted;
+            }
+            return 0;
+    }
+
+    return -PA_ERR_NOTIMPLEMENTED;
+}
+
+/* Called from main context */
+void pa_source_output_send_event(pa_source_output *o, const char *event, pa_proplist *data) {
+    pa_proplist *pl = NULL;
+    pa_source_output_send_event_hook_data hook_data;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(event);
+
+    if (!o->send_event)
+        return;
+
+    if (!data)
+        data = pl = pa_proplist_new();
+
+    hook_data.source_output = o;
+    hook_data.data = data;
+    hook_data.event = event;
+
+    if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], &hook_data) < 0)
+        goto finish;
+
+    o->send_event(o, event, data);
+
+finish:
+    if (pl)
+        pa_proplist_free(pl);
+}
+
+/* Called from main context */
+/* Updates the source output's resampler with whatever the current source
+ * requires -- useful when the underlying source's rate might have changed */
+int pa_source_output_update_rate(pa_source_output *o) {
+    pa_resampler *new_resampler;
+    char *memblockq_name;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+
+    if (o->thread_info.resampler &&
+        pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &o->source->sample_spec) &&
+        pa_channel_map_equal(pa_resampler_input_channel_map(o->thread_info.resampler), &o->source->channel_map))
+
+        new_resampler = o->thread_info.resampler;
+
+    else if (!pa_source_output_is_passthrough(o) &&
+        ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
+         !pa_sample_spec_equal(&o->sample_spec, &o->source->sample_spec) ||
+         !pa_channel_map_equal(&o->channel_map, &o->source->channel_map))) {
+
+        new_resampler = pa_resampler_new(o->core->mempool,
+                                     &o->source->sample_spec, &o->source->channel_map,
+                                     &o->sample_spec, &o->channel_map,
+                                     o->core->lfe_crossover_freq,
+                                     o->requested_resample_method,
+                                     ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+                                     ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+                                     (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+                                     (o->core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) |
+                                     (o->core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0));
+
+        if (!new_resampler) {
+            pa_log_warn("Unsupported resampling operation.");
+            return -PA_ERR_NOTSUPPORTED;
+        }
+    } else
+        new_resampler = NULL;
+
+    if (new_resampler == o->thread_info.resampler)
+        return 0;
+
+    if (o->thread_info.resampler)
+        pa_resampler_free(o->thread_info.resampler);
+
+    o->thread_info.resampler = new_resampler;
+
+    pa_memblockq_free(o->thread_info.delay_memblockq);
+
+    memblockq_name = pa_sprintf_malloc("source output delay_memblockq [%u]", o->index);
+    o->thread_info.delay_memblockq = pa_memblockq_new(
+            memblockq_name,
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            0,
+            &o->source->sample_spec,
+            0,
+            1,
+            0,
+            &o->source->silence);
+    pa_xfree(memblockq_name);
+
+    o->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID;
+
+    pa_log_debug("Updated resampler for source output %d", o->index);
+
+    return 0;
+}
+
+/* Called from the IO thread. */
+void pa_source_output_attach(pa_source_output *o) {
+    pa_assert(o);
+    pa_assert(!o->thread_info.attached);
+
+    o->thread_info.attached = true;
+
+    if (o->attach)
+        o->attach(o);
+}
+
+/* Called from the IO thread. */
+void pa_source_output_detach(pa_source_output *o) {
+    pa_assert(o);
+
+    if (!o->thread_info.attached)
+        return;
+
+    o->thread_info.attached = false;
+
+    if (o->detach)
+        o->detach(o);
+}
+
+/* Called from the main thread. */
+void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *volume) {
+    pa_cvolume old_volume;
+    char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(o);
+    pa_assert(volume);
+
+    old_volume = o->volume;
+
+    if (pa_cvolume_equal(volume, &old_volume))
+        return;
+
+    o->volume = *volume;
+    pa_log_debug("The volume of source output %u changed from %s to %s.", o->index,
+                 pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &o->channel_map, true),
+                 pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &o->channel_map, true));
+
+    if (o->volume_changed)
+        o->volume_changed(o);
+
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED], o);
+}
+
+/* Called from the main thread. */
+void pa_source_output_set_reference_ratio(pa_source_output *o, const pa_cvolume *ratio) {
+    pa_cvolume old_ratio;
+    char old_ratio_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    char new_ratio_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(o);
+    pa_assert(ratio);
+
+    old_ratio = o->reference_ratio;
+
+    if (pa_cvolume_equal(ratio, &old_ratio))
+        return;
+
+    o->reference_ratio = *ratio;
+
+    if (!PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+        return;
+
+    pa_log_debug("Source output %u reference ratio changed from %s to %s.", o->index,
+                 pa_cvolume_snprint_verbose(old_ratio_str, sizeof(old_ratio_str), &old_ratio, &o->channel_map, true),
+                 pa_cvolume_snprint_verbose(new_ratio_str, sizeof(new_ratio_str), ratio, &o->channel_map, true));
+}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
new file mode 100644 (file)
index 0000000..40e4e3a
--- /dev/null
@@ -0,0 +1,393 @@
+#ifndef foopulsesourceoutputhfoo
+#define foopulsesourceoutputhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include <pulsecore/typedefs.h>
+#include <pulse/sample.h>
+#include <pulse/format.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/module.h>
+#include <pulsecore/client.h>
+#include <pulsecore/source.h>
+#include <pulsecore/core.h>
+#include <pulsecore/sink-input.h>
+
+typedef enum pa_source_output_state {
+    PA_SOURCE_OUTPUT_INIT,
+    PA_SOURCE_OUTPUT_RUNNING,
+    PA_SOURCE_OUTPUT_CORKED,
+    PA_SOURCE_OUTPUT_UNLINKED
+} pa_source_output_state_t;
+
+static inline bool PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_state_t x) {
+    return x == PA_SOURCE_OUTPUT_RUNNING || x == PA_SOURCE_OUTPUT_CORKED;
+}
+
+typedef enum pa_source_output_flags {
+    PA_SOURCE_OUTPUT_VARIABLE_RATE = 1,
+    PA_SOURCE_OUTPUT_DONT_MOVE = 2,
+    PA_SOURCE_OUTPUT_START_CORKED = 4,
+    PA_SOURCE_OUTPUT_NO_REMAP = 8,
+    PA_SOURCE_OUTPUT_NO_REMIX = 16,
+    PA_SOURCE_OUTPUT_FIX_FORMAT = 32,
+    PA_SOURCE_OUTPUT_FIX_RATE = 64,
+    PA_SOURCE_OUTPUT_FIX_CHANNELS = 128,
+    PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
+    PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND = 512,
+    PA_SOURCE_OUTPUT_KILL_ON_SUSPEND = 1024,
+    PA_SOURCE_OUTPUT_PASSTHROUGH = 2048
+} pa_source_output_flags_t;
+
+struct pa_source_output {
+    pa_msgobject parent;
+
+    uint32_t index;
+    pa_core *core;
+
+    pa_source_output_state_t state;
+    pa_source_output_flags_t flags;
+
+    char *driver;                         /* may be NULL */
+    pa_proplist *proplist;
+
+    pa_module *module;                    /* may be NULL */
+    pa_client *client;                    /* may be NULL */
+
+    pa_source *source;                    /* NULL while being moved */
+    pa_source *destination_source;        /* only set by filter sources */
+
+    /* A source output can monitor just a single input of a sink, in which case we find it here */
+    pa_sink_input *direct_on_input;       /* may be NULL */
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    pa_format_info *format;
+
+    /* Also see http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Volumes/ */
+    pa_cvolume volume;             /* The volume clients are informed about */
+    pa_cvolume reference_ratio;    /* The ratio of the stream's volume to the source's reference volume */
+    pa_cvolume real_ratio;         /* The ratio of the stream's volume to the source's real volume */
+    pa_cvolume volume_factor;      /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */
+    pa_cvolume soft_volume;        /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as real_ratio * volume_factor */
+
+    pa_cvolume volume_factor_source; /* A second volume factor in format of the source this stream is connected to */
+
+    bool volume_writable:1;
+
+    bool muted:1;
+
+    /* if true then the source we are connected to and/or the volume
+     * set is worth remembering, i.e. was explicitly chosen by the
+     * user and not automatically. module-stream-restore looks for
+     * this.*/
+    bool save_source:1, save_volume:1, save_muted:1;
+
+    pa_resample_method_t requested_resample_method, actual_resample_method;
+
+    /* Pushes a new memchunk into the output. Called from IO thread
+     * context. */
+    void (*push)(pa_source_output *o, const pa_memchunk *chunk); /* may NOT be NULL */
+
+    /* Only relevant for monitor sources right now: called when the
+     * recorded stream is rewound. Called from IO context */
+    void (*process_rewind)(pa_source_output *o, size_t nbytes); /* may be NULL */
+
+    /* Called whenever the maximum rewindable size of the source
+     * changes. Called from IO thread context. */
+    void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */
+
+    /* Called whenever the configured latency of the source
+     * changes. Called from IO context. */
+    void (*update_source_requested_latency) (pa_source_output *o); /* may be NULL */
+
+    /* Called whenever the latency range of the source changes. Called
+     * from IO context. */
+    void (*update_source_latency_range) (pa_source_output *o); /* may be NULL */
+
+    /* Called whenever the fixed latency of the source changes, if there
+     * is one. Called from IO context. */
+    void (*update_source_fixed_latency) (pa_source_output *i); /* may be NULL */
+
+    /* If non-NULL this function is called when the output is first
+     * connected to a source or when the rtpoll/asyncmsgq fields
+     * change. You usually don't need to implement this function
+     * unless you rewrite a source that is piggy-backed onto
+     * another. Called from IO thread context */
+    void (*attach) (pa_source_output *o);           /* may be NULL */
+
+    /* If non-NULL this function is called when the output is
+     * disconnected from its source. Called from IO thread context */
+    void (*detach) (pa_source_output *o);           /* may be NULL */
+
+    /* If non-NULL called whenever the source this output is attached
+     * to suspends or resumes. Called from main context */
+    void (*suspend) (pa_source_output *o, bool b);   /* may be NULL */
+
+    /* If non-NULL called whenever the source this output is attached
+     * to suspends or resumes. Called from IO context */
+    void (*suspend_within_thread) (pa_source_output *o, bool b);   /* may be NULL */
+
+    /* If non-NULL called whenever the source output is moved to a new
+     * source. Called from main context after the source output has been
+     * detached from the old source and before it has been attached to
+     * the new source. If dest is NULL the move was executed in two
+     * phases and the second one failed; the stream will be destroyed
+     * after this call. */
+    void (*moving) (pa_source_output *o, pa_source *dest);   /* may be NULL */
+
+    /* Supposed to unlink and destroy this stream. Called from main
+     * context. */
+    void (*kill)(pa_source_output* o);              /* may NOT be NULL */
+
+    /* Return the current latency (i.e. length of buffered audio) of
+    this stream. Called from main context. This is added to what the
+    PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY message sent to the IO thread
+    returns */
+    pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */
+
+    /* If non-NULL this function is called from thread context if the
+     * state changes. The old state is found in thread_info.state.  */
+    void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */
+
+    /* If non-NULL this function is called before this source output
+     * is moved to a source and if it returns false the move
+     * will not be allowed */
+    bool (*may_move_to) (pa_source_output *o, pa_source *s); /* may be NULL */
+
+    /* If non-NULL this function is used to dispatch asynchronous
+     * control events. */
+    void (*send_event)(pa_source_output *o, const char *event, pa_proplist* data);
+
+    /* If non-NULL this function is called whenever the source output
+     * volume changes. Called from main context */
+    void (*volume_changed)(pa_source_output *o); /* may be NULL */
+
+    /* If non-NULL this function is called whenever the source output
+     * mute status changes. Called from main context */
+    void (*mute_changed)(pa_source_output *o); /* may be NULL */
+
+    struct {
+        pa_source_output_state_t state;
+
+        pa_cvolume soft_volume;
+        bool muted:1;
+
+        bool attached:1; /* True only between ->attach() and ->detach() calls */
+
+        pa_sample_spec sample_spec;
+
+        pa_resampler* resampler;              /* may be NULL */
+
+        /* We maintain a delay memblockq here for source outputs that
+         * don't implement rewind() */
+        pa_memblockq *delay_memblockq;
+
+        /* The requested latency for the source */
+        pa_usec_t requested_source_latency;
+
+        pa_sink_input *direct_on_input;       /* may be NULL */
+    } thread_info;
+
+    void *userdata;
+};
+
+PA_DECLARE_PUBLIC_CLASS(pa_source_output);
+#define PA_SOURCE_OUTPUT(o) pa_source_output_cast(o)
+
+enum {
+    PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY,
+    PA_SOURCE_OUTPUT_MESSAGE_SET_RATE,
+    PA_SOURCE_OUTPUT_MESSAGE_SET_STATE,
+    PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY,
+    PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY,
+    PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_VOLUME,
+    PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE,
+    PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
+typedef struct pa_source_output_send_event_hook_data {
+    pa_source_output *source_output;
+    const char *event;
+    pa_proplist *data;
+} pa_source_output_send_event_hook_data;
+
+typedef struct pa_source_output_new_data {
+    pa_source_output_flags_t flags;
+
+    pa_proplist *proplist;
+    pa_sink_input *direct_on_input;
+
+    const char *driver;
+    pa_module *module;
+    pa_client *client;
+
+    pa_source *source;
+    pa_source *destination_source;
+
+    pa_resample_method_t resample_method;
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    pa_format_info *format;
+    pa_idxset *req_formats;
+    pa_idxset *nego_formats;
+
+    pa_cvolume volume, volume_factor, volume_factor_source;
+    bool muted:1;
+
+    bool sample_spec_is_set:1;
+    bool channel_map_is_set:1;
+
+    bool volume_is_set:1, volume_factor_is_set:1, volume_factor_source_is_set:1;
+    bool muted_is_set:1;
+
+    bool volume_is_absolute:1;
+
+    bool volume_writable:1;
+
+    bool save_source:1, save_volume:1, save_muted:1;
+} pa_source_output_new_data;
+
+pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
+void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
+void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
+bool pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data);
+void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume);
+void pa_source_output_new_data_apply_volume_factor(pa_source_output_new_data *data, const pa_cvolume *volume_factor);
+void pa_source_output_new_data_apply_volume_factor_source(pa_source_output_new_data *data, const pa_cvolume *volume_factor);
+void pa_source_output_new_data_set_muted(pa_source_output_new_data *data, bool mute);
+bool pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_source *s, bool save);
+bool pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats);
+void pa_source_output_new_data_done(pa_source_output_new_data *data);
+
+/* To be called by the implementing module only */
+
+int pa_source_output_new(
+        pa_source_output**o,
+        pa_core *core,
+        pa_source_output_new_data *data);
+
+void pa_source_output_put(pa_source_output *o);
+void pa_source_output_unlink(pa_source_output*o);
+
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec);
+
+void pa_source_output_cork(pa_source_output *o, bool b);
+
+int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
+int pa_source_output_update_rate(pa_source_output *o);
+
+size_t pa_source_output_get_max_rewind(pa_source_output *o);
+
+/* Callable by everyone */
+
+/* External code may request disconnection with this function */
+void pa_source_output_kill(pa_source_output*o);
+
+pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency);
+
+bool pa_source_output_is_volume_readable(pa_source_output *o);
+bool pa_source_output_is_passthrough(pa_source_output *o);
+void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume, bool save, bool absolute);
+pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, bool absolute);
+
+void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save);
+
+void pa_source_output_set_property(pa_source_output *o, const char *key, const char *value);
+void pa_source_output_set_property_arbitrary(pa_source_output *o, const char *key, const uint8_t *value, size_t nbytes);
+void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p);
+
+pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
+
+void pa_source_output_send_event(pa_source_output *o, const char *name, pa_proplist *data);
+
+bool pa_source_output_may_move(pa_source_output *o);
+bool pa_source_output_may_move_to(pa_source_output *o, pa_source *dest);
+int pa_source_output_move_to(pa_source_output *o, pa_source *dest, bool save);
+
+/* The same as pa_source_output_move_to() but in two separate steps,
+ * first the detaching from the old source, then the attaching to the
+ * new source */
+int pa_source_output_start_move(pa_source_output *o);
+int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, bool save);
+void pa_source_output_fail_move(pa_source_output *o);
+
+#define pa_source_output_get_state(o) ((o)->state)
+
+pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o);
+
+/* To be used exclusively by the source driver thread */
+
+void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk);
+void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes);
+void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes);
+
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state);
+
+int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
+
+/* Calls the attach() callback if it's set. The output must be in detached
+ * state. */
+void pa_source_output_attach(pa_source_output *o);
+
+/* Calls the detach() callback if it's set and the output is attached. The
+ * output is allowed to be already detached, in which case this does nothing.
+ *
+ * The reason why this can be called for already-detached outputs is that when
+ * a filter source's output is detached, it has to detach also all outputs
+ * connected to the filter source. In case the filter source's output was
+ * detached because the filter source is being removed, those other outputs
+ * will be moved to another source or removed, and moving and removing involve
+ * detaching the outputs, but the outputs at that point are already detached.
+ *
+ * XXX: Moving or removing an output also involves sending messages to the
+ * output's source. If the output's source is a detached filter source,
+ * shouldn't sending messages to it be prohibited? The messages are processed
+ * in the root source's IO thread, and when the filter source is detached, it
+ * would seem logical to prohibit any interaction with the IO thread that isn't
+ * any more associated with the filter source. Currently sending messages to
+ * detached filter sources mostly works, because the filter sources don't
+ * update their asyncmsgq pointer when detaching, so messages still find their
+ * way to the old IO thread. */
+void pa_source_output_detach(pa_source_output *o);
+
+/* Called from the main thread, from source.c only. The normal way to set the
+ * source output volume is to call pa_source_output_set_volume(), but the flat
+ * volume logic in source.c needs also a function that doesn't do all the extra
+ * stuff that pa_source_output_set_volume() does. This function simply sets
+ * o->volume and fires change notifications. */
+void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *volume);
+
+/* Called from the main thread, from source.c only. This shouldn't be a public
+ * function, but the flat volume logic in source.c currently needs a way to
+ * directly set the source output reference ratio. This function simply sets
+ * o->reference_ratio and logs a message if the value changes. */
+void pa_source_output_set_reference_ratio(pa_source_output *o, const pa_cvolume *ratio);
+
+#define pa_source_output_assert_io_context(s) \
+    pa_assert(pa_thread_mq_get() || !PA_SOURCE_OUTPUT_IS_LINKED((s)->state))
+
+#endif
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
new file mode 100644 (file)
index 0000000..e0d32af
--- /dev/null
@@ -0,0 +1,2920 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/format.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/rtclock.h>
+#include <pulse/internal.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/mix.h>
+#include <pulsecore/flist.h>
+
+#include "source.h"
+
+#define ABSOLUTE_MIN_LATENCY (500)
+#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
+#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
+
+PA_DEFINE_PUBLIC_CLASS(pa_source, pa_msgobject);
+
+struct pa_source_volume_change {
+    pa_usec_t at;
+    pa_cvolume hw_volume;
+
+    PA_LLIST_FIELDS(pa_source_volume_change);
+};
+
+struct source_message_set_port {
+    pa_device_port *port;
+    int ret;
+};
+
+static void source_free(pa_object *o);
+
+static void pa_source_volume_change_push(pa_source *s);
+static void pa_source_volume_change_flush(pa_source *s);
+
+pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) {
+    pa_assert(data);
+
+    pa_zero(*data);
+    data->proplist = pa_proplist_new();
+    data->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_device_port_unref);
+
+    return data;
+}
+
+void pa_source_new_data_set_name(pa_source_new_data *data, const char *name) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    data->name = pa_xstrdup(name);
+}
+
+void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec) {
+    pa_assert(data);
+
+    if ((data->sample_spec_is_set = !!spec))
+        data->sample_spec = *spec;
+}
+
+void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map) {
+    pa_assert(data);
+
+    if ((data->channel_map_is_set = !!map))
+        data->channel_map = *map;
+}
+
+void pa_source_new_data_set_alternate_sample_rate(pa_source_new_data *data, const uint32_t alternate_sample_rate) {
+    pa_assert(data);
+
+    data->alternate_sample_rate_is_set = true;
+    data->alternate_sample_rate = alternate_sample_rate;
+}
+
+void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume) {
+    pa_assert(data);
+
+    if ((data->volume_is_set = !!volume))
+        data->volume = *volume;
+}
+
+void pa_source_new_data_set_muted(pa_source_new_data *data, bool mute) {
+    pa_assert(data);
+
+    data->muted_is_set = true;
+    data->muted = mute;
+}
+
+void pa_source_new_data_set_port(pa_source_new_data *data, const char *port) {
+    pa_assert(data);
+
+    pa_xfree(data->active_port);
+    data->active_port = pa_xstrdup(port);
+}
+
+void pa_source_new_data_done(pa_source_new_data *data) {
+    pa_assert(data);
+
+    pa_proplist_free(data->proplist);
+
+    if (data->ports)
+        pa_hashmap_free(data->ports);
+
+    pa_xfree(data->name);
+    pa_xfree(data->active_port);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_source *s) {
+    pa_assert(s);
+
+    s->set_state = NULL;
+    s->get_volume = NULL;
+    s->set_volume = NULL;
+    s->write_volume = NULL;
+    s->get_mute = NULL;
+    s->set_mute = NULL;
+    s->update_requested_latency = NULL;
+    s->set_port = NULL;
+    s->get_formats = NULL;
+    s->update_rate = NULL;
+}
+
+/* Called from main context */
+pa_source* pa_source_new(
+        pa_core *core,
+        pa_source_new_data *data,
+        pa_source_flags_t flags) {
+
+    pa_source *s;
+    const char *name;
+    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char *pt;
+
+    pa_assert(core);
+    pa_assert(data);
+    pa_assert(data->name);
+    pa_assert_ctl_context();
+
+    s = pa_msgobject_new(pa_source);
+
+    if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) {
+        pa_log_debug("Failed to register name %s.", data->name);
+        pa_xfree(s);
+        return NULL;
+    }
+
+    pa_source_new_data_set_name(data, name);
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) {
+        pa_xfree(s);
+        pa_namereg_unregister(core, name);
+        return NULL;
+    }
+
+    /* FIXME, need to free s here on failure */
+
+    pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+    pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
+
+    pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
+
+    if (!data->channel_map_is_set)
+        pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+
+    pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+    pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
+    /* FIXME: There should probably be a general function for checking whether
+     * the source volume is allowed to be set, like there is for source outputs. */
+    pa_assert(!data->volume_is_set || !(flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
+
+    if (!data->volume_is_set) {
+        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+        data->save_volume = false;
+    }
+
+    pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
+    pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
+
+    if (!data->muted_is_set)
+        data->muted = false;
+
+    if (data->card)
+        pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
+
+    pa_device_init_description(data->proplist, data->card);
+    pa_device_init_icon(data->proplist, false);
+    pa_device_init_intended_roles(data->proplist);
+
+    if (!data->active_port) {
+        pa_device_port *p = pa_device_port_find_best(data->ports);
+        if (p)
+            pa_source_new_data_set_port(data, p->name);
+    }
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
+        pa_xfree(s);
+        pa_namereg_unregister(core, name);
+        return NULL;
+    }
+
+    s->parent.parent.free = source_free;
+    s->parent.process_msg = pa_source_process_msg;
+
+    s->core = core;
+    s->state = PA_SOURCE_INIT;
+    s->flags = flags;
+    s->priority = 0;
+    s->suspend_cause = data->suspend_cause;
+    pa_source_set_mixer_dirty(s, false);
+    s->name = pa_xstrdup(name);
+    s->proplist = pa_proplist_copy(data->proplist);
+    s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
+    s->module = data->module;
+    s->card = data->card;
+
+    s->priority = pa_device_init_priority(s->proplist);
+
+    s->sample_spec = data->sample_spec;
+    s->channel_map = data->channel_map;
+    s->default_sample_rate = s->sample_spec.rate;
+
+    if (data->alternate_sample_rate_is_set)
+        s->alternate_sample_rate = data->alternate_sample_rate;
+    else
+        s->alternate_sample_rate = s->core->alternate_sample_rate;
+
+    if (s->sample_spec.rate == s->alternate_sample_rate) {
+        pa_log_warn("Default and alternate sample rates are the same.");
+        s->alternate_sample_rate = 0;
+    }
+
+    s->outputs = pa_idxset_new(NULL, NULL);
+    s->n_corked = 0;
+    s->monitor_of = NULL;
+    s->output_from_master = NULL;
+
+    s->reference_volume = s->real_volume = data->volume;
+    pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+    s->base_volume = PA_VOLUME_NORM;
+    s->n_volume_steps = PA_VOLUME_NORM+1;
+    s->muted = data->muted;
+    s->refresh_volume = s->refresh_muted = false;
+
+    reset_callbacks(s);
+    s->userdata = NULL;
+
+    s->asyncmsgq = NULL;
+
+    /* As a minor optimization we just steal the list instead of
+     * copying it here */
+    s->ports = data->ports;
+    data->ports = NULL;
+
+    s->active_port = NULL;
+    s->save_port = false;
+
+    if (data->active_port)
+        if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
+            s->save_port = data->save_port;
+
+    /* Hopefully the active port has already been assigned in the previous call
+       to pa_device_port_find_best, but better safe than sorry */
+    if (!s->active_port)
+        s->active_port = pa_device_port_find_best(s->ports);
+
+    if (s->active_port)
+        s->port_latency_offset = s->active_port->latency_offset;
+    else
+        s->port_latency_offset = 0;
+
+    s->save_volume = data->save_volume;
+    s->save_muted = data->save_muted;
+
+    pa_silence_memchunk_get(
+            &core->silence_cache,
+            core->mempool,
+            &s->silence,
+            &s->sample_spec,
+            0);
+
+    s->thread_info.rtpoll = NULL;
+    s->thread_info.outputs = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+                                                 (pa_free_cb_t) pa_source_output_unref);
+    s->thread_info.soft_volume = s->soft_volume;
+    s->thread_info.soft_muted = s->muted;
+    s->thread_info.state = s->state;
+    s->thread_info.max_rewind = 0;
+    s->thread_info.requested_latency_valid = false;
+    s->thread_info.requested_latency = 0;
+    s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
+    s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
+    s->thread_info.fixed_latency = flags & PA_SOURCE_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
+
+    PA_LLIST_HEAD_INIT(pa_source_volume_change, s->thread_info.volume_changes);
+    s->thread_info.volume_changes_tail = NULL;
+    pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
+    s->thread_info.volume_change_safety_margin = core->deferred_volume_safety_margin_usec;
+    s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec;
+    s->thread_info.port_latency_offset = s->port_latency_offset;
+
+    /* FIXME: This should probably be moved to pa_source_put() */
+    pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
+
+    if (s->card)
+        pa_assert_se(pa_idxset_put(s->card->sources, s, NULL) >= 0);
+
+    pt = pa_proplist_to_string_sep(s->proplist, "\n    ");
+    pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s\n    %s",
+                s->index,
+                s->name,
+                pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
+                pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map),
+                pt);
+    pa_xfree(pt);
+
+    return s;
+}
+
+/* Called from main context */
+static int source_set_state(pa_source *s, pa_source_state_t state) {
+    int ret;
+    bool suspend_change;
+    pa_source_state_t original_state;
+
+    pa_assert(s);
+    pa_assert_ctl_context();
+
+    if (s->state == state)
+        return 0;
+
+    original_state = s->state;
+
+    suspend_change =
+        (original_state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) ||
+        (PA_SOURCE_IS_OPENED(original_state) && state == PA_SOURCE_SUSPENDED);
+
+    if (s->set_state)
+        if ((ret = s->set_state(s, state)) < 0)
+            return ret;
+
+    if (s->asyncmsgq)
+        if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
+
+            if (s->set_state)
+                s->set_state(s, original_state);
+
+            return ret;
+        }
+
+    s->state = state;
+
+    if (state != PA_SOURCE_UNLINKED) { /* if we enter UNLINKED state pa_source_unlink() will fire the appropriate events */
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
+    if (suspend_change) {
+        pa_source_output *o;
+        uint32_t idx;
+
+        /* We're suspending or resuming, tell everyone about it */
+
+        PA_IDXSET_FOREACH(o, s->outputs, idx)
+            if (s->state == PA_SOURCE_SUSPENDED &&
+                (o->flags & PA_SOURCE_OUTPUT_KILL_ON_SUSPEND))
+                pa_source_output_kill(o);
+            else if (o->suspend)
+                o->suspend(o, state == PA_SOURCE_SUSPENDED);
+    }
+
+    return 0;
+}
+
+void pa_source_set_get_volume_callback(pa_source *s, pa_source_cb_t cb) {
+    pa_assert(s);
+
+    s->get_volume = cb;
+}
+
+void pa_source_set_set_volume_callback(pa_source *s, pa_source_cb_t cb) {
+    pa_source_flags_t flags;
+
+    pa_assert(s);
+    pa_assert(!s->write_volume || cb);
+
+    s->set_volume = cb;
+
+    /* Save the current flags so we can tell if they've changed */
+    flags = s->flags;
+
+    if (cb) {
+        /* The source implementor is responsible for setting decibel volume support */
+        s->flags |= PA_SOURCE_HW_VOLUME_CTRL;
+    } else {
+        s->flags &= ~PA_SOURCE_HW_VOLUME_CTRL;
+        /* See note below in pa_source_put() about volume sharing and decibel volumes */
+        pa_source_enable_decibel_volume(s, !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
+    }
+
+    /* If the flags have changed after init, let any clients know via a change event */
+    if (s->state != PA_SOURCE_INIT && flags != s->flags)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb) {
+    pa_source_flags_t flags;
+
+    pa_assert(s);
+    pa_assert(!cb || s->set_volume);
+
+    s->write_volume = cb;
+
+    /* Save the current flags so we can tell if they've changed */
+    flags = s->flags;
+
+    if (cb)
+        s->flags |= PA_SOURCE_DEFERRED_VOLUME;
+    else
+        s->flags &= ~PA_SOURCE_DEFERRED_VOLUME;
+
+    /* If the flags have changed after init, let any clients know via a change event */
+    if (s->state != PA_SOURCE_INIT && flags != s->flags)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+void pa_source_set_get_mute_callback(pa_source *s, pa_source_get_mute_cb_t cb) {
+    pa_assert(s);
+
+    s->get_mute = cb;
+}
+
+void pa_source_set_set_mute_callback(pa_source *s, pa_source_cb_t cb) {
+    pa_source_flags_t flags;
+
+    pa_assert(s);
+
+    s->set_mute = cb;
+
+    /* Save the current flags so we can tell if they've changed */
+    flags = s->flags;
+
+    if (cb)
+        s->flags |= PA_SOURCE_HW_MUTE_CTRL;
+    else
+        s->flags &= ~PA_SOURCE_HW_MUTE_CTRL;
+
+    /* If the flags have changed after init, let any clients know via a change event */
+    if (s->state != PA_SOURCE_INIT && flags != s->flags)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+static void enable_flat_volume(pa_source *s, bool enable) {
+    pa_source_flags_t flags;
+
+    pa_assert(s);
+
+    /* Always follow the overall user preference here */
+    enable = enable && s->core->flat_volumes;
+
+    /* Save the current flags so we can tell if they've changed */
+    flags = s->flags;
+
+    if (enable)
+        s->flags |= PA_SOURCE_FLAT_VOLUME;
+    else
+        s->flags &= ~PA_SOURCE_FLAT_VOLUME;
+
+    /* If the flags have changed after init, let any clients know via a change event */
+    if (s->state != PA_SOURCE_INIT && flags != s->flags)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+void pa_source_enable_decibel_volume(pa_source *s, bool enable) {
+    pa_source_flags_t flags;
+
+    pa_assert(s);
+
+    /* Save the current flags so we can tell if they've changed */
+    flags = s->flags;
+
+    if (enable) {
+        s->flags |= PA_SOURCE_DECIBEL_VOLUME;
+        enable_flat_volume(s, true);
+    } else {
+        s->flags &= ~PA_SOURCE_DECIBEL_VOLUME;
+        enable_flat_volume(s, false);
+    }
+
+    /* If the flags have changed after init, let any clients know via a change event */
+    if (s->state != PA_SOURCE_INIT && flags != s->flags)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main context */
+void pa_source_put(pa_source *s) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    pa_assert(s->state == PA_SOURCE_INIT);
+    pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) || pa_source_is_filter(s));
+
+    /* The following fields must be initialized properly when calling _put() */
+    pa_assert(s->asyncmsgq);
+    pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
+
+    /* Generally, flags should be initialized via pa_source_new(). As a
+     * special exception we allow some volume related flags to be set
+     * between _new() and _put() by the callback setter functions above.
+     *
+     * Thus we implement a couple safeguards here which ensure the above
+     * setters were used (or at least the implementor made manual changes
+     * in a compatible way).
+     *
+     * Note: All of these flags set here can change over the life time
+     * of the source. */
+    pa_assert(!(s->flags & PA_SOURCE_HW_VOLUME_CTRL) || s->set_volume);
+    pa_assert(!(s->flags & PA_SOURCE_DEFERRED_VOLUME) || s->write_volume);
+    pa_assert(!(s->flags & PA_SOURCE_HW_MUTE_CTRL) || s->set_mute);
+
+    /* XXX: Currently decibel volume is disabled for all sources that use volume
+     * sharing. When the master source supports decibel volume, it would be good
+     * to have the flag also in the filter source, but currently we don't do that
+     * so that the flags of the filter source never change when it's moved from
+     * a master source to another. One solution for this problem would be to
+     * remove user-visible volume altogether from filter sources when volume
+     * sharing is used, but the current approach was easier to implement... */
+    /* We always support decibel volumes in software, otherwise we leave it to
+     * the source implementor to set this flag as needed.
+     *
+     * Note: This flag can also change over the life time of the source. */
+    if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL) && !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+        pa_source_enable_decibel_volume(s, true);
+        s->soft_volume = s->reference_volume;
+    }
+
+    /* If the source implementor support DB volumes by itself, we should always
+     * try and enable flat volumes too */
+    if ((s->flags & PA_SOURCE_DECIBEL_VOLUME))
+        enable_flat_volume(s, true);
+
+    if (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) {
+        pa_source *root_source = pa_source_get_master(s);
+
+        pa_assert(PA_LIKELY(root_source));
+
+        s->reference_volume = root_source->reference_volume;
+        pa_cvolume_remap(&s->reference_volume, &root_source->channel_map, &s->channel_map);
+
+        s->real_volume = root_source->real_volume;
+        pa_cvolume_remap(&s->real_volume, &root_source->channel_map, &s->channel_map);
+    } else
+        /* We assume that if the sink implementor changed the default
+         * volume he did so in real_volume, because that is the usual
+         * place where he is supposed to place his changes.  */
+        s->reference_volume = s->real_volume;
+
+    s->thread_info.soft_volume = s->soft_volume;
+    s->thread_info.soft_muted = s->muted;
+    pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
+
+    pa_assert((s->flags & PA_SOURCE_HW_VOLUME_CTRL)
+              || (s->base_volume == PA_VOLUME_NORM
+                  && ((s->flags & PA_SOURCE_DECIBEL_VOLUME || (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)))));
+    pa_assert(!(s->flags & PA_SOURCE_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
+    pa_assert(!(s->flags & PA_SOURCE_DYNAMIC_LATENCY) == !(s->thread_info.fixed_latency == 0));
+
+    if (s->suspend_cause)
+        pa_assert_se(source_set_state(s, PA_SOURCE_SUSPENDED) == 0);
+    else
+        pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s);
+
+    /* This function must be called after the PA_CORE_HOOK_SOURCE_PUT hook,
+     * because module-switch-on-connect needs to know the old default source */
+    pa_core_update_default_source(s->core);
+}
+
+/* Called from main context */
+void pa_source_unlink(pa_source *s) {
+    bool linked;
+    pa_source_output *o, PA_UNUSED *j = NULL;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    /* See pa_sink_unlink() for a couple of comments how this function
+     * works. */
+
+    if (s->unlink_requested)
+        return;
+
+    s->unlink_requested = true;
+
+    linked = PA_SOURCE_IS_LINKED(s->state);
+
+    if (linked)
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s);
+
+    if (s->state != PA_SOURCE_UNLINKED)
+        pa_namereg_unregister(s->core, s->name);
+    pa_idxset_remove_by_data(s->core->sources, s, NULL);
+
+    pa_core_update_default_source(s->core);
+
+    if (s->card)
+        pa_idxset_remove_by_data(s->card->sources, s, NULL);
+
+    while ((o = pa_idxset_first(s->outputs, NULL))) {
+        pa_assert(o != j);
+        pa_source_output_kill(o);
+        j = o;
+    }
+
+    if (linked)
+        source_set_state(s, PA_SOURCE_UNLINKED);
+    else
+        s->state = PA_SOURCE_UNLINKED;
+
+    reset_callbacks(s);
+
+    if (linked) {
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], s);
+    }
+}
+
+/* Called from main context */
+static void source_free(pa_object *o) {
+    pa_source *s = PA_SOURCE(o);
+
+    pa_assert(s);
+    pa_assert_ctl_context();
+    pa_assert(pa_source_refcnt(s) == 0);
+    pa_assert(!PA_SOURCE_IS_LINKED(s->state));
+
+    pa_log_info("Freeing source %u \"%s\"", s->index, s->name);
+
+    pa_source_volume_change_flush(s);
+
+    pa_idxset_free(s->outputs, NULL);
+    pa_hashmap_free(s->thread_info.outputs);
+
+    if (s->silence.memblock)
+        pa_memblock_unref(s->silence.memblock);
+
+    pa_xfree(s->name);
+    pa_xfree(s->driver);
+
+    if (s->proplist)
+        pa_proplist_free(s->proplist);
+
+    if (s->ports)
+        pa_hashmap_free(s->ports);
+
+    pa_xfree(s);
+}
+
+/* Called from main context, and not while the IO thread is active, please */
+void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    s->asyncmsgq = q;
+}
+
+/* Called from main context, and not while the IO thread is active, please */
+void pa_source_update_flags(pa_source *s, pa_source_flags_t mask, pa_source_flags_t value) {
+    pa_source_flags_t old_flags;
+    pa_source_output *output;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    /* For now, allow only a minimal set of flags to be changed. */
+    pa_assert((mask & ~(PA_SOURCE_DYNAMIC_LATENCY|PA_SOURCE_LATENCY)) == 0);
+
+    old_flags = s->flags;
+    s->flags = (s->flags & ~mask) | (value & mask);
+
+    if (s->flags == old_flags)
+        return;
+
+    if ((s->flags & PA_SOURCE_LATENCY) != (old_flags & PA_SOURCE_LATENCY))
+        pa_log_debug("Source %s: LATENCY flag %s.", s->name, (s->flags & PA_SOURCE_LATENCY) ? "enabled" : "disabled");
+
+    if ((s->flags & PA_SOURCE_DYNAMIC_LATENCY) != (old_flags & PA_SOURCE_DYNAMIC_LATENCY))
+        pa_log_debug("Source %s: DYNAMIC_LATENCY flag %s.",
+                     s->name, (s->flags & PA_SOURCE_DYNAMIC_LATENCY) ? "enabled" : "disabled");
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_FLAGS_CHANGED], s);
+
+    PA_IDXSET_FOREACH(output, s->outputs, idx) {
+        if (output->destination_source)
+            pa_source_update_flags(output->destination_source, mask, value);
+    }
+}
+
+/* Called from IO context, or before _put() from main context */
+void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+
+    s->thread_info.rtpoll = p;
+}
+
+/* Called from main context */
+int pa_source_update_status(pa_source*s) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (s->state == PA_SOURCE_SUSPENDED)
+        return 0;
+
+    return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
+}
+
+/* Called from any context - must be threadsafe */
+void pa_source_set_mixer_dirty(pa_source *s, bool is_dirty) {
+    pa_atomic_store(&s->mixer_dirty, is_dirty ? 1 : 0);
+}
+
+/* Called from main context */
+int pa_source_suspend(pa_source *s, bool suspend, pa_suspend_cause_t cause) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(cause != 0);
+
+    if (s->monitor_of && cause != PA_SUSPEND_PASSTHROUGH)
+        return -PA_ERR_NOTSUPPORTED;
+
+    if (suspend)
+        s->suspend_cause |= cause;
+    else
+        s->suspend_cause &= ~cause;
+
+    if (!(s->suspend_cause & PA_SUSPEND_SESSION) && (pa_atomic_load(&s->mixer_dirty) != 0)) {
+        /* This might look racy but isn't: If somebody sets mixer_dirty exactly here,
+           it'll be handled just fine. */
+        pa_source_set_mixer_dirty(s, false);
+        pa_log_debug("Mixer is now accessible. Updating alsa mixer settings.");
+        if (s->active_port && s->set_port) {
+            if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
+                struct source_message_set_port msg = { .port = s->active_port, .ret = 0 };
+                pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
+            }
+            else
+                s->set_port(s, s->active_port);
+        }
+        else {
+            if (s->set_mute)
+                s->set_mute(s);
+            if (s->set_volume)
+                s->set_volume(s);
+        }
+    }
+
+    if ((pa_source_get_state(s) == PA_SOURCE_SUSPENDED) == !!s->suspend_cause)
+        return 0;
+
+    pa_log_debug("Suspend cause of source %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming");
+
+    if (s->suspend_cause)
+        return source_set_state(s, PA_SOURCE_SUSPENDED);
+    else
+        return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
+}
+
+/* Called from main context */
+int pa_source_sync_suspend(pa_source *s) {
+    pa_sink_state_t state;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(s->monitor_of);
+
+    state = pa_sink_get_state(s->monitor_of);
+
+    if (state == PA_SINK_SUSPENDED)
+        return source_set_state(s, PA_SOURCE_SUSPENDED);
+
+    pa_assert(PA_SINK_IS_OPENED(state));
+
+    return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
+}
+
+/* Called from main context */
+pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q) {
+    pa_source_output *o, *n;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (!q)
+        q = pa_queue_new();
+
+    for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) {
+        n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx));
+
+        pa_source_output_ref(o);
+
+        if (pa_source_output_start_move(o) >= 0)
+            pa_queue_push(q, o);
+        else
+            pa_source_output_unref(o);
+    }
+
+    return q;
+}
+
+/* Called from main context */
+void pa_source_move_all_finish(pa_source *s, pa_queue *q, bool save) {
+    pa_source_output *o;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(q);
+
+    while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
+        if (pa_source_output_finish_move(o, s, save) < 0)
+            pa_source_output_fail_move(o);
+
+        pa_source_output_unref(o);
+    }
+
+    pa_queue_free(q, NULL);
+}
+
+/* Called from main context */
+void pa_source_move_all_fail(pa_queue *q) {
+    pa_source_output *o;
+
+    pa_assert_ctl_context();
+    pa_assert(q);
+
+    while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
+        pa_source_output_fail_move(o);
+        pa_source_output_unref(o);
+    }
+
+    pa_queue_free(q, NULL);
+}
+
+/* Called from IO thread context */
+void pa_source_process_rewind(pa_source *s, size_t nbytes) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+    if (nbytes <= 0)
+        return;
+
+    if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+        return;
+
+    pa_log_debug("Processing rewind...");
+
+    PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state) {
+        pa_source_output_assert_ref(o);
+        pa_source_output_process_rewind(o, nbytes);
+    }
+}
+
+/* Called from IO thread context */
+void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+    pa_assert(chunk);
+
+    if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+        return;
+
+    if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
+        pa_memchunk vchunk = *chunk;
+
+        pa_memblock_ref(vchunk.memblock);
+        pa_memchunk_make_writable(&vchunk, 0);
+
+        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume))
+            pa_silence_memchunk(&vchunk, &s->sample_spec);
+        else
+            pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume);
+
+        while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+            pa_source_output_assert_ref(o);
+
+            if (!o->thread_info.direct_on_input)
+                pa_source_output_push(o, &vchunk);
+        }
+
+        pa_memblock_unref(vchunk.memblock);
+    } else {
+
+        while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+            pa_source_output_assert_ref(o);
+
+            if (!o->thread_info.direct_on_input)
+                pa_source_output_push(o, chunk);
+        }
+    }
+}
+
+/* Called from IO thread context */
+void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk) {
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+    pa_source_output_assert_ref(o);
+    pa_assert(o->thread_info.direct_on_input);
+    pa_assert(chunk);
+
+    if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+        return;
+
+    if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
+        pa_memchunk vchunk = *chunk;
+
+        pa_memblock_ref(vchunk.memblock);
+        pa_memchunk_make_writable(&vchunk, 0);
+
+        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume))
+            pa_silence_memchunk(&vchunk, &s->sample_spec);
+        else
+            pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume);
+
+        pa_source_output_push(o, &vchunk);
+
+        pa_memblock_unref(vchunk.memblock);
+    } else
+        pa_source_output_push(o, chunk);
+}
+
+/* Called from main thread */
+int pa_source_update_rate(pa_source *s, uint32_t rate, bool passthrough) {
+    int ret;
+    uint32_t desired_rate;
+    uint32_t default_rate = s->default_sample_rate;
+    uint32_t alternate_rate = s->alternate_sample_rate;
+    bool default_rate_is_usable = false;
+    bool alternate_rate_is_usable = false;
+    bool avoid_resampling = s->core->avoid_resampling;
+
+    if (rate == s->sample_spec.rate)
+        return 0;
+
+    if (!s->update_rate && !s->monitor_of)
+        return -1;
+
+    if (PA_UNLIKELY(default_rate == alternate_rate && !passthrough && !avoid_resampling)) {
+        pa_log_debug("Default and alternate sample rates are the same, so there is no point in switching.");
+        return -1;
+    }
+
+    if (PA_SOURCE_IS_RUNNING(s->state)) {
+        pa_log_info("Cannot update rate, SOURCE_IS_RUNNING, will keep using %u Hz",
+                    s->sample_spec.rate);
+        return -1;
+    }
+
+    if (s->monitor_of) {
+        if (PA_SINK_IS_RUNNING(s->monitor_of->state)) {
+            pa_log_info("Cannot update rate, this is a monitor source and the sink is running.");
+            return -1;
+        }
+    }
+
+    if (PA_UNLIKELY(!pa_sample_rate_valid(rate)))
+        return -1;
+
+    if (passthrough) {
+        /* We have to try to use the source output rate */
+        desired_rate = rate;
+
+    } else if (avoid_resampling && (rate >= default_rate || rate >= alternate_rate)) {
+        /* We just try to set the source output's sample rate if it's not too low */
+        desired_rate = rate;
+
+    } else if (default_rate == rate || alternate_rate == rate) {
+        /* We can directly try to use this rate */
+        desired_rate = rate;
+
+    } else {
+        /* See if we can pick a rate that results in less resampling effort */
+        if (default_rate % 11025 == 0 && rate % 11025 == 0)
+            default_rate_is_usable = true;
+        if (default_rate % 4000 == 0 && rate % 4000 == 0)
+            default_rate_is_usable = true;
+        if (alternate_rate && alternate_rate % 11025 == 0 && rate % 11025 == 0)
+            alternate_rate_is_usable = true;
+        if (alternate_rate && alternate_rate % 4000 == 0 && rate % 4000 == 0)
+            alternate_rate_is_usable = true;
+
+        if (alternate_rate_is_usable && !default_rate_is_usable)
+            desired_rate = alternate_rate;
+        else
+            desired_rate = default_rate;
+    }
+
+    if (desired_rate == s->sample_spec.rate)
+        return -1;
+
+    if (!passthrough && pa_source_used_by(s) > 0)
+        return -1;
+
+    pa_log_debug("Suspending source %s due to changing the sample rate.", s->name);
+    pa_source_suspend(s, true, PA_SUSPEND_INTERNAL);
+
+    if (s->update_rate)
+        ret = s->update_rate(s, desired_rate);
+    else {
+        /* This is a monitor source. */
+
+        /* XXX: This code is written with non-passthrough streams in mind. I
+         * have no idea whether the behaviour with passthrough streams is
+         * sensible. */
+        if (!passthrough) {
+            uint32_t old_rate = s->sample_spec.rate;
+
+            s->sample_spec.rate = desired_rate;
+            ret = pa_sink_update_rate(s->monitor_of, desired_rate, false);
+
+            if (ret < 0) {
+                /* Changing the sink rate failed, roll back the old rate for
+                 * the monitor source. Why did we set the source rate before
+                 * calling pa_sink_update_rate(), you may ask. The reason is
+                 * that pa_sink_update_rate() tries to update the monitor
+                 * source rate, but we are already in the process of updating
+                 * the monitor source rate, so there's a risk of entering an
+                 * infinite loop. Setting the source rate before calling
+                 * pa_sink_update_rate() makes the rate == s->sample_spec.rate
+                 * check in the beginning of this function return early, so we
+                 * avoid looping. */
+                s->sample_spec.rate = old_rate;
+            }
+        } else
+            ret = -1;
+    }
+
+    if (ret >= 0) {
+        uint32_t idx;
+        pa_source_output *o;
+
+        PA_IDXSET_FOREACH(o, s->outputs, idx) {
+            if (o->state == PA_SOURCE_OUTPUT_CORKED)
+                pa_source_output_update_rate(o);
+        }
+
+        pa_log_info("Changed sampling rate successfully");
+    }
+
+    pa_source_suspend(s, false, PA_SUSPEND_INTERNAL);
+
+    return ret;
+}
+
+/* Called from main thread */
+pa_usec_t pa_source_get_latency(pa_source *s) {
+    int64_t usec;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (s->state == PA_SOURCE_SUSPENDED)
+        return 0;
+
+    if (!(s->flags & PA_SOURCE_LATENCY))
+        return 0;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
+
+    /* The return value is unsigned, so check that the offset can be added to usec without
+     * underflowing. */
+    if (-s->port_latency_offset <= usec)
+        usec += s->port_latency_offset;
+    else
+        usec = 0;
+
+    return (pa_usec_t)usec;
+}
+
+/* Called from IO thread */
+int64_t pa_source_get_latency_within_thread(pa_source *s, bool allow_negative) {
+    int64_t usec = 0;
+    pa_msgobject *o;
+
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+    /* The returned value is supposed to be in the time domain of the sound card! */
+
+    if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+        return 0;
+
+    if (!(s->flags & PA_SOURCE_LATENCY))
+        return 0;
+
+    o = PA_MSGOBJECT(s);
+
+    /* FIXME: We probably should make this a proper vtable callback instead of going through process_msg() */
+
+    o->process_msg(o, PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL);
+
+    /* If allow_negative is false, the call should only return positive values, */
+    usec += s->thread_info.port_latency_offset;
+    if (!allow_negative && usec < 0)
+        usec = 0;
+
+    return usec;
+}
+
+/* Called from the main thread (and also from the IO thread while the main
+ * thread is waiting).
+ *
+ * When a source uses volume sharing, it never has the PA_SOURCE_FLAT_VOLUME flag
+ * set. Instead, flat volume mode is detected by checking whether the root source
+ * has the flag set. */
+bool pa_source_flat_volume_enabled(pa_source *s) {
+    pa_source_assert_ref(s);
+
+    s = pa_source_get_master(s);
+
+    if (PA_LIKELY(s))
+        return (s->flags & PA_SOURCE_FLAT_VOLUME);
+    else
+        return false;
+}
+
+/* Called from the main thread (and also from the IO thread while the main
+ * thread is waiting). */
+pa_source *pa_source_get_master(pa_source *s) {
+    pa_source_assert_ref(s);
+
+    while (s && (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+        if (PA_UNLIKELY(!s->output_from_master))
+            return NULL;
+
+        s = s->output_from_master->source;
+    }
+
+    return s;
+}
+
+/* Called from main context */
+bool pa_source_is_filter(pa_source *s) {
+    pa_source_assert_ref(s);
+
+    return (s->output_from_master != NULL);
+}
+
+/* Called from main context */
+bool pa_source_is_passthrough(pa_source *s) {
+
+    pa_source_assert_ref(s);
+
+    /* NB Currently only monitor sources support passthrough mode */
+    return (s->monitor_of && pa_sink_is_passthrough(s->monitor_of));
+}
+
+/* Called from main context */
+void pa_source_enter_passthrough(pa_source *s) {
+    pa_cvolume volume;
+
+    /* set the volume to NORM */
+    s->saved_volume = *pa_source_get_volume(s, true);
+    s->saved_save_volume = s->save_volume;
+
+    pa_cvolume_set(&volume, s->sample_spec.channels, PA_MIN(s->base_volume, PA_VOLUME_NORM));
+    pa_source_set_volume(s, &volume, true, false);
+}
+
+/* Called from main context */
+void pa_source_leave_passthrough(pa_source *s) {
+    /* Restore source volume to what it was before we entered passthrough mode */
+    pa_source_set_volume(s, &s->saved_volume, true, s->saved_save_volume);
+
+    pa_cvolume_init(&s->saved_volume);
+    s->saved_save_volume = false;
+}
+
+/* Called from main context. */
+static void compute_reference_ratio(pa_source_output *o) {
+    unsigned c = 0;
+    pa_cvolume remapped;
+    pa_cvolume ratio;
+
+    pa_assert(o);
+    pa_assert(pa_source_flat_volume_enabled(o->source));
+
+    /*
+     * Calculates the reference ratio from the source's reference
+     * volume. This basically calculates:
+     *
+     * o->reference_ratio = o->volume / o->source->reference_volume
+     */
+
+    remapped = o->source->reference_volume;
+    pa_cvolume_remap(&remapped, &o->source->channel_map, &o->channel_map);
+
+    ratio = o->reference_ratio;
+
+    for (c = 0; c < o->sample_spec.channels; c++) {
+
+        /* We don't update when the source volume is 0 anyway */
+        if (remapped.values[c] <= PA_VOLUME_MUTED)
+            continue;
+
+        /* Don't update the reference ratio unless necessary */
+        if (pa_sw_volume_multiply(
+                    ratio.values[c],
+                    remapped.values[c]) == o->volume.values[c])
+            continue;
+
+        ratio.values[c] = pa_sw_volume_divide(
+                o->volume.values[c],
+                remapped.values[c]);
+    }
+
+    pa_source_output_set_reference_ratio(o, &ratio);
+}
+
+/* Called from main context. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void compute_reference_ratios(pa_source *s) {
+    uint32_t idx;
+    pa_source_output *o;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(pa_source_flat_volume_enabled(s));
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        compute_reference_ratio(o);
+
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)
+                && PA_SOURCE_IS_LINKED(o->destination_source->state))
+            compute_reference_ratios(o->destination_source);
+    }
+}
+
+/* Called from main context. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void compute_real_ratios(pa_source *s) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(pa_source_flat_volume_enabled(s));
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        unsigned c;
+        pa_cvolume remapped;
+
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+            /* The origin source uses volume sharing, so this input's real ratio
+             * is handled as a special case - the real ratio must be 0 dB, and
+             * as a result i->soft_volume must equal i->volume_factor. */
+            pa_cvolume_reset(&o->real_ratio, o->real_ratio.channels);
+            o->soft_volume = o->volume_factor;
+
+            if (PA_SOURCE_IS_LINKED(o->destination_source->state))
+                compute_real_ratios(o->destination_source);
+
+            continue;
+        }
+
+        /*
+         * This basically calculates:
+         *
+         * i->real_ratio := i->volume / s->real_volume
+         * i->soft_volume := i->real_ratio * i->volume_factor
+         */
+
+        remapped = s->real_volume;
+        pa_cvolume_remap(&remapped, &s->channel_map, &o->channel_map);
+
+        o->real_ratio.channels = o->sample_spec.channels;
+        o->soft_volume.channels = o->sample_spec.channels;
+
+        for (c = 0; c < o->sample_spec.channels; c++) {
+
+            if (remapped.values[c] <= PA_VOLUME_MUTED) {
+                /* We leave o->real_ratio untouched */
+                o->soft_volume.values[c] = PA_VOLUME_MUTED;
+                continue;
+            }
+
+            /* Don't lose accuracy unless necessary */
+            if (pa_sw_volume_multiply(
+                        o->real_ratio.values[c],
+                        remapped.values[c]) != o->volume.values[c])
+
+                o->real_ratio.values[c] = pa_sw_volume_divide(
+                        o->volume.values[c],
+                        remapped.values[c]);
+
+            o->soft_volume.values[c] = pa_sw_volume_multiply(
+                    o->real_ratio.values[c],
+                    o->volume_factor.values[c]);
+        }
+
+        /* We don't copy the soft_volume to the thread_info data
+         * here. That must be done by the caller */
+    }
+}
+
+static pa_cvolume *cvolume_remap_minimal_impact(
+        pa_cvolume *v,
+        const pa_cvolume *template,
+        const pa_channel_map *from,
+        const pa_channel_map *to) {
+
+    pa_cvolume t;
+
+    pa_assert(v);
+    pa_assert(template);
+    pa_assert(from);
+    pa_assert(to);
+    pa_assert(pa_cvolume_compatible_with_channel_map(v, from));
+    pa_assert(pa_cvolume_compatible_with_channel_map(template, to));
+
+    /* Much like pa_cvolume_remap(), but tries to minimize impact when
+     * mapping from source output to source volumes:
+     *
+     * If template is a possible remapping from v it is used instead
+     * of remapping anew.
+     *
+     * If the channel maps don't match we set an all-channel volume on
+     * the source to ensure that changing a volume on one stream has no
+     * effect that cannot be compensated for in another stream that
+     * does not have the same channel map as the source. */
+
+    if (pa_channel_map_equal(from, to))
+        return v;
+
+    t = *template;
+    if (pa_cvolume_equal(pa_cvolume_remap(&t, to, from), v)) {
+        *v = *template;
+        return v;
+    }
+
+    pa_cvolume_set(v, to->channels, pa_cvolume_max(v));
+    return v;
+}
+
+/* Called from main thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void get_maximum_output_volume(pa_source *s, pa_cvolume *max_volume, const pa_channel_map *channel_map) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert(max_volume);
+    pa_assert(channel_map);
+    pa_assert(pa_source_flat_volume_enabled(s));
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        pa_cvolume remapped;
+
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+            if (PA_SOURCE_IS_LINKED(o->destination_source->state))
+                get_maximum_output_volume(o->destination_source, max_volume, channel_map);
+
+            /* Ignore this output. The origin source uses volume sharing, so this
+             * output's volume will be set to be equal to the root source's real
+             * volume. Obviously this output's current volume must not then
+             * affect what the root source's real volume will be. */
+            continue;
+        }
+
+        remapped = o->volume;
+        cvolume_remap_minimal_impact(&remapped, max_volume, &o->channel_map, channel_map);
+        pa_cvolume_merge(max_volume, max_volume, &remapped);
+    }
+}
+
+/* Called from main thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static bool has_outputs(pa_source *s) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        if (!o->destination_source || !(o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) || has_outputs(o->destination_source))
+            return true;
+    }
+
+    return false;
+}
+
+/* Called from main thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void update_real_volume(pa_source *s, const pa_cvolume *new_volume, pa_channel_map *channel_map) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert(new_volume);
+    pa_assert(channel_map);
+
+    s->real_volume = *new_volume;
+    pa_cvolume_remap(&s->real_volume, channel_map, &s->channel_map);
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+            if (pa_source_flat_volume_enabled(s)) {
+                pa_cvolume new_output_volume;
+
+                /* Follow the root source's real volume. */
+                new_output_volume = *new_volume;
+                pa_cvolume_remap(&new_output_volume, channel_map, &o->channel_map);
+                pa_source_output_set_volume_direct(o, &new_output_volume);
+                compute_reference_ratio(o);
+            }
+
+            if (PA_SOURCE_IS_LINKED(o->destination_source->state))
+                update_real_volume(o->destination_source, new_volume, channel_map);
+        }
+    }
+}
+
+/* Called from main thread. Only called for the root source in shared volume
+ * cases. */
+static void compute_real_volume(pa_source *s) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(pa_source_flat_volume_enabled(s));
+    pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
+
+    /* This determines the maximum volume of all streams and sets
+     * s->real_volume accordingly. */
+
+    if (!has_outputs(s)) {
+        /* In the special case that we have no source outputs we leave the
+         * volume unmodified. */
+        update_real_volume(s, &s->reference_volume, &s->channel_map);
+        return;
+    }
+
+    pa_cvolume_mute(&s->real_volume, s->channel_map.channels);
+
+    /* First let's determine the new maximum volume of all outputs
+     * connected to this source */
+    get_maximum_output_volume(s, &s->real_volume, &s->channel_map);
+    update_real_volume(s, &s->real_volume, &s->channel_map);
+
+    /* Then, let's update the real ratios/soft volumes of all outputs
+     * connected to this source */
+    compute_real_ratios(s);
+}
+
+/* Called from main thread. Only called for the root source in shared volume
+ * cases, except for internal recursive calls. */
+static void propagate_reference_volume(pa_source *s) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(pa_source_flat_volume_enabled(s));
+
+    /* This is called whenever the source volume changes that is not
+     * caused by a source output volume change. We need to fix up the
+     * source output volumes accordingly */
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        pa_cvolume new_volume;
+
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+            if (PA_SOURCE_IS_LINKED(o->destination_source->state))
+                propagate_reference_volume(o->destination_source);
+
+            /* Since the origin source uses volume sharing, this output's volume
+             * needs to be updated to match the root source's real volume, but
+             * that will be done later in update_real_volume(). */
+            continue;
+        }
+
+        /* This basically calculates:
+         *
+         * o->volume := o->reference_volume * o->reference_ratio  */
+
+        new_volume = s->reference_volume;
+        pa_cvolume_remap(&new_volume, &s->channel_map, &o->channel_map);
+        pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio);
+        pa_source_output_set_volume_direct(o, &new_volume);
+    }
+}
+
+/* Called from main thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. The return value indicates
+ * whether any reference volume actually changed. */
+static bool update_reference_volume(pa_source *s, const pa_cvolume *v, const pa_channel_map *channel_map, bool save) {
+    pa_cvolume volume;
+    bool reference_volume_changed;
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(v);
+    pa_assert(channel_map);
+    pa_assert(pa_cvolume_valid(v));
+
+    volume = *v;
+    pa_cvolume_remap(&volume, channel_map, &s->channel_map);
+
+    reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume);
+    pa_source_set_reference_volume_direct(s, &volume);
+
+    s->save_volume = (!reference_volume_changed && s->save_volume) || save;
+
+    if (!reference_volume_changed && !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+        /* If the root source's volume doesn't change, then there can't be any
+         * changes in the other source in the source tree either.
+         *
+         * It's probably theoretically possible that even if the root source's
+         * volume changes slightly, some filter source doesn't change its volume
+         * due to rounding errors. If that happens, we still want to propagate
+         * the changed root source volume to the sources connected to the
+         * intermediate source that didn't change its volume. This theoretical
+         * possibility is the reason why we have that !(s->flags &
+         * PA_SOURCE_SHARE_VOLUME_WITH_MASTER) condition. Probably nobody would
+         * notice even if we returned here false always if
+         * reference_volume_changed is false. */
+        return false;
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)
+                && PA_SOURCE_IS_LINKED(o->destination_source->state))
+            update_reference_volume(o->destination_source, v, channel_map, false);
+    }
+
+    return true;
+}
+
+/* Called from main thread */
+void pa_source_set_volume(
+        pa_source *s,
+        const pa_cvolume *volume,
+        bool send_msg,
+        bool save) {
+
+    pa_cvolume new_reference_volume, root_real_volume;
+    pa_source *root_source;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(!volume || pa_cvolume_valid(volume));
+    pa_assert(volume || pa_source_flat_volume_enabled(s));
+    pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
+
+    /* make sure we don't change the volume in PASSTHROUGH mode ...
+     * ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */
+    if (pa_source_is_passthrough(s) && (!volume || !pa_cvolume_is_norm(volume))) {
+        pa_log_warn("Cannot change volume, source is monitor of a PASSTHROUGH sink");
+        return;
+    }
+
+    /* In case of volume sharing, the volume is set for the root source first,
+     * from which it's then propagated to the sharing sources. */
+    root_source = pa_source_get_master(s);
+
+    if (PA_UNLIKELY(!root_source))
+        return;
+
+    /* As a special exception we accept mono volumes on all sources --
+     * even on those with more complex channel maps */
+
+    if (volume) {
+        if (pa_cvolume_compatible(volume, &s->sample_spec))
+            new_reference_volume = *volume;
+        else {
+            new_reference_volume = s->reference_volume;
+            pa_cvolume_scale(&new_reference_volume, pa_cvolume_max(volume));
+        }
+
+        pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_source->channel_map);
+
+        if (update_reference_volume(root_source, &new_reference_volume, &root_source->channel_map, save)) {
+            if (pa_source_flat_volume_enabled(root_source)) {
+                /* OK, propagate this volume change back to the outputs */
+                propagate_reference_volume(root_source);
+
+                /* And now recalculate the real volume */
+                compute_real_volume(root_source);
+            } else
+                update_real_volume(root_source, &root_source->reference_volume, &root_source->channel_map);
+        }
+
+    } else {
+        /* If volume is NULL we synchronize the source's real and
+         * reference volumes with the stream volumes. */
+
+        pa_assert(pa_source_flat_volume_enabled(root_source));
+
+        /* Ok, let's determine the new real volume */
+        compute_real_volume(root_source);
+
+        /* To propagate the reference volume from the filter to the root source,
+         * we first take the real volume from the root source and remap it to
+         * match the filter. Then, we merge in the reference volume from the
+         * filter on top of this, and remap it back to the root source channel
+         * count and map */
+        root_real_volume = root_source->real_volume;
+        /* First we remap root's real volume to filter channel count and map if needed */
+        if (s != root_source && !pa_channel_map_equal(&s->channel_map, &root_source->channel_map))
+            pa_cvolume_remap(&root_real_volume, &root_source->channel_map, &s->channel_map);
+        /* Then let's 'push' the reference volume if necessary */
+        pa_cvolume_merge(&new_reference_volume, &s->reference_volume, &root_real_volume);
+        /* If the source and its root don't have the same number of channels, we need to remap back */
+        if (s != root_source && !pa_channel_map_equal(&s->channel_map, &root_source->channel_map))
+            pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_source->channel_map);
+
+        update_reference_volume(root_source, &new_reference_volume, &root_source->channel_map, save);
+
+        /* Now that the reference volume is updated, we can update the streams'
+         * reference ratios. */
+        compute_reference_ratios(root_source);
+    }
+
+    if (root_source->set_volume) {
+        /* If we have a function set_volume(), then we do not apply a
+         * soft volume by default. However, set_volume() is free to
+         * apply one to root_source->soft_volume */
+
+        pa_cvolume_reset(&root_source->soft_volume, root_source->sample_spec.channels);
+        if (!(root_source->flags & PA_SOURCE_DEFERRED_VOLUME))
+            root_source->set_volume(root_source);
+
+    } else
+        /* If we have no function set_volume(), then the soft volume
+         * becomes the real volume */
+        root_source->soft_volume = root_source->real_volume;
+
+    /* This tells the source that soft volume and/or real volume changed */
+    if (send_msg)
+        pa_assert_se(pa_asyncmsgq_send(root_source->asyncmsgq, PA_MSGOBJECT(root_source), PA_SOURCE_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0);
+}
+
+/* Called from the io thread if sync volume is used, otherwise from the main thread.
+ * Only to be called by source implementor */
+void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume) {
+
+    pa_source_assert_ref(s);
+    pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
+
+    if (s->flags & PA_SOURCE_DEFERRED_VOLUME)
+        pa_source_assert_io_context(s);
+    else
+        pa_assert_ctl_context();
+
+    if (!volume)
+        pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+    else
+        s->soft_volume = *volume;
+
+    if (PA_SOURCE_IS_LINKED(s->state) && !(s->flags & PA_SOURCE_DEFERRED_VOLUME))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
+    else
+        s->thread_info.soft_volume = s->soft_volume;
+}
+
+/* Called from the main thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void propagate_real_volume(pa_source *s, const pa_cvolume *old_real_volume) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert(old_real_volume);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    /* This is called when the hardware's real volume changes due to
+     * some external event. We copy the real volume into our
+     * reference volume and then rebuild the stream volumes based on
+     * i->real_ratio which should stay fixed. */
+
+    if (!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+        if (pa_cvolume_equal(old_real_volume, &s->real_volume))
+            return;
+
+        /* 1. Make the real volume the reference volume */
+        update_reference_volume(s, &s->real_volume, &s->channel_map, true);
+    }
+
+    if (pa_source_flat_volume_enabled(s)) {
+        PA_IDXSET_FOREACH(o, s->outputs, idx) {
+            pa_cvolume new_volume;
+
+            /* 2. Since the source's reference and real volumes are equal
+             * now our ratios should be too. */
+            pa_source_output_set_reference_ratio(o, &o->real_ratio);
+
+            /* 3. Recalculate the new stream reference volume based on the
+             * reference ratio and the sink's reference volume.
+             *
+             * This basically calculates:
+             *
+             * o->volume = s->reference_volume * o->reference_ratio
+             *
+             * This is identical to propagate_reference_volume() */
+            new_volume = s->reference_volume;
+            pa_cvolume_remap(&new_volume, &s->channel_map, &o->channel_map);
+            pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio);
+            pa_source_output_set_volume_direct(o, &new_volume);
+
+            if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)
+                    && PA_SOURCE_IS_LINKED(o->destination_source->state))
+                propagate_real_volume(o->destination_source, old_real_volume);
+        }
+    }
+
+    /* Something got changed in the hardware. It probably makes sense
+     * to save changed hw settings given that hw volume changes not
+     * triggered by PA are almost certainly done by the user. */
+    if (!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+        s->save_volume = true;
+}
+
+/* Called from io thread */
+void pa_source_update_volume_and_mute(pa_source *s) {
+    pa_assert(s);
+    pa_source_assert_io_context(s);
+
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE, NULL, 0, NULL, NULL);
+}
+
+/* Called from main thread */
+const pa_cvolume *pa_source_get_volume(pa_source *s, bool force_refresh) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (s->refresh_volume || force_refresh) {
+        struct pa_cvolume old_real_volume;
+
+        pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
+
+        old_real_volume = s->real_volume;
+
+        if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->get_volume)
+            s->get_volume(s);
+
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
+
+        update_real_volume(s, &s->real_volume, &s->channel_map);
+        propagate_real_volume(s, &old_real_volume);
+    }
+
+    return &s->reference_volume;
+}
+
+/* Called from main thread. In volume sharing cases, only the root source may
+ * call this. */
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_real_volume) {
+    pa_cvolume old_real_volume;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
+
+    /* The source implementor may call this if the volume changed to make sure everyone is notified */
+
+    old_real_volume = s->real_volume;
+    update_real_volume(s, new_real_volume, &s->channel_map);
+    propagate_real_volume(s, &old_real_volume);
+}
+
+/* Called from main thread */
+void pa_source_set_mute(pa_source *s, bool mute, bool save) {
+    bool old_muted;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    old_muted = s->muted;
+
+    if (mute == old_muted) {
+        s->save_muted |= save;
+        return;
+    }
+
+    s->muted = mute;
+    s->save_muted = save;
+
+    if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute) {
+        s->set_mute_in_progress = true;
+        s->set_mute(s);
+        s->set_mute_in_progress = false;
+    }
+
+    if (!PA_SOURCE_IS_LINKED(s->state))
+        return;
+
+    pa_log_debug("The mute of source %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED], s);
+}
+
+/* Called from main thread */
+bool pa_source_get_mute(pa_source *s, bool force_refresh) {
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if ((s->refresh_muted || force_refresh) && s->get_mute) {
+        bool mute;
+
+        if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
+            if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &mute, 0, NULL) >= 0)
+                pa_source_mute_changed(s, mute);
+        } else {
+            if (s->get_mute(s, &mute) >= 0)
+                pa_source_mute_changed(s, mute);
+        }
+    }
+
+    return s->muted;
+}
+
+/* Called from main thread */
+void pa_source_mute_changed(pa_source *s, bool new_muted) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (s->set_mute_in_progress)
+        return;
+
+    /* pa_source_set_mute() does this same check, so this may appear redundant,
+     * but we must have this here also, because the save parameter of
+     * pa_source_set_mute() would otherwise have unintended side effects
+     * (saving the mute state when it shouldn't be saved). */
+    if (new_muted == s->muted)
+        return;
+
+    pa_source_set_mute(s, new_muted, true);
+}
+
+/* Called from main thread */
+bool pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (p)
+        pa_proplist_update(s->proplist, mode, p);
+
+    if (PA_SOURCE_IS_LINKED(s->state)) {
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
+    return true;
+}
+
+/* Called from main thread */
+/* FIXME -- this should be dropped and be merged into pa_source_update_proplist() */
+void pa_source_set_description(pa_source *s, const char *description) {
+    const char *old;
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
+        return;
+
+    old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+    if (old && description && pa_streq(old, description))
+        return;
+
+    if (description)
+        pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
+    else
+        pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+    if (PA_SOURCE_IS_LINKED(s->state)) {
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
+    }
+}
+
+/* Called from main thread */
+unsigned pa_source_linked_by(pa_source *s) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    return pa_idxset_size(s->outputs);
+}
+
+/* Called from main thread */
+unsigned pa_source_used_by(pa_source *s) {
+    unsigned ret;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    ret = pa_idxset_size(s->outputs);
+    pa_assert(ret >= s->n_corked);
+
+    return ret - s->n_corked;
+}
+
+/* Called from main thread */
+unsigned pa_source_check_suspend(pa_source *s, pa_source_output *ignore) {
+    unsigned ret;
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (!PA_SOURCE_IS_LINKED(s->state))
+        return 0;
+
+    ret = 0;
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        pa_source_output_state_t st;
+
+        if (o == ignore)
+            continue;
+
+        st = pa_source_output_get_state(o);
+
+        /* We do not assert here. It is perfectly valid for a source output to
+         * be in the INIT state (i.e. created, marked done but not yet put)
+         * and we should not care if it's unlinked as it won't contribute
+         * towards our busy status.
+         */
+        if (!PA_SOURCE_OUTPUT_IS_LINKED(st))
+            continue;
+
+        if (st == PA_SOURCE_OUTPUT_CORKED)
+            continue;
+
+        if (o->flags & PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND)
+            continue;
+
+        ret ++;
+    }
+
+    return ret;
+}
+
+/* Called from the IO thread */
+static void sync_output_volumes_within_thread(pa_source *s) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+
+    PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state) {
+        if (pa_cvolume_equal(&o->thread_info.soft_volume, &o->soft_volume))
+            continue;
+
+        o->thread_info.soft_volume = o->soft_volume;
+        //pa_source_output_request_rewind(o, 0, true, false, false);
+    }
+}
+
+/* Called from the IO thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void set_shared_volume_within_thread(pa_source *s) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+
+    PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL);
+
+    PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state) {
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+            set_shared_volume_within_thread(o->destination_source);
+    }
+}
+
+/* Called from IO thread, except when it is not */
+int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_source *s = PA_SOURCE(object);
+    pa_source_assert_ref(s);
+
+    switch ((pa_source_message_t) code) {
+
+        case PA_SOURCE_MESSAGE_ADD_OUTPUT: {
+            pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+
+            pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o));
+
+            if (o->direct_on_input) {
+                o->thread_info.direct_on_input = o->direct_on_input;
+                pa_hashmap_put(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index), o);
+            }
+
+            pa_source_output_attach(o);
+
+            pa_source_output_set_state_within_thread(o, o->state);
+
+            if (o->thread_info.requested_source_latency != (pa_usec_t) -1)
+                pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency);
+
+            pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
+
+            /* We don't just invalidate the requested latency here,
+             * because if we are in a move we might need to fix up the
+             * requested latency. */
+            pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency);
+
+            /* In flat volume mode we need to update the volume as
+             * well */
+            return object->process_msg(object, PA_SOURCE_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
+        }
+
+        case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
+            pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+
+            pa_source_output_set_state_within_thread(o, o->state);
+
+            pa_source_output_detach(o);
+
+            if (o->thread_info.direct_on_input) {
+                pa_hashmap_remove(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index));
+                o->thread_info.direct_on_input = NULL;
+            }
+
+            pa_hashmap_remove_and_free(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index));
+            pa_source_invalidate_requested_latency(s, true);
+
+            /* In flat volume mode we need to update the volume as
+             * well */
+            return object->process_msg(object, PA_SOURCE_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
+        }
+
+        case PA_SOURCE_MESSAGE_SET_SHARED_VOLUME: {
+            pa_source *root_source = pa_source_get_master(s);
+
+            if (PA_LIKELY(root_source))
+                set_shared_volume_within_thread(root_source);
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED:
+
+            if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
+                s->set_volume(s);
+                pa_source_volume_change_push(s);
+            }
+            /* Fall through ... */
+
+        case PA_SOURCE_MESSAGE_SET_VOLUME:
+
+            if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
+                s->thread_info.soft_volume = s->soft_volume;
+            }
+
+            /* Fall through ... */
+
+        case PA_SOURCE_MESSAGE_SYNC_VOLUMES:
+            sync_output_volumes_within_thread(s);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_GET_VOLUME:
+
+            if ((s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->get_volume) {
+                s->get_volume(s);
+                pa_source_volume_change_flush(s);
+                pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
+            }
+
+            /* In case source implementor reset SW volume. */
+            if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
+                s->thread_info.soft_volume = s->soft_volume;
+            }
+
+            return 0;
+
+        case PA_SOURCE_MESSAGE_SET_MUTE:
+
+            if (s->thread_info.soft_muted != s->muted) {
+                s->thread_info.soft_muted = s->muted;
+            }
+
+            if (s->flags & PA_SOURCE_DEFERRED_VOLUME && s->set_mute)
+                s->set_mute(s);
+
+            return 0;
+
+        case PA_SOURCE_MESSAGE_GET_MUTE:
+
+            if (s->flags & PA_SOURCE_DEFERRED_VOLUME && s->get_mute)
+                return s->get_mute(s, userdata);
+
+            return 0;
+
+        case PA_SOURCE_MESSAGE_SET_STATE: {
+
+            bool suspend_change =
+                (s->thread_info.state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
+                (PA_SOURCE_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SOURCE_SUSPENDED);
+
+            s->thread_info.state = PA_PTR_TO_UINT(userdata);
+
+            if (suspend_change) {
+                pa_source_output *o;
+                void *state = NULL;
+
+                while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+                    if (o->suspend_within_thread)
+                        o->suspend_within_thread(o, s->thread_info.state == PA_SOURCE_SUSPENDED);
+            }
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY: {
+
+            pa_usec_t *usec = userdata;
+            *usec = pa_source_get_requested_latency_within_thread(s);
+
+            /* Yes, that's right, the IO thread will see -1 when no
+             * explicit requested latency is configured, the main
+             * thread will see max_latency */
+            if (*usec == (pa_usec_t) -1)
+                *usec = s->thread_info.max_latency;
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_SET_LATENCY_RANGE: {
+            pa_usec_t *r = userdata;
+
+            pa_source_set_latency_range_within_thread(s, r[0], r[1]);
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY_RANGE: {
+            pa_usec_t *r = userdata;
+
+            r[0] = s->thread_info.min_latency;
+            r[1] = s->thread_info.max_latency;
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_GET_FIXED_LATENCY:
+
+            *((pa_usec_t*) userdata) = s->thread_info.fixed_latency;
+            return 0;
+
+        case PA_SOURCE_MESSAGE_SET_FIXED_LATENCY:
+
+            pa_source_set_fixed_latency_within_thread(s, (pa_usec_t) offset);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_GET_MAX_REWIND:
+
+            *((size_t*) userdata) = s->thread_info.max_rewind;
+            return 0;
+
+        case PA_SOURCE_MESSAGE_SET_MAX_REWIND:
+
+            pa_source_set_max_rewind_within_thread(s, (size_t) offset);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY:
+
+            if (s->monitor_of) {
+                *((int64_t*) userdata) = -pa_sink_get_latency_within_thread(s->monitor_of, true);
+                return 0;
+            }
+
+            /* Implementors need to overwrite this implementation! */
+            return -1;
+
+        case PA_SOURCE_MESSAGE_SET_PORT:
+
+            pa_assert(userdata);
+            if (s->set_port) {
+                struct source_message_set_port *msg_data = userdata;
+                msg_data->ret = s->set_port(s, msg_data->port);
+            }
+            return 0;
+
+        case PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE:
+            /* This message is sent from IO-thread and handled in main thread. */
+            pa_assert_ctl_context();
+
+            /* Make sure we're not messing with main thread when no longer linked */
+            if (!PA_SOURCE_IS_LINKED(s->state))
+                return 0;
+
+            pa_source_get_volume(s, true);
+            pa_source_get_mute(s, true);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_SET_PORT_LATENCY_OFFSET:
+            s->thread_info.port_latency_offset = offset;
+            return 0;
+
+        case PA_SOURCE_MESSAGE_MAX:
+            ;
+    }
+
+    return -1;
+}
+
+/* Called from main thread */
+int pa_source_suspend_all(pa_core *c, bool suspend, pa_suspend_cause_t cause) {
+    pa_source *source;
+    uint32_t idx;
+    int ret = 0;
+
+    pa_core_assert_ref(c);
+    pa_assert_ctl_context();
+    pa_assert(cause != 0);
+
+    for (source = PA_SOURCE(pa_idxset_first(c->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(c->sources, &idx))) {
+        int r;
+
+        if (source->monitor_of)
+            continue;
+
+        if ((r = pa_source_suspend(source, suspend, cause)) < 0)
+            ret = r;
+    }
+
+    return ret;
+}
+
+/* Called from IO thread */
+void pa_source_detach_within_thread(pa_source *s) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+    PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
+        pa_source_output_detach(o);
+}
+
+/* Called from IO thread */
+void pa_source_attach_within_thread(pa_source *s) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+    PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
+        pa_source_output_attach(o);
+}
+
+/* Called from IO thread */
+pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
+    pa_usec_t result = (pa_usec_t) -1;
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+
+    if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+        return PA_CLAMP(s->thread_info.fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+
+    if (s->thread_info.requested_latency_valid)
+        return s->thread_info.requested_latency;
+
+    PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
+        if (o->thread_info.requested_source_latency != (pa_usec_t) -1 &&
+            (result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
+            result = o->thread_info.requested_source_latency;
+
+    if (result != (pa_usec_t) -1)
+        result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
+
+    if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+        /* Only cache this if we are fully set up */
+        s->thread_info.requested_latency = result;
+        s->thread_info.requested_latency_valid = true;
+    }
+
+    return result;
+}
+
+/* Called from main thread */
+pa_usec_t pa_source_get_requested_latency(pa_source *s) {
+    pa_usec_t usec = 0;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (s->state == PA_SOURCE_SUSPENDED)
+        return 0;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+
+    return usec;
+}
+
+/* Called from IO thread */
+void pa_source_set_max_rewind_within_thread(pa_source *s, size_t max_rewind) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+
+    if (max_rewind == s->thread_info.max_rewind)
+        return;
+
+    s->thread_info.max_rewind = max_rewind;
+
+    if (PA_SOURCE_IS_LINKED(s->thread_info.state))
+        PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
+            pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
+}
+
+/* Called from main thread */
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (PA_SOURCE_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
+    else
+        pa_source_set_max_rewind_within_thread(s, max_rewind);
+}
+
+/* Called from IO thread */
+void pa_source_invalidate_requested_latency(pa_source *s, bool dynamic) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+
+    if ((s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+        s->thread_info.requested_latency_valid = false;
+    else if (dynamic)
+        return;
+
+    if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+
+        if (s->update_requested_latency)
+            s->update_requested_latency(s);
+
+        while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+            if (o->update_source_requested_latency)
+                o->update_source_requested_latency(o);
+    }
+
+    if (s->monitor_of)
+        pa_sink_invalidate_requested_latency(s->monitor_of, dynamic);
+}
+
+/* Called from main thread */
+void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    /* min_latency == 0:           no limit
+     * min_latency anything else:  specified limit
+     *
+     * Similar for max_latency */
+
+    if (min_latency < ABSOLUTE_MIN_LATENCY)
+        min_latency = ABSOLUTE_MIN_LATENCY;
+
+    if (max_latency <= 0 ||
+        max_latency > ABSOLUTE_MAX_LATENCY)
+        max_latency = ABSOLUTE_MAX_LATENCY;
+
+    pa_assert(min_latency <= max_latency);
+
+    /* Hmm, let's see if someone forgot to set PA_SOURCE_DYNAMIC_LATENCY here... */
+    pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+               max_latency == ABSOLUTE_MAX_LATENCY) ||
+              (s->flags & PA_SOURCE_DYNAMIC_LATENCY));
+
+    if (PA_SOURCE_IS_LINKED(s->state)) {
+        pa_usec_t r[2];
+
+        r[0] = min_latency;
+        r[1] = max_latency;
+
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
+    } else
+        pa_source_set_latency_range_within_thread(s, min_latency, max_latency);
+}
+
+/* Called from main thread */
+void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(min_latency);
+    pa_assert(max_latency);
+
+    if (PA_SOURCE_IS_LINKED(s->state)) {
+        pa_usec_t r[2] = { 0, 0 };
+
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
+
+        *min_latency = r[0];
+        *max_latency = r[1];
+    } else {
+        *min_latency = s->thread_info.min_latency;
+        *max_latency = s->thread_info.max_latency;
+    }
+}
+
+/* Called from IO thread, and from main thread before pa_source_put() is called */
+void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+
+    pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
+    pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
+    pa_assert(min_latency <= max_latency);
+
+    /* Hmm, let's see if someone forgot to set PA_SOURCE_DYNAMIC_LATENCY here... */
+    pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+               max_latency == ABSOLUTE_MAX_LATENCY) ||
+              (s->flags & PA_SOURCE_DYNAMIC_LATENCY) ||
+              s->monitor_of);
+
+    if (s->thread_info.min_latency == min_latency &&
+        s->thread_info.max_latency == max_latency)
+        return;
+
+    s->thread_info.min_latency = min_latency;
+    s->thread_info.max_latency = max_latency;
+
+    if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+        pa_source_output *o;
+        void *state = NULL;
+
+        PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
+            if (o->update_source_latency_range)
+                o->update_source_latency_range(o);
+    }
+
+    pa_source_invalidate_requested_latency(s, false);
+}
+
+/* Called from main thread, before the source is put */
+void pa_source_set_fixed_latency(pa_source *s, pa_usec_t latency) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (s->flags & PA_SOURCE_DYNAMIC_LATENCY) {
+        pa_assert(latency == 0);
+        return;
+    }
+
+    if (latency < ABSOLUTE_MIN_LATENCY)
+        latency = ABSOLUTE_MIN_LATENCY;
+
+    if (latency > ABSOLUTE_MAX_LATENCY)
+        latency = ABSOLUTE_MAX_LATENCY;
+
+    if (PA_SOURCE_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_FIXED_LATENCY, NULL, (int64_t) latency, NULL) == 0);
+    else
+        s->thread_info.fixed_latency = latency;
+}
+
+/* Called from main thread */
+pa_usec_t pa_source_get_fixed_latency(pa_source *s) {
+    pa_usec_t latency;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (s->flags & PA_SOURCE_DYNAMIC_LATENCY)
+        return 0;
+
+    if (PA_SOURCE_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_FIXED_LATENCY, &latency, 0, NULL) == 0);
+    else
+        latency = s->thread_info.fixed_latency;
+
+    return latency;
+}
+
+/* Called from IO thread */
+void pa_source_set_fixed_latency_within_thread(pa_source *s, pa_usec_t latency) {
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+
+    if (s->flags & PA_SOURCE_DYNAMIC_LATENCY) {
+        pa_assert(latency == 0);
+        s->thread_info.fixed_latency = 0;
+
+        return;
+    }
+
+    pa_assert(latency >= ABSOLUTE_MIN_LATENCY);
+    pa_assert(latency <= ABSOLUTE_MAX_LATENCY);
+
+    if (s->thread_info.fixed_latency == latency)
+        return;
+
+    s->thread_info.fixed_latency = latency;
+
+    if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+        pa_source_output *o;
+        void *state = NULL;
+
+        PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
+            if (o->update_source_fixed_latency)
+                o->update_source_fixed_latency(o);
+    }
+
+    pa_source_invalidate_requested_latency(s, false);
+}
+
+/* Called from main thread */
+void pa_source_set_port_latency_offset(pa_source *s, int64_t offset) {
+    pa_source_assert_ref(s);
+
+    s->port_latency_offset = offset;
+
+    if (PA_SOURCE_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT_LATENCY_OFFSET, NULL, offset, NULL) == 0);
+    else
+        s->thread_info.port_latency_offset = offset;
+
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PORT_LATENCY_OFFSET_CHANGED], s);
+}
+
+/* Called from main thread */
+size_t pa_source_get_max_rewind(pa_source *s) {
+    size_t r;
+    pa_assert_ctl_context();
+    pa_source_assert_ref(s);
+
+    if (!PA_SOURCE_IS_LINKED(s->state))
+        return s->thread_info.max_rewind;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
+
+    return r;
+}
+
+/* Called from main context */
+int pa_source_set_port(pa_source *s, const char *name, bool save) {
+    pa_device_port *port;
+    int ret;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (!s->set_port) {
+        pa_log_debug("set_port() operation not implemented for source %u \"%s\"", s->index, s->name);
+        return -PA_ERR_NOTIMPLEMENTED;
+    }
+
+    if (!name)
+        return -PA_ERR_NOENTITY;
+
+    if (!(port = pa_hashmap_get(s->ports, name)))
+        return -PA_ERR_NOENTITY;
+
+    if (s->active_port == port) {
+        s->save_port = s->save_port || save;
+        return 0;
+    }
+
+    if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
+        struct source_message_set_port msg = { .port = port, .ret = 0 };
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
+        ret = msg.ret;
+    }
+    else
+        ret = s->set_port(s, port);
+
+    if (ret < 0)
+        return -PA_ERR_NOENTITY;
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+    pa_log_info("Changed port of source %u \"%s\" to %s", s->index, s->name, port->name);
+
+    s->active_port = port;
+    s->save_port = save;
+
+    /* The active port affects the default source selection. */
+    pa_core_update_default_source(s->core);
+
+    pa_source_set_port_latency_offset(s, s->active_port->latency_offset);
+
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], s);
+
+    return 0;
+}
+
+PA_STATIC_FLIST_DECLARE(pa_source_volume_change, 0, pa_xfree);
+
+/* Called from the IO thread. */
+static pa_source_volume_change *pa_source_volume_change_new(pa_source *s) {
+    pa_source_volume_change *c;
+    if (!(c = pa_flist_pop(PA_STATIC_FLIST_GET(pa_source_volume_change))))
+        c = pa_xnew(pa_source_volume_change, 1);
+
+    PA_LLIST_INIT(pa_source_volume_change, c);
+    c->at = 0;
+    pa_cvolume_reset(&c->hw_volume, s->sample_spec.channels);
+    return c;
+}
+
+/* Called from the IO thread. */
+static void pa_source_volume_change_free(pa_source_volume_change *c) {
+    pa_assert(c);
+    if (pa_flist_push(PA_STATIC_FLIST_GET(pa_source_volume_change), c) < 0)
+        pa_xfree(c);
+}
+
+/* Called from the IO thread. */
+void pa_source_volume_change_push(pa_source *s) {
+    pa_source_volume_change *c = NULL;
+    pa_source_volume_change *nc = NULL;
+    pa_source_volume_change *pc = NULL;
+    uint32_t safety_margin = s->thread_info.volume_change_safety_margin;
+
+    const char *direction = NULL;
+
+    pa_assert(s);
+    nc = pa_source_volume_change_new(s);
+
+    /* NOTE: There is already more different volumes in pa_source that I can remember.
+     *       Adding one more volume for HW would get us rid of this, but I am trying
+     *       to survive with the ones we already have. */
+    pa_sw_cvolume_divide(&nc->hw_volume, &s->real_volume, &s->soft_volume);
+
+    if (!s->thread_info.volume_changes && pa_cvolume_equal(&nc->hw_volume, &s->thread_info.current_hw_volume)) {
+        pa_log_debug("Volume not changing");
+        pa_source_volume_change_free(nc);
+        return;
+    }
+
+    nc->at = pa_source_get_latency_within_thread(s, false);
+    nc->at += pa_rtclock_now() + s->thread_info.volume_change_extra_delay;
+
+    if (s->thread_info.volume_changes_tail) {
+        for (c = s->thread_info.volume_changes_tail; c; c = c->prev) {
+            /* If volume is going up let's do it a bit late. If it is going
+             * down let's do it a bit early. */
+            if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&c->hw_volume)) {
+                if (nc->at + safety_margin > c->at) {
+                    nc->at += safety_margin;
+                    direction = "up";
+                    break;
+                }
+            }
+            else if (nc->at - safety_margin > c->at) {
+                    nc->at -= safety_margin;
+                    direction = "down";
+                    break;
+            }
+        }
+    }
+
+    if (c == NULL) {
+        if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&s->thread_info.current_hw_volume)) {
+            nc->at += safety_margin;
+            direction = "up";
+        } else {
+            nc->at -= safety_margin;
+            direction = "down";
+        }
+        PA_LLIST_PREPEND(pa_source_volume_change, s->thread_info.volume_changes, nc);
+    }
+    else {
+        PA_LLIST_INSERT_AFTER(pa_source_volume_change, s->thread_info.volume_changes, c, nc);
+    }
+
+    pa_log_debug("Volume going %s to %d at %llu", direction, pa_cvolume_avg(&nc->hw_volume), (long long unsigned) nc->at);
+
+    /* We can ignore volume events that came earlier but should happen later than this. */
+    PA_LLIST_FOREACH_SAFE(c, pc, nc->next) {
+        pa_log_debug("Volume change to %d at %llu was dropped", pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at);
+        pa_source_volume_change_free(c);
+    }
+    nc->next = NULL;
+    s->thread_info.volume_changes_tail = nc;
+}
+
+/* Called from the IO thread. */
+static void pa_source_volume_change_flush(pa_source *s) {
+    pa_source_volume_change *c = s->thread_info.volume_changes;
+    pa_assert(s);
+    s->thread_info.volume_changes = NULL;
+    s->thread_info.volume_changes_tail = NULL;
+    while (c) {
+        pa_source_volume_change *next = c->next;
+        pa_source_volume_change_free(c);
+        c = next;
+    }
+}
+
+/* Called from the IO thread. */
+bool pa_source_volume_change_apply(pa_source *s, pa_usec_t *usec_to_next) {
+    pa_usec_t now;
+    bool ret = false;
+
+    pa_assert(s);
+
+    if (!s->thread_info.volume_changes || !PA_SOURCE_IS_LINKED(s->state)) {
+        if (usec_to_next)
+            *usec_to_next = 0;
+        return ret;
+    }
+
+    pa_assert(s->write_volume);
+
+    now = pa_rtclock_now();
+
+    while (s->thread_info.volume_changes && now >= s->thread_info.volume_changes->at) {
+        pa_source_volume_change *c = s->thread_info.volume_changes;
+        PA_LLIST_REMOVE(pa_source_volume_change, s->thread_info.volume_changes, c);
+        pa_log_debug("Volume change to %d at %llu was written %llu usec late",
+                     pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at, (long long unsigned) (now - c->at));
+        ret = true;
+        s->thread_info.current_hw_volume = c->hw_volume;
+        pa_source_volume_change_free(c);
+    }
+
+    if (ret)
+        s->write_volume(s);
+
+    if (s->thread_info.volume_changes) {
+        if (usec_to_next)
+            *usec_to_next = s->thread_info.volume_changes->at - now;
+        if (pa_log_ratelimit(PA_LOG_DEBUG))
+            pa_log_debug("Next volume change in %lld usec", (long long) (s->thread_info.volume_changes->at - now));
+    }
+    else {
+        if (usec_to_next)
+            *usec_to_next = 0;
+        s->thread_info.volume_changes_tail = NULL;
+    }
+    return ret;
+}
+
+/* Called from the main thread */
+/* Gets the list of formats supported by the source. The members and idxset must
+ * be freed by the caller. */
+pa_idxset* pa_source_get_formats(pa_source *s) {
+    pa_idxset *ret;
+
+    pa_assert(s);
+
+    if (s->get_formats) {
+        /* Source supports format query, all is good */
+        ret = s->get_formats(s);
+    } else {
+        /* Source doesn't support format query, so assume it does PCM */
+        pa_format_info *f = pa_format_info_new();
+        f->encoding = PA_ENCODING_PCM;
+
+        ret = pa_idxset_new(NULL, NULL);
+        pa_idxset_put(ret, f, NULL);
+    }
+
+    return ret;
+}
+
+/* Called from the main thread */
+/* Checks if the source can accept this format */
+bool pa_source_check_format(pa_source *s, pa_format_info *f) {
+    pa_idxset *formats = NULL;
+    bool ret = false;
+
+    pa_assert(s);
+    pa_assert(f);
+
+    formats = pa_source_get_formats(s);
+
+    if (formats) {
+        pa_format_info *finfo_device;
+        uint32_t i;
+
+        PA_IDXSET_FOREACH(finfo_device, formats, i) {
+            if (pa_format_info_is_compatible(finfo_device, f)) {
+                ret = true;
+                break;
+            }
+        }
+
+        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+    }
+
+    return ret;
+}
+
+/* Called from the main thread */
+/* Calculates the intersection between formats supported by the source and
+ * in_formats, and returns these, in the order of the source's formats. */
+pa_idxset* pa_source_check_formats(pa_source *s, pa_idxset *in_formats) {
+    pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *source_formats = NULL;
+    pa_format_info *f_source, *f_in;
+    uint32_t i, j;
+
+    pa_assert(s);
+
+    if (!in_formats || pa_idxset_isempty(in_formats))
+        goto done;
+
+    source_formats = pa_source_get_formats(s);
+
+    PA_IDXSET_FOREACH(f_source, source_formats, i) {
+        PA_IDXSET_FOREACH(f_in, in_formats, j) {
+            if (pa_format_info_is_compatible(f_source, f_in))
+                pa_idxset_put(out_formats, pa_format_info_copy(f_in), NULL);
+        }
+    }
+
+done:
+    if (source_formats)
+        pa_idxset_free(source_formats, (pa_free_cb_t) pa_format_info_free);
+
+    return out_formats;
+}
+
+/* Called from the main thread. */
+void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volume) {
+    pa_cvolume old_volume;
+    char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(s);
+    pa_assert(volume);
+
+    old_volume = s->reference_volume;
+
+    if (pa_cvolume_equal(volume, &old_volume))
+        return;
+
+    s->reference_volume = *volume;
+    pa_log_debug("The reference volume of source %s changed from %s to %s.", s->name,
+                 pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &s->channel_map,
+                                            s->flags & PA_SOURCE_DECIBEL_VOLUME),
+                 pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &s->channel_map,
+                                            s->flags & PA_SOURCE_DECIBEL_VOLUME));
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED], s);
+}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
new file mode 100644 (file)
index 0000000..1e33cde
--- /dev/null
@@ -0,0 +1,462 @@
+#ifndef foopulsesourcehfoo
+#define foopulsesourcehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+
+#include <inttypes.h>
+
+#include <pulsecore/typedefs.h>
+#include <pulse/def.h>
+#include <pulse/format.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/msgobject.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/card.h>
+#include <pulsecore/device-port.h>
+#include <pulsecore/queue.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/source-output.h>
+
+#define PA_MAX_OUTPUTS_PER_SOURCE 256
+
+/* Returns true if source is linked: registered and accessible from client side. */
+static inline bool PA_SOURCE_IS_LINKED(pa_source_state_t x) {
+    return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
+}
+
+/* A generic definition for void callback functions */
+typedef void(*pa_source_cb_t)(pa_source *s);
+
+typedef int (*pa_source_get_mute_cb_t)(pa_source *s, bool *mute);
+
+struct pa_source {
+    pa_msgobject parent;
+
+    uint32_t index;
+    pa_core *core;
+
+    pa_source_state_t state;
+
+    /* Set in the beginning of pa_source_unlink() before setting the source
+     * state to UNLINKED. The purpose is to prevent moving streams to a source
+     * that is about to be removed. */
+    bool unlink_requested;
+
+    pa_source_flags_t flags;
+    pa_suspend_cause_t suspend_cause;
+
+    char *name;
+    char *driver;                             /* may be NULL */
+    pa_proplist *proplist;
+
+    pa_module *module;                        /* may be NULL */
+    pa_card *card;                            /* may be NULL */
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    uint32_t default_sample_rate;
+    uint32_t alternate_sample_rate;
+
+    pa_idxset *outputs;
+    unsigned n_corked;
+    pa_sink *monitor_of;                     /* may be NULL */
+    pa_source_output *output_from_master;    /* non-NULL only for filter sources */
+
+    pa_volume_t base_volume; /* shall be constant */
+    unsigned n_volume_steps; /* shall be constant */
+
+    /* Also see http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Volumes/ */
+    pa_cvolume reference_volume; /* The volume exported and taken as reference base for relative source output volumes */
+    pa_cvolume real_volume;      /* The volume that the hardware is configured to  */
+    pa_cvolume soft_volume;      /* The internal software volume we apply to all PCM data while it passes through */
+
+    bool muted:1;
+
+    bool refresh_volume:1;
+    bool refresh_muted:1;
+    bool save_port:1;
+    bool save_volume:1;
+    bool save_muted:1;
+
+    /* Saved volume state while we're in passthrough mode */
+    pa_cvolume saved_volume;
+    bool saved_save_volume:1;
+
+    pa_asyncmsgq *asyncmsgq;
+
+    pa_memchunk silence;
+
+    pa_hashmap *ports;
+    pa_device_port *active_port;
+    pa_atomic_t mixer_dirty;
+
+    /* The latency offset is inherited from the currently active port */
+    int64_t port_latency_offset;
+
+    unsigned priority;
+
+    bool set_mute_in_progress;
+
+    /* Called when the main loop requests a state change. Called from
+     * main loop context. If returns -1 the state change will be
+     * inhibited */
+    int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */
+
+    /* Called when the volume is queried. Called from main loop
+     * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message
+     * will be sent to the IO thread instead. If refresh_volume is
+     * false neither this function is called nor a message is sent.
+     *
+     * You must use the function pa_source_set_get_volume_callback() to
+     * set this callback. */
+    pa_source_cb_t get_volume; /* may be NULL */
+
+    /* Called when the volume shall be changed. Called from main loop
+     * context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message
+     * will be sent to the IO thread instead.
+     *
+     * You must use the function pa_source_set_set_volume_callback() to
+     * set this callback. */
+    pa_source_cb_t set_volume; /* may be NULL */
+
+    /* Source drivers that set PA_SOURCE_DEFERRED_VOLUME must provide this
+     * callback. This callback is not used with source that do not set
+     * PA_SOURCE_DEFERRED_VOLUME. This is called from the IO thread when a
+     * pending hardware volume change has to be written to the
+     * hardware. The requested volume is passed to the callback
+     * implementation in s->thread_info.current_hw_volume.
+     *
+     * The call is done inside pa_source_volume_change_apply(), which is
+     * not called automatically - it is the driver's responsibility to
+     * schedule that function to be called at the right times in the
+     * IO thread.
+     *
+     * You must use the function pa_source_set_write_volume_callback() to
+     * set this callback. */
+    pa_source_cb_t write_volume; /* may be NULL */
+
+    /* If the source mute can change "spontaneously" (i.e. initiated by the
+     * source implementation, not by someone else calling
+     * pa_source_set_mute()), then the source implementation can notify about
+     * changed mute either by calling pa_source_mute_changed() or by calling
+     * pa_source_get_mute() with force_refresh=true. If the implementation
+     * chooses the latter approach, it should implement the get_mute callback.
+     * Otherwise get_mute can be NULL.
+     *
+     * This is called when pa_source_get_mute() is called with
+     * force_refresh=true. This is called from the IO thread if the
+     * PA_SINK_DEFERRED_VOLUME flag is set, otherwise this is called from the
+     * main thread. On success, the implementation is expected to return 0 and
+     * set the mute parameter that is passed as a reference. On failure, the
+     * implementation is expected to return -1.
+     *
+     * You must use the function pa_source_set_get_mute_callback() to
+     * set this callback. */
+    pa_source_get_mute_cb_t get_mute;
+
+    /* Called when the mute setting shall be changed. Called from main
+     * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE
+     * message will be sent to the IO thread instead.
+     *
+     * You must use the function pa_source_set_set_mute_callback() to
+     * set this callback. */
+    pa_source_cb_t set_mute; /* may be NULL */
+
+    /* Called when a the requested latency is changed. Called from IO
+     * thread context. */
+    pa_source_cb_t update_requested_latency; /* may be NULL */
+
+    /* Called whenever the port shall be changed. Called from IO
+     * thread if deferred volumes are enabled, and main thread otherwise. */
+    int (*set_port)(pa_source *s, pa_device_port *port); /*ditto */
+
+    /* Called to get the list of formats supported by the source, sorted
+     * in descending order of preference. */
+    pa_idxset* (*get_formats)(pa_source *s); /* ditto */
+
+    /* Called whenever the sampling frequency shall be changed. Called from
+     * main thread. */
+    int (*update_rate)(pa_source *s, uint32_t rate);
+
+    /* Contains copies of the above data so that the real-time worker
+     * thread can work without access locking */
+    struct {
+        pa_source_state_t state;
+        pa_hashmap *outputs;
+
+        pa_rtpoll *rtpoll;
+
+        pa_cvolume soft_volume;
+        bool soft_muted:1;
+
+        bool requested_latency_valid:1;
+        pa_usec_t requested_latency;
+
+        /* Then number of bytes this source will be rewound for at
+         * max. (Only used on monitor sources) */
+        size_t max_rewind;
+
+        pa_usec_t min_latency; /* we won't go below this latency */
+        pa_usec_t max_latency; /* An upper limit for the latencies */
+
+        pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */
+
+        /* This latency offset is a direct copy from s->port_latency_offset */
+        int64_t port_latency_offset;
+
+        /* Delayed volume change events are queued here. The events
+         * are stored in expiration order. The one expiring next is in
+         * the head of the list. */
+        PA_LLIST_HEAD(pa_source_volume_change, volume_changes);
+        pa_source_volume_change *volume_changes_tail;
+        /* This value is updated in pa_source_volume_change_apply() and
+         * used only by sources with PA_SOURCE_DEFERRED_VOLUME. */
+        pa_cvolume current_hw_volume;
+
+        /* The amount of usec volume up events are delayed and volume
+         * down events are made earlier. */
+        uint32_t volume_change_safety_margin;
+        /* Usec delay added to all volume change events, may be negative. */
+        int32_t volume_change_extra_delay;
+    } thread_info;
+
+    void *userdata;
+};
+
+PA_DECLARE_PUBLIC_CLASS(pa_source);
+#define PA_SOURCE(s) pa_source_cast(s)
+
+typedef enum pa_source_message {
+    PA_SOURCE_MESSAGE_ADD_OUTPUT,
+    PA_SOURCE_MESSAGE_REMOVE_OUTPUT,
+    PA_SOURCE_MESSAGE_GET_VOLUME,
+    PA_SOURCE_MESSAGE_SET_SHARED_VOLUME,
+    PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED,
+    PA_SOURCE_MESSAGE_SET_VOLUME,
+    PA_SOURCE_MESSAGE_SYNC_VOLUMES,
+    PA_SOURCE_MESSAGE_GET_MUTE,
+    PA_SOURCE_MESSAGE_SET_MUTE,
+    PA_SOURCE_MESSAGE_GET_LATENCY,
+    PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY,
+    PA_SOURCE_MESSAGE_SET_STATE,
+    PA_SOURCE_MESSAGE_SET_LATENCY_RANGE,
+    PA_SOURCE_MESSAGE_GET_LATENCY_RANGE,
+    PA_SOURCE_MESSAGE_SET_FIXED_LATENCY,
+    PA_SOURCE_MESSAGE_GET_FIXED_LATENCY,
+    PA_SOURCE_MESSAGE_GET_MAX_REWIND,
+    PA_SOURCE_MESSAGE_SET_MAX_REWIND,
+    PA_SOURCE_MESSAGE_SET_PORT,
+    PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE,
+    PA_SOURCE_MESSAGE_SET_PORT_LATENCY_OFFSET,
+    PA_SOURCE_MESSAGE_MAX
+} pa_source_message_t;
+
+typedef struct pa_source_new_data {
+    pa_suspend_cause_t suspend_cause;
+
+    char *name;
+    pa_proplist *proplist;
+
+    const char *driver;
+    pa_module *module;
+    pa_card *card;
+
+    pa_hashmap *ports;
+    char *active_port;
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    uint32_t alternate_sample_rate;
+    pa_cvolume volume;
+    bool muted:1;
+
+    bool volume_is_set:1;
+    bool muted_is_set:1;
+    bool sample_spec_is_set:1;
+    bool channel_map_is_set:1;
+    bool alternate_sample_rate_is_set:1;
+
+    bool namereg_fail:1;
+
+    bool save_port:1;
+    bool save_volume:1;
+    bool save_muted:1;
+} pa_source_new_data;
+
+pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data);
+void pa_source_new_data_set_name(pa_source_new_data *data, const char *name);
+void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec);
+void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map);
+void pa_source_new_data_set_alternate_sample_rate(pa_source_new_data *data, const uint32_t alternate_sample_rate);
+void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume);
+void pa_source_new_data_set_muted(pa_source_new_data *data, bool mute);
+void pa_source_new_data_set_port(pa_source_new_data *data, const char *port);
+void pa_source_new_data_done(pa_source_new_data *data);
+
+/*** To be called exclusively by the source driver, from main context */
+
+pa_source* pa_source_new(
+        pa_core *core,
+        pa_source_new_data *data,
+        pa_source_flags_t flags);
+
+void pa_source_set_get_volume_callback(pa_source *s, pa_source_cb_t cb);
+void pa_source_set_set_volume_callback(pa_source *s, pa_source_cb_t cb);
+void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb);
+void pa_source_set_get_mute_callback(pa_source *s, pa_source_get_mute_cb_t cb);
+void pa_source_set_set_mute_callback(pa_source *s, pa_source_cb_t cb);
+void pa_source_enable_decibel_volume(pa_source *s, bool enable);
+
+void pa_source_put(pa_source *s);
+void pa_source_unlink(pa_source *s);
+
+void pa_source_set_description(pa_source *s, const char *description);
+void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q);
+void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p);
+
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind);
+void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_source_set_fixed_latency(pa_source *s, pa_usec_t latency);
+
+void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume);
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume);
+void pa_source_mute_changed(pa_source *s, bool new_muted);
+
+int pa_source_sync_suspend(pa_source *s);
+
+void pa_source_update_flags(pa_source *s, pa_source_flags_t mask, pa_source_flags_t value);
+
+/*** May be called by everyone, from main context */
+
+void pa_source_set_port_latency_offset(pa_source *s, int64_t offset);
+
+/* The returned value is supposed to be in the time domain of the sound card! */
+pa_usec_t pa_source_get_latency(pa_source *s);
+pa_usec_t pa_source_get_requested_latency(pa_source *s);
+void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
+pa_usec_t pa_source_get_fixed_latency(pa_source *s);
+
+size_t pa_source_get_max_rewind(pa_source *s);
+
+int pa_source_update_status(pa_source*s);
+int pa_source_suspend(pa_source *s, bool suspend, pa_suspend_cause_t cause);
+int pa_source_suspend_all(pa_core *c, bool suspend, pa_suspend_cause_t cause);
+
+/* Use this instead of checking s->flags & PA_SOURCE_FLAT_VOLUME directly. */
+bool pa_source_flat_volume_enabled(pa_source *s);
+
+/* Get the master source when sharing volumes */
+pa_source *pa_source_get_master(pa_source *s);
+
+bool pa_source_is_filter(pa_source *s);
+
+/* Is the source in passthrough mode? (that is, is this a monitor source for a sink
+ * that has a passthrough sink input connected to it. */
+bool pa_source_is_passthrough(pa_source *s);
+/* These should be called when a source enters/leaves passthrough mode */
+void pa_source_enter_passthrough(pa_source *s);
+void pa_source_leave_passthrough(pa_source *s);
+
+void pa_source_set_volume(pa_source *source, const pa_cvolume *volume, bool sendmsg, bool save);
+const pa_cvolume *pa_source_get_volume(pa_source *source, bool force_refresh);
+
+void pa_source_set_mute(pa_source *source, bool mute, bool save);
+bool pa_source_get_mute(pa_source *source, bool force_refresh);
+
+bool pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p);
+
+int pa_source_set_port(pa_source *s, const char *name, bool save);
+void pa_source_set_mixer_dirty(pa_source *s, bool is_dirty);
+
+int pa_source_update_rate(pa_source *s, uint32_t rate, bool passthrough);
+
+unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */
+unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */
+
+/* Returns how many streams are active that don't allow suspensions. If
+ * "ignore" is non-NULL, that stream is not included in the count. */
+unsigned pa_source_check_suspend(pa_source *s, pa_source_output *ignore);
+
+#define pa_source_get_state(s) ((pa_source_state_t) (s)->state)
+
+/* Moves all inputs away, and stores them in pa_queue */
+pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q);
+void pa_source_move_all_finish(pa_source *s, pa_queue *q, bool save);
+void pa_source_move_all_fail(pa_queue *q);
+
+/* Returns a copy of the source formats. TODO: Get rid of this function (or at
+ * least get rid of the copying). There's no good reason to copy the formats
+ * every time someone wants to know what formats the source supports. The
+ * formats idxset could be stored directly in the pa_source struct.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=71924 */
+pa_idxset* pa_source_get_formats(pa_source *s);
+
+bool pa_source_check_format(pa_source *s, pa_format_info *f);
+pa_idxset* pa_source_check_formats(pa_source *s, pa_idxset *in_formats);
+
+/*** To be called exclusively by the source driver, from IO context */
+
+void pa_source_post(pa_source*s, const pa_memchunk *chunk);
+void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk);
+void pa_source_process_rewind(pa_source *s, size_t nbytes);
+
+int pa_source_process_msg(pa_msgobject *o, int code, void *userdata, int64_t, pa_memchunk *chunk);
+
+void pa_source_attach_within_thread(pa_source *s);
+void pa_source_detach_within_thread(pa_source *s);
+
+pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s);
+
+void pa_source_set_max_rewind_within_thread(pa_source *s, size_t max_rewind);
+
+void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_source_set_fixed_latency_within_thread(pa_source *s, pa_usec_t latency);
+
+void pa_source_update_volume_and_mute(pa_source *s);
+
+bool pa_source_volume_change_apply(pa_source *s, pa_usec_t *usec_to_next);
+
+/*** To be called exclusively by source output drivers, from IO context */
+
+void pa_source_invalidate_requested_latency(pa_source *s, bool dynamic);
+int64_t pa_source_get_latency_within_thread(pa_source *s, bool allow_negative);
+
+/* Called from the main thread, from source-output.c only. The normal way to
+ * set the source reference volume is to call pa_source_set_volume(), but the
+ * flat volume logic in source-output.c needs also a function that doesn't do
+ * all the extra stuff that pa_source_set_volume() does. This function simply
+ * sets s->reference_volume and fires change notifications. */
+void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volume);
+
+#define pa_source_assert_io_context(s) \
+    pa_assert(pa_thread_mq_get() || !PA_SOURCE_IS_LINKED((s)->state))
+
+#endif
diff --git a/src/pulsecore/srbchannel.c b/src/pulsecore/srbchannel.c
new file mode 100644 (file)
index 0000000..4cdfaa7
--- /dev/null
@@ -0,0 +1,379 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 David Henningsson, Canonical Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "srbchannel.h"
+
+#include <pulsecore/atomic.h>
+#include <pulse/xmalloc.h>
+
+/* #define DEBUG_SRBCHANNEL */
+
+/* This ringbuffer might be useful in other contexts too, but
+ * right now it's only used inside the srbchannel, so let's keep it here
+ * for the time being. */
+typedef struct pa_ringbuffer pa_ringbuffer;
+
+struct pa_ringbuffer {
+    pa_atomic_t *count; /* amount of data in the buffer */
+    int capacity;
+    uint8_t *memory;
+    int readindex, writeindex;
+};
+
+static void *pa_ringbuffer_peek(pa_ringbuffer *r, int *count) {
+    int c = pa_atomic_load(r->count);
+
+    if (r->readindex + c > r->capacity)
+        *count = r->capacity - r->readindex;
+    else
+        *count = c;
+
+    return r->memory + r->readindex;
+}
+
+/* Returns true only if the buffer was completely full before the drop. */
+static bool pa_ringbuffer_drop(pa_ringbuffer *r, int count) {
+    bool b = pa_atomic_sub(r->count, count) >= r->capacity;
+
+    r->readindex += count;
+    r->readindex %= r->capacity;
+
+    return b;
+}
+
+static void *pa_ringbuffer_begin_write(pa_ringbuffer *r, int *count) {
+    int c = pa_atomic_load(r->count);
+
+    *count = PA_MIN(r->capacity - r->writeindex, r->capacity - c);
+
+    return r->memory + r->writeindex;
+}
+
+static void pa_ringbuffer_end_write(pa_ringbuffer *r, int count) {
+    pa_atomic_add(r->count, count);
+    r->writeindex += count;
+    r->writeindex %= r->capacity;
+}
+
+struct pa_srbchannel {
+    pa_ringbuffer rb_read, rb_write;
+    pa_fdsem *sem_read, *sem_write;
+    pa_memblock *memblock;
+
+    void *cb_userdata;
+    pa_srbchannel_cb_t callback;
+
+    pa_io_event *read_event;
+    pa_defer_event *defer_event;
+    pa_mainloop_api *mainloop;
+};
+
+/* We always listen to sem_read, and always signal on sem_write.
+ *
+ * This means we signal the same semaphore for two scenarios:
+ * 1) We have written something to our send buffer, and want the other
+ *    side to read it
+ * 2) We have read something from our receive buffer that was previously
+ *    completely full, and want the other side to continue writing
+*/
+
+size_t pa_srbchannel_write(pa_srbchannel *sr, const void *data, size_t l) {
+    size_t written = 0;
+
+    while (l > 0) {
+        int towrite;
+        void *ptr = pa_ringbuffer_begin_write(&sr->rb_write, &towrite);
+
+        if ((size_t) towrite > l)
+            towrite = l;
+
+        if (towrite == 0) {
+#ifdef DEBUG_SRBCHANNEL
+            pa_log("srbchannel output buffer full");
+#endif
+            break;
+        }
+
+        memcpy(ptr, data, towrite);
+        pa_ringbuffer_end_write(&sr->rb_write, towrite);
+        written += towrite;
+        data = (uint8_t*) data + towrite;
+        l -= towrite;
+    }
+#ifdef DEBUG_SRBCHANNEL
+    pa_log("Wrote %d bytes to srbchannel, signalling fdsem", (int) written);
+#endif
+
+    pa_fdsem_post(sr->sem_write);
+    return written;
+}
+
+size_t pa_srbchannel_read(pa_srbchannel *sr, void *data, size_t l) {
+    size_t isread = 0;
+
+    while (l > 0) {
+        int toread;
+        void *ptr = pa_ringbuffer_peek(&sr->rb_read, &toread);
+
+        if ((size_t) toread > l)
+            toread = l;
+
+        if (toread == 0)
+            break;
+
+        memcpy(data, ptr, toread);
+
+        if (pa_ringbuffer_drop(&sr->rb_read, toread)) {
+#ifdef DEBUG_SRBCHANNEL
+            pa_log("Read from full output buffer, signalling fdsem");
+#endif
+            pa_fdsem_post(sr->sem_write);
+        }
+
+        isread += toread;
+        data = (uint8_t*) data + toread;
+        l -= toread;
+    }
+
+#ifdef DEBUG_SRBCHANNEL
+    pa_log("Read %d bytes from srbchannel", (int) isread);
+#endif
+
+    return isread;
+}
+
+/* This is the memory layout of the ringbuffer shm block. It is followed by
+   read and write ringbuffer memory. */
+struct srbheader {
+    pa_atomic_t read_count;
+    pa_atomic_t write_count;
+
+    pa_fdsem_data read_semdata;
+    pa_fdsem_data write_semdata;
+
+    int capacity;
+    int readbuf_offset;
+    int writebuf_offset;
+
+    /* TODO: Maybe a marker here to make sure we talk to a server with equally sized struct */
+};
+
+static void srbchannel_rwloop(pa_srbchannel* sr) {
+    do {
+#ifdef DEBUG_SRBCHANNEL
+        int q;
+        pa_ringbuffer_peek(&sr->rb_read, &q);
+        pa_log("In rw loop from srbchannel, before callback, count = %d", q);
+#endif
+
+        if (sr->callback) {
+            if (!sr->callback(sr, sr->cb_userdata)) {
+#ifdef DEBUG_SRBCHANNEL
+                pa_log("Aborting read loop from srbchannel");
+#endif
+                return;
+            }
+        }
+
+#ifdef DEBUG_SRBCHANNEL
+        pa_ringbuffer_peek(&sr->rb_read, &q);
+        pa_log("In rw loop from srbchannel, after callback, count = %d", q);
+#endif
+
+    } while (pa_fdsem_before_poll(sr->sem_read) < 0);
+}
+
+static void semread_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    pa_srbchannel* sr = userdata;
+
+    pa_fdsem_after_poll(sr->sem_read);
+    srbchannel_rwloop(sr);
+}
+
+static void defer_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+    pa_srbchannel* sr = userdata;
+
+#ifdef DEBUG_SRBCHANNEL
+    pa_log("Calling rw loop from deferred event");
+#endif
+
+    m->defer_enable(e, 0);
+    srbchannel_rwloop(sr);
+}
+
+pa_srbchannel* pa_srbchannel_new(pa_mainloop_api *m, pa_mempool *p) {
+    int capacity;
+    int readfd;
+    struct srbheader *srh;
+
+    pa_srbchannel* sr = pa_xmalloc0(sizeof(pa_srbchannel));
+    sr->mainloop = m;
+    sr->memblock = pa_memblock_new_pool(p, -1);
+    if (!sr->memblock)
+        goto fail;
+
+    srh = pa_memblock_acquire(sr->memblock);
+    pa_zero(*srh);
+
+    sr->rb_read.memory = (uint8_t*) srh + PA_ALIGN(sizeof(*srh));
+    srh->readbuf_offset = sr->rb_read.memory - (uint8_t*) srh;
+
+    capacity = (pa_memblock_get_length(sr->memblock) - srh->readbuf_offset) / 2;
+
+    sr->rb_write.memory = PA_ALIGN_PTR(sr->rb_read.memory + capacity);
+    srh->writebuf_offset = sr->rb_write.memory - (uint8_t*) srh;
+
+    capacity = PA_MIN(capacity, srh->writebuf_offset - srh->readbuf_offset);
+
+    pa_log_debug("SHM block is %d bytes, ringbuffer capacity is 2 * %d bytes",
+        (int) pa_memblock_get_length(sr->memblock), capacity);
+
+    srh->capacity = sr->rb_read.capacity = sr->rb_write.capacity = capacity;
+
+    sr->rb_read.count = &srh->read_count;
+    sr->rb_write.count = &srh->write_count;
+
+    sr->sem_read = pa_fdsem_new_shm(&srh->read_semdata);
+    if (!sr->sem_read)
+        goto fail;
+
+    sr->sem_write = pa_fdsem_new_shm(&srh->write_semdata);
+    if (!sr->sem_write)
+        goto fail;
+
+    readfd = pa_fdsem_get(sr->sem_read);
+
+#ifdef DEBUG_SRBCHANNEL
+    pa_log("Enabling io event on fd %d", readfd);
+#endif
+
+    sr->read_event = m->io_new(m, readfd, PA_IO_EVENT_INPUT, semread_cb, sr);
+    m->io_enable(sr->read_event, PA_IO_EVENT_INPUT);
+
+    return sr;
+
+fail:
+    pa_srbchannel_free(sr);
+
+    return NULL;
+}
+
+static void pa_srbchannel_swap(pa_srbchannel *sr) {
+    pa_srbchannel temp = *sr;
+
+    sr->sem_read = temp.sem_write;
+    sr->sem_write = temp.sem_read;
+    sr->rb_read = temp.rb_write;
+    sr->rb_write = temp.rb_read;
+}
+
+pa_srbchannel* pa_srbchannel_new_from_template(pa_mainloop_api *m, pa_srbchannel_template *t)
+{
+    int temp;
+    struct srbheader *srh;
+    pa_srbchannel* sr = pa_xmalloc0(sizeof(pa_srbchannel));
+
+    sr->mainloop = m;
+    sr->memblock = t->memblock;
+    pa_memblock_ref(sr->memblock);
+    srh = pa_memblock_acquire(sr->memblock);
+
+    sr->rb_read.capacity = sr->rb_write.capacity = srh->capacity;
+    sr->rb_read.count = &srh->read_count;
+    sr->rb_write.count = &srh->write_count;
+
+    sr->rb_read.memory = (uint8_t*) srh + srh->readbuf_offset;
+    sr->rb_write.memory = (uint8_t*) srh + srh->writebuf_offset;
+
+    sr->sem_read = pa_fdsem_open_shm(&srh->read_semdata, t->readfd);
+    if (!sr->sem_read)
+        goto fail;
+
+    sr->sem_write = pa_fdsem_open_shm(&srh->write_semdata, t->writefd);
+    if (!sr->sem_write)
+        goto fail;
+
+    pa_srbchannel_swap(sr);
+    temp = t->readfd; t->readfd = t->writefd; t->writefd = temp;
+
+#ifdef DEBUG_SRBCHANNEL
+    pa_log("Enabling io event on fd %d", t->readfd);
+#endif
+
+    sr->read_event = m->io_new(m, t->readfd, PA_IO_EVENT_INPUT, semread_cb, sr);
+    m->io_enable(sr->read_event, PA_IO_EVENT_INPUT);
+
+    return sr;
+
+fail:
+    pa_srbchannel_free(sr);
+
+    return NULL;
+}
+
+void pa_srbchannel_export(pa_srbchannel *sr, pa_srbchannel_template *t) {
+    t->memblock = sr->memblock;
+    t->readfd = pa_fdsem_get(sr->sem_read);
+    t->writefd = pa_fdsem_get(sr->sem_write);
+}
+
+void pa_srbchannel_set_callback(pa_srbchannel *sr, pa_srbchannel_cb_t callback, void *userdata) {
+    if (sr->callback)
+        pa_fdsem_after_poll(sr->sem_read);
+
+    sr->callback = callback;
+    sr->cb_userdata = userdata;
+
+    if (sr->callback) {
+        /* If there are events to be read already in the ringbuffer, we will not get any IO event for that,
+           because that's how pa_fdsem works. Therefore check the ringbuffer in a defer event instead. */
+        if (!sr->defer_event)
+            sr->defer_event = sr->mainloop->defer_new(sr->mainloop, defer_cb, sr);
+        sr->mainloop->defer_enable(sr->defer_event, 1);
+    }
+}
+
+void pa_srbchannel_free(pa_srbchannel *sr)
+{
+#ifdef DEBUG_SRBCHANNEL
+    pa_log("Freeing srbchannel");
+#endif
+    pa_assert(sr);
+
+    if (sr->defer_event)
+        sr->mainloop->defer_free(sr->defer_event);
+    if (sr->read_event)
+        sr->mainloop->io_free(sr->read_event);
+
+    if (sr->sem_read)
+        pa_fdsem_free(sr->sem_read);
+    if (sr->sem_write)
+        pa_fdsem_free(sr->sem_write);
+
+    if (sr->memblock) {
+        pa_memblock_release(sr->memblock);
+        pa_memblock_unref(sr->memblock);
+    }
+
+    pa_xfree(sr);
+}
diff --git a/src/pulsecore/srbchannel.h b/src/pulsecore/srbchannel.h
new file mode 100644 (file)
index 0000000..09193e0
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef foopulsesrbchannelhfoo
+#define foopulsesrbchannelhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 David Henningsson, Canonical Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulsecore/fdsem.h>
+#include <pulsecore/memblock.h>
+
+/* An shm ringbuffer that is used for low overhead server-client communication.
+ * Signaling is done through eventfd semaphores (pa_fdsem). */
+
+typedef struct pa_srbchannel pa_srbchannel;
+
+typedef struct pa_srbchannel_template {
+    int readfd, writefd;
+    pa_memblock *memblock;
+} pa_srbchannel_template;
+
+pa_srbchannel* pa_srbchannel_new(pa_mainloop_api *m, pa_mempool *p);
+/* Note: this creates a srbchannel with swapped read and write. */
+pa_srbchannel* pa_srbchannel_new_from_template(pa_mainloop_api *m, pa_srbchannel_template *t);
+
+void pa_srbchannel_free(pa_srbchannel *sr);
+
+void pa_srbchannel_export(pa_srbchannel *sr, pa_srbchannel_template *t);
+
+size_t pa_srbchannel_write(pa_srbchannel *sr, const void *data, size_t l);
+size_t pa_srbchannel_read(pa_srbchannel *sr, void *data, size_t l);
+
+/* Set the callback function that is called whenever data becomes available for reading.
+ * It can also be called if the output buffer was full and can now be written to.
+ *
+ * Return false to abort all processing (e g if the srbchannel has been freed during the callback).
+ * Otherwise return true.
+*/
+typedef bool (*pa_srbchannel_cb_t)(pa_srbchannel *sr, void *userdata);
+void pa_srbchannel_set_callback(pa_srbchannel *sr, pa_srbchannel_cb_t callback, void *userdata);
+
+#endif
diff --git a/src/pulsecore/start-child.c b/src/pulsecore/start-child.c
new file mode 100644 (file)
index 0000000..7f0719c
--- /dev/null
@@ -0,0 +1,113 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/pipe.h>
+
+#include "start-child.h"
+
+int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {
+#ifdef HAVE_FORK
+    pid_t child;
+    int pipe_fds[2] = { -1, -1 };
+
+    if (pipe(pipe_fds) < 0) {
+        pa_log("pipe() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if ((child = fork()) == (pid_t) -1) {
+        pa_log("fork() failed: %s", pa_cstrerror(errno));
+        goto fail;
+
+    } else if (child != 0) {
+
+        /* Parent */
+        pa_assert_se(pa_close(pipe_fds[1]) == 0);
+
+        if (pid)
+            *pid = child;
+
+        return pipe_fds[0];
+    } else {
+        /* child */
+
+        pa_reset_personality();
+
+        pa_assert_se(pa_close(pipe_fds[0]) == 0);
+        pa_assert_se(dup2(pipe_fds[1], STDOUT_FILENO) == STDOUT_FILENO);
+
+        if (pipe_fds[1] != STDOUT_FILENO)
+            pa_assert_se(pa_close(pipe_fds[1]) == 0);
+
+        pa_close(STDIN_FILENO);
+        pa_assert_se(open("/dev/null", O_RDONLY) == STDIN_FILENO);
+
+        pa_close(STDERR_FILENO);
+        pa_assert_se(open("/dev/null", O_WRONLY) == STDERR_FILENO);
+
+        pa_close_all(-1);
+        pa_reset_sigs(-1);
+        pa_unblock_sigs(-1);
+        pa_reset_priority();
+        pa_unset_env_recorded();
+
+        /* Make sure our children are not influenced by the
+         * LD_BIND_NOW we set for ourselves. */
+        pa_unset_env("LD_BIND_NOW");
+
+#ifdef PR_SET_PDEATHSIG
+        /* On Linux we can use PR_SET_PDEATHSIG to have the helper
+        process killed when the daemon dies abnormally. On non-Linux
+        machines the client will die as soon as it writes data to
+        stdout again (SIGPIPE) */
+
+        prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
+#endif
+
+        execl(name, name, argv1, NULL);
+        _exit(1);
+    }
+
+fail:
+    pa_close_pipe(pipe_fds);
+#endif /* HAVE_FORK */
+
+    return -1;
+}
diff --git a/src/pulsecore/start-child.h b/src/pulsecore/start-child.h
new file mode 100644 (file)
index 0000000..6222f45
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef foopulsestartchildhfoo
+#define foopulsestartchildhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <unistd.h>
+
+int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid);
+
+#endif
diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c
new file mode 100644 (file)
index 0000000..11f131b
--- /dev/null
@@ -0,0 +1,193 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "strbuf.h"
+
+/* A chunk of the linked list that makes up the string */
+struct chunk {
+    struct chunk *next;
+    size_t length;
+};
+
+#define CHUNK_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(struct chunk)))
+
+struct pa_strbuf {
+    size_t length;
+    struct chunk *head, *tail;
+};
+
+pa_strbuf *pa_strbuf_new(void) {
+    pa_strbuf *sb;
+
+    sb = pa_xnew(pa_strbuf, 1);
+    sb->length = 0;
+    sb->head = sb->tail = NULL;
+
+    return sb;
+}
+
+void pa_strbuf_free(pa_strbuf *sb) {
+    pa_assert(sb);
+
+    while (sb->head) {
+        struct chunk *c = sb->head;
+        sb->head = sb->head->next;
+        pa_xfree(c);
+    }
+
+    pa_xfree(sb);
+}
+
+/* Make a C string from the string buffer. The caller has to free
+ * string with pa_xfree(). */
+char *pa_strbuf_to_string(pa_strbuf *sb) {
+    char *t, *e;
+    struct chunk *c;
+
+    pa_assert(sb);
+
+    e = t = pa_xmalloc(sb->length+1);
+
+    for (c = sb->head; c; c = c->next) {
+        pa_assert((size_t) (e-t) <= sb->length);
+        memcpy(e, CHUNK_TO_TEXT(c), c->length);
+        e += c->length;
+    }
+
+    /* Trailing NUL */
+    *e = 0;
+
+    pa_assert(e == t+sb->length);
+
+    return t;
+}
+
+/* Combination of pa_strbuf_free() and pa_strbuf_to_string() */
+char *pa_strbuf_to_string_free(pa_strbuf *sb) {
+    char *t;
+
+    pa_assert(sb);
+    t = pa_strbuf_to_string(sb);
+    pa_strbuf_free(sb);
+
+    return t;
+}
+
+/* Append a string to the string buffer */
+void pa_strbuf_puts(pa_strbuf *sb, const char *t) {
+
+    pa_assert(sb);
+    pa_assert(t);
+
+    pa_strbuf_putsn(sb, t, strlen(t));
+}
+
+/* Append a character to the string buffer */
+void pa_strbuf_putc(pa_strbuf *sb, char c) {
+    pa_assert(sb);
+
+    pa_strbuf_putsn(sb, &c, 1);
+}
+
+/* Append a new chunk to the linked list */
+static void append(pa_strbuf *sb, struct chunk *c) {
+    pa_assert(sb);
+    pa_assert(c);
+
+    if (sb->tail) {
+        pa_assert(sb->head);
+        sb->tail->next = c;
+    } else {
+        pa_assert(!sb->head);
+        sb->head = c;
+    }
+
+    sb->tail = c;
+    sb->length += c->length;
+    c->next = NULL;
+}
+
+/* Append up to l bytes of a string to the string buffer */
+void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {
+    struct chunk *c;
+
+    pa_assert(sb);
+    pa_assert(t);
+
+    if (!l)
+        return;
+
+    c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l);
+    c->length = l;
+    memcpy(CHUNK_TO_TEXT(c), t, l);
+
+    append(sb, c);
+}
+
+/* Append a printf() style formatted string to the string buffer. */
+/* The following is based on an example from the GNU libc documentation */
+size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
+    size_t size = 100;
+    struct chunk *c = NULL;
+
+    pa_assert(sb);
+    pa_assert(format);
+
+    for(;;) {
+        va_list ap;
+        int r;
+
+        c = pa_xrealloc(c, PA_ALIGN(sizeof(struct chunk)) + size);
+
+        va_start(ap, format);
+        r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap);
+        CHUNK_TO_TEXT(c)[size-1] = 0;
+        va_end(ap);
+
+        if (r > -1 && (size_t) r < size) {
+            c->length = (size_t) r;
+            append(sb, c);
+            return (size_t) r;
+        }
+
+        if (r > -1)    /* glibc 2.1 */
+            size = (size_t) r+1;
+        else           /* glibc 2.0 */
+            size *= 2;
+    }
+}
+
+bool pa_strbuf_isempty(pa_strbuf *sb) {
+    pa_assert(sb);
+
+    return sb->length <= 0;
+}
diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h
new file mode 100644 (file)
index 0000000..469f6f7
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef foostrbufhfoo
+#define foostrbufhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/gccmacro.h>
+#include <pulsecore/macro.h>
+
+typedef struct pa_strbuf pa_strbuf;
+
+pa_strbuf *pa_strbuf_new(void);
+void pa_strbuf_free(pa_strbuf *sb);
+char *pa_strbuf_to_string(pa_strbuf *sb);
+char *pa_strbuf_to_string_free(pa_strbuf *sb);
+
+size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...)  PA_GCC_PRINTF_ATTR(2,3);
+void pa_strbuf_puts(pa_strbuf *sb, const char *t);
+void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m);
+void pa_strbuf_putc(pa_strbuf *sb, char c);
+
+bool pa_strbuf_isempty(pa_strbuf *sb);
+
+#endif
diff --git a/src/pulsecore/stream-util.c b/src/pulsecore/stream-util.c
new file mode 100644 (file)
index 0000000..2574501
--- /dev/null
@@ -0,0 +1,84 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Intel Corporation
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "stream-util.h"
+
+#include <pulse/def.h>
+
+#include <pulsecore/core-format.h>
+#include <pulsecore/macro.h>
+
+int pa_stream_get_volume_channel_map(const pa_cvolume *volume, const pa_channel_map *original_map, const pa_format_info *format,
+                                     pa_channel_map *volume_map) {
+    int r;
+    pa_channel_map volume_map_local;
+
+    pa_assert(volume);
+    pa_assert(format);
+    pa_assert(volume_map);
+
+    if (original_map) {
+        if (volume->channels == original_map->channels) {
+            *volume_map = *original_map;
+            return 0;
+        }
+
+        if (volume->channels == 1) {
+            pa_channel_map_init_mono(volume_map);
+            return 0;
+        }
+
+        pa_log_info("Invalid stream parameters: the volume is incompatible with the channel map.");
+        return -PA_ERR_INVALID;
+    }
+
+    r = pa_format_info_get_channel_map(format, &volume_map_local);
+    if (r == -PA_ERR_NOENTITY) {
+        if (volume->channels == 1) {
+            pa_channel_map_init_mono(volume_map);
+            return 0;
+        }
+
+        pa_log_info("Invalid stream parameters: multi-channel volume is set, but channel map is not.");
+        return -PA_ERR_INVALID;
+    }
+
+    if (r < 0) {
+        pa_log_info("Invalid channel map.");
+        return -PA_ERR_INVALID;
+    }
+
+    if (volume->channels == volume_map_local.channels) {
+        *volume_map = volume_map_local;
+        return 0;
+    }
+
+    if (volume->channels == 1) {
+        pa_channel_map_init_mono(volume_map);
+        return 0;
+    }
+
+    pa_log_info("Invalid stream parameters: the volume is incompatible with the channel map.");
+
+    return -PA_ERR_INVALID;
+}
diff --git a/src/pulsecore/stream-util.h b/src/pulsecore/stream-util.h
new file mode 100644 (file)
index 0000000..b2e47fe
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef foostreamutilhfoo
+#define foostreamutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Intel Corporation
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/format.h>
+#include <pulse/volume.h>
+
+/* This is a helper function that is called from pa_sink_input_new() and
+ * pa_source_output_new(). The job of this function is to figure out what
+ * channel map should be used for interpreting the volume that was set for the
+ * stream. The channel map that the client intended for the volume may be
+ * different than the final stream channel map, because the client may want the
+ * server to decide the stream channel map.
+ *
+ * volume is the volume for which the channel map should be figured out.
+ *
+ * original_map is the channel map that is set in the new data struct's
+ * channel_map field. If the channel map hasn't been set in the new data, then
+ * original_map should be NULL.
+ *
+ * format is the negotiated format for the stream. It's used as a fallback if
+ * original_map is not available.
+ *
+ * On success, the result is saved in volume_map. It's possible that this
+ * function fails to figure out the right channel map for the volume, in which
+ * case a negative error code is returned. */
+int pa_stream_get_volume_channel_map(const pa_cvolume *volume, const pa_channel_map *original_map, const pa_format_info *format,
+                                     pa_channel_map *volume_map);
+
+#endif
diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c
new file mode 100644 (file)
index 0000000..7e5b070
--- /dev/null
@@ -0,0 +1,171 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/strbuf.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "strlist.h"
+
+struct pa_strlist {
+    pa_strlist *next;
+};
+
+#define ITEM_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(pa_strlist)))
+
+pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s) {
+    pa_strlist *n;
+    size_t size;
+
+    pa_assert(s);
+    size = strlen(s);
+    n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1);
+    memcpy(ITEM_TO_TEXT(n), s, size + 1);
+    n->next = l;
+
+    return n;
+}
+
+char *pa_strlist_to_string(pa_strlist *l) {
+    int first = 1;
+    pa_strbuf *b;
+
+    b = pa_strbuf_new();
+    for (; l; l = l->next) {
+        if (!first)
+            pa_strbuf_puts(b, " ");
+        first = 0;
+        pa_strbuf_puts(b, ITEM_TO_TEXT(l));
+    }
+
+    return pa_strbuf_to_string_free(b);
+}
+
+pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s) {
+    pa_strlist *ret = l, *prev = NULL;
+
+    pa_assert(l);
+    pa_assert(s);
+
+    while (l) {
+        if (pa_streq(ITEM_TO_TEXT(l), s)) {
+            pa_strlist *n = l->next;
+
+            if (!prev) {
+                pa_assert(ret == l);
+                ret = n;
+            } else
+                prev->next = n;
+
+            pa_xfree(l);
+
+            l = n;
+
+        } else {
+            prev = l;
+            l = l->next;
+        }
+    }
+
+    return ret;
+}
+
+void pa_strlist_free(pa_strlist *l) {
+    while (l) {
+        pa_strlist *c = l;
+        l = l->next;
+        pa_xfree(c);
+    }
+}
+
+pa_strlist* pa_strlist_pop(pa_strlist *l, char **s) {
+    pa_strlist *r;
+
+    pa_assert(s);
+
+    if (!l) {
+        *s = NULL;
+        return NULL;
+    }
+
+    *s = pa_xstrdup(ITEM_TO_TEXT(l));
+    r = l->next;
+    pa_xfree(l);
+    return r;
+}
+
+pa_strlist* pa_strlist_parse(const char *s) {
+    pa_strlist *head = NULL, *p = NULL;
+    const char *state = NULL;
+    char *r;
+
+    while ((r = pa_split_spaces(s, &state))) {
+        pa_strlist *n;
+        size_t size = strlen(r);
+
+        n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1);
+        n->next = NULL;
+        memcpy(ITEM_TO_TEXT(n), r, size+1);
+        pa_xfree(r);
+
+        if (p)
+            p->next = n;
+        else
+            head = n;
+
+        p = n;
+    }
+
+    return head;
+}
+
+pa_strlist *pa_strlist_reverse(pa_strlist *l) {
+    pa_strlist *r = NULL;
+
+    while (l) {
+        pa_strlist *n;
+
+        n = l->next;
+        l->next = r;
+        r = l;
+        l = n;
+    }
+
+    return r;
+}
+
+pa_strlist *pa_strlist_next(pa_strlist *s) {
+    pa_assert(s);
+
+    return s->next;
+}
+
+const char *pa_strlist_data(pa_strlist *s) {
+    pa_assert(s);
+
+    return ITEM_TO_TEXT(s);
+}
diff --git a/src/pulsecore/strlist.h b/src/pulsecore/strlist.h
new file mode 100644 (file)
index 0000000..3cc71e8
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef foostrlisthfoo
+#define foostrlisthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct pa_strlist pa_strlist;
+
+/* Add the specified server string to the list, return the new linked list head */
+pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s);
+
+/* Remove the specified string from the list, return the new linked list head */
+pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s);
+
+/* Make a whitespace separated string of all server strings. Returned memory has to be freed with pa_xfree() */
+char *pa_strlist_to_string(pa_strlist *l);
+
+/* Free the entire list */
+void pa_strlist_free(pa_strlist *l);
+
+/* Return the next entry in the list in *string and remove it from
+ * the list. Returns the new list head. The memory *string points to
+ * has to be freed with pa_xfree() */
+pa_strlist* pa_strlist_pop(pa_strlist *l, char **s);
+
+/* Parse a whitespace separated server list */
+pa_strlist* pa_strlist_parse(const char *s);
+
+/* Reverse string list */
+pa_strlist *pa_strlist_reverse(pa_strlist *l);
+
+/* Return the next item in the list */
+pa_strlist *pa_strlist_next(pa_strlist *s);
+
+/* Return the string associated to the current item */
+const char *pa_strlist_data(pa_strlist *s);
+
+#endif
diff --git a/src/pulsecore/svolume.orc b/src/pulsecore/svolume.orc
new file mode 100644 (file)
index 0000000..26f618e
--- /dev/null
@@ -0,0 +1,84 @@
+#  This file is part of PulseAudio.
+#
+#  Copyright 2010 Lennart Poettering
+#  Copyright 2010 Wim Taymans <wim.taymans@collabora.co.uk>
+#  Copyright 2010 Arun Raghavan <arun.raghavan@collabora.co.uk>
+#
+#  PulseAudio is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU Lesser General Public License as published
+#  by the Free Software Foundation; either version 2.1 of the License,
+#  or (at your option) any later version.
+#
+#  PulseAudio is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+# S16NE 1- and 2-channel volume scaling work as follows:
+#
+#     params: samples s (signed 16-bit), volume v (signed 32-bit < 2^31)
+#
+#                  32           16                 0 (type of operation)
+#         sample =               |      sample     | (signed)
+#              s = |      0      |      sample     | (unsigned)
+#
+#     if (sample < 0)
+#          signc = |      0      |      0xffff     | (unsigned)
+#     else
+#          signc = |      0      |        0        | (unsigned)
+#
+#     if (sample < 0)
+#             ml = |      0      | -((s*vl) >> 16) | (unsigned)
+#     else
+#             ml = |      0      |   (s*vl) >> 16  | (unsigned)
+#
+#             vh =               |      v >> 16    | (signed, but sign bit is always zero
+#                                                     since PA_VOLUME_MAX is 0x0fffffff)
+#             mh = |         (s * vh) >> 16        | (signed)
+#             ml = |           ml + mh             | (signed)
+#         sample =               |    (ml >> 16)   | (signed, saturated)
+
+.function pa_volume_s16ne_orc_1ch
+.dest 2 samples int16_t
+.param 4 vols int32_t
+.temp 4 v
+.temp 2 vh
+.temp 4 s
+.temp 4 mh
+.temp 4 ml
+.temp 4 signc
+
+loadpl v, vols
+convuwl s, samples
+x2 cmpgtsw signc, 0, s
+x2 andw signc, signc, v
+x2 mulhuw ml, s, v
+subl ml, ml, signc
+convhlw vh, v
+mulswl mh, samples, vh
+addl ml, ml, mh
+convssslw samples, ml
+
+.function pa_volume_s16ne_orc_2ch
+.dest 4 samples int16_t
+.longparam 8 vols
+.temp 8 v
+.temp 4 vh
+.temp 8 s
+.temp 8 mh
+.temp 8 ml
+.temp 8 signc
+
+loadpq v, vols
+x2 convuwl s, samples
+x4 cmpgtsw signc, 0, s
+x4 andw signc, signc, v
+x4 mulhuw ml, s, v
+x2 subl ml, ml, signc
+x2 convhlw vh, v
+x2 mulswl mh, samples, vh
+x2 addl ml, ml, mh
+x2 convssslw samples, ml
diff --git a/src/pulsecore/svolume_arm.c b/src/pulsecore/svolume_arm.c
new file mode 100644 (file)
index 0000000..fdef313
--- /dev/null
@@ -0,0 +1,167 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+
+#include "cpu-arm.h"
+
+#include "sample-util.h"
+
+#if defined (__arm__) && defined (HAVE_ARMV6)
+
+#define MOD_INC() \
+    " subs  r0, r6, %2              \n\t" \
+    " itt cs                        \n\t" \
+    " addcs r0, %1                  \n\t" \
+    " movcs r6, r0                  \n\t"
+
+static pa_do_volume_func_t _volume_ref;
+
+static void pa_volume_s16ne_arm(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    /* Channels must be at least 4, and always a multiple of the original number.
+     * This is also the max amount we overread the volume array, which should
+     * have enough padding. */
+    const int32_t *ve = volumes + (channels == 3 ? 6 : PA_MAX (4U, channels));
+    unsigned rem = PA_ALIGN((size_t) samples) - (size_t) samples;
+
+    /* Make sure we're word-aligned, else performance _really_ sucks */
+    if (rem) {
+        _volume_ref(samples, volumes, channels, rem < length ? rem : length);
+
+        if (rem < length) {
+            length -= rem;
+            samples += rem / sizeof(*samples);
+        } else
+            return; /* we're done */
+    }
+
+    __asm__ __volatile__ (
+        " mov r6, %4                      \n\t" /* r6 = volumes + rem */
+        " mov %3, %3, LSR #1              \n\t" /* length /= sizeof (int16_t) */
+
+        " cmp %3, #4                      \n\t" /* check for 4+ samples */
+        " blt 2f                          \n\t"
+
+        /* See final case for how the multiplication works */
+
+        "1:                               \n\t"
+        " ldrd r2, [r6], #8               \n\t" /* 4 samples at a time */
+        " ldrd r4, [r6], #8               \n\t"
+        " ldrd r0, [%0]                   \n\t"
+
+#ifdef WORDS_BIGENDIAN
+        " smulwt r2, r2, r0               \n\t"
+        " smulwb r3, r3, r0               \n\t"
+        " smulwt r4, r4, r1               \n\t"
+        " smulwb r5, r5, r1               \n\t"
+#else
+        " smulwb r2, r2, r0               \n\t"
+        " smulwt r3, r3, r0               \n\t"
+        " smulwb r4, r4, r1               \n\t"
+        " smulwt r5, r5, r1               \n\t"
+#endif
+
+        " ssat r2, #16, r2                \n\t"
+        " ssat r3, #16, r3                \n\t"
+        " ssat r4, #16, r4                \n\t"
+        " ssat r5, #16, r5                \n\t"
+
+#ifdef WORDS_BIGENDIAN
+        " pkhbt r0, r3, r2, LSL #16       \n\t"
+        " pkhbt r1, r5, r4, LSL #16       \n\t"
+#else
+        " pkhbt r0, r2, r3, LSL #16       \n\t"
+        " pkhbt r1, r4, r5, LSL #16       \n\t"
+#endif
+        " strd  r0, [%0], #8              \n\t"
+
+        MOD_INC()
+
+        " subs %3, %3, #4                 \n\t"
+        " cmp %3, #4                      \n\t"
+        " bge 1b                          \n\t"
+
+        "2:                               \n\t"
+        " cmp %3, #2                      \n\t"
+        " blt 3f                          \n\t"
+
+        " ldrd r2, [r6], #8               \n\t" /* 2  samples at a time */
+        " ldr  r0, [%0]                   \n\t"
+
+#ifdef WORDS_BIGENDIAN
+        " smulwt r2, r2, r0               \n\t"
+        " smulwb r3, r3, r0               \n\t"
+#else
+        " smulwb r2, r2, r0               \n\t"
+        " smulwt r3, r3, r0               \n\t"
+#endif
+
+        " ssat r2, #16, r2                \n\t"
+        " ssat r3, #16, r3                \n\t"
+
+#ifdef WORDS_BIGENDIAN
+        " pkhbt r0, r3, r2, LSL #16       \n\t"
+#else
+        " pkhbt r0, r2, r3, LSL #16       \n\t"
+#endif
+        " str  r0, [%0], #4               \n\t"
+
+        MOD_INC()
+
+        " subs %3, %3, #2                 \n\t"
+
+        "3:                               \n\t" /* check for odd # of samples */
+        " cmp %3, #1                      \n\t"
+        " bne 4f                          \n\t"
+
+        " ldr  r0, [r6], #4               \n\t" /* r0 = volume */
+        " ldrh r2, [%0]                   \n\t" /* r2 = sample */
+
+        " smulwb r0, r0, r2               \n\t" /* r0 = (r0 * r2) >> 16 */
+        " ssat r0, #16, r0                \n\t" /* r0 = PA_CLAMP(r0, 0x7FFF) */
+
+        " strh r0, [%0], #2               \n\t" /* sample = r0 */
+
+        "4:                               \n\t"
+
+        : "+r" (samples), "+r" (volumes), "+r" (ve), "+r" (length)
+        : "r" (volumes + ((rem / sizeof(*samples)) % channels))
+        : "r6", "r5", "r4", "r3", "r2", "r1", "r0", "cc"
+    );
+}
+
+#endif /* defined (__arm__) && defined (HAVE_ARMV6) */
+
+void pa_volume_func_init_arm(pa_cpu_arm_flag_t flags) {
+#if defined (__arm__) && defined (HAVE_ARMV6)
+    pa_log_info("Initialising ARM optimized volume functions.");
+
+    if (!_volume_ref)
+        _volume_ref = pa_get_volume_func(PA_SAMPLE_S16NE);
+
+    pa_set_volume_func(PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_arm);
+#endif /* defined (__arm__) && defined (HAVE_ARMV6) */
+}
diff --git a/src/pulsecore/svolume_c.c b/src/pulsecore/svolume_c.c
new file mode 100644 (file)
index 0000000..659b337
--- /dev/null
@@ -0,0 +1,271 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
+#include <pulsecore/endianmacros.h>
+
+#include "sample-util.h"
+
+static void pa_volume_u8_c(uint8_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+
+    for (channel = 0; length; length--) {
+        int32_t t = pa_mult_s16_volume(*samples - 0x80, volumes[channel]);
+
+        t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
+        *samples++ = (uint8_t) (t + 0x80);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_alaw_c(uint8_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+
+    for (channel = 0; length; length--) {
+        int32_t t = pa_mult_s16_volume(st_alaw2linear16(*samples), volumes[channel]);
+
+        t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+        *samples++ = (uint8_t) st_13linear2alaw((int16_t) t >> 3);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_ulaw_c(uint8_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+
+    for (channel = 0; length; length--) {
+        int32_t t = pa_mult_s16_volume(st_ulaw2linear16(*samples), volumes[channel]);
+
+        t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+        *samples++ = (uint8_t) st_14linear2ulaw((int16_t) t >> 2);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_s16ne_c(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+
+    length /= sizeof(int16_t);
+
+    for (channel = 0; length; length--) {
+        int32_t t = pa_mult_s16_volume(*samples, volumes[channel]);
+
+        t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+        *samples++ = (int16_t) t;
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_s16re_c(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+
+    length /= sizeof(int16_t);
+
+    for (channel = 0; length; length--) {
+        int32_t t = pa_mult_s16_volume(PA_INT16_SWAP(*samples), volumes[channel]);
+
+        t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+        *samples++ = PA_INT16_SWAP((int16_t) t);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_float32ne_c(float *samples, const float *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+
+    length /= sizeof(float);
+
+    for (channel = 0; length; length--) {
+        *samples++ *= volumes[channel];
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_float32re_c(float *samples, const float *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+
+    length /= sizeof(float);
+
+    for (channel = 0; length; length--) {
+        float t;
+
+        t = PA_READ_FLOAT32RE(samples);
+        t *= volumes[channel];
+        PA_WRITE_FLOAT32RE(samples++, t);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_s32ne_c(int32_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+
+    length /= sizeof(int32_t);
+
+    for (channel = 0; length; length--) {
+        int64_t t;
+
+        t = (int64_t)(*samples);
+        t = (t * volumes[channel]) >> 16;
+        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+        *samples++ = (int32_t) t;
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_s32re_c(int32_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+
+    length /= sizeof(int32_t);
+
+    for (channel = 0; length; length--) {
+        int64_t t;
+
+        t = (int64_t) PA_INT32_SWAP(*samples);
+        t = (t * volumes[channel]) >> 16;
+        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+        *samples++ = PA_INT32_SWAP((int32_t) t);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_s24ne_c(uint8_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+    uint8_t *e;
+
+    e = samples + length;
+
+    for (channel = 0; samples < e; samples += 3) {
+        int64_t t;
+
+        t = (int64_t)((int32_t) (PA_READ24NE(samples) << 8));
+        t = (t * volumes[channel]) >> 16;
+        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+        PA_WRITE24NE(samples, ((uint32_t) (int32_t) t) >> 8);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_s24re_c(uint8_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+    uint8_t *e;
+
+    e = samples + length;
+
+    for (channel = 0; samples < e; samples += 3) {
+        int64_t t;
+
+        t = (int64_t)((int32_t) (PA_READ24RE(samples) << 8));
+        t = (t * volumes[channel]) >> 16;
+        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+        PA_WRITE24RE(samples, ((uint32_t) (int32_t) t) >> 8);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_s24_32ne_c(uint32_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+
+    length /= sizeof(uint32_t);
+
+    for (channel = 0; length; length--) {
+        int64_t t;
+
+        t = (int64_t) ((int32_t) (*samples << 8));
+        t = (t * volumes[channel]) >> 16;
+        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+        *samples++ = ((uint32_t) ((int32_t) t)) >> 8;
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static void pa_volume_s24_32re_c(uint32_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    unsigned channel;
+
+    length /= sizeof(uint32_t);
+
+    for (channel = 0; length; length--) {
+        int64_t t;
+
+        t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*samples) << 8));
+        t = (t * volumes[channel]) >> 16;
+        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+        *samples++ = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8);
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+static pa_do_volume_func_t do_volume_table[] = {
+    [PA_SAMPLE_U8]        = (pa_do_volume_func_t) pa_volume_u8_c,
+    [PA_SAMPLE_ALAW]      = (pa_do_volume_func_t) pa_volume_alaw_c,
+    [PA_SAMPLE_ULAW]      = (pa_do_volume_func_t) pa_volume_ulaw_c,
+    [PA_SAMPLE_S16NE]     = (pa_do_volume_func_t) pa_volume_s16ne_c,
+    [PA_SAMPLE_S16RE]     = (pa_do_volume_func_t) pa_volume_s16re_c,
+    [PA_SAMPLE_FLOAT32NE] = (pa_do_volume_func_t) pa_volume_float32ne_c,
+    [PA_SAMPLE_FLOAT32RE] = (pa_do_volume_func_t) pa_volume_float32re_c,
+    [PA_SAMPLE_S32NE]     = (pa_do_volume_func_t) pa_volume_s32ne_c,
+    [PA_SAMPLE_S32RE]     = (pa_do_volume_func_t) pa_volume_s32re_c,
+    [PA_SAMPLE_S24NE]     = (pa_do_volume_func_t) pa_volume_s24ne_c,
+    [PA_SAMPLE_S24RE]     = (pa_do_volume_func_t) pa_volume_s24re_c,
+    [PA_SAMPLE_S24_32NE]  = (pa_do_volume_func_t) pa_volume_s24_32ne_c,
+    [PA_SAMPLE_S24_32RE]  = (pa_do_volume_func_t) pa_volume_s24_32re_c
+};
+
+pa_do_volume_func_t pa_get_volume_func(pa_sample_format_t f) {
+    pa_assert(pa_sample_format_valid(f));
+
+    return do_volume_table[f];
+}
+
+void pa_set_volume_func(pa_sample_format_t f, pa_do_volume_func_t func) {
+    pa_assert(pa_sample_format_valid(f));
+
+    do_volume_table[f] = func;
+}
diff --git a/src/pulsecore/svolume_mmx.c b/src/pulsecore/svolume_mmx.c
new file mode 100644 (file)
index 0000000..1bf70be
--- /dev/null
@@ -0,0 +1,252 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/rtclock.h>
+
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+
+#include "cpu-x86.h"
+
+#include "sample-util.h"
+
+#if (!defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && defined (__i386__)) || defined (__amd64__)
+/* in s: 2 int16_t samples
+ * in v: 2 int32_t volumes, fixed point 16:16
+ * out s: contains scaled and clamped int16_t samples.
+ *
+ * We calculate the high 32 bits of a 32x16 multiply which we then
+ * clamp to 16 bits. The calculation is:
+ *
+ *  vl = (v & 0xffff)
+ *  vh = (v >> 16)
+ *  s = ((s * vl) >> 16) + (s * vh);
+ *
+ * For the first multiply we have to do a sign correction as we need to
+ * multiply a signed int with an unsigned int. Hacker's delight 8-3 gives a
+ * simple formula to correct the sign of the high word after the signed
+ * multiply.
+ */
+#define VOLUME_32x16(s,v)                  /* .. |   vh  |   vl  | */                   \
+      " pxor  %%mm4, %%mm4           \n\t" /* .. |    0  |    0  | */                   \
+      " punpcklwd %%mm4, "#s"        \n\t" /* .. |    0  |   p0  | */                   \
+      " pcmpgtw "#v", %%mm4          \n\t" /* .. |    0  | s(vl) | */                   \
+      " pand "#s", %%mm4             \n\t" /* .. |    0  |  (p0) |  (vl >> 15) & p */   \
+      " movq "#s", %%mm5             \n\t"                                              \
+      " pmulhw "#v", "#s"            \n\t" /* .. |    0  | vl*p0 | */                   \
+      " paddw %%mm4, "#s"            \n\t" /* .. |    0  | vl*p0 | + sign correct */    \
+      " pslld $16, "#s"              \n\t" /* .. | vl*p0 |   0   | */                   \
+      " psrld $16, "#v"              \n\t" /* .. |    0  |   vh  | */                   \
+      " psrad $16, "#s"              \n\t" /* .. |     vl*p0     | sign extend */       \
+      " pmaddwd %%mm5, "#v"          \n\t" /* .. |    p0 * vh    | */                   \
+      " paddd "#s", "#v"             \n\t" /* .. |    p0 * v0    | */                   \
+      " packssdw "#v", "#v"          \n\t" /* .. | p1*v1 | p0*v0 | */
+
+/* approximately advances %3 = (%3 + a) % b. This function requires that
+ * a <= b. */
+#define MOD_ADD(a,b) \
+      " add "#a", %3                 \n\t" \
+      " mov %3, %4                   \n\t" \
+      " sub "#b", %4                 \n\t" \
+      " cmovae %4, %3                \n\t"
+
+/* swap 16 bits */
+#define SWAP_16(s) \
+      " movq "#s", %%mm4             \n\t" /* .. |  h  l |  */ \
+      " psrlw $8, %%mm4              \n\t" /* .. |  0  h |  */ \
+      " psllw $8, "#s"               \n\t" /* .. |  l  0 |  */ \
+      " por %%mm4, "#s"              \n\t" /* .. |  l  h |  */
+
+/* swap 2 registers 16 bits for better pairing */
+#define SWAP_16_2(s1,s2) \
+      " movq "#s1", %%mm4            \n\t" /* .. |  h  l |  */ \
+      " movq "#s2", %%mm5            \n\t"                     \
+      " psrlw $8, %%mm4              \n\t" /* .. |  0  h |  */ \
+      " psrlw $8, %%mm5              \n\t"                     \
+      " psllw $8, "#s1"              \n\t" /* .. |  l  0 |  */ \
+      " psllw $8, "#s2"              \n\t"                     \
+      " por %%mm4, "#s1"             \n\t" /* .. |  l  h |  */ \
+      " por %%mm5, "#s2"             \n\t"
+
+static void pa_volume_s16ne_mmx(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    pa_reg_x86 channel, temp;
+
+    /* Channels must be at least 4, and always a multiple of the original number.
+     * This is also the max amount we overread the volume array, which should
+     * have enough padding. */
+    channels = channels == 3 ? 6 : PA_MAX (4U, channels);
+
+    __asm__ __volatile__ (
+        " xor %3, %3                    \n\t"
+        " sar $1, %2                    \n\t" /* length /= sizeof (int16_t) */
+
+        " test $1, %2                   \n\t" /* check for odd samples */
+        " je 2f                         \n\t"
+
+        " movd (%q1, %3, 4), %%mm0      \n\t" /* |  v0h  |  v0l  | */
+        " movw (%0), %w4                \n\t" /*     ..  |  p0   | */
+        " movd %4, %%mm1                \n\t"
+        VOLUME_32x16 (%%mm1, %%mm0)
+        " movd %%mm0, %4                \n\t" /*     ..  | p0*v0 | */
+        " movw %w4, (%0)                \n\t"
+        " add $2, %0                    \n\t"
+        MOD_ADD ($1, %5)
+
+        "2:                             \n\t"
+        " sar $1, %2                    \n\t" /* prepare for processing 2 samples at a time */
+        " test $1, %2                   \n\t" /* check for odd samples */
+        " je 4f                         \n\t"
+
+        "3:                             \n\t" /* do samples in groups of 2 */
+        " movq (%q1, %3, 4), %%mm0      \n\t" /* |  v1h  |  v1l  |  v0h  |  v0l  | */
+        " movd (%0), %%mm1              \n\t" /*              .. |   p1  |  p0   | */
+        VOLUME_32x16 (%%mm1, %%mm0)
+        " movd %%mm0, (%0)              \n\t" /*              .. | p1*v1 | p0*v0 | */
+        " add $4, %0                    \n\t"
+        MOD_ADD ($2, %5)
+
+        "4:                             \n\t"
+        " sar $1, %2                    \n\t" /* prepare for processing 4 samples at a time */
+        " cmp $0, %2                    \n\t"
+        " je 6f                         \n\t"
+
+        "5:                             \n\t" /* do samples in groups of 4 */
+        " movq (%q1, %3, 4), %%mm0      \n\t" /* |  v1h  |  v1l  |  v0h  |  v0l  | */
+        " movq 8(%q1, %3, 4), %%mm2     \n\t" /* |  v3h  |  v3l  |  v2h  |  v2l  | */
+        " movd (%0), %%mm1              \n\t" /*              .. |   p1  |  p0   | */
+        " movd 4(%0), %%mm3             \n\t" /*              .. |   p3  |  p2   | */
+        VOLUME_32x16 (%%mm1, %%mm0)
+        VOLUME_32x16 (%%mm3, %%mm2)
+        " movd %%mm0, (%0)              \n\t" /*              .. | p1*v1 | p0*v0 | */
+        " movd %%mm2, 4(%0)             \n\t" /*              .. | p3*v3 | p2*v2 | */
+        " add $8, %0                    \n\t"
+        MOD_ADD ($4, %5)
+        " dec %2                        \n\t"
+        " jne 5b                        \n\t"
+
+        "6:                             \n\t"
+        " emms                          \n\t"
+
+        : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
+#if defined (__i386__)
+        : "m" (channels)
+#else
+        : "r" ((pa_reg_x86)channels)
+#endif
+        : "cc"
+    );
+}
+
+static void pa_volume_s16re_mmx(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    pa_reg_x86 channel, temp;
+
+    /* Channels must be at least 4, and always a multiple of the original number.
+     * This is also the max amount we overread the volume array, which should
+     * have enough padding. */
+    channels = channels == 3 ? 6 : PA_MAX (4U, channels);
+
+    __asm__ __volatile__ (
+        " xor %3, %3                    \n\t"
+        " sar $1, %2                    \n\t" /* length /= sizeof (int16_t) */
+        " pcmpeqw %%mm6, %%mm6          \n\t" /* .. |  ffff |  ffff | */
+        " pcmpeqw %%mm7, %%mm7          \n\t" /* .. |  ffff |  ffff | */
+        " pslld  $16, %%mm6             \n\t" /* .. |  ffff |     0 | */
+        " psrld  $31, %%mm7             \n\t" /* .. |     0 |     1 | */
+
+        " test $1, %2                   \n\t" /* check for odd samples */
+        " je 2f                         \n\t"
+
+        " movd (%q1, %3, 4), %%mm0      \n\t" /* |  v0h  |  v0l  | */
+        " movw (%0), %w4                \n\t" /*     ..  |  p0   | */
+        " rorw $8, %w4                  \n\t"
+        " movd %4, %%mm1                \n\t"
+        VOLUME_32x16 (%%mm1, %%mm0)
+        " movd %%mm0, %4                \n\t" /*     ..  | p0*v0 | */
+        " rorw $8, %w4                  \n\t"
+        " movw %w4, (%0)                \n\t"
+        " add $2, %0                    \n\t"
+        MOD_ADD ($1, %5)
+
+        "2:                             \n\t"
+        " sar $1, %2                    \n\t" /* prepare for processing 2 samples at a time */
+        " test $1, %2                   \n\t" /* check for odd samples */
+        " je 4f                         \n\t"
+
+        "3:                             \n\t" /* do samples in groups of 2 */
+        " movq (%q1, %3, 4), %%mm0      \n\t" /* |  v1h  |  v1l  |  v0h  |  v0l  | */
+        " movd (%0), %%mm1              \n\t" /*              .. |   p1  |  p0   | */
+        SWAP_16 (%%mm1)
+        VOLUME_32x16 (%%mm1, %%mm0)
+        SWAP_16 (%%mm0)
+        " movd %%mm0, (%0)              \n\t" /*              .. | p1*v1 | p0*v0 | */
+        " add $4, %0                    \n\t"
+        MOD_ADD ($2, %5)
+
+        "4:                             \n\t"
+        " sar $1, %2                    \n\t" /* prepare for processing 4 samples at a time */
+        " cmp $0, %2                    \n\t"
+        " je 6f                         \n\t"
+
+        "5:                             \n\t" /* do samples in groups of 4 */
+        " movq (%q1, %3, 4), %%mm0      \n\t" /* |  v1h  |  v1l  |  v0h  |  v0l  | */
+        " movq 8(%q1, %3, 4), %%mm2     \n\t" /* |  v3h  |  v3l  |  v2h  |  v2l  | */
+        " movd (%0), %%mm1              \n\t" /*              .. |   p1  |  p0   | */
+        " movd 4(%0), %%mm3             \n\t" /*              .. |   p3  |  p2   | */
+        SWAP_16_2 (%%mm1, %%mm3)
+        VOLUME_32x16 (%%mm1, %%mm0)
+        VOLUME_32x16 (%%mm3, %%mm2)
+        SWAP_16_2 (%%mm0, %%mm2)
+        " movd %%mm0, (%0)              \n\t" /*              .. | p1*v1 | p0*v0 | */
+        " movd %%mm2, 4(%0)             \n\t" /*              .. | p3*v3 | p2*v2 | */
+        " add $8, %0                    \n\t"
+        MOD_ADD ($4, %5)
+        " dec %2                        \n\t"
+        " jne 5b                        \n\t"
+
+        "6:                             \n\t"
+        " emms                          \n\t"
+
+        : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
+#if defined (__i386__)
+        : "m" (channels)
+#else
+        : "r" ((pa_reg_x86)channels)
+#endif
+        : "cc"
+    );
+}
+
+#endif /* (!defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && defined (__i386__)) || defined (__amd64__) */
+
+void pa_volume_func_init_mmx(pa_cpu_x86_flag_t flags) {
+#if (!defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && defined (__i386__)) || defined (__amd64__)
+    if ((flags & PA_CPU_X86_MMX) && (flags & PA_CPU_X86_CMOV)) {
+        pa_log_info("Initialising MMX optimized volume functions.");
+
+        pa_set_volume_func(PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_mmx);
+        pa_set_volume_func(PA_SAMPLE_S16RE, (pa_do_volume_func_t) pa_volume_s16re_mmx);
+    }
+#endif /* (!defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && defined (__i386__)) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/svolume_orc.c b/src/pulsecore/svolume_orc.c
new file mode 100644 (file)
index 0000000..730f817
--- /dev/null
@@ -0,0 +1,50 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+  Copyright 2010 Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "cpu-orc.h"
+#include <pulse/rtclock.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/random.h>
+#include <pulsecore/svolume-orc-gen.h>
+
+pa_do_volume_func_t fallback;
+
+static void
+pa_volume_s16ne_orc(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    if (channels == 2) {
+        int64_t v = (int64_t)volumes[1] << 32 | volumes[0];
+        pa_volume_s16ne_orc_2ch (samples, v, ((length / (sizeof(int16_t))) / 2));
+    } else if (channels == 1)
+        pa_volume_s16ne_orc_1ch (samples, volumes[0], length / (sizeof(int16_t)));
+    else
+        fallback(samples, volumes, channels, length);
+}
+
+void pa_volume_func_init_orc(void) {
+    pa_log_info("Initialising ORC optimized volume functions.");
+
+    fallback = pa_get_volume_func(PA_SAMPLE_S16NE);
+    pa_set_volume_func(PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_orc);
+}
diff --git a/src/pulsecore/svolume_sse.c b/src/pulsecore/svolume_sse.c
new file mode 100644 (file)
index 0000000..9b9663a
--- /dev/null
@@ -0,0 +1,263 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/rtclock.h>
+
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+
+#include "cpu-x86.h"
+
+#include "sample-util.h"
+
+#if (!defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && defined (__i386__)) || defined (__amd64__)
+
+#define VOLUME_32x16(s,v)                  /* .. |   vh  |   vl  | */                   \
+      " pxor %%xmm4, %%xmm4          \n\t" /* .. |    0  |    0  | */                   \
+      " punpcklwd %%xmm4, "#s"       \n\t" /* .. |    0  |   p0  | */                   \
+      " pcmpgtw "#s", %%xmm4         \n\t" /* .. |    0  | s(p0) | */                   \
+      " pand "#v", %%xmm4            \n\t" /* .. |    0  |  (vl) | */                   \
+      " movdqa "#s", %%xmm5          \n\t"                                              \
+      " pmulhuw "#v", "#s"           \n\t" /* .. |    0  | vl*p0 | */                   \
+      " psubd %%xmm4, "#s"           \n\t" /* .. |    0  | vl*p0 | + sign correct */    \
+      " psrld $16, "#v"              \n\t" /* .. |    0  |   vh  | */                   \
+      " pmaddwd %%xmm5, "#v"         \n\t" /* .. |    p0 * vh    | */                   \
+      " paddd "#s", "#v"             \n\t" /* .. |    p0 * v0    | */                   \
+      " packssdw "#v", "#v"          \n\t" /* .. | p1*v1 | p0*v0 | */
+
+#define MOD_ADD(a,b) \
+      " add "#a", %3                 \n\t" /* channel += inc           */ \
+      " mov %3, %4                   \n\t"                                \
+      " sub "#b", %4                 \n\t" /* tmp = channel - channels */ \
+      " cmovae %4, %3                \n\t" /* if (tmp >= 0) channel = tmp  */
+
+/* swap 16 bits */
+#define SWAP_16(s) \
+      " movdqa "#s", %%xmm4          \n\t" /* .. |  h  l |  */ \
+      " psrlw $8, %%xmm4             \n\t" /* .. |  0  h |  */ \
+      " psllw $8, "#s"               \n\t" /* .. |  l  0 |  */ \
+      " por %%xmm4, "#s"             \n\t" /* .. |  l  h |  */
+
+/* swap 2 registers 16 bits for better pairing */
+#define SWAP_16_2(s1,s2) \
+      " movdqa "#s1", %%xmm4         \n\t" /* .. |  h  l |  */ \
+      " movdqa "#s2", %%xmm5         \n\t"                     \
+      " psrlw $8, %%xmm4             \n\t" /* .. |  0  h |  */ \
+      " psrlw $8, %%xmm5             \n\t"                     \
+      " psllw $8, "#s1"              \n\t" /* .. |  l  0 |  */ \
+      " psllw $8, "#s2"              \n\t"                     \
+      " por %%xmm4, "#s1"            \n\t" /* .. |  l  h |  */ \
+      " por %%xmm5, "#s2"            \n\t"
+
+static int channel_overread_table[8] = {8,8,8,12,8,10,12,14};
+
+static void pa_volume_s16ne_sse2(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    pa_reg_x86 channel, temp;
+
+    /* Channels must be at least 8 and always a multiple of the original number.
+     * This is also the max amount we overread the volume array, which should
+     * have enough padding. */
+    if (channels < 8)
+        channels = channel_overread_table[channels];
+
+    __asm__ __volatile__ (
+        " xor %3, %3                    \n\t"
+        " sar $1, %2                    \n\t" /* length /= sizeof (int16_t) */
+
+        " test $1, %2                   \n\t" /* check for odd samples */
+        " je 2f                         \n\t"
+
+        " movd (%q1, %3, 4), %%xmm0     \n\t" /* |  v0h  |  v0l  | */
+        " movw (%0), %w4                \n\t" /*     ..  |   p0  | */
+        " movd %4, %%xmm1               \n\t"
+        VOLUME_32x16 (%%xmm1, %%xmm0)
+        " movd %%xmm0, %4               \n\t" /*     ..  | p0*v0 | */
+        " movw %w4, (%0)                \n\t"
+        " add $2, %0                    \n\t"
+        MOD_ADD ($1, %5)
+
+        "2:                             \n\t"
+        " sar $1, %2                    \n\t" /* prepare for processing 2 samples at a time */
+        " test $1, %2                   \n\t"
+        " je 4f                         \n\t"
+
+        "3:                             \n\t" /* do samples in groups of 2 */
+        " movq (%q1, %3, 4), %%xmm0     \n\t" /* |  v1h  |  v1l  |  v0h  |  v0l  | */
+        " movd (%0), %%xmm1             \n\t" /*              .. |   p1  |  p0   | */
+        VOLUME_32x16 (%%xmm1, %%xmm0)
+        " movd %%xmm0, (%0)             \n\t" /*              .. | p1*v1 | p0*v0 | */
+        " add $4, %0                    \n\t"
+        MOD_ADD ($2, %5)
+
+        "4:                             \n\t"
+        " sar $1, %2                    \n\t" /* prepare for processing 4 samples at a time */
+        " test $1, %2                   \n\t"
+        " je 6f                         \n\t"
+
+        /* FIXME, we can do aligned access of the volume values if we can guarantee
+         * that the array is 16 bytes aligned, we probably have to do the odd values
+         * after this then. */
+        "5:                             \n\t" /* do samples in groups of 4 */
+        " movdqu (%q1, %3, 4), %%xmm0   \n\t" /* |  v3h  |  v3l  ..  v0h  |  v0l  | */
+        " movq (%0), %%xmm1             \n\t" /*              .. |   p3  ..  p0   | */
+        VOLUME_32x16 (%%xmm1, %%xmm0)
+        " movq %%xmm0, (%0)             \n\t" /*              .. | p3*v3 .. p0*v0 | */
+        " add $8, %0                    \n\t"
+        MOD_ADD ($4, %5)
+
+        "6:                             \n\t"
+        " sar $1, %2                    \n\t" /* prepare for processing 8 samples at a time */
+        " cmp $0, %2                    \n\t"
+        " je 8f                         \n\t"
+
+        "7:                             \n\t" /* do samples in groups of 8 */
+        " movdqu (%q1, %3, 4), %%xmm0   \n\t" /* |  v3h  |  v3l  ..  v0h  |  v0l  | */
+        " movdqu 16(%q1, %3, 4), %%xmm2 \n\t" /* |  v7h  |  v7l  ..  v4h  |  v4l  | */
+        " movq (%0), %%xmm1             \n\t" /*              .. |   p3  ..  p0   | */
+        " movq 8(%0), %%xmm3            \n\t" /*              .. |   p7  ..  p4   | */
+        VOLUME_32x16 (%%xmm1, %%xmm0)
+        VOLUME_32x16 (%%xmm3, %%xmm2)
+        " movq %%xmm0, (%0)             \n\t" /*              .. | p3*v3 .. p0*v0 | */
+        " movq %%xmm2, 8(%0)            \n\t" /*              .. | p7*v7 .. p4*v4 | */
+        " add $16, %0                   \n\t"
+        MOD_ADD ($8, %5)
+        " dec %2                        \n\t"
+        " jne 7b                        \n\t"
+        "8:                             \n\t"
+
+        : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
+#if defined (__i386__)
+        : "m" (channels)
+#else
+        : "r" ((pa_reg_x86)channels)
+#endif
+        : "cc"
+    );
+}
+
+static void pa_volume_s16re_sse2(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+    pa_reg_x86 channel, temp;
+
+    /* Channels must be at least 8 and always a multiple of the original number.
+     * This is also the max amount we overread the volume array, which should
+     * have enough padding. */
+    if (channels < 8)
+        channels = channel_overread_table[channels];
+
+    __asm__ __volatile__ (
+        " xor %3, %3                    \n\t"
+        " sar $1, %2                    \n\t" /* length /= sizeof (int16_t) */
+
+        " test $1, %2                   \n\t" /* check for odd samples */
+        " je 2f                         \n\t"
+
+        " movd (%q1, %3, 4), %%xmm0     \n\t" /* |  v0h  |  v0l  | */
+        " movw (%0), %w4                \n\t" /*     ..  |   p0  | */
+        " rorw $8, %w4                  \n\t"
+        " movd %4, %%xmm1               \n\t"
+        VOLUME_32x16 (%%xmm1, %%xmm0)
+        " movd %%xmm0, %4               \n\t" /*     ..  | p0*v0 | */
+        " rorw $8, %w4                  \n\t"
+        " movw %w4, (%0)                \n\t"
+        " add $2, %0                    \n\t"
+        MOD_ADD ($1, %5)
+
+        "2:                             \n\t"
+        " sar $1, %2                    \n\t" /* prepare for processing 2 samples at a time */
+        " test $1, %2                   \n\t"
+        " je 4f                         \n\t"
+
+        "3:                             \n\t" /* do samples in groups of 2 */
+        " movq (%q1, %3, 4), %%xmm0     \n\t" /* |  v1h  |  v1l  |  v0h  |  v0l  | */
+        " movd (%0), %%xmm1             \n\t" /*              .. |   p1  |  p0   | */
+        SWAP_16 (%%xmm1)
+        VOLUME_32x16 (%%xmm1, %%xmm0)
+        SWAP_16 (%%xmm0)
+        " movd %%xmm0, (%0)             \n\t" /*              .. | p1*v1 | p0*v0 | */
+        " add $4, %0                    \n\t"
+        MOD_ADD ($2, %5)
+
+        "4:                             \n\t"
+        " sar $1, %2                    \n\t" /* prepare for processing 4 samples at a time */
+        " test $1, %2                   \n\t"
+        " je 6f                         \n\t"
+
+        /* FIXME, we can do aligned access of the volume values if we can guarantee
+         * that the array is 16 bytes aligned, we probably have to do the odd values
+         * after this then. */
+        "5:                             \n\t" /* do samples in groups of 4 */
+        " movdqu (%q1, %3, 4), %%xmm0   \n\t" /* |  v3h  |  v3l  ..  v0h  |  v0l  | */
+        " movq (%0), %%xmm1             \n\t" /*              .. |   p3  ..  p0   | */
+        SWAP_16 (%%xmm1)
+        VOLUME_32x16 (%%xmm1, %%xmm0)
+        SWAP_16 (%%xmm0)
+        " movq %%xmm0, (%0)             \n\t" /*              .. | p3*v3 .. p0*v0 | */
+        " add $8, %0                    \n\t"
+        MOD_ADD ($4, %5)
+
+        "6:                             \n\t"
+        " sar $1, %2                    \n\t" /* prepare for processing 8 samples at a time */
+        " cmp $0, %2                    \n\t"
+        " je 8f                         \n\t"
+
+        "7:                             \n\t" /* do samples in groups of 8 */
+        " movdqu (%q1, %3, 4), %%xmm0   \n\t" /* |  v3h  |  v3l  ..  v0h  |  v0l  | */
+        " movdqu 16(%q1, %3, 4), %%xmm2 \n\t" /* |  v7h  |  v7l  ..  v4h  |  v4l  | */
+        " movq (%0), %%xmm1             \n\t" /*              .. |   p3  ..  p0   | */
+        " movq 8(%0), %%xmm3            \n\t" /*              .. |   p7  ..  p4   | */
+        SWAP_16_2 (%%xmm1, %%xmm3)
+        VOLUME_32x16 (%%xmm1, %%xmm0)
+        VOLUME_32x16 (%%xmm3, %%xmm2)
+        SWAP_16_2 (%%xmm0, %%xmm2)
+        " movq %%xmm0, (%0)             \n\t" /*              .. | p3*v3 .. p0*v0 | */
+        " movq %%xmm2, 8(%0)            \n\t" /*              .. | p7*v7 .. p4*v4 | */
+        " add $16, %0                   \n\t"
+        MOD_ADD ($8, %5)
+        " dec %2                        \n\t"
+        " jne 7b                        \n\t"
+        "8:                             \n\t"
+
+        : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
+#if defined (__i386__)
+        : "m" (channels)
+#else
+        : "r" ((pa_reg_x86)channels)
+#endif
+        : "cc"
+    );
+}
+
+#endif /* (!defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && defined (__i386__)) || defined (__amd64__) */
+
+void pa_volume_func_init_sse(pa_cpu_x86_flag_t flags) {
+#if (!defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && defined (__i386__)) || defined (__amd64__)
+    if (flags & PA_CPU_X86_SSE2) {
+        pa_log_info("Initialising SSE2 optimized volume functions.");
+
+        pa_set_volume_func(PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_sse2);
+        pa_set_volume_func(PA_SAMPLE_S16RE, (pa_do_volume_func_t) pa_volume_s16re_sse2);
+    }
+#endif /* (!defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && defined (__i386__)) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
new file mode 100644 (file)
index 0000000..8a29957
--- /dev/null
@@ -0,0 +1,800 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <stdarg.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/socket.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+
+#include "tagstruct.h"
+
+#define MAX_TAG_SIZE (64*1024)
+#define MAX_APPENDED_SIZE 128
+#define GROW_TAG_SIZE 100
+
+struct pa_tagstruct {
+    uint8_t *data;
+    size_t length, allocated;
+    size_t rindex;
+
+    enum {
+        PA_TAGSTRUCT_FIXED, /* The tagstruct does not own the data, buffer was provided by caller. */
+        PA_TAGSTRUCT_DYNAMIC, /* Buffer owned by tagstruct, data must be freed. */
+        PA_TAGSTRUCT_APPENDED, /* Data points to appended buffer, used for small tagstructs. Will change to dynamic if needed. */
+    } type;
+    union {
+        uint8_t appended[MAX_APPENDED_SIZE];
+    } per_type;
+};
+
+PA_STATIC_FLIST_DECLARE(tagstructs, 0, pa_xfree);
+
+pa_tagstruct *pa_tagstruct_new(void) {
+    pa_tagstruct*t;
+
+    if (!(t = pa_flist_pop(PA_STATIC_FLIST_GET(tagstructs))))
+        t = pa_xnew(pa_tagstruct, 1);
+    t->data = t->per_type.appended;
+    t->allocated = MAX_APPENDED_SIZE;
+    t->length = t->rindex = 0;
+    t->type = PA_TAGSTRUCT_APPENDED;
+
+    return t;
+}
+
+pa_tagstruct *pa_tagstruct_new_fixed(const uint8_t* data, size_t length) {
+    pa_tagstruct*t;
+
+    pa_assert(data && length);
+
+    if (!(t = pa_flist_pop(PA_STATIC_FLIST_GET(tagstructs))))
+        t = pa_xnew(pa_tagstruct, 1);
+    t->data = (uint8_t*) data;
+    t->allocated = t->length = length;
+    t->rindex = 0;
+    t->type = PA_TAGSTRUCT_FIXED;
+
+    return t;
+}
+
+void pa_tagstruct_free(pa_tagstruct*t) {
+    pa_assert(t);
+
+    if (t->type == PA_TAGSTRUCT_DYNAMIC)
+        pa_xfree(t->data);
+    if (pa_flist_push(PA_STATIC_FLIST_GET(tagstructs), t) < 0)
+        pa_xfree(t);
+}
+
+static inline void extend(pa_tagstruct*t, size_t l) {
+    pa_assert(t);
+    pa_assert(t->type != PA_TAGSTRUCT_FIXED);
+
+    if (t->length+l <= t->allocated)
+        return;
+
+    if (t->type == PA_TAGSTRUCT_DYNAMIC)
+        t->data = pa_xrealloc(t->data, t->allocated = t->length + l + GROW_TAG_SIZE);
+    else if (t->type == PA_TAGSTRUCT_APPENDED) {
+        t->type = PA_TAGSTRUCT_DYNAMIC;
+        t->data = pa_xmalloc(t->allocated = t->length + l + GROW_TAG_SIZE);
+        memcpy(t->data, t->per_type.appended, t->length);
+    }
+}
+
+static void write_u8(pa_tagstruct *t, uint8_t u) {
+    extend(t, 1);
+    t->data[t->length++] = u;
+}
+
+static int read_u8(pa_tagstruct *t, uint8_t *u) {
+    if (t->rindex + 1 > t->length)
+        return -1;
+
+    *u = t->data[t->rindex++];
+    return 0;
+}
+
+static void write_u32(pa_tagstruct *t, uint32_t u) {
+    extend(t, 4);
+    u = htonl(u);
+    memcpy(t->data + t->length, &u, 4);
+    t->length += 4;
+}
+
+static int read_u32(pa_tagstruct *t, uint32_t *u) {
+    if (t->rindex + 4 > t->length)
+        return -1;
+
+    memcpy(u, t->data + t->rindex, 4);
+    *u = ntohl(*u);
+    t->rindex += 4;
+
+    return 0;
+}
+
+static void write_u64(pa_tagstruct *t, uint64_t u) {
+    write_u32(t, u >> 32);
+    write_u32(t, u);
+}
+
+static int read_u64(pa_tagstruct *t, uint64_t *u) {
+    uint32_t tmp;
+
+    if (read_u32(t, &tmp) < 0)
+        return -1;
+
+    *u = ((uint64_t) tmp) << 32;
+
+    if (read_u32(t, &tmp) < 0)
+        return -1;
+
+    *u |= tmp;
+    return 0;
+}
+
+static int read_s64(pa_tagstruct *t, int64_t *u) {
+    uint32_t tmp;
+
+    if (read_u32(t, &tmp) < 0)
+        return -1;
+
+    *u = (int64_t) (((uint64_t) tmp) << 32);
+
+    if (read_u32(t, &tmp) < 0)
+        return -1;
+
+    *u |= (int64_t) tmp;
+    return 0;
+}
+
+static void write_arbitrary(pa_tagstruct *t, const void *p, size_t len) {
+    extend(t, len);
+
+    if (len > 0)
+        memcpy(t->data + t->length, p, len);
+
+    t->length += len;
+}
+
+static int read_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
+    if (t->rindex + length > t->length)
+        return -1;
+
+    *p = t->data + t->rindex;
+    t->rindex += length;
+    return 0;
+}
+
+void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
+    size_t l;
+    pa_assert(t);
+
+    if (s) {
+        write_u8(t, PA_TAG_STRING);
+        l = strlen(s)+1;
+        write_arbitrary(t, s, l);
+    } else
+        write_u8(t, PA_TAG_STRING_NULL);
+}
+
+void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
+    pa_assert(t);
+
+    write_u8(t, PA_TAG_U32);
+    write_u32(t, i);
+}
+
+void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
+    pa_assert(t);
+
+    write_u8(t, PA_TAG_U8);
+    write_u8(t, c);
+}
+
+void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
+    pa_assert(t);
+    pa_assert(ss);
+
+    write_u8(t, PA_TAG_SAMPLE_SPEC);
+    write_u8(t, ss->format);
+    write_u8(t, ss->channels);
+    write_u32(t, ss->rate);
+}
+
+void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
+    pa_assert(t);
+    pa_assert(p);
+
+    write_u8(t, PA_TAG_ARBITRARY);
+    write_u32(t, length);
+    write_arbitrary(t, p, length);
+}
+
+void pa_tagstruct_put_boolean(pa_tagstruct*t, bool b) {
+    pa_assert(t);
+
+    write_u8(t, b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE);
+}
+
+void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
+    pa_assert(t);
+
+    write_u8(t, PA_TAG_TIMEVAL);
+    write_u32(t, tv->tv_sec);
+    write_u32(t, tv->tv_usec);
+}
+
+void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
+    pa_assert(t);
+
+    write_u8(t, PA_TAG_USEC);
+    write_u64(t, u);
+}
+
+void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
+    pa_assert(t);
+
+    write_u8(t, PA_TAG_U64);
+    write_u64(t, u);
+}
+
+void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {
+    pa_assert(t);
+
+    write_u8(t, PA_TAG_S64);
+    write_u64(t, u);
+}
+
+void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {
+    unsigned i;
+
+    pa_assert(t);
+    pa_assert(map);
+
+    write_u8(t, PA_TAG_CHANNEL_MAP);
+    write_u8(t, map->channels);
+
+    for (i = 0; i < map->channels; i ++)
+        write_u8(t, map->map[i]);
+}
+
+void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
+    unsigned i;
+
+    pa_assert(t);
+    pa_assert(cvolume);
+
+    write_u8(t, PA_TAG_CVOLUME);
+    write_u8(t, cvolume->channels);
+
+    for (i = 0; i < cvolume->channels; i ++)
+        write_u32(t, cvolume->values[i]);
+}
+
+void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t vol) {
+    pa_assert(t);
+
+    write_u8(t, PA_TAG_VOLUME);
+    write_u32(t, vol);
+}
+
+void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) {
+    void *state = NULL;
+    pa_assert(t);
+    pa_assert(p);
+
+    write_u8(t, PA_TAG_PROPLIST);
+
+    for (;;) {
+        const char *k;
+        const void *d;
+        size_t l;
+
+        if (!(k = pa_proplist_iterate(p, &state)))
+            break;
+
+        pa_tagstruct_puts(t, k);
+        pa_assert_se(pa_proplist_get(p, k, &d, &l) >= 0);
+        pa_tagstruct_putu32(t, (uint32_t) l);
+        pa_tagstruct_put_arbitrary(t, d, l);
+    }
+
+    pa_tagstruct_puts(t, NULL);
+}
+
+void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f) {
+    pa_assert(t);
+    pa_assert(f);
+
+    write_u8(t, PA_TAG_FORMAT_INFO);
+    pa_tagstruct_putu8(t, (uint8_t) f->encoding);
+    pa_tagstruct_put_proplist(t, f->plist);
+}
+
+static int read_tag(pa_tagstruct *t, uint8_t type) {
+    if (t->rindex + 1 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != type)
+        return -1;
+
+    t->rindex++;
+
+    return 0;
+}
+
+int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
+    int error = 0;
+    size_t n;
+    char *c;
+
+    pa_assert(t);
+    pa_assert(s);
+
+    if (t->rindex+1 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] == PA_TAG_STRING_NULL) {
+        t->rindex++;
+        *s = NULL;
+        return 0;
+    }
+
+    if (read_tag(t, PA_TAG_STRING) < 0)
+        return -1;
+
+    if (t->rindex + 1 > t->length)
+        return -1;
+
+    error = 1;
+    for (n = 0, c = (char*) (t->data + t->rindex); t->rindex + n < t->length; n++, c++)
+        if (!*c) {
+            error = 0;
+            break;
+        }
+
+    if (error)
+        return -1;
+
+    *s = (char*) (t->data + t->rindex);
+
+    t->rindex += n + 1;
+    return 0;
+}
+
+int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) {
+    pa_assert(t);
+    pa_assert(i);
+
+    if (read_tag(t, PA_TAG_U32) < 0)
+        return -1;
+
+    return read_u32(t, i);
+}
+
+int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) {
+    pa_assert(t);
+    pa_assert(c);
+
+    if (read_tag(t, PA_TAG_U8) < 0)
+        return -1;
+
+    return read_u8(t, c);
+}
+
+int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {
+    uint8_t tmp;
+
+    pa_assert(t);
+    pa_assert(ss);
+
+    if (read_tag(t, PA_TAG_SAMPLE_SPEC) < 0)
+        return -1;
+
+    if (read_u8(t, &tmp) < 0)
+        return -1;
+
+    ss->format = tmp;
+
+    if (read_u8(t, &ss->channels) < 0)
+        return -1;
+
+    return read_u32(t, &ss->rate);
+}
+
+int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
+    uint32_t len;
+
+    pa_assert(t);
+    pa_assert(p);
+
+    if (read_tag(t, PA_TAG_ARBITRARY) < 0)
+        return -1;
+
+    if (read_u32(t, &len) < 0 || len != length)
+        return -1;
+
+    return read_arbitrary(t, p, length);
+}
+
+int pa_tagstruct_eof(pa_tagstruct*t) {
+    pa_assert(t);
+
+    return t->rindex >= t->length;
+}
+
+const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) {
+    pa_assert(t);
+    pa_assert(l);
+
+    *l = t->length;
+    return t->data;
+}
+
+int pa_tagstruct_get_boolean(pa_tagstruct*t, bool *b) {
+    pa_assert(t);
+    pa_assert(b);
+
+    if (t->rindex+1 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE)
+        *b = true;
+    else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE)
+        *b = false;
+    else
+        return -1;
+
+    t->rindex +=1;
+    return 0;
+}
+
+int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
+    uint32_t tmp;
+
+    pa_assert(t);
+    pa_assert(tv);
+
+    if (read_tag(t, PA_TAG_TIMEVAL) < 0)
+        return -1;
+
+    if (read_u32(t, &tmp) < 0)
+        return -1;
+
+    tv->tv_sec = tmp;
+
+    if (read_u32(t, &tmp) < 0)
+        return -1;
+
+    tv->tv_usec = tmp;
+
+    return 0;
+}
+
+int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {
+    pa_assert(t);
+    pa_assert(u);
+
+    if (read_tag(t, PA_TAG_USEC) < 0)
+        return -1;
+
+    return read_u64(t, u);
+}
+
+int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
+    pa_assert(t);
+    pa_assert(u);
+
+    if (read_tag(t, PA_TAG_U64) < 0)
+        return -1;
+
+    return read_u64(t, u);
+}
+
+int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {
+    pa_assert(t);
+    pa_assert(u);
+
+    if (read_tag(t, PA_TAG_S64) < 0)
+        return -1;
+
+    return read_s64(t, u);
+}
+
+int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {
+    unsigned i;
+
+    pa_assert(t);
+    pa_assert(map);
+
+    if (read_tag(t, PA_TAG_CHANNEL_MAP) < 0)
+        return -1;
+
+    if (read_u8(t, &map->channels) < 0 || map->channels > PA_CHANNELS_MAX)
+        return -1;
+
+    for (i = 0; i < map->channels; i ++) {
+        uint8_t tmp;
+
+        if (read_u8(t, &tmp) < 0)
+            return -1;
+
+        map->map[i] = tmp;
+    }
+
+    return 0;
+}
+
+int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
+    unsigned i;
+
+    pa_assert(t);
+    pa_assert(cvolume);
+
+    if (read_tag(t, PA_TAG_CVOLUME) < 0)
+        return -1;
+
+    if (read_u8(t, &cvolume->channels) < 0 || cvolume->channels > PA_CHANNELS_MAX)
+        return -1;
+
+    for (i = 0; i < cvolume->channels; i ++) {
+        if (read_u32(t, &cvolume->values[i]) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+int pa_tagstruct_get_volume(pa_tagstruct*t, pa_volume_t *vol) {
+    pa_assert(t);
+    pa_assert(vol);
+
+    if (read_tag(t, PA_TAG_VOLUME) < 0)
+        return -1;
+
+    return read_u32(t, vol);
+}
+
+int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) {
+    pa_assert(t);
+
+    if (read_tag(t, PA_TAG_PROPLIST) < 0)
+        return -1;
+
+    for (;;) {
+        const char *k;
+        const void *d;
+        uint32_t length;
+
+        if (pa_tagstruct_gets(t, &k) < 0)
+            return -1;
+
+        if (!k)
+            break;
+
+        if (!pa_proplist_key_valid(k))
+            return -1;
+
+        if (pa_tagstruct_getu32(t, &length) < 0)
+            return -1;
+
+        if (length > MAX_TAG_SIZE)
+            return -1;
+
+        if (pa_tagstruct_get_arbitrary(t, &d, length) < 0)
+            return -1;
+
+        if (p)
+            pa_assert_se(pa_proplist_set(p, k, d, length) >= 0);
+    }
+
+    return 0;
+}
+
+int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f) {
+    uint8_t encoding;
+
+    pa_assert(t);
+    pa_assert(f);
+
+    if (read_tag(t, PA_TAG_FORMAT_INFO) < 0)
+        return -1;
+
+    if (pa_tagstruct_getu8(t, &encoding) < 0)
+        return -1;
+
+    f->encoding = encoding;
+
+    return pa_tagstruct_get_proplist(t, f->plist);
+}
+
+void pa_tagstruct_put(pa_tagstruct *t, ...) {
+    va_list va;
+    pa_assert(t);
+
+    va_start(va, t);
+
+    for (;;) {
+        int tag = va_arg(va, int);
+
+        if (tag == PA_TAG_INVALID)
+            break;
+
+        switch (tag) {
+            case PA_TAG_STRING:
+            case PA_TAG_STRING_NULL:
+                pa_tagstruct_puts(t, va_arg(va, char*));
+                break;
+
+            case PA_TAG_U32:
+                pa_tagstruct_putu32(t, va_arg(va, uint32_t));
+                break;
+
+            case PA_TAG_U8:
+                pa_tagstruct_putu8(t, (uint8_t) va_arg(va, int));
+                break;
+
+            case PA_TAG_U64:
+                pa_tagstruct_putu64(t, va_arg(va, uint64_t));
+                break;
+
+            case PA_TAG_SAMPLE_SPEC:
+                pa_tagstruct_put_sample_spec(t, va_arg(va, pa_sample_spec*));
+                break;
+
+            case PA_TAG_ARBITRARY: {
+                void *p = va_arg(va, void*);
+                size_t size = va_arg(va, size_t);
+                pa_tagstruct_put_arbitrary(t, p, size);
+                break;
+            }
+
+            case PA_TAG_BOOLEAN_TRUE:
+            case PA_TAG_BOOLEAN_FALSE:
+                pa_tagstruct_put_boolean(t, va_arg(va, int));
+                break;
+
+            case PA_TAG_TIMEVAL:
+                pa_tagstruct_put_timeval(t, va_arg(va, struct timeval*));
+                break;
+
+            case PA_TAG_USEC:
+                pa_tagstruct_put_usec(t, va_arg(va, pa_usec_t));
+                break;
+
+            case PA_TAG_CHANNEL_MAP:
+                pa_tagstruct_put_channel_map(t, va_arg(va, pa_channel_map *));
+                break;
+
+            case PA_TAG_CVOLUME:
+                pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *));
+                break;
+
+            case PA_TAG_VOLUME:
+                pa_tagstruct_put_volume(t, va_arg(va, pa_volume_t));
+                break;
+
+            case PA_TAG_PROPLIST:
+                pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *));
+                break;
+
+            default:
+                pa_assert_not_reached();
+        }
+    }
+
+    va_end(va);
+}
+
+int pa_tagstruct_get(pa_tagstruct *t, ...) {
+    va_list va;
+    int ret = 0;
+
+    pa_assert(t);
+
+    va_start(va, t);
+    while (ret == 0) {
+        int tag = va_arg(va, int);
+
+        if (tag == PA_TAG_INVALID)
+            break;
+
+        switch (tag) {
+            case PA_TAG_STRING:
+            case PA_TAG_STRING_NULL:
+                ret = pa_tagstruct_gets(t, va_arg(va, const char**));
+                break;
+
+            case PA_TAG_U32:
+                ret = pa_tagstruct_getu32(t, va_arg(va, uint32_t*));
+                break;
+
+            case PA_TAG_U8:
+                ret = pa_tagstruct_getu8(t, va_arg(va, uint8_t*));
+                break;
+
+            case PA_TAG_U64:
+                ret = pa_tagstruct_getu64(t, va_arg(va, uint64_t*));
+                break;
+
+            case PA_TAG_SAMPLE_SPEC:
+                ret = pa_tagstruct_get_sample_spec(t, va_arg(va, pa_sample_spec*));
+                break;
+
+            case PA_TAG_ARBITRARY: {
+                const void **p = va_arg(va, const void**);
+                size_t size = va_arg(va, size_t);
+                ret = pa_tagstruct_get_arbitrary(t, p, size);
+                break;
+            }
+
+            case PA_TAG_BOOLEAN_TRUE:
+            case PA_TAG_BOOLEAN_FALSE:
+                ret = pa_tagstruct_get_boolean(t, va_arg(va, bool*));
+                break;
+
+            case PA_TAG_TIMEVAL:
+                ret = pa_tagstruct_get_timeval(t, va_arg(va, struct timeval*));
+                break;
+
+            case PA_TAG_USEC:
+                ret = pa_tagstruct_get_usec(t, va_arg(va, pa_usec_t*));
+                break;
+
+            case PA_TAG_CHANNEL_MAP:
+                ret = pa_tagstruct_get_channel_map(t, va_arg(va, pa_channel_map *));
+                break;
+
+            case PA_TAG_CVOLUME:
+                ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));
+                break;
+
+            case PA_TAG_VOLUME:
+                ret = pa_tagstruct_get_volume(t, va_arg(va, pa_volume_t *));
+                break;
+
+            case PA_TAG_PROPLIST:
+                ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *));
+                break;
+
+            default:
+                pa_assert_not_reached();
+        }
+
+    }
+
+    va_end(va);
+    return ret;
+}
diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
new file mode 100644 (file)
index 0000000..348c65d
--- /dev/null
@@ -0,0 +1,106 @@
+#ifndef footagstructhfoo
+#define footagstructhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <pulse/sample.h>
+#include <pulse/format.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+#include <pulse/proplist.h>
+
+#include <pulsecore/macro.h>
+
+typedef struct pa_tagstruct pa_tagstruct;
+
+/* Due to a stupid design flaw, proplists may only be at the END of a
+ * packet or not before a STRING! Don't forget that! We can't really
+ * fix this without breaking compat. */
+
+enum {
+    PA_TAG_INVALID = 0,
+    PA_TAG_STRING = 't',
+    PA_TAG_STRING_NULL = 'N',
+    PA_TAG_U32 = 'L',
+    PA_TAG_U8 = 'B',
+    PA_TAG_U64 = 'R',
+    PA_TAG_S64 = 'r',
+    PA_TAG_SAMPLE_SPEC = 'a',
+    PA_TAG_ARBITRARY = 'x',
+    PA_TAG_BOOLEAN_TRUE = '1',
+    PA_TAG_BOOLEAN_FALSE = '0',
+    PA_TAG_BOOLEAN = PA_TAG_BOOLEAN_TRUE,
+    PA_TAG_TIMEVAL = 'T',
+    PA_TAG_USEC = 'U'  /* 64bit unsigned */,
+    PA_TAG_CHANNEL_MAP = 'm',
+    PA_TAG_CVOLUME = 'v',
+    PA_TAG_PROPLIST = 'P',
+    PA_TAG_VOLUME = 'V',
+    PA_TAG_FORMAT_INFO = 'f',
+};
+
+pa_tagstruct *pa_tagstruct_new(void);
+pa_tagstruct *pa_tagstruct_new_fixed(const uint8_t* data, size_t length);
+void pa_tagstruct_free(pa_tagstruct*t);
+
+int pa_tagstruct_eof(pa_tagstruct*t);
+const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l);
+
+void pa_tagstruct_put(pa_tagstruct *t, ...);
+
+void pa_tagstruct_puts(pa_tagstruct*t, const char *s);
+void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c);
+void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i);
+void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t i);
+void pa_tagstruct_puts64(pa_tagstruct*t, int64_t i);
+void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss);
+void pa_tagstruct_put_arbitrary(pa_tagstruct*t, const void *p, size_t length);
+void pa_tagstruct_put_boolean(pa_tagstruct*t, bool b);
+void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv);
+void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u);
+void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map);
+void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume);
+void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p);
+void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t volume);
+void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f);
+
+int pa_tagstruct_get(pa_tagstruct *t, ...);
+
+int pa_tagstruct_gets(pa_tagstruct*t, const char **s);
+int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c);
+int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i);
+int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *i);
+int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *i);
+int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss);
+int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length);
+int pa_tagstruct_get_boolean(pa_tagstruct *t, bool *b);
+int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv);
+int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u);
+int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map);
+int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v);
+int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p);
+int pa_tagstruct_get_volume(pa_tagstruct *t, pa_volume_t *v);
+int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f);
+
+#endif
diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c
new file mode 100644 (file)
index 0000000..eaa8663
--- /dev/null
@@ -0,0 +1,223 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/thread.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+
+#include <pulse/mainloop-api.h>
+
+#include "thread-mq.h"
+
+PA_STATIC_TLS_DECLARE_NO_FREE(thread_mq);
+
+static void asyncmsgq_read_cb(pa_mainloop_api *api, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    pa_thread_mq *q = userdata;
+    pa_asyncmsgq *aq;
+
+    pa_assert(events == PA_IO_EVENT_INPUT);
+
+    if (pa_asyncmsgq_read_fd(q->outq) == fd)
+        pa_asyncmsgq_ref(aq = q->outq);
+    else if (pa_asyncmsgq_read_fd(q->inq) == fd)
+        pa_asyncmsgq_ref(aq = q->inq);
+    else
+        pa_assert_not_reached();
+
+    pa_asyncmsgq_read_after_poll(aq);
+
+    for (;;) {
+        pa_msgobject *object;
+        int code;
+        void *data;
+        int64_t offset;
+        pa_memchunk chunk;
+
+        /* Check whether there is a message for us to process */
+        while (pa_asyncmsgq_get(aq, &object, &code, &data, &offset, &chunk, 0) >= 0) {
+            int ret;
+
+            if (!object && code == PA_MESSAGE_SHUTDOWN) {
+                pa_asyncmsgq_done(aq, 0);
+                api->quit(api, 0);
+                break;
+            }
+
+            ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+            pa_asyncmsgq_done(aq, ret);
+        }
+
+        if (pa_asyncmsgq_read_before_poll(aq) == 0)
+            break;
+    }
+
+    pa_asyncmsgq_unref(aq);
+}
+
+static void asyncmsgq_write_inq_cb(pa_mainloop_api *api, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    pa_thread_mq *q = userdata;
+
+    pa_assert(pa_asyncmsgq_write_fd(q->inq) == fd);
+    pa_assert(events == PA_IO_EVENT_INPUT);
+
+    pa_asyncmsgq_write_after_poll(q->inq);
+    pa_asyncmsgq_write_before_poll(q->inq);
+}
+
+static void asyncmsgq_write_outq_cb(pa_mainloop_api *api, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    pa_thread_mq *q = userdata;
+
+    pa_assert(pa_asyncmsgq_write_fd(q->outq) == fd);
+    pa_assert(events == PA_IO_EVENT_INPUT);
+
+    pa_asyncmsgq_write_after_poll(q->outq);
+    pa_asyncmsgq_write_before_poll(q->outq);
+}
+
+int pa_thread_mq_init_thread_mainloop(pa_thread_mq *q, pa_mainloop_api *main_mainloop, pa_mainloop_api *thread_mainloop) {
+    pa_assert(q);
+    pa_assert(main_mainloop);
+    pa_assert(thread_mainloop);
+
+    pa_zero(*q);
+
+    q->inq = pa_asyncmsgq_new(0);
+    if (!q->inq)
+        goto fail;
+
+    q->outq = pa_asyncmsgq_new(0);
+    if (!q->outq)
+        goto fail;
+
+    q->main_mainloop = main_mainloop;
+    q->thread_mainloop = thread_mainloop;
+
+    pa_assert_se(pa_asyncmsgq_read_before_poll(q->outq) == 0);
+    pa_asyncmsgq_write_before_poll(q->outq);
+    pa_assert_se(q->read_main_event = main_mainloop->io_new(main_mainloop, pa_asyncmsgq_read_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_read_cb, q));
+    pa_assert_se(q->write_thread_event = main_mainloop->io_new(thread_mainloop, pa_asyncmsgq_write_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_write_outq_cb, q));
+
+    pa_asyncmsgq_read_before_poll(q->inq);
+    pa_asyncmsgq_write_before_poll(q->inq);
+    pa_assert_se(q->read_thread_event = thread_mainloop->io_new(thread_mainloop, pa_asyncmsgq_read_fd(q->inq), PA_IO_EVENT_INPUT, asyncmsgq_read_cb, q));
+    pa_assert_se(q->write_main_event = main_mainloop->io_new(main_mainloop, pa_asyncmsgq_write_fd(q->inq), PA_IO_EVENT_INPUT, asyncmsgq_write_inq_cb, q));
+
+    return 0;
+
+fail:
+    pa_thread_mq_done(q);
+
+    return -1;
+}
+
+int pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll) {
+    pa_assert(q);
+    pa_assert(mainloop);
+
+    pa_zero(*q);
+
+    q->main_mainloop = mainloop;
+    q->thread_mainloop = NULL;
+
+    q->inq = pa_asyncmsgq_new(0);
+    if (!q->inq)
+        goto fail;
+
+    q->outq = pa_asyncmsgq_new(0);
+    if (!q->outq)
+        goto fail;
+
+    pa_assert_se(pa_asyncmsgq_read_before_poll(q->outq) == 0);
+    pa_assert_se(q->read_main_event = mainloop->io_new(mainloop, pa_asyncmsgq_read_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_read_cb, q));
+
+    pa_asyncmsgq_write_before_poll(q->inq);
+    pa_assert_se(q->write_main_event = mainloop->io_new(mainloop, pa_asyncmsgq_write_fd(q->inq), PA_IO_EVENT_INPUT, asyncmsgq_write_inq_cb, q));
+
+    pa_rtpoll_item_new_asyncmsgq_read(rtpoll, PA_RTPOLL_EARLY, q->inq);
+    pa_rtpoll_item_new_asyncmsgq_write(rtpoll, PA_RTPOLL_LATE, q->outq);
+
+    return 0;
+
+fail:
+    pa_thread_mq_done(q);
+
+    return -1;
+}
+
+void pa_thread_mq_done(pa_thread_mq *q) {
+    pa_assert(q);
+
+    /* Since we are called from main context we can be sure that the
+     * inq is empty. However, the outq might still contain messages
+     * for the main loop, which we need to dispatch (e.g. release
+     * msgs, other stuff). Hence do so if we aren't currently
+     * dispatching anyway. */
+
+    if (q->outq && !pa_asyncmsgq_dispatching(q->outq)) {
+        /* Flushing the asyncmsgq can cause arbitrarily callbacks to run,
+           potentially causing recursion into pa_thread_mq_done again. */
+        pa_asyncmsgq *z = q->outq;
+        pa_asyncmsgq_ref(z);
+        pa_asyncmsgq_flush(z, true);
+        pa_asyncmsgq_unref(z);
+    }
+
+    if (q->main_mainloop) {
+        if (q->read_main_event)
+            q->main_mainloop->io_free(q->read_main_event);
+        if (q->write_main_event)
+            q->main_mainloop->io_free(q->write_main_event);
+        q->read_main_event = q->write_main_event = NULL;
+    }
+
+    if (q->thread_mainloop) {
+        if (q->read_thread_event)
+            q->thread_mainloop->io_free(q->read_thread_event);
+        if (q->write_thread_event)
+            q->thread_mainloop->io_free(q->write_thread_event);
+        q->read_thread_event = q->write_thread_event = NULL;
+    }
+
+    if (q->inq)
+        pa_asyncmsgq_unref(q->inq);
+    if (q->outq)
+        pa_asyncmsgq_unref(q->outq);
+    q->inq = q->outq = NULL;
+
+    q->main_mainloop = NULL;
+    q->thread_mainloop = NULL;
+}
+
+void pa_thread_mq_install(pa_thread_mq *q) {
+    pa_assert(q);
+
+    pa_assert(!(PA_STATIC_TLS_GET(thread_mq)));
+    PA_STATIC_TLS_SET(thread_mq, q);
+}
+
+pa_thread_mq *pa_thread_mq_get(void) {
+    return PA_STATIC_TLS_GET(thread_mq);
+}
diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h
new file mode 100644 (file)
index 0000000..f6daa7f
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef foopulsethreadmqhfoo
+#define foopulsethreadmqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/rtpoll.h>
+
+/* Two way communication between a thread and a mainloop. Before the
+ * thread is started a pa_thread_mq should be initialized and than
+ * attached to the thread using pa_thread_mq_install(). */
+
+typedef struct pa_thread_mq {
+    pa_mainloop_api *main_mainloop;
+    pa_mainloop_api *thread_mainloop;
+    pa_asyncmsgq *inq, *outq;
+    pa_io_event *read_main_event, *write_main_event;
+    pa_io_event *read_thread_event, *write_thread_event;
+} pa_thread_mq;
+
+int pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll);
+int pa_thread_mq_init_thread_mainloop(pa_thread_mq *q, pa_mainloop_api *main_mainloop, pa_mainloop_api *thread_mainloop);
+void pa_thread_mq_done(pa_thread_mq *q);
+
+/* Install the specified pa_thread_mq object for the current thread */
+void pa_thread_mq_install(pa_thread_mq *q);
+
+/* Return the pa_thread_mq object that is set for the current thread */
+pa_thread_mq *pa_thread_mq_get(void);
+
+/* Verify that we are in control context (aka 'main context'). */
+#define pa_assert_ctl_context(s) \
+    pa_assert(!pa_thread_mq_get())
+
+/* Verify that we are in IO context (aka 'thread context'). */
+#define pa_assert_io_context(s) \
+    pa_assert(pa_thread_mq_get())
+
+#endif
diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c
new file mode 100644 (file)
index 0000000..62b0ec4
--- /dev/null
@@ -0,0 +1,254 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pthread.h>
+#include <sched.h>
+#include <errno.h>
+
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/macro.h>
+
+#include "thread.h"
+
+struct pa_thread {
+    pthread_t id;
+    pa_thread_func_t thread_func;
+    void *userdata;
+    pa_atomic_t running;
+    bool joined;
+    char *name;
+};
+
+struct pa_tls {
+    pthread_key_t key;
+};
+
+static void thread_free_cb(void *p) {
+    pa_thread *t = p;
+
+    pa_assert(t);
+
+    if (!t->thread_func) {
+        /* This is a foreign thread, we need to free the struct */
+        pa_xfree(t->name);
+        pa_xfree(t);
+    }
+}
+
+PA_STATIC_TLS_DECLARE(current_thread, thread_free_cb);
+
+static void* internal_thread_func(void *userdata) {
+    pa_thread *t = userdata;
+    pa_assert(t);
+
+#ifdef __linux__
+    prctl(PR_SET_NAME, t->name);
+#elif defined(HAVE_PTHREAD_SETNAME_NP) && defined(OS_IS_DARWIN)
+    pthread_setname_np(t->name);
+#endif
+
+    t->id = pthread_self();
+
+    PA_STATIC_TLS_SET(current_thread, t);
+
+    pa_atomic_inc(&t->running);
+    t->thread_func(t->userdata);
+    pa_atomic_sub(&t->running, 2);
+
+    return NULL;
+}
+
+pa_thread* pa_thread_new(const char *name, pa_thread_func_t thread_func, void *userdata) {
+    pa_thread *t;
+
+    pa_assert(thread_func);
+
+    t = pa_xnew0(pa_thread, 1);
+    t->name = pa_xstrdup(name);
+    t->thread_func = thread_func;
+    t->userdata = userdata;
+
+    if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) {
+        pa_xfree(t);
+        return NULL;
+    }
+
+    pa_atomic_inc(&t->running);
+
+    return t;
+}
+
+int pa_thread_is_running(pa_thread *t) {
+    pa_assert(t);
+
+    /* Unfortunately there is no way to tell whether a "foreign"
+     * thread is still running. See
+     * http://udrepper.livejournal.com/16844.html for more
+     * information */
+    pa_assert(t->thread_func);
+
+    return pa_atomic_load(&t->running) > 0;
+}
+
+void pa_thread_free(pa_thread *t) {
+    pa_assert(t);
+
+    pa_thread_join(t);
+
+    pa_xfree(t->name);
+    pa_xfree(t);
+}
+
+void pa_thread_free_nojoin(pa_thread *t) {
+    pa_assert(t);
+
+    pa_xfree(t->name);
+    pa_xfree(t);
+}
+
+int pa_thread_join(pa_thread *t) {
+    pa_assert(t);
+    pa_assert(t->thread_func);
+
+    if (t->joined)
+        return -1;
+
+    t->joined = true;
+    return pthread_join(t->id, NULL);
+}
+
+pa_thread* pa_thread_self(void) {
+    pa_thread *t;
+
+    if ((t = PA_STATIC_TLS_GET(current_thread)))
+        return t;
+
+    /* This is a foreign thread, let's create a pthread structure to
+     * make sure that we can always return a sensible pointer */
+
+    t = pa_xnew0(pa_thread, 1);
+    t->id = pthread_self();
+    t->joined = true;
+    pa_atomic_store(&t->running, 2);
+
+    PA_STATIC_TLS_SET(current_thread, t);
+
+    return t;
+}
+
+void* pa_thread_get_data(pa_thread *t) {
+    pa_assert(t);
+
+    return t->userdata;
+}
+
+void pa_thread_set_data(pa_thread *t, void *userdata) {
+    pa_assert(t);
+
+    t->userdata = userdata;
+}
+
+void pa_thread_set_name(pa_thread *t, const char *name) {
+    pa_assert(t);
+
+    pa_xfree(t->name);
+    t->name = pa_xstrdup(name);
+
+#ifdef __linux__
+    prctl(PR_SET_NAME, name);
+#elif defined(HAVE_PTHREAD_SETNAME_NP) && defined(OS_IS_DARWIN)
+    pthread_setname_np(name);
+#endif
+}
+
+const char *pa_thread_get_name(pa_thread *t) {
+    pa_assert(t);
+
+#ifdef __linux__
+    if (!t->name) {
+        t->name = pa_xmalloc(17);
+
+        if (prctl(PR_GET_NAME, t->name) >= 0)
+            t->name[16] = 0;
+        else {
+            pa_xfree(t->name);
+            t->name = NULL;
+        }
+    }
+#elif defined(HAVE_PTHREAD_GETNAME_NP) && defined(OS_IS_DARWIN)
+    if (!t->name) {
+        t->name = pa_xmalloc0(17);
+        pthread_getname_np(t->id, t->name, 16);
+    }
+#endif
+
+    return t->name;
+}
+
+void pa_thread_yield(void) {
+#ifdef HAVE_PTHREAD_YIELD
+    pthread_yield();
+#else
+    pa_assert_se(sched_yield() == 0);
+#endif
+}
+
+pa_tls* pa_tls_new(pa_free_cb_t free_cb) {
+    pa_tls *t;
+
+    t = pa_xnew(pa_tls, 1);
+
+    if (pthread_key_create(&t->key, free_cb) < 0) {
+        pa_xfree(t);
+        return NULL;
+    }
+
+    return t;
+}
+
+void pa_tls_free(pa_tls *t) {
+    pa_assert(t);
+
+    pa_assert_se(pthread_key_delete(t->key) == 0);
+    pa_xfree(t);
+}
+
+void *pa_tls_get(pa_tls *t) {
+    pa_assert(t);
+
+    return pthread_getspecific(t->key);
+}
+
+void *pa_tls_set(pa_tls *t, void *userdata) {
+    void *r;
+
+    r = pthread_getspecific(t->key);
+    pa_assert_se(pthread_setspecific(t->key, userdata) == 0);
+    return r;
+}
diff --git a/src/pulsecore/thread-win32.c b/src/pulsecore/thread-win32.c
new file mode 100644 (file)
index 0000000..310c04e
--- /dev/null
@@ -0,0 +1,240 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <windows.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/once.h>
+
+#include "thread.h"
+
+struct pa_thread {
+    HANDLE thread;
+    pa_thread_func_t thread_func;
+    void *userdata;
+};
+
+struct pa_tls {
+    DWORD index;
+    pa_free_cb_t free_func;
+};
+
+struct pa_tls_monitor {
+    HANDLE thread;
+    pa_free_cb_t free_func;
+    void *data;
+};
+
+static pa_tls *thread_tls;
+static pa_once thread_tls_once = PA_ONCE_INIT;
+static pa_tls *monitor_tls;
+
+static void thread_tls_once_func(void) {
+    thread_tls = pa_tls_new(NULL);
+    assert(thread_tls);
+}
+
+static DWORD WINAPI internal_thread_func(LPVOID param) {
+    pa_thread *t = param;
+    assert(t);
+
+    pa_run_once(&thread_tls_once, thread_tls_once_func);
+    pa_tls_set(thread_tls, t);
+
+    t->thread_func(t->userdata);
+
+    return 0;
+}
+
+pa_thread* pa_thread_new(const char *name, pa_thread_func_t thread_func, void *userdata) {
+    pa_thread *t;
+    DWORD thread_id;
+
+    assert(thread_func);
+
+    t = pa_xnew(pa_thread, 1);
+    t->thread_func = thread_func;
+    t->userdata = userdata;
+
+    t->thread = CreateThread(NULL, 0, internal_thread_func, t, 0, &thread_id);
+
+    if (!t->thread) {
+        pa_xfree(t);
+        return NULL;
+    }
+
+    return t;
+}
+
+int pa_thread_is_running(pa_thread *t) {
+    DWORD code;
+
+    assert(t);
+
+    if (!GetExitCodeThread(t->thread, &code))
+        return 0;
+
+    return code == STILL_ACTIVE;
+}
+
+void pa_thread_free(pa_thread *t) {
+    assert(t);
+
+    pa_thread_join(t);
+    CloseHandle(t->thread);
+    pa_xfree(t);
+}
+
+void pa_thread_free_nojoin(pa_thread *t) {
+    pa_assert(t);
+
+    CloseHandle(t->thread);
+    pa_xfree(t);
+}
+
+int pa_thread_join(pa_thread *t) {
+    assert(t);
+
+    if (WaitForSingleObject(t->thread, INFINITE) == WAIT_FAILED)
+        return -1;
+
+    return 0;
+}
+
+pa_thread* pa_thread_self(void) {
+    pa_run_once(&thread_tls_once, thread_tls_once_func);
+    return pa_tls_get(thread_tls);
+}
+
+void* pa_thread_get_data(pa_thread *t) {
+    pa_assert(t);
+
+    return t->userdata;
+}
+
+void pa_thread_set_data(pa_thread *t, void *userdata) {
+    pa_assert(t);
+
+    t->userdata = userdata;
+}
+
+void pa_thread_set_name(pa_thread *t, const char *name) {
+    /* Not implemented */
+}
+
+const char *pa_thread_get_name(pa_thread *t) {
+    /* Not implemented */
+    return NULL;
+}
+
+void pa_thread_yield(void) {
+    Sleep(0);
+}
+
+static DWORD WINAPI monitor_thread_func(LPVOID param) {
+    struct pa_tls_monitor *m = param;
+    assert(m);
+
+    WaitForSingleObject(m->thread, INFINITE);
+
+    CloseHandle(m->thread);
+
+    m->free_func(m->data);
+
+    pa_xfree(m);
+
+    return 0;
+}
+
+pa_tls* pa_tls_new(pa_free_cb_t free_cb) {
+    pa_tls *t;
+
+    t = pa_xnew(pa_tls, 1);
+    t->index = TlsAlloc();
+    t->free_func = free_cb;
+
+    if (t->index == TLS_OUT_OF_INDEXES) {
+        pa_xfree(t);
+        return NULL;
+    }
+
+    return t;
+}
+
+void pa_tls_free(pa_tls *t) {
+    assert(t);
+
+    TlsFree(t->index);
+    pa_xfree(t);
+}
+
+void *pa_tls_get(pa_tls *t) {
+    assert(t);
+
+    return TlsGetValue(t->index);
+}
+
+void *pa_tls_set(pa_tls *t, void *userdata) {
+    void *r;
+
+    assert(t);
+
+    r = TlsGetValue(t->index);
+
+    TlsSetValue(t->index, userdata);
+
+    if (t->free_func) {
+        struct pa_tls_monitor *m;
+
+        PA_ONCE_BEGIN {
+            monitor_tls = pa_tls_new(NULL);
+            assert(monitor_tls);
+            pa_tls_set(monitor_tls, NULL);
+        } PA_ONCE_END;
+
+        m = pa_tls_get(monitor_tls);
+        if (!m) {
+            HANDLE thread;
+
+            m = pa_xnew(struct pa_tls_monitor, 1);
+
+            DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
+                GetCurrentProcess(), &m->thread, 0, FALSE,
+                DUPLICATE_SAME_ACCESS);
+
+            m->free_func = t->free_func;
+
+            pa_tls_set(monitor_tls, m);
+
+            thread = CreateThread(NULL, 0, monitor_thread_func, m, 0, NULL);
+            assert(thread);
+            CloseHandle(thread);
+        }
+
+        m->data = userdata;
+    }
+
+    return r;
+}
diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h
new file mode 100644 (file)
index 0000000..8a14558
--- /dev/null
@@ -0,0 +1,117 @@
+#ifndef foopulsethreadhfoo
+#define foopulsethreadhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/def.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/once.h>
+#include <pulsecore/core-util.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+typedef struct pa_thread pa_thread;
+
+typedef void (*pa_thread_func_t) (void *userdata);
+
+pa_thread* pa_thread_new(const char *name, pa_thread_func_t thread_func, void *userdata);
+void pa_thread_free(pa_thread *t);
+void pa_thread_free_nojoin(pa_thread *t);
+int pa_thread_join(pa_thread *t);
+int pa_thread_is_running(pa_thread *t);
+pa_thread *pa_thread_self(void);
+void pa_thread_yield(void);
+
+void* pa_thread_get_data(pa_thread *t);
+void pa_thread_set_data(pa_thread *t, void *userdata);
+
+const char *pa_thread_get_name(pa_thread *t);
+void pa_thread_set_name(pa_thread *t, const char *name);
+
+typedef struct pa_tls pa_tls;
+
+pa_tls* pa_tls_new(pa_free_cb_t free_cb);
+void pa_tls_free(pa_tls *t);
+void * pa_tls_get(pa_tls *t);
+void *pa_tls_set(pa_tls *t, void *userdata);
+
+#define PA_STATIC_TLS_DECLARE(name, free_cb)                            \
+    static struct {                                                     \
+        pa_once once;                                                   \
+        pa_tls *volatile tls;                                           \
+    } name##_tls = {                                                    \
+        .once = PA_ONCE_INIT,                                           \
+        .tls = NULL                                                     \
+    };                                                                  \
+    static void name##_tls_init(void) {                                 \
+        name##_tls.tls = pa_tls_new(free_cb);                           \
+    }                                                                   \
+    static inline pa_tls* name##_tls_obj(void) {                        \
+        pa_run_once(&name##_tls.once, name##_tls_init);                 \
+        return name##_tls.tls;                                          \
+    }                                                                   \
+    static void name##_tls_destructor(void) PA_GCC_DESTRUCTOR;          \
+    static void name##_tls_destructor(void) {                           \
+        static void (*_free_cb)(void*) = free_cb;                       \
+        if (!pa_in_valgrind())                                          \
+            return;                                                     \
+        if (!name##_tls.tls)                                            \
+            return;                                                     \
+        if (_free_cb) {                                                 \
+            void *p;                                                    \
+            if ((p = pa_tls_get(name##_tls.tls)))                       \
+                _free_cb(p);                                            \
+        }                                                               \
+        pa_tls_free(name##_tls.tls);                                    \
+    }                                                                   \
+    static inline void* name##_tls_get(void) {                          \
+        return pa_tls_get(name##_tls_obj());                            \
+    }                                                                   \
+    static inline void* name##_tls_set(void *p) {                       \
+        return pa_tls_set(name##_tls_obj(), p);                         \
+    }                                                                   \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#if defined(SUPPORT_TLS___THREAD) && !defined(OS_IS_WIN32)
+/* An optimized version of the above that requires no dynamic
+ * allocation if the compiler supports __thread */
+#define PA_STATIC_TLS_DECLARE_NO_FREE(name)                             \
+    static __thread void *name##_tls = NULL;                            \
+    static inline void* name##_tls_get(void) {                          \
+        return name##_tls;                                              \
+    }                                                                   \
+    static inline void* name##_tls_set(void *p) {                       \
+        void *r = name##_tls;                                           \
+        name##_tls = p;                                                 \
+        return r;                                                       \
+    }                                                                   \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+#else
+#define PA_STATIC_TLS_DECLARE_NO_FREE(name) PA_STATIC_TLS_DECLARE(name, NULL)
+#endif
+
+#define PA_STATIC_TLS_GET(name) (name##_tls_get())
+#define PA_STATIC_TLS_SET(name, p) (name##_tls_set(p))
+
+#endif
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
new file mode 100644 (file)
index 0000000..2c82ff6
--- /dev/null
@@ -0,0 +1,524 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+
+#include <pulse/sample.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+
+#include "time-smoother.h"
+
+#define HISTORY_MAX 64
+
+/*
+ * Implementation of a time smoothing algorithm to synchronize remote
+ * clocks to a local one. Evens out noise, adjusts to clock skew and
+ * allows cheap estimations of the remote time while clock updates may
+ * be seldom and received in non-equidistant intervals.
+ *
+ * Basically, we estimate the gradient of received clock samples in a
+ * certain history window (of size 'history_time') with linear
+ * regression. With that info we estimate the remote time in
+ * 'adjust_time' ahead and smoothen our current estimation function
+ * towards that point with a 3rd order polynomial interpolation with
+ * fitting derivatives. (more or less a b-spline)
+ *
+ * The larger 'history_time' is chosen the better we will suppress
+ * noise -- but we'll adjust to clock skew slower..
+ *
+ * The larger 'adjust_time' is chosen the smoother our estimation
+ * function will be -- but we'll adjust to clock skew slower, too.
+ *
+ * If 'monotonic' is true the resulting estimation function is
+ * guaranteed to be monotonic.
+ */
+
+struct pa_smoother {
+    pa_usec_t adjust_time, history_time;
+
+    pa_usec_t time_offset;
+
+    pa_usec_t px, py;     /* Point p, where we want to reach stability */
+    double dp;            /* Gradient we want at point p */
+
+    pa_usec_t ex, ey;     /* Point e, which we estimated before and need to smooth to */
+    double de;            /* Gradient we estimated for point e */
+    pa_usec_t ry;         /* The original y value for ex */
+
+                          /* History of last measurements */
+    pa_usec_t history_x[HISTORY_MAX], history_y[HISTORY_MAX];
+    unsigned history_idx, n_history;
+
+    /* To even out for monotonicity */
+    pa_usec_t last_y, last_x;
+
+    /* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */
+    double a, b, c;
+    bool abc_valid:1;
+
+    bool monotonic:1;
+    bool paused:1;
+    bool smoothing:1; /* If false we skip the polynomial interpolation step */
+
+    pa_usec_t pause_time;
+
+    unsigned min_history;
+};
+
+pa_smoother* pa_smoother_new(
+        pa_usec_t adjust_time,
+        pa_usec_t history_time,
+        bool monotonic,
+        bool smoothing,
+        unsigned min_history,
+        pa_usec_t time_offset,
+        bool paused) {
+
+    pa_smoother *s;
+
+    pa_assert(adjust_time > 0);
+    pa_assert(history_time > 0);
+    pa_assert(min_history >= 2);
+    pa_assert(min_history <= HISTORY_MAX);
+
+    s = pa_xnew(pa_smoother, 1);
+    s->adjust_time = adjust_time;
+    s->history_time = history_time;
+    s->min_history = min_history;
+    s->monotonic = monotonic;
+    s->smoothing = smoothing;
+
+    pa_smoother_reset(s, time_offset, paused);
+
+    return s;
+}
+
+void pa_smoother_free(pa_smoother* s) {
+    pa_assert(s);
+
+    pa_xfree(s);
+}
+
+#define REDUCE(x)                               \
+    do {                                        \
+        x = (x) % HISTORY_MAX;                  \
+    } while(false)
+
+#define REDUCE_INC(x)                           \
+    do {                                        \
+        x = ((x)+1) % HISTORY_MAX;              \
+    } while(false)
+
+static void drop_old(pa_smoother *s, pa_usec_t x) {
+
+    /* Drop items from history which are too old, but make sure to
+     * always keep min_history in the history */
+
+    while (s->n_history > s->min_history) {
+
+        if (s->history_x[s->history_idx] + s->history_time >= x)
+            /* This item is still valid, and thus all following ones
+             * are too, so let's quit this loop */
+            break;
+
+        /* Item is too old, let's drop it */
+        REDUCE_INC(s->history_idx);
+
+        s->n_history --;
+    }
+}
+
+static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
+    unsigned j, i;
+    pa_assert(s);
+
+    /* First try to update an existing history entry */
+    i = s->history_idx;
+    for (j = s->n_history; j > 0; j--) {
+
+        if (s->history_x[i] == x) {
+            s->history_y[i] = y;
+            return;
+        }
+
+        REDUCE_INC(i);
+    }
+
+    /* Drop old entries */
+    drop_old(s, x);
+
+    /* Calculate position for new entry */
+    j = s->history_idx + s->n_history;
+    REDUCE(j);
+
+    /* Fill in entry */
+    s->history_x[j] = x;
+    s->history_y[j] = y;
+
+    /* Adjust counter */
+    s->n_history ++;
+
+    /* And make sure we don't store more entries than fit in */
+    if (s->n_history > HISTORY_MAX) {
+        s->history_idx += s->n_history - HISTORY_MAX;
+        REDUCE(s->history_idx);
+        s->n_history = HISTORY_MAX;
+    }
+}
+
+static double avg_gradient(pa_smoother *s, pa_usec_t x) {
+    unsigned i, j, c = 0;
+    int64_t ax = 0, ay = 0, k, t;
+    double r;
+
+    /* FIXME: Optimization: Jason Newton suggested that instead of
+     * going through the history on each iteration we could calculated
+     * avg_gradient() as we go.
+     *
+     * Second idea: it might make sense to weight history entries:
+     * more recent entries should matter more than old ones. */
+
+    /* Too few measurements, assume gradient of 1 */
+    if (s->n_history < s->min_history)
+        return 1;
+
+    /* First, calculate average of all measurements */
+    i = s->history_idx;
+    for (j = s->n_history; j > 0; j--) {
+
+        ax += (int64_t) s->history_x[i];
+        ay += (int64_t) s->history_y[i];
+        c++;
+
+        REDUCE_INC(i);
+    }
+
+    pa_assert(c >= s->min_history);
+    ax /= c;
+    ay /= c;
+
+    /* Now, do linear regression */
+    k = t = 0;
+
+    i = s->history_idx;
+    for (j = s->n_history; j > 0; j--) {
+        int64_t dx, dy;
+
+        dx = (int64_t) s->history_x[i] - ax;
+        dy = (int64_t) s->history_y[i] - ay;
+
+        k += dx*dy;
+        t += dx*dx;
+
+        REDUCE_INC(i);
+    }
+
+    r = (double) k / (double) t;
+
+    return (s->monotonic && r < 0) ? 0 : r;
+}
+
+static void calc_abc(pa_smoother *s) {
+    pa_usec_t ex, ey, px, py;
+    int64_t kx, ky;
+    double de, dp;
+
+    pa_assert(s);
+
+    if (s->abc_valid)
+        return;
+
+    /* We have two points: (ex|ey) and (px|py) with two gradients at
+     * these points de and dp. We do a polynomial
+     * interpolation of degree 3 with these 6 values */
+
+    ex = s->ex; ey = s->ey;
+    px = s->px; py = s->py;
+    de = s->de; dp = s->dp;
+
+    pa_assert(ex < px);
+
+    /* To increase the dynamic range and simplify calculation, we
+     * move these values to the origin */
+    kx = (int64_t) px - (int64_t) ex;
+    ky = (int64_t) py - (int64_t) ey;
+
+    /* Calculate a, b, c for y=ax^3+bx^2+cx */
+    s->c = de;
+    s->b = (((double) (3*ky)/ (double) kx - dp - (double) (2*de))) / (double) kx;
+    s->a = (dp/(double) kx - 2*s->b - de/(double) kx) / (double) (3*kx);
+
+    s->abc_valid = true;
+}
+
+static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
+    pa_assert(s);
+    pa_assert(y);
+
+    if (x >= s->px) {
+        /* Linear interpolation right from px */
+        int64_t t;
+
+        /* The requested point is right of the point where we wanted
+         * to be on track again, thus just linearly estimate */
+
+        t = (int64_t) s->py + (int64_t) llrint(s->dp * (double) (x - s->px));
+
+        if (t < 0)
+            t = 0;
+
+        *y = (pa_usec_t) t;
+
+        if (deriv)
+            *deriv = s->dp;
+
+    } else if (x <= s->ex) {
+        /* Linear interpolation left from ex */
+        int64_t t;
+
+        t = (int64_t) s->ey - (int64_t) llrint(s->de * (double) (s->ex - x));
+
+        if (t < 0)
+            t = 0;
+
+        *y = (pa_usec_t) t;
+
+        if (deriv)
+            *deriv = s->de;
+
+    } else {
+        /* Spline interpolation between ex and px */
+        double tx, ty;
+
+        /* Ok, we're not yet on track, thus let's interpolate, and
+         * make sure that the first derivative is smooth */
+
+        calc_abc(s);
+
+        /* Move to origin */
+        tx = (double) (x - s->ex);
+
+        /* Horner scheme */
+        ty = (tx * (s->c + tx * (s->b + tx * s->a)));
+
+        /* Move back from origin */
+        ty += (double) s->ey;
+
+        *y = ty >= 0 ? (pa_usec_t) llrint(ty) : 0;
+
+        /* Horner scheme */
+        if (deriv)
+            *deriv = s->c + (tx * (s->b*2 + tx * s->a*3));
+    }
+
+    /* Guarantee monotonicity */
+    if (s->monotonic) {
+
+        if (deriv && *deriv < 0)
+            *deriv = 0;
+    }
+}
+
+void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
+    pa_usec_t ney;
+    double nde;
+    bool is_new;
+
+    pa_assert(s);
+
+    /* Fix up x value */
+    if (s->paused)
+        x = s->pause_time;
+
+    x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
+
+    is_new = x >= s->ex;
+
+    if (is_new) {
+        /* First, we calculate the position we'd estimate for x, so that
+         * we can adjust our position smoothly from this one */
+        estimate(s, x, &ney, &nde);
+        s->ex = x; s->ey = ney; s->de = nde;
+        s->ry = y;
+    }
+
+    /* Then, we add the new measurement to our history */
+    add_to_history(s, x, y);
+
+    /* And determine the average gradient of the history */
+    s->dp = avg_gradient(s, x);
+
+    /* And calculate when we want to be on track again */
+    if (s->smoothing) {
+        s->px = s->ex + s->adjust_time;
+        s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time);
+    } else {
+        s->px = s->ex;
+        s->py = s->ry;
+    }
+
+    s->abc_valid = false;
+
+#ifdef DEBUG_DATA
+    pa_log_debug("%p, put(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
+#endif
+}
+
+pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
+    pa_usec_t y;
+
+    pa_assert(s);
+
+    /* Fix up x value */
+    if (s->paused)
+        x = s->pause_time;
+
+    x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
+
+    if (s->monotonic)
+        if (x <= s->last_x)
+            x = s->last_x;
+
+    estimate(s, x, &y, NULL);
+
+    if (s->monotonic) {
+
+        /* Make sure the querier doesn't jump forth and back. */
+        s->last_x = x;
+
+        if (y < s->last_y)
+            y = s->last_y;
+        else
+            s->last_y = y;
+    }
+
+#ifdef DEBUG_DATA
+    pa_log_debug("%p, get(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
+#endif
+
+    return y;
+}
+
+void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
+    pa_assert(s);
+
+    s->time_offset = offset;
+
+#ifdef DEBUG_DATA
+    pa_log_debug("offset(%llu)", (unsigned long long) offset);
+#endif
+}
+
+void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
+    pa_assert(s);
+
+    if (s->paused)
+        return;
+
+#ifdef DEBUG_DATA
+    pa_log_debug("pause(%llu)", (unsigned long long) x);
+#endif
+
+    s->paused = true;
+    s->pause_time = x;
+}
+
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x, bool fix_now) {
+    pa_assert(s);
+
+    if (!s->paused)
+        return;
+
+    if (x < s->pause_time)
+        x = s->pause_time;
+
+#ifdef DEBUG_DATA
+    pa_log_debug("resume(%llu)", (unsigned long long) x);
+#endif
+
+    s->paused = false;
+    s->time_offset += x - s->pause_time;
+
+    if (fix_now)
+        pa_smoother_fix_now(s);
+}
+
+void pa_smoother_fix_now(pa_smoother *s) {
+    pa_assert(s);
+
+    s->px = s->ex;
+    s->py = s->ry;
+}
+
+pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) {
+    pa_usec_t ney;
+    double nde;
+
+    pa_assert(s);
+
+    /* Fix up x value */
+    if (s->paused)
+        x = s->pause_time;
+
+    x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
+
+    estimate(s, x, &ney, &nde);
+
+    /* Play safe and take the larger gradient, so that we wakeup
+     * earlier when this is used for sleeping */
+    if (s->dp > nde)
+        nde = s->dp;
+
+#ifdef DEBUG_DATA
+    pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde);
+#endif
+
+    return (pa_usec_t) llrint((double) y_delay / nde);
+}
+
+void pa_smoother_reset(pa_smoother *s, pa_usec_t time_offset, bool paused) {
+    pa_assert(s);
+
+    s->px = s->py = 0;
+    s->dp = 1;
+
+    s->ex = s->ey = s->ry = 0;
+    s->de = 1;
+
+    s->history_idx = 0;
+    s->n_history = 0;
+
+    s->last_y = s->last_x = 0;
+
+    s->abc_valid = false;
+
+    s->paused = paused;
+    s->time_offset = s->pause_time = time_offset;
+
+#ifdef DEBUG_DATA
+    pa_log_debug("reset()");
+#endif
+}
diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h
new file mode 100644 (file)
index 0000000..49ae08c
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef foopulsetimesmootherhfoo
+#define foopulsetimesmootherhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/macro.h>
+#include <pulse/sample.h>
+
+typedef struct pa_smoother pa_smoother;
+
+pa_smoother* pa_smoother_new(
+        pa_usec_t x_adjust_time,
+        pa_usec_t x_history_time,
+        bool monotonic,
+        bool smoothing,
+        unsigned min_history,
+        pa_usec_t x_offset,
+        bool paused);
+
+void pa_smoother_free(pa_smoother* s);
+
+/* Adds a new value to our dataset. x = local/system time, y = remote time */
+void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y);
+
+/* Returns an interpolated value based on the dataset. x = local/system time, return value = remote time */
+pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x);
+
+/* Translates a time span from the remote time domain to the local one. x = local/system time when to estimate, y_delay = remote time span */
+pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay);
+
+void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t x_offset);
+
+void pa_smoother_pause(pa_smoother *s, pa_usec_t x);
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x, bool abrupt);
+
+void pa_smoother_reset(pa_smoother *s, pa_usec_t time_offset, bool paused);
+
+void pa_smoother_fix_now(pa_smoother *s);
+
+#endif
diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c
new file mode 100644 (file)
index 0000000..e81dd6b
--- /dev/null
@@ -0,0 +1,82 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/dynarray.h>
+#include <pulsecore/macro.h>
+
+#include "tokenizer.h"
+
+static void parse(pa_dynarray*a, const char *s, unsigned args) {
+    int infty = 0;
+    const char delimiter[] = " \t\n\r";
+    const char *p;
+
+    pa_assert(a);
+    pa_assert(s);
+
+    if (args == 0)
+        infty = 1;
+
+    p = s+strspn(s, delimiter);
+    while (*p && (infty || args >= 2)) {
+        size_t l = strcspn(p, delimiter);
+        char *n = pa_xstrndup(p, l);
+        pa_dynarray_append(a, n);
+        p += l;
+        p += strspn(p, delimiter);
+        args--;
+    }
+
+    if (args && *p) {
+        char *n = pa_xstrdup(p);
+        pa_dynarray_append(a, n);
+    }
+}
+
+pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) {
+    pa_dynarray *a;
+
+    a = pa_dynarray_new(pa_xfree);
+    parse(a, s, args);
+    return (pa_tokenizer*) a;
+}
+
+void pa_tokenizer_free(pa_tokenizer *t) {
+    pa_dynarray *a = (pa_dynarray*) t;
+
+    pa_assert(a);
+    pa_dynarray_free(a);
+}
+
+const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i) {
+    pa_dynarray *a = (pa_dynarray*) t;
+
+    pa_assert(a);
+
+    return pa_dynarray_get(a, i);
+}
diff --git a/src/pulsecore/tokenizer.h b/src/pulsecore/tokenizer.h
new file mode 100644 (file)
index 0000000..806d40c
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef footokenizerhfoo
+#define footokenizerhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct pa_tokenizer pa_tokenizer;
+
+pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args);
+void pa_tokenizer_free(pa_tokenizer *t);
+
+const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i);
+
+#endif
diff --git a/src/pulsecore/typedefs.h b/src/pulsecore/typedefs.h
new file mode 100644 (file)
index 0000000..3652f8f
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef footypedefshfoo
+#define footypedefshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2015 Canonical Ltd.
+  Written by David Henningsson <david.henningsson@canonical.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct pa_card pa_card;
+typedef struct pa_card_profile pa_card_profile;
+typedef struct pa_client pa_client;
+typedef struct pa_core pa_core;
+typedef struct pa_device_port pa_device_port;
+typedef struct pa_sink pa_sink;
+typedef struct pa_sink_volume_change pa_sink_volume_change;
+typedef struct pa_sink_input pa_sink_input;
+typedef struct pa_source pa_source;
+typedef struct pa_source_volume_change pa_source_volume_change;
+typedef struct pa_source_output pa_source_output;
+
+
+#endif
diff --git a/src/pulsecore/usergroup.c b/src/pulsecore/usergroup.c
new file mode 100644 (file)
index 0000000..40dad28
--- /dev/null
@@ -0,0 +1,362 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Ted Percival
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <errno.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "usergroup.h"
+
+#ifdef HAVE_GRP_H
+
+/* Returns a suitable starting size for a getgrnam_r() or getgrgid_r() buffer,
+   plus the size of a struct group.
+ */
+static size_t starting_getgr_buflen(void) {
+    size_t full_size;
+    long n;
+#ifdef _SC_GETGR_R_SIZE_MAX
+    n = sysconf(_SC_GETGR_R_SIZE_MAX);
+#else
+    n = -1;
+#endif
+    if (n <= 0)
+        n = 512;
+
+    full_size = (size_t) n + sizeof(struct group);
+
+    if (full_size < (size_t) n) /* check for integer overflow */
+        return (size_t) n;
+
+    return full_size;
+}
+
+/* Returns a suitable starting size for a getpwnam_r() or getpwuid_r() buffer,
+   plus the size of a struct passwd.
+ */
+static size_t starting_getpw_buflen(void) {
+    long n;
+    size_t full_size;
+
+#ifdef _SC_GETPW_R_SIZE_MAX
+    n = sysconf(_SC_GETPW_R_SIZE_MAX);
+#else
+    n = -1;
+#endif
+    if (n <= 0)
+        n = 512;
+
+    full_size = (size_t) n + sizeof(struct passwd);
+
+    if (full_size < (size_t) n) /* check for integer overflow */
+        return (size_t) n;
+
+    return full_size;
+}
+
+/* Given a memory allocation (*bufptr) and its length (*buflenptr),
+   double the size of the allocation, updating the given buffer and length
+   arguments. This function should be used in conjunction with the pa_*alloc
+   and pa_xfree functions.
+
+   Unlike realloc(), this function does *not* retain the original buffer's
+   contents.
+
+   Returns 0 on success, nonzero on error. The error cause is indicated by
+   errno.
+ */
+static int expand_buffer_trashcontents(void **bufptr, size_t *buflenptr) {
+    size_t newlen;
+
+    if (!bufptr || !*bufptr || !buflenptr) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    newlen = *buflenptr * 2;
+
+    if (newlen < *buflenptr) {
+        errno = EOVERFLOW;
+        return -1;
+    }
+
+    /* Don't bother retaining memory contents; free & alloc anew */
+    pa_xfree(*bufptr);
+
+    *bufptr = pa_xmalloc(newlen);
+    *buflenptr = newlen;
+
+    return 0;
+}
+
+#ifdef HAVE_GETGRGID_R
+/* Thread-safe getgrgid() replacement.
+   Returned value should be freed using pa_getgrgid_free() when the caller is
+   finished with the returned group data.
+
+   API is the same as getgrgid(), errors are indicated by a NULL return;
+   consult errno for the error cause (zero it before calling).
+ */
+struct group *pa_getgrgid_malloc(gid_t gid) {
+    size_t buflen, getgr_buflen;
+    int err;
+    void *buf;
+    void *getgr_buf;
+    struct group *result = NULL;
+
+    buflen = starting_getgr_buflen();
+    buf = pa_xmalloc(buflen);
+
+    getgr_buflen = buflen - sizeof(struct group);
+    getgr_buf = (char *)buf + sizeof(struct group);
+
+    while ((err = getgrgid_r(gid, (struct group *)buf, getgr_buf, getgr_buflen, &result)) == ERANGE) {
+        if (expand_buffer_trashcontents(&buf, &buflen))
+            break;
+
+        getgr_buflen = buflen - sizeof(struct group);
+        getgr_buf = (char *)buf + sizeof(struct group);
+    }
+
+    if (err || !result) {
+        result = NULL;
+        if (buf) {
+            pa_xfree(buf);
+            buf = NULL;
+        }
+    }
+
+    pa_assert(result == buf || result == NULL);
+
+    return result;
+}
+
+void pa_getgrgid_free(struct group *grp) {
+    pa_xfree(grp);
+}
+
+#else /* !HAVE_GETGRGID_R */
+
+struct group *pa_getgrgid_malloc(gid_t gid) {
+    return getgrgid(gid);
+}
+
+void pa_getgrgid_free(struct group *grp) {
+    /* nothing */
+    return;
+}
+
+#endif /* !HAVE_GETGRGID_R */
+
+#ifdef HAVE_GETGRNAM_R
+/* Thread-safe getgrnam() function.
+   Returned value should be freed using pa_getgrnam_free() when the caller is
+   finished with the returned group data.
+
+   API is the same as getgrnam(), errors are indicated by a NULL return;
+   consult errno for the error cause (zero it before calling).
+ */
+struct group *pa_getgrnam_malloc(const char *name) {
+    size_t buflen, getgr_buflen;
+    int err;
+    void *buf;
+    void *getgr_buf;
+    struct group *result = NULL;
+
+    buflen = starting_getgr_buflen();
+    buf = pa_xmalloc(buflen);
+
+    getgr_buflen = buflen - sizeof(struct group);
+    getgr_buf = (char *)buf + sizeof(struct group);
+
+    while ((err = getgrnam_r(name, (struct group *)buf, getgr_buf, getgr_buflen, &result)) == ERANGE) {
+        if (expand_buffer_trashcontents(&buf, &buflen))
+            break;
+
+        getgr_buflen = buflen - sizeof(struct group);
+        getgr_buf = (char *)buf + sizeof(struct group);
+    }
+
+    if (err || !result) {
+        result = NULL;
+        if (buf) {
+            pa_xfree(buf);
+            buf = NULL;
+        }
+    }
+
+    pa_assert(result == buf || result == NULL);
+
+    return result;
+}
+
+void pa_getgrnam_free(struct group *group) {
+    pa_xfree(group);
+}
+
+#else /* !HAVE_GETGRNAM_R */
+
+struct group *pa_getgrnam_malloc(const char *name) {
+    return getgrnam(name);
+}
+
+void pa_getgrnam_free(struct group *group) {
+    /* nothing */
+    return;
+}
+
+#endif /* HAVE_GETGRNAM_R */
+
+#endif /* HAVE_GRP_H */
+
+#ifdef HAVE_PWD_H
+
+#ifdef HAVE_GETPWNAM_R
+/* Thread-safe getpwnam() function.
+   Returned value should be freed using pa_getpwnam_free() when the caller is
+   finished with the returned passwd data.
+
+   API is the same as getpwnam(), errors are indicated by a NULL return;
+   consult errno for the error cause (zero it before calling).
+ */
+struct passwd *pa_getpwnam_malloc(const char *name) {
+    size_t buflen, getpw_buflen;
+    int err;
+    void *buf;
+    void *getpw_buf;
+    struct passwd *result = NULL;
+
+    buflen = starting_getpw_buflen();
+    buf = pa_xmalloc(buflen);
+
+    getpw_buflen = buflen - sizeof(struct passwd);
+    getpw_buf = (char *)buf + sizeof(struct passwd);
+
+    while ((err = getpwnam_r(name, (struct passwd *)buf, getpw_buf, getpw_buflen, &result)) == ERANGE) {
+        if (expand_buffer_trashcontents(&buf, &buflen))
+            break;
+
+        getpw_buflen = buflen - sizeof(struct passwd);
+        getpw_buf = (char *)buf + sizeof(struct passwd);
+    }
+
+    if (err || !result) {
+        result = NULL;
+        if (buf) {
+            pa_xfree(buf);
+            buf = NULL;
+        }
+    }
+
+    pa_assert(result == buf || result == NULL);
+
+    return result;
+}
+
+void pa_getpwnam_free(struct passwd *passwd) {
+    pa_xfree(passwd);
+}
+
+#else /* !HAVE_GETPWNAM_R */
+
+struct passwd *pa_getpwnam_malloc(const char *name) {
+    return getpwnam(name);
+}
+
+void pa_getpwnam_free(struct passwd *passwd) {
+    /* nothing */
+    return;
+}
+
+#endif /* !HAVE_GETPWNAM_R */
+
+#ifdef HAVE_GETPWUID_R
+/* Thread-safe getpwuid() function.
+   Returned value should be freed using pa_getpwuid_free() when the caller is
+   finished with the returned group data.
+
+   API is the same as getpwuid(), errors are indicated by a NULL return;
+   consult errno for the error cause (zero it before calling).
+ */
+struct passwd *pa_getpwuid_malloc(uid_t uid) {
+    size_t buflen, getpw_buflen;
+    int err;
+    void *buf;
+    void *getpw_buf;
+    struct passwd *result = NULL;
+
+    buflen = starting_getpw_buflen();
+    buf = pa_xmalloc(buflen);
+
+    getpw_buflen = buflen - sizeof(struct passwd);
+    getpw_buf = (char *)buf + sizeof(struct passwd);
+
+    while ((err = getpwuid_r(uid, (struct passwd *)buf, getpw_buf, getpw_buflen, &result)) == ERANGE) {
+        if (expand_buffer_trashcontents(&buf, &buflen))
+            break;
+
+        getpw_buflen = buflen - sizeof(struct passwd);
+        getpw_buf = (char *)buf + sizeof(struct passwd);
+    }
+
+    if (err || !result) {
+        result = NULL;
+        if (buf) {
+            pa_xfree(buf);
+            buf = NULL;
+        }
+    }
+
+    pa_assert(result == buf || result == NULL);
+
+    return result;
+}
+
+void pa_getpwuid_free(struct passwd *passwd) {
+    pa_xfree(passwd);
+}
+
+#else /* !HAVE_GETPWUID_R */
+
+struct passwd *pa_getpwuid_malloc(uid_t uid) {
+    return getpwuid(uid);
+}
+
+void pa_getpwuid_free(struct passwd *passwd) {
+    /* nothing */
+    return;
+}
+
+#endif /* !HAVE_GETPWUID_R */
+
+#endif /* HAVE_PWD_H */
diff --git a/src/pulsecore/usergroup.h b/src/pulsecore/usergroup.h
new file mode 100644 (file)
index 0000000..0945059
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef foousergrouphfoo
+#define foousergrouphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Ted Percival
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifdef HAVE_GRP_H
+
+struct group *pa_getgrgid_malloc(gid_t gid);
+void pa_getgrgid_free(struct group *grp);
+
+struct group *pa_getgrnam_malloc(const char *name);
+void pa_getgrnam_free(struct group *group);
+
+#endif /* HAVE_GRP_H */
+
+#ifdef HAVE_PWD_H
+
+struct passwd *pa_getpwuid_malloc(uid_t uid);
+void pa_getpwuid_free(struct passwd *passwd);
+
+struct passwd *pa_getpwnam_malloc(const char *name);
+void pa_getpwnam_free(struct passwd *passwd);
+
+#endif /* HAVE_PWD_H */
+
+#endif /* foousergrouphfoo */
diff --git a/src/pulsecore/winerrno.h b/src/pulsecore/winerrno.h
new file mode 100644 (file)
index 0000000..052d4de
--- /dev/null
@@ -0,0 +1,89 @@
+
+/* Generated with:
+cat /usr/i686-pc-mingw32/sys-root/mingw/include/winerror.h \
+ | awk '/#define WSAE.*WSABASE/{gsub("WSA", ""); print "#undef " $2 "\n#define " $2 " WSA" $2}' \
+ | egrep -v 'EINTR|EBADF|EACCES|EFAULT|EINVAL|EMFILE|_QOS|PROVIDER|PROCTABLE'
+*/
+
+#undef EWOULDBLOCK
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#undef EINPROGRESS
+#define EINPROGRESS WSAEINPROGRESS
+#undef EALREADY
+#define EALREADY WSAEALREADY
+#undef ENOTSOCK
+#define ENOTSOCK WSAENOTSOCK
+#undef EDESTADDRREQ
+#define EDESTADDRREQ WSAEDESTADDRREQ
+#undef EMSGSIZE
+#define EMSGSIZE WSAEMSGSIZE
+#undef EPROTOTYPE
+#define EPROTOTYPE WSAEPROTOTYPE
+#undef ENOPROTOOPT
+#define ENOPROTOOPT WSAENOPROTOOPT
+#undef EPROTONOSUPPORT
+#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
+#undef ESOCKTNOSUPPORT
+#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
+#undef EOPNOTSUPP
+#define EOPNOTSUPP WSAEOPNOTSUPP
+#undef EPFNOSUPPORT
+#define EPFNOSUPPORT WSAEPFNOSUPPORT
+#undef EAFNOSUPPORT
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#undef EADDRINUSE
+#define EADDRINUSE WSAEADDRINUSE
+#undef EADDRNOTAVAIL
+#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
+#undef ENETDOWN
+#define ENETDOWN WSAENETDOWN
+#undef ENETUNREACH
+#define ENETUNREACH WSAENETUNREACH
+#undef ENETRESET
+#define ENETRESET WSAENETRESET
+#undef ECONNABORTED
+#define ECONNABORTED WSAECONNABORTED
+#undef ECONNRESET
+#define ECONNRESET WSAECONNRESET
+#undef ENOBUFS
+#define ENOBUFS WSAENOBUFS
+#undef EISCONN
+#define EISCONN WSAEISCONN
+#undef ENOTCONN
+#define ENOTCONN WSAENOTCONN
+#undef ESHUTDOWN
+#define ESHUTDOWN WSAESHUTDOWN
+#undef ETOOMANYREFS
+#define ETOOMANYREFS WSAETOOMANYREFS
+#undef ETIMEDOUT
+#define ETIMEDOUT WSAETIMEDOUT
+#undef ECONNREFUSED
+#define ECONNREFUSED WSAECONNREFUSED
+#undef ELOOP
+#define ELOOP WSAELOOP
+#undef ENAMETOOLONG
+#define ENAMETOOLONG WSAENAMETOOLONG
+#undef EHOSTDOWN
+#define EHOSTDOWN WSAEHOSTDOWN
+#undef EHOSTUNREACH
+#define EHOSTUNREACH WSAEHOSTUNREACH
+#undef ENOTEMPTY
+#define ENOTEMPTY WSAENOTEMPTY
+#undef EPROCLIM
+#define EPROCLIM WSAEPROCLIM
+#undef EUSERS
+#define EUSERS WSAEUSERS
+#undef EDQUOT
+#define EDQUOT WSAEDQUOT
+#undef ESTALE
+#define ESTALE WSAESTALE
+#undef EREMOTE
+#define EREMOTE WSAEREMOTE
+#undef EDISCON
+#define EDISCON WSAEDISCON
+#undef ENOMORE
+#define ENOMORE WSAENOMORE
+#undef ECANCELLED
+#define ECANCELLED WSAECANCELLED
+#undef EREFUSED
+#define EREFUSED WSAEREFUSED
diff --git a/src/pulsecore/x11prop.c b/src/pulsecore/x11prop.c
new file mode 100644 (file)
index 0000000..f3f7737
--- /dev/null
@@ -0,0 +1,145 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "x11prop.h"
+
+#include <pulsecore/macro.h>
+
+#include <xcb/xproto.h>
+
+#define PA_XCB_FORMAT 8
+
+static xcb_screen_t *screen_of_display(xcb_connection_t *xcb, int screen) {
+    const xcb_setup_t *s;
+    xcb_screen_iterator_t iter;
+
+    if ((s = xcb_get_setup(xcb))) {
+        iter = xcb_setup_roots_iterator(s);
+        for (; iter.rem; --screen, xcb_screen_next(&iter))
+            if (0 == screen)
+                return iter.data;
+    }
+    return NULL;
+}
+
+void pa_x11_set_prop(xcb_connection_t *xcb, int screen, const char *name, const char *data) {
+    xcb_screen_t *xs;
+    xcb_intern_atom_reply_t *reply;
+
+    pa_assert(xcb);
+    pa_assert(name);
+    pa_assert(data);
+
+    if ((xs = screen_of_display(xcb, screen))) {
+        reply = xcb_intern_atom_reply(xcb,
+                                      xcb_intern_atom(xcb, 0, strlen(name), name),
+                                      NULL);
+
+        if (reply) {
+            xcb_change_property(xcb, XCB_PROP_MODE_REPLACE, xs->root, reply->atom,
+                                XCB_ATOM_STRING, PA_XCB_FORMAT,
+                                (int) strlen(data), (const void*) data);
+
+            free(reply);
+        }
+    }
+}
+
+void pa_x11_del_prop(xcb_connection_t *xcb, int screen, const char *name) {
+    xcb_screen_t *xs;
+    xcb_intern_atom_reply_t *reply;
+
+    pa_assert(xcb);
+    pa_assert(name);
+
+    if ((xs = screen_of_display(xcb, screen))) {
+        reply = xcb_intern_atom_reply(xcb,
+                                      xcb_intern_atom(xcb, 0, strlen(name), name),
+                                      NULL);
+
+        if (reply) {
+            xcb_delete_property(xcb, xs->root, reply->atom);
+            free(reply);
+        }
+    }
+}
+
+char* pa_x11_get_prop(xcb_connection_t *xcb, int screen, const char *name, char *p, size_t l) {
+    char *ret = NULL;
+    int len;
+    xcb_get_property_cookie_t req;
+    xcb_get_property_reply_t* prop = NULL;
+    xcb_screen_t *xs;
+    xcb_intern_atom_reply_t *reply;
+
+    pa_assert(xcb);
+    pa_assert(name);
+    pa_assert(p);
+
+    xs = screen_of_display(xcb, screen);
+    /*
+     * Also try and get the settings from the first screen.
+     * This allows for e.g. a Media Center to run on screen 1 (e.g. HDMI) and have
+     * different defaults (e.g. prefer the HDMI sink) than the primary screen 0
+     * which uses the Internal Audio sink.
+     */
+    if (!xs && 0 != screen)
+        xs = screen_of_display(xcb, 0);
+
+    if (xs) {
+        reply = xcb_intern_atom_reply(xcb,
+                                      xcb_intern_atom(xcb, 0, strlen(name), name),
+                                      NULL);
+
+        if (!reply)
+            goto finish;
+
+        req = xcb_get_property(xcb, 0, xs->root, reply->atom, XCB_ATOM_STRING, 0, (uint32_t)(l-1));
+        free(reply);
+        prop = xcb_get_property_reply(xcb, req, NULL);
+
+        if (!prop)
+            goto finish;
+
+        if (PA_XCB_FORMAT != prop->format)
+            goto finish;
+
+        len = xcb_get_property_value_length(prop);
+        if (len < 1 || len >= (int)l)
+            goto finish;
+
+        memcpy(p, xcb_get_property_value(prop), len);
+        p[len] = 0;
+
+        ret = p;
+    }
+
+finish:
+
+    if (prop)
+        free(prop);
+
+    return ret;
+}
diff --git a/src/pulsecore/x11prop.h b/src/pulsecore/x11prop.h
new file mode 100644 (file)
index 0000000..2b9d3f1
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef foox11prophfoo
+#define foox11prophfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2010 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <xcb/xcb.h>
+
+void pa_x11_set_prop(xcb_connection_t *xcb, int screen, const char *name, const char *data);
+void pa_x11_del_prop(xcb_connection_t *xcb, int screen, const char *name);
+char* pa_x11_get_prop(xcb_connection_t *xcb, int screen, const char *name, char *p, size_t l);
+
+#endif
diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c
new file mode 100644 (file)
index 0000000..0c040cf
--- /dev/null
@@ -0,0 +1,305 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/llist.h>
+#include <pulsecore/log.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "x11wrap.h"
+
+typedef struct pa_x11_internal pa_x11_internal;
+
+struct pa_x11_internal {
+    PA_LLIST_FIELDS(pa_x11_internal);
+    pa_x11_wrapper *wrapper;
+    pa_io_event* io_event;
+    int fd;
+};
+
+struct pa_x11_wrapper {
+    PA_REFCNT_DECLARE;
+    pa_core *core;
+
+    char *property_name;
+    Display *display;
+
+    pa_defer_event* defer_event;
+    pa_io_event* io_event;
+
+    PA_LLIST_HEAD(pa_x11_client, clients);
+    PA_LLIST_HEAD(pa_x11_internal, internals);
+};
+
+struct pa_x11_client {
+    PA_LLIST_FIELDS(pa_x11_client);
+    pa_x11_wrapper *wrapper;
+    pa_x11_event_cb_t event_cb;
+    pa_x11_kill_cb_t kill_cb;
+    void *userdata;
+};
+
+/* Dispatch all pending X11 events */
+static void work(pa_x11_wrapper *w) {
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    pa_x11_wrapper_ref(w);
+
+    while (XPending(w->display)) {
+        pa_x11_client *c, *n;
+        XEvent e;
+        XNextEvent(w->display, &e);
+
+        for (c = w->clients; c; c = n) {
+            n = c->next;
+
+            if (c->event_cb)
+                if (c->event_cb(w, &e, c->userdata) != 0)
+                    break;
+        }
+    }
+
+    pa_x11_wrapper_unref(w);
+}
+
+/* IO notification event for the X11 display connection */
+static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    pa_x11_wrapper *w = userdata;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(fd >= 0);
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    work(w);
+}
+
+/* Deferred notification event. Called once each main loop iteration */
+static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+    pa_x11_wrapper *w = userdata;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    m->defer_enable(e, 0);
+
+    work(w);
+}
+
+/* IO notification event for X11 internal connections */
+static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    pa_x11_wrapper *w = userdata;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(fd >= 0);
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    XProcessInternalConnection(w->display, fd);
+
+    work(w);
+}
+
+/* Add a new IO source for the specified X11 internal connection */
+static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) {
+    pa_x11_internal *i;
+    pa_assert(fd >= 0);
+
+    i = pa_xnew(pa_x11_internal, 1);
+    i->wrapper = w;
+    i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w);
+    i->fd = fd;
+
+    PA_LLIST_PREPEND(pa_x11_internal, w->internals, i);
+    return i;
+}
+
+/* Remove an IO source for an X11 internal connection */
+static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) {
+    pa_assert(i);
+
+    PA_LLIST_REMOVE(pa_x11_internal, w->internals, i);
+    w->core->mainloop->io_free(i->io_event);
+    pa_xfree(i);
+}
+
+/* Implementation of XConnectionWatchProc */
+static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) {
+    pa_x11_wrapper *w = (pa_x11_wrapper*) userdata;
+
+    pa_assert(display);
+    pa_assert(w);
+    pa_assert(fd >= 0);
+
+    if (opening)
+        *watch_data = (XPointer) x11_internal_add(w, fd);
+    else
+        x11_internal_remove(w, (pa_x11_internal*) *watch_data);
+}
+
+static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) {
+    pa_x11_wrapper*w;
+    Display *d;
+
+    if (!(d = XOpenDisplay(name))) {
+        pa_log("XOpenDisplay() failed");
+        return NULL;
+    }
+
+    w = pa_xnew(pa_x11_wrapper, 1);
+    PA_REFCNT_INIT(w);
+    w->core = c;
+    w->property_name = pa_xstrdup(t);
+    w->display = d;
+
+    PA_LLIST_HEAD_INIT(pa_x11_client, w->clients);
+    PA_LLIST_HEAD_INIT(pa_x11_internal, w->internals);
+
+    w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w);
+    w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w);
+
+    XAddConnectionWatch(d, x11_watch, (XPointer) w);
+
+    pa_assert_se(pa_shared_set(c, w->property_name, w) >= 0);
+
+    return w;
+}
+
+static void x11_wrapper_free(pa_x11_wrapper*w) {
+    pa_assert(w);
+
+    pa_assert_se(pa_shared_remove(w->core, w->property_name) >= 0);
+
+    pa_assert(!w->clients);
+
+    XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);
+    XCloseDisplay(w->display);
+
+    w->core->mainloop->io_free(w->io_event);
+    w->core->mainloop->defer_free(w->defer_event);
+
+    while (w->internals)
+        x11_internal_remove(w, w->internals);
+
+    pa_xfree(w->property_name);
+    pa_xfree(w);
+}
+
+pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {
+    char t[256];
+    pa_x11_wrapper *w;
+
+    pa_core_assert_ref(c);
+
+    pa_snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "@" : "", name ? name : "");
+
+    if ((w = pa_shared_get(c, t)))
+        return pa_x11_wrapper_ref(w);
+
+    return x11_wrapper_new(c, name, t);
+}
+
+pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w) {
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    PA_REFCNT_INC(w);
+    return w;
+}
+
+void pa_x11_wrapper_unref(pa_x11_wrapper* w) {
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    if (PA_REFCNT_DEC(w) > 0)
+        return;
+
+    x11_wrapper_free(w);
+}
+
+Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) {
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    /* Somebody is using us, schedule a output buffer flush */
+    w->core->mainloop->defer_enable(w->defer_event, 1);
+
+    return w->display;
+}
+
+xcb_connection_t *pa_x11_wrapper_get_xcb_connection(pa_x11_wrapper *w) {
+    return XGetXCBConnection(pa_x11_wrapper_get_display(w));
+}
+
+void pa_x11_wrapper_kill(pa_x11_wrapper *w) {
+    pa_x11_client *c, *n;
+
+    pa_assert(w);
+
+    pa_x11_wrapper_ref(w);
+
+    for (c = w->clients; c; c = n) {
+        n = c->next;
+
+        if (c->kill_cb)
+            c->kill_cb(w, c->userdata);
+    }
+
+    pa_x11_wrapper_unref(w);
+}
+
+pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata) {
+    pa_x11_client *c;
+
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    c = pa_xnew(pa_x11_client, 1);
+    c->wrapper = w;
+    c->event_cb = event_cb;
+    c->kill_cb = kill_cb;
+    c->userdata = userdata;
+
+    PA_LLIST_PREPEND(pa_x11_client, w->clients, c);
+
+    return c;
+}
+
+void pa_x11_client_free(pa_x11_client *c) {
+    pa_assert(c);
+    pa_assert(c->wrapper);
+    pa_assert(PA_REFCNT_VALUE(c->wrapper) >= 1);
+
+    PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c);
+    pa_xfree(c);
+}
diff --git a/src/pulsecore/x11wrap.h b/src/pulsecore/x11wrap.h
new file mode 100644 (file)
index 0000000..0539303
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef foox11wraphfoo
+#define foox11wraphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <X11/Xlib.h>
+#include <X11/Xlib-xcb.h>
+
+#include <pulsecore/core.h>
+
+typedef struct pa_x11_wrapper pa_x11_wrapper;
+
+typedef struct pa_x11_client pa_x11_client;
+
+typedef int (*pa_x11_event_cb_t)(pa_x11_wrapper *w, XEvent *e, void *userdata);
+typedef void (*pa_x11_kill_cb_t)(pa_x11_wrapper *w, void *userdata);
+
+/* Return the X11 wrapper for this core. In case no wrapper was
+    existent before, allocate a new one */
+pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name);
+
+/* Increase the wrapper's reference count by one */
+pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w);
+
+/* Decrease the reference counter of an X11 wrapper object */
+void pa_x11_wrapper_unref(pa_x11_wrapper* w);
+
+/* Return the X11 display object for this connection */
+Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w);
+
+/* Return the XCB connection object for this connection */
+xcb_connection_t *pa_x11_wrapper_get_xcb_connection(pa_x11_wrapper *w);
+
+/* Kill the connection to the X11 display */
+void pa_x11_wrapper_kill(pa_x11_wrapper *w);
+
+/* Register an X11 client, that is called for each X11 event */
+pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata);
+
+/* Free an X11 client object */
+void pa_x11_client_free(pa_x11_client *c);
+
+#endif
diff --git a/src/tests/Makefile b/src/tests/Makefile
new file mode 120000 (symlink)
index 0000000..f5b1dba
--- /dev/null
@@ -0,0 +1 @@
+../modules/Makefile
\ No newline at end of file
diff --git a/src/tests/alsa-mixer-path-test.c b/src/tests/alsa-mixer-path-test.c
new file mode 100644 (file)
index 0000000..ac92d78
--- /dev/null
@@ -0,0 +1,110 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+#include <dirent.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/strlist.h>
+#include <modules/alsa/alsa-mixer.h>
+
+/* This function was copied from alsa-mixer.c */
+static const char *get_default_paths_dir(void) {
+    if (pa_run_from_build_tree())
+        return PA_SRCDIR "/modules/alsa/mixer/paths/";
+    else
+        return PA_ALSA_PATHS_DIR;
+}
+
+static pa_strlist *load_makefile() {
+    FILE *f;
+    bool lookforfiles = false;
+    char buf[2048];
+    pa_strlist *result = NULL;
+    const char *Makefile = PA_BUILDDIR "/Makefile";
+
+    f = pa_fopen_cloexec(Makefile, "r");
+    fail_unless(f != NULL); /* Consider skipping this test instead of failing if Makefile not found? */
+    while (!feof(f)) {
+        if (!fgets(buf, sizeof(buf), f)) {
+            fail_unless(feof(f));
+            break;
+        }
+        if (strstr(buf, "dist_alsapaths_DATA = \\") != NULL) {
+           lookforfiles = true;
+           continue;
+        }
+        if (!lookforfiles)
+           continue;
+        if (!strstr(buf, "\\"))
+           lookforfiles = false;
+        else
+           strstr(buf, "\\")[0] = '\0';
+        pa_strip(buf);
+        pa_log_debug("Shipping file '%s'", pa_path_get_filename(buf));
+        result = pa_strlist_prepend(result, pa_path_get_filename(buf));
+    }
+    fclose(f);
+    return result;
+}
+
+START_TEST (mixer_path_test) {
+    DIR *dir;
+    struct dirent *ent;
+    pa_strlist *ship = load_makefile();
+    const char *pathsdir = get_default_paths_dir();
+    pa_log_debug("Analyzing directory: '%s'", pathsdir);
+
+    dir = opendir(pathsdir);
+    fail_unless(dir != NULL);
+    while ((ent = readdir(dir)) != NULL) {
+        pa_alsa_path *path;
+        if (pa_streq(ent->d_name, ".") || pa_streq(ent->d_name, ".."))
+            continue;
+        pa_log_debug("Analyzing file: '%s'", ent->d_name);
+
+        /* Can the file be parsed? */
+        path = pa_alsa_path_new(pathsdir, ent->d_name, PA_ALSA_DIRECTION_ANY);
+        fail_unless(path != NULL);
+
+        /* Is the file shipped? */
+        if (ship) {
+            pa_strlist *n;
+            bool found = false;
+            for (n = ship; n; n = pa_strlist_next(n))
+                found |= pa_streq(ent->d_name, pa_strlist_data(n));
+            fail_unless(found);
+        }
+    }
+    closedir(dir);
+    pa_strlist_free(ship);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("Alsa-mixer-path");
+    tc = tcase_create("alsa-mixer-path");
+    tcase_add_test(tc, mixer_path_test);
+    tcase_set_timeout(tc, 30);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/alsa-time-test.c b/src/tests/alsa-time-test.c
new file mode 100644 (file)
index 0000000..dff95bb
--- /dev/null
@@ -0,0 +1,251 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <assert.h>
+#include <inttypes.h>
+#include <time.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <alsa/asoundlib.h>
+
+#define SAMPLE_RATE 44100
+#define CHANNELS 2
+
+static uint64_t timespec_us(const struct timespec *ts) {
+    return
+        ts->tv_sec * 1000000LLU +
+        ts->tv_nsec / 1000LLU;
+}
+
+int main(int argc, char *argv[]) {
+    const char *dev;
+    int r, cap, count = 0;
+    snd_pcm_hw_params_t *hwparams;
+    snd_pcm_sw_params_t *swparams;
+    snd_pcm_status_t *status;
+    snd_pcm_t *pcm;
+    unsigned rate = SAMPLE_RATE;
+    unsigned periods = 2;
+    snd_pcm_uframes_t boundary, buffer_size = SAMPLE_RATE/10; /* 100s */
+    int dir = 1;
+    int fillrate;
+    struct timespec start, last_timestamp = { 0, 0 };
+    uint64_t start_us, last_us = 0;
+    snd_pcm_sframes_t last_avail = 0, last_delay = 0;
+    struct pollfd *pollfds;
+    int n_pollfd;
+    int64_t sample_count = 0;
+    uint16_t *samples;
+    struct sched_param sp;
+
+    r = -1;
+#ifdef _POSIX_PRIORITY_SCHEDULING
+    sp.sched_priority = 5;
+    r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp);
+#endif
+    if (r)
+        printf("Could not get RT prio. :(\n");
+
+    snd_pcm_hw_params_alloca(&hwparams);
+    snd_pcm_sw_params_alloca(&swparams);
+    snd_pcm_status_alloca(&status);
+
+    r = clock_gettime(CLOCK_MONOTONIC, &start);
+    assert(r == 0);
+
+    start_us = timespec_us(&start);
+
+    dev = argc > 1 ? argv[1] : "front:0";
+    cap = argc > 2 ? atoi(argv[2]) : 0;
+    fillrate = argc > 3 ? atoi(argv[3]) : 1;
+    assert(fillrate > 0);
+
+    samples = calloc(fillrate, CHANNELS*sizeof(uint16_t));
+    assert(samples);
+
+    if (cap == 0)
+      r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_PLAYBACK, 0);
+    else
+      r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_CAPTURE, 0);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params_any(pcm, hwparams);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 0);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, NULL);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params_set_channels(pcm, hwparams, CHANNELS);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params_set_periods_integer(pcm, hwparams);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params_set_periods_near(pcm, hwparams, &periods, &dir);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams, &buffer_size);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params(pcm, hwparams);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params_current(pcm, hwparams);
+    assert(r == 0);
+
+    r = snd_pcm_sw_params_current(pcm, swparams);
+    assert(r == 0);
+
+    if (cap == 0)
+      r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 1);
+    else
+      r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 0);
+    assert(r == 0);
+
+    r = snd_pcm_sw_params_set_period_event(pcm, swparams, 0);
+    assert(r == 0);
+
+    r = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
+    assert(r == 0);
+    r = snd_pcm_sw_params_set_start_threshold(pcm, swparams, buffer_size - (buffer_size % fillrate));
+    assert(r == 0);
+
+    r = snd_pcm_sw_params_get_boundary(swparams, &boundary);
+    assert(r == 0);
+    r = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary);
+    assert(r == 0);
+
+    r = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE);
+    assert(r == 0);
+
+    r = snd_pcm_sw_params(pcm, swparams);
+    assert(r == 0);
+
+    r = snd_pcm_sw_params_current(pcm, swparams);
+    assert(r == 0);
+
+/*     assert(snd_pcm_hw_params_is_monotonic(hwparams) > 0); */
+
+    n_pollfd = snd_pcm_poll_descriptors_count(pcm);
+    assert(n_pollfd > 0);
+
+    pollfds = malloc(sizeof(struct pollfd) * n_pollfd);
+    assert(pollfds);
+
+    r = snd_pcm_poll_descriptors(pcm, pollfds, n_pollfd);
+    assert(r == n_pollfd);
+
+    printf("Starting. Buffer size is %u frames\n", (unsigned int) buffer_size);
+
+    if (cap) {
+      r = snd_pcm_start(pcm);
+      assert(r == 0);
+    }
+
+    for (;;) {
+        snd_pcm_sframes_t avail, delay;
+        struct timespec now, timestamp;
+        unsigned short revents;
+        int handled = 0;
+        uint64_t now_us, timestamp_us;
+        snd_pcm_state_t state;
+        unsigned long long pos;
+
+        r = poll(pollfds, n_pollfd, 0);
+        assert(r >= 0);
+
+        r = snd_pcm_poll_descriptors_revents(pcm, pollfds, n_pollfd, &revents);
+        assert(r == 0);
+
+        if (cap == 0)
+          assert((revents & ~POLLOUT) == 0);
+        else
+          assert((revents & ~POLLIN) == 0);
+
+        avail = snd_pcm_avail(pcm);
+        assert(avail >= 0);
+
+        r = snd_pcm_status(pcm, status);
+        assert(r == 0);
+
+        /* This assertion fails from time to time. ALSA seems to be broken */
+/*         assert(avail == (snd_pcm_sframes_t) snd_pcm_status_get_avail(status)); */
+/*         printf("%lu %lu\n", (unsigned long) avail, (unsigned long) snd_pcm_status_get_avail(status)); */
+
+        snd_pcm_status_get_htstamp(status, &timestamp);
+        delay = snd_pcm_status_get_delay(status);
+        state = snd_pcm_status_get_state(status);
+
+        r = clock_gettime(CLOCK_MONOTONIC, &now);
+        assert(r == 0);
+
+        assert(!revents || avail > 0);
+
+        if ((!cap && (avail >= fillrate)) || (cap && (unsigned)avail >= buffer_size)) {
+            snd_pcm_sframes_t sframes;
+
+            if (cap == 0)
+              sframes = snd_pcm_writei(pcm, samples, fillrate);
+            else
+              sframes = snd_pcm_readi(pcm, samples, fillrate);
+            assert(sframes == fillrate);
+
+            handled = fillrate;
+            sample_count += fillrate;
+        }
+
+        if (!handled &&
+            memcmp(&timestamp, &last_timestamp, sizeof(timestamp)) == 0 &&
+            avail == last_avail &&
+            delay == last_delay) {
+            /* This is boring */
+            continue;
+        }
+
+        now_us = timespec_us(&now);
+        timestamp_us = timespec_us(&timestamp);
+
+        if (cap == 0)
+            pos = (unsigned long long) ((sample_count - handled - delay) * 1000000LU / SAMPLE_RATE);
+        else
+            pos = (unsigned long long) ((sample_count - handled + delay) * 1000000LU / SAMPLE_RATE);
+
+        if (count++ % 50 == 0)
+            printf("Elapsed\tCPU\tALSA\tPos\tSamples\tavail\tdelay\trevents\thandled\tstate\n");
+
+        printf("%llu\t%llu\t%llu\t%llu\t%llu\t%li\t%li\t%i\t%i\t%i\n",
+               (unsigned long long) (now_us - last_us),
+               (unsigned long long) (now_us - start_us),
+               (unsigned long long) (timestamp_us ? timestamp_us - start_us : 0),
+               pos,
+               (unsigned long long) sample_count,
+               (signed long) avail,
+               (signed long) delay,
+               revents,
+               handled,
+               state);
+
+        if (cap == 0)
+          /** When this assert is hit, most likely something bad
+           * happened, i.e. the avail jumped suddenly. */
+          assert((unsigned) avail <= buffer_size);
+
+        last_avail = avail;
+        last_delay = delay;
+        last_timestamp = timestamp;
+        last_us = now_us;
+    }
+
+    return 0;
+}
diff --git a/src/tests/asyncmsgq-test.c b/src/tests/asyncmsgq-test.c
new file mode 100644 (file)
index 0000000..219c020
--- /dev/null
@@ -0,0 +1,125 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <check.h>
+
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+enum {
+    OPERATION_A,
+    OPERATION_B,
+    OPERATION_C,
+    QUIT
+};
+
+static void the_thread(void *_q) {
+    pa_asyncmsgq *q = _q;
+    int quit = 0;
+
+    do {
+        int code = 0;
+
+        pa_assert_se(pa_asyncmsgq_get(q, NULL, &code, NULL, NULL, NULL, 1) == 0);
+
+        switch (code) {
+
+            case OPERATION_A:
+                pa_log_info("Operation A");
+                break;
+
+            case OPERATION_B:
+                pa_log_info("Operation B");
+                break;
+
+            case OPERATION_C:
+                pa_log_info("Operation C");
+                break;
+
+            case QUIT:
+                pa_log_info("quit");
+                quit = 1;
+                break;
+        }
+
+        pa_asyncmsgq_done(q, 0);
+
+    } while (!quit);
+}
+
+START_TEST (asyncmsgq_test) {
+    pa_asyncmsgq *q;
+    pa_thread *t;
+
+    q = pa_asyncmsgq_new(0);
+    fail_unless(q != NULL);
+
+    t = pa_thread_new("test", the_thread, q);
+    fail_unless(t != NULL);
+
+    pa_log_info("Operation A post");
+    pa_asyncmsgq_post(q, NULL, OPERATION_A, NULL, 0, NULL, NULL);
+
+    pa_thread_yield();
+
+    pa_log_info("Operation B post");
+    pa_asyncmsgq_post(q, NULL, OPERATION_B, NULL, 0, NULL, NULL);
+
+    pa_thread_yield();
+
+    pa_log_info("Operation C send");
+    pa_asyncmsgq_send(q, NULL, OPERATION_C, NULL, 0, NULL);
+
+    pa_thread_yield();
+
+    pa_log_info("Quit post");
+    pa_asyncmsgq_post(q, NULL, QUIT, NULL, 0, NULL, NULL);
+
+    pa_thread_free(t);
+
+    pa_asyncmsgq_unref(q);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Async Message Queue");
+    tc = tcase_create("asyncmsgq");
+    tcase_add_test(tc, asyncmsgq_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/asyncq-test.c b/src/tests/asyncq-test.c
new file mode 100644 (file)
index 0000000..51a557a
--- /dev/null
@@ -0,0 +1,107 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <check.h>
+
+#include <pulse/util.h>
+#include <pulsecore/asyncq.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+static void producer(void *_q) {
+    pa_asyncq *q = _q;
+    int i;
+
+    for (i = 0; i < 1000; i++) {
+        pa_log_debug("pushing %i", i);
+        pa_asyncq_push(q, PA_UINT_TO_PTR(i+1), 1);
+    }
+
+    pa_asyncq_push(q, PA_UINT_TO_PTR(-1), true);
+    pa_log_debug("pushed end");
+}
+
+static void consumer(void *_q) {
+    pa_asyncq *q = _q;
+    void *p;
+    int i;
+
+    pa_msleep(1000);
+
+    for (i = 0;; i++) {
+        p = pa_asyncq_pop(q, true);
+
+        if (p == PA_UINT_TO_PTR(-1))
+            break;
+
+        fail_unless(p == PA_UINT_TO_PTR(i+1));
+
+        pa_log_debug("popped %i", i);
+    }
+
+    pa_log_debug("popped end");
+}
+
+START_TEST (asyncq_test) {
+    pa_asyncq *q;
+    pa_thread *t1, *t2;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    q = pa_asyncq_new(0);
+    fail_unless(q != NULL);
+
+    t1 = pa_thread_new("producer", producer, q);
+    fail_unless(t1 != NULL);
+    t2 = pa_thread_new("consumer", consumer, q);
+    fail_unless(t2 != NULL);
+
+    pa_thread_free(t1);
+    pa_thread_free(t2);
+
+    pa_asyncq_free(q, NULL);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Async Queue");
+    tc = tcase_create("asyncq");
+    tcase_add_test(tc, asyncq_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/channelmap-test.c b/src/tests/channelmap-test.c
new file mode 100644 (file)
index 0000000..eb0187c
--- /dev/null
@@ -0,0 +1,58 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <check.h>
+
+#include <pulse/channelmap.h>
+
+START_TEST (channelmap_test) {
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    pa_channel_map map, map2;
+
+    pa_channel_map_init_auto(&map, 6, PA_CHANNEL_MAP_AIFF);
+
+    fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
+
+    pa_channel_map_init_auto(&map, 6, PA_CHANNEL_MAP_AUX);
+
+    fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
+
+    pa_channel_map_init_auto(&map, 6, PA_CHANNEL_MAP_ALSA);
+
+    fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
+
+    pa_channel_map_init_extend(&map, 14, PA_CHANNEL_MAP_ALSA);
+
+    fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
+
+    pa_channel_map_parse(&map2, cm);
+
+    fail_unless(pa_channel_map_equal(&map, &map2));
+
+    pa_channel_map_parse(&map2, "left,test");
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Channel Map");
+    tc = tcase_create("channelmap");
+    tcase_add_test(tc, channelmap_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/close-test.c b/src/tests/close-test.c
new file mode 100644 (file)
index 0000000..7a6fec5
--- /dev/null
@@ -0,0 +1,20 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <pulsecore/core-util.h>
+
+int main(int argc, char *argv[]) {
+
+    open("/dev/null", O_RDONLY);
+    open("/dev/null", O_RDONLY);
+    open("/dev/null", O_RDONLY);
+    open("/dev/null", O_RDONLY);
+
+    pa_close_all(5, -1);
+
+    return 0;
+}
diff --git a/src/tests/connect-stress.c b/src/tests/connect-stress.c
new file mode 100644 (file)
index 0000000..a243df9
--- /dev/null
@@ -0,0 +1,235 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <check.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/mainloop.h>
+
+#include <pulsecore/sink.h>
+
+/* Set the number of streams such that it allows two simultaneous instances of
+ * connect-stress to be run and not go above the max limit for streams-per-sink.
+ * This leaves enough room for a couple other streams from regular system usage,
+ * which makes a non-error abort less likely (although still easily possible of
+ * playing >=3 streams outside of the test - including internal loopback, rtp,
+ * combine, remap streams etc.) */
+/* #define NSTREAMS ((PA_MAX_INPUTS_PER_SINK/2) - 1) */
+
+/* This test broke when PA_MAX_INPUTS_PER_SINK was increased from 32 to 256.
+ * Because we currently don't have time to figure out why, let's just set
+ * NSTREAMS to 20 in the meantime.
+ */
+#define NSTREAMS 20
+#define NTESTS 1000
+#define SAMPLE_HZ 44100
+
+static pa_context *context = NULL;
+static pa_stream *streams[NSTREAMS];
+static pa_threaded_mainloop *mainloop = NULL;
+static char *bname;
+
+static const pa_sample_spec sample_spec = {
+    .format = PA_SAMPLE_FLOAT32,
+    .rate = SAMPLE_HZ,
+    .channels = 1
+};
+
+static void context_state_callback(pa_context *c, void *userdata);
+
+/* Note: don't conflict with connect(2) declaration */
+static void _connect(const char *name, int *try) {
+    int ret;
+    pa_mainloop_api *api;
+
+    /* Set up a new main loop */
+    mainloop = pa_threaded_mainloop_new();
+    fail_unless(mainloop != NULL);
+
+    api = pa_threaded_mainloop_get_api(mainloop);
+    context = pa_context_new(api, name);
+    fail_unless(context != NULL);
+
+    pa_context_set_state_callback(context, context_state_callback, try);
+
+    /* Connect the context */
+    if (pa_context_connect(context, NULL, 0, NULL) < 0) {
+        fprintf(stderr, "pa_context_connect() failed.\n");
+        ck_abort();
+    }
+
+    ret = pa_threaded_mainloop_start(mainloop);
+    fail_unless(ret == 0);
+}
+
+static void _disconnect(void) {
+    int i;
+
+    fail_unless(mainloop != NULL);
+    fail_unless(context != NULL);
+
+    pa_threaded_mainloop_lock(mainloop);
+
+    for (i = 0; i < NSTREAMS; i++)
+        if (streams[i]) {
+            pa_stream_disconnect(streams[i]);
+            pa_stream_unref(streams[i]);
+            streams[i] = NULL;
+        }
+
+    pa_context_disconnect(context);
+    context = NULL;
+
+    pa_threaded_mainloop_unlock(mainloop);
+    pa_threaded_mainloop_stop(mainloop);
+    pa_threaded_mainloop_free(mainloop);
+    mainloop = NULL;
+}
+
+static const pa_buffer_attr buffer_attr = {
+    .maxlength = SAMPLE_HZ * sizeof(float) * NSTREAMS,
+    .tlength = (uint32_t) -1,
+    .prebuf = 0, /* Setting prebuf to 0 guarantees us the streams will run synchronously, no matter what */
+    .minreq = (uint32_t) -1,
+    .fragsize = 0
+};
+
+static void stream_write_callback(pa_stream *stream, size_t nbytes, void *userdata) {
+    char silence[8192];
+
+    memset(silence, 0, sizeof(silence));
+
+    while (nbytes) {
+        int n = PA_MIN(sizeof(silence), nbytes);
+        pa_stream_write(stream, silence, n, NULL, 0, 0);
+        nbytes -= n;
+    }
+}
+
+static void stream_state_callback(pa_stream *s, void *userdata) {
+    fail_unless(s != NULL);
+
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+        case PA_STREAM_TERMINATED:
+        case PA_STREAM_READY:
+            break;
+
+        default:
+        case PA_STREAM_FAILED:
+            fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+            ck_abort();
+    }
+}
+
+static void context_state_callback(pa_context *c, void *userdata) {
+    int *try;
+
+    fail_unless(c != NULL);
+    fail_unless(userdata != NULL);
+
+    try = (int*)userdata;
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+
+            int i;
+            fprintf(stderr, "Connection (%d of %d) established.\n", (*try)+1, NTESTS);
+
+            for (i = 0; i < NSTREAMS; i++) {
+                char name[64];
+
+                snprintf(name, sizeof(name), "stream #%i", i);
+                streams[i] = pa_stream_new(c, name, &sample_spec, NULL);
+                fail_unless(streams[i] != NULL);
+                pa_stream_set_state_callback(streams[i], stream_state_callback, NULL);
+                pa_stream_set_write_callback(streams[i], stream_write_callback, NULL);
+                pa_stream_connect_playback(streams[i], NULL, &buffer_attr, 0, NULL, NULL);
+            }
+
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            fprintf(stderr, "Connection terminated.\n");
+            pa_context_unref(context);
+            context = NULL;
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
+            ck_abort();
+    }
+}
+
+START_TEST (connect_stress_test) {
+    int i;
+
+    for (i = 0; i < NSTREAMS; i++)
+        streams[i] = NULL;
+
+    for (i = 0; i < NTESTS; i++) {
+        _connect(bname, &i);
+        usleep(rand() % 500000);
+        _disconnect();
+        usleep(rand() % 500000);
+    }
+
+    fprintf(stderr, "Done.\n");
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    bname = argv[0];
+
+    s = suite_create("Connect Stress");
+    tc = tcase_create("connectstress");
+    tcase_add_test(tc, connect_stress_test);
+    tcase_set_timeout(tc, 20 * 60);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/core-util-test.c b/src/tests/core-util-test.c
new file mode 100644 (file)
index 0000000..8d1db0c
--- /dev/null
@@ -0,0 +1,284 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+
+#include <check.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+
+START_TEST (modargs_test_parse_boolean) {
+    ck_assert_int_eq(pa_parse_boolean("true"), true);
+    ck_assert_int_eq(pa_parse_boolean("yes"), true);
+    ck_assert_int_eq(pa_parse_boolean("1"), true);
+
+    ck_assert_int_eq(pa_parse_boolean("false"), false);
+    ck_assert_int_eq(pa_parse_boolean("no"), false);
+    ck_assert_int_eq(pa_parse_boolean("0"), false);
+
+    ck_assert_int_eq(pa_parse_boolean("maybe"), -1);
+    ck_assert_int_eq(pa_parse_boolean("42"), -1);
+}
+END_TEST
+
+START_TEST (modargs_test_parse_volume) {
+    pa_volume_t value;
+
+    // dB volumes
+    ck_assert_int_eq(pa_parse_volume("-20dB", &value), 0);
+    ck_assert_int_eq(value, 30419);
+    ck_assert_int_eq(pa_parse_volume("-10dB", &value), 0);
+    ck_assert_int_eq(value, 44649);
+    ck_assert_int_eq(pa_parse_volume("-1dB", &value), 0);
+    ck_assert_int_eq(value, 63069);
+    ck_assert_int_eq(pa_parse_volume("0dB", &value), 0);
+    ck_assert_int_eq(value, 65536);
+    ck_assert_int_eq(pa_parse_volume("1dB", &value), 0);
+    ck_assert_int_eq(value, 68100);
+    ck_assert_int_eq(pa_parse_volume("10dB", &value), 0);
+    ck_assert_int_eq(value, 96194);
+
+    // lowercase db
+    ck_assert_int_eq(pa_parse_volume("10db", &value), 0);
+    ck_assert_int_eq(value, 96194);
+
+    // percentage volumes
+    ck_assert_int_eq(pa_parse_volume("0%", &value), 0);
+    ck_assert_int_eq(value, 0);
+    ck_assert_int_eq(pa_parse_volume("50%", &value), 0);
+    ck_assert_int_eq(value, 32768);
+    ck_assert_int_eq(pa_parse_volume("100%", &value), 0);
+    ck_assert_int_eq(value, 65536);
+    ck_assert_int_eq(pa_parse_volume("150%", &value), 0);
+    ck_assert_int_eq(value, 98304);
+
+    // integer volumes`
+    ck_assert_int_eq(pa_parse_volume("0", &value), 0);
+    ck_assert_int_eq(value, 0);
+    ck_assert_int_eq(pa_parse_volume("100", &value), 0);
+    ck_assert_int_eq(value, 100);
+    ck_assert_int_eq(pa_parse_volume("1000", &value), 0);
+    ck_assert_int_eq(value, 1000);
+    ck_assert_int_eq(pa_parse_volume("65536", &value), 0);
+    ck_assert_int_eq(value, 65536);
+    ck_assert_int_eq(pa_parse_volume("100000", &value), 0);
+    ck_assert_int_eq(value, 100000);
+
+    // invalid volumes
+    ck_assert_int_lt(pa_parse_volume("", &value), 0);
+    ck_assert_int_lt(pa_parse_volume("-2", &value), 0);
+    ck_assert_int_lt(pa_parse_volume("on", &value), 0);
+    ck_assert_int_lt(pa_parse_volume("off", &value), 0);
+    ck_assert_int_lt(pa_parse_volume("none", &value), 0);
+}
+END_TEST
+
+START_TEST (modargs_test_atoi) {
+    int32_t value;
+
+    // decimal
+    ck_assert_int_eq(pa_atoi("100000", &value), 0);
+    ck_assert_int_eq(value, 100000);
+    ck_assert_int_eq(pa_atoi("-100000", &value), 0);
+    ck_assert_int_eq(value, -100000);
+
+    // hexadecimal
+    ck_assert_int_eq(pa_atoi("0x100000", &value), 0);
+    ck_assert_int_eq(value, 0x100000);
+    ck_assert_int_eq(pa_atoi("-0x100000", &value), 0);
+    ck_assert_int_eq(value, -0x100000);
+
+    // invalid values
+    ck_assert_int_lt(pa_atoi("3.14", &value), 0);
+    ck_assert_int_lt(pa_atoi("7*8", &value), 0);
+    ck_assert_int_lt(pa_atoi("false", &value), 0);
+}
+END_TEST
+
+START_TEST (modargs_test_atou) {
+    uint32_t value;
+
+    // decimal
+    ck_assert_int_eq(pa_atou("100000", &value), 0);
+    ck_assert_int_eq(value, 100000);
+
+    // hexadecimal
+    ck_assert_int_eq(pa_atou("0x100000", &value), 0);
+    ck_assert_int_eq(value, 0x100000);
+
+    // invalid values
+    ck_assert_int_lt(pa_atou("-100000", &value), 0);
+    ck_assert_int_lt(pa_atou("-0x100000", &value), 0);
+    ck_assert_int_lt(pa_atou("3.14", &value), 0);
+    ck_assert_int_lt(pa_atou("7*8", &value), 0);
+    ck_assert_int_lt(pa_atou("false", &value), 0);
+}
+END_TEST
+
+START_TEST (modargs_test_atol) {
+    long value;
+
+    // decimal
+    ck_assert_int_eq(pa_atol("100000", &value), 0);
+    ck_assert_int_eq(value, 100000l);
+    ck_assert_int_eq(pa_atol("-100000", &value), 0);
+    ck_assert_int_eq(value, -100000l);
+
+    // hexadecimal
+    ck_assert_int_eq(pa_atol("0x100000", &value), 0);
+    ck_assert_int_eq(value, 0x100000l);
+    ck_assert_int_eq(pa_atol("-0x100000", &value), 0);
+    ck_assert_int_eq(value, -0x100000l);
+
+    // invalid values
+    ck_assert_int_lt(pa_atol("3.14", &value), 0);
+    ck_assert_int_lt(pa_atol("7*8", &value), 0);
+    ck_assert_int_lt(pa_atol("false", &value), 0);
+}
+END_TEST
+
+START_TEST (modargs_test_atod) {
+    double value;
+    double epsilon = 0.001;
+
+    // decimal
+    ck_assert_int_eq(pa_atod("100000", &value), 0);
+    ck_assert(value > 100000 - epsilon);
+    ck_assert(value < 100000 + epsilon);
+    ck_assert_int_eq(pa_atod("-100000", &value), 0);
+    ck_assert(value > -100000 - epsilon);
+    ck_assert(value < -100000 + epsilon);
+    ck_assert_int_eq(pa_atod("3.14", &value), 0);
+    ck_assert(value > 3.14 - epsilon);
+    ck_assert(value < 3.14 + epsilon);
+
+    // invalid values
+    ck_assert_int_lt(pa_atod("7*8", &value), 0);
+    ck_assert_int_lt(pa_atod("false", &value), 0);
+}
+END_TEST
+
+START_TEST (modargs_test_replace) {
+    char* value;
+
+    value = pa_replace("abcde", "bcd", "XYZ");
+    ck_assert_str_eq(value, "aXYZe");
+    pa_xfree(value);
+
+    value = pa_replace("abe", "b", "bab");
+    ck_assert_str_eq(value, "ababe");
+    pa_xfree(value);
+
+    value = pa_replace("abe", "c", "bab");
+    ck_assert_str_eq(value, "abe");
+    pa_xfree(value);
+
+    value = pa_replace("abcde", "bcd", "");
+    ck_assert_str_eq(value, "ae");
+    pa_xfree(value);
+}
+END_TEST
+
+START_TEST (modargs_test_replace_fail_1) {
+    pa_replace(NULL, "b", "bab");
+}
+END_TEST
+
+START_TEST (modargs_test_replace_fail_2) {
+    pa_replace("abe", NULL, "bab");
+}
+END_TEST
+
+START_TEST (modargs_test_replace_fail_3) {
+    pa_replace("abcde", "b", NULL);
+}
+END_TEST
+
+START_TEST (modargs_test_escape) {
+    char* value;
+
+    value = pa_escape("abcde", "bcd");
+    ck_assert_str_eq(value, "a\\b\\c\\de");
+    pa_xfree(value);
+
+    value = pa_escape("\\", "bcd");
+    ck_assert_str_eq(value, "\\\\");
+    pa_xfree(value);
+
+    value = pa_escape("\\", NULL);
+    ck_assert_str_eq(value, "\\\\");
+    pa_xfree(value);
+}
+END_TEST
+
+START_TEST (modargs_test_replace_fail_4) {
+    pa_replace("abe", "", "bab");
+}
+END_TEST
+
+START_TEST (modargs_test_unescape) {
+    char* value;
+
+    value = pa_unescape(pa_xstrdup("a\\b\\c\\de"));
+    ck_assert_str_eq(value, "abcde");
+    pa_xfree(value);
+
+    value = pa_unescape(pa_xstrdup("\\\\"));
+    ck_assert_str_eq(value, "\\");
+    pa_xfree(value);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("Core-Util");
+
+    tc = tcase_create("core-util");
+    suite_add_tcase(s, tc);
+    tcase_add_test(tc, modargs_test_parse_boolean);
+    tcase_add_test(tc, modargs_test_parse_volume);
+    tcase_add_test(tc, modargs_test_atoi);
+    tcase_add_test(tc, modargs_test_atou);
+    tcase_add_test(tc, modargs_test_atol);
+    tcase_add_test(tc, modargs_test_atod);
+    tcase_add_test(tc, modargs_test_replace);
+    tcase_add_test_raise_signal(tc, modargs_test_replace_fail_1, SIGABRT);
+    tcase_add_test_raise_signal(tc, modargs_test_replace_fail_2, SIGABRT);
+    tcase_add_test_raise_signal(tc, modargs_test_replace_fail_3, SIGABRT);
+    tcase_add_test_raise_signal(tc, modargs_test_replace_fail_4, SIGABRT);
+    tcase_add_test(tc, modargs_test_escape);
+    tcase_add_test(tc, modargs_test_unescape);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/cpu-mix-test.c b/src/tests/cpu-mix-test.c
new file mode 100644 (file)
index 0000000..cad8984
--- /dev/null
@@ -0,0 +1,224 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <pulsecore/cpu.h>
+#include <pulsecore/cpu-arm.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/mix.h>
+
+#include "runtime-test-util.h"
+
+#define SAMPLES 1028
+#define TIMES 1000
+#define TIMES2 100
+
+static void acquire_mix_streams(pa_mix_info streams[], unsigned nstreams) {
+    unsigned i;
+
+    for (i = 0; i < nstreams; i++)
+        streams[i].ptr = pa_memblock_acquire_chunk(&streams[i].chunk);
+}
+
+static void release_mix_streams(pa_mix_info streams[], unsigned nstreams) {
+    unsigned i;
+
+    for (i = 0; i < nstreams; i++)
+        pa_memblock_release(streams[i].chunk.memblock);
+}
+
+static void run_mix_test(
+        pa_do_mix_func_t func,
+        pa_do_mix_func_t orig_func,
+        int align,
+        int channels,
+        bool correct,
+        bool perf) {
+
+    PA_DECLARE_ALIGNED(8, int16_t, in0[SAMPLES * 4]) = { 0 };
+    PA_DECLARE_ALIGNED(8, int16_t, in1[SAMPLES * 4]) = { 0 };
+    PA_DECLARE_ALIGNED(8, int16_t, out[SAMPLES * 4]) = { 0 };
+    PA_DECLARE_ALIGNED(8, int16_t, out_ref[SAMPLES * 4]) = { 0 };
+    int16_t *samples0, *samples1;
+    int16_t *samples, *samples_ref;
+    int nsamples;
+    pa_mempool *pool;
+    pa_memchunk c0, c1;
+    pa_mix_info m[2];
+    int i;
+
+    pa_assert(channels == 1 || channels == 2 || channels == 4);
+
+    /* Force sample alignment as requested */
+    samples0 = in0 + (8 - align);
+    samples1 = in1 + (8 - align);
+    samples = out + (8 - align);
+    samples_ref = out_ref + (8 - align);
+    nsamples = channels * (SAMPLES - (8 - align));
+
+    fail_unless((pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true)) != NULL, NULL);
+
+    pa_random(samples0, nsamples * sizeof(int16_t));
+    c0.memblock = pa_memblock_new_fixed(pool, samples0, nsamples * sizeof(int16_t), false);
+    c0.length = pa_memblock_get_length(c0.memblock);
+    c0.index = 0;
+
+    pa_random(samples1, nsamples * sizeof(int16_t));
+    c1.memblock = pa_memblock_new_fixed(pool, samples1, nsamples * sizeof(int16_t), false);
+    c1.length = pa_memblock_get_length(c1.memblock);
+    c1.index = 0;
+
+    m[0].chunk = c0;
+    m[0].volume.channels = channels;
+    for (i = 0; i < channels; i++) {
+        m[0].volume.values[i] = PA_VOLUME_NORM;
+        m[0].linear[i].i = 0x5555;
+    }
+
+    m[1].chunk = c1;
+    m[1].volume.channels = channels;
+    for (i = 0; i < channels; i++) {
+        m[1].volume.values[i] = PA_VOLUME_NORM;
+        m[1].linear[i].i = 0x6789;
+    }
+
+    if (correct) {
+        acquire_mix_streams(m, 2);
+        orig_func(m, 2, channels, samples_ref, nsamples * sizeof(int16_t));
+        release_mix_streams(m, 2);
+
+        acquire_mix_streams(m, 2);
+        func(m, 2, channels, samples, nsamples * sizeof(int16_t));
+        release_mix_streams(m, 2);
+
+        for (i = 0; i < nsamples; i++) {
+            if (samples[i] != samples_ref[i]) {
+                pa_log_debug("Correctness test failed: align=%d, channels=%d", align, channels);
+                pa_log_debug("%d: %hd != %04hd (%hd + %hd)",
+                    i,
+                    samples[i], samples_ref[i],
+                    samples0[i], samples1[i]);
+                ck_abort();
+            }
+        }
+    }
+
+    if (perf) {
+        pa_log_debug("Testing %d-channel mixing performance with %d sample alignment", channels, align);
+
+        PA_RUNTIME_TEST_RUN_START("func", TIMES, TIMES2) {
+            acquire_mix_streams(m, 2);
+            func(m, 2, channels, samples, nsamples * sizeof(int16_t));
+            release_mix_streams(m, 2);
+        } PA_RUNTIME_TEST_RUN_STOP
+
+        PA_RUNTIME_TEST_RUN_START("orig", TIMES, TIMES2) {
+            acquire_mix_streams(m, 2);
+            orig_func(m, 2, channels, samples_ref, nsamples * sizeof(int16_t));
+            release_mix_streams(m, 2);
+        } PA_RUNTIME_TEST_RUN_STOP
+    }
+
+    pa_memblock_unref(c0.memblock);
+    pa_memblock_unref(c1.memblock);
+
+    pa_mempool_unref(pool);
+}
+
+START_TEST (mix_special_test) {
+    pa_cpu_info cpu_info = { PA_CPU_UNDEFINED, {}, false };
+    pa_do_mix_func_t orig_func, special_func;
+
+    cpu_info.force_generic_code = true;
+    pa_mix_func_init(&cpu_info);
+    orig_func = pa_get_mix_func(PA_SAMPLE_S16NE);
+
+    cpu_info.force_generic_code = false;
+    pa_mix_func_init(&cpu_info);
+    special_func = pa_get_mix_func(PA_SAMPLE_S16NE);
+
+    pa_log_debug("Checking special mix (s16, stereo)");
+    run_mix_test(special_func, orig_func, 7, 2, true, true);
+
+    pa_log_debug("Checking special mix (s16, 4-channel)");
+    run_mix_test(special_func, orig_func, 7, 4, true, true);
+
+    pa_log_debug("Checking special mix (s16, mono)");
+    run_mix_test(special_func, orig_func, 7, 1, true, true);
+}
+END_TEST
+
+#if defined (__arm__) && defined (__linux__) && defined (HAVE_NEON)
+START_TEST (mix_neon_test) {
+    pa_do_mix_func_t orig_func, neon_func;
+    pa_cpu_arm_flag_t flags = 0;
+
+    pa_cpu_get_arm_flags(&flags);
+
+    if (!(flags & PA_CPU_ARM_NEON)) {
+        pa_log_info("NEON not supported. Skipping");
+        return;
+    }
+
+    orig_func = pa_get_mix_func(PA_SAMPLE_S16NE);
+    pa_mix_func_init_neon(flags);
+    neon_func = pa_get_mix_func(PA_SAMPLE_S16NE);
+
+    pa_log_debug("Checking NEON mix (s16, stereo)");
+    run_mix_test(neon_func, orig_func, 7, 2, true, true);
+
+    pa_log_debug("Checking NEON mix (s16, 4-channel)");
+    run_mix_test(neon_func, orig_func, 7, 4, true, true);
+
+    pa_log_debug("Checking NEON mix (s16, mono)");
+    run_mix_test(neon_func, orig_func, 7, 1, true, true);
+}
+END_TEST
+#endif /* defined (__arm__) && defined (__linux__) && defined (HAVE_NEON) */
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("CPU");
+
+    tc = tcase_create("mix");
+    tcase_add_test(tc, mix_special_test);
+#if defined (__arm__) && defined (__linux__) && defined (HAVE_NEON)
+    tcase_add_test(tc, mix_neon_test);
+#endif
+    tcase_set_timeout(tc, 120);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/cpu-remap-test.c b/src/tests/cpu-remap-test.c
new file mode 100644 (file)
index 0000000..c4eec45
--- /dev/null
@@ -0,0 +1,434 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <pulsecore/cpu-x86.h>
+#include <pulsecore/cpu.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/remap.h>
+
+#include "runtime-test-util.h"
+
+#define SAMPLES 1027
+#define TIMES 1000
+#define TIMES2 100
+
+static void run_remap_test_float(
+        pa_remap_t *remap_func,
+        pa_remap_t *remap_orig,
+        int align,
+        bool correct,
+        bool perf) {
+
+    PA_DECLARE_ALIGNED(8, float, out_buf_ref[SAMPLES*8]) = { 0.0f, };
+    PA_DECLARE_ALIGNED(8, float, out_buf[SAMPLES*8]) = { 0.0f, };
+    PA_DECLARE_ALIGNED(8, float, in_buf[SAMPLES*8]);
+    float *out, *out_ref;
+    float *in;
+    unsigned n_ic = remap_func->i_ss.channels;
+    unsigned n_oc = remap_func->o_ss.channels;
+    unsigned i, nsamples;
+
+    pa_assert(n_ic >= 1 && n_ic <= 8);
+    pa_assert(n_oc >= 1 && n_oc <= 8);
+
+    /* Force sample alignment as requested */
+    out = out_buf + (8 - align);
+    out_ref = out_buf_ref + (8 - align);
+    in = in_buf + (8 - align);
+    nsamples = SAMPLES - (8 - align);
+
+    for (i = 0; i < nsamples * n_ic; i++)
+        in[i] = 2.1f * (rand()/(float) RAND_MAX - 0.5f);
+
+    if (correct) {
+        remap_orig->do_remap(remap_orig, out_ref, in, nsamples);
+        remap_func->do_remap(remap_func, out, in, nsamples);
+
+        for (i = 0; i < nsamples * n_oc; i++) {
+            if (fabsf(out[i] - out_ref[i]) > 0.0001f) {
+                pa_log_debug("Correctness test failed: align=%d", align);
+                pa_log_debug("%d: %.24f != %.24f", i,
+                    out[i], out_ref[i]);
+                ck_abort();
+            }
+        }
+    }
+
+    if (perf) {
+        pa_log_debug("Testing remap performance with %d sample alignment", align);
+
+        PA_RUNTIME_TEST_RUN_START("func", TIMES, TIMES2) {
+            remap_func->do_remap(remap_func, out, in, nsamples);
+        } PA_RUNTIME_TEST_RUN_STOP
+
+        PA_RUNTIME_TEST_RUN_START("orig", TIMES, TIMES2) {
+            remap_orig->do_remap(remap_orig, out_ref, in, nsamples);
+        } PA_RUNTIME_TEST_RUN_STOP
+    }
+}
+
+static void run_remap_test_s16(
+        pa_remap_t *remap_func,
+        pa_remap_t *remap_orig,
+        int align,
+        bool correct,
+        bool perf) {
+
+    PA_DECLARE_ALIGNED(8, int16_t, out_buf_ref[SAMPLES*8]) = { 0 };
+    PA_DECLARE_ALIGNED(8, int16_t, out_buf[SAMPLES*8]) = { 0 };
+    PA_DECLARE_ALIGNED(8, int16_t, in_buf[SAMPLES*8]);
+    int16_t *out, *out_ref;
+    int16_t *in;
+    unsigned n_ic = remap_func->i_ss.channels;
+    unsigned n_oc = remap_func->o_ss.channels;
+    unsigned i, nsamples;
+
+    pa_assert(n_ic >= 1 && n_ic <= 8);
+    pa_assert(n_oc >= 1 && n_oc <= 8);
+
+    /* Force sample alignment as requested */
+    out = out_buf + (8 - align);
+    out_ref = out_buf_ref + (8 - align);
+    in = in_buf + (8 - align);
+    nsamples = SAMPLES - (8 - align);
+
+    pa_random(in, nsamples * n_ic * sizeof(int16_t));
+
+    if (correct) {
+        remap_orig->do_remap(remap_orig, out_ref, in, nsamples);
+        remap_func->do_remap(remap_func, out, in, nsamples);
+
+        for (i = 0; i < nsamples * n_oc; i++) {
+            if (abs(out[i] - out_ref[i]) > 3) {
+                pa_log_debug("Correctness test failed: align=%d", align);
+                pa_log_debug("%d: %d != %d", i, out[i], out_ref[i]);
+                ck_abort();
+            }
+        }
+    }
+
+    if (perf) {
+        pa_log_debug("Testing remap performance with %d sample alignment", align);
+
+        PA_RUNTIME_TEST_RUN_START("func", TIMES, TIMES2) {
+            remap_func->do_remap(remap_func, out, in, nsamples);
+        } PA_RUNTIME_TEST_RUN_STOP
+
+        PA_RUNTIME_TEST_RUN_START("orig", TIMES, TIMES2) {
+            remap_orig->do_remap(remap_orig, out_ref, in, nsamples);
+        } PA_RUNTIME_TEST_RUN_STOP
+    }
+}
+
+static void setup_remap_channels(
+    pa_remap_t *m,
+    pa_sample_format_t f,
+    unsigned in_channels,
+    unsigned out_channels,
+    bool rearrange) {
+
+    unsigned i, o;
+
+    m->format = f;
+    m->i_ss.channels = in_channels;
+    m->o_ss.channels = out_channels;
+
+    if (rearrange) {
+        for (o = 0; o < out_channels; o++) {
+            for (i = 0; i < in_channels; i++) {
+                m->map_table_f[o][i] = (o == i) ? 1.0f : 0.0f;
+                m->map_table_i[o][i] = (o == i) ? 0x10000 : 0;
+            }
+        }
+    } else {
+        for (o = 0; o < out_channels; o++) {
+            for (i = 0; i < in_channels; i++) {
+                m->map_table_f[o][i] = 1.0f / in_channels;
+                m->map_table_i[o][i] = 0x10000 / in_channels;
+            }
+        }
+    }
+}
+
+static void remap_test_channels(
+    pa_remap_t *remap_func, pa_remap_t *remap_orig) {
+
+    if (!remap_orig->do_remap) {
+        pa_log_warn("No reference remapping function, abort test");
+        return;
+    }
+
+    if (!remap_func->do_remap || remap_func->do_remap == remap_orig->do_remap) {
+        pa_log_warn("No remapping function, abort test");
+        return;
+    }
+
+    pa_assert(remap_func->format == remap_orig->format);
+
+    switch (remap_func->format) {
+    case PA_SAMPLE_FLOAT32NE:
+        run_remap_test_float(remap_func, remap_orig, 0, true, false);
+        run_remap_test_float(remap_func, remap_orig, 1, true, false);
+        run_remap_test_float(remap_func, remap_orig, 2, true, false);
+        run_remap_test_float(remap_func, remap_orig, 3, true, true);
+        break;
+    case PA_SAMPLE_S16NE:
+        run_remap_test_s16(remap_func, remap_orig, 0, true, false);
+        run_remap_test_s16(remap_func, remap_orig, 1, true, false);
+        run_remap_test_s16(remap_func, remap_orig, 2, true, false);
+        run_remap_test_s16(remap_func, remap_orig, 3, true, true);
+        break;
+    default:
+        pa_assert_not_reached();
+    }
+}
+
+static void remap_init_test_channels(
+        pa_init_remap_func_t init_func,
+        pa_init_remap_func_t orig_init_func,
+        pa_sample_format_t f,
+        unsigned in_channels,
+        unsigned out_channels,
+        bool rearrange) {
+
+    pa_remap_t remap_orig, remap_func;
+
+    setup_remap_channels(&remap_orig, f, in_channels, out_channels, rearrange);
+    orig_init_func(&remap_orig);
+
+    setup_remap_channels(&remap_func, f, in_channels, out_channels, rearrange);
+    init_func(&remap_func);
+
+    remap_test_channels(&remap_func, &remap_orig);
+}
+
+static void remap_init2_test_channels(
+        pa_sample_format_t f,
+        unsigned in_channels,
+        unsigned out_channels,
+        bool rearrange) {
+
+    pa_cpu_info cpu_info = { PA_CPU_UNDEFINED, {}, false };
+    pa_remap_t remap_orig, remap_func;
+
+    cpu_info.force_generic_code = true;
+    pa_remap_func_init(&cpu_info);
+    setup_remap_channels(&remap_orig, f, in_channels, out_channels, rearrange);
+    pa_init_remap_func(&remap_orig);
+
+    cpu_info.force_generic_code = false;
+    pa_remap_func_init(&cpu_info);
+    setup_remap_channels(&remap_func, f, in_channels, out_channels, rearrange);
+    pa_init_remap_func(&remap_func);
+
+    remap_test_channels(&remap_func, &remap_orig);
+}
+
+START_TEST (remap_special_test) {
+    pa_log_debug("Checking special remap (float, mono->stereo)");
+    remap_init2_test_channels(PA_SAMPLE_FLOAT32NE, 1, 2, false);
+    pa_log_debug("Checking special remap (float, mono->4-channel)");
+    remap_init2_test_channels(PA_SAMPLE_FLOAT32NE, 1, 4, false);
+
+    pa_log_debug("Checking special remap (s16, mono->stereo)");
+    remap_init2_test_channels(PA_SAMPLE_S16NE, 1, 2, false);
+    pa_log_debug("Checking special remap (s16, mono->4-channel)");
+    remap_init2_test_channels(PA_SAMPLE_S16NE, 1, 4, false);
+
+    pa_log_debug("Checking special remap (float, stereo->mono)");
+    remap_init2_test_channels(PA_SAMPLE_FLOAT32NE, 2, 1, false);
+    pa_log_debug("Checking special remap (float, 4-channel->mono)");
+    remap_init2_test_channels(PA_SAMPLE_FLOAT32NE, 4, 1, false);
+
+    pa_log_debug("Checking special remap (s16, stereo->mono)");
+    remap_init2_test_channels(PA_SAMPLE_S16NE, 2, 1, false);
+    pa_log_debug("Checking special remap (s16, 4-channel->mono)");
+    remap_init2_test_channels(PA_SAMPLE_S16NE, 4, 1, false);
+}
+END_TEST
+
+START_TEST (rearrange_special_test) {
+    pa_log_debug("Checking special remap (s16, stereo rearrange)");
+    remap_init2_test_channels(PA_SAMPLE_S16NE, 2, 2, true);
+    pa_log_debug("Checking special remap (float, stereo rearrange)");
+    remap_init2_test_channels(PA_SAMPLE_FLOAT32NE, 2, 2, true);
+
+    pa_log_debug("Checking special remap (s16, 4-channel rearrange)");
+    remap_init2_test_channels(PA_SAMPLE_S16NE, 4, 4, true);
+    pa_log_debug("Checking special remap (float, 4-channel rearrange)");
+    remap_init2_test_channels(PA_SAMPLE_FLOAT32NE, 4, 4, true);
+}
+END_TEST
+
+#if defined (__i386__) || defined (__amd64__)
+START_TEST (remap_mmx_test) {
+    pa_cpu_x86_flag_t flags = 0;
+    pa_init_remap_func_t init_func, orig_init_func;
+
+    pa_cpu_get_x86_flags(&flags);
+    if (!(flags & PA_CPU_X86_MMX)) {
+        pa_log_info("MMX not supported. Skipping");
+        return;
+    }
+
+    pa_log_debug("Checking MMX remap (float, mono->stereo)");
+    orig_init_func = pa_get_init_remap_func();
+    pa_remap_func_init_mmx(flags);
+    init_func = pa_get_init_remap_func();
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_FLOAT32NE, 1, 2, false);
+
+    pa_log_debug("Checking MMX remap (s16, mono->stereo)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_S16NE, 1, 2, false);
+}
+END_TEST
+
+START_TEST (remap_sse2_test) {
+    pa_cpu_x86_flag_t flags = 0;
+    pa_init_remap_func_t init_func, orig_init_func;
+
+    pa_cpu_get_x86_flags(&flags);
+    if (!(flags & PA_CPU_X86_SSE2)) {
+        pa_log_info("SSE2 not supported. Skipping");
+        return;
+    }
+
+    pa_log_debug("Checking SSE2 remap (float, mono->stereo)");
+    orig_init_func = pa_get_init_remap_func();
+    pa_remap_func_init_sse(flags);
+    init_func = pa_get_init_remap_func();
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_FLOAT32NE, 1, 2, false);
+
+    pa_log_debug("Checking SSE2 remap (s16, mono->stereo)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_S16NE, 1, 2, false);
+}
+END_TEST
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+#if defined (__arm__) && defined (__linux__) && defined (HAVE_NEON)
+START_TEST (remap_neon_test) {
+    pa_cpu_arm_flag_t flags = 0;
+    pa_init_remap_func_t init_func, orig_init_func;
+
+    pa_cpu_get_arm_flags(&flags);
+    if (!(flags & PA_CPU_ARM_NEON)) {
+        pa_log_info("NEON not supported. Skipping");
+        return;
+    }
+
+    orig_init_func = pa_get_init_remap_func();
+    pa_remap_func_init_neon(flags);
+    init_func = pa_get_init_remap_func();
+
+    pa_log_debug("Checking NEON remap (float, mono->stereo)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_FLOAT32NE, 1, 2, false);
+    pa_log_debug("Checking NEON remap (float, mono->4-channel)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_FLOAT32NE, 1, 4, false);
+
+    pa_log_debug("Checking NEON remap (s16, mono->stereo)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_S16NE, 1, 2, false);
+    pa_log_debug("Checking NEON remap (s16, mono->4-channel)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_S16NE, 1, 4, false);
+
+    pa_log_debug("Checking NEON remap (float, stereo->mono)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_FLOAT32NE, 2, 1, false);
+    pa_log_debug("Checking NEON remap (float, 4-channel->mono)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_FLOAT32NE, 4, 1, false);
+
+    pa_log_debug("Checking NEON remap (s16, stereo->mono)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_S16NE, 2, 1, false);
+    pa_log_debug("Checking NEON remap (s16, 4-channel->mono)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_S16NE, 4, 1, false);
+
+    pa_log_debug("Checking NEON remap (float, 4-channel->4-channel)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_FLOAT32NE, 4, 4, false);
+    pa_log_debug("Checking NEON remap (s16, 4-channel->4-channel)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_S16NE, 4, 4, false);
+}
+END_TEST
+
+START_TEST (rearrange_neon_test) {
+    pa_cpu_arm_flag_t flags = 0;
+    pa_init_remap_func_t init_func, orig_init_func;
+
+    pa_cpu_get_arm_flags(&flags);
+    if (!(flags & PA_CPU_ARM_NEON)) {
+        pa_log_info("NEON not supported. Skipping");
+        return;
+    }
+
+    orig_init_func = pa_get_init_remap_func();
+    pa_remap_func_init_neon(flags);
+    init_func = pa_get_init_remap_func();
+
+    pa_log_debug("Checking NEON remap (float, stereo rearrange)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_FLOAT32NE, 2, 2, true);
+    pa_log_debug("Checking NEON remap (s16, stereo rearrange)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_S16NE, 2, 2, true);
+
+    pa_log_debug("Checking NEON remap (float, 4-channel rearrange)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_FLOAT32NE, 4, 4, true);
+    pa_log_debug("Checking NEON remap (s16, 4-channel rearrange)");
+    remap_init_test_channels(init_func, orig_init_func, PA_SAMPLE_S16NE, 4, 4, true);
+}
+END_TEST
+#endif
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("CPU");
+
+    tc = tcase_create("remap");
+    tcase_add_test(tc, remap_special_test);
+#if defined (__i386__) || defined (__amd64__)
+    tcase_add_test(tc, remap_mmx_test);
+    tcase_add_test(tc, remap_sse2_test);
+#endif
+#if defined (__arm__) && defined (__linux__) && defined (HAVE_NEON)
+    tcase_add_test(tc, remap_neon_test);
+#endif
+    tcase_set_timeout(tc, 120);
+    suite_add_tcase(s, tc);
+
+    tc = tcase_create("rearrange");
+    tcase_add_test(tc, rearrange_special_test);
+#if defined (__arm__) && defined (__linux__) && defined (HAVE_NEON)
+    tcase_add_test(tc, rearrange_neon_test);
+#endif
+    tcase_set_timeout(tc, 120);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/cpu-sconv-test.c b/src/tests/cpu-sconv-test.c
new file mode 100644 (file)
index 0000000..3f189d1
--- /dev/null
@@ -0,0 +1,263 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <pulsecore/cpu-arm.h>
+#include <pulsecore/cpu-x86.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/sconv.h>
+
+#include "runtime-test-util.h"
+
+#define SAMPLES 1028
+#define TIMES 1000
+#define TIMES2 100
+
+static void run_conv_test_float_to_s16(
+        pa_convert_func_t func,
+        pa_convert_func_t orig_func,
+        int align,
+        bool correct,
+        bool perf) {
+
+    PA_DECLARE_ALIGNED(8, int16_t, s[SAMPLES]) = { 0 };
+    PA_DECLARE_ALIGNED(8, int16_t, s_ref[SAMPLES]) = { 0 };
+    PA_DECLARE_ALIGNED(8, float, f[SAMPLES]);
+    int16_t *samples, *samples_ref;
+    float *floats;
+    int i, nsamples;
+
+    /* Force sample alignment as requested */
+    samples = s + (8 - align);
+    samples_ref = s_ref + (8 - align);
+    floats = f + (8 - align);
+    nsamples = SAMPLES - (8 - align);
+
+    for (i = 0; i < nsamples; i++) {
+        floats[i] = 2.1f * (rand()/(float) RAND_MAX - 0.5f);
+    }
+
+    if (correct) {
+        orig_func(nsamples, floats, samples_ref);
+        func(nsamples, floats, samples);
+
+        for (i = 0; i < nsamples; i++) {
+            if (abs(samples[i] - samples_ref[i]) > 1) {
+                pa_log_debug("Correctness test failed: align=%d", align);
+                pa_log_debug("%d: %04hx != %04hx (%.24f)\n", i, samples[i], samples_ref[i], floats[i]);
+                ck_abort();
+            }
+        }
+    }
+
+    if (perf) {
+        pa_log_debug("Testing sconv performance with %d sample alignment", align);
+
+        PA_RUNTIME_TEST_RUN_START("func", TIMES, TIMES2) {
+            func(nsamples, floats, samples);
+        } PA_RUNTIME_TEST_RUN_STOP
+
+        PA_RUNTIME_TEST_RUN_START("orig", TIMES, TIMES2) {
+            orig_func(nsamples, floats, samples_ref);
+        } PA_RUNTIME_TEST_RUN_STOP
+    }
+}
+
+/* This test is currently only run under NEON */
+#if defined (__arm__) && defined (__linux__) && defined (HAVE_NEON)
+static void run_conv_test_s16_to_float(
+        pa_convert_func_t func,
+        pa_convert_func_t orig_func,
+        int align,
+        bool correct,
+        bool perf) {
+
+    PA_DECLARE_ALIGNED(8, float, f[SAMPLES]) = { 0.0f };
+    PA_DECLARE_ALIGNED(8, float, f_ref[SAMPLES]) = { 0.0f };
+    PA_DECLARE_ALIGNED(8, int16_t, s[SAMPLES]);
+    float *floats, *floats_ref;
+    int16_t *samples;
+    int i, nsamples;
+
+    /* Force sample alignment as requested */
+    floats = f + (8 - align);
+    floats_ref = f_ref + (8 - align);
+    samples = s + (8 - align);
+    nsamples = SAMPLES - (8 - align);
+
+    pa_random(samples, nsamples * sizeof(int16_t));
+
+    if (correct) {
+        orig_func(nsamples, samples, floats_ref);
+        func(nsamples, samples, floats);
+
+        for (i = 0; i < nsamples; i++) {
+            if (fabsf(floats[i] - floats_ref[i]) > 0.0001f) {
+                pa_log_debug("Correctness test failed: align=%d", align);
+                pa_log_debug("%d: %.24f != %.24f (%d)\n", i, floats[i], floats_ref[i], samples[i]);
+                ck_abort();
+            }
+        }
+    }
+
+    if (perf) {
+        pa_log_debug("Testing sconv performance with %d sample alignment", align);
+
+        PA_RUNTIME_TEST_RUN_START("func", TIMES, TIMES2) {
+            func(nsamples, samples, floats);
+        } PA_RUNTIME_TEST_RUN_STOP
+
+        PA_RUNTIME_TEST_RUN_START("orig", TIMES, TIMES2) {
+            orig_func(nsamples, samples, floats_ref);
+        } PA_RUNTIME_TEST_RUN_STOP
+    }
+}
+#endif /* defined (__arm__) && defined (__linux__) && defined (HAVE_NEON) */
+
+#if defined (__i386__) || defined (__amd64__)
+START_TEST (sconv_sse2_test) {
+    pa_cpu_x86_flag_t flags = 0;
+    pa_convert_func_t orig_func, sse2_func;
+
+    pa_cpu_get_x86_flags(&flags);
+
+    if (!(flags & PA_CPU_X86_SSE2)) {
+        pa_log_info("SSE2 not supported. Skipping");
+        return;
+    }
+
+    orig_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE);
+    pa_convert_func_init_sse(PA_CPU_X86_SSE2);
+    sse2_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE);
+
+    pa_log_debug("Checking SSE2 sconv (float -> s16)");
+    run_conv_test_float_to_s16(sse2_func, orig_func, 0, true, false);
+    run_conv_test_float_to_s16(sse2_func, orig_func, 1, true, false);
+    run_conv_test_float_to_s16(sse2_func, orig_func, 2, true, false);
+    run_conv_test_float_to_s16(sse2_func, orig_func, 3, true, false);
+    run_conv_test_float_to_s16(sse2_func, orig_func, 4, true, false);
+    run_conv_test_float_to_s16(sse2_func, orig_func, 5, true, false);
+    run_conv_test_float_to_s16(sse2_func, orig_func, 6, true, false);
+    run_conv_test_float_to_s16(sse2_func, orig_func, 7, true, true);
+}
+END_TEST
+
+START_TEST (sconv_sse_test) {
+    pa_cpu_x86_flag_t flags = 0;
+    pa_convert_func_t orig_func, sse_func;
+
+    pa_cpu_get_x86_flags(&flags);
+
+    if (!(flags & PA_CPU_X86_SSE)) {
+        pa_log_info("SSE not supported. Skipping");
+        return;
+    }
+
+    orig_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE);
+    pa_convert_func_init_sse(PA_CPU_X86_SSE);
+    sse_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE);
+
+    pa_log_debug("Checking SSE sconv (float -> s16)");
+    run_conv_test_float_to_s16(sse_func, orig_func, 0, true, false);
+    run_conv_test_float_to_s16(sse_func, orig_func, 1, true, false);
+    run_conv_test_float_to_s16(sse_func, orig_func, 2, true, false);
+    run_conv_test_float_to_s16(sse_func, orig_func, 3, true, false);
+    run_conv_test_float_to_s16(sse_func, orig_func, 4, true, false);
+    run_conv_test_float_to_s16(sse_func, orig_func, 5, true, false);
+    run_conv_test_float_to_s16(sse_func, orig_func, 6, true, false);
+    run_conv_test_float_to_s16(sse_func, orig_func, 7, true, true);
+}
+END_TEST
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+#if defined (__arm__) && defined (__linux__) && defined (HAVE_NEON)
+START_TEST (sconv_neon_test) {
+    pa_cpu_arm_flag_t flags = 0;
+    pa_convert_func_t orig_from_func, neon_from_func;
+    pa_convert_func_t orig_to_func, neon_to_func;
+
+    pa_cpu_get_arm_flags(&flags);
+
+    if (!(flags & PA_CPU_ARM_NEON)) {
+        pa_log_info("NEON not supported. Skipping");
+        return;
+    }
+
+    orig_from_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE);
+    orig_to_func = pa_get_convert_to_float32ne_function(PA_SAMPLE_S16LE);
+    pa_convert_func_init_neon(flags);
+    neon_from_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE);
+    neon_to_func = pa_get_convert_to_float32ne_function(PA_SAMPLE_S16LE);
+
+    pa_log_debug("Checking NEON sconv (float -> s16)");
+    run_conv_test_float_to_s16(neon_from_func, orig_from_func, 0, true, false);
+    run_conv_test_float_to_s16(neon_from_func, orig_from_func, 1, true, false);
+    run_conv_test_float_to_s16(neon_from_func, orig_from_func, 2, true, false);
+    run_conv_test_float_to_s16(neon_from_func, orig_from_func, 3, true, false);
+    run_conv_test_float_to_s16(neon_from_func, orig_from_func, 4, true, false);
+    run_conv_test_float_to_s16(neon_from_func, orig_from_func, 5, true, false);
+    run_conv_test_float_to_s16(neon_from_func, orig_from_func, 6, true, false);
+    run_conv_test_float_to_s16(neon_from_func, orig_from_func, 7, true, true);
+
+    pa_log_debug("Checking NEON sconv (s16 -> float)");
+    run_conv_test_s16_to_float(neon_to_func, orig_to_func, 0, true, false);
+    run_conv_test_s16_to_float(neon_to_func, orig_to_func, 1, true, false);
+    run_conv_test_s16_to_float(neon_to_func, orig_to_func, 2, true, false);
+    run_conv_test_s16_to_float(neon_to_func, orig_to_func, 3, true, false);
+    run_conv_test_s16_to_float(neon_to_func, orig_to_func, 4, true, false);
+    run_conv_test_s16_to_float(neon_to_func, orig_to_func, 5, true, false);
+    run_conv_test_s16_to_float(neon_to_func, orig_to_func, 6, true, false);
+    run_conv_test_s16_to_float(neon_to_func, orig_to_func, 7, true, true);
+}
+END_TEST
+#endif /* defined (__arm__) && defined (__linux__) && defined (HAVE_NEON) */
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("CPU");
+
+    tc = tcase_create("sconv");
+#if defined (__i386__) || defined (__amd64__)
+    tcase_add_test(tc, sconv_sse2_test);
+    tcase_add_test(tc, sconv_sse_test);
+#endif
+#if defined (__arm__) && defined (__linux__) && defined (HAVE_NEON)
+    tcase_add_test(tc, sconv_neon_test);
+#endif
+    tcase_set_timeout(tc, 120);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/cpu-volume-test.c b/src/tests/cpu-volume-test.c
new file mode 100644 (file)
index 0000000..535488e
--- /dev/null
@@ -0,0 +1,247 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <pulsecore/cpu-arm.h>
+#include <pulsecore/cpu-x86.h>
+#include <pulsecore/cpu-orc.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/sample-util.h>
+
+#include "runtime-test-util.h"
+
+/* Common defines for svolume tests */
+#define SAMPLES 1028
+#define TIMES 1000
+#define TIMES2 100
+#define PADDING 16
+
+static void run_volume_test(
+        pa_do_volume_func_t func,
+        pa_do_volume_func_t orig_func,
+        int align,
+        int channels,
+        bool correct,
+        bool perf) {
+
+    PA_DECLARE_ALIGNED(8, int16_t, s[SAMPLES]) = { 0 };
+    PA_DECLARE_ALIGNED(8, int16_t, s_ref[SAMPLES]) = { 0 };
+    PA_DECLARE_ALIGNED(8, int16_t, s_orig[SAMPLES]) = { 0 };
+    int32_t volumes[channels + PADDING];
+    int16_t *samples, *samples_ref, *samples_orig;
+    int i, padding, nsamples, size;
+
+    /* Force sample alignment as requested */
+    samples = s + (8 - align);
+    samples_ref = s_ref + (8 - align);
+    samples_orig = s_orig + (8 - align);
+    nsamples = SAMPLES - (8 - align);
+    if (nsamples % channels)
+        nsamples -= nsamples % channels;
+    size = nsamples * sizeof(int16_t);
+
+    pa_random(samples, size);
+    memcpy(samples_ref, samples, size);
+    memcpy(samples_orig, samples, size);
+
+    for (i = 0; i < channels; i++)
+        volumes[i] = PA_CLAMP_VOLUME((pa_volume_t)(rand() >> 15));
+    for (padding = 0; padding < PADDING; padding++, i++)
+        volumes[i] = volumes[padding];
+
+    if (correct) {
+        orig_func(samples_ref, volumes, channels, size);
+        func(samples, volumes, channels, size);
+
+        for (i = 0; i < nsamples; i++) {
+            if (samples[i] != samples_ref[i]) {
+                pa_log_debug("Correctness test failed: align=%d, channels=%d", align, channels);
+                pa_log_debug("%d: %04hx != %04hx (%04hx * %08x)", i, samples[i], samples_ref[i],
+                        samples_orig[i], volumes[i % channels]);
+                ck_abort();
+            }
+        }
+    }
+
+    if (perf) {
+        pa_log_debug("Testing svolume %dch performance with %d sample alignment", channels, align);
+
+        PA_RUNTIME_TEST_RUN_START("func", TIMES, TIMES2) {
+            memcpy(samples, samples_orig, size);
+            func(samples, volumes, channels, size);
+        } PA_RUNTIME_TEST_RUN_STOP
+
+        PA_RUNTIME_TEST_RUN_START("orig", TIMES, TIMES2) {
+            memcpy(samples_ref, samples_orig, size);
+            orig_func(samples_ref, volumes, channels, size);
+        } PA_RUNTIME_TEST_RUN_STOP
+
+        fail_unless(memcmp(samples_ref, samples, size) == 0);
+    }
+}
+
+#if defined (__i386__) || defined (__amd64__)
+START_TEST (svolume_mmx_test) {
+    pa_do_volume_func_t orig_func, mmx_func;
+    pa_cpu_x86_flag_t flags = 0;
+    int i, j;
+
+    pa_cpu_get_x86_flags(&flags);
+
+    if (!((flags & PA_CPU_X86_MMX) && (flags & PA_CPU_X86_CMOV))) {
+        pa_log_info("MMX/CMOV not supported. Skipping");
+        return;
+    }
+
+    orig_func = pa_get_volume_func(PA_SAMPLE_S16NE);
+    pa_volume_func_init_mmx(flags);
+    mmx_func = pa_get_volume_func(PA_SAMPLE_S16NE);
+
+    pa_log_debug("Checking MMX svolume");
+    for (i = 1; i <= 3; i++) {
+        for (j = 0; j < 7; j++)
+            run_volume_test(mmx_func, orig_func, j, i, true, false);
+    }
+    run_volume_test(mmx_func, orig_func, 7, 1, true, true);
+    run_volume_test(mmx_func, orig_func, 7, 2, true, true);
+    run_volume_test(mmx_func, orig_func, 7, 3, true, true);
+}
+END_TEST
+
+START_TEST (svolume_sse_test) {
+    pa_do_volume_func_t orig_func, sse_func;
+    pa_cpu_x86_flag_t flags = 0;
+    int i, j;
+
+    pa_cpu_get_x86_flags(&flags);
+
+    if (!(flags & PA_CPU_X86_SSE2)) {
+        pa_log_info("SSE2 not supported. Skipping");
+        return;
+    }
+
+    orig_func = pa_get_volume_func(PA_SAMPLE_S16NE);
+    pa_volume_func_init_sse(flags);
+    sse_func = pa_get_volume_func(PA_SAMPLE_S16NE);
+
+    pa_log_debug("Checking SSE2 svolume");
+    for (i = 1; i <= 3; i++) {
+        for (j = 0; j < 7; j++)
+            run_volume_test(sse_func, orig_func, j, i, true, false);
+    }
+    run_volume_test(sse_func, orig_func, 7, 1, true, true);
+    run_volume_test(sse_func, orig_func, 7, 2, true, true);
+    run_volume_test(sse_func, orig_func, 7, 3, true, true);
+}
+END_TEST
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+#if defined (__arm__) && defined (__linux__)
+START_TEST (svolume_arm_test) {
+    pa_do_volume_func_t orig_func, arm_func;
+    pa_cpu_arm_flag_t flags = 0;
+    int i, j;
+
+    pa_cpu_get_arm_flags(&flags);
+
+    if (!(flags & PA_CPU_ARM_V6)) {
+        pa_log_info("ARMv6 instructions not supported. Skipping");
+        return;
+    }
+
+    orig_func = pa_get_volume_func(PA_SAMPLE_S16NE);
+    pa_volume_func_init_arm(flags);
+    arm_func = pa_get_volume_func(PA_SAMPLE_S16NE);
+
+    pa_log_debug("Checking ARM svolume");
+    for (i = 1; i <= 3; i++) {
+        for (j = 0; j < 7; j++)
+            run_volume_test(arm_func, orig_func, j, i, true, false);
+    }
+    run_volume_test(arm_func, orig_func, 7, 1, true, true);
+    run_volume_test(arm_func, orig_func, 7, 2, true, true);
+    run_volume_test(arm_func, orig_func, 7, 3, true, true);
+}
+END_TEST
+#endif /* defined (__arm__) && defined (__linux__) */
+
+START_TEST (svolume_orc_test) {
+    pa_do_volume_func_t orig_func, orc_func;
+    pa_cpu_info cpu_info;
+    int i, j;
+
+#if defined (__i386__) || defined (__amd64__)
+    pa_zero(cpu_info);
+    cpu_info.cpu_type = PA_CPU_X86;
+    pa_cpu_get_x86_flags(&cpu_info.flags.x86);
+#endif
+
+    orig_func = pa_get_volume_func(PA_SAMPLE_S16NE);
+
+    if (!pa_cpu_init_orc(cpu_info)) {
+        pa_log_info("Orc not supported. Skipping");
+        return;
+    }
+
+    orc_func = pa_get_volume_func(PA_SAMPLE_S16NE);
+
+    pa_log_debug("Checking Orc svolume");
+    for (i = 1; i <= 2; i++) {
+        for (j = 0; j < 7; j++)
+            run_volume_test(orc_func, orig_func, j, i, true, false);
+    }
+    run_volume_test(orc_func, orig_func, 7, 1, true, true);
+    run_volume_test(orc_func, orig_func, 7, 2, true, true);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("CPU");
+
+    tc = tcase_create("svolume");
+#if defined (__i386__) || defined (__amd64__)
+    tcase_add_test(tc, svolume_mmx_test);
+    tcase_add_test(tc, svolume_sse_test);
+#endif
+#if defined (__arm__) && defined (__linux__)
+    tcase_add_test(tc, svolume_arm_test);
+#endif
+    tcase_add_test(tc, svolume_orc_test);
+    tcase_set_timeout(tc, 120);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/cpulimit-test.c b/src/tests/cpulimit-test.c
new file mode 100644 (file)
index 0000000..e01a5b8
--- /dev/null
@@ -0,0 +1,110 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include <check.h>
+
+#include <pulse/mainloop.h>
+
+#ifdef TEST2
+#include <pulse/mainloop-signal.h>
+#endif
+
+#include <daemon/cpulimit.h>
+
+/* A simple example for testing the cpulimit subsystem */
+
+static time_t start;
+
+#ifdef TEST2
+
+static void func(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
+    time_t now;
+    time(&now);
+
+    if ((now - start) >= 30) {
+        m->quit(m, 1);
+        fprintf(stderr, "Test failed\n");
+        ck_abort();
+    } else
+        raise(SIGUSR1);
+}
+
+#endif
+
+START_TEST (cpulimit_test) {
+    pa_mainloop *m;
+
+    m = pa_mainloop_new();
+    fail_unless(m != NULL);
+
+    pa_cpu_limit_init(pa_mainloop_get_api(m));
+
+    time(&start);
+
+#ifdef TEST2
+    pa_signal_init(pa_mainloop_get_api(m));
+    pa_signal_new(SIGUSR1, func, NULL);
+    raise(SIGUSR1);
+    pa_mainloop_run(m, NULL);
+    pa_signal_done();
+#else
+    for (;;) {
+        time_t now;
+        time(&now);
+
+        if ((now - start) >= 30) {
+            fprintf(stderr, "Test failed\n");
+            ck_abort();
+            break;
+        }
+    }
+#endif
+
+    pa_cpu_limit_done();
+
+    pa_mainloop_free(m);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("CPU Limit");
+    tc = tcase_create("cpulimit");
+    tcase_add_test(tc, cpulimit_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/extended-test.c b/src/tests/extended-test.c
new file mode 100644 (file)
index 0000000..0d08fac
--- /dev/null
@@ -0,0 +1,222 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <check.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/mainloop.h>
+
+#define NSTREAMS 4
+#define SINE_HZ 440
+#define SAMPLE_HZ 8000
+
+static pa_context *context = NULL;
+static pa_stream *streams[NSTREAMS];
+static pa_mainloop_api *mainloop_api = NULL;
+static const char *bname;
+
+static float data[SAMPLE_HZ]; /* one second space */
+
+static int n_streams_ready = 0;
+
+static const pa_buffer_attr buffer_attr = {
+    .maxlength = SAMPLE_HZ*sizeof(float)*NSTREAMS, /* exactly space for the entire play time */
+    .tlength = (uint32_t) -1,
+    .prebuf = 0, /* Setting prebuf to 0 guarantees us the streams will run synchronously, no matter what */
+    .minreq = (uint32_t) -1,
+    .fragsize = 0
+};
+
+static void nop_free_cb(void *p) {}
+
+static void underflow_cb(struct pa_stream *s, void *userdata) {
+    int i = (int) (long) userdata;
+
+    fprintf(stderr, "Stream %i finished\n", i);
+
+    if (++n_streams_ready >= 2*NSTREAMS) {
+        fprintf(stderr, "We're done\n");
+        mainloop_api->quit(mainloop_api, 0);
+    }
+}
+
+/* This routine is called whenever the stream state changes */
+static void stream_state_callback(pa_stream *s, void *userdata) {
+    fail_unless(s != NULL);
+
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+        case PA_STREAM_TERMINATED:
+            break;
+
+        case PA_STREAM_READY: {
+
+            int r, i = (int) (long) userdata;
+
+            fprintf(stderr, "Writing data to stream %i.\n", i);
+
+            r = pa_stream_write(s, data, sizeof(data), nop_free_cb, (int64_t) sizeof(data) * (int64_t) i, PA_SEEK_ABSOLUTE);
+            fail_unless(r == 0);
+
+            /* Be notified when this stream is drained */
+            pa_stream_set_underflow_callback(s, underflow_cb, userdata);
+
+            /* All streams have been set up, let's go! */
+            if (++n_streams_ready >= NSTREAMS) {
+                fprintf(stderr, "Uncorking\n");
+                pa_operation_unref(pa_stream_cork(s, 0, NULL, NULL));
+            }
+
+            break;
+        }
+
+        default:
+        case PA_STREAM_FAILED:
+            fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+            ck_abort();
+    }
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+    fail_unless(c != NULL);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+
+            int i;
+            fprintf(stderr, "Connection established.\n");
+
+            for (i = 0; i < NSTREAMS; i++) {
+                char name[64];
+                pa_format_info *formats[1];
+
+                formats[0] = pa_format_info_new();
+                formats[0]->encoding = PA_ENCODING_PCM;
+                pa_format_info_set_sample_format(formats[0], PA_SAMPLE_FLOAT32);
+                pa_format_info_set_rate(formats[0], SAMPLE_HZ);
+                pa_format_info_set_channels(formats[0], 1);
+
+                fprintf(stderr, "Creating stream %i\n", i);
+
+                snprintf(name, sizeof(name), "stream #%i", i);
+
+                streams[i] = pa_stream_new_extended(c, name, formats, 1, NULL);
+                fail_unless(streams[i] != NULL);
+                pa_stream_set_state_callback(streams[i], stream_state_callback, (void*) (long) i);
+                pa_stream_connect_playback(streams[i], NULL, &buffer_attr, PA_STREAM_START_CORKED, NULL, i == 0 ? NULL : streams[0]);
+
+                pa_format_info_free(formats[0]);
+            }
+
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            mainloop_api->quit(mainloop_api, 0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
+            ck_abort();
+    }
+}
+
+START_TEST (extended_test) {
+    pa_mainloop* m = NULL;
+    int i, ret = 1;
+
+    for (i = 0; i < SAMPLE_HZ; i++)
+        data[i] = (float) sin(((double) i/SAMPLE_HZ)*2*M_PI*SINE_HZ)/2;
+
+    for (i = 0; i < NSTREAMS; i++)
+        streams[i] = NULL;
+
+    /* Set up a new main loop */
+    m = pa_mainloop_new();
+    fail_unless(m != NULL);
+
+    mainloop_api = pa_mainloop_get_api(m);
+
+    context = pa_context_new(mainloop_api, bname);
+    fail_unless(context != NULL);
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+
+    /* Connect the context */
+    if (pa_context_connect(context, NULL, 0, NULL) < 0) {
+        fprintf(stderr, "pa_context_connect() failed.\n");
+        goto quit;
+    }
+
+    if (pa_mainloop_run(m, &ret) < 0)
+        fprintf(stderr, "pa_mainloop_run() failed.\n");
+
+quit:
+    pa_context_unref(context);
+
+    for (i = 0; i < NSTREAMS; i++)
+        if (streams[i])
+            pa_stream_unref(streams[i]);
+
+    pa_mainloop_free(m);
+
+    fail_unless(ret == 0);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    bname = argv[0];
+
+    s = suite_create("Extended");
+    tc = tcase_create("extended");
+    tcase_add_test(tc, extended_test);
+    /* 4s of audio, 0.5s grace time */
+    tcase_set_timeout(tc, 4.5);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/flist-test.c b/src/tests/flist-test.c
new file mode 100644 (file)
index 0000000..3abc123
--- /dev/null
@@ -0,0 +1,101 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+#define THREADS_MAX 20
+
+static pa_flist *flist;
+static int quit = 0;
+
+static void spin(void) {
+    int k;
+
+    /* Spin a little */
+    k = rand() % 10000;
+    for (; k > 0; k--)
+        pa_thread_yield();
+}
+
+static void thread_func(void *data) {
+    char *s = data;
+    int n = 0;
+    int b = 1;
+
+    while (!quit) {
+        char *text;
+
+        /* Allocate some memory, if possible take it from the flist */
+        if (b && (text = pa_flist_pop(flist)))
+            pa_log("%s: popped '%s'", s, text);
+        else {
+            text = pa_sprintf_malloc("Block %i, allocated by %s", n++, s);
+            pa_log("%s: allocated '%s'", s, text);
+        }
+
+        b = !b;
+
+        spin();
+
+        /* Give it back to the flist if possible */
+        if (pa_flist_push(flist, text) < 0) {
+            pa_log("%s: failed to push back '%s'", s, text);
+            pa_xfree(text);
+        } else
+            pa_log("%s: pushed", s);
+
+        spin();
+    }
+
+    if (pa_flist_push(flist, s) < 0)
+        pa_xfree(s);
+}
+
+int main(int argc, char* argv[]) {
+    pa_thread *threads[THREADS_MAX];
+    int i;
+
+    flist = pa_flist_new(0);
+
+    for (i = 0; i < THREADS_MAX; i++) {
+        threads[i] = pa_thread_new("test", thread_func, pa_sprintf_malloc("Thread #%i", i+1));
+        pa_assert(threads[i]);
+    }
+
+    pa_msleep(60000);
+    quit = 1;
+
+    for (i = 0; i < THREADS_MAX; i++)
+        pa_thread_free(threads[i]);
+
+    pa_flist_free(flist, pa_xfree);
+
+    return 0;
+}
diff --git a/src/tests/format-test.c b/src/tests/format-test.c
new file mode 100644 (file)
index 0000000..20869da
--- /dev/null
@@ -0,0 +1,169 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <check.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulse/format.h>
+#include <pulse/xmalloc.h>
+
+#define INIT(f) f = pa_format_info_new()
+#define DEINIT(f) pa_format_info_free(f);
+#define REINIT(f) { DEINIT(f); INIT(f); }
+
+START_TEST (format_test) {
+    pa_format_info *f1 = NULL, *f2 = NULL;
+    int rates1[] = { 32000, 44100, 48000 }, i, temp_int1 = -1, PA_UNUSED temp_int2 = -1, *temp_int_array;
+    const char *strings[] = { "thing1", "thing2", "thing3" };
+    char *temp_str, **temp_str_array;
+
+    /* 1. Simple fixed format int check */
+    INIT(f1); INIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f1, PA_PROP_FORMAT_RATE, 32000);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
+    fail_unless(!pa_format_info_is_compatible(f1, f2));
+
+    /* 2. Check int array membership - positive */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int_array(f1, PA_PROP_FORMAT_RATE, rates1, PA_ELEMENTSOF(rates1));
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
+    fail_unless(pa_format_info_is_compatible(f1, f2));
+    fail_unless(pa_format_info_is_compatible(f2, f1));
+
+    /* 3. Check int array membership - negative */
+    REINIT(f2);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 96000);
+    fail_unless(!pa_format_info_is_compatible(f1, f2));
+    fail_unless(!pa_format_info_is_compatible(f2, f1));
+
+    /* 4. Check int range - positive */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int_range(f1, PA_PROP_FORMAT_RATE, 32000, 48000);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
+    fail_unless(pa_format_info_is_compatible(f1, f2));
+    fail_unless(pa_format_info_is_compatible(f2, f1));
+
+    /* 5. Check int range - negative */
+    REINIT(f2);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 96000);
+    fail_unless(!pa_format_info_is_compatible(f1, f2));
+    fail_unless(!pa_format_info_is_compatible(f2, f1));
+
+    /* 6. Simple fixed format string check */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f1, "format.test_string", "thing1");
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f2, "format.test_string", "notthing1");
+    fail_unless(!pa_format_info_is_compatible(f1, f2));
+
+    /* 7. Check string array membership - positive */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string_array(f1, "format.test_string", strings, PA_ELEMENTSOF(strings));
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f2, "format.test_string", "thing3");
+    fail_unless(pa_format_info_is_compatible(f1, f2));
+    fail_unless(pa_format_info_is_compatible(f2, f1));
+
+    /* 8. Check string array membership - negative */
+    REINIT(f2);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f2, "format.test_string", "thing5");
+    fail_unless(!pa_format_info_is_compatible(f1, f2));
+    fail_unless(!pa_format_info_is_compatible(f2, f1));
+
+    /* 9. Verify setting/getting an int */
+    REINIT(f1);
+    pa_format_info_set_prop_int(f1, "format.test_string", 42);
+    fail_unless(pa_format_info_get_prop_type(f1, "format.test_string") == PA_PROP_TYPE_INT);
+    fail_unless(pa_format_info_get_prop_int(f1, "format.test_string", &temp_int1) == 0);
+    fail_unless(temp_int1 == 42);
+
+    /* 10. Verify setting/getting an int range */
+    REINIT(f1);
+    pa_format_info_set_prop_int_range(f1, "format.test_string", 0, 100);
+    pa_assert(pa_format_info_get_prop_type(f1, "format.test_string") == PA_PROP_TYPE_INT_RANGE);
+    pa_assert(pa_format_info_get_prop_int_range(f1, "format.test_string", &temp_int1, &temp_int2) == 0);
+    pa_assert(temp_int1 == 0 && temp_int2 == 100);
+
+    /* 11. Verify setting/getting an int array */
+    REINIT(f1);
+    pa_format_info_set_prop_int_array(f1, "format.test_string", rates1, PA_ELEMENTSOF(rates1));
+    fail_unless(pa_format_info_get_prop_type(f1, "format.test_string") == PA_PROP_TYPE_INT_ARRAY);
+    fail_unless(pa_format_info_get_prop_int_array(f1, "format.test_string", &temp_int_array, &temp_int1) == 0);
+    fail_unless(temp_int1 == PA_ELEMENTSOF(rates1));
+    for (i = 0; i < temp_int1; i++)
+        fail_unless(temp_int_array[i] == rates1[i]);
+    pa_xfree(temp_int_array);
+
+    /* 12. Verify setting/getting a string */
+    REINIT(f1);
+    pa_format_info_set_prop_string(f1, "format.test_string", "foo");
+    fail_unless(pa_format_info_get_prop_type(f1, "format.test_string") == PA_PROP_TYPE_STRING);
+    fail_unless(pa_format_info_get_prop_string(f1, "format.test_string", &temp_str) == 0);
+    fail_unless(pa_streq(temp_str, "foo"));
+    pa_xfree(temp_str);
+
+    /* 13. Verify setting/getting an int array */
+    REINIT(f1);
+    pa_format_info_set_prop_string_array(f1, "format.test_string", strings, PA_ELEMENTSOF(strings));
+    fail_unless(pa_format_info_get_prop_type(f1, "format.test_string") == PA_PROP_TYPE_STRING_ARRAY);
+    fail_unless(pa_format_info_get_prop_string_array(f1, "format.test_string", &temp_str_array, &temp_int1) == 0);
+    fail_unless(temp_int1 == PA_ELEMENTSOF(strings));
+    for (i = 0; i < temp_int1; i++)
+        fail_unless(pa_streq(temp_str_array[i], strings[i]));
+    pa_format_info_free_string_array(temp_str_array, temp_int1);
+
+    DEINIT(f1);
+    DEINIT(f2);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Format");
+    tc = tcase_create("format");
+    tcase_add_test(tc, format_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/get-binary-name-test.c b/src/tests/get-binary-name-test.c
new file mode 100644 (file)
index 0000000..cd53bde
--- /dev/null
@@ -0,0 +1,74 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <check.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+
+START_TEST (getbinaryname_test) {
+    char *exename;
+    size_t allocated = 128;
+
+    for (;;) {
+        exename = pa_xmalloc(allocated);
+
+        if (!pa_get_binary_name(exename, allocated)) {
+            pa_log_error("failed to read binary name");
+            pa_xfree(exename);
+            ck_abort();
+        }
+
+        if (strlen(exename) < allocated - 1) {
+            pa_log("%s", exename);
+            pa_xfree(exename);
+            return;
+        }
+
+        pa_xfree(exename);
+        allocated *= 2;
+    }
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Binary Name");
+    tc = tcase_create("getbinaryname");
+    tcase_add_test(tc, getbinaryname_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/gtk-test.c b/src/tests/gtk-test.c
new file mode 100644 (file)
index 0000000..8edc5f2
--- /dev/null
@@ -0,0 +1,83 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+#include <pulse/context.h>
+#include <pulse/glib-mainloop.h>
+
+pa_context *ctxt;
+pa_glib_mainloop *m;
+
+static void context_state_callback(pa_context *c, void *userdata);
+
+static void connect(void) {
+    int r;
+
+    ctxt = pa_context_new(pa_glib_mainloop_get_api(m), NULL);
+    g_assert(ctxt);
+
+    r = pa_context_connect(ctxt, NULL, PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL, NULL);
+    g_assert(r == 0);
+
+    pa_context_set_state_callback(ctxt, context_state_callback, NULL);
+}
+
+static void context_state_callback(pa_context *c, void *userdata) {
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_FAILED:
+            pa_context_unref(ctxt);
+            ctxt = NULL;
+            connect();
+            break;
+        default:
+            break;
+    }
+}
+
+int main(int argc, char *argv[]) {
+
+    GtkWidget *window;
+
+    gtk_init(&argc, &argv);
+
+    g_set_application_name("This is a test");
+    gtk_window_set_default_icon_name("foobar");
+    g_setenv("PULSE_PROP_media.role", "phone", TRUE);
+
+    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW (window), g_get_application_name());
+    gtk_widget_show_all(window);
+
+    m = pa_glib_mainloop_new(NULL);
+    g_assert(m);
+
+    connect();
+    gtk_main();
+
+    pa_context_unref(ctxt);
+    pa_glib_mainloop_free(m);
+
+    return 0;
+}
diff --git a/src/tests/hook-list-test.c b/src/tests/hook-list-test.c
new file mode 100644 (file)
index 0000000..aec2a5d
--- /dev/null
@@ -0,0 +1,61 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <pulsecore/hook-list.h>
+#include <pulsecore/log.h>
+
+static pa_hook_result_t func1(const char *hook_data, const char *call_data, const char *slot_data) {
+    pa_log("(func1) hook=%s call=%s slot=%s", hook_data, call_data, slot_data);
+    /* succeed when it runs to here */
+    fail_unless(1);
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t func2(const char *hook_data, const char *call_data, const char *slot_data) {
+    pa_log("(func2) hook=%s call=%s slot=%s", hook_data, call_data, slot_data);
+    /* succeed when it runs to here */
+    fail_unless(1);
+    return PA_HOOK_OK;
+}
+
+START_TEST (hooklist_test) {
+    pa_hook hook;
+    pa_hook_slot *slot;
+
+    pa_hook_init(&hook, (void*) "hook");
+
+    pa_hook_connect(&hook, PA_HOOK_LATE, (pa_hook_cb_t) func1, (void*) "slot1");
+    slot = pa_hook_connect(&hook, PA_HOOK_NORMAL, (pa_hook_cb_t) func2, (void*) "slot2");
+    pa_hook_connect(&hook, PA_HOOK_NORMAL, (pa_hook_cb_t) func1, (void*) "slot3");
+
+    pa_hook_fire(&hook, (void*) "call1");
+
+    pa_hook_slot_free(slot);
+
+    pa_hook_fire(&hook, (void*) "call2");
+
+    pa_hook_done(&hook);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Hook List");
+    tc = tcase_create("hooklist");
+    tcase_add_test(tc, hooklist_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c
new file mode 100644 (file)
index 0000000..bb69e52
--- /dev/null
@@ -0,0 +1,294 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <check.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/mainloop.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+
+#define INTERPOLATE
+//#define CORK
+
+static pa_context *context = NULL;
+static pa_stream *stream = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
+static bool playback = true;
+static pa_usec_t latency = 0;
+static const char *bname = NULL;
+
+static void stream_write_cb(pa_stream *p, size_t nbytes, void *userdata) {
+    /* Just some silence */
+
+    for (;;) {
+        void *data;
+
+        fail_unless((nbytes = pa_stream_writable_size(p)) != (size_t) -1);
+
+        if (nbytes <= 0)
+            break;
+
+        fail_unless(pa_stream_begin_write(p, &data, &nbytes) == 0);
+        pa_memzero(data, nbytes);
+        fail_unless(pa_stream_write(p, data, nbytes, NULL, 0, PA_SEEK_RELATIVE) == 0);
+    }
+}
+
+static void stream_read_cb(pa_stream *p, size_t nbytes, void *userdata) {
+    /* We don't care about the data, just drop it */
+
+    for (;;) {
+        const void *data;
+
+        pa_assert_se((nbytes = pa_stream_readable_size(p)) != (size_t) -1);
+
+        if (nbytes <= 0)
+            break;
+
+        fail_unless(pa_stream_peek(p, &data, &nbytes) == 0);
+        fail_unless(pa_stream_drop(p) == 0);
+    }
+}
+
+static void stream_latency_cb(pa_stream *p, void *userdata) {
+#ifndef INTERPOLATE
+    pa_operation *o;
+
+    o = pa_stream_update_timing_info(p, NULL, NULL);
+    pa_operation_unref(o);
+#endif
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+    fail_unless(c != NULL);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+            pa_stream_flags_t flags = PA_STREAM_AUTO_TIMING_UPDATE;
+            pa_buffer_attr attr;
+            static const pa_sample_spec ss = {
+                .format = PA_SAMPLE_S16LE,
+                .rate = 44100,
+                .channels = 2
+            };
+
+            pa_zero(attr);
+            attr.maxlength = (uint32_t) -1;
+            attr.tlength = latency > 0 ? (uint32_t) pa_usec_to_bytes(latency, &ss) : (uint32_t) -1;
+            attr.prebuf = (uint32_t) -1;
+            attr.minreq = (uint32_t) -1;
+            attr.fragsize = (uint32_t) -1;
+
+#ifdef INTERPOLATE
+            flags |= PA_STREAM_INTERPOLATE_TIMING;
+#endif
+
+            if (latency > 0)
+                flags |= PA_STREAM_ADJUST_LATENCY;
+
+            pa_log("Connection established");
+
+            stream = pa_stream_new(c, "interpol-test", &ss, NULL);
+            fail_unless(stream != NULL);
+
+            if (playback) {
+                pa_assert_se(pa_stream_connect_playback(stream, NULL, &attr, flags, NULL, NULL) == 0);
+                pa_stream_set_write_callback(stream, stream_write_cb, NULL);
+            } else {
+                pa_assert_se(pa_stream_connect_record(stream, NULL, &attr, flags) == 0);
+                pa_stream_set_read_callback(stream, stream_read_cb, NULL);
+            }
+
+            pa_stream_set_latency_update_callback(stream, stream_latency_cb, NULL);
+
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            pa_log_error("Context error: %s", pa_strerror(pa_context_errno(c)));
+            ck_abort();
+    }
+}
+
+START_TEST (interpol_test) {
+    pa_threaded_mainloop* m = NULL;
+    int k;
+    struct timeval start, last_info = { 0, 0 };
+    pa_usec_t old_t = 0, old_rtc = 0;
+#ifdef CORK
+    bool corked = false;
+#endif
+
+    /* Set up a new main loop */
+    m = pa_threaded_mainloop_new();
+    fail_unless(m != NULL);
+    mainloop_api = pa_threaded_mainloop_get_api(m);
+    fail_unless(mainloop_api != NULL);
+    context = pa_context_new(mainloop_api, bname);
+    fail_unless(context != NULL);
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+
+    fail_unless(pa_context_connect(context, NULL, 0, NULL) >= 0);
+
+    pa_gettimeofday(&start);
+
+    fail_unless(pa_threaded_mainloop_start(m) >= 0);
+
+/* #ifdef CORK */
+    for (k = 0; k < 20000; k++)
+/* #else */
+/*     for (k = 0; k < 2000; k++) */
+/* #endif */
+    {
+        bool success = false, changed = false;
+        pa_usec_t t, rtc, d;
+        struct timeval now, tv;
+        bool playing = false;
+
+        pa_threaded_mainloop_lock(m);
+
+        if (stream) {
+            const pa_timing_info *info;
+
+            if (pa_stream_get_time(stream, &t) >= 0 &&
+                pa_stream_get_latency(stream, &d, NULL) >= 0)
+                success = true;
+
+            if ((info = pa_stream_get_timing_info(stream))) {
+                if (memcmp(&last_info, &info->timestamp, sizeof(struct timeval))) {
+                    changed = true;
+                    last_info = info->timestamp;
+                }
+                if (info->playing)
+                    playing = true;
+            }
+        }
+
+        pa_threaded_mainloop_unlock(m);
+
+        pa_gettimeofday(&now);
+
+        if (success) {
+#ifdef CORK
+            bool cork_now;
+#endif
+            rtc = pa_timeval_diff(&now, &start);
+            pa_log_info("%i\t%llu\t%llu\t%llu\t%llu\t%lli\t%u\t%u\t%llu\t%llu", k,
+                   (unsigned long long) rtc,
+                   (unsigned long long) t,
+                   (unsigned long long) (rtc-old_rtc),
+                   (unsigned long long) (t-old_t),
+                   (signed long long) rtc - (signed long long) t,
+                   changed,
+                   playing,
+                   (unsigned long long) latency,
+                   (unsigned long long) d);
+
+            fflush(stdout);
+            old_t = t;
+            old_rtc = rtc;
+
+#ifdef CORK
+            cork_now = (rtc / (2*PA_USEC_PER_SEC)) % 2 == 1;
+
+            if (corked != cork_now) {
+                pa_threaded_mainloop_lock(m);
+                pa_operation_unref(pa_stream_cork(stream, cork_now, NULL, NULL));
+                pa_threaded_mainloop_unlock(m);
+
+                pa_log(cork_now ? "Corking" : "Uncorking");
+
+                corked = cork_now;
+            }
+#endif
+        }
+
+        /* Spin loop, ugly but normal usleep() is just too badly grained */
+        tv = now;
+        while (pa_timeval_diff(pa_gettimeofday(&now), &tv) < 1000)
+            pa_thread_yield();
+    }
+
+    if (m)
+        pa_threaded_mainloop_stop(m);
+
+    if (stream) {
+        pa_stream_disconnect(stream);
+        pa_stream_unref(stream);
+    }
+
+    if (context) {
+        pa_context_disconnect(context);
+        pa_context_unref(context);
+    }
+
+    if (m)
+        pa_threaded_mainloop_free(m);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    bname = argv[0];
+    playback = argc <= 1 || !pa_streq(argv[1], "-r");
+    latency = (argc >= 2 && !pa_streq(argv[1], "-r")) ? atoi(argv[1]) : (argc >= 3 ? atoi(argv[2]) : 0);
+
+    s = suite_create("Interpol");
+    tc = tcase_create("interpol");
+    tcase_add_test(tc, interpol_test);
+    tcase_set_timeout(tc, 5 * 60);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/ipacl-test.c b/src/tests/ipacl-test.c
new file mode 100644 (file)
index 0000000..4ce3490
--- /dev/null
@@ -0,0 +1,116 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <check.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/ipacl.h>
+#include <pulsecore/arpa-inet.h>
+
+static void do_ip_acl_check(const char *s, int fd, int expected) {
+    pa_ip_acl *acl;
+    int result;
+
+    acl = pa_ip_acl_new(s);
+    fail_unless(acl != NULL);
+    result = pa_ip_acl_check(acl, fd);
+    pa_ip_acl_free(acl);
+
+    pa_log_info("%-20s result=%u (should be %u)", s, result, expected);
+    fail_unless(result == expected);
+}
+
+START_TEST (ipacl_test) {
+    struct sockaddr_in sa;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 sa6;
+#endif
+    int fd;
+    int r;
+
+    fd = socket(PF_INET, SOCK_STREAM, 0);
+    fail_unless(fd >= 0);
+
+    sa.sin_family = AF_INET;
+    sa.sin_port = htons(22);
+    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+    r = connect(fd, (struct sockaddr*) &sa, sizeof(sa));
+    fail_unless(r >= 0);
+
+    do_ip_acl_check("127.0.0.1", fd, 1);
+    do_ip_acl_check("127.0.0.2/0", fd, 1);
+    do_ip_acl_check("127.0.0.1/32", fd, 1);
+    do_ip_acl_check("127.0.0.1/7", fd, 1);
+    do_ip_acl_check("127.0.0.2", fd, 0);
+    do_ip_acl_check("127.0.0.0/8;0.0.0.0/32", fd, 1);
+    do_ip_acl_check("128.0.0.2/9", fd, 0);
+    do_ip_acl_check("::1/9", fd, 0);
+
+    close(fd);
+
+#ifdef HAVE_IPV6
+    if ( (fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0 ) {
+      pa_log_error("Unable to open IPv6 socket, IPv6 tests ignored");
+      return;
+    }
+
+    memset(&sa6, 0, sizeof(sa6));
+    sa6.sin6_family = AF_INET6;
+    sa6.sin6_port = htons(22);
+    fail_unless(inet_pton(AF_INET6, "::1", &sa6.sin6_addr) == 1);
+
+    r = connect(fd, (struct sockaddr*) &sa6, sizeof(sa6));
+    fail_unless(r >= 0);
+
+    do_ip_acl_check("::1", fd, 1);
+    do_ip_acl_check("::1/9", fd, 1);
+    do_ip_acl_check("::/0", fd, 1);
+    do_ip_acl_check("::2/128", fd, 0);
+    do_ip_acl_check("::2/127", fd, 0);
+    do_ip_acl_check("::2/126", fd, 1);
+
+    close(fd);
+#endif
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("IP ACL");
+    tc = tcase_create("ipacl");
+    tcase_add_test(tc, ipacl_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/json-test.c b/src/tests/json-test.c
new file mode 100644 (file)
index 0000000..3e956db
--- /dev/null
@@ -0,0 +1,280 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2016 Arun Raghavan <mail@arunraghavan.net>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <pulse/json.h>
+#include <pulsecore/core-util.h>
+
+START_TEST (string_test) {
+    pa_json_object *o;
+    unsigned int i;
+    const char *strings_parse[] = {
+        "\"\"", "\"test\"", "\"test123\"", "\"123\"", "\"newline\\n\"", "\"  spaces \"",
+        "   \"lots of spaces\"     ", "\"esc\\nape\"", "\"escape a \\\" quote\"",
+    };
+    const char *strings_compare[] = {
+        "", "test", "test123", "123", "newline\n", "  spaces ",
+        "lots of spaces", "esc\nape", "escape a \" quote",
+    };
+
+    for (i = 0; i < PA_ELEMENTSOF(strings_parse); i++) {
+        o = pa_json_parse(strings_parse[i]);
+
+        fail_unless(o != NULL);
+        fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_STRING);
+        fail_unless(pa_streq(pa_json_object_get_string(o), strings_compare[i]));
+
+        pa_json_object_free(o);
+    }
+}
+END_TEST
+
+START_TEST(int_test) {
+    pa_json_object *o;
+    unsigned int i;
+    const char *ints_parse[] = { "1", "-1", "1234", "0" };
+    const int ints_compare[] = { 1, -1, 1234, 0 };
+
+    for (i = 0; i < PA_ELEMENTSOF(ints_parse); i++) {
+        o = pa_json_parse(ints_parse[i]);
+
+        fail_unless(o != NULL);
+        fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_INT);
+        fail_unless(pa_json_object_get_int(o) == ints_compare[i]);
+
+        pa_json_object_free(o);
+    }
+}
+END_TEST
+
+START_TEST(double_test) {
+    pa_json_object *o;
+    unsigned int i;
+    const char *doubles_parse[] = {
+        "1.0", "-1.1", "1234e2", "1234e0", "0.1234", "-0.1234", "1234e-1", "1234.5e-1", "1234.5e+2",
+    };
+    const double doubles_compare[] = {
+        1.0, -1.1, 123400.0, 1234.0, 0.1234, -0.1234, 123.4, 123.45, 123450.0,
+    };
+
+    for (i = 0; i < PA_ELEMENTSOF(doubles_parse); i++) {
+        o = pa_json_parse(doubles_parse[i]);
+
+        fail_unless(o != NULL);
+        fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_DOUBLE);
+        fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(o), doubles_compare[i]));
+
+        pa_json_object_free(o);
+    }
+}
+END_TEST
+
+START_TEST(null_test) {
+    pa_json_object *o;
+
+    o = pa_json_parse("null");
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_NULL);
+
+    pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(bool_test) {
+    pa_json_object *o;
+
+    o = pa_json_parse("true");
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_BOOL);
+    fail_unless(pa_json_object_get_bool(o) == true);
+
+    pa_json_object_free(o);
+
+    o = pa_json_parse("false");
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_BOOL);
+    fail_unless(pa_json_object_get_bool(o) == false);
+
+    pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(object_test) {
+    pa_json_object *o;
+    const pa_json_object *v;
+
+    o = pa_json_parse(" { \"name\" : \"A Person\" } ");
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    v = pa_json_object_get_object_member(o, "name");
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_STRING);
+    fail_unless(pa_streq(pa_json_object_get_string(v), "A Person"));
+
+    pa_json_object_free(o);
+
+    o = pa_json_parse(" { \"age\" : -45.3e-0 } ");
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    v = pa_json_object_get_object_member(o, "age");
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_DOUBLE);
+    fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(v), -45.3));
+
+    pa_json_object_free(o);
+
+    o = pa_json_parse("{\"person\":true}");
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    v = pa_json_object_get_object_member(o, "person");
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_BOOL);
+    fail_unless(pa_json_object_get_bool(v) == true);
+
+    pa_json_object_free(o);
+
+    o = pa_json_parse("{ \"parent\": { \"child\": false } }");
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    v = pa_json_object_get_object_member(o, "parent");
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_OBJECT);
+    v = pa_json_object_get_object_member(v, "child");
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_BOOL);
+    fail_unless(pa_json_object_get_bool(v) == false);
+
+    pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(array_test) {
+    pa_json_object *o;
+    const pa_json_object *v, *v2;
+
+    o = pa_json_parse(" [  ] ");
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(o) == 0);
+
+    pa_json_object_free(o);
+
+    o = pa_json_parse("[\"a member\"]");
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(o) == 1);
+
+    v = pa_json_object_get_array_member(o, 0);
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_STRING);
+    fail_unless(pa_streq(pa_json_object_get_string(v), "a member"));
+
+    pa_json_object_free(o);
+
+    o = pa_json_parse("[\"a member\", 1234.5, { \"another\": true } ]");
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(o) == 3);
+
+    v = pa_json_object_get_array_member(o, 0);
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_STRING);
+    fail_unless(pa_streq(pa_json_object_get_string(v), "a member"));
+    v = pa_json_object_get_array_member(o, 1);
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_DOUBLE);
+    fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(v), 1234.5));
+    v = pa_json_object_get_array_member(o, 2);
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_OBJECT);
+    v2 =pa_json_object_get_object_member(v, "another");
+    fail_unless(v2 != NULL);
+    fail_unless(pa_json_object_get_type(v2) == PA_JSON_TYPE_BOOL);
+    fail_unless(pa_json_object_get_bool(v2) == true);
+
+    pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(bad_test) {
+    unsigned int i;
+    const char *bad_parse[] = {
+        "\"" /* Quote not closed */,
+        "123456789012345678901234567890" /* Overflow */,
+        "0.123456789012345678901234567890" /* Overflow */,
+        "1e123456789012345678901234567890" /* Overflow */,
+        "1e" /* Bad number string */,
+        "1." /* Bad number string */,
+        "1.e3" /* Bad number string */,
+        "-" /* Bad number string */,
+        "{ \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": { \"a\": {  \"a\": { } } } } } } } } } } } } } } } } } } } } } }" /* Nested too deep */,
+        "[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ { \"a\": \"b\" } ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ]" /* Nested too deep */,
+        "asdf" /* Unquoted string */,
+        "{ a: true }" /* Unquoted key in object */,
+        "\"    \a\"" /* Alarm is not a valid character */
+    };
+
+    for (i = 0; i < PA_ELEMENTSOF(bad_parse); i++) {
+        fail_unless(pa_json_parse(bad_parse[i]) == NULL);
+    }
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("JSON");
+    tc = tcase_create("json");
+    tcase_add_test(tc, string_test);
+    tcase_add_test(tc, int_test);
+    tcase_add_test(tc, double_test);
+    tcase_add_test(tc, null_test);
+    tcase_add_test(tc, bool_test);
+    tcase_add_test(tc, object_test);
+    tcase_add_test(tc, array_test);
+    tcase_add_test(tc, bad_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/ladspa-dbus.py b/src/tests/ladspa-dbus.py
new file mode 100644 (file)
index 0000000..6f540e5
--- /dev/null
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+
+USAGE = """
+Usage:
+    python ladspa-dbus.py <sinkname> [values]
+
+The "sinkname" parameter is the name of the ladspa sink. The "values"
+parameter is a comma-separated list of ladspa sink parameter values. A
+value in the list can be either string "default" or a float.
+
+Example usage:
+
+    python ladspa-dbus.py ladspa_1 10.0,default,4.0,0.6,default
+
+This command will configure sink "ladspa_1" by setting the first value
+to 10.0, the second to the default value (specified in the ladspa
+filter), the third to 4.0 and so on.
+"""
+
+import dbus
+import os
+import sys
+
+def get_ladspa_property_interface(sinkname):
+
+    # do some D-Bus stuff to get to the real ladspa property object
+    session = dbus.SessionBus()
+
+    # get the private D-Bus socket address from PulseAudio properties
+    session_property_iface = dbus.Interface(session.get_object("org.PulseAudio1", "/org/pulseaudio/server_lookup1"), "org.freedesktop.DBus.Properties")
+    socket = session_property_iface.Get("org.PulseAudio.ServerLookup1", "Address")
+
+    # connect to the private PulseAudio D-Bus socket
+    connection = dbus.connection.Connection(socket)
+
+    # core object for listing the sinks
+    core = connection.get_object(object_path="/org/pulseaudio/core1")
+
+    # object path to the ladspa sink
+    ladspa_sink_path = core.GetSinkByName(sinkname)
+
+    # property interface proxy for the sink
+    ladspa_sink_property_iface = dbus.Interface(connection.get_object(object_path=ladspa_sink_path), "org.freedesktop.DBus.Properties")
+
+    return ladspa_sink_property_iface
+
+def parse_arguments(args):
+
+    sinkname = None
+    arguments = []
+    defaults = []
+
+    if len(args) >= 2:
+        sinkname = args[1]
+
+        if len(args) == 3:
+            tokens = args[2].split(",")
+
+            for token in tokens:
+                if token == "default":
+                    arguments.append(0.0)
+                    defaults.append(True)
+                else:
+                    arguments.append(float(token))
+                    defaults.append(False)
+
+    """
+    print("Input arguments:")
+    print("         sink: " + sink)
+    print("    arguments: " + str(arguments))
+    print("     defaults: " + str(defaults))
+    """
+
+    return sinkname, arguments, defaults
+
+def print_arguments(arguments, defaults):
+    for i in range(len(arguments)):
+        default = ""
+        if defaults[i]:
+            default = "default"
+        print(str(i) + " : " + str(arguments[i]) + " \t"  + default)
+
+
+sinkname, arguments, defaults = parse_arguments(sys.argv)
+
+if sinkname == None:
+    print USAGE
+    sys.exit(1)
+
+# get the D-Bus property interface of the sink
+ladspa = get_ladspa_property_interface(sinkname)
+
+# read the current sink arguments from PulseAudio
+oldarguments, olddefaults = ladspa.Get("org.PulseAudio.Ext.Ladspa1", "AlgorithmParameters")
+
+print("Current LADSPA parameters for sink " + sinkname + ":")
+print_arguments(oldarguments, olddefaults)
+
+if len(arguments) != 0:
+    # set new arguments if they were provided on the command line
+
+    # Set the arguments ...
+    ladspa.Set("org.PulseAudio.Ext.Ladspa1", "AlgorithmParameters", (dbus.Array(arguments), dbus.Array(defaults)))
+
+    # ... and read them back.
+    newarguments, newdefaults = ladspa.Get("org.PulseAudio.Ext.Ladspa1", "AlgorithmParameters")
+
+    print("New LADSPA parameters for sink " + sinkname + ":")
+    print_arguments(newarguments, newdefaults)
+
+# test the GetAll functionality
+# print(str(ladspa.GetAll("org.PulseAudio.Ext.Ladspa1")))
diff --git a/src/tests/lfe-filter-test.c b/src/tests/lfe-filter-test.c
new file mode 100644 (file)
index 0000000..d779e05
--- /dev/null
@@ -0,0 +1,196 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/sample.h>
+#include <pulsecore/memblock.h>
+
+#include <pulsecore/filter/lfe-filter.h>
+
+struct lfe_filter_test {
+    pa_lfe_filter_t *lf;
+    pa_mempool *pool;
+    pa_sample_spec *ss;
+};
+
+static uint8_t *ori_sample_ptr;
+
+#define ONE_BLOCK_SAMPLES 4096
+#define TOTAL_SAMPLES 8192
+#define TOLERANT_VARIATION 1
+
+static void save_data_block(struct lfe_filter_test *lft, void *d, pa_memblock *blk) {
+    uint8_t *dst = d, *src;
+    size_t blk_size = pa_frame_size(lft->ss) * ONE_BLOCK_SAMPLES;
+
+    src = pa_memblock_acquire(blk);
+    memcpy(dst, src, blk_size);
+    pa_memblock_release(blk);
+}
+
+static pa_memblock* generate_data_block(struct lfe_filter_test *lft, int start) {
+    pa_memblock *r;
+    uint8_t *d, *s = ori_sample_ptr;
+    size_t blk_size = pa_frame_size(lft->ss) * ONE_BLOCK_SAMPLES;
+
+    pa_assert_se(r = pa_memblock_new(lft->pool, blk_size));
+    d = pa_memblock_acquire(r);
+    memcpy(d, s + start,  blk_size);
+    pa_memblock_release(r);
+
+    return r;
+}
+
+static int compare_data_block(struct lfe_filter_test *lft, void *a, void *b) {
+    int ret = 0;
+    uint32_t i;
+    uint16_t *r = a, *u = b;
+
+    pa_assert(lft->ss->format == PA_SAMPLE_S16NE);
+
+    for (i = 0; i < ONE_BLOCK_SAMPLES; i++) {
+        if (abs(*r++ - *u++) > TOLERANT_VARIATION) {
+            pa_log_error("lfe-filter-test: test failed, the output data in the position 0x%x of a block does not equal!", i);
+            ret = -1;
+            break;
+        }
+    }
+    return ret;
+}
+
+/* in this test case, we pass two blocks of sample data to lfe-filter, each
+   block contains 4096 samples, and don't let rewind_samples exceed TOTAL_SAMPLES */
+static int lfe_filter_rewind_test(struct lfe_filter_test *lft, int rewind_samples)
+{
+    int ret = -1, pos, i;
+    pa_memchunk mc;
+    uint8_t *outptr;
+    uint32_t fz = pa_frame_size(lft->ss);
+
+    if (rewind_samples > TOTAL_SAMPLES || rewind_samples < TOTAL_SAMPLES - ONE_BLOCK_SAMPLES) {
+        pa_log_error("lfe-filter-test: Please keep %d samples < rewind_samples < %d samples", TOTAL_SAMPLES - ONE_BLOCK_SAMPLES, TOTAL_SAMPLES);
+        return ret;
+    }
+
+    outptr = pa_xmalloc(fz * TOTAL_SAMPLES);
+
+    /* let lfe-filter process all samples first, and save the processed data to the temp buffer,
+       then rewind back to some position, reprocess some samples and compare the output data with
+       the processed data saved before. */
+    for (i = 0; i < TOTAL_SAMPLES / ONE_BLOCK_SAMPLES; i++) {
+        mc.memblock = generate_data_block(lft, i * ONE_BLOCK_SAMPLES * fz);
+        mc.length = pa_memblock_get_length(mc.memblock);
+        mc.index = 0;
+        pa_lfe_filter_process(lft->lf, &mc);
+        save_data_block(lft, outptr + i * ONE_BLOCK_SAMPLES * fz, mc.memblock);
+        pa_memblock_unref(mc.memblock);
+    }
+
+    pa_lfe_filter_rewind(lft->lf, rewind_samples * fz);
+    pos = (TOTAL_SAMPLES - rewind_samples) * fz;
+    mc.memblock = generate_data_block(lft, pos);
+    mc.length = pa_memblock_get_length(mc.memblock);
+    mc.index = 0;
+    pa_lfe_filter_process(lft->lf, &mc);
+    ret = compare_data_block(lft, outptr + pos, pa_memblock_acquire(mc.memblock));
+    pa_memblock_release(mc.memblock);
+    pa_memblock_unref(mc.memblock);
+
+    pa_xfree(outptr);
+
+    return ret;
+}
+
+START_TEST (lfe_filter_test) {
+    pa_sample_spec a;
+    int ret = -1;
+    unsigned i, crossover_freq = 120;
+    pa_channel_map chmapmono = {1, {PA_CHANNEL_POSITION_LFE}};
+    struct lfe_filter_test lft;
+    short *tmp_ptr;
+
+    pa_log_set_level(PA_LOG_DEBUG);
+
+    a.channels = 1;
+    a.rate = 44100;
+    a.format = PA_SAMPLE_S16NE;
+
+    lft.ss = &a;
+    pa_assert_se(lft.pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true));
+
+    /* We prepare pseudo-random input audio samples for lfe-filter rewind testing*/
+    ori_sample_ptr = pa_xmalloc(pa_frame_size(lft.ss) * TOTAL_SAMPLES);
+    tmp_ptr = (short *) ori_sample_ptr;
+    for (i = 0; i < pa_frame_size(lft.ss) * TOTAL_SAMPLES / sizeof(short); i++)
+        *tmp_ptr++ = random();
+
+    /* we create a lfe-filter with cutoff frequency 120Hz and max rewind time 10 seconds */
+    pa_assert_se(lft.lf = pa_lfe_filter_new(&a, &chmapmono, crossover_freq, a.rate * 10));
+    /* rewind to a block boundary */
+    ret = lfe_filter_rewind_test(&lft, ONE_BLOCK_SAMPLES);
+    if (ret)
+        pa_log_error("lfe-filer-test: rewind to block boundary test failed!!!");
+    pa_lfe_filter_free(lft.lf);
+
+    /* we create a lfe-filter with cutoff frequency 120Hz and max rewind time 10 seconds */
+    pa_assert_se(lft.lf = pa_lfe_filter_new(&a, &chmapmono, crossover_freq, a.rate * 10));
+    /* rewind to the middle position of a block */
+    ret = lfe_filter_rewind_test(&lft, ONE_BLOCK_SAMPLES + ONE_BLOCK_SAMPLES / 2);
+    if (ret)
+        pa_log_error("lfe-filer-test: rewind to middle of block test failed!!!");
+
+    pa_xfree(ori_sample_ptr);
+
+    pa_lfe_filter_free(lft.lf);
+
+    pa_mempool_unref(lft.pool);
+
+    if (!ret)
+        pa_log_debug("lfe-filter-test: tests for both rewind to block boundary and rewind to middle position of a block passed!");
+
+    fail_unless(ret == 0);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("lfe-filter");
+    tc = tcase_create("lfe-filter");
+    tcase_add_test(tc, lfe_filter_test);
+    tcase_set_timeout(tc, 10);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/lo-latency-test.c b/src/tests/lo-latency-test.c
new file mode 100644 (file)
index 0000000..813b337
--- /dev/null
@@ -0,0 +1,188 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Collabora Ltd.
+  Author: Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <check.h>
+
+#include "lo-test-util.h"
+
+#define SAMPLE_HZ 44100
+#define CHANNELS 2
+#define N_OUT (SAMPLE_HZ * 1)
+
+static float out[N_OUT][CHANNELS];
+
+pa_lo_test_context test_ctx;
+static const char *context_name = NULL;
+
+static struct timeval tv_out, tv_in;
+
+static void nop_free_cb(void *p) {
+}
+
+static void write_cb(pa_stream *s, size_t nbytes, void *userdata) {
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
+    static int ppos = 0;
+    int r, nsamp;
+
+    /* Get the real requested bytes since the last write might have been
+     * incomplete if it caused a wrap around */
+    nbytes = pa_stream_writable_size(s);
+    nsamp = nbytes / ctx->fs;
+
+    if (ppos + nsamp > N_OUT) {
+        /* Wrap-around, write to end and exit. Next iteration will fill up the
+         * rest */
+        nbytes = (N_OUT - ppos) * ctx->fs;
+    }
+
+    if (ppos == 0)
+        pa_gettimeofday(&tv_out);
+
+    r = pa_stream_write(s, &out[ppos][0], nbytes, nop_free_cb, 0, PA_SEEK_RELATIVE);
+    fail_unless(r == 0);
+
+    ppos = (ppos + nbytes / ctx->fs) % N_OUT;
+}
+
+#define WINDOW (2 * CHANNELS)
+
+static void read_cb(pa_stream *s, size_t nbytes, void *userdata) {
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
+    static float last = 0.0f;
+    const float *in;
+    float cur;
+    int r;
+    unsigned int i = 0;
+    size_t l;
+
+    r = pa_stream_peek(s, (const void **)&in, &l);
+    fail_unless(r == 0);
+
+    if (l == 0)
+        return;
+
+#if 0
+    {
+        static int fd = -1;
+
+        if (fd == -1) {
+            fd = open("loopback.raw", O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+            fail_if(fd < 0);
+        }
+
+        r = write(fd, in, l);
+    }
+#endif
+
+    do {
+#if 0
+        {
+            int j;
+            fprintf(stderr, "%g (", pa_rms(in, WINDOW));
+            for (j = 0; j < WINDOW; j++)
+                fprintf(stderr, "%g ", in[j]);
+            fprintf(stderr, ")\n");
+        }
+#endif
+        if (i + (ctx->ss * WINDOW) < l)
+            cur = pa_rms(in, WINDOW);
+        else
+            cur = pa_rms(in, (l - i) / ctx->ss);
+
+        /* We leave the definition of 0 generous since the window might
+         * straddle the 0->1 transition, raising the average power. We keep the
+         * definition of 1 tight in this case and detect the transition in the
+         * next round. */
+        if (cur - last > 0.4f) {
+            pa_gettimeofday(&tv_in);
+            fprintf(stderr, "Latency %llu\n", (unsigned long long) pa_timeval_diff(&tv_in, &tv_out));
+        }
+
+        last = cur;
+        in += WINDOW;
+        i += ctx->ss * WINDOW;
+    } while (i + (ctx->ss * WINDOW) <= l);
+
+    pa_stream_drop(s);
+}
+
+START_TEST (loopback_test) {
+    int i, pulse_hz = SAMPLE_HZ / 1000;
+
+    test_ctx.context_name = context_name;
+
+    test_ctx.sample_spec.format = PA_SAMPLE_FLOAT32,
+    test_ctx.sample_spec.rate = SAMPLE_HZ,
+    test_ctx.sample_spec.channels = CHANNELS,
+
+    test_ctx.play_latency = 25;
+    test_ctx.rec_latency = 5;
+
+    test_ctx.read_cb = read_cb;
+    test_ctx.write_cb = write_cb;
+
+    /* Generate a square pulse */
+    for (i = 0; i < N_OUT; i++)
+        if (i < pulse_hz)
+            out[i][0] = out[i][1] = 1.0f;
+        else
+            out[i][0] = out[i][1] = 0.0f;
+
+    fail_unless(pa_lo_test_init(&test_ctx) == 0);
+    fail_unless(pa_lo_test_run(&test_ctx) == 0);
+    pa_lo_test_deinit(&test_ctx);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    context_name = argv[0];
+
+    s = suite_create("Loopback latency");
+    tc = tcase_create("loopback latency");
+    tcase_add_test(tc, loopback_test);
+    tcase_set_timeout(tc, 5 * 60);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/lo-test-util.c b/src/tests/lo-test-util.c
new file mode 100644 (file)
index 0000000..fae91a2
--- /dev/null
@@ -0,0 +1,324 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Collabora Ltd.
+  Author: Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "lo-test-util.h"
+
+/* Keep the frequency high so RMS over ranges of a few ms remains relatively
+ * high as well */
+#define TONE_HZ 4410
+
+static void nop_free_cb(void *p) {
+}
+
+static void underflow_cb(struct pa_stream *s, void *userdata) {
+    pa_log_warn("Underflow");
+}
+
+static void overflow_cb(struct pa_stream *s, void *userdata) {
+    pa_log_warn("Overlow");
+}
+
+/*
+ * We run a simple volume calibration so that we know we can detect the signal
+ * being played back. We start with the playback stream at 100% volume, and
+ * capture at 0.
+ *
+ * First, we then play a sine wave and increase the capture volume till the
+ * signal is clearly received.
+ *
+ * Next, we play back silence and make sure that the level is low enough to
+ * distinguish from when playback is happening.
+ *
+ * Finally, we hand off to the real read/write callbacks to run the actual
+ * test.
+ */
+
+enum {
+    CALIBRATION_ONE,
+    CALIBRATION_ZERO,
+    CALIBRATION_DONE,
+};
+
+static int cal_state = CALIBRATION_ONE;
+
+static void calibrate_write_cb(pa_stream *s, size_t nbytes, void *userdata) {
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
+    int i, nsamp = nbytes / ctx->fs;
+    float tmp[nsamp][2];
+    static int count = 0;
+
+    /* Write out a sine tone */
+    for (i = 0; i < nsamp; i++)
+        tmp[i][0] = tmp[i][1] = cal_state == CALIBRATION_ONE ? sinf(count++ * TONE_HZ * 2 * M_PI / ctx->sample_spec.rate) : 0.0f;
+
+    pa_assert_se(pa_stream_write(s, &tmp, nbytes, nop_free_cb, 0, PA_SEEK_RELATIVE) == 0);
+
+    if (cal_state == CALIBRATION_DONE)
+        pa_stream_set_write_callback(s, ctx->write_cb, ctx);
+}
+
+static void calibrate_read_cb(pa_stream *s, size_t nbytes, void *userdata) {
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
+    static double v = 0;
+    static int skip = 0, confirm;
+
+    pa_cvolume vol;
+    pa_operation *o;
+    int nsamp;
+    float *in;
+    size_t l;
+
+    pa_assert_se(pa_stream_peek(s, (const void **)&in, &l) == 0);
+
+    nsamp = l / ctx->fs;
+
+    /* For each state or volume step change, throw out a few samples so we know
+     * we're seeing the changed samples. */
+    if (skip++ < 100)
+        goto out;
+    else
+        skip = 0;
+
+    switch (cal_state) {
+        case CALIBRATION_ONE:
+            /* Try to detect the sine wave. RMS is 0.5, */
+            if (pa_rms(in, nsamp) < 0.40f) {
+                confirm = 0;
+                v += 0.02f;
+
+                if (v > 1.0) {
+                    pa_log_error("Capture signal too weak at 100%% volume (%g). Giving up.", pa_rms(in, nsamp));
+                    pa_assert_not_reached();
+                }
+
+                pa_cvolume_set(&vol, ctx->sample_spec.channels, v * PA_VOLUME_NORM);
+                o = pa_context_set_source_output_volume(ctx->context, pa_stream_get_index(s), &vol, NULL, NULL);
+                pa_assert(o != NULL);
+                pa_operation_unref(o);
+            } else {
+                /* Make sure the signal strength is steadily above our threshold */
+                if (++confirm > 5) {
+#if 0
+                    pa_log_debug(stderr, "Capture volume = %g (%g)", v, pa_rms(in, nsamp));
+#endif
+                    cal_state = CALIBRATION_ZERO;
+                }
+            }
+
+            break;
+
+        case CALIBRATION_ZERO:
+            /* Now make sure silence doesn't trigger a false positive because
+             * of noise. */
+            if (pa_rms(in, nsamp) > 0.1f) {
+                pa_log_warn("Too much noise on capture (%g). Giving up.", pa_rms(in, nsamp));
+                pa_assert_not_reached();
+            }
+
+            cal_state = CALIBRATION_DONE;
+            pa_stream_set_read_callback(s, ctx->read_cb, ctx);
+
+            break;
+
+        default:
+            break;
+    }
+
+out:
+    pa_stream_drop(s);
+}
+
+/* This routine is called whenever the stream state changes */
+static void stream_state_callback(pa_stream *s, void *userdata) {
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
+
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+        case PA_STREAM_TERMINATED:
+            break;
+
+        case PA_STREAM_READY: {
+            pa_cvolume vol;
+            pa_operation *o;
+
+            /* Set volumes for calibration */
+            if (s == ctx->play_stream) {
+                pa_cvolume_set(&vol, ctx->sample_spec.channels, PA_VOLUME_NORM);
+                o = pa_context_set_sink_input_volume(ctx->context, pa_stream_get_index(s), &vol, NULL, NULL);
+            } else {
+                pa_cvolume_set(&vol, ctx->sample_spec.channels, pa_sw_volume_from_linear(0.0));
+                o = pa_context_set_source_output_volume(ctx->context, pa_stream_get_index(s), &vol, NULL, NULL);
+            }
+
+            if (!o) {
+                pa_log_error("Could not set stream volume: %s", pa_strerror(pa_context_errno(ctx->context)));
+                pa_assert_not_reached();
+            } else
+                pa_operation_unref(o);
+
+            break;
+        }
+
+        case PA_STREAM_FAILED:
+        default:
+            pa_log_error("Stream error: %s", pa_strerror(pa_context_errno(ctx->context)));
+            pa_assert_not_reached();
+    }
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
+    pa_mainloop_api *api;
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+            pa_buffer_attr buffer_attr;
+
+            pa_make_realtime(4);
+
+            /* Create playback stream */
+            buffer_attr.maxlength = -1;
+            buffer_attr.tlength = ctx->sample_spec.rate * ctx->fs * ctx->play_latency / 1000;
+            buffer_attr.prebuf = 0; /* Setting prebuf to 0 guarantees us the stream will run synchronously, no matter what */
+            buffer_attr.minreq = -1;
+            buffer_attr.fragsize = -1;
+
+            ctx->play_stream = pa_stream_new(c, "loopback: play", &ctx->sample_spec, NULL);
+            pa_assert(ctx->play_stream != NULL);
+            pa_stream_set_state_callback(ctx->play_stream, stream_state_callback, ctx);
+            pa_stream_set_write_callback(ctx->play_stream, calibrate_write_cb, ctx);
+            pa_stream_set_underflow_callback(ctx->play_stream, underflow_cb, userdata);
+
+            pa_stream_connect_playback(ctx->play_stream, getenv("TEST_SINK"), &buffer_attr,
+                    PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
+
+            /* Create capture stream */
+            buffer_attr.maxlength = -1;
+            buffer_attr.tlength = (uint32_t) -1;
+            buffer_attr.prebuf = 0;
+            buffer_attr.minreq = (uint32_t) -1;
+            buffer_attr.fragsize = ctx->sample_spec.rate * ctx->fs * ctx->rec_latency / 1000;
+
+            ctx->rec_stream = pa_stream_new(c, "loopback: rec", &ctx->sample_spec, NULL);
+            pa_assert(ctx->rec_stream != NULL);
+            pa_stream_set_state_callback(ctx->rec_stream, stream_state_callback, ctx);
+            pa_stream_set_read_callback(ctx->rec_stream, calibrate_read_cb, ctx);
+            pa_stream_set_overflow_callback(ctx->rec_stream, overflow_cb, userdata);
+
+            pa_stream_connect_record(ctx->rec_stream, getenv("TEST_SOURCE"), &buffer_attr,
+                    PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
+
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            api = pa_mainloop_get_api(ctx->mainloop);
+            api->quit(api, 0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            pa_log_error("Context error: %s", pa_strerror(pa_context_errno(c)));
+            pa_assert_not_reached();
+    }
+}
+
+int pa_lo_test_init(pa_lo_test_context *ctx) {
+    /* FIXME: need to deal with non-float samples at some point */
+    pa_assert(ctx->sample_spec.format == PA_SAMPLE_FLOAT32);
+
+    ctx->ss = pa_sample_size(&ctx->sample_spec);
+    ctx->fs = pa_frame_size(&ctx->sample_spec);
+
+    ctx->mainloop = pa_mainloop_new();
+    ctx->context = pa_context_new(pa_mainloop_get_api(ctx->mainloop), ctx->context_name);
+
+    pa_context_set_state_callback(ctx->context, context_state_callback, ctx);
+
+    /* Connect the context */
+    if (pa_context_connect(ctx->context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
+        pa_log_error("pa_context_connect() failed.");
+        goto quit;
+    }
+
+    return 0;
+
+quit:
+    pa_context_unref(ctx->context);
+    pa_mainloop_free(ctx->mainloop);
+
+    return -1;
+}
+
+int pa_lo_test_run(pa_lo_test_context *ctx) {
+    int ret;
+
+    if (pa_mainloop_run(ctx->mainloop, &ret) < 0) {
+        pa_log_error("pa_mainloop_run() failed.");
+        return -1;
+    }
+
+    return 0;
+}
+
+void pa_lo_test_deinit(pa_lo_test_context *ctx) {
+    if (ctx->play_stream) {
+        pa_stream_disconnect(ctx->play_stream);
+        pa_stream_unref(ctx->play_stream);
+    }
+
+    if (ctx->rec_stream) {
+        pa_stream_disconnect(ctx->rec_stream);
+        pa_stream_unref(ctx->rec_stream);
+    }
+
+    if (ctx->context)
+        pa_context_unref(ctx->context);
+
+    if (ctx->mainloop)
+        pa_mainloop_free(ctx->mainloop);
+}
+
+float pa_rms(const float *s, int n) {
+    float sq = 0;
+    int i;
+
+    for (i = 0; i < n; i++)
+        sq += s[i] * s[i];
+
+    return sqrtf(sq / n);
+}
diff --git a/src/tests/lo-test-util.h b/src/tests/lo-test-util.h
new file mode 100644 (file)
index 0000000..a6b0cde
--- /dev/null
@@ -0,0 +1,55 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Collabora Ltd.
+  Author: Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/pulseaudio.h>
+
+typedef struct pa_lo_test_context {
+    /* Tests need to set these */
+    const char *context_name;
+
+    pa_sample_spec sample_spec;
+    int play_latency; /* ms */
+    int rec_latency; /* ms */
+
+    pa_stream_request_cb_t write_cb, read_cb;
+
+    /* These are set by lo_test_init() */
+    pa_mainloop *mainloop;
+    pa_context *context;
+
+    pa_stream *play_stream, *rec_stream;
+
+    int ss, fs; /* sample size, frame size for convenience */
+} pa_lo_test_context;
+
+/* Initialise the test parameters, connect */
+int pa_lo_test_init(pa_lo_test_context *ctx);
+/* Start running the test */
+int pa_lo_test_run(pa_lo_test_context *ctx);
+/* Clean up */
+void pa_lo_test_deinit(pa_lo_test_context *ctx);
+
+/* Return RMS for the given signal. Assumes the data is a single channel for
+ * simplicity */
+float pa_rms(const float *s, int n);
diff --git a/src/tests/lock-autospawn-test.c b/src/tests/lock-autospawn-test.c
new file mode 100644 (file)
index 0000000..d475d2d
--- /dev/null
@@ -0,0 +1,129 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <string.h>
+
+#include <pulsecore/poll.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/lock-autospawn.h>
+#include <pulse/util.h>
+
+static void thread_func(void*k) {
+    fail_unless(pa_autospawn_lock_init() >= 0);
+
+    pa_log("%i, Trying to acquire lock.", PA_PTR_TO_INT(k));
+
+    fail_unless(pa_autospawn_lock_acquire(true) > 0);
+
+    pa_log("%i, Got the lock!, Sleeping for 5s", PA_PTR_TO_INT(k));
+
+    pa_msleep(5000);
+
+    pa_log("%i, Releasing", PA_PTR_TO_INT(k));
+
+    pa_autospawn_lock_release();
+
+    pa_autospawn_lock_done(false);
+}
+
+static void thread_func2(void *k) {
+    int fd;
+
+    fail_unless((fd = pa_autospawn_lock_init()) >= 0);
+
+    pa_log("%i, Trying to acquire lock.", PA_PTR_TO_INT(k));
+
+    for (;;) {
+        struct pollfd pollfd;
+        int j;
+
+        if ((j = pa_autospawn_lock_acquire(false)) > 0)
+            break;
+
+        fail_unless(j == 0);
+
+        memset(&pollfd, 0, sizeof(pollfd));
+        pollfd.fd = fd;
+        pollfd.events = POLLIN;
+
+        fail_unless(pa_poll(&pollfd, 1, -1) == 1);
+
+        pa_log("%i, woke up", PA_PTR_TO_INT(k));
+    }
+
+    pa_log("%i, Got the lock!, Sleeping for 5s", PA_PTR_TO_INT(k));
+
+    pa_msleep(5000);
+
+    pa_log("%i, Releasing", PA_PTR_TO_INT(k));
+
+    pa_autospawn_lock_release();
+
+    pa_autospawn_lock_done(false);
+}
+
+START_TEST (lockautospawn_test) {
+    pa_thread *a, *b, *c, *d;
+
+    pa_assert_se((a = pa_thread_new("test1", thread_func, PA_INT_TO_PTR(1))));
+    pa_assert_se((b = pa_thread_new("test2", thread_func2, PA_INT_TO_PTR(2))));
+    pa_assert_se((c = pa_thread_new("test3", thread_func2, PA_INT_TO_PTR(3))));
+    pa_assert_se((d = pa_thread_new("test4", thread_func, PA_INT_TO_PTR(4))));
+
+    pa_thread_join(a);
+    pa_thread_join(b);
+    pa_thread_join(c);
+    pa_thread_join(d);
+
+    pa_thread_free(a);
+    pa_thread_free(b);
+    pa_thread_free(c);
+    pa_thread_free(d);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Lock Auto Spawn");
+    tc = tcase_create("lockautospawn");
+    tcase_add_test(tc, lockautospawn_test);
+    /* the default timeout is too small,
+     * set it to a reasonable large one.
+     */
+    tcase_set_timeout(tc, 60 * 60);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c
new file mode 100644 (file)
index 0000000..3a3e9bb
--- /dev/null
@@ -0,0 +1,140 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <check.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-rtclock.h>
+
+#ifdef GLIB_MAIN_LOOP
+
+#include <glib.h>
+#include <pulse/glib-mainloop.h>
+
+static GMainLoop* glib_main_loop = NULL;
+
+#else /* GLIB_MAIN_LOOP */
+#include <pulse/mainloop.h>
+#endif /* GLIB_MAIN_LOOP */
+
+static pa_defer_event *de;
+
+static void iocb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    unsigned char c;
+    pa_assert_se(read(fd, &c, sizeof(c)) >= 0);
+    fprintf(stderr, "IO EVENT: %c\n", c < 32 ? '.' : c);
+    a->defer_enable(de, 1);
+}
+
+static void dcb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
+    fprintf(stderr, "DEFER EVENT\n");
+    a->defer_enable(e, 0);
+}
+
+static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    fprintf(stderr, "TIME EVENT\n");
+
+#if defined(GLIB_MAIN_LOOP)
+    g_main_loop_quit(glib_main_loop);
+#else
+    a->quit(a, 0);
+#endif
+}
+
+START_TEST (mainloop_test) {
+    pa_mainloop_api *a;
+    pa_io_event *ioe;
+    pa_time_event *te;
+    struct timeval tv;
+
+#ifdef GLIB_MAIN_LOOP
+    pa_glib_mainloop *g;
+
+    glib_main_loop = g_main_loop_new(NULL, FALSE);
+    fail_if(!glib_main_loop);
+
+    g = pa_glib_mainloop_new(NULL);
+    fail_if(!g);
+
+    a = pa_glib_mainloop_get_api(g);
+    fail_if(!a);
+#else /* GLIB_MAIN_LOOP */
+    pa_mainloop *m;
+
+    m = pa_mainloop_new();
+    fail_if(!m);
+
+    a = pa_mainloop_get_api(m);
+    fail_if(!a);
+#endif /* GLIB_MAIN_LOOP */
+
+    ioe = a->io_new(a, 0, PA_IO_EVENT_INPUT, iocb, NULL);
+    fail_if(!ioe);
+
+    de = a->defer_new(a, dcb, NULL);
+    fail_if(!de);
+
+    te = a->time_new(a, pa_timeval_rtstore(&tv, pa_rtclock_now() + 2 * PA_USEC_PER_SEC, true), tcb, NULL);
+
+#if defined(GLIB_MAIN_LOOP)
+    g_main_loop_run(glib_main_loop);
+#else
+    pa_mainloop_run(m, NULL);
+#endif
+
+    a->time_free(te);
+    a->defer_free(de);
+    a->io_free(ioe);
+
+#ifdef GLIB_MAIN_LOOP
+    pa_glib_mainloop_free(g);
+    g_main_loop_unref(glib_main_loop);
+#else
+    pa_mainloop_free(m);
+#endif
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("MainLoop");
+    tc = tcase_create("mainloop");
+    tcase_add_test(tc, mainloop_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c
new file mode 100644 (file)
index 0000000..3127ccf
--- /dev/null
@@ -0,0 +1,106 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/mcalign.h>
+
+/* A simple program for testing pa_mcalign */
+
+int main(int argc, char *argv[]) {
+    pa_mempool *p;
+    pa_mcalign *a;
+    pa_memchunk c;
+
+    p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true);
+
+    a = pa_mcalign_new(11);
+
+    pa_memchunk_reset(&c);
+
+    srand((unsigned) time(NULL));
+
+    for (;;) {
+        ssize_t r;
+        size_t l;
+
+        if (!c.memblock) {
+            c.memblock = pa_memblock_new(p, 2048);
+            c.index = c.length = 0;
+        }
+
+        pa_assert(c.index < pa_memblock_get_length(c.memblock));
+
+        l = pa_memblock_get_length(c.memblock) - c.index;
+
+        l = l <= 1 ? l : (size_t) rand() % (l-1) +1;
+
+        p = pa_memblock_acquire(c.memblock);
+
+        if ((r = read(STDIN_FILENO, (uint8_t*) p + c.index, l)) <= 0) {
+            pa_memblock_release(c.memblock);
+            fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
+            break;
+        }
+
+        pa_memblock_release(c.memblock);
+
+        c.length = (size_t) r;
+        pa_mcalign_push(a, &c);
+        fprintf(stderr, "Read %zd bytes\n", r);
+
+        c.index += (size_t) r;
+
+        if (c.index >= pa_memblock_get_length(c.memblock)) {
+            pa_memblock_unref(c.memblock);
+            pa_memchunk_reset(&c);
+        }
+
+        for (;;) {
+            pa_memchunk t;
+
+            if (pa_mcalign_pop(a, &t) < 0)
+                break;
+
+            p = pa_memblock_acquire(t.memblock);
+            pa_loop_write(STDOUT_FILENO, (uint8_t*) p + t.index, t.length, NULL);
+            pa_memblock_release(t.memblock);
+            fprintf(stderr, "Wrote %lu bytes.\n", (unsigned long) t.length);
+
+            pa_memblock_unref(t.memblock);
+        }
+    }
+
+    pa_mcalign_free(a);
+
+    if (c.memblock)
+        pa_memblock_unref(c.memblock);
+
+    pa_mempool_unref(p);
+
+    return 0;
+}
diff --git a/src/tests/memblock-test.c b/src/tests/memblock-test.c
new file mode 100644 (file)
index 0000000..d48349f
--- /dev/null
@@ -0,0 +1,199 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <check.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/macro.h>
+
+static void release_cb(pa_memimport *i, uint32_t block_id, void *userdata) {
+    pa_log("%s: Imported block %u is released.", (char*) userdata, block_id);
+}
+
+static void revoke_cb(pa_memexport *e, uint32_t block_id, void *userdata) {
+    pa_log("%s: Exported block %u is revoked.", (char*) userdata, block_id);
+}
+
+static void print_stats(pa_mempool *p, const char *text) {
+    const pa_mempool_stat*s = pa_mempool_get_stat(p);
+
+    pa_log_debug("%s = {\n"
+                 "\tn_allocated = %u\n"
+                 "\tn_accumulated = %u\n"
+                 "\tn_imported = %u\n"
+                 "\tn_exported = %u\n"
+                 "\tallocated_size = %u\n"
+                 "\taccumulated_size = %u\n"
+                 "\timported_size = %u\n"
+                 "\texported_size = %u\n"
+                 "\tn_too_large_for_pool = %u\n"
+                 "\tn_pool_full = %u\n"
+                 "}",
+           text,
+           (unsigned) pa_atomic_load(&s->n_allocated),
+           (unsigned) pa_atomic_load(&s->n_accumulated),
+           (unsigned) pa_atomic_load(&s->n_imported),
+           (unsigned) pa_atomic_load(&s->n_exported),
+           (unsigned) pa_atomic_load(&s->allocated_size),
+           (unsigned) pa_atomic_load(&s->accumulated_size),
+           (unsigned) pa_atomic_load(&s->imported_size),
+           (unsigned) pa_atomic_load(&s->exported_size),
+           (unsigned) pa_atomic_load(&s->n_too_large_for_pool),
+           (unsigned) pa_atomic_load(&s->n_pool_full));
+}
+
+START_TEST (memblock_test) {
+    pa_mempool *pool_a, *pool_b, *pool_c;
+    unsigned id_a, id_b, id_c;
+    pa_memexport *export_a, *export_b;
+    pa_memimport *import_b, *import_c;
+    pa_memblock *mb_a, *mb_b, *mb_c;
+    int r, i;
+    pa_memblock* blocks[5];
+    pa_mem_type_t mem_type;
+    uint32_t id, shm_id;
+    size_t offset, size;
+    char *x;
+
+    const char txt[] = "This is a test!";
+
+    pool_a = pa_mempool_new(PA_MEM_TYPE_SHARED_POSIX, 0, true);
+    fail_unless(pool_a != NULL);
+    pool_b = pa_mempool_new(PA_MEM_TYPE_SHARED_POSIX, 0, true);
+    fail_unless(pool_b != NULL);
+    pool_c = pa_mempool_new(PA_MEM_TYPE_SHARED_POSIX, 0, true);
+    fail_unless(pool_c != NULL);
+
+    pa_mempool_get_shm_id(pool_a, &id_a);
+    pa_mempool_get_shm_id(pool_b, &id_b);
+    pa_mempool_get_shm_id(pool_c, &id_c);
+
+    blocks[0] = pa_memblock_new_fixed(pool_a, (void*) txt, sizeof(txt), 1);
+
+    blocks[1] = pa_memblock_new(pool_a, sizeof(txt));
+    x = pa_memblock_acquire(blocks[1]);
+    snprintf(x, pa_memblock_get_length(blocks[1]), "%s", txt);
+    pa_memblock_release(blocks[1]);
+
+    blocks[2] = pa_memblock_new_pool(pool_a, sizeof(txt));
+    x = pa_memblock_acquire(blocks[2]);
+    snprintf(x, pa_memblock_get_length(blocks[2]), "%s", txt);
+    pa_memblock_release(blocks[2]);
+
+    blocks[3] = pa_memblock_new_malloced(pool_a, pa_xstrdup(txt), sizeof(txt));
+    blocks[4] = NULL;
+
+    for (i = 0; blocks[i]; i++) {
+        pa_log("Memory block %u", i);
+
+        mb_a = blocks[i];
+        fail_unless(mb_a != NULL);
+
+        export_a = pa_memexport_new(pool_a, revoke_cb, (void*) "A");
+        fail_unless(export_a != NULL);
+        export_b = pa_memexport_new(pool_b, revoke_cb, (void*) "B");
+        fail_unless(export_b != NULL);
+
+        import_b = pa_memimport_new(pool_b, release_cb, (void*) "B");
+        fail_unless(import_b != NULL);
+        import_c = pa_memimport_new(pool_c, release_cb, (void*) "C");
+        fail_unless(import_b != NULL);
+
+        r = pa_memexport_put(export_a, mb_a, &mem_type, &id, &shm_id, &offset, &size);
+        fail_unless(r >= 0);
+        fail_unless(shm_id == id_a);
+
+        pa_log("A: Memory block exported as %u", id);
+
+        mb_b = pa_memimport_get(import_b, PA_MEM_TYPE_SHARED_POSIX, id, shm_id, offset, size, false);
+        fail_unless(mb_b != NULL);
+        r = pa_memexport_put(export_b, mb_b, &mem_type, &id, &shm_id, &offset, &size);
+        fail_unless(r >= 0);
+        fail_unless(shm_id == id_a || shm_id == id_b);
+        pa_memblock_unref(mb_b);
+
+        pa_log("B: Memory block exported as %u", id);
+
+        mb_c = pa_memimport_get(import_c, PA_MEM_TYPE_SHARED_POSIX, id, shm_id, offset, size, false);
+        fail_unless(mb_c != NULL);
+        x = pa_memblock_acquire(mb_c);
+        pa_log_debug("1 data=%s", x);
+        pa_memblock_release(mb_c);
+
+        print_stats(pool_a, "A");
+        print_stats(pool_b, "B");
+        print_stats(pool_c, "C");
+
+        pa_memexport_free(export_b);
+        x = pa_memblock_acquire(mb_c);
+        pa_log_debug("2 data=%s", x);
+        pa_memblock_release(mb_c);
+        pa_memblock_unref(mb_c);
+
+        pa_memimport_free(import_b);
+
+        pa_memblock_unref(mb_a);
+
+        pa_memimport_free(import_c);
+        pa_memexport_free(export_a);
+    }
+
+    pa_log("vacuuming...");
+
+    pa_mempool_vacuum(pool_a);
+    pa_mempool_vacuum(pool_b);
+    pa_mempool_vacuum(pool_c);
+
+    pa_log("vacuuming done...");
+
+    pa_mempool_unref(pool_a);
+    pa_mempool_unref(pool_b);
+    pa_mempool_unref(pool_c);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("Memblock");
+    tc = tcase_create("memblock");
+    tcase_add_test(tc, memblock_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c
new file mode 100644 (file)
index 0000000..2a9b88a
--- /dev/null
@@ -0,0 +1,661 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include <check.h>
+
+#include <pulsecore/memblockq.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core-util.h>
+
+#include <pulse/xmalloc.h>
+
+static const char *fixed[] = {
+    "1122444411441144__22__11______3333______________________________",
+    "__________________3333__________________________________________"
+};
+static const char *manual[] = {
+    "1122444411441144__22__11______3333______________________________",
+    "__________________3333______________________________"
+};
+
+/*
+ * utility function to create a memchunk
+ */
+static pa_memchunk memchunk_from_str(pa_mempool *p, const char* data)
+{
+    pa_memchunk res;
+    size_t size = strlen(data);
+
+    res.memblock = pa_memblock_new_fixed(p, (void*)data, size, true);
+    ck_assert_ptr_ne(res.memblock, NULL);
+
+    res.index = 0;
+    res.length = pa_memblock_get_length(res.memblock);
+
+    return res;
+}
+
+static void dump_chunk(const pa_memchunk *chunk, pa_strbuf *buf) {
+    size_t n;
+    void *q;
+    char *e;
+
+    fail_unless(chunk != NULL);
+
+    q = pa_memblock_acquire(chunk->memblock);
+    for (e = (char*) q + chunk->index, n = 0; n < chunk->length; n++, e++) {
+        fprintf(stderr, "%c", *e);
+        pa_strbuf_putc(buf, *e);
+    }
+    pa_memblock_release(chunk->memblock);
+}
+
+static void dump(pa_memblockq *bq, int n) {
+    pa_memchunk out;
+    pa_strbuf *buf;
+    char *str;
+
+    pa_assert(bq);
+
+    /* First let's dump this as fixed block */
+    fprintf(stderr, "FIXED >");
+    pa_memblockq_peek_fixed_size(bq, 64, &out);
+    buf = pa_strbuf_new();
+    dump_chunk(&out, buf);
+    pa_memblock_unref(out.memblock);
+    str = pa_strbuf_to_string_free(buf);
+    fail_unless(pa_streq(str, fixed[n]));
+    pa_xfree(str);
+    fprintf(stderr, "<\n");
+
+    /* Then let's dump the queue manually */
+    fprintf(stderr, "MANUAL>");
+
+    buf = pa_strbuf_new();
+    for (;;) {
+        if (pa_memblockq_peek(bq, &out) < 0)
+            break;
+
+        dump_chunk(&out, buf);
+        pa_memblock_unref(out.memblock);
+        pa_memblockq_drop(bq, out.length);
+    }
+    str = pa_strbuf_to_string_free(buf);
+    fail_unless(pa_streq(str, manual[n]));
+    pa_xfree(str);
+    fprintf(stderr, "<\n");
+}
+
+/*
+ * utility function to validate invariants
+ *
+ * The different values like base, maxlength etc follow certain rules.
+ * This convenience function makes sure that changes don't violate
+ * these rules.
+ */
+static void check_queue_invariants(pa_memblockq *bq) {
+    size_t base = pa_memblockq_get_base(bq);
+    size_t maxlength = pa_memblockq_get_maxlength(bq);
+    size_t tlength = pa_memblockq_get_tlength(bq);
+    size_t minreq = pa_memblockq_get_minreq(bq);
+    size_t prebuf = pa_memblockq_get_prebuf(bq);
+    size_t length = pa_memblockq_get_length(bq);
+
+    /* base > zero */
+    ck_assert_int_gt(base, 0);
+
+    /* maxlength multiple of base
+     * maxlength >= base */
+    ck_assert_int_eq(maxlength % base, 0);
+    ck_assert_int_ge(maxlength, base);
+
+    /* tlength multiple of base
+     * tlength >= base
+     * tlength <= maxlength */
+    ck_assert_int_eq(tlength % base, 0);
+    ck_assert_int_ge(tlength, base);
+    ck_assert_int_le(tlength, maxlength);
+
+    /* minreq multiple of base
+     * minreq >= base
+     * minreq <= tlength */
+    ck_assert_int_eq(minreq % base, 0);
+    ck_assert_int_ge(minreq, base);
+    ck_assert_int_le(minreq, tlength);
+
+    /* prebuf multiple of base
+     * prebuf >= 0
+     * prebuf <= tlength + base - minreq
+     * prebuf <= tlength (because minreq >= base) */
+    ck_assert_int_eq(prebuf % base, 0);
+    ck_assert_int_ge(prebuf, 0);
+    ck_assert_int_le(prebuf, tlength + base - minreq);
+    ck_assert_int_le(prebuf, tlength);
+
+    /* length >= 0
+     * length <= maxlength */
+    ck_assert_int_ge(length, 0);
+    ck_assert_int_le(length, maxlength);
+}
+
+START_TEST (memchunk_from_str_test) {
+    pa_mempool *p;
+    pa_memchunk chunk;
+
+    p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true);
+    ck_assert_ptr_ne(p, NULL);
+
+    /* allocate memchunk and check default settings */
+    chunk = memchunk_from_str(p, "abcd");
+    ck_assert_ptr_ne(chunk.memblock, NULL);
+    ck_assert_int_eq(chunk.index, 0);
+    ck_assert_int_eq(chunk.length, 4);
+
+    /* cleanup */
+    pa_memblock_unref(chunk.memblock);
+    pa_mempool_unref(p);
+}
+END_TEST
+
+START_TEST (memblockq_test_initial_properties) {
+    pa_mempool *p;
+    pa_memblockq *bq;
+    pa_memchunk silence;
+    pa_sample_spec ss = {
+        .format = PA_SAMPLE_S32BE,
+        .rate = 48000,
+        .channels = 1
+    };
+    int64_t idx = 0;
+    size_t maxlength = 100;
+    size_t tlength = 20;
+    size_t prebuf = 16;
+    size_t minreq = 8;
+    size_t maxrewind = 40;
+
+    p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true);
+    ck_assert_ptr_ne(p, NULL);
+
+    silence = memchunk_from_str(p, "__");
+
+    bq = pa_memblockq_new("test memblockq", idx, maxlength, tlength, &ss, prebuf, minreq, maxrewind, &silence);
+    fail_unless(bq != NULL);
+
+    /* check initial properties */
+    ck_assert_int_eq(pa_memblockq_is_readable(bq), false);
+    ck_assert_int_eq(pa_memblockq_get_length(bq), 0);
+    ck_assert_int_eq(pa_memblockq_get_maxlength(bq), maxlength);
+    ck_assert_int_eq(pa_memblockq_get_tlength(bq), tlength);
+    ck_assert_int_eq(pa_memblockq_get_prebuf(bq), prebuf);
+    ck_assert_int_eq(pa_memblockq_get_minreq(bq), minreq);
+    ck_assert_int_eq(pa_memblockq_get_maxrewind(bq), maxrewind);
+    ck_assert_int_eq(pa_memblockq_get_base(bq), pa_frame_size(&ss));
+    ck_assert_int_eq(pa_memblockq_get_read_index(bq), 0);
+    ck_assert_int_eq(pa_memblockq_get_write_index(bq), 0);
+
+    check_queue_invariants(bq);
+
+    /* Check reporting of missing bytes:
+     * Initially, tlength bytes are missing. The second call doesn't
+     * report additional missing data since the first call. */
+    ck_assert_int_eq(pa_memblockq_pop_missing(bq), tlength);
+    ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0);
+
+    /* cleanup */
+    pa_memblockq_free(bq);
+    pa_memblock_unref(silence.memblock);
+    pa_mempool_unref(p);
+}
+END_TEST
+
+START_TEST (memblockq_test) {
+    int ret;
+
+    pa_mempool *p;
+    pa_memblockq *bq;
+    pa_memchunk chunk1, chunk2, chunk3, chunk4;
+    pa_memchunk silence;
+    pa_sample_spec ss = {
+        .format = PA_SAMPLE_S16LE,
+        .rate = 48000,
+        .channels = 1
+    };
+
+    p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true);
+    ck_assert_ptr_ne(p, NULL);
+
+    silence = memchunk_from_str(p, "__");
+
+    bq = pa_memblockq_new("test memblockq", 0, 200, 10, &ss, 4, 4, 40, &silence);
+    fail_unless(bq != NULL);
+    check_queue_invariants(bq);
+
+    chunk1 = memchunk_from_str(p, "11");
+    chunk2 = memchunk_from_str(p, "XX22");
+    chunk2.index += 2;
+    chunk2.length -= 2;
+    chunk3 = memchunk_from_str(p, "3333");
+    chunk4 = memchunk_from_str(p, "44444444");
+
+    ret = pa_memblockq_push(bq, &chunk1);
+    fail_unless(ret == 0);
+
+    ret = pa_memblockq_push(bq, &chunk2);
+    fail_unless(ret == 0);
+
+    ret = pa_memblockq_push(bq, &chunk3);
+    fail_unless(ret == 0);
+
+    ret = pa_memblockq_push(bq, &chunk4);
+    fail_unless(ret == 0);
+
+    check_queue_invariants(bq);
+
+    pa_memblockq_seek(bq, -6, 0, true);
+    ret = pa_memblockq_push(bq, &chunk3);
+    fail_unless(ret == 0);
+
+    pa_memblockq_seek(bq, -2, 0, true);
+    ret = pa_memblockq_push(bq, &chunk1);
+    fail_unless(ret == 0);
+
+    pa_memblockq_seek(bq, -10, 0, true);
+    ret = pa_memblockq_push(bq, &chunk4);
+    fail_unless(ret == 0);
+
+    pa_memblockq_seek(bq, 10, 0, true);
+
+    ret = pa_memblockq_push(bq, &chunk1);
+    fail_unless(ret == 0);
+
+    pa_memblockq_seek(bq, -6, 0, true);
+    ret = pa_memblockq_push(bq, &chunk2);
+    fail_unless(ret == 0);
+
+    /* Test splitting */
+    pa_memblockq_seek(bq, -12, 0, true);
+    ret = pa_memblockq_push(bq, &chunk1);
+    fail_unless(ret == 0);
+
+    pa_memblockq_seek(bq, 20, 0, true);
+
+    /* Test merging */
+    ret = pa_memblockq_push(bq, &chunk3);
+    fail_unless(ret == 0);
+    pa_memblockq_seek(bq, -2, 0, true);
+
+    chunk3.index += 2;
+    chunk3.length -= 2;
+    ret = pa_memblockq_push(bq, &chunk3);
+    fail_unless(ret == 0);
+
+    pa_memblockq_seek(bq, 30, PA_SEEK_RELATIVE, true);
+
+    dump(bq, 0);
+
+    pa_memblockq_rewind(bq, 52);
+
+    dump(bq, 1);
+
+    check_queue_invariants(bq);
+
+    pa_memblockq_free(bq);
+    pa_memblock_unref(silence.memblock);
+    pa_memblock_unref(chunk1.memblock);
+    pa_memblock_unref(chunk2.memblock);
+    pa_memblock_unref(chunk3.memblock);
+    pa_memblock_unref(chunk4.memblock);
+
+    pa_mempool_unref(p);
+}
+END_TEST
+
+START_TEST (memblockq_test_length_changes) {
+    pa_mempool *p;
+    pa_memblockq *bq;
+    pa_memchunk silence, data;
+    pa_sample_spec ss = {
+        .format = PA_SAMPLE_S32BE,
+        .rate = 48000,
+        .channels = 1
+    };
+    int64_t idx = 0;
+    size_t maxlength = 60;
+    size_t tlength = 40;
+    size_t prebuf = 16;
+    size_t minreq = 20;
+    size_t maxrewind = 40;
+
+    p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true);
+    ck_assert_ptr_ne(p, NULL);
+
+    silence = memchunk_from_str(p, "____");
+
+    bq = pa_memblockq_new("test memblockq", idx, maxlength, tlength, &ss, prebuf, minreq, maxrewind, &silence);
+    fail_unless(bq != NULL);
+
+    data = memchunk_from_str(p, "12345678");
+
+    /* insert some data */
+    ck_assert_int_eq(pa_memblockq_push(bq, &data), 0);
+    ck_assert_int_eq(pa_memblockq_push(bq, &data), 0);
+    ck_assert_int_eq(pa_memblockq_push(bq, &data), 0);
+    ck_assert_int_eq(pa_memblockq_push(bq, &data), 0);
+
+    /* check state */
+    ck_assert_int_eq(pa_memblockq_get_length(bq), 32);
+
+    /* adjust maximum length
+     * This might modify tlength, prebuf, minreq, too. */
+    pa_memblockq_set_maxlength(bq, maxlength/2);
+    check_queue_invariants(bq);
+
+    /* adjust target length
+     * This might modify minreq, too. */
+    pa_memblockq_set_tlength(bq, tlength/2);
+    check_queue_invariants(bq);
+
+    /* adjust minimum requested length
+     * This might modify prebuf, too. */
+    pa_memblockq_set_minreq(bq, minreq/2);
+    check_queue_invariants(bq);
+
+    /* adjust prebuffer length */
+    pa_memblockq_set_prebuf(bq, prebuf/2);
+    check_queue_invariants(bq);
+
+    /* cleanup */
+    pa_memblockq_free(bq);
+    pa_memblock_unref(silence.memblock);
+    pa_memblock_unref(data.memblock);
+    pa_mempool_unref(p);
+}
+END_TEST
+
+START_TEST (memblockq_test_pop_missing) {
+    pa_mempool *p;
+    pa_memblockq *bq;
+    pa_memchunk silence, data, chunk;
+    pa_sample_spec ss = {
+        .format = PA_SAMPLE_S16BE,
+        .rate = 48000,
+        .channels = 1
+    };
+    int64_t idx = 0;
+    size_t maxlength = 200;
+    size_t tlength = 100;
+    size_t prebuf = 0;
+    size_t minreq = 80;
+    size_t maxrewind = 0;
+
+    p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true);
+    ck_assert_ptr_ne(p, NULL);
+
+    silence = memchunk_from_str(p, "____");
+    data = memchunk_from_str(p, "1234567890");
+
+    bq = pa_memblockq_new("test memblockq", idx, maxlength, tlength, &ss, prebuf, minreq, maxrewind, &silence);
+    fail_unless(bq != NULL);
+
+    /* The following equation regarding the internal variables of a memblockq
+     * is always true:
+     *
+     *   length + missing + requested = tlength
+     *
+     * "length" is the current memblockq length (write index minus read index)
+     * and "tlength" is the target length. The intuitive meaning of "missing"
+     * would be the difference between tlength and length, but actually
+     * "missing" and "requested" together constitute the amount that is missing
+     * from the queue. Writing to the queue decrements "requested" and reading
+     * from the queue increments "missing". pa_memblockq_pop_missing() resets
+     * "missing" to zero, returns the old "missing" value and adds the
+     * equivalent amount to "requested".
+     *
+     * This test has comments between each step documenting the assumed state
+     * of those internal variables. */
+
+    /* length + missing + requested = tlength
+     * 0      + 100     + 0         = 100 */
+
+    ck_assert_int_eq(pa_memblockq_pop_missing(bq), tlength);
+
+    /* length + missing + requested = tlength
+     * 0      + 0       + 100       = 100 */
+
+    for (int i = 0; i != 2; ++i)
+        ck_assert_int_eq(pa_memblockq_push(bq, &data), 0);
+    check_queue_invariants(bq);
+
+    /* length + missing + requested = tlength
+     * 20     + 0       + 80        = 100 */
+
+    ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0);
+
+    /* length + missing + requested = tlength
+     * 20     + 0       + 80        = 100 */
+
+    for (int i = 0; i != 8; ++i)
+        ck_assert_int_eq(pa_memblockq_push(bq, &data), 0);
+    check_queue_invariants(bq);
+
+    /* length + missing + requested = tlength
+     * 100    + 0       + 0         = 100 */
+
+    ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0);
+
+    /* length + missing + requested = tlength
+     * 100    + 0       + 0         = 100 */
+
+    ck_assert_int_eq(pa_memblockq_peek_fixed_size(bq, 40, &chunk), 0);
+    pa_memblockq_drop(bq, 40);
+    ck_assert_int_eq(chunk.length - chunk.index, 40);
+    pa_memblock_unref(chunk.memblock);
+    check_queue_invariants(bq);
+
+    /* length + missing + requested = tlength
+     * 60     + 40      + 0         = 100 */
+
+    /* 40 bytes are missing, but that's less than minreq, so 0 is reported */
+    ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0);
+
+    /* length + missing + requested = tlength
+     * 60     + 40      + 0         = 100 */
+
+    /* Now we push 30 bytes even though it was not requested, so the requested
+     * counter goes negative! */
+    for (int i = 0; i != 3; ++i)
+        ck_assert_int_eq(pa_memblockq_push(bq, &data), 0);
+    check_queue_invariants(bq);
+
+    /* length + missing + requested = tlength
+     * 90     + 40      + -30       = 100 */
+
+    /* missing < minreq, so nothing is reported missing. */
+    ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0);
+
+    /* length + missing + requested = tlength
+     * 90     + 40      + -30       = 100 */
+
+    ck_assert_int_eq(pa_memblockq_peek_fixed_size(bq, 20, &chunk), 0);
+    pa_memblockq_drop(bq, 20);
+    ck_assert_int_eq(chunk.length - chunk.index, 20);
+    pa_memblock_unref(chunk.memblock);
+    check_queue_invariants(bq);
+
+    /* length + missing + requested = tlength
+     * 70     + 60      + -30       = 100 */
+
+    /* missing < minreq, so nothing is reported missing. */
+    ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0);
+
+    /* length + missing + requested = tlength
+     * 70     + 60      + -30       = 100 */
+
+    /* We push more data again even though it was not requested, so the
+     * requested counter goes further into the negative range. */
+    for (int i = 0; i != 5; ++i)
+        ck_assert_int_eq(pa_memblockq_push(bq, &data), 0);
+    check_queue_invariants(bq);
+
+    /* length + missing + requested = tlength
+     * 120    + 60      + -80       = 100 */
+
+    /* missing < minreq, so nothing is reported missing. */
+    ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0);
+
+    /* length + missing + requested = tlength
+     * 120    + 60      + -80       = 100 */
+
+    ck_assert_int_eq(pa_memblockq_peek_fixed_size(bq, 20, &chunk), 0);
+    pa_memblockq_drop(bq, 20);
+    ck_assert_int_eq(chunk.length - chunk.index, 20);
+    pa_memblock_unref(chunk.memblock);
+    check_queue_invariants(bq);
+
+    /* length + missing + requested = tlength
+     * 100    + 80      + -80       = 100 */
+
+    /* missing has now reached the minreq threshold */
+    ck_assert_int_eq(pa_memblockq_pop_missing(bq), 80);
+
+    /* length + missing + requested = tlength
+     * 100    + 0       + 0         = 100 */
+
+    /* cleanup */
+    pa_memblockq_free(bq);
+    pa_memblock_unref(silence.memblock);
+    pa_memblock_unref(data.memblock);
+    pa_mempool_unref(p);
+}
+END_TEST
+
+START_TEST (memblockq_test_tlength_change) {
+    int ret;
+    size_t missing;
+
+    pa_mempool *p;
+    pa_memblockq *bq;
+    pa_memchunk chunk;
+    char buffer[2048];
+    pa_sample_spec ss = {
+        .format = PA_SAMPLE_S16LE,
+        .rate = 48000,
+        .channels = 1
+    };
+
+    pa_log_set_level(PA_LOG_DEBUG);
+
+    bq = pa_memblockq_new("test memblockq", 0, 4096, 2048, &ss, 0, 512, 512, NULL);
+    fail_unless(bq != NULL);
+
+    /* Empty buffer, so expect tlength */
+    missing = pa_memblockq_pop_missing(bq);
+    fail_unless(missing == 2048);
+
+    /* Everything requested, so should be satisfied */
+    missing = pa_memblockq_pop_missing(bq);
+    fail_unless(missing == 0);
+
+    p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true);
+
+    chunk.memblock = pa_memblock_new_fixed(p, buffer, sizeof(buffer), 1);
+    fail_unless(chunk.memblock != NULL);
+
+    chunk.index = 0;
+    chunk.length = sizeof(buffer);
+
+    /* Fill buffer (i.e. satisfy earlier request) */
+    ret = pa_memblockq_push(bq, &chunk);
+    fail_unless(ret == 0);
+
+    /* Should still be happy */
+    missing = pa_memblockq_pop_missing(bq);
+    fail_unless(missing == 0);
+
+    /* Check that we don't request less than minreq */
+    pa_memblockq_drop(bq, 400);
+    missing = pa_memblockq_pop_missing(bq);
+    ck_assert_int_eq(missing, 0);
+
+    missing = pa_memblockq_pop_missing(bq);
+    fail_unless(missing == 0);
+
+    /* Reduce tlength under what's dropped and under previous minreq */
+    pa_memblockq_set_tlength(bq, 256);
+    pa_memblockq_set_minreq(bq, 64);
+
+    /* We are now overbuffered and should not request more */
+    missing = pa_memblockq_pop_missing(bq);
+    fail_unless(missing == 0);
+
+    /* Drop more data so we are below tlength again, but just barely */
+    pa_memblockq_drop(bq, 1400);
+
+    /* Should still honour minreq */
+    missing = pa_memblockq_pop_missing(bq);
+    fail_unless(missing == 0);
+
+    /* Finally drop enough to fall below minreq */
+    pa_memblockq_drop(bq, 80);
+
+    /* And expect a request */
+    missing = pa_memblockq_pop_missing(bq);
+    fail_unless(missing == 88);
+
+    pa_memblockq_free(bq);
+    pa_memblock_unref(chunk.memblock);
+    pa_mempool_unref(p);
+}
+END_TEST
+
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("Memblock Queue");
+    tc = tcase_create("memblockq");
+    tcase_add_test(tc, memchunk_from_str_test);
+    tcase_add_test(tc, memblockq_test_initial_properties);
+    tcase_add_test(tc, memblockq_test);
+    tcase_add_test(tc, memblockq_test_length_changes);
+    tcase_add_test(tc, memblockq_test_pop_missing);
+    tcase_add_test(tc, memblockq_test_tlength_change);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/mix-test.c b/src/tests/mix-test.c
new file mode 100644 (file)
index 0000000..d9255e3
--- /dev/null
@@ -0,0 +1,362 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+
+#include <check.h>
+
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/mix.h>
+
+/* PA_SAMPLE_U8 */
+static const uint8_t u8_result[3][10] = {
+{ 0x00, 0xff, 0x7f, 0x80, 0x9f, 0x3f, 0x01, 0xf0, 0x20, 0x21 },
+{ 0x0c, 0xf2, 0x7f, 0x80, 0x9b, 0x45, 0x0d, 0xe4, 0x29, 0x2a },
+{ 0x00, 0xff, 0x7e, 0x80, 0xba, 0x04, 0x00, 0xff, 0x00, 0x00 },
+};
+
+/* PA_SAMPLE_ALAW */
+static const uint8_t alaw_result[3][10] = {
+{ 0x00, 0xff, 0x7f, 0x80, 0x9f, 0x3f, 0x01, 0xf0, 0x20, 0x21 },
+{ 0x06, 0xf2, 0x72, 0x86, 0x92, 0x32, 0x07, 0xf6, 0x26, 0x27 },
+{ 0x31, 0xec, 0x6d, 0xb1, 0x8c, 0x2d, 0x36, 0xe1, 0x2a, 0x2a },
+};
+
+/* PA_SAMPLE_ULAW */
+static const uint8_t ulaw_result[3][10] = {
+{ 0x00, 0xff, 0x7f, 0x80, 0x9f, 0x3f, 0x01, 0xf0, 0x20, 0x21 },
+{ 0x03, 0xff, 0xff, 0x83, 0xa2, 0x42, 0x04, 0xf2, 0x23, 0x24 },
+{ 0x00, 0xff, 0xff, 0x80, 0x91, 0x31, 0x00, 0xe9, 0x12, 0x13 },
+};
+
+static const uint16_t s16ne_result[3][10] = {
+{ 0x0000, 0xffff, 0x7fff, 0x8000, 0x9fff, 0x3fff, 0x0001, 0xf000, 0x0020, 0x0021 },
+{ 0x0000, 0xffff, 0x7332, 0x8ccd, 0xa998, 0x3998, 0x0000, 0xf199, 0x001c, 0x001d },
+{ 0x0000, 0xfffe, 0x7fff, 0x8000, 0x8000, 0x7997, 0x0001, 0xe199, 0x003c, 0x003e },
+};
+
+static const float float32ne_result[3][10] = {
+{ 0.000000, -1.000000, 1.000000, 4711.000000, 0.222000, 0.330000, -0.300000, 99.000000, -0.555000, -0.123000 },
+{ 0.000000, -0.899987, 0.899987, 4239.837402, 0.199797, 0.296996, -0.269996, 89.098679, -0.499493, -0.110698 },
+{ 0.000000, -1.899987, 1.899987, 8950.837891, 0.421797, 0.626996, -0.569996, 188.098679, -1.054493, -0.233698 },
+};
+
+static const uint32_t s32ne_result[3][10] = {
+{ 0x00000001, 0xffff0002, 0x7fff0003, 0x80000004, 0x9fff0005, 0x3fff0006, 0x00010007, 0xf0000008, 0x00200009, 0x0021000a },
+{ 0x00000000, 0xffff199b, 0x7332199c, 0x8ccd0003, 0xa998d99e, 0x3998999f, 0x0000e66c, 0xf199a007, 0x001cccc8, 0x001db32e },
+{ 0x00000001, 0xfffe199d, 0x7fffffff, 0x80000000, 0x80000000, 0x799799a5, 0x0001e673, 0xe199a00f, 0x003cccd1, 0x003eb338 },
+};
+
+/* attention: result is in BE, not NE! */
+static const uint8_t s24be_result[3][30] = {
+{ 0x00, 0x00, 0x01, 0xff, 0xff, 0x02, 0x7f, 0xff, 0x03, 0x80, 0x00, 0x04, 0x9f, 0xff, 0x05, 0x3f, 0xff, 0x06, 0x01, 0x00, 0x07, 0xf0, 0x00, 0x08, 0x20, 0x00, 0x09, 0x21, 0x00, 0x0a },
+{ 0x00, 0x00, 0x00, 0xff, 0xff, 0x1b, 0x73, 0x32, 0x1c, 0x8c, 0xcd, 0x03, 0xa9, 0x98, 0xde, 0x39, 0x98, 0x9f, 0x00, 0xe6, 0x6c, 0xf1, 0x99, 0xa7, 0x1c, 0xcc, 0xc8, 0x1d, 0xb3, 0x2e },
+{ 0x00, 0x00, 0x01, 0xff, 0xfe, 0x1d, 0x7f, 0xff, 0xff, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x79, 0x97, 0xa5, 0x01, 0xe6, 0x73, 0xe1, 0x99, 0xaf, 0x3c, 0xcc, 0xd1, 0x3e, 0xb3, 0x38 },
+};
+
+static const uint32_t s24_32ne_result[3][10] = {
+{ 0x00000001, 0xffff0002, 0x7fff0003, 0x80000004, 0x9fff0005, 0x3fff0006, 0x00010007, 0xf0000008, 0x00200009, 0x0021000a },
+{ 0x00000000, 0x00ff199b, 0x00ff199c, 0x00000003, 0x00ff199e, 0x00ff199f, 0x0000e66c, 0x00000007, 0x001cccc8, 0x001db32e },
+{ 0x00000001, 0x00fe199d, 0x00fe199f, 0x00000007, 0x00fe19a3, 0x00fe19a5, 0x0001e673, 0x0000000f, 0x003cccd1, 0x003eb338 },
+};
+
+static void compare_block(const pa_sample_spec *ss, const pa_memchunk *chunk, int iter) {
+    void *d;
+    unsigned i;
+
+    d = pa_memblock_acquire(chunk->memblock);
+
+    switch (ss->format) {
+        case PA_SAMPLE_U8: {
+            const uint8_t *v = u8_result[iter];
+            uint8_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                fail_unless(*u == *v, NULL);
+                ++u;
+                ++v;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_ALAW: {
+            const uint8_t *v = alaw_result[iter];
+            uint8_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                fail_unless(*u == *v, NULL);
+                ++u;
+                ++v;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_ULAW: {
+            const uint8_t *v = ulaw_result[iter];
+            uint8_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                fail_unless(*u == *v, NULL);
+                ++u;
+                ++v;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_S16NE:
+        case PA_SAMPLE_S16RE: {
+            const uint16_t *v = s16ne_result[iter];
+            uint16_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                uint16_t uu = PA_MAYBE_UINT16_SWAP(ss->format != PA_SAMPLE_S16NE, *u);
+                fail_unless(uu == *v, NULL);
+                ++u;
+                ++v;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32NE:
+        case PA_SAMPLE_FLOAT32RE: {
+            const float *v = float32ne_result[iter];
+            float *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                float uu = ss->format == PA_SAMPLE_FLOAT32NE ? *u : PA_READ_FLOAT32RE(u);
+                fail_unless(fabsf(uu - *v) <= 1e-6f, NULL);
+                ++u;
+                ++v;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S32RE: {
+            const uint32_t *v = s32ne_result[iter];
+            uint32_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                uint32_t uu = PA_MAYBE_UINT32_SWAP(ss->format != PA_SAMPLE_S32NE, *u);
+                fail_unless(uu == *v, NULL);
+                ++u;
+                ++v;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_S24_32NE:
+        case PA_SAMPLE_S24_32RE: {
+            const uint32_t *v = s24_32ne_result[iter];
+            uint32_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                uint32_t uu = PA_MAYBE_UINT32_SWAP(ss->format != PA_SAMPLE_S24_32NE, *u);
+                fail_unless(uu == *v, NULL);
+                ++u;
+                ++v;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_S24NE:
+        case PA_SAMPLE_S24RE: {
+            const uint8_t *v = s24be_result[iter];
+            uint8_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                uint32_t uu = ss->format == PA_SAMPLE_S24LE ? PA_READ24LE(u) : PA_READ24BE(u);
+                fail_unless(uu == PA_READ24BE(v), NULL);
+
+                u += 3;
+                v += 3;
+            }
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_memblock_release(chunk->memblock);
+}
+
+static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
+    pa_memblock *r;
+    void *d;
+    unsigned i;
+
+    pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10));
+    d = pa_memblock_acquire(r);
+
+    switch (ss->format) {
+
+        case PA_SAMPLE_U8:
+        case PA_SAMPLE_ULAW:
+        case PA_SAMPLE_ALAW: {
+            memcpy(d, u8_result[0], sizeof(u8_result[0]));
+            break;
+        }
+
+        case PA_SAMPLE_S16NE:
+        case PA_SAMPLE_S16RE: {
+            if (ss->format == PA_SAMPLE_S16RE) {
+                uint16_t *u = d;
+                for (i = 0; i < 10; i++)
+                    u[i] = PA_UINT16_SWAP(s16ne_result[0][i]);
+            } else
+                memcpy(d, s16ne_result[0], sizeof(s16ne_result[0]));
+            break;
+        }
+
+        case PA_SAMPLE_S24_32NE:
+        case PA_SAMPLE_S24_32RE:
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S32RE: {
+            if (ss->format == PA_SAMPLE_S24_32RE || ss->format == PA_SAMPLE_S32RE) {
+                uint32_t *u = d;
+                for (i = 0; i < 10; i++)
+                    u[i] = PA_UINT32_SWAP(s32ne_result[0][i]);
+            } else
+                memcpy(d, s32ne_result[0], sizeof(s32ne_result[0]));
+            break;
+        }
+
+        case PA_SAMPLE_S24NE:
+        case PA_SAMPLE_S24RE:
+            if (ss->format == PA_SAMPLE_S24LE) {
+                uint8_t *u = d;
+                for (i = 0; i < 30; i += 3)
+                    PA_WRITE24LE(&u[i], PA_READ24BE(&s24be_result[0][i]));
+            } else
+                memcpy(d, s24be_result[0], sizeof(s24be_result[0]));
+            break;
+
+        case PA_SAMPLE_FLOAT32NE:
+        case PA_SAMPLE_FLOAT32RE: {
+            if (ss->format == PA_SAMPLE_FLOAT32RE) {
+                float *u = d;
+                for (i = 0; i < 10; i++)
+                    PA_WRITE_FLOAT32RE(&u[i], float32ne_result[0][i]);
+            } else
+                memcpy(d, float32ne_result[0], sizeof(float32ne_result[0]));
+
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_memblock_release(r);
+
+    return r;
+}
+
+START_TEST (mix_test) {
+    pa_mempool *pool;
+    pa_sample_spec a;
+    pa_cvolume v;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    fail_unless((pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true)) != NULL, NULL);
+
+    a.channels = 1;
+    a.rate = 44100;
+
+    v.channels = a.channels;
+    v.values[0] = pa_sw_volume_from_linear(0.9);
+
+    for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) {
+        pa_memchunk i, j, k;
+        pa_mix_info m[2];
+        void *ptr;
+
+        pa_log_debug("=== mixing: %s", pa_sample_format_to_string(a.format));
+
+        /* Generate block */
+        i.memblock = generate_block(pool, &a);
+        i.length = pa_memblock_get_length(i.memblock);
+        i.index = 0;
+
+        /* Make a copy */
+        j = i;
+        pa_memblock_ref(j.memblock);
+        pa_memchunk_make_writable(&j, 0);
+
+        /* Adjust volume of the copy */
+        pa_volume_memchunk(&j, &a, &v);
+
+        compare_block(&a, &j, 1);
+
+        m[0].chunk = i;
+        m[0].volume.values[0] = PA_VOLUME_NORM;
+        m[0].volume.channels = a.channels;
+        m[1].chunk = j;
+        m[1].volume.values[0] = PA_VOLUME_NORM;
+        m[1].volume.channels = a.channels;
+
+        k.memblock = pa_memblock_new(pool, i.length);
+        k.length = i.length;
+        k.index = 0;
+
+        ptr = pa_memblock_acquire_chunk(&k);
+        pa_mix(m, 2, ptr, k.length, &a, NULL, false);
+        pa_memblock_release(k.memblock);
+
+        compare_block(&a, &k, 2);
+
+        pa_memblock_unref(i.memblock);
+        pa_memblock_unref(j.memblock);
+        pa_memblock_unref(k.memblock);
+    }
+
+    pa_mempool_unref(pool);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Mix");
+    tc = tcase_create("mix");
+    tcase_add_test(tc, mix_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/mult-s16-test.c b/src/tests/mult-s16-test.c
new file mode 100644 (file)
index 0000000..91740c2
--- /dev/null
@@ -0,0 +1,114 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <pulse/rtclock.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+
+#include "runtime-test-util.h"
+
+static inline int32_t pa_mult_s16_volume_32(int16_t v, int32_t cv) {
+    /* Multiplying the 32 bit volume factor with the
+     * 16 bit sample might result in an 48 bit value. We
+     * want to do without 64 bit integers and hence do
+     * the multiplication independently for the HI and
+     * LO part of the volume. */
+    int32_t hi = cv >> 16;
+    int32_t lo = cv & 0xFFFF;
+    return ((v * lo) >> 16) + (v * hi);
+}
+
+static inline int32_t pa_mult_s16_volume_64(int16_t v, int32_t cv) {
+    /* Multiply with 64 bit integers on 64 bit platforms */
+    return (v * (int64_t) cv) >> 16;
+}
+
+#define SAMPLES 1028
+#define TIMES 10000
+#define TIMES2 100
+
+START_TEST (mult_s16_test) {
+    int16_t samples[SAMPLES];
+    int32_t volumes[SAMPLES];
+    int32_t sum1 = 0, sum2 = 0;
+    int i;
+
+    pa_random(samples, sizeof(samples));
+    pa_random(volumes, sizeof(volumes));
+
+    for (i = 0; i < SAMPLES; i++) {
+        int32_t a = pa_mult_s16_volume_32(samples[i], volumes[i]);
+        int32_t b = pa_mult_s16_volume_64(samples[i], volumes[i]);
+
+        if (a != b) {
+            pa_log_debug("%d: %d != %d", i, a, b);
+            ck_abort();
+        }
+    }
+
+    PA_RUNTIME_TEST_RUN_START("32 bit mult", TIMES, TIMES2) {
+        for (i = 0; i < SAMPLES; i++) {
+            sum1 += pa_mult_s16_volume_32(samples[i], volumes[i]);
+        }
+    } PA_RUNTIME_TEST_RUN_STOP
+
+    PA_RUNTIME_TEST_RUN_START("64 bit mult", TIMES, TIMES2) {
+        for (i = 0; i < SAMPLES; i++)
+            sum2 += pa_mult_s16_volume_64(samples[i], volumes[i]);
+    } PA_RUNTIME_TEST_RUN_STOP
+
+    fail_unless(sum1 == sum2);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+#ifdef HAVE_FAST_64BIT_OPERATIONS
+    pa_log_debug("Detected CPU with fast 64-bit operations.");
+#else
+    pa_log_debug("Not detected CPU with fast 64-bit operations.");
+#endif
+
+    s = suite_create("Mult-s16");
+    tc = tcase_create("mult-s16");
+    tcase_add_test(tc, mult_s16_test);
+    tcase_set_timeout(tc, 120);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/once-test.c b/src/tests/once-test.c
new file mode 100644 (file)
index 0000000..cb56187
--- /dev/null
@@ -0,0 +1,149 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#ifdef HAVE_PTHREAD_SETAFFINITY_NP
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#ifdef __FreeBSD__
+#include <pthread_np.h>
+#endif
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#endif
+#endif
+#endif
+
+#include <check.h>
+
+#include <pulsecore/thread.h>
+#include <pulsecore/once.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/atomic.h>
+#include <pulse/xmalloc.h>
+
+static pa_once once = PA_ONCE_INIT;
+static volatile unsigned n_run = 0;
+static const char * volatile ran_by = NULL;
+#ifdef HAVE_PTHREAD
+static pthread_barrier_t barrier;
+#endif
+static unsigned n_cpu;
+
+#define N_ITERATIONS 500
+#define N_THREADS 100
+
+static void once_func(void) {
+    n_run++;
+    ran_by = (const char*) pa_thread_get_data(pa_thread_self());
+}
+
+static void thread_func(void *data) {
+#ifdef HAVE_PTHREAD
+    int r;
+
+#ifdef HAVE_PTHREAD_SETAFFINITY_NP
+    static pa_atomic_t i_cpu = PA_ATOMIC_INIT(0);
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+    cpuset_t mask;
+#else
+    cpu_set_t mask;
+#endif
+
+    CPU_ZERO(&mask);
+    CPU_SET((size_t) (pa_atomic_inc(&i_cpu) % n_cpu), &mask);
+    fail_unless(pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) == 0);
+#endif
+
+    pa_log_debug("started up: %s", (char *) data);
+
+    r = pthread_barrier_wait(&barrier);
+    fail_unless(r == 0 || r == PTHREAD_BARRIER_SERIAL_THREAD);
+#endif /* HAVE_PTHREAD */
+
+    pa_run_once(&once, once_func);
+}
+
+START_TEST (once_test) {
+    unsigned n, i;
+
+    n_cpu = pa_ncpus();
+
+    for (n = 0; n < N_ITERATIONS; n++) {
+        pa_thread* threads[N_THREADS];
+
+#ifdef HAVE_PTHREAD
+        fail_unless(pthread_barrier_init(&barrier, NULL, N_THREADS) == 0);
+#endif
+
+        /* Yes, kinda ugly */
+        pa_zero(once);
+
+        for (i = 0; i < N_THREADS; i++)
+            threads[i] = pa_thread_new("once", thread_func, pa_sprintf_malloc("Thread #%i", i+1));
+
+        for (i = 0; i < N_THREADS; i++)
+            pa_thread_join(threads[i]);
+
+        fail_unless(n_run == 1);
+        pa_log_info("ran by %s", ran_by);
+
+        for (i = 0; i < N_THREADS; i++) {
+            pa_xfree(pa_thread_get_data(threads[i]));
+            pa_thread_free(threads[i]);
+        }
+
+        n_run = 0;
+        ran_by = NULL;
+
+#ifdef HAVE_PTHREAD
+        fail_unless(pthread_barrier_destroy(&barrier) == 0);
+#endif
+    }
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("Once");
+    tc = tcase_create("once");
+    tcase_add_test(tc, once_test);
+    /* the default timeout is too small,
+     * set it to a reasonable large one.
+     */
+    tcase_set_timeout(tc, 60 * 60);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/pacat-simple.c b/src/tests/pacat-simple.c
new file mode 100644 (file)
index 0000000..113ff95
--- /dev/null
@@ -0,0 +1,114 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <pulse/simple.h>
+#include <pulse/error.h>
+
+#define BUFSIZE 1024
+
+int main(int argc, char*argv[]) {
+
+    /* The Sample format to use */
+    static const pa_sample_spec ss = {
+        .format = PA_SAMPLE_S16LE,
+        .rate = 44100,
+        .channels = 2
+    };
+
+    pa_simple *s = NULL;
+    int ret = 1;
+    int error;
+
+    /* replace STDIN with the specified file if needed */
+    if (argc > 1) {
+        int fd;
+
+        if ((fd = open(argv[1], O_RDONLY)) < 0) {
+            fprintf(stderr, __FILE__": open() failed: %s\n", strerror(errno));
+            goto finish;
+        }
+
+        if (dup2(fd, STDIN_FILENO) < 0) {
+            fprintf(stderr, __FILE__": dup2() failed: %s\n", strerror(errno));
+            goto finish;
+        }
+
+        close(fd);
+    }
+
+    /* Create a new playback stream */
+    if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error))) {
+        fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
+        goto finish;
+    }
+
+    for (;;) {
+        uint8_t buf[BUFSIZE];
+        ssize_t r;
+
+#if 0
+        pa_usec_t latency;
+
+        if ((latency = pa_simple_get_latency(s, &error)) == (pa_usec_t) -1) {
+            fprintf(stderr, __FILE__": pa_simple_get_latency() failed: %s\n", pa_strerror(error));
+            goto finish;
+        }
+
+        fprintf(stderr, "%0.0f usec    \r", (float)latency);
+#endif
+
+        /* Read some data ... */
+        if ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) {
+            if (r == 0) /* EOF */
+                break;
+
+            fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno));
+            goto finish;
+        }
+
+        /* ... and play it */
+        if (pa_simple_write(s, buf, (size_t) r, &error) < 0) {
+            fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
+            goto finish;
+        }
+    }
+
+    /* Make sure that every single sample was played */
+    if (pa_simple_drain(s, &error) < 0) {
+        fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
+        goto finish;
+    }
+
+    ret = 0;
+
+finish:
+
+    if (s)
+        pa_simple_free(s);
+
+    return ret;
+}
diff --git a/src/tests/parec-simple.c b/src/tests/parec-simple.c
new file mode 100644 (file)
index 0000000..338a0e8
--- /dev/null
@@ -0,0 +1,94 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pulse/simple.h>
+#include <pulse/error.h>
+
+#define BUFSIZE 1024
+
+/* A simple routine calling UNIX write() in a loop */
+static ssize_t loop_write(int fd, const void*data, size_t size) {
+    ssize_t ret = 0;
+
+    while (size > 0) {
+        ssize_t r;
+
+        if ((r = write(fd, data, size)) < 0)
+            return r;
+
+        if (r == 0)
+            break;
+
+        ret += r;
+        data = (const uint8_t*) data + r;
+        size -= (size_t) r;
+    }
+
+    return ret;
+}
+
+int main(int argc, char*argv[]) {
+    /* The sample type to use */
+    static const pa_sample_spec ss = {
+        .format = PA_SAMPLE_S16LE,
+        .rate = 44100,
+        .channels = 2
+    };
+    pa_simple *s = NULL;
+    int ret = 1;
+    int error;
+
+    /* Create the recording stream */
+    if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error))) {
+        fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
+        goto finish;
+    }
+
+    for (;;) {
+        uint8_t buf[BUFSIZE];
+
+        /* Record some data ... */
+        if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
+            fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
+            goto finish;
+        }
+
+        /* And write it to STDOUT */
+        if (loop_write(STDOUT_FILENO, buf, sizeof(buf)) != sizeof(buf)) {
+            fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
+            goto finish;
+        }
+    }
+
+    ret = 0;
+
+finish:
+
+    if (s)
+        pa_simple_free(s);
+
+    return ret;
+}
diff --git a/src/tests/proplist-test.c b/src/tests/proplist-test.c
new file mode 100644 (file)
index 0000000..a0074f8
--- /dev/null
@@ -0,0 +1,119 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <check.h>
+
+#include <pulse/proplist.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+
+START_TEST (proplist_test) {
+    pa_modargs *ma;
+    pa_proplist *a, *b, *c, *d;
+    char *s, *t, *u, *v;
+    const char *text;
+    const char *x[] = { "foo", NULL };
+
+    a = pa_proplist_new();
+    fail_unless(pa_proplist_sets(a, PA_PROP_MEDIA_TITLE, "Brandenburgische Konzerte") == 0);
+    fail_unless(pa_proplist_sets(a, PA_PROP_MEDIA_ARTIST, "Johann Sebastian Bach") == 0);
+
+    b = pa_proplist_new();
+    fail_unless(pa_proplist_sets(b, PA_PROP_MEDIA_TITLE, "Goldbergvariationen") == 0);
+    fail_unless(pa_proplist_set(b, PA_PROP_MEDIA_ICON, "\0\1\2\3\4\5\6\7", 8) == 0);
+
+    pa_proplist_update(a, PA_UPDATE_MERGE, b);
+
+    fail_unless(!pa_proplist_gets(a, PA_PROP_MEDIA_ICON));
+
+    pa_log_debug("%s", pa_strnull(pa_proplist_gets(a, PA_PROP_MEDIA_TITLE)));
+    fail_unless(pa_proplist_unset(b, PA_PROP_MEDIA_TITLE) == 0);
+
+    s = pa_proplist_to_string(a);
+    t = pa_proplist_to_string(b);
+    pa_log_debug("---\n%s---\n%s", s, t);
+
+    c = pa_proplist_from_string(s);
+    u = pa_proplist_to_string(c);
+    fail_unless(pa_streq(s, u));
+
+    pa_xfree(s);
+    pa_xfree(t);
+    pa_xfree(u);
+
+    pa_proplist_free(a);
+    pa_proplist_free(b);
+    pa_proplist_free(c);
+
+    text = "  eins = zwei drei = \"\\\"vier\\\"\" fuenf=sechs sieben ='\\a\\c\\h\\t\\'\\\"' neun= hex:0123456789abCDef ";
+
+    pa_log_debug("%s", text);
+    d = pa_proplist_from_string(text);
+    v = pa_proplist_to_string(d);
+    pa_proplist_free(d);
+    pa_log_debug("%s", v);
+    d = pa_proplist_from_string(v);
+    pa_xfree(v);
+    v = pa_proplist_to_string(d);
+    pa_proplist_free(d);
+    pa_log_debug("%s", v);
+    pa_xfree(v);
+
+    ma = pa_modargs_new("foo='foobar=waldo foo2=\"lj\\\"dhflh\" foo3=\"kjlskj\\'\"'", x);
+    fail_unless(ma != NULL);
+    a = pa_proplist_new();
+    fail_unless(a != NULL);
+
+    fail_unless(pa_modargs_get_proplist(ma, "foo", a, PA_UPDATE_REPLACE) >= 0);
+
+    pa_log_debug("%s", v = pa_proplist_to_string(a));
+    pa_xfree(v);
+
+    pa_proplist_free(a);
+    pa_modargs_free(ma);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("Property List");
+    tc = tcase_create("propertylist");
+    tcase_add_test(tc, proplist_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/queue-test.c b/src/tests/queue-test.c
new file mode 100644 (file)
index 0000000..dfdac63
--- /dev/null
@@ -0,0 +1,83 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <check.h>
+
+#include <pulsecore/queue.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+START_TEST (queue_test) {
+    pa_queue *q;
+
+    q = pa_queue_new();
+    fail_unless(q != NULL);
+
+    fail_unless(pa_queue_isempty(q));
+
+    pa_queue_push(q, (void*) "eins");
+    pa_log("%s\n", (char*) pa_queue_pop(q));
+
+    fail_unless(pa_queue_isempty(q));
+
+    pa_queue_push(q, (void*) "zwei");
+    pa_queue_push(q, (void*) "drei");
+    pa_queue_push(q, (void*) "vier");
+
+    pa_log("%s\n", (char*) pa_queue_pop(q));
+    pa_log("%s\n", (char*) pa_queue_pop(q));
+
+    pa_queue_push(q, (void*) "fuenf");
+
+    pa_log("%s\n", (char*) pa_queue_pop(q));
+    pa_log("%s\n", (char*) pa_queue_pop(q));
+
+    fail_unless(pa_queue_isempty(q));
+
+    pa_queue_push(q, (void*) "sechs");
+    pa_queue_push(q, (void*) "sieben");
+
+    pa_queue_free(q, NULL);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Queue");
+    tc = tcase_create("queue");
+    tcase_add_test(tc, queue_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c
new file mode 100644 (file)
index 0000000..0dcc2f1
--- /dev/null
@@ -0,0 +1,106 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/sample.h>
+
+#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/memblock.h>
+
+struct resample_flags {
+    const char *str;
+    pa_resample_flags_t value;
+};
+
+/* Call like this to get an initializer for struct resample_flags:
+ *     RESAMPLE_FLAGS(PA_RESAMPLER_NO_LFE)
+ */
+#define RESAMPLE_FLAGS(flags) { .str = #flags, .value = (flags) }
+
+
+int main(int argc, char *argv[]) {
+
+    static const pa_channel_map maps[] = {
+        { 1, { PA_CHANNEL_POSITION_MONO } },
+        { 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } },
+        { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER } },
+        { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_LFE } },
+        { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_CENTER } },
+        { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE } },
+        { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_REAR_CENTER } },
+        { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT } },
+        { 5, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_CENTER } },
+        { 5, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE } },
+        { 6, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE, PA_CHANNEL_POSITION_CENTER } },
+        { 8, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT } },
+        { 0, { 0 } }
+    };
+
+    static const struct resample_flags flag_sets[] = {
+        RESAMPLE_FLAGS(0),
+        RESAMPLE_FLAGS(PA_RESAMPLER_NO_REMAP),
+        RESAMPLE_FLAGS(PA_RESAMPLER_NO_REMIX),
+        RESAMPLE_FLAGS(PA_RESAMPLER_NO_LFE),
+        RESAMPLE_FLAGS(PA_RESAMPLER_NO_FILL_SINK),
+        RESAMPLE_FLAGS(PA_RESAMPLER_NO_LFE | PA_RESAMPLER_NO_FILL_SINK),
+        { .str = NULL, .value = 0 },
+    };
+
+    unsigned i, j, k;
+    pa_mempool *pool;
+    unsigned crossover_freq = 120;
+
+    pa_log_set_level(PA_LOG_DEBUG);
+
+    pa_assert_se(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true));
+
+    for (i = 0; maps[i].channels > 0; i++)
+        for (j = 0; maps[j].channels > 0; j++) {
+            char a[PA_CHANNEL_MAP_SNPRINT_MAX], b[PA_CHANNEL_MAP_SNPRINT_MAX];
+            pa_resampler *r;
+            pa_sample_spec ss1, ss2;
+
+            ss1.channels = maps[i].channels;
+            ss2.channels = maps[j].channels;
+
+            ss1.rate = ss2.rate = 44100;
+            ss1.format = ss2.format = PA_SAMPLE_S16NE;
+
+            for (k = 0; flag_sets[k].str; k++) {
+                pa_log_info("Converting from '%s' to '%s' with flags %s.", pa_channel_map_snprint(a, sizeof(a), &maps[i]),
+                            pa_channel_map_snprint(b, sizeof(b), &maps[j]), flag_sets[k].str);
+
+                r = pa_resampler_new(pool, &ss1, &maps[i], &ss2, &maps[j], crossover_freq, PA_RESAMPLER_AUTO,
+                                     flag_sets[k].value);
+
+                /* We don't really care for the resampler. We just want to
+                 * see the remixing debug output. */
+
+                pa_resampler_free(r);
+            }
+        }
+
+    pa_mempool_unref(pool);
+
+    return 0;
+}
diff --git a/src/tests/resampler-test.c b/src/tests/resampler-test.c
new file mode 100644 (file)
index 0000000..40000d5
--- /dev/null
@@ -0,0 +1,479 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <getopt.h>
+#include <locale.h>
+
+#include <pulse/pulseaudio.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/log.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-util.h>
+
+static void dump_block(const char *label, const pa_sample_spec *ss, const pa_memchunk *chunk) {
+    void *d;
+    unsigned i;
+
+    if (getenv("MAKE_CHECK"))
+        return;
+    printf("%s:  \t", label);
+
+    d = pa_memblock_acquire(chunk->memblock);
+
+    switch (ss->format) {
+
+        case PA_SAMPLE_U8:
+        case PA_SAMPLE_ULAW:
+        case PA_SAMPLE_ALAW: {
+            uint8_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("      0x%02x ", *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_S16NE:
+        case PA_SAMPLE_S16RE: {
+            uint16_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("    0x%04x ", *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S32RE: {
+            uint32_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("0x%08x ", *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_S24_32NE:
+        case PA_SAMPLE_S24_32RE: {
+            uint32_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("0x%08x ", *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32NE:
+        case PA_SAMPLE_FLOAT32RE: {
+            float *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                printf("%4.3g ", ss->format == PA_SAMPLE_FLOAT32NE ? *u : PA_READ_FLOAT32RE(u));
+                u++;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_S24LE:
+        case PA_SAMPLE_S24BE: {
+            uint8_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                printf("  0x%06x ", PA_READ24NE(u));
+                u += pa_frame_size(ss);
+            }
+
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    printf("\n");
+
+    pa_memblock_release(chunk->memblock);
+}
+
+static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
+    pa_memblock *r;
+    void *d;
+    unsigned i;
+
+    pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10));
+    d = pa_memblock_acquire(r);
+
+    switch (ss->format) {
+
+        case PA_SAMPLE_U8:
+        case PA_SAMPLE_ULAW:
+        case PA_SAMPLE_ALAW: {
+            uint8_t *u = d;
+
+            u[0] = 0x00;
+            u[1] = 0xFF;
+            u[2] = 0x7F;
+            u[3] = 0x80;
+            u[4] = 0x9f;
+            u[5] = 0x3f;
+            u[6] = 0x1;
+            u[7] = 0xF0;
+            u[8] = 0x20;
+            u[9] = 0x21;
+            break;
+        }
+
+        case PA_SAMPLE_S16NE:
+        case PA_SAMPLE_S16RE: {
+            uint16_t *u = d;
+
+            u[0] = 0x0000;
+            u[1] = 0xFFFF;
+            u[2] = 0x7FFF;
+            u[3] = 0x8000;
+            u[4] = 0x9fff;
+            u[5] = 0x3fff;
+            u[6] = 0x1;
+            u[7] = 0xF000;
+            u[8] = 0x20;
+            u[9] = 0x21;
+            break;
+        }
+
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S32RE: {
+            uint32_t *u = d;
+
+            u[0] = 0x00000001;
+            u[1] = 0xFFFF0002;
+            u[2] = 0x7FFF0003;
+            u[3] = 0x80000004;
+            u[4] = 0x9fff0005;
+            u[5] = 0x3fff0006;
+            u[6] =    0x10007;
+            u[7] = 0xF0000008;
+            u[8] =   0x200009;
+            u[9] =   0x21000A;
+            break;
+        }
+
+        case PA_SAMPLE_S24_32NE:
+        case PA_SAMPLE_S24_32RE: {
+            uint32_t *u = d;
+
+            u[0] = 0x000001;
+            u[1] = 0xFF0002;
+            u[2] = 0x7F0003;
+            u[3] = 0x800004;
+            u[4] = 0x9f0005;
+            u[5] = 0x3f0006;
+            u[6] =    0x107;
+            u[7] = 0xF00008;
+            u[8] =   0x2009;
+            u[9] =   0x210A;
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32NE:
+        case PA_SAMPLE_FLOAT32RE: {
+            float *u = d;
+
+            u[0] = 0.0f;
+            u[1] = -1.0f;
+            u[2] = 1.0f;
+            u[3] = 4711.0f;
+            u[4] = 0.222f;
+            u[5] = 0.33f;
+            u[6] = -.3f;
+            u[7] = 99.0f;
+            u[8] = -0.555f;
+            u[9] = -.123f;
+
+            if (ss->format == PA_SAMPLE_FLOAT32RE)
+                for (i = 0; i < 10; i++)
+                    PA_WRITE_FLOAT32RE(&u[i], u[i]);
+
+            break;
+        }
+
+        case PA_SAMPLE_S24NE:
+        case PA_SAMPLE_S24RE: {
+            uint8_t *u = d;
+
+            PA_WRITE24NE(u,    0x000001);
+            PA_WRITE24NE(u+3,  0xFF0002);
+            PA_WRITE24NE(u+6,  0x7F0003);
+            PA_WRITE24NE(u+9,  0x800004);
+            PA_WRITE24NE(u+12, 0x9f0005);
+            PA_WRITE24NE(u+15, 0x3f0006);
+            PA_WRITE24NE(u+18,    0x107);
+            PA_WRITE24NE(u+21, 0xF00008);
+            PA_WRITE24NE(u+24,   0x2009);
+            PA_WRITE24NE(u+27,   0x210A);
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_memblock_release(r);
+
+    return r;
+}
+
+static void help(const char *argv0) {
+    printf("%s [options]\n\n"
+           "-h, --help                            Show this help\n"
+           "-v, --verbose                         Print debug messages\n"
+           "      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to 44100)\n"
+           "      --from-format=SAMPLEFORMAT      From sample type (defaults to s16le)\n"
+           "      --from-channels=CHANNELS        From number of channels (defaults to 1)\n"
+           "      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to 44100)\n"
+           "      --to-format=SAMPLEFORMAT        To sample type (defaults to s16le)\n"
+           "      --to-channels=CHANNELS          To number of channels (defaults to 1)\n"
+           "      --resample-method=METHOD        Resample method (defaults to auto)\n"
+           "      --seconds=SECONDS               From stream duration (defaults to 60)\n"
+           "\n"
+           "If the formats are not specified, the test performs all formats combinations,\n"
+           "back and forth.\n"
+           "\n"
+           "Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, alaw,\n"
+           "s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+           "\n"
+           "See --dump-resample-methods for possible values of resample methods.\n",
+           argv0);
+}
+
+enum {
+    ARG_VERSION = 256,
+    ARG_FROM_SAMPLERATE,
+    ARG_FROM_SAMPLEFORMAT,
+    ARG_FROM_CHANNELS,
+    ARG_TO_SAMPLERATE,
+    ARG_TO_SAMPLEFORMAT,
+    ARG_TO_CHANNELS,
+    ARG_SECONDS,
+    ARG_RESAMPLE_METHOD,
+    ARG_DUMP_RESAMPLE_METHODS
+};
+
+static void dump_resample_methods(void) {
+    int i;
+
+    for (i = 0; i < PA_RESAMPLER_MAX; i++)
+        if (pa_resample_method_supported(i))
+            printf("%s\n", pa_resample_method_to_string(i));
+
+}
+
+int main(int argc, char *argv[]) {
+    pa_mempool *pool = NULL;
+    pa_sample_spec a, b;
+    int ret = 1, c;
+    bool all_formats = true;
+    pa_resample_method_t method;
+    int seconds;
+    unsigned crossover_freq = 120;
+
+    static const struct option long_options[] = {
+        {"help",                  0, NULL, 'h'},
+        {"verbose",               0, NULL, 'v'},
+        {"version",               0, NULL, ARG_VERSION},
+        {"from-rate",             1, NULL, ARG_FROM_SAMPLERATE},
+        {"from-format",           1, NULL, ARG_FROM_SAMPLEFORMAT},
+        {"from-channels",         1, NULL, ARG_FROM_CHANNELS},
+        {"to-rate",               1, NULL, ARG_TO_SAMPLERATE},
+        {"to-format",             1, NULL, ARG_TO_SAMPLEFORMAT},
+        {"to-channels",           1, NULL, ARG_TO_CHANNELS},
+        {"seconds",               1, NULL, ARG_SECONDS},
+        {"resample-method",       1, NULL, ARG_RESAMPLE_METHOD},
+        {"dump-resample-methods", 0, NULL, ARG_DUMP_RESAMPLE_METHODS},
+        {NULL,                    0, NULL, 0}
+    };
+
+    setlocale(LC_ALL, "");
+#ifdef ENABLE_NLS
+    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
+#endif
+
+    pa_log_set_level(PA_LOG_WARN);
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_INFO);
+
+    a.channels = b.channels = 1;
+    a.rate = b.rate = 44100;
+    a.format = b.format = PA_SAMPLE_S16LE;
+
+    method = PA_RESAMPLER_AUTO;
+    seconds = 60;
+
+    while ((c = getopt_long(argc, argv, "hv", long_options, NULL)) != -1) {
+
+        switch (c) {
+            case 'h' :
+                help(argv[0]);
+                ret = 0;
+                goto quit;
+
+            case 'v':
+                pa_log_set_level(PA_LOG_DEBUG);
+                break;
+
+            case ARG_VERSION:
+                printf("%s %s\n", argv[0], PACKAGE_VERSION);
+                ret = 0;
+                goto quit;
+
+            case ARG_DUMP_RESAMPLE_METHODS:
+                dump_resample_methods();
+                ret = 0;
+                goto quit;
+
+            case ARG_FROM_CHANNELS:
+                a.channels = (uint8_t) atoi(optarg);
+                break;
+
+            case ARG_FROM_SAMPLEFORMAT:
+                a.format = pa_parse_sample_format(optarg);
+                all_formats = false;
+                break;
+
+            case ARG_FROM_SAMPLERATE:
+                a.rate = (uint32_t) atoi(optarg);
+                break;
+
+            case ARG_TO_CHANNELS:
+                b.channels = (uint8_t) atoi(optarg);
+                break;
+
+            case ARG_TO_SAMPLEFORMAT:
+                b.format = pa_parse_sample_format(optarg);
+                all_formats = false;
+                break;
+
+            case ARG_TO_SAMPLERATE:
+                b.rate = (uint32_t) atoi(optarg);
+                break;
+
+            case ARG_SECONDS:
+                seconds = atoi(optarg);
+                break;
+
+            case ARG_RESAMPLE_METHOD:
+                if (*optarg == '\0' || pa_streq(optarg, "help")) {
+                    dump_resample_methods();
+                    ret = 0;
+                    goto quit;
+                }
+                method = pa_parse_resample_method(optarg);
+                break;
+
+            default:
+                goto quit;
+        }
+    }
+
+    ret = 0;
+    pa_assert_se(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true));
+
+    if (!all_formats) {
+
+        pa_resampler *resampler;
+        pa_memchunk i, j;
+        pa_usec_t ts;
+
+        pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS);
+        pa_log_debug("=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)", seconds,
+                   a.rate, a.channels, pa_sample_format_to_string(a.format),
+                   b.rate, b.channels, pa_sample_format_to_string(b.format));
+
+        ts = pa_rtclock_now();
+        pa_assert_se(resampler = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0));
+        pa_log_info("init: %llu", (long long unsigned)(pa_rtclock_now() - ts));
+
+        i.memblock = pa_memblock_new(pool, pa_usec_to_bytes(1*PA_USEC_PER_SEC, &a));
+
+        ts = pa_rtclock_now();
+        i.length = pa_memblock_get_length(i.memblock);
+        i.index = 0;
+        while (seconds--) {
+            pa_resampler_run(resampler, &i, &j);
+            if (j.memblock)
+                pa_memblock_unref(j.memblock);
+        }
+        pa_log_info("resampling: %llu", (long long unsigned)(pa_rtclock_now() - ts));
+        pa_memblock_unref(i.memblock);
+
+        pa_resampler_free(resampler);
+
+        goto quit;
+    }
+
+    for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) {
+        for (b.format = 0; b.format < PA_SAMPLE_MAX; b.format ++) {
+            pa_resampler *forth, *back;
+            pa_memchunk i, j, k;
+
+            pa_log_debug("=== %s -> %s -> %s -> /2",
+                       pa_sample_format_to_string(a.format),
+                       pa_sample_format_to_string(b.format),
+                       pa_sample_format_to_string(a.format));
+
+            pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0));
+            pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, crossover_freq, method, 0));
+
+            i.memblock = generate_block(pool, &a);
+            i.length = pa_memblock_get_length(i.memblock);
+            i.index = 0;
+            pa_resampler_run(forth, &i, &j);
+            pa_resampler_run(back, &j, &k);
+
+            dump_block("before", &a, &i);
+            dump_block("after", &b, &j);
+            dump_block("reverse", &a, &k);
+
+            pa_memblock_unref(i.memblock);
+            pa_memblock_unref(j.memblock);
+            pa_memblock_unref(k.memblock);
+
+            pa_resampler_free(forth);
+            pa_resampler_free(back);
+        }
+    }
+
+ quit:
+    if (pool)
+        pa_mempool_unref(pool);
+
+    return ret;
+}
diff --git a/src/tests/rtpoll-test.c b/src/tests/rtpoll-test.c
new file mode 100644 (file)
index 0000000..24c6619
--- /dev/null
@@ -0,0 +1,106 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+#include <signal.h>
+
+#include <pulsecore/poll.h>
+#include <pulsecore/log.h>
+#include <pulsecore/rtpoll.h>
+
+static int before(pa_rtpoll_item *i) {
+    pa_log("before");
+    return 0;
+}
+
+static void after(pa_rtpoll_item *i) {
+    pa_log("after");
+}
+
+static int worker(pa_rtpoll_item *w) {
+    pa_log("worker");
+    return 0;
+}
+
+START_TEST (rtpoll_test) {
+    pa_rtpoll *p;
+    pa_rtpoll_item *i, *w;
+    struct pollfd *pollfd;
+
+    p = pa_rtpoll_new();
+
+    i = pa_rtpoll_item_new(p, PA_RTPOLL_EARLY, 1);
+    pa_rtpoll_item_set_before_callback(i, before);
+    pa_rtpoll_item_set_after_callback(i, after);
+
+    pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+    pollfd->fd = 0;
+    pollfd->events = POLLIN;
+
+    w = pa_rtpoll_item_new(p, PA_RTPOLL_NORMAL, 0);
+    pa_rtpoll_item_set_before_callback(w, worker);
+
+    pa_rtpoll_set_timer_relative(p, 10000000); /* 10 s */
+
+    fail_unless(pa_rtpoll_run(p) >= 0);
+
+    pa_rtpoll_item_free(i);
+
+    i = pa_rtpoll_item_new(p, PA_RTPOLL_EARLY, 1);
+    pa_rtpoll_item_set_before_callback(i, before);
+    pa_rtpoll_item_set_after_callback(i, after);
+
+    pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+    pollfd->fd = 0;
+    pollfd->events = POLLIN;
+
+    fail_unless(pa_rtpoll_run(p) >= 0);
+
+    pa_rtpoll_item_free(i);
+
+    pa_rtpoll_item_free(w);
+
+    pa_rtpoll_free(p);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("RT Poll");
+    tc = tcase_create("rtpoll");
+    tcase_add_test(tc, rtpoll_test);
+    /* the default timeout is too small,
+     * set it to a reasonable large one.
+     */
+    tcase_set_timeout(tc, 60 * 60);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c
new file mode 100644 (file)
index 0000000..7bd8808
--- /dev/null
@@ -0,0 +1,128 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <inttypes.h>
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#ifdef HAVE_PTHREAD_SETAFFINITY_NP
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#ifdef __FreeBSD__
+#include <pthread_np.h>
+#endif
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#endif
+#endif
+#endif
+
+#include <pulse/util.h>
+#include <pulse/timeval.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-rtclock.h>
+
+static int msec_lower, msec_upper;
+
+static void work(void *p) PA_GCC_NORETURN;
+
+static void work(void *p) {
+
+    pa_log_notice("CPU%i: Created thread.", PA_PTR_TO_UINT(p));
+
+    pa_make_realtime(12);
+
+#ifdef HAVE_PTHREAD_SETAFFINITY_NP
+{
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+    cpuset_t mask;
+#else
+    cpu_set_t mask;
+#endif
+
+    CPU_ZERO(&mask);
+    CPU_SET((size_t) PA_PTR_TO_UINT(p), &mask);
+    pa_assert_se(pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) == 0);
+}
+#endif
+
+    for (;;) {
+        struct timeval now, end;
+        uint64_t usec;
+
+        pa_log_notice("CPU%i: Sleeping for 1s", PA_PTR_TO_UINT(p));
+        pa_msleep(1000);
+
+        usec =
+            (uint64_t) ((((double) rand())*(double)(msec_upper-msec_lower)*PA_USEC_PER_MSEC)/RAND_MAX) +
+            (uint64_t) ((uint64_t) msec_lower*PA_USEC_PER_MSEC);
+
+        pa_log_notice("CPU%i: Freezing for %ims", PA_PTR_TO_UINT(p), (int) (usec/PA_USEC_PER_MSEC));
+
+        pa_rtclock_get(&end);
+        pa_timeval_add(&end, usec);
+
+        do {
+            pa_rtclock_get(&now);
+        } while (pa_timeval_cmp(&now, &end) < 0);
+    }
+}
+
+int main(int argc, char*argv[]) {
+    unsigned n;
+
+    pa_log_set_level(PA_LOG_INFO);
+
+    srand((unsigned) time(NULL));
+
+    if (argc >= 3) {
+        msec_lower = atoi(argv[1]);
+        msec_upper = atoi(argv[2]);
+    } else if (argc >= 2) {
+        msec_lower = 0;
+        msec_upper = atoi(argv[1]);
+    } else {
+        msec_lower = 0;
+        msec_upper = 1000;
+    }
+
+    pa_assert(msec_upper > 0);
+    pa_assert(msec_upper >= msec_lower);
+
+    pa_log_notice("Creating random latencies in the range of %ims to %ims.", msec_lower, msec_upper);
+
+    for (n = 1; n < pa_ncpus(); n++) {
+        pa_assert_se(pa_thread_new("rtstutter", work, PA_UINT_TO_PTR(n)));
+    }
+
+    work(PA_INT_TO_PTR(0));
+
+    return 0;
+}
diff --git a/src/tests/runtime-test-util.h b/src/tests/runtime-test-util.h
new file mode 100644 (file)
index 0000000..7d3443a
--- /dev/null
@@ -0,0 +1,56 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+***/
+
+#ifndef fooruntimetestutilhfoo
+#define fooruntimetestutilhfoo
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+
+#include <pulsecore/macro.h>
+#include <pulse/rtclock.h>
+
+#define PA_RUNTIME_TEST_RUN_START(l, t1, t2)                    \
+{                                                               \
+    int _j, _k;                                                 \
+    int _times = (t1), _times2 = (t2);                          \
+    pa_usec_t _start, _stop;                                    \
+    pa_usec_t _min = INT_MAX, _max = 0;                         \
+    double _s1 = 0, _s2 = 0;                                    \
+    const char *_label = (l);                                   \
+                                                                \
+    for (_k = 0; _k < _times2; _k++) {                          \
+        _start = pa_rtclock_now();                              \
+        for (_j = 0; _j < _times; _j++)
+
+#define PA_RUNTIME_TEST_RUN_STOP                                \
+        _stop = pa_rtclock_now();                               \
+                                                                \
+        if (_min > (_stop - _start)) _min = _stop - _start;     \
+        if (_max < (_stop - _start)) _max = _stop - _start;     \
+        _s1 += _stop - _start;                                  \
+        _s2 += (_stop - _start) * (_stop - _start);             \
+    }                                                           \
+    pa_log_debug("%s: %llu usec (avg: %g, min = %llu, max = %llu, stddev = %g).", _label, \
+            (long long unsigned int)_s1,                        \
+            ((double)_s1 / _times2),                            \
+            (long long unsigned int)_min,                       \
+            (long long unsigned int)_max,                       \
+            sqrt(_times2 * _s2 - _s1 * _s1) / _times2);         \
+}
+
+#endif
diff --git a/src/tests/sig2str-test.c b/src/tests/sig2str-test.c
new file mode 100644 (file)
index 0000000..f1d82d8
--- /dev/null
@@ -0,0 +1,127 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <signal.h>
+#include <stdio.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+static const char *names[] = {
+    "SIG-1",
+    "SIG0",
+    "SIGHUP",
+    "SIGINT",
+    "SIGQUIT",
+    "SIGULL",
+    "SIGTRAP",
+    "SIGABRT",
+    "SIGBUS",
+    "SIGFPE",
+    "SIGKILL",
+    "SIGUSR1",
+    "SIGSEGV",
+    "SIGUSR2",
+    "SIGPIPE",
+    "SIGALRM",
+    "SIGTERM",
+    "SIGSTKFLT",
+    "SIGCHLD",
+    "SIGCONT",
+    "SIGSTOP",
+    "SIGTSTP",
+    "SIGTTIN",
+    "SIGTTOU",
+    "SIGURG",
+    "SIGXCPU",
+    "SIGXFSZ",
+    "SIGVTALRM",
+    "SIGPROF",
+    "SIGWINCH",
+    "SIGIO",
+    "SIGPWR",
+    "SIGSYS",
+    "SIG32",
+    "SIG33",
+    "SIGRTMIN+0",
+    "SIGRTMIN+1",
+    "SIGRTMIN+2",
+    "SIGRTMIN+3",
+    "SIGRTMIN+4",
+    "SIGRTMIN+5",
+    "SIGRTMIN+6",
+    "SIGRTMIN+7",
+    "SIGRTMIN+8",
+    "SIGRTMIN+9",
+    "SIGRTMIN+10",
+    "SIGRTMIN+11",
+    "SIGRTMIN+12",
+    "SIGRTMIN+13",
+    "SIGRTMIN+14",
+    "SIGRTMIN+15",
+    "SIGRTMIN+16",
+    "SIGRTMIN+17",
+    "SIGRTMIN+18",
+    "SIGRTMIN+19",
+    "SIGRTMIN+20",
+    "SIGRTMIN+21",
+    "SIGRTMIN+22",
+    "SIGRTMIN+23",
+    "SIGRTMIN+24",
+    "SIGRTMIN+25",
+    "SIGRTMIN+26",
+    "SIGRTMIN+27",
+    "SIGRTMIN+28",
+    "SIGRTMIN+29",
+    "SIGRTMIN+30",
+    "SIG65"
+};
+
+START_TEST (sig2str_test) {
+    int sig;
+
+    for (sig = -1; sig <= NSIG; sig++) {
+        printf("%i = %s\n", sig, pa_sig2str(sig));
+        fail_unless(pa_streq(pa_sig2str(sig), names[sig+1]));
+    }
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Signal String");
+    tc = tcase_create("sig2str");
+    tcase_add_test(tc, sig2str_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/sigbus-test.c b/src/tests/sigbus-test.c
new file mode 100644 (file)
index 0000000..dbfc3d1
--- /dev/null
@@ -0,0 +1,92 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <check.h>
+
+#include <pulsecore/memtrap.h>
+#include <pulsecore/core-util.h>
+
+START_TEST (sigbus_test) {
+    void *p;
+    int fd;
+    pa_memtrap *m;
+    const size_t page_size = pa_page_size();
+
+    pa_log_set_level(PA_LOG_DEBUG);
+    pa_memtrap_install();
+
+    /* Create the memory map */
+    fail_unless((fd = open("sigbus-test-map", O_RDWR|O_TRUNC|O_CREAT, 0660)) >= 0);
+    fail_unless(unlink("sigbus-test-map") == 0);
+    fail_unless(ftruncate(fd, page_size) >= 0);
+    fail_unless((p = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) != MAP_FAILED);
+
+    /* Register memory map */
+    m = pa_memtrap_add(p, page_size);
+
+    /* Use memory map */
+    pa_snprintf(p, page_size, "This is a test that should work fine.");
+
+    /* Verify memory map */
+    pa_log("Let's see if this worked: %s", (char*) p);
+    pa_log("And memtrap says it is good: %s", pa_yes_no(pa_memtrap_is_good(m)));
+    fail_unless(pa_memtrap_is_good(m) == true);
+
+    /* Invalidate mapping */
+    fail_unless(ftruncate(fd, 0) >= 0);
+
+    /* Use memory map */
+    pa_snprintf(p, page_size, "This is a test that should fail but get caught.");
+
+    /* Verify memory map */
+    pa_log("Let's see if this worked: %s", (char*) p);
+    pa_log("And memtrap says it is good: %s", pa_yes_no(pa_memtrap_is_good(m)));
+    fail_unless(pa_memtrap_is_good(m) == false);
+
+    pa_memtrap_remove(m);
+    munmap(p, page_size);
+    close(fd);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Sig Bus");
+    tc = tcase_create("sigbus");
+    tcase_add_test(tc, sigbus_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/smoother-test.c b/src/tests/smoother-test.c
new file mode 100644 (file)
index 0000000..5e12f11
--- /dev/null
@@ -0,0 +1,104 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <check.h>
+
+#include <pulse/timeval.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/time-smoother.h>
+
+START_TEST (smoother_test) {
+    pa_usec_t x;
+    unsigned u = 0;
+    pa_smoother *s;
+    int m;
+
+/*     unsigned msec[] = { */
+/*         200, 200, */
+/*         300, 320, */
+/*         400, 400, */
+/*         500, 480, */
+/*         0, 0 */
+/*     }; */
+
+    int msec[200];
+
+    srand(0);
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    for (m = 0, u = 0; u < PA_ELEMENTSOF(msec); u+= 2) {
+
+        msec[u] = m+1 + (rand() % 100) - 50;
+        msec[u+1] = m + (rand() % 2000) - 1000   + 5000;
+
+        m += rand() % 100;
+
+        if (msec[u] < 0)
+            msec[u] = 0;
+
+        if (msec[u+1] < 0)
+            msec[u+1] = 0;
+    }
+
+    s = pa_smoother_new(700*PA_USEC_PER_MSEC, 2000*PA_USEC_PER_MSEC, false, true, 6, 0, true);
+
+    for (x = 0, u = 0; x < PA_USEC_PER_SEC * 10; x += PA_USEC_PER_MSEC) {
+
+        while (u < PA_ELEMENTSOF(msec) && (pa_usec_t) msec[u]*PA_USEC_PER_MSEC < x) {
+            pa_smoother_put(s, (pa_usec_t) msec[u] * PA_USEC_PER_MSEC, (pa_usec_t) msec[u+1] * PA_USEC_PER_MSEC);
+            pa_log_debug("%i\t\t%i", msec[u],  msec[u+1]);
+            u += 2;
+
+            if (u < PA_ELEMENTSOF(msec))
+                pa_smoother_resume(s, (pa_usec_t) msec[u] * PA_USEC_PER_MSEC, true);
+        }
+
+        pa_log_debug("%llu\t%llu", (unsigned long long) (x/PA_USEC_PER_MSEC), (unsigned long long) (pa_smoother_get(s, x)/PA_USEC_PER_MSEC));
+    }
+
+    pa_smoother_free(s);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Smoother");
+    tc = tcase_create("smoother");
+    tcase_add_test(tc, smoother_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/srbchannel-test.c b/src/tests/srbchannel-test.c
new file mode 100644 (file)
index 0000000..0e7b0ce
--- /dev/null
@@ -0,0 +1,145 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 David Henningsson, Canonical Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <check.h>
+
+#include <pulse/mainloop.h>
+#include <pulsecore/packet.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/memblock.h>
+
+static unsigned packets_received;
+static unsigned packets_checksum;
+static size_t packets_length;
+
+static void packet_received(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
+    const uint8_t *pdata;
+    size_t plen;
+    unsigned i;
+
+    pdata = pa_packet_data(packet, &plen);
+    fail_unless(packets_length == plen);
+
+    packets_received++;
+    for (i = 0; i < plen; i++)
+        packets_checksum += pdata[i];
+}
+
+static void packet_test(unsigned npackets, size_t plength, pa_mainloop *ml, pa_pstream *p1, pa_pstream *p2) {
+    pa_packet *packet = pa_packet_new(plength);
+    unsigned i;
+    unsigned psum = 0, totalsum = 0;
+    uint8_t *pdata;
+    size_t plen;
+
+    pa_log_info("Sending %d packets of length %zd", npackets, plength);
+    packets_received = 0;
+    packets_checksum = 0;
+    packets_length = plength;
+    pa_pstream_set_receive_packet_callback(p2, packet_received, NULL);
+
+    pdata = (uint8_t *) pa_packet_data(packet, &plen);
+    for (i = 0; i < plen; i++) {
+        pdata[i] = i;
+        psum += pdata[i];
+    }
+
+    for (i = 0; i < npackets; i++) {
+        pa_pstream_send_packet(p1, packet, NULL);
+        totalsum += psum;
+        pa_mainloop_iterate(ml, 0, NULL);
+    }
+
+    while (packets_received < npackets)
+        pa_mainloop_iterate(ml, 1, NULL);
+
+    fail_unless(packets_checksum == totalsum);
+    pa_log_debug("Correct checksum received (%d)", packets_checksum);
+    pa_packet_unref(packet);
+}
+
+START_TEST (srbchannel_test) {
+
+    int pipefd[4];
+
+    pa_mainloop *ml = pa_mainloop_new();
+    pa_mempool *mp = pa_mempool_new(PA_MEM_TYPE_SHARED_POSIX, 0, true);
+    pa_iochannel *io1, *io2;
+    pa_pstream *p1, *p2;
+    pa_srbchannel *sr1, *sr2;
+    pa_srbchannel_template srt;
+
+    fail_unless(pipe(pipefd) == 0);
+    fail_unless(pipe(&pipefd[2]) == 0);
+    io1 = pa_iochannel_new(pa_mainloop_get_api(ml), pipefd[2], pipefd[1]);
+    io2 = pa_iochannel_new(pa_mainloop_get_api(ml), pipefd[0], pipefd[3]);
+    p1 = pa_pstream_new(pa_mainloop_get_api(ml), io1, mp);
+    p2 = pa_pstream_new(pa_mainloop_get_api(ml), io2, mp);
+
+    pa_log_debug("Pipes: fd %d -> %d, %d -> %d", pipefd[1], pipefd[0], pipefd[3], pipefd[2]);
+
+    packet_test(250, 5, ml, p1, p2);
+    packet_test(10, 1234567, ml, p1, p2);
+
+    pa_log_debug("And now the same thing with srbchannel...");
+
+    sr1 = pa_srbchannel_new(pa_mainloop_get_api(ml), mp);
+    pa_srbchannel_export(sr1, &srt);
+    pa_pstream_set_srbchannel(p1, sr1);
+    sr2 = pa_srbchannel_new_from_template(pa_mainloop_get_api(ml), &srt);
+    pa_pstream_set_srbchannel(p2, sr2);
+
+    packet_test(250, 5, ml, p1, p2);
+    packet_test(10, 1234567, ml, p1, p2);
+
+    pa_pstream_unref(p1);
+    pa_pstream_unref(p2);
+    pa_mempool_unref(mp);
+    pa_mainloop_free(ml);
+}
+END_TEST
+
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("srbchannel");
+    tc = tcase_create("srbchannel");
+    tcase_add_test(tc, srbchannel_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/stripnul.c b/src/tests/stripnul.c
new file mode 100644 (file)
index 0000000..e4b07aa
--- /dev/null
@@ -0,0 +1,71 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#define MAX_BUFFER (16*1024)
+
+int main(int argc, char *argv[]) {
+    FILE *i, *o;
+    size_t granularity;
+    bool found = false;
+    uint8_t *zero;
+
+    pa_assert_se(argc >= 2);
+    pa_assert_se((granularity = (size_t) atoi(argv[1])) >= 1);
+    pa_assert(granularity <= MAX_BUFFER);
+    pa_assert_se((i = (argc >= 3) ? fopen(argv[2], "r") : stdin));
+    pa_assert_se((o = (argc >= 4) ? fopen(argv[3], "w") : stdout));
+
+    zero = pa_xmalloc0(granularity);
+
+    for (;;) {
+        uint8_t buffer[MAX_BUFFER], *p;
+        size_t k;
+
+        k = fread(buffer, granularity, sizeof(buffer)/granularity, i);
+
+        if (k <= 0)
+            break;
+
+        if (found)
+            pa_assert_se(fwrite(buffer, granularity, k, o) == k);
+        else {
+            for (p = buffer; ((size_t) (p-buffer)/granularity) < k; p += granularity)
+                if (memcmp(p, zero, granularity)) {
+                    size_t left;
+                    found = true;
+                    left = (size_t) (k - (size_t) (p-buffer)/granularity);
+                    pa_assert_se(fwrite(p, granularity, left, o) == left);
+                    break;
+                }
+        }
+    }
+
+    fflush(o);
+
+    return 0;
+}
diff --git a/src/tests/strlist-test.c b/src/tests/strlist-test.c
new file mode 100644 (file)
index 0000000..f4ec1c3
--- /dev/null
@@ -0,0 +1,70 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <check.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/strlist.h>
+#include <pulsecore/core-util.h>
+
+START_TEST (strlist_test) {
+    char *t, *u;
+    pa_strlist *l = NULL;
+
+    l = pa_strlist_prepend(l, "e");
+    l = pa_strlist_prepend(l, "d");
+    l = pa_strlist_prepend(l, "c");
+    l = pa_strlist_prepend(l, "b");
+    l = pa_strlist_prepend(l, "a");
+
+    t = pa_strlist_to_string(l);
+    pa_strlist_free(l);
+
+    fprintf(stderr, "1: %s\n", t);
+    fail_unless(pa_streq(t, "a b c d e"));
+
+    l = pa_strlist_parse(t);
+    pa_xfree(t);
+
+    t = pa_strlist_to_string(l);
+    fprintf(stderr, "2: %s\n", t);
+    fail_unless(pa_streq(t, "a b c d e"));
+    pa_xfree(t);
+
+    l = pa_strlist_pop(l, &u);
+    fprintf(stderr, "3: %s\n", u);
+    fail_unless(pa_streq(u, "a"));
+    pa_xfree(u);
+
+    l = pa_strlist_remove(l, "c");
+
+    t = pa_strlist_to_string(l);
+    fprintf(stderr, "4: %s\n", t);
+    fail_unless(pa_streq(t, "b d e"));
+    pa_xfree(t);
+
+    pa_strlist_free(l);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("StrList");
+    tc = tcase_create("strlist");
+    tcase_add_test(tc, strlist_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/sync-playback.c b/src/tests/sync-playback.c
new file mode 100644 (file)
index 0000000..9ef038c
--- /dev/null
@@ -0,0 +1,219 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <check.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/mainloop.h>
+
+#define NSTREAMS 4
+#define SINE_HZ 440
+#define SAMPLE_HZ 8000
+
+static pa_context *context = NULL;
+static pa_stream *streams[NSTREAMS];
+static pa_mainloop_api *mainloop_api = NULL;
+static const char *bname = NULL;
+
+static float data[SAMPLE_HZ]; /* one second space */
+
+static int n_streams_ready = 0;
+
+static const pa_sample_spec sample_spec = {
+    .format = PA_SAMPLE_FLOAT32,
+    .rate = SAMPLE_HZ,
+    .channels = 1
+};
+
+static const pa_buffer_attr buffer_attr = {
+    .maxlength = SAMPLE_HZ*sizeof(float)*NSTREAMS, /* exactly space for the entire play time */
+    .tlength = (uint32_t) -1,
+    .prebuf = 0, /* Setting prebuf to 0 guarantees us the streams will run synchronously, no matter what */
+    .minreq = (uint32_t) -1,
+    .fragsize = 0
+};
+
+static void nop_free_cb(void *p) {}
+
+static void underflow_cb(struct pa_stream *s, void *userdata) {
+    int i = (int) (long) userdata;
+
+    fprintf(stderr, "Stream %i finished\n", i);
+
+    if (++n_streams_ready >= 2*NSTREAMS) {
+        fprintf(stderr, "We're done\n");
+        mainloop_api->quit(mainloop_api, 0);
+    }
+}
+
+/* This routine is called whenever the stream state changes */
+static void stream_state_callback(pa_stream *s, void *userdata) {
+    fail_unless(s != NULL);
+
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+        case PA_STREAM_TERMINATED:
+            break;
+
+        case PA_STREAM_READY: {
+
+            int r, i = (int) (long) userdata;
+
+            fprintf(stderr, "Writing data to stream %i.\n", i);
+
+            r = pa_stream_write(s, data, sizeof(data), nop_free_cb, (int64_t) sizeof(data) * (int64_t) i, PA_SEEK_ABSOLUTE);
+            fail_unless(r == 0);
+
+            /* Be notified when this stream is drained */
+            pa_stream_set_underflow_callback(s, underflow_cb, userdata);
+
+            /* All streams have been set up, let's go! */
+            if (++n_streams_ready >= NSTREAMS) {
+                fprintf(stderr, "Uncorking\n");
+                pa_operation_unref(pa_stream_cork(s, 0, NULL, NULL));
+            }
+
+            break;
+        }
+
+        default:
+        case PA_STREAM_FAILED:
+            fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+            ck_abort();
+    }
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+    fail_unless(c != NULL);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+
+            int i;
+            fprintf(stderr, "Connection established.\n");
+
+            for (i = 0; i < NSTREAMS; i++) {
+                char name[64];
+
+                fprintf(stderr, "Creating stream %i\n", i);
+
+                snprintf(name, sizeof(name), "stream #%i", i);
+
+                streams[i] = pa_stream_new(c, name, &sample_spec, NULL);
+                fail_unless(streams[i] != NULL);
+                pa_stream_set_state_callback(streams[i], stream_state_callback, (void*) (long) i);
+                pa_stream_connect_playback(streams[i], NULL, &buffer_attr, PA_STREAM_START_CORKED, NULL, i == 0 ? NULL : streams[0]);
+            }
+
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            mainloop_api->quit(mainloop_api, 0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
+            ck_abort();
+    }
+}
+
+START_TEST (sync_playback_test) {
+    pa_mainloop* m = NULL;
+    int i, ret = 0;
+
+    for (i = 0; i < SAMPLE_HZ; i++)
+        data[i] = (float) sin(((double) i/SAMPLE_HZ)*2*M_PI*SINE_HZ)/2;
+
+    for (i = 0; i < NSTREAMS; i++)
+        streams[i] = NULL;
+
+    /* Set up a new main loop */
+    m = pa_mainloop_new();
+    fail_unless(m != NULL);
+
+    mainloop_api = pa_mainloop_get_api(m);
+
+    context = pa_context_new(mainloop_api, bname);
+    fail_unless(context != NULL);
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+
+    /* Connect the context */
+    if (pa_context_connect(context, NULL, 0, NULL) < 0) {
+        fprintf(stderr, "pa_context_connect() failed.\n");
+        goto quit;
+    }
+
+    if (pa_mainloop_run(m, &ret) < 0)
+        fprintf(stderr, "pa_mainloop_run() failed.\n");
+
+quit:
+    pa_context_unref(context);
+
+    for (i = 0; i < NSTREAMS; i++)
+        if (streams[i])
+            pa_stream_unref(streams[i]);
+
+    pa_mainloop_free(m);
+
+    fail_unless(ret == 0);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    bname = argv[0];
+
+    s = suite_create("Sync Playback");
+    tc = tcase_create("syncplayback");
+    tcase_add_test(tc, sync_playback_test);
+    /* 4s of audio, 0.5s grace time */
+    tcase_set_timeout(tc, 4.5);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/test-daemon.sh b/src/tests/test-daemon.sh
new file mode 100755 (executable)
index 0000000..c1907a1
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# This script is modified from dbus's run-with-temp-session-bus.sh.
+#
+
+SCRIPTNAME="$0"
+
+die()
+{
+    if ! test -z "$DBUS_SESSION_BUS_PID" ; then
+        echo "killing message bus "$DBUS_SESSION_BUS_PID >&2
+        kill -9 $DBUS_SESSION_BUS_PID
+    fi
+    echo $SCRIPTNAME: $* >&2
+    exit 1
+}
+
+## convenient to be able to ctrl+C without leaking the message bus process
+trap 'die "Received SIGINT"' INT
+
+unset DBUS_SESSION_BUS_ADDRESS
+unset DBUS_SESSION_BUS_PID
+
+echo "Running dbus-launch --sh-syntax" >&2
+
+eval `dbus-launch --sh-syntax`
+
+if test -z "$DBUS_SESSION_BUS_PID" ; then
+    die "Failed to launch message bus for test script to run"
+fi
+
+echo "Started bus pid $DBUS_SESSION_BUS_PID at $DBUS_SESSION_BUS_ADDRESS" >&2
+
+TEMP_PULSE_DIR=`mktemp -d`
+export PULSE_RUNTIME_PATH=${TEMP_PULSE_DIR}
+
+# this script would be called inside src/ directory, so we need to use the correct path.
+# notice that for tests, we don't load ALSA related modules.
+pulseaudio -n \
+        --log-target=file:${PWD}/pulse-daemon.log \
+        --log-level=debug \
+        --load="module-null-sink" \
+        --load="module-null-source" \
+        --load="module-suspend-on-idle" \
+        --load="module-native-protocol-unix" \
+        --load="module-cli-protocol-unix" \
+        --dl-search-path="$(dirname $SCRIPTNAME)/.libs/" \
+        &
+
+# wait a few seconds to let the daemon start!
+sleep 5
+
+unset DISPLAY
+
+EXIT_CODE=0
+
+for ONE_TEST in $@; do
+    ${ONE_TEST} || EXIT_CODE=1
+done
+
+# terminate the designated pulseaudio daemon
+pacmd exit
+
+wait
+
+kill -TERM $DBUS_SESSION_BUS_PID || die "Message bus vanished! should not have happened" && echo "Killed daemon $DBUS_SESSION_BUS_PID" >&2
+
+sleep 2
+
+## be sure it really died
+kill -9 $DBUS_SESSION_BUS_PID > /dev/null 2>&1 || true
+
+exit $EXIT_CODE
diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c
new file mode 100644 (file)
index 0000000..af366a5
--- /dev/null
@@ -0,0 +1,99 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <check.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/thread-mainloop.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-rtclock.h>
+
+static void tcb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    pa_assert_se(pa_threaded_mainloop_in_thread(userdata));
+    fprintf(stderr, "TIME EVENT START\n");
+    pa_threaded_mainloop_signal(userdata, 1);
+    fprintf(stderr, "TIME EVENT END\n");
+}
+
+START_TEST (thread_mainloop_test) {
+    pa_mainloop_api *a;
+    pa_threaded_mainloop *m;
+    struct timeval tv;
+
+    m = pa_threaded_mainloop_new();
+    fail_unless(m != NULL);
+    a = pa_threaded_mainloop_get_api(m);
+    fail_unless(m != NULL);
+
+    fail_unless(pa_threaded_mainloop_start(m) >= 0);
+
+    pa_threaded_mainloop_lock(m);
+
+    fail_unless(!pa_threaded_mainloop_in_thread(m));
+
+    a->time_new(a, pa_timeval_rtstore(&tv, pa_rtclock_now() + 5 * PA_USEC_PER_SEC, true), tcb, m);
+
+    fprintf(stderr, "waiting 5s (signal)\n");
+    pa_threaded_mainloop_wait(m);
+    fprintf(stderr, "wait completed\n");
+    pa_threaded_mainloop_accept(m);
+    fprintf(stderr, "signal accepted\n");
+
+    pa_threaded_mainloop_unlock(m);
+
+    fprintf(stderr, "waiting 5s (sleep)\n");
+    pa_msleep(5000);
+
+    pa_threaded_mainloop_stop(m);
+
+    pa_threaded_mainloop_free(m);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Thread MainLoop");
+    tc = tcase_create("threadmainloop");
+    tcase_add_test(tc, thread_mainloop_test);
+    /* the default timeout is too small,
+     * set it to a reasonable large one.
+     */
+    tcase_set_timeout(tc, 60 * 60);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/thread-test.c b/src/tests/thread-test.c
new file mode 100644 (file)
index 0000000..0c83e67
--- /dev/null
@@ -0,0 +1,164 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/once.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+static pa_mutex *mutex = NULL;
+static pa_cond *cond1 = NULL, *cond2 = NULL;
+static pa_tls *tls = NULL;
+
+static int magic_number = 0;
+
+#define THREADS_MAX 20
+
+static void once_func(void) {
+    pa_log("once!");
+}
+
+static pa_once once = PA_ONCE_INIT;
+
+static void thread_func(void *data) {
+    pa_tls_set(tls, data);
+
+    pa_log_info("thread_func() for %s starting...", (char*) pa_tls_get(tls));
+
+    pa_mutex_lock(mutex);
+
+    for (;;) {
+        int k, n;
+
+        pa_log_info("%s waiting ...", (char*) pa_tls_get(tls));
+
+        for (;;) {
+
+            if (magic_number < 0)
+                goto quit;
+
+            if (magic_number != 0)
+                break;
+
+            pa_cond_wait(cond1, mutex);
+        }
+
+        k = magic_number;
+        magic_number = 0;
+
+        pa_mutex_unlock(mutex);
+
+        pa_run_once(&once, once_func);
+
+        pa_cond_signal(cond2, 0);
+
+        pa_log_info("%s got number %i", (char*) pa_tls_get(tls), k);
+
+        /* Spin! */
+        for (n = 0; n < k; n++)
+            pa_thread_yield();
+
+        pa_mutex_lock(mutex);
+    }
+
+quit:
+
+    pa_mutex_unlock(mutex);
+
+    pa_log_info("thread_func() for %s done...", (char*) pa_tls_get(tls));
+}
+
+START_TEST (thread_test) {
+    int i, k;
+    pa_thread* t[THREADS_MAX];
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    mutex = pa_mutex_new(false, false);
+    cond1 = pa_cond_new();
+    cond2 = pa_cond_new();
+    tls = pa_tls_new(pa_xfree);
+
+    for (i = 0; i < THREADS_MAX; i++) {
+        t[i] = pa_thread_new("test", thread_func, pa_sprintf_malloc("Thread #%i", i+1));
+        fail_unless(t[i] != 0);
+    }
+
+    pa_mutex_lock(mutex);
+
+    pa_log("loop-init");
+
+    for (k = 0; k < 100; k++) {
+        pa_assert(magic_number == 0);
+
+        /* There's a thread waiting for us to change magic_number to a non-zero
+         * value. The "+ 1" part ensures that we don't accidentally set
+         * magic_number to zero here. */
+        magic_number = (int) rand() % 0x10000 + 1;
+
+        pa_log_info("iteration %i (%i)", k, magic_number);
+
+        pa_cond_signal(cond1, 0);
+
+        pa_cond_wait(cond2, mutex);
+    }
+
+    pa_log("loop-exit");
+
+    magic_number = -1;
+    pa_cond_signal(cond1, 1);
+
+    pa_mutex_unlock(mutex);
+
+    for (i = 0; i < THREADS_MAX; i++)
+        pa_thread_free(t[i]);
+
+    pa_mutex_free(mutex);
+    pa_cond_free(cond1);
+    pa_cond_free(cond2);
+    pa_tls_free(tls);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Thread");
+    tc = tcase_create("thread");
+    tcase_add_test(tc, thread_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/usergroup-test.c b/src/tests/usergroup-test.c
new file mode 100644 (file)
index 0000000..3525e5a
--- /dev/null
@@ -0,0 +1,167 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Ted Percival
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+
+#include <check.h>
+
+#include <pulsecore/usergroup.h>
+#include <pulsecore/core-util.h>
+
+static int load_reference_structs(struct group **gr, struct passwd **pw) {
+    setpwent();
+    *pw = getpwent();
+    endpwent();
+
+    setgrent();
+    *gr = getgrent();
+    endgrent();
+
+    return (*gr && *pw) ? 0 : 1;
+}
+
+static int compare_group(const struct group *a, const struct group *b) {
+    char **amem, **bmem;
+
+    if (!pa_streq(a->gr_name, b->gr_name)) {
+        fprintf(stderr, "Group name mismatch: [%s] [%s]\n", a->gr_name, b->gr_name);
+        return 1;
+    }
+
+    if (!pa_streq(a->gr_passwd, b->gr_passwd)) {
+        fprintf(stderr, "Group password mismatch: [%s] [%s]\n", a->gr_passwd, b->gr_passwd);
+        return 1;
+    }
+
+    if (a->gr_gid != b->gr_gid) {
+        fprintf(stderr, "Gid mismatch: [%lu] [%lu]\n", (unsigned long) a->gr_gid, (unsigned long) b->gr_gid);
+        return 1;
+    }
+
+    /* XXX: Assuming the group ordering is identical. */
+    for (amem = a->gr_mem, bmem = b->gr_mem; *amem && *bmem; ++amem, ++bmem) {
+        if (!pa_streq(*amem, *bmem)) {
+            fprintf(stderr, "Group member mismatch: [%s] [%s]\n", *amem, *bmem);
+            return 1;
+        }
+    }
+
+    if (*amem || *bmem) {
+        fprintf(stderr, "Mismatched group count\n");
+        return 1;
+    }
+
+    return 0;
+}
+
+static int compare_passwd(const struct passwd *a, const struct passwd *b) {
+    if (!pa_streq(a->pw_name, b->pw_name)) {
+        fprintf(stderr, "pw_name mismatch: [%s] [%s]\n", a->pw_name, b->pw_name);
+        return 1;
+    }
+
+    if (!pa_streq(a->pw_passwd, b->pw_passwd)) {
+        fprintf(stderr, "pw_passwd mismatch: [%s] [%s]\n", a->pw_passwd, b->pw_passwd);
+        return 1;
+    }
+
+    if (a->pw_uid != b->pw_uid) {
+        fprintf(stderr, "pw_uid mismatch: [%lu] [%lu]\n", (unsigned long) a->pw_uid, (unsigned long) b->pw_uid);
+        return 1;
+    }
+
+    if (a->pw_gid != b->pw_gid) {
+        fprintf(stderr, "pw_gid mismatch: [%lu] [%lu]\n", (unsigned long) a->pw_gid, (unsigned long) b->pw_gid);
+        return 1;
+    }
+
+    if (!pa_streq(a->pw_gecos, b->pw_gecos)) {
+        fprintf(stderr, "pw_gecos mismatch: [%s] [%s]\n", a->pw_gecos, b->pw_gecos);
+        return 1;
+    }
+
+    if (!pa_streq(a->pw_dir, b->pw_dir)) {
+        fprintf(stderr, "pw_dir mismatch: [%s] [%s]\n", a->pw_dir, b->pw_dir);
+        return 1;
+    }
+
+    if (!pa_streq(a->pw_shell, b->pw_shell)) {
+        fprintf(stderr, "pw_shell mismatch: [%s] [%s]\n", a->pw_shell, b->pw_shell);
+        return 1;
+    }
+
+    return 0;
+}
+
+START_TEST (usergroup_test) {
+    struct group *gr;
+    struct passwd *pw;
+    struct group *reference_group = NULL;
+    struct passwd *reference_passwd = NULL;
+
+    fail_if(load_reference_structs(&reference_group, &reference_passwd));
+
+    errno = 0;
+    gr = pa_getgrgid_malloc(reference_group->gr_gid);
+    fail_if(compare_group(reference_group, gr));
+    pa_getgrgid_free(gr);
+
+    errno = 0;
+    gr = pa_getgrnam_malloc(reference_group->gr_name);
+    fail_if(compare_group(reference_group, gr));
+    pa_getgrnam_free(gr);
+
+    errno = 0;
+    pw = pa_getpwuid_malloc(reference_passwd->pw_uid);
+    fail_if(compare_passwd(reference_passwd, pw));
+    pa_getpwuid_free(pw);
+
+    errno = 0;
+    pw = pa_getpwnam_malloc(reference_passwd->pw_name);
+    fail_if(compare_passwd(reference_passwd, pw));
+    pa_getpwnam_free(pw);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Usergroup");
+    tc = tcase_create("usergroup");
+    tcase_add_test(tc, usergroup_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/utf8-test.c b/src/tests/utf8-test.c
new file mode 100644 (file)
index 0000000..1e1e777
--- /dev/null
@@ -0,0 +1,72 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <check.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+
+START_TEST (utf8_valid) {
+    fail_unless(pa_utf8_valid("hallo") != NULL);
+    fail_unless(pa_utf8_valid("hallo\n") != NULL);
+    fail_unless(pa_utf8_valid("hüpfburg\n") == NULL);
+    fail_unless(pa_utf8_valid("hallo\n") != NULL);
+    fail_unless(pa_utf8_valid("hüpfburg\n") != NULL);
+}
+END_TEST
+
+START_TEST (utf8_filter) {
+    char *c;
+
+    {
+        char res1[] = { 0x68, 0x5f, 0x70, 0x66, 0x62, 0x75, 0x72, 0x67, '\0' };
+        c = pa_utf8_filter("hüpfburg");
+        pa_log_debug("%s %s", res1, c);
+        fail_unless(pa_streq(c, res1));
+        pa_xfree(c);
+    }
+
+    {
+        char res2[] = { 0x68, 0xc3, 0xbc, 0x70, 0x66, 0x62, 0x75, 0x72, 0x67, '\0' };
+        c = pa_utf8_filter("hüpfburg");
+        fail_unless(pa_streq(c, res2));
+        pa_log_debug("%s %s", res2, c);
+        pa_xfree(c);
+    }
+
+    {
+        char res3[] = { 0x5f, 0x78, 0x6b, 0x6e, 0x5f, 0x72, 0x7a, 0x6d, 0x5f, 0x72, 0x7a, 0x65, 0x6c, 0x74, 0x5f, 0x72, 0x73, 0x7a, 0xdf, 0xb3, 0x5f, 0x64, 0x73, 0x6a, 0x6b, 0x66, 0x68, '\0' };
+        c = pa_utf8_filter("üxknärzmörzeltörszß³§dsjkfh");
+        pa_log_debug("%s %s", res3, c);
+        fail_unless(pa_streq(c, res3));
+        pa_xfree(c);
+    }
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("UTF8");
+    tc = tcase_create("utf8");
+    tcase_add_test(tc, utf8_valid);
+    tcase_add_test(tc, utf8_filter);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/volume-test.c b/src/tests/volume-test.c
new file mode 100644 (file)
index 0000000..191bc21
--- /dev/null
@@ -0,0 +1,170 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+
+#include <check.h>
+
+#include <pulse/volume.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+START_TEST (volume_test) {
+    pa_volume_t v;
+    pa_cvolume cv;
+    float b;
+    pa_channel_map map;
+    pa_volume_t md = 0;
+    unsigned mdn = 0;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    pa_log("Attenuation of sample 1 against 32767: %g dB", 20.0*log10(1.0/32767.0));
+    pa_log("Smallest possible attenuation > 0 applied to 32767: %li", lrint(32767.0*pa_sw_volume_to_linear(1)));
+
+    for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) {
+
+        double dB = pa_sw_volume_to_dB(v);
+        double f = pa_sw_volume_to_linear(v);
+
+        pa_log_debug("Volume: %3i; percent: %i%%; decibel %0.2f; linear = %0.2f; volume(decibel): %3i; volume(linear): %3i",
+               v, (v*100)/PA_VOLUME_NORM, dB, f, pa_sw_volume_from_dB(dB), pa_sw_volume_from_linear(f));
+    }
+
+    map.channels = cv.channels = 2;
+    map.map[0] = PA_CHANNEL_POSITION_LEFT;
+    map.map[1] = PA_CHANNEL_POSITION_RIGHT;
+
+    for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) {
+        char s[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+        pa_cvolume_set(&cv, 2, v);
+
+        pa_log_debug("Volume: %3i [%s]", v, pa_cvolume_snprint_verbose(s, sizeof(s), &cv, &map, true));
+    }
+
+    for (cv.values[0] = PA_VOLUME_MUTED; cv.values[0] <= PA_VOLUME_NORM*2; cv.values[0] += 4096)
+        for (cv.values[1] = PA_VOLUME_MUTED; cv.values[1] <= PA_VOLUME_NORM*2; cv.values[1] += 4096) {
+            char s[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+            pa_log_debug("Volume: [%s]; balance: %2.1f",
+                         pa_cvolume_snprint_verbose(s, sizeof(s), &cv, &map, true),
+                         pa_cvolume_get_balance(&cv, &map));
+        }
+
+    for (cv.values[0] = PA_VOLUME_MUTED+4096; cv.values[0] <= PA_VOLUME_NORM*2; cv.values[0] += 4096)
+        for (cv.values[1] = PA_VOLUME_MUTED; cv.values[1] <= PA_VOLUME_NORM*2; cv.values[1] += 4096)
+            for (b = -1.0f; b <= 1.0f; b += 0.2f) {
+                char s[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+                pa_cvolume r;
+                float k;
+
+                pa_log_debug("Before: volume: [%s]; balance: %2.1f",
+                             pa_cvolume_snprint_verbose(s, sizeof(s), &cv, &map, true),
+                             pa_cvolume_get_balance(&cv, &map));
+
+                r = cv;
+                pa_cvolume_set_balance(&r, &map,b);
+
+                k = pa_cvolume_get_balance(&r, &map);
+                pa_log_debug("After: volume: [%s]; balance: %2.1f (intended: %2.1f) %s",
+                             pa_cvolume_snprint_verbose(s, sizeof(s), &r, &map, true),
+                             k,
+                             b,
+                             k < b - .05 || k > b + .5 ? "MISMATCH" : "");
+            }
+
+    for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 51) {
+
+        double l = pa_sw_volume_to_linear(v);
+        pa_volume_t k = pa_sw_volume_from_linear(l);
+        double db = pa_sw_volume_to_dB(v);
+        pa_volume_t r = pa_sw_volume_from_dB(db);
+        pa_volume_t w;
+
+        fail_unless(k == v);
+        fail_unless(r == v);
+
+        for (w = PA_VOLUME_MUTED; w < PA_VOLUME_NORM*2; w += 37) {
+
+            double t = pa_sw_volume_to_linear(w);
+            double db2 = pa_sw_volume_to_dB(w);
+            pa_volume_t p, p1, p2;
+            double q, qq;
+
+            p = pa_sw_volume_multiply(v, w);
+            if (isfinite(db) && isfinite(db2))
+                qq = db + db2;
+            else
+                qq = -INFINITY;
+            p2 = pa_sw_volume_from_dB(qq);
+            q = l*t;
+            p1 = pa_sw_volume_from_linear(q);
+
+            if (p2 > p && p2 - p > md)
+                md = p2 - p;
+            if (p2 < p && p - p2 > md)
+                md = p - p2;
+            if (p1 > p && p1 - p > md)
+                md = p1 - p;
+            if (p1 < p && p - p1 > md)
+                md = p - p1;
+
+            if (p1 != p || p2 != p)
+                mdn++;
+        }
+    }
+
+    pa_log("max deviation: %lu n=%lu", (unsigned long) md, (unsigned long) mdn);
+
+    fail_unless(md <= 1);
+
+    /* mdn counts the times there were rounding errors during the test. The
+     * number of rounding errors seems to vary slightly depending on the
+     * hardware. The original limit was 251 errors, but it was increased to 253
+     * when the test was failing on Tanu's laptop.
+     * See https://bugs.freedesktop.org/show_bug.cgi?id=72374 */
+    fail_unless(mdn <= 253);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    s = suite_create("Volume");
+    tc = tcase_create("volume");
+    tcase_add_test(tc, volume_test);
+    tcase_set_timeout(tc, 120);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/volume-ui.py b/src/tests/volume-ui.py
new file mode 100644 (file)
index 0000000..7909b80
--- /dev/null
@@ -0,0 +1,282 @@
+#!/usr/bin/python
+
+import pygtk, gtk, sys
+from ctypes import *
+
+try:
+    libpulse = cdll.LoadLibrary("../.libs/libpulse.so")
+except OSError:
+    try:
+        libpulse = cdll.LoadLibrary(".libs/libpulse.so")
+    except OSError:
+        libpulse = cdll.LoadLibrary("libpulse.so")
+
+class ChannelMap(Structure):
+    _fields_ = [("channels", c_ubyte),
+                ("map", c_uint * 32)]
+
+    _to_name = libpulse.pa_channel_map_to_name
+    _to_name.restype = c_char_p
+    _to_name.argtypes = [c_void_p]
+
+    _to_pretty_name = libpulse.pa_channel_map_to_pretty_name
+    _to_pretty_name.restype = c_char_p
+    _to_pretty_name.argtypes = [c_void_p]
+
+    _snprint = libpulse.pa_channel_map_snprint
+    _snprint.restype = c_char_p
+    _snprint.argtypes = [c_char_p, c_ulong, c_void_p]
+
+    _position_to_string = libpulse.pa_channel_position_to_string
+    _position_to_string.restype = c_char_p
+    _position_to_string.argtypes = [c_uint]
+
+    _position_to_pretty_string = libpulse.pa_channel_position_to_pretty_string
+    _position_to_pretty_string.restype = c_char_p
+    _position_to_pretty_string.argtypes = [c_uint]
+
+    _can_balance = libpulse.pa_channel_map_can_balance
+    _can_balance.restype = c_int
+    _can_balance.argtypes = [c_void_p]
+
+    _can_fade = libpulse.pa_channel_map_can_fade
+    _can_fade.restype = c_int
+    _can_fade.argtypes = [c_void_p]
+
+    _parse = libpulse.pa_channel_map_parse
+    _parse.restype = c_void_p
+    _parse.argtypes = [c_void_p, c_char_p]
+
+    def to_name(this):
+        return this._to_name(byref(this))
+
+    def to_pretty_name(this):
+        return this._to_pretty_name(byref(this))
+
+    def snprint(this):
+        s = create_string_buffer(336)
+        r = this._snprint(s, len(s), byref(this))
+
+        if r is None:
+            return None
+        else:
+            return s.value
+
+    def position_to_string(this, pos):
+        return this._position_to_string(pos)
+
+    def position_to_pretty_string(this, pos):
+        return this._position_to_pretty_string(pos)
+
+    def can_balance(this):
+        return bool(this._can_balance(byref(this)))
+
+    def can_fade(this):
+        return bool(this._can_fade(byref(this)))
+
+    def parse(this, s):
+        if this._parse(byref(this), s) is None:
+            raise Exception("Parse failure")
+
+
+class CVolume(Structure):
+    _fields_ = [("channels", c_ubyte),
+                ("values", c_uint32 * 32)]
+
+    _snprint = libpulse.pa_cvolume_snprint
+    _snprint.restype = c_char_p
+    _snprint.argtypes = [c_char_p, c_ulong, c_void_p]
+
+    _max = libpulse.pa_cvolume_max
+    _max.restype = c_uint32
+    _max.argtypes = [c_void_p]
+
+    _scale = libpulse.pa_cvolume_scale
+    _scale.restype = c_void_p
+    _scale.argtypes = [c_void_p, c_uint32]
+
+    _get_balance = libpulse.pa_cvolume_get_balance
+    _get_balance.restype = c_float
+    _get_balance.argtypes = [c_void_p, c_void_p]
+
+    _get_fade = libpulse.pa_cvolume_get_fade
+    _get_fade.restype = c_float
+    _get_fade.argtypes = [c_void_p, c_void_p]
+
+    _set_balance = libpulse.pa_cvolume_set_balance
+    _set_balance.restype = c_void_p
+    _set_balance.argtypes = [c_void_p, c_void_p, c_float]
+
+    _set_fade = libpulse.pa_cvolume_set_fade
+    _set_fade.restype = c_void_p
+    _set_fade.argtypes = [c_void_p, c_void_p, c_float]
+
+    _to_dB = libpulse.pa_sw_volume_to_dB
+    _to_dB.restype = c_double
+    _to_dB.argytpes = [c_uint32]
+
+    def snprint(this):
+        s = create_string_buffer(320)
+        r = this._snprint(s, len(s), byref(this))
+
+        if r is None:
+            return None
+        else:
+            return s.raw
+
+    def max(this):
+        return this._max(byref(this))
+
+    def scale(this, v):
+        return this._scale(byref(this), v)
+
+    def get_balance(this, cm):
+        return this._get_balance(byref(this), byref(cm))
+
+    def get_fade(this, cm):
+        return this._get_fade(byref(this), byref(cm))
+
+    def set_balance(this, cm, f):
+        return this._set_balance(byref(this), byref(cm), f)
+
+    def set_fade(this, cm, f):
+        return this._set_fade(byref(this), byref(cm), f)
+
+    def to_dB(this, channel = None):
+        if channel is None:
+            return this._to_dB(this.max())
+
+        return this._to_dB(this.values[channel])
+
+cm = ChannelMap()
+
+if len(sys.argv) > 1:
+    cm.parse(sys.argv[1])
+else:
+    cm.parse("surround-51")
+
+v = CVolume()
+v.channels = cm.channels
+
+for i in range(cm.channels):
+    v.values[i] = 65536
+
+title = cm.to_pretty_name()
+if title is None:
+    title = cm.snprint()
+
+window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+window.set_title(unicode(title))
+window.set_border_width(12)
+
+vbox = gtk.VBox(spacing=6)
+
+channel_labels = {}
+channel_scales = {}
+channel_dB_labels = {}
+
+def update_volume(update_channels = True, update_fade = True, update_balance = True, update_scale = True):
+    if update_channels:
+        for i in range(cm.channels):
+            channel_scales[i].set_value(v.values[i])
+
+    if update_scale:
+        value_scale.set_value(v.max())
+
+    if update_balance:
+        balance_scale.set_value(v.get_balance(cm))
+
+    if update_fade:
+        fade_scale.set_value(v.get_fade(cm))
+
+    for i in range(cm.channels):
+        channel_dB_labels[i].set_label("%0.2f dB" % v.to_dB(i))
+
+    value_dB_label.set_label("%0.2f dB" % v.to_dB())
+
+def fade_value_changed(fs):
+    v.set_fade(cm, fade_scale.get_value())
+    update_volume(update_fade = False)
+
+def balance_value_changed(fs):
+    v.set_balance(cm, balance_scale.get_value())
+    update_volume(update_balance = False)
+
+def value_value_changed(fs):
+    v.scale(int(value_scale.get_value()))
+    update_volume(update_scale = False)
+
+def channel_value_changed(fs, i):
+    v.values[i] = int(channel_scales[i].get_value())
+    update_volume(update_channels = False)
+
+for i in range(cm.channels):
+    channel_labels[i] = gtk.Label(cm.position_to_pretty_string(cm.map[i]))
+    channel_labels[i].set_alignment(0, 1)
+    vbox.pack_start(channel_labels[i], expand=False, fill=True)
+
+    channel_scales[i] = gtk.HScale()
+    channel_scales[i].set_range(0, 65536*3/2)
+    channel_scales[i].set_digits(0)
+    channel_scales[i].set_value_pos(gtk.POS_RIGHT)
+    vbox.pack_start(channel_scales[i], expand=False, fill=True)
+
+    channel_dB_labels[i] = gtk.Label("-xxx dB")
+    channel_dB_labels[i].set_alignment(1, 1)
+    vbox.pack_start(channel_dB_labels[i], expand=False, fill=True)
+
+value_label = gtk.Label("Value")
+value_label.set_alignment(0, .5)
+vbox.pack_start(value_label, expand=False, fill=True)
+value_scale = gtk.HScale()
+value_scale.set_range(0, 65536*3/2)
+value_scale.set_value_pos(gtk.POS_RIGHT)
+value_scale.set_digits(0)
+vbox.pack_start(value_scale, expand=False, fill=True)
+value_dB_label = gtk.Label("-xxx dB")
+value_dB_label.set_alignment(1, 1)
+vbox.pack_start(value_dB_label, expand=False, fill=True)
+
+balance_label = gtk.Label("Balance")
+balance_label.set_alignment(0, .5)
+vbox.pack_start(balance_label, expand=False, fill=True)
+balance_scale = gtk.HScale()
+balance_scale.set_range(-1.0, +1.0)
+balance_scale.set_value_pos(gtk.POS_RIGHT)
+balance_scale.set_digits(2)
+vbox.pack_start(balance_scale, expand=False, fill=True)
+
+fade_label = gtk.Label("Fade")
+fade_label.set_alignment(0, .5)
+vbox.pack_start(fade_label, expand=False, fill=True)
+fade_scale = gtk.HScale()
+fade_scale.set_range(-1.0, +1.0)
+fade_scale.set_value_pos(gtk.POS_RIGHT)
+fade_scale.set_digits(2)
+vbox.pack_start(fade_scale, expand=False, fill=True)
+
+window.add(vbox)
+window.set_default_size(600, 50)
+
+update_volume()
+
+for i in range(cm.channels):
+    channel_scales[i].connect("value_changed", channel_value_changed, i)
+fade_scale.connect("value_changed", fade_value_changed)
+balance_scale.connect("value_changed", balance_value_changed)
+value_scale.connect("value_changed", value_value_changed)
+
+vbox.show_all()
+
+if not cm.can_balance():
+    balance_label.hide()
+    balance_scale.hide()
+
+if not cm.can_fade():
+    fade_label.hide()
+    fade_scale.hide()
+
+
+window.show()
+
+gtk.main()
diff --git a/src/utils/Makefile b/src/utils/Makefile
new file mode 120000 (symlink)
index 0000000..f5b1dba
--- /dev/null
@@ -0,0 +1 @@
+../modules/Makefile
\ No newline at end of file
diff --git a/src/utils/pacat.c b/src/utils/pacat.c
new file mode 100644 (file)
index 0000000..6c4db4b
--- /dev/null
@@ -0,0 +1,1251 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <locale.h>
+
+#include <sndfile.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/sndfile-util.h>
+#include <pulsecore/sample-util.h>
+
+#define TIME_EVENT_USEC 50000
+
+#define CLEAR_LINE "\x1B[K"
+
+static enum { RECORD, PLAYBACK } mode = PLAYBACK;
+static const char *purpose = NULL;
+
+static pa_context *context = NULL;
+static pa_stream *stream = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
+
+/* Playback Mode (raw):
+ *
+ * We can only write audio to the PA stream in multiples of the stream's
+ * sample-spec frame size. Meanwhile, the STDIN read(2) system call can return
+ * a length much smaller than the frame-aligned size requested - leading to
+ * invalid writes. This can be reproduced by choosing a starved STDIN backend
+ * (e.g. "pacat /dev/random", "echo 1234 | pacat"), or an incomplete WAV file
+ * in raw non-paplay mode.
+ *
+ * Solve this by writing only frame-aligned sizes, while caching the resulting
+ * trailing partial frames here. This partial frame is then directly written
+ * in the next stream write iteration. Rinse and repeat.
+ */
+static void *partialframe_buf = NULL;
+static size_t partialframe_len = 0;
+
+/* Recording Mode buffers */
+static void *buffer = NULL;
+static size_t buffer_length = 0, buffer_index = 0;
+
+static void *silence_buffer = NULL;
+static size_t silence_buffer_length = 0;
+
+static pa_io_event* stdio_event = NULL;
+
+static pa_proplist *proplist = NULL;
+static char *device = NULL;
+
+static SNDFILE* sndfile = NULL;
+
+static bool verbose = false;
+static pa_volume_t volume = PA_VOLUME_NORM;
+static bool volume_is_set = false;
+
+static pa_sample_spec sample_spec = {
+    .format = PA_SAMPLE_S16LE,
+    .rate = 44100,
+    .channels = 2
+};
+static bool sample_spec_set = false;
+
+static pa_channel_map channel_map;
+static bool channel_map_set = false;
+
+static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames) = NULL;
+static sf_count_t (*writef_function)(SNDFILE *_sndfile, const void *ptr, sf_count_t frames) = NULL;
+
+static pa_stream_flags_t flags = 0;
+
+static size_t latency = 0, process_time = 0;
+static int32_t latency_msec = 0, process_time_msec = 0;
+
+static bool raw = true;
+static int file_format = -1;
+
+static uint32_t monitor_stream = PA_INVALID_INDEX;
+
+static uint32_t cork_requests = 0;
+
+/* A shortcut for terminating the application */
+static void quit(int ret) {
+    pa_assert(mainloop_api);
+    mainloop_api->quit(mainloop_api, ret);
+}
+
+/* Connection draining complete */
+static void context_drain_complete(pa_context*c, void *userdata) {
+    pa_context_disconnect(c);
+}
+
+/* Stream draining complete */
+static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
+    pa_operation *o = NULL;
+
+    if (!success) {
+        pa_log(_("Failed to drain stream: %s"), pa_strerror(pa_context_errno(context)));
+        quit(1);
+    }
+
+    if (verbose)
+        pa_log(_("Playback stream drained."));
+
+    pa_stream_disconnect(stream);
+    pa_stream_unref(stream);
+    stream = NULL;
+
+    if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
+        pa_context_disconnect(context);
+    else {
+        pa_operation_unref(o);
+        if (verbose)
+            pa_log(_("Draining connection to server."));
+    }
+}
+
+/* Start draining */
+static void start_drain(void) {
+
+    if (stream) {
+        pa_operation *o;
+
+        pa_stream_set_write_callback(stream, NULL, NULL);
+
+        if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
+            pa_log(_("pa_stream_drain(): %s"), pa_strerror(pa_context_errno(context)));
+            quit(1);
+            return;
+        }
+
+        pa_operation_unref(o);
+    } else
+        quit(0);
+}
+
+/* This is called whenever new data may be written to the stream */
+static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
+    pa_assert(s);
+    pa_assert(length > 0);
+
+    if (raw) {
+        pa_assert(!sndfile);
+
+        if (stdio_event)
+            mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
+
+    } else {
+        sf_count_t bytes;
+        void *data;
+
+        pa_assert(sndfile);
+
+        for (;;) {
+            size_t data_length = length;
+
+            if (pa_stream_begin_write(s, &data, &data_length) < 0) {
+                pa_log(_("pa_stream_begin_write() failed: %s"), pa_strerror(pa_context_errno(context)));
+                quit(1);
+                return;
+            }
+
+            if (readf_function) {
+                size_t k = pa_frame_size(&sample_spec);
+
+                if ((bytes = readf_function(sndfile, data, (sf_count_t) (data_length/k))) > 0)
+                    bytes *= (sf_count_t) k;
+
+            } else
+                bytes = sf_read_raw(sndfile, data, (sf_count_t) data_length);
+
+            if (bytes > 0)
+                pa_stream_write(s, data, (size_t) bytes, NULL, 0, PA_SEEK_RELATIVE);
+            else
+                pa_stream_cancel_write(s);
+
+            /* EOF? */
+            if (bytes < (sf_count_t) data_length) {
+                start_drain();
+                break;
+            }
+
+            /* Request fulfilled */
+            if ((size_t) bytes >= length)
+                break;
+
+            length -= bytes;
+        }
+    }
+}
+
+/* This is called whenever new data is available */
+static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
+
+    pa_assert(s);
+    pa_assert(length > 0);
+
+    if (raw) {
+        pa_assert(!sndfile);
+
+        if (stdio_event)
+            mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
+
+        while (pa_stream_readable_size(s) > 0) {
+            const void *data;
+
+            if (pa_stream_peek(s, &data, &length) < 0) {
+                pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context)));
+                quit(1);
+                return;
+            }
+
+            pa_assert(length > 0);
+
+            /* If there is a hole in the stream, we generate silence, except
+             * if it's a passthrough stream in which case we skip the hole. */
+            if (data || !(flags & PA_STREAM_PASSTHROUGH)) {
+                buffer = pa_xrealloc(buffer, buffer_index + buffer_length + length);
+                if (data)
+                    memcpy((uint8_t *) buffer + buffer_index + buffer_length, data, length);
+                else
+                    pa_silence_memory((uint8_t *) buffer + buffer_index + buffer_length, length, &sample_spec);
+
+                buffer_length += length;
+            }
+
+            pa_stream_drop(s);
+        }
+
+    } else {
+        pa_assert(sndfile);
+
+        while (pa_stream_readable_size(s) > 0) {
+            sf_count_t bytes;
+            const void *data;
+
+            if (pa_stream_peek(s, &data, &length) < 0) {
+                pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context)));
+                quit(1);
+                return;
+            }
+
+            pa_assert(length > 0);
+
+            if (!data && (flags & PA_STREAM_PASSTHROUGH)) {
+                pa_stream_drop(s);
+                continue;
+            }
+
+            if (!data && length > silence_buffer_length) {
+                silence_buffer = pa_xrealloc(silence_buffer, length);
+                pa_silence_memory((uint8_t *) silence_buffer + silence_buffer_length, length - silence_buffer_length, &sample_spec);
+                silence_buffer_length = length;
+            }
+
+            if (writef_function) {
+                size_t k = pa_frame_size(&sample_spec);
+
+                if ((bytes = writef_function(sndfile, data ? data : silence_buffer, (sf_count_t) (length/k))) > 0)
+                    bytes *= (sf_count_t) k;
+
+            } else
+                bytes = sf_write_raw(sndfile, data ? data : silence_buffer, (sf_count_t) length);
+
+            if (bytes < (sf_count_t) length)
+                quit(1);
+
+            pa_stream_drop(s);
+        }
+    }
+}
+
+/* This routine is called whenever the stream state changes */
+static void stream_state_callback(pa_stream *s, void *userdata) {
+    pa_assert(s);
+
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_CREATING:
+        case PA_STREAM_TERMINATED:
+            break;
+
+        case PA_STREAM_READY:
+
+            if (verbose) {
+                const pa_buffer_attr *a;
+                char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
+
+                pa_log(_("Stream successfully created."));
+
+                if (!(a = pa_stream_get_buffer_attr(s)))
+                    pa_log(_("pa_stream_get_buffer_attr() failed: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+                else {
+
+                    if (mode == PLAYBACK)
+                        pa_log(_("Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"), a->maxlength, a->tlength, a->prebuf, a->minreq);
+                    else {
+                        pa_assert(mode == RECORD);
+                        pa_log(_("Buffer metrics: maxlength=%u, fragsize=%u"), a->maxlength, a->fragsize);
+                    }
+                }
+
+                pa_log(_("Using sample spec '%s', channel map '%s'."),
+                        pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)),
+                        pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s)));
+
+                pa_log(_("Connected to device %s (index: %u, suspended: %s)."),
+                        pa_stream_get_device_name(s),
+                        pa_stream_get_device_index(s),
+                        pa_yes_no(pa_stream_is_suspended(s)));
+            }
+
+            break;
+
+        case PA_STREAM_FAILED:
+        default:
+            pa_log(_("Stream error: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+            quit(1);
+    }
+}
+
+static void stream_suspended_callback(pa_stream *s, void *userdata) {
+    pa_assert(s);
+
+    if (verbose) {
+        if (pa_stream_is_suspended(s))
+            pa_log(_("Stream device suspended.%s"), CLEAR_LINE);
+        else
+            pa_log(_("Stream device resumed.%s"), CLEAR_LINE);
+    }
+}
+
+static void stream_underflow_callback(pa_stream *s, void *userdata) {
+    pa_assert(s);
+
+    if (verbose)
+        pa_log(_("Stream underrun.%s"),  CLEAR_LINE);
+}
+
+static void stream_overflow_callback(pa_stream *s, void *userdata) {
+    pa_assert(s);
+
+    if (verbose)
+        pa_log(_("Stream overrun.%s"), CLEAR_LINE);
+}
+
+static void stream_started_callback(pa_stream *s, void *userdata) {
+    pa_assert(s);
+
+    if (verbose)
+        pa_log(_("Stream started.%s"), CLEAR_LINE);
+}
+
+static void stream_moved_callback(pa_stream *s, void *userdata) {
+    pa_assert(s);
+
+    if (verbose)
+        pa_log(_("Stream moved to device %s (%u, %ssuspended).%s"), pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : _("not "),  CLEAR_LINE);
+}
+
+static void stream_buffer_attr_callback(pa_stream *s, void *userdata) {
+    pa_assert(s);
+
+    if (verbose)
+        pa_log(_("Stream buffer attributes changed.%s"),  CLEAR_LINE);
+}
+
+static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) {
+    char *t;
+
+    pa_assert(s);
+    pa_assert(name);
+    pa_assert(pl);
+
+    t = pa_proplist_to_string_sep(pl, ", ");
+    pa_log("Got event '%s', properties '%s'", name, t);
+
+    if (pa_streq(name, PA_STREAM_EVENT_REQUEST_CORK)) {
+        if (cork_requests == 0) {
+            pa_log(_("Cork request stack is empty: corking stream"));
+            pa_operation_unref(pa_stream_cork(s, 1, NULL, NULL));
+        }
+        cork_requests++;
+    } else if (pa_streq(name, PA_STREAM_EVENT_REQUEST_UNCORK)) {
+        if (cork_requests == 1) {
+            pa_log(_("Cork request stack is empty: uncorking stream"));
+            pa_operation_unref(pa_stream_cork(s, 0, NULL, NULL));
+        }
+        if (cork_requests == 0)
+            pa_log(_("Warning: Received more uncork requests than cork requests."));
+        else
+            cork_requests--;
+    }
+
+    pa_xfree(t);
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+    pa_assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+            pa_buffer_attr buffer_attr;
+
+            pa_assert(c);
+            pa_assert(!stream);
+
+            if (verbose)
+                pa_log(_("Connection established.%s"), CLEAR_LINE);
+
+            if (!(stream = pa_stream_new_with_proplist(c, NULL, &sample_spec, &channel_map, proplist))) {
+                pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c)));
+                goto fail;
+            }
+
+            pa_stream_set_state_callback(stream, stream_state_callback, NULL);
+            pa_stream_set_write_callback(stream, stream_write_callback, NULL);
+            pa_stream_set_read_callback(stream, stream_read_callback, NULL);
+            pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
+            pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
+            pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
+            pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
+            pa_stream_set_started_callback(stream, stream_started_callback, NULL);
+            pa_stream_set_event_callback(stream, stream_event_callback, NULL);
+            pa_stream_set_buffer_attr_callback(stream, stream_buffer_attr_callback, NULL);
+
+            pa_zero(buffer_attr);
+            buffer_attr.maxlength = (uint32_t) -1;
+            buffer_attr.prebuf = (uint32_t) -1;
+
+            if (latency_msec > 0) {
+                buffer_attr.fragsize = buffer_attr.tlength = pa_usec_to_bytes(latency_msec * PA_USEC_PER_MSEC, &sample_spec);
+                flags |= PA_STREAM_ADJUST_LATENCY;
+            } else if (latency > 0) {
+                buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) latency;
+                flags |= PA_STREAM_ADJUST_LATENCY;
+            } else
+                buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) -1;
+
+            if (process_time_msec > 0) {
+                buffer_attr.minreq = pa_usec_to_bytes(process_time_msec * PA_USEC_PER_MSEC, &sample_spec);
+            } else if (process_time > 0)
+                buffer_attr.minreq = (uint32_t) process_time;
+            else
+                buffer_attr.minreq = (uint32_t) -1;
+
+            if (mode == PLAYBACK) {
+                pa_cvolume cv;
+                if (pa_stream_connect_playback(stream, device, &buffer_attr, flags, volume_is_set ? pa_cvolume_set(&cv, sample_spec.channels, volume) : NULL, NULL) < 0) {
+                    pa_log(_("pa_stream_connect_playback() failed: %s"), pa_strerror(pa_context_errno(c)));
+                    goto fail;
+                }
+
+            } else {
+                if (monitor_stream != PA_INVALID_INDEX && (pa_stream_set_monitor_stream(stream, monitor_stream) < 0)) {
+                    pa_log(_("Failed to set monitor stream: %s"), pa_strerror(pa_context_errno(c)));
+                    goto fail;
+                }
+                if (pa_stream_connect_record(stream, device, &buffer_attr, flags) < 0) {
+                    pa_log(_("pa_stream_connect_record() failed: %s"), pa_strerror(pa_context_errno(c)));
+                    goto fail;
+                }
+            }
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            quit(0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
+            goto fail;
+    }
+
+    return;
+
+fail:
+    quit(1);
+
+}
+
+/* New data on STDIN **/
+static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    uint8_t *buf = NULL;
+    size_t writable, towrite, r;
+
+    pa_assert(a == mainloop_api);
+    pa_assert(e);
+    pa_assert(stdio_event == e);
+
+    /* Stream not ready? */
+    if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY ||
+        !(writable = pa_stream_writable_size(stream))) {
+
+        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
+        return;
+    }
+
+    if (pa_stream_begin_write(stream, (void **)&buf, &writable) < 0) {
+        pa_log(_("pa_stream_begin_write() failed: %s"), pa_strerror(pa_context_errno(context)));
+        quit(1);
+        return;
+    }
+
+    /* Partial frame cached from a previous write iteration? */
+    if (partialframe_len) {
+        pa_assert(partialframe_len < pa_frame_size(&sample_spec));
+        memcpy(buf, partialframe_buf, partialframe_len);
+    }
+
+    if ((r = pa_read(fd, buf + partialframe_len, writable - partialframe_len, userdata)) <= 0) {
+        if (r == 0) {
+            if (verbose)
+                pa_log(_("Got EOF."));
+
+            start_drain();
+
+        } else {
+            pa_log(_("read() failed: %s"), strerror(errno));
+            quit(1);
+        }
+
+        mainloop_api->io_free(stdio_event);
+        stdio_event = NULL;
+        return;
+    }
+    r += partialframe_len;
+
+    /* Cache any trailing partial frames for the next write */
+    towrite = pa_frame_align(r, &sample_spec);
+    partialframe_len = r - towrite;
+
+    if (partialframe_len)
+        memcpy(partialframe_buf, buf + towrite, partialframe_len);
+
+    if (towrite) {
+        if (pa_stream_write(stream, buf, towrite, NULL, 0, PA_SEEK_RELATIVE) < 0) {
+            pa_log(_("pa_stream_write() failed: %s"), pa_strerror(pa_context_errno(context)));
+            quit(1);
+            return;
+        }
+    } else
+        pa_stream_cancel_write(stream);
+}
+
+/* Some data may be written to STDOUT */
+static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    ssize_t r;
+
+    pa_assert(a == mainloop_api);
+    pa_assert(e);
+    pa_assert(stdio_event == e);
+
+    if (!buffer) {
+        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
+        return;
+    }
+
+    pa_assert(buffer_length);
+
+    if ((r = pa_write(fd, (uint8_t*) buffer+buffer_index, buffer_length, userdata)) <= 0) {
+        pa_log(_("write() failed: %s"), strerror(errno));
+        quit(1);
+
+        mainloop_api->io_free(stdio_event);
+        stdio_event = NULL;
+        return;
+    }
+
+    buffer_length -= (uint32_t) r;
+    buffer_index += (uint32_t) r;
+
+    if (!buffer_length) {
+        pa_xfree(buffer);
+        buffer = NULL;
+        buffer_length = buffer_index = 0;
+    }
+}
+
+/* UNIX signal to quit received */
+static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
+    if (verbose)
+        pa_log(_("Got signal, exiting."));
+    quit(0);
+}
+
+/* Show the current latency */
+static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
+    pa_usec_t l, usec;
+    int negative = 0;
+
+    pa_assert(s);
+
+    if (!success ||
+        pa_stream_get_time(s, &usec) < 0 ||
+        pa_stream_get_latency(s, &l, &negative) < 0) {
+        pa_log(_("Failed to get latency: %s"), pa_strerror(pa_context_errno(context)));
+        quit(1);
+        return;
+    }
+
+    fprintf(stderr, _("Time: %0.3f sec; Latency: %0.0f usec."),
+            (float) usec / 1000000,
+            (float) l * (negative?-1.0f:1.0f));
+    fprintf(stderr, "        \r");
+}
+
+#ifdef SIGUSR1
+/* Someone requested that the latency is shown */
+static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
+
+    if (!stream)
+        return;
+
+    pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL));
+}
+#endif
+
+static void time_event_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
+    if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) {
+        pa_operation *o;
+        if (!(o = pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)))
+            pa_log(_("pa_stream_update_timing_info() failed: %s"), pa_strerror(pa_context_errno(context)));
+        else
+            pa_operation_unref(o);
+    }
+
+    pa_context_rttime_restart(context, e, pa_rtclock_now() + TIME_EVENT_USEC);
+}
+
+static void help(const char *argv0) {
+
+    printf(_("%s [options]\n"
+             "%s\n\n"
+             "  -h, --help                            Show this help\n"
+             "      --version                         Show version\n\n"
+             "  -r, --record                          Create a connection for recording\n"
+             "  -p, --playback                        Create a connection for playback\n\n"
+             "  -v, --verbose                         Enable verbose operations\n\n"
+             "  -s, --server=SERVER                   The name of the server to connect to\n"
+             "  -d, --device=DEVICE                   The name of the sink/source to connect to\n"
+             "  -n, --client-name=NAME                How to call this client on the server\n"
+             "      --stream-name=NAME                How to call this stream on the server\n"
+             "      --volume=VOLUME                   Specify the initial (linear) volume in range 0...65536\n"
+             "      --rate=SAMPLERATE                 The sample rate in Hz (defaults to 44100)\n"
+             "      --format=SAMPLEFORMAT             The sample type, one of s16le, s16be, u8, float32le,\n"
+             "                                        float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
+             "                                        s24-32le, s24-32be (defaults to s16ne)\n"
+             "      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"
+             "                                        (defaults to 2)\n"
+             "      --channel-map=CHANNELMAP          Channel map to use instead of the default\n"
+             "      --fix-format                      Take the sample format from the sink/source the stream is\n"
+             "                                        being connected to.\n"
+             "      --fix-rate                        Take the sampling rate from the sink/source the stream is\n"
+             "                                        being connected to.\n"
+             "      --fix-channels                    Take the number of channels and the channel map\n"
+             "                                        from the sink/source the stream is being connected to.\n"
+             "      --no-remix                        Don't upmix or downmix channels.\n"
+             "      --no-remap                        Map channels by index instead of name.\n"
+             "      --latency=BYTES                   Request the specified latency in bytes.\n"
+             "      --process-time=BYTES              Request the specified process time per request in bytes.\n"
+             "      --latency-msec=MSEC               Request the specified latency in msec.\n"
+             "      --process-time-msec=MSEC          Request the specified process time per request in msec.\n"
+             "      --property=PROPERTY=VALUE         Set the specified property to the specified value.\n"
+             "      --raw                             Record/play raw PCM data.\n"
+             "      --passthrough                     Passthrough data.\n"
+             "      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
+             "      --list-file-formats               List available file formats.\n"
+             "      --monitor-stream=INDEX            Record from the sink input with index INDEX.\n")
+           , argv0, purpose);
+}
+
+enum {
+    ARG_VERSION = 256,
+    ARG_STREAM_NAME,
+    ARG_VOLUME,
+    ARG_SAMPLERATE,
+    ARG_SAMPLEFORMAT,
+    ARG_CHANNELS,
+    ARG_CHANNELMAP,
+    ARG_FIX_FORMAT,
+    ARG_FIX_RATE,
+    ARG_FIX_CHANNELS,
+    ARG_NO_REMAP,
+    ARG_NO_REMIX,
+    ARG_LATENCY,
+    ARG_PROCESS_TIME,
+    ARG_RAW,
+    ARG_PASSTHROUGH,
+    ARG_PROPERTY,
+    ARG_FILE_FORMAT,
+    ARG_LIST_FILE_FORMATS,
+    ARG_LATENCY_MSEC,
+    ARG_PROCESS_TIME_MSEC,
+    ARG_MONITOR_STREAM,
+};
+
+int main(int argc, char *argv[]) {
+    pa_mainloop* m = NULL;
+    int ret = 1, c;
+    char *bn, *server = NULL;
+    pa_time_event *time_event = NULL;
+    const char *filename = NULL;
+    /* type for pa_read/_write. passed as userdata to the callbacks */
+    unsigned long type = 0;
+
+    static const struct option long_options[] = {
+        {"record",       0, NULL, 'r'},
+        {"playback",     0, NULL, 'p'},
+        {"device",       1, NULL, 'd'},
+        {"server",       1, NULL, 's'},
+        {"client-name",  1, NULL, 'n'},
+        {"stream-name",  1, NULL, ARG_STREAM_NAME},
+        {"version",      0, NULL, ARG_VERSION},
+        {"help",         0, NULL, 'h'},
+        {"verbose",      0, NULL, 'v'},
+        {"volume",       1, NULL, ARG_VOLUME},
+        {"rate",         1, NULL, ARG_SAMPLERATE},
+        {"format",       1, NULL, ARG_SAMPLEFORMAT},
+        {"channels",     1, NULL, ARG_CHANNELS},
+        {"channel-map",  1, NULL, ARG_CHANNELMAP},
+        {"fix-format",   0, NULL, ARG_FIX_FORMAT},
+        {"fix-rate",     0, NULL, ARG_FIX_RATE},
+        {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
+        {"no-remap",     0, NULL, ARG_NO_REMAP},
+        {"no-remix",     0, NULL, ARG_NO_REMIX},
+        {"latency",      1, NULL, ARG_LATENCY},
+        {"process-time", 1, NULL, ARG_PROCESS_TIME},
+        {"property",     1, NULL, ARG_PROPERTY},
+        {"raw",          0, NULL, ARG_RAW},
+        {"passthrough",  0, NULL, ARG_PASSTHROUGH},
+        {"file-format",  2, NULL, ARG_FILE_FORMAT},
+        {"list-file-formats", 0, NULL, ARG_LIST_FILE_FORMATS},
+        {"latency-msec", 1, NULL, ARG_LATENCY_MSEC},
+        {"process-time-msec", 1, NULL, ARG_PROCESS_TIME_MSEC},
+        {"monitor-stream", 1, NULL, ARG_MONITOR_STREAM},
+        {NULL,           0, NULL, 0}
+    };
+
+    setlocale(LC_ALL, "");
+#ifdef ENABLE_NLS
+    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
+#endif
+
+    bn = pa_path_get_filename(argv[0]);
+
+    if (strstr(bn, "play")) {
+        mode = PLAYBACK;
+        raw = false;
+        purpose = _("Play back encoded audio files on a PulseAudio sound server.");
+    } else if (strstr(bn, "record")) {
+        mode = RECORD;
+        raw = false;
+        purpose = _("Capture audio data from a PulseAudio sound server and write it to a file.");
+    } else if (strstr(bn, "rec") || strstr(bn, "mon")) {
+        mode = RECORD;
+        raw = true;
+        purpose = _("Capture audio data from a PulseAudio sound server and write it to STDOUT or the specified file.");
+    } else { /* pacat */
+        mode = PLAYBACK;
+        raw = true;
+        purpose = _("Play back audio data from STDIN or the specified file on a PulseAudio sound server.");
+    }
+
+    proplist = pa_proplist_new();
+
+    while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
+
+        switch (c) {
+            case 'h' :
+                help(bn);
+                ret = 0;
+                goto quit;
+
+            case ARG_VERSION:
+                printf(_("pacat %s\n"
+                         "Compiled with libpulse %s\n"
+                         "Linked with libpulse %s\n"),
+                       PACKAGE_VERSION,
+                       pa_get_headers_version(),
+                       pa_get_library_version());
+                ret = 0;
+                goto quit;
+
+            case 'r':
+                mode = RECORD;
+                break;
+
+            case 'p':
+                mode = PLAYBACK;
+                break;
+
+            case 'd':
+                pa_xfree(device);
+                device = pa_xstrdup(optarg);
+                break;
+
+            case 's':
+                pa_xfree(server);
+                server = pa_xstrdup(optarg);
+                break;
+
+            case 'n': {
+                char *t;
+
+                if (!(t = pa_locale_to_utf8(optarg)) ||
+                    pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
+
+                    pa_log(_("Invalid client name '%s'"), t ? t : optarg);
+                    pa_xfree(t);
+                    goto quit;
+                }
+
+                pa_xfree(t);
+                break;
+            }
+
+            case ARG_STREAM_NAME: {
+                char *t;
+
+                if (!(t = pa_locale_to_utf8(optarg)) ||
+                    pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t) < 0) {
+
+                    pa_log(_("Invalid stream name '%s'"), t ? t : optarg);
+                    pa_xfree(t);
+                    goto quit;
+                }
+
+                pa_xfree(t);
+                break;
+            }
+
+            case 'v':
+                verbose = 1;
+                break;
+
+            case ARG_VOLUME: {
+                int v = atoi(optarg);
+                volume = v < 0 ? 0U : (pa_volume_t) v;
+                volume_is_set = true;
+                break;
+            }
+
+            case ARG_CHANNELS:
+                sample_spec.channels = (uint8_t) atoi(optarg);
+                sample_spec_set = true;
+                break;
+
+            case ARG_SAMPLEFORMAT:
+                sample_spec.format = pa_parse_sample_format(optarg);
+                sample_spec_set = true;
+                break;
+
+            case ARG_SAMPLERATE:
+                sample_spec.rate = (uint32_t) atoi(optarg);
+                sample_spec_set = true;
+                break;
+
+            case ARG_CHANNELMAP:
+                if (!pa_channel_map_parse(&channel_map, optarg)) {
+                    pa_log(_("Invalid channel map '%s'"), optarg);
+                    goto quit;
+                }
+
+                channel_map_set = true;
+                break;
+
+            case ARG_FIX_CHANNELS:
+                flags |= PA_STREAM_FIX_CHANNELS;
+                break;
+
+            case ARG_FIX_RATE:
+                flags |= PA_STREAM_FIX_RATE;
+                break;
+
+            case ARG_FIX_FORMAT:
+                flags |= PA_STREAM_FIX_FORMAT;
+                break;
+
+            case ARG_NO_REMIX:
+                flags |= PA_STREAM_NO_REMIX_CHANNELS;
+                break;
+
+            case ARG_NO_REMAP:
+                flags |= PA_STREAM_NO_REMAP_CHANNELS;
+                break;
+
+            case ARG_LATENCY:
+                if (((latency = (size_t) atoi(optarg))) <= 0) {
+                    pa_log(_("Invalid latency specification '%s'"), optarg);
+                    goto quit;
+                }
+                break;
+
+            case ARG_PROCESS_TIME:
+                if (((process_time = (size_t) atoi(optarg))) <= 0) {
+                    pa_log(_("Invalid process time specification '%s'"), optarg);
+                    goto quit;
+                }
+                break;
+
+            case ARG_LATENCY_MSEC:
+                if (((latency_msec = (int32_t) atoi(optarg))) <= 0) {
+                    pa_log(_("Invalid latency specification '%s'"), optarg);
+                    goto quit;
+                }
+                break;
+
+            case ARG_PROCESS_TIME_MSEC:
+                if (((process_time_msec = (int32_t) atoi(optarg))) <= 0) {
+                    pa_log(_("Invalid process time specification '%s'"), optarg);
+                    goto quit;
+                }
+                break;
+
+            case ARG_PROPERTY: {
+                char *t;
+
+                if (!(t = pa_locale_to_utf8(optarg)) ||
+                    pa_proplist_setp(proplist, t) < 0) {
+
+                    pa_xfree(t);
+                    pa_log(_("Invalid property '%s'"), optarg);
+                    goto quit;
+                }
+
+                pa_xfree(t);
+                break;
+            }
+
+            case ARG_RAW:
+                raw = true;
+                break;
+
+            case ARG_PASSTHROUGH:
+                flags |= PA_STREAM_PASSTHROUGH;
+                break;
+
+            case ARG_FILE_FORMAT:
+                if (optarg) {
+                    if ((file_format = pa_sndfile_format_from_string(optarg)) < 0) {
+                        pa_log(_("Unknown file format %s."), optarg);
+                        goto quit;
+                    }
+                }
+
+                raw = false;
+                break;
+
+            case ARG_LIST_FILE_FORMATS:
+                pa_sndfile_dump_formats();
+                ret = 0;
+                goto quit;
+
+            case ARG_MONITOR_STREAM:
+                if (pa_atou(optarg, &monitor_stream) < 0) {
+                    pa_log(_("Failed to parse the argument for --monitor-stream"));
+                    goto quit;
+                }
+                break;
+
+            default:
+                goto quit;
+        }
+    }
+
+    if (!pa_sample_spec_valid(&sample_spec)) {
+        pa_log(_("Invalid sample specification"));
+        goto quit;
+    }
+
+    if (optind+1 == argc) {
+        int fd;
+
+        filename = argv[optind];
+
+        if ((fd = pa_open_cloexec(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
+            pa_log(_("open(): %s"), strerror(errno));
+            goto quit;
+        }
+
+        if (dup2(fd, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO) < 0) {
+            pa_log(_("dup2(): %s"), strerror(errno));
+            goto quit;
+        }
+
+        pa_close(fd);
+
+    } else if (optind+1 <= argc) {
+        pa_log(_("Too many arguments."));
+        goto quit;
+    }
+
+    if (!raw) {
+        SF_INFO sfi;
+        pa_zero(sfi);
+
+        if (mode == RECORD) {
+            /* This might patch up the sample spec */
+            if (pa_sndfile_write_sample_spec(&sfi, &sample_spec) < 0) {
+                pa_log(_("Failed to generate sample specification for file."));
+                goto quit;
+            }
+
+            if (file_format <= 0) {
+                char *extension;
+                if (filename && (extension = strrchr(filename, '.')))
+                    file_format = pa_sndfile_format_from_string(extension+1);
+                if (file_format <= 0)
+                    file_format = SF_FORMAT_WAV;
+                /* Transparently upgrade classic .wav to wavex for multichannel audio */
+                if (file_format == SF_FORMAT_WAV &&
+                    (sample_spec.channels > 2 ||
+                    (channel_map_set &&
+                    !(sample_spec.channels == 1 && channel_map.map[0] == PA_CHANNEL_POSITION_MONO) &&
+                    !(sample_spec.channels == 2 && channel_map.map[0] == PA_CHANNEL_POSITION_LEFT
+                                                && channel_map.map[1] == PA_CHANNEL_POSITION_RIGHT))))
+                    file_format = SF_FORMAT_WAVEX;
+            }
+
+            sfi.format |= file_format;
+        }
+
+        if (!(sndfile = sf_open_fd(mode == RECORD ? STDOUT_FILENO : STDIN_FILENO,
+                                   mode == RECORD ? SFM_WRITE : SFM_READ,
+                                   &sfi, 0))) {
+            pa_log(_("Failed to open audio file."));
+            goto quit;
+        }
+
+        if (mode == PLAYBACK) {
+            if (sample_spec_set)
+                pa_log(_("Warning: specified sample specification will be overwritten with specification from file."));
+
+            if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
+                pa_log(_("Failed to determine sample specification from file."));
+                goto quit;
+            }
+            sample_spec_set = true;
+
+            if (!channel_map_set) {
+                /* Allow the user to overwrite the channel map on the command line */
+                if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
+                    if (sample_spec.channels > 2)
+                        pa_log(_("Warning: Failed to determine channel map from file."));
+                } else
+                    channel_map_set = true;
+            }
+        }
+    }
+
+    if (!channel_map_set)
+        pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+
+    if (!pa_channel_map_compatible(&channel_map, &sample_spec)) {
+        pa_log(_("Channel map doesn't match sample specification"));
+        goto quit;
+    }
+
+    if (!raw) {
+        pa_proplist *sfp;
+
+        if (mode == PLAYBACK)
+            readf_function = pa_sndfile_readf_function(&sample_spec);
+        else {
+            if (pa_sndfile_write_channel_map(sndfile, &channel_map) < 0)
+                pa_log(_("Warning: failed to write channel map to file."));
+
+            writef_function = pa_sndfile_writef_function(&sample_spec);
+        }
+
+        /* Fill in libsndfile prop list data */
+        sfp = pa_proplist_new();
+        pa_sndfile_init_proplist(sndfile, sfp);
+        pa_proplist_update(proplist, PA_UPDATE_MERGE, sfp);
+        pa_proplist_free(sfp);
+    }
+
+    if (verbose) {
+        char tss[PA_SAMPLE_SPEC_SNPRINT_MAX], tcm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+        pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."),
+                mode == RECORD ? _("recording") : _("playback"),
+                pa_sample_spec_snprint(tss, sizeof(tss), &sample_spec),
+                pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map));
+    }
+
+    /* Fill in client name if none was set */
+    if (!pa_proplist_contains(proplist, PA_PROP_APPLICATION_NAME)) {
+        char *t;
+
+        if ((t = pa_locale_to_utf8(bn))) {
+            pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t);
+            pa_xfree(t);
+        }
+    }
+
+    /* Fill in media name if none was set */
+    if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) {
+        const char *t;
+
+        if ((t = filename) ||
+            (t = pa_proplist_gets(proplist, PA_PROP_APPLICATION_NAME)))
+            pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t);
+
+        if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) {
+            pa_log(_("Failed to set media name."));
+            goto quit;
+        }
+    }
+
+    if (raw && mode == PLAYBACK)
+        partialframe_buf = pa_xmalloc(pa_frame_size(&sample_spec));
+
+    /* Set up a new main loop */
+    if (!(m = pa_mainloop_new())) {
+        pa_log(_("pa_mainloop_new() failed."));
+        goto quit;
+    }
+
+    mainloop_api = pa_mainloop_get_api(m);
+
+    pa_assert_se(pa_signal_init(mainloop_api) == 0);
+    pa_signal_new(SIGINT, exit_signal_callback, NULL);
+    pa_signal_new(SIGTERM, exit_signal_callback, NULL);
+#ifdef SIGUSR1
+    pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
+#endif
+    pa_disable_sigpipe();
+
+    if (raw) {
+#ifdef OS_IS_WIN32
+        /* need to turn on binary mode for stdio io. Windows, meh */
+        setmode(mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO, O_BINARY);
+#endif
+        if (!(stdio_event = mainloop_api->io_new(mainloop_api,
+                                                 mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
+                                                 mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
+                                                 mode == PLAYBACK ? stdin_callback : stdout_callback, &type))) {
+            pa_log(_("io_new() failed."));
+            goto quit;
+        }
+    }
+
+    /* Create a new connection context */
+    if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
+        pa_log(_("pa_context_new() failed."));
+        goto quit;
+    }
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+
+    /* Connect the context */
+    if (pa_context_connect(context, server, 0, NULL) < 0) {
+        pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
+        goto quit;
+    }
+
+    if (verbose) {
+        if (!(time_event = pa_context_rttime_new(context, pa_rtclock_now() + TIME_EVENT_USEC, time_event_callback, NULL))) {
+            pa_log(_("pa_context_rttime_new() failed."));
+            goto quit;
+        }
+    }
+
+    /* Run the main loop */
+    if (pa_mainloop_run(m, &ret) < 0) {
+        pa_log(_("pa_mainloop_run() failed."));
+        goto quit;
+    }
+
+quit:
+    if (stream)
+        pa_stream_unref(stream);
+
+    if (context)
+        pa_context_unref(context);
+
+    if (stdio_event) {
+        pa_assert(mainloop_api);
+        mainloop_api->io_free(stdio_event);
+    }
+
+    if (time_event) {
+        pa_assert(mainloop_api);
+        mainloop_api->time_free(time_event);
+    }
+
+    if (m) {
+        pa_signal_done();
+        pa_mainloop_free(m);
+    }
+
+    pa_xfree(silence_buffer);
+    pa_xfree(buffer);
+    pa_xfree(partialframe_buf);
+
+    pa_xfree(server);
+    pa_xfree(device);
+
+    if (sndfile)
+        sf_close(sndfile);
+
+    if (proplist)
+        pa_proplist_free(proplist);
+
+    return ret;
+}
diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c
new file mode 100644 (file)
index 0000000..616573c
--- /dev/null
@@ -0,0 +1,359 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/un.h>
+#include <getopt.h>
+#include <locale.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/poll.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/pid.h>
+
+static void help(const char *argv0) {
+    printf("%s %s\n",    argv0, "exit");
+    printf("%s %s\n",    argv0, "help");
+    printf("%s %s\n",    argv0, "list-(modules|sinks|sources|clients|cards|samples)");
+    printf("%s %s\n",    argv0, "list-(sink-inputs|source-outputs)");
+    printf("%s %s\n",    argv0, "stat");
+    printf("%s %s\n",    argv0, "info");
+    printf("%s %s %s\n", argv0, "load-module", _("NAME [ARGS ...]"));
+    printf("%s %s %s\n", argv0, "unload-module", _("NAME|#N"));
+    printf("%s %s %s\n", argv0, "describe-module", _("NAME"));
+    printf("%s %s %s\n", argv0, "set-(sink|source)-volume", _("NAME|#N VOLUME"));
+    printf("%s %s %s\n", argv0, "set-(sink-input|source-output)-volume", _("#N VOLUME"));
+    printf("%s %s %s\n", argv0, "set-(sink|source)-mute", _("NAME|#N 1|0"));
+    printf("%s %s %s\n", argv0, "set-(sink-input|source-output)-mute", _("#N 1|0"));
+    printf("%s %s %s\n", argv0, "update-(sink|source)-proplist", _("NAME|#N KEY=VALUE"));
+    printf("%s %s %s\n", argv0, "update-(sink-input|source-output)-proplist", _("#N KEY=VALUE"));
+    printf("%s %s %s\n", argv0, "set-default-(sink|source)", _("NAME|#N"));
+    printf("%s %s %s\n", argv0, "kill-(client|sink-input|source-output)", _("#N"));
+    printf("%s %s %s\n", argv0, "play-sample", _("NAME SINK|#N"));
+    printf("%s %s %s\n", argv0, "remove-sample", _("NAME"));
+    printf("%s %s %s\n", argv0, "load-sample", _("NAME FILENAME"));
+    printf("%s %s %s\n", argv0, "load-sample-lazy", _("NAME FILENAME"));
+    printf("%s %s %s\n", argv0, "load-sample-dir-lazy", _("PATHNAME"));
+    printf("%s %s %s\n", argv0, "play-file", _("FILENAME SINK|#N"));
+    printf("%s %s\n",    argv0, "dump");
+    printf("%s %s %s\n", argv0, "move-(sink-input|source-output)", _("#N SINK|SOURCE"));
+    printf("%s %s %s\n", argv0, "suspend-(sink|source)", _("NAME|#N 1|0"));
+    printf("%s %s %s\n", argv0, "suspend", _("1|0"));
+    printf("%s %s %s\n", argv0, "set-card-profile", _("CARD PROFILE"));
+    printf("%s %s %s\n", argv0, "set-(sink|source)-port", _("NAME|#N PORT"));
+    printf("%s %s %s\n", argv0, "set-port-latency-offset", _("CARD-NAME|CARD-#N PORT OFFSET"));
+    printf("%s %s %s\n", argv0, "set-log-target", _("TARGET"));
+    printf("%s %s %s\n", argv0, "set-log-level", _("NUMERIC-LEVEL"));
+    printf("%s %s %s\n", argv0, "set-log-meta", _("1|0"));
+    printf("%s %s %s\n", argv0, "set-log-time", _("1|0"));
+    printf("%s %s %s\n", argv0, "set-log-backtrace", _("FRAMES"));
+
+    printf(_("\n"
+         "  -h, --help                            Show this help\n"
+         "      --version                         Show version\n"
+         "When no command is given pacmd starts in the interactive mode.\n" ));
+}
+
+enum {
+    ARG_VERSION = 256
+};
+
+int main(int argc, char*argv[]) {
+    pid_t pid;
+    int fd = -1;
+    int ret = 1, i;
+    struct sockaddr_un sa;
+    char *ibuf = NULL;
+    char *obuf = NULL;
+    size_t buf_size, ibuf_size, ibuf_index, ibuf_length, obuf_size, obuf_index, obuf_length;
+    char *cli;
+    bool ibuf_eof, obuf_eof, ibuf_closed, obuf_closed;
+    struct pollfd pollfd[3];
+    struct pollfd *watch_socket, *watch_stdin, *watch_stdout;
+    int stdin_type = 0, stdout_type = 0, fd_type = 0;
+
+    char *bn = NULL;
+    int c;
+
+    static const struct option long_options[] = {
+        {"version",     0, NULL, ARG_VERSION},
+        {"help",        0, NULL, 'h'},
+        {NULL,          0, NULL, 0}
+    };
+
+    setlocale(LC_ALL, "");
+#ifdef ENABLE_NLS
+    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
+#endif
+
+    bn = pa_path_get_filename(argv[0]);
+
+    while ((c = getopt_long(argc, argv, "h", long_options, NULL)) != -1) {
+        switch (c) {
+            case 'h' :
+                help(bn);
+                ret = 0;
+                goto quit;
+            case ARG_VERSION:
+                printf(_("pacmd %s\n"
+                         "Compiled with libpulse %s\n"
+                         "Linked with libpulse %s\n"),
+                       PACKAGE_VERSION,
+                       pa_get_headers_version(),
+                       pa_get_library_version());
+                ret = 0;
+                goto quit;
+            default:
+                goto quit;
+        }
+    }
+
+    if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) {
+        pa_log(_("No PulseAudio daemon running, or not running as session daemon."));
+        goto quit;
+    }
+
+    if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+        pa_log(_("socket(PF_UNIX, SOCK_STREAM, 0): %s"), strerror(errno));
+        goto quit;
+    }
+
+    pa_zero(sa);
+    sa.sun_family = AF_UNIX;
+
+    if (!(cli = pa_runtime_path("cli")))
+        goto quit;
+
+    pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path));
+    pa_xfree(cli);
+
+    for (i = 0; i < 5; i++) {
+        int r;
+
+        if ((r = connect(fd, (struct sockaddr*) &sa, sizeof(sa))) < 0 && (errno != ECONNREFUSED && errno != ENOENT)) {
+            pa_log(_("connect(): %s"), strerror(errno));
+            goto quit;
+        }
+
+        if (r >= 0)
+            break;
+
+        if (pa_pid_file_kill(SIGUSR2, NULL, "pulseaudio") < 0) {
+            pa_log(_("Failed to kill PulseAudio daemon."));
+            goto quit;
+        }
+
+        pa_msleep(300);
+    }
+
+    if (i >= 5) {
+        pa_log(_("Daemon not responding."));
+        goto quit;
+    }
+
+    buf_size = pa_pipe_buf(fd);
+    ibuf_size = PA_MIN(buf_size, pa_pipe_buf(STDIN_FILENO));
+    ibuf = pa_xmalloc(ibuf_size);
+    obuf_size = PA_MIN(buf_size, pa_pipe_buf(STDOUT_FILENO));
+    obuf = pa_xmalloc(obuf_size);
+    ibuf_index = ibuf_length = obuf_index = obuf_length = 0;
+    ibuf_eof = obuf_eof = ibuf_closed = obuf_closed = false;
+
+    if (argc > 1) {
+        for (i = 1; i < argc; i++) {
+            size_t k;
+
+            k = PA_MIN(ibuf_size - ibuf_length, strlen(argv[i]));
+            memcpy(ibuf + ibuf_length, argv[i], k);
+            ibuf_length += k;
+
+            if (ibuf_length < ibuf_size) {
+                ibuf[ibuf_length] = i < argc-1 ? ' ' : '\n';
+                ibuf_length++;
+            }
+        }
+
+        ibuf_eof = true;
+    }
+
+    if (!ibuf_eof && isatty(STDIN_FILENO)) {
+        /* send hello to enable interactive mode (welcome message, prompt) */
+        if (pa_write(fd, "hello\n", 6, &fd_type) < 0) {
+            pa_log(_("write(): %s"), strerror(errno));
+            goto quit;
+        }
+    }
+
+    for (;;) {
+        struct pollfd *p;
+
+        if (ibuf_eof &&
+            obuf_eof &&
+            ibuf_length <= 0 &&
+            obuf_length <= 0)
+            break;
+
+        if (ibuf_length <= 0 && ibuf_eof && !ibuf_closed) {
+            shutdown(fd, SHUT_WR);
+            ibuf_closed = true;
+        }
+
+        if (obuf_length <= 0 && obuf_eof && !obuf_closed) {
+            shutdown(fd, SHUT_RD);
+            obuf_closed = true;
+        }
+
+        pa_zero(pollfd);
+
+        p = pollfd;
+
+        if (ibuf_length > 0 || (!obuf_eof && obuf_length <= 0)) {
+            watch_socket = p++;
+            watch_socket->fd = fd;
+            watch_socket->events =
+                (ibuf_length > 0 ? POLLOUT : 0) |
+                (!obuf_eof && obuf_length <= 0 ? POLLIN : 0);
+        } else
+            watch_socket = NULL;
+
+        if (!ibuf_eof && ibuf_length <= 0) {
+            watch_stdin = p++;
+            watch_stdin->fd = STDIN_FILENO;
+            watch_stdin->events = POLLIN;
+        } else
+            watch_stdin = NULL;
+
+        if (obuf_length > 0) {
+            watch_stdout = p++;
+            watch_stdout->fd = STDOUT_FILENO;
+            watch_stdout->events = POLLOUT;
+        } else
+            watch_stdout = NULL;
+
+        if (pa_poll(pollfd, p-pollfd, -1) < 0) {
+
+            if (errno == EINTR)
+                continue;
+
+            pa_log(_("poll(): %s"), strerror(errno));
+            goto quit;
+        }
+
+        if (watch_stdin) {
+            if (watch_stdin->revents & POLLIN) {
+                ssize_t r;
+                pa_assert(ibuf_length <= 0);
+
+                if ((r = pa_read(STDIN_FILENO, ibuf, ibuf_size, &stdin_type)) <= 0) {
+                    if (r < 0) {
+                        pa_log(_("read(): %s"), strerror(errno));
+                        goto quit;
+                    }
+
+                    ibuf_eof = true;
+                } else {
+                    ibuf_length = (size_t) r;
+                    ibuf_index = 0;
+                }
+            } else if (watch_stdin->revents & POLLHUP)
+                ibuf_eof = true;
+        }
+
+        if (watch_socket) {
+            if (watch_socket->revents & POLLIN) {
+                ssize_t r;
+                pa_assert(obuf_length <= 0);
+
+                if ((r = pa_read(fd, obuf, obuf_size, &fd_type)) <= 0) {
+                    if (r < 0) {
+                        pa_log(_("read(): %s"), strerror(errno));
+                        goto quit;
+                    }
+
+                    obuf_eof = true;
+                } else {
+                    obuf_length = (size_t) r;
+                    obuf_index = 0;
+                }
+            } else if (watch_socket->revents & POLLHUP)
+                obuf_eof = true;
+        }
+
+        if (watch_stdout) {
+            if (watch_stdout->revents & POLLHUP) {
+                obuf_eof = true;
+                obuf_length = 0;
+            } else if (watch_stdout->revents & POLLOUT) {
+                ssize_t r;
+                pa_assert(obuf_length > 0);
+
+                if ((r = pa_write(STDOUT_FILENO, obuf + obuf_index, obuf_length, &stdout_type)) < 0) {
+                    pa_log(_("write(): %s"), strerror(errno));
+                    goto quit;
+                }
+
+                obuf_length -= (size_t) r;
+                obuf_index += obuf_index;
+            }
+        }
+
+        if (watch_socket) {
+            if (watch_socket->revents & POLLHUP) {
+                ibuf_eof = true;
+                ibuf_length = 0;
+            } else if (watch_socket->revents & POLLOUT) {
+                ssize_t r;
+                pa_assert(ibuf_length > 0);
+
+                if ((r = pa_write(fd, ibuf + ibuf_index, ibuf_length, &fd_type)) < 0) {
+                    pa_log(_("write(): %s"), strerror(errno));
+                    goto quit;
+                }
+
+                ibuf_length -= (size_t) r;
+                ibuf_index += obuf_index;
+            }
+        }
+    }
+
+    ret = 0;
+
+quit:
+    if (fd >= 0)
+        pa_close(fd);
+
+    pa_xfree(obuf);
+    pa_xfree(ibuf);
+
+    return ret;
+}
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
new file mode 100644 (file)
index 0000000..e9bf005
--- /dev/null
@@ -0,0 +1,2119 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <locale.h>
+#include <ctype.h>
+
+#include <sndfile.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/ext-device-restore.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/sndfile-util.h>
+
+static pa_context *context = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
+
+static char
+    *list_type = NULL,
+    *sample_name = NULL,
+    *sink_name = NULL,
+    *source_name = NULL,
+    *module_name = NULL,
+    *module_args = NULL,
+    *card_name = NULL,
+    *profile_name = NULL,
+    *port_name = NULL,
+    *formats = NULL;
+
+static uint32_t
+    sink_input_idx = PA_INVALID_INDEX,
+    source_output_idx = PA_INVALID_INDEX,
+    sink_idx = PA_INVALID_INDEX;
+
+static bool short_list_format = false;
+static uint32_t module_index;
+static int32_t latency_offset;
+static bool suspend;
+static pa_cvolume volume;
+static enum volume_flags {
+    VOL_UINT     = 0,
+    VOL_PERCENT  = 1,
+    VOL_LINEAR   = 2,
+    VOL_DECIBEL  = 3,
+    VOL_ABSOLUTE = 0 << 4,
+    VOL_RELATIVE = 1 << 4,
+} volume_flags;
+
+static enum mute_flags {
+    INVALID_MUTE = -1,
+    UNMUTE = 0,
+    MUTE = 1,
+    TOGGLE_MUTE = 2
+} mute = INVALID_MUTE;
+
+static pa_proplist *proplist = NULL;
+
+static SNDFILE *sndfile = NULL;
+static pa_stream *sample_stream = NULL;
+static pa_sample_spec sample_spec;
+static pa_channel_map channel_map;
+static size_t sample_length = 0;
+
+/* This variable tracks the number of ongoing asynchronous operations. When a
+ * new operation begins, this is incremented simply with actions++, and when
+ * an operation finishes, this is decremented with the complete_action()
+ * function, which shuts down the program if actions reaches zero. */
+static int actions = 0;
+
+static bool nl = false;
+
+static enum {
+    NONE,
+    EXIT,
+    STAT,
+    INFO,
+    UPLOAD_SAMPLE,
+    PLAY_SAMPLE,
+    REMOVE_SAMPLE,
+    LIST,
+    MOVE_SINK_INPUT,
+    MOVE_SOURCE_OUTPUT,
+    LOAD_MODULE,
+    UNLOAD_MODULE,
+    SUSPEND_SINK,
+    SUSPEND_SOURCE,
+    SET_CARD_PROFILE,
+    SET_SINK_PORT,
+    SET_DEFAULT_SINK,
+    SET_SOURCE_PORT,
+    SET_DEFAULT_SOURCE,
+    SET_SINK_VOLUME,
+    SET_SOURCE_VOLUME,
+    SET_SINK_INPUT_VOLUME,
+    SET_SOURCE_OUTPUT_VOLUME,
+    SET_SINK_MUTE,
+    SET_SOURCE_MUTE,
+    SET_SINK_INPUT_MUTE,
+    SET_SOURCE_OUTPUT_MUTE,
+    SET_SINK_FORMATS,
+    SET_PORT_LATENCY_OFFSET,
+    SUBSCRIBE
+} action = NONE;
+
+static void quit(int ret) {
+    pa_assert(mainloop_api);
+    mainloop_api->quit(mainloop_api, ret);
+}
+
+static void context_drain_complete(pa_context *c, void *userdata) {
+    pa_context_disconnect(c);
+}
+
+static void drain(void) {
+    pa_operation *o;
+
+    if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
+        pa_context_disconnect(context);
+    else
+        pa_operation_unref(o);
+}
+
+static void complete_action(void) {
+    pa_assert(actions > 0);
+
+    if (!(--actions))
+        drain();
+}
+
+static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) {
+    char s[PA_BYTES_SNPRINT_MAX];
+    if (!i) {
+        pa_log(_("Failed to get statistics: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    pa_bytes_snprint(s, sizeof(s), i->memblock_total_size);
+    printf(_("Currently in use: %u blocks containing %s bytes total.\n"), i->memblock_total, s);
+
+    pa_bytes_snprint(s, sizeof(s), i->memblock_allocated_size);
+    printf(_("Allocated during whole lifetime: %u blocks containing %s bytes total.\n"), i->memblock_allocated, s);
+
+    pa_bytes_snprint(s, sizeof(s), i->scache_size);
+    printf(_("Sample cache size: %s\n"), s);
+
+    complete_action();
+}
+
+static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) {
+    char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+    if (!i) {
+        pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    printf(_("Server String: %s\n"
+             "Library Protocol Version: %u\n"
+             "Server Protocol Version: %u\n"
+             "Is Local: %s\n"
+             "Client Index: %u\n"
+             "Tile Size: %zu\n"),
+             pa_context_get_server(c),
+             pa_context_get_protocol_version(c),
+             pa_context_get_server_protocol_version(c),
+             pa_yes_no_localised(pa_context_is_local(c)),
+             pa_context_get_index(c),
+             pa_context_get_tile_size(c, NULL));
+
+    pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec);
+    pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
+
+    printf(_("User Name: %s\n"
+             "Host Name: %s\n"
+             "Server Name: %s\n"
+             "Server Version: %s\n"
+             "Default Sample Specification: %s\n"
+             "Default Channel Map: %s\n"
+             "Default Sink: %s\n"
+             "Default Source: %s\n"
+             "Cookie: %04x:%04x\n"),
+           i->user_name,
+           i->host_name,
+           i->server_name,
+           i->server_version,
+           ss,
+           cm,
+           i->default_sink_name,
+           i->default_source_name,
+           i->cookie >> 16,
+           i->cookie & 0xFFFFU);
+
+    complete_action();
+}
+
+static const char* get_available_str_ynonly(int available) {
+    switch (available) {
+        case PA_PORT_AVAILABLE_YES: return ", available";
+        case PA_PORT_AVAILABLE_NO: return ", not available";
+    }
+    return "";
+}
+
+static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
+
+    static const char *state_table[] = {
+        [1+PA_SINK_INVALID_STATE] = "n/a",
+        [1+PA_SINK_RUNNING] = "RUNNING",
+        [1+PA_SINK_IDLE] = "IDLE",
+        [1+PA_SINK_SUSPENDED] = "SUSPENDED"
+    };
+
+    char
+        s[PA_SAMPLE_SPEC_SNPRINT_MAX],
+        cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX],
+        v[PA_VOLUME_SNPRINT_VERBOSE_MAX],
+        cm[PA_CHANNEL_MAP_SNPRINT_MAX],
+        f[PA_FORMAT_INFO_SNPRINT_MAX];
+    char *pl;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last) {
+        complete_action();
+        return;
+    }
+
+    pa_assert(i);
+
+    if (nl && !short_list_format)
+        printf("\n");
+    nl = true;
+
+    if (short_list_format) {
+        printf("%u\t%s\t%s\t%s\t%s\n",
+               i->index,
+               i->name,
+               pa_strnull(i->driver),
+               pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+               state_table[1+i->state]);
+        return;
+    }
+
+    printf(_("Sink #%u\n"
+             "\tState: %s\n"
+             "\tName: %s\n"
+             "\tDescription: %s\n"
+             "\tDriver: %s\n"
+             "\tSample Specification: %s\n"
+             "\tChannel Map: %s\n"
+             "\tOwner Module: %u\n"
+             "\tMute: %s\n"
+             "\tVolume: %s\n"
+             "\t        balance %0.2f\n"
+             "\tBase Volume: %s\n"
+             "\tMonitor Source: %s\n"
+             "\tLatency: %0.0f usec, configured %0.0f usec\n"
+             "\tFlags: %s%s%s%s%s%s%s\n"
+             "\tProperties:\n\t\t%s\n"),
+           i->index,
+           state_table[1+i->state],
+           i->name,
+           pa_strnull(i->description),
+           pa_strnull(i->driver),
+           pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+           pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+           i->owner_module,
+           pa_yes_no_localised(i->mute),
+           pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, i->flags & PA_SINK_DECIBEL_VOLUME),
+           pa_cvolume_get_balance(&i->volume, &i->channel_map),
+           pa_volume_snprint_verbose(v, sizeof(v), i->base_volume, i->flags & PA_SINK_DECIBEL_VOLUME),
+           pa_strnull(i->monitor_source_name),
+           (double) i->latency, (double) i->configured_latency,
+           i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
+           i->flags & PA_SINK_NETWORK ? "NETWORK " : "",
+           i->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
+           i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+           i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
+           i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
+           i->flags & PA_SINK_SET_FORMATS ? "SET_FORMATS " : "",
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+    pa_xfree(pl);
+
+    if (i->ports) {
+        pa_sink_port_info **p;
+
+        printf(_("\tPorts:\n"));
+        for (p = i->ports; *p; p++)
+            printf("\t\t%s: %s (priority: %u%s)\n", (*p)->name, (*p)->description,
+                    (*p)->priority, get_available_str_ynonly((*p)->available));
+    }
+
+    if (i->active_port)
+        printf(_("\tActive Port: %s\n"),
+               i->active_port->name);
+
+    if (i->formats) {
+        uint8_t j;
+
+        printf(_("\tFormats:\n"));
+        for (j = 0; j < i->n_formats; j++)
+            printf("\t\t%s\n", pa_format_info_snprint(f, sizeof(f), i->formats[j]));
+    }
+}
+
+static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
+
+    static const char *state_table[] = {
+        [1+PA_SOURCE_INVALID_STATE] = "n/a",
+        [1+PA_SOURCE_RUNNING] = "RUNNING",
+        [1+PA_SOURCE_IDLE] = "IDLE",
+        [1+PA_SOURCE_SUSPENDED] = "SUSPENDED"
+    };
+
+    char
+        s[PA_SAMPLE_SPEC_SNPRINT_MAX],
+        cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX],
+        v[PA_VOLUME_SNPRINT_VERBOSE_MAX],
+        cm[PA_CHANNEL_MAP_SNPRINT_MAX],
+        f[PA_FORMAT_INFO_SNPRINT_MAX];
+    char *pl;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last) {
+        complete_action();
+        return;
+    }
+
+    pa_assert(i);
+
+    if (nl && !short_list_format)
+        printf("\n");
+    nl = true;
+
+    if (short_list_format) {
+        printf("%u\t%s\t%s\t%s\t%s\n",
+               i->index,
+               i->name,
+               pa_strnull(i->driver),
+               pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+               state_table[1+i->state]);
+        return;
+    }
+
+    printf(_("Source #%u\n"
+             "\tState: %s\n"
+             "\tName: %s\n"
+             "\tDescription: %s\n"
+             "\tDriver: %s\n"
+             "\tSample Specification: %s\n"
+             "\tChannel Map: %s\n"
+             "\tOwner Module: %u\n"
+             "\tMute: %s\n"
+             "\tVolume: %s\n"
+             "\t        balance %0.2f\n"
+             "\tBase Volume: %s\n"
+             "\tMonitor of Sink: %s\n"
+             "\tLatency: %0.0f usec, configured %0.0f usec\n"
+             "\tFlags: %s%s%s%s%s%s\n"
+             "\tProperties:\n\t\t%s\n"),
+           i->index,
+           state_table[1+i->state],
+           i->name,
+           pa_strnull(i->description),
+           pa_strnull(i->driver),
+           pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+           pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+           i->owner_module,
+           pa_yes_no_localised(i->mute),
+           pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, i->flags & PA_SOURCE_DECIBEL_VOLUME),
+           pa_cvolume_get_balance(&i->volume, &i->channel_map),
+           pa_volume_snprint_verbose(v, sizeof(v), i->base_volume, i->flags & PA_SOURCE_DECIBEL_VOLUME),
+           i->monitor_of_sink_name ? i->monitor_of_sink_name : _("n/a"),
+           (double) i->latency, (double) i->configured_latency,
+           i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
+           i->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
+           i->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
+           i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+           i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
+           i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+    pa_xfree(pl);
+
+    if (i->ports) {
+        pa_source_port_info **p;
+
+        printf(_("\tPorts:\n"));
+        for (p = i->ports; *p; p++)
+            printf("\t\t%s: %s (priority: %u%s)\n", (*p)->name, (*p)->description,
+                    (*p)->priority, get_available_str_ynonly((*p)->available));
+    }
+
+    if (i->active_port)
+        printf(_("\tActive Port: %s\n"),
+               i->active_port->name);
+
+    if (i->formats) {
+        uint8_t j;
+
+        printf(_("\tFormats:\n"));
+        for (j = 0; j < i->n_formats; j++)
+            printf("\t\t%s\n", pa_format_info_snprint(f, sizeof(f), i->formats[j]));
+    }
+}
+
+static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
+    char t[32];
+    char *pl;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last) {
+        complete_action();
+        return;
+    }
+
+    pa_assert(i);
+
+    if (nl && !short_list_format)
+        printf("\n");
+    nl = true;
+
+    pa_snprintf(t, sizeof(t), "%u", i->n_used);
+
+    if (short_list_format) {
+        printf("%u\t%s\t%s\t\n", i->index, i->name, i->argument ? i->argument : "");
+        return;
+    }
+
+    printf(_("Module #%u\n"
+             "\tName: %s\n"
+             "\tArgument: %s\n"
+             "\tUsage counter: %s\n"
+             "\tProperties:\n\t\t%s\n"),
+           i->index,
+           i->name,
+           i->argument ? i->argument : "",
+           i->n_used != PA_INVALID_INDEX ? t : _("n/a"),
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+    pa_xfree(pl);
+}
+
+static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
+    char t[32];
+    char *pl;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get client information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last) {
+        complete_action();
+        return;
+    }
+
+    pa_assert(i);
+
+    if (nl && !short_list_format)
+        printf("\n");
+    nl = true;
+
+    pa_snprintf(t, sizeof(t), "%u", i->owner_module);
+
+    if (short_list_format) {
+        printf("%u\t%s\t%s\n",
+               i->index,
+               pa_strnull(i->driver),
+               pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)));
+        return;
+    }
+
+    printf(_("Client #%u\n"
+             "\tDriver: %s\n"
+             "\tOwner Module: %s\n"
+             "\tProperties:\n\t\t%s\n"),
+           i->index,
+           pa_strnull(i->driver),
+           i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+    pa_xfree(pl);
+}
+
+static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_last, void *userdata) {
+    char t[32];
+    char *pl;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get card information: %s"), pa_strerror(pa_context_errno(c)));
+        complete_action();
+        return;
+    }
+
+    if (is_last) {
+        complete_action();
+        return;
+    }
+
+    pa_assert(i);
+
+    if (nl && !short_list_format)
+        printf("\n");
+    nl = true;
+
+    pa_snprintf(t, sizeof(t), "%u", i->owner_module);
+
+    if (short_list_format) {
+        printf("%u\t%s\t%s\n", i->index, i->name, pa_strnull(i->driver));
+        return;
+    }
+
+    printf(_("Card #%u\n"
+             "\tName: %s\n"
+             "\tDriver: %s\n"
+             "\tOwner Module: %s\n"
+             "\tProperties:\n\t\t%s\n"),
+           i->index,
+           i->name,
+           pa_strnull(i->driver),
+           i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+    pa_xfree(pl);
+
+    if (i->n_profiles > 0) {
+        pa_card_profile_info2 **p;
+
+        printf(_("\tProfiles:\n"));
+        for (p = i->profiles2; *p; p++)
+            printf(_("\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"), (*p)->name,
+                (*p)->description, (*p)->n_sinks, (*p)->n_sources, (*p)->priority, pa_yes_no_localised((*p)->available));
+    }
+
+    if (i->active_profile)
+        printf(_("\tActive Profile: %s\n"),
+               i->active_profile->name);
+
+    if (i->ports) {
+        pa_card_port_info **p;
+
+        printf(_("\tPorts:\n"));
+        for (p = i->ports; *p; p++) {
+            pa_card_profile_info **pr = (*p)->profiles;
+            printf("\t\t%s: %s (priority: %u, latency offset: %" PRId64 " usec%s)\n", (*p)->name,
+                (*p)->description, (*p)->priority, (*p)->latency_offset,
+                get_available_str_ynonly((*p)->available));
+
+            if (!pa_proplist_isempty((*p)->proplist)) {
+                printf(_("\t\t\tProperties:\n\t\t\t\t%s\n"), pl = pa_proplist_to_string_sep((*p)->proplist, "\n\t\t\t\t"));
+                pa_xfree(pl);
+            }
+
+            if (pr) {
+                printf(_("\t\t\tPart of profile(s): %s"), pa_strnull((*pr)->name));
+                pr++;
+                while (*pr) {
+                    printf(", %s", pa_strnull((*pr)->name));
+                    pr++;
+                }
+                printf("\n");
+            }
+        }
+    }
+}
+
+static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
+    char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], f[PA_FORMAT_INFO_SNPRINT_MAX];
+    char *pl;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last) {
+        complete_action();
+        return;
+    }
+
+    pa_assert(i);
+
+    if (nl && !short_list_format)
+        printf("\n");
+    nl = true;
+
+    pa_snprintf(t, sizeof(t), "%u", i->owner_module);
+    pa_snprintf(k, sizeof(k), "%u", i->client);
+
+    if (short_list_format) {
+        printf("%u\t%u\t%s\t%s\t%s\n",
+               i->index,
+               i->sink,
+               i->client != PA_INVALID_INDEX ? k : "-",
+               pa_strnull(i->driver),
+               pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec));
+        return;
+    }
+
+    printf(_("Sink Input #%u\n"
+             "\tDriver: %s\n"
+             "\tOwner Module: %s\n"
+             "\tClient: %s\n"
+             "\tSink: %u\n"
+             "\tSample Specification: %s\n"
+             "\tChannel Map: %s\n"
+             "\tFormat: %s\n"
+             "\tCorked: %s\n"
+             "\tMute: %s\n"
+             "\tVolume: %s\n"
+             "\t        balance %0.2f\n"
+             "\tBuffer Latency: %0.0f usec\n"
+             "\tSink Latency: %0.0f usec\n"
+             "\tResample method: %s\n"
+             "\tProperties:\n\t\t%s\n"),
+           i->index,
+           pa_strnull(i->driver),
+           i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
+           i->client != PA_INVALID_INDEX ? k : _("n/a"),
+           i->sink,
+           pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+           pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+           pa_format_info_snprint(f, sizeof(f), i->format),
+           pa_yes_no_localised(i->corked),
+           pa_yes_no_localised(i->mute),
+           pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
+           pa_cvolume_get_balance(&i->volume, &i->channel_map),
+           (double) i->buffer_usec,
+           (double) i->sink_usec,
+           i->resample_method ? i->resample_method : _("n/a"),
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+    pa_xfree(pl);
+}
+
+static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
+    char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], f[PA_FORMAT_INFO_SNPRINT_MAX];
+    char *pl;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last) {
+        complete_action();
+        return;
+    }
+
+    pa_assert(i);
+
+    if (nl && !short_list_format)
+        printf("\n");
+    nl = true;
+
+    pa_snprintf(t, sizeof(t), "%u", i->owner_module);
+    pa_snprintf(k, sizeof(k), "%u", i->client);
+
+    if (short_list_format) {
+        printf("%u\t%u\t%s\t%s\t%s\n",
+               i->index,
+               i->source,
+               i->client != PA_INVALID_INDEX ? k : "-",
+               pa_strnull(i->driver),
+               pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec));
+        return;
+    }
+
+    printf(_("Source Output #%u\n"
+             "\tDriver: %s\n"
+             "\tOwner Module: %s\n"
+             "\tClient: %s\n"
+             "\tSource: %u\n"
+             "\tSample Specification: %s\n"
+             "\tChannel Map: %s\n"
+             "\tFormat: %s\n"
+             "\tCorked: %s\n"
+             "\tMute: %s\n"
+             "\tVolume: %s\n"
+             "\t        balance %0.2f\n"
+             "\tBuffer Latency: %0.0f usec\n"
+             "\tSource Latency: %0.0f usec\n"
+             "\tResample method: %s\n"
+             "\tProperties:\n\t\t%s\n"),
+           i->index,
+           pa_strnull(i->driver),
+           i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
+           i->client != PA_INVALID_INDEX ? k : _("n/a"),
+           i->source,
+           pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+           pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+           pa_format_info_snprint(f, sizeof(f), i->format),
+           pa_yes_no_localised(i->corked),
+           pa_yes_no_localised(i->mute),
+           pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
+           pa_cvolume_get_balance(&i->volume, &i->channel_map),
+           (double) i->buffer_usec,
+           (double) i->source_usec,
+           i->resample_method ? i->resample_method : _("n/a"),
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+    pa_xfree(pl);
+}
+
+static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
+    char t[PA_BYTES_SNPRINT_MAX], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char *pl;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get sample information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last) {
+        complete_action();
+        return;
+    }
+
+    pa_assert(i);
+
+    if (nl && !short_list_format)
+        printf("\n");
+    nl = true;
+
+    pa_bytes_snprint(t, sizeof(t), i->bytes);
+
+    if (short_list_format) {
+        printf("%u\t%s\t%s\t%0.3f\n",
+               i->index,
+               i->name,
+               pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : "-",
+               (double) i->duration/1000000.0);
+        return;
+    }
+
+    printf(_("Sample #%u\n"
+             "\tName: %s\n"
+             "\tSample Specification: %s\n"
+             "\tChannel Map: %s\n"
+             "\tVolume: %s\n"
+             "\t        balance %0.2f\n"
+             "\tDuration: %0.1fs\n"
+             "\tSize: %s\n"
+             "\tLazy: %s\n"
+             "\tFilename: %s\n"
+             "\tProperties:\n\t\t%s\n"),
+           i->index,
+           i->name,
+           pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : _("n/a"),
+           pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a"),
+           pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
+           pa_cvolume_get_balance(&i->volume, &i->channel_map),
+           (double) i->duration/1000000.0,
+           t,
+           pa_yes_no_localised(i->lazy),
+           i->filename ? i->filename : _("n/a"),
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+    pa_xfree(pl);
+}
+
+static void simple_callback(pa_context *c, int success, void *userdata) {
+    if (!success) {
+        pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    complete_action();
+}
+
+static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
+    if (idx == PA_INVALID_INDEX) {
+        pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    printf("%u\n", idx);
+
+    complete_action();
+}
+
+static void volume_relative_adjust(pa_cvolume *cv) {
+    pa_assert(volume_flags & VOL_RELATIVE);
+
+    /* Relative volume change is additive in case of UINT or PERCENT
+     * and multiplicative for LINEAR or DECIBEL */
+    if ((volume_flags & 0x0F) == VOL_UINT || (volume_flags & 0x0F) == VOL_PERCENT) {
+        unsigned i;
+        for (i = 0; i < cv->channels; i++) {
+            if (cv->values[i] + volume.values[i] < PA_VOLUME_NORM)
+                cv->values[i] = PA_VOLUME_MUTED;
+            else
+                cv->values[i] = cv->values[i] + volume.values[i] - PA_VOLUME_NORM;
+        }
+    }
+    if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL)
+        pa_sw_cvolume_multiply(cv, cv, &volume);
+}
+
+static void unload_module_by_name_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
+    static bool unloaded = false;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last) {
+        if (unloaded == false)
+            pa_log(_("Failed to unload module: Module %s not loaded"), module_name);
+        complete_action();
+        return;
+    }
+
+    pa_assert(i);
+
+    if (pa_streq(module_name, i->name)) {
+        unloaded = true;
+        actions++;
+        pa_operation_unref(pa_context_unload_module(c, i->index, simple_callback, NULL));
+    }
+}
+
+static void fill_volume(pa_cvolume *cv, unsigned supported) {
+    if (volume.channels == 1) {
+        pa_cvolume_set(&volume, supported, volume.values[0]);
+    } else if (volume.channels != supported) {
+        pa_log(_("Failed to set volume: You tried to set volumes for %d channels, whereas channel/s supported = %d\n"),
+            volume.channels, supported);
+        quit(1);
+        return;
+    }
+
+    if (volume_flags & VOL_RELATIVE)
+        volume_relative_adjust(cv);
+    else
+        *cv = volume;
+}
+
+static void get_sink_volume_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
+    pa_cvolume cv;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last)
+        return;
+
+    pa_assert(i);
+
+    cv = i->volume;
+    fill_volume(&cv, i->channel_map.channels);
+
+    pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &cv, simple_callback, NULL));
+}
+
+static void get_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
+    pa_cvolume cv;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last)
+        return;
+
+    pa_assert(i);
+
+    cv = i->volume;
+    fill_volume(&cv, i->channel_map.channels);
+
+    pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &cv, simple_callback, NULL));
+}
+
+static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
+    pa_cvolume cv;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last)
+        return;
+
+    pa_assert(i);
+
+    cv = i->volume;
+    fill_volume(&cv, i->channel_map.channels);
+
+    pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
+}
+
+static void get_source_output_volume_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
+    pa_cvolume cv;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last)
+        return;
+
+    pa_assert(o);
+
+    cv = o->volume;
+    fill_volume(&cv, o->channel_map.channels);
+
+    pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &cv, simple_callback, NULL));
+}
+
+static void sink_toggle_mute_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
+    if (is_last < 0) {
+        pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last)
+        return;
+
+    pa_assert(i);
+
+    pa_operation_unref(pa_context_set_sink_mute_by_name(c, i->name, !i->mute, simple_callback, NULL));
+}
+
+static void source_toggle_mute_callback(pa_context *c, const pa_source_info *o, int is_last, void *userdata) {
+    if (is_last < 0) {
+        pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last)
+        return;
+
+    pa_assert(o);
+
+    pa_operation_unref(pa_context_set_source_mute_by_name(c, o->name, !o->mute, simple_callback, NULL));
+}
+
+static void sink_input_toggle_mute_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
+    if (is_last < 0) {
+        pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last)
+        return;
+
+    pa_assert(i);
+
+    pa_operation_unref(pa_context_set_sink_input_mute(c, i->index, !i->mute, simple_callback, NULL));
+}
+
+static void source_output_toggle_mute_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
+    if (is_last < 0) {
+        pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last)
+        return;
+
+    pa_assert(o);
+
+    pa_operation_unref(pa_context_set_source_output_mute(c, o->index, !o->mute, simple_callback, NULL));
+}
+
+/* PA_MAX_FORMATS is defined in internal.h so we just define a sane value here */
+#define MAX_FORMATS 256
+
+static void set_sink_formats(pa_context *c, uint32_t sink, const char *str) {
+    pa_format_info *f_arr[MAX_FORMATS];
+    char *format = NULL;
+    const char *state = NULL;
+    int i = 0;
+    pa_operation *o = NULL;
+
+    while ((format = pa_split(str, ";", &state))) {
+        pa_format_info *f = pa_format_info_from_string(pa_strip(format));
+
+        if (!f) {
+            pa_log(_("Failed to set format: invalid format string %s"), format);
+            goto error;
+        }
+
+        f_arr[i++] = f;
+        pa_xfree(format);
+    }
+
+    o = pa_ext_device_restore_save_formats(c, PA_DEVICE_TYPE_SINK, sink, i, f_arr, simple_callback, NULL);
+    if (o) {
+        pa_operation_unref(o);
+        actions++;
+    }
+
+done:
+    if (format)
+        pa_xfree(format);
+    while(i--)
+        pa_format_info_free(f_arr[i]);
+
+    return;
+
+error:
+    while(i--)
+        pa_format_info_free(f_arr[i]);
+    quit(1);
+    goto done;
+}
+
+static void stream_state_callback(pa_stream *s, void *userdata) {
+    pa_assert(s);
+
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_CREATING:
+        case PA_STREAM_READY:
+            break;
+
+        case PA_STREAM_TERMINATED:
+            drain();
+            break;
+
+        case PA_STREAM_FAILED:
+        default:
+            pa_log(_("Failed to upload sample: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+            quit(1);
+    }
+}
+
+static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
+    sf_count_t l;
+    float *d;
+    pa_assert(s && length && sndfile);
+
+    d = pa_xmalloc(length);
+
+    pa_assert(sample_length >= length);
+    l = (sf_count_t) (length/pa_frame_size(&sample_spec));
+
+    if ((sf_readf_float(sndfile, d, l)) != l) {
+        pa_xfree(d);
+        pa_log(_("Premature end of file"));
+        quit(1);
+        return;
+    }
+
+    pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
+
+    sample_length -= length;
+
+    if (sample_length <= 0) {
+        pa_stream_set_write_callback(sample_stream, NULL, NULL);
+        pa_stream_finish_upload(sample_stream);
+    }
+}
+
+static const char *subscription_event_type_to_string(pa_subscription_event_type_t t) {
+
+    switch (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
+
+    case PA_SUBSCRIPTION_EVENT_NEW:
+        return _("new");
+
+    case PA_SUBSCRIPTION_EVENT_CHANGE:
+        return _("change");
+
+    case PA_SUBSCRIPTION_EVENT_REMOVE:
+        return _("remove");
+    }
+
+    return _("unknown");
+}
+
+static const char *subscription_event_facility_to_string(pa_subscription_event_type_t t) {
+
+    switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+
+    case PA_SUBSCRIPTION_EVENT_SINK:
+        return _("sink");
+
+    case PA_SUBSCRIPTION_EVENT_SOURCE:
+        return _("source");
+
+    case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
+        return _("sink-input");
+
+    case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
+        return _("source-output");
+
+    case PA_SUBSCRIPTION_EVENT_MODULE:
+        return _("module");
+
+    case PA_SUBSCRIPTION_EVENT_CLIENT:
+        return _("client");
+
+    case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
+        return _("sample-cache");
+
+    case PA_SUBSCRIPTION_EVENT_SERVER:
+        return _("server");
+
+    case PA_SUBSCRIPTION_EVENT_CARD:
+        return _("card");
+    }
+
+    return _("unknown");
+}
+
+static void context_subscribe_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    pa_assert(c);
+
+    printf(_("Event '%s' on %s #%u\n"),
+           subscription_event_type_to_string(t),
+           subscription_event_facility_to_string(t),
+           idx);
+    fflush(stdout);
+}
+
+static void context_state_callback(pa_context *c, void *userdata) {
+    pa_operation *o = NULL;
+
+    pa_assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY:
+            switch (action) {
+                case STAT:
+                    o = pa_context_stat(c, stat_callback, NULL);
+                    break;
+
+                case INFO:
+                    o = pa_context_get_server_info(c, get_server_info_callback, NULL);
+                    break;
+
+                case PLAY_SAMPLE:
+                    o = pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL);
+                    break;
+
+                case REMOVE_SAMPLE:
+                    o = pa_context_remove_sample(c, sample_name, simple_callback, NULL);
+                    break;
+
+                case UPLOAD_SAMPLE:
+                    sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
+                    pa_assert(sample_stream);
+
+                    pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
+                    pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
+                    pa_stream_connect_upload(sample_stream, sample_length);
+                    actions++;
+                    break;
+
+                case EXIT:
+                    o = pa_context_exit_daemon(c, simple_callback, NULL);
+                    break;
+
+                case LIST:
+                    if (list_type) {
+                        if (pa_streq(list_type, "modules"))
+                            o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
+                        else if (pa_streq(list_type, "sinks"))
+                            o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
+                        else if (pa_streq(list_type, "sources"))
+                            o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
+                        else if (pa_streq(list_type, "sink-inputs"))
+                            o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
+                        else if (pa_streq(list_type, "source-outputs"))
+                            o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
+                        else if (pa_streq(list_type, "clients"))
+                            o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
+                        else if (pa_streq(list_type, "samples"))
+                            o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
+                        else if (pa_streq(list_type, "cards"))
+                            o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
+                        else
+                            pa_assert_not_reached();
+                    } else {
+                        o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
+                        if (o) {
+                            pa_operation_unref(o);
+                            actions++;
+                        }
+
+                        o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
+                        if (o) {
+                            pa_operation_unref(o);
+                            actions++;
+                        }
+
+                        o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
+                        if (o) {
+                            pa_operation_unref(o);
+                            actions++;
+                        }
+                        o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
+                        if (o) {
+                            pa_operation_unref(o);
+                            actions++;
+                        }
+
+                        o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
+                        if (o) {
+                            pa_operation_unref(o);
+                            actions++;
+                        }
+
+                        o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
+                        if (o) {
+                            pa_operation_unref(o);
+                            actions++;
+                        }
+
+                        o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
+                        if (o) {
+                            pa_operation_unref(o);
+                            actions++;
+                        }
+
+                        o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
+                        if (o) {
+                            pa_operation_unref(o);
+                            actions++;
+                        }
+
+                        o = NULL;
+                    }
+                    break;
+
+                case MOVE_SINK_INPUT:
+                    o = pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL);
+                    break;
+
+                case MOVE_SOURCE_OUTPUT:
+                    o = pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL);
+                    break;
+
+                case LOAD_MODULE:
+                    o = pa_context_load_module(c, module_name, module_args, index_callback, NULL);
+                    break;
+
+                case UNLOAD_MODULE:
+                    if (module_name)
+                        o = pa_context_get_module_info_list(c, unload_module_by_name_callback, NULL);
+                    else
+                        o = pa_context_unload_module(c, module_index, simple_callback, NULL);
+                    break;
+
+                case SUSPEND_SINK:
+                    if (sink_name)
+                        o = pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL);
+                    else
+                        o = pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
+                    break;
+
+                case SUSPEND_SOURCE:
+                    if (source_name)
+                        o = pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL);
+                    else
+                        o = pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
+                    break;
+
+                case SET_CARD_PROFILE:
+                    o = pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL);
+                    break;
+
+                case SET_SINK_PORT:
+                    o = pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL);
+                    break;
+
+                case SET_DEFAULT_SINK:
+                    o = pa_context_set_default_sink(c, sink_name, simple_callback, NULL);
+                    break;
+
+                case SET_SOURCE_PORT:
+                    o = pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL);
+                    break;
+
+                case SET_DEFAULT_SOURCE:
+                    o = pa_context_set_default_source(c, source_name, simple_callback, NULL);
+                    break;
+
+                case SET_SINK_MUTE:
+                    if (mute == TOGGLE_MUTE)
+                        o = pa_context_get_sink_info_by_name(c, sink_name, sink_toggle_mute_callback, NULL);
+                    else
+                        o = pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL);
+                    break;
+
+                case SET_SOURCE_MUTE:
+                    if (mute == TOGGLE_MUTE)
+                        o = pa_context_get_source_info_by_name(c, source_name, source_toggle_mute_callback, NULL);
+                    else
+                        o = pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL);
+                    break;
+
+                case SET_SINK_INPUT_MUTE:
+                    if (mute == TOGGLE_MUTE)
+                        o = pa_context_get_sink_input_info(c, sink_input_idx, sink_input_toggle_mute_callback, NULL);
+                    else
+                        o = pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL);
+                    break;
+
+                case SET_SOURCE_OUTPUT_MUTE:
+                    if (mute == TOGGLE_MUTE)
+                        o = pa_context_get_source_output_info(c, source_output_idx, source_output_toggle_mute_callback, NULL);
+                    else
+                        o = pa_context_set_source_output_mute(c, source_output_idx, mute, simple_callback, NULL);
+                    break;
+
+                case SET_SINK_VOLUME:
+                    o = pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL);
+                    break;
+
+                case SET_SOURCE_VOLUME:
+                    o = pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL);
+                    break;
+
+                case SET_SINK_INPUT_VOLUME:
+                    o = pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL);
+                    break;
+
+                case SET_SOURCE_OUTPUT_VOLUME:
+                    o = pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL);
+                    break;
+
+                case SET_SINK_FORMATS:
+                    set_sink_formats(c, sink_idx, formats);
+                    break;
+
+                case SET_PORT_LATENCY_OFFSET:
+                    o = pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL);
+                    break;
+
+                case SUBSCRIBE:
+                    pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
+
+                    o = pa_context_subscribe(c,
+                                             PA_SUBSCRIPTION_MASK_SINK|
+                                             PA_SUBSCRIPTION_MASK_SOURCE|
+                                             PA_SUBSCRIPTION_MASK_SINK_INPUT|
+                                             PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
+                                             PA_SUBSCRIPTION_MASK_MODULE|
+                                             PA_SUBSCRIPTION_MASK_CLIENT|
+                                             PA_SUBSCRIPTION_MASK_SAMPLE_CACHE|
+                                             PA_SUBSCRIPTION_MASK_SERVER|
+                                             PA_SUBSCRIPTION_MASK_CARD,
+                                             NULL,
+                                             NULL);
+                    break;
+
+                default:
+                    pa_assert_not_reached();
+            }
+
+            if (o) {
+                pa_operation_unref(o);
+                actions++;
+            }
+
+            if (actions == 0) {
+                pa_log("Operation failed: %s", pa_strerror(pa_context_errno(c)));
+                quit(1);
+            }
+
+            break;
+
+        case PA_CONTEXT_TERMINATED:
+            quit(0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
+            quit(1);
+    }
+}
+
+static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
+    pa_log(_("Got SIGINT, exiting."));
+    quit(0);
+}
+
+static int parse_volume(const char *vol_spec, pa_volume_t *vol, enum volume_flags *vol_flags) {
+    double v;
+    char *vs;
+    const char *atod_input;
+
+    pa_assert(vol_spec);
+    pa_assert(vol);
+    pa_assert(vol_flags);
+
+    vs = pa_xstrdup(vol_spec);
+
+    *vol_flags = (pa_startswith(vs, "+") || pa_startswith(vs, "-")) ? VOL_RELATIVE : VOL_ABSOLUTE;
+    if (strchr(vs, '.'))
+        *vol_flags |= VOL_LINEAR;
+    if (pa_endswith(vs, "%")) {
+        *vol_flags |= VOL_PERCENT;
+        vs[strlen(vs)-1] = 0;
+    }
+    if (pa_endswith(vs, "db") || pa_endswith(vs, "dB")) {
+        *vol_flags |= VOL_DECIBEL;
+        vs[strlen(vs)-2] = 0;
+    }
+
+    atod_input = vs;
+
+    if (atod_input[0] == '+')
+        atod_input++; /* pa_atod() doesn't accept leading '+', so skip it. */
+
+    if (pa_atod(atod_input, &v) < 0) {
+        pa_log(_("Invalid volume specification"));
+        pa_xfree(vs);
+        return -1;
+    }
+
+    pa_xfree(vs);
+
+    if (*vol_flags & VOL_RELATIVE) {
+        if ((*vol_flags & 0x0F) == VOL_UINT)
+            v += (double) PA_VOLUME_NORM;
+        if ((*vol_flags & 0x0F) == VOL_PERCENT)
+            v += 100.0;
+        if ((*vol_flags & 0x0F) == VOL_LINEAR)
+            v += 1.0;
+    }
+    if ((*vol_flags & 0x0F) == VOL_PERCENT)
+        v = v * (double) PA_VOLUME_NORM / 100;
+    if ((*vol_flags & 0x0F) == VOL_LINEAR)
+        v = pa_sw_volume_from_linear(v);
+    if ((*vol_flags & 0x0F) == VOL_DECIBEL)
+        v = pa_sw_volume_from_dB(v);
+
+    if (!PA_VOLUME_IS_VALID((pa_volume_t) v)) {
+        pa_log(_("Volume outside permissible range.\n"));
+        return -1;
+    }
+
+    *vol = (pa_volume_t) v;
+
+    return 0;
+}
+
+static int parse_volumes(char *args[], unsigned n) {
+    unsigned i;
+
+    if (n >= PA_CHANNELS_MAX) {
+        pa_log(_("Invalid number of volume specifications.\n"));
+        return -1;
+    }
+
+    volume.channels = n;
+    for (i = 0; i < volume.channels; i++) {
+        enum volume_flags flags;
+
+        if (parse_volume(args[i], &volume.values[i], &flags) < 0)
+            return -1;
+
+        if (i > 0 && flags != volume_flags) {
+            pa_log(_("Inconsistent volume specification.\n"));
+            return -1;
+        } else
+            volume_flags = flags;
+    }
+
+    return 0;
+}
+
+static enum mute_flags parse_mute(const char *mute_text) {
+    int b;
+
+    pa_assert(mute_text);
+
+    if (pa_streq("toggle", mute_text))
+        return TOGGLE_MUTE;
+
+    b = pa_parse_boolean(mute_text);
+    switch (b) {
+        case 0:
+            return UNMUTE;
+        case 1:
+            return MUTE;
+        default:
+            return INVALID_MUTE;
+    }
+}
+
+static void help(const char *argv0) {
+
+    printf("%s %s %s\n",    argv0, _("[options]"), "stat");
+    printf("%s %s %s\n",    argv0, _("[options]"), "info");
+    printf("%s %s %s %s\n", argv0, _("[options]"), "list [short]", _("[TYPE]"));
+    printf("%s %s %s\n",    argv0, _("[options]"), "exit");
+    printf("%s %s %s %s\n", argv0, _("[options]"), "upload-sample", _("FILENAME [NAME]"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "play-sample ", _("NAME [SINK]"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "remove-sample ", _("NAME"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "load-module ", _("NAME [ARGS ...]"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "unload-module ", _("NAME|#N"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "move-(sink-input|source-output)", _("#N SINK|SOURCE"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "suspend-(sink|source)", _("NAME|#N 1|0"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "set-card-profile ", _("CARD PROFILE"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "set-default-(sink|source)", _("NAME"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-port", _("NAME|#N PORT"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-volume", _("NAME|#N VOLUME [VOLUME ...]"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-volume", _("#N VOLUME [VOLUME ...]"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-mute", _("NAME|#N 1|0|toggle"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-mute", _("#N 1|0|toggle"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "set-sink-formats", _("#N FORMATS"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "set-port-latency-offset", _("CARD-NAME|CARD-#N PORT OFFSET"));
+    printf("%s %s %s\n",    argv0, _("[options]"), "subscribe");
+    printf(_("\nThe special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
+             "can be used to specify the default sink, source and monitor.\n"));
+
+    printf(_("\n"
+             "  -h, --help                            Show this help\n"
+             "      --version                         Show version\n\n"
+             "  -s, --server=SERVER                   The name of the server to connect to\n"
+             "  -n, --client-name=NAME                How to call this client on the server\n"));
+}
+
+enum {
+    ARG_VERSION = 256
+};
+
+int main(int argc, char *argv[]) {
+    pa_mainloop *m = NULL;
+    int ret = 1, c;
+    char *server = NULL, *bn;
+
+    static const struct option long_options[] = {
+        {"server",      1, NULL, 's'},
+        {"client-name", 1, NULL, 'n'},
+        {"version",     0, NULL, ARG_VERSION},
+        {"help",        0, NULL, 'h'},
+        {NULL,          0, NULL, 0}
+    };
+
+    setlocale(LC_ALL, "");
+#ifdef ENABLE_NLS
+    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
+#endif
+
+    bn = pa_path_get_filename(argv[0]);
+
+    proplist = pa_proplist_new();
+
+    while ((c = getopt_long(argc, argv, "+s:n:h", long_options, NULL)) != -1) {
+        switch (c) {
+            case 'h' :
+                help(bn);
+                ret = 0;
+                goto quit;
+
+            case ARG_VERSION:
+                printf(_("pactl %s\n"
+                         "Compiled with libpulse %s\n"
+                         "Linked with libpulse %s\n"),
+                       PACKAGE_VERSION,
+                       pa_get_headers_version(),
+                       pa_get_library_version());
+                ret = 0;
+                goto quit;
+
+            case 's':
+                pa_xfree(server);
+                server = pa_xstrdup(optarg);
+                break;
+
+            case 'n': {
+                char *t;
+
+                if (!(t = pa_locale_to_utf8(optarg)) ||
+                    pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
+
+                    pa_log(_("Invalid client name '%s'"), t ? t : optarg);
+                    pa_xfree(t);
+                    goto quit;
+                }
+
+                pa_xfree(t);
+                break;
+            }
+
+            default:
+                goto quit;
+        }
+    }
+
+    if (optind < argc) {
+        if (pa_streq(argv[optind], "stat")) {
+            action = STAT;
+
+        } else if (pa_streq(argv[optind], "info"))
+            action = INFO;
+
+        else if (pa_streq(argv[optind], "exit"))
+            action = EXIT;
+
+        else if (pa_streq(argv[optind], "list")) {
+            action = LIST;
+
+            for (int i = optind+1; i < argc; i++) {
+                if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
+                    pa_streq(argv[i], "sinks")   || pa_streq(argv[i], "sink-inputs") ||
+                    pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
+                    pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards")) {
+                    list_type = pa_xstrdup(argv[i]);
+                } else if (pa_streq(argv[i], "short")) {
+                    short_list_format = true;
+                } else {
+                    pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards");
+                    goto quit;
+                }
+            }
+
+        } else if (pa_streq(argv[optind], "upload-sample")) {
+            struct SF_INFO sfi;
+            action = UPLOAD_SAMPLE;
+
+            if (optind+1 >= argc) {
+                pa_log(_("Please specify a sample file to load"));
+                goto quit;
+            }
+
+            if (optind+2 < argc)
+                sample_name = pa_xstrdup(argv[optind+2]);
+            else {
+                char *f = pa_path_get_filename(argv[optind+1]);
+                sample_name = pa_xstrndup(f, strcspn(f, "."));
+            }
+
+            pa_zero(sfi);
+            if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
+                pa_log(_("Failed to open sound file."));
+                goto quit;
+            }
+
+            if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
+                pa_log(_("Failed to determine sample specification from file."));
+                goto quit;
+            }
+            sample_spec.format = PA_SAMPLE_FLOAT32;
+
+            if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
+                if (sample_spec.channels > 2)
+                    pa_log(_("Warning: Failed to determine sample specification from file."));
+                pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+            }
+
+            pa_assert(pa_channel_map_compatible(&channel_map, &sample_spec));
+            sample_length = (size_t) sfi.frames*pa_frame_size(&sample_spec);
+
+        } else if (pa_streq(argv[optind], "play-sample")) {
+            action = PLAY_SAMPLE;
+            if (argc != optind+2 && argc != optind+3) {
+                pa_log(_("You have to specify a sample name to play"));
+                goto quit;
+            }
+
+            sample_name = pa_xstrdup(argv[optind+1]);
+
+            if (optind+2 < argc)
+                sink_name = pa_xstrdup(argv[optind+2]);
+
+        } else if (pa_streq(argv[optind], "remove-sample")) {
+            action = REMOVE_SAMPLE;
+            if (argc != optind+2) {
+                pa_log(_("You have to specify a sample name to remove"));
+                goto quit;
+            }
+
+            sample_name = pa_xstrdup(argv[optind+1]);
+
+        } else if (pa_streq(argv[optind], "move-sink-input")) {
+            action = MOVE_SINK_INPUT;
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a sink input index and a sink"));
+                goto quit;
+            }
+
+            sink_input_idx = (uint32_t) atoi(argv[optind+1]);
+            sink_name = pa_xstrdup(argv[optind+2]);
+
+        } else if (pa_streq(argv[optind], "move-source-output")) {
+            action = MOVE_SOURCE_OUTPUT;
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a source output index and a source"));
+                goto quit;
+            }
+
+            source_output_idx = (uint32_t) atoi(argv[optind+1]);
+            source_name = pa_xstrdup(argv[optind+2]);
+
+        } else if (pa_streq(argv[optind], "load-module")) {
+            int i;
+            size_t n = 0;
+            char *p;
+
+            action = LOAD_MODULE;
+
+            if (argc <= optind+1) {
+                pa_log(_("You have to specify a module name and arguments."));
+                goto quit;
+            }
+
+            module_name = argv[optind+1];
+
+            for (i = optind+2; i < argc; i++)
+                n += strlen(argv[i])+1;
+
+            if (n > 0) {
+                p = module_args = pa_xmalloc(n);
+
+                for (i = optind+2; i < argc; i++)
+                    p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
+            }
+
+        } else if (pa_streq(argv[optind], "unload-module")) {
+            action = UNLOAD_MODULE;
+
+            if (argc != optind+2) {
+                pa_log(_("You have to specify a module index or name"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind + 1], &module_index) < 0)
+                module_name = argv[optind + 1];
+
+        } else if (pa_streq(argv[optind], "suspend-sink")) {
+            int b;
+
+            action = SUSPEND_SINK;
+
+            if (argc > optind+3 || optind+1 >= argc) {
+                pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
+                goto quit;
+            }
+
+            if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
+                pa_log(_("Invalid suspend specification."));
+                goto quit;
+            }
+
+            suspend = !!b;
+
+            if (argc > optind+2)
+                sink_name = pa_xstrdup(argv[optind+1]);
+
+        } else if (pa_streq(argv[optind], "suspend-source")) {
+            int b;
+
+            action = SUSPEND_SOURCE;
+
+            if (argc > optind+3 || optind+1 >= argc) {
+                pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
+                goto quit;
+            }
+
+            if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
+                pa_log(_("Invalid suspend specification."));
+                goto quit;
+            }
+
+            suspend = !!b;
+
+            if (argc > optind+2)
+                source_name = pa_xstrdup(argv[optind+1]);
+        } else if (pa_streq(argv[optind], "set-card-profile")) {
+            action = SET_CARD_PROFILE;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a card name/index and a profile name"));
+                goto quit;
+            }
+
+            card_name = pa_xstrdup(argv[optind+1]);
+            profile_name = pa_xstrdup(argv[optind+2]);
+
+        } else if (pa_streq(argv[optind], "set-sink-port")) {
+            action = SET_SINK_PORT;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a sink name/index and a port name"));
+                goto quit;
+            }
+
+            sink_name = pa_xstrdup(argv[optind+1]);
+            port_name = pa_xstrdup(argv[optind+2]);
+
+        } else if (pa_streq(argv[optind], "set-default-sink")) {
+            action = SET_DEFAULT_SINK;
+
+            if (argc != optind+2) {
+                pa_log(_("You have to specify a sink name"));
+                goto quit;
+            }
+
+            sink_name = pa_xstrdup(argv[optind+1]);
+
+        } else if (pa_streq(argv[optind], "set-source-port")) {
+            action = SET_SOURCE_PORT;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a source name/index and a port name"));
+                goto quit;
+            }
+
+            source_name = pa_xstrdup(argv[optind+1]);
+            port_name = pa_xstrdup(argv[optind+2]);
+
+        } else if (pa_streq(argv[optind], "set-default-source")) {
+            action = SET_DEFAULT_SOURCE;
+
+            if (argc != optind+2) {
+                pa_log(_("You have to specify a source name"));
+                goto quit;
+            }
+
+            source_name = pa_xstrdup(argv[optind+1]);
+
+        } else if (pa_streq(argv[optind], "set-sink-volume")) {
+            action = SET_SINK_VOLUME;
+
+            if (argc < optind+3) {
+                pa_log(_("You have to specify a sink name/index and a volume"));
+                goto quit;
+            }
+
+            sink_name = pa_xstrdup(argv[optind+1]);
+
+            if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
+                goto quit;
+
+        } else if (pa_streq(argv[optind], "set-source-volume")) {
+            action = SET_SOURCE_VOLUME;
+
+            if (argc < optind+3) {
+                pa_log(_("You have to specify a source name/index and a volume"));
+                goto quit;
+            }
+
+            source_name = pa_xstrdup(argv[optind+1]);
+
+            if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
+                goto quit;
+
+        } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
+            action = SET_SINK_INPUT_VOLUME;
+
+            if (argc < optind+3) {
+                pa_log(_("You have to specify a sink input index and a volume"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
+                pa_log(_("Invalid sink input index"));
+                goto quit;
+            }
+
+            if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
+                goto quit;
+
+        } else if (pa_streq(argv[optind], "set-source-output-volume")) {
+            action = SET_SOURCE_OUTPUT_VOLUME;
+
+            if (argc < optind+3) {
+                pa_log(_("You have to specify a source output index and a volume"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
+                pa_log(_("Invalid source output index"));
+                goto quit;
+            }
+
+            if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
+                goto quit;
+
+        } else if (pa_streq(argv[optind], "set-sink-mute")) {
+            action = SET_SINK_MUTE;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"));
+                goto quit;
+            }
+
+            if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
+                pa_log(_("Invalid mute specification"));
+                goto quit;
+            }
+
+            sink_name = pa_xstrdup(argv[optind+1]);
+
+        } else if (pa_streq(argv[optind], "set-source-mute")) {
+            action = SET_SOURCE_MUTE;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a source name/index and a mute action (0, 1, or 'toggle')"));
+                goto quit;
+            }
+
+            if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
+                pa_log(_("Invalid mute specification"));
+                goto quit;
+            }
+
+            source_name = pa_xstrdup(argv[optind+1]);
+
+        } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
+            action = SET_SINK_INPUT_MUTE;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a sink input index and a mute action (0, 1, or 'toggle')"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
+                pa_log(_("Invalid sink input index specification"));
+                goto quit;
+            }
+
+            if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
+                pa_log(_("Invalid mute specification"));
+                goto quit;
+            }
+
+        } else if (pa_streq(argv[optind], "set-source-output-mute")) {
+            action = SET_SOURCE_OUTPUT_MUTE;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a source output index and a mute action (0, 1, or 'toggle')"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
+                pa_log(_("Invalid source output index specification"));
+                goto quit;
+            }
+
+            if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
+                pa_log(_("Invalid mute specification"));
+                goto quit;
+            }
+
+        } else if (pa_streq(argv[optind], "subscribe"))
+
+            action = SUBSCRIBE;
+
+        else if (pa_streq(argv[optind], "set-sink-formats")) {
+            int32_t tmp;
+
+            if (argc != optind+3 || pa_atoi(argv[optind+1], &tmp) < 0) {
+                pa_log(_("You have to specify a sink index and a semicolon-separated list of supported formats"));
+                goto quit;
+            }
+
+            sink_idx = tmp;
+            action = SET_SINK_FORMATS;
+            formats = pa_xstrdup(argv[optind+2]);
+
+        } else if (pa_streq(argv[optind], "set-port-latency-offset")) {
+            action = SET_PORT_LATENCY_OFFSET;
+
+            if (argc != optind+4) {
+                pa_log(_("You have to specify a card name/index, a port name and a latency offset"));
+                goto quit;
+            }
+
+            card_name = pa_xstrdup(argv[optind+1]);
+            port_name = pa_xstrdup(argv[optind+2]);
+            if (pa_atoi(argv[optind + 3], &latency_offset) < 0) {
+                pa_log(_("Could not parse latency offset"));
+                goto quit;
+            }
+
+        } else if (pa_streq(argv[optind], "help")) {
+            help(bn);
+            ret = 0;
+            goto quit;
+        }
+    }
+
+    if (action == NONE) {
+        pa_log(_("No valid command specified."));
+        goto quit;
+    }
+
+    if (!(m = pa_mainloop_new())) {
+        pa_log(_("pa_mainloop_new() failed."));
+        goto quit;
+    }
+
+    mainloop_api = pa_mainloop_get_api(m);
+
+    pa_assert_se(pa_signal_init(mainloop_api) == 0);
+    pa_signal_new(SIGINT, exit_signal_callback, NULL);
+    pa_signal_new(SIGTERM, exit_signal_callback, NULL);
+    pa_disable_sigpipe();
+
+    if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
+        pa_log(_("pa_context_new() failed."));
+        goto quit;
+    }
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+    if (pa_context_connect(context, server, 0, NULL) < 0) {
+        pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
+        goto quit;
+    }
+
+    if (pa_mainloop_run(m, &ret) < 0) {
+        pa_log(_("pa_mainloop_run() failed."));
+        goto quit;
+    }
+
+quit:
+    if (sample_stream)
+        pa_stream_unref(sample_stream);
+
+    if (context)
+        pa_context_unref(context);
+
+    if (m) {
+        pa_signal_done();
+        pa_mainloop_free(m);
+    }
+
+    pa_xfree(server);
+    pa_xfree(list_type);
+    pa_xfree(sample_name);
+    pa_xfree(sink_name);
+    pa_xfree(source_name);
+    pa_xfree(module_args);
+    pa_xfree(card_name);
+    pa_xfree(profile_name);
+    pa_xfree(port_name);
+    pa_xfree(formats);
+
+    if (sndfile)
+        sf_close(sndfile);
+
+    if (proplist)
+        pa_proplist_free(proplist);
+
+    return ret;
+}
diff --git a/src/utils/padsp.c b/src/utils/padsp.c
new file mode 100644 (file)
index 0000000..251f201
--- /dev/null
@@ -0,0 +1,2744 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef _FILE_OFFSET_BITS
+#undef _FILE_OFFSET_BITS
+#endif
+
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE 1
+#endif
+
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <signal.h>
+
+#ifdef __linux__
+#include <linux/sockios.h>
+#endif
+
+#include <pulse/pulseaudio.h>
+#include <pulse/gccmacro.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
+
+/* On some systems SIOCINQ isn't defined, but FIONREAD is just an alias */
+#if !defined(SIOCINQ) && defined(FIONREAD)
+# define SIOCINQ FIONREAD
+#endif
+
+/* make sure gcc doesn't redefine open and friends as macros */
+#undef open
+#undef open64
+
+typedef enum {
+    FD_INFO_MIXER,
+    FD_INFO_STREAM,
+} fd_info_type_t;
+
+typedef struct fd_info fd_info;
+
+struct fd_info {
+    pthread_mutex_t mutex;
+    int ref;
+    int unusable;
+
+    fd_info_type_t type;
+    int app_fd, thread_fd;
+
+    pa_sample_spec sample_spec;
+    size_t fragment_size;
+    unsigned n_fragments;
+
+    pa_threaded_mainloop *mainloop;
+    pa_context *context;
+    pa_stream *play_stream;
+    pa_stream *rec_stream;
+    int play_precork;
+    int rec_precork;
+
+    pa_io_event *io_event;
+    pa_io_event_flags_t io_flags;
+
+    void *buf;
+    size_t leftover;
+    size_t rec_offset;
+
+    int operation_success;
+
+    pa_cvolume sink_volume, source_volume;
+    uint32_t sink_index, source_index;
+    int volume_modify_count;
+
+    int optr_n_blocks;
+
+    PA_LLIST_FIELDS(fd_info);
+};
+
+static int dsp_drain(fd_info *i);
+static void fd_info_remove_from_list(fd_info *i);
+
+static pthread_mutex_t fd_infos_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t func_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static PA_LLIST_HEAD(fd_info, fd_infos) = NULL;
+
+static int (*_ioctl)(int, int, void*) = NULL;
+static int (*_close)(int) = NULL;
+static int (*_open)(const char *, int, mode_t) = NULL;
+static int (*___open_2)(const char *, int) = NULL;
+static FILE* (*_fopen)(const char *path, const char *mode) = NULL;
+static int (*_stat)(const char *, struct stat *) = NULL;
+#ifdef _STAT_VER
+static int (*___xstat)(int, const char *, struct stat *) = NULL;
+#endif
+#ifdef HAVE_OPEN64
+static int (*_open64)(const char *, int, mode_t) = NULL;
+static int (*___open64_2)(const char *, int) = NULL;
+static FILE* (*_fopen64)(const char *path, const char *mode) = NULL;
+static int (*_stat64)(const char *, struct stat64 *) = NULL;
+#ifdef _STAT_VER
+static int (*___xstat64)(int, const char *, struct stat64 *) = NULL;
+#endif
+#endif
+static int (*_fclose)(FILE *f) = NULL;
+static int (*_access)(const char *, int) = NULL;
+
+/* dlsym() violates ISO C, so confide the breakage into this function to
+ * avoid warnings. */
+typedef void (*fnptr)(void);
+static inline fnptr dlsym_fn(void *handle, const char *symbol) {
+    return (fnptr) (long) dlsym(handle, symbol);
+}
+
+#define LOAD_IOCTL_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_ioctl)  \
+        _ioctl = (int (*)(int, int, void*)) dlsym_fn(RTLD_NEXT, "ioctl"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_OPEN_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_open) \
+        _open = (int (*)(const char *, int, mode_t)) dlsym_fn(RTLD_NEXT, "open"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD___OPEN_2_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!___open_2) \
+        ___open_2 = (int (*)(const char *, int)) dlsym_fn(RTLD_NEXT, "__open_2"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_OPEN64_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_open64) \
+        _open64 = (int (*)(const char *, int, mode_t)) dlsym_fn(RTLD_NEXT, "open64"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD___OPEN64_2_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!___open64_2) \
+        ___open64_2 = (int (*)(const char *, int)) dlsym_fn(RTLD_NEXT, "__open64_2"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_CLOSE_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_close) \
+        _close = (int (*)(int)) dlsym_fn(RTLD_NEXT, "close"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_ACCESS_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_access) \
+        _access = (int (*)(const char*, int)) dlsym_fn(RTLD_NEXT, "access"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_STAT_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_stat) \
+        _stat = (int (*)(const char *, struct stat *)) dlsym_fn(RTLD_NEXT, "stat"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_STAT64_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_stat64) \
+        _stat64 = (int (*)(const char *, struct stat64 *)) dlsym_fn(RTLD_NEXT, "stat64"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_XSTAT_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!___xstat) \
+        ___xstat = (int (*)(int, const char *, struct stat *)) dlsym_fn(RTLD_NEXT, "__xstat"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_XSTAT64_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!___xstat64) \
+        ___xstat64 = (int (*)(int, const char *, struct stat64 *)) dlsym_fn(RTLD_NEXT, "__xstat64"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_FOPEN_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_fopen) \
+        _fopen = (FILE* (*)(const char *, const char*)) dlsym_fn(RTLD_NEXT, "fopen"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_FOPEN64_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_fopen64) \
+        _fopen64 = (FILE* (*)(const char *, const char*)) dlsym_fn(RTLD_NEXT, "fopen64"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_FCLOSE_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_fclose) \
+        _fclose = (int (*)(FILE *)) dlsym_fn(RTLD_NEXT, "fclose"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define CONTEXT_CHECK_DEAD_GOTO(i, label) do { \
+if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY) { \
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
+    goto label; \
+} \
+} while(0)
+
+#define PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, label) do { \
+if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY || \
+    !(i)->play_stream || pa_stream_get_state((i)->play_stream) != PA_STREAM_READY) { \
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
+    goto label; \
+} \
+} while(0)
+
+#define RECORD_STREAM_CHECK_DEAD_GOTO(i, label) do { \
+if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY || \
+    !(i)->rec_stream || pa_stream_get_state((i)->rec_stream) != PA_STREAM_READY) { \
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
+    goto label; \
+} \
+} while(0)
+
+static void debug(int level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
+
+#define DEBUG_LEVEL_ALWAYS                0
+#define DEBUG_LEVEL_NORMAL                1
+#define DEBUG_LEVEL_VERBOSE               2
+
+static void debug(int level, const char *format, ...) {
+    va_list ap;
+    const char *dlevel_s;
+    int dlevel;
+
+    dlevel_s = getenv("PADSP_DEBUG");
+    if (!dlevel_s)
+        return;
+
+    dlevel = atoi(dlevel_s);
+
+    if (dlevel < level)
+        return;
+
+    va_start(ap, format);
+    vfprintf(stderr, format, ap);
+    va_end(ap);
+}
+
+static int padsp_disabled(void) {
+    static int *sym;
+    static int sym_resolved = 0;
+
+    /* If the current process has a symbol __padsp_disabled__ we use
+     * it to detect whether we should enable our stuff or not. A
+     * program needs to be compiled with -rdynamic for this to work!
+     * The symbol must be an int containing a three bit bitmask: bit 1
+     * -> disable /dev/dsp emulation, bit 2 -> disable /dev/sndstat
+     * emulation, bit 3 -> disable /dev/mixer emulation. Hence a value
+     * of 7 disables padsp entirely. */
+
+    pthread_mutex_lock(&func_mutex);
+    if (!sym_resolved) {
+        sym = (int*) dlsym(RTLD_DEFAULT, "__padsp_disabled__");
+        sym_resolved = 1;
+    }
+    pthread_mutex_unlock(&func_mutex);
+
+    if (!sym)
+        return 0;
+
+    return *sym;
+}
+
+static int dsp_cloak_enable(void) {
+    if (padsp_disabled() & 1)
+        return 0;
+
+    if (getenv("PADSP_NO_DSP") || getenv("PULSE_INTERNAL"))
+        return 0;
+
+    return 1;
+}
+
+static int sndstat_cloak_enable(void) {
+    if (padsp_disabled() & 2)
+        return 0;
+
+    if (getenv("PADSP_NO_SNDSTAT") || getenv("PULSE_INTERNAL"))
+        return 0;
+
+    return 1;
+}
+
+static int mixer_cloak_enable(void) {
+    if (padsp_disabled() & 4)
+        return 0;
+
+    if (getenv("PADSP_NO_MIXER") || getenv("PULSE_INTERNAL"))
+        return 0;
+
+    return 1;
+}
+static pthread_key_t recursion_key;
+
+static void recursion_key_alloc(void) {
+    pthread_key_create(&recursion_key, NULL);
+}
+
+static int function_enter(void) {
+    /* Avoid recursive calls */
+    static pthread_once_t recursion_key_once = PTHREAD_ONCE_INIT;
+    pthread_once(&recursion_key_once, recursion_key_alloc);
+
+    if (pthread_getspecific(recursion_key))
+        return 0;
+
+    pthread_setspecific(recursion_key, (void*) 1);
+    return 1;
+}
+
+static void function_exit(void) {
+    pthread_setspecific(recursion_key, NULL);
+}
+
+static void fd_info_free(fd_info *i) {
+    assert(i);
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": freeing fd info (fd=%i)\n", i->app_fd);
+
+    dsp_drain(i);
+
+    if (i->mainloop)
+        pa_threaded_mainloop_stop(i->mainloop);
+
+    if (i->play_stream) {
+        pa_stream_disconnect(i->play_stream);
+        pa_stream_unref(i->play_stream);
+    }
+
+    if (i->rec_stream) {
+        pa_stream_disconnect(i->rec_stream);
+        pa_stream_unref(i->rec_stream);
+    }
+
+    if (i->context) {
+        pa_context_disconnect(i->context);
+        pa_context_unref(i->context);
+    }
+
+    if (i->mainloop)
+        pa_threaded_mainloop_free(i->mainloop);
+
+    if (i->app_fd >= 0) {
+        LOAD_CLOSE_FUNC();
+        _close(i->app_fd);
+    }
+
+    if (i->thread_fd >= 0) {
+        LOAD_CLOSE_FUNC();
+        _close(i->thread_fd);
+    }
+
+    free(i->buf);
+
+    pthread_mutex_destroy(&i->mutex);
+    free(i);
+}
+
+static fd_info *fd_info_ref(fd_info *i) {
+    assert(i);
+
+    pthread_mutex_lock(&i->mutex);
+    assert(i->ref >= 1);
+    i->ref++;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": ref++, now %i\n", i->ref);
+    pthread_mutex_unlock(&i->mutex);
+
+    return i;
+}
+
+static void fd_info_unref(fd_info *i) {
+    int r;
+    pthread_mutex_lock(&i->mutex);
+    assert(i->ref >= 1);
+    r = --i->ref;
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": ref--, now %i\n", i->ref);
+    pthread_mutex_unlock(&i->mutex);
+
+    if (r <= 0)
+        fd_info_free(i);
+}
+
+static void context_state_cb(pa_context *c, void *userdata) {
+    fd_info *i = userdata;
+    assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_READY:
+        case PA_CONTEXT_TERMINATED:
+        case PA_CONTEXT_FAILED:
+            pa_threaded_mainloop_signal(i->mainloop, 0);
+            break;
+
+        case PA_CONTEXT_UNCONNECTED:
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+    }
+}
+
+static void reset_params(fd_info *i) {
+    assert(i);
+
+    i->sample_spec.format = PA_SAMPLE_U8;
+    i->sample_spec.channels = 1;
+    i->sample_spec.rate = 8000;
+    i->fragment_size = 0;
+    i->n_fragments = 0;
+}
+
+static const char *client_name(char *buf, size_t n) {
+    char *p;
+    const char *e;
+
+    if ((e = getenv("PADSP_CLIENT_NAME")))
+        return e;
+
+    if ((p = pa_get_binary_name_malloc())) {
+        snprintf(buf, n, "OSS Emulation[%s]", p);
+        pa_xfree(p);
+    } else
+        snprintf(buf, n, "OSS");
+
+    return buf;
+}
+
+static const char *stream_name(void) {
+    const char *e;
+
+    if ((e = getenv("PADSP_STREAM_NAME")))
+        return e;
+
+    return "Audio Stream";
+}
+
+static void atfork_prepare(void) {
+    fd_info *i;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_prepare() enter\n");
+
+    function_enter();
+
+    pthread_mutex_lock(&fd_infos_mutex);
+
+    for (i = fd_infos; i; i = i->next) {
+        pthread_mutex_lock(&i->mutex);
+        pa_threaded_mainloop_lock(i->mainloop);
+    }
+
+    pthread_mutex_lock(&func_mutex);
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_prepare() exit\n");
+}
+
+static void atfork_parent(void) {
+    fd_info *i;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_parent() enter\n");
+
+    pthread_mutex_unlock(&func_mutex);
+
+    for (i = fd_infos; i; i = i->next) {
+        pa_threaded_mainloop_unlock(i->mainloop);
+        pthread_mutex_unlock(&i->mutex);
+    }
+
+    pthread_mutex_unlock(&fd_infos_mutex);
+
+    function_exit();
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_parent() exit\n");
+}
+
+static void atfork_child(void) {
+    fd_info *i;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_child() enter\n");
+
+    /* We do only the bare minimum to get all fds closed */
+    pthread_mutex_init(&func_mutex, NULL);
+    pthread_mutex_init(&fd_infos_mutex, NULL);
+
+    for (i = fd_infos; i; i = i->next) {
+        pthread_mutex_init(&i->mutex, NULL);
+
+        if (i->context) {
+            pa_context_disconnect(i->context);
+            pa_context_unref(i->context);
+            i->context = NULL;
+        }
+
+        if (i->play_stream) {
+            pa_stream_unref(i->play_stream);
+            i->play_stream = NULL;
+        }
+
+        if (i->rec_stream) {
+            pa_stream_unref(i->rec_stream);
+            i->rec_stream = NULL;
+        }
+
+        if (i->app_fd >= 0) {
+            LOAD_CLOSE_FUNC();
+            _close(i->app_fd);
+            i->app_fd = -1;
+        }
+
+        if (i->thread_fd >= 0) {
+            LOAD_CLOSE_FUNC();
+            _close(i->thread_fd);
+            i->thread_fd = -1;
+        }
+
+        i->unusable = 1;
+    }
+
+    function_exit();
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_child() exit\n");
+}
+
+static void install_atfork(void) {
+    pthread_atfork(atfork_prepare, atfork_parent, atfork_child);
+}
+
+static void stream_success_cb(pa_stream *s, int success, void *userdata) {
+    fd_info *i = userdata;
+
+    assert(s);
+    assert(i);
+
+    i->operation_success = success;
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+}
+
+static void context_success_cb(pa_context *c, int success, void *userdata) {
+    fd_info *i = userdata;
+
+    assert(c);
+    assert(i);
+
+    i->operation_success = success;
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+}
+
+static fd_info* fd_info_new(fd_info_type_t type, int *_errno) {
+    fd_info *i;
+    int sfds[2] = { -1, -1 };
+    char name[64];
+    static pthread_once_t install_atfork_once = PTHREAD_ONCE_INIT;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": fd_info_new()\n");
+
+    signal(SIGPIPE, SIG_IGN); /* Yes, ugly as hell */
+
+    pthread_once(&install_atfork_once, install_atfork);
+
+    if (!(i = malloc(sizeof(fd_info)))) {
+        *_errno = ENOMEM;
+        goto fail;
+    }
+
+    i->app_fd = i->thread_fd = -1;
+    i->type = type;
+
+    i->mainloop = NULL;
+    i->context = NULL;
+    i->play_stream = NULL;
+    i->rec_stream = NULL;
+    i->play_precork = 0;
+    i->rec_precork = 0;
+    i->io_event = NULL;
+    i->io_flags = 0;
+    pthread_mutex_init(&i->mutex, NULL);
+    i->ref = 1;
+    i->buf = NULL;
+    i->leftover = 0;
+    i->rec_offset = 0;
+    i->unusable = 0;
+    pa_cvolume_reset(&i->sink_volume, 2);
+    pa_cvolume_reset(&i->source_volume, 2);
+    i->volume_modify_count = 0;
+    i->sink_index = (uint32_t) -1;
+    i->source_index = (uint32_t) -1;
+    i->optr_n_blocks = 0;
+    PA_LLIST_INIT(fd_info, i);
+
+    reset_params(i);
+
+    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfds) < 0) {
+        *_errno = errno;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": socket() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    i->app_fd = sfds[0];
+    i->thread_fd = sfds[1];
+
+    if (!(i->mainloop = pa_threaded_mainloop_new())) {
+        *_errno = EIO;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_threaded_mainloop_new() failed\n");
+        goto fail;
+    }
+
+    if (!(i->context = pa_context_new(pa_threaded_mainloop_get_api(i->mainloop), client_name(name, sizeof(name))))) {
+        *_errno = EIO;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_context_new() failed\n");
+        goto fail;
+    }
+
+    pa_context_set_state_callback(i->context, context_state_cb, i);
+
+    if (pa_context_connect(i->context, NULL, 0, NULL) < 0) {
+        *_errno = ECONNREFUSED;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    pa_threaded_mainloop_lock(i->mainloop);
+
+    if (pa_threaded_mainloop_start(i->mainloop) < 0) {
+        *_errno = EIO;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_threaded_mainloop_start() failed\n");
+        goto unlock_and_fail;
+    }
+
+    /* Wait until the context is ready */
+    pa_threaded_mainloop_wait(i->mainloop);
+
+    if (pa_context_get_state(i->context) != PA_CONTEXT_READY) {
+        *_errno = ECONNREFUSED;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto unlock_and_fail;
+    }
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+    return i;
+
+unlock_and_fail:
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+fail:
+
+    if (i)
+        fd_info_unref(i);
+
+    return NULL;
+}
+
+static void fd_info_add_to_list(fd_info *i) {
+    assert(i);
+
+    pthread_mutex_lock(&fd_infos_mutex);
+    PA_LLIST_PREPEND(fd_info, fd_infos, i);
+    pthread_mutex_unlock(&fd_infos_mutex);
+
+    fd_info_ref(i);
+}
+
+static void fd_info_remove_from_list(fd_info *i) {
+    assert(i);
+
+    pthread_mutex_lock(&fd_infos_mutex);
+    PA_LLIST_REMOVE(fd_info, fd_infos, i);
+    pthread_mutex_unlock(&fd_infos_mutex);
+
+    fd_info_unref(i);
+}
+
+static fd_info* fd_info_find(int fd) {
+    fd_info *i;
+
+    pthread_mutex_lock(&fd_infos_mutex);
+
+    for (i = fd_infos; i; i = i->next)
+        if (i->app_fd == fd && !i->unusable) {
+            fd_info_ref(i);
+            break;
+        }
+
+    pthread_mutex_unlock(&fd_infos_mutex);
+
+    return i;
+}
+
+static void fix_metrics(fd_info *i) {
+    size_t fs;
+    char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
+
+    fs = pa_frame_size(&i->sample_spec);
+
+    /* Don't fix things more than necessary */
+    if ((i->fragment_size % fs) == 0 &&
+        i->n_fragments >= 2 &&
+        i->fragment_size > 0)
+        return;
+
+    i->fragment_size = (i->fragment_size/fs)*fs;
+
+    /* Number of fragments set? */
+    if (i->n_fragments < 2) {
+        if (i->fragment_size > 0) {
+            i->n_fragments = (unsigned) (pa_bytes_per_second(&i->sample_spec) / 2 / i->fragment_size);
+            if (i->n_fragments < 2)
+                i->n_fragments = 2;
+        } else
+            i->n_fragments = 12;
+    }
+
+    /* Fragment size set? */
+    if (i->fragment_size <= 0) {
+        i->fragment_size = pa_bytes_per_second(&i->sample_spec) / 2 / i->n_fragments;
+        if (i->fragment_size < 1024)
+            i->fragment_size = 1024;
+    }
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": sample spec: %s\n", pa_sample_spec_snprint(t, sizeof(t), &i->sample_spec));
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": fixated metrics to %i fragments, %li bytes each.\n", i->n_fragments, (long)i->fragment_size);
+}
+
+static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
+    fd_info *i = userdata;
+    assert(s);
+
+    if (i->io_event) {
+        pa_mainloop_api *api;
+        size_t n;
+
+        api = pa_threaded_mainloop_get_api(i->mainloop);
+
+        if (s == i->play_stream) {
+            n = pa_stream_writable_size(i->play_stream);
+            if (n == (size_t)-1) {
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_writable_size(): %s\n",
+                    pa_strerror(pa_context_errno(i->context)));
+            }
+
+            if (n >= i->fragment_size)
+                i->io_flags |= PA_IO_EVENT_INPUT;
+            else
+                i->io_flags &= ~PA_IO_EVENT_INPUT;
+        }
+
+        if (s == i->rec_stream) {
+            n = pa_stream_readable_size(i->rec_stream);
+            if (n == (size_t)-1) {
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_readable_size(): %s\n",
+                    pa_strerror(pa_context_errno(i->context)));
+            }
+
+            if (n >= i->fragment_size)
+                i->io_flags |= PA_IO_EVENT_OUTPUT;
+            else
+                i->io_flags &= ~PA_IO_EVENT_OUTPUT;
+        }
+
+        api->io_enable(i->io_event, i->io_flags);
+    }
+}
+
+static void stream_latency_update_cb(pa_stream *s, void *userdata) {
+    fd_info *i = userdata;
+    assert(s);
+
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+}
+
+static void fd_info_shutdown(fd_info *i) {
+    assert(i);
+
+    if (i->io_event) {
+        pa_mainloop_api *api;
+        api = pa_threaded_mainloop_get_api(i->mainloop);
+        api->io_free(i->io_event);
+        i->io_event = NULL;
+        i->io_flags = 0;
+    }
+
+    if (i->thread_fd >= 0) {
+        close(i->thread_fd);
+        i->thread_fd = -1;
+    }
+}
+
+static int fd_info_copy_data(fd_info *i, int force) {
+    size_t n;
+
+    if (!i->play_stream && !i->rec_stream)
+        return -1;
+
+    if ((i->play_stream) && (pa_stream_get_state(i->play_stream) == PA_STREAM_READY)) {
+        n = pa_stream_writable_size(i->play_stream);
+
+        if (n == (size_t)-1) {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_writable_size(): %s\n",
+                pa_strerror(pa_context_errno(i->context)));
+            return -1;
+        }
+
+        while (n >= i->fragment_size || force) {
+            ssize_t r;
+            size_t to_write;
+
+            if (!i->buf) {
+                if (!(i->buf = malloc(i->fragment_size))) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": malloc() failed.\n");
+                    return -1;
+                }
+
+                i->leftover = 0;
+            }
+
+            if ((r = read(i->thread_fd, ((uint8_t *) i->buf) + i->leftover, i->fragment_size - i->leftover)) <= 0) {
+
+                if (errno == EAGAIN)
+                    break;
+
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": read(): %s\n", r == 0 ? "EOF" : strerror(errno));
+                return -1;
+            }
+
+            to_write = pa_frame_align(r + i->leftover, &i->sample_spec);
+
+            if (pa_stream_write(i->play_stream, i->buf, to_write, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_write(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                return -1;
+            }
+
+            i->leftover += r - to_write;
+            if (i->leftover)
+                memmove(i->buf, ((uint8_t *) i->buf) + to_write, i->leftover);
+
+            assert(n >= (size_t) to_write);
+            n -= (size_t) to_write;
+        }
+
+        if (n >= i->fragment_size)
+            i->io_flags |= PA_IO_EVENT_INPUT;
+        else
+            i->io_flags &= ~PA_IO_EVENT_INPUT;
+    }
+
+    if ((i->rec_stream) && (pa_stream_get_state(i->rec_stream) == PA_STREAM_READY)) {
+        n = pa_stream_readable_size(i->rec_stream);
+
+        if (n == (size_t)-1) {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_readable_size(): %s\n",
+                pa_strerror(pa_context_errno(i->context)));
+            return -1;
+        }
+
+        while (n >= i->fragment_size || force) {
+            ssize_t r;
+            const void *data;
+            const char *buf;
+            size_t len;
+
+            if (pa_stream_peek(i->rec_stream, &data, &len) < 0) {
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_peek(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                return -1;
+            }
+
+            if (len <= 0)
+                break;
+
+            if (!data) {
+                /* Maybe we should generate silence here, but I'm lazy and
+                 * I'll just skip any holes in the stream. */
+                if (pa_stream_drop(i->rec_stream) < 0) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drop(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                    return -1;
+                }
+
+                assert(n >= len);
+                n -= len;
+                continue;
+            }
+
+            buf = (const char*)data + i->rec_offset;
+
+            if ((r = write(i->thread_fd, buf, len - i->rec_offset)) <= 0) {
+
+                if (errno == EAGAIN)
+                    break;
+
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": write(): %s\n", strerror(errno));
+                return -1;
+            }
+
+            assert((size_t)r <= len - i->rec_offset);
+            i->rec_offset += (size_t) r;
+
+            if (i->rec_offset == len) {
+                if (pa_stream_drop(i->rec_stream) < 0) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drop(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                    return -1;
+                }
+                i->rec_offset = 0;
+            }
+
+            assert(n >= (size_t) r);
+            n -= (size_t) r;
+        }
+
+        if (n >= i->fragment_size)
+            i->io_flags |= PA_IO_EVENT_OUTPUT;
+        else
+            i->io_flags &= ~PA_IO_EVENT_OUTPUT;
+    }
+
+    if (i->io_event) {
+        pa_mainloop_api *api;
+
+        api = pa_threaded_mainloop_get_api(i->mainloop);
+        api->io_enable(i->io_event, i->io_flags);
+    }
+
+    /* So, we emptied the socket now, let's tell dsp_empty_socket()
+     * about this */
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+
+    return 0;
+}
+
+static void stream_state_cb(pa_stream *s, void * userdata) {
+    fd_info *i = userdata;
+    assert(s);
+
+    switch (pa_stream_get_state(s)) {
+
+        case PA_STREAM_READY:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": stream established.\n");
+            break;
+
+        case PA_STREAM_FAILED:
+            if (s == i->play_stream) {
+                debug(DEBUG_LEVEL_NORMAL,
+                    __FILE__": pa_stream_connect_playback() failed: %s\n",
+                    pa_strerror(pa_context_errno(i->context)));
+                pa_stream_unref(i->play_stream);
+                i->play_stream = NULL;
+            } else if (s == i->rec_stream) {
+                debug(DEBUG_LEVEL_NORMAL,
+                    __FILE__": pa_stream_connect_record() failed: %s\n",
+                    pa_strerror(pa_context_errno(i->context)));
+                pa_stream_unref(i->rec_stream);
+                i->rec_stream = NULL;
+            }
+            fd_info_shutdown(i);
+            break;
+
+        case PA_STREAM_TERMINATED:
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+            break;
+    }
+}
+
+static int create_playback_stream(fd_info *i) {
+    pa_buffer_attr attr;
+    int n, flags;
+
+    assert(i);
+
+    fix_metrics(i);
+
+    if (!(i->play_stream = pa_stream_new(i->context, stream_name(), &i->sample_spec, NULL))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    pa_stream_set_state_callback(i->play_stream, stream_state_cb, i);
+    pa_stream_set_write_callback(i->play_stream, stream_request_cb, i);
+    pa_stream_set_latency_update_callback(i->play_stream, stream_latency_update_cb, i);
+
+    memset(&attr, 0, sizeof(attr));
+    attr.maxlength = (uint32_t) (i->fragment_size * (i->n_fragments+1));
+    attr.tlength = (uint32_t) (i->fragment_size * i->n_fragments);
+    attr.prebuf = (uint32_t) i->fragment_size;
+    attr.minreq = (uint32_t) i->fragment_size;
+
+    flags = PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_EARLY_REQUESTS;
+    if (i->play_precork) {
+        flags |= PA_STREAM_START_CORKED;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": creating stream corked\n");
+    }
+    if (pa_stream_connect_playback(i->play_stream, NULL, &attr, flags, NULL, NULL) < 0) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    n = (int) i->fragment_size;
+    setsockopt(i->app_fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n));
+    n = (int) i->fragment_size;
+    setsockopt(i->thread_fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+static int create_record_stream(fd_info *i) {
+    pa_buffer_attr attr;
+    int n, flags;
+
+    assert(i);
+
+    fix_metrics(i);
+
+    if (!(i->rec_stream = pa_stream_new(i->context, stream_name(), &i->sample_spec, NULL))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    pa_stream_set_state_callback(i->rec_stream, stream_state_cb, i);
+    pa_stream_set_read_callback(i->rec_stream, stream_request_cb, i);
+    pa_stream_set_latency_update_callback(i->rec_stream, stream_latency_update_cb, i);
+
+    memset(&attr, 0, sizeof(attr));
+    attr.maxlength = (uint32_t) (i->fragment_size * (i->n_fragments+1));
+    attr.fragsize = (uint32_t) i->fragment_size;
+
+    flags = PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE;
+    if (i->rec_precork) {
+        flags |= PA_STREAM_START_CORKED;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": creating stream corked\n");
+    }
+    if (pa_stream_connect_record(i->rec_stream, NULL, &attr, flags) < 0) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    n = (int) i->fragment_size;
+    setsockopt(i->app_fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
+    n = (int) i->fragment_size;
+    setsockopt(i->thread_fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n));
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+static void free_streams(fd_info *i) {
+    assert(i);
+
+    if (i->play_stream) {
+        pa_stream_disconnect(i->play_stream);
+        pa_stream_unref(i->play_stream);
+        i->play_stream = NULL;
+        i->io_flags |= PA_IO_EVENT_INPUT;
+    }
+
+    if (i->rec_stream) {
+        pa_stream_disconnect(i->rec_stream);
+        pa_stream_unref(i->rec_stream);
+        i->rec_stream = NULL;
+        i->io_flags |= PA_IO_EVENT_OUTPUT;
+    }
+
+    if (i->io_event) {
+        pa_mainloop_api *api;
+
+        api = pa_threaded_mainloop_get_api(i->mainloop);
+        api->io_enable(i->io_event, i->io_flags);
+    }
+}
+
+static void io_event_cb(pa_mainloop_api *api, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
+    fd_info *i = userdata;
+
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+
+    if (flags & PA_IO_EVENT_INPUT) {
+
+        if (!i->play_stream) {
+            if (create_playback_stream(i) < 0)
+                goto fail;
+        } else {
+            if (fd_info_copy_data(i, 0) < 0)
+                goto fail;
+        }
+
+    } else if (flags & PA_IO_EVENT_OUTPUT) {
+
+        if (!i->rec_stream) {
+            if (create_record_stream(i) < 0)
+                goto fail;
+        } else {
+            if (fd_info_copy_data(i, 0) < 0)
+                goto fail;
+        }
+
+    } else if (flags & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR))
+        goto fail;
+
+    return;
+
+fail:
+    /* We can't do anything better than removing the event source */
+    fd_info_shutdown(i);
+}
+
+static int dsp_open(int flags, int *_errno) {
+    fd_info *i;
+    pa_mainloop_api *api;
+    int ret;
+    int f;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": dsp_open()\n");
+
+    if (!(i = fd_info_new(FD_INFO_STREAM, _errno)))
+        return -1;
+
+    if ((flags & O_NONBLOCK) == O_NONBLOCK) {
+        if ((f = fcntl(i->app_fd, F_GETFL)) >= 0)
+            fcntl(i->app_fd, F_SETFL, f|O_NONBLOCK);
+    }
+    if ((f = fcntl(i->thread_fd, F_GETFL)) >= 0)
+        fcntl(i->thread_fd, F_SETFL, f|O_NONBLOCK);
+
+    fcntl(i->app_fd, F_SETFD, FD_CLOEXEC);
+    fcntl(i->thread_fd, F_SETFD, FD_CLOEXEC);
+
+    pa_threaded_mainloop_lock(i->mainloop);
+    api = pa_threaded_mainloop_get_api(i->mainloop);
+
+    switch (flags & O_ACCMODE) {
+    case O_RDONLY:
+        i->io_flags = PA_IO_EVENT_OUTPUT;
+        shutdown(i->thread_fd, SHUT_RD);
+        shutdown(i->app_fd, SHUT_WR);
+        break;
+    case O_WRONLY:
+        i->io_flags = PA_IO_EVENT_INPUT;
+        shutdown(i->thread_fd, SHUT_WR);
+        shutdown(i->app_fd, SHUT_RD);
+        break;
+    case O_RDWR:
+        i->io_flags = PA_IO_EVENT_INPUT | PA_IO_EVENT_OUTPUT;
+        break;
+    default:
+        pa_threaded_mainloop_unlock(i->mainloop);
+        fd_info_unref(i);
+        *_errno = EIO;
+        return -1;
+    }
+
+    if (!(i->io_event = api->io_new(api, i->thread_fd, i->io_flags, io_event_cb, i)))
+        goto fail;
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": dsp_open() succeeded, fd=%i\n", i->app_fd);
+
+    fd_info_add_to_list(i);
+    ret = i->app_fd;
+    fd_info_unref(i);
+
+    return ret;
+
+fail:
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    if (i)
+        fd_info_unref(i);
+
+    *_errno = EIO;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": dsp_open() failed\n");
+
+    return -1;
+}
+
+static void sink_info_cb(pa_context *context, const pa_sink_info *si, int eol, void *userdata) {
+    fd_info *i = userdata;
+
+    if (eol < 0) {
+        i->operation_success = 0;
+        pa_threaded_mainloop_signal(i->mainloop, 0);
+        return;
+    }
+
+    if (eol)
+        return;
+
+    if (!pa_cvolume_equal(&i->sink_volume, &si->volume))
+        i->volume_modify_count++;
+
+    i->sink_volume = si->volume;
+    i->sink_index = si->index;
+
+    i->operation_success = 1;
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+}
+
+static void source_info_cb(pa_context *context, const pa_source_info *si, int eol, void *userdata) {
+    fd_info *i = userdata;
+
+    if (eol < 0) {
+        i->operation_success = 0;
+        pa_threaded_mainloop_signal(i->mainloop, 0);
+        return;
+    }
+
+    if (eol)
+        return;
+
+    if (!pa_cvolume_equal(&i->source_volume, &si->volume))
+        i->volume_modify_count++;
+
+    i->source_volume = si->volume;
+    i->source_index = si->index;
+
+    i->operation_success = 1;
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+}
+
+static void subscribe_cb(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    fd_info *i = userdata;
+    pa_operation *o = NULL;
+
+    if (i->sink_index != idx)
+        return;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
+        return;
+
+    if (!(o = pa_context_get_sink_info_by_index(i->context, i->sink_index, sink_info_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
+        return;
+    }
+
+    pa_operation_unref(o);
+}
+
+static int mixer_open(int flags, int *_errno) {
+    fd_info *i;
+    pa_operation *o = NULL;
+    int ret;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": mixer_open()\n");
+
+    if (!(i = fd_info_new(FD_INFO_MIXER, _errno)))
+        return -1;
+
+    pa_threaded_mainloop_lock(i->mainloop);
+
+    pa_context_set_subscribe_callback(i->context, subscribe_cb, i);
+
+    if (!(o = pa_context_subscribe(i->context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, context_success_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to subscribe to events: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        pa_threaded_mainloop_wait(i->mainloop);
+        CONTEXT_CHECK_DEAD_GOTO(i, fail);
+    }
+
+    pa_operation_unref(o);
+    o = NULL;
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__":Failed to subscribe to events: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    /* Get sink info */
+
+    if (!(o = pa_context_get_sink_info_by_name(i->context, NULL, sink_info_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        pa_threaded_mainloop_wait(i->mainloop);
+        CONTEXT_CHECK_DEAD_GOTO(i, fail);
+    }
+
+    pa_operation_unref(o);
+    o = NULL;
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    /* Get source info */
+
+    if (!(o = pa_context_get_source_info_by_name(i->context, NULL, source_info_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get source info: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        pa_threaded_mainloop_wait(i->mainloop);
+        CONTEXT_CHECK_DEAD_GOTO(i, fail);
+    }
+
+    pa_operation_unref(o);
+    o = NULL;
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get source info: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": mixer_open() succeeded, fd=%i\n", i->app_fd);
+
+    fd_info_add_to_list(i);
+    ret = i->app_fd;
+    fd_info_unref(i);
+
+    return ret;
+
+fail:
+    if (o)
+        pa_operation_unref(o);
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    if (i)
+        fd_info_unref(i);
+
+    *_errno = EIO;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": mixer_open() failed\n");
+
+    return -1;
+}
+
+static int sndstat_open(int flags, int *_errno) {
+    static const char sndstat[] =
+        "Sound Driver:3.8.1a-980706 (PulseAudio Virtual OSS)\n"
+        "Kernel: POSIX\n"
+        "Config options: 0\n"
+        "\n"
+        "Installed drivers:\n"
+        "Type 255: PulseAudio Virtual OSS\n"
+        "\n"
+        "Card config:\n"
+        "PulseAudio Virtual OSS\n"
+        "\n"
+        "Audio devices:\n"
+        "0: PulseAudio Virtual OSS\n"
+        "\n"
+        "Synth devices: NOT ENABLED IN CONFIG\n"
+        "\n"
+        "Midi devices:\n"
+        "\n"
+        "Timers:\n"
+        "\n"
+        "Mixers:\n"
+        "0: PulseAudio Virtual OSS\n";
+
+    char *fn;
+    mode_t u;
+    int fd = -1;
+    int e;
+
+    fn = pa_sprintf_malloc("%s" PA_PATH_SEP "padsp-sndstat-XXXXXX", pa_get_temp_dir());
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": sndstat_open()\n");
+
+    if (flags != O_RDONLY
+#ifdef O_LARGEFILE
+        && flags != (O_RDONLY|O_LARGEFILE)
+#endif
+       ) {
+        *_errno = EACCES;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": bad access!\n");
+        goto fail;
+    }
+
+    u = umask(0077);
+    fd = mkstemp(fn);
+    e = errno;
+    umask(u);
+
+    if (fd < 0) {
+        *_errno = e;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": mkstemp() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    unlink(fn);
+    pa_xfree(fn);
+    fn = NULL;
+
+    if (write(fd, sndstat, sizeof(sndstat) -1) != sizeof(sndstat)-1) {
+        *_errno = errno;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": write() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    if (lseek(fd, SEEK_SET, 0) < 0) {
+        *_errno = errno;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": lseek() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    return fd;
+
+fail:
+    pa_xfree(fn);
+    if (fd >= 0)
+        close(fd);
+    return -1;
+}
+
+static int real_open(const char *filename, int flags, mode_t mode) {
+    int r, _errno = 0;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": open(%s)\n", filename?filename:"NULL");
+
+    if (!function_enter()) {
+        LOAD_OPEN_FUNC();
+        return _open(filename, flags, mode);
+    }
+
+    if (filename && dsp_cloak_enable() && (pa_streq(filename, "/dev/dsp") || pa_streq(filename, "/dev/adsp") || pa_streq(filename, "/dev/audio")))
+        r = dsp_open(flags, &_errno);
+    else if (filename && mixer_cloak_enable() && pa_streq(filename, "/dev/mixer"))
+        r = mixer_open(flags, &_errno);
+    else if (filename && sndstat_cloak_enable() && pa_streq(filename, "/dev/sndstat"))
+        r = sndstat_open(flags, &_errno);
+    else {
+        function_exit();
+        LOAD_OPEN_FUNC();
+        return _open(filename, flags, mode);
+    }
+
+    function_exit();
+
+    if (_errno)
+        errno = _errno;
+
+    return r;
+}
+
+int open(const char *filename, int flags, ...) {
+    va_list args;
+    mode_t mode = 0;
+
+    if (flags & O_CREAT) {
+        va_start(args, flags);
+        if (sizeof(mode_t) < sizeof(int))
+            mode = (mode_t) va_arg(args, int);
+        else
+            mode = va_arg(args, mode_t);
+        va_end(args);
+    }
+
+    return real_open(filename, flags, mode);
+}
+
+static bool is_audio_device_node(const char *path) {
+    return
+        pa_streq(path, "/dev/dsp") ||
+        pa_streq(path, "/dev/adsp") ||
+        pa_streq(path, "/dev/audio") ||
+        pa_streq(path, "/dev/sndstat") ||
+        pa_streq(path, "/dev/mixer");
+}
+
+int __open_2(const char *filename, int flags) {
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": __open_2(%s)\n", filename?filename:"NULL");
+
+    if ((flags & O_CREAT) ||
+        !filename ||
+        !is_audio_device_node(filename)) {
+        LOAD___OPEN_2_FUNC();
+        return ___open_2(filename, flags);
+    }
+    return real_open(filename, flags, 0);
+}
+
+static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {
+    int ret = -1;
+
+    switch (request) {
+        case SOUND_MIXER_READ_DEVMASK :
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_DEVMASK\n");
+
+            *(int*) argp = SOUND_MASK_PCM | SOUND_MASK_IGAIN;
+            break;
+
+        case SOUND_MIXER_READ_RECMASK :
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_RECMASK\n");
+
+            *(int*) argp = SOUND_MASK_IGAIN;
+            break;
+
+        case SOUND_MIXER_READ_STEREODEVS:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_STEREODEVS\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+            *(int*) argp = 0;
+            if (i->sink_volume.channels > 1)
+                *(int*) argp |= SOUND_MASK_PCM;
+            if (i->source_volume.channels > 1)
+                *(int*) argp |= SOUND_MASK_IGAIN;
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            break;
+
+        case SOUND_MIXER_READ_RECSRC:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_RECSRC\n");
+
+            *(int*) argp = SOUND_MASK_IGAIN;
+            break;
+
+        case SOUND_MIXER_WRITE_RECSRC:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_WRITE_RECSRC\n");
+            break;
+
+        case SOUND_MIXER_READ_CAPS:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_CAPS\n");
+
+            *(int*) argp = 0;
+            break;
+
+        case SOUND_MIXER_READ_PCM:
+        case SOUND_MIXER_READ_IGAIN: {
+            pa_cvolume *v;
+
+            if (request == SOUND_MIXER_READ_PCM)
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_PCM\n");
+            else
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_IGAIN\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            if (request == SOUND_MIXER_READ_PCM)
+                v = &i->sink_volume;
+            else
+                v = &i->source_volume;
+
+            *(int*) argp =
+                ((v->values[0]*100/PA_VOLUME_NORM)) |
+                ((v->values[v->channels > 1 ? 1 : 0]*100/PA_VOLUME_NORM) << 8);
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            break;
+        }
+
+        case SOUND_MIXER_WRITE_PCM:
+        case SOUND_MIXER_WRITE_IGAIN: {
+            pa_cvolume v, *pv;
+
+            if (request == SOUND_MIXER_WRITE_PCM)
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_WRITE_PCM\n");
+            else
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_WRITE_IGAIN\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            if (request == SOUND_MIXER_WRITE_PCM) {
+                v = i->sink_volume;
+                pv = &i->sink_volume;
+            } else {
+                v = i->source_volume;
+                pv = &i->source_volume;
+            }
+
+            pv->values[0] = ((*(int*) argp & 0xFF)*PA_VOLUME_NORM)/100;
+            pv->values[1] = ((*(int*) argp >> 8)*PA_VOLUME_NORM)/100;
+
+            if (!pa_cvolume_equal(pv, &v)) {
+                pa_operation *o;
+
+                if (request == SOUND_MIXER_WRITE_PCM)
+                    o = pa_context_set_sink_volume_by_index(i->context, i->sink_index, pv, context_success_cb, i);
+                else
+                    o = pa_context_set_source_volume_by_index(i->context, i->source_index, pv, context_success_cb, i);
+
+                if (!o)
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__":Failed set volume: %s", pa_strerror(pa_context_errno(i->context)));
+                else {
+
+                    i->operation_success = 0;
+                    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+                        CONTEXT_CHECK_DEAD_GOTO(i, exit_loop);
+
+                        pa_threaded_mainloop_wait(i->mainloop);
+                    }
+                exit_loop:
+
+                    if (!i->operation_success)
+                        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to set volume: %s\n", pa_strerror(pa_context_errno(i->context)));
+
+                    pa_operation_unref(o);
+                }
+
+                /* We don't wait for completion here */
+                i->volume_modify_count++;
+            }
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            break;
+        }
+
+        case SOUND_MIXER_INFO: {
+            mixer_info *mi = argp;
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_INFO\n");
+
+            memset(mi, 0, sizeof(mixer_info));
+            strncpy(mi->id, "PULSEAUDIO", sizeof(mi->id));
+            strncpy(mi->name, "PulseAudio Virtual OSS", sizeof(mi->name));
+            pa_threaded_mainloop_lock(i->mainloop);
+            mi->modify_counter = i->volume_modify_count;
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+        }
+
+        default:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": unknown ioctl 0x%08lx\n", request);
+
+            *_errno = EINVAL;
+            goto fail;
+    }
+
+    ret = 0;
+
+fail:
+
+    return ret;
+}
+
+static int map_format(int *fmt, pa_sample_spec *ss) {
+
+    switch (*fmt) {
+        case AFMT_MU_LAW:
+            ss->format = PA_SAMPLE_ULAW;
+            break;
+
+        case AFMT_A_LAW:
+            ss->format = PA_SAMPLE_ALAW;
+            break;
+
+        case AFMT_S8:
+            *fmt = AFMT_U8;
+            /* fall through */
+        case AFMT_U8:
+            ss->format = PA_SAMPLE_U8;
+            break;
+
+        case AFMT_U16_BE:
+            *fmt = AFMT_S16_BE;
+            /* fall through */
+        case AFMT_S16_BE:
+            ss->format = PA_SAMPLE_S16BE;
+            break;
+
+        case AFMT_U16_LE:
+            *fmt = AFMT_S16_LE;
+            /* fall through */
+        case AFMT_S16_LE:
+            ss->format = PA_SAMPLE_S16LE;
+            break;
+
+        default:
+            ss->format = PA_SAMPLE_S16NE;
+            *fmt = AFMT_S16_NE;
+            break;
+    }
+
+    return 0;
+}
+
+static int map_format_back(pa_sample_format_t format) {
+    switch (format) {
+        case PA_SAMPLE_S16LE: return AFMT_S16_LE;
+        case PA_SAMPLE_S16BE: return AFMT_S16_BE;
+        case PA_SAMPLE_ULAW: return AFMT_MU_LAW;
+        case PA_SAMPLE_ALAW: return AFMT_A_LAW;
+        case PA_SAMPLE_U8: return AFMT_U8;
+        default:
+            abort();
+    }
+}
+
+static int dsp_flush_fd(int fd) {
+#ifdef SIOCINQ
+    int l;
+
+    if (ioctl(fd, SIOCINQ, &l) < 0) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ: %s\n", strerror(errno));
+        return -1;
+    }
+
+    while (l > 0) {
+        char buf[1024];
+        size_t k;
+        ssize_t r;
+
+        k = (size_t) l > sizeof(buf) ? sizeof(buf) : (size_t) l;
+        r = read(fd, buf, k);
+        if (r < 0) {
+            if (errno == EAGAIN)
+                break;
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": read(): %s\n", strerror(errno));
+            return -1;
+        } else if (r == 0)
+            break;
+        l -= r;
+    }
+
+    return 0;
+#else
+# warning "Your platform does not support SIOCINQ, something might not work as intended."
+    return 0;
+#endif
+}
+
+static int dsp_flush_socket(fd_info *i) {
+    int res = 0;
+
+    if ((i->thread_fd < 0) && (i->app_fd < 0))
+        return -1;
+
+    if (i->thread_fd >= 0)
+        res = dsp_flush_fd(i->thread_fd);
+
+    if (res < 0)
+        return res;
+
+    if (i->app_fd >= 0)
+        res = dsp_flush_fd(i->app_fd);
+
+    if (res < 0)
+        return res;
+
+    return 0;
+}
+
+static int dsp_empty_socket(fd_info *i) {
+#ifdef SIOCINQ
+    int ret = -1;
+
+    /* Empty the socket */
+    for (;;) {
+        int l;
+
+        if (i->thread_fd < 0)
+            break;
+
+        if (ioctl(i->thread_fd, SIOCINQ, &l) < 0) {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ: %s\n", strerror(errno));
+            break;
+        }
+
+        if (!l) {
+            ret = 0;
+            break;
+        }
+
+        pa_threaded_mainloop_wait(i->mainloop);
+    }
+
+    return ret;
+#else
+# warning "Your platform does not support SIOCINQ, something might not work as intended."
+    return 0;
+#endif
+}
+
+static int dsp_drain(fd_info *i) {
+    pa_operation *o = NULL;
+    int r = -1;
+
+    if (!i->mainloop)
+        return 0;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Draining.\n");
+
+    pa_threaded_mainloop_lock(i->mainloop);
+
+    if (dsp_empty_socket(i) < 0)
+        goto fail;
+
+    if (!i->play_stream)
+        goto fail;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Really draining.\n");
+
+    if (!(o = pa_stream_drain(i->play_stream, stream_success_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drain(): %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
+
+        pa_threaded_mainloop_wait(i->mainloop);
+    }
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drain() 2: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    r = 0;
+
+fail:
+
+    if (o)
+        pa_operation_unref(o);
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    return r;
+}
+
+static int dsp_trigger(fd_info *i) {
+    pa_operation *o = NULL;
+    int r = -1;
+
+    if (!i->play_stream)
+        return 0;
+
+    pa_threaded_mainloop_lock(i->mainloop);
+
+    if (dsp_empty_socket(i) < 0)
+        goto fail;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Triggering.\n");
+
+    if (!(o = pa_stream_trigger(i->play_stream, stream_success_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_trigger(): %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
+
+        pa_threaded_mainloop_wait(i->mainloop);
+    }
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_trigger(): %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    r = 0;
+
+fail:
+
+    if (o)
+        pa_operation_unref(o);
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    return r;
+}
+
+static int dsp_cork(fd_info *i, pa_stream *s, int b) {
+    pa_operation *o = NULL;
+    int r = -1;
+
+    pa_threaded_mainloop_lock(i->mainloop);
+
+    if (!(o = pa_stream_cork(s, b, stream_success_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_cork(): %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        if (s == i->play_stream)
+            PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
+        else if (s == i->rec_stream)
+            RECORD_STREAM_CHECK_DEAD_GOTO(i, fail);
+
+        pa_threaded_mainloop_wait(i->mainloop);
+    }
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_cork(): %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    r = 0;
+
+fail:
+
+    if (o)
+        pa_operation_unref(o);
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    return r;
+}
+
+static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {
+    int ret = -1;
+
+    if (i->thread_fd == -1) {
+        /*
+         * We've encountered some fatal error and are just waiting
+         * for a close.
+         */
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": got ioctl 0x%08lx in fatal error state\n", request);
+        *_errno = EIO;
+        return -1;
+    }
+
+    switch (request) {
+        case SNDCTL_DSP_SETFMT: {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETFMT: %i\n", *(int*) argp);
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            if (*(int*) argp == AFMT_QUERY)
+                *(int*) argp = map_format_back(i->sample_spec.format);
+            else {
+                map_format((int*) argp, &i->sample_spec);
+                free_streams(i);
+            }
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+        }
+
+        case SNDCTL_DSP_SPEED: {
+            pa_sample_spec ss;
+            int valid;
+            char t[256];
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SPEED: %i\n", *(int*) argp);
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            ss = i->sample_spec;
+            ss.rate = *(int*) argp;
+
+            if ((valid = pa_sample_spec_valid(&ss))) {
+                i->sample_spec = ss;
+                free_streams(i);
+            }
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": ss: %s\n", pa_sample_spec_snprint(t, sizeof(t), &i->sample_spec));
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            if (!valid) {
+                *_errno = EINVAL;
+                goto fail;
+            }
+
+            break;
+        }
+
+        case SNDCTL_DSP_STEREO:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_STEREO: %i\n", *(int*) argp);
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            i->sample_spec.channels = *(int*) argp ? 2 : 1;
+            free_streams(i);
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+            return 0;
+
+        case SNDCTL_DSP_CHANNELS: {
+            pa_sample_spec ss;
+            int valid;
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_CHANNELS: %i\n", *(int*) argp);
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            ss = i->sample_spec;
+            ss.channels = *(int*) argp;
+
+            if ((valid = pa_sample_spec_valid(&ss))) {
+                i->sample_spec = ss;
+                free_streams(i);
+            }
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            if (!valid) {
+                *_errno = EINVAL;
+                goto fail;
+            }
+
+            break;
+        }
+
+        case SNDCTL_DSP_GETBLKSIZE:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETBLKSIZE\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            fix_metrics(i);
+            *(int*) argp = i->fragment_size;
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            break;
+
+        case SNDCTL_DSP_SETFRAGMENT:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETFRAGMENT: 0x%08x\n", *(int*) argp);
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            i->fragment_size = 1 << ((*(int*) argp) & 31);
+            i->n_fragments = (*(int*) argp) >> 16;
+
+            /* 0x7FFF means that we can set whatever we like */
+            if (i->n_fragments == 0x7FFF)
+                i->n_fragments = 12;
+
+            free_streams(i);
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            break;
+
+        case SNDCTL_DSP_GETCAPS:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_CAPS\n");
+
+            *(int*) argp = DSP_CAP_DUPLEX | DSP_CAP_TRIGGER
+#ifdef DSP_CAP_MULTI
+              | DSP_CAP_MULTI
+#endif
+              ;
+            break;
+
+        case SNDCTL_DSP_GETODELAY: {
+            int l;
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETODELAY\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            *(int*) argp = 0;
+
+            for (;;) {
+                pa_usec_t usec;
+
+                PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, exit_loop);
+
+                if (pa_stream_get_latency(i->play_stream, &usec, NULL) >= 0) {
+                    *(int*) argp = pa_usec_to_bytes(usec, &i->sample_spec);
+                    break;
+                }
+
+                if (pa_context_errno(i->context) != PA_ERR_NODATA) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_get_latency(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                    break;
+                }
+
+                pa_threaded_mainloop_wait(i->mainloop);
+            }
+
+        exit_loop:
+
+#ifdef SIOCINQ
+            if (ioctl(i->thread_fd, SIOCINQ, &l) < 0)
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ failed: %s\n", strerror(errno));
+            else
+                *(int*) argp += l;
+#else
+# warning "Your platform does not support SIOCINQ, something might not work as intended."
+#endif
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": ODELAY: %i\n", *(int*) argp);
+
+            break;
+        }
+
+        case SNDCTL_DSP_RESET: {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_RESET\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            free_streams(i);
+            dsp_flush_socket(i);
+
+            i->optr_n_blocks = 0;
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+        }
+
+        case SNDCTL_DSP_GETFMTS: {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETFMTS\n");
+
+            *(int*) argp = AFMT_MU_LAW|AFMT_A_LAW|AFMT_U8|AFMT_S16_LE|AFMT_S16_BE;
+            break;
+        }
+
+        case SNDCTL_DSP_POST:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_POST\n");
+
+            if (dsp_trigger(i) < 0)
+                *_errno = EIO;
+            break;
+
+        case SNDCTL_DSP_GETTRIGGER:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETTRIGGER\n");
+
+            *(int*) argp = 0;
+            if (!i->play_precork)
+                *(int*) argp |= PCM_ENABLE_OUTPUT;
+            if (!i->rec_precork)
+                *(int*) argp |= PCM_ENABLE_INPUT;
+
+            break;
+
+        case SNDCTL_DSP_SETTRIGGER:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETTRIGGER: 0x%08x\n", *(int*) argp);
+
+            if (!i->io_event) {
+                *_errno = EIO;
+                break;
+            }
+
+            i->play_precork = !((*(int*) argp) & PCM_ENABLE_OUTPUT);
+
+            if (i->play_stream) {
+                if (dsp_cork(i, i->play_stream, !((*(int*) argp) & PCM_ENABLE_OUTPUT)) < 0)
+                    *_errno = EIO;
+                if (dsp_trigger(i) < 0)
+                    *_errno = EIO;
+            }
+
+            i->rec_precork = !((*(int*) argp) & PCM_ENABLE_INPUT);
+
+            if (i->rec_stream) {
+                if (dsp_cork(i, i->rec_stream, !((*(int*) argp) & PCM_ENABLE_INPUT)) < 0)
+                    *_errno = EIO;
+            }
+
+            break;
+
+        case SNDCTL_DSP_SYNC:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SYNC\n");
+
+            if (dsp_drain(i) < 0)
+                *_errno = EIO;
+
+            break;
+
+        case SNDCTL_DSP_GETOSPACE:
+        case SNDCTL_DSP_GETISPACE: {
+            audio_buf_info *bi = (audio_buf_info*) argp;
+            int l = 0;
+            size_t k = 0;
+
+            if (request == SNDCTL_DSP_GETOSPACE)
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETOSPACE\n");
+            else
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETISPACE\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            fix_metrics(i);
+
+            if (request == SNDCTL_DSP_GETOSPACE) {
+                if (i->play_stream) {
+                    if ((k = pa_stream_writable_size(i->play_stream)) == (size_t) -1)
+                        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_writable_size(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                } else
+                    k = i->fragment_size * i->n_fragments;
+
+#ifdef SIOCINQ
+                if (ioctl(i->thread_fd, SIOCINQ, &l) < 0) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ failed: %s\n", strerror(errno));
+                    l = 0;
+                }
+#else
+# warning "Your platform does not dsp_flush_fd, something might not work as intended."
+#endif
+
+                bi->bytes = k > (size_t) l ? k - l : 0;
+            } else {
+                if (i->rec_stream) {
+                    if ((k = pa_stream_readable_size(i->rec_stream)) == (size_t) -1)
+                        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_readable_size(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                } else
+                    k = 0;
+
+#ifdef SIOCINQ
+                if (ioctl(i->app_fd, SIOCINQ, &l) < 0) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ failed: %s\n", strerror(errno));
+                    l = 0;
+                }
+#else
+# warning "Your platform does not dsp_flush_fd, something might not work as intended."
+#endif
+                bi->bytes = k + l;
+            }
+
+            bi->fragsize = i->fragment_size;
+            bi->fragstotal = i->n_fragments;
+            bi->fragments = bi->bytes / bi->fragsize;
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": fragsize=%i, fragstotal=%i, bytes=%i, fragments=%i\n", bi->fragsize, bi->fragstotal, bi->bytes, bi->fragments);
+
+            break;
+        }
+
+#ifdef HAVE_DECL_SOUND_PCM_READ_RATE
+        case SOUND_PCM_READ_RATE:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_RATE\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+            *(int*) argp = i->sample_spec.rate;
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+#endif
+
+#ifdef HAVE_DECL_SOUND_PCM_READ_CHANNELS
+        case SOUND_PCM_READ_CHANNELS:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_CHANNELS\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+            *(int*) argp = i->sample_spec.channels;
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+#endif
+
+#ifdef HAVE_DECL_SOUND_PCM_READ_BITS
+        case SOUND_PCM_READ_BITS:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_BITS\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+            *(int*) argp = pa_sample_size(&i->sample_spec)*8;
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+#endif
+
+        case SNDCTL_DSP_GETOPTR: {
+            count_info *info;
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETOPTR\n");
+
+            info = (count_info*) argp;
+            memset(info, 0, sizeof(*info));
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            for (;;) {
+                pa_usec_t usec;
+
+                PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, exit_loop2);
+
+                if (pa_stream_get_time(i->play_stream, &usec) >= 0) {
+                    size_t k = pa_usec_to_bytes(usec, &i->sample_spec);
+                    int m;
+
+                    info->bytes = (int) k;
+                    m = k / i->fragment_size;
+                    info->blocks = m - i->optr_n_blocks;
+                    i->optr_n_blocks = m;
+
+                    break;
+                }
+
+                if (pa_context_errno(i->context) != PA_ERR_NODATA) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_get_latency(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                    break;
+                }
+
+                pa_threaded_mainloop_wait(i->mainloop);
+            }
+
+        exit_loop2:
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": GETOPTR bytes=%i, blocks=%i, ptr=%i\n", info->bytes, info->blocks, info->ptr);
+
+            break;
+        }
+
+        case SNDCTL_DSP_GETIPTR:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": invalid ioctl SNDCTL_DSP_GETIPTR\n");
+            goto inval;
+
+        case SNDCTL_DSP_SETDUPLEX:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETDUPLEX\n");
+            /* this is a no-op */
+            break;
+
+        default:
+            /* Mixer ioctls are valid on /dev/dsp as well */
+            return mixer_ioctl(i, request, argp, _errno);
+
+inval:
+            *_errno = EINVAL;
+            goto fail;
+    }
+
+    ret = 0;
+
+fail:
+
+    return ret;
+}
+
+#ifdef sun
+int ioctl(int fd, int request, ...) {
+#else
+int ioctl(int fd, unsigned long request, ...) {
+#endif
+    fd_info *i;
+    va_list args;
+    void *argp;
+    int r, _errno = 0;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": ioctl()\n");
+
+    va_start(args, request);
+    argp = va_arg(args, void *);
+    va_end(args);
+
+    if (!function_enter()) {
+        LOAD_IOCTL_FUNC();
+        return _ioctl(fd, request, argp);
+    }
+
+    if (!(i = fd_info_find(fd))) {
+        function_exit();
+        LOAD_IOCTL_FUNC();
+        return _ioctl(fd, request, argp);
+    }
+
+    if (i->type == FD_INFO_MIXER)
+        r = mixer_ioctl(i, request, argp, &_errno);
+    else
+        r = dsp_ioctl(i, request, argp, &_errno);
+
+    fd_info_unref(i);
+
+    if (_errno)
+        errno = _errno;
+
+    function_exit();
+
+    return r;
+}
+
+int close(int fd) {
+    fd_info *i;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": close()\n");
+
+    if (!function_enter()) {
+        LOAD_CLOSE_FUNC();
+        return _close(fd);
+    }
+
+    if (!(i = fd_info_find(fd))) {
+        function_exit();
+        LOAD_CLOSE_FUNC();
+        return _close(fd);
+    }
+
+    fd_info_remove_from_list(i);
+    fd_info_unref(i);
+
+    function_exit();
+
+    return 0;
+}
+
+int access(const char *pathname, int mode) {
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": access(%s)\n", pathname?pathname:"NULL");
+
+    if (!pathname ||
+        !is_audio_device_node(pathname)) {
+        LOAD_ACCESS_FUNC();
+        return _access(pathname, mode);
+    }
+
+    if (mode & X_OK) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": access(%s, %x) = EACCESS\n", pathname, mode);
+        errno = EACCES;
+        return -1;
+    }
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": access(%s, %x) = OK\n", pathname, mode);
+
+    return 0;
+}
+
+int stat(const char *pathname, struct stat *buf) {
+#ifdef HAVE_OPEN64
+    struct stat64 parent;
+#else
+    struct stat parent;
+#endif
+    int ret;
+
+    if (!pathname ||
+        !buf ||
+        !is_audio_device_node(pathname)) {
+        debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat(%s)\n", pathname?pathname:"NULL");
+        LOAD_STAT_FUNC();
+        return _stat(pathname, buf);
+    }
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": stat(%s)\n", pathname);
+
+#ifdef _STAT_VER
+#ifdef HAVE_OPEN64
+    ret = __xstat64(_STAT_VER, "/dev", &parent);
+#else
+    ret = __xstat(_STAT_VER, "/dev", &parent);
+#endif
+#else
+#ifdef HAVE_OPEN64
+    ret = stat64("/dev", &parent);
+#else
+    ret = stat("/dev", &parent);
+#endif
+#endif
+
+    if (ret) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": unable to stat \"/dev\"\n");
+        return -1;
+    }
+
+    buf->st_dev = parent.st_dev;
+    buf->st_ino = 0xDEADBEEF;   /* FIXME: Can we do this in a safe way? */
+    buf->st_mode = S_IFCHR | S_IRUSR | S_IWUSR;
+    buf->st_nlink = 1;
+    buf->st_uid = getuid();
+    buf->st_gid = getgid();
+    buf->st_rdev = 0x0E03;      /* FIXME: Linux specific */
+    buf->st_size = 0;
+    buf->st_atime = 1181557705;
+    buf->st_mtime = 1181557705;
+    buf->st_ctime = 1181557705;
+    buf->st_blksize = 1;
+    buf->st_blocks = 0;
+
+    return 0;
+}
+
+#ifdef HAVE_OPEN64
+
+int stat64(const char *pathname, struct stat64 *buf) {
+    struct stat oldbuf;
+    int ret;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat64(%s)\n", pathname?pathname:"NULL");
+
+    if (!pathname ||
+        !buf ||
+        !is_audio_device_node(pathname)) {
+        LOAD_STAT64_FUNC();
+        return _stat64(pathname, buf);
+    }
+
+    ret = stat(pathname, &oldbuf);
+    if (ret)
+        return ret;
+
+    buf->st_dev = oldbuf.st_dev;
+    buf->st_ino = oldbuf.st_ino;
+    buf->st_mode = oldbuf.st_mode;
+    buf->st_nlink = oldbuf.st_nlink;
+    buf->st_uid = oldbuf.st_uid;
+    buf->st_gid = oldbuf.st_gid;
+    buf->st_rdev = oldbuf.st_rdev;
+    buf->st_size = oldbuf.st_size;
+    buf->st_atime = oldbuf.st_atime;
+    buf->st_mtime = oldbuf.st_mtime;
+    buf->st_ctime = oldbuf.st_ctime;
+    buf->st_blksize = oldbuf.st_blksize;
+    buf->st_blocks = oldbuf.st_blocks;
+
+    return 0;
+}
+
+int open64(const char *filename, int flags, ...) {
+    va_list args;
+    mode_t mode = 0;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": open64(%s)\n", filename?filename:"NULL");
+
+    if (flags & O_CREAT) {
+        va_start(args, flags);
+        if (sizeof(mode_t) < sizeof(int))
+            mode = va_arg(args, int);
+        else
+            mode = va_arg(args, mode_t);
+        va_end(args);
+    }
+
+    if (!filename ||
+        !is_audio_device_node(filename)) {
+        LOAD_OPEN64_FUNC();
+        return _open64(filename, flags, mode);
+    }
+
+    return real_open(filename, flags, mode);
+}
+
+int __open64_2(const char *filename, int flags) {
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": __open64_2(%s)\n", filename?filename:"NULL");
+
+    if ((flags & O_CREAT) ||
+        !filename ||
+        !is_audio_device_node(filename)) {
+        LOAD___OPEN64_2_FUNC();
+        return ___open64_2(filename, flags);
+    }
+
+    return real_open(filename, flags, 0);
+}
+
+#endif
+
+#ifdef _STAT_VER
+
+int __xstat(int ver, const char *pathname, struct stat *buf) {
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat(%s)\n", pathname?pathname:"NULL");
+
+    if (!pathname ||
+        !buf ||
+        !is_audio_device_node(pathname)) {
+        LOAD_XSTAT_FUNC();
+        return ___xstat(ver, pathname, buf);
+    }
+
+    if (ver != _STAT_VER) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    return stat(pathname, buf);
+}
+
+#ifdef HAVE_OPEN64
+
+int __xstat64(int ver, const char *pathname, struct stat64 *buf) {
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat64(%s)\n", pathname?pathname:"NULL");
+
+    if (!pathname ||
+        !buf ||
+        !is_audio_device_node(pathname)) {
+        LOAD_XSTAT64_FUNC();
+        return ___xstat64(ver, pathname, buf);
+    }
+
+    if (ver != _STAT_VER) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    return stat64(pathname, buf);
+}
+
+#endif
+
+#endif
+
+FILE* fopen(const char *filename, const char *mode) {
+    FILE *f = NULL;
+    int fd;
+    mode_t m;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen(%s)\n", filename?filename:"NULL");
+
+    if (!filename ||
+        !mode ||
+        !is_audio_device_node(filename)) {
+        LOAD_FOPEN_FUNC();
+        return _fopen(filename, mode);
+    }
+
+    switch (mode[0]) {
+    case 'r':
+        m = O_RDONLY;
+        break;
+    case 'w':
+    case 'a':
+        m = O_WRONLY;
+        break;
+    default:
+        errno = EINVAL;
+        return NULL;
+    }
+
+    if ((((mode[1] == 'b') || (mode[1] == 't')) && (mode[2] == '+')) || (mode[1] == '+'))
+        m = O_RDWR;
+
+    if ((fd = real_open(filename, m, 0)) < 0)
+        return NULL;
+
+    if (!(f = fdopen(fd, mode))) {
+        close(fd);
+        return NULL;
+    }
+
+    return f;
+}
+
+#ifdef HAVE_OPEN64
+
+FILE *fopen64(const char *filename, const char *mode) {
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen64(%s)\n", filename?filename:"NULL");
+
+    if (!filename ||
+        !mode ||
+        !is_audio_device_node(filename)) {
+        LOAD_FOPEN64_FUNC();
+        return _fopen64(filename, mode);
+    }
+
+    return fopen(filename, mode);
+}
+
+#endif
+
+int fclose(FILE *f) {
+    fd_info *i;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": fclose()\n");
+
+    if (!function_enter()) {
+        LOAD_FCLOSE_FUNC();
+        return _fclose(f);
+    }
+
+    if (!(i = fd_info_find(fileno(f)))) {
+        function_exit();
+        LOAD_FCLOSE_FUNC();
+        return _fclose(f);
+    }
+
+    fd_info_remove_from_list(i);
+
+    /* Dirty trick to avoid that the fd is not freed twice, once by us
+     * and once by the real fclose() */
+    i->app_fd = -1;
+
+    fd_info_unref(i);
+
+    function_exit();
+
+    LOAD_FCLOSE_FUNC();
+    return _fclose(f);
+}
diff --git a/src/utils/padsp.in b/src/utils/padsp.in
new file mode 100644 (file)
index 0000000..fea00d2
--- /dev/null
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+# This file is part of PulseAudio.
+#
+# Copyright 2006 Lennart Poettering
+# Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+while getopts 'hs:n:m:MSDd' param ; do
+       case $param in
+               s)
+                       PULSE_SERVER="$OPTARG"
+                       export PULSE_SERVER
+                       ;;
+               n)
+                       PADSP_CLIENT_NAME="$OPTARG"
+                       export PADSP_CLIENT_NAME
+                       ;;
+               m)
+                       PADSP_STREAM_NAME="$OPTARG"
+                       export PADSP_STREAM_NAME
+                       ;;
+               M)
+                       PADSP_NO_MIXER=1
+                       export PADSP_NO_MIXER
+                       ;;
+               S)
+                       PADSP_NO_SNDSTAT=1
+                       export PADSP_NO_SNDSTAT
+                       ;;
+               D)
+                       PADSP_NO_DSP=1
+                       export PADSP_NO_DSP
+                       ;;
+               d)
+                       if [ x"$PADSP_DEBUG" = x ]; then
+                               PADSP_DEBUG=1
+                       else
+                               PADSP_DEBUG=$(( $PADSP_DEBUG + 1 ))
+                       fi
+                       export PADSP_DEBUG
+                       ;;
+               *)
+                       echo "$0 - redirect OSS audio devices to PulseAudio"
+                       echo " "
+                       echo "$0 [options] application [arguments]"
+                       echo " "
+                       echo "options:"
+                       echo "  -h                  show brief help"
+                       echo "  -s <host>[:<port>]  contact a specific PulseAudio server"
+                       echo "  -n <name>           client name to report to the server"
+                       echo "  -m <name>           stream name to report to the server"
+                       echo "  -M                  disable /dev/mixer emulation"
+                       echo "  -S                  disable /dev/sndstat emulation"
+                       echo "  -D                  disable /dev/dsp emulation"
+                       echo "  -d                  enable debug output"
+                       exit 0
+                       ;;
+       esac
+done
+
+shift $(( $OPTIND - 1 ))
+
+if [ x"$LD_PRELOAD" = x ] ; then
+   LD_PRELOAD="@PULSEDSP_LOCATION@/libpulsedsp.so"
+else
+   LD_PRELOAD="$LD_PRELOAD @PULSEDSP_LOCATION@/libpulsedsp.so"
+fi
+
+export LD_PRELOAD
+
+exec "$@"
diff --git a/src/utils/pasuspender.c b/src/utils/pasuspender.c
new file mode 100644 (file)
index 0000000..f1f39b2
--- /dev/null
@@ -0,0 +1,342 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <locale.h>
+
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
+
+#include <pulse/pulseaudio.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+
+static pa_context *context = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
+static char **child_argv = NULL;
+static int child_argc = 0;
+static pid_t child_pid = (pid_t) -1;
+static int child_ret = 0;
+static int dead = 1;
+static int fork_failed = 0;
+
+static void quit(int ret) {
+    pa_assert(mainloop_api);
+    mainloop_api->quit(mainloop_api, ret);
+}
+
+static void context_drain_complete(pa_context *c, void *userdata) {
+    pa_context_disconnect(c);
+}
+
+static void drain(void) {
+    pa_operation *o;
+
+    if (context) {
+        if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
+            pa_context_disconnect(context);
+        else
+            pa_operation_unref(o);
+    } else
+        quit(0);
+}
+
+static int start_child(void) {
+
+    if ((child_pid = fork()) < 0) {
+        fprintf(stderr, _("fork(): %s\n"), strerror(errno));
+        fork_failed = 1;
+
+        return -1;
+
+    } else if (child_pid == 0) {
+        /* Child */
+
+#ifdef __linux__
+        prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
+#endif
+
+        if (execvp(child_argv[0], child_argv) < 0)
+            fprintf(stderr, _("execvp(): %s\n"), strerror(errno));
+
+        _exit(1);
+
+    } else {
+
+        /* parent */
+        dead = 0;
+    }
+
+    return 0;
+}
+
+static void resume_complete(pa_context *c, int success, void *userdata) {
+    static int n = 0;
+
+    n++;
+
+    if (!success) {
+        fprintf(stderr, _("Failure to resume: %s\n"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (n >= 2)
+        drain(); /* drain and quit */
+}
+
+static void resume(void) {
+    static int n = 0;
+
+    n++;
+
+    if (n > 1)
+        return;
+
+    if (context) {
+        if (pa_context_is_local(context)) {
+            pa_operation_unref(pa_context_suspend_sink_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
+            pa_operation_unref(pa_context_suspend_source_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
+        } else
+            drain();
+    } else {
+        quit(0);
+    }
+}
+
+static void suspend_complete(pa_context *c, int success, void *userdata) {
+    static int n = 0;
+
+    n++;
+
+    if (!success) {
+        fprintf(stderr, _("Failure to suspend: %s\n"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (n >= 2) {
+        if (start_child() < 0)
+            resume();
+    }
+}
+
+static void context_state_callback(pa_context *c, void *userdata) {
+    pa_assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY:
+            if (pa_context_is_local(c)) {
+                pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, 1, suspend_complete, NULL));
+                pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, 1, suspend_complete, NULL));
+            } else {
+                fprintf(stderr, _("WARNING: Sound server is not local, not suspending.\n"));
+                if (start_child() < 0)
+                    drain();
+            }
+
+            break;
+
+        case PA_CONTEXT_TERMINATED:
+            quit(0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            fprintf(stderr, _("Connection failure: %s\n"), pa_strerror(pa_context_errno(c)));
+
+            pa_context_unref(context);
+            context = NULL;
+
+            if (child_pid == (pid_t) -1) {
+                /* not started yet, then we do it now */
+                if (start_child() < 0)
+                    quit(1);
+            } else if (dead)
+                /* already started, and dead, so let's quit */
+                quit(1);
+
+            break;
+    }
+}
+
+static void sigint_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
+    fprintf(stderr, _("Got SIGINT, exiting.\n"));
+    resume();
+}
+
+static void sigchld_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
+    int status = 0;
+    pid_t p;
+
+    p = waitpid(-1, &status, WNOHANG);
+
+    if (p != child_pid)
+        return;
+
+    dead = 1;
+
+    if (WIFEXITED(status))
+        child_ret = WEXITSTATUS(status);
+    else if (WIFSIGNALED(status)) {
+        fprintf(stderr, _("WARNING: Child process terminated by signal %u\n"), WTERMSIG(status));
+        child_ret = 1;
+    }
+
+    resume();
+}
+
+static void help(const char *argv0) {
+
+    printf(_("%s [options] ... \n\n"
+           "  -h, --help                            Show this help\n"
+           "      --version                         Show version\n"
+           "  -s, --server=SERVER                   The name of the server to connect to\n\n"),
+           argv0);
+}
+
+enum {
+    ARG_VERSION = 256
+};
+
+int main(int argc, char *argv[]) {
+    pa_mainloop* m = NULL;
+    int c, ret = 1;
+    char *server = NULL, *bn;
+
+    static const struct option long_options[] = {
+        {"server",      1, NULL, 's'},
+        {"version",     0, NULL, ARG_VERSION},
+        {"help",        0, NULL, 'h'},
+        {NULL,          0, NULL, 0}
+    };
+
+    setlocale(LC_ALL, "");
+#ifdef ENABLE_NLS
+    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
+#endif
+
+    bn = pa_path_get_filename(argv[0]);
+
+    while ((c = getopt_long(argc, argv, "s:h", long_options, NULL)) != -1) {
+        switch (c) {
+            case 'h' :
+                help(bn);
+                ret = 0;
+                goto quit;
+
+            case ARG_VERSION:
+                printf(_("pasuspender %s\n"
+                         "Compiled with libpulse %s\n"
+                         "Linked with libpulse %s\n"),
+                       PACKAGE_VERSION,
+                       pa_get_headers_version(),
+                       pa_get_library_version());
+                ret = 0;
+                goto quit;
+
+            case 's':
+                pa_xfree(server);
+                server = pa_xstrdup(optarg);
+                break;
+
+            default:
+                goto quit;
+        }
+    }
+
+    child_argv = argv + optind;
+    child_argc = argc - optind;
+
+    if (child_argc <= 0) {
+        help(bn);
+        ret = 0;
+        goto quit;
+    }
+
+    if (!(m = pa_mainloop_new())) {
+        fprintf(stderr, _("pa_mainloop_new() failed.\n"));
+        goto quit;
+    }
+
+    pa_assert_se(mainloop_api = pa_mainloop_get_api(m));
+    pa_assert_se(pa_signal_init(mainloop_api) == 0);
+    pa_signal_new(SIGINT, sigint_callback, NULL);
+    pa_signal_new(SIGCHLD, sigchld_callback, NULL);
+#ifdef SIGPIPE
+    signal(SIGPIPE, SIG_IGN);
+#endif
+
+    if (!(context = pa_context_new(mainloop_api, bn))) {
+        fprintf(stderr, _("pa_context_new() failed.\n"));
+        goto quit;
+    }
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+
+    if (pa_context_connect(context, server, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) {
+        fprintf(stderr, "pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(context)));
+        goto quit;
+    }
+
+    if (pa_mainloop_run(m, &ret) < 0) {
+        fprintf(stderr, _("pa_mainloop_run() failed.\n"));
+        goto quit;
+    }
+
+    if (ret == 0 && fork_failed)
+        ret = 1;
+
+quit:
+    if (context)
+        pa_context_unref(context);
+
+    if (m) {
+        pa_signal_done();
+        pa_mainloop_free(m);
+    }
+
+    pa_xfree(server);
+
+    if (!dead)
+        kill(child_pid, SIGTERM);
+
+    return ret == 0 ? child_ret : ret;
+}
diff --git a/src/utils/pax11publish.c b/src/utils/pax11publish.c
new file mode 100644 (file)
index 0000000..a91dbf7
--- /dev/null
@@ -0,0 +1,220 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <getopt.h>
+#include <assert.h>
+#include <locale.h>
+
+#include <xcb/xcb.h>
+
+#include <pulse/util.h>
+#include <pulse/client-conf.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/log.h>
+#include <pulsecore/authkey.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/x11prop.h>
+
+int main(int argc, char *argv[]) {
+    const char *dname = NULL, *sink = NULL, *source = NULL, *server = NULL, *cookie_file = PA_NATIVE_COOKIE_FILE;
+    int c, ret = 1, screen = 0;
+    xcb_connection_t *xcb = NULL;
+    enum { DUMP, EXPORT, IMPORT, REMOVE } mode = DUMP;
+
+    setlocale(LC_ALL, "");
+#ifdef ENABLE_NLS
+    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
+#endif
+
+    while ((c = getopt(argc, argv, "deiD:S:O:I:c:hr")) != -1) {
+        switch (c) {
+            case 'D' :
+                dname = optarg;
+                break;
+            case 'h':
+                printf(_("%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n\n"
+                       " -d    Show current PulseAudio data attached to X11 display (default)\n"
+                       " -e    Export local PulseAudio data to X11 display\n"
+                       " -i    Import PulseAudio data from X11 display to local environment variables and cookie file.\n"
+                       " -r    Remove PulseAudio data from X11 display\n"),
+                       pa_path_get_filename(argv[0]));
+                ret = 0;
+                goto finish;
+            case 'd':
+                mode = DUMP;
+                break;
+            case 'e':
+                mode = EXPORT;
+                break;
+            case 'i':
+                mode = IMPORT;
+                break;
+            case 'r':
+                mode = REMOVE;
+                break;
+            case 'c':
+                cookie_file = optarg;
+                break;
+            case 'I':
+                source = optarg;
+                break;
+            case 'O':
+                sink = optarg;
+                break;
+            case 'S':
+                server = optarg;
+                break;
+            default:
+                fprintf(stderr, _("Failed to parse command line.\n"));
+                goto finish;
+        }
+    }
+
+    if (!(xcb = xcb_connect(dname, &screen))) {
+        pa_log(_("xcb_connect() failed"));
+        goto finish;
+    }
+
+    if (xcb_connection_has_error(xcb)) {
+        pa_log(_("xcb_connection_has_error() returned true"));
+        goto finish;
+    }
+
+    switch (mode) {
+        case DUMP: {
+            char t[1024];
+            if (pa_x11_get_prop(xcb, screen, "PULSE_SERVER", t, sizeof(t)))
+                printf(_("Server: %s\n"), t);
+            if (pa_x11_get_prop(xcb, screen, "PULSE_SOURCE", t, sizeof(t)))
+                printf(_("Source: %s\n"), t);
+            if (pa_x11_get_prop(xcb, screen, "PULSE_SINK", t, sizeof(t)))
+                printf(_("Sink: %s\n"), t);
+            if (pa_x11_get_prop(xcb, screen, "PULSE_COOKIE", t, sizeof(t)))
+                printf(_("Cookie: %s\n"), t);
+
+            break;
+        }
+
+        case IMPORT: {
+            char t[1024];
+            if (pa_x11_get_prop(xcb, screen, "PULSE_SERVER", t, sizeof(t)))
+                printf("PULSE_SERVER='%s'\nexport PULSE_SERVER\n", t);
+            if (pa_x11_get_prop(xcb, screen, "PULSE_SOURCE", t, sizeof(t)))
+                printf("PULSE_SOURCE='%s'\nexport PULSE_SOURCE\n", t);
+            if (pa_x11_get_prop(xcb, screen, "PULSE_SINK", t, sizeof(t)))
+                printf("PULSE_SINK='%s'\nexport PULSE_SINK\n", t);
+
+            if (pa_x11_get_prop(xcb, screen, "PULSE_COOKIE", t, sizeof(t))) {
+                uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
+                size_t l;
+                if ((l = pa_parsehex(t, cookie, sizeof(cookie))) != sizeof(cookie)) {
+                    fprintf(stderr, _("Failed to parse cookie data\n"));
+                    goto finish;
+                }
+
+                if (pa_authkey_save(cookie_file, cookie, l) < 0) {
+                    fprintf(stderr, _("Failed to save cookie data\n"));
+                    goto finish;
+                }
+            }
+
+            break;
+        }
+
+        case EXPORT: {
+            pa_client_conf *conf = pa_client_conf_new();
+            uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
+            char hx[PA_NATIVE_COOKIE_LENGTH*2+1];
+            assert(conf);
+
+            pa_client_conf_load(conf, false, true);
+
+            pa_x11_del_prop(xcb, screen, "PULSE_SERVER");
+            pa_x11_del_prop(xcb, screen, "PULSE_SINK");
+            pa_x11_del_prop(xcb, screen, "PULSE_SOURCE");
+            pa_x11_del_prop(xcb, screen, "PULSE_ID");
+            pa_x11_del_prop(xcb, screen, "PULSE_COOKIE");
+
+            if (server)
+                pa_x11_set_prop(xcb, screen, "PULSE_SERVER", server);
+            else if (conf->default_server)
+                pa_x11_set_prop(xcb, screen, "PULSE_SERVER", conf->default_server);
+            else {
+                char hn[256];
+                if (!pa_get_fqdn(hn, sizeof(hn))) {
+                    fprintf(stderr, _("Failed to get FQDN.\n"));
+                    goto finish;
+                }
+
+                pa_x11_set_prop(xcb, screen, "PULSE_SERVER", hn);
+            }
+
+            if (sink)
+                pa_x11_set_prop(xcb, screen, "PULSE_SINK", sink);
+            else if (conf->default_sink)
+                pa_x11_set_prop(xcb, screen, "PULSE_SINK", conf->default_sink);
+
+            if (source)
+                pa_x11_set_prop(xcb, screen, "PULSE_SOURCE", source);
+            if (conf->default_source)
+                pa_x11_set_prop(xcb, screen, "PULSE_SOURCE", conf->default_source);
+
+            pa_client_conf_free(conf);
+
+            if (pa_authkey_load(cookie_file, true, cookie, sizeof(cookie)) < 0) {
+                fprintf(stderr, _("Failed to load cookie data\n"));
+                goto finish;
+            }
+
+            pa_x11_set_prop(xcb, screen, "PULSE_COOKIE", pa_hexstr(cookie, sizeof(cookie), hx, sizeof(hx)));
+            break;
+        }
+
+        case REMOVE:
+            pa_x11_del_prop(xcb, screen, "PULSE_SERVER");
+            pa_x11_del_prop(xcb, screen, "PULSE_SINK");
+            pa_x11_del_prop(xcb, screen, "PULSE_SOURCE");
+            pa_x11_del_prop(xcb, screen, "PULSE_ID");
+            pa_x11_del_prop(xcb, screen, "PULSE_COOKIE");
+            pa_x11_del_prop(xcb, screen, "PULSE_SESSION_ID");
+            break;
+
+        default:
+            fprintf(stderr, _("Not yet implemented.\n"));
+            goto finish;
+    }
+
+    ret = 0;
+
+finish:
+
+    if (xcb) {
+        xcb_flush(xcb);
+        xcb_disconnect(xcb);
+    }
+
+    return ret;
+}
diff --git a/src/utils/qpaeq b/src/utils/qpaeq
new file mode 100755 (executable)
index 0000000..ac4b9e4
--- /dev/null
@@ -0,0 +1,575 @@
+#!/usr/bin/env python
+#    qpaeq is a equalizer interface for pulseaudio's equalizer sinks
+#    Copyright (C) 2009  Jason Newton <nevion@gmail.com
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+import os,math,sys
+try:
+    import PyQt4,sip
+    from PyQt4 import QtGui,QtCore
+    import dbus.mainloop.qt
+    import dbus
+except ImportError as e:
+    sys.stderr.write('There was an error importing needed libraries\n'
+                     'Make sure you have qt4 and dbus-python installed\n'
+                     'The error that occured was:\n'
+                     '\t%s\n' % (str(e)))
+    sys.exit(-1)
+
+from functools import partial
+
+import signal
+signal.signal(signal.SIGINT, signal.SIG_DFL)
+SYNC_TIMEOUT = 4*1000
+
+CORE_PATH = "/org/pulseaudio/core1"
+CORE_IFACE = "org.PulseAudio.Core1"
+def connect():
+    try:
+        if 'PULSE_DBUS_SERVER' in os.environ:
+            address = os.environ['PULSE_DBUS_SERVER']
+        else:
+            bus = dbus.SessionBus() # Should be UserBus, but D-Bus doesn't implement that yet.
+            server_lookup = bus.get_object('org.PulseAudio1', '/org/pulseaudio/server_lookup1')
+            address = server_lookup.Get('org.PulseAudio.ServerLookup1', 'Address', dbus_interface='org.freedesktop.DBus.Properties')
+        return dbus.connection.Connection(address)
+    except Exception as e:
+        sys.stderr.write('There was an error connecting to pulseaudio, '
+                         'please make sure you have the pulseaudio dbus '
+                         'module loaded, exiting...\n')
+        sys.exit(-1)
+
+
+#TODO: signals: sink Filter changed, sink reconfigured (window size) (sink iface)
+#TODO: manager signals: new sink, removed sink, new profile, removed profile
+#TODO: add support for changing of window_size 1000-fft_size (adv option)
+#TODO: reconnect support loop 1 second trying to reconnect
+#TODO: just resample the filters for profiles when loading to different sizes
+#TODO: add preamp
+prop_iface='org.freedesktop.DBus.Properties'
+eq_iface='org.PulseAudio.Ext.Equalizing1.Equalizer'
+device_iface='org.PulseAudio.Core1.Device'
+class QPaeq(QtGui.QWidget):
+    manager_path='/org/pulseaudio/equalizing1'
+    manager_iface='org.PulseAudio.Ext.Equalizing1.Manager'
+    core_iface='org.PulseAudio.Core1'
+    core_path='/org/pulseaudio/core1'
+    module_name='module-equalizer-sink'
+
+    def __init__(self):
+        QtGui.QWidget.__init__(self)
+        self.setWindowTitle('qpaeq')
+        self.slider_widget=None
+        self.sink_name=None
+        self.filter_state=None
+
+        self.create_layout()
+
+        self.set_connection()
+        self.connect_to_sink(self.sinks[0])
+        self.set_callbacks()
+        self.setMinimumSize(self.sizeHint())
+
+    def create_layout(self):
+        self.main_layout=QtGui.QVBoxLayout()
+        self.setLayout(self.main_layout)
+        toprow_layout=QtGui.QHBoxLayout()
+        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        #sizePolicy.setHeightForWidth(self.profile_box.sizePolicy().hasHeightForWidth())
+
+        toprow_layout.addWidget(QtGui.QLabel('Sink'))
+        self.sink_box = QtGui.QComboBox()
+        self.sink_box.setSizePolicy(sizePolicy)
+        self.sink_box.setDuplicatesEnabled(False)
+        self.sink_box.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically)
+        #self.sink_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
+        toprow_layout.addWidget(self.sink_box)
+
+        toprow_layout.addWidget(QtGui.QLabel('Channel'))
+        self.channel_box = QtGui.QComboBox()
+        self.channel_box.setSizePolicy(sizePolicy)
+        toprow_layout.addWidget(self.channel_box)
+
+        toprow_layout.addWidget(QtGui.QLabel('Preset'))
+        self.profile_box = QtGui.QComboBox()
+        self.profile_box.setSizePolicy(sizePolicy)
+        self.profile_box.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically)
+        #self.profile_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
+        toprow_layout.addWidget(self.profile_box)
+
+        large_icon_size=self.style().pixelMetric(QtGui.QStyle.PM_LargeIconSize)
+        large_icon_size=QtCore.QSize(large_icon_size,large_icon_size)
+        save_profile=QtGui.QToolButton()
+        save_profile.setIcon(self.style().standardIcon(QtGui.QStyle.SP_DriveFDIcon))
+        save_profile.setIconSize(large_icon_size)
+        save_profile.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
+        save_profile.clicked.connect(self.save_profile)
+        remove_profile=QtGui.QToolButton()
+        remove_profile.setIcon(self.style().standardIcon(QtGui.QStyle.SP_TrashIcon))
+        remove_profile.setIconSize(large_icon_size)
+        remove_profile.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
+        remove_profile.clicked.connect(self.remove_profile)
+        toprow_layout.addWidget(save_profile)
+        toprow_layout.addWidget(remove_profile)
+
+        reset_button = QtGui.QPushButton('Reset')
+        reset_button.clicked.connect(self.reset)
+        toprow_layout.addStretch()
+        toprow_layout.addWidget(reset_button)
+        self.layout().addLayout(toprow_layout)
+
+        self.profile_box.activated.connect(self.load_profile)
+        self.channel_box.activated.connect(self.select_channel)
+    def connect_to_sink(self,name):
+        #TODO: clear slots for profile buttons
+
+        #flush any pending saves for other sinks
+        if self.filter_state is not None:
+            self.filter_state.flush_state()
+        sink=self.connection.get_object(object_path=name)
+        self.sink_props=dbus.Interface(sink,dbus_interface=prop_iface)
+        self.sink=dbus.Interface(sink,dbus_interface=eq_iface)
+        self.filter_state=FilterState(sink)
+        #sample_rate,filter_rate,channels,channel)
+
+        self.channel_box.clear()
+        self.channel_box.addItem('All',self.filter_state.channels)
+        for i in range(self.filter_state.channels):
+            self.channel_box.addItem('%d' %(i+1,),i)
+        self.setMinimumSize(self.sizeHint())
+
+        self.set_slider_widget(SliderArray(self.filter_state))
+
+        self.sink_name=name
+        #set the signal listener for this sink
+        core=self._get_core()
+        #temporary hack until signal filtering works properly
+        core.ListenForSignal('',[dbus.ObjectPath(self.sink_name),dbus.ObjectPath(self.manager_path)])
+        #for x in ['FilterChanged']:
+        #    core.ListenForSignal("%s.%s" %(self.eq_iface,x),[dbus.ObjectPath(self.sink_name)])
+        #core.ListenForSignal(self.eq_iface,[dbus.ObjectPath(self.sink_name)])
+        self.sink.connect_to_signal('FilterChanged',self.read_filter)
+
+    def set_slider_widget(self,widget):
+        layout=self.layout()
+        if self.slider_widget is not None:
+            i=layout.indexOf(self.slider_widget)
+            layout.removeWidget(self.slider_widget)
+            self.slider_widget.deleteLater()
+            layout.insertWidget(i,self.slider_widget)
+        else:
+            layout.addWidget(widget)
+        self.slider_widget=widget
+        self.read_filter()
+    def _get_core(self):
+        core_obj=self.connection.get_object(object_path=self.core_path)
+        core=dbus.Interface(core_obj,dbus_interface=self.core_iface)
+        return core
+    def sink_added(self,sink):
+        #TODO: preserve selected sink
+        self.update_sinks()
+    def sink_removed(self,sink):
+        #TODO: preserve selected sink, try connecting to backup otherwise
+        if sink==self.sink_name:
+            #connect to new sink?
+            pass
+        self.update_sinks()
+    def save_profile(self):
+        #popup dialog box for name
+        current=self.profile_box.currentIndex()
+        profile,ok=QtGui.QInputDialog.getItem(self,'Preset Name','Preset',self.profiles,current)
+        if not ok or profile=='':
+            return
+        if profile in self.profiles:
+            mbox=QtGui.QMessageBox(self)
+            mbox.setText('%s preset already exists'%(profile,))
+            mbox.setInformativeText('Do you want to save over it?')
+            mbox.setStandardButtons(mbox.Save|mbox.Discard|mbox.Cancel)
+            mbox.setDefaultButton(mbox.Save)
+            ret=mbox.exec_()
+            if ret!=mbox.Save:
+                return
+        self.sink.SaveProfile(self.filter_state.channel,dbus.String(profile))
+        if self.filter_state.channel==self.filter_state.channels:
+            for x in range(1,self.filter_state.channels):
+                self.sink.LoadProfile(x,dbus.String(profile))
+    def remove_profile(self):
+        #find active profile name, remove it
+        profile=self.profile_box.currentText()
+        manager=dbus.Interface(self.manager_obj,dbus_interface=self.manager_iface)
+        manager.RemoveProfile(dbus.String(profile))
+    def load_profile(self,x):
+        profile=self.profile_box.itemText(x)
+        self.filter_state.load_profile(profile)
+    def select_channel(self,x):
+        self.filter_state.channel = self.channel_box.itemData(x).toPyObject()
+        self._set_profile_name()
+        self.filter_state.readback()
+
+    #TODO: add back in preamp!
+    #print(frequencies)
+    #main_layout.addLayout(self.create_slider(partial(self.update_coefficient,0),
+    #    'Preamp')[0]
+    #)
+    def set_connection(self):
+        self.connection=connect()
+
+        self.manager_obj=self.connection.get_object(object_path=self.manager_path)
+        manager_props=dbus.Interface(self.manager_obj,dbus_interface=prop_iface)
+        try:
+            self.sinks=manager_props.Get(self.manager_iface,'EqualizedSinks')
+        except dbus.exceptions.DBusException:
+            # probably module not yet loaded, try to load it:
+            try:
+                core=self.connection.get_object(object_path=self.core_path)
+                core.LoadModule(self.module_name,{},dbus_interface=self.core_iface)
+                # yup, we don't need to re-create manager_obj and manager_props,
+                # these are late-bound
+                self.sinks=manager_props.Get(self.manager_iface,'EqualizedSinks')
+            except dbus.exceptions.DBusException:
+                sys.stderr.write('It seems that running pulseaudio does not support '
+                                 'equalizer features and loading %s module failed.\n'
+                                 'Exiting...\n' % self.module_name)
+                sys.exit(-1)
+
+    def set_callbacks(self):
+        manager=dbus.Interface(self.manager_obj,dbus_interface=self.manager_iface)
+        manager.connect_to_signal('ProfilesChanged',self.update_profiles)
+        manager.connect_to_signal('SinkAdded',self.sink_added)
+        manager.connect_to_signal('SinkRemoved',self.sink_removed)
+        #self._get_core().ListenForSignal(self.manager_iface,[])
+        #self._get_core().ListenForSignal(self.manager_iface,[dbus.ObjectPath(self.manager_path)])
+        #core=self._get_core()
+        #for x in ['ProfilesChanged','SinkAdded','SinkRemoved']:
+        #    core.ListenForSignal("%s.%s" %(self.manager_iface,x),[dbus.ObjectPath(self.manager_path)])
+        self.update_profiles()
+        self.update_sinks()
+    def update_profiles(self):
+        #print('update profiles called!')
+        manager_props=dbus.Interface(self.manager_obj,dbus_interface=prop_iface)
+        self.profiles=manager_props.Get(self.manager_iface,'Profiles')
+        self.profile_box.blockSignals(True)
+        self.profile_box.clear()
+        self.profile_box.addItems(self.profiles)
+        self.profile_box.blockSignals(False)
+        self._set_profile_name()
+    def update_sinks(self):
+        self.sink_box.blockSignals(True)
+        self.sink_box.clear()
+        for x in self.sinks:
+            sink=self.connection.get_object(object_path=x)
+            sink_props=dbus.Interface(sink,dbus_interface=prop_iface)
+            simple_name=sink_props.Get(device_iface,'Name')
+            self.sink_box.addItem(simple_name,x)
+        self.sink_box.blockSignals(False)
+        self.sink_box.setMinimumSize(self.sink_box.sizeHint())
+    def read_filter(self):
+        #print(self.filter_frequencies)
+        self.filter_state.readback()
+    def reset(self):
+        coefs=dbus.Array([1/math.sqrt(2.0)]*(self.filter_state.filter_rate//2+1))
+        preamp=1.0
+        self.filter_state.set_filter(preamp,coefs)
+    def _set_profile_name(self):
+        self.profile_box.blockSignals(True)
+        profile_name=self.sink.BaseProfile(self.filter_state.channel)
+        if profile_name is not None:
+            i=self.profile_box.findText(profile_name)
+            if i>=0:
+                self.profile_box.setCurrentIndex(i)
+        self.profile_box.blockSignals(False)
+
+
+class SliderArray(QtGui.QWidget):
+    def __init__(self,filter_state,parent=None):
+        super(SliderArray,self).__init__(parent)
+        #self.setStyleSheet('padding: 0px; border-width: 0px; margin: 0px;')
+        #self.setStyleSheet('font-family: monospace;'+outline%('blue'))
+        self.filter_state=filter_state
+        self.setLayout(QtGui.QHBoxLayout())
+        self.sub_array=None
+        self.set_sub_array(SliderArraySub(self.filter_state))
+        self.inhibit_resize=0
+    def set_sub_array(self,widget):
+        if self.sub_array is not None:
+            self.layout().removeWidget(self.sub_array)
+            self.sub_array.disconnect_signals()
+            self.sub_array.deleteLater()
+        self.sub_array=widget
+        self.layout().addWidget(self.sub_array)
+        self.sub_array.connect_signals()
+        self.filter_state.readback()
+    def resizeEvent(self,event):
+        super(SliderArray,self).resizeEvent(event)
+        if self.inhibit_resize==0:
+            self.inhibit_resize+=1
+            #self.add_sliders_to_fit()
+            t=QtCore.QTimer(self)
+            t.setSingleShot(True)
+            t.setInterval(0)
+            t.timeout.connect(partial(self.add_sliders_to_fit,event))
+            t.start()
+    def add_sliders_to_fit(self,event):
+        if event.oldSize().width()>0 and event.size().width()>0:
+            i=len(self.filter_state.frequencies)*int(round(float(event.size().width())/event.oldSize().width()))
+        else:
+            i=len(self.filter_state.frequencies)
+
+        t_w=self.size().width()
+        def evaluate(filter_state, target, variable):
+            base_freqs=self.filter_state.freq_proper(self.filter_state.DEFAULT_FREQUENCIES)
+            filter_state._set_frequency_values(subdivide(base_freqs,variable))
+            new_widget=SliderArraySub(filter_state)
+            w=new_widget.sizeHint().width()
+            return w-target
+        def searcher(initial,evaluator):
+            i=initial
+            def d(e): return 1 if e>=0 else -1
+            error=evaluator(i)
+            old_direction=d(error)
+            i-=old_direction
+            while True:
+                error=evaluator(i)
+                direction=d(error)
+                if direction!=old_direction:
+                    k=i-1
+                    #while direction<0 and error!=0:
+                    #    k-=1
+                    #    error=evaluator(i)
+                    #    direction=d(error)
+                    return k, evaluator(k)
+                i-=direction
+                old_direction=direction
+        searcher(i,partial(evaluate,self.filter_state,t_w))
+        self.set_sub_array(SliderArraySub(self.filter_state))
+        self.inhibit_resize-=1
+
+class SliderArraySub(QtGui.QWidget):
+    def __init__(self,filter_state,parent=None):
+        super(SliderArraySub,self).__init__(parent)
+        self.filter_state=filter_state
+        self.setLayout(QtGui.QGridLayout())
+        self.slider=[None]*len(self.filter_state.frequencies)
+        self.label=[None]*len(self.slider)
+        #self.setStyleSheet('padding: 0px; border-width: 0px; margin: 0px;')
+        #self.setStyleSheet('font-family: monospace;'+outline%('blue'))
+        qt=QtCore.Qt
+        #self.layout().setHorizontalSpacing(1)
+        def add_slider(slider,label, c):
+            self.layout().addWidget(slider,0,c,qt.AlignHCenter)
+            self.layout().addWidget(label,1,c,qt.AlignHCenter)
+            self.layout().setColumnMinimumWidth(c,max(label.sizeHint().width(),slider.sizeHint().width()))
+        def create_slider(slider_label):
+            slider=QtGui.QSlider(QtCore.Qt.Vertical,self)
+            label=SliderLabel(slider_label,filter_state,self)
+            slider.setRange(-1000,2000)
+            slider.setSingleStep(1)
+            return (slider,label)
+        self.preamp_slider,self.preamp_label=create_slider('Preamp')
+        add_slider(self.preamp_slider,self.preamp_label,0)
+        for i,hz in enumerate(self.filter_state.frequencies):
+            slider,label=create_slider(self.hz2label(hz))
+            self.slider[i]=slider
+            #slider.setStyleSheet('font-family: monospace;'+outline%('red',))
+            self.label[i]=label
+            c=i+1
+            add_slider(slider,label,i+1)
+    def hz2label(self, hz):
+        if hz==0:
+            label_text='DC'
+        elif hz==self.filter_state.sample_rate//2:
+            label_text='Coda'
+        else:
+            label_text=hz2str(hz)
+        return label_text
+
+    def connect_signals(self):
+        def connect(writer,reader,slider,label):
+            slider.valueChanged.connect(writer)
+            self.filter_state.readFilter.connect(reader)
+            label_cb=partial(slider.setValue,0)
+            label.clicked.connect(label_cb)
+            return label_cb
+
+        self.preamp_writer_cb=self.write_preamp
+        self.preamp_reader_cb=self.sync_preamp
+        self.preamp_label_cb=connect(self.preamp_writer_cb,
+                self.preamp_reader_cb,
+                self.preamp_slider,
+                self.preamp_label)
+        self.writer_callbacks=[None]*len(self.slider)
+        self.reader_callbacks=[None]*len(self.slider)
+        self.label_callbacks=[None]*len(self.label)
+        for i in range(len(self.slider)):
+            self.writer_callbacks[i]=partial(self.write_coefficient,i)
+            self.reader_callbacks[i]=partial(self.sync_coefficient,i)
+            self.label_callbacks[i]=connect(self.writer_callbacks[i],
+                    self.reader_callbacks[i],
+                    self.slider[i],
+                    self.label[i])
+    def disconnect_signals(self):
+        def disconnect(writer,reader,label_cb,slider,label):
+            slider.valueChanged.disconnect(writer)
+            self.filter_state.readFilter.disconnect(reader)
+            label.clicked.disconnect(label_cb)
+        disconnect(self.preamp_writer_cb, self.preamp_reader_cb,
+                self.preamp_label_cb, self.preamp_slider, self.preamp_label)
+        for i in range(len(self.slider)):
+            disconnect(self.writer_callbacks[i],
+                    self.reader_callbacks[i],
+                    self.label_callbacks[i],
+                    self.slider[i],
+                    self.label[i])
+
+    def write_preamp(self, v):
+        self.filter_state.preamp=self.slider2coef(v)
+        self.filter_state.seed()
+    def sync_preamp(self):
+        self.preamp_slider.blockSignals(True)
+        self.preamp_slider.setValue(self.coef2slider(self.filter_state.preamp))
+        self.preamp_slider.blockSignals(False)
+
+
+    def write_coefficient(self,i,v):
+        self.filter_state.coefficients[i]=self.slider2coef(v)/math.sqrt(2.0)
+        self.filter_state.seed()
+    def sync_coefficient(self,i):
+        slider=self.slider[i]
+        slider.blockSignals(True)
+        slider.setValue(self.coef2slider(math.sqrt(2.0)*self.filter_state.coefficients[i]))
+        slider.blockSignals(False)
+    @staticmethod
+    def slider2coef(x):
+        return (1.0+(x/1000.0))
+    @staticmethod
+    def coef2slider(x):
+        return int((x-1.0)*1000)
+outline='border-width: 1px; border-style: solid; border-color: %s;'
+
+class SliderLabel(QtGui.QLabel):
+    clicked=QtCore.pyqtSignal()
+    def __init__(self,label_text,filter_state,parent=None):
+        super(SliderLabel,self).__init__(parent)
+        self.setStyleSheet('font-family: monospace;')
+        self.setText(label_text)
+        self.setMinimumSize(self.sizeHint())
+    def mouseDoubleClickEvent(self, event):
+        self.clicked.emit()
+        super(SliderLabel,self).mouseDoubleClickEvent(event)
+
+#until there are server side state savings, do it in the client but try and avoid
+#simulaneous broadcasting situations
+class FilterState(QtCore.QObject):
+    #DEFAULT_FREQUENCIES=map(float,[25,50,75,100,150,200,300,400,500,800,1e3,1.5e3,3e3,5e3,7e3,10e3,15e3,20e3])
+    DEFAULT_FREQUENCIES=[31.75,63.5,125,250,500,1e3,2e3,4e3,8e3,16e3]
+    readFilter=QtCore.pyqtSignal()
+    def __init__(self,sink):
+        super(FilterState,self).__init__()
+        self.sink_props=dbus.Interface(sink,dbus_interface=prop_iface)
+        self.sink=dbus.Interface(sink,dbus_interface=eq_iface)
+        self.sample_rate=self.get_eq_attr('SampleRate')
+        self.filter_rate=self.get_eq_attr('FilterSampleRate')
+        self.channels=self.get_eq_attr('NChannels')
+        self.channel=self.channels
+        self.set_frequency_values(self.DEFAULT_FREQUENCIES)
+        self.sync_timer=QtCore.QTimer()
+        self.sync_timer.setSingleShot(True)
+        self.sync_timer.timeout.connect(self.save_state)
+
+    def get_eq_attr(self,attr):
+        return self.sink_props.Get(eq_iface,attr)
+    def freq_proper(self,xs):
+        return [0]+xs+[self.sample_rate//2]
+    def _set_frequency_values(self,freqs):
+        self.frequencies=freqs
+        #print('base',self.frequencies)
+        self.filter_frequencies=[int(round(x)) for x in self.translate_rates(self.filter_rate,self.sample_rate,
+                    self.frequencies)]
+        self.coefficients=[0.0]*len(self.frequencies)
+        self.preamp=1.0
+    def set_frequency_values(self,freqs):
+        self._set_frequency_values(self.freq_proper(freqs))
+    @staticmethod
+    def translate_rates(dst,src,rates):
+        return list([x*dst/src for x in rates])
+    def seed(self):
+        self.sink.SeedFilter(self.channel,self.filter_frequencies,self.coefficients,self.preamp)
+        self.sync_timer.start(SYNC_TIMEOUT)
+    def readback(self):
+        coefs,preamp=self.sink.FilterAtPoints(self.channel,self.filter_frequencies)
+        self.coefficients=coefs
+        self.preamp=preamp
+        self.readFilter.emit()
+    def set_filter(self,preamp,coefs):
+        self.sink.SetFilter(self.channel,dbus.Array(coefs),self.preamp)
+        self.sync_timer.start(SYNC_TIMEOUT)
+    def save_state(self):
+        print('saving state')
+        self.sink.SaveState()
+    def load_profile(self,profile):
+        self.sink.LoadProfile(self.channel,dbus.String(profile))
+        self.sync_timer.start(SYNC_TIMEOUT)
+    def flush_state(self):
+        if self.sync_timer.isActive():
+            self.sync_timer.stop()
+            self.save_state()
+
+
+def safe_log(k,b):
+    i=0
+    while k//b!=0:
+        i+=1
+        k=k//b
+    return i
+def hz2str(hz):
+    p=safe_log(hz,10.0)
+    if p<3:
+        return '%dHz' %(hz,)
+    elif hz%1000==0:
+        return '%dKHz' %(hz/(10.0**3),)
+    else:
+        return '%.1fKHz' %(hz/(10.0**3),)
+
+def subdivide(xs, t_points):
+    while len(xs)<t_points:
+        m=[0]*(2*len(xs)-1)
+        m[0:len(m):2]=xs
+        for i in range(1,len(m),2):
+            m[i]=(m[i-1]+m[i+1])//2
+        xs=m
+    p_drop=len(xs)-t_points
+    p_drop_left=p_drop//2
+    p_drop_right=p_drop-p_drop_left
+    #print('xs',xs)
+    #print('dropping %d, %d left, %d right' %(p_drop,p_drop_left,p_drop_right))
+    c=len(xs)//2
+    left=xs[0:p_drop_left*2:2]+xs[p_drop_left*2:c]
+    right=list(reversed(xs[c:]))
+    right=right[0:p_drop_right*2:2]+right[p_drop_right*2:]
+    right=list(reversed(right))
+    return left+right
+
+def main():
+    dbus.mainloop.qt.DBusQtMainLoop(set_as_default=True)
+    app=QtGui.QApplication(sys.argv)
+    qpaeq_main=QPaeq()
+    qpaeq_main.show()
+    sys.exit(app.exec_())
+
+if __name__=='__main__':
+    main()
diff --git a/todo b/todo
new file mode 100644 (file)
index 0000000..653bedc
--- /dev/null
+++ b/todo
@@ -0,0 +1,47 @@
+Build System:
+- Remove symdef files and use macros (like most other projects)
+- Use own name mangling scheme instead of ltdl's, which will eliminate the
+  need for .la files or extra trickery.
+
+Porting:
+- rtp module ported to Win32 (sendmsg/recvmsg emulation)
+
+I18N:
+- iconv stuff sent from utils to server (UTF-8)
+- iconv sample loading in server
+- gettextify pulseaudio
+
+Cleanups:
+- drop dependency of libpulse on libX11, instead use an external mini binary
+
+Network:
+- module-tunnel: improve latency calculation
+- module-tunnel: more reliable audio streaming over wifi
+- Compressed network streams for tunnels/rtp streams. (Might be a good GSoC project)
+  This builds on passthrough support. A good candidate codec would be CELT.
+
+Test:
+- autoload
+
+Auth/Crypto:
+- ssl
+- key rings for auth
+- challenge response auth
+- sasl auth 
+
+Features:
+- use scatter/gather io for sockets
+- examine if it is possible to mimic esd's handling of half duplex cards
+  (switch to capture when a recording client connects and drop playback during
+  that time)
+- add an API to libpulse for allocating memory from the pa_context memory pool
+- configuration file syntax:
+  - multiline configuration statements
+  - recursive .if
+
+Long term:
+- pass meta info for hearing impaired
+- X11: support for the X11 synchronization extension
+
+Backends for:
+- portaudio  (semi-done)
diff --git a/vala/libpulse-mainloop-glib.deps b/vala/libpulse-mainloop-glib.deps
new file mode 100644 (file)
index 0000000..69bebf3
--- /dev/null
@@ -0,0 +1 @@
+libpulse
diff --git a/vala/libpulse-mainloop-glib.vapi b/vala/libpulse-mainloop-glib.vapi
new file mode 100644 (file)
index 0000000..a54cb45
--- /dev/null
@@ -0,0 +1,13 @@
+using GLib;
+
+namespace PulseAudio {
+        [Compact]
+        [CCode (cheader_filename="pulse/glib-mainloop.h", cname="pa_glib_mainloop", cprefix="pa_glib_mainloop_", free_function="pa_glib_mainloop_free")]
+        public class GLibMainLoop {
+
+                [CCode (cname="pa_glib_mainloop_new")]
+                public GLibMainLoop(MainContext? c = null);
+
+                public unowned MainLoopApi get_api();
+        }
+}
diff --git a/vala/libpulse-simple.deps b/vala/libpulse-simple.deps
new file mode 100644 (file)
index 0000000..f90ee89
--- /dev/null
@@ -0,0 +1 @@
+libpulse-simple
diff --git a/vala/libpulse-simple.vapi b/vala/libpulse-simple.vapi
new file mode 100644 (file)
index 0000000..c7ece05
--- /dev/null
@@ -0,0 +1,34 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2012 Alexander Kurtz <kurtz.alex@googlemail.com>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+namespace PulseAudio {
+        [Compact]
+        [CCode (cheader_filename="pulse/simple.h", cname="pa_simple", cprefix="pa_simple_")]
+        class Simple {
+                public Simple(string? server = null, string? name = null, Stream.Direction dir = Stream.Direction.PLAYBACK,
+                              string? dev = null, string stream_name = "",
+                              SampleSpec ss = SampleSpec(){ format = SampleFormat.S16NE, rate = 44100, channels = 2 },
+                              ChannelMap? map = null, Stream.BufferAttr? attr = null, out int error = null);
+                public int write(void* data, size_t bytes, out int error = null);
+                public int drain(out int error = null);
+                public int read(void* data, size_t bytes, out int error = null);
+                public usec get_latency(out int error = null);
+                public int flush(out int error = null);
+        }
+}
diff --git a/vala/libpulse.deps b/vala/libpulse.deps
new file mode 100644 (file)
index 0000000..b3188f7
--- /dev/null
@@ -0,0 +1 @@
+posix
diff --git a/vala/libpulse.vapi b/vala/libpulse.vapi
new file mode 100644 (file)
index 0000000..107b745
--- /dev/null
@@ -0,0 +1,1535 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+using GLib;
+using Posix;
+
+[CCode (cheader_filename="pulse/pulseaudio.h")]
+namespace PulseAudio {
+
+        [CCode (cname="pa_get_library_version")]
+        public unowned string get_library_version();
+
+        [CCode (cname="PA_API_VERSION")]
+        public const int API_VERSION;
+
+        [CCode (cname="PA_PROTOCOL_VERSION")]
+        public const int PROTOCOL_VERSION;
+
+        [CCode (cname="PA_MAJOR")]
+        public const int MAJOR;
+
+        [CCode (cname="PA_MINOR")]
+        public const int MINOR;
+
+        [CCode (cname="PA_MICRO")]
+        public const int MICRO;
+
+        [CCode (cname="PA_CHECK_VERSION")]
+        public bool CHECK_VERSION(int major, int minor, int micro);
+
+        [CCode (cname="PA_INVALID_INDEX")]
+        public const uint32 INVALID_INDEX;
+
+        [CCode (cname="pa_free_cb_t", has_target=false)]
+        public delegate void FreeCb(void *p);
+
+        [CCode (cname="pa_sample_format_t", cprefix="PA_SAMPLE_", has_type_id=false)]
+        public enum SampleFormat {
+                U8,
+                ALAW,
+                ULAW,
+                S16LE,
+                S16BE,
+                FLOAT32LE,
+                FLOAT32BE,
+                S32LE,
+                S32BE,
+                S24LE,
+                S24BE,
+                S24_32LE,
+                S24_32BE,
+                MAX,
+                S16NE,
+                S16RE,
+                FLOAT32NE,
+                FLOAT32RE,
+                S32NE,
+                S32RE,
+                S24NE,
+                S24RE,
+                S24_32NE,
+                S24_32RE;
+
+                [CCode (cname="pa_sample_size_of_format")]
+                public size_t size();
+
+                [CCode (cname="pa_sample_format_to_string")]
+                public unowned string? to_string();
+
+                [CCode (cname="pa_sample_format_is_le")]
+                public int is_le();
+
+                [CCode (cname="pa_sample_format_is_be")]
+                public int is_be();
+
+                [CCode (cname="pa_sample_format_is_ne")]
+                public int is_ne();
+
+                [CCode (cname="pa_sample_format_is_re")]
+                public int is_re();
+
+                [CCode (cname="pa_parse_sample_format")]
+                public static SampleFormat parse(string b);
+        }
+
+        [CCode (cname="pa_usec_t")]
+        public struct usec : uint64 {
+        }
+
+        [CCode (cname="pa_sample_spec", has_type_id=false)]
+        public struct SampleSpec {
+                public SampleFormat format;
+                public uint32 rate;
+                public uint8 channels;
+
+                [CCode (cname="PA_SAMPLE_SPEC_SNPRINT_MAX")]
+                public const size_t SNPRINT_MAX;
+
+                [CCode (cname="pa_bytes_per_second")]
+                public size_t bytes_per_second();
+
+                [CCode (cname="pa_frame_size")]
+                public size_t frame_size();
+
+                [CCode (cname="pa_sample_size")]
+                public size_t sample_size();
+
+                [CCode (cname="pa_bytes_to_usec", instance_pos=1.1)]
+                public usec bytes_to_usec(size_t size);
+
+                [CCode (cname="pa_usec_to_bytes", instance_pos=1.1)]
+                public size_t usec_to_bytes(usec u);
+
+                [CCode (cname="pa_sample_spec_init")]
+                public unowned SampleSpec? init();
+
+                [CCode (cname="pa_sample_spec_valid")]
+                public bool valid();
+
+                [CCode (cname="pa_sample_spec_equal")]
+                public bool equal(SampleSpec other);
+
+                [CCode (cname="pa_sample_spec_snprint", instance_pos=3.1)]
+                public unowned string snprint(char[] buf);
+
+                public string sprint() {
+                        var buffer = new char[SNPRINT_MAX];
+                        this.snprint(buffer);
+                        return (string) buffer;
+                }
+
+                public string to_string() {
+                        return sprint();
+                }
+
+                [CCode (cname="pa_sample_spec_init")]
+                public SampleSpec();
+        }
+
+        // [CCode (cname="PA_BYTES_SNPRINT_MAX")]
+        [CCode (cname="PA_SAMPLE_SPEC_SNPRINT_MAX")]
+        public const size_t BYTES_SNPRINT_MAX;
+
+        [CCode (cname="pa_bytes_snprint")]
+        public unowned string bytes_snprint(char[] buf, uint bytes);
+
+        public string bytes_sprint(uint bytes) {
+                var buffer = new char[BYTES_SNPRINT_MAX];
+                bytes_snprint(buffer, bytes);
+                return (string) buffer;
+        }
+
+        [CCode (cname="pa_volume_t", has_type_id=false)]
+        public struct Volume : uint32 {
+
+                [CCode (cname="PA_SW_VOLUME_SNPRINT_DB_MAX")]
+                public const size_t SW_SNPRINT_DB_MAX;
+
+                [CCode (cname="PA_VOLUME_SNPRINT_MAX")]
+                public const size_t SNPRINT_MAX;
+
+                [CCode (cname="PA_VOLUME_MAX")]
+                public const Volume MAX;
+
+                [CCode (cname="PA_VOLUME_NORM")]
+                public const Volume NORM;
+
+                [CCode (cname="PA_VOLUME_MUTED")]
+                public const Volume MUTED;
+
+                // [CCode (cname="PA_VOLUME_INVALID")]
+                [CCode (cname="PA_VOLUME_MAX")]
+                public const Volume INVALID;
+
+                [CCode (cname="pa_volume_snprint", instance_pos = 3.1)]
+                public unowned string snprint(char[] s);
+
+                public string sprint() {
+                        var buffer = new char[SNPRINT_MAX];
+                        this.snprint(buffer);
+                        return (string) buffer;
+                }
+
+                public string to_string() {
+                        return sprint();
+                }
+
+                [CCode (cname="pa_sw_volume_snprint_dB", instance_pos = 3.1)]
+                public unowned string sw_snprint_dB(char[] s);
+
+                public string sw_sprint_dB() {
+                        var buffer = new char[SW_SNPRINT_DB_MAX];
+                        this.sw_snprint_dB(buffer);
+                        return (string) buffer;
+                }
+
+                [CCode (cname="pa_sw_volume_multiply")]
+                public Volume sw_multiply(Volume other);
+
+                [CCode (cname="pa_sw_volume_divide")]
+                public Volume sw_divide(Volume other);
+
+                [CCode (cname="pa_sw_volume_from_dB")]
+                public static Volume sw_from_dB(double f);
+
+                [CCode (cname="pa_sw_volume_to_dB")]
+                public double sw_to_dB();
+
+                [CCode (cname="pa_sw_volume_from_linear")]
+                public static Volume sw_from_linear(double f);
+
+                [CCode (cname="pa_sw_volume_to_linear")]
+                public double sw_to_linear();
+        }
+
+        [CCode (cname="PA_DECIBEL_MININFTY")]
+        public const double DECIBEL_MININFTY;
+
+        [CCode (cname="PA_CHANNELS_MAX")]
+        public const int CHANNELS_MAX;
+
+        [CCode (cname="PA_RATE_MAX")]
+        public const int RATE_MAX;
+
+        [CCode (cname="pa_cvolume", has_type_id=false)]
+        public struct CVolume {
+                public uint8 channels;
+                // TODO: Replace array length with CHANNELS_MAX once vala's bug #647788 is fixed
+                public Volume values[32];
+
+                [CCode (cname="PA_SW_CVOLUME_SNPRINT_DB_MAX")]
+                public const size_t SW_SNPRINT_DB_MAX;
+
+                [CCode (cname="PA_CVOLUME_SNPRINT_MAX")]
+                public const size_t SNPRINT_MAX;
+
+                [CCode (cname="pa_cvolume_equal")]
+                public bool equal(CVolume other);
+
+                [CCode (cname="pa_cvolume_init")]
+                public unowned CVolume? init();
+
+                [CCode (cname="pa_cvolume_reset")]
+                public unowned CVolume? reset(uint8 channels);
+
+                [CCode (cname="pa_cvolume_mute")]
+                public unowned CVolume? mute(uint8 channels);
+
+                [CCode (cname="pa_cvolume_snprint", instance_pos = 3.1)]
+                public unowned string snprint(char[] s);
+
+                public string sprint() {
+                        var buffer = new char[SNPRINT_MAX];
+                        this.snprint(buffer);
+                        return (string) buffer;
+                }
+
+                public string to_string() {
+                        return sprint();
+                }
+
+                [CCode (cname="pa_sw_cvolume_snprint_dB", instance_pos = 3.1)]
+                public unowned string sw_snprint_dB(char [] s);
+
+                public string sw_sprint_dB() {
+                        var buffer = new char[SW_SNPRINT_DB_MAX];
+                        this.sw_snprint_dB(buffer);
+                        return (string) buffer;
+                }
+
+                [CCode (cname="pa_cvolume_init")]
+                public CVolume();
+
+                [CCode (cname="pa_cvolume_avg")]
+                public Volume avg();
+
+                [CCode (cname="pa_cvolume_max")]
+                public Volume max();
+
+                [CCode (cname="pa_cvolume_min")]
+                public Volume min();
+
+                [CCode (cname="pa_cvolume_avg_mask")]
+                public Volume avg_mask(ChannelMap map, ChannelPositionMask mask);
+
+                [CCode (cname="pa_cvolume_max_mask")]
+                public Volume max_mask(ChannelMap map, ChannelPositionMask mask);
+
+                [CCode (cname="pa_cvolume_min_mask")]
+                public Volume min_mask(ChannelMap map, ChannelPositionMask mask);
+
+                [CCode (cname="pa_cvolume_valid")]
+                public bool valid();
+
+                [CCode (cname="pa_cvolume_channels_equal_to")]
+                public bool channels_equal_to(Volume other);
+
+                [CCode (cname="pa_cvolume_is_muted")]
+                public bool is_muted();
+
+                [CCode (cname="pa_cvolume_is_norm")]
+                public bool is_norm();
+
+                [CCode (cname="pa_sw_cvolume_multiply")]
+                public unowned CVolume? multiply(CVolume other);
+
+                [CCode (cname="pa_sw_cvolume_divide")]
+                public unowned CVolume? divide(CVolume other);
+
+                [CCode (cname="pa_sw_cvolume_multiply_scalar")]
+                public unowned CVolume? multiply_scalar(Volume other);
+
+                [CCode (cname="pa_sw_cvolume_divide_scalar")]
+                public unowned CVolume? divide_scalar(Volume other);
+
+                [CCode (cname="pa_cvolume_remap")]
+                public unowned CVolume? remap(ChannelMap from, ChannelMap to);
+
+                [CCode (cname="pa_cvolume_compatible")]
+                public bool compatible(SampleSpec ss);
+
+                [CCode (cname="pa_cvolume_compatible_with_channel_map")]
+                public bool compatible_with_channel_map(ChannelMap cm);
+
+                [CCode (cname="pa_cvolume_set")]
+                public unowned CVolume? set(uint8 channels, Volume v);
+
+                [CCode (cname="pa_cvolume_get_balance")]
+                public float get_balance(ChannelMap map);
+
+                [CCode (cname="pa_cvolume_set_balance")]
+                public unowned CVolume? set_balance(ChannelMap map, float b);
+
+                [CCode (cname="pa_cvolume_get_fade")]
+                public float get_fade(ChannelMap map);
+
+                [CCode (cname="pa_cvolume_set_fade")]
+                public unowned CVolume? set_fade(ChannelMap map, float f);
+
+                [CCode (cname="pa_cvolume_scale")]
+                public unowned CVolume? scale(Volume max);
+
+                [CCode (cname="pa_cvolume_scale_mask")]
+                public unowned CVolume? scale_mask(Volume max, ChannelMap map, ChannelPositionMask mask);
+
+                [CCode (cname="pa_cvolume_set_position")]
+                public unowned CVolume? set_position(ChannelMap map, ChannelPosition p, Volume v);
+
+                [CCode (cname="pa_cvolume_get_position")]
+                public Volume get_position(ChannelMap map, ChannelPosition p);
+
+                [CCode (cname="pa_cvolume_merge")]
+                public unowned CVolume? merge(CVolume other);
+
+                [CCode (cname="pa_cvolume_inc")]
+                public unowned CVolume? inc(Volume plus = 1);
+
+                [CCode (cname="pa_cvolume_dec")]
+                public unowned CVolume? dec(Volume minus = 1);
+        }
+
+        [CCode (cname="pa_channel_map", has_type_id=false)]
+        public struct ChannelMap {
+                public uint8 channels;
+                // TODO: Replace array length with CHANNELS_MAX once vala's bug #647788 is fixed
+                public ChannelPosition map[32];
+
+                [CCode (cname="PA_CHANNEL_MAP_SNPRINT_MAX")]
+                public const size_t SNPRINT_MAX;
+
+                [CCode (cname="pa_channel_map_init")]
+                public ChannelMap();
+
+                [CCode (cname="pa_channel_map_init")]
+                public unowned ChannelMap? init();
+
+                [CCode (cname="pa_channel_map_init_mono")]
+                public unowned ChannelMap? init_mono();
+
+                [CCode (cname="pa_channel_map_init_stereo")]
+                public unowned ChannelMap? init_stereo();
+
+                [CCode (cname="pa_channel_map_init_auto")]
+                public unowned ChannelMap? init_auto(uint8 channels, ChannelMapDef def = ChannelMapDef.DEFAULT);
+
+                [CCode (cname="pa_channel_map_init_extend")]
+                public unowned ChannelMap? init_extend(uint8 channels, ChannelMapDef def = ChannelMapDef.DEFAULT);
+
+                [CCode (cname="pa_channel_map_snprint", instance_pos = 3.1)]
+                public unowned string snprint(char[] s);
+
+                public string sprint() {
+                        var buffer = new char[SNPRINT_MAX];
+                        this.snprint(buffer);
+                        return (string) buffer;
+                }
+
+                public string to_string() {
+                        return sprint();
+                }
+
+                [CCode (cname="pa_channel_map_parse")]
+                public unowned ChannelMap? parse(string s);
+
+                [CCode (cname="pa_channel_map_equal")]
+                public bool equal(ChannelMap other);
+
+                [CCode (cname="pa_channel_map_superset")]
+                public bool superset(ChannelMap other);
+
+                [CCode (cname="pa_channel_map_valid")]
+                public bool valid();
+
+                [CCode (cname="pa_channel_map_compatible")]
+                public bool compatible(SampleSpec ss);
+
+                [CCode (cname="pa_channel_map_can_balance")]
+                public bool can_balance();
+
+                [CCode (cname="pa_channel_map_can_fade")]
+                public bool can_fade();
+
+                [CCode (cname="pa_channel_map_to_name")]
+                public unowned string? to_name();
+
+                [CCode (cname="pa_channel_map_to_pretty_name")]
+                public unowned string? to_pretty_name();
+
+                [CCode (cname="pa_channel_map_has_position")]
+                public bool has_position(ChannelPosition p);
+
+                [CCode (cname="pa_channel_map_mask")]
+                public ChannelPositionMask mask();
+        }
+
+        [CCode (cname="pa_channel_position_mask_t", has_type_id=false)]
+        public struct ChannelPositionMask : uint64 {
+        }
+
+        [CCode (cname="pa_channel_position_t", cprefix="PA_CHANNEL_POSITION_", has_type_id=false)]
+        public enum ChannelPosition {
+                INVALID,
+                MONO,
+                FRONT_LEFT,
+                FRONT_RIGHT,
+                FRONT_CENTER,
+                REAR_CENTER,
+                REAR_LEFT,
+                REAR_RIGHT,
+                LFE,
+                FRONT_LEFT_OF_CENTER,
+                FRONT_RIGHT_OF_CENTER,
+                SIDE_LEFT,
+                SIDE_RIGHT,
+                TOP_CENTER,
+                AUX0,
+                AUX1,
+                AUX2,
+                AUX3,
+                AUX4,
+                AUX5,
+                AUX6,
+                AUX7,
+                AUX8,
+                AUX9,
+                AUX10,
+                AUX11,
+                AUX12,
+                AUX13,
+                AUX14,
+                AUX15,
+                AUX16,
+                AUX17,
+                AUX18,
+                AUX19,
+                AUX20,
+                AUX21,
+                AUX22,
+                AUX23,
+                AUX24,
+                AUX25,
+                AUX26,
+                AUX27,
+                AUX28,
+                AUX29,
+                AUX30,
+                AUX31,
+                MAX;
+
+                [CCode (cname="PA_CHANNEL_POSITION_MASK")]
+                public ChannelPositionMask mask();
+
+                [CCode (cname="pa_channel_position_to_string")]
+                public unowned string? to_string();
+
+                [CCode (cname="pa_channel_position_to_pretty_string")]
+                public unowned string? to_pretty_string();
+
+                [CCode (cname="pa_channel_position_from_string")]
+                public static ChannelPosition from_string(string s);
+        }
+
+        [CCode (cname="pa_channel_map_def_t", cprefix="PA_CHANNEL_MAP_", has_type_id=false)]
+        public enum ChannelMapDef {
+                AIFF,
+                WAVEEX,
+                AUX,
+                DEFAULT,
+
+                [CCode (cname="PA_CHANNEL_MAP_DEF_MAX")]
+                MAX
+        }
+
+        [Compact]
+        [CCode (cname="pa_proplist", cprefix="pa_proplist_", free_function="pa_proplist_free", has_type_id=false)]
+        public class Proplist {
+
+                [CCode (cname="PA_PROP_MEDIA_NAME")]
+                public const string PROP_MEDIA_NAME;
+                [CCode (cname="PA_PROP_MEDIA_TITLE")]
+                public const string PROP_MEDIA_TITLE;
+                [CCode (cname="PA_PROP_MEDIA_ARTIST")]
+                public const string PROP_MEDIA_ARTIST;
+                [CCode (cname="PA_PROP_MEDIA_COPYRIGHT")]
+                public const string PROP_MEDIA_COPYRIGHT;
+                [CCode (cname="PA_PROP_MEDIA_SOFTWARE")]
+                public const string PROP_MEDIA_SOFTWARE;
+                [CCode (cname="PA_PROP_MEDIA_LANGUAGE")]
+                public const string PROP_MEDIA_LANGUAGE;
+                [CCode (cname="PA_PROP_MEDIA_FILENAME")]
+                public const string PROP_MEDIA_FILENAME;
+                [CCode (cname="PA_PROP_MEDIA_ICON_NAME")]
+                public const string PROP_MEDIA_ICON_NAME;
+                [CCode (cname="PA_PROP_MEDIA_ROLE")]
+                public const string PROP_MEDIA_ROLE;
+                [CCode (cname="PA_PROP_EVENT_ID")]
+                public const string PROP_EVENT_ID;
+                [CCode (cname="PA_PROP_EVENT_DESCRIPTION")]
+                public const string PROP_EVENT_DESCRIPTION;
+                [CCode (cname="PA_PROP_EVENT_MOUSE_X")]
+                public const string PROP_EVENT_MOUSE_X;
+                [CCode (cname="PA_PROP_EVENT_MOUSE_Y")]
+                public const string PROP_EVENT_MOUSE_Y;
+                [CCode (cname="PA_PROP_EVENT_MOUSE_HPOS")]
+                public const string PROP_EVENT_MOUSE_HPOS;
+                [CCode (cname="PA_PROP_EVENT_MOUSE_VPOS")]
+                public const string PROP_EVENT_MOUSE_VPOS;
+                [CCode (cname="PA_PROP_EVENT_MOUSE_BUTTON")]
+                public const string PROP_EVENT_MOUSE_BUTTON;
+                [CCode (cname="PA_PROP_WINDOW_NAME")]
+                public const string PROP_WINDOW_NAME;
+                [CCode (cname="PA_PROP_WINDOW_ID")]
+                public const string PROP_WINDOW_ID;
+                [CCode (cname="PA_PROP_WINDOW_ICON_NAME")]
+                public const string PROP_WINDOW_ICON_NAME;
+                [CCode (cname="PA_PROP_WINDOW_X11_DISPLAY")]
+                public const string PROP_WINDOW_X11_DISPLAY;
+                [CCode (cname="PA_PROP_WINDOW_X11_SCREEN")]
+                public const string PROP_WINDOW_X11_SCREEN;
+                [CCode (cname="PA_PROP_WINDOW_X11_MONITOR")]
+                public const string PROP_WINDOW_X11_MONITOR;
+                [CCode (cname="PA_PROP_WINDOW_X11_XID")]
+                public const string PROP_WINDOW_X11_XID;
+                [CCode (cname="PA_PROP_APPLICATION_NAME")]
+                public const string PROP_APPLICATION_NAME;
+                [CCode (cname="PA_PROP_APPLICATION_ID")]
+                public const string PROP_APPLICATION_ID;
+                [CCode (cname="PA_PROP_APPLICATION_VERSION")]
+                public const string PROP_APPLICATION_VERSION;
+                [CCode (cname="PA_PROP_APPLICATION_ICON_NAME")]
+                public const string PROP_APPLICATION_ICON_NAME;
+                [CCode (cname="PA_PROP_APPLICATION_LANGUAGE")]
+                public const string PROP_APPLICATION_LANGUAGE;
+                [CCode (cname="PA_PROP_APPLICATION_PROCESS_ID")]
+                public const string PROP_APPLICATION_PROCESS_ID;
+                [CCode (cname="PA_PROP_APPLICATION_PROCESS_BINARY")]
+                public const string PROP_APPLICATION_PROCESS_BINARY;
+                [CCode (cname="PA_PROP_APPLICATION_PROCESS_USER")]
+                public const string PROP_APPLICATION_PROCESS_USER;
+                [CCode (cname="PA_PROP_APPLICATION_PROCESS_HOST")]
+                public const string PROP_APPLICATION_PROCESS_HOST;
+                [CCode (cname="PA_PROP_APPLICATION_PROCESS_MACHINE_ID")]
+                public const string PROP_APPLICATION_PROCESS_MACHINE_ID;
+                [CCode (cname="PA_PROP_APPLICATION_PROCESS_SESSION_ID")]
+                public const string PROP_APPLICATION_PROCESS_SESSION_ID;
+                [CCode (cname="PA_PROP_DEVICE_STRING")]
+                public const string PROP_DEVICE_STRING;
+                [CCode (cname="PA_PROP_DEVICE_API")]
+                public const string PROP_DEVICE_API;
+                [CCode (cname="PA_PROP_DEVICE_DESCRIPTION")]
+                public const string PROP_DEVICE_DESCRIPTION;
+                [CCode (cname="PA_PROP_DEVICE_BUS_PATH")]
+                public const string PROP_DEVICE_BUS_PATH;
+                [CCode (cname="PA_PROP_DEVICE_SERIAL")]
+                public const string PROP_DEVICE_SERIAL;
+                [CCode (cname="PA_PROP_DEVICE_VENDOR_ID")]
+                public const string PROP_DEVICE_VENDOR_ID;
+                [CCode (cname="PA_PROP_DEVICE_VENDOR_NAME")]
+                public const string PROP_DEVICE_VENDOR_NAME;
+                [CCode (cname="PA_PROP_DEVICE_PRODUCT_ID")]
+                public const string PROP_DEVICE_PRODUCT_ID;
+                [CCode (cname="PA_PROP_DEVICE_PRODUCT_NAME")]
+                public const string PROP_DEVICE_PRODUCT_NAME;
+                [CCode (cname="PA_PROP_DEVICE_CLASS")]
+                public const string PROP_DEVICE_CLASS;
+                [CCode (cname="PA_PROP_DEVICE_FORM_FACTOR")]
+                public const string PROP_DEVICE_FORM_FACTOR;
+                [CCode (cname="PA_PROP_DEVICE_BUS")]
+                public const string PROP_DEVICE_BUS;
+                [CCode (cname="PA_PROP_DEVICE_ICON_NAME")]
+                public const string PROP_DEVICE_ICON_NAME;
+                [CCode (cname="PA_PROP_DEVICE_ACCESS_MODE")]
+                public const string PROP_DEVICE_ACCESS_MODE;
+                [CCode (cname="PA_PROP_DEVICE_MASTER_DEVICE")]
+                public const string PROP_DEVICE_MASTER_DEVICE;
+                [CCode (cname="PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE")]
+                public const string PROP_DEVICE_BUFFERING_BUFFER_SIZE;
+                [CCode (cname="PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE")]
+                public const string PROP_DEVICE_BUFFERING_FRAGMENT_SIZE;
+                [CCode (cname="PA_PROP_DEVICE_PROFILE_NAME")]
+                public const string PROP_DEVICE_PROFILE_NAME;
+                [CCode (cname="PA_PROP_DEVICE_INTENDED_ROLES")]
+                public const string PROP_DEVICE_INTENDED_ROLES;
+                [CCode (cname="PA_PROP_DEVICE_PROFILE_DESCRIPTION")]
+                public const string PROP_DEVICE_PROFILE_DESCRIPTION;
+                [CCode (cname="PA_PROP_MODULE_AUTHOR")]
+                public const string PROP_MODULE_AUTHOR;
+                [CCode (cname="PA_PROP_MODULE_DESCRIPTION")]
+                public const string PROP_MODULE_DESCRIPTION;
+                [CCode (cname="PA_PROP_MODULE_USAGE")]
+                public const string PROP_MODULE_USAGE;
+                [CCode (cname="PA_PROP_MODULE_VERSION")]
+                public const string PROP_MODULE_VERSION;
+
+                [CCode (cname="pa_proplist_new")]
+                public Proplist();
+
+                public int sets(string key, string value);
+                public int setp(string pair);
+
+                [PrintfFormat]
+                public int setf(string key, string format, ...);
+
+                public int set(string key, void* data, size_t size);
+
+                public unowned string? gets(string key);
+
+                public int get(string key, out void* data, out size_t size);
+
+                public void update(UpdateMode mode, Proplist other);
+
+                public void unset(string key);
+
+                [CCode (array_length = false)]
+                public void unset_many(string[] key);
+
+                public unowned string? iterate(ref void* state);
+
+                public string to_string();
+
+                public string to_string_sep(string sep);
+
+                public static Proplist? from_string(string s);
+
+                public int contains(string key);
+
+                public void clear();
+
+                public Proplist copy();
+
+                public uint size();
+
+                public bool is_empty();
+        }
+
+        [CCode (cname="pa_update_mode_t", cprefix="PA_UPDATE_", has_type_id=false)]
+        public enum UpdateMode {
+                SET,
+                MERGE,
+                REPLACE
+        }
+
+        [CCode (cname="PA_OK")]
+        public const int OK;
+
+        [CCode (cname="int", cprefix="PA_ERR_", has_type_id=false)]
+        public enum Error {
+                ACCESS,
+                COMMAND,
+                INVALID,
+                EXIST,
+                NOENTITY,
+                CONNECTIONREFUSED,
+                PROTOCOL,
+                TIMEOUT,
+                AUTHKEY,
+                INTERNAL,
+                CONNECTIONTERMINATED,
+                KILLED,
+                INVALIDSERVER,
+                MODINITFAILED,
+                BADSTATE,
+                NODATA,
+                VERSION,
+                TOOLARGE,
+                NOTSUPPORTED,
+                UNKNOWN,
+                NOEXTENSION,
+                OBSOLETE,
+                NOTIMPLEMENTED,
+                FORKED,
+                IO,
+                MAX
+        }
+
+        [CCode (cname="pa_strerror")]
+        public unowned string? strerror(Error e);
+
+        public delegate void VoidFunc();
+
+        [CCode (cname="pa_spawn_api", has_type_id=false)]
+        public struct SpawnApi {
+                VoidFunc? prefork;
+                VoidFunc? postfork;
+                VoidFunc? atfork;
+        }
+
+        [CCode (cname="pa_io_event_flags_t", cprefix="PA_IO_EVENT_", has_type_id=false)]
+        public enum IoEventFlags {
+                NULL,
+                INPUT,
+                OUTPUT,
+                HANGUP,
+                ERROR
+        }
+
+        [Compact]
+        [CCode (cname="pa_io_event", unref_function="", ref_function="", has_type_id=false)]
+        public struct IoEvent {
+        }
+
+        [Compact]
+        [CCode (cname="pa_time_event", unref_function="", ref_function="", has_type_id=false)]
+        public struct TimeEvent {
+        }
+
+        [Compact]
+        [CCode (cname="pa_defer_event", unref_function="", ref_function="", has_type_id=false)]
+        public struct DeferEvent {
+        }
+
+        [Compact]
+        [CCode (cname="pa_signal_event", cprefix="pa_signal_", free_function="pa_signal_free", has_type_id=false)]
+        public struct SignalEvent {
+
+                [CCode (cname="pa_signal_new")]
+                public SignalEvent(int sig, MainLoopApi.SignalEventCb cb);
+
+                public void set_destroy(MainLoopApi.SignalEventDestroyCb cb);
+        }
+
+        [Compact]
+        [CCode (cname="pa_mainloop_api", unref_function="", ref_function="", has_type_id=false)]
+        public class MainLoopApi {
+                public void* userdata;
+
+                /* Callbacks for the consumer to implement*/
+                public delegate void IoEventCb(MainLoopApi a, IoEvent e, int fd, IoEventFlags flags);
+                public delegate void IoEventDestroyCb(MainLoopApi a, IoEvent e);
+
+                public delegate void TimeEventCb(MainLoopApi a, TimeEvent e, ref timeval t);
+                public delegate void TimeEventDestroyCb(MainLoopApi a, TimeEvent e);
+
+                public delegate void DeferEventCb(MainLoopApi a, DeferEvent e);
+                public delegate void DeferEventDestroyCb(MainLoopApi a, DeferEvent e);
+
+                public delegate void SignalEventCb(MainLoopApi a, SignalEvent e);
+                public delegate void SignalEventDestroyCb(MainLoopApi a, SignalEvent e);
+
+                /* Callbacks for the provider to implement */
+                public delegate IoEvent IoNewCb(MainLoopApi a, int fd, IoEventFlags flags, IoEventCb cb);
+                public delegate void IoEnableCb(MainLoopApi a, IoEvent e, IoEventFlags flags);
+                public delegate void IoFreeCb(MainLoopApi a, IoEvent e);
+                public delegate void IoSetDestroyCb(MainLoopApi a, IoEvent e, IoEventDestroyCb? cb);
+
+                public delegate TimeEvent TimeNewCb(MainLoopApi a, timeval? t, TimeEventCb cb);
+                public delegate void TimeRestartCb(MainLoopApi a, TimeEvent e, timeval? t);
+                public delegate void TimeFreeCb(MainLoopApi a, TimeEvent e);
+                public delegate void TimeSetDestroyCb(MainLoopApi a, TimeEvent e, TimeEventDestroyCb? cb);
+
+                public delegate DeferEvent DeferNewCb(MainLoopApi a, DeferEventCb cb);
+                public delegate void DeferEnableCb(MainLoopApi a, DeferEvent e, bool b);
+                public delegate void DeferFreeCb(MainLoopApi a, DeferEvent e);
+                public delegate void DeferSetDestroyCb(MainLoopApi a, DeferEvent e, DeferEventDestroyCb? cb);
+
+                public delegate void QuitCb(MainLoopApi a, int retval);
+
+                public delegate void OnceCb(MainLoopApi a);
+
+                public IoNewCb io_new;
+                public IoEnableCb io_enable;
+                public IoFreeCb io_free;
+                public IoSetDestroyCb io_set_destroy;
+
+                public TimeNewCb time_new;
+                public TimeRestartCb time_restart;
+                public TimeFreeCb time_free;
+                public TimeSetDestroyCb time_set_destroy;
+
+                public DeferNewCb defer_new;
+                public DeferEnableCb defer_enable;
+                public DeferFreeCb defer_free;
+                public DeferSetDestroyCb defer_set_destroy;
+
+                public QuitCb quit;
+
+                [CCode (cname="pa_mainloop_api_once")]
+                public void once(OnceCb cb);
+        }
+
+        [CCode (cname="pa_signal_init")]
+        public void signal_init(MainLoopApi api);
+
+        [CCode (cname="pa_signal_done")]
+        public void signal_done();
+
+        [CCode (cname="pa_poll_func")]
+        public delegate int PollFunc(pollfd[] ufds, int timeout);
+
+        [Compact]
+        [CCode (cname="pa_mainloop", cprefix="pa_mainloop_", free_function="pa_mainloop_free", has_type_id=false)]
+        public class MainLoop {
+
+                [CCode (cname="pa_mainloop_new")]
+                public MainLoop();
+
+                public int prepare(int timeout = -1);
+                public int poll();
+                public int dispatch();
+                public int get_retval();
+                public int iterate(bool block = true, out int retval = null);
+                public int run(out int retval = null);
+                public unowned MainLoopApi get_api();
+                public void quit(int retval);
+                public void wakeup();
+                public void set_poll_func(PollFunc poll_func);
+        }
+
+        [Compact]
+        [CCode (cname="pa_threaded_mainloop", cprefix="pa_threaded_mainloop_", free_function="pa_threaded_mainloop_free")]
+        public class ThreadedMainLoop {
+
+                [CCode (cname="pa_threaded_mainloop_new")]
+                public ThreadedMainLoop();
+
+                public int start();
+                public void stop();
+                public void lock();
+                public void unlock();
+                public void wait();
+                public void signal(bool WaitForAccept = false);
+                public void accept();
+                public int get_retval();
+                public unowned MainLoopApi get_api();
+                public bool in_thread();
+        }
+
+        [Compact]
+        [CCode (cname="pa_operation", cprefix="pa_operation_", unref_function="pa_operation_unref", ref_function="pa_operation_ref", has_type_id=false)]
+        public class Operation {
+
+                [CCode (cname="pa_operation_state_t", cprefix="PA_OPERATION_", has_type_id=false)]
+                public enum State {
+                        RUNNING,
+                        DONE,
+                        CANCELED
+                }
+
+                public void cancel();
+                public State get_state();
+        }
+
+        [Compact]
+        [CCode (cname="pa_context", cprefix="pa_context_", unref_function="pa_context_unref", ref_function="pa_context_ref", has_type_id=false)]
+        public class Context {
+
+                [CCode (cname="pa_context_flags_t", cprefix="PA_CONTEXT_", has_type_id=false)]
+                public enum Flags {
+                        NOAUTOSPAWN,
+                        NOFAIL
+                }
+
+                [CCode (cname="pa_context_state_t", cprefix="PA_CONTEXT_", has_type_id=false)]
+                public enum State {
+                        UNCONNECTED,
+                        CONNECTING,
+                        AUTHORIZING,
+                        SETTING_NAME,
+                        READY,
+                        FAILED,
+                        TERMINATED;
+
+                        [CCode (cname="PA_CONTEXT_IS_GOOD", has_type_id=false)]
+                        public bool IS_GOOD();
+                }
+
+                [CCode (cname="pa_subscription_mask_t", cprefix="PA_SUBSCRIPTION_MASK_", has_type_id=false)]
+                public enum SubscriptionMask {
+                        NULL,
+                        SINK,
+                        SOURCE,
+                        SINK_INPUT,
+                        SOURCE_OUTPUT,
+                        MODULE,
+                        CLIENT,
+                        SAMPLE_CACHE,
+                        SERVER,
+                        CARD,
+                        ALL;
+
+                        [CCode (cname="pa_subscription_match_flags")]
+                        public bool match_flags(SubscriptionEventType t);
+                }
+
+                [CCode (cname="pa_subscription_event_type_t", cprefix="PA_SUBSCRIPTION_EVENT_", has_type_id=false)]
+                public enum SubscriptionEventType {
+                        SINK,
+                        SOURCE,
+                        SINK_INPUT,
+                        SOURCE_OUTPUT,
+                        MODULE,
+                        CLIENT,
+                        SAMPLE_CACHE,
+                        SERVER,
+                        CARD,
+                        FACILITY_MASK,
+                        NEW,
+                        CHANGE,
+                        REMOVE,
+                        TYPE_MASK
+                }
+
+                [CCode (cname = "pa_context_notify_cb_t")]
+                public delegate void NotifyCb(Context c);
+                [CCode (cname = "pa_context_success_cb_t")]
+                public delegate void SuccessCb(Context c, int success);
+                [CCode (cname = "pa_context_event_cb_t")]
+                public delegate void EventCb(Context c, string name, Proplist? proplist);
+                [CCode (cname = "pa_context_subscribe_cb_t")]
+                public delegate void SubscribeCb(Context c, SubscriptionEventType t, uint32 idx);
+                [CCode (cname = "pa_sink_info_cb_t")]
+                public delegate void SinkInfoCb(Context c, SinkInfo? i, int eol);
+                [CCode (cname = "pa_source_info_cb_t")]
+                public delegate void SourceInfoCb(Context c, SourceInfo? i, int eol);
+                [CCode (cname = "pa_card_info_cb_t")]
+                public delegate void CardInfoCb(Context c, CardInfo? i, int eol);
+                [CCode (cname = "pa_sink_input_info_cb_t")]
+                public delegate void SinkInputInfoCb(Context c, SinkInputInfo? i, int eol);
+                [CCode (cname = "pa_source_output_info_cb_t")]
+                public delegate void SourceOutputInfoCb(Context c, SourceOutputInfo? i, int eol);
+                [CCode (cname = "pa_server_info_cb_t")]
+                public delegate void ServerInfoCb(Context c, ServerInfo? i);
+                [CCode (cname = "pa_stat_info_cb_t")]
+                public delegate void StatInfoCb(Context c, ServerInfo? i);
+                [CCode (cname = "pa_module_info_cb_t")]
+                public delegate void ModuleInfoCb(Context c, ModuleInfo? i, int eol);
+                [CCode (cname = "pa_client_info_cb_t")]
+                public delegate void ClientInfoCb(Context c, ClientInfo? i, int eol);
+                [CCode (cname = "pa_sample_info_cb_t")]
+                public delegate void SampleInfoCb(Context c, SampleInfo? i, int eol);
+                [CCode (cname = "pa_context_index_cb_t")]
+                public delegate void IndexCb(Context c, uint32 idx);
+
+                [CCode (cname="pa_context_new_with_proplist")]
+                public Context(MainLoopApi api, string? name, Proplist? proplist = null);
+
+                public void set_state_callback(NotifyCb? cb = null);
+                public void set_event_callback(EventCb? cb = null);
+                public void set_subscribe_callback(SubscribeCb? cb = null);
+
+                public Error errno();
+
+                public int is_pending();
+                public State get_state();
+                public int is_local();
+                public unowned string? get_server();
+                public uint32 get_protocol_version();
+                public uint32 get_server_protocol_version();
+                public uint32 get_index();
+
+                public int connect(string? server = null, Flags flags = 0, SpawnApi? api = null);
+                public void disconnect();
+
+                public Operation? drain(NotifyCb? cb = null);
+                public Operation? exit_daemon(SuccessCb? cb = null);
+                public Operation? set_default_sink(string name, SuccessCb? cb = null);
+                public Operation? set_default_source(string name, SuccessCb? cb = null);
+                public Operation? set_name(string name, SuccessCb? cb = null);
+
+                [CCode (array_length = false)]
+                public Operation? proplist_remove(string[] keys, SuccessCb? cb = null);
+                public Operation? proplist_update(UpdateMode mode, Proplist pl, SuccessCb? cb = null);
+
+                public Operation? subscribe(SubscriptionMask mask, SuccessCb? cb = null);
+
+                public Operation? get_sink_info_by_name(string name, SinkInfoCb cb);
+                public Operation? get_sink_info_by_index(uint32 idx, SinkInfoCb cb);
+                public Operation? get_sink_info_list(SinkInfoCb cb);
+
+                public Operation? set_sink_volume_by_name(string name, CVolume volume, SuccessCb? cb = null);
+                public Operation? set_sink_volume_by_index(uint32 idx, CVolume volume, SuccessCb? cb = null);
+                public Operation? set_sink_mute_by_name(string name, bool mute, SuccessCb? cb = null);
+                public Operation? set_sink_mute_by_index(uint32 idx, bool mute, SuccessCb? cb = null);
+
+                public Operation? suspend_sink_by_name(string name, bool suspend, SuccessCb? cb = null);
+                public Operation? suspend_sink_by_index(uint32 idx, bool suspend, SuccessCb? cb = null);
+
+                public Operation? set_sink_port_by_name(string name, string port, SuccessCb? cb = null);
+                public Operation? set_sink_port_by_index(uint32 idx, string port, SuccessCb? cb = null);
+
+                public Operation? get_source_info_by_name(string name, SourceInfoCb cb);
+                public Operation? get_source_info_by_index(uint32 idx, SourceInfoCb cb);
+                public Operation? get_source_info_list(SourceInfoCb cb);
+
+                public Operation? set_source_volume_by_name(string name, CVolume volume, SuccessCb? cb = null);
+                public Operation? set_source_volume_by_index(uint32 idx, CVolume volume, SuccessCb? cb = null);
+                public Operation? set_source_mute_by_name(string name, bool mute, SuccessCb? cb = null);
+                public Operation? set_source_mute_by_index(uint32 idx, bool mute, SuccessCb? cb = null);
+
+                public Operation? suspend_source_by_name(string name, bool suspend, SuccessCb? cb = null);
+                public Operation? suspend_source_by_index(uint32 idx, bool suspend, SuccessCb? cb = null);
+
+                public Operation? set_source_port_by_name(string name, string port, SuccessCb? cb = null);
+                public Operation? set_source_port_by_index(uint32 idx, string port, SuccessCb? cb = null);
+
+                public Operation? get_server_info(ServerInfoCb cb);
+
+                public Operation? get_module_info(uint32 idx, ModuleInfoCb cb);
+                public Operation? get_module_info_list(ModuleInfoCb cb);
+
+                public Operation? load_module(string name, string? argument, IndexCb? cb = null);
+                public Operation? unload_module(uint32 idx, SuccessCb? cb = null);
+
+                public Operation? get_client_info(uint32 idx, ClientInfoCb cb);
+                public Operation? get_client_info_list(ClientInfoCb cb);
+
+                public Operation? kill_client(uint32 idx, SuccessCb? cb = null);
+
+                public Operation? get_card_info_by_name(string name, CardInfoCb cb);
+                public Operation? get_card_info_by_index(uint32 idx, CardInfoCb cb);
+                public Operation? get_card_info_list(CardInfoCb cb);
+
+                public Operation? set_card_profile_by_index(uint32 idx, string profile, SuccessCb? cb = null);
+                public Operation? set_card_profile_by_name(string name, string profile, SuccessCb? cb = null);
+
+                public Operation? get_sink_input_info(uint32 idx, SinkInputInfoCb cb);
+                public Operation? get_sink_input_info_list(SinkInputInfoCb cb);
+
+                public Operation? move_sink_input_by_index(uint32 idx, uint32 sink_idx, SuccessCb? cb = null);
+                public Operation? move_sink_input_by_name(uint32 idx, string sink_name, SuccessCb? cb = null);
+
+                public Operation? set_sink_input_volume(uint32 idx, CVolume volume, SuccessCb? cb = null);
+                public Operation? set_sink_input_mute(uint32 idx, bool mute, SuccessCb? cb = null);
+
+                public Operation? kill_sink_input(uint32 idx, SuccessCb? cb = null);
+
+                public Operation? get_source_output_info(uint32 idx, SourceOutputInfoCb cb);
+                public Operation? get_source_output_info_list(SourceOutputInfoCb cb);
+
+                public Operation? move_source_output_by_index(uint32 idx, uint32 source_idx, SuccessCb? cb = null);
+                public Operation? move_source_output_by_name(uint32 idx, string source_name, SuccessCb? cb = null);
+
+                public Operation? kill_source_output(uint32 idx, SuccessCb? cb = null);
+
+                public Operation? stat(StatInfoCb cb);
+
+                public Operation? get_sample_info_by_name(string name, SampleInfoCb cb);
+                public Operation? get_sample_info_by_index(uint32 idx, SampleInfoCb cb);
+                public Operation? get_sample_info_list(SampleInfoCb cb);
+
+                public Operation? remove_sample(string name, SuccessCb? cb = null);
+
+                public Operation? play_sample(string name, string? device = null, Volume volume = Volume.INVALID, SuccessCb? cb = null);
+                public Operation? play_sample_with_proplist(string name, string? device = null, Volume volume = Volume.INVALID, Proplist? p = null, IndexCb? cb = null);
+        }
+
+        [Compact]
+        [CCode (cname="pa_stream", cprefix="pa_stream_", unref_function="pa_stream_unref", ref_function="pa_stream_ref", has_type_id=false)]
+        public class Stream {
+
+                [CCode (cname="pa_stream_flags_t", cprefix="PA_STREAM_", has_type_id=false)]
+                public enum Flags {
+                        START_CORKED,
+                        INTERPOLATE_TIMING,
+                        NOT_MONOTONIC,
+                        AUTO_TIMING_UPDATE,
+                        NO_REMAP_CHANNELS,
+                        NO_REMIX_CHANNELS,
+                        FIX_FORMAT,
+                        FIX_RATE,
+                        FIX_CHANNELS,
+                        DONT_MOVE,
+                        VARIABLE_RATE,
+                        PEAK_DETECT,
+                        START_MUTED,
+                        ADJUST_LATENCY,
+                        EARLY_REQUESTS,
+                        DONT_INHIBIT_AUTO_SUSPEND,
+                        START_UNMUTED,
+                        FAIL_ON_SUSPEND
+                }
+
+                [CCode (cname="pa_stream_state_t", cprefix="PA_STREAM_", has_type_id=false)]
+                public enum State {
+                        UNCONNECTED,
+                        CREATING,
+                        READY,
+                        FAILED,
+                        TERMINATED;
+
+                        [CCode (cname="PA_STREAM_IS_GOOD")]
+                        public bool IS_GOOD();
+                }
+
+                [CCode (cname="pa_stream_direction_t", cprefix="PA_STREAM_", has_type_id=false)]
+                public enum Direction {
+                        NODIRECTION,
+                        PLAYBACK,
+                        RECORD,
+                        UPLOAD
+                }
+
+                [CCode (cname="pa_seek_mode_t", cprefix="PA_SEEK_", has_type_id=false)]
+                public enum SeekMode {
+                        RELATIVE,
+                        ABSOLUTE,
+                        RELATIVE_ON_READ,
+                        RELATIVE_END
+                }
+
+                [CCode (cname="pa_buffer_attr", has_type_id=false)]
+                public struct BufferAttr {
+                        uint32 maxlength;
+                        uint32 tlength;
+                        uint32 prebuf;
+                        uint32 minreq;
+                        uint32 fragsize;
+                }
+
+                [CCode (cname="pa_timing_info", has_type_id=false)]
+                public struct TimingInfo {
+                        timeval timestamp;
+                        int synchronized_clocks;
+                        usec sink_usec;
+                        usec source_usec;
+                        usec transport_usec;
+                        int playing;
+                        int write_index_corrupt;
+                        int64 write_index;
+                        int read_index_corrupt;
+                        int64 read_index;
+                        usec configured_sink_usec;
+                        usec configured_source_usec;
+                        int64 since_underrun;
+                }
+
+                [CCode (cname="PA_STREAM_EVENT_REQUEST_CORK")]
+                public const string EVENT_REQUEST_CORK;
+
+                [CCode (cname="PA_STREAM_EVENT_REQUEST_UNCORK")]
+                public const string EVENT_REQUEST_UNCORK;
+
+                public delegate void SuccessCb(Stream s, int success);
+                public delegate void RequestCb(Stream s, size_t nbytes);
+                public delegate void NotifyCb(Stream s);
+                public delegate void EventCb(Stream s, string name, Proplist proplist);
+
+                [CCode (cname="pa_stream_new_with_proplist")]
+                public Stream(Context c, string name, SampleSpec ss, ChannelMap? map = null, Proplist? proplist = null);
+
+                public State get_state();
+                public Context get_context();
+                public uint32 get_index();
+                public uint32 get_device_index();
+                public unowned string? get_device_name();
+                public int is_suspended();
+                public int is_corked();
+
+                public int connect_playback(string? dev = null, BufferAttr? a = null, Flags flags = 0, CVolume? volume = null, Stream? sync_stream = null);
+                public int connect_record(string? dev = null, BufferAttr? a = null, Flags flags = 0);
+                public int connect_upload(size_t length);
+                public int disconnect();
+                public int finish_upload();
+
+                public int begin_write(out void* data, out size_t nbytes);
+                public int cancel_write();
+                public int write(void *data, size_t bytes, FreeCb? free_cb = null, int64 offset = 0, SeekMode mode = SeekMode.RELATIVE);
+                public int peek(out void *data, out size_t nbytes);
+                public int drop();
+                public size_t writable_size();
+                public size_t readable_size();
+
+                public void set_state_callback(NotifyCb? cb = null);
+                public void set_write_callback(RequestCb? cb = null);
+                public void set_read_callback(RequestCb? cb = null);
+                public void set_overflow_callback(NotifyCb? cb = null);
+                public void set_underflow_callback(NotifyCb? cb = null);
+                public void set_started_callback(NotifyCb? cb = null);
+                public void set_latency_update_callback(NotifyCb? cb = null);
+                public void set_moved_callback(NotifyCb? cb = null);
+                public void set_suspended_callback(NotifyCb? cb = null);
+                public void set_event_callback(EventCb? cb = null);
+                public void set_buffer_attr_callback(NotifyCb? cb = null);
+
+                public Operation? drain(SuccessCb? cb = null);
+                public Operation? update_timing_info(SuccessCb? cb = null);
+
+                public Operation? cork(bool b, SuccessCb? cb = null);
+                public Operation? flush(SuccessCb? cb = null);
+                public Operation? prebuf(SuccessCb? cb = null);
+                public Operation? trigger(SuccessCb? cb = null);
+
+                public Operation? set_name(string name, SuccessCb? cb = null);
+                public Operation? set_buffer_attr(BufferAttr attr, SuccessCb? cb = null);
+                public Operation? update_sample_rate(uint32 rate, SuccessCb? cb = null);
+
+                [CCode (array_length = false)]
+                public Operation? proplist_remove(string[] keys, SuccessCb? cb = null);
+                public Operation? proplist_update(UpdateMode mode, Proplist pl, SuccessCb? cb = null);
+
+                public unowned TimingInfo? get_timing_info();
+                public int get_time(out usec u);
+                public int get_latency(out usec u, out bool negative = null);
+
+                public unowned SampleSpec? get_sample_spec();
+                public unowned ChannelMap? get_channel_map();
+                public unowned BufferAttr? get_buffer_attr();
+
+                public int set_monitor_stream(uint32 sink_input);
+                public uint32 get_monitor_stream();
+        }
+
+        [CCode (cname="pa_sink_port_info", has_type_id=false)]
+        public struct SinkPortInfo {
+                public string name;
+                public string description;
+                public uint32 priority;
+        }
+
+        [CCode (cname="pa_sink_info", has_type_id=false)]
+        public struct SinkInfo {
+                public string name;
+                public uint32 index;
+                public string description;
+                public SampleSpec sample_spec;
+                public ChannelMap channel_map;
+                public uint32 owner_module;
+                public CVolume volume;
+                public int mute;
+                public uint32 monitor_source;
+                public string monitor_source_name;
+                public usec latency;
+                public string driver;
+                public SinkFlags flags;
+                public Proplist proplist;
+                public usec configured_latency;
+                public Volume base_volume;
+                public SinkState state;
+                public uint32 n_volume_steps;
+                public uint32 card;
+                public uint32 n_ports;
+                public SinkPortInfo*[] ports;
+                public SinkPortInfo* active_port;
+        }
+
+        [CCode (cname="pa_source_port_info", has_type_id=false)]
+        public struct SourcePortInfo {
+                public string name;
+                public string description;
+                public uint32 priority;
+        }
+
+        [CCode (cname="pa_source_info", has_type_id=false)]
+        public struct SourceInfo {
+                public string name;
+                public uint32 index;
+                public string description;
+                public SampleSpec sample_spec;
+                public ChannelMap channel_map;
+                public uint32 owner_module;
+                public CVolume volume;
+                public int mute;
+                public uint32 monitor_of_sink;
+                public string monitor_of_sink_name;
+                public usec latency;
+                public string driver;
+                public SourceFlags flags;
+                public Proplist proplist;
+                public usec configured_latency;
+                public Volume base_volume;
+                public SourceState state;
+                public uint32 n_volume_steps;
+                public uint32 card;
+                public uint32 n_ports;
+                public SourcePortInfo*[] ports;
+                public SourcePortInfo* active_port;
+        }
+
+        [CCode (cname="pa_server_info", has_type_id=false)]
+        public struct ServerInfo {
+                public string user_name;
+                public string host_name;
+                public string server_version;
+                public string server_name;
+                public SampleSpec sample_spec;
+                public string default_sink_name;
+                public string default_source_name;
+                public ChannelMap channel_map;
+        }
+
+        [CCode (cname="pa_module_info", has_type_id=false)]
+        public struct ModuleInfo {
+                public uint32 index;
+                public string name;
+                public string argument;
+                public uint32 n_used;
+                public Proplist proplist;
+        }
+
+        [CCode (cname="pa_client_info", has_type_id=false)]
+        public struct ClientInfo {
+                public uint32 index;
+                public string name;
+                public uint32 owner_module;
+                public string driver;
+                public Proplist proplist;
+        }
+
+        [CCode (cname="pa_card_profile_info", has_type_id=false)]
+        public struct CardProfileInfo {
+                public string name;
+                public string description;
+                public uint32 n_sinks;
+                public uint32 n_sources;
+                public uint32 priority;
+        }
+
+        [CCode (cname="pa_card_info", has_type_id=false)]
+        public struct CardInfo {
+                public uint32 index;
+                public string name;
+                public uint32 owner_module;
+                public string driver;
+                public uint32 n_profiles;
+                public CardProfileInfo[] profiles;
+                public CardProfileInfo *active_profile;
+                public Proplist proplist;
+        }
+
+        [CCode (cname="pa_sink_input_info", has_type_id=false)]
+        public struct SinkInputInfo {
+                public uint32 index;
+                public string name;
+                public uint32 owner_module;
+                public uint32 client;
+                public uint32 sink;
+                public SampleSpec sample_spec;
+                public ChannelMap channel_map;
+                public CVolume volume;
+                public uint32 buffer_usec;
+                public uint32 sink_usec;
+                public string resample_method;
+                public string driver;
+                public int mute;
+                public Proplist proplist;
+                public int corked;
+                public int has_volume;
+                public int volume_writable;
+        }
+
+        [CCode (cname="pa_source_output_info", has_type_id=false)]
+        public struct SourceOutputInfo {
+                public uint32 index;
+                public string name;
+                public uint32 owner_module;
+                public uint32 client;
+                public uint32 source;
+                public SampleSpec sample_spec;
+                public ChannelMap channel_map;
+                public uint32 buffer_usec;
+                public uint32 sink_usec;
+                public string resample_method;
+                public string driver;
+                public Proplist proplist;
+                public int corked;
+                public CVolume volume;
+                public int mute;
+                public int has_volume;
+                public int volume_writable;
+        }
+
+        [CCode (cname="pa_stat_info", has_type_id=false)]
+        public struct StatInfo {
+                public uint32 memblock_total;
+                public uint32 memblock_total_size;
+                public uint32 memblock_allocated;
+                public uint32 memblock_allocated_size;
+                public uint32 scache_size;
+        }
+
+        [CCode (cname="pa_sample_info", has_type_id=false)]
+        public struct SampleInfo {
+                public uint32 index;
+                public string name;
+                public CVolume volume;
+                public SampleSpec sample_spec;
+                public ChannelMap channel_map;
+                public usec duration;
+                public uint32 bytes;
+                public bool lazy;
+                public string filename;
+                public Proplist proplist;
+        }
+
+        [CCode (cname="pa_sink_flags_t", cprefix="PA_SINK_", has_type_id=false)]
+        public enum SinkFlags {
+                HW_VOLUME_CTRL,
+                LATENCY,
+                HARDWARE,
+                NETWORK,
+                HW_MUTE_CTRL,
+                DECIBEL_VOLUME,
+                FLAT_VOLUME,
+                DYNAMIC_LATENCY
+        }
+
+        [CCode (cname="pa_source_flags_t", cprefix="PA_SOURCE_", has_type_id=false)]
+        public enum SourceFlags {
+                HW_VOLUME_CTRL,
+                LATENCY,
+                HARDWARE,
+                NETWORK,
+                HW_MUTE_CTRL,
+                DECIBEL_VOLUME,
+                DYNAMIC_LATENCY
+        }
+
+        [CCode (cname="pa_sink_state_t", cprefix="PA_SINK_", has_type_id=false)]
+        public enum SinkState {
+                INVALID_STATE,
+                RUNNING,
+                IDLE,
+                SUSPENDED;
+
+                [CCode (cname="PA_SINK_IS_OPENED")]
+                public bool IS_OPENED();
+        }
+
+        [CCode (cname="pa_source_state_t", cprefix="PA_SOURCE_", has_type_id=false)]
+        public enum SourceState {
+                INVALID_STATE,
+                RUNNING,
+                IDLE,
+                SUSPENDED;
+
+                [CCode (cname="PA_SOURCE_IS_OPENED")]
+                public bool IS_OPENED();
+        }
+
+        [CCode (cname="pa_gettimeofday")]
+        public unowned timeval gettimeofday(out timeval tv);
+
+        [CCode (cname="pa_timeval_diff")]
+        public usec timeval_diff(ref timeval a, ref timeval b);
+
+        [CCode (cname="pa_timeval_cmp")]
+        public int timeval_cmp(ref timeval a, ref timeval b);
+
+        [CCode (cname="pa_timeval_age")]
+        public usec timeval_age(ref timeval a);
+
+        [CCode (cname="pa_timeval_add")]
+        public unowned timeval timeval_add(ref timeval tv, usec x);
+
+        [CCode (cname="pa_timeval_sub")]
+        public unowned timeval timeval_sub(ref timeval tv, usec x);
+
+        [CCode (cname="pa_timeval_store")]
+        public unowned timeval timeval_store(out timeval tv, usec c);
+
+        [CCode (cname="pa_timeval_load")]
+        public usec timeval_load(timeval tv);
+
+        [CCode (cname="PA_USEC_MAX")]
+        public const usec USEC_MAX;
+
+        [CCode (cname="PA_USEC_INVALID")]
+        public const usec USEC_INVALID;
+
+        [CCode (cname="PA_MSEC_PER_SEC")]
+        public const usec MSEC_PER_SEC;
+
+        [CCode (cname="PA_USEC_PER_SEC")]
+        public const usec USEC_PER_SEC;
+
+        [CCode (cname="PA_NSEC_PER_SEC")]
+        public const uint64 NSEC_PER_SEC;
+
+
+        [CCode (cname="PA_USEC_PER_MSEC")]
+        public const usec USEC_PER_MSEC;
+
+        [CCode (cname="PA_NSEC_PER_MSEC")]
+        public const uint64 NSEC_PER_MSEC;
+
+
+        [CCode (cname="PA_NSEC_PER_USEC")]
+        public const uint64 NSEC_PER_USEC;
+}